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

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

SString::endWrite()/endAppend() now safe

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