Starshatter_Open
Open source Starshatter engine
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
Text.cpp
Go to the documentation of this file.
1 /* Project FoundationEx
2  Destroyer Studios LLC
3  Copyright © 1997-2004. All Rights Reserved.
4 
5  SUBSYSTEM: FoundationEx
6  FILE: text.cpp
7  AUTHOR: John DiCamillo
8 
9 
10  OVERVIEW
11  ========
12  Implementation of the Text class
13 */
14 
15 #include "MemDebug.h"
16 #include "Text.h"
17 #include "stdio.h"
18 #include <ctype.h>
19 
20 // +-------------------------------------------------------------------+
21 // SPECIAL TEXT REP FOR NULL STRINGS
22 // This is used only by the default constructor for the Text object,
23 // to prevent extra rep allocation when constructing empty strings.
24 
25 TextRep TextRep::nullrep;
26 
28  : ref(1234567), data(0), length(0), hash(0), sensitive(true)
29 {
30 #ifdef MEM_DEBUG
31  data = new(__FILE__,__LINE__) char[4];
32 #else
33  data = new char[4];
34 #endif
35 
36  if (data)
37  ZeroMemory(data, 4);
38 }
39 
40 // +-------------------------------------------------------------------+
41 
42 ThreadSync TextRep::sync;
43 
44 
45 TextRep::TextRep(const char* s)
46  : ref(1), length(0), sensitive(true)
47 {
48  if (s) length = ::strlen(s);
49 
50 #ifdef MEM_DEBUG
51  data = new(__FILE__,__LINE__) char[length+1];
52 #else
53  data = new char[length+1];
54 #endif
55 
56  if (data) {
57  if (s) ::strcpy(data, s);
58  else data[length] = '\0';
59 
60  dohash();
61  }
62 }
63 
64 TextRep::TextRep(const char* s, int len)
65  : ref(1), length(len), sensitive(true)
66 {
67  if (length < 0) length = 0;
68 
69 #ifdef MEM_DEBUG
70  data = new(__FILE__,__LINE__) char[length+1];
71 #else
72  data = new char[length+1];
73 #endif
74 
75  if (data) {
76  ::CopyMemory(data, s, length);
77  data[length] = '\0';
78  dohash();
79  }
80 }
81 
82 TextRep::TextRep(char c, int len)
83  : ref(1), length(len), sensitive(true)
84 {
85  if (length < 0) length = 0;
86 
87 #ifdef MEM_DEBUG
88  data = new(__FILE__,__LINE__) char[length+1];
89 #else
90  data = new char[length+1];
91 #endif
92 
93  if (data) {
94  ::FillMemory(data, length, c);
95  data[length] = '\0';
96  dohash();
97  }
98 }
99 
100 TextRep::TextRep(const TextRep* rep)
101  : ref(1)
102 {
103  length = rep->length;
104 
105 #ifdef MEM_DEBUG
106  data = new(__FILE__,__LINE__) char[length+1];
107 #else
108  data = new char[length+1];
109 #endif
110 
111  hash = rep->hash;
112  sensitive = rep->sensitive;
113 
114  if (data)
115  ::strcpy(data, rep->data);
116 }
117 
119 {
120  delete[] data;
121 }
122 
123 void
124 TextRep::addref()
125 {
126  sync.acquire();
127  ref++;
128  sync.release();
129 }
130 
131 long
132 TextRep::deref()
133 {
134  sync.acquire();
135  long r = --ref;
136  sync.release();
137  return r;
138 }
139 
140 inline static void mash(unsigned& hash, unsigned chars)
141 {
142  hash = (chars ^ ((hash << 5) | (hash >> (8*sizeof(unsigned) - 5))));
143 }
144 
145 void
146 TextRep::dohash()
147 {
148  unsigned hv = (unsigned)length; // Mix in the string length.
149  unsigned i = length*sizeof(char)/sizeof(unsigned);
150  const unsigned* p = (const unsigned*)data;
151 
152  while (i--)
153  mash(hv, *p++); // XOR in the characters.
154 
155  // XOR in any remaining characters:
156  i = length*sizeof(char)%sizeof(unsigned);
157  if (i) {
158  unsigned h = 0;
159  const char* c = (const char*)p;
160  while (i--)
161  h = ((h << 8*sizeof(char)) | *c++);
162  mash(hv, h);
163  }
164 
165  hash = hv;
166 }
167 
168 // +-------------------------------------------------------------------+
169 
171 {
172  rep = &TextRep::nullrep;
173  rep->addref();
174  sym = rep->data;
175 }
176 
177 Text::Text(char c)
178 {
179  char buf[2]; buf[0] = c; buf[1] = '\0';
180 
181 #ifdef MEM_DEBUG
182  rep = new(__FILE__,__LINE__) TextRep(buf);
183 #else
184  rep = new TextRep(buf);
185 #endif
186 
187  if (!rep) {
188  rep = &TextRep::nullrep;
189  rep->addref();
190  }
191 
192  sym = rep->data;
193 }
194 
195 Text::Text(const char* s)
196 {
197 #ifdef MEM_DEBUG
198  rep = new(__FILE__,__LINE__) TextRep(s);
199 #else
200  rep = new TextRep(s);
201 #endif
202 
203  if (!rep) {
204  rep = &TextRep::nullrep;
205  rep->addref();
206  }
207 
208  sym = rep->data;
209 }
210 
211 Text::Text(const char* s, int len)
212 {
213 #ifdef MEM_DEBUG
214  rep = new(__FILE__,__LINE__) TextRep(s, len);
215 #else
216  rep = new TextRep(s, len);
217 #endif
218 
219  if (!rep) {
220  rep = &TextRep::nullrep;
221  rep->addref();
222  }
223 
224  sym = rep->data;
225 }
226 
227 Text::Text(char c, int len)
228 {
229 #ifdef MEM_DEBUG
230  rep = new(__FILE__,__LINE__) TextRep(c, len);
231 #else
232  rep = new TextRep(c, len);
233 #endif
234 
235  if (!rep) {
236  rep = &TextRep::nullrep;
237  rep->addref();
238  }
239 
240  sym = rep->data;
241 }
242 
243 Text::Text(const Text& s)
244 {
245  rep = s.rep;
246  rep->addref();
247  sym = rep->data;
248 }
249 
251 {
252  if (rep->deref() == 0) delete rep;
253 
254  rep = &TextRep::nullrep;
255  sym = rep->data;
256 }
257 
258 Text&
259 Text::operator=(const char* s)
260 {
261  if (rep->deref() == 0) delete rep;
262 #ifdef MEM_DEBUG
263  rep = new(__FILE__,__LINE__) TextRep(s);
264 #else
265  rep = new TextRep(s);
266 #endif
267 
268  if (!rep)
269  rep = &TextRep::nullrep;
270  sym = rep->data;
271  return *this;
272 }
273 
274 Text&
276 {
277  s.rep->addref();
278  if (rep->deref() == 0) delete rep;
279  rep = s.rep;
280  sym = rep->data;
281  return *this;
282 }
283 
284 Text
286 {
287 #ifdef MEM_DEBUG
288  char* buf = new(__FILE__,__LINE__) char[rep->length + 2];
289 #else
290  char* buf = new char[rep->length + 2];
291 #endif
292 
293  if (buf) {
294  ::strcpy(buf, sym);
295  buf[rep->length] = c;
296  buf[rep->length+1] = '\0';
297  Text retval(buf);
298  delete [] buf;
299  return retval;
300  }
301 
302  else {
303  return *this;
304  }
305 }
306 
307 Text
308 Text::operator+(const char* s)
309 {
310 #ifdef MEM_DEBUG
311  char* buf = new(__FILE__,__LINE__) char[::strlen(s) + rep->length + 1];
312 #else
313  char* buf = new char[::strlen(s) + rep->length + 1];
314 #endif
315 
316  if (buf) {
317  ::strcpy(buf, sym);
318  ::strcat(buf, s);
319  Text retval(buf);
320  delete [] buf;
321  return retval;
322  }
323 
324  else {
325  return *this;
326  }
327 }
328 
329 Text
331 {
332 #ifdef MEM_DEBUG
333  char* buf = new(__FILE__,__LINE__) char[s.rep->length + rep->length + 1];
334 #else
335  char* buf = new char[s.rep->length + rep->length + 1];
336 #endif
337 
338  if (buf) {
339  ::strcpy(buf, sym);
340  ::strcat(buf, s.sym);
341  Text retval(buf);
342  delete [] buf;
343  return retval;
344  }
345 
346  else {
347  return *this;
348  }
349 }
350 
351 bool
353 {
354  return rep->sensitive;
355 }
356 
357 void
359 {
360  rep->sensitive = s;
361 }
362 
363 Text&
365 {
366 #ifdef MEM_DEBUG
367  char* buf = new(__FILE__,__LINE__) char[rep->length + 2];
368 #else
369  char* buf = new char[rep->length + 2];
370 #endif
371 
372  if (buf) {
373  ::strcpy(buf, sym);
374  buf[rep->length] = c;
375  buf[rep->length+1] = '\0';
376  if (rep->deref() == 0) delete rep;
377 
378 #ifdef MEM_DEBUG
379  rep = new(__FILE__,__LINE__) TextRep(buf);
380 #else
381  rep = new TextRep(buf);
382 #endif
383 
384  if (!rep)
385  rep = &TextRep::nullrep;
386 
387  sym = rep->data;
388  delete [] buf;
389  }
390 
391  return *this;
392 }
393 
394 Text&
395 Text::append(const char* s)
396 {
397 #ifdef MEM_DEBUG
398  char* buf = new(__FILE__,__LINE__) char[::strlen(s) + rep->length + 1];
399 #else
400  char* buf = new char[::strlen(s) + rep->length + 1];
401 #endif
402 
403  if (buf) {
404  ::strcpy(buf, sym);
405  ::strcat(buf, s);
406  if (rep->deref() == 0) delete rep;
407 
408 #ifdef MEM_DEBUG
409  rep = new(__FILE__,__LINE__) TextRep(buf);
410 #else
411  rep = new TextRep(buf);
412 #endif
413 
414  if (!rep)
415  rep = &TextRep::nullrep;
416 
417  sym = rep->data;
418  delete [] buf;
419  }
420 
421  return *this;
422 }
423 
424 Text&
426 {
427 #ifdef MEM_DEBUG
428  char* buf = new(__FILE__,__LINE__) char[s.rep->length + rep->length + 1];
429 #else
430  char* buf = new char[s.rep->length + rep->length + 1];
431 #endif
432 
433  if (buf) {
434  int lenA = rep->length;
435  int lenB = s.rep->length;
436 
437  CopyMemory(buf, sym, lenA);
438  CopyMemory(buf + lenA, s.sym, lenB);
439  buf[lenA + lenB] = 0;
440 
441  if (rep->deref() == 0) delete rep;
442 
443 #ifdef MEM_DEBUG
444  rep = new(__FILE__,__LINE__) TextRep(buf, lenA + lenB);
445 #else
446  rep = new TextRep(buf);
447 #endif
448 
449  if (!rep)
450  rep = &TextRep::nullrep;
451 
452  sym = rep->data;
453  delete [] buf;
454  }
455 
456  return *this;
457 }
458 
459 void
460 Text::clone()
461 {
462  if (rep->ref > 1) {
463  rep->deref();
464 
465 #ifdef MEM_DEBUG
466  TextRep* t = new(__FILE__,__LINE__) TextRep(rep);
467 #else
468  TextRep* t = new TextRep(rep);
469 #endif
470 
471  rep = t;
472 
473  if (!rep)
474  rep = &TextRep::nullrep;
475 
476  sym = rep->data;
477  }
478 }
479 
480 char
481 Text::operator[](int index) const
482 {
483  if (index < (int) rep->length)
484  return sym[index];
485  else
486  throw "BOUNDS ERROR";
487 
488  return '\0';
489 }
490 
491 char
492 Text::operator()(int index) const
493 {
494  return sym[index];
495 }
496 
497 char&
499 {
500  if (index < (int) rep->length) {
501  clone();
502  return (char&) sym[index];
503  }
504  else
505  throw "BOUNDS ERROR";
506 
507  return (char&) sym[0];
508 }
509 
510 char&
512 {
513  clone();
514  return (char&) sym[index];
515 }
516 
517 Text
518 Text::operator()(int start, int len) const
519 {
520  if (start > rep->length || len <= 0)
521  return Text();
522 
523  if (start + len > rep->length)
524  len = rep->length - start;
525 
526 #ifdef MEM_DEBUG
527  char* buf = new(__FILE__,__LINE__) char[len+1];
528 #else
529  char* buf = new char[len+1];
530 #endif
531 
532  if (buf) {
533  ::strncpy(buf, sym+start, len);
534  buf[len] = '\0';
535 
536  Text retval(buf);
537  delete [] buf;
538  return retval;
539  }
540 
541  return Text();
542 }
543 
544 bool
545 Text::contains(char c) const
546 {
547  if (rep->length > 0) {
548  if (!rep->sensitive) {
549  char alt = c;
550  if (islower(alt)) alt = toupper(alt);
551  else if (isupper(alt)) alt = tolower(alt);
552 
553  if (strchr(rep->data, alt) != 0)
554  return true;
555  }
556 
557  if (strchr(rep->data, c) != 0)
558  return true;
559  }
560 
561  return false;
562 }
563 
564 bool
565 Text::contains(const char* pattern) const
566 {
567  if (rep->length > 0 && pattern && *pattern) {
568  if (rep->sensitive) {
569  if (strstr(rep->data, pattern) != 0)
570  return true;
571  }
572  else {
573  Text smash1(*this);
574  smash1.toLower();
575  Text smash2(pattern);
576  smash2.toLower();
577 
578  if (strstr(smash1.data(), smash2.data()) != 0)
579  return true;
580  }
581  }
582 
583  return false;
584 }
585 
586 bool
587 Text::containsAnyOf(const char* charSet) const
588 {
589  if (rep->length > 0 && charSet && *charSet) {
590  if (rep->sensitive) {
591  if (strpbrk(rep->data, charSet) != 0)
592  return true;
593  }
594  else {
595  Text smash1(*this);
596  smash1.toLower();
597  Text smash2(charSet);
598  smash2.toLower();
599 
600  if (strpbrk(smash1.data(), smash2.data()) != 0)
601  return true;
602  }
603  }
604 
605  return false;
606 }
607 
608 int
609 Text::indexOf(char c) const
610 {
611  if (rep->length > 0) {
612  if (!rep->sensitive) {
613  char alt = c;
614  if (islower(alt)) alt = toupper(alt);
615  else if (isupper(alt)) alt = tolower(alt);
616 
617  const char* p = strchr(rep->data, alt);
618 
619  if (p)
620  return (p - rep->data);
621  }
622 
623  const char* p = strchr(rep->data, c);
624 
625  if (p)
626  return (p - rep->data);
627  }
628 
629  return -1;
630 }
631 
632 int
633 Text::indexOf(const char* pattern) const
634 {
635  if (rep->length > 0 && pattern && *pattern) {
636  if (rep->sensitive) {
637  const char* p = strstr(rep->data, pattern);
638  if (p) return (p - rep->data);
639  }
640  else {
641  Text smash1(*this);
642  smash1.toLower();
643  Text smash2(pattern);
644  smash2.toLower();
645 
646  const char* p = strstr(smash1.data(), smash2.data());
647  if (p) return (p - smash1.data());
648  }
649  }
650 
651  return -1;
652 }
653 
654 void
656 {
657  clone();
658  size_t n = rep->length;
659  char* p = (char*) sym;
660  while (n--) {
661  *p = tolower((unsigned char)*p);
662  p++;
663  }
664 
665  rep->dohash();
666 }
667 
668 void
670 {
671  clone();
672  size_t n = rep->length;
673  char* p = (char*) sym;
674  while ( n-- ) {
675  *p = toupper((unsigned char)*p);
676  p++;
677  }
678 
679  rep->dohash();
680 }
681 
682 Text
683 Text::substring(int start, int length)
684 {
685  Text result;
686 
687  if (start >= 0 && start < (int) rep->length && length > 0) {
688  if (start + length > (int) rep->length)
689  length = (int) rep->length - start;
690 
691  const char* s = sym + start;
692 
693 #ifdef MEM_DEBUG
694  result.rep = new(__FILE__,__LINE__) TextRep(s, length);
695 #else
696  result.rep = new TextRep(s, length);
697 #endif
698 
699  if (!result.rep)
700  result.rep = &TextRep::nullrep;
701 
702  result.sym = result.rep->data;
703  }
704 
705  return result;
706 }
707 
708 Text
710 {
711  Text result;
712 
713  if (rep->length) {
714  const char* p = sym;
715  const char* q = sym + rep->length-1;
716 
717  while (p && *p && isspace(*p)) p++;
718  while (q>p && *q && isspace(*q)) q--;
719 
720  result = substring(p-sym, q-p+1);
721  }
722 
723  return result;
724 }
725 
726 Text
727 Text::replace(const char* pattern, const char* substitution)
728 {
729  Text result;
730 
731  if (rep->length && pattern && *pattern) {
732  int index = 0;
733  int skip = strlen(pattern);
734  do {
735  const char* p = strstr(rep->data + index, pattern);
736  if (p) {
737  int len = (p - rep->data + index);
738  result.append(substring(index, len));
739  result.append(substitution);
740  index += len + skip;
741  }
742  else if (index < rep->length) {
743  result.append(substring(index, rep->length-index));
744  index = -1;
745  }
746  }
747  while (index >= 0 && index < rep->length);
748  }
749 
750  return result;
751 }