From 6ae1ef7f21b089a5f7c54179615989c2b65668fe Mon Sep 17 00:00:00 2001 From: Dvir Volk Date: Sun, 1 May 2016 16:24:44 +0300 Subject: [PATCH] added rmutil --- rmutil/Makefile | 17 + rmutil/rmutil.a | Bin 0 -> 62726 bytes rmutil/sds.c | 1274 ++++++++++++++++++++++++++++++++++++++++++++++ rmutil/sds.h | 273 ++++++++++ rmutil/sds.o | Bin 0 -> 27992 bytes rmutil/strings.c | 53 ++ rmutil/strings.h | 22 + rmutil/strings.o | Bin 0 -> 14312 bytes rmutil/util.c | 117 +++++ rmutil/util.h | 67 +++ rmutil/util.o | Bin 0 -> 15480 bytes 11 files changed, 1823 insertions(+) create mode 100644 rmutil/Makefile create mode 100644 rmutil/rmutil.a create mode 100644 rmutil/sds.c create mode 100644 rmutil/sds.h create mode 100644 rmutil/sds.o create mode 100644 rmutil/strings.c create mode 100644 rmutil/strings.h create mode 100644 rmutil/strings.o create mode 100644 rmutil/util.c create mode 100644 rmutil/util.h create mode 100644 rmutil/util.o diff --git a/rmutil/Makefile b/rmutil/Makefile new file mode 100644 index 0000000..318becd --- /dev/null +++ b/rmutil/Makefile @@ -0,0 +1,17 @@ +#set environment variable RM_INCLUDE_DIR to the location of redismodule.h + +CFLAGS = -g -fPIC -lc -lm -O3 -std=gnu99 -I$(RM_INCLUDE_DIR) +CC=gcc +CFLAGS = -I$(RM_INCLUDE_DIR) +.SUFFIXES: .c .so .xo .o + +OBJS=util.o strings.o sds.o + +all: rmutil.a + +clean: + rm -rf *.xo *.so *.o + +rmutil.a: $(OBJS) + ar rcs $@ $^ + diff --git a/rmutil/rmutil.a b/rmutil/rmutil.a new file mode 100644 index 0000000000000000000000000000000000000000..887bcdf6f6d44f9d01da4600c8bbd235e75c7d94 GIT binary patch literal 62726 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 a%h-7Dz)c@I + * Copyright (c) 2015, Oran Agra + * Copyright (c) 2015, Redis Labs, Inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include "sds.h" +#include "sdsalloc.h" + +static inline int sdsHdrSize(char type) { + switch(type&SDS_TYPE_MASK) { + case SDS_TYPE_5: + return sizeof(struct sdshdr5); + case SDS_TYPE_8: + return sizeof(struct sdshdr8); + case SDS_TYPE_16: + return sizeof(struct sdshdr16); + case SDS_TYPE_32: + return sizeof(struct sdshdr32); + case SDS_TYPE_64: + return sizeof(struct sdshdr64); + } + return 0; +} + +static inline char sdsReqType(size_t string_size) { + if (string_size < 32) + return SDS_TYPE_5; + if (string_size < 0xff) + return SDS_TYPE_8; + if (string_size < 0xffff) + return SDS_TYPE_16; + if (string_size < 0xffffffff) + return SDS_TYPE_32; + return SDS_TYPE_64; +} + +/* Create a new sds string with the content specified by the 'init' pointer + * and 'initlen'. + * If NULL is used for 'init' the string is initialized with zero bytes. + * + * The string is always null-termined (all the sds strings are, always) so + * even if you create an sds string with: + * + * mystring = sdsnewlen("abc",3); + * + * You can print the string with printf() as there is an implicit \0 at the + * end of the string. However the string is binary safe and can contain + * \0 characters in the middle, as the length is stored in the sds header. */ +sds sdsnewlen(const void *init, size_t initlen) { + void *sh; + sds s; + char type = sdsReqType(initlen); + /* Empty strings are usually created in order to append. Use type 8 + * since type 5 is not good at this. */ + if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8; + int hdrlen = sdsHdrSize(type); + unsigned char *fp; /* flags pointer. */ + + sh = s_malloc(hdrlen+initlen+1); + if (!init) + memset(sh, 0, hdrlen+initlen+1); + if (sh == NULL) return NULL; + s = (char*)sh+hdrlen; + fp = ((unsigned char*)s)-1; + switch(type) { + case SDS_TYPE_5: { + *fp = type | (initlen << SDS_TYPE_BITS); + break; + } + case SDS_TYPE_8: { + SDS_HDR_VAR(8,s); + sh->len = initlen; + sh->alloc = initlen; + *fp = type; + break; + } + case SDS_TYPE_16: { + SDS_HDR_VAR(16,s); + sh->len = initlen; + sh->alloc = initlen; + *fp = type; + break; + } + case SDS_TYPE_32: { + SDS_HDR_VAR(32,s); + sh->len = initlen; + sh->alloc = initlen; + *fp = type; + break; + } + case SDS_TYPE_64: { + SDS_HDR_VAR(64,s); + sh->len = initlen; + sh->alloc = initlen; + *fp = type; + break; + } + } + if (initlen && init) + memcpy(s, init, initlen); + s[initlen] = '\0'; + return s; +} + +/* Create an empty (zero length) sds string. Even in this case the string + * always has an implicit null term. */ +sds sdsempty(void) { + return sdsnewlen("",0); +} + +/* Create a new sds string starting from a null terminated C string. */ +sds sdsnew(const char *init) { + size_t initlen = (init == NULL) ? 0 : strlen(init); + return sdsnewlen(init, initlen); +} + +/* Duplicate an sds string. */ +sds sdsdup(const sds s) { + return sdsnewlen(s, sdslen(s)); +} + +/* Free an sds string. No operation is performed if 's' is NULL. */ +void sdsfree(sds s) { + if (s == NULL) return; + s_free((char*)s-sdsHdrSize(s[-1])); +} + +/* Set the sds string length to the length as obtained with strlen(), so + * considering as content only up to the first null term character. + * + * This function is useful when the sds string is hacked manually in some + * way, like in the following example: + * + * s = sdsnew("foobar"); + * s[2] = '\0'; + * sdsupdatelen(s); + * printf("%d\n", sdslen(s)); + * + * The output will be "2", but if we comment out the call to sdsupdatelen() + * the output will be "6" as the string was modified but the logical length + * remains 6 bytes. */ +void sdsupdatelen(sds s) { + int reallen = strlen(s); + sdssetlen(s, reallen); +} + +/* Modify an sds string in-place to make it empty (zero length). + * However all the existing buffer is not discarded but set as free space + * so that next append operations will not require allocations up to the + * number of bytes previously available. */ +void sdsclear(sds s) { + sdssetlen(s, 0); + s[0] = '\0'; +} + +/* Enlarge the free space at the end of the sds string so that the caller + * is sure that after calling this function can overwrite up to addlen + * bytes after the end of the string, plus one more byte for nul term. + * + * Note: this does not change the *length* of the sds string as returned + * by sdslen(), but only the free buffer space we have. */ +sds sdsMakeRoomFor(sds s, size_t addlen) { + void *sh, *newsh; + size_t avail = sdsavail(s); + size_t len, newlen; + char type, oldtype = s[-1] & SDS_TYPE_MASK; + int hdrlen; + + /* Return ASAP if there is enough space left. */ + if (avail >= addlen) return s; + + len = sdslen(s); + sh = (char*)s-sdsHdrSize(oldtype); + newlen = (len+addlen); + if (newlen < SDS_MAX_PREALLOC) + newlen *= 2; + else + newlen += SDS_MAX_PREALLOC; + + type = sdsReqType(newlen); + + /* Don't use type 5: the user is appending to the string and type 5 is + * not able to remember empty space, so sdsMakeRoomFor() must be called + * at every appending operation. */ + if (type == SDS_TYPE_5) type = SDS_TYPE_8; + + hdrlen = sdsHdrSize(type); + if (oldtype==type) { + newsh = s_realloc(sh, hdrlen+newlen+1); + if (newsh == NULL) return NULL; + s = (char*)newsh+hdrlen; + } else { + /* Since the header size changes, need to move the string forward, + * and can't use realloc */ + newsh = s_malloc(hdrlen+newlen+1); + if (newsh == NULL) return NULL; + memcpy((char*)newsh+hdrlen, s, len+1); + s_free(sh); + s = (char*)newsh+hdrlen; + s[-1] = type; + sdssetlen(s, len); + } + sdssetalloc(s, newlen); + return s; +} + +/* Reallocate the sds string so that it has no free space at the end. The + * contained string remains not altered, but next concatenation operations + * will require a reallocation. + * + * After the call, the passed sds string is no longer valid and all the + * references must be substituted with the new pointer returned by the call. */ +sds sdsRemoveFreeSpace(sds s) { + void *sh, *newsh; + char type, oldtype = s[-1] & SDS_TYPE_MASK; + int hdrlen; + size_t len = sdslen(s); + sh = (char*)s-sdsHdrSize(oldtype); + + type = sdsReqType(len); + hdrlen = sdsHdrSize(type); + if (oldtype==type) { + newsh = s_realloc(sh, hdrlen+len+1); + if (newsh == NULL) return NULL; + s = (char*)newsh+hdrlen; + } else { + newsh = s_malloc(hdrlen+len+1); + if (newsh == NULL) return NULL; + memcpy((char*)newsh+hdrlen, s, len+1); + s_free(sh); + s = (char*)newsh+hdrlen; + s[-1] = type; + sdssetlen(s, len); + } + sdssetalloc(s, len); + return s; +} + +/* Return the total size of the allocation of the specifed sds string, + * including: + * 1) The sds header before the pointer. + * 2) The string. + * 3) The free buffer at the end if any. + * 4) The implicit null term. + */ +size_t sdsAllocSize(sds s) { + size_t alloc = sdsalloc(s); + return sdsHdrSize(s[-1])+alloc+1; +} + +/* Return the pointer of the actual SDS allocation (normally SDS strings + * are referenced by the start of the string buffer). */ +void *sdsAllocPtr(sds s) { + return (void*) (s-sdsHdrSize(s[-1])); +} + +/* Increment the sds length and decrements the left free space at the + * end of the string according to 'incr'. Also set the null term + * in the new end of the string. + * + * This function is used in order to fix the string length after the + * user calls sdsMakeRoomFor(), writes something after the end of + * the current string, and finally needs to set the new length. + * + * Note: it is possible to use a negative increment in order to + * right-trim the string. + * + * Usage example: + * + * Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the + * following schema, to cat bytes coming from the kernel to the end of an + * sds string without copying into an intermediate buffer: + * + * oldlen = sdslen(s); + * s = sdsMakeRoomFor(s, BUFFER_SIZE); + * nread = read(fd, s+oldlen, BUFFER_SIZE); + * ... check for nread <= 0 and handle it ... + * sdsIncrLen(s, nread); + */ +void sdsIncrLen(sds s, int incr) { + unsigned char flags = s[-1]; + size_t len; + switch(flags&SDS_TYPE_MASK) { + case SDS_TYPE_5: { + unsigned char *fp = ((unsigned char*)s)-1; + unsigned char oldlen = SDS_TYPE_5_LEN(flags); + assert((incr > 0 && oldlen+incr < 32) || (incr < 0 && oldlen >= (unsigned int)(-incr))); + *fp = SDS_TYPE_5 | ((oldlen+incr) << SDS_TYPE_BITS); + len = oldlen+incr; + break; + } + case SDS_TYPE_8: { + SDS_HDR_VAR(8,s); + assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr))); + len = (sh->len += incr); + break; + } + case SDS_TYPE_16: { + SDS_HDR_VAR(16,s); + assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr))); + len = (sh->len += incr); + break; + } + case SDS_TYPE_32: { + SDS_HDR_VAR(32,s); + assert((incr >= 0 && sh->alloc-sh->len >= (unsigned int)incr) || (incr < 0 && sh->len >= (unsigned int)(-incr))); + len = (sh->len += incr); + break; + } + case SDS_TYPE_64: { + SDS_HDR_VAR(64,s); + assert((incr >= 0 && sh->alloc-sh->len >= (uint64_t)incr) || (incr < 0 && sh->len >= (uint64_t)(-incr))); + len = (sh->len += incr); + break; + } + default: len = 0; /* Just to avoid compilation warnings. */ + } + s[len] = '\0'; +} + +/* Grow the sds to have the specified length. Bytes that were not part of + * the original length of the sds will be set to zero. + * + * if the specified length is smaller than the current length, no operation + * is performed. */ +sds sdsgrowzero(sds s, size_t len) { + size_t curlen = sdslen(s); + + if (len <= curlen) return s; + s = sdsMakeRoomFor(s,len-curlen); + if (s == NULL) return NULL; + + /* Make sure added region doesn't contain garbage */ + memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */ + sdssetlen(s, len); + return s; +} + +/* Append the specified binary-safe string pointed by 't' of 'len' bytes to the + * end of the specified sds string 's'. + * + * After the call, the passed sds string is no longer valid and all the + * references must be substituted with the new pointer returned by the call. */ +sds sdscatlen(sds s, const void *t, size_t len) { + size_t curlen = sdslen(s); + + s = sdsMakeRoomFor(s,len); + if (s == NULL) return NULL; + memcpy(s+curlen, t, len); + sdssetlen(s, curlen+len); + s[curlen+len] = '\0'; + return s; +} + +/* Append the specified null termianted C string to the sds string 's'. + * + * After the call, the passed sds string is no longer valid and all the + * references must be substituted with the new pointer returned by the call. */ +sds sdscat(sds s, const char *t) { + return sdscatlen(s, t, strlen(t)); +} + +/* Append the specified sds 't' to the existing sds 's'. + * + * After the call, the modified sds string is no longer valid and all the + * references must be substituted with the new pointer returned by the call. */ +sds sdscatsds(sds s, const sds t) { + return sdscatlen(s, t, sdslen(t)); +} + +/* Destructively modify the sds string 's' to hold the specified binary + * safe string pointed by 't' of length 'len' bytes. */ +sds sdscpylen(sds s, const char *t, size_t len) { + if (sdsalloc(s) < len) { + s = sdsMakeRoomFor(s,len-sdslen(s)); + if (s == NULL) return NULL; + } + memcpy(s, t, len); + s[len] = '\0'; + sdssetlen(s, len); + return s; +} + +/* Like sdscpylen() but 't' must be a null-termined string so that the length + * of the string is obtained with strlen(). */ +sds sdscpy(sds s, const char *t) { + return sdscpylen(s, t, strlen(t)); +} + +/* Helper for sdscatlonglong() doing the actual number -> string + * conversion. 's' must point to a string with room for at least + * SDS_LLSTR_SIZE bytes. + * + * The function returns the length of the null-terminated string + * representation stored at 's'. */ +#define SDS_LLSTR_SIZE 21 +int sdsll2str(char *s, long long value) { + char *p, aux; + unsigned long long v; + size_t l; + + /* Generate the string representation, this method produces + * an reversed string. */ + v = (value < 0) ? -value : value; + p = s; + do { + *p++ = '0'+(v%10); + v /= 10; + } while(v); + if (value < 0) *p++ = '-'; + + /* Compute length and add null term. */ + l = p-s; + *p = '\0'; + + /* Reverse the string. */ + p--; + while(s < p) { + aux = *s; + *s = *p; + *p = aux; + s++; + p--; + } + return l; +} + +/* Identical sdsll2str(), but for unsigned long long type. */ +int sdsull2str(char *s, unsigned long long v) { + char *p, aux; + size_t l; + + /* Generate the string representation, this method produces + * an reversed string. */ + p = s; + do { + *p++ = '0'+(v%10); + v /= 10; + } while(v); + + /* Compute length and add null term. */ + l = p-s; + *p = '\0'; + + /* Reverse the string. */ + p--; + while(s < p) { + aux = *s; + *s = *p; + *p = aux; + s++; + p--; + } + return l; +} + +/* Create an sds string from a long long value. It is much faster than: + * + * sdscatprintf(sdsempty(),"%lld\n", value); + */ +sds sdsfromlonglong(long long value) { + char buf[SDS_LLSTR_SIZE]; + int len = sdsll2str(buf,value); + + return sdsnewlen(buf,len); +} + +/* Like sdscatprintf() but gets va_list instead of being variadic. */ +sds sdscatvprintf(sds s, const char *fmt, va_list ap) { + va_list cpy; + char staticbuf[1024], *buf = staticbuf, *t; + size_t buflen = strlen(fmt)*2; + + /* We try to start using a static buffer for speed. + * If not possible we revert to heap allocation. */ + if (buflen > sizeof(staticbuf)) { + buf = s_malloc(buflen); + if (buf == NULL) return NULL; + } else { + buflen = sizeof(staticbuf); + } + + /* Try with buffers two times bigger every time we fail to + * fit the string in the current buffer size. */ + while(1) { + buf[buflen-2] = '\0'; + va_copy(cpy,ap); + vsnprintf(buf, buflen, fmt, cpy); + va_end(cpy); + if (buf[buflen-2] != '\0') { + if (buf != staticbuf) s_free(buf); + buflen *= 2; + buf = s_malloc(buflen); + if (buf == NULL) return NULL; + continue; + } + break; + } + + /* Finally concat the obtained string to the SDS string and return it. */ + t = sdscat(s, buf); + if (buf != staticbuf) s_free(buf); + return t; +} + +/* Append to the sds string 's' a string obtained using printf-alike format + * specifier. + * + * After the call, the modified sds string is no longer valid and all the + * references must be substituted with the new pointer returned by the call. + * + * Example: + * + * s = sdsnew("Sum is: "); + * s = sdscatprintf(s,"%d+%d = %d",a,b,a+b). + * + * Often you need to create a string from scratch with the printf-alike + * format. When this is the need, just use sdsempty() as the target string: + * + * s = sdscatprintf(sdsempty(), "... your format ...", args); + */ +sds sdscatprintf(sds s, const char *fmt, ...) { + va_list ap; + char *t; + va_start(ap, fmt); + t = sdscatvprintf(s,fmt,ap); + va_end(ap); + return t; +} + +/* This function is similar to sdscatprintf, but much faster as it does + * not rely on sprintf() family functions implemented by the libc that + * are often very slow. Moreover directly handling the sds string as + * new data is concatenated provides a performance improvement. + * + * However this function only handles an incompatible subset of printf-alike + * format specifiers: + * + * %s - C String + * %S - SDS string + * %i - signed int + * %I - 64 bit signed integer (long long, int64_t) + * %u - unsigned int + * %U - 64 bit unsigned integer (unsigned long long, uint64_t) + * %% - Verbatim "%" character. + */ +sds sdscatfmt(sds s, char const *fmt, ...) { + size_t initlen = sdslen(s); + const char *f = fmt; + int i; + va_list ap; + + va_start(ap,fmt); + f = fmt; /* Next format specifier byte to process. */ + i = initlen; /* Position of the next byte to write to dest str. */ + while(*f) { + char next, *str; + size_t l; + long long num; + unsigned long long unum; + + /* Make sure there is always space for at least 1 char. */ + if (sdsavail(s)==0) { + s = sdsMakeRoomFor(s,1); + } + + switch(*f) { + case '%': + next = *(f+1); + f++; + switch(next) { + case 's': + case 'S': + str = va_arg(ap,char*); + l = (next == 's') ? strlen(str) : sdslen(str); + if (sdsavail(s) < l) { + s = sdsMakeRoomFor(s,l); + } + memcpy(s+i,str,l); + sdsinclen(s,l); + i += l; + break; + case 'i': + case 'I': + if (next == 'i') + num = va_arg(ap,int); + else + num = va_arg(ap,long long); + { + char buf[SDS_LLSTR_SIZE]; + l = sdsll2str(buf,num); + if (sdsavail(s) < l) { + s = sdsMakeRoomFor(s,l); + } + memcpy(s+i,buf,l); + sdsinclen(s,l); + i += l; + } + break; + case 'u': + case 'U': + if (next == 'u') + unum = va_arg(ap,unsigned int); + else + unum = va_arg(ap,unsigned long long); + { + char buf[SDS_LLSTR_SIZE]; + l = sdsull2str(buf,unum); + if (sdsavail(s) < l) { + s = sdsMakeRoomFor(s,l); + } + memcpy(s+i,buf,l); + sdsinclen(s,l); + i += l; + } + break; + default: /* Handle %% and generally %. */ + s[i++] = next; + sdsinclen(s,1); + break; + } + break; + default: + s[i++] = *f; + sdsinclen(s,1); + break; + } + f++; + } + va_end(ap); + + /* Add null-term */ + s[i] = '\0'; + return s; +} + +/* Remove the part of the string from left and from right composed just of + * contiguous characters found in 'cset', that is a null terminted C string. + * + * After the call, the modified sds string is no longer valid and all the + * references must be substituted with the new pointer returned by the call. + * + * Example: + * + * s = sdsnew("AA...AA.a.aa.aHelloWorld :::"); + * s = sdstrim(s,"Aa. :"); + * printf("%s\n", s); + * + * Output will be just "Hello World". + */ +sds sdstrim(sds s, const char *cset) { + char *start, *end, *sp, *ep; + size_t len; + + sp = start = s; + ep = end = s+sdslen(s)-1; + while(sp <= end && strchr(cset, *sp)) sp++; + while(ep > sp && strchr(cset, *ep)) ep--; + len = (sp > ep) ? 0 : ((ep-sp)+1); + if (s != sp) memmove(s, sp, len); + s[len] = '\0'; + sdssetlen(s,len); + return s; +} + +/* Turn the string into a smaller (or equal) string containing only the + * substring specified by the 'start' and 'end' indexes. + * + * start and end can be negative, where -1 means the last character of the + * string, -2 the penultimate character, and so forth. + * + * The interval is inclusive, so the start and end characters will be part + * of the resulting string. + * + * The string is modified in-place. + * + * Example: + * + * s = sdsnew("Hello World"); + * sdsrange(s,1,-1); => "ello World" + */ +void sdsrange(sds s, int start, int end) { + size_t newlen, len = sdslen(s); + + if (len == 0) return; + if (start < 0) { + start = len+start; + if (start < 0) start = 0; + } + if (end < 0) { + end = len+end; + if (end < 0) end = 0; + } + newlen = (start > end) ? 0 : (end-start)+1; + if (newlen != 0) { + if (start >= (signed)len) { + newlen = 0; + } else if (end >= (signed)len) { + end = len-1; + newlen = (start > end) ? 0 : (end-start)+1; + } + } else { + start = 0; + } + if (start && newlen) memmove(s, s+start, newlen); + s[newlen] = 0; + sdssetlen(s,newlen); +} + +/* Apply tolower() to every character of the sds string 's'. */ +void sdstolower(sds s) { + int len = sdslen(s), j; + + for (j = 0; j < len; j++) s[j] = tolower(s[j]); +} + +/* Apply toupper() to every character of the sds string 's'. */ +void sdstoupper(sds s) { + int len = sdslen(s), j; + + for (j = 0; j < len; j++) s[j] = toupper(s[j]); +} + +/* Compare two sds strings s1 and s2 with memcmp(). + * + * Return value: + * + * positive if s1 > s2. + * negative if s1 < s2. + * 0 if s1 and s2 are exactly the same binary string. + * + * If two strings share exactly the same prefix, but one of the two has + * additional characters, the longer string is considered to be greater than + * the smaller one. */ +int sdscmp(const sds s1, const sds s2) { + size_t l1, l2, minlen; + int cmp; + + l1 = sdslen(s1); + l2 = sdslen(s2); + minlen = (l1 < l2) ? l1 : l2; + cmp = memcmp(s1,s2,minlen); + if (cmp == 0) return l1-l2; + return cmp; +} + +/* Split 's' with separator in 'sep'. An array + * of sds strings is returned. *count will be set + * by reference to the number of tokens returned. + * + * On out of memory, zero length string, zero length + * separator, NULL is returned. + * + * Note that 'sep' is able to split a string using + * a multi-character separator. For example + * sdssplit("foo_-_bar","_-_"); will return two + * elements "foo" and "bar". + * + * This version of the function is binary-safe but + * requires length arguments. sdssplit() is just the + * same function but for zero-terminated strings. + */ +sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count) { + int elements = 0, slots = 5, start = 0, j; + sds *tokens; + + if (seplen < 1 || len < 0) return NULL; + + tokens = s_malloc(sizeof(sds)*slots); + if (tokens == NULL) return NULL; + + if (len == 0) { + *count = 0; + return tokens; + } + for (j = 0; j < (len-(seplen-1)); j++) { + /* make sure there is room for the next element and the final one */ + if (slots < elements+2) { + sds *newtokens; + + slots *= 2; + newtokens = s_realloc(tokens,sizeof(sds)*slots); + if (newtokens == NULL) goto cleanup; + tokens = newtokens; + } + /* search the separator */ + if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) { + tokens[elements] = sdsnewlen(s+start,j-start); + if (tokens[elements] == NULL) goto cleanup; + elements++; + start = j+seplen; + j = j+seplen-1; /* skip the separator */ + } + } + /* Add the final element. We are sure there is room in the tokens array. */ + tokens[elements] = sdsnewlen(s+start,len-start); + if (tokens[elements] == NULL) goto cleanup; + elements++; + *count = elements; + return tokens; + +cleanup: + { + int i; + for (i = 0; i < elements; i++) sdsfree(tokens[i]); + s_free(tokens); + *count = 0; + return NULL; + } +} + +/* Free the result returned by sdssplitlen(), or do nothing if 'tokens' is NULL. */ +void sdsfreesplitres(sds *tokens, int count) { + if (!tokens) return; + while(count--) + sdsfree(tokens[count]); + s_free(tokens); +} + +/* Append to the sds string "s" an escaped string representation where + * all the non-printable characters (tested with isprint()) are turned into + * escapes in the form "\n\r\a...." or "\x". + * + * After the call, the modified sds string is no longer valid and all the + * references must be substituted with the new pointer returned by the call. */ +sds sdscatrepr(sds s, const char *p, size_t len) { + s = sdscatlen(s,"\"",1); + while(len--) { + switch(*p) { + case '\\': + case '"': + s = sdscatprintf(s,"\\%c",*p); + break; + case '\n': s = sdscatlen(s,"\\n",2); break; + case '\r': s = sdscatlen(s,"\\r",2); break; + case '\t': s = sdscatlen(s,"\\t",2); break; + case '\a': s = sdscatlen(s,"\\a",2); break; + case '\b': s = sdscatlen(s,"\\b",2); break; + default: + if (isprint(*p)) + s = sdscatprintf(s,"%c",*p); + else + s = sdscatprintf(s,"\\x%02x",(unsigned char)*p); + break; + } + p++; + } + return sdscatlen(s,"\"",1); +} + +/* Helper function for sdssplitargs() that returns non zero if 'c' + * is a valid hex digit. */ +int is_hex_digit(char c) { + return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || + (c >= 'A' && c <= 'F'); +} + +/* Helper function for sdssplitargs() that converts a hex digit into an + * integer from 0 to 15 */ +int hex_digit_to_int(char c) { + switch(c) { + case '0': return 0; + case '1': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + case 'a': case 'A': return 10; + case 'b': case 'B': return 11; + case 'c': case 'C': return 12; + case 'd': case 'D': return 13; + case 'e': case 'E': return 14; + case 'f': case 'F': return 15; + default: return 0; + } +} + +/* Split a line into arguments, where every argument can be in the + * following programming-language REPL-alike form: + * + * foo bar "newline are supported\n" and "\xff\x00otherstuff" + * + * The number of arguments is stored into *argc, and an array + * of sds is returned. + * + * The caller should free the resulting array of sds strings with + * sdsfreesplitres(). + * + * Note that sdscatrepr() is able to convert back a string into + * a quoted string in the same format sdssplitargs() is able to parse. + * + * The function returns the allocated tokens on success, even when the + * input string is empty, or NULL if the input contains unbalanced + * quotes or closed quotes followed by non space characters + * as in: "foo"bar or "foo' + */ +sds *sdssplitargs(const char *line, int *argc) { + const char *p = line; + char *current = NULL; + char **vector = NULL; + + *argc = 0; + while(1) { + /* skip blanks */ + while(*p && isspace(*p)) p++; + if (*p) { + /* get a token */ + int inq=0; /* set to 1 if we are in "quotes" */ + int insq=0; /* set to 1 if we are in 'single quotes' */ + int done=0; + + if (current == NULL) current = sdsempty(); + while(!done) { + if (inq) { + if (*p == '\\' && *(p+1) == 'x' && + is_hex_digit(*(p+2)) && + is_hex_digit(*(p+3))) + { + unsigned char byte; + + byte = (hex_digit_to_int(*(p+2))*16)+ + hex_digit_to_int(*(p+3)); + current = sdscatlen(current,(char*)&byte,1); + p += 3; + } else if (*p == '\\' && *(p+1)) { + char c; + + p++; + switch(*p) { + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + case 'b': c = '\b'; break; + case 'a': c = '\a'; break; + default: c = *p; break; + } + current = sdscatlen(current,&c,1); + } else if (*p == '"') { + /* closing quote must be followed by a space or + * nothing at all. */ + if (*(p+1) && !isspace(*(p+1))) goto err; + done=1; + } else if (!*p) { + /* unterminated quotes */ + goto err; + } else { + current = sdscatlen(current,p,1); + } + } else if (insq) { + if (*p == '\\' && *(p+1) == '\'') { + p++; + current = sdscatlen(current,"'",1); + } else if (*p == '\'') { + /* closing quote must be followed by a space or + * nothing at all. */ + if (*(p+1) && !isspace(*(p+1))) goto err; + done=1; + } else if (!*p) { + /* unterminated quotes */ + goto err; + } else { + current = sdscatlen(current,p,1); + } + } else { + switch(*p) { + case ' ': + case '\n': + case '\r': + case '\t': + case '\0': + done=1; + break; + case '"': + inq=1; + break; + case '\'': + insq=1; + break; + default: + current = sdscatlen(current,p,1); + break; + } + } + if (*p) p++; + } + /* add the token to the vector */ + vector = s_realloc(vector,((*argc)+1)*sizeof(char*)); + vector[*argc] = current; + (*argc)++; + current = NULL; + } else { + /* Even on empty input string return something not NULL. */ + if (vector == NULL) vector = s_malloc(sizeof(void*)); + return vector; + } + } + +err: + while((*argc)--) + sdsfree(vector[*argc]); + s_free(vector); + if (current) sdsfree(current); + *argc = 0; + return NULL; +} + +/* Modify the string substituting all the occurrences of the set of + * characters specified in the 'from' string to the corresponding character + * in the 'to' array. + * + * For instance: sdsmapchars(mystring, "ho", "01", 2) + * will have the effect of turning the string "hello" into "0ell1". + * + * The function returns the sds string pointer, that is always the same + * as the input pointer since no resize is needed. */ +sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) { + size_t j, i, l = sdslen(s); + + for (j = 0; j < l; j++) { + for (i = 0; i < setlen; i++) { + if (s[j] == from[i]) { + s[j] = to[i]; + break; + } + } + } + return s; +} + +/* Join an array of C strings using the specified separator (also a C string). + * Returns the result as an sds string. */ +sds sdsjoin(char **argv, int argc, char *sep) { + sds join = sdsempty(); + int j; + + for (j = 0; j < argc; j++) { + join = sdscat(join, argv[j]); + if (j != argc-1) join = sdscat(join,sep); + } + return join; +} + +/* Like sdsjoin, but joins an array of SDS strings. */ +sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen) { + sds join = sdsempty(); + int j; + + for (j = 0; j < argc; j++) { + join = sdscatsds(join, argv[j]); + if (j != argc-1) join = sdscatlen(join,sep,seplen); + } + return join; +} + +/* Wrappers to the allocators used by SDS. Note that SDS will actually + * just use the macros defined into sdsalloc.h in order to avoid to pay + * the overhead of function calls. Here we define these wrappers only for + * the programs SDS is linked to, if they want to touch the SDS internals + * even if they use a different allocator. */ +void *sds_malloc(size_t size) { return s_malloc(size); } +void *sds_realloc(void *ptr, size_t size) { return s_realloc(ptr,size); } +void sds_free(void *ptr) { s_free(ptr); } + +#if defined(SDS_TEST_MAIN) +#include +#include "testhelp.h" +#include "limits.h" + +#define UNUSED(x) (void)(x) +int sdsTest(void) { + { + sds x = sdsnew("foo"), y; + + test_cond("Create a string and obtain the length", + sdslen(x) == 3 && memcmp(x,"foo\0",4) == 0) + + sdsfree(x); + x = sdsnewlen("foo",2); + test_cond("Create a string with specified length", + sdslen(x) == 2 && memcmp(x,"fo\0",3) == 0) + + x = sdscat(x,"bar"); + test_cond("Strings concatenation", + sdslen(x) == 5 && memcmp(x,"fobar\0",6) == 0); + + x = sdscpy(x,"a"); + test_cond("sdscpy() against an originally longer string", + sdslen(x) == 1 && memcmp(x,"a\0",2) == 0) + + x = sdscpy(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk"); + test_cond("sdscpy() against an originally shorter string", + sdslen(x) == 33 && + memcmp(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0",33) == 0) + + sdsfree(x); + x = sdscatprintf(sdsempty(),"%d",123); + test_cond("sdscatprintf() seems working in the base case", + sdslen(x) == 3 && memcmp(x,"123\0",4) == 0) + + sdsfree(x); + x = sdsnew("--"); + x = sdscatfmt(x, "Hello %s World %I,%I--", "Hi!", LLONG_MIN,LLONG_MAX); + test_cond("sdscatfmt() seems working in the base case", + sdslen(x) == 60 && + memcmp(x,"--Hello Hi! World -9223372036854775808," + "9223372036854775807--",60) == 0) + printf("[%s]\n",x); + + sdsfree(x); + x = sdsnew("--"); + x = sdscatfmt(x, "%u,%U--", UINT_MAX, ULLONG_MAX); + test_cond("sdscatfmt() seems working with unsigned numbers", + sdslen(x) == 35 && + memcmp(x,"--4294967295,18446744073709551615--",35) == 0) + + sdsfree(x); + x = sdsnew(" x "); + sdstrim(x," x"); + test_cond("sdstrim() works when all chars match", + sdslen(x) == 0) + + sdsfree(x); + x = sdsnew(" x "); + sdstrim(x," "); + test_cond("sdstrim() works when a single char remains", + sdslen(x) == 1 && x[0] == 'x') + + sdsfree(x); + x = sdsnew("xxciaoyyy"); + sdstrim(x,"xy"); + test_cond("sdstrim() correctly trims characters", + sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0) + + y = sdsdup(x); + sdsrange(y,1,1); + test_cond("sdsrange(...,1,1)", + sdslen(y) == 1 && memcmp(y,"i\0",2) == 0) + + sdsfree(y); + y = sdsdup(x); + sdsrange(y,1,-1); + test_cond("sdsrange(...,1,-1)", + sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0) + + sdsfree(y); + y = sdsdup(x); + sdsrange(y,-2,-1); + test_cond("sdsrange(...,-2,-1)", + sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0) + + sdsfree(y); + y = sdsdup(x); + sdsrange(y,2,1); + test_cond("sdsrange(...,2,1)", + sdslen(y) == 0 && memcmp(y,"\0",1) == 0) + + sdsfree(y); + y = sdsdup(x); + sdsrange(y,1,100); + test_cond("sdsrange(...,1,100)", + sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0) + + sdsfree(y); + y = sdsdup(x); + sdsrange(y,100,100); + test_cond("sdsrange(...,100,100)", + sdslen(y) == 0 && memcmp(y,"\0",1) == 0) + + sdsfree(y); + sdsfree(x); + x = sdsnew("foo"); + y = sdsnew("foa"); + test_cond("sdscmp(foo,foa)", sdscmp(x,y) > 0) + + sdsfree(y); + sdsfree(x); + x = sdsnew("bar"); + y = sdsnew("bar"); + test_cond("sdscmp(bar,bar)", sdscmp(x,y) == 0) + + sdsfree(y); + sdsfree(x); + x = sdsnew("aar"); + y = sdsnew("bar"); + test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0) + + sdsfree(y); + sdsfree(x); + x = sdsnewlen("\a\n\0foo\r",7); + y = sdscatrepr(sdsempty(),x,sdslen(x)); + test_cond("sdscatrepr(...data...)", + memcmp(y,"\"\\a\\n\\x00foo\\r\"",15) == 0) + + { + unsigned int oldfree; + char *p; + int step = 10, j, i; + + sdsfree(x); + sdsfree(y); + x = sdsnew("0"); + test_cond("sdsnew() free/len buffers", sdslen(x) == 1 && sdsavail(x) == 0); + + /* Run the test a few times in order to hit the first two + * SDS header types. */ + for (i = 0; i < 10; i++) { + int oldlen = sdslen(x); + x = sdsMakeRoomFor(x,step); + int type = x[-1]&SDS_TYPE_MASK; + + test_cond("sdsMakeRoomFor() len", sdslen(x) == oldlen); + if (type != SDS_TYPE_5) { + test_cond("sdsMakeRoomFor() free", sdsavail(x) >= step); + oldfree = sdsavail(x); + } + p = x+oldlen; + for (j = 0; j < step; j++) { + p[j] = 'A'+j; + } + sdsIncrLen(x,step); + } + test_cond("sdsMakeRoomFor() content", + memcmp("0ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJ",x,101) == 0); + test_cond("sdsMakeRoomFor() final length",sdslen(x)==101); + + sdsfree(x); + } + } + test_report() + return 0; +} +#endif + +#ifdef SDS_TEST_MAIN +int main(void) { + return sdsTest(); +} +#endif diff --git a/rmutil/sds.h b/rmutil/sds.h new file mode 100644 index 0000000..394f8b5 --- /dev/null +++ b/rmutil/sds.h @@ -0,0 +1,273 @@ +/* SDSLib 2.0 -- A C dynamic strings library + * + * Copyright (c) 2006-2015, Salvatore Sanfilippo + * Copyright (c) 2015, Oran Agra + * Copyright (c) 2015, Redis Labs, Inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __SDS_H +#define __SDS_H + +#define SDS_MAX_PREALLOC (1024*1024) + +#include +#include +#include + +typedef char *sds; + +/* Note: sdshdr5 is never used, we just access the flags byte directly. + * However is here to document the layout of type 5 SDS strings. */ +struct __attribute__ ((__packed__)) sdshdr5 { + unsigned char flags; /* 3 lsb of type, and 5 msb of string length */ + char buf[]; +}; +struct __attribute__ ((__packed__)) sdshdr8 { + uint8_t len; /* used */ + uint8_t alloc; /* excluding the header and null terminator */ + unsigned char flags; /* 3 lsb of type, 5 unused bits */ + char buf[]; +}; +struct __attribute__ ((__packed__)) sdshdr16 { + uint16_t len; /* used */ + uint16_t alloc; /* excluding the header and null terminator */ + unsigned char flags; /* 3 lsb of type, 5 unused bits */ + char buf[]; +}; +struct __attribute__ ((__packed__)) sdshdr32 { + uint32_t len; /* used */ + uint32_t alloc; /* excluding the header and null terminator */ + unsigned char flags; /* 3 lsb of type, 5 unused bits */ + char buf[]; +}; +struct __attribute__ ((__packed__)) sdshdr64 { + uint64_t len; /* used */ + uint64_t alloc; /* excluding the header and null terminator */ + unsigned char flags; /* 3 lsb of type, 5 unused bits */ + char buf[]; +}; + +#define SDS_TYPE_5 0 +#define SDS_TYPE_8 1 +#define SDS_TYPE_16 2 +#define SDS_TYPE_32 3 +#define SDS_TYPE_64 4 +#define SDS_TYPE_MASK 7 +#define SDS_TYPE_BITS 3 +#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (void*)((s)-(sizeof(struct sdshdr##T))); +#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T)))) +#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS) + +static inline size_t sdslen(const sds s) { + unsigned char flags = s[-1]; + switch(flags&SDS_TYPE_MASK) { + case SDS_TYPE_5: + return SDS_TYPE_5_LEN(flags); + case SDS_TYPE_8: + return SDS_HDR(8,s)->len; + case SDS_TYPE_16: + return SDS_HDR(16,s)->len; + case SDS_TYPE_32: + return SDS_HDR(32,s)->len; + case SDS_TYPE_64: + return SDS_HDR(64,s)->len; + } + return 0; +} + +static inline size_t sdsavail(const sds s) { + unsigned char flags = s[-1]; + switch(flags&SDS_TYPE_MASK) { + case SDS_TYPE_5: { + return 0; + } + case SDS_TYPE_8: { + SDS_HDR_VAR(8,s); + return sh->alloc - sh->len; + } + case SDS_TYPE_16: { + SDS_HDR_VAR(16,s); + return sh->alloc - sh->len; + } + case SDS_TYPE_32: { + SDS_HDR_VAR(32,s); + return sh->alloc - sh->len; + } + case SDS_TYPE_64: { + SDS_HDR_VAR(64,s); + return sh->alloc - sh->len; + } + } + return 0; +} + +static inline void sdssetlen(sds s, size_t newlen) { + unsigned char flags = s[-1]; + switch(flags&SDS_TYPE_MASK) { + case SDS_TYPE_5: + { + unsigned char *fp = ((unsigned char*)s)-1; + *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS); + } + break; + case SDS_TYPE_8: + SDS_HDR(8,s)->len = newlen; + break; + case SDS_TYPE_16: + SDS_HDR(16,s)->len = newlen; + break; + case SDS_TYPE_32: + SDS_HDR(32,s)->len = newlen; + break; + case SDS_TYPE_64: + SDS_HDR(64,s)->len = newlen; + break; + } +} + +static inline void sdsinclen(sds s, size_t inc) { + unsigned char flags = s[-1]; + switch(flags&SDS_TYPE_MASK) { + case SDS_TYPE_5: + { + unsigned char *fp = ((unsigned char*)s)-1; + unsigned char newlen = SDS_TYPE_5_LEN(flags)+inc; + *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS); + } + break; + case SDS_TYPE_8: + SDS_HDR(8,s)->len += inc; + break; + case SDS_TYPE_16: + SDS_HDR(16,s)->len += inc; + break; + case SDS_TYPE_32: + SDS_HDR(32,s)->len += inc; + break; + case SDS_TYPE_64: + SDS_HDR(64,s)->len += inc; + break; + } +} + +/* sdsalloc() = sdsavail() + sdslen() */ +static inline size_t sdsalloc(const sds s) { + unsigned char flags = s[-1]; + switch(flags&SDS_TYPE_MASK) { + case SDS_TYPE_5: + return SDS_TYPE_5_LEN(flags); + case SDS_TYPE_8: + return SDS_HDR(8,s)->alloc; + case SDS_TYPE_16: + return SDS_HDR(16,s)->alloc; + case SDS_TYPE_32: + return SDS_HDR(32,s)->alloc; + case SDS_TYPE_64: + return SDS_HDR(64,s)->alloc; + } + return 0; +} + +static inline void sdssetalloc(sds s, size_t newlen) { + unsigned char flags = s[-1]; + switch(flags&SDS_TYPE_MASK) { + case SDS_TYPE_5: + /* Nothing to do, this type has no total allocation info. */ + break; + case SDS_TYPE_8: + SDS_HDR(8,s)->alloc = newlen; + break; + case SDS_TYPE_16: + SDS_HDR(16,s)->alloc = newlen; + break; + case SDS_TYPE_32: + SDS_HDR(32,s)->alloc = newlen; + break; + case SDS_TYPE_64: + SDS_HDR(64,s)->alloc = newlen; + break; + } +} + +sds sdsnewlen(const void *init, size_t initlen); +sds sdsnew(const char *init); +sds sdsempty(void); +sds sdsdup(const sds s); +void sdsfree(sds s); +sds sdsgrowzero(sds s, size_t len); +sds sdscatlen(sds s, const void *t, size_t len); +sds sdscat(sds s, const char *t); +sds sdscatsds(sds s, const sds t); +sds sdscpylen(sds s, const char *t, size_t len); +sds sdscpy(sds s, const char *t); + +sds sdscatvprintf(sds s, const char *fmt, va_list ap); +#ifdef __GNUC__ +sds sdscatprintf(sds s, const char *fmt, ...) + __attribute__((format(printf, 2, 3))); +#else +sds sdscatprintf(sds s, const char *fmt, ...); +#endif + +sds sdscatfmt(sds s, char const *fmt, ...); +sds sdstrim(sds s, const char *cset); +void sdsrange(sds s, int start, int end); +void sdsupdatelen(sds s); +void sdsclear(sds s); +int sdscmp(const sds s1, const sds s2); +sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count); +void sdsfreesplitres(sds *tokens, int count); +void sdstolower(sds s); +void sdstoupper(sds s); +sds sdsfromlonglong(long long value); +sds sdscatrepr(sds s, const char *p, size_t len); +sds *sdssplitargs(const char *line, int *argc); +sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen); +sds sdsjoin(char **argv, int argc, char *sep); +sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen); + +/* Low level functions exposed to the user API */ +sds sdsMakeRoomFor(sds s, size_t addlen); +void sdsIncrLen(sds s, int incr); +sds sdsRemoveFreeSpace(sds s); +size_t sdsAllocSize(sds s); +void *sdsAllocPtr(sds s); + +/* Export the allocator used by SDS to the program using SDS. + * Sometimes the program SDS is linked to, may use a different set of + * allocators, but may want to allocate or free things that SDS will + * respectively free or allocate. */ +void *sds_malloc(size_t size); +void *sds_realloc(void *ptr, size_t size); +void sds_free(void *ptr); + +#ifdef REDIS_TEST +int sdsTest(int argc, char *argv[]); +#endif + +#endif diff --git a/rmutil/sds.o b/rmutil/sds.o new file mode 100644 index 0000000000000000000000000000000000000000..41d2f1f9db1e53b290d9bedd56bd1d54dd9608bf GIT binary patch literal 27992 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 +#include +#include +#include "strings.h" + + +#include "sds.h" + +RedisModuleString *RMUtils_CreateFormattedString(RedisModuleCtx *ctx, const char *fmt, ...) { + sds s = sdsempty(); + + va_list ap; + va_start(ap, fmt); + s = sdscatvprintf(s, fmt, ap); + va_end(ap); + + RedisModuleString *ret = RedisModule_CreateString(ctx, (const char *)s, sdslen(s)); + sdsfree(s); + return ret; +} + +int RMUtils_StringEquals(RedisModuleString *s1, RedisModuleString *s2) { + + const char *c1, *c2; + size_t l1, l2; + c1 = RedisModule_StringPtrLen(s1, &l1); + c2 = RedisModule_StringPtrLen(s2, &l2); + + return strncasecmp(c1, c2, MIN(l1,l2)) == 0; +} + +void RMUtils_StringToLower(RedisModuleString *s) { + + size_t l; + char *c = (char *)RedisModule_StringPtrLen(s, &l); + size_t i; + for (i = 0; i < l; i++) { + *c = tolower(*c); + ++c; + } + +} + +void RMUtils_StringToUpper(RedisModuleString *s) { + size_t l; + char *c = (char *)RedisModule_StringPtrLen(s, &l); + size_t i; + for (i = 0; i < l; i++) { + *c = toupper(*c); + ++c; + } + +} diff --git a/rmutil/strings.h b/rmutil/strings.h new file mode 100644 index 0000000..3df87ce --- /dev/null +++ b/rmutil/strings.h @@ -0,0 +1,22 @@ +#ifndef __RMUTIL_STRINGS_H__ +#define __RMUTIL_STRINGS_H__ + +#include + +/* +* Create a new RedisModuleString object from a printf-style format and arguments. +* Note that RedisModuleString objects CANNOT be used as formatting arguments. +*/ +RedisModuleString *RMUtils_CreateFormattedString(RedisModuleCtx *ctx, const char *fmt, ...); + +/* Return 1 if the two strings are equal. Case *sensitive* */ +int RMUtils_StringEquals(RedisModuleString *s1, RedisModuleString *s2); + +/* Converts a redis string to lowercase in place without reallocating anything */ +void RMUtils_StringToLower(RedisModuleString *s); + +/* Converts a redis string to uppercase in place without reallocating anything */ +void RMUtils_StringToUpper(RedisModuleString *s); + + +#endif diff --git a/rmutil/strings.o b/rmutil/strings.o new file mode 100644 index 0000000000000000000000000000000000000000..f75e3eb88f8bb7f63f4e19b0fe47fde7992f3f74 GIT binary patch literal 14312 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 +#include +#include +#include +#include +#include +#include +#include "util.h" + +/** +Check if an argument exists in an argument list (argv,argc), starting at offset. +@return 0 if it doesn't exist, otherwise the offset it exists in +*/ +int RMUtil_ArgExists(const char *arg, RedisModuleString **argv, int argc, int offset) { + + for (; offset < argc; offset++) { + size_t l; + const char *carg = RedisModule_StringPtrLen(argv[offset], &l); + if (carg != NULL && strncasecmp(carg, arg, l) == 0) { + return offset; + } + } + return 0; +} + +RMUtilInfo *RMUtil_GetRedisInfo(RedisModuleCtx *ctx) { + + RedisModuleCallReply *r = RedisModule_Call(ctx, "INFO", "c", "all"); + if (r == NULL || RedisModule_CallReplyType(r) == REDISMODULE_REPLY_ERROR) { + return NULL; + } + + + int cap = 100; // rough estimate of info lines + RMUtilInfo *info = malloc(sizeof(RMUtilInfo)); + info->entries = calloc(cap, sizeof(RMUtilInfoEntry)); + + int i = 0; + char *text = (char *)RedisModule_StringPtrLen(RedisModule_CreateStringFromCallReply(r), NULL); + char *line = text; + while (line) { + char *line = strsep(&text, "\r\n"); + if (line == NULL) break; + + if (!(*line >= 'a' && *line <= 'z')) { //skip non entry lines + continue; + } + + char *key = strsep(&line, ":"); + info->entries[i].key = key; + info->entries[i].val = line; + printf("Got info '%s' = '%s'\n", key, line); + i++; + if (i >= cap) { + cap *=2; + info->entries = realloc(info->entries, cap*sizeof(RMUtilInfoEntry)); + } + } + info->numEntries = i; + + return info; + +} +void RMUtilRedisInfo_Free(RMUtilInfo *info) { + + free(info->entries); + free(info); + +} + +int RMUtilInfo_GetInt(RMUtilInfo *info, const char *key, long long *val) { + + const char *p = NULL; + if (!RMUtilInfo_GetString(info, key, &p)) { + return 0; + } + + *val = strtoll(p, NULL, 10); + if ((errno == ERANGE && (*val == LONG_MAX || *val == LONG_MIN)) || + (errno != 0 && *val == 0)) { + *val = -1; + return 0; + } + + + return 1; +} + + +int RMUtilInfo_GetString(RMUtilInfo *info, const char *key, const char **str) { + int i; + for (i = 0; i < info->numEntries; i++) { + if (!strcmp(key, info->entries[i].key)) { + *str = info->entries[i].val; + return 1; + } + } + return 0; +} + +int RMUtilInfo_GetDouble(RMUtilInfo *info, const char *key, double *d) { + const char *p = NULL; + if (!RMUtilInfo_GetString(info, key, &p)) { + printf("not found %s\n", key); + return 0; + } + + *d = strtod(p, NULL); + printf("p: %s, d: %f\n",p,*d); + if ((errno == ERANGE && (*d == HUGE_VAL || *d == -HUGE_VAL)) || + (errno != 0 && *d == 0)) { + return 0; + } + + + return 1; +} diff --git a/rmutil/util.h b/rmutil/util.h new file mode 100644 index 0000000..6c07709 --- /dev/null +++ b/rmutil/util.h @@ -0,0 +1,67 @@ +#ifndef __UTIL_H__ +#define __UTIL_H__ + +#include + +// make sure the response is not NULL or an error, and if it is sends the error to the client and exit the current function +#define REDIS_ASSERT_NOERROR(r) \ + if (r == NULL) { \ + return RedisModule_ReplyWithError(ctx,"ERR reply is NULL"); \ + } else if (RedisModule_CallReplyType(r) == REDISMODULE_REPLY_ERROR) { \ + RedisModule_ReplyWithCallReply(ctx,r); \ + return REDISMODULE_ERR; \ + } + + +/* RedisModule utilities. */ + +/* Return the offset of an arg if it exists in the arg list, or 0 if it's not there */ +int RMUtil_ArgExists(const char *arg, RedisModuleString **argv, int argc, int offset); + +// NOT IMPLEMENTED YET +int RMUtil_ParseArgs(RedisModuleString **argv, int argc, const char *fmt, ...); + +// A single key/value entry in a redis info map +typedef struct { + const char *key; + const char *val; +} RMUtilInfoEntry; + +// Representation of INFO command response, as a list of k/v pairs +typedef struct { + RMUtilInfoEntry *entries; + int numEntries; +} RMUtilInfo; + +/* +* Get redis INFO result and parse it as RMUtilInfo. +* Returns NULL if something goes wrong. +* The resulting object needs to be freed with RMUtilRedisInfo_Free +*/ +RMUtilInfo *RMUtil_GetRedisInfo(RedisModuleCtx *ctx); + +/* +* Free an RMUtilInfo object and its entries +*/ +void RMUtilRedisInfo_Free(RMUtilInfo *info); + +/** +* Get an integer value from an info object. Returns 1 if the value was found and +* is an integer, 0 otherwise. the value is placed in 'val' +*/ +int RMUtilInfo_GetInt(RMUtilInfo *info, const char *key, long long *val); + +/* +* Get a string value from an info object. The value is placed in str. +* Returns 1 if the key was found, 0 if not +*/ +int RMUtilInfo_GetString(RMUtilInfo *info, const char *key, const char **str); + +/* +* Get a double value from an info object. Returns 1 if the value was found and is +* a correctly formatted double, 0 otherwise. the value is placed in 'd' +*/ +int RMUtilInfo_GetDouble(RMUtilInfo *info, const char *key, double *d); + + +#endif diff --git a/rmutil/util.o b/rmutil/util.o new file mode 100644 index 0000000000000000000000000000000000000000..4c0cb297883e8e50b89dd05ed7fb6d75de8af54a GIT binary patch literal 15480 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 ap_r0j`?##b+F!w&FKJ$16_XCZ_Wue1h&3_* literal 0 HcmV?d00001