/* Web Polygraph http://www.web-polygraph.org/ * (C) 2003-2006 The Measurement Factory * Licensed under the Apache License, Version 2.0 */ #include "xstd/xstd.h" #include "xstd/h/iostream.h" #include "xstd/h/sstream.h" #include "xstd/h/string.h" #include "xstd/Assert.h" #include "xstd/String.h" #include "xstd/gadgets.h" /* StrBuf */ void StrBuf::alloc(int aSize) { theBuf = new char[theSize = aSize]; } void StrBuf::set(const char *aBuf, int aSize) { Assert(!theUseLvl); //cerr << here << "new string: " << aSize << ' ' << aBuf << endl; delete[] theBuf; alloc(aSize); memcpy(theBuf, aBuf, aSize); } StrBuf *StrBuf::clone() const { StrBuf *sb = new StrBuf; sb->set(theBuf, theSize); return sb; } void StrBuf::realloc(int newSize) { if (newSize > theSize) { char *buf = theBuf; const int sz = theSize; theBuf = 0; alloc(newSize); if (buf) memcpy(theBuf, buf, sz); delete[] buf; } } /* String */ String::String(const char *s, int len): theBuf(0) { Assert(s); Assert(len >= 0); if (len > 0) { theBuf = new StrBuf; theBuf->alloc(len+1); memcpy(theBuf->theBuf, s, len); theBuf->theBuf[len] = '\0'; theBuf->use(); } } void String::set(const char *s) { if (theBuf) theBuf = theBuf->abandon(); if (s) { if (const int l = strlen(s)) { theBuf = new StrBuf; theBuf->set(s, l+1); theBuf->use(); } } } void String::set(const String &s) { if (theBuf == s.theBuf) return; // either "copy to self" or "0 = 0" if (theBuf) theBuf->abandon(); if ((theBuf = s.theBuf)) theBuf->use(); } char *String::alloc(int size) { if (theBuf) theBuf->abandon(); theBuf = new StrBuf; theBuf->alloc(size); theBuf->use(); return theBuf->theBuf; } void String::stretch(int newSize) { if (!theBuf || theBuf->theSize < newSize) { if (theBuf) theBuf->realloc(newSize); else alloc(newSize); } } // clone a private copy if we are using shared memory // (copy-on-write or lazy copying) void String::aboutToMod() { if (theBuf && theBuf->theUseLvl > 1) { StrBuf *sb = theBuf; theBuf = sb->clone(); theBuf->use(); sb->abandon(); } } const char *String::chr(char c) const { return theBuf ? strchr(cstr(), c) : 0; } const char *String::rchr(char c) const { return theBuf ? strrchr(cstr(), c) : 0; } const char *String::str(const char *s) const { return theBuf ? strstr(cstr(), s) : 0; } const char *String::str(const String &s) const { return str(s.cstr()); } String String::operator ()(int min, int maxp1) const { maxp1 = Min(maxp1, len()); const int len = maxp1 - min; Assert(len >= 0); return String(data() + min, len); } Area String::area(int offset, int size) const { if (offset == 0 && size == npos && len() == 0) return Area::Empty(); Assert(offset < len() || size == 0); return Area::Create(data(), offset, Min(size, len()-offset)); } int String::cmp(const String &s) const { return strcmp(cstr(), s.cstr()); } int String::cmp(const String &s, int len) const { return strncmp(cstr(), s.cstr(), len); } int String::cmp(const char *s) const { return strcmp(cstr(), s); } int String::cmp(const char *s, int size) const { return strncmp(cstr(), s, size); } bool String::startsWith(const String &s) const { return s.prefixOf(this->cstr()); } bool String::prefixOf(const char *buf) const { if (const int l = len()) return strncmp(theBuf->theBuf, buf, l) == 0; else return true; // empty string is always a prefix? } bool String::casePrefixOf(const char *buf, int size) const { if (const int l = len()) return l <= size && !strncasecmp(theBuf->theBuf, buf, l); else return true; // empty string is always a prefix? } bool String::caseEndsWith(const char *buf, int size) const { if (const int l = len()) return size <= l && !strncasecmp(theBuf->theBuf + l-size, buf, l); else return false; } int String::find(char c, int offset) const { if (!Should(offset <= len())) return npos; if (const char *p = strchr(cstr() + offset, c)) return p - cstr(); else return npos; } int String::find(const Area &area, int offset) const { if (!area) return npos; for (const int maxo = len() - area.size(); offset <= maxo; ++offset) { offset = find(area[0], offset); if (maxo < offset) break; if (strncmp(data()+offset, area.data(), area.size()) == 0) return offset; } return npos; } int String::hash() const { const int step = Max(1, len() / 16); unsigned int res = 0; for (int i = 0; i < len(); i += step) res = res*33U + data()[i]; return Max(1, abs((int)res)); } // adds 0 after appending if not already there void String::append(const char *buf, int size) { Assert(buf || !size); aboutToMod(); const bool addTerm = !size || buf[size-1] != (char)0; const int l = len(); stretch(l + size + (int)addTerm); Assert(theBuf); if (size) memcpy(theBuf->theBuf+l, buf, size); theBuf->theBuf[len()] = '\0'; } String &String::operator +=(const String &s) { append(s.data(), s.len()); return *this; } String &String::operator +=(char c) { append(&c, 1); return *this; } String String::operator +(char c) const { String s(*this); s += c; return s; } /* operators on strings */ ostream &operator <<(ostream &os, const String &s) { if (s) os.write(s.data(), s.len()); return os; } String operator +(const String &s1, const String &s2) { String s(s1); s += s2; return s; } String Stream2String(ostringstream &os) { const std::string s = os.str(); const String res = s.size() > 0 ? String(s.data(), s.size()) : String(); streamFreeze(os, false); return res; } /* StrIter */ StrIter::StrIter(const String &aStr, char aDel): theStr(aStr), theTokBeg(0), theTokEnd(0), theDel(aDel) { if (theStr) { theTokBeg = theTokEnd = theStr.data(); moveEnd(); } } String StrIter::token() const { return String(tokBeg(), tokLen()); } void StrIter::next() { if (theTokEnd < theStr.data() + theStr.len()) { theTokBeg = ++theTokEnd; moveEnd(); } else { theTokBeg = theTokEnd; } } void StrIter::moveEnd() { while (theTokEnd < theStr.data() + theStr.len() && *theTokEnd != theDel) ++theTokEnd; }