/* * twemproxy - A fast and lightweight proxy for memcached protocol. * Copyright (C) 2011 Twitter, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include /* * String (struct string) is a sequence of unsigned char objects terminated * by the null character '\0'. The length of the string is pre-computed and * made available explicitly as an additional field. This means that we don't * have to walk the entire character sequence until the null terminating * character everytime that the length of the String is requested * * The only way to create a String is to initialize it using, string_init() * and duplicate an existing String - string_duplicate() or copy an existing * raw sequence of character bytes - string_copy(). Such String's must be * freed using string_deinit() * * We can also create String as reference to raw string - string_set_raw() * or to text string - string_set_text() or string(). Such String don't have * to be freed. */ void string_init(struct string *str) { str->len = 0; str->data = NULL; } void string_deinit(struct string *str) { ASSERT((str->len == 0 && str->data == NULL) || (str->len != 0 && str->data != NULL)); if (str->data != NULL) { nc_free(str->data); string_init(str); } } bool string_empty(const struct string *str) { ASSERT((str->len == 0 && str->data == NULL) || (str->len != 0 && str->data != NULL)); return str->len == 0 ? true : false; } rstatus_t string_duplicate(struct string *dst, const struct string *src) { ASSERT(dst->len == 0 && dst->data == NULL); ASSERT(src->len != 0 && src->data != NULL); dst->data = nc_strndup(src->data, src->len + 1); if (dst->data == NULL) { return NC_ENOMEM; } dst->len = src->len; dst->data[dst->len] = '\0'; return NC_OK; } rstatus_t string_copy(struct string *dst, const uint8_t *src, uint32_t srclen) { ASSERT(dst->len == 0 && dst->data == NULL); ASSERT(src != NULL && srclen != 0); dst->data = nc_strndup(src, srclen + 1); if (dst->data == NULL) { return NC_ENOMEM; } dst->len = srclen; dst->data[dst->len] = '\0'; return NC_OK; } int string_compare(const struct string *s1, const struct string *s2) { if (s1->len != s2->len) { return s1->len > s2->len ? 1 : -1; } return nc_strncmp(s1->data, s2->data, s1->len); } static char * _safe_utoa(int _base, uint64_t val, char *buf) { char hex[] = "0123456789abcdef"; uint32_t base = (uint32_t) _base; *buf-- = 0; do { *buf-- = hex[val % base]; } while ((val /= base) != 0); return buf + 1; } static char * _safe_itoa(int base, int64_t val, char *buf) { char hex[] = "0123456789abcdef"; char *orig_buf = buf; const int32_t is_neg = (val < 0); *buf-- = 0; if (is_neg) { val = -val; } if (is_neg && base == 16) { int ix; val -= 1; for (ix = 0; ix < 16; ++ix) buf[-ix] = '0'; } do { *buf-- = hex[val % base]; } while ((val /= base) != 0); if (is_neg && base == 10) { *buf-- = '-'; } if (is_neg && base == 16) { int ix; buf = orig_buf - 1; for (ix = 0; ix < 16; ++ix, --buf) { /* *INDENT-OFF* */ switch (*buf) { case '0': *buf = 'f'; break; case '1': *buf = 'e'; break; case '2': *buf = 'd'; break; case '3': *buf = 'c'; break; case '4': *buf = 'b'; break; case '5': *buf = 'a'; break; case '6': *buf = '9'; break; case '7': *buf = '8'; break; case '8': *buf = '7'; break; case '9': *buf = '6'; break; case 'a': *buf = '5'; break; case 'b': *buf = '4'; break; case 'c': *buf = '3'; break; case 'd': *buf = '2'; break; case 'e': *buf = '1'; break; case 'f': *buf = '0'; break; } /* *INDENT-ON* */ } } return buf + 1; } static const char * _safe_check_longlong(const char *fmt, int32_t * have_longlong) { *have_longlong = false; if (*fmt == 'l') { fmt++; if (*fmt != 'l') { *have_longlong = (sizeof(long) == sizeof(int64_t)); } else { fmt++; *have_longlong = true; } } return fmt; } int _safe_vsnprintf(char *to, size_t size, const char *format, va_list ap) { char *start = to; char *end = start + size - 1; for (; *format; ++format) { int32_t have_longlong = false; if (*format != '%') { if (to == end) { /* end of buffer */ break; } *to++ = *format; /* copy ordinary char */ continue; } ++format; /* skip '%' */ format = _safe_check_longlong(format, &have_longlong); switch (*format) { case 'd': case 'i': case 'u': case 'x': case 'p': { int64_t ival = 0; uint64_t uval = 0; if (*format == 'p') have_longlong = (sizeof(void *) == sizeof(uint64_t)); if (have_longlong) { if (*format == 'u') { uval = va_arg(ap, uint64_t); } else { ival = va_arg(ap, int64_t); } } else { if (*format == 'u') { uval = va_arg(ap, uint32_t); } else { ival = va_arg(ap, int32_t); } } { char buff[22]; const int base = (*format == 'x' || *format == 'p') ? 16 : 10; /* *INDENT-OFF* */ char *val_as_str = (*format == 'u') ? _safe_utoa(base, uval, &buff[sizeof(buff) - 1]) : _safe_itoa(base, ival, &buff[sizeof(buff) - 1]); /* *INDENT-ON* */ /* Strip off "ffffffff" if we have 'x' format without 'll' */ if (*format == 'x' && !have_longlong && ival < 0) { val_as_str += 8; } while (*val_as_str && to < end) { *to++ = *val_as_str++; } continue; } } case 's': { const char *val = va_arg(ap, char *); if (!val) { val = "(null)"; } while (*val && to < end) { *to++ = *val++; } continue; } } } *to = 0; return (int)(to - start); } int _safe_snprintf(char *to, size_t n, const char *fmt, ...) { int result; va_list args; va_start(args, fmt); result = _safe_vsnprintf(to, n, fmt, args); va_end(args); return result; }