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