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