omnomnum 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/ext/omnomnum/extconf.rb +51 -0
  3. data/ext/omnomnum/omnomnum/branchlut/branchlut.c +276 -0
  4. data/ext/omnomnum/omnomnum/branchlut/branchlut.h +14 -0
  5. data/ext/omnomnum/omnomnum/dtoa.c +68 -0
  6. data/ext/omnomnum/omnomnum/dtoa.h +39 -0
  7. data/ext/omnomnum/omnomnum/grisu2/diy_fp.h +61 -0
  8. data/ext/omnomnum/omnomnum/grisu2/double.h +121 -0
  9. data/ext/omnomnum/omnomnum/grisu2/fast_exponent.h +44 -0
  10. data/ext/omnomnum/omnomnum/grisu2/grisu2.c +120 -0
  11. data/ext/omnomnum/omnomnum/grisu2/grisu2.h +36 -0
  12. data/ext/omnomnum/omnomnum/grisu2/k_comp.h +32 -0
  13. data/ext/omnomnum/omnomnum/grisu2/powers.h +27 -0
  14. data/ext/omnomnum/omnomnum/grisu2/powers_ten_round64.h +36 -0
  15. data/ext/omnomnum/omnomnum/grisu2/prettify.h +76 -0
  16. data/ext/omnomnum/omnomnum/itoa.c +40 -0
  17. data/ext/omnomnum/omnomnum/itoa.h +40 -0
  18. data/ext/omnomnum/omnomnum/main.c +87 -0
  19. data/ext/omnomnum/omnomnum/omnomnum.c +208 -0
  20. data/ext/omnomnum/omnomnum/omnomnum.h +47 -0
  21. data/ext/omnomnum/omnomnum/parser.c +3445 -0
  22. data/ext/omnomnum/omnomnum/parser.h +130 -0
  23. data/ext/omnomnum/omnomnum/scan.c +55 -0
  24. data/ext/omnomnum/omnomnum/scan.h +68 -0
  25. data/ext/omnomnum/omnomnum/scanner.c +4332 -0
  26. data/ext/omnomnum/omnomnum/scanner.def.c +97 -0
  27. data/ext/omnomnum/omnomnum/scanner.def.h +105 -0
  28. data/ext/omnomnum/omnomnum/scanner.h +44 -0
  29. data/ext/omnomnum/omnomnum/sds.c +1278 -0
  30. data/ext/omnomnum/omnomnum/sds.h +280 -0
  31. data/ext/omnomnum/omnomnum/sdsalloc.h +43 -0
  32. data/ext/omnomnum/omnomnum/test/test_benchmark.c +107 -0
  33. data/ext/omnomnum/omnomnum/test/test_omnomnum.c +146 -0
  34. data/ext/omnomnum/omnomnum/test/test_omnomnum.h +6 -0
  35. data/ext/omnomnum/omnomnum/test/test_util.c +98 -0
  36. data/ext/omnomnum/omnomnum/util.c +84 -0
  37. data/ext/omnomnum/omnomnum/util.h +43 -0
  38. data/ext/omnomnum/ruby_omnomnum.c +96 -0
  39. data/ext/omnomnum/ruby_omnomnum.h +40 -0
  40. data/lib/omnomnum.rb +31 -0
  41. data/lib/omnomnum/omnomnum.so +0 -0
  42. data/lib/omnomnum/version.rb +32 -0
  43. metadata +114 -0
