// This file is a part of the Framsticks GDK. // Copyright (C) 1999-2014 Maciej Komosinski and Szymon Ulatowski. See LICENSE.txt for details. // Refer to http://www.framsticks.com/ for further information. #include "sstring.h" #ifdef SSTRING_SIMPLE // simple sstring implementation using direct character arrays // - duplicate = copy all characters // - no mutex needed #include "sstring-simple.cpp" #else /////////////////////////////////////////////////////////////////////////// // old sstring implementation using SBuf references // - duplicate = copy buffer pointer // - mutex required to be thread safe #include #include "extvalue.h" #include #ifdef MULTITHREADED #include static pthread_mutex_t sstring_ref_lock=PTHREAD_MUTEX_INITIALIZER; #define REF_LOCK pthread_mutex_lock(&sstring_ref_lock); #define REF_UNLOCK pthread_mutex_unlock(&sstring_ref_lock) #else #define REF_LOCK #define REF_UNLOCK #endif static int guessMemSize(int request) { return request+min(request/2,10000)+8; } SBuf::SBuf() { txt=(char*)""; size=used=0; refcount=1; } SBuf::SBuf(int initsize) { size=guessMemSize(initsize); if (size>0) { txt=(char*)malloc(size+1); txt[0]=0; } else txt=(char*)""; used=0; refcount=1; } SBuf::~SBuf() { freeBuf(); } void SBuf::initEmpty() { txt=(char*)""; used=size=0; refcount=1; } void SBuf::freeBuf() { if (!size) return; free(txt); used=0; } void SBuf::copyFrom(const char *ch,int chlen) { if (chlen==-1) chlen=strlen(ch); if (chlen>0) { if (chlen=needed) return; needed=guessMemSize(needed); txt=(char*)realloc(txt,needed+1); size=needed; } ////////////////////////////////////////////////// // to be moved somewhere else? // public domain source: http://isthe.com/chongo/src/fnv typedef unsigned long Fnv32_t; #define FNV_32_PRIME ((Fnv32_t)0x01000193) Fnv32_t fnv_32_buf(void *buf, size_t len, Fnv32_t hval) { unsigned char *bp = (unsigned char *)buf; /* start of buffer */ unsigned char *be = bp + len; /* beyond end of buffer */ while (bp < be) { /* multiply by the 32 bit FNV magic prime mod 2^32 */ #if defined(NO_FNV_GCC_OPTIMIZATION) hval *= FNV_32_PRIME; #else hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24); #endif /* xor the bottom with the current octet */ hval ^= (Fnv32_t)*bp++; } /* return our new hash value */ return hval; } ////////////////////////////////////////////////// unsigned long SBuf::hash() const { return fnv_32_buf(txt,used,FNV_32_PRIME); } ///////////////////////////////////////////// SString::SString() { initEmpty(); } SString::~SString() { REF_LOCK; detach(); REF_UNLOCK; } SString::SString(int x) { buf=new SBuf(x); } SString::SString(const char *t,int t_len) { initEmpty(); if (!t) return; copyFrom(t,t_len); } SString::SString(const SString &from) { if (from.buf==&SBuf::empty()) buf=&SBuf::empty(); else { REF_LOCK; buf=from.buf; if (buf->size) buf->refcount++; REF_UNLOCK; } } void SString::initEmpty() { buf=&SBuf::empty(); } void SString::memoryHint(int howbig) { detachCopy(howbig); } void SString::detachEmpty(int ensuresize) { if (buf==&SBuf::empty()) { buf=new SBuf(ensuresize); return; } if (buf->refcount<2) buf->ensureSize(ensuresize); else { buf->refcount--; buf=new SBuf(ensuresize); } } void SString::detach() { if (buf==&SBuf::empty()) return; if (!--buf->refcount) delete buf; } void SString::detachCopy(int ensuresize) { if (buf==&SBuf::empty()) { buf=new SBuf(ensuresize); return; } if (buf->refcount<2) { buf->ensureSize(ensuresize); return; } buf->refcount--; SBuf *newbuf=new SBuf(ensuresize); newbuf->copyFrom(buf->txt,min(ensuresize,buf->used)); buf=newbuf; } char *SString::directWrite(int ensuresize) { if (ensuresize<0) ensuresize=len(); REF_LOCK; detachCopy(ensuresize); REF_UNLOCK; appending=buf->used; return buf->txt; } /* char *SString::directWrite() { return directWrite(buf->used); } */ char *SString::directAppend(int maxappend) { REF_LOCK; detachCopy(buf->used+maxappend); REF_UNLOCK; appending=buf->used; return buf->txt+appending; } void SString::endWrite(int newlength) { if (newlength<0) newlength=strlen(buf->txt); else buf->txt[newlength]=0; buf->used=newlength; } void SString::endAppend(int newappend) { if (newappend<0) newappend=strlen(buf->txt+appending); else buf->txt[appending+newappend]=0; buf->used=appending+newappend; } ////////////// append ///////////////// void SString::operator+=(const char *s) { if (!s) return; int x=strlen(s); if (!x) return; append(s,x); } void SString::append(const char *txt,int count) { if (!count) return; REF_LOCK; detachCopy(buf->used+count); REF_UNLOCK; buf->append(txt,count); } void SString::operator+=(const SString&s) { append((const char*)s,s.len()); } SString SString::operator+(const SString& s) const { SString ret(*this); ret+=s; return ret; } ///////////////////////////// void SString::copyFrom(const char *ch,int chlen) { if (!ch) chlen=0; else if (chlen<0) chlen=strlen(ch); REF_LOCK; detachEmpty(chlen); REF_UNLOCK; memcpy(buf->txt,ch,chlen); buf->txt[chlen]=0; buf->used=chlen; } void SString::operator=(const char *ch) { copyFrom(ch); } void SString::operator=(const SString&s) { if (s.buf==buf) return; REF_LOCK; detach(); buf=s.buf; if (buf->size) buf->refcount++; REF_UNLOCK; } /////////////////////////////////////// SString SString::substr(int begin, int length) const { if (begin<0) { length+=begin; begin=0; } if (length>=(len()-begin)) length=len()-begin; if (length<=0) return SString(); if (length==len()) return *this; return SString((*this)(begin),length); } /////////////////////////////////////// int SString::equals(const SString& s) const { if (s.buf==buf) return 1; return !strcmp(buf->txt,s.buf->txt); } /////////////////////////////////////// int SString::indexOf(int character,int start) const { const char *found=strchr(buf->txt+start,character); return found?found-buf->txt:-1; } int SString::indexOf(const char *substring,int start) const { char *found=strstr(buf->txt+start,substring); return found?found-buf->txt:-1; } int SString::indexOf(const SString & substring,int start) const { char *found=strstr(buf->txt+start,((const char*)substring)); return found?found-buf->txt:-1; } int SString::getNextToken (int& pos,SString &token,char separator) const { if (pos>=len()) {token=0;return 0;} int p1=pos,p2; const char *t1=buf->txt+pos; const char *t2=strchr(t1,separator); if (t2) pos=(p2=(t2-buf->txt))+1; else p2=pos=len(); strncpy(token.directWrite(p2-p1),t1,p2-p1); token.endWrite(p2-p1); return 1; } int SString::startsWith(const char *pattern) const { const char *t=(const char*)(*this); for (;*pattern;pattern++,t++) if (*t != *pattern) return 0; return 1; } SString SString::valueOf(int i) { return SString::sprintf("%d",i); } SString SString::valueOf(long i) { return SString::sprintf("%d",i); } SString SString::valueOf(double d) { SString tmp=SString::sprintf("%.15g",d); if ((!strchr(tmp,'.'))&&(!strchr(tmp,'e'))) tmp+=".0"; return tmp; } SString SString::valueOf(const SString& s) { return s; } #ifdef LINUX #define VSNPRINTF_RETURNS_REQUIRED_SIZE #endif #if defined _WIN32 && !defined __BORLANDC__ #define USE_VSCPRINTF #endif #if 0 //testing _vscprintf #define USE_VSCPRINTF int _vscprintf(const char *format,va_list argptr) { return vsnprintf("",0,format,argptr); } #endif SString SString::sprintf(const char* format, ...) { int n, size = 30; va_list ap; SString ret; #ifdef USE_VSCPRINTF va_start(ap, format); size=_vscprintf(format, ap); va_end(ap); #endif while (1) { char* p=ret.directWrite(size); assert(p!=NULL); size=ret.directMaxLen()+1; /* Try to print in the allocated space. */ va_start(ap, format); n = vsnprintf(p, size, format, ap); va_end(ap); /* If that worked, return the string. */ if (n > -1 && n < size) { ret.endWrite(n); return ret; } /* Else try again with more space. */ #ifdef VSNPRINTF_RETURNS_REQUIRED_SIZE if (n > -1) /* glibc 2.1 */ size = n; /* precisely what is needed */ else /* glibc 2.0 */ #endif size *= 2; /* twice the old size */ } } SString &SString::empty() { static SString empty; return empty; } SBuf &SBuf::empty() { static SBuf empty; return empty; } #endif //#ifdef SSTRING_SIMPLE