sds.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. /* SDSLib, A C dynamic strings library
  2. *
  3. * Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions are met:
  8. *
  9. * * Redistributions of source code must retain the above copyright notice,
  10. * this list of conditions and the following disclaimer.
  11. * * Redistributions in binary form must reproduce the above copyright
  12. * notice, this list of conditions and the following disclaimer in the
  13. * documentation and/or other materials provided with the distribution.
  14. * * Neither the name of Redis nor the names of its contributors may be used
  15. * to endorse or promote products derived from this software without
  16. * specific prior written permission.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  19. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  20. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  21. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  22. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  23. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  24. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  25. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  26. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  27. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  28. * POSSIBILITY OF SUCH DAMAGE.
  29. */
  30. #include <stdio.h>
  31. #include <stdlib.h>
  32. #include <string.h>
  33. #include <ctype.h>
  34. #include "sds.h"
  35. #ifdef SDS_ABORT_ON_OOM
  36. static void sdsOomAbort(void) {
  37. fprintf(stderr,"SDS: Out Of Memory (SDS_ABORT_ON_OOM defined)\n");
  38. abort();
  39. }
  40. #endif
  41. sds sdsnewlen(const void *init, size_t initlen) {
  42. struct sdshdr *sh;
  43. sh = malloc(sizeof(struct sdshdr)+initlen+1);
  44. #ifdef SDS_ABORT_ON_OOM
  45. if (sh == NULL) sdsOomAbort();
  46. #else
  47. if (sh == NULL) return NULL;
  48. #endif
  49. sh->len = initlen;
  50. sh->free = 0;
  51. if (initlen) {
  52. if (init) memcpy(sh->buf, init, initlen);
  53. else memset(sh->buf,0,initlen);
  54. }
  55. sh->buf[initlen] = '\0';
  56. return (char*)sh->buf;
  57. }
  58. sds sdsempty(void) {
  59. return sdsnewlen("",0);
  60. }
  61. sds sdsnew(const char *init) {
  62. size_t initlen = (init == NULL) ? 0 : strlen(init);
  63. return sdsnewlen(init, initlen);
  64. }
  65. sds sdsdup(const sds s) {
  66. return sdsnewlen(s, sdslen(s));
  67. }
  68. void sdsfree(sds s) {
  69. if (s == NULL) return;
  70. free(s-sizeof(struct sdshdr));
  71. }
  72. void sdsupdatelen(sds s) {
  73. struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
  74. int reallen = strlen(s);
  75. sh->free += (sh->len-reallen);
  76. sh->len = reallen;
  77. }
  78. static sds sdsMakeRoomFor(sds s, size_t addlen) {
  79. struct sdshdr *sh, *newsh;
  80. size_t free = sdsavail(s);
  81. size_t len, newlen;
  82. if (free >= addlen) return s;
  83. len = sdslen(s);
  84. sh = (void*) (s-(sizeof(struct sdshdr)));
  85. newlen = (len+addlen)*2;
  86. newsh = realloc(sh, sizeof(struct sdshdr)+newlen+1);
  87. #ifdef SDS_ABORT_ON_OOM
  88. if (newsh == NULL) sdsOomAbort();
  89. #else
  90. if (newsh == NULL) return NULL;
  91. #endif
  92. newsh->free = newlen - len;
  93. return newsh->buf;
  94. }
  95. /* Grow the sds to have the specified length. Bytes that were not part of
  96. * the original length of the sds will be set to zero. */
  97. sds sdsgrowzero(sds s, size_t len) {
  98. struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
  99. size_t totlen, curlen = sh->len;
  100. if (len <= curlen) return s;
  101. s = sdsMakeRoomFor(s,len-curlen);
  102. if (s == NULL) return NULL;
  103. /* Make sure added region doesn't contain garbage */
  104. sh = (void*)(s-(sizeof(struct sdshdr)));
  105. memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */
  106. totlen = sh->len+sh->free;
  107. sh->len = len;
  108. sh->free = totlen-sh->len;
  109. return s;
  110. }
  111. sds sdscatlen(sds s, const void *t, size_t len) {
  112. struct sdshdr *sh;
  113. size_t curlen = sdslen(s);
  114. s = sdsMakeRoomFor(s,len);
  115. if (s == NULL) return NULL;
  116. sh = (void*) (s-(sizeof(struct sdshdr)));
  117. memcpy(s+curlen, t, len);
  118. sh->len = curlen+len;
  119. sh->free = sh->free-len;
  120. s[curlen+len] = '\0';
  121. return s;
  122. }
  123. sds sdscat(sds s, const char *t) {
  124. return sdscatlen(s, t, strlen(t));
  125. }
  126. sds sdscpylen(sds s, char *t, size_t len) {
  127. struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
  128. size_t totlen = sh->free+sh->len;
  129. if (totlen < len) {
  130. s = sdsMakeRoomFor(s,len-sh->len);
  131. if (s == NULL) return NULL;
  132. sh = (void*) (s-(sizeof(struct sdshdr)));
  133. totlen = sh->free+sh->len;
  134. }
  135. memcpy(s, t, len);
  136. s[len] = '\0';
  137. sh->len = len;
  138. sh->free = totlen-len;
  139. return s;
  140. }
  141. sds sdscpy(sds s, char *t) {
  142. return sdscpylen(s, t, strlen(t));
  143. }
  144. sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
  145. va_list cpy;
  146. char *buf, *t;
  147. size_t buflen = 16;
  148. while(1) {
  149. buf = malloc(buflen);
  150. #ifdef SDS_ABORT_ON_OOM
  151. if (buf == NULL) sdsOomAbort();
  152. #else
  153. if (buf == NULL) return NULL;
  154. #endif
  155. buf[buflen-2] = '\0';
  156. va_copy(cpy,ap);
  157. vsnprintf(buf, buflen, fmt, cpy);
  158. if (buf[buflen-2] != '\0') {
  159. free(buf);
  160. buflen *= 2;
  161. continue;
  162. }
  163. break;
  164. }
  165. t = sdscat(s, buf);
  166. free(buf);
  167. return t;
  168. }
  169. sds sdscatprintf(sds s, const char *fmt, ...) {
  170. va_list ap;
  171. char *t;
  172. va_start(ap, fmt);
  173. t = sdscatvprintf(s,fmt,ap);
  174. va_end(ap);
  175. return t;
  176. }
  177. sds sdstrim(sds s, const char *cset) {
  178. struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
  179. char *start, *end, *sp, *ep;
  180. size_t len;
  181. sp = start = s;
  182. ep = end = s+sdslen(s)-1;
  183. while(sp <= end && strchr(cset, *sp)) sp++;
  184. while(ep > start && strchr(cset, *ep)) ep--;
  185. len = (sp > ep) ? 0 : ((ep-sp)+1);
  186. if (sh->buf != sp) memmove(sh->buf, sp, len);
  187. sh->buf[len] = '\0';
  188. sh->free = sh->free+(sh->len-len);
  189. sh->len = len;
  190. return s;
  191. }
  192. sds sdsrange(sds s, int start, int end) {
  193. struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
  194. size_t newlen, len = sdslen(s);
  195. if (len == 0) return s;
  196. if (start < 0) {
  197. start = len+start;
  198. if (start < 0) start = 0;
  199. }
  200. if (end < 0) {
  201. end = len+end;
  202. if (end < 0) end = 0;
  203. }
  204. newlen = (start > end) ? 0 : (end-start)+1;
  205. if (newlen != 0) {
  206. if (start >= (signed)len) {
  207. newlen = 0;
  208. } else if (end >= (signed)len) {
  209. end = len-1;
  210. newlen = (start > end) ? 0 : (end-start)+1;
  211. }
  212. } else {
  213. start = 0;
  214. }
  215. if (start && newlen) memmove(sh->buf, sh->buf+start, newlen);
  216. sh->buf[newlen] = 0;
  217. sh->free = sh->free+(sh->len-newlen);
  218. sh->len = newlen;
  219. return s;
  220. }
  221. void sdstolower(sds s) {
  222. int len = sdslen(s), j;
  223. for (j = 0; j < len; j++) s[j] = tolower(s[j]);
  224. }
  225. void sdstoupper(sds s) {
  226. int len = sdslen(s), j;
  227. for (j = 0; j < len; j++) s[j] = toupper(s[j]);
  228. }
  229. int sdscmp(sds s1, sds s2) {
  230. size_t l1, l2, minlen;
  231. int cmp;
  232. l1 = sdslen(s1);
  233. l2 = sdslen(s2);
  234. minlen = (l1 < l2) ? l1 : l2;
  235. cmp = memcmp(s1,s2,minlen);
  236. if (cmp == 0) return l1-l2;
  237. return cmp;
  238. }
  239. /* Split 's' with separator in 'sep'. An array
  240. * of sds strings is returned. *count will be set
  241. * by reference to the number of tokens returned.
  242. *
  243. * On out of memory, zero length string, zero length
  244. * separator, NULL is returned.
  245. *
  246. * Note that 'sep' is able to split a string using
  247. * a multi-character separator. For example
  248. * sdssplit("foo_-_bar","_-_"); will return two
  249. * elements "foo" and "bar".
  250. *
  251. * This version of the function is binary-safe but
  252. * requires length arguments. sdssplit() is just the
  253. * same function but for zero-terminated strings.
  254. */
  255. sds *sdssplitlen(char *s, int len, char *sep, int seplen, int *count) {
  256. int elements = 0, slots = 5, start = 0, j;
  257. sds *tokens = malloc(sizeof(sds)*slots);
  258. #ifdef SDS_ABORT_ON_OOM
  259. if (tokens == NULL) sdsOomAbort();
  260. #endif
  261. if (seplen < 1 || len < 0 || tokens == NULL) return NULL;
  262. if (len == 0) {
  263. *count = 0;
  264. return tokens;
  265. }
  266. for (j = 0; j < (len-(seplen-1)); j++) {
  267. /* make sure there is room for the next element and the final one */
  268. if (slots < elements+2) {
  269. sds *newtokens;
  270. slots *= 2;
  271. newtokens = realloc(tokens,sizeof(sds)*slots);
  272. if (newtokens == NULL) {
  273. #ifdef SDS_ABORT_ON_OOM
  274. sdsOomAbort();
  275. #else
  276. goto cleanup;
  277. #endif
  278. }
  279. tokens = newtokens;
  280. }
  281. /* search the separator */
  282. if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) {
  283. tokens[elements] = sdsnewlen(s+start,j-start);
  284. if (tokens[elements] == NULL) {
  285. #ifdef SDS_ABORT_ON_OOM
  286. sdsOomAbort();
  287. #else
  288. goto cleanup;
  289. #endif
  290. }
  291. elements++;
  292. start = j+seplen;
  293. j = j+seplen-1; /* skip the separator */
  294. }
  295. }
  296. /* Add the final element. We are sure there is room in the tokens array. */
  297. tokens[elements] = sdsnewlen(s+start,len-start);
  298. if (tokens[elements] == NULL) {
  299. #ifdef SDS_ABORT_ON_OOM
  300. sdsOomAbort();
  301. #else
  302. goto cleanup;
  303. #endif
  304. }
  305. elements++;
  306. *count = elements;
  307. return tokens;
  308. #ifndef SDS_ABORT_ON_OOM
  309. cleanup:
  310. {
  311. int i;
  312. for (i = 0; i < elements; i++) sdsfree(tokens[i]);
  313. free(tokens);
  314. return NULL;
  315. }
  316. #endif
  317. }
  318. void sdsfreesplitres(sds *tokens, int count) {
  319. if (!tokens) return;
  320. while(count--)
  321. sdsfree(tokens[count]);
  322. free(tokens);
  323. }
  324. sds sdsfromlonglong(long long value) {
  325. char buf[32], *p;
  326. unsigned long long v;
  327. v = (value < 0) ? -value : value;
  328. p = buf+31; /* point to the last character */
  329. do {
  330. *p-- = '0'+(v%10);
  331. v /= 10;
  332. } while(v);
  333. if (value < 0) *p-- = '-';
  334. p++;
  335. return sdsnewlen(p,32-(p-buf));
  336. }
  337. sds sdscatrepr(sds s, char *p, size_t len) {
  338. s = sdscatlen(s,"\"",1);
  339. if (s == NULL) return NULL;
  340. while(len--) {
  341. switch(*p) {
  342. case '\\':
  343. case '"':
  344. s = sdscatprintf(s,"\\%c",*p);
  345. break;
  346. case '\n': s = sdscatlen(s,"\\n",2); break;
  347. case '\r': s = sdscatlen(s,"\\r",2); break;
  348. case '\t': s = sdscatlen(s,"\\t",2); break;
  349. case '\a': s = sdscatlen(s,"\\a",2); break;
  350. case '\b': s = sdscatlen(s,"\\b",2); break;
  351. default:
  352. if (isprint(*p))
  353. s = sdscatprintf(s,"%c",*p);
  354. else
  355. s = sdscatprintf(s,"\\x%02x",(unsigned char)*p);
  356. break;
  357. }
  358. p++;
  359. if (s == NULL) return NULL;
  360. }
  361. return sdscatlen(s,"\"",1);
  362. }
  363. /* Split a line into arguments, where every argument can be in the
  364. * following programming-language REPL-alike form:
  365. *
  366. * foo bar "newline are supported\n" and "\xff\x00otherstuff"
  367. *
  368. * The number of arguments is stored into *argc, and an array
  369. * of sds is returned. The caller should sdsfree() all the returned
  370. * strings and finally free() the array itself.
  371. *
  372. * Note that sdscatrepr() is able to convert back a string into
  373. * a quoted string in the same format sdssplitargs() is able to parse.
  374. */
  375. sds *sdssplitargs(char *line, int *argc) {
  376. char *p = line;
  377. char *current = NULL;
  378. char **vector = NULL, **_vector = NULL;
  379. *argc = 0;
  380. while(1) {
  381. /* skip blanks */
  382. while(*p && isspace(*p)) p++;
  383. if (*p) {
  384. /* get a token */
  385. int inq=0; /* set to 1 if we are in "quotes" */
  386. int done=0;
  387. if (current == NULL) {
  388. current = sdsempty();
  389. if (current == NULL) goto err;
  390. }
  391. while(!done) {
  392. if (inq) {
  393. if (*p == '\\' && *(p+1)) {
  394. char c;
  395. p++;
  396. switch(*p) {
  397. case 'n': c = '\n'; break;
  398. case 'r': c = '\r'; break;
  399. case 't': c = '\t'; break;
  400. case 'b': c = '\b'; break;
  401. case 'a': c = '\a'; break;
  402. default: c = *p; break;
  403. }
  404. current = sdscatlen(current,&c,1);
  405. } else if (*p == '"') {
  406. /* closing quote must be followed by a space */
  407. if (*(p+1) && !isspace(*(p+1))) goto err;
  408. done=1;
  409. } else if (!*p) {
  410. /* unterminated quotes */
  411. goto err;
  412. } else {
  413. current = sdscatlen(current,p,1);
  414. }
  415. } else {
  416. switch(*p) {
  417. case ' ':
  418. case '\n':
  419. case '\r':
  420. case '\t':
  421. case '\0':
  422. done=1;
  423. break;
  424. case '"':
  425. inq=1;
  426. break;
  427. default:
  428. current = sdscatlen(current,p,1);
  429. break;
  430. }
  431. }
  432. if (*p) p++;
  433. if (current == NULL) goto err;
  434. }
  435. /* add the token to the vector */
  436. _vector = realloc(vector,((*argc)+1)*sizeof(char*));
  437. if (_vector == NULL) goto err;
  438. vector = _vector;
  439. vector[*argc] = current;
  440. (*argc)++;
  441. current = NULL;
  442. } else {
  443. return vector;
  444. }
  445. }
  446. err:
  447. while((*argc)--)
  448. sdsfree(vector[*argc]);
  449. if (vector != NULL) free(vector);
  450. if (current != NULL) sdsfree(current);
  451. return NULL;
  452. }
  453. #ifdef SDS_TEST_MAIN
  454. #include <stdio.h>
  455. int __failed_tests = 0;
  456. int __test_num = 0;
  457. #define test_cond(descr,_c) do { \
  458. __test_num++; printf("%d - %s: ", __test_num, descr); \
  459. if(_c) printf("PASSED\n"); else {printf("FAILED\n"); __failed_tests++;} \
  460. } while(0);
  461. #define test_report() do { \
  462. printf("%d tests, %d passed, %d failed\n", __test_num, \
  463. __test_num-__failed_tests, __failed_tests); \
  464. if (__failed_tests) { \
  465. printf("=== WARNING === We have failed tests here...\n"); \
  466. } \
  467. } while(0);
  468. int main(void) {
  469. {
  470. sds x = sdsnew("foo"), y;
  471. test_cond("Create a string and obtain the length",
  472. sdslen(x) == 3 && memcmp(x,"foo\0",4) == 0)
  473. sdsfree(x);
  474. x = sdsnewlen("foo",2);
  475. test_cond("Create a string with specified length",
  476. sdslen(x) == 2 && memcmp(x,"fo\0",3) == 0)
  477. x = sdscat(x,"bar");
  478. test_cond("Strings concatenation",
  479. sdslen(x) == 5 && memcmp(x,"fobar\0",6) == 0);
  480. x = sdscpy(x,"a");
  481. test_cond("sdscpy() against an originally longer string",
  482. sdslen(x) == 1 && memcmp(x,"a\0",2) == 0)
  483. x = sdscpy(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk");
  484. test_cond("sdscpy() against an originally shorter string",
  485. sdslen(x) == 33 &&
  486. memcmp(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0",33) == 0)
  487. sdsfree(x);
  488. x = sdscatprintf(sdsempty(),"%d",123);
  489. test_cond("sdscatprintf() seems working in the base case",
  490. sdslen(x) == 3 && memcmp(x,"123\0",4) ==0)
  491. sdsfree(x);
  492. x = sdstrim(sdsnew("xxciaoyyy"),"xy");
  493. test_cond("sdstrim() correctly trims characters",
  494. sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0)
  495. y = sdsrange(sdsdup(x),1,1);
  496. test_cond("sdsrange(...,1,1)",
  497. sdslen(y) == 1 && memcmp(y,"i\0",2) == 0)
  498. sdsfree(y);
  499. y = sdsrange(sdsdup(x),1,-1);
  500. test_cond("sdsrange(...,1,-1)",
  501. sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)
  502. sdsfree(y);
  503. y = sdsrange(sdsdup(x),-2,-1);
  504. test_cond("sdsrange(...,-2,-1)",
  505. sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0)
  506. sdsfree(y);
  507. y = sdsrange(sdsdup(x),2,1);
  508. test_cond("sdsrange(...,2,1)",
  509. sdslen(y) == 0 && memcmp(y,"\0",1) == 0)
  510. sdsfree(y);
  511. y = sdsrange(sdsdup(x),1,100);
  512. test_cond("sdsrange(...,1,100)",
  513. sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)
  514. sdsfree(y);
  515. y = sdsrange(sdsdup(x),100,100);
  516. test_cond("sdsrange(...,100,100)",
  517. sdslen(y) == 0 && memcmp(y,"\0",1) == 0)
  518. sdsfree(y);
  519. sdsfree(x);
  520. x = sdsnew("foo");
  521. y = sdsnew("foa");
  522. test_cond("sdscmp(foo,foa)", sdscmp(x,y) > 0)
  523. sdsfree(y);
  524. sdsfree(x);
  525. x = sdsnew("bar");
  526. y = sdsnew("bar");
  527. test_cond("sdscmp(bar,bar)", sdscmp(x,y) == 0)
  528. sdsfree(y);
  529. sdsfree(x);
  530. x = sdsnew("aar");
  531. y = sdsnew("bar");
  532. test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0)
  533. }
  534. test_report()
  535. }
  536. #endif