sds.c 40.0 KB
Newer Older
A
antirez 已提交
1
/* SDSLib 2.0 -- A C dynamic strings library
A
antirez 已提交
2
 *
A
antirez 已提交
3
 * Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>
A
antirez 已提交
4
 * Copyright (c) 2015, Oran Agra
A
antirez 已提交
5
 * Copyright (c) 2015, Redis Labs, Inc
A
antirez 已提交
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *   * Neither the name of Redis nor the names of its contributors may be used
 *     to endorse or promote products derived from this software without
 *     specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
37
#include <assert.h>
38
#include <limits.h>
39
#include "sds.h"
40
#include "sdsalloc.h"
A
antirez 已提交
41

42 43
const char *SDS_NOINIT = "SDS_NOINIT";

44 45
static inline int sdsHdrSize(char type) {
    switch(type&SDS_TYPE_MASK) {
A
antirez 已提交
46 47
        case SDS_TYPE_5:
            return sizeof(struct sdshdr5);
48 49 50 51 52 53 54 55 56 57 58 59 60
        case SDS_TYPE_8:
            return sizeof(struct sdshdr8);
        case SDS_TYPE_16:
            return sizeof(struct sdshdr16);
        case SDS_TYPE_32:
            return sizeof(struct sdshdr32);
        case SDS_TYPE_64:
            return sizeof(struct sdshdr64);
    }
    return 0;
}

static inline char sdsReqType(size_t string_size) {
61
    if (string_size < 1<<5)
A
antirez 已提交
62
        return SDS_TYPE_5;
63
    if (string_size < 1<<8)
64
        return SDS_TYPE_8;
65
    if (string_size < 1<<16)
66
        return SDS_TYPE_16;
67
#if (LONG_MAX == LLONG_MAX)
68
    if (string_size < 1ll<<32)
69 70
        return SDS_TYPE_32;
    return SDS_TYPE_64;
71 72 73
#else
    return SDS_TYPE_32;
#endif
74 75
}

76 77 78
/* Create a new sds string with the content specified by the 'init' pointer
 * and 'initlen'.
 * If NULL is used for 'init' the string is initialized with zero bytes.
79
 * If SDS_NOINIT is used, the buffer is left uninitialized;
80 81 82 83
 *
 * The string is always null-termined (all the sds strings are, always) so
 * even if you create an sds string with:
 *
Holynn's avatar
Holynn 已提交
84
 * mystring = sdsnewlen("abc",3);
85 86 87 88
 *
 * You can print the string with printf() as there is an implicit \0 at the
 * end of the string. However the string is binary safe and can contain
 * \0 characters in the middle, as the length is stored in the sds header. */
A
antirez 已提交
89
sds sdsnewlen(const void *init, size_t initlen) {
90 91 92
    void *sh;
    sds s;
    char type = sdsReqType(initlen);
93 94 95
    /* Empty strings are usually created in order to append. Use type 8
     * since type 5 is not good at this. */
    if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8;
96
    int hdrlen = sdsHdrSize(type);
A
antirez 已提交
97 98
    unsigned char *fp; /* flags pointer. */

99
    sh = s_malloc(hdrlen+initlen+1);
A
antirez 已提交
100
    if (sh == NULL) return NULL;
101 102 103
    if (init==SDS_NOINIT)
        init = NULL;
    else if (!init)
104 105
        memset(sh, 0, hdrlen+initlen+1);
    s = (char*)sh+hdrlen;
A
antirez 已提交
106
    fp = ((unsigned char*)s)-1;
107
    switch(type) {
A
antirez 已提交
108 109 110 111
        case SDS_TYPE_5: {
            *fp = type | (initlen << SDS_TYPE_BITS);
            break;
        }
112 113 114 115
        case SDS_TYPE_8: {
            SDS_HDR_VAR(8,s);
            sh->len = initlen;
            sh->alloc = initlen;
A
antirez 已提交
116
            *fp = type;
117 118 119 120 121 122
            break;
        }
        case SDS_TYPE_16: {
            SDS_HDR_VAR(16,s);
            sh->len = initlen;
            sh->alloc = initlen;
A
antirez 已提交
123
            *fp = type;
124 125 126 127 128 129
            break;
        }
        case SDS_TYPE_32: {
            SDS_HDR_VAR(32,s);
            sh->len = initlen;
            sh->alloc = initlen;
A
antirez 已提交
130
            *fp = type;
131 132 133 134 135 136
            break;
        }
        case SDS_TYPE_64: {
            SDS_HDR_VAR(64,s);
            sh->len = initlen;
            sh->alloc = initlen;
A
antirez 已提交
137
            *fp = type;
138 139 140
            break;
        }
    }
141
    if (initlen && init)
142 143 144 145 146
        memcpy(s, init, initlen);
    s[initlen] = '\0';
    return s;
}

147 148
/* Create an empty (zero length) sds string. Even in this case the string
 * always has an implicit null term. */
A
antirez 已提交
149 150 151 152
sds sdsempty(void) {
    return sdsnewlen("",0);
}

F
FuGangqiang 已提交
153
/* Create a new sds string starting from a null terminated C string. */
A
antirez 已提交
154 155 156 157 158
sds sdsnew(const char *init) {
    size_t initlen = (init == NULL) ? 0 : strlen(init);
    return sdsnewlen(init, initlen);
}

159
/* Duplicate an sds string. */
A
antirez 已提交
160 161 162 163
sds sdsdup(const sds s) {
    return sdsnewlen(s, sdslen(s));
}

164
/* Free an sds string. No operation is performed if 's' is NULL. */
A
antirez 已提交
165 166
void sdsfree(sds s) {
    if (s == NULL) return;
167
    s_free((char*)s-sdsHdrSize(s[-1]));
A
antirez 已提交
168 169
}

170 171 172 173 174 175 176 177 178 179 180 181 182 183
/* Set the sds string length to the length as obtained with strlen(), so
 * considering as content only up to the first null term character.
 *
 * This function is useful when the sds string is hacked manually in some
 * way, like in the following example:
 *
 * s = sdsnew("foobar");
 * s[2] = '\0';
 * sdsupdatelen(s);
 * printf("%d\n", sdslen(s));
 *
 * The output will be "2", but if we comment out the call to sdsupdatelen()
 * the output will be "6" as the string was modified but the logical length
 * remains 6 bytes. */
