source: cpp/frams/util/sstring.cpp @ 977

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

Increased SString and std::string compatibility: introduced length(), size(), and capacity(), and removed legacy methods that have std::string equivalents

  • Property svn:eol-style set to native
File size: 10.9 KB
Line 
1// This file is a part of Framsticks SDK.  http://www.framsticks.com/
2// Copyright (C) 1999-2020  Maciej Komosinski and Szymon Ulatowski.
3// See LICENSE.txt for details.
4
5#include "sstring.h"
6#include <common/nonstd.h> //to be sure the vsnprintf-related stuff gets included
7
8#ifdef SSTRING_SIMPLE
9
10// simple sstring implementation using direct character arrays
11// - duplicate = copy all characters
12// - no mutex needed
13
14#include "sstring-simple.cpp"
15
16#else
17///////////////////////////////////////////////////////////////////////////
18// old sstring implementation using SBuf references
19// - duplicate = copy buffer pointer
20// - mutex required to be thread safe
21
22#include <common/nonstd_stl.h>
23#include "extvalue.h"
24#include <assert.h>
25#include <common/nonstd_math.h>
26
27#ifdef MULTITHREADED
28#include <pthread.h>
29static pthread_mutex_t sstring_ref_lock = PTHREAD_MUTEX_INITIALIZER;
30#define REF_LOCK pthread_mutex_lock(&sstring_ref_lock);
31#define REF_UNLOCK pthread_mutex_unlock(&sstring_ref_lock)
32#else
33#define REF_LOCK
34#define REF_UNLOCK
35#endif
36
37static int guessMemSize(int request)
38{
39        return request + min(request / 2, 10000) + 8;
40}
41
42SBuf::SBuf()
43{
44        txt = (char*)"";
45        size = used = 0;
46        refcount = 1;
47}
48
49SBuf::SBuf(int initsize)
50{
51        size = guessMemSize(initsize);
52        if (size > 0) { txt = (char*)malloc(size + 1); txt[0] = 0; }
53        else    txt = (char*)"";
54        used = 0;
55        refcount = 1;
56}
57
58SBuf::~SBuf()
59{
60        freeBuf();
61}
62
63void SBuf::initEmpty()
64{
65        txt = (char*)"";
66        used = size = 0;
67        refcount = 1;
68}
69
70void SBuf::freeBuf()
71{
72        if (!size) return;
73        free(txt); used = 0;
74}
75
76void SBuf::copyFrom(const char *ch, int chlen)
77{
78        if (chlen == -1) chlen = strlen(ch);
79        if (chlen > 0)
80        {
81                if (chlen < size)
82                {
83                        memmove(txt, ch, chlen);
84                }
85                else
86                {
87                        size = guessMemSize(chlen);
88                        char *newtxt = (char*)malloc(size + 1);
89                        memcpy(newtxt, ch, chlen);
90                        free(txt);
91                        txt = newtxt;
92                }
93        }
94        txt[chlen] = 0;
95        used = chlen;
96}
97
98void SBuf::append(const char *ch, int chlen)
99{ // doesn't check anything!
100        memmove(txt + used, ch, chlen);
101        used += chlen;
102        txt[used] = 0;
103}
104
105void SBuf::ensureSize(int needed)
106{
107        if (size >= needed) return;
108        needed = guessMemSize(needed);
109        txt = (char*)realloc(txt, needed + 1);
110        size = needed;
111}
112
113/////////////////////////////////////////////
114
115SString::SString()
116{
117        initEmpty();
118}
119
120SString::~SString()
121{
122        REF_LOCK;
123        detach();
124        REF_UNLOCK;
125}
126
127SString::SString(const char *t, int t_len)
128{
129        initEmpty();
130        if (!t) return;
131        copyFrom(t, t_len);
132}
133
134SString::SString(SString&& from)
135{
136        buf = from.buf;
137        from.buf = &SBuf::empty();
138}
139
140SString::SString(const SString &from)
141{
142        if (from.buf == &SBuf::empty())
143                buf = &SBuf::empty();
144        else
145        {
146                REF_LOCK;
147                buf = from.buf;
148                if (buf->size)
149                        buf->refcount++;
150                REF_UNLOCK;
151        }
152}
153
154SString::SString(char in)
155{
156        initEmpty();
157        copyFrom(&in, 1);
158}
159
160void SString::initEmpty()
161{
162        buf = &SBuf::empty();
163}
164
165void SString::detachEmpty(int ensuresize)
166{
167        if (buf == &SBuf::empty()) { buf = new SBuf(ensuresize); return; }
168        if (buf->refcount < 2) buf->ensureSize(ensuresize);
169        else
170        {
171                buf->refcount--;
172                buf = new SBuf(ensuresize);
173        }
174}
175
176void SString::detach()
177{
178        if (buf == &SBuf::empty()) return;
179        if (!--buf->refcount) delete buf;
180}
181
182void SString::detachCopy(int ensuresize)
183{
184        if (buf == &SBuf::empty()) { buf = new SBuf(ensuresize); return; }
185        if (buf->refcount < 2)
186        {
187                buf->ensureSize(ensuresize);
188                return;
189        }
190        buf->refcount--;
191        SBuf *newbuf = new SBuf(ensuresize);
192        newbuf->copyFrom(buf->txt, min(ensuresize, buf->used));
193        buf = newbuf;
194}
195
196char *SString::directWrite(int ensuresize)
197{
198        if (ensuresize < 0) ensuresize = length();
199        REF_LOCK;
200        detachCopy(ensuresize);
201        REF_UNLOCK;
202        appending = buf->used;
203        return buf->txt;
204}
205
206/*
207char *SString::directWrite()
208{
209return directWrite(buf->used);
210}
211*/
212char *SString::directAppend(int maxappend)
213{
214        REF_LOCK;
215        detachCopy(buf->used + maxappend);
216        REF_UNLOCK;
217        appending = buf->used;
218        return buf->txt + appending;
219}
220
221void SString::endWrite(int newlength)
222{
223        if (newlength < 0) newlength = strlen(buf->txt);
224        else buf->txt[newlength] = 0;
225        buf->used = newlength;
226}
227
228void SString::endAppend(int newappend)
229{
230        if (newappend < 0) newappend = strlen(buf->txt + appending);
231        else buf->txt[appending + newappend] = 0;
232        buf->used = appending + newappend;
233}
234
235////////////// append /////////////////
236
237void SString::operator+=(const char *s)
238{
239        if (!s) return;
240        int x = strlen(s);
241        if (!x) return;
242        append(s, x);
243}
244
245void SString::append(const char *txt, int count)
246{
247        if (!count) return;
248        REF_LOCK;
249        detachCopy(buf->used + count);
250        REF_UNLOCK;
251        buf->append(txt, count);
252}
253
254void SString::operator+=(const SString&s)
255{
256        append(s.c_str(), s.length());
257}
258
259SString SString::operator+(const SString& s) const
260{
261        SString ret(*this);
262        ret += s;
263        return ret;
264}
265
266/////////////////////////////
267
268void SString::copyFrom(const char *ch, int chlen)
269{
270        if (!ch) chlen = 0;
271        else if (chlen < 0) chlen = strlen(ch);
272        REF_LOCK;
273        detachEmpty(chlen);
274        REF_UNLOCK;
275        memmove(buf->txt, ch, chlen);
276        buf->txt[chlen] = 0; buf->used = chlen;
277}
278
279void SString::operator=(const char *ch)
280{
281        copyFrom(ch);
282}
283
284void SString::operator=(const SString&s)
285{
286        if (s.buf == buf) return;
287        REF_LOCK;
288        detach();
289        buf = s.buf;
290        if (buf->size) buf->refcount++;
291        REF_UNLOCK;
292}
293///////////////////////////////////////
294
295SString SString::substr(int begin, int numchars) const
296{
297        if (begin < 0) { numchars += begin; begin = 0; }
298        if (numchars >= (length() - begin)) numchars = length() - begin;
299        if (numchars <= 0) return SString();
300        if (numchars == length()) return *this;
301        return SString((*this)(begin), numchars);
302}
303
304///////////////////////////////////////
305
306bool SString::equals(const SString& s) const
307{
308        if (s.buf == buf) return true;
309        return strcmp(buf->txt, s.buf->txt) == 0;
310}
311
312///////////////////////////////////////
313
314int SString::indexOf(int character, int start) const
315{
316        const char *found = strchr(buf->txt + start, character);
317        return found ? found - buf->txt : -1;
318}
319
320int SString::indexOf(const char *substring, int start) const
321{
322        char *found = strstr(buf->txt + start, substring);
323        return found ? found - buf->txt : -1;
324}
325
326int SString::indexOf(const SString & substring, int start) const
327{
328        char *found = strstr(buf->txt + start, substring.c_str());
329        return found ? found - buf->txt : -1;
330}
331
332bool SString::getNextToken(int& pos, SString &token, char separator) const
333{
334        if (pos >= length()) { token = 0; return false; }
335        int p1 = pos, p2;
336        const char *t1 = buf->txt + pos;
337        const char *t2 = strchr(t1, separator);
338        if (t2) pos = (p2 = (t2 - buf->txt)) + 1; else p2 = pos = length();
339        strncpy(token.directWrite(p2 - p1), t1, p2 - p1);
340        token.endWrite(p2 - p1);
341        return true;
342}
343
344bool SString::startsWith(const char *pattern) const
345{
346        const char *t = this->c_str();
347        for (; *pattern; pattern++, t++)
348                if (*t != *pattern) return false;
349        return true;
350}
351
352SString SString::valueOf(int i)
353{
354        return SString::sprintf("%d", i);
355}
356SString SString::valueOf(long i)
357{
358        return SString::sprintf("%d", i);
359}
360SString SString::valueOf(double d)
361{
362        SString tmp;
363        char* here = tmp.directWrite(30);
364        tmp.endWrite(doubleToString(d, -1, here, 30));
365        if ((!strchr(tmp.c_str(), '.')) && (!strchr(tmp.c_str(), 'e'))) tmp += ".0";
366        return tmp;
367}
368SString SString::valueOf(const SString& s)
369{
370        return s;
371}
372
373#if 0 //testing _vscprintf
374#define USE_VSCPRINTF
375int _vscprintf(const char *format, va_list argptr)
376{
377        return vsnprintf("", 0, format, argptr);
378}
379#endif
380
381SString SString::sprintf(const char* format, ...)
382{
383        int n, size = 30;
384        va_list ap;
385
386        SString ret;
387
388#ifdef USE_VSCPRINTF
389        va_start(ap, format);
390        size = _vscprintf(format, ap);
391        va_end(ap);
392#endif
393
394        while (1)
395        {
396                char* p = ret.directWrite(size);
397                assert(p != NULL);
398                size = ret.capacity() + 1;
399                /* Try to print in the allocated space. */
400                va_start(ap, format);
401                n = vsnprintf(p, size, format, ap);
402                va_end(ap);
403                /* If that worked, return the string. */
404
405#ifdef __ANDROID__
406                //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.
407                //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).
408                if (n < 0 && size >= (1 << 24)) //wants more than 16M
409                {
410                        buf[size - 1] = 0; //just to ensure there is at least some ending \0 in memory... who knows what buggy vsnprintf() did.
411                        __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);
412                        //in my tests, it always used 0 bytes, so it produced a 0-length string: ""
413                        va_copy(ap_copy, ap);
414                        n = vsprintf(buf, format, ap_copy); //hoping 16M is enough
415                        va_end(ap_copy);
416                        __android_log_print(ANDROID_LOG_INFO, LOG_APP_NAME, "Fallback to vsprintf() produced string: '%s'", buf);
417                        if (n < 0) //vsprintf was also buggy. If we were strict, we should abort the app now.
418                        {
419                                strcpy(buf, "[STR_ERR] "); //a special prefix just to indicate the returned string is incorrect
420                                strcat(buf, format); //append and return the original formatting string
421                                __android_log_print(ANDROID_LOG_ERROR, LOG_APP_NAME, "vsprintf() also failed, using the incorrect resulting string: '%s'", buf);
422                        }
423                        n = strlen(buf); //pretend vsnprintf() or vsprintf() was OK to exit the endless loop
424                }
425#endif
426
427                if (n > -1 && n < size)
428                {
429                        ret.endWrite(n);
430                        return ret;
431                }
432                /* Else try again with more space. */
433#ifdef VSNPRINTF_RETURNS_REQUIRED_SIZE
434                if (n > -1)    /* glibc 2.1 */
435                        size = n; /* precisely what is needed */
436                else           /* glibc 2.0 */
437#endif
438                        size *= 2;  /* twice the old size */
439        }
440}
441
442SString &SString::empty()
443{
444        static SString empty;
445        return empty;
446}
447
448SBuf &SBuf::empty()
449{
450        static SBuf empty;
451        return empty;
452}
453
454#endif //#ifdef SSTRING_SIMPLE
455
456//////////////////////////////////////////////////
457// to be moved somewhere else?
458// public domain source: http://isthe.com/chongo/src/fnv
459typedef uint32_t Fnv32_t;
460
461#define FNV_32_PRIME ((Fnv32_t)0x01000193)
462#define FNV1_32_INIT ((Fnv32_t)0x811c9dc5)
463#define FNV1_32A_INIT FNV1_32_INIT
464
465Fnv32_t fnv_32a_buf(void *buf, size_t len, Fnv32_t hval)
466{
467        unsigned char *bp = (unsigned char *)buf;       /* start of buffer */
468        unsigned char *be = bp + len;           /* beyond end of buffer */
469
470        while (bp < be) {
471
472                /* xor the bottom with the current octet */
473                hval ^= (Fnv32_t)*bp++;
474
475                /* multiply by the 32 bit FNV magic prime mod 2^32 */
476#if defined(NO_FNV_GCC_OPTIMIZATION)
477                hval *= FNV_32_PRIME;
478#else
479                hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24);
480#endif
481
482        }
483
484        /* return our new hash value */
485        return hval;
486}
487//////////////////////////////////////////////////
488
489#ifdef SSTRING_SIMPLE
490uint32_t SString::hash() const
491{
492        return fnv_32a_buf(txt, used, FNV1_32A_INIT);
493}
494#else
495uint32_t SBuf::hash() const
496{
497        return fnv_32a_buf(txt, used, FNV1_32A_INIT);
498}
499#endif
Note: See TracBrowser for help on using the repository browser.