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