A
antirez 已提交
184
void sdsupdatelen(sds s) {
185
    size_t reallen = strlen(s);
186
    sdssetlen(s, reallen);
A
antirez 已提交
187 188
}

T
Ted Nyman 已提交
189
/* Modify an sds string in-place to make it empty (zero length).
190 191 192
 * However all the existing buffer is not discarded but set as free space
 * so that next append operations will not require allocations up to the
 * number of bytes previously available. */
193
void sdsclear(sds s) {
194 195
    sdssetlen(s, 0);
    s[0] = '\0';
196 197
}

198 199 200
/* Enlarge the free space at the end of the sds string so that the caller
 * is sure that after calling this function can overwrite up to addlen
 * bytes after the end of the string, plus one more byte for nul term.
201
 *
202
 * Note: this does not change the *length* of the sds string as returned
203
 * by sdslen(), but only the free buffer space we have. */
204
sds sdsMakeRoomFor(sds s, size_t addlen) {
205 206
    void *sh, *newsh;
    size_t avail = sdsavail(s);
207
    size_t len, newlen;
A
antirez 已提交
208
    char type, oldtype = s[-1] & SDS_TYPE_MASK;
209
    int hdrlen;
A
antirez 已提交
210

211
    /* Return ASAP if there is enough space left. */
212
    if (avail >= addlen) return s;
213

A
antirez 已提交
214
    len = sdslen(s);
215
    sh = (char*)s-sdsHdrSize(oldtype);
216 217 218 219 220
    newlen = (len+addlen);
    if (newlen < SDS_MAX_PREALLOC)
        newlen *= 2;
    else
        newlen += SDS_MAX_PREALLOC;
221

222
    type = sdsReqType(newlen);
223 224 225 226 227 228

    /* Don't use type 5: the user is appending to the string and type 5 is
     * not able to remember empty space, so sdsMakeRoomFor() must be called
     * at every appending operation. */
    if (type == SDS_TYPE_5) type = SDS_TYPE_8;

229 230
    hdrlen = sdsHdrSize(type);
    if (oldtype==type) {
231
        newsh = s_realloc(sh, hdrlen+newlen+1);
232 233 234
        if (newsh == NULL) return NULL;
        s = (char*)newsh+hdrlen;
    } else {
A
antirez 已提交
235 236
        /* Since the header size changes, need to move the string forward,
         * and can't use realloc */
237
        newsh = s_malloc(hdrlen+newlen+1);
238 239
        if (newsh == NULL) return NULL;
        memcpy((char*)newsh+hdrlen, s, len+1);
240
        s_free(sh);
241 242 243 244 245 246
        s = (char*)newsh+hdrlen;
        s[-1] = type;
        sdssetlen(s, len);
    }
    sdssetalloc(s, newlen);
    return s;
A
antirez 已提交
247 248
}

249 250
/* Reallocate the sds string so that it has no free space at the end. The
 * contained string remains not altered, but next concatenation operations
251 252 253 254
 * will require a reallocation.
 *
 * After the call, the passed sds string is no longer valid and all the
 * references must be substituted with the new pointer returned by the call. */
255
sds sdsRemoveFreeSpace(sds s) {
256
    void *sh, *newsh;
A
antirez 已提交
257
    char type, oldtype = s[-1] & SDS_TYPE_MASK;
258
    int hdrlen, oldhdrlen = sdsHdrSize(oldtype);
259
    size_t len = sdslen(s);
260
    size_t avail = sdsavail(s);
261
    sh = (char*)s-oldhdrlen;
262

263 264 265
    /* Return ASAP if there is no space left. */
    if (avail == 0) return s;

266 267
    /* Check what would be the minimum SDS header that is just good enough to
     * fit this string. */
268 269
    type = sdsReqType(len);
    hdrlen = sdsHdrSize(type);
270 271 272 273 274 275 276

    /* If the type is the same, or at least a large enough type is still
     * required, we just realloc(), letting the allocator to do the copy
     * only if really needed. Otherwise if the change is huge, we manually
     * reallocate the string to use the different header type. */
    if (oldtype==type || type > SDS_TYPE_8) {
        newsh = s_realloc(sh, oldhdrlen+len+1);
277
        if (newsh == NULL) return NULL;
278
        s = (char*)newsh+oldhdrlen;
279
    } else {
280
        newsh = s_malloc(hdrlen+len+1);
281 282
        if (newsh == NULL) return NULL;
        memcpy((char*)newsh+hdrlen, s, len+1);
283
        s_free(sh);
284 285 286 287 288 289
        s = (char*)newsh+hdrlen;
        s[-1] = type;
        sdssetlen(s, len);
    }
    sdssetalloc(s, len);
    return s;
290 291
}

J
Jack Drogon 已提交
292
/* Return the total size of the allocation of the specified sds string,
293 294 295 296 297 298
 * including:
 * 1) The sds header before the pointer.
 * 2) The string.
 * 3) The free buffer at the end if any.
 * 4) The implicit null term.
 */
A
antirez 已提交
299
size_t sdsAllocSize(sds s) {
300 301 302
    size_t alloc = sdsalloc(s);
    return sdsHdrSize(s[-1])+alloc+1;
}
A
antirez 已提交
303

304 305 306 307
/* Return the pointer of the actual SDS allocation (normally SDS strings
 * are referenced by the start of the string buffer). */
void *sdsAllocPtr(sds s) {
    return (void*) (s-sdsHdrSize(s[-1]));
A
antirez 已提交
308 309
}

