- Main Page
- Modules
- Namespaces
- Classes
- Files
- Related Pages
- Examples

# 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 |