/* * Worldvisions Weaver Software: * Copyright (C) 1997-2002 Net Integration Technologies, Inc. * * Implementation of a simple and efficient printable-string class. Most * of the class is actually inlined and can be found in wvstring.h. */ #include "wvstring.h" #include #include WvStringBuf WvFastString::nullbuf = { 0, 1 }; const WvFastString WvFastString::null; const WvString WvString::empty(""); // always a handy function static inline int _max(int x, int y) { return x>y ? x : y; } void WvFastString::setsize(size_t i) { unlink(); newbuf(i); } WvFastString::WvFastString() { link(&nullbuf, NULL); } WvFastString::WvFastString(const WvFastString &s) { link(s.buf, s.str); } WvFastString::WvFastString(const WvString &s) { link(s.buf, s.str); } void WvFastString::construct(const char *_str) { // just copy the pointer - no need to allocate memory! str = (char *)_str; // I promise not to change anything! buf = NULL; } WvFastString::WvFastString(const char *_str) { construct(_str); } void WvString::copy_constructor(const WvFastString &s) { unlink(); // WvFastString has already been created by now if (!s.buf) { link(&nullbuf, s.str); unique(); } else link(s.buf, s.str); // already in a nice, safe WvStreamBuf } WvFastString WvFastString::offset(size_t i) const { WvFastString retval(*this); size_t l = retval.len(); retval.str += (i < l ? i : l); return retval; } WvString::WvString(const char *_str) { unlink(); // WvFastString has already been created by now construct(_str); } // This function returns the NULL of a reversed string representation // for unsigned integers template inline static char *wv_uitoar(char *begin, T i) { if (!begin) return NULL; char *end = begin; if (i == 0) *end++ = '0'; else { while (i > 0) { switch (i % 10) { case 0: *end++ = '0'; break; case 1: *end++ = '1'; break; case 2: *end++ = '2'; break; case 3: *end++ = '3'; break; case 4: *end++ = '4'; break; case 5: *end++ = '5'; break; case 6: *end++ = '6'; break; case 7: *end++ = '7'; break; case 8: *end++ = '8'; break; case 9: *end++ = '9'; break; default: ; } i /= 10; } } *end = '\0'; return end; } // This function returns the NULL of a reversed string representation // for signed integers template inline static char *wv_itoar(char *begin, T i) { if (!begin) return NULL; bool negative = false; if (i < 0) { negative = true; i = -i; } char *end = wv_uitoar(begin, i); if (negative) { *end++ = '-'; *end = '\0'; } return end; } inline static void wv_strrev(char *begin, char *end) { if (!begin && !end) return; --end; while (begin < end) { *begin ^= *end; *end ^= *begin; *begin ^= *end; ++begin; --end; } } // NOTE: make sure that 32 bytes is big enough for your longest int. // This is true up to at least 64 bits. WvFastString::WvFastString(short i) { newbuf(32); wv_strrev(str, wv_itoar(str, i)); } WvFastString::WvFastString(unsigned short i) { newbuf(32); wv_strrev(str, wv_uitoar(str, i)); } WvFastString::WvFastString(int i) { newbuf(32); wv_strrev(str, wv_itoar(str, i)); } WvFastString::WvFastString(unsigned int i) { newbuf(32); wv_strrev(str, wv_uitoar(str, i)); } WvFastString::WvFastString(long i) { newbuf(32); wv_strrev(str, wv_itoar(str, i)); } WvFastString::WvFastString(unsigned long i) { newbuf(32); wv_strrev(str, wv_uitoar(str, i)); } WvFastString::WvFastString(long long i) { newbuf(32); wv_strrev(str, wv_itoar(str, i)); } WvFastString::WvFastString(unsigned long long i) { newbuf(32); wv_strrev(str, wv_uitoar(str, i)); } WvFastString::WvFastString(double i) { newbuf(32); sprintf(str, "%g", i); } WvFastString::~WvFastString() { unlink(); } void WvFastString::unlink() { if (buf && ! --buf->links) { free(buf); buf = NULL; } } void WvFastString::link(WvStringBuf *_buf, const char *_str) { buf = _buf; if (buf) buf->links++; str = (char *)_str; // I promise not to change it without asking! } WvStringBuf *WvFastString::alloc(size_t size) { WvStringBuf *abuf = (WvStringBuf *)malloc( (WVSTRINGBUF_SIZE(buf) + size + WVSTRING_EXTRA) | 3); abuf->links = 0; abuf->size = size; return abuf; } WvString &WvString::append(WvStringParm s) { if (s) { if (*this) *this = WvString("%s%s", *this, s); else *this = s; } return *this; } size_t WvFastString::len() const { return str ? strlen(str) : 0; } void WvFastString::newbuf(size_t size) { buf = alloc(size); buf->links = 1; str = buf->data; } // If the string is linked to more than once, we need to make our own copy // of it. If it was linked to only once, then it's already "unique". WvString &WvString::unique() { if (!is_unique() && str) { WvStringBuf *newb = alloc(len() + 1); memcpy(newb->data, str, newb->size); unlink(); link(newb, newb->data); } return *this; } bool WvString::is_unique() const { return (buf->links <= 1); } WvFastString &WvFastString::operator= (const WvFastString &s2) { if (s2.buf == buf && s2.str == str) return *this; // no change else { unlink(); link(s2.buf, s2.str); } return *this; } WvString &WvString::operator= (int i) { unlink(); newbuf(32); sprintf(str, "%d", i); return *this; } WvString &WvString::operator= (const WvFastString &s2) { if (s2.str == str && (!s2.buf || s2.buf == buf)) return *this; // no change else if (!s2.buf) { // We have a string, and we're about to free() it. if (str && buf && buf->links == 1) { // Set buf->size, if we don't already know it. if (buf->size == 0) buf->size = strlen(str); if (str < s2.str && s2.str <= (str + buf->size)) { // If the two strings overlap, we'll just need to // shift s2.str over to here. memmove(buf->data, s2.str, buf->size); return *this; } } // assigning from a non-copied string - copy data if needed. unlink(); link(&nullbuf, s2.str); unique(); } else { // just a normal string link unlink(); link(s2.buf, s2.str); } return *this; } // string comparison bool WvFastString::operator== (WvStringParm s2) const { return (str==s2.str) || (str && s2.str && !strcmp(str, s2.str)); } bool WvFastString::operator!= (WvStringParm s2) const { return (str!=s2.str) && (!str || !s2.str || strcmp(str, s2.str)); } bool WvFastString::operator< (WvStringParm s2) const { if (str == s2.str) return false; if (str == 0) return true; if (s2.str == 0) return false; return strcmp(str, s2.str) < 0; } bool WvFastString::operator== (const char *s2) const { return (str==s2) || (str && s2 && !strcmp(str, s2)); } bool WvFastString::operator!= (const char *s2) const { return (str!=s2) && (!str || !s2 || strcmp(str, s2)); } bool WvFastString::operator< (const char *s2) const { if (str == s2) return false; if (str == 0) return true; if (s2 == 0) return false; return strcmp(str, s2) < 0; } // not operator is 'true' if string is empty bool WvFastString::operator! () const { return !str || !str[0]; } /** * parse a 'percent' operator from a format string. For example: * cptr out: zeropad justify maxlen argnum return pointer * "%s" false 0 0 0 "s" * "%-15s" false -15 0 0 "s" * "%15.5s" false 15 5 0 "s" * "%015.5s" true 15 5 0 "s" * "%15$2s" false 15 0 2 "s" * and so on. On entry, cptr should _always_ point at a percent '%' char. * argnum is the argument number. */ static const char *pparse(const char *cptr, bool &zeropad, int &justify, int &maxlen, int &argnum) { assert(*cptr == '%'); cptr++; zeropad = (*cptr == '0'); justify = atoi(cptr); for (; *cptr && *cptr!='.' && *cptr!='%' && *cptr!='$' && !isalpha(*cptr); cptr++) ; if (!*cptr) return cptr; if (*cptr == '.') maxlen = atoi(cptr+1); else maxlen = 0; for (; *cptr && *cptr!='%' && *cptr!='$' && !isalpha(*cptr); cptr++) ; if (!*cptr) return cptr; if (*cptr == '$') argnum = atoi(cptr+1); else argnum = 0; for (; *cptr && *cptr!='%' && !isalpha(*cptr); cptr++) ; return cptr; } /** * Accept a printf-like format specifier (but more limited) and an array * of WvStrings, and render them into another WvString. For example: * WvString x[] = {"foo", "blue", 1234}; * WvString ret = WvString::do_format("%s%10.2s%-10s", x); * * The 'ret' string will be: "foo bl1234 " * Note that only '%s' is supported, though integers can be rendered * automatically into WvStrings. %d, %f, etc are not allowed! * * This function is usually called from some other function which allocates * the array automatically. * * %$ns (n > 0) is also supported for internationalization purposes. e.g. * ("%$2s is arg2, and %$1s ia arg1", arg1, arg2) */ void WvFastString::do_format(WvFastString &output, const char *format, const WvFastString * const *argv) { static const char blank[] = "(nil)"; const WvFastString * const *argptr = argv; const WvFastString * const *argP; const char *iptr = format, *arg; char *optr; int total = 0, aplen, ladd, justify, maxlen, argnum; bool zeropad; // count the number of bytes we'll need while (*iptr) { if (*iptr != '%') { total++; iptr++; continue; } // otherwise, iptr is at a percent expression argnum=0; iptr = pparse(iptr, zeropad, justify, maxlen, argnum); if (*iptr == '%') // literal percent { total++; iptr++; continue; } assert(*iptr == 's' || *iptr == 'c'); if (*iptr == 's') { argP = (argnum > 0 ) ? (argv + argnum -1): argptr; if (!*argP || !(**argP).cstr()) arg = blank; else arg = (**argP).cstr(); ladd = _max(abs(justify), strlen(arg)); if (maxlen && maxlen < ladd) ladd = maxlen; total += ladd; if ( argnum <= 0 ) argptr++; iptr++; continue; } if (*iptr++ == 'c') { if (argnum <= 0) argptr++; total++; } } output.setsize(total + 1); // actually render the final string iptr = format; optr = output.str; argptr = argv; while (*iptr) { if (*iptr != '%') { *optr++ = *iptr++; continue; } // otherwise, iptr is at a "percent expression" argnum=0; iptr = pparse(iptr, zeropad, justify, maxlen, argnum); if (*iptr == '%') { *optr++ = *iptr++; continue; } if (*iptr == 's') { argP = (argnum > 0 ) ? (argv + argnum -1): argptr; if (!*argP || !(**argP).cstr()) arg = blank; else arg = (**argP).cstr(); aplen = strlen(arg); if (maxlen && maxlen < aplen) aplen = maxlen; if (justify > aplen) { if (zeropad) memset(optr, '0', justify-aplen); else memset(optr, ' ', justify-aplen); optr += justify-aplen; } strncpy(optr, arg, aplen); optr += aplen; if (justify < 0 && -justify > aplen) { if (zeropad) memset(optr, '0', -justify-aplen); else memset(optr, ' ', -justify-aplen); optr += -justify - aplen; } if ( argnum <= 0 ) argptr++; iptr++; continue; } if (*iptr++ == 'c') { argP = (argnum > 0 ) ? (argv + argnum -1): argptr++; if (!*argP || !(**argP)) arg = " "; else arg = (**argP); *optr++ = (char)atoi(arg); } } *optr = 0; }