310
/* Increment the sds length and decrements the left free space at the
311
 * end of the string according to 'incr'. Also set the null term
312 313 314 315 316 317 318 319 320
 * in the new end of the string.
 *
 * This function is used in order to fix the string length after the
 * user calls sdsMakeRoomFor(), writes something after the end of
 * the current string, and finally needs to set the new length.
 *
 * Note: it is possible to use a negative increment in order to
 * right-trim the string.
 *
321 322
 * Usage example:
 *
323
 * Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the
324 325
 * following schema, to cat bytes coming from the kernel to the end of an
 * sds string without copying into an intermediate buffer:
326 327 328 329 330
 *
 * oldlen = sdslen(s);
 * s = sdsMakeRoomFor(s, BUFFER_SIZE);
 * nread = read(fd, s+oldlen, BUFFER_SIZE);
 * ... check for nread <= 0 and handle it ...
331
 * sdsIncrLen(s, nread);
332
 */
333
void sdsIncrLen(sds s, ssize_t incr) {
A
antirez 已提交
334
    unsigned char flags = s[-1];
335 336
    size_t len;
    switch(flags&SDS_TYPE_MASK) {
A
antirez 已提交
337 338 339 340
        case SDS_TYPE_5: {
            unsigned char *fp = ((unsigned char*)s)-1;
            unsigned char oldlen = SDS_TYPE_5_LEN(flags);
            assert((incr > 0 && oldlen+incr < 32) || (incr < 0 && oldlen >= (unsigned int)(-incr)));
341 342
            *fp = SDS_TYPE_5 | ((oldlen+incr) << SDS_TYPE_BITS);
            len = oldlen+incr;
A
antirez 已提交
343 344
            break;
        }
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
        case SDS_TYPE_8: {
            SDS_HDR_VAR(8,s);
            assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));
            len = (sh->len += incr);
            break;
        }
        case SDS_TYPE_16: {
            SDS_HDR_VAR(16,s);
            assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));
            len = (sh->len += incr);
            break;
        }
        case SDS_TYPE_32: {
            SDS_HDR_VAR(32,s);
            assert((incr >= 0 && sh->alloc-sh->len >= (unsigned int)incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));
            len = (sh->len += incr);
            break;
        }
        case SDS_TYPE_64: {
            SDS_HDR_VAR(64,s);
            assert((incr >= 0 && sh->alloc-sh->len >= (uint64_t)incr) || (incr < 0 && sh->len >= (uint64_t)(-incr)));
            len = (sh->len += incr);
            break;
        }
369
        default: len = 0; /* Just to avoid compilation warnings. */
370 371
    }
    s[len] = '\0';
372 373
}

374
/* Grow the sds to have the specified length. Bytes that were not part of
375 376 377 378
 * the original length of the sds will be set to zero.
 *
 * if the specified length is smaller than the current length, no operation
 * is performed. */
379
sds sdsgrowzero(sds s, size_t len) {
380
    size_t curlen = sdslen(s);
381 382 383 384 385 386

    if (len <= curlen) return s;
    s = sdsMakeRoomFor(s,len-curlen);
    if (s == NULL) return NULL;

    /* Make sure added region doesn't contain garbage */
387
    memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */
388
    sdssetlen(s, len);
389 390 391
    return s;
}

392 393 394 395 396
/* Append the specified binary-safe string pointed by 't' of 'len' bytes to the
 * end of the specified sds string 's'.
 *
 * After the call, the passed sds string is no longer valid and all the
 * references must be substituted with the new pointer returned by the call. */
397
sds sdscatlen(sds s, const void *t, size_t len) {
A
antirez 已提交
398 399 400 401 402
    size_t curlen = sdslen(s);

    s = sdsMakeRoomFor(s,len);
    if (s == NULL) return NULL;
    memcpy(s+curlen, t, len);
403
    sdssetlen(s, curlen+len);
A
antirez 已提交
404 405 406 407
    s[curlen+len] = '\0';
    return s;
}

408 409 410 411
/* Append the specified null termianted C string to the sds string 's'.
 *
 * After the call, the passed sds string is no longer valid and all the
 * references must be substituted with the new pointer returned by the call. */
412
sds sdscat(sds s, const char *t) {
A
antirez 已提交
413 414 415
    return sdscatlen(s, t, strlen(t));
}

416 417 418 419
/* Append the specified sds 't' to the existing sds 's'.
 *
 * After the call, the modified sds string is no longer valid and all the
 * references must be substituted with the new pointer returned by the call. */
420
sds sdscatsds(sds s, const sds t) {
A
antirez 已提交
421 422 423
    return sdscatlen(s, t, sdslen(t));
}

424 425
/* Destructively modify the sds string 's' to hold the specified binary
 * safe string pointed by 't' of length 'len' bytes. */
426
sds sdscpylen(sds s, const char *t, size_t len) {
427 428
    if (sdsalloc(s) < len) {
        s = sdsMakeRoomFor(s,len-sdslen(s));
A
antirez 已提交
429 430 431 432
        if (s == NULL) return NULL;
    }
    memcpy(s, t, len);
    s[len] = '\0';
433
    sdssetlen(s, len);
A
antirez 已提交
434 435 436
    return s;
}

437 438
/* Like sdscpylen() but 't' must be a null-termined string so that the length
 * of the string is obtained with strlen(). */
439
sds sdscpy(sds s, const char *t) {
A
antirez 已提交
440 441 442
    return sdscpylen(s, t, strlen(t));
}

A
antirez 已提交
443 444 445 446
/* Helper for sdscatlonglong() doing the actual number -> string
 * conversion. 's' must point to a string with room for at least
 * SDS_LLSTR_SIZE bytes.
 *
447
 * The function returns the length of the null-terminated string
A
antirez 已提交
448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
 * representation stored at 's'. */
#define SDS_LLSTR_SIZE 21
int sdsll2str(char *s, long long value) {
    char *p, aux;
    unsigned long long v;
    size_t l;

    /* Generate the string representation, this method produces
     * an reversed string. */
    v = (value < 0) ? -value : value;
    p = s;
    do {
        *p++ = '0'+(v%10);
        v /= 10;
    } while(v);
    if (value < 0) *p++ = '-';

    /* Compute length and add null term. */
    l = p-s;
    *p = '\0';

    /* Reverse the string. */
    p--;
    while(s < p) {
        aux = *s;
        *s = *p;
        *p = aux;
        s++;
        p--;
    }
    return l;
}

