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

Last change on this file since 1040 was 1040, checked in by Maciej Komosinski, 3 months ago

Follow-up to r897: A workaround for Android bug in vsnprintf() and vsprintf() in SString, https://github.com/android-ndk/ndk/issues/879

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