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

Last change on this file since 875 was 826, checked in by Maciej Komosinski, 6 years ago

Used the Dragon4 algorithm to print floating point values with full precision instead of "%.17g"

  • Property svn:eol-style set to native
File size: 9.6 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(int x)
130{
131        buf = new SBuf(x);
132}
133
134SString::SString(const char *t, int t_len)
135{
136        initEmpty();
137        if (!t) return;
138        copyFrom(t, t_len);
139}
140
141SString::SString(SString&& from)
142{
143        buf = from.buf;
144        from.buf = &SBuf::empty();
145}
146
147SString::SString(const SString &from)
148{
149        if (from.buf == &SBuf::empty())
150                buf = &SBuf::empty();
151        else
152        {
153                REF_LOCK;
154                buf = from.buf;
155                if (buf->size)
156                        buf->refcount++;
157                REF_UNLOCK;
158        }
159}
160
161void SString::initEmpty()
162{
163        buf = &SBuf::empty();
164}
165
166void SString::memoryHint(int howbig)
167{
168        detachCopy(howbig);
169}
170
171void SString::detachEmpty(int ensuresize)
172{
173        if (buf == &SBuf::empty()) { buf = new SBuf(ensuresize); return; }
174        if (buf->refcount < 2) buf->ensureSize(ensuresize);
175        else
176        {
177                buf->refcount--;
178                buf = new SBuf(ensuresize);
179        }
180}
181
182void SString::detach()
183{
184        if (buf == &SBuf::empty()) return;
185        if (!--buf->refcount) delete buf;
186}
187
188void SString::detachCopy(int ensuresize)
189{
190        if (buf == &SBuf::empty()) { buf = new SBuf(ensuresize); return; }
191        if (buf->refcount < 2)
192        {
193                buf->ensureSize(ensuresize);
194                return;
195        }
196        buf->refcount--;
197        SBuf *newbuf = new SBuf(ensuresize);
198        newbuf->copyFrom(buf->txt, min(ensuresize, buf->used));
199        buf = newbuf;
200}
201
202char *SString::directWrite(int ensuresize)
203{
204        if (ensuresize < 0) ensuresize = len();
205        REF_LOCK;
206        detachCopy(ensuresize);
207        REF_UNLOCK;
208        appending = buf->used;
209        return buf->txt;
210}
211
212/*
213char *SString::directWrite()
214{
215return directWrite(buf->used);
216}
217*/
218char *SString::directAppend(int maxappend)
219{
220        REF_LOCK;
221        detachCopy(buf->used + maxappend);
222        REF_UNLOCK;
223        appending = buf->used;
224        return buf->txt + appending;
225}
226
227void SString::endWrite(int newlength)
228{
229        if (newlength < 0) newlength = strlen(buf->txt);
230        else buf->txt[newlength] = 0;
231        buf->used = newlength;
232}
233
234void SString::endAppend(int newappend)
235{
236        if (newappend < 0) newappend = strlen(buf->txt + appending);
237        else buf->txt[appending + newappend] = 0;
238        buf->used = appending + newappend;
239}
240
241////////////// append /////////////////
242
243void SString::operator+=(const char *s)
244{
245        if (!s) return;
246        int x = strlen(s);
247        if (!x) return;
248        append(s, x);
249}
250
251void SString::append(const char *txt, int count)
252{
253        if (!count) return;
254        REF_LOCK;
255        detachCopy(buf->used + count);
256        REF_UNLOCK;
257        buf->append(txt, count);
258}
259
260void SString::operator+=(const SString&s)
261{
262        append(s.c_str(), s.len());
263}
264
265SString SString::operator+(const SString& s) const
266{
267        SString ret(*this);
268        ret += s;
269        return ret;
270}
271
272/////////////////////////////
273
274void SString::copyFrom(const char *ch, int chlen)
275{
276        if (!ch) chlen = 0;
277        else if (chlen < 0) chlen = strlen(ch);
278        REF_LOCK;
279        detachEmpty(chlen);
280        REF_UNLOCK;
281        memmove(buf->txt, ch, chlen);
282        buf->txt[chlen] = 0; buf->used = chlen;
283}
284
285void SString::operator=(const char *ch)
286{
287        copyFrom(ch);
288}
289
290void SString::operator=(const SString&s)
291{
292        if (s.buf == buf) return;
293        REF_LOCK;
294        detach();
295        buf = s.buf;
296        if (buf->size) buf->refcount++;
297        REF_UNLOCK;
298}
299///////////////////////////////////////
300
301SString SString::substr(int begin, int length) const
302{
303        if (begin < 0) { length += begin; begin = 0; }
304        if (length >= (len() - begin)) length = len() - begin;
305        if (length <= 0) return SString();
306        if (length == len()) return *this;
307        return SString((*this)(begin), length);
308}
309
310///////////////////////////////////////
311
312bool SString::equals(const SString& s) const
313{
314        if (s.buf == buf) return true;
315        return strcmp(buf->txt, s.buf->txt) == 0;
316}
317
318///////////////////////////////////////
319
320int SString::indexOf(int character, int start) const
321{
322        const char *found = strchr(buf->txt + start, character);
323        return found ? found - buf->txt : -1;
324}
325
326int SString::indexOf(const char *substring, int start) const
327{
328        char *found = strstr(buf->txt + start, substring);
329        return found ? found - buf->txt : -1;
330}
331
332int SString::indexOf(const SString & substring, int start) const
333{
334        char *found = strstr(buf->txt + start, substring.c_str());
335        return found ? found - buf->txt : -1;
336}
337
338bool SString::getNextToken(int& pos, SString &token, char separator) const
339{
340        if (pos >= len()) { token = 0; return false; }
341        int p1 = pos, p2;
342        const char *t1 = buf->txt + pos;
343        const char *t2 = strchr(t1, separator);
344        if (t2) pos = (p2 = (t2 - buf->txt)) + 1; else p2 = pos = len();
345        strncpy(token.directWrite(p2 - p1), t1, p2 - p1);
346        token.endWrite(p2 - p1);
347        return true;
348}
349
350bool SString::startsWith(const char *pattern) const
351{
352        const char *t = this->c_str();
353        for (; *pattern; pattern++, t++)
354                if (*t != *pattern) return false;
355        return true;
356}
357
358SString SString::valueOf(int i)
359{
360        return SString::sprintf("%d", i);
361}
362SString SString::valueOf(long i)
363{
364        return SString::sprintf("%d", i);
365}
366SString SString::valueOf(double d)
367{
368#ifdef USE_PRINTFLOAT_DRAGON4
369        SString tmp;
370        char* here=tmp.directWrite(30);
371        tmp.endWrite(PrintFloat64(here,30,d,
372                                  ((d<-1e17)||(d>1e17)||((d<1e-4)&&(d>-1e-4)&&(d!=0.0)))
373                                  ? PrintFloatFormat_Scientific : PrintFloatFormat_Positional,
374                                  -1));//http://www.ryanjuckett.com/programming/printing-floating-point-numbers/
375#else
376        SString tmp = SString::sprintf("%.17g", d); //https://stackoverflow.com/questions/16839658/printf-width-specifier-to-maintain-precision-of-floating-point-value
377#endif
378        if ((!strchr(tmp.c_str(), '.')) && (!strchr(tmp.c_str(), 'e'))) tmp += ".0";
379        return tmp;
380}
381SString SString::valueOf(const SString& s)
382{
383        return s;
384}
385
386#if 0 //testing _vscprintf
387#define USE_VSCPRINTF
388int _vscprintf(const char *format, va_list argptr)
389{
390        return vsnprintf("", 0, format, argptr);
391}
392#endif
393
394SString SString::sprintf(const char* format, ...)
395{
396        int n, size = 30;
397        va_list ap;
398
399        SString ret;
400
401#ifdef USE_VSCPRINTF
402        va_start(ap, format);
403        size = _vscprintf(format, ap);
404        va_end(ap);
405#endif
406
407        while (1)
408        {
409                char* p = ret.directWrite(size);
410                assert(p != NULL);
411                size = ret.directMaxLen() + 1;
412                /* Try to print in the allocated space. */
413                va_start(ap, format);
414                n = vsnprintf(p, size, format, ap);
415                va_end(ap);
416                /* If that worked, return the string. */
417                if (n > -1 && n < size)
418                {
419                        ret.endWrite(n);
420                        return ret;
421                }
422                /* Else try again with more space. */
423#ifdef VSNPRINTF_RETURNS_REQUIRED_SIZE
424                if (n > -1)    /* glibc 2.1 */
425                        size = n; /* precisely what is needed */
426                else           /* glibc 2.0 */
427#endif
428                        size *= 2;  /* twice the old size */
429        }
430}
431
432SString &SString::empty()
433{
434        static SString empty;
435        return empty;
436}
437
438SBuf &SBuf::empty()
439{
440        static SBuf empty;
441        return empty;
442}
443
444#endif //#ifdef SSTRING_SIMPLE
445
446//////////////////////////////////////////////////
447// to be moved somewhere else?
448// public domain source: http://isthe.com/chongo/src/fnv
449typedef uint32_t Fnv32_t;
450
451#define FNV_32_PRIME ((Fnv32_t)0x01000193)
452#define FNV1_32_INIT ((Fnv32_t)0x811c9dc5)
453#define FNV1_32A_INIT FNV1_32_INIT
454
455Fnv32_t fnv_32a_buf(void *buf, size_t len, Fnv32_t hval)
456{
457        unsigned char *bp = (unsigned char *)buf;       /* start of buffer */
458        unsigned char *be = bp + len;           /* beyond end of buffer */
459
460        while (bp < be) {
461
462                /* xor the bottom with the current octet */
463                hval ^= (Fnv32_t)*bp++;
464
465                /* multiply by the 32 bit FNV magic prime mod 2^32 */
466#if defined(NO_FNV_GCC_OPTIMIZATION)
467                hval *= FNV_32_PRIME;
468#else
469                hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24);
470#endif
471
472        }
473
474        /* return our new hash value */
475        return hval;
476}
477//////////////////////////////////////////////////
478
479#ifdef SSTRING_SIMPLE
480uint32_t SString::hash() const
481{
482        return fnv_32a_buf(txt, used, FNV1_32A_INIT);
483}
484#else
485uint32_t SBuf::hash() const
486{
487        return fnv_32a_buf(txt, used, FNV1_32A_INIT);
488}
489#endif
Note: See TracBrowser for help on using the repository browser.