3 * Iter Vehemens ad Necem (IVAN)
4 * Copyright (C) Timo Kiviluoto
5 * Released under the GNU General
8 * See LICENSING which should be included
9 * along with this file for more details
21 char **festring::IntegerMap
= 0;
22 const char *festring::EmptyString
= "";
23 festring::csizetype
festring::NPos
= festring::sizetype(-1);
26 void festring::CreateOwnData (cchar
*CStr
, sizetype N
) {
28 Reserved
= N
|FESTRING_PAGE
;
29 char *Ptr
= sizeof(rcint
)+new char[Reserved
+sizeof(rcint
)+1];
32 if (N
> 0 && CStr
) memmove(Ptr
, CStr
, N
);
37 void festring::ensureUniqueOwned (bool allocEmpty
) {
39 if (Size
== 0 && !allocEmpty
) Empty(); else CreateOwnData(Data
, Size
);
42 if (Size
== 0 && !allocEmpty
) { Empty(); return; }
46 CreateOwnData(Ptr
, Size
);
51 festring
&festring::Append (cchar
*CStr
, sizetype N
) {
52 sizetype OldSize
= Size
;
53 sizetype NewSize
= OldSize
+N
;
55 if (OwnsData
&& OldPtr
&& !REFS(OldPtr
) && NewSize
<= Reserved
) {
56 if (N
> 0) memmove(OldPtr
+OldSize
, CStr
, N
);
65 festring
&festring::operator = (cchar
*CStr
) {
66 sizetype NewSize
= (CStr
? strlen(CStr
) : 0);
67 if (NewSize
> 0x7ffff000) ABORT("String too big (or invalid)");
68 if (NewSize
== 0) { Empty(); return *this; }
71 if (Ptr
&& OwnsData
) {
72 if (!REFS(Ptr
) && NewSize
<= Reserved
) {
73 if (NewSize
> 0) memmove(Ptr
, CStr
, NewSize
);
76 if (!REFS(Ptr
)--) delete [] REFSA(Ptr
);
78 Data
= const_cast<char *>(CStr
);
84 festring
&festring::operator = (cfestring
&Str
) {
85 sizetype NewSize
= Str
.Size
;
86 if (NewSize
== 0) { Empty(); return *this; }
89 char *StrPtr
= Str
.Data
;
90 if (Ptr
&& OwnsData
) {
91 if (!REFS(Ptr
) && NewSize
<= Reserved
) {
92 if (StrPtr
&& NewSize
> 0) memmove(Ptr
, StrPtr
, NewSize
);
95 if (!REFS(Ptr
)--) delete [] REFSA(Ptr
);
97 if ((Data
= StrPtr
) && (OwnsData
= Str
.OwnsData
)) {
99 Reserved
= Str
.Reserved
;
105 /* Size must be > 0 */
106 festring
&festring::Capitalize () {
107 if (Size
> 0 && *Data
>= 'a' && *Data
<= 'z') {
115 void festring::SlowAppend (char Char
) {
118 sizetype OldSize
= Size
++;
120 if (OwnsData
&& !REFS(OldPtr
)--) DeletePtr
= REFSA(OldPtr
);
121 Reserved
= (OldSize
+1)|FESTRING_PAGE
;
122 char *NewPtr
= sizeof(rcint
)+new char[Reserved
+sizeof(rcint
)+1];
125 if (OldSize
> 0) memmove(NewPtr
, OldPtr
, OldSize
);
126 NewPtr
[OldSize
] = Char
;
127 if (DeletePtr
) delete [] DeletePtr
;
130 Reserved
= FESTRING_PAGE
;
131 char *Ptr
= sizeof(rcint
)+new char[FESTRING_PAGE
+sizeof(rcint
)+1];
140 void festring::SlowAppend (cchar
*CStr
, sizetype N
) {
144 sizetype OldSize
= Size
;
145 sizetype NewSize
= OldSize
+N
;
148 if (OwnsData
&& !REFS(OldPtr
)--) DeletePtr
= REFSA(OldPtr
);
149 Reserved
= NewSize
|FESTRING_PAGE
;
150 char *NewPtr
= sizeof(rcint
)+new char[Reserved
+sizeof(rcint
)+1];
153 if (OldSize
> 0) memmove(NewPtr
, OldPtr
, OldSize
);
154 if (N
> 0) memmove(NewPtr
+OldSize
, CStr
, N
);
156 if (DeletePtr
) delete [] DeletePtr
;
158 CreateOwnData(CStr
, N
);
163 void festring::Assign (sizetype N
, char C
) {
164 if (N
== 0) { Empty(); return; }
167 if (OwnsData
&& Ptr
) {
175 delete [] REFSA(Ptr
);
178 Reserved
= N
|FESTRING_PAGE
;
179 Ptr
= sizeof(rcint
)+new char[Reserved
+sizeof(rcint
)+1];
182 if (N
> 0) memset(Ptr
, C
, N
);
187 void festring::Resize (sizetype N
, char C
) {
188 if (N
== 0) { Empty(); return; }
189 sizetype OldSize
= Size
;
195 if (OwnsData
&& OldPtr
) {
198 memset(OldPtr
+OldSize
, C
, N
-OldSize
);
201 DeletePtr
= REFSA(OldPtr
);
206 Reserved
= N
|FESTRING_PAGE
;
207 NewPtr
= sizeof(rcint
)+new char[Reserved
+sizeof(rcint
)+1];
210 if (OldSize
> 0) memmove(NewPtr
, OldPtr
, OldSize
);
211 if (N
> OldSize
) memset(NewPtr
+OldSize
, C
, N
-OldSize
);
213 if (DeletePtr
) delete [] DeletePtr
;
215 if (OwnsData
&& OldPtr
) {
216 if (!REFS(OldPtr
)) return;
219 Reserved
= N
|FESTRING_PAGE
;
220 NewPtr
= sizeof(rcint
)+new char[Reserved
+sizeof(rcint
)+1];
223 if (N
> 0) memmove(NewPtr
, OldPtr
, N
);
229 festring::sizetype
festring::Find (char Char
, sizetype Pos
) const {
232 char *Result
= static_cast<char *>(memchr(Ptr
+Pos
, Char
, Size
-Pos
));
233 if (Result
) return Result
-Ptr
;
239 festring::sizetype
festring::Find (cchar
*CStr
, sizetype Pos
, sizetype N
) const {
240 if (N
&& Pos
< Size
) {
244 while (Size
-Pos
>= N
) {
245 if (Size
-Pos
< N
) return NPos
;
246 char *Result
= static_cast<char *>(memchr(Ptr
+Pos
, Char
, Size
-Pos
));
247 if (!Result
) return NPos
;
248 if (!memcmp(Result
, CStr
, N
)) return Result
-Ptr
;
257 festring::sizetype
festring::FindLast (char Char
, sizetype Pos
) const {
260 if (Pos
>= Size
) Pos
= Size
-1;
262 for (c
= Pos
; c
!= NPos
&& Ptr
[c
] != Char
; --c
);
269 festring::sizetype
festring::FindLast (const char *CStr
, sizetype Pos
, sizetype N
) const {
272 if (Ptr
&& Size
>= N
) {
274 if (Pos
> Size
-N
) Pos
= Size
-N
;
275 for (sizetype c
= Pos
; c
!= NPos
; --c
) if (Ptr
[c
] == Char
&& !memcmp(Ptr
+c
, CStr
, N
)) return c
;
283 void festring::Erase (sizetype Pos
, sizetype Length
) {
284 if (Pos
>= Size
) return;
285 if (Length
>= Size
&& Pos
== 0) { Empty(); return; }
287 if (OldPtr
&& Length
) {
288 sizetype OldSize
= Size
;
290 truth MoveReq
= (Length
< OldSize
-Pos
);
294 sizetype End
= Pos
+Length
;
295 memmove(OldPtr
+Pos
, OldPtr
+End
, OldSize
-End
);
303 sizetype NewSize
= (MoveReq
? OldSize
-Length
: Pos
);
305 Reserved
= NewSize
|FESTRING_PAGE
;
306 char *Ptr
= sizeof(rcint
)+new char[Reserved
+sizeof(rcint
)+1];
310 if (Pos
> 0) memmove(Ptr
, OldPtr
, Pos
);
312 sizetype End
= Pos
+Length
;
313 if (OldSize
> End
) memmove(Ptr
+Pos
, OldPtr
+End
, OldSize
-End
);
320 void festring::Insert (sizetype Pos
, cchar
*CStr
, sizetype N
) {
322 sizetype OldSize
= Size
;
324 // this implies Data != 0
327 sizetype NewSize
= OldSize
+N
;
331 if (NewSize
<= Reserved
) {
332 char *Ptr
= OldPtr
+Pos
;
333 memmove(Ptr
+N
, Ptr
, OldSize
-Pos
);
334 if (N
> 0) memmove(Ptr
, CStr
, N
);
337 DeletePtr
= REFSA(OldPtr
);
343 Reserved
= NewSize
|FESTRING_PAGE
;
344 char* NewPtr
= sizeof(rcint
)+new char[Reserved
+sizeof(rcint
)+1];
347 if (Pos
> 0) memmove(NewPtr
, OldPtr
, Pos
);
348 if (N
> 0) memmove(NewPtr
+Pos
, CStr
, N
);
349 if (OldSize
> Pos
) memmove(NewPtr
+Pos
+N
, OldPtr
+Pos
, OldSize
-Pos
);
351 if (DeletePtr
) delete [] DeletePtr
;
352 } else if (Pos
== OldSize
) {
355 ABORT("Illegal festring insertion detected!");
361 /* Creates map of char representations of numbers 0-999 used by
362 * festring::Append(sLong). Due to automatization, you don't need
363 * to explicitly call it. */
364 void festring::InstallIntegerMap () {
365 Alloc2D(IntegerMap
, 1000, 3);
366 char Ones
= '0', Tens
= '0', Hundreds
= '0';
367 for (int c
= 0; c
< 1000; ++c
) {
368 IntegerMap
[c
][0] = Hundreds
;
369 IntegerMap
[c
][1] = Tens
;
370 IntegerMap
[c
][2] = Ones
;
379 atexit(DeInstallIntegerMap
);
383 /* Deletes the integer map used by festring::Append(sLong).
384 * Due to automatization, you don't need to explicitly call it. */
385 void festring::DeInstallIntegerMap () {
386 delete [] IntegerMap
;
391 /* Displays numbers in the range [-2147483647, 2147483647].
392 * Much faster than sprintf and (nonstandard) itoa. */
393 festring
&festring::Append (sLong Integer
) {
394 if (!IntegerMap
) InstallIntegerMap();
395 char IntegerBuffer
[12];
396 char *BufferPtr
= IntegerBuffer
;
397 truth Negative
= false;
399 if (Integer
< -2147483647) return Append("-Inf", 4);
403 } else if (Integer
> 2147483647) return Append("Inf", 3);
404 truth ForceZeros
= false;
405 if (Integer
>= 2000000000) {
407 Integer
-= 2000000000;
409 } else if (Integer
>= 1000000000) {
411 Integer
-= 1000000000;
414 if (ForceZeros
|| Integer
>= 1000000) {
415 int Temp
= Integer
/1000000;
416 *BufferPtr
++ = IntegerMap
[Temp
][0];
417 *BufferPtr
++ = IntegerMap
[Temp
][1];
418 *BufferPtr
++ = IntegerMap
[Temp
][2];
419 Integer
-= Temp
*1000000;
422 if (ForceZeros
|| Integer
>= 1000) {
423 int Temp
= Integer
/1000;
424 *BufferPtr
++ = IntegerMap
[Temp
][0];
425 *BufferPtr
++ = IntegerMap
[Temp
][1];
426 *BufferPtr
++ = IntegerMap
[Temp
][2];
427 Integer
-= Temp
*1000;
429 *BufferPtr
++ = IntegerMap
[Integer
][0];
430 *BufferPtr
++ = IntegerMap
[Integer
][1];
431 *BufferPtr
++ = IntegerMap
[Integer
][2];
433 char *EndPtr
= BufferPtr
;
434 for (BufferPtr
= IntegerBuffer
; *BufferPtr
== '0'; ++BufferPtr
);
435 if (Negative
) *--BufferPtr
= '-';
436 else if (!*BufferPtr
) --BufferPtr
; // check if the original Integer was zero
437 return Append(BufferPtr
, EndPtr
-BufferPtr
);
441 festring
&festring::Append (int64_t Integer
) {
443 auto len
= snprintf(buf
, sizeof(buf
)-1, LONG_LONG_PFORMAT
, Integer
);
444 return Append(buf
, len
);
448 /* The Result string receives up to Length characters from source,
449 * but words are left uncut if possible. */
450 void festring::SplitString (festring
&Source
, festring
&Result
, sizetype Length
) {
452 if (Source.GetSize() <= Length) {
457 sizetype Pos = Source.FindLast(' ', Length);
459 Result.Append(Source, Pos);
460 Source.Erase(0, Pos+1);
462 Result.Append(Source, Length);
463 Source.Erase(0, Length);
466 SplitStringColored(Source
, Result
, Length
);
470 /* The Result string receives up to Length characters from source,
471 * but words are left uncut if possible. */
472 void festring::SplitStringColored (festring
&Source
, festring
&Result
, sizetype Length
) {
473 if (Source
.GetSize() <= Length
|| Length
< 1) {
480 sizetype len
= 0; // collected
483 while (Source
.Size
> 0) {
484 cchar
*s
= Source
.Data
;
485 sizetype left
= Source
.Size
;
486 sizetype wordlen
= 0;
488 // skip leading spaces
490 if (*s
== '\1') { ++s
; --left
; if (left
) { clrchar
= *s
++; --left
; } continue; }
491 if (*s
== '\2') { clrchar
= 0; ++s
; --left
; continue; }
492 if (*s
!= ' ') break;
499 while (left
> 0 && *s
!= ' ') {
500 if (*s
== '\1') { ++s
; --left
; if (left
) { clrchar
= *s
++; --left
; } continue; }
501 if (*s
== '\2') { clrchar
= 0; ++s
; --left
; continue; }
507 sizetype cplen
= (sizetype
)(s
-Source
.Data
);
509 if (len
== 0 && wordlen
> Length
) {
510 // oops, first word is too long, do it specially
511 cchar
*s
= Source
.Data
;
512 sizetype left
= Source
.Size
;
513 // append chars up to `Length`
516 if (*s
== '\1') { ++s
; --left
; if (left
) { clrchar
= *s
++; --left
; } continue; }
517 if (*s
== '\2') { clrchar
= 0; ++s
; --left
; if (wordlen
== Length
) break; continue; }
518 if (wordlen
== Length
) break;
523 cplen
= (sizetype
)(s
-Source
.Data
);
524 Result
.Append(Source
.Data
, cplen
);
525 Source
.Erase(0, cplen
);
527 if (clrchar
!= 0) { char ccbuf
[2]; ccbuf
[0] = '\1'; ccbuf
[1] = clrchar
; Source
.Insert(0, ccbuf
, 2); }
532 if (len
== 0 || len
+wordlen
<= Length
) {
534 Result
.Append(Source
.Data
, cplen
);
536 // erase appended part
537 Source
.Erase(0, cplen
);
541 // can't append; remove leading space (if any)
542 if (Source
.Size
> 0 && *Source
.Data
== ' ') Source
.Erase(0, 1);
544 if (clrchar
!= 0) { char ccbuf
[2]; ccbuf
[0] = '\1'; ccbuf
[1] = clrchar
; Source
.Insert(0, ccbuf
, 2); }
549 if (Source
.Size
> 0) Source
.Erase(0, Source
.Size
);
553 /* Divides Source into lines of size up to Length without cutting words
554 * and stores them one by one to StringVector. You can also specify a
555 * Marginal, in which case a number of spaces is inserted in the
556 * beginning of each line except the first. It returns the number of
558 int festring::SplitString (cfestring
&Source
, std::vector
<festring
> &StringVector
, sizetype Length
, sizetype Marginal
) {
560 if (!Length) ABORT("Illegal Length 0 passed to festring::SplitString()!");
561 if (Marginal >= Length) ABORT("Illegal festring::SplitString() call: Marginal must be less than Length!");
562 festring CopyOfSource(Source);
563 if (StringVector.empty()) StringVector.push_back(festring()); else StringVector[0].Empty();
564 SplitString(CopyOfSource, StringVector[0], Length);
566 while (!CopyOfSource.IsEmpty()) {
567 if (StringVector.size() <= Size) StringVector.push_back(festring());
568 festring &String = StringVector[Size++];
569 String.Assign(Marginal, ' ');
570 SplitString(CopyOfSource, String, Length-Marginal);
574 return SplitStringColored(Source
, StringVector
, Length
, Marginal
);
578 int festring::SplitStringColored (cfestring
&Source
, std::vector
<festring
> &StringVector
, sizetype Length
, sizetype Marginal
) {
579 if (!Length
) ABORT("Illegal Length 0 passed to festring::SplitStringColored()!");
580 if (Marginal
>= Length
) ABORT("Illegal festring::SplitStringColored() call: Marginal must be less than Length!");
581 festring
CopyOfSource(Source
);
582 if (StringVector
.empty()) StringVector
.push_back(festring()); else StringVector
[0].Empty();
583 SplitStringColored(CopyOfSource
, StringVector
[0], Length
);
585 while (!CopyOfSource
.IsEmpty()) {
586 if (StringVector
.size() <= Size
) StringVector
.push_back(festring());
587 festring
&String
= StringVector
[Size
++];
588 String
.Assign(Marginal
, ' ');
589 SplitStringColored(CopyOfSource
, String
, Length
-Marginal
);
595 truth
festring::hasCtlCodes () const {
597 for (sizetype c
= Size
; c
> 0; --c
, ++str
) {
598 if (*str
== '\1' || *str
== '\2') return true;
604 // without color codes
605 festring::sizetype
festring::rawLength () const {
608 for (sizetype c
= Size
; c
> 0; --c
, ++str
) {
610 case '\1': if (c
> 1) { ++str
; --c
; } break;
619 static inline char Capitalize (char Char
) { return (char)(Char
>= 'a' && Char
<= 'z' ? Char
-32 : Char
); }
622 truth
festring::startsWith (cchar
*str
, int slen
) const {
623 if (!str
) return (slen
<= 0);
624 int realslen
= (int)strlen(str
);
625 if (slen
< 0) slen
= (int)realslen
; else if (slen
> realslen
) return false;
626 if ((sizetype
)slen
> Size
) return false;
627 if (slen
== 0) return true;
628 return (memcmp(Data
, str
, slen
) == 0);
632 truth
festring::endsWith (cchar
*str
, int slen
) const {
633 if (!str
) return (slen
<= 0);
634 int realslen
= (int)strlen(str
);
635 if (slen
< 0) slen
= (int)realslen
; else if (slen
> realslen
) return false;
636 if (slen
== 0) return true;
637 if ((sizetype
)slen
> Size
) return false;
638 return (memcmp(Data
+Size
-slen
, str
, slen
) == 0);
642 truth
festring::startsWithCI (cchar
*str
, int slen
) const {
643 if (!str
) return (slen
<= 0);
644 int realslen
= (int)strlen(str
);
645 if (slen
< 0) slen
= (int)realslen
; else if (slen
> realslen
) return false;
646 if (slen
== 0) return true;
647 if ((sizetype
)slen
> Size
) return false;
649 for (; slen
> 0; ++str
, ++ep
, --slen
) {
650 if (::Capitalize(*str
) != ::Capitalize(*ep
)) return false;
656 truth
festring::endsWithCI (cchar
*str
, int slen
) const {
657 if (!str
) return (slen
<= 0);
658 int realslen
= (int)strlen(str
);
659 if (slen
< 0) slen
= (int)realslen
; else if (slen
> realslen
) return false;
660 if (slen
== 0) return true;
661 if ((sizetype
)slen
> Size
) return false;
662 cchar
*ep
= Data
+Size
-slen
;
663 for (; slen
> 0; ++str
, ++ep
, --slen
) {
664 if (::Capitalize(*str
) != ::Capitalize(*ep
)) return false;
670 truth
festring::startsWith (cfestring
&str
) const { return startsWith(str
.Data
, (int)str
.Size
); }
671 truth
festring::endsWith (cfestring
&str
) const { return endsWith(str
.Data
, (int)str
.Size
); }
672 truth
festring::startsWithCI (cfestring
&str
) const { return startsWithCI(str
.Data
, (int)str
.Size
); }
673 truth
festring::endsWithCI (cfestring
&str
) const { return endsWithCI(str
.Data
, (int)str
.Size
); }
676 /* Returns the position of the first occurance of What in Where
677 * starting at Begin or after it, ignoring the case of letters.
678 * If the search fails, festring::NPos is returned instead. */
679 festring::sizetype
festring::IgnoreCaseFind (cfestring
&Where
, cfestring
&What
, sizetype Begin
) {
680 if (What
.IsEmpty()) return Begin
;
681 for (; Where
.GetSize() >= What
.GetSize() + Begin
; ++Begin
) {
682 if (::Capitalize(Where
[Begin
]) == ::Capitalize(What
[0])) {
684 for (sizetype c
= 1; c
< What
.GetSize(); ++c
)
685 if (::Capitalize(Where
[Begin
+ c
]) != ::Capitalize(What
[c
])) {
689 if (Equal
) return Begin
;
696 /* Replaces all occurances of What in Where after Begin with With */
697 void festring::SearchAndReplace (festring
&Where
, cfestring
&What
, cfestring
&With
, sizetype Begin
) {
698 for (sizetype Pos
= Where
.Find(What
, Begin
); Pos
!= NPos
; Pos
= Where
.Find(What
, Pos
)) {
699 Where
.Erase(Pos
, What
.GetSize());
700 Where
.Insert(Pos
, With
);
705 /* Returns whether First is behind Second in alphabetical order, ignoring case */
706 bool festring::IgnoreCaseCompare (cfestring
&First
, cfestring
& Second
) {
707 for (sizetype Pos
= 0; Pos
< First
.GetSize() && Pos
< Second
.GetSize(); ++Pos
) {
708 char Char1
= ::Capitalize(First
[Pos
]);
709 char Char2
= ::Capitalize(Second
[Pos
]);
710 if (Char1
!= Char2
) return Char1
< Char2
;
712 return First
.GetSize() < Second
.GetSize();
716 void festring::SwapData (festring
&Str
) {
717 char *const TData
= Data
;
718 csizetype TSize
= Size
;
719 csizetype TReserved
= Reserved
;
720 ctruth TOwnsData
= OwnsData
;
723 Reserved
= Str
.Reserved
;
724 OwnsData
= Str
.OwnsData
;
727 Str
.Reserved
= TReserved
;
728 Str
.OwnsData
= TOwnsData
;
732 sLong
festring::GetCheckSum () const {
734 for (uShort c
= 0; c
< GetSize(); ++c
) Counter
= sLong(Data
[c
]);