[385] | 1 | #include "sstring.h" |
---|
| 2 | #include "extvalue.h" |
---|
| 3 | #include <assert.h> |
---|
[970] | 4 | #include <common/nonstd_math.h> |
---|
[385] | 5 | |
---|
[1040] | 6 | #ifdef __ANDROID__ |
---|
| 7 | #include <android/log.h> //only needed to print error messages related to a workaround for Android bug |
---|
| 8 | #endif |
---|
[970] | 9 | |
---|
[1040] | 10 | |
---|
[385] | 11 | void SString::initEmpty() |
---|
| 12 | { |
---|
[973] | 13 | txt = NULL; used = 0; allocated = 0; |
---|
[385] | 14 | } |
---|
| 15 | |
---|
| 16 | SString::SString() |
---|
| 17 | { |
---|
[793] | 18 | initEmpty(); |
---|
[385] | 19 | } |
---|
| 20 | |
---|
| 21 | SString::~SString() |
---|
| 22 | { |
---|
[973] | 23 | reallocate(0); |
---|
[385] | 24 | } |
---|
| 25 | |
---|
[793] | 26 | SString::SString(const char *t, int t_len) |
---|
[385] | 27 | { |
---|
[793] | 28 | initEmpty(); |
---|
| 29 | if (!t) return; |
---|
| 30 | copyFrom(t, t_len); |
---|
[385] | 31 | } |
---|
| 32 | |
---|
| 33 | SString::SString(const SString &from) |
---|
| 34 | { |
---|
[793] | 35 | initEmpty(); |
---|
| 36 | operator=(from); |
---|
[385] | 37 | } |
---|
| 38 | |
---|
| 39 | SString::SString(SString&& from) |
---|
| 40 | { |
---|
[973] | 41 | txt = from.txt; allocated = from.allocated; used = from.used; |
---|
| 42 | from.txt = NULL; from.allocated = 0; from.used = 0; |
---|
[385] | 43 | } |
---|
| 44 | |
---|
[955] | 45 | SString::SString(char in) |
---|
| 46 | { |
---|
| 47 | initEmpty(); |
---|
| 48 | append(&in, 1); |
---|
| 49 | } |
---|
| 50 | |
---|
[973] | 51 | void SString::reallocate(int newsize) |
---|
[385] | 52 | { |
---|
[973] | 53 | if (newsize == allocated) return; |
---|
[793] | 54 | txt = (char*)realloc(txt, newsize); |
---|
[973] | 55 | allocated = newsize; |
---|
[385] | 56 | } |
---|
| 57 | |
---|
[973] | 58 | void SString::ensureAllocated(int needed) |
---|
[385] | 59 | { |
---|
[973] | 60 | if (allocated > needed) return; |
---|
| 61 | reallocate((allocated > 0) ? (needed + needed / 2 + 1) : (needed + 1)); |
---|
[385] | 62 | } |
---|
| 63 | |
---|
| 64 | char *SString::directWrite(int ensuresize) |
---|
| 65 | { |
---|
[973] | 66 | ensureAllocated(ensuresize); |
---|
[793] | 67 | appending = used; |
---|
| 68 | return txt; |
---|
[385] | 69 | } |
---|
| 70 | |
---|
| 71 | char *SString::directAppend(int maxappend) |
---|
| 72 | { |
---|
[973] | 73 | ensureAllocated(used + maxappend); |
---|
[793] | 74 | appending = used; |
---|
| 75 | return txt + appending; |
---|
[385] | 76 | } |
---|
| 77 | |
---|
| 78 | void SString::endWrite(int newlength) |
---|
| 79 | { |
---|
[1280] | 80 | if (newlength < 0) newlength = (int)strlen(txt); |
---|
[989] | 81 | else |
---|
| 82 | { |
---|
| 83 | if (newlength >= allocated) |
---|
| 84 | { |
---|
[996] | 85 | logMessage("SString", "endWrite", LOG_CRITICAL, "newlength >= allocated"); |
---|
[989] | 86 | assert(newlength < allocated); |
---|
| 87 | if (allocated == 0) return; |
---|
| 88 | newlength = allocated - 1; |
---|
| 89 | } |
---|
| 90 | txt[newlength] = 0; |
---|
| 91 | } |
---|
[793] | 92 | used = newlength; |
---|
[385] | 93 | } |
---|
| 94 | |
---|
| 95 | void SString::endAppend(int newappend) |
---|
| 96 | { |
---|
[1280] | 97 | if (newappend < 0) endWrite(appending + (int)strlen(txt + appending)); |
---|
[989] | 98 | else endWrite(newappend + appending); |
---|
[385] | 99 | } |
---|
| 100 | |
---|
| 101 | ////////////// append ///////////////// |
---|
| 102 | |
---|
| 103 | void SString::operator+=(const char *s) |
---|
| 104 | { |
---|
[793] | 105 | if (!s) return; |
---|
[1280] | 106 | int x = (int)strlen(s); |
---|
[793] | 107 | if (!x) return; |
---|
| 108 | append(s, x); |
---|
[385] | 109 | } |
---|
| 110 | |
---|
[793] | 111 | void SString::append(const char *t, int n) |
---|
[385] | 112 | { |
---|
[793] | 113 | if (!n) return; |
---|
[973] | 114 | ensureAllocated(used + n); |
---|
[793] | 115 | memmove(txt + used, t, n); |
---|
| 116 | used += n; |
---|
| 117 | txt[used] = 0; |
---|
[385] | 118 | } |
---|
| 119 | |
---|
| 120 | void SString::operator+=(const SString&s) |
---|
| 121 | { |
---|
[973] | 122 | append(s.c_str(), s.length()); |
---|
[385] | 123 | } |
---|
| 124 | |
---|
| 125 | SString SString::operator+(const SString& s) const |
---|
| 126 | { |
---|
[955] | 127 | SString ret; |
---|
[973] | 128 | ret.reserve(length() + s.length()); |
---|
[793] | 129 | ret = *this; |
---|
| 130 | ret += s; |
---|
| 131 | return ret; |
---|
[385] | 132 | } |
---|
| 133 | |
---|
| 134 | ///////////////////////////// |
---|
| 135 | |
---|
[793] | 136 | void SString::copyFrom(const char *ch, int chlen) |
---|
[385] | 137 | { |
---|
[793] | 138 | if (!ch) chlen = 0; |
---|
[1280] | 139 | else if (chlen < 0) chlen = (int)strlen(ch); |
---|
[793] | 140 | if (chlen) |
---|
[385] | 141 | { |
---|
[973] | 142 | ensureAllocated(chlen); |
---|
[793] | 143 | memmove(txt, ch, chlen); |
---|
| 144 | txt[chlen] = 0; |
---|
| 145 | used = chlen; |
---|
[385] | 146 | } |
---|
[793] | 147 | else |
---|
[385] | 148 | { |
---|
[793] | 149 | if (txt) |
---|
[385] | 150 | { |
---|
[793] | 151 | txt[0] = 0; |
---|
| 152 | used = 0; |
---|
[385] | 153 | } |
---|
| 154 | } |
---|
| 155 | } |
---|
| 156 | |
---|
| 157 | void SString::operator=(const char *ch) |
---|
| 158 | { |
---|
[793] | 159 | copyFrom(ch); |
---|
[385] | 160 | } |
---|
| 161 | |
---|
| 162 | void SString::operator=(const SString&s) |
---|
| 163 | { |
---|
[793] | 164 | if (&s == this) return; |
---|
[973] | 165 | copyFrom(s.c_str(), s.length()); |
---|
[385] | 166 | } |
---|
| 167 | |
---|
| 168 | /////////////////////////////////////// |
---|
| 169 | |
---|
[973] | 170 | SString SString::substr(int begin, int numchars) const |
---|
[385] | 171 | { |
---|
[973] | 172 | if (begin < 0) { numchars += begin; begin = 0; } |
---|
| 173 | if (numchars >= (length() - begin)) numchars = length() - begin; |
---|
| 174 | if (numchars <= 0) return SString(); |
---|
| 175 | if (numchars == length()) return *this; |
---|
| 176 | return SString((*this)(begin), numchars); |
---|
[385] | 177 | } |
---|
| 178 | |
---|
| 179 | /////////////////////////////////////// |
---|
| 180 | |
---|
[395] | 181 | bool SString::equals(const SString& s) const |
---|
[385] | 182 | { |
---|
[793] | 183 | if (this == &s) return true; |
---|
[973] | 184 | if (length() != s.length()) return false; |
---|
[793] | 185 | return strcmp(getPtr(), s.getPtr()) == 0; |
---|
[385] | 186 | } |
---|
| 187 | |
---|
| 188 | /////////////////////////////////////// |
---|
| 189 | |
---|
[793] | 190 | int SString::indexOf(int character, int start) const |
---|
[385] | 191 | { |
---|
[793] | 192 | const char *found = strchr(getPtr() + start, character); |
---|
| 193 | return found ? found - getPtr() : -1; |
---|
[385] | 194 | } |
---|
| 195 | |
---|
[793] | 196 | int SString::indexOf(const char *substring, int start) const |
---|
[385] | 197 | { |
---|
[793] | 198 | const char *found = strstr(getPtr() + start, substring); |
---|
| 199 | return found ? found - getPtr() : -1; |
---|
[385] | 200 | } |
---|
| 201 | |
---|
[793] | 202 | int SString::indexOf(const SString & substring, int start) const |
---|
[385] | 203 | { |
---|
[793] | 204 | const char *found = strstr(getPtr() + start, substring.c_str()); |
---|
| 205 | return found ? found - getPtr() : -1; |
---|
[385] | 206 | } |
---|
| 207 | |
---|
[793] | 208 | bool SString::getNextToken(int& pos, SString &token, char separator) const |
---|
[385] | 209 | { |
---|
[973] | 210 | if (pos >= length()) { token = 0; return false; } |
---|
[793] | 211 | int p1 = pos, p2; |
---|
| 212 | const char *t1 = getPtr() + pos; |
---|
| 213 | const char *t2 = strchr(t1, separator); |
---|
[973] | 214 | if (t2) pos = (p2 = (t2 - getPtr())) + 1; else p2 = pos = length(); |
---|
[793] | 215 | strncpy(token.directWrite(p2 - p1), t1, p2 - p1); |
---|
| 216 | token.endWrite(p2 - p1); |
---|
| 217 | return true; |
---|
[385] | 218 | } |
---|
| 219 | |
---|
[395] | 220 | bool SString::startsWith(const char *pattern) const |
---|
[385] | 221 | { |
---|
[793] | 222 | const char *t = this->c_str(); |
---|
| 223 | for (; *pattern; pattern++, t++) |
---|
| 224 | if (*t != *pattern) return false; |
---|
| 225 | return true; |
---|
[385] | 226 | } |
---|
| 227 | |
---|
| 228 | SString SString::valueOf(int i) |
---|
| 229 | { |
---|
[793] | 230 | return SString::sprintf("%d", i); |
---|
[385] | 231 | } |
---|
| 232 | SString SString::valueOf(long i) |
---|
| 233 | { |
---|
[793] | 234 | return SString::sprintf("%d", i); |
---|
[385] | 235 | } |
---|
| 236 | SString SString::valueOf(double d) |
---|
| 237 | { |
---|
[826] | 238 | SString tmp; |
---|
| 239 | char* here = tmp.directWrite(30); |
---|
[970] | 240 | tmp.endWrite(doubleToString(d, -1, here, 30)); |
---|
[793] | 241 | if ((!strchr(tmp.c_str(), '.')) && (!strchr(tmp.c_str(), 'e'))) tmp += ".0"; |
---|
| 242 | return tmp; |
---|
[385] | 243 | } |
---|
| 244 | SString SString::valueOf(const SString& s) |
---|
| 245 | { |
---|
[793] | 246 | return s; |
---|
[385] | 247 | } |
---|
| 248 | |
---|
| 249 | SString SString::sprintf(const char* format, ...) |
---|
| 250 | { |
---|
[793] | 251 | int n, size = 30; |
---|
| 252 | va_list ap; |
---|
[385] | 253 | |
---|
[793] | 254 | SString ret; |
---|
[385] | 255 | |
---|
| 256 | #ifdef USE_VSCPRINTF |
---|
[793] | 257 | va_start(ap, format); |
---|
| 258 | size = _vscprintf(format, ap); |
---|
| 259 | va_end(ap); |
---|
[385] | 260 | #endif |
---|
| 261 | |
---|
[793] | 262 | while (1) |
---|
[385] | 263 | { |
---|
[793] | 264 | char* p = ret.directWrite(size); |
---|
| 265 | assert(p != NULL); |
---|
[973] | 266 | size = ret.capacity() + 1; |
---|
[793] | 267 | /* Try to print in the allocated space. */ |
---|
| 268 | va_start(ap, format); |
---|
| 269 | n = vsnprintf(p, size, format, ap); |
---|
| 270 | va_end(ap); |
---|
| 271 | /* If that worked, return the string. */ |
---|
[897] | 272 | |
---|
| 273 | #ifdef __ANDROID__ |
---|
| 274 | //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. |
---|
| 275 | //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). |
---|
| 276 | if (n < 0 && size >= (1 << 24)) //wants more than 16M |
---|
| 277 | { |
---|
[1040] | 278 | p[size - 1] = 0; //just to ensure there is at least some ending \0 in memory... who knows what buggy vsnprintf() did. |
---|
| 279 | __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(p), format); |
---|
[897] | 280 | //in my tests, it always used 0 bytes, so it produced a 0-length string: "" |
---|
[1040] | 281 | va_start(ap, format); |
---|
| 282 | n = vsnprintf(p, size, format, ap); //hoping 16M is enough |
---|
| 283 | va_end(ap); |
---|
| 284 | __android_log_print(ANDROID_LOG_INFO, LOG_APP_NAME, "Fallback to vsprintf() produced string: '%s'", p); |
---|
[897] | 285 | if (n < 0) //vsprintf was also buggy. If we were strict, we should abort the app now. |
---|
| 286 | { |
---|
[1040] | 287 | strcpy(p, "[STR_ERR] "); //a special prefix just to indicate the returned string is incorrect |
---|
| 288 | strcat(p, format); //append and return the original formatting string |
---|
| 289 | __android_log_print(ANDROID_LOG_ERROR, LOG_APP_NAME, "vsprintf() also failed, using the incorrect resulting string: '%s'", p); |
---|
[897] | 290 | } |
---|
[1040] | 291 | n = strlen(p); //pretend vsnprintf() or vsprintf() was OK to exit the endless loop |
---|
[996] | 292 | } |
---|
[897] | 293 | #endif |
---|
| 294 | |
---|
[793] | 295 | if (n > -1 && n < size) |
---|
[385] | 296 | { |
---|
[793] | 297 | ret.endWrite(n); |
---|
| 298 | return ret; |
---|
[385] | 299 | } |
---|
[793] | 300 | /* Else try again with more space. */ |
---|
[385] | 301 | #ifdef VSNPRINTF_RETURNS_REQUIRED_SIZE |
---|
[793] | 302 | if (n > -1) /* glibc 2.1 */ |
---|
| 303 | size = n; /* precisely what is needed */ |
---|
| 304 | else /* glibc 2.0 */ |
---|
[385] | 305 | #endif |
---|
[793] | 306 | size *= 2; /* twice the old size */ |
---|
[996] | 307 | } |
---|
[385] | 308 | } |
---|
| 309 | |
---|
| 310 | SString &SString::empty() |
---|
| 311 | { |
---|
[793] | 312 | static SString empty; |
---|
| 313 | return empty; |
---|
[385] | 314 | } |
---|