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

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

Genetic format ID becomes a string (no longer limited to a single character)

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