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

Last change on this file since 826 was 826, checked in by Maciej Komosinski, 19 months 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.