@@ -0,0 +1,97 @@
1
+ /* OmNomNum 0.0.2 -- Gobbles up numbers in strings.
2
+ *
3
+ * Copyright (c) 2017, Jesse Buesking <jessebuesking 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 OmNomNum 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
+
31
+ #include "scanner.def.h"
32
+ // pull in definitions for malloc and free
33
+ #include <stdlib.h>
34
+
35
+ size_t RESET_LIST_SIZE = 8;
36
+
37
+ void initYYSTYPEList(YYSTYPEList *l, size_t initialSize) {
38
+ l->values = (YYSTYPE *)malloc(initialSize * sizeof(YYSTYPE));
39
+ l->used = 0;
40
+ l->size = initialSize;
41
+ }
42
+
43
+ void insertYYSTYPE(YYSTYPEList *l, YYSTYPE element) {
44
+ if (l->used == l->size) {
45
+ if (l->size == 0) {
46
+ l->size = 1;
47
+ } else {
48
+ l->size *= 2;
49
+ }
50
+ l->values = (YYSTYPE *)realloc(l->values, l->size * sizeof(YYSTYPE));
51
+ }
52
+ l->values[l->used] = element;
53
+ l->used += 1;
54
+ }
55
+
56
+ void freeYYSTYPElist(YYSTYPEList *l) {
57
+ free(l->values);
58
+ }
59
+
60
+ void resetYYSTYPElist(YYSTYPEList *l) {
61
+ if (l->size > RESET_LIST_SIZE) {
62
+ l->size = RESET_LIST_SIZE;
63
+ l->values = (YYSTYPE *)realloc(l->values, l->size * sizeof(YYSTYPE));
64
+ }
65
+ l->used = 0;
66
+ }
67
+
68
+ int compare(const void* a, const void* b) {
69
+ YYSTYPE yya = * ( (YYSTYPE*) a );
70
+ YYSTYPE yyb = * ( (YYSTYPE*) b );
71
+
72
+ if ( yya.begin == yyb.begin ) return 0;
73
+ else if ( yya.begin < yyb.begin ) return -1;
74
+ else return 1;
75
+ }
76
+
77
+ void sortYYSTYPElist(YYSTYPEList *l) {
78
+ qsort(l->values, l->used, sizeof(YYSTYPE), compare);
79
+ }
80
+
81
+ void initParserState(ParserState *state) {
82
+ state->error = NO_ERROR;
83
+ state->parse_second = false;
84
+ initYYSTYPEList(&(state->yystypeList), 4);
85
+ }
86
+
87
+ void resetParserState(ParserState *state) {
88
+ state->precision = 6;
89
+ sdsfree(state->result);
90
+ state->error = NO_ERROR;
91
+ resetYYSTYPElist(&(state->yystypeList));
92
+ state->parse_second = false;
93
+ }
94
+
95
+ void freeParserState(ParserState *state) {
96
+ freeYYSTYPElist(&(state->yystypeList));
97
+ }
@@ -0,0 +1,105 @@
1
+ /* OmNomNum 0.0.2 -- Gobbles up numbers in strings.
2
+ *
3
+ * Copyright (c) 2017, Jesse Buesking <jessebuesking 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 OmNomNum 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
+
31
+ #ifndef SCANNER_DEF_H
32
+ #define SCANNER_DEF_H
33
+
34
+ #include "sds.h"
35
+ #include <stdbool.h>
36
+
37
+ #ifndef RESET_YYSTYPE
38
+ #define RESET_YYSTYPE(A) \
39
+ A.is_dbl = false; \
40
+ A.is_frac = false; \
41
+ A.suffix = NO_SUFFIX; \
42
+ A.leave_alone = false;
43
+ #endif
44
+
45
+ enum suffixValues
46
+ {
47
+ NO_SUFFIX,
48
+ ST,
49
+ STS,
50
+ ND,
51
+ NDS,
52
+ RD,
53
+ RDS,
54
+ TH,
55
+ THS
56
+ };
57
+
58
+ enum errors {
59
+ NO_ERROR,
60
+ SYNTAX_ERROR,
61
+ PARSE_FAILURE
62
+ };
63
+
64
+ typedef struct {
65
+ double dbl;
66
+ bool is_dbl;
67
+
68
+ double frac_num;
69
+ double frac_denom;
70
+ bool is_frac;
71
+
72
+ bool leave_alone;
73
+
74
+ unsigned int begin;
75
+ unsigned int end;
76
+ enum suffixValues suffix;
77
+ } YYSTYPE;
78
+
79
+ typedef struct {
80
+ YYSTYPE *values;
81
+ size_t used;
82
+ size_t size;
83
+ } YYSTYPEList;
84
+
85
+ typedef struct {
86
+ int precision;
87
+ sds result;
88
+ enum errors error;
89
+ YYSTYPEList yystypeList;
90
+ bool parse_second;
91
+ bool is_parsing;
92
+ int last_token;
93
+ } ParserState;
94
+
95
+ void initYYSTYPEList(YYSTYPEList *l, size_t initialSize);
96
+ void insertYYSTYPE(YYSTYPEList *l, YYSTYPE element);
97
+ void resetYYSTYPElist(YYSTYPEList *l);
98
+ void freeYYSTYPElist(YYSTYPEList *l);
99
+ void sortYYSTYPElist(YYSTYPEList *l);
100
+
101
+ void initParserState(ParserState *state);
102
+ void resetParserState(ParserState *state);
103
+ void freeParserState(ParserState *state);
104
+
105
+ #endif // SCANNER_DEF_H
@@ -0,0 +1,44 @@
1
+ /* OmNomNum 0.0.2 -- Gobbles up numbers in strings.
2
+ *
3
+ * Copyright (c) 2017, Jesse Buesking <jessebuesking 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 OmNomNum 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
+
31
+ #ifndef OMNOMNUM_SCANNER_H_
32
+ #define OMNOMNUM_SCANNER_H_
33
+
34
+ // pull in definitions for malloc and free
35
+ #include <stdlib.h>
36
+
37
+ #include "scanner.def.h"
38
+ #include "parser.h"
39
+ #include "scan.h"
40
+ #include "util.h"
41
+
42
+ int omnomnum_scanner_start(ParserState *state, void *pParser, YYSTYPE *yylval, scanstate *ss);
43
+
44
+ #endif // OMNOMNUM_SCANNER_H_
@@ -0,0 +1,1278 @@
1
+ /* See https://github.com/antirez/sds/blob/master/LICENSE
2
+ * SDSLib 2.0 -- A C dynamic strings library
3
+ *
4
+ * Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>
5
+ * Copyright (c) 2015, Oran Agra
6
+ * Copyright (c) 2015, Redis Labs, Inc
7
+ * All rights reserved.
8
+ *
9
+ * Redistribution and use in source and binary forms, with or without
10
+ * modification, are permitted provided that the following conditions are met:
11
+ *
12
+ * * Redistributions of source code must retain the above copyright notice,
13
+ * this list of conditions and the following disclaimer.
14
+ * * Redistributions in binary form must reproduce the above copyright
15
+ * notice, this list of conditions and the following disclaimer in the
16
+ * documentation and/or other materials provided with the distribution.
17
+ * * Neither the name of Redis nor the names of its contributors may be used
18
+ * to endorse or promote products derived from this software without
19
+ * specific prior written permission.
20
+ *
21
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31
+ * POSSIBILITY OF SUCH DAMAGE.
32
+ */
33
+
34
+ #include <stdio.h>
35
+ #include <stdlib.h>
36
+ #include <string.h>
37
+ #include <ctype.h>
38
+ #include <assert.h>
39
+ #include <limits.h>
40
+ #include "sds.h"
41
+ #include "sdsalloc.h"
42
+
43
+ static inline int sdsHdrSize(char type) {
44
+ switch(type&SDS_TYPE_MASK) {
45
+ case SDS_TYPE_5:
46
+ return sizeof(struct sdshdr5);
47
+ case SDS_TYPE_8:
48
+ return sizeof(struct sdshdr8);
49
+ case SDS_TYPE_16:
50
+ return sizeof(struct sdshdr16);
51
+ case SDS_TYPE_32:
52
+ return sizeof(struct sdshdr32);
53
+ case SDS_TYPE_64:
54
+ return sizeof(struct sdshdr64);
55
+ }
56
+ return 0;
57
+ }
58
+
59
+ static inline char sdsReqType(size_t string_size) {
60
+ if (string_size < 1<<5)
61
+ return SDS_TYPE_5;
62
+ if (string_size < 1<<8)
63
+ return SDS_TYPE_8;
64
+ if (string_size < 1<<16)
65
+ return SDS_TYPE_16;
66
+ #if (LONG_MAX == LLONG_MAX)
67
+ if (string_size < 1ll<<32)
68
+ return SDS_TYPE_32;
69
+ #endif
70
+ return SDS_TYPE_64;
71
+ }
72
+
73
+ /* Create a new sds string with the content specified by the 'init' pointer
74
+ * and 'initlen'.
75
+ * If NULL is used for 'init' the string is initialized with zero bytes.
76
+ *
77
+ * The string is always null-termined (all the sds strings are, always) so
78
+ * even if you create an sds string with:
79
+ *
80
+ * mystring = sdsnewlen("abc",3);
81
+ *
82
+ * You can print the string with printf() as there is an implicit \0 at the
83
+ * end of the string. However the string is binary safe and can contain
84
+ * \0 characters in the middle, as the length is stored in the sds header. */
85
+ sds sdsnewlen(const void *init, size_t initlen) {
86
+ void *sh;
87
+ sds s;
88
+ char type = sdsReqType(initlen);
89
+ /* Empty strings are usually created in order to append. Use type 8
90
+ * since type 5 is not good at this. */
91
+ if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8;
92
+ int hdrlen = sdsHdrSize(type);
93
+ unsigned char *fp; /* flags pointer. */
94
+
95
+ sh = s_malloc(hdrlen+initlen+1);
96
+ if (!init)
97
+ memset(sh, 0, hdrlen+initlen+1);
98
+ if (sh == NULL) return NULL;
99
+ s = (char*)sh+hdrlen;
100
+ fp = ((unsigned char*)s)-1;
101
+ switch(type) {
102
+ case SDS_TYPE_5: {
103
+ *fp = type | (initlen << SDS_TYPE_BITS);
104
+ break;
105
+ }
106
+ case SDS_TYPE_8: {
107
+ SDS_HDR_VAR(8,s);
108
+ sh->len = initlen;
109
+ sh->alloc = initlen;
110
+ *fp = type;
111
+ break;
112
+ }
113
+ case SDS_TYPE_16: {
114
+ SDS_HDR_VAR(16,s);
115
+ sh->len = initlen;
116
+ sh->alloc = initlen;
117
+ *fp = type;
118
+ break;
119
+ }
120
+ case SDS_TYPE_32: {
121
+ SDS_HDR_VAR(32,s);
122
+ sh->len = initlen;
123
+ sh->alloc = initlen;
124
+ *fp = type;
125
+ break;
126
+ }
127
+ case SDS_TYPE_64: {
128
+ SDS_HDR_VAR(64,s);
129
+ sh->len = initlen;
130
+ sh->alloc = initlen;
131
+ *fp = type;
132
+ break;
133
+ }
134
+ }
135
+ if (initlen && init)
136
+ memcpy(s, init, initlen);
137
+ s[initlen] = '\0';
138
+ return s;
139
+ }
140
+
141
+ /* Create an empty (zero length) sds string. Even in this case the string
142
+ * always has an implicit null term. */
143
+ sds sdsempty(void) {
144
+ return sdsnewlen("",0);
145
+ }
146
+
147
+ /* Create a new sds string starting from a null terminated C string. */
148
+ sds sdsnew(const char *init) {
149
+ size_t initlen = (init == NULL) ? 0 : strlen(init);
150
+ return sdsnewlen(init, initlen);
151
+ }
152
+
153
+ /* Duplicate an sds string. */
154
+ sds sdsdup(const sds s) {
155
+ return sdsnewlen(s, sdslen(s));
156
+ }
157
+
158
+ /* Free an sds string. No operation is performed if 's' is NULL. */
159
+ void sdsfree(sds s) {
160
+ if (s == NULL) return;
161
+ s_free((char*)s-sdsHdrSize(s[-1]));
162
+ }
163
+
164
+ /* Set the sds string length to the length as obtained with strlen(), so
165
+ * considering as content only up to the first null term character.
166
+ *
167
+ * This function is useful when the sds string is hacked manually in some
168
+ * way, like in the following example:
169
+ *
170
+ * s = sdsnew("foobar");
171
+ * s[2] = '\0';
172
+ * sdsupdatelen(s);
173
+ * printf("%d\n", sdslen(s));
174
+ *
175
+ * The output will be "2", but if we comment out the call to sdsupdatelen()
176
+ * the output will be "6" as the string was modified but the logical length
177
+ * remains 6 bytes. */
178
+ void sdsupdatelen(sds s) {
179
+ int reallen = strlen(s);
180
+ sdssetlen(s, reallen);
181
+ }
182
+
183
+ /* Modify an sds string in-place to make it empty (zero length).
184
+ * However all the existing buffer is not discarded but set as free space
185
+ * so that next append operations will not require allocations up to the
186
+ * number of bytes previously available. */
187
+ void sdsclear(sds s) {
188
+ sdssetlen(s, 0);
189
+ s[0] = '\0';
190
+ }
191
+
192
+ /* Enlarge the free space at the end of the sds string so that the caller
193
+ * is sure that after calling this function can overwrite up to addlen
194
+ * bytes after the end of the string, plus one more byte for nul term.
195
+ *
196
+ * Note: this does not change the *length* of the sds string as returned
197
+ * by sdslen(), but only the free buffer space we have. */
198
+ sds sdsMakeRoomFor(sds s, size_t addlen) {
199
+ void *sh, *newsh;
200
+ size_t avail = sdsavail(s);
201
+ size_t len, newlen;
202
+ char type, oldtype = s[-1] & SDS_TYPE_MASK;
203
+ int hdrlen;
204
+
205
+ /* Return ASAP if there is enough space left. */
206
+ if (avail >= addlen) return s;
207
+
208
+ len = sdslen(s);
209
+ sh = (char*)s-sdsHdrSize(oldtype);
210
+ newlen = (len+addlen);
211
+ if (newlen < SDS_MAX_PREALLOC)
212
+ newlen *= 2;
213
+ else
214
+ newlen += SDS_MAX_PREALLOC;
215
+
216
+ type = sdsReqType(newlen);
217
+
218
+ /* Don't use type 5: the user is appending to the string and type 5 is
219
+ * not able to remember empty space, so sdsMakeRoomFor() must be called
220
+ * at every appending operation. */
221
+ if (type == SDS_TYPE_5) type = SDS_TYPE_8;
222
+
223
+ hdrlen = sdsHdrSize(type);
224
+ if (oldtype==type) {
225
+ newsh = s_realloc(sh, hdrlen+newlen+1);
226
+ if (newsh == NULL) return NULL;
227
+ s = (char*)newsh+hdrlen;
228
+ } else {
229
+ /* Since the header size changes, need to move the string forward,
230
+ * and can't use realloc */
231
+ newsh = s_malloc(hdrlen+newlen+1);
232
+ if (newsh == NULL) return NULL;
233
+ memcpy((char*)newsh+hdrlen, s, len+1);
234
+ s_free(sh);
235
+ s = (char*)newsh+hdrlen;
236
+ s[-1] = type;
237
+ sdssetlen(s, len);
238
+ }
239
+ sdssetalloc(s, newlen);
240
+ return s;
241
+ }
242
+
243
+ /* Reallocate the sds string so that it has no free space at the end. The
244
+ * contained string remains not altered, but next concatenation operations
245
+ * will require a reallocation.
246
+ *
247
+ * After the call, the passed sds string is no longer valid and all the
248
+ * references must be substituted with the new pointer returned by the call. */
249
+ sds sdsRemoveFreeSpace(sds s) {
250
+ void *sh, *newsh;
251
+ char type, oldtype = s[-1] & SDS_TYPE_MASK;
252
+ int hdrlen;
253
+ size_t len = sdslen(s);
254
+ sh = (char*)s-sdsHdrSize(oldtype);
255
+
256
+ type = sdsReqType(len);
257
+ hdrlen = sdsHdrSize(type);
258
+ if (oldtype==type) {
259
+ newsh = s_realloc(sh, hdrlen+len+1);
260
+ if (newsh == NULL) return NULL;
261
+ s = (char*)newsh+hdrlen;
262
+ } else {
263
+ newsh = s_malloc(hdrlen+len+1);
264
+ if (newsh == NULL) return NULL;
265
+ memcpy((char*)newsh+hdrlen, s, len+1);
266
+ s_free(sh);
267
+ s = (char*)newsh+hdrlen;
268
+ s[-1] = type;
269
+ sdssetlen(s, len);
270
+ }
271
+ sdssetalloc(s, len);
272
+ return s;
273
+ }
274
+
275
+ /* Return the total size of the allocation of the specifed sds string,
276
+ * including:
277
+ * 1) The sds header before the pointer.
278
+ * 2) The string.
279
+ * 3) The free buffer at the end if any.
280
+ * 4) The implicit null term.
281
+ */
282
+ size_t sdsAllocSize(sds s) {
283
+ size_t alloc = sdsalloc(s);
284
+ return sdsHdrSize(s[-1])+alloc+1;
285
+ }
286
+
287
+ /* Return the pointer of the actual SDS allocation (normally SDS strings
288
+ * are referenced by the start of the string buffer). */
289
+ void *sdsAllocPtr(sds s) {
290
+ return (void*) (s-sdsHdrSize(s[-1]));
291
+ }
292
+
293
+ /* Increment the sds length and decrements the left free space at the
294
+ * end of the string according to 'incr'. Also set the null term
295
+ * in the new end of the string.
296
+ *
297
+ * This function is used in order to fix the string length after the
298
+ * user calls sdsMakeRoomFor(), writes something after the end of
299
+ * the current string, and finally needs to set the new length.
300
+ *
301
+ * Note: it is possible to use a negative increment in order to
302
+ * right-trim the string.
303
+ *
304
+ * Usage example:
305
+ *
306
+ * Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the
307
+ * following schema, to cat bytes coming from the kernel to the end of an
308
+ * sds string without copying into an intermediate buffer:
309
+ *
310
+ * oldlen = sdslen(s);
311
+ * s = sdsMakeRoomFor(s, BUFFER_SIZE);
312
+ * nread = read(fd, s+oldlen, BUFFER_SIZE);
313
+ * ... check for nread <= 0 and handle it ...
314
+ * sdsIncrLen(s, nread);
315
+ */
316
+ void sdsIncrLen(sds s, int incr) {
317
+ unsigned char flags = s[-1];
318
+ size_t len;
319
+ switch(flags&SDS_TYPE_MASK) {
320
+ case SDS_TYPE_5: {
321
+ unsigned char *fp = ((unsigned char*)s)-1;
322
+ unsigned char oldlen = SDS_TYPE_5_LEN(flags);
323
+ assert((incr > 0 && oldlen+incr < 32) || (incr < 0 && oldlen >= (unsigned int)(-incr)));
324
+ *fp = SDS_TYPE_5 | ((oldlen+incr) << SDS_TYPE_BITS);
325
+ len = oldlen+incr;
326
+ break;
327
+ }
328
+ case SDS_TYPE_8: {
329
+ SDS_HDR_VAR(8,s);
330
+ assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));
331
+ len = (sh->len += incr);
332
+ break;
333
+ }
334
+ case SDS_TYPE_16: {
335
+ SDS_HDR_VAR(16,s);
336
+ assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));
337
+ len = (sh->len += incr);
338
+ break;
339
+ }
340
+ case SDS_TYPE_32: {
341
+ SDS_HDR_VAR(32,s);
342
+ assert((incr >= 0 && sh->alloc-sh->len >= (unsigned int)incr) || (incr < 0 && sh->len >= (unsigned int)(-incr)));
343
+ len = (sh->len += incr);
344
+ break;
345
+ }
346
+ case SDS_TYPE_64: {
347
+ SDS_HDR_VAR(64,s);
348
+ assert((incr >= 0 && sh->alloc-sh->len >= (uint64_t)incr) || (incr < 0 && sh->len >= (uint64_t)(-incr)));
349
+ len = (sh->len += incr);
350
+ break;
351
+ }
352
+ default: len = 0; /* Just to avoid compilation warnings. */
353
+ }
354
+ s[len] = '\0';
355
+ }
356
+
357
+ /* Grow the sds to have the specified length. Bytes that were not part of
358
+ * the original length of the sds will be set to zero.
359
+ *
360
+ * if the specified length is smaller than the current length, no operation
361
+ * is performed. */
362
+ sds sdsgrowzero(sds s, size_t len) {
363
+ size_t curlen = sdslen(s);
364
+
365
+ if (len <= curlen) return s;
366
+ s = sdsMakeRoomFor(s,len-curlen);
367
+ if (s == NULL) return NULL;
368
+
369
+ /* Make sure added region doesn't contain garbage */
370
+ memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */
371
+ sdssetlen(s, len);
372
+ return s;
373
+ }
374
+
375
+ /* Append the specified binary-safe string pointed by 't' of 'len' bytes to the
376
+ * end of the specified sds string 's'.
377
+ *
378
+ * After the call, the passed sds string is no longer valid and all the
379
+ * references must be substituted with the new pointer returned by the call. */
380
+ sds sdscatlen(sds s, const void *t, size_t len) {
381
+ size_t curlen = sdslen(s);
382
+
383
+ s = sdsMakeRoomFor(s,len);
384
+ if (s == NULL) return NULL;
385
+ memcpy(s+curlen, t, len);
386
+ sdssetlen(s, curlen+len);
387
+ s[curlen+len] = '\0';
388
+ return s;
389
+ }
390
+
391
+ /* Append the specified null termianted C string to the sds string 's'.
392
+ *
393
+ * After the call, the passed sds string is no longer valid and all the
394
+ * references must be substituted with the new pointer returned by the call. */
395
+ sds sdscat(sds s, const char *t) {
396
+ return sdscatlen(s, t, strlen(t));
397
+ }
398
+
399
+ /* Append the specified sds 't' to the existing sds 's'.
400
+ *
401
+ * After the call, the modified sds string is no longer valid and all the
402
+ * references must be substituted with the new pointer returned by the call. */
403
+ sds sdscatsds(sds s, const sds t) {
404
+ return sdscatlen(s, t, sdslen(t));
405
+ }
406
+
407
+ /* Destructively modify the sds string 's' to hold the specified binary
408
+ * safe string pointed by 't' of length 'len' bytes. */
409
+ sds sdscpylen(sds s, const char *t, size_t len) {
410
+ if (sdsalloc(s) < len) {
411
+ s = sdsMakeRoomFor(s,len-sdslen(s));
412
+ if (s == NULL) return NULL;
413
+ }
414
+ memcpy(s, t, len);
415
+ s[len] = '\0';
416
+ sdssetlen(s, len);
417
+ return s;
418
+ }
419
+
420
+ /* Like sdscpylen() but 't' must be a null-termined string so that the length
421
+ * of the string is obtained with strlen(). */
422
+ sds sdscpy(sds s, const char *t) {
423
+ return sdscpylen(s, t, strlen(t));
424
+ }
425
+
426
+ /* Helper for sdscatlonglong() doing the actual number -> string
427
+ * conversion. 's' must point to a string with room for at least
428
+ * SDS_LLSTR_SIZE bytes.
429
+ *
430
+ * The function returns the length of the null-terminated string
431
+ * representation stored at 's'. */
432
+ #define SDS_LLSTR_SIZE 21
433
+ int sdsll2str(char *s, long long value) {
434
+ char *p, aux;
435
+ unsigned long long v;
436
+ size_t l;
437
+
438
+ /* Generate the string representation, this method produces
439
+ * an reversed string. */
440
+ v = (value < 0) ? -value : value;
441
+ p = s;
442
+ do {
443
+ *p++ = '0'+(v%10);
444
+ v /= 10;
445
+ } while(v);
446
+ if (value < 0) *p++ = '-';
447
+
448
+ /* Compute length and add null term. */
449
+ l = p-s;
450
+ *p = '\0';
451
+
452
+ /* Reverse the string. */
453
+ p--;
454
+ while(s < p) {
455
+ aux = *s;
456
+ *s = *p;
457
+ *p = aux;
458
+ s++;
459
+ p--;
460
+ }
461
+ return l;
462
+ }
463
+
464
+ /* Identical sdsll2str(), but for unsigned long long type. */
465
+ int sdsull2str(char *s, unsigned long long v) {
466
+ char *p, aux;
467
+ size_t l;
468
+
469
+ /* Generate the string representation, this method produces
470
+ * an reversed string. */
471
+ p = s;
472
+ do {
473
+ *p++ = '0'+(v%10);
474
+ v /= 10;
475
+ } while(v);
476
+
477
+ /* Compute length and add null term. */
478
+ l = p-s;
479
+ *p = '\0';
480
+
481
+ /* Reverse the string. */
482
+ p--;
483
+ while(s < p) {
484
+ aux = *s;
485
+ *s = *p;
486
+ *p = aux;
487
+ s++;
488
+ p--;
489
+ }
490
+ return l;
491
+ }
492
+
493
+ /* Create an sds string from a long long value. It is much faster than:
494
+ *
495
+ * sdscatprintf(sdsempty(),"%lld\n", value);
496
+ */
497
+ sds sdsfromlonglong(long long value) {
498
+ char buf[SDS_LLSTR_SIZE];
499
+ int len = sdsll2str(buf,value);
500
+
501
+ return sdsnewlen(buf,len);
502
+ }
503
+
504
+ /* Like sdscatprintf() but gets va_list instead of being variadic. */
505
+ sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
506
+ va_list cpy;
507
+ char staticbuf[1024], *buf = staticbuf, *t;
508
+ size_t buflen = strlen(fmt)*2;
509
+
510
+ /* We try to start using a static buffer for speed.
511
+ * If not possible we revert to heap allocation. */
512
+ if (buflen > sizeof(staticbuf)) {
513
+ buf = s_malloc(buflen);
514
+ if (buf == NULL) return NULL;
515
+ } else {
516
+ buflen = sizeof(staticbuf);
517
+ }
518
+
519
+ /* Try with buffers two times bigger every time we fail to
520
+ * fit the string in the current buffer size. */
521
+ while(1) {
522
+ buf[buflen-2] = '\0';
523
+ va_copy(cpy,ap);
524
+ vsnprintf(buf, buflen, fmt, cpy);
525
+ va_end(cpy);
526
+ if (buf[buflen-2] != '\0') {
527
+ if (buf != staticbuf) s_free(buf);
528
+ buflen *= 2;
529
+ buf = s_malloc(buflen);
530
+ if (buf == NULL) return NULL;
531
+ continue;
532
+ }
533
+ break;
534
+ }
535
+
536
+ /* Finally concat the obtained string to the SDS string and return it. */
537
+ t = sdscat(s, buf);
538
+ if (buf != staticbuf) s_free(buf);
539
+ return t;
540
+ }
541
+
542
+ /* Append to the sds string 's' a string obtained using printf-alike format
543
+ * specifier.
544
+ *
545
+ * After the call, the modified sds string is no longer valid and all the
546
+ * references must be substituted with the new pointer returned by the call.
547
+ *
548
+ * Example:
549
+ *
550
+ * s = sdsnew("Sum is: ");
551
+ * s = sdscatprintf(s,"%d+%d = %d",a,b,a+b).
552
+ *
553
+ * Often you need to create a string from scratch with the printf-alike
554
+ * format. When this is the need, just use sdsempty() as the target string:
555
+ *
556
+ * s = sdscatprintf(sdsempty(), "... your format ...", args);
557
+ */
558
+ sds sdscatprintf(sds s, const char *fmt, ...) {
559
+ va_list ap;
560
+ char *t;
561
+ va_start(ap, fmt);
562
+ t = sdscatvprintf(s,fmt,ap);
563
+ va_end(ap);
564
+ return t;
565
+ }
566
+
567
+ /* This function is similar to sdscatprintf, but much faster as it does
568
+ * not rely on sprintf() family functions implemented by the libc that
569
+ * are often very slow. Moreover directly handling the sds string as
570
+ * new data is concatenated provides a performance improvement.
571
+ *
572
+ * However this function only handles an incompatible subset of printf-alike
573
+ * format specifiers:
574
+ *
575
+ * %s - C String
576
+ * %S - SDS string
577
+ * %i - signed int
578
+ * %I - 64 bit signed integer (long long, int64_t)
579
+ * %u - unsigned int
580
+ * %U - 64 bit unsigned integer (unsigned long long, uint64_t)
581
+ * %% - Verbatim "%" character.
582
+ */
583
+ sds sdscatfmt(sds s, char const *fmt, ...) {
584
+ size_t initlen = sdslen(s);
585
+ const char *f = fmt;
586
+ int i;
587
+ va_list ap;
588
+
589
+ va_start(ap,fmt);
590
+ f = fmt; /* Next format specifier byte to process. */
591
+ i = initlen; /* Position of the next byte to write to dest str. */
592
+ while(*f) {
593
+ char next, *str;
594
+ size_t l;
595
+ long long num;
596
+ unsigned long long unum;
597
+
598
+ /* Make sure there is always space for at least 1 char. */
599
+ if (sdsavail(s)==0) {
600
+ s = sdsMakeRoomFor(s,1);
601
+ }
602
+
603
+ switch(*f) {
604
+ case '%':
605
+ next = *(f+1);
606
+ f++;
607
+ switch(next) {
608
+ case 's':
609
+ case 'S':
610
+ str = va_arg(ap,char*);
611
+ l = (next == 's') ? strlen(str) : sdslen(str);
612
+ if (sdsavail(s) < l) {
613
+ s = sdsMakeRoomFor(s,l);
614
+ }
615
+ memcpy(s+i,str,l);
616
+ sdsinclen(s,l);
617
+ i += l;
618
+ break;
619
+ case 'i':
620
+ case 'I':
621
+ if (next == 'i')
622
+ num = va_arg(ap,int);
623
+ else
624
+ num = va_arg(ap,long long);
625
+ {
626
+ char buf[SDS_LLSTR_SIZE];
627
+ l = sdsll2str(buf,num);
628
+ if (sdsavail(s) < l) {
629
+ s = sdsMakeRoomFor(s,l);
630
+ }
631
+ memcpy(s+i,buf,l);
632
+ sdsinclen(s,l);
633
+ i += l;
634
+ }
635
+ break;
636
+ case 'u':
637
+ case 'U':
638
+ if (next == 'u')
639
+ unum = va_arg(ap,unsigned int);
640
+ else
641
+ unum = va_arg(ap,unsigned long long);
642
+ {
643
+ char buf[SDS_LLSTR_SIZE];
644
+ l = sdsull2str(buf,unum);
645
+ if (sdsavail(s) < l) {
646
+ s = sdsMakeRoomFor(s,l);
647
+ }
648
+ memcpy(s+i,buf,l);
649
+ sdsinclen(s,l);
650
+ i += l;
651
+ }
652
+ break;
653
+ default: /* Handle %% and generally %<unknown>. */
654
+ s[i++] = next;
655
+ sdsinclen(s,1);
656
+ break;
657
+ }
658
+ break;
659
+ default:
660
+ s[i++] = *f;
661
+ sdsinclen(s,1);
662
+ break;
663
+ }
664
+ f++;
665
+ }
666
+ va_end(ap);
667
+
668
+ /* Add null-term */
669
+ s[i] = '\0';
670
+ return s;
671
+ }
672
+
673
+ /* Remove the part of the string from left and from right composed just of
674
+ * contiguous characters found in 'cset', that is a null terminted C string.
675
+ *
676
+ * After the call, the modified sds string is no longer valid and all the
677
+ * references must be substituted with the new pointer returned by the call.
678
+ *
679
+ * Example:
680
+ *
681
+ * s = sdsnew("AA...AA.a.aa.aHelloWorld :::");
682
+ * s = sdstrim(s,"Aa. :");
683
+ * printf("%s\n", s);
684
+ *
685
+ * Output will be just "Hello World".
686
+ */
687
+ sds sdstrim(sds s, const char *cset) {
688
+ char *start, *end, *sp, *ep;
689
+ size_t len;
690
+
691
+ sp = start = s;
692
+ ep = end = s+sdslen(s)-1;
693
+ while(sp <= end && strchr(cset, *sp)) sp++;
694
+ while(ep > sp && strchr(cset, *ep)) ep--;
695
+ len = (sp > ep) ? 0 : ((ep-sp)+1);
696
+ if (s != sp) memmove(s, sp, len);
697
+ s[len] = '\0';
698
+ sdssetlen(s,len);
699
+ return s;
700
+ }
701
+
702
+ /* Turn the string into a smaller (or equal) string containing only the
703
+ * substring specified by the 'start' and 'end' indexes.
704
+ *
705
+ * start and end can be negative, where -1 means the last character of the
706
+ * string, -2 the penultimate character, and so forth.
707
+ *
708
+ * The interval is inclusive, so the start and end characters will be part
709
+ * of the resulting string.
710
+ *
711
+ * The string is modified in-place.
712
+ *
713
+ * Example:
714
+ *
715
+ * s = sdsnew("Hello World");
716
+ * sdsrange(s,1,-1); => "ello World"
717
+ */
718
+ void sdsrange(sds s, int start, int end) {
719
+ size_t newlen, len = sdslen(s);
720
+
721
+ if (len == 0) return;
722
+ if (start < 0) {
723
+ start = len+start;
724
+ if (start < 0) start = 0;
725
+ }
726
+ if (end < 0) {
727
+ end = len+end;
728
+ if (end < 0) end = 0;
729
+ }
730
+ newlen = (start > end) ? 0 : (end-start)+1;
731
+ if (newlen != 0) {
732
+ if (start >= (signed)len) {
733
+ newlen = 0;
734
+ } else if (end >= (signed)len) {
735
+ end = len-1;
736
+ newlen = (start > end) ? 0 : (end-start)+1;
737
+ }
738
+ } else {
739
+ start = 0;
740
+ }
741
+ if (start && newlen) memmove(s, s+start, newlen);
742
+ s[newlen] = 0;
743
+ sdssetlen(s,newlen);
744
+ }
745
+
746
+ /* Apply tolower() to every character of the sds string 's'. */
747
+ void sdstolower(sds s) {
748
+ int len = sdslen(s), j;
749
+
750
+ for (j = 0; j < len; j++) s[j] = tolower(s[j]);
751
+ }
752
+
753
+ /* Apply toupper() to every character of the sds string 's'. */
754
+ void sdstoupper(sds s) {
755
+ int len = sdslen(s), j;
756
+
757
+ for (j = 0; j < len; j++) s[j] = toupper(s[j]);
758
+ }
759
+
760
+ /* Compare two sds strings s1 and s2 with memcmp().
761
+ *
762
+ * Return value:
763
+ *
764
+ * positive if s1 > s2.
765
+ * negative if s1 < s2.
766
+ * 0 if s1 and s2 are exactly the same binary string.
767
+ *
768
+ * If two strings share exactly the same prefix, but one of the two has
769
+ * additional characters, the longer string is considered to be greater than
770
+ * the smaller one. */
771
+ int sdscmp(const sds s1, const sds s2) {
772
+ size_t l1, l2, minlen;
773
+ int cmp;
774
+
775
+ l1 = sdslen(s1);
776
+ l2 = sdslen(s2);
777
+ minlen = (l1 < l2) ? l1 : l2;
778
+ cmp = memcmp(s1,s2,minlen);
779
+ if (cmp == 0) return l1-l2;
780
+ return cmp;
781
+ }
782
+
783
+ /* Split 's' with separator in 'sep'. An array
784
+ * of sds strings is returned. *count will be set
785
+ * by reference to the number of tokens returned.
786
+ *
787
+ * On out of memory, zero length string, zero length
788
+ * separator, NULL is returned.
789
+ *
790
+ * Note that 'sep' is able to split a string using
791
+ * a multi-character separator. For example
792
+ * sdssplit("foo_-_bar","_-_"); will return two
793
+ * elements "foo" and "bar".
794
+ *
795
+ * This version of the function is binary-safe but
796
+ * requires length arguments. sdssplit() is just the
797
+ * same function but for zero-terminated strings.
798
+ */
799
+ sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count) {
800
+ int elements = 0, slots = 5, start = 0, j;
801
+ sds *tokens;
802
+
803
+ if (seplen < 1 || len < 0) return NULL;
804
+
805
+ tokens = s_malloc(sizeof(sds)*slots);
806
+ if (tokens == NULL) return NULL;
807
+
808
+ if (len == 0) {
809
+ *count = 0;
810
+ return tokens;
811
+ }
812
+ for (j = 0; j < (len-(seplen-1)); j++) {
813
+ /* make sure there is room for the next element and the final one */
814
+ if (slots < elements+2) {
815
+ sds *newtokens;
816
+
817
+ slots *= 2;
818
+ newtokens = s_realloc(tokens,sizeof(sds)*slots);
819
+ if (newtokens == NULL) goto cleanup;
820
+ tokens = newtokens;
821
+ }
822
+ /* search the separator */
823
+ if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) {
824
+ tokens[elements] = sdsnewlen(s+start,j-start);
825
+ if (tokens[elements] == NULL) goto cleanup;
826
+ elements++;
827
+ start = j+seplen;
828
+ j = j+seplen-1; /* skip the separator */
829
+ }
830
+ }
831
+ /* Add the final element. We are sure there is room in the tokens array. */
832
+ tokens[elements] = sdsnewlen(s+start,len-start);
833
+ if (tokens[elements] == NULL) goto cleanup;
834
+ elements++;
835
+ *count = elements;
836
+ return tokens;
837
+
838
+ cleanup:
839
+ {
840
+ int i;
841
+ for (i = 0; i < elements; i++) sdsfree(tokens[i]);
842
+ s_free(tokens);
843
+ *count = 0;
844
+ return NULL;
845
+ }
846
+ }
847
+
848
+ /* Free the result returned by sdssplitlen(), or do nothing if 'tokens' is NULL. */
849
+ void sdsfreesplitres(sds *tokens, int count) {
850
+ if (!tokens) return;
851
+ while(count--)
852
+ sdsfree(tokens[count]);
853
+ s_free(tokens);
854
+ }
855
+
856
+ /* Append to the sds string "s" an escaped string representation where
857
+ * all the non-printable characters (tested with isprint()) are turned into
858
+ * escapes in the form "\n\r\a...." or "\x<hex-number>".
859
+ *
860
+ * After the call, the modified sds string is no longer valid and all the
861
+ * references must be substituted with the new pointer returned by the call. */
862
+ sds sdscatrepr(sds s, const char *p, size_t len) {
863
+ s = sdscatlen(s,"\"",1);
864
+ while(len--) {
865
+ switch(*p) {
866
+ case '\\':
867
+ case '"':
868
+ s = sdscatprintf(s,"\\%c",*p);
869
+ break;
870
+ case '\n': s = sdscatlen(s,"\\n",2); break;
871
+ case '\r': s = sdscatlen(s,"\\r",2); break;
872
+ case '\t': s = sdscatlen(s,"\\t",2); break;
873
+ case '\a': s = sdscatlen(s,"\\a",2); break;
874
+ case '\b': s = sdscatlen(s,"\\b",2); break;
875
+ default:
876
+ if (isprint(*p))
877
+ s = sdscatprintf(s,"%c",*p);
878
+ else
879
+ s = sdscatprintf(s,"\\x%02x",(unsigned char)*p);
880
+ break;
881
+ }
882
+ p++;
883
+ }
884
+ return sdscatlen(s,"\"",1);
885
+ }
886
+
887
+ /* Helper function for sdssplitargs() that returns non zero if 'c'
888
+ * is a valid hex digit. */
889
+ int is_hex_digit(char c) {
890
+ return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
891
+ (c >= 'A' && c <= 'F');
892
+ }
893
+
894
+ /* Helper function for sdssplitargs() that converts a hex digit into an
895
+ * integer from 0 to 15 */
896
+ int hex_digit_to_int(char c) {
897
+ switch(c) {
898
+ case '0': return 0;
899
+ case '1': return 1;
900
+ case '2': return 2;
901
+ case '3': return 3;
902
+ case '4': return 4;
903
+ case '5': return 5;
904
+ case '6': return 6;
905
+ case '7': return 7;
906
+ case '8': return 8;
907
+ case '9': return 9;
908
+ case 'a': case 'A': return 10;
909
+ case 'b': case 'B': return 11;
910
+ case 'c': case 'C': return 12;
911
+ case 'd': case 'D': return 13;
912
+ case 'e': case 'E': return 14;
913
+ case 'f': case 'F': return 15;
914
+ default: return 0;
915
+ }
916
+ }
917
+
918
+ /* Split a line into arguments, where every argument can be in the
919
+ * following programming-language REPL-alike form:
920
+ *
921
+ * foo bar "newline are supported\n" and "\xff\x00otherstuff"
922
+ *
923
+ * The number of arguments is stored into *argc, and an array
924
+ * of sds is returned.
925
+ *
926
+ * The caller should free the resulting array of sds strings with
927
+ * sdsfreesplitres().
928
+ *
929
+ * Note that sdscatrepr() is able to convert back a string into
930
+ * a quoted string in the same format sdssplitargs() is able to parse.
931
+ *
932
+ * The function returns the allocated tokens on success, even when the
933
+ * input string is empty, or NULL if the input contains unbalanced
934
+ * quotes or closed quotes followed by non space characters
935
+ * as in: "foo"bar or "foo'
936
+ */
937
+ sds *sdssplitargs(const char *line, int *argc) {
938
+ const char *p = line;
939
+ char *current = NULL;
940
+ char **vector = NULL;
941
+
942
+ *argc = 0;
943
+ while(1) {
944
+ /* skip blanks */
945
+ while(*p && isspace(*p)) p++;
946
+ if (*p) {
947
+ /* get a token */
948
+ int inq=0; /* set to 1 if we are in "quotes" */
949
+ int insq=0; /* set to 1 if we are in 'single quotes' */
950
+ int done=0;
951
+
952
+ if (current == NULL) current = sdsempty();
953
+ while(!done) {
954
+ if (inq) {
955
+ if (*p == '\\' && *(p+1) == 'x' &&
956
+ is_hex_digit(*(p+2)) &&
957
+ is_hex_digit(*(p+3)))
958
+ {
959
+ unsigned char byte;
960
+
961
+ byte = (hex_digit_to_int(*(p+2))*16)+
962
+ hex_digit_to_int(*(p+3));
963
+ current = sdscatlen(current,(char*)&byte,1);
964
+ p += 3;
965
+ } else if (*p == '\\' && *(p+1)) {
966
+ char c;
967
+
968
+ p++;
969
+ switch(*p) {
970
+ case 'n': c = '\n'; break;
971
+ case 'r': c = '\r'; break;
972
+ case 't': c = '\t'; break;
973
+ case 'b': c = '\b'; break;
974
+ case 'a': c = '\a'; break;
975
+ default: c = *p; break;
976
+ }
977
+ current = sdscatlen(current,&c,1);
978
+ } else if (*p == '"') {
979
+ /* closing quote must be followed by a space or
980
+ * nothing at all. */
981
+ if (*(p+1) && !isspace(*(p+1))) goto err;
982
+ done=1;
983
+ } else if (!*p) {
984
+ /* unterminated quotes */
985
+ goto err;
986
+ } else {
987
+ current = sdscatlen(current,p,1);
988
+ }
989
+ } else if (insq) {
990
+ if (*p == '\\' && *(p+1) == '\'') {
991
+ p++;
992
+ current = sdscatlen(current,"'",1);
993
+ } else if (*p == '\'') {
994
+ /* closing quote must be followed by a space or
995
+ * nothing at all. */
996
+ if (*(p+1) && !isspace(*(p+1))) goto err;
997
+ done=1;
998
+ } else if (!*p) {
999
+ /* unterminated quotes */
1000
+ goto err;
1001
+ } else {
1002
+ current = sdscatlen(current,p,1);
1003
+ }
1004
+ } else {
1005
+ switch(*p) {
1006
+ case ' ':
1007
+ case '\n':
1008
+ case '\r':
1009
+ case '\t':
1010
+ case '\0':
1011
+ done=1;
1012
+ break;
1013
+ case '"':
1014
+ inq=1;
1015
+ break;
1016
+ case '\'':
1017
+ insq=1;
1018
+ break;
1019
+ default:
1020
+ current = sdscatlen(current,p,1);
1021
+ break;
1022
+ }
1023
+ }
1024
+ if (*p) p++;
1025
+ }
1026
+ /* add the token to the vector */
1027
+ vector = s_realloc(vector,((*argc)+1)*sizeof(char*));
1028
+ vector[*argc] = current;
1029
+ (*argc)++;
1030
+ current = NULL;
1031
+ } else {
1032
+ /* Even on empty input string return something not NULL. */
1033
+ if (vector == NULL) vector = s_malloc(sizeof(void*));
1034
+ return vector;
1035
+ }
1036
+ }
1037
+
1038
+ err:
1039
+ while((*argc)--)
1040
+ sdsfree(vector[*argc]);
1041
+ s_free(vector);
1042
+ if (current) sdsfree(current);
1043
+ *argc = 0;
1044
+ return NULL;
1045
+ }
1046
+
1047
+ /* Modify the string substituting all the occurrences of the set of
1048
+ * characters specified in the 'from' string to the corresponding character
1049
+ * in the 'to' array.
1050
+ *
1051
+ * For instance: sdsmapchars(mystring, "ho", "01", 2)
1052
+ * will have the effect of turning the string "hello" into "0ell1".
1053
+ *
1054
+ * The function returns the sds string pointer, that is always the same
1055
+ * as the input pointer since no resize is needed. */
1056
+ sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) {
1057
+ size_t j, i, l = sdslen(s);
1058
+
1059
+ for (j = 0; j < l; j++) {
1060
+ for (i = 0; i < setlen; i++) {
1061
+ if (s[j] == from[i]) {
1062
+ s[j] = to[i];
1063
+ break;
1064
+ }
1065
+ }
1066
+ }
1067
+ return s;
1068
+ }
1069
+
1070
+ /* Join an array of C strings using the specified separator (also a C string).
1071
+ * Returns the result as an sds string. */
1072
+ sds sdsjoin(char **argv, int argc, char *sep) {
1073
+ sds join = sdsempty();
1074
+ int j;
1075
+
1076
+ for (j = 0; j < argc; j++) {
1077
+ join = sdscat(join, argv[j]);
1078
+ if (j != argc-1) join = sdscat(join,sep);
1079
+ }
1080
+ return join;
1081
+ }
1082
+
1083
+ /* Like sdsjoin, but joins an array of SDS strings. */
1084
+ sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen) {
1085
+ sds join = sdsempty();
1086
+ int j;
1087
+
1088
+ for (j = 0; j < argc; j++) {
1089
+ join = sdscatsds(join, argv[j]);
1090
+ if (j != argc-1) join = sdscatlen(join,sep,seplen);
1091
+ }
1092
+ return join;
1093
+ }
1094
+
1095
+ /* Wrappers to the allocators used by SDS. Note that SDS will actually
1096
+ * just use the macros defined into sdsalloc.h in order to avoid to pay
1097
+ * the overhead of function calls. Here we define these wrappers only for
1098
+ * the programs SDS is linked to, if they want to touch the SDS internals
1099
+ * even if they use a different allocator. */
1100
+ void *sds_malloc(size_t size) { return s_malloc(size); }
1101
+ void *sds_realloc(void *ptr, size_t size) { return s_realloc(ptr,size); }
1102
+ void sds_free(void *ptr) { s_free(ptr); }
1103
+
1104
+ #if defined(SDS_TEST_MAIN)
1105
+ #include <stdio.h>
1106
+ #include "testhelp.h"
1107
+ #include "limits.h"
1108
+
1109
+ #define UNUSED(x) (void)(x)
1110
+ int sdsTest(void) {
1111
+ {
1112
+ sds x = sdsnew("foo"), y;
1113
+
1114
+ test_cond("Create a string and obtain the length",
1115
+ sdslen(x) == 3 && memcmp(x,"foo\0",4) == 0)
1116
+
1117
+ sdsfree(x);
1118
+ x = sdsnewlen("foo",2);
1119
+ test_cond("Create a string with specified length",
1120
+ sdslen(x) == 2 && memcmp(x,"fo\0",3) == 0)
1121
+
1122
+ x = sdscat(x,"bar");
1123
+ test_cond("Strings concatenation",
1124
+ sdslen(x) == 5 && memcmp(x,"fobar\0",6) == 0);
1125
+
1126
+ x = sdscpy(x,"a");
1127
+ test_cond("sdscpy() against an originally longer string",
1128
+ sdslen(x) == 1 && memcmp(x,"a\0",2) == 0)
1129
+
1130
+ x = sdscpy(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk");
1131
+ test_cond("sdscpy() against an originally shorter string",
1132
+ sdslen(x) == 33 &&
1133
+ memcmp(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0",33) == 0)
1134
+
1135
+ sdsfree(x);
1136
+ x = sdscatprintf(sdsempty(),"%d",123);
1137
+ test_cond("sdscatprintf() seems working in the base case",
1138
+ sdslen(x) == 3 && memcmp(x,"123\0",4) == 0)
1139
+
1140
+ sdsfree(x);
1141
+ x = sdsnew("--");
1142
+ x = sdscatfmt(x, "Hello %s World %I,%I--", "Hi!", LLONG_MIN,LLONG_MAX);
1143
+ test_cond("sdscatfmt() seems working in the base case",
1144
+ sdslen(x) == 60 &&
1145
+ memcmp(x,"--Hello Hi! World -9223372036854775808,"
1146
+ "9223372036854775807--",60) == 0)
1147
+ printf("[%s]\n",x);
1148
+
1149
+ sdsfree(x);
1150
+ x = sdsnew("--");
1151
+ x = sdscatfmt(x, "%u,%U--", UINT_MAX, ULLONG_MAX);
1152
+ test_cond("sdscatfmt() seems working with unsigned numbers",
1153
+ sdslen(x) == 35 &&
1154
+ memcmp(x,"--4294967295,18446744073709551615--",35) == 0)
1155
+
1156
+ sdsfree(x);
1157
+ x = sdsnew(" x ");
1158
+ sdstrim(x," x");
1159
+ test_cond("sdstrim() works when all chars match",
1160
+ sdslen(x) == 0)
1161
+
1162
+ sdsfree(x);
1163
+ x = sdsnew(" x ");
1164
+ sdstrim(x," ");
1165
+ test_cond("sdstrim() works when a single char remains",
1166
+ sdslen(x) == 1 && x[0] == 'x')
1167
+
1168
+ sdsfree(x);
1169
+ x = sdsnew("xxciaoyyy");
1170
+ sdstrim(x,"xy");
1171
+ test_cond("sdstrim() correctly trims characters",
1172
+ sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0)
1173
+
1174
+ y = sdsdup(x);
1175
+ sdsrange(y,1,1);
1176
+ test_cond("sdsrange(...,1,1)",
1177
+ sdslen(y) == 1 && memcmp(y,"i\0",2) == 0)
1178
+
1179
+ sdsfree(y);
1180
+ y = sdsdup(x);
1181
+ sdsrange(y,1,-1);
1182
+ test_cond("sdsrange(...,1,-1)",
1183
+ sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)
1184
+
1185
+ sdsfree(y);
1186
+ y = sdsdup(x);
1187
+ sdsrange(y,-2,-1);
1188
+ test_cond("sdsrange(...,-2,-1)",
1189
+ sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0)
1190
+
1191
+ sdsfree(y);
1192
+ y = sdsdup(x);
1193
+ sdsrange(y,2,1);
1194
+ test_cond("sdsrange(...,2,1)",
1195
+ sdslen(y) == 0 && memcmp(y,"\0",1) == 0)
1196
+
1197
+ sdsfree(y);
1198
+ y = sdsdup(x);
1199
+ sdsrange(y,1,100);
1200
+ test_cond("sdsrange(...,1,100)",
1201
+ sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)
1202
+
1203
+ sdsfree(y);
1204
+ y = sdsdup(x);
1205
+ sdsrange(y,100,100);
1206
+ test_cond("sdsrange(...,100,100)",
1207
+ sdslen(y) == 0 && memcmp(y,"\0",1) == 0)
1208
+
1209
+ sdsfree(y);
1210
+ sdsfree(x);
1211
+ x = sdsnew("foo");
1212
+ y = sdsnew("foa");
1213
+ test_cond("sdscmp(foo,foa)", sdscmp(x,y) > 0)
1214
+
1215
+ sdsfree(y);
1216
+ sdsfree(x);
1217
+ x = sdsnew("bar");
1218
+ y = sdsnew("bar");
1219
+ test_cond("sdscmp(bar,bar)", sdscmp(x,y) == 0)
1220
+
1221
+ sdsfree(y);
1222
+ sdsfree(x);
1223
+ x = sdsnew("aar");
1224
+ y = sdsnew("bar");
1225
+ test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0)
1226
+
1227
+ sdsfree(y);
1228
+ sdsfree(x);
1229
+ x = sdsnewlen("\a\n\0foo\r",7);
1230
+ y = sdscatrepr(sdsempty(),x,sdslen(x));
1231
+ test_cond("sdscatrepr(...data...)",
1232
+ memcmp(y,"\"\\a\\n\\x00foo\\r\"",15) == 0)
1233
+
1234
+ {
1235
+ unsigned int oldfree;
1236
+ char *p;
1237
+ int step = 10, j, i;
1238
+
1239
+ sdsfree(x);
1240
+ sdsfree(y);
1241
+ x = sdsnew("0");
1242
+ test_cond("sdsnew() free/len buffers", sdslen(x) == 1 && sdsavail(x) == 0);
1243
+
1244
+ /* Run the test a few times in order to hit the first two
1245
+ * SDS header types. */
1246
+ for (i = 0; i < 10; i++) {
1247
+ int oldlen = sdslen(x);
1248
+ x = sdsMakeRoomFor(x,step);
1249
+ int type = x[-1]&SDS_TYPE_MASK;
1250
+
1251
+ test_cond("sdsMakeRoomFor() len", sdslen(x) == oldlen);
1252
+ if (type != SDS_TYPE_5) {
1253
+ test_cond("sdsMakeRoomFor() free", sdsavail(x) >= step);
1254
+ oldfree = sdsavail(x);
1255
+ }
1256
+ p = x+oldlen;
1257
+ for (j = 0; j < step; j++) {
1258
+ p[j] = 'A'+j;
1259
+ }
1260
+ sdsIncrLen(x,step);
1261
+ }
1262
+ test_cond("sdsMakeRoomFor() content",
1263
+ memcmp("0ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJ",x,101) == 0);
1264
+ test_cond("sdsMakeRoomFor() final length",sdslen(x)==101);
1265
+
1266
+ sdsfree(x);
1267
+ }
1268
+ }
1269
+ test_report()
1270
+ return 0;
1271
+ }
1272
+ #endif
1273
+
1274
+ #ifdef SDS_TEST_MAIN
1275
+ int main(void) {
1276
+ return sdsTest();
1277
+ }
1278
+ #endif