481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509
/* Identical sdsll2str(), but for unsigned long long type. */
int sdsull2str(char *s, unsigned long long v) {
    char *p, aux;
    size_t l;

    /* Generate the string representation, this method produces
     * an reversed string. */
    p = s;
    do {
        *p++ = '0'+(v%10);
        v /= 10;
    } while(v);

    /* Compute length and add null term. */
    l = p-s;
    *p = '\0';

    /* Reverse the string. */
    p--;
    while(s < p) {
        aux = *s;
        *s = *p;
        *p = aux;
        s++;
        p--;
    }
    return l;
}

A
antirez 已提交
510 511 512 513 514 515 516 517 518 519 520
/* Create an sds string from a long long value. It is much faster than:
 *
 * sdscatprintf(sdsempty(),"%lld\n", value);
 */
sds sdsfromlonglong(long long value) {
    char buf[SDS_LLSTR_SIZE];
    int len = sdsll2str(buf,value);

    return sdsnewlen(buf,len);
}

521
/* Like sdscatprintf() but gets va_list instead of being variadic. */
522 523
sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
    va_list cpy;
524 525
    char staticbuf[1024], *buf = staticbuf, *t;
    size_t buflen = strlen(fmt)*2;
A
antirez 已提交
526

527 528 529
    /* We try to start using a static buffer for speed.
     * If not possible we revert to heap allocation. */
    if (buflen > sizeof(staticbuf)) {
530
        buf = s_malloc(buflen);
A
antirez 已提交
531
        if (buf == NULL) return NULL;
532 533 534 535 536 537 538
    } else {
        buflen = sizeof(staticbuf);
    }

    /* Try with buffers two times bigger every time we fail to
     * fit the string in the current buffer size. */
    while(1) {
A
antirez 已提交
539
        buf[buflen-2] = '\0';
540 541
        va_copy(cpy,ap);
        vsnprintf(buf, buflen, fmt, cpy);
542
        va_end(cpy);
A
antirez 已提交
543
        if (buf[buflen-2] != '\0') {
544
            if (buf != staticbuf) s_free(buf);
A
antirez 已提交
545
            buflen *= 2;
546
            buf = s_malloc(buflen);
547
            if (buf == NULL) return NULL;
A
antirez 已提交
548 549 550 551
            continue;
        }
        break;
    }
552 553

    /* Finally concat the obtained string to the SDS string and return it. */
A
antirez 已提交
554
    t = sdscat(s, buf);
555
    if (buf != staticbuf) s_free(buf);
A
antirez 已提交
556 557 558
    return t;
}

559 560 561 562 563 564 565 566
/* Append to the sds string 's' a string obtained using printf-alike format
 * specifier.
 *
 * After the call, the modified sds string is no longer valid and all the
 * references must be substituted with the new pointer returned by the call.
 *
 * Example:
 *
S
Sun He 已提交
567
 * s = sdsnew("Sum is: ");
568 569 570 571 572 573 574
 * s = sdscatprintf(s,"%d+%d = %d",a,b,a+b).
 *
 * Often you need to create a string from scratch with the printf-alike
 * format. When this is the need, just use sdsempty() as the target string:
 *
 * s = sdscatprintf(sdsempty(), "... your format ...", args);
 */
575 576 577 578 579 580 581 582 583
sds sdscatprintf(sds s, const char *fmt, ...) {
    va_list ap;
    char *t;
    va_start(ap, fmt);
    t = sdscatvprintf(s,fmt,ap);
    va_end(ap);
    return t;
}

A
antirez 已提交
584 585 586 587 588 589 590 591 592 593
/* This function is similar to sdscatprintf, but much faster as it does
 * not rely on sprintf() family functions implemented by the libc that
 * are often very slow. Moreover directly handling the sds string as
 * new data is concatenated provides a performance improvement.
 *
 * However this function only handles an incompatible subset of printf-alike
 * format specifiers:
 *
 * %s - C String
 * %S - SDS string
594
 * %i - signed int
A
antirez 已提交
595
 * %I - 64 bit signed integer (long long, int64_t)
596 597
 * %u - unsigned int
 * %U - 64 bit unsigned integer (unsigned long long, uint64_t)
A
antirez 已提交
598 599 600 601 602
 * %% - Verbatim "%" character.
 */
sds sdscatfmt(sds s, char const *fmt, ...) {
    size_t initlen = sdslen(s);
    const char *f = fmt;
603
    long i;
A
antirez 已提交
604 605
    va_list ap;

606 607 608 609
    /* To avoid continuous reallocations, let's start with a buffer that
     * can hold at least two times the format string itself. It's not the
     * best heuristic but seems to work in practice. */
    s = sdsMakeRoomFor(s, initlen + strlen(fmt)*2);
A
antirez 已提交
610 611 612 613 614
    va_start(ap,fmt);
    f = fmt;    /* Next format specifier byte to process. */
    i = initlen; /* Position of the next byte to write to dest str. */
    while(*f) {
        char next, *str;
615
        size_t l;
A
antirez 已提交
616
        long long num;
617
        unsigned long long unum;
A
antirez 已提交
618 619

        /* Make sure there is always space for at least 1 char. */
620
        if (sdsavail(s)==0) {
621 622
            s = sdsMakeRoomFor(s,1);
        }
A
antirez 已提交
623 624 625 626 627 628 629 630 631 632

        switch(*f) {
        case '%':
            next = *(f+1);
            f++;
            switch(next) {
            case 's':
            case 'S':
                str = va_arg(ap,char*);
                l = (next == 's') ? strlen(str) : sdslen(str);
633
                if (sdsavail(s) < l) {
634 635
                    s = sdsMakeRoomFor(s,l);
                }
A
antirez 已提交
636
                memcpy(s+i,str,l);
637
                sdsinclen(s,l);
A
antirez 已提交
638 639 640 641 642 643 644 645 646 647 648
                i += l;
                break;
            case 'i':
            case 'I':
                if (next == 'i')
                    num = va_arg(ap,int);
                else
                    num = va_arg(ap,long long);
                {
                    char buf[SDS_LLSTR_SIZE];
                    l = sdsll2str(buf,num);
649
                    if (sdsavail(s) < l) {
650 651 652
                        s = sdsMakeRoomFor(s,l);
                    }
                    memcpy(s+i,buf,l);
653
                    sdsinclen(s,l);
654 655 656 657 658 659 660 661 662 663 664 665
                    i += l;
                }
                break;
            case 'u':
            case 'U':
                if (next == 'u')
                    unum = va_arg(ap,unsigned int);
                else
                    unum = va_arg(ap,unsigned long long);
                {
                    char buf[SDS_LLSTR_SIZE];
                    l = sdsull2str(buf,unum);
666
                    if (sdsavail(s) < l) {
667 668
                        s = sdsMakeRoomFor(s,l);
                    }
A
antirez 已提交
669
                    memcpy(s+i,buf,l);
670
                    sdsinclen(s,l);
A
antirez 已提交
671 672 673 674 675
                    i += l;
                }
                break;
            default: /* Handle %% and generally %<unknown>. */
                s[i++] = next;
676
                sdsinclen(s,1);
A
antirez 已提交
677 678 679 680 681
                break;
            }
            break;
        default:
            s[i++] = *f;
682
            sdsinclen(s,1);
A
antirez 已提交
683 684 685 686 687 688 689 690 691 692 693
            break;
        }
        f++;
    }
    va_end(ap);

    /* Add null-term */
    s[i] = '\0';
    return s;
}

