#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "sds.h"
#ifdef SDS_ABORT_ON_OOM
static
void
sdsOomAbort(
void
) {
fprintf
(stderr,
"SDS: Out Of Memory (SDS_ABORT_ON_OOM defined)\n"
);
abort
();
}
#endif
sds sdsnewlen(
const
void
*init,
size_t
initlen) {
struct
sdshdr *sh;
sh =
malloc
(
sizeof
(
struct
sdshdr)+initlen+1);
#ifdef SDS_ABORT_ON_OOM
if
(sh == NULL) sdsOomAbort();
#else
if
(sh == NULL)
return
NULL;
#endif
sh->len = initlen;
sh->
free
= 0;
if
(initlen) {
if
(init)
memcpy
(sh->buf, init, initlen);
else
memset
(sh->buf,0,initlen);
}
sh->buf[initlen] =
'\0'
;
return
(
char
*)sh->buf;
}
sds sdsempty(
void
) {
return
sdsnewlen(
""
,0);
}
sds sdsnew(
const
char
*init) {
size_t
initlen = (init == NULL) ? 0 :
strlen
(init);
return
sdsnewlen(init, initlen);
}
sds sdsdup(
const
sds s) {
return
sdsnewlen(s, sdslen(s));
}
void
sdsfree(sds s) {
if
(s == NULL)
return
;
free
(s-
sizeof
(
struct
sdshdr));
}
void
sdsupdatelen(sds s) {
struct
sdshdr *sh = (
void
*) (s-(
sizeof
(
struct
sdshdr)));
int
reallen =
strlen
(s);
sh->
free
+= (sh->len-reallen);
sh->len = reallen;
}
static
sds sdsMakeRoomFor(sds s,
size_t
addlen) {
struct
sdshdr *sh, *newsh;
size_t
free
= sdsavail(s);
size_t
len, newlen;
if
(
free
>= addlen)
return
s;
len = sdslen(s);
sh = (
void
*) (s-(
sizeof
(
struct
sdshdr)));
newlen = (len+addlen)*2;
newsh =
realloc
(sh,
sizeof
(
struct
sdshdr)+newlen+1);
#ifdef SDS_ABORT_ON_OOM
if
(newsh == NULL) sdsOomAbort();
#else
if
(newsh == NULL)
return
NULL;
#endif
newsh->
free
= newlen - len;
return
newsh->buf;
}
sds sdsgrowzero(sds s,
size_t
len) {
struct
sdshdr *sh = (
void
*)(s-(
sizeof
(
struct
sdshdr)));
size_t
totlen, curlen = sh->len;
if
(len <= curlen)
return
s;
s = sdsMakeRoomFor(s,len-curlen);
if
(s == NULL)
return
NULL;
sh = (
void
*)(s-(
sizeof
(
struct
sdshdr)));
memset
(s+curlen,0,(len-curlen+1));
totlen = sh->len+sh->
free
;
sh->len = len;
sh->
free
= totlen-sh->len;
return
s;
}
sds sdscatlen(sds s,
const
void
*t,
size_t
len) {
struct
sdshdr *sh;
size_t
curlen = sdslen(s);
s = sdsMakeRoomFor(s,len);
if
(s == NULL)
return
NULL;
sh = (
void
*) (s-(
sizeof
(
struct
sdshdr)));
memcpy
(s+curlen, t, len);
sh->len = curlen+len;
sh->
free
= sh->
free
-len;
s[curlen+len] =
'\0'
;
return
s;
}
sds sdscat(sds s,
const
char
*t) {
return
sdscatlen(s, t,
strlen
(t));
}
sds sdscpylen(sds s,
char
*t,
size_t
len) {
struct
sdshdr *sh = (
void
*) (s-(
sizeof
(
struct
sdshdr)));
size_t
totlen = sh->
free
+sh->len;
if
(totlen < len) {
s = sdsMakeRoomFor(s,len-sh->len);
if
(s == NULL)
return
NULL;
sh = (
void
*) (s-(
sizeof
(
struct
sdshdr)));
totlen = sh->
free
+sh->len;
}
memcpy
(s, t, len);
s[len] =
'\0'
;
sh->len = len;
sh->
free
= totlen-len;
return
s;
}
sds sdscpy(sds s,
char
*t) {
return
sdscpylen(s, t,
strlen
(t));
}
sds sdscatvprintf(sds s,
const
char
*fmt,
va_list
ap) {
va_list
cpy;
char
*buf, *t;
size_t
buflen = 16;
while
(1) {
buf =
malloc
(buflen);
#ifdef SDS_ABORT_ON_OOM
if
(buf == NULL) sdsOomAbort();
#else
if
(buf == NULL)
return
NULL;
#endif
buf[buflen-2] =
'\0'
;
va_copy(cpy,ap);
vsnprintf(buf, buflen, fmt, cpy);
if
(buf[buflen-2] !=
'\0'
) {
free
(buf);
buflen *= 2;
continue
;
}
break
;
}
t = sdscat(s, buf);
free
(buf);
return
t;
}
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;
}
sds sdstrim(sds s,
const
char
*cset) {
struct
sdshdr *sh = (
void
*) (s-(
sizeof
(
struct
sdshdr)));
char
*start, *end, *sp, *ep;
size_t
len;
sp = start = s;
ep = end = s+sdslen(s)-1;
while
(sp <= end &&
strchr
(cset, *sp)) sp++;
while
(ep > start &&
strchr
(cset, *ep)) ep--;
len = (sp > ep) ? 0 : ((ep-sp)+1);
if
(sh->buf != sp)
memmove
(sh->buf, sp, len);
sh->buf[len] =
'\0'
;
sh->
free
= sh->
free
+(sh->len-len);
sh->len = len;
return
s;
}
sds sdsrange(sds s,
int
start,
int
end) {
struct
sdshdr *sh = (
void
*) (s-(
sizeof
(
struct
sdshdr)));
size_t
newlen, len = sdslen(s);
if
(len == 0)
return
s;
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) {
if
(start >= (
signed
)len) {
newlen = 0;
}
else
if
(end >= (
signed
)len) {
end = len-1;
newlen = (start > end) ? 0 : (end-start)+1;
}
}
else
{
start = 0;
}
if
(start && newlen)
memmove
(sh->buf, sh->buf+start, newlen);
sh->buf[newlen] = 0;
sh->
free
= sh->
free
+(sh->len-newlen);
sh->len = newlen;
return
s;
}
void
sdstolower(sds s) {
int
len = sdslen(s), j;
for
(j = 0; j < len; j++) s[j] =
tolower
(s[j]);
}
void
sdstoupper(sds s) {
int
len = sdslen(s), j;
for
(j = 0; j < len; j++) s[j] =
toupper
(s[j]);
}
int
sdscmp(sds s1, sds s2) {
size_t
l1, l2, minlen;
int
cmp;
l1 = sdslen(s1);
l2 = sdslen(s2);
minlen = (l1 < l2) ? l1 : l2;
cmp =
memcmp
(s1,s2,minlen);
if
(cmp == 0)
return
l1-l2;
return
cmp;
}
sds *sdssplitlen(
char
*s,
int
len,
char
*sep,
int
seplen,
int
*count) {
int
elements = 0, slots = 5, start = 0, j;
sds *tokens =
malloc
(
sizeof
(sds)*slots);
#ifdef SDS_ABORT_ON_OOM
if
(tokens == NULL) sdsOomAbort();
#endif
if
(seplen < 1 || len < 0 || tokens == NULL)
return
NULL;
if
(len == 0) {
*count = 0;
return
tokens;
}
for
(j = 0; j < (len-(seplen-1)); j++) {
if
(slots < elements+2) {
sds *newtokens;
slots *= 2;
newtokens =
realloc
(tokens,
sizeof
(sds)*slots);
if
(newtokens == NULL) {
#ifdef SDS_ABORT_ON_OOM
sdsOomAbort();
#else
goto
cleanup;
#endif
}
tokens = newtokens;
}
if
((seplen == 1 && *(s+j) == sep[0]) || (
memcmp
(s+j,sep,seplen) == 0)) {
tokens[elements] = sdsnewlen(s+start,j-start);
if
(tokens[elements] == NULL) {
#ifdef SDS_ABORT_ON_OOM
sdsOomAbort();
#else
goto
cleanup;
#endif
}
elements++;
start = j+seplen;
j = j+seplen-1;
}
}
tokens[elements] = sdsnewlen(s+start,len-start);
if
(tokens[elements] == NULL) {
#ifdef SDS_ABORT_ON_OOM
sdsOomAbort();
#else
goto
cleanup;
#endif
}
elements++;
*count = elements;
return
tokens;
#ifndef SDS_ABORT_ON_OOM
cleanup:
{
int
i;
for
(i = 0; i < elements; i++) sdsfree(tokens[i]);
free
(tokens);
return
NULL;
}
#endif
}
void
sdsfreesplitres(sds *tokens,
int
count) {
if
(!tokens)
return
;
while
(count--)
sdsfree(tokens[count]);
free
(tokens);
}
sds sdsfromlonglong(
long
long
value) {
char
buf[32], *p;
unsigned
long
long
v;
v = (value < 0) ? -value : value;
p = buf+31;
do
{
*p-- =
'0'
+(v%10);
v /= 10;
}
while
(v);
if
(value < 0) *p-- =
'-'
;
p++;
return
sdsnewlen(p,32-(p-buf));
}
sds sdscatrepr(sds s,
char
*p,
size_t
len) {
s = sdscatlen(s,
"\""
,1);
if
(s == NULL)
return
NULL;
while
(len--) {
switch
(*p) {
case
'\\'
:
case
'"'
:
s = sdscatprintf(s,
"\\%c"
,*p);
break
;
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
;
default
:
if
(isprint(*p))
s = sdscatprintf(s,
"%c"
,*p);
else
s = sdscatprintf(s,
"\\x%02x"
,(unsigned
char
)*p);
break
;
}
p++;
if
(s == NULL)
return
NULL;
}
return
sdscatlen(s,
"\""
,1);
}
sds *sdssplitargs(
char
*line,
int
*argc) {
char
*p = line;
char
*current = NULL;
char
**vector = NULL, **_vector = NULL;
*argc = 0;
while
(1) {
while
(*p &&
isspace
(*p)) p++;
if
(*p) {
int
inq=0;
int
done=0;
if
(current == NULL) {
current = sdsempty();
if
(current == NULL)
goto
err;
}
while
(!done) {
if
(inq) {
if
(*p ==
'\\'
&& *(p+1)) {
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 ==
'"'
) {
if
(*(p+1) && !
isspace
(*(p+1)))
goto
err;
done=1;
}
else
if
(!*p) {
goto
err;
}
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
;
default
:
current = sdscatlen(current,p,1);
break
;
}
}
if
(*p) p++;
if
(current == NULL)
goto
err;
}
_vector =
realloc
(vector,((*argc)+1)*
sizeof
(
char
*));
if
(_vector == NULL)
goto
err;
vector = _vector;
vector[*argc] = current;
(*argc)++;
current = NULL;
}
else
{
return
vector;
}
}
err:
while
((*argc)--)
sdsfree(vector[*argc]);
if
(vector != NULL)
free
(vector);
if
(current != NULL) sdsfree(current);
return
NULL;
}
#ifdef SDS_TEST_MAIN
#include <stdio.h>
int
__failed_tests = 0;
int
__test_num = 0;
#define test_cond(descr,_c) do { \
__test_num++;
printf
(
"%d - %s: "
, __test_num, descr); \
if
(_c)
printf
(
"PASSED\n"
);
else
{
printf
(
"FAILED\n"
); __failed_tests++;} \
}
while
(0);
#define test_report() do { \
printf
(
"%d tests, %d passed, %d failed\n"
, __test_num, \
__test_num-__failed_tests, __failed_tests); \
if
(__failed_tests) { \
printf
(
"=== WARNING === We have failed tests here...\n"
); \
} \
}
while
(0);
int
main(
void
) {
{
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 = sdstrim(sdsnew(
"xxciaoyyy"
),
"xy"
);
test_cond(
"sdstrim() correctly trims characters"
,
sdslen(x) == 4 &&
memcmp
(x,
"ciao\0"
,5) == 0)
y = sdsrange(sdsdup(x),1,1);
test_cond(
"sdsrange(...,1,1)"
,
sdslen(y) == 1 &&
memcmp
(y,
"i\0"
,2) == 0)
sdsfree(y);
y = sdsrange(sdsdup(x),1,-1);
test_cond(
"sdsrange(...,1,-1)"
,
sdslen(y) == 3 &&
memcmp
(y,
"iao\0"
,4) == 0)
sdsfree(y);
y = sdsrange(sdsdup(x),-2,-1);
test_cond(
"sdsrange(...,-2,-1)"
,
sdslen(y) == 2 &&
memcmp
(y,
"ao\0"
,3) == 0)
sdsfree(y);
y = sdsrange(sdsdup(x),2,1);
test_cond(
"sdsrange(...,2,1)"
,
sdslen(y) == 0 &&
memcmp
(y,
"\0"
,1) == 0)
sdsfree(y);
y = sdsrange(sdsdup(x),1,100);
test_cond(
"sdsrange(...,1,100)"
,
sdslen(y) == 3 &&
memcmp
(y,
"iao\0"
,4) == 0)
sdsfree(y);
y = sdsrange(sdsdup(x),100,100);
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)
}
test_report()
}
#endif