source: cpp/frams/util/sstring-simple.cpp @ 1279

Last change on this file since 1279 was 1130, checked in by Maciej Komosinski, 4 years ago

Used std::min(), std::max() explicitly to avoid compiler confusion. Used std::size() explicitly instead of the equivalent macro

  • Property svn:eol-style set to native
File size: 7.3 KB
Line 
1#include "sstring.h"
2#include "extvalue.h"
3#include <assert.h>
4#include <common/nonstd_math.h>
5
6#ifdef __ANDROID__
7#include <android/log.h> //only needed to print error messages related to a workaround for Android bug
8#endif
9
10
11void SString::initEmpty()
12{
13        txt = NULL; used = 0; allocated = 0;
14}
15
16SString::SString()
17{
18        initEmpty();
19}
20
21SString::~SString()
22{
23        reallocate(0);
24}
25
26SString::SString(const char *t, int t_len)
27{
28        initEmpty();
29        if (!t) return;
30        copyFrom(t, t_len);
31}
32
33SString::SString(const SString &from)
34{
35        initEmpty();
36        operator=(from);
37}
38
39SString::SString(SString&& from)
40{
41        txt = from.txt; allocated = from.allocated; used = from.used;
42        from.txt = NULL; from.allocated = 0; from.used = 0;
43}
44
45SString::SString(char in)
46{
47        initEmpty();
48        append(&in, 1);
49}
50
51void SString::reallocate(int newsize)
52{
53        if (newsize == allocated) return;
54        txt = (char*)realloc(txt, newsize);
55        allocated = newsize;
56}
57
58void SString::ensureAllocated(int needed)
59{
60        if (allocated > needed) return;
61        reallocate((allocated > 0) ? (needed + needed / 2 + 1) : (needed + 1));
62}
63
64char *SString::directWrite(int ensuresize)
65{
66        ensureAllocated(ensuresize);
67        appending = used;
68        return txt;
69}
70
71char *SString::directAppend(int maxappend)
72{
73        ensureAllocated(used + maxappend);
74        appending = used;
75        return txt + appending;
76}
77
78void SString::endWrite(int newlength)
79{
80        if (newlength < 0) newlength = strlen(txt);
81        else
82        {
83                if (newlength >= allocated)
84                {
85                        logMessage("SString", "endWrite", LOG_CRITICAL, "newlength >= allocated");
86                        assert(newlength < allocated);
87                        if (allocated == 0) return;
88                        newlength = allocated - 1;
89                }
90                txt[newlength] = 0;
91        }
92        used = newlength;
93}
94
95void SString::endAppend(int newappend)
96{
97        if (newappend < 0) endWrite(appending + strlen(txt + appending));
98        else endWrite(newappend + appending);
99}
100
101////////////// append /////////////////
102
103void SString::operator+=(const char *s)
104{
105        if (!s) return;
106        int x = strlen(s);
107        if (!x) return;
108        append(s, x);
109}
110
111void SString::append(const char *t, int n)
112{
113        if (!n) return;
114        ensureAllocated(used + n);
115        memmove(txt + used, t, n);
116        used += n;
117        txt[used] = 0;
118}
119
120void SString::operator+=(const SString&s)
121{
122        append(s.c_str(), s.length());
123}
124
125SString SString::operator+(const SString& s) const
126{
127        SString ret;
128        ret.reserve(length() + s.length());
129        ret = *this;
130        ret += s;
131        return ret;
132}
133
134/////////////////////////////
135
136void SString::copyFrom(const char *ch, int chlen)
137{
138        if (!ch) chlen = 0;
139        else if (chlen < 0) chlen = strlen(ch);
140        if (chlen)
141        {
142                ensureAllocated(chlen);
143                memmove(txt, ch, chlen);
144                txt[chlen] = 0;
145                used = chlen;
146        }
147        else
148        {
149                if (txt)
150                {
151                        txt[0] = 0;
152                        used = 0;
153                }
154        }
155}
156
157void SString::operator=(const char *ch)
158{
159        copyFrom(ch);
160}
161
162void SString::operator=(const SString&s)
163{
164        if (&s == this) return;
165        copyFrom(s.c_str(), s.length());
166}
167
168///////////////////////////////////////
169
170SString SString::substr(int begin, int numchars) const
171{
172        if (begin < 0) { numchars += begin; begin = 0; }
173        if (numchars >= (length() - begin)) numchars = length() - begin;
174        if (numchars <= 0) return SString();
175        if (numchars == length()) return *this;
176        return SString((*this)(begin), numchars);
177}
178
179///////////////////////////////////////
180
181bool SString::equals(const SString& s) const
182{
183        if (this == &s) return true;
184        if (length() != s.length()) return false;
185        return strcmp(getPtr(), s.getPtr()) == 0;
186}
187
188///////////////////////////////////////
189
190int SString::indexOf(int character, int start) const
191{
192        const char *found = strchr(getPtr() + start, character);
193        return found ? found - getPtr() : -1;
194}
195
196int SString::indexOf(const char *substring, int start) const
197{
198        const char *found = strstr(getPtr() + start, substring);
199        return found ? found - getPtr() : -1;
200}
201
202int SString::indexOf(const SString & substring, int start) const
203{
204        const char *found = strstr(getPtr() + start, substring.c_str());
205        return found ? found - getPtr() : -1;
206}
207
208bool SString::getNextToken(int& pos, SString &token, char separator) const
209{
210        if (pos >= length()) { token = 0; return false; }
211        int p1 = pos, p2;
212        const char *t1 = getPtr() + pos;
213        const char *t2 = strchr(t1, separator);
214        if (t2) pos = (p2 = (t2 - getPtr())) + 1; else p2 = pos = length();
215        strncpy(token.directWrite(p2 - p1), t1, p2 - p1);
216        token.endWrite(p2 - p1);
217        return true;
218}
219
220bool SString::startsWith(const char *pattern) const
221{
222        const char *t = this->c_str();
223        for (; *pattern; pattern++, t++)
224                if (*t != *pattern) return false;
225        return true;
226}
227
228SString SString::valueOf(int i)
229{
230        return SString::sprintf("%d", i);
231}
232SString SString::valueOf(long i)
233{
234        return SString::sprintf("%d", i);
235}
236SString SString::valueOf(double d)
237{
238        SString tmp;
239        char* here = tmp.directWrite(30);
240        tmp.endWrite(doubleToString(d, -1, here, 30));
241        if ((!strchr(tmp.c_str(), '.')) && (!strchr(tmp.c_str(), 'e'))) tmp += ".0";
242        return tmp;
243}
244SString SString::valueOf(const SString& s)
245{
246        return s;
247}
248
249SString SString::sprintf(const char* format, ...)
250{
251        int n, size = 30;
252        va_list ap;
253
254        SString ret;
255
256#ifdef USE_VSCPRINTF
257        va_start(ap, format);
258        size = _vscprintf(format, ap);
259        va_end(ap);
260#endif
261
262        while (1)
263        {
264                char* p = ret.directWrite(size);
265                assert(p != NULL);
266                size = ret.capacity() + 1;
267                /* Try to print in the allocated space. */
268                va_start(ap, format);
269                n = vsnprintf(p, size, format, ap);
270                va_end(ap);
271                /* If that worked, return the string. */
272
273#ifdef __ANDROID__
274                //Workaround for Android bug. /system/lib64/libc.so? maybe only arm 64-bit? "If an encoding error occurs, a negative number is returned". On some devices keeps returning -1 forever.
275                //https://github.com/android-ndk/ndk/issues/879 but unfortunately during google play tests (Firebase Test Lab) this problem turned out to be not limited to Chinese devices and occurred in Mate 9, Galaxy S9, Pixel, Pixel 2, Moto Z (even with the en_GB locale; the locale is not important but the problem seem to be utf8 non-ascii chars in the format string).
276                if (n < 0 && size >= (1 << 24)) //wants more than 16M
277                {
278                        p[size - 1] = 0; //just to ensure there is at least some ending \0 in memory... who knows what buggy vsnprintf() did.
279                        __android_log_print(ANDROID_LOG_ERROR, LOG_APP_NAME, "Giving up due to Android bug: vsnprintf() wants more than %d bytes, it used %zu bytes, for format='%s'", size, strlen(p), format);
280                        //in my tests, it always used 0 bytes, so it produced a 0-length string: ""
281                        va_start(ap, format);
282                        n = vsnprintf(p, size, format, ap); //hoping 16M is enough
283                        va_end(ap);
284                        __android_log_print(ANDROID_LOG_INFO, LOG_APP_NAME, "Fallback to vsprintf() produced string: '%s'", p);
285                        if (n < 0) //vsprintf was also buggy. If we were strict, we should abort the app now.
286                        {
287                                strcpy(p, "[STR_ERR] "); //a special prefix just to indicate the returned string is incorrect
288                                strcat(p, format); //append and return the original formatting string
289                                __android_log_print(ANDROID_LOG_ERROR, LOG_APP_NAME, "vsprintf() also failed, using the incorrect resulting string: '%s'", p);
290                        }
291                        n = strlen(p); //pretend vsnprintf() or vsprintf() was OK to exit the endless loop
292        }
293#endif
294
295                if (n > -1 && n < size)
296                {
297                        ret.endWrite(n);
298                        return ret;
299                }
300                /* Else try again with more space. */
301#ifdef VSNPRINTF_RETURNS_REQUIRED_SIZE
302                if (n > -1)    /* glibc 2.1 */
303                        size = n; /* precisely what is needed */
304                else           /* glibc 2.0 */
305#endif
306                        size *= 2;  /* twice the old size */
307                }
308}
309
310SString &SString::empty()
311{
312        static SString empty;
313        return empty;
314}
Note: See TracBrowser for help on using the repository browser.