694 695 696 697 698 699 700 701 702
/* Remove the part of the string from left and from right composed just of
 * contiguous characters found in 'cset', that is a null terminted C string.
 *
 * After the call, the modified sds string is no longer valid and all the
 * references must be substituted with the new pointer returned by the call.
 *
 * Example:
 *
 * s = sdsnew("AA...AA.a.aa.aHelloWorld     :::");
F
FuGangqiang 已提交
703
 * s = sdstrim(s,"Aa. :");
704 705
 * printf("%s\n", s);
 *
H
hdmg 已提交
706
 * Output will be just "HelloWorld".
707
 */
A
antirez 已提交
708 709 710 711 712 713 714
sds sdstrim(sds s, const char *cset) {
    char *start, *end, *sp, *ep;
    size_t len;

    sp = start = s;
    ep = end = s+sdslen(s)-1;
    while(sp <= end && strchr(cset, *sp)) sp++;
B
Brochen 已提交
715
    while(ep > sp && strchr(cset, *ep)) ep--;
A
antirez 已提交
716
    len = (sp > ep) ? 0 : ((ep-sp)+1);
717 718 719
    if (s != sp) memmove(s, sp, len);
    s[len] = '\0';
    sdssetlen(s,len);
A
antirez 已提交
720 721 722
    return s;
}

723 724 725 726 727 728 729 730 731 732 733 734 735 736
/* Turn the string into a smaller (or equal) string containing only the
 * substring specified by the 'start' and 'end' indexes.
 *
 * start and end can be negative, where -1 means the last character of the
 * string, -2 the penultimate character, and so forth.
 *
 * The interval is inclusive, so the start and end characters will be part
 * of the resulting string.
 *
 * The string is modified in-place.
 *
 * Example:
 *
 * s = sdsnew("Hello World");
C
codeeply 已提交
737
 * sdsrange(s,1,-1); => "ello World"
738
 */
739
void sdsrange(sds s, ssize_t start, ssize_t end) {
A
antirez 已提交
740 741
    size_t newlen, len = sdslen(s);

742
    if (len == 0) return;
A
antirez 已提交
743 744 745 746 747 748 749 750 751 752
    if (start < 0) {
        start = len+start;
        if (start < 0) start = 0;
    }
    if (end < 0) {
        end = len+end;
        if (end < 0) end = 0;
    }
    newlen = (start > end) ? 0 : (end-start)+1;
    if (newlen != 0) {
753
        if (start >= (ssize_t)len) {
754
            newlen = 0;
755
        } else if (end >= (ssize_t)len) {
756 757 758
            end = len-1;
            newlen = (start > end) ? 0 : (end-start)+1;
        }
A
antirez 已提交
759 760 761
    } else {
        start = 0;
    }
762 763 764
    if (start && newlen) memmove(s, s+start, newlen);
    s[newlen] = 0;
    sdssetlen(s,newlen);
A
antirez 已提交
765 766
}

767
/* Apply tolower() to every character of the sds string 's'. */
A
antirez 已提交
768
void sdstolower(sds s) {
769
    size_t len = sdslen(s), j;
A
antirez 已提交
770 771 772 773

    for (j = 0; j < len; j++) s[j] = tolower(s[j]);
}

774
/* Apply toupper() to every character of the sds string 's'. */
A
antirez 已提交
775
void sdstoupper(sds s) {
776
    size_t len = sdslen(s), j;
A
antirez 已提交
777 778 779 780

    for (j = 0; j < len; j++) s[j] = toupper(s[j]);
}

781 782 783 784
/* Compare two sds strings s1 and s2 with memcmp().
 *
 * Return value:
 *
S
Sun He 已提交
785 786
 *     positive if s1 > s2.
 *     negative if s1 < s2.
787 788 789 790 791
 *     0 if s1 and s2 are exactly the same binary string.
 *
 * If two strings share exactly the same prefix, but one of the two has
 * additional characters, the longer string is considered to be greater than
 * the smaller one. */
792
int sdscmp(const sds s1, const sds s2) {
A
antirez 已提交
793 794 795 796 797 798 799
    size_t l1, l2, minlen;
    int cmp;

    l1 = sdslen(s1);
    l2 = sdslen(s2);
    minlen = (l1 < l2) ? l1 : l2;
    cmp = memcmp(s1,s2,minlen);
800
    if (cmp == 0) return l1>l2? 1: (l1<l2? -1: 0);
A
antirez 已提交
801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819
    return cmp;
}

/* Split 's' with separator in 'sep'. An array
 * of sds strings is returned. *count will be set
 * by reference to the number of tokens returned.
 *
 * On out of memory, zero length string, zero length
 * separator, NULL is returned.
 *
 * Note that 'sep' is able to split a string using
 * a multi-character separator. For example
 * sdssplit("foo_-_bar","_-_"); will return two
 * elements "foo" and "bar".
 *
 * This version of the function is binary-safe but
 * requires length arguments. sdssplit() is just the
 * same function but for zero-terminated strings.
 */
