WvStreams
wvstring.cc
00001 /*
00002  * Worldvisions Weaver Software:
00003  *   Copyright (C) 1997-2002 Net Integration Technologies, Inc.
00004  * 
00005  * Implementation of a simple and efficient printable-string class.  Most
00006  * of the class is actually inlined and can be found in wvstring.h.
00007  */
00008 #include "wvstring.h"
00009 #include <ctype.h>
00010 #include <assert.h>
00011 
00012 WvStringBuf WvFastString::nullbuf = { 0, 1 };
00013 const WvFastString WvFastString::null;
00014 
00015 const WvString WvString::empty("");
00016 
00017 
00018 // always a handy function
00019 static inline int _max(int x, int y)
00020 {
00021     return x>y ? x : y;
00022 }
00023 
00024 
00025 void WvFastString::setsize(size_t i)
00026 {
00027     unlink();
00028     newbuf(i);
00029 }
00030 
00031 
00032 
00033 WvFastString::WvFastString()
00034 {
00035     link(&nullbuf, NULL);
00036 }
00037 
00038 
00039 WvFastString::WvFastString(const WvFastString &s)
00040 {
00041     link(s.buf, s.str);
00042 }
00043 
00044 
00045 WvFastString::WvFastString(const WvString &s)
00046 {
00047     link(s.buf, s.str);
00048 }
00049 
00050 
00051 void WvFastString::construct(const char *_str)
00052 {
00053     // just copy the pointer - no need to allocate memory!
00054     str = (char *)_str; // I promise not to change anything!
00055     buf = NULL;
00056 }
00057 
00058 
00059 WvFastString::WvFastString(const char *_str)
00060 {
00061     construct(_str);
00062 }
00063 
00064 
00065 void WvString::copy_constructor(const WvFastString &s)
00066 {
00067     unlink();   // WvFastString has already been created by now
00068 
00069     if (!s.buf)
00070     {
00071         link(&nullbuf, s.str);
00072         unique();
00073     }
00074     else
00075         link(s.buf, s.str); // already in a nice, safe WvStreamBuf
00076 }
00077 
00078 
00079 WvFastString WvFastString::offset(size_t i) const
00080 { 
00081     WvFastString retval(*this);
00082     size_t l = retval.len(); 
00083     retval.str += (i < l ? i : l); 
00084     return retval;
00085 }
00086 
00087 
00088 WvString::WvString(const char *_str)
00089 {
00090     unlink();   // WvFastString has already been created by now
00091     construct(_str);
00092 }
00093 
00094 
00095 // This function returns the NULL of a reversed string representation
00096 // for unsigned integers
00097 template <typename T>
00098 inline static char *wv_uitoar(char *begin, T i)
00099 {
00100     if (!begin)
00101         return NULL;
00102 
00103     char *end = begin;
00104 
00105     if (i == 0)
00106         *end++ = '0';
00107     else
00108     {
00109         while (i > 0)
00110         {
00111             switch (i % 10)
00112             {
00113             case 0: *end++ = '0'; break;
00114             case 1: *end++ = '1'; break;
00115             case 2: *end++ = '2'; break;
00116             case 3: *end++ = '3'; break;
00117             case 4: *end++ = '4'; break;
00118             case 5: *end++ = '5'; break;
00119             case 6: *end++ = '6'; break;
00120             case 7: *end++ = '7'; break;
00121             case 8: *end++ = '8'; break;
00122             case 9: *end++ = '9'; break;
00123             default: ;
00124             }
00125             i /= 10;
00126         }
00127     }
00128 
00129     *end = '\0';
00130     return end;
00131 }
00132 
00133 // This function returns the NULL of a reversed string representation
00134 // for signed integers
00135 template <typename T>
00136 inline static char *wv_itoar(char *begin, T i)
00137 {
00138     if (!begin)
00139         return NULL;
00140 
00141     bool negative = false;
00142     if (i < 0)
00143     {
00144         negative = true;
00145         i = -i;
00146     }
00147     char *end = wv_uitoar(begin, i);
00148     if (negative)
00149     {
00150         *end++ = '-';
00151         *end = '\0';
00152     }
00153     return end;
00154 }
00155 
00156 
00157 inline static void wv_strrev(char *begin, char *end)
00158 {
00159     if (!begin && !end)
00160         return;
00161 
00162     --end;
00163 
00164     while (begin < end)
00165     {
00166         *begin ^= *end;
00167         *end ^= *begin;
00168         *begin ^= *end;
00169         ++begin;
00170         --end;
00171     }
00172 }
00173 
00174 
00175 
00176 // NOTE: make sure that 32 bytes is big enough for your longest int.
00177 // This is true up to at least 64 bits.
00178 WvFastString::WvFastString(short i)
00179 {
00180     newbuf(32);
00181     wv_strrev(str, wv_itoar(str, i));
00182 }
00183 
00184 
00185 WvFastString::WvFastString(unsigned short i)
00186 {
00187     newbuf(32);
00188     wv_strrev(str, wv_uitoar(str, i));
00189 }
00190 
00191 
00192 WvFastString::WvFastString(int i)
00193 {
00194     newbuf(32);
00195     wv_strrev(str, wv_itoar(str, i));
00196 }
00197 
00198 
00199 WvFastString::WvFastString(unsigned int i)
00200 {
00201     newbuf(32);
00202     wv_strrev(str, wv_uitoar(str, i));
00203 }
00204 
00205 
00206 WvFastString::WvFastString(long i)
00207 {
00208     newbuf(32);
00209     wv_strrev(str, wv_itoar(str, i));
00210 }
00211 
00212 
00213 WvFastString::WvFastString(unsigned long i)
00214 {
00215     newbuf(32);
00216     wv_strrev(str, wv_uitoar(str, i));
00217 }
00218 
00219 
00220 WvFastString::WvFastString(long long i)
00221 {
00222     newbuf(32);
00223     wv_strrev(str, wv_itoar(str, i));
00224 }
00225 
00226 
00227 WvFastString::WvFastString(unsigned long long i)
00228 {
00229     newbuf(32);
00230     wv_strrev(str, wv_uitoar(str, i));
00231 }
00232 
00233 
00234 WvFastString::WvFastString(double i)
00235 {
00236     newbuf(32);
00237     sprintf(str, "%g", i);
00238 }
00239 
00240 
00241 WvFastString::~WvFastString()
00242 {
00243     unlink();
00244 }
00245 
00246 
00247 void WvFastString::unlink()
00248 { 
00249     if (buf && ! --buf->links)
00250     {
00251         free(buf);
00252         buf = NULL;
00253     }
00254 }
00255     
00256 
00257 void WvFastString::link(WvStringBuf *_buf, const char *_str)
00258 {
00259     buf = _buf;
00260     if (buf)
00261         buf->links++;
00262     str = (char *)_str; // I promise not to change it without asking!
00263 }
00264     
00265 
00266 WvStringBuf *WvFastString::alloc(size_t size)
00267 { 
00268     WvStringBuf *abuf = (WvStringBuf *)malloc(
00269                       (WVSTRINGBUF_SIZE(buf) + size + WVSTRING_EXTRA) | 3);
00270     abuf->links = 0;
00271     abuf->size = size;
00272     return abuf;
00273 }
00274 
00275 
00276 WvString &WvString::append(WvStringParm s)
00277 {
00278     if (s)
00279     {
00280         if (*this)
00281             *this = WvString("%s%s", *this, s);
00282         else
00283             *this = s;
00284     }
00285     
00286     return *this;
00287 }
00288 
00289 
00290 size_t WvFastString::len() const
00291 {
00292     return str ? strlen(str) : 0;
00293 }
00294 
00295 
00296 void WvFastString::newbuf(size_t size)
00297 {
00298     buf = alloc(size);
00299     buf->links = 1;
00300     str = buf->data;
00301 }
00302 
00303 
00304 // If the string is linked to more than once, we need to make our own copy 
00305 // of it.  If it was linked to only once, then it's already "unique".
00306 WvString &WvString::unique()
00307 {
00308     if (!is_unique() && str)
00309     {
00310         WvStringBuf *newb = alloc(len() + 1);
00311         memcpy(newb->data, str, newb->size);
00312         unlink();
00313         link(newb, newb->data);
00314     }
00315             
00316     return *this; 
00317 }
00318 
00319 
00320 bool WvString::is_unique() const
00321 {
00322     return (buf->links <= 1);
00323 }
00324 
00325 
00326 WvFastString &WvFastString::operator= (const WvFastString &s2)
00327 {
00328     if (s2.buf == buf && s2.str == str)
00329         return *this; // no change
00330     else
00331     {
00332         unlink();
00333         link(s2.buf, s2.str);
00334     }
00335     return *this;
00336 }
00337 
00338 
00339 WvString &WvString::operator= (int i)
00340 {
00341     unlink();
00342     newbuf(32);
00343     sprintf(str, "%d", i);
00344     return *this;
00345 }
00346 
00347 
00348 WvString &WvString::operator= (const WvFastString &s2)
00349 {
00350     if (s2.str == str && (!s2.buf || s2.buf == buf))
00351         return *this; // no change
00352     else if (!s2.buf)
00353     {
00354         // We have a string, and we're about to free() it.
00355         if (str && buf && buf->links == 1)
00356         {
00357             // Set buf->size, if we don't already know it.
00358             if (buf->size == 0)
00359                 buf->size = strlen(str);
00360 
00361             if (str < s2.str && s2.str <= (str + buf->size))
00362             {
00363                 // If the two strings overlap, we'll just need to
00364                 // shift s2.str over to here.
00365                 memmove(buf->data, s2.str, buf->size);
00366                 return *this;
00367             }
00368         }
00369         // assigning from a non-copied string - copy data if needed.
00370         unlink();
00371         link(&nullbuf, s2.str);
00372         unique();
00373     }
00374     else
00375     {
00376         // just a normal string link
00377         unlink();
00378         link(s2.buf, s2.str);
00379     }
00380     return *this;
00381 }
00382 
00383 
00384 // string comparison
00385 bool WvFastString::operator== (WvStringParm s2) const
00386 {
00387     return (str==s2.str) || (str && s2.str && !strcmp(str, s2.str));
00388 }
00389 
00390 
00391 bool WvFastString::operator!= (WvStringParm s2) const
00392 {
00393     return (str!=s2.str) && (!str || !s2.str || strcmp(str, s2.str));
00394 }
00395 
00396 
00397 bool WvFastString::operator< (WvStringParm s2) const
00398 {
00399     if (str == s2.str) return false;
00400     if (str == 0) return true;
00401     if (s2.str == 0) return false;
00402     return strcmp(str, s2.str) < 0;
00403 }
00404 
00405 
00406 bool WvFastString::operator== (const char *s2) const
00407 {
00408     return (str==s2) || (str && s2 && !strcmp(str, s2));
00409 }
00410 
00411 
00412 bool WvFastString::operator!= (const char *s2) const
00413 {
00414     return (str!=s2) && (!str || !s2 || strcmp(str, s2));
00415 }
00416 
00417 
00418 bool WvFastString::operator< (const char *s2) const
00419 {
00420     if (str == s2) return false;
00421     if (str == 0) return true;
00422     if (s2 == 0) return false;
00423     return strcmp(str, s2) < 0;
00424 }
00425 
00426 
00427 // not operator is 'true' if string is empty
00428 bool WvFastString::operator! () const
00429 {
00430     return !str || !str[0];
00431 }
00432 
00433 
00445 static const char *pparse(const char *cptr, bool &zeropad,
00446                           int &justify, int &maxlen, int &argnum)
00447 {
00448     assert(*cptr == '%');
00449     cptr++;
00450 
00451     zeropad = (*cptr == '0');
00452 
00453     justify = atoi(cptr);
00454     
00455     for (; *cptr && *cptr!='.' && *cptr!='%' && *cptr!='$' 
00456                                         && !isalpha(*cptr); cptr++)
00457         ;
00458     if (!*cptr) return cptr;
00459     
00460     if (*cptr == '.')
00461         maxlen = atoi(cptr+1);
00462     else
00463         maxlen = 0;
00464     
00465     for (; *cptr && *cptr!='%' && *cptr!='$' && !isalpha(*cptr); cptr++)
00466         ;
00467     if (!*cptr) return cptr;
00468     
00469     if (*cptr == '$')
00470         argnum = atoi(cptr+1);
00471     else
00472         argnum = 0;
00473 
00474     for (; *cptr && *cptr!='%' && !isalpha(*cptr); cptr++)
00475         ;
00476 
00477     return cptr;
00478 }
00479 
00480 
00497 void WvFastString::do_format(WvFastString &output, const char *format,
00498                              const WvFastString * const *argv)
00499 {
00500     static const char blank[] = "(nil)";
00501     const WvFastString * const *argptr = argv;
00502     const WvFastString * const *argP;
00503     const char *iptr = format, *arg;
00504     char *optr;
00505     int total = 0, aplen, ladd, justify, maxlen, argnum;
00506     bool zeropad;
00507     
00508     // count the number of bytes we'll need
00509     while (*iptr)
00510     {
00511         if (*iptr != '%')
00512         {
00513             total++;
00514             iptr++;
00515             continue;
00516         }
00517         
00518         // otherwise, iptr is at a percent expression
00519         argnum=0;
00520         iptr = pparse(iptr, zeropad, justify, maxlen, argnum);
00521         if (*iptr == '%') // literal percent
00522         {
00523             total++;
00524             iptr++;
00525             continue;
00526         }
00527         
00528         assert(*iptr == 's' || *iptr == 'c');
00529 
00530         if (*iptr == 's')
00531         {
00532             argP = (argnum > 0 ) ?  (argv + argnum -1): argptr;
00533             if (!*argP || !(**argP).cstr())
00534                 arg = blank;
00535             else
00536                 arg = (**argP).cstr();
00537             ladd = _max(abs(justify), strlen(arg));
00538             if (maxlen && maxlen < ladd)
00539                 ladd = maxlen;
00540             total += ladd;
00541             if ( argnum <= 0 ) 
00542                 argptr++;
00543             iptr++;
00544             continue;
00545         }
00546         
00547         if (*iptr++ == 'c')
00548         {
00549             if (argnum <= 0)
00550                 argptr++;
00551             total++;
00552         }
00553     }
00554     
00555     output.setsize(total + 1);
00556     
00557     // actually render the final string
00558     iptr = format;
00559     optr = output.str;
00560     argptr = argv;
00561     while (*iptr)
00562     {
00563         if (*iptr != '%')
00564         {
00565             *optr++ = *iptr++;
00566             continue;
00567         }
00568         
00569         // otherwise, iptr is at a "percent expression"
00570         argnum=0;
00571         iptr = pparse(iptr, zeropad, justify, maxlen, argnum);
00572         if (*iptr == '%')
00573         {
00574             *optr++ = *iptr++;
00575             continue;
00576         }
00577         if (*iptr == 's')
00578         {
00579             argP = (argnum > 0 ) ?  (argv + argnum -1): argptr;
00580             if (!*argP || !(**argP).cstr())
00581                 arg = blank;
00582             else
00583                 arg = (**argP).cstr();
00584             aplen = strlen(arg);
00585             if (maxlen && maxlen < aplen)
00586                 aplen = maxlen;
00587         
00588             if (justify > aplen)
00589             {
00590                 if (zeropad)
00591                     memset(optr, '0', justify-aplen);
00592                 else
00593                     memset(optr, ' ', justify-aplen);
00594                 optr += justify-aplen;
00595             }
00596         
00597             strncpy(optr, arg, aplen);
00598             optr += aplen;
00599         
00600             if (justify < 0 && -justify > aplen)
00601             {
00602                 if (zeropad)
00603                     memset(optr, '0', -justify-aplen);
00604                 else
00605                     memset(optr, ' ', -justify-aplen);
00606                 optr += -justify - aplen;
00607             }
00608             
00609             if ( argnum <= 0 ) 
00610                argptr++;
00611             iptr++;
00612             continue;
00613         }
00614         if (*iptr++ == 'c')
00615         {
00616             argP = (argnum > 0 ) ?  (argv + argnum -1): argptr++;
00617             if (!*argP || !(**argP))
00618                 arg = " ";
00619             else
00620                 arg = (**argP);
00621             *optr++ = (char)atoi(arg);
00622         }
00623     }
00624     *optr = 0;
00625 }
00626 
00627