WvStreams
wvstring.cc
1/*
2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2002 Net Integration Technologies, Inc.
4 *
5 * Implementation of a simple and efficient printable-string class. Most
6 * of the class is actually inlined and can be found in wvstring.h.
7 */
8#include "wvstring.h"
9#include <ctype.h>
10#include <assert.h>
11
12WvStringBuf WvFastString::nullbuf = { 0, 1 };
13const WvFastString WvFastString::null;
14
15const WvString WvString::empty("");
16
17
18// always a handy function
19static inline int _max(int x, int y)
20{
21 return x>y ? x : y;
22}
23
24
25void WvFastString::setsize(size_t i)
26{
27 unlink();
28 newbuf(i);
29}
30
31
32
34{
35 link(&nullbuf, NULL);
36}
37
38
40{
41 link(s.buf, s.str);
42}
43
44
46{
47 link(s.buf, s.str);
48}
49
50
51void WvFastString::construct(const char *_str)
52{
53 // just copy the pointer - no need to allocate memory!
54 str = (char *)_str; // I promise not to change anything!
55 buf = NULL;
56}
57
58
60{
61 construct(_str);
62}
63
64
65void WvString::copy_constructor(const WvFastString &s)
66{
67 unlink(); // WvFastString has already been created by now
68
69 if (!s.buf)
70 {
71 link(&nullbuf, s.str);
72 unique();
73 }
74 else
75 link(s.buf, s.str); // already in a nice, safe WvStreamBuf
76}
77
78
80{
81 WvFastString retval(*this);
82 size_t l = retval.len();
83 retval.str += (i < l ? i : l);
84 return retval;
85}
86
87
88WvString::WvString(const char *_str)
89{
90 unlink(); // WvFastString has already been created by now
91 construct(_str);
92}
93
94
95// This function returns the NULL of a reversed string representation
96// for unsigned integers
97template <typename T>
98inline static char *wv_uitoar(char *begin, T i)
99{
100 if (!begin)
101 return NULL;
102
103 char *end = begin;
104
105 if (i == 0)
106 *end++ = '0';
107 else
108 {
109 while (i > 0)
110 {
111 switch (i % 10)
112 {
113 case 0: *end++ = '0'; break;
114 case 1: *end++ = '1'; break;
115 case 2: *end++ = '2'; break;
116 case 3: *end++ = '3'; break;
117 case 4: *end++ = '4'; break;
118 case 5: *end++ = '5'; break;
119 case 6: *end++ = '6'; break;
120 case 7: *end++ = '7'; break;
121 case 8: *end++ = '8'; break;
122 case 9: *end++ = '9'; break;
123 default: ;
124 }
125 i /= 10;
126 }
127 }
128
129 *end = '\0';
130 return end;
131}
132
133// This function returns the NULL of a reversed string representation
134// for signed integers
135template <typename T>
136inline static char *wv_itoar(char *begin, T i)
137{
138 if (!begin)
139 return NULL;
140
141 bool negative = false;
142 if (i < 0)
143 {
144 negative = true;
145 i = -i;
146 }
147 char *end = wv_uitoar(begin, i);
148 if (negative)
149 {
150 *end++ = '-';
151 *end = '\0';
152 }
153 return end;
154}
155
156
157inline static void wv_strrev(char *begin, char *end)
158{
159 if (!begin && !end)
160 return;
161
162 --end;
163
164 while (begin < end)
165 {
166 *begin ^= *end;
167 *end ^= *begin;
168 *begin ^= *end;
169 ++begin;
170 --end;
171 }
172}
173
174
175
176// NOTE: make sure that 32 bytes is big enough for your longest int.
177// This is true up to at least 64 bits.
179{
180 newbuf(32);
181 wv_strrev(str, wv_itoar(str, i));
182}
183
184
185WvFastString::WvFastString(unsigned short i)
186{
187 newbuf(32);
188 wv_strrev(str, wv_uitoar(str, i));
189}
190
191
193{
194 newbuf(32);
195 wv_strrev(str, wv_itoar(str, i));
196}
197
198
199WvFastString::WvFastString(unsigned int i)
200{
201 newbuf(32);
202 wv_strrev(str, wv_uitoar(str, i));
203}
204
205
207{
208 newbuf(32);
209 wv_strrev(str, wv_itoar(str, i));
210}
211
212
213WvFastString::WvFastString(unsigned long i)
214{
215 newbuf(32);
216 wv_strrev(str, wv_uitoar(str, i));
217}
218
219
221{
222 newbuf(32);
223 wv_strrev(str, wv_itoar(str, i));
224}
225
226
227WvFastString::WvFastString(unsigned long long i)
228{
229 newbuf(32);
230 wv_strrev(str, wv_uitoar(str, i));
231}
232
233
235{
236 newbuf(32);
237 sprintf(str, "%g", i);
238}
239
240
241WvFastString::~WvFastString()
242{
243 unlink();
244}
245
246
247void WvFastString::unlink()
248{
249 if (buf && ! --buf->links)
250 {
251 free(buf);
252 buf = NULL;
253 }
254}
255
256
257void WvFastString::link(WvStringBuf *_buf, const char *_str)
258{
259 buf = _buf;
260 if (buf)
261 buf->links++;
262 str = (char *)_str; // I promise not to change it without asking!
263}
264
265
266WvStringBuf *WvFastString::alloc(size_t size)
267{
268 WvStringBuf *abuf = (WvStringBuf *)malloc(
269 (WVSTRINGBUF_SIZE(buf) + size + WVSTRING_EXTRA) | 3);
270 abuf->links = 0;
271 abuf->size = size;
272 return abuf;
273}
274
275
276WvString &WvString::append(WvStringParm s)
277{
278 if (s)
279 {
280 if (*this)
281 *this = WvString("%s%s", *this, s);
282 else
283 *this = s;
284 }
285
286 return *this;
287}
288
289
290size_t WvFastString::len() const
291{
292 return str ? strlen(str) : 0;
293}
294
295
296void WvFastString::newbuf(size_t size)
297{
298 buf = alloc(size);
299 buf->links = 1;
300 str = buf->data;
301}
302
303
304// If the string is linked to more than once, we need to make our own copy
305// of it. If it was linked to only once, then it's already "unique".
307{
308 if (!is_unique() && str)
309 {
310 WvStringBuf *newb = alloc(len() + 1);
311 memcpy(newb->data, str, newb->size);
312 unlink();
313 link(newb, newb->data);
314 }
315
316 return *this;
317}
318
319
321{
322 return (buf->links <= 1);
323}
324
325
326WvFastString &WvFastString::operator= (const WvFastString &s2)
327{
328 if (s2.buf == buf && s2.str == str)
329 return *this; // no change
330 else
331 {
332 unlink();
333 link(s2.buf, s2.str);
334 }
335 return *this;
336}
337
338
339WvString &WvString::operator= (int i)
340{
341 unlink();
342 newbuf(32);
343 sprintf(str, "%d", i);
344 return *this;
345}
346
347
348WvString &WvString::operator= (const WvFastString &s2)
349{
350 if (s2.str == str && (!s2.buf || s2.buf == buf))
351 return *this; // no change
352 else if (!s2.buf)
353 {
354 // We have a string, and we're about to free() it.
355 if (str && buf && buf->links == 1)
356 {
357 // Set buf->size, if we don't already know it.
358 if (buf->size == 0)
359 buf->size = strlen(str);
360
361 if (str < s2.str && s2.str <= (str + buf->size))
362 {
363 // If the two strings overlap, we'll just need to
364 // shift s2.str over to here.
365 memmove(buf->data, s2.str, buf->size);
366 return *this;
367 }
368 }
369 // assigning from a non-copied string - copy data if needed.
370 unlink();
371 link(&nullbuf, s2.str);
372 unique();
373 }
374 else
375 {
376 // just a normal string link
377 unlink();
378 link(s2.buf, s2.str);
379 }
380 return *this;
381}
382
383
384// string comparison
385bool WvFastString::operator== (WvStringParm s2) const
386{
387 return (str==s2.str) || (str && s2.str && !strcmp(str, s2.str));
388}
389
390
391bool WvFastString::operator!= (WvStringParm s2) const
392{
393 return (str!=s2.str) && (!str || !s2.str || strcmp(str, s2.str));
394}
395
396
397bool WvFastString::operator< (WvStringParm s2) const
398{
399 if (str == s2.str) return false;
400 if (str == 0) return true;
401 if (s2.str == 0) return false;
402 return strcmp(str, s2.str) < 0;
403}
404
405
406bool WvFastString::operator== (const char *s2) const
407{
408 return (str==s2) || (str && s2 && !strcmp(str, s2));
409}
410
411
412bool WvFastString::operator!= (const char *s2) const
413{
414 return (str!=s2) && (!str || !s2 || strcmp(str, s2));
415}
416
417
418bool WvFastString::operator< (const char *s2) const
419{
420 if (str == s2) return false;
421 if (str == 0) return true;
422 if (s2 == 0) return false;
423 return strcmp(str, s2) < 0;
424}
425
426
427// not operator is 'true' if string is empty
429{
430 return !str || !str[0];
431}
432
433
445static const char *pparse(const char *cptr, bool &zeropad,
446 int &justify, int &maxlen, int &argnum)
447{
448 assert(*cptr == '%');
449 cptr++;
450
451 zeropad = (*cptr == '0');
452
453 justify = atoi(cptr);
454
455 for (; *cptr && *cptr!='.' && *cptr!='%' && *cptr!='$'
456 && !isalpha(*cptr); cptr++)
457 ;
458 if (!*cptr) return cptr;
459
460 if (*cptr == '.')
461 maxlen = atoi(cptr+1);
462 else
463 maxlen = 0;
464
465 for (; *cptr && *cptr!='%' && *cptr!='$' && !isalpha(*cptr); cptr++)
466 ;
467 if (!*cptr) return cptr;
468
469 if (*cptr == '$')
470 argnum = atoi(cptr+1);
471 else
472 argnum = 0;
473
474 for (; *cptr && *cptr!='%' && !isalpha(*cptr); cptr++)
475 ;
476
477 return cptr;
478}
479
480
497void WvFastString::do_format(WvFastString &output, const char *format,
498 const WvFastString * const *argv)
499{
500 static const char blank[] = "(nil)";
501 const WvFastString * const *argptr = argv;
502 const WvFastString * const *argP;
503 const char *iptr = format, *arg;
504 char *optr;
505 int total = 0, aplen, ladd, justify, maxlen, argnum;
506 bool zeropad;
507
508 // count the number of bytes we'll need
509 while (*iptr)
510 {
511 if (*iptr != '%')
512 {
513 total++;
514 iptr++;
515 continue;
516 }
517
518 // otherwise, iptr is at a percent expression
519 argnum=0;
520 iptr = pparse(iptr, zeropad, justify, maxlen, argnum);
521 if (*iptr == '%') // literal percent
522 {
523 total++;
524 iptr++;
525 continue;
526 }
527
528 assert(*iptr == 's' || *iptr == 'c');
529
530 if (*iptr == 's')
531 {
532 argP = (argnum > 0 ) ? (argv + argnum -1): argptr;
533 if (!*argP || !(**argP).cstr())
534 arg = blank;
535 else
536 arg = (**argP).cstr();
537 ladd = _max(abs(justify), strlen(arg));
538 if (maxlen && maxlen < ladd)
539 ladd = maxlen;
540 total += ladd;
541 if ( argnum <= 0 )
542 argptr++;
543 iptr++;
544 continue;
545 }
546
547 if (*iptr++ == 'c')
548 {
549 if (argnum <= 0)
550 argptr++;
551 total++;
552 }
553 }
554
555 output.setsize(total + 1);
556
557 // actually render the final string
558 iptr = format;
559 optr = output.str;
560 argptr = argv;
561 while (*iptr)
562 {
563 if (*iptr != '%')
564 {
565 *optr++ = *iptr++;
566 continue;
567 }
568
569 // otherwise, iptr is at a "percent expression"
570 argnum=0;
571 iptr = pparse(iptr, zeropad, justify, maxlen, argnum);
572 if (*iptr == '%')
573 {
574 *optr++ = *iptr++;
575 continue;
576 }
577 if (*iptr == 's')
578 {
579 argP = (argnum > 0 ) ? (argv + argnum -1): argptr;
580 if (!*argP || !(**argP).cstr())
581 arg = blank;
582 else
583 arg = (**argP).cstr();
584 aplen = strlen(arg);
585 if (maxlen && maxlen < aplen)
586 aplen = maxlen;
587
588 if (justify > aplen)
589 {
590 if (zeropad)
591 memset(optr, '0', justify-aplen);
592 else
593 memset(optr, ' ', justify-aplen);
594 optr += justify-aplen;
595 }
596
597 strncpy(optr, arg, aplen);
598 optr += aplen;
599
600 if (justify < 0 && -justify > aplen)
601 {
602 if (zeropad)
603 memset(optr, '0', -justify-aplen);
604 else
605 memset(optr, ' ', -justify-aplen);
606 optr += -justify - aplen;
607 }
608
609 if ( argnum <= 0 )
610 argptr++;
611 iptr++;
612 continue;
613 }
614 if (*iptr++ == 'c')
615 {
616 argP = (argnum > 0 ) ? (argv + argnum -1): argptr++;
617 if (!*argP || !(**argP))
618 arg = " ";
619 else
620 arg = (**argP);
621 *optr++ = (char)atoi(arg);
622 }
623 }
624 *optr = 0;
625}
626
627
A WvFastString acts exactly like a WvString, but can take (const char *) strings without needing to a...
Definition: wvstring.h:94
WvFastString()
Create an empty, NULL string.
Definition: wvstring.cc:33
bool operator!() const
the not operator is 'true' if string is empty
Definition: wvstring.cc:428
const char * cstr() const
return a (const char *) for this string.
Definition: wvstring.h:267
static void do_format(WvFastString &output, const char *format, const WvFastString *const *a)
when this is called, we assume output.str == NULL; it will be filled.
Definition: wvstring.cc:497
WvFastString offset(size_t i) const
Returns a copy of string pointed i bytes into this.
Definition: wvstring.cc:79
WvString is an implementation of a simple and efficient printable-string class.
Definition: wvstring.h:330
WvString & unique()
make the buf and str pointers owned only by this WvString.
Definition: wvstring.cc:306
bool is_unique() const
returns true if this string is already unique()
Definition: wvstring.cc:320