820 821 822
sds *sdssplitlen(const char *s, ssize_t len, const char *sep, int seplen, int *count) {
    int elements = 0, slots = 5;
    long start = 0, j;
823
    sds *tokens;
A
antirez 已提交
824

825 826
    if (seplen < 1 || len < 0) return NULL;

827
    tokens = s_malloc(sizeof(sds)*slots);
828 829
    if (tokens == NULL) return NULL;

830 831 832 833
    if (len == 0) {
        *count = 0;
        return tokens;
    }
A
antirez 已提交
834 835 836
    for (j = 0; j < (len-(seplen-1)); j++) {
        /* make sure there is room for the next element and the final one */
        if (slots < elements+2) {
A
antirez 已提交
837 838
            sds *newtokens;

A
antirez 已提交
839
            slots *= 2;
840
            newtokens = s_realloc(tokens,sizeof(sds)*slots);
841
            if (newtokens == NULL) goto cleanup;
A
antirez 已提交
842 843 844 845 846
            tokens = newtokens;
        }
        /* search the separator */
        if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) {
            tokens[elements] = sdsnewlen(s+start,j-start);
847
            if (tokens[elements] == NULL) goto cleanup;
A
antirez 已提交
848 849 850 851 852 853 854
            elements++;
            start = j+seplen;
            j = j+seplen-1; /* skip the separator */
        }
    }
    /* Add the final element. We are sure there is room in the tokens array. */
    tokens[elements] = sdsnewlen(s+start,len-start);
855
    if (tokens[elements] == NULL) goto cleanup;
A
antirez 已提交
856 857 858 859 860 861 862 863
    elements++;
    *count = elements;
    return tokens;

cleanup:
    {
        int i;
        for (i = 0; i < elements; i++) sdsfree(tokens[i]);
864
        s_free(tokens);
865
        *count = 0;
A
antirez 已提交
866 867 868
        return NULL;
    }
}
869

870
/* Free the result returned by sdssplitlen(), or do nothing if 'tokens' is NULL. */
871 872 873 874
void sdsfreesplitres(sds *tokens, int count) {
    if (!tokens) return;
    while(count--)
        sdsfree(tokens[count]);
875
    s_free(tokens);
876
}
877

878 879 880 881 882 883
/* Append to the sds string "s" an escaped string representation where
 * all the non-printable characters (tested with isprint()) are turned into
 * escapes in the form "\n\r\a...." or "\x<hex-number>".
 *
 * After the call, the modified sds string is no longer valid and all the
 * references must be substituted with the new pointer returned by the call. */
884
sds sdscatrepr(sds s, const char *p, size_t len) {
885 886 887 888 889 890 891
    s = sdscatlen(s,"\"",1);
    while(len--) {
        switch(*p) {
        case '\\':
        case '"':
            s = sdscatprintf(s,"\\%c",*p);
            break;
892 893 894 895 896
        case '\n': s = sdscatlen(s,"\\n",2); break;
        case '\r': s = sdscatlen(s,"\\r",2); break;
        case '\t': s = sdscatlen(s,"\\t",2); break;
        case '\a': s = sdscatlen(s,"\\a",2); break;
        case '\b': s = sdscatlen(s,"\\b",2); break;
897 898 899 900 901 902 903 904 905 906 907
        default:
            if (isprint(*p))
                s = sdscatprintf(s,"%c",*p);
            else
                s = sdscatprintf(s,"\\x%02x",(unsigned char)*p);
            break;
        }
        p++;
    }
    return sdscatlen(s,"\"",1);
}
908

909 910 911 912 913 914 915
/* Helper function for sdssplitargs() that returns non zero if 'c'
 * is a valid hex digit. */
int is_hex_digit(char c) {
    return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
           (c >= 'A' && c <= 'F');
}

916
/* Helper function for sdssplitargs() that converts a hex digit into an
917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939
 * integer from 0 to 15 */
int hex_digit_to_int(char c) {
    switch(c) {
    case '0': return 0;
    case '1': return 1;
    case '2': return 2;
    case '3': return 3;
    case '4': return 4;
    case '5': return 5;
    case '6': return 6;
    case '7': return 7;
    case '8': return 8;
    case '9': return 9;
    case 'a': case 'A': return 10;
    case 'b': case 'B': return 11;
    case 'c': case 'C': return 12;
    case 'd': case 'D': return 13;
    case 'e': case 'E': return 14;
    case 'f': case 'F': return 15;
    default: return 0;
    }
}

940 941 942 943 944 945
/* Split a line into arguments, where every argument can be in the
 * following programming-language REPL-alike form:
 *
 * foo bar "newline are supported\n" and "\xff\x00otherstuff"
 *
 * The number of arguments is stored into *argc, and an array
946 947 948 949
 * of sds is returned.
 *
 * The caller should free the resulting array of sds strings with
 * sdsfreesplitres().
950 951 952
 *
 * Note that sdscatrepr() is able to convert back a string into
 * a quoted string in the same format sdssplitargs() is able to parse.
953 954 955 956 957
 *
 * The function returns the allocated tokens on success, even when the
 * input string is empty, or NULL if the input contains unbalanced
 * quotes or closed quotes followed by non space characters
 * as in: "foo"bar or "foo'
958
 */
