Synesis Software STLSoft - ... Robust, Lightweight, Cross-platform, Template Software ...

fastformat/inserters/integer.hpp

Go to the documentation of this file.
00001 /* /////////////////////////////////////////////////////////////////////////
00002  * File:        fastformat/inserters/integer.hpp
00003  *
00004  * Purpose:     Inserter functions for floating-point types
00005  *
00006  * Created:     26th May 2009
00007  * Updated:     7th March 2010
00008  *
00009  * Home:        http://www.fastformat.org/
00010  *
00011  * Copyright (c) 2009-2010, Matthew Wilson and Synesis Software
00012  * All rights reserved.
00013  *
00014  * Redistribution and use in source and binary forms, with or without
00015  * modification, are permitted provided that the following conditions are
00016  * met:
00017  *
00018  * - Redistributions of source code must retain the above copyright notice,
00019  *   this list of conditions and the following disclaimer.
00020  * - Redistributions in binary form must reproduce the above copyright
00021  *   notice, this list of conditions and the following disclaimer in the
00022  *   documentation and/or other materials provided with the distribution.
00023  * - Neither the name(s) of Matthew Wilson and Synesis Software nor the
00024  *   names of any contributors may be used to endorse or promote products
00025  *   derived from this software without specific prior written permission.
00026  *
00027  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
00028  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
00029  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
00030  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
00031  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00032  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00033  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00034  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00035  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00036  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00037  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00038  *
00039  * ////////////////////////////////////////////////////////////////////// */
00040 
00041 
00047 #ifndef FASTFORMAT_INCL_FASTFORMAT_INSERTERS_HPP_INTEGER
00048 #define FASTFORMAT_INCL_FASTFORMAT_INSERTERS_HPP_INTEGER
00049 
00050 /* /////////////////////////////////////////////////////////////////////////
00051  * Version information
00052  */
00053 
00054 #ifndef FASTFORMAT_DOCUMENTATION_SKIP_SECTION
00055 # define FASTFORMAT_VER_FASTFORMAT_INSERTERS_HPP_INTEGER_MAJOR      1
00056 # define FASTFORMAT_VER_FASTFORMAT_INSERTERS_HPP_INTEGER_MINOR      1
00057 # define FASTFORMAT_VER_FASTFORMAT_INSERTERS_HPP_INTEGER_REVISION   2
00058 # define FASTFORMAT_VER_FASTFORMAT_INSERTERS_HPP_INTEGER_EDIT       7
00059 #endif /* !FASTFORMAT_DOCUMENTATION_SKIP_SECTION */
00060 
00061 /* /////////////////////////////////////////////////////////////////////////
00062  * Includes
00063  */
00064 
00065 #include <fastformat/fastformat.h>
00066 #include <fastformat/quality/contract.h>
00067 #include <fastformat/util/string/snprintf.h>
00068 
00069 #include <stlsoft/conversion/integer_to_string.hpp>
00070 #include <stlsoft/meta/is_integral_type.hpp>
00071 #include <stlsoft/string/case_functions.hpp>
00072 #include <stlsoft/string/ctype_traits.hpp>
00073 #include <stlsoft/string/shim_string.hpp>
00074 #include <stlsoft/shims/access/string/std/c_string.h>
00075 #include <stlsoft/util/limit_traits.h>
00076 #include <stlsoft/util/integral_printf_traits.hpp>
00077 
00078 #include <ctype.h>      /* for ::toupper */
00079 #include <stdlib.h>     /* for abs() */
00080 
00081 /* /////////////////////////////////////////////////////////////////////////
00082  * Compatibility
00083  */
00084 
00085 #if defined(STLSOFT_COMPILER_IS_MSVC) && \
00086     _MSC_VER < 1300
00087  /* can't use shim string with some old compilers - not clear why - so 
00088   * must use a string type. stlsoft::simple_string is used because it
00089   * guarantees contiguous storage, so can sprintf() into its buffer
00090   */
00091 # define FASTFORMAT_INSERTER_INTEGER_NO_USE_SHIM_STRING_
00092 # include <stlsoft/string/simple_string.hpp>
00093 #endif
00094 
00095 /* /////////////////////////////////////////////////////////////////////////
00096  * Namespace
00097  */
00098 
00099 #if !defined(FASTFORMAT_NO_NAMESPACE)
00100 namespace fastformat
00101 {
00102 namespace inserters
00103 {
00104 #endif /* !FASTFORMAT_NO_NAMESPACE */
00105 
00106 /* /////////////////////////////////////////////////////////////////////////
00107  * Implementation
00108  */
00109 
00110 #ifndef FASTFORMAT_DOCUMENTATION_SKIP_SECTION
00111 namespace integer_impl
00112 {
00113 
00114 # ifdef FASTFORMAT_INSERTER_INTEGER_NO_USE_SHIM_STRING_
00115 typedef stlsoft::basic_simple_string<ff_char_t>         ff_to_i_r_t_;
00116 # else /* ? FASTFORMAT_INSERTER_INTEGER_NO_USE_SHIM_STRING_ */
00117 typedef stlsoft::basic_shim_string<ff_char_t, 20>       ff_to_i_r_t_;
00118 # endif /* FASTFORMAT_INSERTER_INTEGER_NO_USE_SHIM_STRING_ */
00119 
00120 inline int default_width_sentinel_()
00121 {
00122     return stlsoft::limit_traits<int>::minimum();
00123 }
00124 
00125 inline int default_precision_sentinel_()
00126 {
00127     return -1;
00128 }
00129 
00130 inline ff_char_t* make_x_upper_(ff_char_t* from, ff_char_t* to)
00131 {
00132     for(; to != from; ++from)
00133     {
00134         if('x' == *from)
00135         {
00136             *from = 'X';
00137             break;
00138         }
00139     }
00140 
00141     return from;
00142 }
00143 
00144 template <typename I>
00145 inline ff_to_i_r_t_ integer_helper_2(
00146     I const&            value
00147 ,   ff_char_t const*    fmt
00148 )
00149 {
00150     STLSOFT_STATIC_ASSERT(stlsoft::is_integral_type<I>::value);
00151 
00152     FASTFORMAT_CONTRACT_ENFORCE_PRECONDITION_PARAMS_INTERNAL(NULL != fmt, "fmt may not be null");
00153 
00154     typedef ff_to_i_r_t_   result_t;
00155 
00156     enum { maxRepeats = 3 };
00157 
00158 #ifdef FASTFORMAT_INSERTER_INTEGER_NO_USE_SHIM_STRING_
00159 
00160     result_t    result(20u, '~');
00161 
00162     { for(int i = 0;; )
00163     {
00164         int n = fastformat_util_snprintf(&result[0], result.size(), fmt, value);
00165 
00166         if(n > int(result.size()))
00167         {
00168             // An implementation that does it correctly, so just resize
00169             // to the desired size and go again
00170 
00171             // This'll only return false when exception-handling is
00172             // suppressed, but the code is correct without pre-processor
00173             // logic in either case, so leave as is
00174             result.resize(n);
00175         }
00176         else if(n < 0)
00177         {
00178             if(maxRepeats == ++i)
00179             {
00180                 result.clear();
00181                 break;
00182             }
00183             else
00184             {
00185                 // This'll only return false when exception-handling is
00186                 // suppressed, but the code is correct without pre-processor
00187                 // logic in either case, so leave as is
00188                 result.resize(1u + result.size() * 3);
00189             }
00190         }
00191         else
00192         {
00193             // It's worked!
00194             //
00195             // We must nul-terminate, and resize, so that the shim string's
00196             // length is evaluated correctly
00197             result.resize(size_t(n));
00198 
00199             break;
00200         }
00201     }}
00202 
00203     return result;
00204 
00205 #else /* ? FASTFORMAT_INSERTER_INTEGER_NO_USE_SHIM_STRING_ */
00206 
00207 
00208 # if _STLSOFT_VER < 0x010a0000 && \
00209      defined(_STLSOFT_1_10_VER) && \
00210      _STLSOFT_1_10_VER < 0x010a0109
00211 
00212     /* Using STLSoft 1.10.1 alpha 1 - alpha 8 */
00213 
00214 #  error This class cannot work with STLSoft 1.10 versions between 1.10.1 alpha 1 and 1.10.1 alpha 9. Please download the latest version of STLSoft 1.10 alpha
00215 
00216 # elif _STLSOFT_VER >= 0x010a0000 || \
00217        defined(_STLSOFT_1_10_VER)
00218 
00219     /* Using STLSoft 1.10+ version of basic_shim_string */
00220 
00221     // We use the public interface of the shim string.
00222 
00223     result_t result(64);
00224 
00225 #  ifndef STLSOFT_CF_THROW_BAD_ALLOC
00226     if(!result.empty())
00227 #  endif /* !STLSOFT_CF_THROW_BAD_ALLOC */
00228     {
00229         // Unlike integral types, the string representation of floating
00230         // point values do not have a known maximum length, so we must be
00231         // flexible.
00232         //
00233         // However, some implementations of snprintf() return -1 instead of
00234         // the length that would be required, so we cannot assume -1
00235         // indicates error. To handle this, we double the size of the buffer
00236         // up to four times, at which point we admit defeat.
00237 
00238         { for(int i = 0;; )
00239         {
00240             int n = fastformat_util_snprintf(result.data(), result.size() - 1, fmt, value);
00241 
00242             if(n > int(result.size()))
00243             {
00244                 // An implementation that does it correctly, so just resize
00245                 // to the desired size and go again
00246 
00247                 // This'll only return false when exception-handling is
00248                 // suppressed, but the code is correct without pre-processor
00249                 // logic in either case, so leave as is
00250                 if(!result.resize(n))
00251                 {
00252                     result.truncate(0);
00253                     break;
00254                 }
00255             }
00256             else if(n < 0)
00257             {
00258                 if(maxRepeats == ++i)
00259                 {
00260                     result.truncate(0);
00261                     break;
00262                 }
00263                 else
00264                 {
00265                     // This'll only return false when exception-handling is
00266                     // suppressed, but the code is correct without pre-processor
00267                     // logic in either case, so leave as is
00268                     if(!result.resize(1u + result.size() * 3))
00269                     {
00270                         result.truncate(0);
00271                         break;
00272                     }
00273                 }
00274             }
00275             else
00276             {
00277                 // It's worked!
00278                 //
00279                 // We must nul-terminate, and resize, so that the shim string's
00280                 // length is evaluated correctly
00281                 result.truncate(size_t(n));
00282 
00283                 break;
00284             }
00285         }}
00286     }
00287 
00288     return result;
00289 
00290 # else /* ? STLSoft 1.x */
00291 
00292     /* Using STLSoft 1.9+ version of basic_shim_string */
00293 
00294     // We have to use the internal buffer, which means that we are
00295     // relying on the internal workings of shim string, which is ugly.
00296     // With 1.10, we don't need to do this.
00297 
00298     result_t                result(64);
00299     result_t::buffer_type&  buffer = result.get_buffer();
00300 
00301 #  ifndef STLSOFT_CF_THROW_BAD_ALLOC
00302     if(!buffer.empty())
00303 #  endif /* !STLSOFT_CF_THROW_BAD_ALLOC */
00304     {
00305         // Unlike integral types, the string representation of floating
00306         // point values do not have a known maximum length, so we must be
00307         // flexible.
00308         //
00309         // However, some implementations of snprintf() return -1 instead of
00310         // the length that would be required, so we cannot assume -1
00311         // indicates error. To handle this, we double the size of the buffer
00312         // up to four times, at which point we admit defeat.
00313 
00314         { for(int i = 0;; )
00315         {
00316             int n = fastformat_util_snprintf(&buffer[0], buffer.size() - 1, fmt, value);
00317 
00318             if(n > int(buffer.size() - 1))
00319             {
00320                 // An implementation that does it correctly, so just resize
00321                 // to the desired size and go again
00322 
00323                 // This'll only return false when exception-handling is
00324                 // suppressed, but the code is correct without pre-processor
00325                 // logic in either case, so leave as is
00326                 if(!buffer.resize(size_t(n) + 1u))
00327                 {
00328                     buffer.resize(1u);
00329                     break;
00330                 }
00331             }
00332             else if(n < 0)
00333             {
00334                 if(maxRepeats == ++i)
00335                 {
00336                     break;
00337                 }
00338                 else
00339                 {
00340                     // This'll only return false when exception-handling is
00341                     // suppressed, but the code is correct without pre-processor
00342                     // logic in either case, so leave as is
00343                     if(!buffer.resize(1u + buffer.size() * 3))
00344                     {
00345                         buffer.resize(1u);
00346                         break;
00347                     }
00348                 }
00349             }
00350             else
00351             {
00352                 // It's worked!
00353                 //
00354                 // We must nul-terminate, and resize, so that the shim string's
00355                 // length is evaluated correctly
00356                 buffer[size_t(n)] = '\0';
00357 
00358                 buffer.resize(size_t(n) + 1u);
00359 
00360                 break;
00361             }
00362         }}
00363     }
00364 
00365     return result;
00366 # endif /* STLSoft 1.x */
00367 #endif /* FASTFORMAT_INSERTER_INTEGER_NO_USE_SHIM_STRING_ */
00368 }
00369 
00370 inline ff_to_i_r_t_ hex_integer_helper(
00371     stlsoft::uint64_t   value
00372 ,   int                 uppercaseAlpha
00373 )
00374 {
00375     ff_char_t   result[17];
00376     int         n = fastformat_util_snprintf(
00377                         &result[0]
00378                     ,   STLSOFT_NUM_ELEMENTS(result)
00379 #ifdef FASTFORMAT_USE_WIDE_STRINGS
00380                     ,    stlsoft::integral_printf_traits<stlsoft::uint64_t>::hex_format_w()
00381 #else /* ? FASTFORMAT_USE_WIDE_STRINGS */
00382                     ,    stlsoft::integral_printf_traits<stlsoft::uint64_t>::hex_format_a()
00383 #endif /* FASTFORMAT_USE_WIDE_STRINGS */
00384                     ,   value);
00385 
00386     if(uppercaseAlpha)
00387     {
00388         typedef stlsoft::ctype_traits<ff_char_t> ctype_traits_t;
00389 
00390         stlsoft::std_transform(result, result + n, result, ctype_traits_t::to_upper);
00391     }
00392 
00393     return ff_to_i_r_t_(result, size_t(n));
00394 }
00395 
00396 inline ff_to_i_r_t_ hex_integer_helper(
00397     stlsoft::sint64_t   value
00398 ,   int                 uppercaseAlpha
00399 )
00400 {
00401     return hex_integer_helper(static_cast<stlsoft::uint64_t>(value), uppercaseAlpha);
00402 }
00403 
00404 template <typename I>
00405 inline ff_to_i_r_t_ hex_integer_helper(
00406     I const&    value
00407 ,   int         uppercaseAlpha
00408 )
00409 {
00410     ff_char_t   fmt[3] = { '%', uppercaseAlpha ? 'X' : 'x', '\0' };
00411     ff_char_t   result[17];
00412     int         n = fastformat_util_snprintf(&result[0], STLSOFT_NUM_ELEMENTS(result), fmt, value);
00413 
00414     return ff_to_i_r_t_(result, size_t(n));
00415 }
00416 
00417 template <typename I>
00418 inline ff_to_i_r_t_ integer_helper_5(
00419     I const&    value
00420 ,   int         minimumWidth
00421 ,   int         precision
00422 ,   int         base
00423 ,   int         uppercaseAlpha
00424 )
00425 {
00426     STLSOFT_STATIC_ASSERT(stlsoft::is_integral_type<I>::value);
00427 
00428     FASTFORMAT_CONTRACT_ENFORCE_PRECONDITION_PARAMS_INTERNAL(10 == base || 16 == base, "base must be 10 (decimal) or 16 (hex)");
00429 
00430     if( default_width_sentinel_() == minimumWidth &&
00431         precision < 0)
00432     {
00433         // no special formatting (width or precision) required
00434 
00435         if(10 == base)
00436         {
00437             ff_char_t           sz[21];
00438             size_t              n;
00439             ff_char_t const*    s = stlsoft::integer_to_string(&sz[0], STLSOFT_NUM_ELEMENTS(sz), value, n);
00440 
00441             return ff_to_i_r_t_(s, n);
00442         }
00443         else
00444         {
00445             FASTFORMAT_CONTRACT_ENFORCE_ASSUMPTION(16 == base);
00446 
00447             return hex_integer_helper(value, uppercaseAlpha);
00448         }
00449     }
00450     else
00451     {
00452         // Performance tests with ff::real() showed a substantial advantage
00453         // in using integer_to_string() over fastformat_util_snprintf() for
00454         // the preparation of the format string, so we'll do so again here,
00455         // even though it's a little more complex, due to the need to work
00456         // for different integral types.
00457 
00458         // Now need to prepare the format string, and then invoke integer_helper_2()
00459         //
00460         // 1. %
00461         // 2. width
00462         // 3. .
00463         // 4. precision
00464         // 5. type
00465         // 6. nul
00466 
00467         ff_char_t           fmt_[101]; // max possible is 1 ('%') + 20 + 1 + 20 + 2 ('ld')
00468         ff_char_t*          fmt     =   fmt_;
00469         ff_char_t const*    type;
00470         size_t              typeLen;
00471 
00472         if(10 == base)
00473         {
00474 #ifdef FASTFORMAT_USE_WIDE_STRINGS
00475             type    =   stlsoft::integral_printf_traits<I>::format_w() + 1;
00476 #else /* ? FASTFORMAT_USE_WIDE_STRINGS */
00477             type    =   stlsoft::integral_printf_traits<I>::format_a() + 1;
00478 #endif /* FASTFORMAT_USE_WIDE_STRINGS */
00479             typeLen =   stlsoft::c_str_len(type);
00480         }
00481         else
00482         {
00483             FASTFORMAT_CONTRACT_ENFORCE_ASSUMPTION(16 == base);
00484 
00485 #ifdef FASTFORMAT_USE_WIDE_STRINGS
00486             type    =   stlsoft::integral_printf_traits<I>::hex_format_w() + 1;
00487 #else /* ? FASTFORMAT_USE_WIDE_STRINGS */
00488             type    =   stlsoft::integral_printf_traits<I>::hex_format_a() + 1;
00489 #endif /* FASTFORMAT_USE_WIDE_STRINGS */
00490             typeLen =   stlsoft::c_str_len(type);
00491         }
00492 
00493         if(default_width_sentinel_() == minimumWidth)
00494         {
00495             minimumWidth = 0;
00496         }
00497         if(precision < 0)
00498         {
00499             precision = (0 == value) ? 1 : 0;
00500         }
00501 
00502         const size_t    fmtDim  =   STLSOFT_NUM_ELEMENTS(fmt_) - 1;
00503         ff_char_t*      end     =   &fmt[fmtDim];
00504         size_t          n1;
00505         size_t          n2;
00506 
00507         // Because integer_to_string (i) writes backwards, and (ii) appends
00508         // a nul terminator, we need to do this is a strange order
00509 
00510         // 4. precision
00511         stlsoft::integer_to_string(end - (21 + typeLen), 21, precision, n1);
00512 
00513         // 5. type
00514         ::memcpy(end - (typeLen + 1), type, sizeof(ff_char_t) * typeLen);
00515         if(uppercaseAlpha)
00516         {
00517             // Make only the first (should be only) 'x' uppercase
00518             make_x_upper_(end - (typeLen + 1), end - (typeLen + 1) + typeLen);
00519         }
00520 
00521         // 6. nul
00522         *--end = '\0';
00523 
00524         end -= typeLen;
00525         end -= n1;
00526 
00527         // 2. width
00528         stlsoft::integer_to_string(end - 21, 21, minimumWidth, n2);
00529 
00530         // 3. .
00531         *--end = '.';
00532 
00533         end -= n2;
00534 
00535         // 1. %
00536         *--end = '%';
00537         fmt = end;
00538 
00539         return integer_helper_2(value, fmt);
00540     }
00541 }
00542 
00543 template <typename I>
00544 inline ff_to_i_r_t_ integer_helper_3(
00545     I const&    value
00546 ,   int         minimumWidth
00547 ,   int         precision
00548 )
00549 {
00550     return integer_helper_5(value, minimumWidth, precision, 10, false);
00551 }
00552 
00553 template <typename I>
00554 inline ff_to_i_r_t_ integer_helper_hex_3(
00555     I const&    value
00556 ,   int         minimumWidth
00557 ,   int         precision
00558 ,   int         uppercaseX
00559 )
00560 {
00561     return integer_helper_5(value, minimumWidth, precision, 16, uppercaseX);
00562 }
00563 
00564 } /* namespace integer_impl */
00565 #endif /* !FASTFORMAT_DOCUMENTATION_SKIP_SECTION */
00566 
00567 /* /////////////////////////////////////////////////////////////////////////
00568  * Inserter functions
00569  */
00570 
00585 template <typename I>
00586 inline integer_impl::ff_to_i_r_t_ integer(
00587     I const&    value
00588 ,   int         minimumWidth
00589 ,   int         precision
00590 )
00591 {
00592     STLSOFT_STATIC_ASSERT(stlsoft::is_integral_type<I>::value);
00593 
00594     FASTFORMAT_CONTRACT_ENFORCE_PRECONDITION_PARAMS_APPL_LAYER(abs(minimumWidth) < 512, "maximum value for width exceeded");
00595     FASTFORMAT_CONTRACT_ENFORCE_PRECONDITION_PARAMS_APPL_LAYER(abs(minimumWidth) < 1 || precision <= abs(minimumWidth), "decimal places must not exceed width");
00596 
00597     return integer_impl::integer_helper_3(value, minimumWidth, precision);
00598 }
00599 
00600 /* /////////////////////////////////////////////////////////////////////////
00601  * Namespace
00602  */
00603 
00604 #if !defined(FASTFORMAT_NO_NAMESPACE)
00605 } // namespace inserters
00606 using ::fastformat::inserters::integer;
00607 } // namespace fastformat
00608 #endif /* !FASTFORMAT_NO_NAMESPACE */
00609 
00610 /* /////////////////////////////////////////////////////////////////////////
00611  * Inclusion
00612  */
00613 
00614 #ifdef STLSOFT_PPF_pragma_once_SUPPORT
00615 # pragma once
00616 #endif /* STLSOFT_PPF_pragma_once_SUPPORT */
00617 
00618 /* ////////////////////////////////////////////////////////////////////// */
00619 
00620 #endif /* !FASTFORMAT_INCL_FASTFORMAT_INSERTERS_HPP_INTEGER */
00621 
00622 /* ///////////////////////////// end of file //////////////////////////// */

FastFormat Library documentation © Matthew Wilson, 2006-2009 SourceForge.net Logo