| 1 | /* |
| 2 | * LegacyClonk |
| 3 | * |
| 4 | * Copyright (c) RedWolf Design |
| 5 | * Copyright (c) 2017-2021, The LegacyClonk Team and contributors |
| 6 | * |
| 7 | * Distributed under the terms of the ISC license; see accompanying file |
| 8 | * "COPYING" for details. |
| 9 | * |
| 10 | * "Clonk" is a registered trademark of Matthes Bender, used with permission. |
| 11 | * See accompanying file "TRADEMARK" for details. |
| 12 | * |
| 13 | * To redistribute this file separately, substitute the full license texts |
| 14 | * for the above references. |
| 15 | */ |
| 16 | |
| 17 | // Standard buffer classes |
| 18 | |
| 19 | #pragma once |
| 20 | |
| 21 | #include "Standard.h" |
| 22 | |
| 23 | #include <stdlib.h> |
| 24 | #include <assert.h> |
| 25 | #include <stdarg.h> |
| 26 | |
| 27 | #include <concepts> |
| 28 | #include <cstring> |
| 29 | #include <utility> |
| 30 | #include <type_traits> |
| 31 | |
| 32 | // Base buffer class. Either references or holds data. |
| 33 | class StdBuf |
| 34 | { |
| 35 | public: |
| 36 | // *** Construction |
| 37 | |
| 38 | StdBuf() : fRef(true), pData(nullptr), iSize(0) {} |
| 39 | |
| 40 | // Constructor from other buffer (copy construction) |
| 41 | // Set by constant data. Copies data if not specified otherwise. |
| 42 | StdBuf(const void *pData, size_t iSize, bool fCopy = true) |
| 43 | : fRef(true), pData(pData), iSize(iSize) |
| 44 | { |
| 45 | if (fCopy) Copy(); |
| 46 | } |
| 47 | |
| 48 | StdBuf(const StdBuf &Buf2, bool fCopy = true) |
| 49 | : fRef(true), pData(nullptr), iSize(0) |
| 50 | { |
| 51 | if (fCopy) |
| 52 | { |
| 53 | Copy(Buf2); |
| 54 | } |
| 55 | else |
| 56 | { |
| 57 | Ref(Buf2); |
| 58 | } |
| 59 | } |
| 60 | |
| 61 | StdBuf(StdBuf &&Buf2, bool fCopy = false) |
| 62 | : fRef(true), pData(nullptr), iSize(0) |
| 63 | { |
| 64 | if (fCopy) |
| 65 | { |
| 66 | Copy(Buf2); |
| 67 | } |
| 68 | else if (!Buf2.isRef()) |
| 69 | { |
| 70 | Take(Buf2: std::forward<StdBuf>(t&: Buf2)); |
| 71 | } |
| 72 | else |
| 73 | { |
| 74 | Ref(Buf2); |
| 75 | } |
| 76 | } |
| 77 | |
| 78 | ~StdBuf() |
| 79 | { |
| 80 | Clear(); |
| 81 | } |
| 82 | |
| 83 | static StdBuf MakeRef(const void *pData, size_t iSize) |
| 84 | { |
| 85 | return StdBuf(pData, iSize, false); |
| 86 | } |
| 87 | |
| 88 | static StdBuf TakeOrRef(StdBuf &other) |
| 89 | { |
| 90 | StdBuf ret; |
| 91 | if (other.isRef()) ret.Ref(Buf2: other); |
| 92 | else ret.Take(Buf2&: other); |
| 93 | return ret; |
| 94 | } |
| 95 | |
| 96 | protected: |
| 97 | // Reference? Otherwise, this object holds the data. |
| 98 | bool fRef; |
| 99 | // Data |
| 100 | union |
| 101 | { |
| 102 | const void *pData; |
| 103 | void *pMData; |
| 104 | #ifndef NDEBUG |
| 105 | char *szString; // for debugger preview |
| 106 | #endif |
| 107 | }; |
| 108 | size_t iSize; |
| 109 | |
| 110 | public: |
| 111 | // *** Getters |
| 112 | |
| 113 | bool isNull() const { return !getData(); } |
| 114 | const void *getData() const { return fRef ? pData : pMData; } |
| 115 | void *getMData() { assert(!fRef); return pMData; } |
| 116 | size_t getSize() const { return iSize; } |
| 117 | bool isRef() const { return fRef; } |
| 118 | |
| 119 | const void *getPtr(size_t i) const { return reinterpret_cast<const char *>(getData()) + i; } |
| 120 | void *getMPtr(size_t i) { return reinterpret_cast<char *>(getMData()) + i; } |
| 121 | |
| 122 | StdBuf getPart(size_t iStart, size_t inSize) const |
| 123 | { |
| 124 | assert(iStart + inSize <= iSize); |
| 125 | return StdBuf(getPtr(i: iStart), inSize, false); |
| 126 | } |
| 127 | |
| 128 | // *** Setters |
| 129 | |
| 130 | // * Direct setters |
| 131 | |
| 132 | // Reference given data |
| 133 | void Ref(const void *pnData, size_t inSize) |
| 134 | { |
| 135 | Clear(); |
| 136 | fRef = true; pData = pnData; iSize = inSize; |
| 137 | } |
| 138 | |
| 139 | // Take over data (hold it) |
| 140 | void Take(void *pnData, size_t inSize) |
| 141 | { |
| 142 | Clear(); |
| 143 | if (pnData) |
| 144 | { |
| 145 | fRef = false; pMData = pnData; iSize = inSize; |
| 146 | } |
| 147 | } |
| 148 | |
| 149 | // Transfer puffer ownership to the caller |
| 150 | void *GrabPointer() |
| 151 | { |
| 152 | if (isNull()) return nullptr; |
| 153 | // Do not give out a buffer which someone else will free |
| 154 | if (fRef) Copy(); |
| 155 | void *pMData = getMData(); |
| 156 | pData = pMData; fRef = true; |
| 157 | return pMData; |
| 158 | } |
| 159 | |
| 160 | // * Buffer data operations |
| 161 | |
| 162 | // Create new buffer with given size |
| 163 | void New(size_t inSize) |
| 164 | { |
| 165 | Clear(); |
| 166 | pMData = malloc(size: iSize = inSize); |
| 167 | fRef = false; |
| 168 | } |
| 169 | |
| 170 | // Write data into the buffer |
| 171 | void Write(const void *pnData, size_t inSize, size_t iAt = 0) |
| 172 | { |
| 173 | assert(iAt + inSize <= iSize); |
| 174 | if (pnData && inSize) memcpy(dest: getMPtr(i: iAt), src: pnData, n: inSize); |
| 175 | } |
| 176 | |
| 177 | // Move data around inside the buffer (checks overlap) |
| 178 | void Move(size_t iFrom, size_t inSize, size_t iTo = 0) |
| 179 | { |
| 180 | assert(iFrom + inSize <= iSize); assert(iTo + inSize <= iSize); |
| 181 | memmove(dest: getMPtr(i: iTo), src: getPtr(i: iFrom), n: inSize); |
| 182 | } |
| 183 | |
| 184 | // Compare to memory |
| 185 | int Compare(const void *pCData, size_t iCSize, size_t iAt = 0) const |
| 186 | { |
| 187 | assert(iAt + iCSize <= getSize()); |
| 188 | return memcmp(s1: getPtr(i: iAt), s2: pCData, n: iCSize); |
| 189 | } |
| 190 | |
| 191 | // Grow the buffer |
| 192 | void Grow(size_t iGrow) |
| 193 | { |
| 194 | // Grow dereferences |
| 195 | if (fRef) { Copy(inSize: iSize + iGrow); return; } |
| 196 | if (!iGrow) return; |
| 197 | // Realloc |
| 198 | pMData = realloc(ptr: pMData, size: iSize += iGrow); |
| 199 | } |
| 200 | |
| 201 | // Shrink the buffer |
| 202 | void Shrink(size_t iShrink) |
| 203 | { |
| 204 | assert(iSize >= iShrink); |
| 205 | // Shrink dereferences |
| 206 | if (fRef) { Copy(inSize: iSize - iShrink); return; } |
| 207 | if (!iShrink) return; |
| 208 | // Realloc |
| 209 | pMData = realloc(ptr: pMData, size: iSize -= iShrink); |
| 210 | } |
| 211 | |
| 212 | // Clear buffer |
| 213 | void Clear() |
| 214 | { |
| 215 | if (!fRef) free(ptr: pMData); |
| 216 | pMData = nullptr; fRef = true; iSize = 0; |
| 217 | } |
| 218 | |
| 219 | // Free buffer that had been grabbed |
| 220 | static void DeletePointer(void *data) |
| 221 | { |
| 222 | free(ptr: data); |
| 223 | } |
| 224 | |
| 225 | // * Composed actions |
| 226 | |
| 227 | // Set buffer size (dereferences) |
| 228 | void SetSize(size_t inSize) |
| 229 | { |
| 230 | if (inSize > iSize) |
| 231 | Grow(iGrow: inSize - iSize); |
| 232 | else |
| 233 | Shrink(iShrink: iSize - inSize); |
| 234 | } |
| 235 | |
| 236 | // Write buffer contents into the buffer |
| 237 | void Write(const StdBuf &Buf2, size_t iAt = 0) |
| 238 | { |
| 239 | Write(pnData: Buf2.getData(), inSize: Buf2.getSize(), iAt); |
| 240 | } |
| 241 | |
| 242 | // Compare (a part of) this buffer's contents to another's |
| 243 | int Compare(const StdBuf &Buf2, size_t iAt = 0) const |
| 244 | { |
| 245 | return Compare(pCData: Buf2.getData(), iCSize: Buf2.getSize(), iAt); |
| 246 | } |
| 247 | |
| 248 | // Create a copy of the data (dereferences, obviously) |
| 249 | void Copy(size_t inSize) |
| 250 | { |
| 251 | if (isNull() && !inSize) return; |
| 252 | const void *pOldData = getData(); |
| 253 | size_t iOldSize = iSize; |
| 254 | New(inSize); |
| 255 | Write(pnData: pOldData, inSize: (std::min)(a: iOldSize, b: inSize)); |
| 256 | } |
| 257 | |
| 258 | void Copy() |
| 259 | { |
| 260 | Copy(inSize: iSize); |
| 261 | } |
| 262 | |
| 263 | // Copy data from address |
| 264 | void Copy(const void *pnData, size_t inSize) |
| 265 | { |
| 266 | Ref(pnData, inSize); Copy(); |
| 267 | } |
| 268 | |
| 269 | // Copy from another buffer |
| 270 | void Copy(const StdBuf &Buf2) |
| 271 | { |
| 272 | Copy(pnData: Buf2.getData(), inSize: Buf2.getSize()); |
| 273 | } |
| 274 | |
| 275 | // Create a copy and return it |
| 276 | StdBuf Duplicate() const |
| 277 | { |
| 278 | StdBuf Buf; Buf.Copy(Buf2: *this); return Buf; |
| 279 | } |
| 280 | |
| 281 | // Append data from address |
| 282 | void Append(const void *pnData, size_t inSize) |
| 283 | { |
| 284 | Grow(iGrow: inSize); |
| 285 | Write(pnData, inSize, iAt: iSize - inSize); |
| 286 | } |
| 287 | |
| 288 | // Append data from another buffer |
| 289 | void Append(const StdBuf &Buf2) |
| 290 | { |
| 291 | Append(pnData: Buf2.getData(), inSize: Buf2.getSize()); |
| 292 | } |
| 293 | |
| 294 | // Reference another buffer's contents |
| 295 | void Ref(const StdBuf &Buf2) |
| 296 | { |
| 297 | Ref(pnData: Buf2.getData(), inSize: Buf2.getSize()); |
| 298 | } |
| 299 | |
| 300 | // Create a reference to this buffer's contents |
| 301 | StdBuf getRef() const |
| 302 | { |
| 303 | return StdBuf(getData(), getSize(), false); |
| 304 | } |
| 305 | |
| 306 | // take over another buffer's contents |
| 307 | void Take(StdBuf &Buf2) |
| 308 | { |
| 309 | Take(pnData: Buf2.GrabPointer(), inSize: Buf2.getSize()); |
| 310 | } |
| 311 | |
| 312 | void Take(StdBuf &&Buf2) |
| 313 | { |
| 314 | Take(pnData: Buf2.GrabPointer(), inSize: Buf2.getSize()); |
| 315 | } |
| 316 | |
| 317 | // * File support |
| 318 | bool LoadFromFile(const char *szFile); |
| 319 | bool SaveToFile(const char *szFile) const; |
| 320 | |
| 321 | // *** Operators |
| 322 | |
| 323 | // Null check |
| 324 | bool operator!() const { return isNull(); } |
| 325 | |
| 326 | // Appending |
| 327 | StdBuf &operator+=(const StdBuf &Buf2) |
| 328 | { |
| 329 | Append(Buf2); |
| 330 | return *this; |
| 331 | } |
| 332 | |
| 333 | StdBuf operator+(const StdBuf &Buf2) const |
| 334 | { |
| 335 | StdBuf Buf(getRef()); |
| 336 | Buf.Append(Buf2); |
| 337 | return Buf; |
| 338 | } |
| 339 | |
| 340 | // Compare |
| 341 | bool operator==(const StdBuf &Buf2) const |
| 342 | { |
| 343 | return getSize() == Buf2.getSize() && !Compare(Buf2); |
| 344 | } |
| 345 | |
| 346 | // Set (take if possible) |
| 347 | StdBuf &operator=(StdBuf &&Buf2) |
| 348 | { |
| 349 | if (Buf2.isRef()) |
| 350 | { |
| 351 | Ref(Buf2); |
| 352 | } |
| 353 | else |
| 354 | { |
| 355 | Take(Buf2: std::forward<StdBuf>(t&: Buf2)); |
| 356 | } |
| 357 | return *this; |
| 358 | } |
| 359 | |
| 360 | StdBuf &operator=(const StdBuf &Buf2) { Copy(Buf2); return *this; } |
| 361 | |
| 362 | // *** Compiling |
| 363 | |
| 364 | void CompileFunc(class StdCompiler *pComp, int iType = 0); |
| 365 | |
| 366 | template<class elem_t> |
| 367 | const elem_t *getPtr(size_t pos = 0) const |
| 368 | { |
| 369 | return reinterpret_cast<const elem_t *>(reinterpret_cast<const char *>(getData()) + pos); |
| 370 | } |
| 371 | |
| 372 | template<class elem_t> |
| 373 | elem_t *getMPtr(size_t pos = 0) |
| 374 | { |
| 375 | return reinterpret_cast<elem_t *>(reinterpret_cast<char *>(getMData()) + pos); |
| 376 | } |
| 377 | }; |
| 378 | |
| 379 | // Stringbuffer (operates on null-terminated character buffers) |
| 380 | class StdStrBuf : protected StdBuf |
| 381 | { |
| 382 | public: |
| 383 | // *** Construction |
| 384 | |
| 385 | StdStrBuf() |
| 386 | : StdBuf() {} |
| 387 | |
| 388 | // references the string literal |
| 389 | template<size_t N> |
| 390 | StdStrBuf(const char(&str)[N]) |
| 391 | : StdBuf(str, strlen(str) + 1, false) { } |
| 392 | |
| 393 | // See StdBuf::StdBuf. Copies by default or references if desired. |
| 394 | StdStrBuf(const StdStrBuf &Buf2, bool fCopy = true) : StdBuf(Buf2, fCopy) {} |
| 395 | StdStrBuf(StdStrBuf &&Buf2, bool fCopy = false) : StdBuf(std::forward<StdStrBuf>(t&: Buf2), fCopy) {} |
| 396 | |
| 397 | // Set by constant data. Copies by default or references if desired. |
| 398 | explicit StdStrBuf(const char *pData, bool fCopy = true) |
| 399 | : StdBuf(pData, pData ? strlen(s: pData) + 1 : 0, fCopy) {} |
| 400 | |
| 401 | // As previous constructor, but set length manually. |
| 402 | StdStrBuf(const char *pData, size_t iLength, bool fCopy = true) |
| 403 | : StdBuf(pData, pData ? iLength + 1 : 0, fCopy) {} |
| 404 | |
| 405 | static StdStrBuf MakeRef(const StdStrBuf &Buf2) { return Buf2.getRef(); } |
| 406 | static StdStrBuf MakeRef(const char *str) { return StdStrBuf(str, false); } |
| 407 | |
| 408 | public: |
| 409 | // *** Getters |
| 410 | |
| 411 | bool isNull() const { return StdBuf::isNull(); } |
| 412 | const char *getData() const { return StdBuf::getPtr<char>(); } |
| 413 | char *getMData() { return StdBuf::getMPtr<char>(); } |
| 414 | size_t getSize() const { return StdBuf::getSize(); } |
| 415 | size_t getLength() const { return getSize() ? getSize() - 1 : 0; } |
| 416 | bool isRef() const { return StdBuf::isRef(); } |
| 417 | |
| 418 | const char *getPtr(size_t i) const { return StdBuf::getPtr<char>(pos: i); } |
| 419 | char *getMPtr(size_t i) { return StdBuf::getMPtr<char>(pos: i); } |
| 420 | |
| 421 | // For convenience. Note that writing can't be allowed. |
| 422 | char operator[](size_t i) const { return *getPtr(i); } |
| 423 | |
| 424 | // Analogous to StdBuf |
| 425 | void Ref(const char *pnData) { StdBuf::Ref(pnData, inSize: pnData ? strlen(s: pnData) + 1 : 0); } |
| 426 | void Ref(const char *pnData, size_t iLength) { assert((!pnData && !iLength) || strlen(pnData) == iLength); StdBuf::Ref(pnData, inSize: iLength + 1); } |
| 427 | void Take(StdStrBuf &&Buf2) { StdBuf::Take(Buf2: std::forward<StdStrBuf>(t&: Buf2)); } |
| 428 | void Take(char *pnData) { StdBuf::Take(pnData, inSize: pnData ? strlen(s: pnData) + 1 : 0); } |
| 429 | void Take(char *pnData, size_t iLength) { assert((!pnData && !iLength) || strlen(pnData) == iLength); StdBuf::Take(pnData, inSize: iLength + 1); } |
| 430 | char *GrabPointer() { return reinterpret_cast<char *>(StdBuf::GrabPointer()); } |
| 431 | |
| 432 | void Ref(const StdStrBuf &Buf2) { StdBuf::Ref(pnData: Buf2.getData(), inSize: Buf2.getSize()); } |
| 433 | StdStrBuf getRef() const { return StdStrBuf(getData(), getLength(), false); } |
| 434 | void Take(StdStrBuf &Buf2) { StdBuf::Take(Buf2); } |
| 435 | |
| 436 | void Clear() { StdBuf::Clear(); } |
| 437 | void Copy() { StdBuf::Copy(); } |
| 438 | void Copy(const char *pnData) { StdBuf::Copy(pnData, inSize: pnData ? strlen(s: pnData) + 1 : 0); } |
| 439 | void Copy(const StdStrBuf &Buf2) { StdBuf::Copy(Buf2); } |
| 440 | StdStrBuf Duplicate() const { StdStrBuf Buf; Buf.Copy(Buf2: *this); return Buf; } |
| 441 | void Move(size_t iFrom, size_t inSize, size_t iTo = 0) { StdBuf::Move(iFrom, inSize, iTo); } |
| 442 | |
| 443 | // Byte-wise compare (will compare characters up to the length of the second string) |
| 444 | int Compare(const StdStrBuf &Buf2, size_t iAt = 0) const |
| 445 | { |
| 446 | assert(iAt <= getLength()); |
| 447 | return StdBuf::Compare(pCData: Buf2.getData(), iCSize: Buf2.getLength(), iAt); |
| 448 | } |
| 449 | |
| 450 | // Grows the string to contain the specified number more/less characters. |
| 451 | // Note: Will set the terminator, but won't initialize - use Append* instead. |
| 452 | void Grow(size_t iGrow) |
| 453 | { |
| 454 | StdBuf::Grow(iGrow: getSize() ? iGrow : iGrow + 1); |
| 455 | *getMPtr(i: getLength()) = '\0'; |
| 456 | } |
| 457 | |
| 458 | void Shrink(size_t iShrink) |
| 459 | { |
| 460 | assert(iShrink <= getLength()); |
| 461 | StdBuf::Shrink(iShrink); |
| 462 | *getMPtr(i: getLength()) = '\0'; |
| 463 | } |
| 464 | |
| 465 | void SetLength(size_t iLength) |
| 466 | { |
| 467 | if (iLength == getLength() && !isNull()) return; |
| 468 | if (iLength >= getLength()) |
| 469 | Grow(iGrow: iLength - getLength()); |
| 470 | else |
| 471 | Shrink(iShrink: getLength() - iLength); |
| 472 | } |
| 473 | |
| 474 | // Append string |
| 475 | void Append(const char *pnData, size_t iChars) |
| 476 | { |
| 477 | Grow(iGrow: iChars); |
| 478 | Write(pnData, inSize: iChars, iAt: iSize - iChars - 1); |
| 479 | } |
| 480 | |
| 481 | void Append(const char *pnData) |
| 482 | { |
| 483 | Append(pnData, iChars: strlen(s: pnData)); |
| 484 | } |
| 485 | |
| 486 | void Append(const StdStrBuf &Buf2) |
| 487 | { |
| 488 | Append(pnData: Buf2.getData(), iChars: Buf2.getLength()); |
| 489 | } |
| 490 | |
| 491 | // Copy string |
| 492 | void Copy(const char *pnData, size_t iChars) |
| 493 | { |
| 494 | Clear(); |
| 495 | Append(pnData, iChars); |
| 496 | } |
| 497 | |
| 498 | // * File support |
| 499 | bool LoadFromFile(const char *szFile); |
| 500 | bool SaveToFile(const char *szFile) const; |
| 501 | |
| 502 | // * Operators |
| 503 | |
| 504 | bool operator!() const { return isNull(); } |
| 505 | |
| 506 | StdStrBuf &operator+=(const StdStrBuf &Buf2) { Append(Buf2); return *this; } |
| 507 | StdStrBuf &operator+=(const char *szString) { Append(pnData: szString); return *this; } |
| 508 | StdStrBuf operator+(const StdStrBuf &Buf2) const { StdStrBuf Buf = getRef(); Buf.Append(Buf2); return Buf; } |
| 509 | StdStrBuf operator+(const char *szString) const { StdStrBuf Buf = getRef(); Buf.Append(pnData: szString); return Buf; } |
| 510 | |
| 511 | bool operator==(const StdStrBuf &Buf2) const |
| 512 | { |
| 513 | return getLength() == Buf2.getLength() && !Compare(Buf2); |
| 514 | } |
| 515 | |
| 516 | bool operator==(const char *szString) const { return StdStrBuf(szString, false) == *this; } |
| 517 | |
| 518 | // Note this references the data. |
| 519 | StdStrBuf &operator=(const StdStrBuf &Buf2) { Copy(Buf2); return *this; } |
| 520 | |
| 521 | template<std::convertible_to<const char *> T> |
| 522 | StdStrBuf &operator=(T szString) { Copy(szString); return *this; } |
| 523 | |
| 524 | template<size_t N> |
| 525 | StdStrBuf &operator=(const char (&szString)[N]) { Ref(szString); return *this; } |
| 526 | |
| 527 | explicit operator bool() const { return getData(); } |
| 528 | |
| 529 | // less-than operation for map |
| 530 | inline bool operator<(const StdStrBuf &v2) |
| 531 | { |
| 532 | size_t iLen = getLength(), iLen2 = v2.getLength(); |
| 533 | if (iLen == iLen2) |
| 534 | return iLen ? (strcmp(s1: getData(), s2: v2.getData()) < 0) : false; |
| 535 | else |
| 536 | return iLen < iLen2; |
| 537 | } |
| 538 | |
| 539 | // * String specific |
| 540 | |
| 541 | void AppendChars(char cChar, size_t iCnt) |
| 542 | { |
| 543 | Grow(iGrow: iCnt); |
| 544 | for (size_t i = getLength() - iCnt; i < getLength(); i++) |
| 545 | *getMPtr(i) = cChar; |
| 546 | } |
| 547 | |
| 548 | void AppendChar(char cChar) |
| 549 | { |
| 550 | AppendChars(cChar, iCnt: 1); |
| 551 | } |
| 552 | |
| 553 | void InsertChar(char cChar, size_t insert_before) |
| 554 | { |
| 555 | assert(insert_before <= getLength()); |
| 556 | Grow(iGrow: 1); |
| 557 | for (size_t i = getLength() - 1; i > insert_before; --i) |
| 558 | *getMPtr(i) = *getPtr(i: i - 1); |
| 559 | *getMPtr(i: insert_before) = cChar; |
| 560 | } |
| 561 | |
| 562 | // Append data until given character (or string end) occurs. |
| 563 | void AppendUntil(const char *szString, char cUntil) |
| 564 | { |
| 565 | const char *pPos = strchr(s: szString, c: cUntil); |
| 566 | if (pPos) |
| 567 | Append(pnData: szString, iChars: pPos - szString); |
| 568 | else |
| 569 | Append(pnData: szString); |
| 570 | } |
| 571 | |
| 572 | // See above |
| 573 | void CopyUntil(const char *szString, char cUntil) |
| 574 | { |
| 575 | Clear(); |
| 576 | AppendUntil(szString, cUntil); |
| 577 | } |
| 578 | |
| 579 | // cut end after given char into another string. Return whether char was found at all |
| 580 | bool SplitAtChar(char cSplit, StdStrBuf *psSplit) |
| 581 | { |
| 582 | if (!getData()) return false; |
| 583 | const char *pPos = strchr(s: getData(), c: cSplit); |
| 584 | if (!pPos) return false; |
| 585 | size_t iPos = pPos - getData(); |
| 586 | if (psSplit) psSplit->Take(Buf2: copyPart(iStart: iPos + 1, inSize: getLength() - iPos - 1)); |
| 587 | Shrink(iShrink: getLength() - iPos); |
| 588 | return true; |
| 589 | } |
| 590 | |
| 591 | StdStrBuf copyPart(size_t iStart, size_t inSize) const |
| 592 | { |
| 593 | assert(iStart + inSize <= iSize); |
| 594 | if (!inSize) return StdStrBuf(); |
| 595 | StdStrBuf sResult; |
| 596 | sResult.Copy(pnData: getPtr(i: iStart), iChars: inSize); |
| 597 | return sResult; |
| 598 | } |
| 599 | |
| 600 | // replace all occurences of one string with another. Return number of replacements. |
| 601 | int Replace(const char *szOld, const char *szNew, size_t iStartSearch = 0); |
| 602 | int ReplaceChar(char cOld, char cNew, size_t iStartSearch = 0); |
| 603 | |
| 604 | // replace the trailing part of a string with something else |
| 605 | void ReplaceEnd(size_t iPos, const char *szNewEnd); |
| 606 | |
| 607 | // get an indexed section from the string like Section1;Section2;Section3 |
| 608 | bool GetSection(size_t idx, StdStrBuf *psOutSection, char cSeparator = ';') const; |
| 609 | |
| 610 | // Checks wether the contents are valid UTF-8, and if not, convert them from windows-1252 to UTF-8. |
| 611 | void EnsureUnicode(); |
| 612 | |
| 613 | // check if a string consists only of the given chars |
| 614 | bool ValidateChars(const char *szInitialChars, const char *szMidChars); |
| 615 | |
| 616 | void EscapeString() |
| 617 | { |
| 618 | Replace(szOld: "\\" , szNew: "\\\\" ); |
| 619 | Replace(szOld: "\"" , szNew: "\\\"" ); |
| 620 | } |
| 621 | |
| 622 | bool TrimSpaces(); // kill spaces at beginning and end. Return if changed. |
| 623 | |
| 624 | // * Compiling |
| 625 | void CompileFunc(class StdCompiler *pComp, int iRawType = 0); |
| 626 | }; |
| 627 | |