stringf.cpp

Go to the documentation of this file.
00001 /*
00002 This program is distributed under the terms of the 'MIT license'. The text
00003 of this licence follows...
00004 
00005 Copyright (c) 2007 J.D.Medhurst (a.k.a. Tixy)
00006 
00007 Permission is hereby granted, free of charge, to any person obtaining a copy
00008 of this software and associated documentation files (the "Software"), to deal
00009 in the Software without restriction, including without limitation the rights
00010 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00011 copies of the Software, and to permit persons to whom the Software is
00012 furnished to do so, subject to the following conditions:
00013 
00014 The above copyright notice and this permission notice shall be included in
00015 all copies or substantial portions of the Software.
00016 
00017 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00018 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00019 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
00020 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00021 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00022 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
00023 THE SOFTWARE.
00024 */
00025 
00032 #include "common.h"
00033 #include "stringf.h"
00034 
00035 
00036 
00037 //
00038 // StringFormatter::ConversionSpec
00039 //
00040 
00041 longlong StringFormatter::ConversionSpec::GetIntArg(unsigned flags)
00042     {
00043     va_list& args = Args;
00044     if(flags&LengthMod_ll)
00045         return va_arg(args,ulonglong); // long long argument
00046 
00047     if(flags&LengthMod_l)
00048         {
00049         // long argument...
00050         unsigned long int val = va_arg(args,unsigned long int);
00051         if(flags&SignedInt)
00052             return (signed long int)val;
00053         else
00054             return val;
00055         }
00056 
00057     // length is <= length of an int, these arguments are promoted to int when
00058     // passsed through variable length argument APIs, so we can get it's value now...
00059     unsigned val = va_arg(args,unsigned int);
00060 
00061     // and then cast it to the correct size...
00062     if(flags&LengthMod_h)
00063         {
00064         if(flags&SignedInt)
00065             return (signed short int)val;
00066         else
00067             return (unsigned short int)val;
00068         }
00069     else if(flags&LengthMod_hh)
00070         {
00071         if(flags&SignedInt)
00072             return (signed char)val;
00073         else
00074             return (unsigned char)val;
00075         }
00076     else
00077         {
00078         if(flags&SignedInt)
00079             return (signed int)val;
00080         else
00081             return val;
00082         }
00083     }
00084 
00085 
00086 const char* StringFormatter::ConversionSpec::ReadInt(const char* format, int& val)
00087     {
00088     unsigned c = *format++;
00089     if(c=='*')
00090         {
00091         val = GetIntArg();
00092         return format;
00093         }
00094 
00095     // decode decimal number...
00096     unsigned v = 0;
00097     for(;;)
00098         {
00099         c -= '0';
00100         if(c>=10u)
00101             break; // not a decimal digit
00102         v = v*10+c;
00103         c = *format++;
00104         }
00105     --format;
00106     val = v;
00107     return format;
00108     }
00109 
00110 
00111 const char* StringFormatter::ConversionSpec::Decode(const char* format)
00112     {
00113     char c;
00114 
00115     // get format flags...
00116     unsigned flags = 0;
00117     for(;;)
00118         {
00119         c = *format++;
00120         if(c=='-')
00121             flags |= FlagMinus;
00122         else if(c=='+')
00123             flags |= FlagPlus;
00124         else if(c==' ')
00125             flags |= FlagSpace;
00126         else if(c=='#')
00127             flags |= FlagHash;
00128         else if(c=='0')
00129             flags |= FlagZero;
00130         else
00131             break;
00132         }
00133     --format;
00134 
00135     // get field width...
00136     format = ReadInt(format,FieldWidth);
00137     if(FieldWidth<0)
00138         {
00139         FieldWidth = -FieldWidth;
00140         flags |= FlagMinus;
00141         }
00142 
00143     // get precision...
00144     Precision = -1;
00145     if(*format=='.')
00146         {
00147         ++format;
00148         format = ReadInt(format,Precision);
00149         }
00150 
00151     // get length modifier...
00152     #define LENGTH_MODIFIER(type)                       \
00153         (                                               \
00154         sizeof(type)==sizeof(char)?LengthMod_hh :       \
00155         sizeof(type)==sizeof(short)?LengthMod_h :       \
00156         sizeof(type)==sizeof(int)?0 :                   \
00157         sizeof(type)==sizeof(long)?LengthMod_l :        \
00158         sizeof(type)==sizeof(longlong)?LengthMod_ll :   \
00159         LengthMod_unknown                               \
00160         )
00161 
00162     c = *format++;
00163     if(c=='h')
00164         {
00165         if(*format!=c)
00166             flags |= LengthMod_h;
00167         else
00168             {
00169             flags |= LengthMod_hh;
00170             ++format;
00171             }
00172         }
00173     else if(c=='l')
00174         {
00175         if(*format!=c)
00176             flags |= LengthMod_l;
00177         else
00178             {
00179             flags |= LengthMod_ll;
00180             ++format;
00181             }
00182         }
00183     else if(c=='L')
00184         flags |= LengthMod_L;
00185     else if(c=='j')
00186         {
00187         flags |= LENGTH_MODIFIER(intmax_t);
00188         ASSERT_COMPILE(LENGTH_MODIFIER(intmax_t)!=LengthMod_unknown);
00189         }
00190     else if(c=='z')
00191         {
00192         flags |= LENGTH_MODIFIER(size_t);
00193         ASSERT_COMPILE(LENGTH_MODIFIER(size_t)!=LengthMod_unknown);
00194         }
00195     else if(c=='t')
00196         {
00197         flags |= LENGTH_MODIFIER(ptrdiff_t);
00198         ASSERT_COMPILE(LENGTH_MODIFIER(ptrdiff_t)!=LengthMod_unknown);
00199         }
00200     else
00201         --format; // restore format
00202 
00203     // overide FlagZero if we have FlagMinus
00204     if(flags&FlagMinus)
00205         flags &= ~FlagZero;
00206     Flags = flags;
00207 
00208     // finally, get conversion specifier...
00209     c = *format++;
00210     ConversionSpecifier = c;
00211     if(!c)
00212         --format; // avoid dropping null character from format string
00213     return format;
00214     }
00215 
00216 
00217 
00218 //
00219 // StringFormatter
00220 //
00221 
00222 char* StringFormatter::PushHex(ulonglong val, char* dst, int precision, int x, int prefix)
00223     {
00224     if(!val)
00225         {
00226         // value is zero...
00227         prefix = false;  // suppress prefix
00228         if(!precision)   // produce nothing if precision is zero...
00229             return dst;
00230         }
00231 
00232     if(precision<0)
00233         precision = 1; // -ve precission means produce as many digits as required, i.e. 1 or more.
00234 
00235     int alphaAdjust = x-'X'+7;
00236     do
00237         {
00238         unsigned c = val&0xf;
00239         if(c>=10)
00240             c += alphaAdjust;
00241         c += '0';
00242         *--dst = c;
00243         --precision;
00244         val >>= 4;
00245         }
00246     while(val);
00247 
00248     while(--precision>=0)
00249         *--dst = '0';
00250 
00251     if(prefix)
00252         {
00253         *--dst = x;
00254         *--dst = '0';
00255         }
00256 
00257     return dst;
00258     }
00259 
00260 
00261 char* StringFormatter::PushOctal(ulonglong val, char* dst, int precision, int prefix)
00262     {
00263     if(!precision && !val) // produce nothing if precision is zero and val is zero
00264         return dst;
00265 
00266     if(precision<0)
00267         precision = 1; // -ve precission means produce as many digits as required, i.e. 1 or more.
00268 
00269     do
00270         {
00271         unsigned c = val&0x7;
00272         c += '0';
00273         *--dst = c;
00274         --precision;
00275         val >>= 3;
00276         }
00277     while(val);
00278 
00279     while(--precision>=0)
00280         *--dst = '0';
00281 
00282     if(prefix && *dst!='0')
00283         *--dst = '0';
00284 
00285     return dst;
00286     }
00287 
00288 
00289 char* StringFormatter::PushDecimal(ulonglong val, char* dst, int precision)
00290     {
00291     if(!precision && !val) // produce nothing if precision is zero and val is zero
00292         return dst;
00293 
00294     if(precision<0)
00295         precision = 1; // -ve precission means produce as many digits as required, i.e. 1 or more.
00296 
00297     if(val<=0xffffffffu)
00298         {
00299         // value fits into 32 bits, so we can avoid 'long long' variables (and be faster)...
00300         unsigned a = (unsigned)val;
00301         while(a)
00302             {
00303             // calculate a/10 and a%10 using fixed point arithamic, see http://www.cs.uiowa.edu/~jones/bcd/divide.html ...
00304 
00305             // calculate q = a/10 by multiplying by 1/10 accurate to 35 binary places...
00306             unsigned q = a>>1;
00307             q += q>>1;
00308             q += q>>4;
00309             q += q>>8;
00310             q += q>>16;
00311             q  = q>>3;
00312             // q now equals either val/10 or val/10-1
00313 
00314             // calculate r = remainder of val/10
00315             unsigned r = a - q*10;
00316             if(r>=10)
00317                 {
00318                 // remainder too big (due to rounding error in approximate divide by 10) so adjust values...
00319                 r -= 10;
00320                 ++q;
00321                 }
00322             a = q; // a now equal a/10
00323 
00324             *--dst = r+'0'; // store single digit
00325             --precision;
00326             }
00327         }
00328     else
00329         {
00330         // value doesn't fit into 32 bits, so we have to use 'long long' variables...
00331         ulonglong a = val;
00332         do
00333             {
00334             // calculate a/10 and a%10 using fixed point arithamic, see http://www.cs.uiowa.edu/~jones/bcd/divide.html ...
00335 
00336             // calculate q = a/10 by multiplying by 1/10 accurate to 131 binary places...
00337             ulonglong q = a>>1;
00338             q += q>>1;
00339             q += q>>4;
00340             q += q>>8;
00341             q += q>>16;
00342             q += (q>>32);
00343             q += ((q>>32)>>32); // shift by 64 can generate warnings, so do shift in two steps, lets hope the compiler is smart enough to realise this is a nop when long long is 64 bits or less
00344             ASSERT_COMPILE(sizeof(ulonglong)<=sizeof(uint64_t)*2); // we've stopped at 128 bits, assert long long isn't bigger than this!
00345             q  = q>>3;
00346             // q now equals either val/10 or val/10-1
00347 
00348             // calculate r = remainder of val/10
00349             unsigned r = a - q*10;
00350             if(r>=10)
00351                 {
00352                 // remainder too big (due to rounding error in approximate divide by 10) so adjust values...
00353                 r -= 10;
00354                 ++q;
00355                 }
00356             a = q; // a now equal a/10
00357 
00358             *--dst = r+'0'; // store single digit
00359             --precision;
00360             }
00361         while(a);
00362         }
00363 
00364     while(--precision>=0)
00365         *--dst = '0';
00366 
00367     return dst;
00368     }
00369 
00370 
00371 
00372 char* StringFormatter::DefaultUnkownFormat(char*& dstEnd, ConversionSpec& spec)
00373     {
00374     char c = spec.ConversionSpecifier;
00375 
00376     // check for floating point conversions...
00377     if(c>='A' && c<='Z')
00378         c += 'a'-'A';
00379     if(c=='a' || c=='e' || c=='f' || c=='g')
00380         {
00381         // ignore value because we don't support floating point...
00382         if(spec.Flags&LengthMod_L)
00383             va_arg(spec.Args,long double);
00384         else
00385             va_arg(spec.Args,double);
00386         return dstEnd;
00387         }
00388 
00389     // ignore unkown options...
00390     return dstEnd;
00391     }
00392 
00393 
00394 
00395 #ifndef STRINGFORMATTER_UNKOWNFORMAT_DEFINED
00396 
00397 char* StringFormatter::UnkownFormat(char*& dstEnd, ConversionSpec& format)
00398     {
00399     return DefaultUnkownFormat(dstEnd, format);
00400     }
00401 
00402 #endif
00403 
00404 
00405 size_t StringFormatter::VFormat(const char* formatString, va_list args)
00406     {
00407     ConversionSpec convertionSpec(args);
00408 
00409     size_t outSize = 0;
00410     const char* src = formatString;
00411     for(;;)
00412         {
00413         char c;
00414 
00415         // get characters to copy...
00416         const char* srcBase = src;
00417         do c = *src++;
00418         while(c!=0 && c!='%');
00419 
00420         // ouput everything up to character which stopped us...
00421         size_t size = src-srcBase-1;
00422         outSize += size;
00423         Out(srcBase,size);
00424 
00425         if(c==0)
00426             {
00427             // end of formatString...
00428             return outSize;
00429             }
00430 
00431         if(*src=='%')
00432             {
00433             // '%%' found, output '%' and continue...
00434             ++outSize;
00435             Out(src++,1);
00436             continue;
00437             }
00438 
00439         // get format data...
00440         src = convertionSpec.Decode(src);
00441 
00442         // initialise data for conversion...
00443         char temp[FormatTextBufferSize];
00444         ASSERT_COMPILE(FormatTextBufferSize>sizeof(longlong)/2*5+1);     // check big enough for decimal number with sign character
00445         ASSERT_COMPILE(FormatTextBufferSize>(sizeof(longlong)*8+2)/3+1); // check big enough for octal number with leading zero
00446         ASSERT_COMPILE(FormatTextBufferSize>(sizeof(longlong)*2)+2);     // check big enough for hex number with leading '0x'
00447         char* stringEnd = temp+sizeof(temp);
00448         register char* string = stringEnd;
00449         size_t zeroPadPosition = 0;
00450         unsigned flags = convertionSpec.Flags;
00451 
00452         // do conversion...
00453         c = convertionSpec.ConversionSpecifier;
00454         if(c=='c')
00455             {
00456             *--string = (char)convertionSpec.GetIntArg();
00457             }
00458         else if(c=='p')
00459             {
00460             ulonglong val = (ulonglong)(uintptr_t)convertionSpec.GetPointerArg();
00461             string = PushHex(val,string,sizeof(void*)*2,'x',false);
00462             }
00463         else if(c=='s')
00464             {
00465             string = (char*)convertionSpec.GetPointerArg();
00466             stringEnd = string;
00467             int precision = convertionSpec.Precision;
00468             while(*stringEnd++ && precision--) {}
00469             --stringEnd;
00470             }
00471         else if(c=='n')
00472             {
00473             void* ptr = convertionSpec.GetPointerArg();
00474                  if(flags&LengthMod_ll)     *(longlong*)ptr = outSize;
00475             else if(flags&LengthMod_l)      *(long*)ptr = outSize;
00476             else if(flags&LengthMod_h)      *(short*)ptr = outSize;
00477             else if(flags&LengthMod_hh)     *(signed char*)ptr = outSize;
00478             else                            *(int*)ptr = outSize;
00479             }
00480         else if(c!='d' && c!='i' && c!='o' && c!='u' && c!='x' && c!='X')
00481             {
00482             string = UnkownFormat(stringEnd,convertionSpec);
00483             }
00484         else // an integer conversion...
00485             {
00486             // clip precission so we don't overflow our buffer...
00487             const int MaxPrecision = FormatTextBufferSize-2; // allow 2 extra for hex prefix '0x'
00488             int precision = convertionSpec.Precision;
00489             if(precision>MaxPrecision)
00490                 precision = MaxPrecision;
00491             if(precision>=0)
00492                 flags &= ~FlagZero;
00493 
00494             if(c=='d' || c=='i')
00495                 {
00496                 longlong val = convertionSpec.GetIntArg(flags|SignedInt);
00497                 char sign = 0;
00498                 if(val<0)
00499                     {
00500                     sign = '-';
00501                     val = -val;
00502                     }
00503                 else if(flags&FlagPlus)
00504                     sign = '+';
00505                 else if(flags&FlagSpace)
00506                     sign = ' ';
00507                 string = PushDecimal(val,string,precision);
00508                 if(sign)
00509                     {
00510                     zeroPadPosition = 1;
00511                     *--string = sign;
00512                     }
00513                 }
00514             else
00515                 {
00516                 ulonglong val = convertionSpec.GetIntArg(flags);
00517                 if(c=='u')
00518                     {
00519                     string = PushDecimal(val,string,precision);
00520                     }
00521                 else if(c=='o')
00522                     {
00523                     string = PushOctal(val,string,precision,flags&FlagHash);
00524                     }
00525                 else // c=='x' || c=='X'
00526                     {
00527                     if(flags&FlagHash)
00528                         zeroPadPosition = 2; // position after any '0x' prefix
00529                     string = PushHex(val,string,precision,c,flags&FlagHash);
00530                     }
00531                 }
00532             }
00533 
00534         // work out where we need to insert padding to get required field width...
00535         char* padPoint;
00536         char pad = ' ';
00537         if(flags&FlagMinus)
00538             {
00539             padPoint = stringEnd;
00540             }
00541         else if(flags&FlagZero)
00542             {
00543             pad = '0';
00544             padPoint = string+zeroPadPosition;
00545             if(padPoint<string || padPoint>stringEnd)
00546                 padPoint = stringEnd;
00547             }
00548         else
00549             {
00550             padPoint = string;
00551             }
00552 
00553         // calculate width of converted argument....
00554         int width = stringEnd-string;
00555 
00556         // add produced string (up to zeroPadPosition)...
00557         size = padPoint-string;
00558         outSize += size;
00559         Out(string,size);
00560         string += size;
00561 
00562         // pad (if required)...
00563         int fieldWidth = convertionSpec.FieldWidth;
00564         if(width<fieldWidth)
00565             {
00566             size = fieldWidth-width;
00567             outSize += size;
00568             Out(pad,size);
00569             }
00570 
00571         // add rest of produced string...
00572         size = stringEnd-string;
00573         outSize += size;
00574         Out(string,size);
00575         string += size;
00576         }
00577     }
00578 
00579 
00580 size_t StringFormatter::Format(const char* formatString, ...)
00581     {
00582     va_list args;
00583     va_start(args,formatString);
00584     size_t size = VFormat(formatString,args);
00585     va_end(args);
00586     return size;
00587     }
00588 
00589 
00590 ASSERT_COMPILE(16/sizeof(void*)*sizeof(void*)==16); // code assumes sizeof(void*) is a factor of 16
00591 
00592 size_t StringFormatter::HexDumpLine(const void* data, size_t size, ptrdiff_t addressOffset)
00593     {
00594     const uint8_t* src = (const uint8_t*)data;
00595     char bytes[16];
00596 
00597     Format("%0*x",sizeof(void*)*2,src+addressOffset);
00598 
00599     for(unsigned i=0; i<16; i++)
00600         {
00601         const char* format;
00602         unsigned b;
00603         if(i<size)
00604             {
00605             b = *src++;
00606             format = "%*.2x";
00607             }
00608         else
00609             {
00610             b = ' ';
00611             format = "%*c";
00612             }
00613         int width = (i&(sizeof(uintptr_t)-1))==0 ? 4 : 3;
00614 
00615         Format(format,width,b);
00616 
00617         if(b<32 || b>0x7e)
00618             b = '.';
00619         bytes[i] = b;
00620         }
00621 
00622     Format("  %.16s\n",bytes);
00623 
00624     return size>16 ? size-16 : 0;
00625     }
00626 
00627 
00628 
00629 //
00630 // StringBufferFormatter
00631 //
00632 
00633 StringBufferFormatter::StringBufferFormatter(char* buffer, size_t size)
00634     {
00635     BufferStart = buffer;
00636     BufferPtr = buffer;
00637     char* end = buffer+size;
00638     if(end<buffer)
00639         end = (char*)~(uintptr_t)0; // clip buffer to end of memory
00640     BufferEnd = end;
00641     }
00642 
00643 
00644 void StringBufferFormatter::Out(const char* text, size_t textSize)
00645     {
00646     char* out = BufferPtr;
00647     char* end = out+textSize;
00648     if(end>BufferEnd || end<out)
00649         end = BufferEnd;
00650     while(out<end)
00651         *out++ = *text++;
00652     BufferPtr = out;
00653     }
00654 
00655 
00656 void StringBufferFormatter::Out(char character, size_t repeatCount)
00657     {
00658     char* out = BufferPtr;
00659     char* end = out+repeatCount;
00660     if(end>BufferEnd || end<out)
00661         end = BufferEnd;
00662     while(out<end)
00663         *out++ = character;
00664     BufferPtr = out;
00665     }
00666 
00667 
00668 char* StringBufferFormatter::End()
00669     {
00670     char* end = BufferPtr;
00671     if(end<BufferEnd)
00672         *end = 0;
00673     BufferPtr = BufferStart;
00674     return end;
00675     }
00676 
00677 
00678 
00679 //
00680 // C Standard library functions
00681 //
00682 
00683 extern "C" int vsprintf(char* str, const char* format, va_list ap)
00684     {
00685     StringBufferFormatter formatter(str,~(size_t)0);
00686     int len = formatter.VFormat(format, ap);
00687     formatter.End();
00688     return len;
00689     }
00690 
00691 
00692 extern "C" int sprintf(char* str, const char* format, ...)
00693     {
00694     va_list args;
00695     va_start(args,format);
00696     int len = vsprintf(str,format,args);
00697     va_end(args);
00698     return len;
00699     }
00700 
00701 
00702 extern "C" int vsnprintf(char* str, size_t size, const char* format, va_list ap)
00703     {
00704     StringBufferFormatter formatter(str,size);
00705     size_t len = formatter.VFormat(format, ap);
00706     formatter.End();
00707     if(len>=size && size) // make sure we have a terminating nul
00708         str[size-1] = 0;
00709     return len;
00710     }
00711 
00712 
00713 extern "C" int snprintf(char* str, size_t size, const char* format, ...)
00714     {
00715     va_list args;
00716     va_start(args,format);
00717     size = vsnprintf(str,size,format,args);
00718     va_end(args);
00719     return size;
00720     }
00721 
00722 

Generated by  doxygen 1.6.1