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 
12 WvStringBuf WvFastString::nullbuf = { 0, 1 };
13 const WvFastString WvFastString::null;
14 
15 const WvString WvString::empty("");
16 
17 
18 // always a handy function
19 static inline int _max(int x, int y)
20 {
21  return x>y ? x : y;
22 }
23 
24 
25 void 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 
51 void 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 
59 WvFastString::WvFastString(const char *_str)
60 {
61  construct(_str);
62 }
63 
64 
65 void 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 
88 WvString::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
97 template <typename T>
98 inline 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
135 template <typename T>
136 inline 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 
157 inline 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 
185 WvFastString::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 
199 WvFastString::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 
213 WvFastString::WvFastString(unsigned long i)
214 {
215  newbuf(32);
216  wv_strrev(str, wv_uitoar(str, i));
217 }
218 
219 
220 WvFastString::WvFastString(long long i)
221 {
222  newbuf(32);
223  wv_strrev(str, wv_itoar(str, i));
224 }
225 
226 
227 WvFastString::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 
241 WvFastString::~WvFastString()
242 {
243  unlink();
244 }
245 
246 
247 void WvFastString::unlink()
248 {
249  if (buf && ! --buf->links)
250  {
251  free(buf);
252  buf = NULL;
253  }
254 }
255 
256 
257 void 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 
266 WvStringBuf *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 
276 WvString &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 
290 size_t WvFastString::len() const
291 {
292  return str ? strlen(str) : 0;
293 }
294 
295 
296 void 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 
326 WvFastString &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 
339 WvString &WvString::operator= (int i)
340 {
341  unlink();
342  newbuf(32);
343  sprintf(str, "%d", i);
344  return *this;
345 }
346 
347 
348 WvString &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
385 bool WvFastString::operator== (WvStringParm s2) const
386 {
387  return (str==s2.str) || (str && s2.str && !strcmp(str, s2.str));
388 }
389 
390 
391 bool WvFastString::operator!= (WvStringParm s2) const
392 {
393  return (str!=s2.str) && (!str || !s2.str || strcmp(str, s2.str));
394 }
395 
396 
397 bool 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 
406 bool WvFastString::operator== (const char *s2) const
407 {
408  return (str==s2) || (str && s2 && !strcmp(str, s2));
409 }
410 
411 
412 bool WvFastString::operator!= (const char *s2) const
413 {
414  return (str!=s2) && (!str || !s2 || strcmp(str, s2));
415 }
416 
417 
418 bool 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 
445 static 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 
497 void 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 
WvFastString::operator!
bool operator!() const
the not operator is 'true' if string is empty
Definition: wvstring.cc:428
WvFastString::offset
WvFastString offset(size_t i) const
Returns a copy of string pointed i bytes into this.
Definition: wvstring.cc:79
WvStringBuf
Definition: wvstring.h:67
WvString
WvString is an implementation of a simple and efficient printable-string class.
Definition: wvstring.h:329
WvFastString::cstr
const char * cstr() const
return a (const char *) for this string.
Definition: wvstring.h:267
WvString::is_unique
bool is_unique() const
returns true if this string is already unique()
Definition: wvstring.cc:320
WvString::unique
WvString & unique()
make the buf and str pointers owned only by this WvString.
Definition: wvstring.cc:306
WvFastString::do_format
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::WvFastString
WvFastString()
Create an empty, NULL string.
Definition: wvstring.cc:33
WvFastString
A WvFastString acts exactly like a WvString, but can take (const char *) strings without needing to a...
Definition: wvstring.h:93