Main Page | Modules | Class Hierarchy | Class List | Directories | File List | Class Members | File Members

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

Generated by  doxygen 1.4.4