Sources/aliyun-log-c-sdk/log_sds.c (227 lines of code) (raw):

/* SDSLib, A C dynamic strings library * * Copyright (c) 2006-2012, Salvatore Sanfilippo <antirez at gmail dot com> * 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 <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <assert.h> #include "log_sds.h" size_t log_sdslen(const log_sds s) { struct log_sdshdr *sh = (struct log_sdshdr *) (s - (sizeof(struct log_sdshdr))); return sh->len; } size_t log_sdsavail(const log_sds s) { struct log_sdshdr *sh = (struct log_sdshdr *) (s - (sizeof(struct log_sdshdr))); return sh->free; } /* Create a new log_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 log_sds strings are, always) so * even if you create an log_sds string with: * * mystring = log_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 log_sds header. */ log_sds log_sdsnewlen(const void *init, size_t initlen) { struct log_sdshdr *sh; if (init) { sh = malloc(sizeof(struct log_sdshdr) + initlen + 1); } else { sh = calloc(sizeof(struct log_sdshdr) + initlen + 1, 1); } if (sh == NULL) return NULL; sh->len = initlen; sh->free = 0; if (initlen && init) memcpy(sh->buf, init, initlen); sh->buf[initlen] = '\0'; return (char *) sh->buf; } log_sds log_sdsnewEmpty(size_t preAlloclen) { struct log_sdshdr *sh; sh = malloc(sizeof(struct log_sdshdr) + preAlloclen + 1); if (sh == NULL) return NULL; sh->len = 0; sh->free = preAlloclen; sh->buf[0] = '\0'; return (char *) sh->buf; } /* Create an empty (zero length) log_sds string. Even in this case the string * always has an implicit null term. */ log_sds log_sdsempty(void) { return log_sdsnewlen("", 0); } /* Create a new log_sds string starting from a null terminated C string. */ log_sds log_sdsnew(const char *init) { size_t initlen = (init == NULL) ? 0 : strlen(init); return log_sdsnewlen(init, initlen); } /* Duplicate an log_sds string. */ log_sds log_sdsdup(const log_sds s) { if (s == NULL) return NULL; return log_sdsnewlen(s, log_sdslen(s)); } /* Free an log_sds string. No operation is performed if 's' is NULL. */ void log_sdsfree(log_sds s) { if (s == NULL) return; free(s - sizeof(struct log_sdshdr)); } /* Set the log_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 log_sds string is hacked manually in some * way, like in the following example: * * s = log_sdsnew("foobar"); * s[2] = '\0'; * log_sdsupdatelen(s); * printf("%d\n", log_sdslen(s)); * * The output will be "2", but if we comment out the call to log_sdsupdatelen() * the output will be "6" as the string was modified but the logical length * remains 6 bytes. */ void log_sdsupdatelen(log_sds s) { struct log_sdshdr *sh = (void *) (s - (sizeof(struct log_sdshdr))); int reallen = strlen(s); sh->free += (sh->len - reallen); sh->len = reallen; } /* Modify an log_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 log_sdsclear(log_sds s) { struct log_sdshdr *sh = (void *) (s - (sizeof(struct log_sdshdr))); sh->free += sh->len; sh->len = 0; sh->buf[0] = '\0'; } /* Enlarge the free space at the end of the log_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 log_sds string as returned * by log_sdslen(), but only the free buffer space we have. */ log_sds log_sdsMakeRoomFor(log_sds s, size_t addlen) { struct log_sdshdr *sh, *newsh; size_t free = log_sdsavail(s); size_t len, newlen; if (free >= addlen) return s; len = log_sdslen(s); sh = (void *) (s - (sizeof(struct log_sdshdr))); newlen = (len + addlen); if (newlen < LOG_SDS_MAX_PREALLOC) newlen *= 2; else newlen += LOG_SDS_MAX_PREALLOC; newsh = realloc(sh, sizeof(struct log_sdshdr) + newlen + 1); if (newsh == NULL) return NULL; newsh->free = newlen - len; return newsh->buf; } /* Reallocate the log_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 log_sds string is no longer valid and all the * references must be substituted with the new pointer returned by the call. */ log_sds log_sdsRemoveFreeSpace(log_sds s) { struct log_sdshdr *sh; sh = (void *) (s - (sizeof(struct log_sdshdr))); sh = realloc(sh, sizeof(struct log_sdshdr) + sh->len + 1); sh->free = 0; return sh->buf; } /* Return the total size of the allocation of the specifed log_sds string, * including: * 1) The log_sds header before the pointer. * 2) The string. * 3) The free buffer at the end if any. * 4) The implicit null term. */ size_t log_sdsAllocSize(log_sds s) { struct log_sdshdr *sh = (void *) (s - (sizeof(struct log_sdshdr))); return sizeof(*sh) + sh->len + sh->free + 1; } /* Increment the log_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 log_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 log_sdsIncrLen() and log_sdsMakeRoomFor() it is possible to mount the * following schema, to cat bytes coming from the kernel to the end of an * log_sds string without copying into an intermediate buffer: * * oldlen = log_sdslen(s); * s = log_sdsMakeRoomFor(s, BUFFER_SIZE); * nread = read(fd, s+oldlen, BUFFER_SIZE); * ... check for nread <= 0 and handle it ... * log_sdsIncrLen(s, nread); */ void log_sdsIncrLen(log_sds s, int incr) { struct log_sdshdr *sh = (void *) (s - (sizeof(struct log_sdshdr))); if (incr >= 0) assert(sh->free >= (unsigned int) incr); else assert(sh->len >= (unsigned int) (-incr)); sh->len += incr; sh->free -= incr; s[sh->len] = '\0'; } /* Grow the log_sds to have the specified length. Bytes that were not part of * the original length of the log_sds will be set to zero. * * if the specified length is smaller than the current length, no operation * is performed. */ log_sds log_sdsgrowzero(log_sds s, size_t len) { struct log_sdshdr *sh = (void *) (s - (sizeof(struct log_sdshdr))); size_t totlen, curlen = sh->len; if (len <= curlen) return s; s = log_sdsMakeRoomFor(s, len - curlen); if (s == NULL) return NULL; /* Make sure added region doesn't contain garbage */ sh = (void *) (s - (sizeof(struct log_sdshdr))); memset(s + curlen, 0, (len - curlen + 1)); /* also set trailing \0 byte */ totlen = sh->len + sh->free; sh->len = len; sh->free = totlen - sh->len; return s; } /* Append the specified binary-safe string pointed by 't' of 'len' bytes to the * end of the specified log_sds string 's'. * * After the call, the passed log_sds string is no longer valid and all the * references must be substituted with the new pointer returned by the call. */ log_sds log_sdscatlen(log_sds s, const void *t, size_t len) { struct log_sdshdr *sh; size_t curlen = log_sdslen(s); s = log_sdsMakeRoomFor(s, len); if (s == NULL) return NULL; sh = (void *) (s - (sizeof(struct log_sdshdr))); memcpy(s + curlen, t, len); sh->len = curlen + len; sh->free = sh->free - len; s[curlen + len] = '\0'; return s; } log_sds log_sdscatchar(log_sds s, char c) { struct log_sdshdr *sh; size_t curlen = log_sdslen(s); s = log_sdsMakeRoomFor(s, 1); if (s == NULL) return NULL; sh = (void *) (s - (sizeof(struct log_sdshdr))); s[curlen] = c; s[curlen + 1] = '\0'; ++sh->len; --sh->free; return s; } /* Append the specified null termianted C string to the log_sds string 's'. * * After the call, the passed log_sds string is no longer valid and all the * references must be substituted with the new pointer returned by the call. */ log_sds log_sdscat(log_sds s, const char *t) { if (s == NULL || t == NULL) { return s; } return log_sdscatlen(s, t, strlen(t)); } /* Append the specified log_sds 't' to the existing log_sds 's'. * * After the call, the modified log_sds string is no longer valid and all the * references must be substituted with the new pointer returned by the call. */ log_sds log_sdscatsds(log_sds s, const log_sds t) { return log_sdscatlen(s, t, log_sdslen(t)); } /* Destructively modify the log_sds string 's' to hold the specified binary * safe string pointed by 't' of length 'len' bytes. */ log_sds log_sdscpylen(log_sds s, const char *t, size_t len) { struct log_sdshdr *sh = (void *) (s - (sizeof(struct log_sdshdr))); size_t totlen = sh->free + sh->len; if (totlen < len) { s = log_sdsMakeRoomFor(s, len - sh->len); if (s == NULL) return NULL; sh = (void *) (s - (sizeof(struct log_sdshdr))); totlen = sh->free + sh->len; } memcpy(s, t, len); s[len] = '\0'; sh->len = len; sh->free = totlen - len; return s; } /* Like log_sdscpylen() but 't' must be a null-termined string so that the length * of the string is obtained with strlen(). */ log_sds log_sdscpy(log_sds s, const char *t) { return log_sdscpylen(s, t, strlen(t)); } /* Like log_sdscatprintf() but gets va_list instead of being variadic. */ log_sds log_sdscatvprintf(log_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 = 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) free(buf); buflen *= 2; buf = malloc(buflen); if (buf == NULL) return NULL; continue; } break; } /* Finally concat the obtained string to the SDS string and return it. */ t = log_sdscat(s, buf); if (buf != staticbuf) free(buf); return t; } /* Append to the log_sds string 's' a string obtained using printf-alike format * specifier. * * After the call, the modified log_sds string is no longer valid and all the * references must be substituted with the new pointer returned by the call. * * Example: * * s = log_sdsnew("Sum is: "); * s = log_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 log_sdsempty() as the target string: * * s = log_sdscatprintf(log_sdsempty(), "... your format ...", args); */ log_sds log_sdscatprintf(log_sds s, const char *fmt, ...) { va_list ap; char *t; va_start(ap, fmt); t = log_sdscatvprintf(s, fmt, ap); va_end(ap); return t; } /* Append to the log_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<hex-number>". * * After the call, the modified log_sds string is no longer valid and all the * references must be substituted with the new pointer returned by the call. */ log_sds log_sdscatrepr(log_sds s, const char *p, size_t len) { s = log_sdscatlen(s,"\"",1); while(len--) { switch(*p) { case '\\': case '"': s = log_sdscatprintf(s,"\\%c",*p); break; case '\n': s = log_sdscatlen(s,"\\n",2); break; case '\r': s = log_sdscatlen(s,"\\r",2); break; case '\t': s = log_sdscatlen(s,"\\t",2); break; case '\a': s = log_sdscatlen(s,"\\a",2); break; case '\b': s = log_sdscatlen(s,"\\b",2); break; default: if (isprint(*p)) s = log_sdscatprintf(s,"%c",*p); else s = log_sdscatprintf(s,"\\\\x%02x",(unsigned char)*p); break; } p++; } return log_sdscatlen(s,"\"",1); }