1 //
2 // Copyright (c) 2008-2017 the Urho3D project.
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 // THE SOFTWARE.
21 //
22 
23 #include "../Precompiled.h"
24 
25 #include "../Core/StringUtils.h"
26 
27 #include <cstdio>
28 
29 #include "../DebugNew.h"
30 
31 namespace Urho3D
32 {
33 
34 static const String base64_chars =
35         "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
36         "abcdefghijklmnopqrstuvwxyz"
37         "0123456789+/";
38 
CountElements(const char * buffer,char separator)39 unsigned CountElements(const char* buffer, char separator)
40 {
41     if (!buffer)
42         return 0;
43 
44     const char* endPos = buffer + String::CStringLength(buffer);
45     const char* pos = buffer;
46     unsigned ret = 0;
47 
48     while (pos < endPos)
49     {
50         if (*pos != separator)
51             break;
52         ++pos;
53     }
54 
55     while (pos < endPos)
56     {
57         const char* start = pos;
58 
59         while (start < endPos)
60         {
61             if (*start == separator)
62                 break;
63 
64             ++start;
65         }
66 
67         if (start == endPos)
68         {
69             ++ret;
70             break;
71         }
72 
73         const char* end = start;
74 
75         while (end < endPos)
76         {
77             if (*end != separator)
78                 break;
79 
80             ++end;
81         }
82 
83         ++ret;
84         pos = end;
85     }
86 
87     return ret;
88 }
89 
ToBool(const String & source)90 bool ToBool(const String& source)
91 {
92     return ToBool(source.CString());
93 }
94 
ToBool(const char * source)95 bool ToBool(const char* source)
96 {
97     unsigned length = String::CStringLength(source);
98 
99     for (unsigned i = 0; i < length; ++i)
100     {
101         char c = (char)tolower(source[i]);
102         if (c == 't' || c == 'y' || c == '1')
103             return true;
104         else if (c != ' ' && c != '\t')
105             break;
106     }
107 
108     return false;
109 }
110 
ToInt(const String & source,int base)111 int ToInt(const String& source, int base)
112 {
113     return ToInt(source.CString(), base);
114 }
115 
ToInt(const char * source,int base)116 int ToInt(const char* source, int base)
117 {
118     if (!source)
119         return 0;
120 
121     // Shield against runtime library assert by converting illegal base values to 0 (autodetect)
122     if (base < 2 || base > 36)
123         base = 0;
124 
125     return (int)strtol(source, 0, base);
126 }
127 
ToInt64(const char * source,int base)128 long long ToInt64(const char* source, int base)
129 {
130     if (!source)
131         return 0;
132 
133     // Shield against runtime library assert by converting illegal base values to 0 (autodetect)
134     if (base < 2 || base > 36)
135         base = 0;
136 
137     return strtoll(source, 0, base);
138 }
139 
ToInt64(const String & source,int base)140 long long ToInt64(const String& source, int base)
141 {
142     return ToInt64(source.CString(), base);
143 }
144 
ToUInt(const String & source,int base)145 unsigned ToUInt(const String& source, int base)
146 {
147     return ToUInt(source.CString(), base);
148 }
149 
ToUInt64(const char * source,int base)150 unsigned long long ToUInt64(const char* source, int base)
151 {
152     if (!source)
153         return 0;
154 
155     // Shield against runtime library assert by converting illegal base values to 0 (autodetect)
156     if (base < 2 || base > 36)
157         base = 0;
158 
159     return strtoull(source, 0, base);
160 }
161 
ToUInt64(const String & source,int base)162 unsigned long long ToUInt64(const String& source, int base)
163 {
164     return ToUInt64(source.CString(), base);
165 }
166 
ToUInt(const char * source,int base)167 unsigned ToUInt(const char* source, int base)
168 {
169     if (!source)
170         return 0;
171 
172     if (base < 2 || base > 36)
173         base = 0;
174 
175     return (unsigned)strtoul(source, 0, base);
176 }
177 
ToFloat(const String & source)178 float ToFloat(const String& source)
179 {
180     return ToFloat(source.CString());
181 }
182 
ToFloat(const char * source)183 float ToFloat(const char* source)
184 {
185     if (!source)
186         return 0;
187 
188     return (float)strtod(source, 0);
189 }
190 
ToDouble(const String & source)191 double ToDouble(const String& source)
192 {
193     return ToDouble(source.CString());
194 }
195 
ToDouble(const char * source)196 double ToDouble(const char* source)
197 {
198     if (!source)
199         return 0;
200 
201     return strtod(source, 0);
202 }
203 
ToColor(const String & source)204 Color ToColor(const String& source)
205 {
206     return ToColor(source.CString());
207 }
208 
ToColor(const char * source)209 Color ToColor(const char* source)
210 {
211     Color ret;
212 
213     unsigned elements = CountElements(source, ' ');
214     if (elements < 3)
215         return ret;
216 
217     char* ptr = (char*)source;
218     ret.r_ = (float)strtod(ptr, &ptr);
219     ret.g_ = (float)strtod(ptr, &ptr);
220     ret.b_ = (float)strtod(ptr, &ptr);
221     if (elements > 3)
222         ret.a_ = (float)strtod(ptr, &ptr);
223 
224     return ret;
225 }
226 
ToIntRect(const String & source)227 IntRect ToIntRect(const String& source)
228 {
229     return ToIntRect(source.CString());
230 }
231 
ToIntRect(const char * source)232 IntRect ToIntRect(const char* source)
233 {
234     IntRect ret(IntRect::ZERO);
235 
236     unsigned elements = CountElements(source, ' ');
237     if (elements < 4)
238         return ret;
239 
240     char* ptr = (char*)source;
241     ret.left_ = (int)strtol(ptr, &ptr, 10);
242     ret.top_ = (int)strtol(ptr, &ptr, 10);
243     ret.right_ = (int)strtol(ptr, &ptr, 10);
244     ret.bottom_ = (int)strtol(ptr, &ptr, 10);
245 
246     return ret;
247 }
248 
ToIntVector2(const String & source)249 IntVector2 ToIntVector2(const String& source)
250 {
251     return ToIntVector2(source.CString());
252 }
253 
ToIntVector2(const char * source)254 IntVector2 ToIntVector2(const char* source)
255 {
256     IntVector2 ret(IntVector2::ZERO);
257 
258     unsigned elements = CountElements(source, ' ');
259     if (elements < 2)
260         return ret;
261 
262     char* ptr = (char*)source;
263     ret.x_ = (int)strtol(ptr, &ptr, 10);
264     ret.y_ = (int)strtol(ptr, &ptr, 10);
265 
266     return ret;
267 }
268 
ToIntVector3(const String & source)269 IntVector3 ToIntVector3(const String& source)
270 {
271     return ToIntVector3(source.CString());
272 }
273 
ToIntVector3(const char * source)274 IntVector3 ToIntVector3(const char* source)
275 {
276     IntVector3 ret(IntVector3::ZERO);
277 
278     unsigned elements = CountElements(source, ' ');
279     if (elements < 3)
280         return ret;
281 
282     char* ptr = (char*)source;
283     ret.x_ = (int)strtol(ptr, &ptr, 10);
284     ret.y_ = (int)strtol(ptr, &ptr, 10);
285     ret.z_ = (int)strtol(ptr, &ptr, 10);
286 
287     return ret;
288 }
289 
ToRect(const String & source)290 Rect ToRect(const String& source)
291 {
292     return ToRect(source.CString());
293 }
294 
ToRect(const char * source)295 Rect ToRect(const char* source)
296 {
297     Rect ret(Rect::ZERO);
298 
299     unsigned elements = CountElements(source, ' ');
300     if (elements < 4)
301         return ret;
302 
303     char* ptr = (char*)source;
304     ret.min_.x_ = (float)strtod(ptr, &ptr);
305     ret.min_.y_ = (float)strtod(ptr, &ptr);
306     ret.max_.x_ = (float)strtod(ptr, &ptr);
307     ret.max_.y_ = (float)strtod(ptr, &ptr);
308 
309     return ret;
310 }
311 
ToQuaternion(const String & source)312 Quaternion ToQuaternion(const String& source)
313 {
314     return ToQuaternion(source.CString());
315 }
316 
ToQuaternion(const char * source)317 Quaternion ToQuaternion(const char* source)
318 {
319     unsigned elements = CountElements(source, ' ');
320     char* ptr = (char*)source;
321 
322     if (elements < 3)
323         return Quaternion::IDENTITY;
324     else if (elements < 4)
325     {
326         // 3 coords specified: conversion from Euler angles
327         float x, y, z;
328         x = (float)strtod(ptr, &ptr);
329         y = (float)strtod(ptr, &ptr);
330         z = (float)strtod(ptr, &ptr);
331 
332         return Quaternion(x, y, z);
333     }
334     else
335     {
336         // 4 coords specified: full quaternion
337         Quaternion ret;
338         ret.w_ = (float)strtod(ptr, &ptr);
339         ret.x_ = (float)strtod(ptr, &ptr);
340         ret.y_ = (float)strtod(ptr, &ptr);
341         ret.z_ = (float)strtod(ptr, &ptr);
342 
343         return ret;
344     }
345 }
346 
ToVector2(const String & source)347 Vector2 ToVector2(const String& source)
348 {
349     return ToVector2(source.CString());
350 }
351 
ToVector2(const char * source)352 Vector2 ToVector2(const char* source)
353 {
354     Vector2 ret(Vector2::ZERO);
355 
356     unsigned elements = CountElements(source, ' ');
357     if (elements < 2)
358         return ret;
359 
360     char* ptr = (char*)source;
361     ret.x_ = (float)strtod(ptr, &ptr);
362     ret.y_ = (float)strtod(ptr, &ptr);
363 
364     return ret;
365 }
366 
ToVector3(const String & source)367 Vector3 ToVector3(const String& source)
368 {
369     return ToVector3(source.CString());
370 }
371 
ToVector3(const char * source)372 Vector3 ToVector3(const char* source)
373 {
374     Vector3 ret(Vector3::ZERO);
375 
376     unsigned elements = CountElements(source, ' ');
377     if (elements < 3)
378         return ret;
379 
380     char* ptr = (char*)source;
381     ret.x_ = (float)strtod(ptr, &ptr);
382     ret.y_ = (float)strtod(ptr, &ptr);
383     ret.z_ = (float)strtod(ptr, &ptr);
384 
385     return ret;
386 }
387 
ToVector4(const String & source,bool allowMissingCoords)388 Vector4 ToVector4(const String& source, bool allowMissingCoords)
389 {
390     return ToVector4(source.CString(), allowMissingCoords);
391 }
392 
ToVector4(const char * source,bool allowMissingCoords)393 Vector4 ToVector4(const char* source, bool allowMissingCoords)
394 {
395     Vector4 ret(Vector4::ZERO);
396 
397     unsigned elements = CountElements(source, ' ');
398     char* ptr = (char*)source;
399 
400     if (!allowMissingCoords)
401     {
402         if (elements < 4)
403             return ret;
404 
405         ret.x_ = (float)strtod(ptr, &ptr);
406         ret.y_ = (float)strtod(ptr, &ptr);
407         ret.z_ = (float)strtod(ptr, &ptr);
408         ret.w_ = (float)strtod(ptr, &ptr);
409 
410         return ret;
411     }
412     else
413     {
414         if (elements > 0)
415             ret.x_ = (float)strtod(ptr, &ptr);
416         if (elements > 1)
417             ret.y_ = (float)strtod(ptr, &ptr);
418         if (elements > 2)
419             ret.z_ = (float)strtod(ptr, &ptr);
420         if (elements > 3)
421             ret.w_ = (float)strtod(ptr, &ptr);
422 
423         return ret;
424     }
425 }
426 
ToVectorVariant(const String & source)427 Variant ToVectorVariant(const String& source)
428 {
429     return ToVectorVariant(source.CString());
430 }
431 
ToVectorVariant(const char * source)432 Variant ToVectorVariant(const char* source)
433 {
434     Variant ret;
435     unsigned elements = CountElements(source, ' ');
436 
437     switch (elements)
438     {
439     case 1:
440         ret.FromString(VAR_FLOAT, source);
441         break;
442 
443     case 2:
444         ret.FromString(VAR_VECTOR2, source);
445         break;
446 
447     case 3:
448         ret.FromString(VAR_VECTOR3, source);
449         break;
450 
451     case 4:
452         ret.FromString(VAR_VECTOR4, source);
453         break;
454 
455     case 9:
456         ret.FromString(VAR_MATRIX3, source);
457         break;
458 
459     case 12:
460         ret.FromString(VAR_MATRIX3X4, source);
461         break;
462 
463     case 16:
464         ret.FromString(VAR_MATRIX4, source);
465         break;
466 
467     default:
468         // Illegal input. Return variant remains empty
469         break;
470     }
471 
472     return ret;
473 }
474 
ToMatrix3(const String & source)475 Matrix3 ToMatrix3(const String& source)
476 {
477     return ToMatrix3(source.CString());
478 }
479 
ToMatrix3(const char * source)480 Matrix3 ToMatrix3(const char* source)
481 {
482     Matrix3 ret(Matrix3::ZERO);
483 
484     unsigned elements = CountElements(source, ' ');
485     if (elements < 9)
486         return ret;
487 
488     char* ptr = (char*)source;
489     ret.m00_ = (float)strtod(ptr, &ptr);
490     ret.m01_ = (float)strtod(ptr, &ptr);
491     ret.m02_ = (float)strtod(ptr, &ptr);
492     ret.m10_ = (float)strtod(ptr, &ptr);
493     ret.m11_ = (float)strtod(ptr, &ptr);
494     ret.m12_ = (float)strtod(ptr, &ptr);
495     ret.m20_ = (float)strtod(ptr, &ptr);
496     ret.m21_ = (float)strtod(ptr, &ptr);
497     ret.m22_ = (float)strtod(ptr, &ptr);
498 
499     return ret;
500 }
501 
ToMatrix3x4(const String & source)502 Matrix3x4 ToMatrix3x4(const String& source)
503 {
504     return ToMatrix3x4(source.CString());
505 }
506 
ToMatrix3x4(const char * source)507 Matrix3x4 ToMatrix3x4(const char* source)
508 {
509     Matrix3x4 ret(Matrix3x4::ZERO);
510 
511     unsigned elements = CountElements(source, ' ');
512     if (elements < 12)
513         return ret;
514 
515     char* ptr = (char*)source;
516     ret.m00_ = (float)strtod(ptr, &ptr);
517     ret.m01_ = (float)strtod(ptr, &ptr);
518     ret.m02_ = (float)strtod(ptr, &ptr);
519     ret.m03_ = (float)strtod(ptr, &ptr);
520     ret.m10_ = (float)strtod(ptr, &ptr);
521     ret.m11_ = (float)strtod(ptr, &ptr);
522     ret.m12_ = (float)strtod(ptr, &ptr);
523     ret.m13_ = (float)strtod(ptr, &ptr);
524     ret.m20_ = (float)strtod(ptr, &ptr);
525     ret.m21_ = (float)strtod(ptr, &ptr);
526     ret.m22_ = (float)strtod(ptr, &ptr);
527     ret.m23_ = (float)strtod(ptr, &ptr);
528 
529     return ret;
530 }
531 
ToMatrix4(const String & source)532 Matrix4 ToMatrix4(const String& source)
533 {
534     return ToMatrix4(source.CString());
535 }
536 
ToMatrix4(const char * source)537 Matrix4 ToMatrix4(const char* source)
538 {
539     Matrix4 ret(Matrix4::ZERO);
540 
541     unsigned elements = CountElements(source, ' ');
542     if (elements < 16)
543         return ret;
544 
545     char* ptr = (char*)source;
546     ret.m00_ = (float)strtod(ptr, &ptr);
547     ret.m01_ = (float)strtod(ptr, &ptr);
548     ret.m02_ = (float)strtod(ptr, &ptr);
549     ret.m03_ = (float)strtod(ptr, &ptr);
550     ret.m10_ = (float)strtod(ptr, &ptr);
551     ret.m11_ = (float)strtod(ptr, &ptr);
552     ret.m12_ = (float)strtod(ptr, &ptr);
553     ret.m13_ = (float)strtod(ptr, &ptr);
554     ret.m20_ = (float)strtod(ptr, &ptr);
555     ret.m21_ = (float)strtod(ptr, &ptr);
556     ret.m22_ = (float)strtod(ptr, &ptr);
557     ret.m23_ = (float)strtod(ptr, &ptr);
558     ret.m30_ = (float)strtod(ptr, &ptr);
559     ret.m31_ = (float)strtod(ptr, &ptr);
560     ret.m32_ = (float)strtod(ptr, &ptr);
561     ret.m33_ = (float)strtod(ptr, &ptr);
562 
563     return ret;
564 }
565 
ToString(void * value)566 String ToString(void* value)
567 {
568     return ToStringHex((unsigned)(size_t)value);
569 }
570 
ToStringHex(unsigned value)571 String ToStringHex(unsigned value)
572 {
573     char tempBuffer[CONVERSION_BUFFER_LENGTH];
574     sprintf(tempBuffer, "%08x", value);
575     return String(tempBuffer);
576 }
577 
BufferToString(String & dest,const void * data,unsigned size)578 void BufferToString(String& dest, const void* data, unsigned size)
579 {
580     // Precalculate needed string size
581     const unsigned char* bytes = (const unsigned char*)data;
582     unsigned length = 0;
583     for (unsigned i = 0; i < size; ++i)
584     {
585         // Room for separator
586         if (i)
587             ++length;
588 
589         // Room for the value
590         if (bytes[i] < 10)
591             ++length;
592         else if (bytes[i] < 100)
593             length += 2;
594         else
595             length += 3;
596     }
597 
598     dest.Resize(length);
599     unsigned index = 0;
600 
601     // Convert values
602     for (unsigned i = 0; i < size; ++i)
603     {
604         if (i)
605             dest[index++] = ' ';
606 
607         if (bytes[i] < 10)
608         {
609             dest[index++] = '0' + bytes[i];
610         }
611         else if (bytes[i] < 100)
612         {
613             dest[index++] = (char)('0' + bytes[i] / 10);
614             dest[index++] = (char)('0' + bytes[i] % 10);
615         }
616         else
617         {
618             dest[index++] = (char)('0' + bytes[i] / 100);
619             dest[index++] = (char)('0' + bytes[i] % 100 / 10);
620             dest[index++] = (char)('0' + bytes[i] % 10);
621         }
622     }
623 }
624 
StringToBuffer(PODVector<unsigned char> & dest,const String & source)625 void StringToBuffer(PODVector<unsigned char>& dest, const String& source)
626 {
627     StringToBuffer(dest, source.CString());
628 }
629 
StringToBuffer(PODVector<unsigned char> & dest,const char * source)630 void StringToBuffer(PODVector<unsigned char>& dest, const char* source)
631 {
632     if (!source)
633     {
634         dest.Clear();
635         return;
636     }
637 
638     unsigned size = CountElements(source, ' ');
639     dest.Resize(size);
640 
641     bool inSpace = true;
642     unsigned index = 0;
643     unsigned value = 0;
644 
645     // Parse values
646     const char* ptr = source;
647     while (*ptr)
648     {
649         if (inSpace && *ptr != ' ')
650         {
651             inSpace = false;
652             value = (unsigned)(*ptr - '0');
653         }
654         else if (!inSpace && *ptr != ' ')
655         {
656             value *= 10;
657             value += *ptr - '0';
658         }
659         else if (!inSpace && *ptr == ' ')
660         {
661             dest[index++] = (unsigned char)value;
662             inSpace = true;
663         }
664 
665         ++ptr;
666     }
667 
668     // Write the final value
669     if (!inSpace && index < size)
670         dest[index] = (unsigned char)value;
671 }
672 
GetStringListIndex(const String & value,const String * strings,unsigned defaultIndex,bool caseSensitive)673 unsigned GetStringListIndex(const String& value, const String* strings, unsigned defaultIndex, bool caseSensitive)
674 {
675     return GetStringListIndex(value.CString(), strings, defaultIndex, caseSensitive);
676 }
677 
GetStringListIndex(const char * value,const String * strings,unsigned defaultIndex,bool caseSensitive)678 unsigned GetStringListIndex(const char* value, const String* strings, unsigned defaultIndex, bool caseSensitive)
679 {
680     unsigned i = 0;
681 
682     while (!strings[i].Empty())
683     {
684         if (!strings[i].Compare(value, caseSensitive))
685             return i;
686         ++i;
687     }
688 
689     return defaultIndex;
690 }
691 
GetStringListIndex(const char * value,const char ** strings,unsigned defaultIndex,bool caseSensitive)692 unsigned GetStringListIndex(const char* value, const char** strings, unsigned defaultIndex, bool caseSensitive)
693 {
694     unsigned i = 0;
695 
696     while (strings[i])
697     {
698         if (!String::Compare(value, strings[i], caseSensitive))
699             return i;
700         ++i;
701     }
702 
703     return defaultIndex;
704 }
705 
ToString(const char * formatString,...)706 String ToString(const char* formatString, ...)
707 {
708     String ret;
709     va_list args;
710     va_start(args, formatString);
711     ret.AppendWithFormatArgs(formatString, args);
712     va_end(args);
713     return ret;
714 }
715 
IsAlpha(unsigned ch)716 bool IsAlpha(unsigned ch)
717 {
718     return ch < 256 ? isalpha(ch) != 0 : false;
719 }
720 
IsDigit(unsigned ch)721 bool IsDigit(unsigned ch)
722 {
723     return ch < 256 ? isdigit(ch) != 0 : false;
724 }
725 
ToUpper(unsigned ch)726 unsigned ToUpper(unsigned ch)
727 {
728     return (unsigned)toupper(ch);
729 }
730 
ToLower(unsigned ch)731 unsigned ToLower(unsigned ch)
732 {
733     return (unsigned)tolower(ch);
734 }
735 
GetFileSizeString(unsigned long long memorySize)736 String GetFileSizeString(unsigned long long memorySize)
737 {
738     static const char* memorySizeStrings = "kMGTPE";
739 
740     String output;
741 
742     if (memorySize < 1024)
743     {
744         output = String(memorySize) + " b";
745     }
746     else
747     {
748         const int exponent = (int)(log((double)memorySize) / log(1024.0));
749         const double majorValue = ((double)memorySize) / pow(1024.0, exponent);
750         char buffer[64];
751         memset(buffer, 0, 64);
752         sprintf(buffer, "%.1f", majorValue);
753         output = buffer;
754         output += " ";
755         output += memorySizeStrings[exponent - 1];
756     }
757 
758     return output;
759 }
760 
761 // Implementation of base64 decoding originally by Ren� Nyffenegger.
762 // Modified by Konstantin Guschin and Lasse Oorni
763 
764 /*
765 base64.cpp and base64.h
766 
767 Copyright (C) 2004-2017 Ren� Nyffenegger
768 
769 This source code is provided 'as-is', without any express or implied
770 warranty. In no event will the author be held liable for any damages
771 arising from the use of this software.
772 
773 Permission is granted to anyone to use this software for any purpose,
774 including commercial applications, and to alter it and redistribute it
775 freely, subject to the following restrictions:
776 
777 1. The origin of this source code must not be misrepresented; you must not
778 claim that you wrote the original source code. If you use this source code
779 in a product, an acknowledgment in the product documentation would be
780 appreciated but is not required.
781 
782 2. Altered source versions must be plainly marked as such, and must not be
783 misrepresented as being the original source code.
784 
785 3. This notice may not be removed or altered from any source distribution.
786 
787 Ren� Nyffenegger rene.nyffenegger@adp-gmbh.ch
788 
789 */
790 
IsBase64(char c)791 static inline bool IsBase64(char c) {
792     return (isalnum(c) || (c == '+') || (c == '/'));
793 }
794 
DecodeBase64(String encodedString)795 PODVector<unsigned char> DecodeBase64(String encodedString)
796 {
797     int inLen = encodedString.Length();
798     int i = 0;
799     int j = 0;
800     int in_ = 0;
801     unsigned char charArray4[4], charArray3[3];
802     PODVector<unsigned char> ret;
803 
804     while (inLen-- && (encodedString[in_] != '=') && IsBase64(encodedString[in_]))
805     {
806         charArray4[i++] = encodedString[in_];
807         in_++;
808 
809         if (i == 4)
810         {
811             for (i = 0; i < 4; i++)
812                 charArray4[i] = base64_chars.Find(charArray4[i]);
813 
814             charArray3[0] = (charArray4[0] << 2) + ((charArray4[1] & 0x30) >> 4);
815             charArray3[1] = ((charArray4[1] & 0xf) << 4) + ((charArray4[2] & 0x3c) >> 2);
816             charArray3[2] = ((charArray4[2] & 0x3) << 6) + charArray4[3];
817 
818             for (i = 0; (i < 3); i++)
819                 ret.Push(charArray3[i]);
820 
821             i = 0;
822         }
823     }
824 
825     if (i)
826     {
827         for (j = i; j <4; j++)
828             charArray4[j] = 0;
829 
830         for (j = 0; j <4; j++)
831             charArray4[j] = base64_chars.Find(charArray4[j]);
832 
833         charArray3[0] = (charArray4[0] << 2) + ((charArray4[1] & 0x30) >> 4);
834         charArray3[1] = ((charArray4[1] & 0xf) << 4) + ((charArray4[2] & 0x3c) >> 2);
835         charArray3[2] = ((charArray4[2] & 0x3) << 6) + charArray4[3];
836 
837         for (j = 0; (j < i - 1); j++)
838             ret.Push(charArray3[j]);
839     }
840 
841     return ret;
842 }
843 
844 }
845