mirror of
https://codeberg.org/redict/redict.git
synced 2025-01-22 08:08:53 -05:00
Freshen up sds.c from hiredict
For ABI compatibility, etc. Signed-off-by: Drew DeVault <sir@cmpwn.com>
This commit is contained in:
parent
abd89c3ce2
commit
04992aead7
509
src/sds.c
509
src/sds.c
@ -1,21 +1,28 @@
|
||||
// Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
// Copyright (c) 2015, Oran Agra
|
||||
// Copyright (c) 2015, Redis Labs, Inc
|
||||
// SPDX-FileCopyrightText: 2024 Redict Contributors
|
||||
// SPDX-FileCopyrightText: 2024 Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
/* SDSLib 2.0 -- A C dynamic strings library
|
||||
*
|
||||
* Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2015, Oran Agra
|
||||
* Copyright (c) 2015, Redis Labs, Inc
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024 Hiredict Contributors
|
||||
* SPDX-FileCopyrightText: 2024 Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* SPDX-FileCopyrightText: 2024 Oran Agra
|
||||
* SPDX-FileCopyrightText: 2024 Redis Labs, Inc
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
* SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
*
|
||||
*/
|
||||
|
||||
#include "fmacros.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include "redictassert.h"
|
||||
#include "sds.h"
|
||||
#include "sdsalloc.h"
|
||||
#include "util.h"
|
||||
|
||||
const char *SDS_NOINIT = "SDS_NOINIT";
|
||||
|
||||
@ -36,19 +43,15 @@ static inline int sdsHdrSize(char type) {
|
||||
}
|
||||
|
||||
static inline char sdsReqType(size_t string_size) {
|
||||
if (string_size < 1<<5)
|
||||
if (string_size < 32)
|
||||
return SDS_TYPE_5;
|
||||
if (string_size < 1<<8)
|
||||
if (string_size < 0xff)
|
||||
return SDS_TYPE_8;
|
||||
if (string_size < 1<<16)
|
||||
if (string_size < 0xffff)
|
||||
return SDS_TYPE_16;
|
||||
#if (LONG_MAX == LLONG_MAX)
|
||||
if (string_size < 1ll<<32)
|
||||
if (string_size < 0xffffffff)
|
||||
return SDS_TYPE_32;
|
||||
return SDS_TYPE_64;
|
||||
#else
|
||||
return SDS_TYPE_32;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline size_t sdsTypeMaxSize(char type) {
|
||||
@ -355,7 +358,7 @@ sds sdsResize(sds s, size_t size, int would_regrow) {
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Return the total size of the allocation of the specified sds string,
|
||||
/* Return the total size of the allocation of the specifed sds string,
|
||||
* including:
|
||||
* 1) The sds header before the pointer.
|
||||
* 2) The string.
|
||||
@ -396,7 +399,7 @@ void *sdsAllocPtr(sds s) {
|
||||
* ... check for nread <= 0 and handle it ...
|
||||
* sdsIncrLen(s, nread);
|
||||
*/
|
||||
void sdsIncrLen(sds s, ssize_t incr) {
|
||||
void sdsIncrLen(sds s, int incr) {
|
||||
unsigned char flags = s[-1];
|
||||
size_t len;
|
||||
switch(flags&SDS_TYPE_MASK) {
|
||||
@ -471,7 +474,7 @@ sds sdscatlen(sds s, const void *t, size_t len) {
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Append the specified null terminated C string to the sds string 's'.
|
||||
/* 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. */
|
||||
@ -506,13 +509,80 @@ sds sdscpy(sds s, const char *t) {
|
||||
return sdscpylen(s, t, strlen(t));
|
||||
}
|
||||
|
||||
/* Helper for sdscatlonglong() doing the actual number -> string
|
||||
* conversion. 's' must point to a string with room for at least
|
||||
* SDS_LLSTR_SIZE bytes.
|
||||
*
|
||||
* The function returns the length of the null-terminated string
|
||||
* 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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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[LONG_STR_SIZE];
|
||||
int len = ll2string(buf,sizeof(buf),value);
|
||||
char buf[SDS_LLSTR_SIZE];
|
||||
int len = sdsll2str(buf,value);
|
||||
|
||||
return sdsnewlen(buf,len);
|
||||
}
|
||||
@ -522,7 +592,6 @@ sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
|
||||
va_list cpy;
|
||||
char staticbuf[1024], *buf = staticbuf, *t;
|
||||
size_t buflen = strlen(fmt)*2;
|
||||
int bufstrlen;
|
||||
|
||||
/* We try to start using a static buffer for speed.
|
||||
* If not possible we revert to heap allocation. */
|
||||
@ -533,19 +602,16 @@ sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
|
||||
buflen = sizeof(staticbuf);
|
||||
}
|
||||
|
||||
/* Alloc enough space for buffer and \0 after failing to
|
||||
/* Try with buffers two times bigger every time we fail to
|
||||
* fit the string in the current buffer size. */
|
||||
while(1) {
|
||||
buf[buflen-2] = '\0';
|
||||
va_copy(cpy,ap);
|
||||
bufstrlen = vsnprintf(buf, buflen, fmt, cpy);
|
||||
vsnprintf(buf, buflen, fmt, cpy);
|
||||
va_end(cpy);
|
||||
if (bufstrlen < 0) {
|
||||
if (buf[buflen-2] != '\0') {
|
||||
if (buf != staticbuf) s_free(buf);
|
||||
return NULL;
|
||||
}
|
||||
if (((size_t)bufstrlen) >= buflen) {
|
||||
if (buf != staticbuf) s_free(buf);
|
||||
buflen = ((size_t)bufstrlen) + 1;
|
||||
buflen *= 2;
|
||||
buf = s_malloc(buflen);
|
||||
if (buf == NULL) return NULL;
|
||||
continue;
|
||||
@ -554,7 +620,7 @@ sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
|
||||
}
|
||||
|
||||
/* Finally concat the obtained string to the SDS string and return it. */
|
||||
t = sdscatlen(s, buf, bufstrlen);
|
||||
t = sdscat(s, buf);
|
||||
if (buf != staticbuf) s_free(buf);
|
||||
return t;
|
||||
}
|
||||
@ -601,18 +667,12 @@ sds sdscatprintf(sds s, const char *fmt, ...) {
|
||||
* %% - Verbatim "%" character.
|
||||
*/
|
||||
sds sdscatfmt(sds s, char const *fmt, ...) {
|
||||
size_t initlen = sdslen(s);
|
||||
const char *f = fmt;
|
||||
long i;
|
||||
va_list ap;
|
||||
|
||||
/* 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, strlen(fmt)*2);
|
||||
va_start(ap,fmt);
|
||||
f = fmt; /* Next format specifier byte to process. */
|
||||
i = initlen; /* Position of the next byte to write to dest str. */
|
||||
i = sdslen(s); /* Position of the next byte to write to dest str. */
|
||||
while(*f) {
|
||||
char next, *str;
|
||||
size_t l;
|
||||
@ -622,12 +682,12 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
|
||||
/* Make sure there is always space for at least 1 char. */
|
||||
if (sdsavail(s)==0) {
|
||||
s = sdsMakeRoomFor(s,1);
|
||||
if (s == NULL) goto fmt_error;
|
||||
}
|
||||
|
||||
switch(*f) {
|
||||
case '%':
|
||||
next = *(f+1);
|
||||
if (next == '\0') break;
|
||||
f++;
|
||||
switch(next) {
|
||||
case 's':
|
||||
@ -636,6 +696,7 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
|
||||
l = (next == 's') ? strlen(str) : sdslen(str);
|
||||
if (sdsavail(s) < l) {
|
||||
s = sdsMakeRoomFor(s,l);
|
||||
if (s == NULL) goto fmt_error;
|
||||
}
|
||||
memcpy(s+i,str,l);
|
||||
sdsinclen(s,l);
|
||||
@ -648,10 +709,11 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
|
||||
else
|
||||
num = va_arg(ap,long long);
|
||||
{
|
||||
char buf[LONG_STR_SIZE];
|
||||
l = ll2string(buf,sizeof(buf),num);
|
||||
char buf[SDS_LLSTR_SIZE];
|
||||
l = sdsll2str(buf,num);
|
||||
if (sdsavail(s) < l) {
|
||||
s = sdsMakeRoomFor(s,l);
|
||||
if (s == NULL) goto fmt_error;
|
||||
}
|
||||
memcpy(s+i,buf,l);
|
||||
sdsinclen(s,l);
|
||||
@ -665,10 +727,11 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
|
||||
else
|
||||
unum = va_arg(ap,unsigned long long);
|
||||
{
|
||||
char buf[LONG_STR_SIZE];
|
||||
l = ull2string(buf,sizeof(buf),unum);
|
||||
char buf[SDS_LLSTR_SIZE];
|
||||
l = sdsull2str(buf,unum);
|
||||
if (sdsavail(s) < l) {
|
||||
s = sdsMakeRoomFor(s,l);
|
||||
if (s == NULL) goto fmt_error;
|
||||
}
|
||||
memcpy(s+i,buf,l);
|
||||
sdsinclen(s,l);
|
||||
@ -693,10 +756,14 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
|
||||
/* Add null-term */
|
||||
s[i] = '\0';
|
||||
return s;
|
||||
|
||||
fmt_error:
|
||||
va_end(ap);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Remove the part of the string from left and from right composed just of
|
||||
* contiguous characters found in 'cset', that is a null terminated C string.
|
||||
* 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.
|
||||
@ -710,14 +777,14 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
|
||||
* Output will be just "Hello World".
|
||||
*/
|
||||
sds sdstrim(sds s, const char *cset) {
|
||||
char *end, *sp, *ep;
|
||||
char *start, *end, *sp, *ep;
|
||||
size_t len;
|
||||
|
||||
sp = s;
|
||||
sp = start = s;
|
||||
ep = end = s+sdslen(s)-1;
|
||||
while(sp <= end && strchr(cset, *sp)) sp++;
|
||||
while(ep > sp && strchr(cset, *ep)) ep--;
|
||||
len = (ep-sp)+1;
|
||||
len = (sp > ep) ? 0 : ((ep-sp)+1);
|
||||
if (s != sp) memmove(s, sp, len);
|
||||
s[len] = '\0';
|
||||
sdssetlen(s,len);
|
||||
@ -750,25 +817,43 @@ void sdssubstr(sds s, size_t start, size_t len) {
|
||||
*
|
||||
* The string is modified in-place.
|
||||
*
|
||||
* NOTE: this function can be misleading and can have unexpected behaviour,
|
||||
* specifically when you want the length of the new string to be 0.
|
||||
* Having start==end will result in a string with one character.
|
||||
* please consider using sdssubstr instead.
|
||||
* Return value:
|
||||
* -1 (error) if sdslen(s) is larger than maximum positive ssize_t value.
|
||||
* 0 on success.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* s = sdsnew("Hello World");
|
||||
* sdsrange(s,1,-1); => "ello World"
|
||||
*/
|
||||
void sdsrange(sds s, ssize_t start, ssize_t end) {
|
||||
int sdsrange(sds s, ssize_t start, ssize_t end) {
|
||||
size_t newlen, len = sdslen(s);
|
||||
if (len == 0) return;
|
||||
if (start < 0)
|
||||
if (len > SSIZE_MAX) return -1;
|
||||
|
||||
if (len == 0) return 0;
|
||||
if (start < 0) {
|
||||
start = len+start;
|
||||
if (end < 0)
|
||||
if (start < 0) start = 0;
|
||||
}
|
||||
if (end < 0) {
|
||||
end = len+end;
|
||||
if (end < 0) end = 0;
|
||||
}
|
||||
newlen = (start > end) ? 0 : (end-start)+1;
|
||||
sdssubstr(s, start, newlen);
|
||||
if (newlen != 0) {
|
||||
if (start >= (ssize_t)len) {
|
||||
newlen = 0;
|
||||
} else if (end >= (ssize_t)len) {
|
||||
end = len-1;
|
||||
newlen = (start > end) ? 0 : (end-start)+1;
|
||||
}
|
||||
} else {
|
||||
start = 0;
|
||||
}
|
||||
if (start && newlen) memmove(s, s+start, newlen);
|
||||
s[newlen] = 0;
|
||||
sdssetlen(s,newlen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Apply tolower() to every character of the sds string 's'. */
|
||||
@ -804,7 +889,7 @@ int sdscmp(const sds s1, const sds s2) {
|
||||
l2 = sdslen(s2);
|
||||
minlen = (l1 < l2) ? l1 : l2;
|
||||
cmp = memcmp(s1,s2,minlen);
|
||||
if (cmp == 0) return l1>l2? 1: (l1<l2? -1: 0);
|
||||
if (cmp == 0) return l1-l2;
|
||||
return cmp;
|
||||
}
|
||||
|
||||
@ -824,18 +909,19 @@ int sdscmp(const sds s1, const sds s2) {
|
||||
* requires length arguments. sdssplit() is just the
|
||||
* same function but for zero-terminated strings.
|
||||
*/
|
||||
sds *sdssplitlen(const char *s, ssize_t len, const char *sep, int seplen, int *count) {
|
||||
int elements = 0, slots = 5;
|
||||
long start = 0, j;
|
||||
sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count) {
|
||||
int elements = 0, slots = 5, start = 0, j;
|
||||
sds *tokens;
|
||||
|
||||
if (seplen < 1 || len <= 0) {
|
||||
*count = 0;
|
||||
return NULL;
|
||||
}
|
||||
if (seplen < 1 || len < 0) return NULL;
|
||||
|
||||
tokens = s_malloc(sizeof(sds)*slots);
|
||||
if (tokens == NULL) return NULL;
|
||||
|
||||
if (len == 0) {
|
||||
*count = 0;
|
||||
return tokens;
|
||||
}
|
||||
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) {
|
||||
@ -887,7 +973,6 @@ void sdsfreesplitres(sds *tokens, int count) {
|
||||
* 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. */
|
||||
sds sdscatrepr(sds s, const char *p, size_t len) {
|
||||
s = sdsMakeRoomFor(s, len + 2);
|
||||
s = sdscatlen(s,"\"",1);
|
||||
while(len--) {
|
||||
switch(*p) {
|
||||
@ -901,8 +986,8 @@ sds sdscatrepr(sds s, const char *p, size_t len) {
|
||||
case '\a': s = sdscatlen(s,"\\a",2); break;
|
||||
case '\b': s = sdscatlen(s,"\\b",2); break;
|
||||
default:
|
||||
if (isprint(*p))
|
||||
s = sdscatlen(s, p, 1);
|
||||
if (isprint((int) *p))
|
||||
s = sdscatprintf(s,"%c",*p);
|
||||
else
|
||||
s = sdscatprintf(s,"\\x%02x",(unsigned char)*p);
|
||||
break;
|
||||
@ -990,7 +1075,7 @@ sds *sdssplitargs(const char *line, int *argc) {
|
||||
*argc = 0;
|
||||
while(1) {
|
||||
/* skip blanks */
|
||||
while(*p && isspace(*p)) p++;
|
||||
while(*p && isspace((int) *p)) p++;
|
||||
if (*p) {
|
||||
/* get a token */
|
||||
int inq=0; /* set to 1 if we are in "quotes" */
|
||||
@ -1001,8 +1086,8 @@ sds *sdssplitargs(const char *line, int *argc) {
|
||||
while(!done) {
|
||||
if (inq) {
|
||||
if (*p == '\\' && *(p+1) == 'x' &&
|
||||
is_hex_digit(*(p+2)) &&
|
||||
is_hex_digit(*(p+3)))
|
||||
isxdigit((int) *(p+2)) &&
|
||||
isxdigit((int) *(p+3)))
|
||||
{
|
||||
unsigned char byte;
|
||||
|
||||
@ -1026,7 +1111,7 @@ sds *sdssplitargs(const char *line, int *argc) {
|
||||
} else if (*p == '"') {
|
||||
/* closing quote must be followed by a space or
|
||||
* nothing at all. */
|
||||
if (*(p+1) && !isspace(*(p+1))) goto err;
|
||||
if (*(p+1) && !isspace((int) *(p+1))) goto err;
|
||||
done=1;
|
||||
} else if (!*p) {
|
||||
/* unterminated quotes */
|
||||
@ -1041,7 +1126,7 @@ sds *sdssplitargs(const char *line, int *argc) {
|
||||
} else if (*p == '\'') {
|
||||
/* closing quote must be followed by a space or
|
||||
* nothing at all. */
|
||||
if (*(p+1) && !isspace(*(p+1))) goto err;
|
||||
if (*(p+1) && !isspace((int) *(p+1))) goto err;
|
||||
done=1;
|
||||
} else if (!*p) {
|
||||
/* unterminated quotes */
|
||||
@ -1072,10 +1157,18 @@ sds *sdssplitargs(const char *line, int *argc) {
|
||||
if (*p) p++;
|
||||
}
|
||||
/* add the token to the vector */
|
||||
vector = s_realloc(vector,((*argc)+1)*sizeof(char*));
|
||||
{
|
||||
char **new_vector = s_realloc(vector,((*argc)+1)*sizeof(char*));
|
||||
if (new_vector == NULL) {
|
||||
s_free(vector);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
vector = new_vector;
|
||||
vector[*argc] = current;
|
||||
(*argc)++;
|
||||
current = NULL;
|
||||
}
|
||||
} else {
|
||||
/* Even on empty input string return something not NULL. */
|
||||
if (vector == NULL) vector = s_malloc(sizeof(void*));
|
||||
@ -1205,269 +1298,3 @@ error:
|
||||
sdsfree(res);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef REDICT_TEST
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include "testhelp.h"
|
||||
|
||||
#define UNUSED(x) (void)(x)
|
||||
|
||||
static sds sdsTestTemplateCallback(sds varname, void *arg) {
|
||||
UNUSED(arg);
|
||||
static const char *_var1 = "variable1";
|
||||
static const char *_var2 = "variable2";
|
||||
|
||||
if (!strcmp(varname, _var1)) return sdsnew("value1");
|
||||
else if (!strcmp(varname, _var2)) return sdsnew("value2");
|
||||
else return NULL;
|
||||
}
|
||||
|
||||
int sdsTest(int argc, char **argv, int flags) {
|
||||
UNUSED(argc);
|
||||
UNUSED(argv);
|
||||
UNUSED(flags);
|
||||
|
||||
{
|
||||
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",
|
||||
sdslen(x) == 3 && memcmp(x,"123\0",4) == 0);
|
||||
|
||||
sdsfree(x);
|
||||
x = sdscatprintf(sdsempty(),"a%cb",0);
|
||||
test_cond("sdscatprintf() seems working with \\0 inside of result",
|
||||
sdslen(x) == 3 && memcmp(x,"a\0""b\0",4) == 0);
|
||||
|
||||
{
|
||||
sdsfree(x);
|
||||
char etalon[1024*1024];
|
||||
for (size_t i = 0; i < sizeof(etalon); i++) {
|
||||
etalon[i] = '0';
|
||||
}
|
||||
x = sdscatprintf(sdsempty(),"%0*d",(int)sizeof(etalon),0);
|
||||
test_cond("sdscatprintf() can print 1MB",
|
||||
sdslen(x) == sizeof(etalon) && memcmp(x,etalon,sizeof(etalon)) == 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);
|
||||
printf("[%s]\n",x);
|
||||
|
||||
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);
|
||||
|
||||
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');
|
||||
|
||||
sdsfree(x);
|
||||
x = sdsnew("xxciaoyyy");
|
||||
sdstrim(x,"xy");
|
||||
test_cond("sdstrim() correctly trims characters",
|
||||
sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0);
|
||||
|
||||
y = sdsdup(x);
|
||||
sdsrange(y,1,1);
|
||||
test_cond("sdsrange(...,1,1)",
|
||||
sdslen(y) == 1 && memcmp(y,"i\0",2) == 0);
|
||||
|
||||
sdsfree(y);
|
||||
y = sdsdup(x);
|
||||
sdsrange(y,1,-1);
|
||||
test_cond("sdsrange(...,1,-1)",
|
||||
sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0);
|
||||
|
||||
sdsfree(y);
|
||||
y = sdsdup(x);
|
||||
sdsrange(y,-2,-1);
|
||||
test_cond("sdsrange(...,-2,-1)",
|
||||
sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0);
|
||||
|
||||
sdsfree(y);
|
||||
y = sdsdup(x);
|
||||
sdsrange(y,2,1);
|
||||
test_cond("sdsrange(...,2,1)",
|
||||
sdslen(y) == 0 && memcmp(y,"\0",1) == 0);
|
||||
|
||||
sdsfree(y);
|
||||
y = sdsdup(x);
|
||||
sdsrange(y,1,100);
|
||||
test_cond("sdsrange(...,1,100)",
|
||||
sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0);
|
||||
|
||||
sdsfree(y);
|
||||
y = sdsdup(x);
|
||||
sdsrange(y,100,100);
|
||||
test_cond("sdsrange(...,100,100)",
|
||||
sdslen(y) == 0 && memcmp(y,"\0",1) == 0);
|
||||
|
||||
sdsfree(y);
|
||||
y = sdsdup(x);
|
||||
sdsrange(y,4,6);
|
||||
test_cond("sdsrange(...,4,6)",
|
||||
sdslen(y) == 0 && memcmp(y,"\0",1) == 0);
|
||||
|
||||
sdsfree(y);
|
||||
y = sdsdup(x);
|
||||
sdsrange(y,3,6);
|
||||
test_cond("sdsrange(...,3,6)",
|
||||
sdslen(y) == 1 && memcmp(y,"o\0",2) == 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);
|
||||
|
||||
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);
|
||||
|
||||
{
|
||||
unsigned int oldfree;
|
||||
char *p;
|
||||
int i;
|
||||
size_t step = 10, j;
|
||||
|
||||
sdsfree(x);
|
||||
sdsfree(y);
|
||||
x = sdsnew("0");
|
||||
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++) {
|
||||
size_t 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);
|
||||
UNUSED(oldfree);
|
||||
}
|
||||
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);
|
||||
|
||||
sdsfree(x);
|
||||
}
|
||||
|
||||
/* Simple template */
|
||||
x = sdstemplate("v1={variable1} v2={variable2}", sdsTestTemplateCallback, NULL);
|
||||
test_cond("sdstemplate() normal flow",
|
||||
memcmp(x,"v1=value1 v2=value2",19) == 0);
|
||||
sdsfree(x);
|
||||
|
||||
/* Template with callback error */
|
||||
x = sdstemplate("v1={variable1} v3={doesnotexist}", sdsTestTemplateCallback, NULL);
|
||||
test_cond("sdstemplate() with callback error", x == NULL);
|
||||
|
||||
/* Template with empty var name */
|
||||
x = sdstemplate("v1={", sdsTestTemplateCallback, NULL);
|
||||
test_cond("sdstemplate() with empty var name", x == NULL);
|
||||
|
||||
/* Template with truncated var name */
|
||||
x = sdstemplate("v1={start", sdsTestTemplateCallback, NULL);
|
||||
test_cond("sdstemplate() with truncated var name", x == NULL);
|
||||
|
||||
/* Template with quoting */
|
||||
x = sdstemplate("v1={{{variable1}} {{} v2={variable2}", sdsTestTemplateCallback, NULL);
|
||||
test_cond("sdstemplate() with quoting",
|
||||
memcmp(x,"v1={value1} {} v2=value2",24) == 0);
|
||||
sdsfree(x);
|
||||
|
||||
/* Test sdsresize - extend */
|
||||
x = sdsnew("1234567890123456789012345678901234567890");
|
||||
x = sdsResize(x, 200, 1);
|
||||
test_cond("sdsrezie() expand len", sdslen(x) == 40);
|
||||
test_cond("sdsrezie() expand strlen", strlen(x) == 40);
|
||||
test_cond("sdsrezie() expand alloc", sdsalloc(x) == 200);
|
||||
/* Test sdsresize - trim free space */
|
||||
x = sdsResize(x, 80, 1);
|
||||
test_cond("sdsrezie() shrink len", sdslen(x) == 40);
|
||||
test_cond("sdsrezie() shrink strlen", strlen(x) == 40);
|
||||
test_cond("sdsrezie() shrink alloc", sdsalloc(x) == 80);
|
||||
/* Test sdsresize - crop used space */
|
||||
x = sdsResize(x, 30, 1);
|
||||
test_cond("sdsrezie() crop len", sdslen(x) == 30);
|
||||
test_cond("sdsrezie() crop strlen", strlen(x) == 30);
|
||||
test_cond("sdsrezie() crop alloc", sdsalloc(x) == 30);
|
||||
/* Test sdsresize - extend to different class */
|
||||
x = sdsResize(x, 400, 1);
|
||||
test_cond("sdsrezie() expand len", sdslen(x) == 30);
|
||||
test_cond("sdsrezie() expand strlen", strlen(x) == 30);
|
||||
test_cond("sdsrezie() expand alloc", sdsalloc(x) == 400);
|
||||
/* Test sdsresize - shrink to different class */
|
||||
x = sdsResize(x, 4, 1);
|
||||
test_cond("sdsrezie() crop len", sdslen(x) == 4);
|
||||
test_cond("sdsrezie() crop strlen", strlen(x) == 4);
|
||||
test_cond("sdsrezie() crop alloc", sdsalloc(x) == 4);
|
||||
sdsfree(x);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
69
src/sds.h
69
src/sds.h
@ -1,11 +1,18 @@
|
||||
// Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
// Copyright (c) 2015, Oran Agra
|
||||
// Copyright (c) 2015, Redis Labs, Inc
|
||||
// SPDX-FileCopyrightText: 2024 Redict Contributors
|
||||
// SPDX-FileCopyrightText: 2024 Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
/* SDSLib 2.0 -- A C dynamic strings library
|
||||
*
|
||||
* Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2015, Oran Agra
|
||||
* Copyright (c) 2015, Redis Labs, Inc
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024 Hiredict Contributors
|
||||
* SPDX-FileCopyrightText: 2024 Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* SPDX-FileCopyrightText: 2024 Oran Agra
|
||||
* SPDX-FileCopyrightText: 2024 Redis Labs, Inc
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
* SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __SDS_H
|
||||
#define __SDS_H
|
||||
@ -13,6 +20,14 @@
|
||||
#define SDS_MAX_PREALLOC (1024*1024)
|
||||
extern const char *SDS_NOINIT;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
typedef long long ssize_t;
|
||||
#define SSIZE_MAX (LLONG_MAX >> 1)
|
||||
#ifndef __clang__
|
||||
#define __attribute__(x)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
@ -57,7 +72,7 @@ struct __attribute__ ((__packed__)) sdshdr64 {
|
||||
#define SDS_TYPE_64 4
|
||||
#define SDS_TYPE_MASK 7
|
||||
#define SDS_TYPE_BITS 3
|
||||
#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (void*)((s)-(sizeof(struct sdshdr##T)));
|
||||
#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T)));
|
||||
#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))
|
||||
#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS)
|
||||
|
||||
@ -110,20 +125,20 @@ static inline void sdssetlen(sds s, size_t newlen) {
|
||||
case SDS_TYPE_5:
|
||||
{
|
||||
unsigned char *fp = ((unsigned char*)s)-1;
|
||||
*fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);
|
||||
*fp = (unsigned char)(SDS_TYPE_5 | (newlen << SDS_TYPE_BITS));
|
||||
}
|
||||
break;
|
||||
case SDS_TYPE_8:
|
||||
SDS_HDR(8,s)->len = newlen;
|
||||
SDS_HDR(8,s)->len = (uint8_t)newlen;
|
||||
break;
|
||||
case SDS_TYPE_16:
|
||||
SDS_HDR(16,s)->len = newlen;
|
||||
SDS_HDR(16,s)->len = (uint16_t)newlen;
|
||||
break;
|
||||
case SDS_TYPE_32:
|
||||
SDS_HDR(32,s)->len = newlen;
|
||||
SDS_HDR(32,s)->len = (uint32_t)newlen;
|
||||
break;
|
||||
case SDS_TYPE_64:
|
||||
SDS_HDR(64,s)->len = newlen;
|
||||
SDS_HDR(64,s)->len = (uint64_t)newlen;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -134,21 +149,21 @@ static inline void sdsinclen(sds s, size_t inc) {
|
||||
case SDS_TYPE_5:
|
||||
{
|
||||
unsigned char *fp = ((unsigned char*)s)-1;
|
||||
unsigned char newlen = SDS_TYPE_5_LEN(flags)+inc;
|
||||
unsigned char newlen = SDS_TYPE_5_LEN(flags)+(unsigned char)inc;
|
||||
*fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);
|
||||
}
|
||||
break;
|
||||
case SDS_TYPE_8:
|
||||
SDS_HDR(8,s)->len += inc;
|
||||
SDS_HDR(8,s)->len += (uint8_t)inc;
|
||||
break;
|
||||
case SDS_TYPE_16:
|
||||
SDS_HDR(16,s)->len += inc;
|
||||
SDS_HDR(16,s)->len += (uint16_t)inc;
|
||||
break;
|
||||
case SDS_TYPE_32:
|
||||
SDS_HDR(32,s)->len += inc;
|
||||
SDS_HDR(32,s)->len += (uint32_t)inc;
|
||||
break;
|
||||
case SDS_TYPE_64:
|
||||
SDS_HDR(64,s)->len += inc;
|
||||
SDS_HDR(64,s)->len += (uint64_t)inc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -178,16 +193,16 @@ static inline void sdssetalloc(sds s, size_t newlen) {
|
||||
/* Nothing to do, this type has no total allocation info. */
|
||||
break;
|
||||
case SDS_TYPE_8:
|
||||
SDS_HDR(8,s)->alloc = newlen;
|
||||
SDS_HDR(8,s)->alloc = (uint8_t)newlen;
|
||||
break;
|
||||
case SDS_TYPE_16:
|
||||
SDS_HDR(16,s)->alloc = newlen;
|
||||
SDS_HDR(16,s)->alloc = (uint16_t)newlen;
|
||||
break;
|
||||
case SDS_TYPE_32:
|
||||
SDS_HDR(32,s)->alloc = newlen;
|
||||
SDS_HDR(32,s)->alloc = (uint32_t)newlen;
|
||||
break;
|
||||
case SDS_TYPE_64:
|
||||
SDS_HDR(64,s)->alloc = newlen;
|
||||
SDS_HDR(64,s)->alloc = (uint64_t)newlen;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -216,11 +231,11 @@ sds sdscatprintf(sds s, const char *fmt, ...);
|
||||
sds sdscatfmt(sds s, char const *fmt, ...);
|
||||
sds sdstrim(sds s, const char *cset);
|
||||
void sdssubstr(sds s, size_t start, size_t len);
|
||||
void sdsrange(sds s, ssize_t start, ssize_t end);
|
||||
int sdsrange(sds s, ssize_t start, ssize_t end);
|
||||
void sdsupdatelen(sds s);
|
||||
void sdsclear(sds s);
|
||||
int sdscmp(const sds s1, const sds s2);
|
||||
sds *sdssplitlen(const char *s, ssize_t len, const char *sep, int seplen, int *count);
|
||||
sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count);
|
||||
void sdsfreesplitres(sds *tokens, int count);
|
||||
void sdstolower(sds s);
|
||||
void sdstoupper(sds s);
|
||||
@ -243,7 +258,7 @@ sds sdstemplate(const char *template, sdstemplate_callback_t cb_func, void *cb_a
|
||||
/* Low level functions exposed to the user API */
|
||||
sds sdsMakeRoomFor(sds s, size_t addlen);
|
||||
sds sdsMakeRoomForNonGreedy(sds s, size_t addlen);
|
||||
void sdsIncrLen(sds s, ssize_t incr);
|
||||
void sdsIncrLen(sds s, int incr);
|
||||
sds sdsRemoveFreeSpace(sds s, int would_regrow);
|
||||
sds sdsResize(sds s, size_t size, int would_regrow);
|
||||
size_t sdsAllocSize(sds s);
|
||||
@ -258,7 +273,7 @@ void *sds_realloc(void *ptr, size_t size);
|
||||
void sds_free(void *ptr);
|
||||
|
||||
#ifdef REDICT_TEST
|
||||
int sdsTest(int argc, char *argv[], int flags);
|
||||
int sdsTest(int argc, char *argv[]);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user