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

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

Added functions to properly round floating point values to specified precision

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