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

Last change on this file since 955 was 955, checked in by Maciej Komosinski, 4 years 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
RevLine 
[286]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.
[109]4
5#include "sstring.h"
[222]6#include <common/nonstd.h> //to be sure the vsnprintf-related stuff gets included
[109]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>
[826]25#ifdef USE_PRINTFLOAT_DRAGON4
26#include <PrintFloat/PrintFloat.h>
27#endif
[109]28
29#ifdef MULTITHREADED
30#include <pthread.h>
[793]31static pthread_mutex_t sstring_ref_lock = PTHREAD_MUTEX_INITIALIZER;
[109]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{
[793]41        return request + min(request / 2, 10000) + 8;
[109]42}
43
44SBuf::SBuf()
45{
[793]46        txt = (char*)"";
47        size = used = 0;
48        refcount = 1;
[109]49}
50
51SBuf::SBuf(int initsize)
52{
[793]53        size = guessMemSize(initsize);
[889]54        if (size > 0) { txt = (char*)malloc(size + 1); txt[0] = 0; }
[793]55        else    txt = (char*)"";
56        used = 0;
57        refcount = 1;
[109]58}
59
60SBuf::~SBuf()
61{
[793]62        freeBuf();
[109]63}
64
65void SBuf::initEmpty()
66{
[793]67        txt = (char*)"";
68        used = size = 0;
69        refcount = 1;
[109]70}
71
72void SBuf::freeBuf()
73{
[793]74        if (!size) return;
75        free(txt); used = 0;
[109]76}
77
[793]78void SBuf::copyFrom(const char *ch, int chlen)
[109]79{
[793]80        if (chlen == -1) chlen = strlen(ch);
81        if (chlen > 0)
[109]82        {
[793]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                }
[109]95        }
[793]96        txt[chlen] = 0;
97        used = chlen;
[109]98}
99
[793]100void SBuf::append(const char *ch, int chlen)
[109]101{ // doesn't check anything!
[793]102        memmove(txt + used, ch, chlen);
103        used += chlen;
104        txt[used] = 0;
[109]105}
106
107void SBuf::ensureSize(int needed)
108{
[793]109        if (size >= needed) return;
110        needed = guessMemSize(needed);
111        txt = (char*)realloc(txt, needed + 1);
112        size = needed;
[109]113}
114
115/////////////////////////////////////////////
116
117SString::SString()
118{
[793]119        initEmpty();
[109]120}
121
122SString::~SString()
123{
[793]124        REF_LOCK;
125        detach();
126        REF_UNLOCK;
[109]127}
128
[793]129SString::SString(const char *t, int t_len)
[109]130{
[793]131        initEmpty();
132        if (!t) return;
133        copyFrom(t, t_len);
[109]134}
135
[367]136SString::SString(SString&& from)
137{
[793]138        buf = from.buf;
139        from.buf = &SBuf::empty();
[367]140}
141
[109]142SString::SString(const SString &from)
143{
[793]144        if (from.buf == &SBuf::empty())
145                buf = &SBuf::empty();
146        else
[109]147        {
[793]148                REF_LOCK;
149                buf = from.buf;
150                if (buf->size)
151                        buf->refcount++;
152                REF_UNLOCK;
[109]153        }
154}
155
[955]156SString::SString(char in)
157{
158        initEmpty();
159        copyFrom(&in, 1);
160}
161
[109]162void SString::initEmpty()
163{
[793]164        buf = &SBuf::empty();
[109]165}
166
167void SString::memoryHint(int howbig)
168{
[793]169        detachCopy(howbig);
[109]170}
[793]171
[109]172void SString::detachEmpty(int ensuresize)
173{
[793]174        if (buf == &SBuf::empty()) { buf = new SBuf(ensuresize); return; }
175        if (buf->refcount < 2) buf->ensureSize(ensuresize);
176        else
[109]177        {
[793]178                buf->refcount--;
179                buf = new SBuf(ensuresize);
[109]180        }
181}
182
183void SString::detach()
184{
[793]185        if (buf == &SBuf::empty()) return;
186        if (!--buf->refcount) delete buf;
[109]187}
188
189void SString::detachCopy(int ensuresize)
190{
[793]191        if (buf == &SBuf::empty()) { buf = new SBuf(ensuresize); return; }
192        if (buf->refcount < 2)
[109]193        {
[793]194                buf->ensureSize(ensuresize);
195                return;
[109]196        }
[793]197        buf->refcount--;
198        SBuf *newbuf = new SBuf(ensuresize);
199        newbuf->copyFrom(buf->txt, min(ensuresize, buf->used));
200        buf = newbuf;
[109]201}
202
203char *SString::directWrite(int ensuresize)
204{
[793]205        if (ensuresize < 0) ensuresize = len();
206        REF_LOCK;
207        detachCopy(ensuresize);
208        REF_UNLOCK;
209        appending = buf->used;
210        return buf->txt;
[109]211}
212
213/*
214char *SString::directWrite()
215{
216return directWrite(buf->used);
217}
218*/
219char *SString::directAppend(int maxappend)
220{
[793]221        REF_LOCK;
222        detachCopy(buf->used + maxappend);
223        REF_UNLOCK;
224        appending = buf->used;
225        return buf->txt + appending;
[109]226}
227
228void SString::endWrite(int newlength)
229{
[793]230        if (newlength < 0) newlength = strlen(buf->txt);
231        else buf->txt[newlength] = 0;
232        buf->used = newlength;
[109]233}
234
235void SString::endAppend(int newappend)
236{
[793]237        if (newappend < 0) newappend = strlen(buf->txt + appending);
238        else buf->txt[appending + newappend] = 0;
239        buf->used = appending + newappend;
[109]240}
241
242////////////// append /////////////////
243
244void SString::operator+=(const char *s)
245{
[793]246        if (!s) return;
247        int x = strlen(s);
248        if (!x) return;
249        append(s, x);
[109]250}
251
[793]252void SString::append(const char *txt, int count)
[109]253{
[793]254        if (!count) return;
255        REF_LOCK;
256        detachCopy(buf->used + count);
257        REF_UNLOCK;
258        buf->append(txt, count);
[109]259}
260
261void SString::operator+=(const SString&s)
262{
[793]263        append(s.c_str(), s.len());
[109]264}
265
266SString SString::operator+(const SString& s) const
267{
[793]268        SString ret(*this);
269        ret += s;
270        return ret;
[109]271}
272
273/////////////////////////////
274
[793]275void SString::copyFrom(const char *ch, int chlen)
[109]276{
[793]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;
[109]284}
285
286void SString::operator=(const char *ch)
287{
[793]288        copyFrom(ch);
[109]289}
290
291void SString::operator=(const SString&s)
292{
[793]293        if (s.buf == buf) return;
294        REF_LOCK;
295        detach();
296        buf = s.buf;
297        if (buf->size) buf->refcount++;
298        REF_UNLOCK;
[109]299}
300///////////////////////////////////////
301
302SString SString::substr(int begin, int length) const
303{
[793]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);
[109]309}
310
311///////////////////////////////////////
312
[247]313bool SString::equals(const SString& s) const
[109]314{
[793]315        if (s.buf == buf) return true;
316        return strcmp(buf->txt, s.buf->txt) == 0;
[109]317}
318
319///////////////////////////////////////
320
[793]321int SString::indexOf(int character, int start) const
[109]322{
[793]323        const char *found = strchr(buf->txt + start, character);
324        return found ? found - buf->txt : -1;
[109]325}
326
[793]327int SString::indexOf(const char *substring, int start) const
[109]328{
[793]329        char *found = strstr(buf->txt + start, substring);
330        return found ? found - buf->txt : -1;
[109]331}
332
[793]333int SString::indexOf(const SString & substring, int start) const
[109]334{
[793]335        char *found = strstr(buf->txt + start, substring.c_str());
336        return found ? found - buf->txt : -1;
[109]337}
338
[793]339bool SString::getNextToken(int& pos, SString &token, char separator) const
[109]340{
[793]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;
[109]349}
350
[395]351bool SString::startsWith(const char *pattern) const
[109]352{
[793]353        const char *t = this->c_str();
354        for (; *pattern; pattern++, t++)
355                if (*t != *pattern) return false;
356        return true;
[109]357}
358
359SString SString::valueOf(int i)
360{
[793]361        return SString::sprintf("%d", i);
[109]362}
363SString SString::valueOf(long i)
364{
[793]365        return SString::sprintf("%d", i);
[109]366}
367SString SString::valueOf(double d)
368{
[826]369#ifdef USE_PRINTFLOAT_DRAGON4
370        SString tmp;
[889]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/
[826]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
[793]379        if ((!strchr(tmp.c_str(), '.')) && (!strchr(tmp.c_str(), 'e'))) tmp += ".0";
380        return tmp;
[109]381}
382SString SString::valueOf(const SString& s)
383{
[793]384        return s;
[109]385}
386
387#if 0 //testing _vscprintf
388#define USE_VSCPRINTF
[793]389int _vscprintf(const char *format, va_list argptr)
[109]390{
[793]391        return vsnprintf("", 0, format, argptr);
[109]392}
393#endif
394
395SString SString::sprintf(const char* format, ...)
396{
[793]397        int n, size = 30;
398        va_list ap;
[109]399
[793]400        SString ret;
[109]401
402#ifdef USE_VSCPRINTF
[793]403        va_start(ap, format);
404        size = _vscprintf(format, ap);
405        va_end(ap);
[109]406#endif
407
[793]408        while (1)
[109]409        {
[793]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. */
[897]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
[793]441                if (n > -1 && n < size)
[109]442                {
[793]443                        ret.endWrite(n);
444                        return ret;
[109]445                }
[793]446                /* Else try again with more space. */
[109]447#ifdef VSNPRINTF_RETURNS_REQUIRED_SIZE
[793]448                if (n > -1)    /* glibc 2.1 */
449                        size = n; /* precisely what is needed */
450                else           /* glibc 2.0 */
[109]451#endif
[793]452                        size *= 2;  /* twice the old size */
[109]453        }
454}
455
456SString &SString::empty()
457{
[793]458        static SString empty;
459        return empty;
[109]460}
461
462SBuf &SBuf::empty()
463{
[793]464        static SBuf empty;
465        return empty;
[109]466}
467
468#endif //#ifdef SSTRING_SIMPLE
[347]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{
[793]481        unsigned char *bp = (unsigned char *)buf;       /* start of buffer */
482        unsigned char *be = bp + len;           /* beyond end of buffer */
[347]483
[793]484        while (bp < be) {
[347]485
[793]486                /* xor the bottom with the current octet */
487                hval ^= (Fnv32_t)*bp++;
[347]488
[793]489                /* multiply by the 32 bit FNV magic prime mod 2^32 */
[347]490#if defined(NO_FNV_GCC_OPTIMIZATION)
[793]491                hval *= FNV_32_PRIME;
[347]492#else
[793]493                hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24);
[347]494#endif
495
[793]496        }
[347]497
[793]498        /* return our new hash value */
499        return hval;
[347]500}
501//////////////////////////////////////////////////
502
503#ifdef SSTRING_SIMPLE
504uint32_t SString::hash() const
505{
[793]506        return fnv_32a_buf(txt, used, FNV1_32A_INIT);
[347]507}
508#else
509uint32_t SBuf::hash() const
510{
[793]511        return fnv_32a_buf(txt, used, FNV1_32A_INIT);
[347]512}
513#endif
Note: See TracBrowser for help on using the repository browser.