959 960
sds *sdssplitargs(const char *line, int *argc) {
    const char *p = line;
961 962 963 964 965 966 967 968 969
    char *current = NULL;
    char **vector = NULL;

    *argc = 0;
    while(1) {
        /* skip blanks */
        while(*p && isspace(*p)) p++;
        if (*p) {
            /* get a token */
A
antirez 已提交
970 971
            int inq=0;  /* set to 1 if we are in "quotes" */
            int insq=0; /* set to 1 if we are in 'single quotes' */
972
            int done=0;
973 974 975 976

            if (current == NULL) current = sdsempty();
            while(!done) {
                if (inq) {
977 978 979 980 981 982 983 984 985 986 987
                    if (*p == '\\' && *(p+1) == 'x' &&
                                             is_hex_digit(*(p+2)) &&
                                             is_hex_digit(*(p+3)))
                    {
                        unsigned char byte;

                        byte = (hex_digit_to_int(*(p+2))*16)+
                                hex_digit_to_int(*(p+3));
                        current = sdscatlen(current,(char*)&byte,1);
                        p += 3;
                    } else if (*p == '\\' && *(p+1)) {
988 989 990 991 992 993 994 995 996 997 998 999 1000
                        char c;

                        p++;
                        switch(*p) {
                        case 'n': c = '\n'; break;
                        case 'r': c = '\r'; break;
                        case 't': c = '\t'; break;
                        case 'b': c = '\b'; break;
                        case 'a': c = '\a'; break;
                        default: c = *p; break;
                        }
                        current = sdscatlen(current,&c,1);
                    } else if (*p == '"') {
A
antirez 已提交
1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017
                        /* closing quote must be followed by a space or
                         * nothing at all. */
                        if (*(p+1) && !isspace(*(p+1))) goto err;
                        done=1;
                    } else if (!*p) {
                        /* unterminated quotes */
                        goto err;
                    } else {
                        current = sdscatlen(current,p,1);
                    }
                } else if (insq) {
                    if (*p == '\\' && *(p+1) == '\'') {
                        p++;
                        current = sdscatlen(current,"'",1);
                    } else if (*p == '\'') {
                        /* closing quote must be followed by a space or
                         * nothing at all. */
1018 1019 1020 1021 1022
                        if (*(p+1) && !isspace(*(p+1))) goto err;
                        done=1;
                    } else if (!*p) {
                        /* unterminated quotes */
                        goto err;
1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037
                    } else {
                        current = sdscatlen(current,p,1);
                    }
                } else {
                    switch(*p) {
                    case ' ':
                    case '\n':
                    case '\r':
                    case '\t':
                    case '\0':
                        done=1;
                        break;
                    case '"':
                        inq=1;
                        break;
A
antirez 已提交
1038 1039 1040
                    case '\'':
                        insq=1;
                        break;
1041 1042 1043 1044 1045 1046 1047 1048
                    default:
                        current = sdscatlen(current,p,1);
                        break;
                    }
                }
                if (*p) p++;
            }
            /* add the token to the vector */
1049
            vector = s_realloc(vector,((*argc)+1)*sizeof(char*));
1050 1051 1052 1053
            vector[*argc] = current;
            (*argc)++;
            current = NULL;
        } else {
1054
            /* Even on empty input string return something not NULL. */
1055
            if (vector == NULL) vector = s_malloc(sizeof(void*));
1056 1057 1058
            return vector;
        }
    }
1059 1060

err:
1061
    while((*argc)--)
1062
        sdsfree(vector[*argc]);
1063
    s_free(vector);
1064
    if (current) sdsfree(current);
1065
    *argc = 0;
1066
    return NULL;
1067
}
1068

1069
/* Modify the string substituting all the occurrences of the set of
G
guiquanz 已提交
1070
 * characters specified in the 'from' string to the corresponding character
1071 1072 1073 1074 1075 1076 1077
 * in the 'to' array.
 *
 * For instance: sdsmapchars(mystring, "ho", "01", 2)
 * will have the effect of turning the string "hello" into "0ell1".
 *
 * The function returns the sds string pointer, that is always the same
 * as the input pointer since no resize is needed. */
1078
sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) {
1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091
    size_t j, i, l = sdslen(s);

    for (j = 0; j < l; j++) {
        for (i = 0; i < setlen; i++) {
            if (s[j] == from[i]) {
                s[j] = to[i];
                break;
            }
        }
    }
    return s;
}

1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104
/* Join an array of C strings using the specified separator (also a C string).
 * Returns the result as an sds string. */
sds sdsjoin(char **argv, int argc, char *sep) {
    sds join = sdsempty();
    int j;

    for (j = 0; j < argc; j++) {
        join = sdscat(join, argv[j]);
        if (j != argc-1) join = sdscat(join,sep);
    }
    return join;
}

1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116
/* Like sdsjoin, but joins an array of SDS strings. */
sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen) {
    sds join = sdsempty();
    int j;

    for (j = 0; j < argc; j++) {
        join = sdscatsds(join, argv[j]);
        if (j != argc-1) join = sdscatlen(join,sep,seplen);
    }
    return join;
}

1117 1118 1119 1120 1121 1122 1123 1124 1125
/* Wrappers to the allocators used by SDS. Note that SDS will actually
 * just use the macros defined into sdsalloc.h in order to avoid to pay
 * the overhead of function calls. Here we define these wrappers only for
 * the programs SDS is linked to, if they want to touch the SDS internals
 * even if they use a different allocator. */
void *sds_malloc(size_t size) { return s_malloc(size); }
void *sds_realloc(void *ptr, size_t size) { return s_realloc(ptr,size); }
void sds_free(void *ptr) { s_free(ptr); }

1126
#if defined(SDS_TEST_MAIN)
1127 1128
#include <stdio.h>
#include "testhelp.h"
A
antirez 已提交
1129
#include "limits.h"
1130

