master ohmu/fastfetch-openbsd / tests / strbuf.c
ohmu  ·  2026-10-15
   1#include "common/FFstrbuf.h"
   2#include "common/textModifier.h"
   3
   4#include <string.h>
   5#include <stdlib.h>
   6#include <stdio.h>
   7#include <stdnoreturn.h>
   8
   9noreturn static void testFailed(const FFstrbuf* strbuf, const char* expression, int lineNo) {
  10    fputs(FASTFETCH_TEXT_MODIFIER_ERROR, stderr);
  11    fprintf(stderr, "[%d] %s, strbuf:", lineNo, expression);
  12    ffStrbufWriteTo(strbuf, stderr);
  13    fputs(FASTFETCH_TEXT_MODIFIER_RESET, stderr);
  14    fputc('\n', stderr);
  15    exit(1);
  16}
  17
  18#define VERIFY(expression) \
  19    if (!(expression)) testFailed(&strbuf, #expression, __LINE__)
  20
  21int shouldNotBeCalled(int c) {
  22    (void) c;
  23    exit(1);
  24}
  25
  26const FFstrbuf testCreateStatic = FF_STRBUF_STATIC("TEST");
  27
  28int main(void) {
  29    FFstrbuf strbuf;
  30
  31    // destroy 0
  32    ffStrbufInit(&strbuf);
  33    ffStrbufDestroy(&strbuf);
  34
  35    // initA
  36
  37    ffStrbufInit(&strbuf);
  38
  39    VERIFY(strbuf.chars[0] == 0);
  40    VERIFY(strbuf.allocated == 0);
  41    VERIFY(strbuf.length == 0);
  42
  43    VERIFY(!ffStrbufStartsWithC(&strbuf, '0'));
  44    VERIFY(!ffStrbufEndsWithC(&strbuf, '0'));
  45    VERIFY(!ffStrbufStartsWithS(&strbuf, "0"));
  46    VERIFY(!ffStrbufEndsWithS(&strbuf, "0"));
  47    VERIFY(ffStrbufCountC(&strbuf, '0') == 0);
  48
  49    // Ensure following functions work with non-allocated string
  50    ffStrbufAppendS(&strbuf, "");
  51    ffStrbufAppendF(&strbuf, "%s", "");
  52    ffStrbufAppendTransformS(&strbuf, "", shouldNotBeCalled);
  53    ffStrbufPrependS(&strbuf, "");
  54    ffStrbufTrim(&strbuf, ' ');
  55
  56    VERIFY(strbuf.chars[0] == 0);
  57    VERIFY(strbuf.allocated == 0);
  58    VERIFY(strbuf.length == 0);
  59
  60    // append(N)C
  61
  62    ffStrbufAppendC(&strbuf, '1');
  63    VERIFY(ffStrbufEqualS(&strbuf, "1"));
  64    VERIFY(strbuf.allocated >= 1);
  65    ffStrbufAppendNC(&strbuf, 5, '2');
  66    VERIFY(ffStrbufEqualS(&strbuf, "122222"));
  67    VERIFY(strbuf.allocated >= 6);
  68    ffStrbufClear(&strbuf);
  69
  70    // appendS
  71
  72    ffStrbufAppendS(&strbuf, "12345");
  73    ffStrbufAppendS(&strbuf, NULL);
  74
  75    VERIFY(strbuf.length == 5);
  76    VERIFY(strbuf.allocated >= 6);
  77    VERIFY(ffStrbufEqualS(&strbuf, "12345"));
  78
  79    // appendNS
  80
  81    ffStrbufAppendNS(&strbuf, 4, "67890");
  82    ffStrbufAppendNS(&strbuf, 0, NULL);
  83
  84    VERIFY(strbuf.length == 9);
  85    VERIFY(strbuf.allocated >= 10);
  86    VERIFY(ffStrbufEqualS(&strbuf, "123456789"));
  87
  88    // appendS long
  89
  90    ffStrbufAppendS(&strbuf, "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
  91    VERIFY(strbuf.length == 109);
  92    VERIFY(strbuf.allocated >= 110);
  93    VERIFY(strbuf.chars[strbuf.length] == 0);
  94
  95    // substr
  96
  97    VERIFY(ffStrbufSubstrBefore(&strbuf, 9));
  98    VERIFY(strbuf.length == 9);
  99    VERIFY(strbuf.allocated >= 110);
 100    VERIFY(strbuf.chars[strbuf.length] == 0);
 101    VERIFY(ffStrbufEqualS(&strbuf, "123456789"));
 102
 103    VERIFY(!ffStrbufSubstrBeforeFirstC(&strbuf, '0'));
 104    VERIFY(!ffStrbufSubstrBeforeLastC(&strbuf, '0'));
 105    VERIFY(!ffStrbufSubstrAfterFirstC(&strbuf, '0'));
 106    VERIFY(!ffStrbufSubstrAfterLastC(&strbuf, '0'));
 107    VERIFY(ffStrbufEqualS(&strbuf, "123456789"));
 108
 109    VERIFY(ffStrbufSubstrBeforeFirstC(&strbuf, '9'));
 110    VERIFY(ffStrbufSubstrBeforeLastC(&strbuf, '8'));
 111    VERIFY(ffStrbufSubstrAfterFirstC(&strbuf, '1'));
 112    VERIFY(ffStrbufSubstrAfterLastC(&strbuf, '2'));
 113    VERIFY(ffStrbufEqualS(&strbuf, "34567"));
 114
 115    ffStrbufSetS(&strbuf, "123456789");
 116
 117    // startsWithC
 118
 119    VERIFY(ffStrbufStartsWithC(&strbuf, '1'));
 120    VERIFY(!ffStrbufStartsWithC(&strbuf, '2'));
 121
 122    // startsWithS
 123
 124    VERIFY(ffStrbufStartsWithS(&strbuf, "123"));
 125    VERIFY(ffStrbufStartsWithS(&strbuf, "123456789"));
 126    VERIFY(!ffStrbufStartsWithS(&strbuf, "1234567890123"));
 127
 128    // endsWithC
 129    VERIFY(ffStrbufEndsWithC(&strbuf, '9'));
 130    VERIFY(!ffStrbufEndsWithC(&strbuf, '1'));
 131
 132    // endsWithS
 133
 134    VERIFY(ffStrbufEndsWithS(&strbuf, "789"));
 135    VERIFY(ffStrbufEndsWithS(&strbuf, "123456789"));
 136    VERIFY(!ffStrbufEndsWithS(&strbuf, "1234567890123"));
 137
 138    // toNumber
 139
 140    VERIFY(ffStrbufToDouble(&strbuf, -DBL_MAX) == 123456789.0);
 141    VERIFY(ffStrbufToUInt(&strbuf, 999) == 123456789);
 142
 143    // countC
 144
 145    VERIFY(ffStrbufCountC(&strbuf, '1') == 1);
 146    VERIFY(ffStrbufCountC(&strbuf, '0') == 0);
 147
 148    // removeS
 149
 150    ffStrbufRemoveS(&strbuf, "78");
 151
 152    VERIFY(strbuf.length == 7);
 153    VERIFY(strcmp(strbuf.chars, "1234569") == 0);
 154
 155    // removeStrings
 156
 157    ffStrbufRemoveStrings(&strbuf, 3, (const char*[]) { "23", "45", "9" });
 158
 159    VERIFY(strbuf.length == 2);
 160    VERIFY(strcmp(strbuf.chars, "16") == 0);
 161
 162    // PrependS
 163
 164    ffStrbufPrependS(&strbuf, "123");
 165    ffStrbufPrependS(&strbuf, NULL);
 166
 167    VERIFY(strbuf.length == 5);
 168    VERIFY(strcmp(strbuf.chars, "12316") == 0);
 169
 170    // indexC
 171    VERIFY(ffStrbufFirstIndexC(&strbuf, '1') == 0);
 172    VERIFY(ffStrbufNextIndexC(&strbuf, 1, '1') == 3);
 173    VERIFY(ffStrbufNextIndexC(&strbuf, 4, '1') == 5);
 174    VERIFY(ffStrbufLastIndexC(&strbuf, '1') == 3);
 175    VERIFY(ffStrbufPreviousIndexC(&strbuf, 2, '1') == 0);
 176    VERIFY(ffStrbufPreviousIndexC(&strbuf, 0, '1') == 0);
 177    VERIFY(ffStrbufPreviousIndexC(&strbuf, 0, '2') == 5);
 178
 179    // indexS
 180    VERIFY(ffStrbufFirstIndexS(&strbuf, "12316") == 0);
 181    VERIFY(ffStrbufNextIndexS(&strbuf, 1, "1") == 3);
 182    VERIFY(ffStrbufNextIndexS(&strbuf, 4, "1") == 5);
 183
 184    // ignCase
 185    ffStrbufSetS(&strbuf, "AbCdEfG");
 186
 187    VERIFY(ffStrbufIgnCaseCompS(&strbuf, "aBcDeFg") == 0);
 188    VERIFY(ffStrbufStartsWithIgnCaseS(&strbuf, "ABCD"));
 189    VERIFY(ffStrbufEndsWithIgnCaseS(&strbuf, "defg"));
 190    VERIFY(!ffStrbufStartsWithIgnCaseS(&strbuf, "aBcDeFgH"));
 191    VERIFY(!ffStrbufEndsWithIgnCaseS(&strbuf, "0aBcDeFg"));
 192
 193    // ensure
 194    ffStrbufEnsureEndsWithC(&strbuf, '$');
 195    VERIFY(ffStrbufEqualS(&strbuf, "AbCdEfG$"));
 196    ffStrbufEnsureEndsWithC(&strbuf, '$');
 197    VERIFY(ffStrbufEqualS(&strbuf, "AbCdEfG$"));
 198
 199    // trimRight
 200    ffStrbufTrimRight(&strbuf, '$');
 201    VERIFY(ffStrbufEqualS(&strbuf, "AbCdEfG"));
 202    ffStrbufTrimRight(&strbuf, '$');
 203    VERIFY(ffStrbufEqualS(&strbuf, "AbCdEfG"));
 204
 205    // clear
 206    ffStrbufClear(&strbuf);
 207    VERIFY(strbuf.allocated > 0);
 208    VERIFY(strbuf.length == 0);
 209    VERIFY(strbuf.chars && strbuf.chars[0] == 0);
 210
 211    // Destroy
 212
 213    ffStrbufDestroy(&strbuf);
 214
 215    VERIFY(strbuf.allocated == 0);
 216    VERIFY(strbuf.length == 0);
 217    VERIFY(strbuf.chars && strbuf.chars[0] == 0);
 218
 219    // initA
 220    ffStrbufInitA(&strbuf, 32);
 221
 222    VERIFY(strbuf.allocated == 32);
 223    VERIFY(strbuf.length == 0);
 224    VERIFY(strbuf.chars && strbuf.chars[0] == 0);
 225
 226    // appendF
 227    ffStrbufAppendF(&strbuf, "%s", "1234567890123456789012345678901");
 228    VERIFY(strbuf.allocated == 32);
 229    VERIFY(ffStrbufEqualS(&strbuf, "1234567890123456789012345678901"));
 230
 231    ffStrbufDestroy(&strbuf);
 232
 233    // initMoveS
 234    {
 235        char* heapStr = strdup("1234567890");
 236        ffStrbufInitMoveS(&strbuf, heapStr);
 237        VERIFY(ffStrbufEqualS(&strbuf, "1234567890"));
 238        VERIFY(strbuf.allocated >= 11);
 239        ffStrbufDestroy(&strbuf);
 240    }
 241
 242    // initF
 243    ffStrbufInitF(&strbuf, "%s", "1234567890123456789012345678901");
 244    VERIFY(strbuf.allocated >= 32);
 245    VERIFY(ffStrbufEqualS(&strbuf, "1234567890123456789012345678901"));
 246
 247    // containC
 248    VERIFY(ffStrbufContainC(&strbuf, '1'));
 249    VERIFY(!ffStrbufContainC(&strbuf, '-'));
 250
 251    // replaceAllC
 252    ffStrbufReplaceAllC(&strbuf, '1', '-');
 253    VERIFY(ffStrbufEqualS(&strbuf, "-234567890-234567890-234567890-"));
 254    ffStrbufReplaceAllC(&strbuf, '1', '-');
 255    VERIFY(ffStrbufEqualS(&strbuf, "-234567890-234567890-234567890-"));
 256
 257    // trim
 258    ffStrbufTrim(&strbuf, '-');
 259    VERIFY(ffStrbufEqualS(&strbuf, "234567890-234567890-234567890"));
 260
 261    ffStrbufDestroy(&strbuf);
 262
 263    // ffStrbufCreateS
 264    {
 265        FF_STRBUF_AUTO_DESTROY testCreate = ffStrbufCreateS("TEST");
 266        VERIFY(ffStrbufEqualS(&testCreate, "TEST"));
 267    }
 268
 269    // ffStrbufCreateStatic
 270    ffStrbufInitStatic(&strbuf, "TEST");
 271    VERIFY(ffStrbufEqualS(&strbuf, "TEST"));
 272    VERIFY(strbuf.length == 4);
 273    VERIFY(strbuf.allocated == 0);
 274
 275    ffStrbufDestroy(&strbuf);
 276    VERIFY(strbuf.length == 0);
 277    VERIFY(strbuf.allocated == 0);
 278
 279    // ffStrbufCreateStatic / Allocate
 280    ffStrbufInitStatic(&strbuf, "TEST");
 281    ffStrbufEnsureFree(&strbuf, 0);
 282    VERIFY(ffStrbufEqualS(&strbuf, "TEST"));
 283    VERIFY(strbuf.length == 4);
 284    VERIFY(strbuf.allocated > 0);
 285
 286    // ffStrbufCreateStatic / Append
 287    ffStrbufInitStatic(&strbuf, "TEST");
 288    ffStrbufAppendS(&strbuf, "_TEST");
 289    VERIFY(ffStrbufEqualS(&strbuf, "TEST_TEST"));
 290    VERIFY(strbuf.length == 9);
 291    VERIFY(strbuf.allocated >= 10);
 292    ffStrbufAppendC(&strbuf, '_');
 293    VERIFY(ffStrbufEqualS(&strbuf, "TEST_TEST_"));
 294    ffStrbufDestroy(&strbuf);
 295    VERIFY(strbuf.length == 0);
 296    VERIFY(strbuf.allocated == 0);
 297
 298    // ffStrbufCreateStatic / Prepend
 299    ffStrbufInitStatic(&strbuf, "TEST");
 300    ffStrbufPrependS(&strbuf, "TEST_");
 301    VERIFY(ffStrbufEqualS(&strbuf, "TEST_TEST"));
 302    VERIFY(strbuf.length == 9);
 303    VERIFY(strbuf.allocated >= 10);
 304    ffStrbufPrependC(&strbuf, '_');
 305    VERIFY(ffStrbufEqualS(&strbuf, "_TEST_TEST"));
 306    ffStrbufDestroy(&strbuf);
 307    VERIFY(strbuf.length == 0);
 308    VERIFY(strbuf.allocated == 0);
 309
 310    // ffStrbufCreateStatic / Clear
 311    ffStrbufInitStatic(&strbuf, "TEST");
 312    ffStrbufClear(&strbuf);
 313    VERIFY(strbuf.length == 0);
 314    VERIFY(strbuf.allocated == 0);
 315    ffStrbufDestroy(&strbuf);
 316
 317    // ffStrbufCreateStatic / Set
 318    ffStrbufInitStatic(&strbuf, "TEST"); // static
 319    ffStrbufSetStatic(&strbuf, "test");
 320    VERIFY(ffStrbufEqualS(&strbuf, "test"));
 321    VERIFY(strbuf.length == 4);
 322    VERIFY(strbuf.allocated == 0);
 323    ffStrbufDestroy(&strbuf);
 324
 325    // ffStrbufCreateStatic / Set
 326    ffStrbufInitS(&strbuf, "TEST"); // allocated
 327    ffStrbufSetStatic(&strbuf, "test");
 328    VERIFY(ffStrbufEqualS(&strbuf, "test"));
 329    VERIFY(strbuf.length == 4);
 330    VERIFY(strbuf.allocated == 0);
 331    ffStrbufDestroy(&strbuf);
 332
 333    // ffStrbufCreateStatic / TrimL
 334    ffStrbufInitStatic(&strbuf, "_TEST_");
 335    ffStrbufTrimLeft(&strbuf, '_');
 336    VERIFY(ffStrbufEqualS(&strbuf, "TEST_"));
 337    VERIFY(strbuf.length == 5);
 338    VERIFY(strbuf.allocated == 0);
 339    ffStrbufDestroy(&strbuf);
 340
 341    // ffStrbufCreateStatic / TrimR
 342    ffStrbufInitStatic(&strbuf, "_TEST_");
 343    ffStrbufTrimRight(&strbuf, ' ');
 344    VERIFY(strbuf.allocated == 0);
 345    VERIFY(ffStrbufEqualS(&strbuf, "_TEST_"));
 346    ffStrbufTrimRight(&strbuf, '_');
 347    VERIFY(ffStrbufEqualS(&strbuf, "_TEST"));
 348    VERIFY(strbuf.length == 5);
 349    VERIFY(strbuf.allocated > 0);
 350    ffStrbufDestroy(&strbuf);
 351
 352    // ffStrbufCreateStatic / Substr
 353    ffStrbufInitStatic(&strbuf, "__TEST__");
 354    VERIFY(ffStrbufRemoveSubstr(&strbuf, 0, 6));
 355    VERIFY(ffStrbufEqualS(&strbuf, "__"));
 356    VERIFY(strbuf.length == 2);
 357    VERIFY(strbuf.allocated > 0);
 358    ffStrbufDestroy(&strbuf);
 359
 360    // ffStrbufCreateStatic / Substr
 361    ffStrbufInitStatic(&strbuf, "__TEST__");
 362    VERIFY(ffStrbufRemoveSubstr(&strbuf, 2, 8));
 363    VERIFY(ffStrbufEqualS(&strbuf, "__"));
 364    VERIFY(strbuf.length == 2);
 365    VERIFY(strbuf.allocated > 0);
 366    ffStrbufDestroy(&strbuf);
 367
 368    // ffStrbufCreateStatic / Substr
 369    ffStrbufInitStatic(&strbuf, "__TEST__");
 370    VERIFY(ffStrbufRemoveSubstr(&strbuf, 2, 6));
 371    VERIFY(ffStrbufEqualS(&strbuf, "____"));
 372    VERIFY(strbuf.length == 4);
 373    VERIFY(strbuf.allocated > 0);
 374    ffStrbufDestroy(&strbuf);
 375
 376    // ffStrbufCreateStatic / ReplaceAllC
 377    ffStrbufInitStatic(&strbuf, "__TEST__");
 378    ffStrbufReplaceAllC(&strbuf, '_', '-');
 379    VERIFY(ffStrbufEqualS(&strbuf, "--TEST--"));
 380    VERIFY(strbuf.length == 8);
 381    VERIFY(strbuf.allocated > 0);
 382    ffStrbufDestroy(&strbuf);
 383
 384    // ffStrbufCreateStatic / TrimSpace
 385    ffStrbufInitStatic(&strbuf, "\n TEST\n ");
 386    ffStrbufTrimSpace(&strbuf);
 387    VERIFY(strbuf.length == 4);
 388    VERIFY(strbuf.allocated > 0);
 389    VERIFY(ffStrbufEqualS(&strbuf, "TEST"));
 390    ffStrbufDestroy(&strbuf);
 391
 392    // ffStrbufCreate / TrimSpace
 393    ffStrbufInitS(&strbuf, "\n TEST\n ");
 394    ffStrbufTrimSpace(&strbuf);
 395    VERIFY(strbuf.length == 4);
 396    VERIFY(strbuf.allocated > 0);
 397    VERIFY(ffStrbufEqualS(&strbuf, "TEST"));
 398    ffStrbufDestroy(&strbuf);
 399
 400    // ffStrbufEnsureFixedLengthFree / empty buffer
 401    ffStrbufInit(&strbuf);
 402    ffStrbufEnsureFixedLengthFree(&strbuf, 10);
 403    VERIFY(strbuf.length == 0);
 404    VERIFY(strbuf.allocated == 11);
 405    ffStrbufDestroy(&strbuf);
 406    ffStrbufInitA(&strbuf, 10);
 407    ffStrbufEnsureFixedLengthFree(&strbuf, 10);
 408    VERIFY(strbuf.length == 0);
 409    VERIFY(strbuf.allocated == 11);
 410    ffStrbufEnsureFixedLengthFree(&strbuf, 12);
 411    VERIFY(strbuf.length == 0);
 412    VERIFY(strbuf.allocated == 13);
 413    ffStrbufDestroy(&strbuf);
 414
 415    // ffStrbufEnsureFixedLengthFree / empty buffer with zero free length
 416    ffStrbufInit(&strbuf);
 417    ffStrbufEnsureFixedLengthFree(&strbuf, 0);
 418    VERIFY(strbuf.length == 0);
 419    VERIFY(strbuf.allocated == 1);
 420    ffStrbufDestroy(&strbuf);
 421
 422    // ffStrbufEnsureFixedLengthFree / empty buffer but oldFree >= newFree
 423    ffStrbufInitA(&strbuf, 11);
 424    ffStrbufEnsureFixedLengthFree(&strbuf, 10);
 425    VERIFY(strbuf.length == 0);
 426    VERIFY(strbuf.allocated == 11);
 427    ffStrbufDestroy(&strbuf);
 428    ffStrbufInitA(&strbuf, 12);
 429    ffStrbufEnsureFixedLengthFree(&strbuf, 10);
 430    VERIFY(strbuf.length == 0);
 431    VERIFY(strbuf.allocated == 12);
 432    ffStrbufDestroy(&strbuf);
 433
 434    // ffStrbufEnsureFixedLengthFree / non empty buffer
 435    ffStrbufAppendF(&strbuf, "%s", "1234567890");
 436    VERIFY(strbuf.length == 10);
 437    VERIFY(strbuf.allocated == 32);
 438    ffStrbufEnsureFixedLengthFree(&strbuf, 0);
 439    VERIFY(strbuf.length == 10);
 440    VERIFY(strbuf.allocated == 32);
 441    ffStrbufEnsureFixedLengthFree(&strbuf, 20); // less than oldFree (=21)
 442    VERIFY(strbuf.length == 10);
 443    VERIFY(strbuf.allocated == 32);
 444    ffStrbufEnsureFixedLengthFree(&strbuf, 21); // equal to oldFree (=21)
 445    VERIFY(strbuf.length == 10);
 446    VERIFY(strbuf.allocated == 32);
 447    ffStrbufEnsureFixedLengthFree(&strbuf, 22); // greater than oldFree (=21)
 448    VERIFY(strbuf.length == 10);
 449    VERIFY(strbuf.allocated == 33);
 450    ffStrbufDestroy(&strbuf);
 451
 452    // ffStrbufEnsureFixedLengthFree / static buffer
 453    ffStrbufInitStatic(&strbuf, "__TEST__");
 454    VERIFY(strbuf.length > 0);
 455    VERIFY(strbuf.allocated == 0);
 456    ffStrbufEnsureFixedLengthFree(&strbuf, 10);
 457    VERIFY(strbuf.length == strlen("__TEST__"));
 458    VERIFY(strbuf.allocated == strlen("__TEST__") + 1 + 10);
 459    VERIFY(ffStrbufEqualS(&strbuf, "__TEST__"));
 460    ffStrbufDestroy(&strbuf);
 461
 462    // ffStrbufInsertNC
 463    ffStrbufInitStatic(&strbuf, "123456");
 464    ffStrbufInsertNC(&strbuf, 0, 2, 'A');
 465    VERIFY(ffStrbufEqualS(&strbuf, "AA123456"));
 466    ffStrbufInsertNC(&strbuf, 4, 2, 'B');
 467    VERIFY(ffStrbufEqualS(&strbuf, "AA12BB3456"));
 468    ffStrbufInsertNC(&strbuf, strbuf.length, 2, 'C');
 469    VERIFY(ffStrbufEqualS(&strbuf, "AA12BB3456CC"));
 470    ffStrbufInsertNC(&strbuf, 999, 2, 'D');
 471    VERIFY(ffStrbufEqualS(&strbuf, "AA12BB3456CCDD"));
 472    ffStrbufDestroy(&strbuf);
 473
 474    // smallest allocation test
 475    {
 476        FF_STRBUF_AUTO_DESTROY strbuf1 = ffStrbufCreateA(10);
 477        VERIFY(strbuf1.allocated == 10);
 478        ffStrbufEnsureFree(&strbuf1, 16);
 479        VERIFY(strbuf1.allocated == 32);
 480
 481        FF_STRBUF_AUTO_DESTROY strbuf2 = ffStrbufCreate();
 482        VERIFY(strbuf2.allocated == 0);
 483        ffStrbufEnsureFree(&strbuf2, 16);
 484        VERIFY(strbuf2.allocated == 32);
 485    }
 486
 487    {
 488        int i = 0;
 489        char* lineptr = NULL;
 490        size_t n = 0;
 491        const char* text = "Processor\t: ARMv7\nprocessor\t: 0\nBogoMIPS\t: 38.00\n\nprocessor\t: 1\nBogoMIPS\t: 38.00";
 492        ffStrbufSetS(&strbuf, text);
 493
 494        while (ffStrbufGetline(&lineptr, &n, &strbuf)) {
 495            ++i;
 496            switch (i) {
 497                case 1:
 498                    VERIFY(strcmp(lineptr, "Processor\t: ARMv7") == 0);
 499                    VERIFY(n == strlen("Processor\t: ARMv7"));
 500                    break;
 501                case 2:
 502                    VERIFY(strcmp(lineptr, "processor\t: 0") == 0);
 503                    VERIFY(n == strlen("processor\t: 0"));
 504                    break;
 505                case 3:
 506                    VERIFY(strcmp(lineptr, "BogoMIPS\t: 38.00") == 0);
 507                    VERIFY(n == strlen("BogoMIPS\t: 38.00"));
 508                    break;
 509                case 4:
 510                    VERIFY(strcmp(lineptr, "") == 0);
 511                    VERIFY(n == 0);
 512                    break;
 513                case 5:
 514                    VERIFY(strcmp(lineptr, "processor\t: 1") == 0);
 515                    VERIFY(n == strlen("processor\t: 1"));
 516                    break;
 517                case 6:
 518                    VERIFY(strcmp(lineptr, "BogoMIPS\t: 38.00") == 0);
 519                    VERIFY(n == strlen("BogoMIPS\t: 38.00"));
 520                    break;
 521                default:
 522                    VERIFY(false);
 523                    break;
 524            }
 525        }
 526        VERIFY(ffStrbufEqualS(&strbuf, text));
 527        VERIFY(*lineptr == '\0');
 528        VERIFY(i == 6);
 529
 530        lineptr = NULL;
 531        n = 0;
 532        i = 0;
 533        text = "\n";
 534        ffStrbufSetS(&strbuf, text);
 535        while (ffStrbufGetline(&lineptr, &n, &strbuf)) {
 536            ++i;
 537            switch (i) {
 538                case 1:
 539                    VERIFY(strcmp(lineptr, "") == 0);
 540                    VERIFY(n == 0);
 541                    break;
 542                default:
 543                    VERIFY(false);
 544                    break;
 545            }
 546        }
 547        VERIFY(ffStrbufEqualS(&strbuf, text));
 548        VERIFY(*lineptr == '\0');
 549        VERIFY(i == 1);
 550
 551        lineptr = NULL;
 552        n = 0;
 553        i = 0;
 554        text = "abcd";
 555        ffStrbufSetS(&strbuf, text);
 556        while (ffStrbufGetline(&lineptr, &n, &strbuf)) {
 557            ++i;
 558            switch (i) {
 559                case 1:
 560                    VERIFY(strcmp(lineptr, "abcd") == 0);
 561                    VERIFY(n == strlen("abcd"));
 562                    break;
 563                default:
 564                    VERIFY(false);
 565                    break;
 566            }
 567        }
 568        VERIFY(ffStrbufEqualS(&strbuf, text));
 569        VERIFY(*lineptr == '\0');
 570        VERIFY(i == 1);
 571
 572        lineptr = NULL;
 573        n = 0;
 574        i = 0;
 575        text = "";
 576        ffStrbufSetS(&strbuf, text);
 577        while (ffStrbufGetline(&lineptr, &n, &strbuf)) {
 578            ++i;
 579            VERIFY(false);
 580        }
 581
 582        VERIFY(ffStrbufEqualS(&strbuf, text));
 583        VERIFY(*lineptr == '\0');
 584        VERIFY(i == 0);
 585    }
 586
 587    ffStrbufSetS(&strbuf, "Hello World");
 588    VERIFY(ffStrbufRemoveDupWhitespaces(&strbuf) == false);
 589    VERIFY(strcmp(strbuf.chars, "Hello World") == 0);
 590
 591    ffStrbufSetS(&strbuf, "Hello   World");
 592    VERIFY(ffStrbufRemoveDupWhitespaces(&strbuf) == true);
 593    VERIFY(strcmp(strbuf.chars, "Hello World") == 0);
 594
 595    ffStrbufSetS(&strbuf, "   Hello World   ");
 596    VERIFY(ffStrbufRemoveDupWhitespaces(&strbuf) == true);
 597    VERIFY(strcmp(strbuf.chars, " Hello World ") == 0);
 598
 599    ffStrbufSetS(&strbuf, "   Hello   World   ");
 600    VERIFY(ffStrbufRemoveDupWhitespaces(&strbuf) == true);
 601    VERIFY(strcmp(strbuf.chars, " Hello World ") == 0);
 602
 603    ffStrbufSetS(&strbuf, "   ");
 604    VERIFY(ffStrbufRemoveDupWhitespaces(&strbuf) == true);
 605    VERIFY(strcmp(strbuf.chars, " ") == 0);
 606
 607    ffStrbufClear(&strbuf);
 608    VERIFY(ffStrbufRemoveDupWhitespaces(&strbuf) == false);
 609    VERIFY(strcmp(strbuf.chars, "") == 0);
 610
 611    ffStrbufSetStatic(&strbuf, "   ");
 612    VERIFY(ffStrbufRemoveDupWhitespaces(&strbuf) == false);
 613    VERIFY(strcmp(strbuf.chars, "   ") == 0);
 614
 615    {
 616        ffStrbufSetStatic(&strbuf, "abcdef");
 617        FF_STRBUF_AUTO_DESTROY newStr = ffStrbufCreateCopy(&strbuf);
 618        VERIFY(newStr.allocated == 0);
 619        VERIFY(newStr.chars == strbuf.chars);
 620    }
 621
 622    {
 623        ffStrbufSetStatic(&strbuf, "abcdef");
 624        FF_STRBUF_AUTO_DESTROY newStr = ffStrbufCreateS("123456");
 625        ffStrbufSet(&newStr, &strbuf);
 626        VERIFY(newStr.allocated == 0);
 627        VERIFY(newStr.chars == strbuf.chars);
 628        VERIFY(ffStrbufEqualS(&newStr, "abcdef"));
 629    }
 630
 631    {
 632        ffStrbufClear(&strbuf);
 633        FF_STRBUF_AUTO_DESTROY newStr = ffStrbufCreateS("123456");
 634        uint32_t oldAlloc = newStr.allocated;
 635        VERIFY(oldAlloc > newStr.length);
 636        ffStrbufSet(&newStr, &strbuf);
 637        VERIFY(newStr.allocated == oldAlloc);
 638        VERIFY(newStr.chars != strbuf.chars);
 639        VERIFY(ffStrbufEqualS(&newStr, ""));
 640    }
 641
 642    {
 643        ffStrbufSetStatic(&strbuf, "abcdefghijkl");
 644        FF_STRBUF_AUTO_DESTROY newStr = ffStrbufCreateS("123456");
 645        ffStrbufSet(&newStr, &strbuf);
 646        VERIFY(newStr.allocated == 0);
 647        VERIFY(newStr.chars == strbuf.chars);
 648        VERIFY(ffStrbufEqualS(&newStr, "abcdefghijkl"));
 649    }
 650
 651    {
 652        ffStrbufClear(&strbuf);
 653        FF_STRBUF_AUTO_DESTROY newStr = ffStrbufCreateCopy(&strbuf);
 654        VERIFY(newStr.allocated == 0);
 655        VERIFY(newStr.chars == strbuf.chars);
 656        VERIFY(newStr.chars[0] == '\0');
 657    }
 658
 659    {
 660        ffStrbufClear(&strbuf);
 661        FF_STRBUF_AUTO_DESTROY newStr = ffStrbufCreateS("123456");
 662        ffStrbufSet(&newStr, &strbuf);
 663        VERIFY(newStr.allocated > 0);
 664        VERIFY(newStr.chars != strbuf.chars);
 665        VERIFY(ffStrbufEqualS(&newStr, ""));
 666    }
 667
 668    {
 669        ffStrbufSetStatic(&strbuf, "abc");
 670        VERIFY(ffStrbufMatchSeparatedS(&strbuf, "abc:def:ghi", ' ') == false);
 671        VERIFY(ffStrbufMatchSeparatedS(&strbuf, "abc:def:ghi", ':') == true);
 672        VERIFY(ffStrbufMatchSeparatedS(&strbuf, "def:ghi", ' ') == false);
 673        VERIFY(ffStrbufMatchSeparatedS(&strbuf, "def:ghi", ':') == false);
 674        VERIFY(ffStrbufMatchSeparatedS(&strbuf, "def", ':') == false);
 675        VERIFY(ffStrbufMatchSeparatedS(&strbuf, "abc", ':') == true);
 676        VERIFY(ffStrbufMatchSeparatedS(&strbuf, "", ' ') == false);
 677        VERIFY(ffStrbufMatchSeparatedS(&strbuf, ":abc:", ':') == true);
 678        VERIFY(ffStrbufMatchSeparatedS(&strbuf, "abc:", ':') == true);
 679        VERIFY(ffStrbufMatchSeparatedS(&strbuf, ":abc", ':') == true);
 680        VERIFY(ffStrbufMatchSeparatedS(&strbuf, ":ABC", ':') == false);
 681        VERIFY(ffStrbufMatchSeparatedS(&strbuf, ":abcdef", ':') == false);
 682    }
 683
 684    {
 685        // ffStrbufMatchSeparatedNS with explicit compLength: the length-bounded
 686        // contract is load-bearing for the Linux keyboard handler, which passes
 687        // a fixed-size buffer length rather than a NUL-terminated string.
 688        ffStrbufSetStatic(&strbuf, "abc");
 689        VERIFY(ffStrbufMatchSeparatedNS(&strbuf, 11, "abc:def:ghi", ':') == true);
 690        VERIFY(ffStrbufMatchSeparatedNS(&strbuf, 3, "abc:def:ghi", ':') == true);
 691        VERIFY(ffStrbufMatchSeparatedNS(&strbuf, 7, "abc:def:ghi", ':') == true); // "abc:def"
 692        VERIFY(ffStrbufMatchSeparatedNS(&strbuf, 4, "abc:def:ghi", ':') == true); // "abc:"
 693        VERIFY(ffStrbufMatchSeparatedNS(&strbuf, 5, "abc:def:ghi", ':') == true); // "abc:d" truncated, but "abc" is a complete segment
 694        VERIFY(ffStrbufMatchSeparatedNS(&strbuf, 3, "abd:def:ghi", ':') == false);
 695        VERIFY(ffStrbufMatchSeparatedNS(&strbuf, 0, "abc:def:ghi", ':') == false);
 696        VERIFY(ffStrbufMatchSeparatedNS(&strbuf, 0, "", ':') == false);
 697        VERIFY(ffStrbufMatchSeparatedNS(&strbuf, 11, "abc:def:ghi", ' ') == false); // no separator in bounds
 698        VERIFY(ffStrbufMatchSeparatedNS(&strbuf, 12, "abc:def:ghi\0j", ':') == true); // embedded NUL inside bounds
 699
 700        ffStrbufClear(&strbuf);
 701        VERIFY(ffStrbufMatchSeparatedNS(&strbuf, 11, "abc:def:ghi", ':') == false); // no empty segment
 702        VERIFY(ffStrbufMatchSeparatedNS(&strbuf, 8, "abc::def", ':') == true); // empty segment in the middle
 703        VERIFY(ffStrbufMatchSeparatedNS(&strbuf, 4, "abc:", ':') == true); // trailing empty segment
 704        VERIFY(ffStrbufMatchSeparatedNS(&strbuf, 4, ":abc", ':') == true); // leading empty segment
 705        VERIFY(ffStrbufMatchSeparatedNS(&strbuf, 0, "abc", ':') == true); // empty separated string itself
 706    }
 707
 708    {
 709        ffStrbufSetStatic(&strbuf, "ABC");
 710        VERIFY(ffStrbufMatchSeparatedIgnCaseS(&strbuf, "abc:def:ghi", ' ') == false);
 711        VERIFY(ffStrbufMatchSeparatedIgnCaseS(&strbuf, "abc:def:ghi", ':') == true);
 712        VERIFY(ffStrbufMatchSeparatedIgnCaseS(&strbuf, "def:ghi", ' ') == false);
 713        VERIFY(ffStrbufMatchSeparatedIgnCaseS(&strbuf, "def:ghi", ':') == false);
 714        VERIFY(ffStrbufMatchSeparatedIgnCaseS(&strbuf, "def", ':') == false);
 715        VERIFY(ffStrbufMatchSeparatedIgnCaseS(&strbuf, "abc", ':') == true);
 716        VERIFY(ffStrbufMatchSeparatedIgnCaseS(&strbuf, "", ' ') == false);
 717        VERIFY(ffStrbufMatchSeparatedIgnCaseS(&strbuf, ":abc:", ':') == true);
 718        VERIFY(ffStrbufMatchSeparatedIgnCaseS(&strbuf, "abc:", ':') == true);
 719        VERIFY(ffStrbufMatchSeparatedIgnCaseS(&strbuf, ":abc", ':') == true);
 720        VERIFY(ffStrbufMatchSeparatedIgnCaseS(&strbuf, ":ABC", ':') == true);
 721        VERIFY(ffStrbufMatchSeparatedIgnCaseS(&strbuf, ":abcdef", ':') == false);
 722    }
 723
 724    {
 725        ffStrbufSetStatic(&strbuf, "abc:def:ghi");
 726        VERIFY(ffStrbufSeparatedContainS(&strbuf, "abc", ' ') == false);
 727        VERIFY(ffStrbufSeparatedContainS(&strbuf, "abc", ':') == true);
 728        VERIFY(ffStrbufSeparatedContainS(&strbuf, "def", ' ') == false);
 729        VERIFY(ffStrbufSeparatedContainS(&strbuf, "def", ':') == true);
 730        VERIFY(ffStrbufSeparatedContainS(&strbuf, "DEF", ':') == false);
 731        VERIFY(ffStrbufSeparatedContainS(&strbuf, "a", ':') == false);
 732        VERIFY(ffStrbufSeparatedContainS(&strbuf, "e", ':') == false);
 733        VERIFY(ffStrbufSeparatedContainS(&strbuf, "i", ':') == false);
 734    }
 735
 736    {
 737        ffStrbufSetStatic(&strbuf, "ABC:DEF:GHI");
 738        VERIFY(ffStrbufSeparatedContainIgnCaseS(&strbuf, "abc", ' ') == false);
 739        VERIFY(ffStrbufSeparatedContainIgnCaseS(&strbuf, "abc", ':') == true);
 740        VERIFY(ffStrbufSeparatedContainIgnCaseS(&strbuf, "def", ' ') == false);
 741        VERIFY(ffStrbufSeparatedContainIgnCaseS(&strbuf, "def", ':') == true);
 742        VERIFY(ffStrbufSeparatedContainIgnCaseS(&strbuf, "DEF", ':') == true);
 743        VERIFY(ffStrbufSeparatedContainIgnCaseS(&strbuf, "a", ':') == false);
 744        VERIFY(ffStrbufSeparatedContainIgnCaseS(&strbuf, "e", ':') == false);
 745        VERIFY(ffStrbufSeparatedContainIgnCaseS(&strbuf, "i", ':') == false);
 746    }
 747
 748    {
 749        // ffStrbufSeparatedContainNS with empty strings: an empty list contains
 750        // only the empty segment (symmetric to ffStrbufMatchSeparatedNS); an
 751        // empty comp matches only an empty segment in the list.
 752        ffStrbufClear(&strbuf);
 753        VERIFY(ffStrbufSeparatedContainNS(&strbuf, 0, "", ':') == true); // empty list contains the empty segment
 754        VERIFY(ffStrbufSeparatedContainNS(&strbuf, 3, "abc", ':') == false); // empty list contains no non-empty segment
 755        VERIFY(ffStrbufSeparatedContainNS(&strbuf, 0, "abc", ':') == true); // compLength == 0 means an empty segment
 756
 757        ffStrbufSetStatic(&strbuf, "abc::def");
 758        VERIFY(ffStrbufSeparatedContainNS(&strbuf, 0, "", ':') == true); // empty segment in the middle
 759        ffStrbufSetStatic(&strbuf, "abc:");
 760        VERIFY(ffStrbufSeparatedContainNS(&strbuf, 0, "", ':') == true); // trailing empty segment
 761        ffStrbufSetStatic(&strbuf, ":abc");
 762        VERIFY(ffStrbufSeparatedContainNS(&strbuf, 0, "", ':') == true); // leading empty segment
 763        ffStrbufSetStatic(&strbuf, "abc:def");
 764        VERIFY(ffStrbufSeparatedContainNS(&strbuf, 0, "", ':') == false); // no empty segment
 765        ffStrbufSetStatic(&strbuf, "abc");
 766        VERIFY(ffStrbufSeparatedContainNS(&strbuf, 0, "", ':') == false); // no empty segment
 767    }
 768
 769    {
 770        ffStrbufSetStatic(&strbuf, "abc");
 771        ffStrbufSubstr(&strbuf, 0, 1); // start, end
 772        VERIFY(ffStrbufEqualS(&strbuf, "a"));
 773
 774        ffStrbufSetStatic(&strbuf, "abc");
 775        ffStrbufSubstr(&strbuf, 1, 1);
 776        VERIFY(ffStrbufEqualS(&strbuf, ""));
 777
 778        ffStrbufSetStatic(&strbuf, "abc");
 779        ffStrbufSubstr(&strbuf, 2, 1);
 780        VERIFY(ffStrbufEqualS(&strbuf, ""));
 781
 782        ffStrbufSetStatic(&strbuf, "abc");
 783        ffStrbufSubstr(&strbuf, 2, 3);
 784        VERIFY(ffStrbufEqualS(&strbuf, "c"));
 785
 786        ffStrbufSetStatic(&strbuf, "abc");
 787        ffStrbufSubstr(&strbuf, 0, 3);
 788        VERIFY(ffStrbufEqualS(&strbuf, "abc"));
 789    }
 790
 791    {
 792        ffStrbufSetS(&strbuf, "abc");
 793        ffStrbufSubstr(&strbuf, 0, 1); // start, end
 794        VERIFY(ffStrbufEqualS(&strbuf, "a"));
 795
 796        ffStrbufSetS(&strbuf, "abc");
 797        ffStrbufSubstr(&strbuf, 1, 1);
 798        VERIFY(ffStrbufEqualS(&strbuf, ""));
 799
 800        ffStrbufSetS(&strbuf, "abc");
 801        ffStrbufSubstr(&strbuf, 2, 1);
 802        VERIFY(ffStrbufEqualS(&strbuf, ""));
 803
 804        ffStrbufSetS(&strbuf, "abc");
 805        ffStrbufSubstr(&strbuf, 2, 3);
 806        VERIFY(ffStrbufEqualS(&strbuf, "c"));
 807
 808        ffStrbufSetS(&strbuf, "abc");
 809        ffStrbufSubstr(&strbuf, 0, 3);
 810        VERIFY(ffStrbufEqualS(&strbuf, "abc"));
 811
 812        ffStrbufDestroy(&strbuf);
 813    }
 814
 815    {
 816        ffStrbufAppendUtf32CodePoint(&strbuf, 0x6587);
 817        ffStrbufAppendUtf32CodePoint(&strbuf, 0x6cc9);
 818        ffStrbufAppendUtf32CodePoint(&strbuf, 0x9a7f);
 819        VERIFY(ffStrbufEqualS(&strbuf, (const char*) u8"文泉驿"));
 820
 821        ffStrbufDestroy(&strbuf);
 822    }
 823
 824    {
 825        ffStrbufAppendSInt(&strbuf, 1234567890);
 826        VERIFY(ffStrbufEqualS(&strbuf, "1234567890"));
 827
 828        ffStrbufClear(&strbuf);
 829        ffStrbufAppendSInt(&strbuf, -1234567890);
 830        VERIFY(ffStrbufEqualS(&strbuf, "-1234567890"));
 831
 832        ffStrbufClear(&strbuf);
 833        ffStrbufAppendSInt(&strbuf, 0);
 834        VERIFY(ffStrbufEqualS(&strbuf, "0"));
 835
 836        ffStrbufClear(&strbuf);
 837        ffStrbufAppendUInt(&strbuf, 1234567890);
 838        VERIFY(ffStrbufEqualS(&strbuf, "1234567890"));
 839
 840        ffStrbufClear(&strbuf);
 841        ffStrbufAppendUInt(&strbuf, 0);
 842        VERIFY(ffStrbufEqualS(&strbuf, "0"));
 843
 844        ffStrbufDestroy(&strbuf);
 845    }
 846
 847    {
 848        ffStrbufAppendDouble(&strbuf, 120.0, 0, true);
 849        VERIFY(ffStrbufEqualS(&strbuf, "120"));
 850
 851        ffStrbufClear(&strbuf);
 852        ffStrbufAppendDouble(&strbuf, 120.0, 1, true);
 853        VERIFY(ffStrbufEqualS(&strbuf, "120.0"));
 854
 855        ffStrbufClear(&strbuf);
 856        ffStrbufAppendDouble(&strbuf, 120.0, 5, true);
 857        VERIFY(ffStrbufEqualS(&strbuf, "120.00000"));
 858
 859        ffStrbufClear(&strbuf);
 860        ffStrbufAppendDouble(&strbuf, 120.123456789, 5, true);
 861        VERIFY(ffStrbufEqualS(&strbuf, "120.12346"));
 862
 863        ffStrbufClear(&strbuf);
 864        ffStrbufAppendDouble(&strbuf, 120.888888, 0, true);
 865        VERIFY(ffStrbufEqualS(&strbuf, "121"));
 866
 867        ffStrbufClear(&strbuf);
 868        ffStrbufAppendDouble(&strbuf, 120.999999, 2, true);
 869        VERIFY(ffStrbufEqualS(&strbuf, "121.00"));
 870
 871        ffStrbufClear(&strbuf);
 872        ffStrbufAppendDouble(&strbuf, 120.123456789, 0, true);
 873        VERIFY(ffStrbufEqualS(&strbuf, "120"));
 874
 875        ffStrbufClear(&strbuf);
 876        ffStrbufAppendDouble(&strbuf, 120.123456789, -1, true);
 877        VERIFY(ffStrbufEqualS(&strbuf, "120.123456789"));
 878
 879        ffStrbufClear(&strbuf);
 880        ffStrbufAppendDouble(&strbuf, 120.123, 5, true);
 881        VERIFY(ffStrbufEqualS(&strbuf, "120.12300"));
 882
 883        ffStrbufClear(&strbuf);
 884        ffStrbufAppendDouble(&strbuf, -120.0, 0, true);
 885        VERIFY(ffStrbufEqualS(&strbuf, "-120"));
 886
 887        ffStrbufClear(&strbuf);
 888        ffStrbufAppendDouble(&strbuf, -120.0, 1, true);
 889        VERIFY(ffStrbufEqualS(&strbuf, "-120.0"));
 890
 891        ffStrbufClear(&strbuf);
 892        ffStrbufAppendDouble(&strbuf, -120.0, 5, true);
 893        VERIFY(ffStrbufEqualS(&strbuf, "-120.00000"));
 894
 895        ffStrbufClear(&strbuf);
 896        ffStrbufAppendDouble(&strbuf, -120.123456789, 5, true);
 897        VERIFY(ffStrbufEqualS(&strbuf, "-120.12346"));
 898
 899        ffStrbufClear(&strbuf);
 900        ffStrbufAppendDouble(&strbuf, -120.123456789, 0, true);
 901        VERIFY(ffStrbufEqualS(&strbuf, "-120"));
 902
 903        ffStrbufClear(&strbuf);
 904        ffStrbufAppendDouble(&strbuf, -120.123456789, -1, true);
 905        VERIFY(ffStrbufEqualS(&strbuf, "-120.123456789"));
 906
 907        ffStrbufClear(&strbuf);
 908        ffStrbufAppendDouble(&strbuf, -120.123, 5, true);
 909        VERIFY(ffStrbufEqualS(&strbuf, "-120.12300"));
 910
 911        ffStrbufClear(&strbuf);
 912        ffStrbufAppendDouble(&strbuf, -120.888888, 0, true);
 913        VERIFY(ffStrbufEqualS(&strbuf, "-121"));
 914
 915        ffStrbufClear(&strbuf);
 916        ffStrbufAppendDouble(&strbuf, -120.999999, 2, true);
 917        VERIFY(ffStrbufEqualS(&strbuf, "-121.00"));
 918
 919        ffStrbufClear(&strbuf);
 920        ffStrbufAppendDouble(&strbuf, 1.2345e50, 1, true);
 921        VERIFY(ffStrbufEqualS(&strbuf, "1.2345e50"));
 922
 923        ffStrbufClear(&strbuf);
 924        ffStrbufAppendDouble(&strbuf, -1.2345e50, 1, true);
 925        VERIFY(ffStrbufEqualS(&strbuf, "-1.2345e50"));
 926
 927        ffStrbufClear(&strbuf);
 928        ffStrbufAppendDouble(&strbuf, 1.2345e50, 0, true);
 929        VERIFY(ffStrbufEqualS(&strbuf, "1.2345e50"));
 930
 931        ffStrbufClear(&strbuf);
 932        ffStrbufAppendDouble(&strbuf, -1.2345e50, 0, true);
 933        VERIFY(ffStrbufEqualS(&strbuf, "-1.2345e50"));
 934
 935        ffStrbufClear(&strbuf);
 936        ffStrbufAppendDouble(&strbuf, 1.2345e50, -1, true);
 937        VERIFY(ffStrbufEqualS(&strbuf, "1.2345e50"));
 938
 939        ffStrbufClear(&strbuf);
 940        ffStrbufAppendDouble(&strbuf, -1.2345e50, -1, true);
 941        VERIFY(ffStrbufEqualS(&strbuf, "-1.2345e50"));
 942
 943        ffStrbufClear(&strbuf);
 944        ffStrbufAppendDouble(&strbuf, 1.2345e20, 1, true);
 945        VERIFY(ffStrbufEqualS(&strbuf, "123450000000000000000.0"));
 946
 947        ffStrbufClear(&strbuf);
 948        ffStrbufAppendDouble(&strbuf, -1.2345e20, 1, true);
 949        VERIFY(ffStrbufEqualS(&strbuf, "-123450000000000000000.0"));
 950
 951        ffStrbufClear(&strbuf);
 952        ffStrbufAppendDouble(&strbuf, +0.0, 0, true);
 953        VERIFY(ffStrbufEqualS(&strbuf, "0"));
 954
 955        ffStrbufClear(&strbuf);
 956        ffStrbufAppendDouble(&strbuf, -0.0, 0, true);
 957        VERIFY(ffStrbufEqualS(&strbuf, "-0"));
 958
 959        ffStrbufDestroy(&strbuf);
 960    }
 961
 962    {
 963        ffStrbufAppendDouble(&strbuf, 120.0, 0, false);
 964        VERIFY(ffStrbufEqualS(&strbuf, "120"));
 965
 966        ffStrbufClear(&strbuf);
 967        ffStrbufAppendDouble(&strbuf, 120.0, 1, false);
 968        VERIFY(ffStrbufEqualS(&strbuf, "120"));
 969
 970        ffStrbufClear(&strbuf);
 971        ffStrbufAppendDouble(&strbuf, 120.0, 5, false);
 972        VERIFY(ffStrbufEqualS(&strbuf, "120"));
 973
 974        ffStrbufClear(&strbuf);
 975        ffStrbufAppendDouble(&strbuf, 120.123456789, 5, false);
 976        VERIFY(ffStrbufEqualS(&strbuf, "120.12346"));
 977
 978        ffStrbufClear(&strbuf);
 979        ffStrbufAppendDouble(&strbuf, 120.888888, 0, false);
 980        VERIFY(ffStrbufEqualS(&strbuf, "121"));
 981
 982        ffStrbufClear(&strbuf);
 983        ffStrbufAppendDouble(&strbuf, 120.999999, 2, false);
 984        VERIFY(ffStrbufEqualS(&strbuf, "121"));
 985
 986        ffStrbufClear(&strbuf);
 987        ffStrbufAppendDouble(&strbuf, 120.123456789, 0, false);
 988        VERIFY(ffStrbufEqualS(&strbuf, "120"));
 989
 990        ffStrbufClear(&strbuf);
 991        ffStrbufAppendDouble(&strbuf, 120.123456789, -1, false);
 992        VERIFY(ffStrbufEqualS(&strbuf, "120.123456789"));
 993
 994        ffStrbufClear(&strbuf);
 995        ffStrbufAppendDouble(&strbuf, 120.123, 5, false);
 996        VERIFY(ffStrbufEqualS(&strbuf, "120.123"));
 997
 998        ffStrbufClear(&strbuf);
 999        ffStrbufAppendDouble(&strbuf, -120.0, 0, false);
1000        VERIFY(ffStrbufEqualS(&strbuf, "-120"));
1001
1002        ffStrbufClear(&strbuf);
1003        ffStrbufAppendDouble(&strbuf, -120.0, 1, false);
1004        VERIFY(ffStrbufEqualS(&strbuf, "-120"));
1005
1006        ffStrbufClear(&strbuf);
1007        ffStrbufAppendDouble(&strbuf, -120.0, 5, false);
1008        VERIFY(ffStrbufEqualS(&strbuf, "-120"));
1009
1010        ffStrbufClear(&strbuf);
1011        ffStrbufAppendDouble(&strbuf, -120.123456789, 5, false);
1012        VERIFY(ffStrbufEqualS(&strbuf, "-120.12346"));
1013
1014        ffStrbufClear(&strbuf);
1015        ffStrbufAppendDouble(&strbuf, -120.123456789, 0, false);
1016        VERIFY(ffStrbufEqualS(&strbuf, "-120"));
1017
1018        ffStrbufClear(&strbuf);
1019        ffStrbufAppendDouble(&strbuf, -120.123456789, -1, false);
1020        VERIFY(ffStrbufEqualS(&strbuf, "-120.123456789"));
1021
1022        ffStrbufClear(&strbuf);
1023        ffStrbufAppendDouble(&strbuf, -120.123, 5, false);
1024        VERIFY(ffStrbufEqualS(&strbuf, "-120.123"));
1025
1026        ffStrbufClear(&strbuf);
1027        ffStrbufAppendDouble(&strbuf, -120.888888, 0, false);
1028        VERIFY(ffStrbufEqualS(&strbuf, "-121"));
1029
1030        ffStrbufClear(&strbuf);
1031        ffStrbufAppendDouble(&strbuf, -120.999999, 2, false);
1032        VERIFY(ffStrbufEqualS(&strbuf, "-121"));
1033
1034        ffStrbufClear(&strbuf);
1035        ffStrbufAppendDouble(&strbuf, 1.2345e50, 1, false);
1036        VERIFY(ffStrbufEqualS(&strbuf, "1.2345e50"));
1037
1038        ffStrbufClear(&strbuf);
1039        ffStrbufAppendDouble(&strbuf, -1.2345e50, 1, false);
1040        VERIFY(ffStrbufEqualS(&strbuf, "-1.2345e50"));
1041
1042        ffStrbufClear(&strbuf);
1043        ffStrbufAppendDouble(&strbuf, 1.2345e50, 0, false);
1044        VERIFY(ffStrbufEqualS(&strbuf, "1.2345e50"));
1045
1046        ffStrbufClear(&strbuf);
1047        ffStrbufAppendDouble(&strbuf, -1.2345e50, 0, false);
1048        VERIFY(ffStrbufEqualS(&strbuf, "-1.2345e50"));
1049
1050        ffStrbufClear(&strbuf);
1051        ffStrbufAppendDouble(&strbuf, 1.2345e50, -1, false);
1052        VERIFY(ffStrbufEqualS(&strbuf, "1.2345e50"));
1053
1054        ffStrbufClear(&strbuf);
1055        ffStrbufAppendDouble(&strbuf, -1.2345e50, -1, false);
1056        VERIFY(ffStrbufEqualS(&strbuf, "-1.2345e50"));
1057
1058        ffStrbufClear(&strbuf);
1059        ffStrbufAppendDouble(&strbuf, 1.2345e20, 1, false);
1060        VERIFY(ffStrbufEqualS(&strbuf, "123450000000000000000"));
1061
1062        ffStrbufClear(&strbuf);
1063        ffStrbufAppendDouble(&strbuf, -1.2345e20, 1, false);
1064        VERIFY(ffStrbufEqualS(&strbuf, "-123450000000000000000"));
1065
1066        ffStrbufClear(&strbuf);
1067        ffStrbufAppendDouble(&strbuf, +0.0, 0, false);
1068        VERIFY(ffStrbufEqualS(&strbuf, "0"));
1069
1070        ffStrbufClear(&strbuf);
1071        ffStrbufAppendDouble(&strbuf, -0.0, 0, false);
1072        VERIFY(ffStrbufEqualS(&strbuf, "-0"));
1073
1074        ffStrbufDestroy(&strbuf);
1075    }
1076
1077    // decode hex escape sequences
1078    {
1079        ffStrbufSetS(&strbuf, "Basic\\x20data\\x20partition");
1080        VERIFY(ffStrbufDecodeHexEscapeSequences(&strbuf) == true);
1081        VERIFY(ffStrbufEqualS(&strbuf, "Basic data partition"));
1082
1083        ffStrbufSetS(&strbuf, "\\x41\\x42\\x43");
1084        VERIFY(ffStrbufDecodeHexEscapeSequences(&strbuf) == true);
1085        VERIFY(ffStrbufEqualS(&strbuf, "ABC"));
1086
1087        ffStrbufSetS(&strbuf, "abc\\x4");
1088        VERIFY(ffStrbufDecodeHexEscapeSequences(&strbuf) == false);
1089        VERIFY(ffStrbufEqualS(&strbuf, "abc\\x4"));
1090
1091        ffStrbufSetS(&strbuf, "abc\\xZZ");
1092        VERIFY(ffStrbufDecodeHexEscapeSequences(&strbuf) == false);
1093        VERIFY(ffStrbufEqualS(&strbuf, "abc\\xZZ"));
1094
1095        ffStrbufSetS(&strbuf, "abc\\x2G");
1096        VERIFY(ffStrbufDecodeHexEscapeSequences(&strbuf) == false);
1097        VERIFY(ffStrbufEqualS(&strbuf, "abc\\x2G"));
1098    }
1099
1100    // setS
1101    ffStrbufInitStatic(&strbuf, "STATIC");
1102    ffStrbufSetS(&strbuf, "DYNAMIC");
1103    VERIFY(ffStrbufEqualS(&strbuf, "DYNAMIC"));
1104    VERIFY(strbuf.allocated > 0);
1105    ffStrbufDestroy(&strbuf);
1106
1107    ffStrbufInit(&strbuf);
1108    ffStrbufSetS(&strbuf, "");
1109    VERIFY(ffStrbufEqualS(&strbuf, ""));
1110    VERIFY(strbuf.allocated == 0);
1111    ffStrbufDestroy(&strbuf);
1112
1113    ffStrbufInitStatic(&strbuf, "STATIC");
1114    ffStrbufSetS(&strbuf, "");
1115    VERIFY(ffStrbufEqualS(&strbuf, ""));
1116    VERIFY(strbuf.allocated == 0);
1117    ffStrbufDestroy(&strbuf);
1118
1119    ffStrbufInitStatic(&strbuf, "STATIC");
1120    ffStrbufSetStatic(&strbuf, "");
1121    VERIFY(ffStrbufEqualS(&strbuf, ""));
1122    VERIFY(strbuf.allocated == 0);
1123    ffStrbufDestroy(&strbuf);
1124
1125    ffStrbufInitS(&strbuf, "DYNAMIC");
1126    ffStrbufSetStatic(&strbuf, "");
1127    VERIFY(ffStrbufEqualS(&strbuf, ""));
1128    VERIFY(strbuf.allocated == 0);
1129    ffStrbufDestroy(&strbuf);
1130
1131    ffStrbufInitS(&strbuf, "DYNAMIC");
1132    ffStrbufSetS(&strbuf, "ANOTHER DYNAMIC");
1133    VERIFY(ffStrbufEqualS(&strbuf, "ANOTHER DYNAMIC"));
1134    VERIFY(strbuf.allocated > 0);
1135    ffStrbufDestroy(&strbuf);
1136
1137    ffStrbufInit(&strbuf);
1138    ffStrbufSetStatic(&strbuf, "");
1139    VERIFY(ffStrbufEqualS(&strbuf, ""));
1140    VERIFY(strbuf.allocated == 0);
1141    ffStrbufDestroy(&strbuf);
1142
1143    // set
1144    ffStrbufInitStatic(&strbuf, "STATIC");
1145    {
1146        FF_STRBUF_AUTO_DESTROY other = ffStrbufCreateS("DYNAMIC");
1147        ffStrbufSet(&strbuf, &other);
1148    }
1149    VERIFY(ffStrbufEqualS(&strbuf, "DYNAMIC"));
1150    VERIFY(strbuf.allocated > 0);
1151    ffStrbufDestroy(&strbuf);
1152
1153    ffStrbufInit(&strbuf);
1154    {
1155        FF_STRBUF_AUTO_DESTROY other = ffStrbufCreateS("");
1156        ffStrbufSet(&strbuf, &other);
1157    }
1158    VERIFY(ffStrbufEqualS(&strbuf, ""));
1159    VERIFY(strbuf.allocated == 0);
1160    ffStrbufDestroy(&strbuf);
1161
1162    ffStrbufInitStatic(&strbuf, "STATIC");
1163    {
1164        FF_STRBUF_AUTO_DESTROY other = ffStrbufCreateS("");
1165        ffStrbufSet(&strbuf, &other);
1166    }
1167    VERIFY(ffStrbufEqualS(&strbuf, ""));
1168    VERIFY(strbuf.allocated == 0);
1169    ffStrbufDestroy(&strbuf);
1170
1171    ffStrbufInitS(&strbuf, "DYNAMIC");
1172    {
1173        FF_STRBUF_AUTO_DESTROY other = ffStrbufCreateS("");
1174        ffStrbufSet(&strbuf, &other);
1175    }
1176    VERIFY(ffStrbufEqualS(&strbuf, ""));
1177    VERIFY(strbuf.allocated > 0);
1178    ffStrbufDestroy(&strbuf);
1179
1180    ffStrbufInitS(&strbuf, "DYNAMIC");
1181    {
1182        FF_STRBUF_AUTO_DESTROY other = ffStrbufCreateStatic("STATIC");
1183        ffStrbufSet(&strbuf, &other);
1184    }
1185    VERIFY(ffStrbufEqualS(&strbuf, "STATIC"));
1186    VERIFY(strbuf.allocated == 0);
1187    ffStrbufDestroy(&strbuf);
1188
1189    #if FASTFETCH_STRBUF_DEFAULT_ALLOC == 32
1190    ffStrbufEnsureFreeNoCheck(&strbuf, 1);
1191    VERIFY(strbuf.allocated == 32);
1192    ffStrbufDestroy(&strbuf);
1193
1194    ffStrbufEnsureFreeNoCheck(&strbuf, 31);
1195    VERIFY(strbuf.allocated == 32);
1196    ffStrbufDestroy(&strbuf);
1197
1198    ffStrbufEnsureFreeNoCheck(&strbuf, 32);
1199    VERIFY(strbuf.allocated == 64);
1200    ffStrbufDestroy(&strbuf);
1201
1202    ffStrbufEnsureFreeNoCheck(&strbuf, 33);
1203    VERIFY(strbuf.allocated == 64);
1204    ffStrbufDestroy(&strbuf);
1205
1206    ffStrbufEnsureFreeNoCheck(&strbuf, 63);
1207    VERIFY(strbuf.allocated == 64);
1208    ffStrbufDestroy(&strbuf);
1209
1210    ffStrbufEnsureFreeNoCheck(&strbuf, 64);
1211    VERIFY(strbuf.allocated == 128);
1212    ffStrbufDestroy(&strbuf);
1213    #endif
1214
1215    // Success
1216    puts("\e[32mAll tests passed!" FASTFETCH_TEXT_MODIFIER_RESET);
1217}