1 // -*- Mode: C++; tab-width: 2; -*-
2 // vi: set ts=2:
3 //
4
5 #include <BALL/DATATYPE/string.h>
6
7 #include <QString>
8 #include <QByteArray>
9
10 #include <cstdio>
11 #include <cstdarg>
12 #include <limits>
13
14 #include <algorithm>
15
16 using std::ostream;
17 using std::istream;
18 using std::stringstream;
19 using std::endl;
20 using std::ends;
21 using std::vector;
22
23 namespace BALL
24 {
25
26 #ifndef BALL_HAVE_VSNPRINTF
BALLString_vsnprintf(char * s,size_t n,const char * format,va_list ap)27 int BALLString_vsnprintf(char* s, size_t n, const char* format, va_list ap)
28 {
29 // this is an ugly hack - this is safe only up to
30 // the static buffer size - no time to implement
31 // something more sophisticated... OK
32 char* tmp = new char[65536];
33 vsprintf(tmp, format, ap);
34 if (n > 65535)
35 {
36 n = 65535;
37 }
38 strncpy(s, tmp, n - 1);
39 s[n - 1] = (char)0;
40 delete [] tmp;
41 return (int)strlen(s);
42 }
43 # define vsnprintf BALLString_vsnprintf
44 #endif
45
46 const char* String::CHARACTER_CLASS__ASCII_ALPHA = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
47 const char* String::CHARACTER_CLASS__ASCII_ALPHANUMERIC = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
48 const char* String::CHARACTER_CLASS__ASCII_LOWER = "abcdefghijklmnopqrstuvwxyz";
49 const char* String::CHARACTER_CLASS__ASCII_NUMERIC = "0123456789";
50 const char* String::CHARACTER_CLASS__ASCII_UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
51 const char* String::CHARACTER_CLASS__WHITESPACE = " \n\t\r\f\v";
52 const char* String::CHARACTER_CLASS__QUOTES = "\"";
53
54 const Size String::EndPos = std::numeric_limits<Size>::max();
55
56 const String String::EMPTY("");
57
58 String::CompareMode String::compare_mode_ = String::CASE_SENSITIVE;
59
UnboundSubstring(const char * file,int line)60 Substring::UnboundSubstring::UnboundSubstring(const char* file, int line)
61 : Exception::GeneralException(file, line, "UnboundSubstring", "trying to use a substring that was not bound to a string.")
62 {
63 }
64
InvalidSubstring(const char * file,int line)65 Substring::InvalidSubstring::InvalidSubstring(const char* file, int line)
66 : Exception::GeneralException(file, line, "InvalidSubstring", "the substring is not valid")
67 {
68 }
69
Substring()70 Substring::Substring()
71 : bound_(0),
72 from_((Index)String::EndPos),
73 to_((Index)String::EndPos)
74 {
75 }
76
Substring(const Substring & substring,bool)77 Substring::Substring(const Substring& substring, bool /* deep */)
78 : bound_(substring.bound_),
79 from_(substring.from_),
80 to_(substring.to_)
81 {
82 }
83
Substring(const String & s,Index from,Size len)84 Substring::Substring(const String& s, Index from, Size len)
85 {
86 s.validateRange_(from, len);
87
88 bound_ = (String *)& s;
89 from_ = from;
90 to_ = from + (Index)len - 1;
91 }
92
~Substring()93 Substring::~Substring()
94 {
95 }
96
dump(ostream & s,Size depth) const97 void Substring::dump(ostream& s, Size depth) const
98 {
99 if (bound_ == 0)
100 {
101 throw UnboundSubstring(__FILE__, __LINE__);
102 }
103
104 BALL_DUMP_STREAM_PREFIX(s);
105
106 BALL_DUMP_DEPTH(s, depth);
107 s << " bound String: " << (void *)bound_ << endl;
108
109 BALL_DUMP_DEPTH(s, depth);
110 s << " from index: " << from_ << endl;
111
112 BALL_DUMP_DEPTH(s, depth);
113 s << " to index: " << to_ << endl;
114
115 BALL_DUMP_DEPTH(s, depth);
116 s << " string: ";
117
118 const char *end_of_string = bound_->c_str() + to_;
119
120 for (const char* ptr = bound_->c_str() + from_; ptr <= end_of_string; ptr++)
121 s << *ptr;
122
123 s << endl;
124
125 BALL_DUMP_STREAM_SUFFIX(s);
126 }
127
operator <<(ostream & s,const Substring & substring)128 ostream& operator << (ostream &s, const Substring& substring)
129 {
130 if (substring.isBound() == false)
131 {
132 return s;
133 }
134
135 const char* char_ptr = substring.bound_->c_str() + substring.from_;
136 const char* end_of_string = substring.bound_->c_str() + substring.to_;
137
138 while (char_ptr <= end_of_string)
139 {
140 s.put(*char_ptr++);
141 }
142
143 return s;
144 }
145
String(const QString & string)146 String::String(const QString& string)
147 {
148 assign(string.toLocal8Bit().data());
149 }
150
String(const QByteArray & string)151 String::String(const QByteArray& string)
152 {
153 assign(string.data());
154 }
155
String(const char * char_ptr,Index from,Size len)156 String::String(const char* char_ptr, Index from, Size len)
157 : string()
158 {
159 validateCharPtrRange_(from, len, char_ptr);
160 if (len > 0)
161 {
162 assign(char_ptr + from, len);
163 }
164 }
165
166 // hand-coded create method
create(bool,bool empty) const167 void* String::create(bool /* deep */, bool empty) const
168 {
169 void* ptr;
170 if (empty == true)
171 {
172 ptr = (void*)new String;
173 }
174 else
175 {
176 ptr = (void*)new String(*this);
177 }
178
179 return ptr;
180 }
181
String(Size buffer_size,const char * format,...)182 String::String(Size buffer_size, const char* format, ... )
183 : string()
184 {
185 if (buffer_size <= 0)
186 {
187 throw Exception::IndexUnderflow(__FILE__, __LINE__);
188 }
189
190 if (format == 0)
191 {
192 throw Exception::NullPointer(__FILE__, __LINE__);
193 }
194
195 char* buffer = new char[buffer_size];
196 memset(buffer, 0, buffer_size);
197
198 va_list var_arg_list;
199 va_start(var_arg_list, format);
200 vsnprintf(buffer, (Size)buffer_size, format, var_arg_list);
201 va_end(var_arg_list);
202
203 // this is a safeguard for strange vsnprintf implementations
204 buffer[buffer_size-1] = '\0';
205
206 assign(buffer);
207
208 delete [] buffer;
209 }
210
211 #ifdef BALL_HAS_SSTREAM
String(std::stringstream & s)212 String::String(std::stringstream& s)
213 #else
214 String::String(std::strstream& s)
215 #endif
216 : string("")
217 {
218 s >> (*this);
219 }
220
221 # define BALL_STRING_DEFINE_CONSTRUCTOR_METHOD(type, format_string) \
222 String::String(type t)\
223 { \
224 setlocale(LC_NUMERIC, "C"); \
225 char buffer[128]; \
226 \
227 sprintf(buffer, format_string, t); \
228 assign(buffer);\
229 }\
230
231 BALL_STRING_DEFINE_CONSTRUCTOR_METHOD(short, "%hd")
232 BALL_STRING_DEFINE_CONSTRUCTOR_METHOD(unsigned short, "%hu")
233 BALL_STRING_DEFINE_CONSTRUCTOR_METHOD(int, "%d")
234 BALL_STRING_DEFINE_CONSTRUCTOR_METHOD(unsigned int, "%u")
235 BALL_STRING_DEFINE_CONSTRUCTOR_METHOD(long, "%ld")
236 BALL_STRING_DEFINE_CONSTRUCTOR_METHOD(unsigned long, "%lu")
237 #ifdef BALL_ALLOW_LONG64_TYPE_OVERLOADS
238 BALL_STRING_DEFINE_CONSTRUCTOR_METHOD(LongIndex, "%lld")
239 BALL_STRING_DEFINE_CONSTRUCTOR_METHOD(LongSize, "%llu")
240 #endif
241 BALL_STRING_DEFINE_CONSTRUCTOR_METHOD(float, "%f")
242 BALL_STRING_DEFINE_CONSTRUCTOR_METHOD(double, "%f")
243
244 #undef BALL_STRING_DEFINE_CONSTRUCTOR_METHOD
245
~String()246 String::~String()
247 {
248 }
249
set(const String & s)250 void String::set(const String& s)
251 {
252 std::string::operator = (s);
253 }
254
set(const String & s,Index from,Size len)255 void String::set(const String& s, Index from, Size len)
256 {
257 s.validateRange_(from, len);
258
259 if (len == 0)
260 {
261 erase();
262 }
263 else
264 {
265 assign(s.c_str() + from, len);
266 }
267 }
268
set(const char * s,Index from,Size len)269 void String::set(const char* s, Index from, Size len)
270 {
271 validateCharPtrRange_(from, len, s);
272
273 if (len == 0)
274 {
275 erase();
276 }
277 else
278 {
279 assign(s + from, len);
280 }
281 }
282
set(Size buffer_size,const char * format,...)283 void String::set(Size buffer_size, const char *format, ... )
284 {
285 if (buffer_size <= 0)
286 {
287 throw Exception::IndexUnderflow(__FILE__, __LINE__);
288 }
289
290 if (format == 0)
291 {
292 throw Exception::NullPointer(__FILE__, __LINE__);
293 }
294
295 char* buffer = new char[buffer_size];
296 memset(buffer, 0, buffer_size);
297
298 va_list var_arg_list;
299 va_start(var_arg_list, format);
300 vsnprintf(buffer, (Size)buffer_size, format, var_arg_list);
301 va_end(var_arg_list);
302
303 // this is a safeguard for strange vsnprintf implementations
304 buffer[buffer_size-1] = '\0';
305
306 assign(buffer);
307
308 delete [] buffer;
309 }
310
311 # define BALL_STRING_DEFINE_SET_METHOD(type, format_string) \
312 void String::set(type t)\
313 { \
314 char buffer[128]; \
315 \
316 sprintf(buffer, format_string, t); \
317 \
318 assign(buffer);\
319 }
320
321 BALL_STRING_DEFINE_SET_METHOD(short, "%hd")
322 BALL_STRING_DEFINE_SET_METHOD(unsigned short, "%hu")
323 BALL_STRING_DEFINE_SET_METHOD(int, "%d")
324 BALL_STRING_DEFINE_SET_METHOD(unsigned int, "%u")
325 BALL_STRING_DEFINE_SET_METHOD(long, "%ld")
326 BALL_STRING_DEFINE_SET_METHOD(unsigned long, "%lu")
327 #ifdef BALL_ALLOW_LONG64_TYPE_OVERLOADS
328 BALL_STRING_DEFINE_SET_METHOD(LongIndex, "%lld")
329 BALL_STRING_DEFINE_SET_METHOD(LongSize, "%llu")
330 #endif
331 BALL_STRING_DEFINE_SET_METHOD(float, "%f")
332 BALL_STRING_DEFINE_SET_METHOD(double, "%f")
333
334 #undef BALL_STRING_DEFINE_SET_METHOD
335
get(char * char_ptr,Index from,Size max_len) const336 void String::get(char* char_ptr, Index from, Size max_len) const
337 {
338 validateIndex_(from);
339
340 if (max_len == 0)
341 {
342 return;
343 }
344
345 Size len = std::min(max_len, (Size)(size() - from));
346
347 const char* string_ptr = &(c_str()[from]);
348
349 Size i = 0;
350 while (i < len)
351 {
352 *char_ptr = *string_ptr;
353
354 char_ptr++;
355 string_ptr++;
356 i++;
357 }
358 *char_ptr = '\0';
359 }
360
toBool() const361 bool String::toBool() const
362 {
363 string::size_type str_index = find_first_not_of(CHARACTER_CLASS__WHITESPACE);
364
365 if (size() == 0)
366 {
367 return true;
368 }
369
370 if (str_index != string::npos)
371 {
372 Size index = (Index)str_index;
373 if (!(c_str()[index] == '0' && (isWhitespace(c_str()[index + 1]) == true || c_str()[index + 1] == '\0'))
374 && !(c_str()[index++] == 'f'
375 && c_str()[index++] == 'a'
376 && c_str()[index++] == 'l'
377 && c_str()[index++] == 's'
378 && c_str()[index++] == 'e'
379 && (isWhitespace(c_str()[index]) == true || c_str()[index] == '\0')))
380 {
381 return true;
382 }
383 }
384
385 return false;
386 }
387
toShort() const388 short String::toShort() const
389 {
390 if (!isFloat())
391 {
392 throw Exception::InvalidFormat(__FILE__, __LINE__, string(c_str()));
393 }
394
395 errno = 0;
396 int i = atoi(c_str());
397
398 if ((errno == ERANGE) || (i < (int)std::numeric_limits<short>::min()) || (i > (int)std::numeric_limits<short>::max()))
399 {
400 errno = 0;
401 throw Exception::InvalidFormat(__FILE__, __LINE__, string("out of range: ") + c_str());
402 }
403 errno = 0;
404
405 return (short)i;
406 }
407
toUnsignedShort() const408 unsigned short String::toUnsignedShort() const
409 {
410 if (!isFloat())
411 {
412 throw Exception::InvalidFormat(__FILE__, __LINE__, string(c_str()));
413 }
414
415 errno = 0;
416 int i = atoi(c_str());
417
418 if ((errno == ERANGE) || (i < (int)0) || (i > (int)std::numeric_limits<unsigned short>::max()))
419 {
420 errno = 0;
421 throw Exception::InvalidFormat(__FILE__, __LINE__, string("out of range: ") + c_str());
422 }
423 errno = 0;
424
425 return (unsigned short)i;
426 }
427
428
toInt() const429 int String::toInt() const
430 {
431 if (!isFloat())
432 {
433 throw Exception::InvalidFormat(__FILE__, __LINE__, string(c_str()));
434 }
435
436 errno = 0;
437 int i = atoi(c_str());
438
439 if (errno == ERANGE)
440 {
441 throw Exception::InvalidFormat(__FILE__, __LINE__, string("out of range: ") + string(c_str()));
442 }
443
444 return i;
445 }
446
447
toUnsignedInt() const448 unsigned int String::toUnsignedInt() const
449 {
450 if (!isFloat())
451 {
452 throw Exception::InvalidFormat(__FILE__, __LINE__, string(c_str()));
453 }
454
455 errno = 0;
456 unsigned int ui = (unsigned int)strtoul(c_str(), (char **)0, 10);
457
458 if (errno == ERANGE)
459 {
460 throw Exception::InvalidFormat(__FILE__, __LINE__, string(c_str()));
461 }
462
463 return ui;
464 }
465
466
toLong() const467 long String::toLong() const
468 {
469 if (!isFloat())
470 {
471 throw Exception::InvalidFormat(__FILE__, __LINE__, string(c_str()));
472 }
473
474 errno = 0;
475 long l = atol(c_str());
476
477 if (errno == ERANGE)
478 {
479 throw Exception::InvalidFormat(__FILE__, __LINE__, string(c_str()));
480 }
481
482 return l;
483 }
484
485
toUnsignedLong() const486 unsigned long String::toUnsignedLong() const
487 {
488 if (!isFloat())
489 {
490 throw Exception::InvalidFormat(__FILE__, __LINE__, string(c_str()));
491 }
492
493 errno = 0;
494 unsigned long ul = strtoul(c_str(), (char **)0, 10);
495
496 if (errno == ERANGE)
497 {
498 throw Exception::InvalidFormat(__FILE__, __LINE__, string(c_str()));
499 }
500
501 return ul;
502 }
503
504
toFloat() const505 float String::toFloat() const
506 {
507 if (!isFloat())
508 {
509 throw Exception::InvalidFormat(__FILE__, __LINE__, string(c_str()));
510 }
511
512 errno = 0;
513 float f = (float)atof(c_str());
514
515 if (errno == ERANGE)
516 {
517 throw Exception::InvalidFormat(__FILE__, __LINE__, string(c_str()));
518 }
519
520 return f;
521 }
522
523
toDouble() const524 double String::toDouble() const
525 {
526 if (!isFloat())
527 {
528 throw Exception::InvalidFormat(__FILE__, __LINE__, string(c_str()));
529 }
530 errno = 0;
531 double d = atof(c_str());
532
533 if (errno == ERANGE)
534 {
535 throw Exception::InvalidFormat(__FILE__, __LINE__, string(c_str()));
536 }
537
538 return d;
539 }
540
toLower(Index from,Size len)541 void String::toLower(Index from, Size len)
542 {
543 validateRange_(from, len);
544
545 std::transform(begin()+from, begin()+from+len, begin()+from, ::tolower);
546 }
547
toUpper(Index from,Size len)548 void String::toUpper(Index from, Size len)
549 {
550 validateRange_(from, len);
551
552 std::transform(begin()+from, begin()+from+len, begin()+from, ::toupper);
553 }
554
countFields(const char * delimiters) const555 Size String::countFields(const char* delimiters) const
556 {
557 if (delimiters == 0)
558 {
559 throw Exception::NullPointer(__FILE__, __LINE__);
560 }
561
562 Size number_of_fields = 0;
563
564 const char* end_char = &c_str()[size()];
565 const char* current_delimiter = 0;
566 const char* current_char = c_str();
567
568 while (current_char < end_char)
569 {
570 for (; current_char < end_char; ++current_char)
571 {
572 current_delimiter = (char*)strchr(delimiters, *current_char);
573
574 if (current_delimiter == 0)
575 {
576 break;
577 }
578 }
579
580 if (current_char < end_char)
581 {
582 ++number_of_fields;
583 ++current_char;
584 }
585
586 for (; current_char < end_char; ++current_char)
587 {
588 current_delimiter = (char*)strchr(delimiters, *current_char);
589
590 if (current_delimiter != 0)
591 {
592 break;
593 }
594 }
595 }
596
597 return number_of_fields;
598 }
599
countFieldsQuoted(const char * delimiters,const char * quotes) const600 Size String::countFieldsQuoted(const char* delimiters, const char* quotes) const
601 {
602 if ((delimiters == 0) || (quotes == 0))
603 {
604 throw Exception::NullPointer(__FILE__, __LINE__);
605 }
606
607 Size number_of_fields = 0;
608
609 const char* end_char = &c_str()[size()];
610 const char* current_delimiter = 0;
611 const char* current_char = c_str();
612 const char* current_quote = 0;
613 const char* last_quote = 0;
614
615 while (current_char < end_char)
616 {
617 for (; current_char < end_char; ++current_char)
618 {
619 current_quote = (char*)strchr(quotes, *current_char);
620
621 if (current_quote != 0)
622 {
623 if (last_quote == 0)
624 {
625 last_quote = current_char;
626 }
627 else
628 {
629 // if the same quote character is used again,
630 // it is a closing quote
631 if (*last_quote == *current_quote)
632 {
633 last_quote = 0;
634 }
635 }
636 }
637
638 if ((last_quote == 0) && (current_quote != current_char))
639 {
640 current_delimiter = (char*)strchr(delimiters, *current_char);
641
642 if (current_delimiter == 0)
643 {
644 break;
645 }
646 }
647 }
648
649 if (current_char < end_char)
650 {
651 ++number_of_fields;
652 ++current_char;
653 }
654
655 for (; current_char < end_char; ++current_char)
656 {
657 current_quote = (char*)strchr(quotes, *current_char);
658
659 if (current_quote != 0)
660 {
661 if (last_quote == 0)
662 {
663 last_quote = current_char;
664 }
665 else
666 {
667 // if the same quote character is used again,
668 // it is a closing quote
669 if (*last_quote == *current_quote)
670 {
671 last_quote = 0;
672 }
673 }
674 }
675
676 if (last_quote == 0)
677 {
678 current_delimiter = (char*)strchr(delimiters, *current_char);
679
680 if (current_delimiter != 0)
681 {
682 break;
683 }
684 }
685 }
686 }
687
688 return number_of_fields;
689 }
690
getField(Index index,const char * delimiters,Index * from_and_next_field) const691 String String::getField(Index index, const char* delimiters, Index* from_and_next_field) const
692 {
693 if ((from_and_next_field != 0) && (*from_and_next_field < 0))
694 {
695 throw Exception::IndexUnderflow(__FILE__, __LINE__, *from_and_next_field, 0);
696 }
697
698 if (delimiters == 0)
699 {
700 throw Exception::NullPointer(__FILE__, __LINE__);
701 }
702
703 // allow also negative indices (last field == -1)
704 if (index < 0)
705 {
706 index = (Index)countFields(delimiters) + index;
707 if (index < 0)
708 {
709 throw Exception::IndexUnderflow(__FILE__, __LINE__, index);
710 }
711 }
712
713 const char *end = &c_str()[size()];
714 Index current_index = 0;
715 const char *current_delimiter = 0;
716 const char *current_char = &c_str()[from_and_next_field == 0 ? 0 : *from_and_next_field];
717 const char *field_begin = 0;
718
719 while (current_char < end)
720 {
721 for (; current_char < end; ++current_char)
722 {
723 current_delimiter = (char*)strchr(delimiters, *current_char);
724
725 if (current_delimiter == 0)
726 {
727 break;
728 }
729 }
730
731 if (current_index == index)
732 {
733 field_begin = current_char;
734
735 for (++current_char; current_char < end; ++current_char)
736 {
737 current_delimiter = (char*)strchr(delimiters, *current_char);
738
739 if (current_delimiter != 0)
740 {
741 break;
742 }
743 }
744
745 if (from_and_next_field != 0)
746 {
747 if (current_char >= end)
748 {
749 *from_and_next_field = (Index)EndPos;
750 }
751 else
752 {
753 *from_and_next_field = (Index)(current_char - c_str());
754 }
755 }
756
757 if (field_begin < end)
758 {
759 return String(field_begin, 0, (Size)(current_char - field_begin));
760 }
761 }
762
763 ++current_index;
764
765 for (; current_char < end; ++current_char)
766 {
767 current_delimiter = (char*)strchr(delimiters, *current_char);
768
769 if (current_delimiter != 0)
770 {
771 break;
772 }
773 }
774 }
775
776 if (from_and_next_field != 0)
777 {
778 *from_and_next_field = (Index)EndPos;
779 }
780
781 return String();
782 }
783
eatDelimiters_(const char * start,const char * end,const char * delimiters)784 const char* eatDelimiters_(const char* start, const char* end, const char* delimiters)
785 {
786 const char* current_delimiter = (char*)strchr(delimiters, *start);
787 while ((current_delimiter != 0) && (start < end))
788 {
789 start++;
790 current_delimiter = (char*)strchr(delimiters, *start);
791 }
792
793 return start;
794 }
795
getFieldQuoted(Index index,const char * delimiters,const char * quotes,Index * from_and_next_field) const796 String String::getFieldQuoted(Index index, const char* delimiters,
797 const char* quotes, Index* from_and_next_field) const
798 {
799 if ((from_and_next_field != 0) && (*from_and_next_field < 0))
800 {
801 throw Exception::IndexUnderflow(__FILE__, __LINE__, *from_and_next_field, 0);
802 }
803
804 if ((delimiters == 0) || (quotes == 0))
805 {
806 throw Exception::NullPointer(__FILE__, __LINE__);
807 }
808
809 // allow also negative indices (last field == -1)
810 if (index < 0)
811 {
812 index = (Index)countFieldsQuoted(delimiters) + index;
813 if (index < 0)
814 {
815 throw Exception::IndexUnderflow(__FILE__, __LINE__, index);
816 }
817 }
818
819 String field;
820 const char* end = &c_str()[size()];
821 Index current_index = -1;
822 const char* current_delimiter = 0;
823 const char* current_quote = 0;
824 const char* last_quote = 0;
825 const char* current_char = &c_str()[from_and_next_field == 0 ? 0 : *from_and_next_field];
826
827 do
828 {
829 current_char = eatDelimiters_(current_char, end, delimiters);
830 current_index++;
831
832 // we are at the start of a new field
833 while (current_char < end)
834 {
835 current_quote = (char*)strchr(quotes, *current_char);
836 if (current_quote != 0)
837 {
838 if (last_quote != 0)
839 {
840 // reached the terminating quote
841 if (*last_quote == *current_quote)
842 {
843 last_quote = 0;
844 }
845 else
846 {
847 // just another quote character which doesn't matter
848 if (index == current_index)
849 {
850 field += *current_char;
851 }
852 }
853 }
854 else
855 {
856 // this is a new quote
857 last_quote = current_quote;
858 }
859 }
860 else
861 {
862 if (last_quote == 0)
863 {
864 current_delimiter = (char*)strchr(delimiters, *current_char);
865 if (current_delimiter != 0)
866 {
867 // we found a delimiter
868 // continue with the outer loop -> eat these delimiters!
869 break;
870 }
871 }
872 if (current_index == index)
873 {
874 field += *current_char;
875 }
876 }
877
878 current_char++;
879 }
880
881 if (current_index == index)
882 {
883 break;
884 }
885 }
886 while ((current_char < end) && (current_index < index));
887
888 if (from_and_next_field != 0)
889 {
890 *from_and_next_field = (Index)(current_char - &(c_str()[0]));
891 if (current_char >= end)
892 {
893 *from_and_next_field = (Index)EndPos;
894 }
895 }
896
897 return field;
898 }
899
split(String string_array[],Size array_size,const char * delimiters,Index from) const900 Size String::split(String string_array[], Size array_size, const char* delimiters, Index from) const
901 {
902 Size array_index = 0;
903
904 if (array_size < 1)
905 {
906 return 0;
907 }
908
909 while(from != (Index)EndPos)
910 {
911 string_array[array_index] = getField(0, delimiters, &from);
912
913 if (string_array[array_index] != "")
914 {
915 array_index++;
916 }
917
918 if (array_index == array_size)
919 {
920 return array_index;
921 }
922 }
923
924 return array_index;
925 }
926
split(vector<String> & strings,const char * delimiters,Index from) const927 Size String::split(vector<String>& strings, const char* delimiters, Index from) const
928 {
929 // clear the vector anyway
930 strings.clear();
931
932 while(from != (Index)EndPos)
933 {
934 String field = getField(0, delimiters, &from);
935
936 if (field != "")
937 {
938 strings.push_back(field);
939 }
940 }
941
942 return (Size)strings.size();
943 }
944
splitQuoted(vector<String> & strings,const char * delimiters,const char * quotes,Index from) const945 Size String::splitQuoted(vector<String>& strings, const char* delimiters, const char* quotes, Index from) const
946 {
947 // clear the vector anyway
948 strings.clear();
949
950 while (from != (Index)EndPos)
951 {
952 String field = getFieldQuoted(0, delimiters, quotes, &from);
953
954 if (field != "")
955 {
956 strings.push_back(field);
957 }
958 }
959
960 return (Size)strings.size();
961 }
962
trimLeft(const char * trimmed_chars)963 String& String::trimLeft(const char* trimmed_chars)
964 {
965 if ((trimmed_chars == 0) || (size() == 0))
966 {
967 return *this;
968 }
969
970 string::size_type index = find_first_not_of(trimmed_chars);
971
972 if (index != string::npos)
973 {
974 // erase the whitespace characters on the left
975 erase(0, index);
976 }
977 else
978 {
979 // if nothing was found, the string might contain only whitespaces!
980 String trimmed(trimmed_chars);
981 if (trimmed.has((*this)[0]))
982 {
983 assign("");
984 }
985 }
986
987 return *this;
988 }
989
trimRight(const char * trimmed_chars)990 String& String::trimRight(const char* trimmed_chars)
991 {
992 if (trimmed_chars == 0 ||
993 size() == 0)
994 {
995 return *this;
996 }
997
998 string::size_type index = find_last_not_of(trimmed_chars);
999
1000 if (index != string::npos)
1001 {
1002 // delete the whitespace characters on the right hand side
1003 erase(index + 1);
1004 }
1005 else
1006 {
1007 // if nothing was found, the string might contain only whitespaces!
1008 String trimmed(trimmed_chars);
1009 if (trimmed.has((*this)[size() - 1]))
1010 {
1011 assign("");
1012 }
1013 }
1014
1015 return *this;
1016 }
1017
operator +(const char * char_ptr,const String & s)1018 String operator + (const char* char_ptr, const String& s)
1019 {
1020 String result(char_ptr);
1021 result.append(s);
1022 return result;
1023 }
1024
operator +(char c,const String & s)1025 String operator + (char c, const String& s)
1026 {
1027 String result(c);
1028 result.append(s);
1029 return result;
1030 }
1031
hasPrefix(const String & s) const1032 bool String::hasPrefix(const String& s) const
1033 {
1034 if (s.size() > size())
1035 {
1036 return false;
1037 }
1038 if (s.size() == 0)
1039 {
1040 return true;
1041 }
1042
1043 return (memcmp(c_str(), s.c_str(), s.size()) == 0);
1044 }
1045
hasSuffix(const String & s) const1046 bool String::hasSuffix(const String& s) const
1047 {
1048 if (s.size() > size())
1049 {
1050 return false;
1051 }
1052 if (s.size() == 0)
1053 {
1054 return true;
1055 }
1056
1057 int result = memcmp(c_str() + size() - s.size(), s.c_str(), s.size());
1058
1059 return (result == 0);
1060 }
1061
1062
1063 #define BALL_STRING_DEFINE_IS_CLASS_METHOD(func, isclass) \
1064 bool func() const\
1065 { \
1066 const char* end = &c_str()[size()]; \
1067 \
1068 for (const char* ptr = c_str(); ptr < end; ptr++) \
1069 if (!isclass(*ptr)) \
1070 return false; \
1071 \
1072 return true; \
1073 }
1074
BALL_STRING_DEFINE_IS_CLASS_METHOD(String::isAlpha,isAlpha)1075 BALL_STRING_DEFINE_IS_CLASS_METHOD(String::isAlpha, isAlpha)
1076 BALL_STRING_DEFINE_IS_CLASS_METHOD(String::isAlnum, isAlnum)
1077 BALL_STRING_DEFINE_IS_CLASS_METHOD(String::isDigit, isDigit)
1078 BALL_STRING_DEFINE_IS_CLASS_METHOD(String::isWhitespace, isWhitespace)
1079 BALL_STRING_DEFINE_IS_CLASS_METHOD(String::isSpace, isSpace)
1080
1081 #undef BALL_STRING_DEFINE_IS_CLASS_METHOD
1082
1083 String& String::reverse(Index from, Size len)
1084 {
1085 validateRange_(from, len);
1086
1087 std::reverse(begin()+from, begin()+from+len);
1088 return *this;
1089 }
1090
compare(const String & s,Index from,Size len) const1091 int String::compare(const String& s, Index from, Size len) const
1092 {
1093 validateRange_(from, len);
1094
1095 if ((this == &s) && (from == 0))
1096 {
1097 return 0;
1098 }
1099
1100 Size newlen = std::min((Size)s.size(), len);
1101
1102 int result = 0;
1103 if (compare_mode_ == CASE_INSENSITIVE)
1104 {
1105 const char* s1 = c_str() + from;
1106 const char* s2 = s.c_str();
1107
1108 for (; newlen > 0; s1++, s2++, newlen--)
1109 {
1110 result = tolower(*s1) - tolower(*s2);
1111
1112 if (result != 0)
1113 {
1114 break;
1115 }
1116 }
1117 }
1118 else
1119 {
1120 result = strncmp(c_str() + from, s.c_str(), newlen);
1121 }
1122
1123 if ((result == 0) && (len != newlen))
1124 {
1125 result = (int)len - (int)s.size();
1126 }
1127
1128 return result;
1129 }
1130
compare(const String & s,Index from) const1131 int String::compare(const String& s, Index from) const
1132 {
1133 validateIndex_(from);
1134
1135 if ((this == &s) && (from == 0))
1136 {
1137 return 0;
1138 }
1139
1140 Size len = (Size)(size() - from);
1141 Size newlen = std::min((Size)s.size(), len);
1142
1143 int result = 0;
1144 if (compare_mode_ == CASE_INSENSITIVE)
1145 {
1146 const char* s1 = c_str() + from;
1147 const char* s2 = s.c_str();
1148
1149 for (; newlen > 0; s1++, s2++, newlen--)
1150 {
1151 result = tolower(*s1) - tolower(*s2);
1152
1153 if (result != 0)
1154 {
1155 break;
1156 }
1157 }
1158
1159 }
1160 else
1161 {
1162 result = strncmp(c_str() + from, s.c_str(), newlen);
1163 }
1164
1165 if (result == 0)
1166 {
1167 result = (int)len - (int)s.size();
1168 }
1169
1170 return result;
1171 }
1172
compare(const char * char_ptr,Index from,Size len) const1173 int String::compare(const char* char_ptr, Index from, Size len) const
1174 {
1175 if (char_ptr == 0)
1176 {
1177 throw Exception::NullPointer(__FILE__, __LINE__);
1178 }
1179
1180 validateRange_(from, len);
1181
1182 if ((c_str() + from) == char_ptr)
1183 {
1184 return 0;
1185 }
1186
1187 Size newlen = (Size)strlen(char_ptr);
1188
1189 newlen = std::min(len, newlen);
1190
1191 int result = 0;
1192 if (compare_mode_ == CASE_INSENSITIVE)
1193 {
1194 const char* ptr1 = c_str() + from;
1195 const char* ptr2 = char_ptr;
1196
1197 for (; newlen > 0; ptr1++, ptr2++)
1198 {
1199 newlen--;
1200 result = tolower(*ptr1) - tolower(*ptr2);
1201
1202 if (result != 0)
1203 {
1204 break;
1205 }
1206 }
1207
1208 }
1209 else
1210 {
1211 result = strncmp(c_str() + from, char_ptr, newlen);
1212 }
1213
1214 if ((result == 0) && (len != newlen))
1215 {
1216 result = (int)size() - (int)from - (int)strlen(char_ptr);
1217 }
1218
1219 return result;
1220 }
1221
compare(const char * char_ptr,Index from) const1222 int String::compare(const char* char_ptr, Index from) const
1223 {
1224 if (char_ptr == 0)
1225 {
1226 throw Exception::NullPointer(__FILE__, __LINE__);
1227 }
1228
1229 // indices may be given as negative arguments: start from the end
1230 // -1 therefore means the last bit.
1231 Size string_size = (Size)size();
1232 if (from < 0)
1233 {
1234 from = (Index)string_size + from;
1235
1236 // if the value is out of bounds - throw an exception
1237 // and leave it...
1238 if (from < 0)
1239 {
1240 throw Exception::IndexUnderflow(__FILE__, __LINE__, from, string_size);
1241 }
1242 }
1243
1244 if ((Size)from > string_size)
1245 {
1246 throw Exception::IndexOverflow(__FILE__, __LINE__, from, string_size);
1247 }
1248
1249 Size len = string_size - from;
1250
1251 if ((c_str() + from) == char_ptr)
1252 {
1253 return 0;
1254 }
1255
1256 Size newlen = std::min((Size)strlen(char_ptr), len);
1257
1258 newlen = len;
1259
1260 int result = 0;
1261
1262 if (compare_mode_ == CASE_INSENSITIVE)
1263 {
1264 const char* ptr1 = c_str() + from;
1265 const char* ptr2 = char_ptr;
1266
1267 for (; newlen > 0; ptr1++, ptr2++)
1268 {
1269 newlen--;
1270 result = tolower(*ptr1) - tolower(*ptr2);
1271
1272 if (result != 0)
1273 {
1274 break;
1275 }
1276 }
1277
1278 }
1279 else
1280 {
1281 result = strncmp(c_str() + from, char_ptr, newlen);
1282 }
1283
1284 if ((result == 0) && (len == newlen))
1285 {
1286 return (int)string_size - (int)from - (int)strlen(char_ptr);
1287 }
1288
1289 return result;
1290 }
1291
getline(istream & s,String & str,char delimiter)1292 istream& getline(istream& s, String& str, char delimiter)
1293 {
1294 char c;
1295
1296 str.destroy();
1297
1298 while (s.get(c))
1299 {
1300 if (c == delimiter)
1301 {
1302 break;
1303 }
1304 str.append(1, c);
1305 }
1306
1307 return s;
1308 }
1309
dump(ostream & s,Size depth) const1310 void String::dump(ostream &s, Size depth) const
1311 {
1312 BALL_DUMP_STREAM_PREFIX(s);
1313
1314 BALL_DUMP_DEPTH(s, depth);
1315 s << " capacity: " << capacity() << endl;
1316
1317 BALL_DUMP_DEPTH(s, depth);
1318 s << " size: " << size() << endl;
1319
1320 BALL_DUMP_DEPTH(s, depth);
1321 s << " string: ";
1322
1323 const char* end_ptr = &c_str()[size()];
1324
1325 for (const char *char_ptr = c_str(); char_ptr < end_ptr; char_ptr++)
1326 s << *char_ptr;
1327
1328 s << endl;
1329
1330 BALL_DUMP_STREAM_SUFFIX(s);
1331 }
1332
substitute(const String & to_replace,const String & replacing)1333 Size String::substitute(const String& to_replace, const String& replacing)
1334 {
1335 Size replaced_size = (Size)to_replace.size();
1336
1337 string::size_type found = 0;
1338 if (to_replace != "")
1339 {
1340 found = find(to_replace);
1341 }
1342
1343 if (found != string::npos)
1344 {
1345 replace(found, replaced_size, replacing);
1346 }
1347
1348 return ((found == string::npos) ? EndPos : (Size)found);
1349 }
1350
1351
validateIndex_(Index & index) const1352 void String::validateIndex_(Index& index) const
1353 {
1354 // indices may be given as negative arguments: start from the end
1355 // -1 therefore means the last bit.
1356 Size string_size = (Size)size();
1357 if (index < 0)
1358 {
1359 index = (Index)string_size + index;
1360
1361 // if the value is out of bounds - throw an exception
1362 // and leave it...
1363 if (index < 0)
1364 {
1365 throw Exception::IndexUnderflow(__FILE__, __LINE__, index, string_size);
1366 }
1367 }
1368
1369 if ((Size)index > string_size)
1370 {
1371 throw Exception::IndexOverflow(__FILE__, __LINE__, index, string_size);
1372 }
1373 }
1374
validateRange_(Index & from,Size & len) const1375 void String::validateRange_(Index& from, Size& len) const
1376 {
1377 Size string_size = (Size)size();
1378
1379 // indices may be given as negative arguments: start from the end
1380 // -1 therefore means the last character of the string.
1381 if (from < 0)
1382 {
1383 from = (Index)string_size + from;
1384
1385 // if the values are out of bounds - throw an exception
1386 // and leave it...
1387 if (from < 0)
1388 {
1389 throw Exception::IndexUnderflow(__FILE__, __LINE__, from, string_size);
1390 }
1391 }
1392
1393 if (((Size)from > string_size) || ((string_size > 0) && ((Size)from == string_size) && (len != 0)))
1394 {
1395 throw Exception::IndexOverflow(__FILE__, __LINE__, from, string_size);
1396 }
1397
1398 if (len == EndPos)
1399 {
1400 len = string_size - from;
1401 }
1402
1403 if (len > (string_size - from))
1404 {
1405 throw Exception::IndexOverflow(__FILE__, __LINE__, (Index)len, string_size);
1406 }
1407 }
1408
validateRange_(Index & from,Size & len) const1409 void Substring::validateRange_(Index& from, Size& len) const
1410 {
1411 Size size = to_ - from_ + 1;
1412
1413 // indices may be given as negative arguments: start from the end
1414 // -1 therefore means the to bit.
1415 if (from < 0)
1416 {
1417 from = (Index)size + from;
1418
1419 // if the values are out of bounds - throw an exception
1420 // and leave it...
1421 if (from < 0)
1422 {
1423 throw Exception::IndexUnderflow(__FILE__, __LINE__, from, size);
1424 }
1425 }
1426
1427 if (((Size)from > size) || ((size > 0) && ((Size)from == size)))
1428 {
1429 throw Exception::IndexOverflow(__FILE__, __LINE__, from, size);
1430 }
1431
1432 if (len == String::EndPos)
1433 {
1434 len = size - from;
1435 }
1436
1437 if (len > (size - from))
1438 {
1439 throw Exception::IndexOverflow(__FILE__, __LINE__, (Index)len, size);
1440 }
1441 }
1442
validateCharPtrRange_(Index & from,Size & len,const char * char_ptr)1443 void String::validateCharPtrRange_(Index& from, Size& len, const char* char_ptr)
1444 {
1445 if (char_ptr == 0)
1446 {
1447 throw Exception::NullPointer(__FILE__, __LINE__);
1448 }
1449
1450 Size total_len = (Size)strlen(char_ptr);
1451
1452 // indices may be given as negative arguments: start from the end
1453 // -1 therefore means the to bit.
1454 if (from < 0)
1455 {
1456 from = (Index)total_len + from;
1457
1458 // if the values are out of bounds - throw an exception
1459 // and leave it...
1460 if (from < 0)
1461 {
1462 throw Exception::IndexUnderflow(__FILE__, __LINE__, from, len);
1463 }
1464 }
1465
1466 if (((Size)from > total_len) || ((total_len > 0) && ((Size)from == total_len)))
1467 {
1468 throw Exception::IndexOverflow(__FILE__, __LINE__, from, len);
1469 }
1470
1471 if (len == EndPos)
1472 {
1473 len = total_len - from;
1474 }
1475
1476 if (len > (total_len - from))
1477 {
1478 throw Exception::IndexOverflow(__FILE__, __LINE__, (Index)len, total_len);
1479 }
1480 }
1481
operator ==(const char * char_ptr) const1482 bool Substring::operator == (const char* char_ptr) const
1483 {
1484 if (bound_ == 0)
1485 {
1486 throw UnboundSubstring(__FILE__, __LINE__);
1487 }
1488
1489 if (char_ptr == 0)
1490 {
1491 throw Exception::NullPointer(__FILE__, __LINE__);
1492 }
1493
1494 // to prevent compiler warning:
1495 if ((signed)strlen(char_ptr) != (to_ - from_ +1))
1496 {
1497 return false;
1498 }
1499 return (bound_->compare(char_ptr, from_, to_ - from_ + 1) == 0);
1500 }
1501
1502
operator !=(const char * char_ptr) const1503 bool Substring::operator != (const char* char_ptr) const
1504 {
1505 if (bound_ == 0)
1506 {
1507 throw UnboundSubstring(__FILE__, __LINE__);
1508 }
1509 if (char_ptr == 0)
1510 {
1511 throw Exception::NullPointer(__FILE__, __LINE__);
1512 }
1513 // to prevent compiler warning:
1514 if ((signed)strlen(char_ptr) != (to_ - from_ +1))
1515 {
1516 return true;
1517 }
1518 return (bound_->compare(char_ptr, from_, to_ - from_ + 1) != 0);
1519 }
1520
1521
bind(const Substring & s,Index from,Size len)1522 Substring& Substring::bind(const Substring& s, Index from, Size len)
1523 {
1524 s.validateRange_(from, len);
1525
1526 bound_ = s.bound_;
1527 from += s.from_;
1528 to_ = s.from_ + (Index)len - 1;
1529 from_ = from;
1530
1531 return *this;
1532 }
1533
1534
set(const char * char_ptr,Size size)1535 void Substring::set(const char* char_ptr, Size size)
1536 {
1537 if (bound_ == 0)
1538 {
1539 throw UnboundSubstring(__FILE__, __LINE__);
1540 }
1541 if (char_ptr == 0)
1542 {
1543 throw Exception::NullPointer(__FILE__, __LINE__);
1544 }
1545 if (size == 0)
1546 {
1547 throw Exception::SizeUnderflow(__FILE__, __LINE__);
1548 }
1549
1550 if (size == String::EndPos)
1551 {
1552 bound_->replace(from_, to_ - from_ + 1, string(char_ptr));
1553 }
1554 else
1555 {
1556 bound_->replace(from_, to_ - from_ + 1, char_ptr, size);
1557 }
1558 }
1559
1560
operator ==(const Substring & s) const1561 bool Substring::operator == (const Substring& s) const
1562 {
1563 if (bound_ == 0 || s.bound_ == 0)
1564 {
1565 throw UnboundSubstring(__FILE__, __LINE__);
1566 }
1567 if ((s.to_ - s.from_) != (to_ - from_))
1568 {
1569 return false;
1570 }
1571 return (bound_->compare(s.c_str() + from_, from_, to_ - from_ + 1) == 0);
1572 }
1573
1574
operator !=(const Substring & s) const1575 bool Substring::operator != (const Substring& s) const
1576 {
1577 if (bound_ == 0 || s.bound_ == 0)
1578 {
1579 throw UnboundSubstring(__FILE__, __LINE__);
1580 }
1581 if ((s.to_ - s.from_) != (to_ - from_))
1582 {
1583 return true;
1584 }
1585 return (bound_->compare(s.c_str() + from_, from_, to_ - from_ + 1) != 0);
1586 }
1587
1588
before(const String & s,Index from) const1589 Substring String::before(const String& s, Index from) const
1590 {
1591 Position found = EndPos;
1592 if (s != "")
1593 {
1594 found = (Position)find(s, from);
1595 }
1596
1597 if (found == 0 || found == EndPos)
1598 {
1599 return Substring(*this, 0, 0);
1600 }
1601
1602 return getSubstring(0, found);
1603 }
1604
1605
through(const String & s,Index from) const1606 Substring String::through (const String& s, Index from) const
1607 {
1608 Position found = EndPos;
1609 if (s != "")
1610 {
1611 found = (Position)find(s, from);
1612 }
1613
1614 if (found == EndPos)
1615 {
1616 return Substring(*this, 0, 0);
1617 }
1618
1619 return getSubstring(0, found + (Size)s.size());
1620 }
1621
1622
from(const String & s,Index from) const1623 Substring String::from(const String& s, Index from) const
1624 {
1625 if (s == "")
1626 {
1627 return getSubstring(0);
1628 }
1629
1630 Size found = (Size)find(s, from);
1631
1632 if (found == EndPos)
1633 {
1634 return Substring(*this, -1, 0);
1635 }
1636
1637 return getSubstring((Index)found);
1638 }
1639
1640
after(const String & s,Index from) const1641 Substring String::after(const String& s, Index from) const
1642
1643 {
1644 if (s == "")
1645 {
1646 return getSubstring(0);
1647 }
1648
1649 Position found = (Position)find(s, from);
1650
1651 if ((found == EndPos) || (found + s.size() >= size()))
1652 {
1653 return Substring(*this, -1, 0);
1654 }
1655
1656 return getSubstring((Index)found + (Index)s.size());
1657 }
1658
1659
right(Size len) const1660 Substring String::right(Size len) const
1661 {
1662 // to save calls to size()
1663 Size s = (Size)size();
1664 if (len > s)
1665 {
1666 len = s;
1667 }
1668
1669 Index from = (Index)s - (Index)len;
1670
1671 if (len > 0)
1672 {
1673 from = (Index)s - (Index)len;
1674 }
1675 else
1676 {
1677 if (s > 0)
1678 {
1679 from = (Index)s - 1;
1680 }
1681 else
1682 {
1683 from = 0;
1684 }
1685 }
1686
1687 return Substring(*this, from, len);
1688 }
1689
1690
1691 // ================================================== Base64 methods
1692
1693 char String::B64Chars_[64] = {
1694 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
1695 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
1696 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
1697 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
1698 '8', '9', '+', '/'
1699 };
1700
1701 int String::Index_64_[128] = {
1702 -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
1703 -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
1704 -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
1705 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1,
1706 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
1707 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
1708 -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
1709 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
1710 };
1711
1712 #define base64val_(c) Index_64_[(unsigned int)(c)]
1713
encodeBase64()1714 String String::encodeBase64()
1715 {
1716 Size in_length((Size)this->size());
1717 const char* in = this->c_str();
1718 String out;
1719
1720 while (in_length >= 3)
1721 {
1722 out += B64Chars_[in[0] >> 2];
1723 out += B64Chars_[((in[0] << 4) & 0x30) | (in[1] >> 4)];
1724 out += B64Chars_[((in[1] << 2) & 0x3c) | (in[2] >> 6)];
1725 out += B64Chars_[in[2] & 0x3f];
1726 in_length -= 3;
1727 in += 3;
1728 }
1729
1730 if (in_length > 0)
1731 {
1732 unsigned char fragment;
1733
1734 out += B64Chars_[in[0] >> 2];
1735 fragment = (in[0] << 4) & 0x30;
1736 if (in_length > 1)
1737 {
1738 fragment |= in[1] >> 4;
1739 }
1740 out += B64Chars_[fragment];
1741 out += (in_length < 2) ? '=' : B64Chars_[(in[1] << 2) & 0x3c];
1742 out += '=';
1743 }
1744
1745 return out;
1746 }
1747
decodeBase64()1748 String String::decodeBase64()
1749 {
1750 const char* in = this->c_str();
1751 String out;
1752
1753 unsigned char digit4;
1754 do
1755 {
1756 unsigned char digit1 = in[0];
1757 if (digit1 > 127 || base64val_ (digit1) == -1) //-1 == BAD
1758 {
1759 return String::EMPTY;
1760 }
1761
1762 unsigned char digit2 = in[1];
1763 if (digit2 > 127 || base64val_ (digit2) == -1)
1764 {
1765 return String::EMPTY;
1766 }
1767
1768 unsigned char digit3 = in[2];
1769 if (digit3 > 127 || ((digit3 != '=') && (base64val_ (digit3) == -1)))
1770 {
1771 return String::EMPTY;
1772 }
1773
1774 digit4 = in[3];
1775 if (digit4 > 127 || ((digit4 != '=') && (base64val_ (digit4) == -1)))
1776 {
1777 return String::EMPTY;
1778 }
1779
1780 in += 4;
1781
1782 // digits are already sanity-checked
1783 out += (base64val_(digit1) << 2) | (base64val_(digit2) >> 4);
1784 if (digit3 != '=')
1785 {
1786 out += ((base64val_(digit2) << 4) & 0xf0) | (base64val_(digit3) >> 2);
1787 if (digit4 != '=')
1788 {
1789 out += ((base64val_(digit3) << 6) & 0xc0) | base64val_(digit4);
1790 }
1791 }
1792 }
1793 while (*in && digit4 != '=');
1794
1795 return out;
1796 }
1797
1798 # ifdef BALL_NO_INLINE_FUNCTIONS
1799 # include <BALL/DATATYPE/string.iC>
1800 # endif
1801
1802 } // namespace BALL
1803