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

Last change on this file since 906 was 897, checked in by Maciej Komosinski, 5 years ago

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