1131
#define UNUSED(x) (void)(x)
A
antirez 已提交
1132
int sdsTest(void) {
1133
    {
A
antirez 已提交
1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159
        sds x = sdsnew("foo"), y;

        test_cond("Create a string and obtain the length",
            sdslen(x) == 3 && memcmp(x,"foo\0",4) == 0)

        sdsfree(x);
        x = sdsnewlen("foo",2);
        test_cond("Create a string with specified length",
            sdslen(x) == 2 && memcmp(x,"fo\0",3) == 0)

        x = sdscat(x,"bar");
        test_cond("Strings concatenation",
            sdslen(x) == 5 && memcmp(x,"fobar\0",6) == 0);

        x = sdscpy(x,"a");
        test_cond("sdscpy() against an originally longer string",
            sdslen(x) == 1 && memcmp(x,"a\0",2) == 0)

        x = sdscpy(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk");
        test_cond("sdscpy() against an originally shorter string",
            sdslen(x) == 33 &&
            memcmp(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0",33) == 0)

        sdsfree(x);
        x = sdscatprintf(sdsempty(),"%d",123);
        test_cond("sdscatprintf() seems working in the base case",
A
antirez 已提交
1160 1161 1162 1163 1164 1165 1166 1167 1168
            sdslen(x) == 3 && memcmp(x,"123\0",4) == 0)

        sdsfree(x);
        x = sdsnew("--");
        x = sdscatfmt(x, "Hello %s World %I,%I--", "Hi!", LLONG_MIN,LLONG_MAX);
        test_cond("sdscatfmt() seems working in the base case",
            sdslen(x) == 60 &&
            memcmp(x,"--Hello Hi! World -9223372036854775808,"
                     "9223372036854775807--",60) == 0)
A
antirez 已提交
1169
        printf("[%s]\n",x);
A
antirez 已提交
1170

1171 1172 1173 1174 1175 1176 1177
        sdsfree(x);
        x = sdsnew("--");
        x = sdscatfmt(x, "%u,%U--", UINT_MAX, ULLONG_MAX);
        test_cond("sdscatfmt() seems working with unsigned numbers",
            sdslen(x) == 35 &&
            memcmp(x,"--4294967295,18446744073709551615--",35) == 0)

A
antirez 已提交
1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189
        sdsfree(x);
        x = sdsnew(" x ");
        sdstrim(x," x");
        test_cond("sdstrim() works when all chars match",
            sdslen(x) == 0)

        sdsfree(x);
        x = sdsnew(" x ");
        sdstrim(x," ");
        test_cond("sdstrim() works when a single char remains",
            sdslen(x) == 1 && x[0] == 'x')

A
antirez 已提交
1190
        sdsfree(x);
A
antirez 已提交
1191 1192
        x = sdsnew("xxciaoyyy");
        sdstrim(x,"xy");
A
antirez 已提交
1193 1194 1195
        test_cond("sdstrim() correctly trims characters",
            sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0)

A
antirez 已提交
1196 1197
        y = sdsdup(x);
        sdsrange(y,1,1);
A
antirez 已提交
1198 1199 1200 1201
        test_cond("sdsrange(...,1,1)",
            sdslen(y) == 1 && memcmp(y,"i\0",2) == 0)

        sdsfree(y);
A
antirez 已提交
1202 1203
        y = sdsdup(x);
        sdsrange(y,1,-1);
A
antirez 已提交
1204 1205 1206 1207
        test_cond("sdsrange(...,1,-1)",
            sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)

        sdsfree(y);
A
antirez 已提交
1208 1209
        y = sdsdup(x);
        sdsrange(y,-2,-1);
A
antirez 已提交
1210 1211 1212 1213
        test_cond("sdsrange(...,-2,-1)",
            sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0)

        sdsfree(y);
A
antirez 已提交
1214 1215
        y = sdsdup(x);
        sdsrange(y,2,1);
A
antirez 已提交
1216 1217 1218 1219
        test_cond("sdsrange(...,2,1)",
            sdslen(y) == 0 && memcmp(y,"\0",1) == 0)

        sdsfree(y);
A
antirez 已提交
1220 1221
        y = sdsdup(x);
        sdsrange(y,1,100);
A
antirez 已提交
1222 1223 1224 1225
        test_cond("sdsrange(...,1,100)",
            sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)

        sdsfree(y);
A
antirez 已提交
1226 1227
        y = sdsdup(x);
        sdsrange(y,100,100);
A
antirez 已提交
1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247
        test_cond("sdsrange(...,100,100)",
            sdslen(y) == 0 && memcmp(y,"\0",1) == 0)

        sdsfree(y);
        sdsfree(x);
        x = sdsnew("foo");
        y = sdsnew("foa");
        test_cond("sdscmp(foo,foa)", sdscmp(x,y) > 0)

        sdsfree(y);
        sdsfree(x);
        x = sdsnew("bar");
        y = sdsnew("bar");
        test_cond("sdscmp(bar,bar)", sdscmp(x,y) == 0)

        sdsfree(y);
        sdsfree(x);
        x = sdsnew("aar");
        y = sdsnew("bar");
        test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0)
1248

A
antirez 已提交
1249 1250 1251 1252 1253 1254 1255
        sdsfree(y);
        sdsfree(x);
        x = sdsnewlen("\a\n\0foo\r",7);
        y = sdscatrepr(sdsempty(),x,sdslen(x));
        test_cond("sdscatrepr(...data...)",
            memcmp(y,"\"\\a\\n\\x00foo\\r\"",15) == 0)

1256
        {
1257
            unsigned int oldfree;
1258 1259
            char *p;
            int step = 10, j, i;
1260 1261

            sdsfree(x);
F
FuGangqiang 已提交
1262
            sdsfree(y);
1263
            x = sdsnew("0");
1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286
            test_cond("sdsnew() free/len buffers", sdslen(x) == 1 && sdsavail(x) == 0);

            /* Run the test a few times in order to hit the first two
             * SDS header types. */
            for (i = 0; i < 10; i++) {
                int oldlen = sdslen(x);
                x = sdsMakeRoomFor(x,step);
                int type = x[-1]&SDS_TYPE_MASK;

                test_cond("sdsMakeRoomFor() len", sdslen(x) == oldlen);
                if (type != SDS_TYPE_5) {
                    test_cond("sdsMakeRoomFor() free", sdsavail(x) >= step);
                    oldfree = sdsavail(x);
                }
                p = x+oldlen;
                for (j = 0; j < step; j++) {
                    p[j] = 'A'+j;
                }
                sdsIncrLen(x,step);
            }
            test_cond("sdsMakeRoomFor() content",
                memcmp("0ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJ",x,101) == 0);
            test_cond("sdsMakeRoomFor() final length",sdslen(x)==101);
F
FuGangqiang 已提交
1287 1288

            sdsfree(x);
1289
        }
1290 1291
    }
    test_report()
1292
    return 0;
1293 1294
}
#endif
1295 1296 1297 1298 1299 1300

#ifdef SDS_TEST_MAIN
int main(void) {
    return sdsTest();
}
#endif