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

Last change on this file since 996 was 996, checked in by Maciej Komosinski, 8 months ago

Added helpful stdout messages before crash for cases when assert() would not work/print anything

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