diff options
Diffstat (limited to 'examples/redis-unstable/src/util.c')
| -rw-r--r-- | examples/redis-unstable/src/util.c | 1774 |
1 files changed, 0 insertions, 1774 deletions
diff --git a/examples/redis-unstable/src/util.c b/examples/redis-unstable/src/util.c deleted file mode 100644 index ba3d9d0..0000000 --- a/examples/redis-unstable/src/util.c +++ /dev/null @@ -1,1774 +0,0 @@ -/* - * Copyright (c) 2009-current, Redis Ltd. - * Copyright (c) 2012, Twitter, 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 "fmacros.h" -#include "fpconv_dtoa.h" -#include "fast_float_strtod.h" -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <ctype.h> -#include <limits.h> -#include <math.h> -#include <unistd.h> -#include <sys/time.h> -#include <float.h> -#include <stdint.h> -#include <errno.h> -#include <time.h> -#include <sys/stat.h> -#include <dirent.h> -#include <fcntl.h> -#include <libgen.h> - -#include "util.h" -#include "sha256.h" -#include "config.h" - -#define UNUSED(x) ((void)(x)) - -/* Selectively define static_assert. Attempt to avoid include server.h in this file. */ -#ifndef static_assert -#define static_assert(expr, lit) extern char __static_assert_failure[(expr) ? 1:-1] -#endif - -static_assert(UINTPTR_MAX == 0xffffffffffffffff || UINTPTR_MAX == 0xffffffff, "Unsupported pointer size"); - -/* Glob-style pattern matching. */ -static int stringmatchlen_impl(const char *pattern, int patternLen, - const char *string, int stringLen, int nocase, int *skipLongerMatches, int nesting) -{ - /* Protection against abusive patterns. */ - if (nesting > 1000) return 0; - - while(patternLen && stringLen) { - switch(pattern[0]) { - case '*': - while (patternLen && pattern[1] == '*') { - pattern++; - patternLen--; - } - if (patternLen == 1) - return 1; /* match */ - while(stringLen) { - if (stringmatchlen_impl(pattern+1, patternLen-1, - string, stringLen, nocase, skipLongerMatches, nesting+1)) - return 1; /* match */ - if (*skipLongerMatches) - return 0; /* no match */ - string++; - stringLen--; - } - /* There was no match for the rest of the pattern starting - * from anywhere in the rest of the string. If there were - * any '*' earlier in the pattern, we can terminate the - * search early without trying to match them to longer - * substrings. This is because a longer match for the - * earlier part of the pattern would require the rest of the - * pattern to match starting later in the string, and we - * have just determined that there is no match for the rest - * of the pattern starting from anywhere in the current - * string. */ - *skipLongerMatches = 1; - return 0; /* no match */ - break; - case '?': - string++; - stringLen--; - break; - case '[': - { - int not, match; - - pattern++; - patternLen--; - not = patternLen && pattern[0] == '^'; - if (not) { - pattern++; - patternLen--; - } - match = 0; - while(1) { - if (patternLen >= 2 && pattern[0] == '\\') { - pattern++; - patternLen--; - if (pattern[0] == string[0]) - match = 1; - } else if (patternLen == 0) { - pattern--; - patternLen++; - break; - } else if (pattern[0] == ']') { - break; - } else if (patternLen >= 3 && pattern[1] == '-') { - int start = pattern[0]; - int end = pattern[2]; - int c = string[0]; - if (start > end) { - int t = start; - start = end; - end = t; - } - if (nocase) { - start = tolower(start); - end = tolower(end); - c = tolower(c); - } - pattern += 2; - patternLen -= 2; - if (c >= start && c <= end) - match = 1; - } else { - if (!nocase) { - if (pattern[0] == string[0]) - match = 1; - } else { - if (tolower((int)pattern[0]) == tolower((int)string[0])) - match = 1; - } - } - pattern++; - patternLen--; - } - if (not) - match = !match; - if (!match) - return 0; /* no match */ - string++; - stringLen--; - break; - } - case '\\': - if (patternLen >= 2) { - pattern++; - patternLen--; - } - /* fall through */ - default: - if (!nocase) { - if (pattern[0] != string[0]) - return 0; /* no match */ - } else { - if (tolower((int)pattern[0]) != tolower((int)string[0])) - return 0; /* no match */ - } - string++; - stringLen--; - break; - } - pattern++; - patternLen--; - if (stringLen == 0) { - while(patternLen && *pattern == '*') { - pattern++; - patternLen--; - } - break; - } - } - if (patternLen == 0 && stringLen == 0) - return 1; - return 0; -} - -/* - * glob-style pattern matching to check if a given pattern fully includes - * the prefix of a string. For the match to succeed, the pattern must end with - * an unescaped '*' character. - * - * Returns: 1 if the `pattern` fully matches the `prefixStr`. Returns 0 otherwise. - */ -int prefixmatch(const char *pattern, int patternLen, - const char *prefixStr, int prefixStrLen, int nocase) { - int skipLongerMatches = 0; - - /* Step 1: Verify if the pattern matches the prefix string completely. */ - if (!stringmatchlen_impl(pattern, patternLen, prefixStr, prefixStrLen, nocase, &skipLongerMatches, 0)) - return 0; - - /* Step 2: Verify that the pattern ends with an unescaped '*', indicating - * it can match any suffix of the string beyond the prefix. This check - * remains outside stringmatchlen_impl() to keep its complexity manageable. - */ - if (patternLen == 0 || pattern[patternLen - 1] != '*' ) - return 0; - - /* Count backward the number of consecutive backslashes preceding the '*' - * to determine if the '*' is escaped. */ - int backslashCount = 0; - for (int i = patternLen - 2; i >= 0; i--) { - if (pattern[i] == '\\') - ++backslashCount; - else - break; /* Stop counting when a non-backslash character is found. */ - } - - /* Return 1 if the '*' is not escaped (i.e., even count), 0 otherwise. */ - return (backslashCount % 2 == 0); -} - -/* Glob-style pattern matching to a string. */ -int stringmatchlen(const char *pattern, int patternLen, - const char *string, int stringLen, int nocase) { - int skipLongerMatches = 0; - return stringmatchlen_impl(pattern,patternLen,string,stringLen,nocase,&skipLongerMatches,0); -} - -int stringmatch(const char *pattern, const char *string, int nocase) { - return stringmatchlen(pattern,strlen(pattern),string,strlen(string),nocase); -} - -/* Fuzz stringmatchlen() trying to crash it with bad input. */ -int stringmatchlen_fuzz_test(void) { - char str[32]; - char pat[32]; - int cycles = 10000000; - int total_matches = 0; - while(cycles--) { - int strlen = rand() % sizeof(str); - int patlen = rand() % sizeof(pat); - for (int j = 0; j < strlen; j++) str[j] = rand() % 128; - for (int j = 0; j < patlen; j++) pat[j] = rand() % 128; - total_matches += stringmatchlen(pat, patlen, str, strlen, 0); - } - return total_matches; -} - - -/* Convert a string representing an amount of memory into the number of - * bytes, so for instance memtoull("1Gb") will return 1073741824 that is - * (1024*1024*1024). - * - * On parsing error, if *err is not NULL, it's set to 1, otherwise it's - * set to 0. On error the function return value is 0, regardless of the - * fact 'err' is NULL or not. */ -unsigned long long memtoull(const char *p, int *err) { - const char *u; - char buf[128]; - long mul; /* unit multiplier */ - unsigned long long val; - unsigned int digits; - - if (err) *err = 0; - - /* Search the first non digit character. */ - u = p; - if (*u == '-') { - if (err) *err = 1; - return 0; - } - while(*u && isdigit(*u)) u++; - if (*u == '\0' || !strcasecmp(u,"b")) { - mul = 1; - } else if (!strcasecmp(u,"k")) { - mul = 1000; - } else if (!strcasecmp(u,"kb")) { - mul = 1024; - } else if (!strcasecmp(u,"m")) { - mul = 1000*1000; - } else if (!strcasecmp(u,"mb")) { - mul = 1024*1024; - } else if (!strcasecmp(u,"g")) { - mul = 1000L*1000*1000; - } else if (!strcasecmp(u,"gb")) { - mul = 1024L*1024*1024; - } else { - if (err) *err = 1; - return 0; - } - - /* Copy the digits into a buffer, we'll use strtoll() to convert - * the digit (without the unit) into a number. */ - digits = u-p; - if (digits >= sizeof(buf)) { - if (err) *err = 1; - return 0; - } - memcpy(buf,p,digits); - buf[digits] = '\0'; - - char *endptr; - errno = 0; - val = strtoull(buf,&endptr,10); - if ((val == 0 && errno == EINVAL) || *endptr != '\0') { - if (err) *err = 1; - return 0; - } - return val*mul; -} - -/* Search a memory buffer for any set of bytes, like strpbrk(). - * Returns pointer to first found char or NULL. - */ -const char *mempbrk(const char *s, size_t len, const char *chars, size_t charslen) { - for (size_t j = 0; j < len; j++) { - for (size_t n = 0; n < charslen; n++) - if (s[j] == chars[n]) return &s[j]; - } - - return NULL; -} - -/* Modify the buffer replacing all occurrences of chars from the 'from' - * set with the corresponding char in the 'to' set. Always returns s. - */ -char *memmapchars(char *s, size_t len, const char *from, const char *to, size_t setlen) { - for (size_t j = 0; j < len; j++) { - for (size_t i = 0; i < setlen; i++) { - if (s[j] == from[i]) { - s[j] = to[i]; - break; - } - } - } - return s; -} - -/* Return the number of digits of 'v' when converted to string in radix 10. - * See ll2string() for more information. */ -uint32_t digits10(uint64_t v) { - if (v < 10) return 1; - if (v < 100) return 2; - if (v < 1000) return 3; - if (v < 1000000000000UL) { - if (v < 100000000UL) { - if (v < 1000000) { - if (v < 10000) return 4; - return 5 + (v >= 100000); - } - return 7 + (v >= 10000000UL); - } - if (v < 10000000000UL) { - return 9 + (v >= 1000000000UL); - } - return 11 + (v >= 100000000000UL); - } - return 12 + digits10(v / 1000000000000UL); -} - -/* Like digits10() but for signed values. */ -uint32_t sdigits10(int64_t v) { - if (v < 0) { - /* Abs value of LLONG_MIN requires special handling. */ - uint64_t uv = (v != LLONG_MIN) ? - (uint64_t)-v : ((uint64_t) LLONG_MAX)+1; - return digits10(uv)+1; /* +1 for the minus. */ - } else { - return digits10(v); - } -} - -/* Convert a long long into a string. Returns the number of - * characters needed to represent the number. - * If the buffer is not big enough to store the string, 0 is returned. */ -int ll2string(char *dst, size_t dstlen, long long svalue) { - unsigned long long value; - int negative = 0; - - /* The ull2string function with 64bit unsigned integers for simplicity, so - * we convert the number here and remember if it is negative. */ - if (svalue < 0) { - if (svalue != LLONG_MIN) { - value = -svalue; - } else { - value = ((unsigned long long) LLONG_MAX)+1; - } - if (dstlen < 2) - goto err; - negative = 1; - dst[0] = '-'; - dst++; - dstlen--; - } else { - value = svalue; - } - - /* Converts the unsigned long long value to string*/ - int length = ull2string(dst, dstlen, value); - if (length == 0) return 0; - return length + negative; - -err: - /* force add Null termination */ - if (dstlen > 0) - dst[0] = '\0'; - return 0; -} - -/* Convert a unsigned long long into a string. Returns the number of - * characters needed to represent the number. - * If the buffer is not big enough to store the string, 0 is returned. - * - * Based on the following article (that apparently does not provide a - * novel approach but only publicizes an already used technique): - * - * https://www.facebook.com/notes/facebook-engineering/three-optimization-tips-for-c/10151361643253920 */ -int ull2string(char *dst, size_t dstlen, unsigned long long value) { - static const char digits[201] = - "0001020304050607080910111213141516171819" - "2021222324252627282930313233343536373839" - "4041424344454647484950515253545556575859" - "6061626364656667686970717273747576777879" - "8081828384858687888990919293949596979899"; - - /* Check length. */ - uint32_t length = digits10(value); - if (length >= dstlen) goto err;; - - /* Null term. */ - uint32_t next = length - 1; - dst[next + 1] = '\0'; - while (value >= 100) { - int const i = (value % 100) * 2; - value /= 100; - dst[next] = digits[i + 1]; - dst[next - 1] = digits[i]; - next -= 2; - } - - /* Handle last 1-2 digits. */ - if (value < 10) { - dst[next] = '0' + (uint32_t) value; - } else { - int i = (uint32_t) value * 2; - dst[next] = digits[i + 1]; - dst[next - 1] = digits[i]; - } - return length; -err: - /* force add Null termination */ - if (dstlen > 0) - dst[0] = '\0'; - return 0; -} - -/* Convert a string into a long long. Returns 1 if the string could be parsed - * into a (non-overflowing) long long, 0 otherwise. The value will be set to - * the parsed value when appropriate. - * - * Note that this function demands that the string strictly represents - * a long long: no spaces or other characters before or after the string - * representing the number are accepted, nor zeroes at the start if not - * for the string "0" representing the zero number. - * - * Because of its strictness, it is safe to use this function to check if - * you can convert a string into a long long, and obtain back the string - * from the number without any loss in the string representation. */ -int string2ll(const char *s, size_t slen, long long *value) { - const char *p = s; - size_t plen = 0; - int negative = 0; - unsigned long long v; - - /* A string of zero length or excessive length is not a valid number. */ - if (plen == slen || slen >= LONG_STR_SIZE) - return 0; - - /* Special case: first and only digit is 0. */ - if (slen == 1 && p[0] == '0') { - if (value != NULL) *value = 0; - return 1; - } - - /* Handle negative numbers: just set a flag and continue like if it - * was a positive number. Later convert into negative. */ - if (p[0] == '-') { - negative = 1; - p++; plen++; - - /* Abort on only a negative sign. */ - if (plen == slen) - return 0; - } - - /* First digit should be 1-9, otherwise the string should just be 0. */ - if (p[0] >= '1' && p[0] <= '9') { - v = p[0]-'0'; - p++; plen++; - } else { - return 0; - } - - /* Parse all the other digits, checking for overflow at every step. */ - while (plen < slen && p[0] >= '0' && p[0] <= '9') { - if (v > (ULLONG_MAX / 10)) /* Overflow. */ - return 0; - v *= 10; - - if (v > (ULLONG_MAX - (p[0]-'0'))) /* Overflow. */ - return 0; - v += p[0]-'0'; - - p++; plen++; - } - - /* Return if not all bytes were used. */ - if (plen < slen) - return 0; - - /* Convert to negative if needed, and do the final overflow check when - * converting from unsigned long long to long long. */ - if (negative) { - if (v > ((unsigned long long)(-(LLONG_MIN+1))+1)) /* Overflow. */ - return 0; - if (value != NULL) *value = -v; - } else { - if (v > LLONG_MAX) /* Overflow. */ - return 0; - if (value != NULL) *value = v; - } - return 1; -} - -/* Helper function to convert a string to an unsigned long long value. - * The function attempts to use the faster string2ll() function inside - * Redis: if it fails, strtoull() is used instead. The function returns - * 1 if the conversion happened successfully or 0 if the number is - * invalid or out of range. */ -int string2ull(const char *s, unsigned long long *value) { - long long ll; - if (string2ll(s,strlen(s),&ll)) { - if (ll < 0) return 0; /* Negative values are out of range. */ - *value = ll; - return 1; - } - errno = 0; - char *endptr = NULL; - *value = strtoull(s,&endptr,10); - if (errno == EINVAL || errno == ERANGE || !(*s != '\0' && *endptr == '\0')) - return 0; /* strtoull() failed. */ - return 1; /* Conversion done! */ -} - -/* Convert a string into a long. Returns 1 if the string could be parsed into a - * (non-overflowing) long, 0 otherwise. The value will be set to the parsed - * value when appropriate. */ -int string2l(const char *s, size_t slen, long *lval) { - long long llval; - - if (!string2ll(s,slen,&llval)) - return 0; - - if (llval < LONG_MIN || llval > LONG_MAX) - return 0; - - *lval = (long)llval; - return 1; -} - -/* return 1 if c>= start && c <= end, 0 otherwise*/ -static int safe_is_c_in_range(char c, char start, char end) { - if (c >= start && c <= end) return 1; - return 0; -} - -static int base_16_char_type(char c) { - if (safe_is_c_in_range(c, '0', '9')) return 0; - if (safe_is_c_in_range(c, 'a', 'f')) return 1; - if (safe_is_c_in_range(c, 'A', 'F')) return 2; - return -1; -} - -/** This is an async-signal safe version of string2l to convert unsigned long to string. - * The function translates @param src until it reaches a value that is not 0-9, a-f or A-F, or @param we read slen characters. - * On successes writes the result to @param result_output and returns 1. - * if the string represents an overflow value, return -1. */ -int string2ul_base16_async_signal_safe(const char *src, size_t slen, unsigned long *result_output) { - static char ascii_to_dec[] = {'0', 'a' - 10, 'A' - 10}; - - int char_type = 0; - size_t curr_char_idx = 0; - unsigned long result = 0; - int base = 16; - while ((-1 != (char_type = base_16_char_type(src[curr_char_idx]))) && - curr_char_idx < slen) { - unsigned long curr_val = src[curr_char_idx] - ascii_to_dec[char_type]; - if ((result > ULONG_MAX / base) || (result > (ULONG_MAX - curr_val)/base)) /* Overflow. */ - return -1; - result = result * base + curr_val; - ++curr_char_idx; - } - - *result_output = result; - return 1; -} - -/* Convert a string into a double. Returns 1 if the string could be parsed - * into a (non-overflowing) double, 0 otherwise. The value will be set to - * the parsed value when appropriate. - * - * Note that this function demands that the string strictly represents - * a double: no spaces or other characters before or after the string - * representing the number are accepted. */ -int string2ld(const char *s, size_t slen, long double *dp) { - char buf[MAX_LONG_DOUBLE_CHARS]; - long double value; - char *eptr; - - if (slen == 0 || slen >= sizeof(buf)) return 0; - memcpy(buf,s,slen); - buf[slen] = '\0'; - - errno = 0; - value = strtold(buf, &eptr); - if (isspace(buf[0]) || eptr[0] != '\0' || - (size_t)(eptr-buf) != slen || - (errno == ERANGE && - (value == HUGE_VAL || value == -HUGE_VAL || fpclassify(value) == FP_ZERO)) || - errno == EINVAL || - isnan(value)) - return 0; - - if (dp) *dp = value; - return 1; -} - -/* Convert a string into a double. Returns 1 if the string could be parsed - * into a (non-overflowing) double, 0 otherwise. The value will be set to - * the parsed value when appropriate. - * - * Note that this function demands that the string strictly represents - * a double: no spaces or other characters before or after the string - * representing the number are accepted. */ -int string2d(const char *s, size_t slen, double *dp) { - errno = 0; - char *eptr; - /* Fast path to reject empty strings, or strings starting by space explicitly */ - if (unlikely(slen == 0 || - isspace(((const char*)s)[0]))) - return 0; - *dp = fast_float_strtod(s, &eptr); - /* If `fast_float_strtod` didn't consume full input, try `strtod` - * Given fast_float does not support hexadecimal strings representation */ - if (unlikely((size_t)(eptr - (char*)s) != slen)) { - char *fallback_eptr; - *dp = strtod(s, &fallback_eptr); - if ((size_t)(fallback_eptr - (char*)s) != slen) return 0; - } - if (unlikely(errno == EINVAL || - (errno == ERANGE && - (*dp == HUGE_VAL || *dp == -HUGE_VAL || fpclassify(*dp) == FP_ZERO)) || - isnan(*dp))) - return 0; - return 1; -} - -/* Returns 1 if the double value can safely be represented in long long without - * precision loss, in which case the corresponding long long is stored in the out variable. */ -int double2ll(double d, long long *out) { -#if (DBL_MANT_DIG >= 52) && (DBL_MANT_DIG <= 63) && (LLONG_MAX == 0x7fffffffffffffffLL) - /* Check if the float is in a safe range to be casted into a - * long long. We are assuming that long long is 64 bit here. - * Also we are assuming that there are no implementations around where - * double has precision < 52 bit. - * - * Under this assumptions we test if a double is inside a range - * where casting to long long is safe. Then using two castings we - * make sure the decimal part is zero. If all this is true we can use - * integer without precision loss. - * - * Note that numbers above 2^52 and below 2^63 use all the fraction bits as real part, - * and the exponent bits are positive, which means the "decimal" part must be 0. - * i.e. all double values in that range are representable as a long without precision loss, - * but not all long values in that range can be represented as a double. - * we only care about the first part here. */ - if (d < (double)(-LLONG_MAX/2) || d > (double)(LLONG_MAX/2)) - return 0; - long long ll = d; - if (ll == d) { - *out = ll; - return 1; - } -#endif - return 0; -} - -/* Convert a double to a string representation. Returns the number of bytes - * required. The representation should always be parsable by strtod(3). - * This function does not support human-friendly formatting like ld2string - * does. It is intended mainly to be used inside t_zset.c when writing scores - * into a listpack representing a sorted set. */ -int d2string(char *buf, size_t len, double value) { - if (isnan(value)) { - /* Libc in some systems will format nan in a different way, - * like nan, -nan, NAN, nan(char-sequence). - * So we normalize it and create a single nan form in an explicit way. */ - len = snprintf(buf,len,"nan"); - } else if (isinf(value)) { - /* Libc in odd systems (Hi Solaris!) will format infinite in a - * different way, so better to handle it in an explicit way. */ - if (value < 0) - len = snprintf(buf,len,"-inf"); - else - len = snprintf(buf,len,"inf"); - } else if (value == 0) { - /* See: http://en.wikipedia.org/wiki/Signed_zero, "Comparisons". */ - if (1.0/value < 0) - len = snprintf(buf,len,"-0"); - else - len = snprintf(buf,len,"0"); - } else { - long long lvalue; - /* Integer printing function is much faster, check if we can safely use it. */ - if (double2ll(value, &lvalue)) - len = ll2string(buf,len,lvalue); - else { - len = fpconv_dtoa(value, buf); - buf[len] = '\0'; - } - } - - return len; -} - -/* Convert a double into a string with 'fractional_digits' digits after the dot precision. - * This is an optimized version of snprintf "%.<fractional_digits>f". - * We convert the double to long and multiply it by 10 ^ <fractional_digits> to shift - * the decimal places. - * Note that multiply it of input value by 10 ^ <fractional_digits> can overflow but on the scenario - * that we currently use within redis this that is not possible. - * After we get the long representation we use the logic from ull2string function on this file - * which is based on the following article: - * https://www.facebook.com/notes/facebook-engineering/three-optimization-tips-for-c/10151361643253920 - * - * Input values: - * char: the buffer to store the string representation - * dstlen: the buffer length - * dvalue: the input double - * fractional_digits: the number of fractional digits after the dot precision. between 1 and 17 - * - * Return values: - * Returns the number of characters needed to represent the number. - * If the buffer is not big enough to store the string, 0 is returned. - */ -int fixedpoint_d2string(char *dst, size_t dstlen, double dvalue, int fractional_digits) { - if (fractional_digits < 1 || fractional_digits > 17) - goto err; - /* min size of 2 ( due to 0. ) + n fractional_digitits + \0 */ - if ((int)dstlen < (fractional_digits+3)) - goto err; - if (dvalue == 0) { - dst[0] = '0'; - dst[1] = '.'; - memset(dst + 2, '0', fractional_digits); - dst[fractional_digits+2] = '\0'; - return fractional_digits + 2; - } - /* scale and round */ - static double powers_of_ten[] = {1.0, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, - 10000000.0, 100000000.0, 1000000000.0, 10000000000.0, 100000000000.0, 1000000000000.0, - 10000000000000.0, 100000000000000.0, 1000000000000000.0, 10000000000000000.0, - 100000000000000000.0 }; - long long svalue = llrint(dvalue * powers_of_ten[fractional_digits]); - unsigned long long value; - /* write sign */ - int negative = 0; - if (svalue < 0) { - if (svalue != LLONG_MIN) { - value = -svalue; - } else { - value = ((unsigned long long) LLONG_MAX)+1; - } - if (dstlen < 2) - goto err; - negative = 1; - dst[0] = '-'; - dst++; - dstlen--; - } else { - value = svalue; - } - - static const char digitsd[201] = - "0001020304050607080910111213141516171819" - "2021222324252627282930313233343536373839" - "4041424344454647484950515253545556575859" - "6061626364656667686970717273747576777879" - "8081828384858687888990919293949596979899"; - - /* Check length. */ - uint32_t ndigits = digits10(value); - if (ndigits >= dstlen) goto err; - int integer_digits = ndigits - fractional_digits; - /* Fractional only check to avoid representing 0.7750 as .7750. - * This means we need to increment the length and store 0 as the first character. - */ - if (integer_digits < 1) { - dst[0] = '0'; - integer_digits = 1; - } - dst[integer_digits] = '.'; - int size = integer_digits + 1 + fractional_digits; - /* fill with 0 from fractional digits until size */ - memset(dst + integer_digits + 1, '0', fractional_digits); - int next = size - 1; - while (value >= 100) { - int const i = (value % 100) * 2; - value /= 100; - dst[next] = digitsd[i + 1]; - dst[next - 1] = digitsd[i]; - next -= 2; - /* dot position */ - if (next == integer_digits) { - next--; - } - } - - /* Handle last 1-2 digits. */ - if (value < 10) { - dst[next] = '0' + (uint32_t) value; - } else { - int i = (uint32_t) value * 2; - dst[next] = digitsd[i + 1]; - dst[next - 1] = digitsd[i]; - } - /* Null term. */ - dst[size] = '\0'; - return size + negative; -err: - /* force add Null termination */ - if (dstlen > 0) - dst[0] = '\0'; - return 0; -} - -/* Trims off trailing zeros from a string representing a double. */ -int trimDoubleString(char *buf, size_t len) { - if (strchr(buf,'.') != NULL) { - char *p = buf+len-1; - while(*p == '0') { - p--; - len--; - } - if (*p == '.') len--; - } - buf[len] = '\0'; - return len; -} - -/* Create a string object from a long double. - * If mode is humanfriendly it does not use exponential format and trims trailing - * zeroes at the end (may result in loss of precision). - * If mode is default exp format is used and the output of snprintf() - * is not modified (may result in loss of precision). - * If mode is hex hexadecimal format is used (no loss of precision) - * - * The function returns the length of the string or zero if there was not - * enough buffer room to store it. */ -int ld2string(char *buf, size_t len, long double value, ld2string_mode mode) { - size_t l = 0; - - if (isinf(value)) { - /* Libc in odd systems (Hi Solaris!) will format infinite in a - * different way, so better to handle it in an explicit way. */ - if (len < 5) goto err; /* No room. 5 is "-inf\0" */ - if (value > 0) { - memcpy(buf,"inf",3); - l = 3; - } else { - memcpy(buf,"-inf",4); - l = 4; - } - } else if (isnan(value)) { - /* Libc in some systems will format nan in a different way, - * like nan, -nan, NAN, nan(char-sequence). - * So we normalize it and create a single nan form in an explicit way. */ - if (len < 4) goto err; /* No room. 4 is "nan\0" */ - memcpy(buf, "nan", 3); - l = 3; - } else { - switch (mode) { - case LD_STR_AUTO: - l = snprintf(buf,len,"%.17Lg",value); - if (l+1 > len) goto err;; /* No room. */ - break; - case LD_STR_HEX: - l = snprintf(buf,len,"%La",value); - if (l+1 > len) goto err; /* No room. */ - break; - case LD_STR_HUMAN: - /* We use 17 digits precision since with 128 bit floats that precision - * after rounding is able to represent most small decimal numbers in a - * way that is "non surprising" for the user (that is, most small - * decimal numbers will be represented in a way that when converted - * back into a string are exactly the same as what the user typed.) */ - l = snprintf(buf,len,"%.17Lf",value); - if (l+1 > len) goto err; /* No room. */ - /* Now remove trailing zeroes after the '.' */ - if (strchr(buf,'.') != NULL) { - char *p = buf+l-1; - while(*p == '0') { - p--; - l--; - } - if (*p == '.') l--; - } - if (l == 2 && buf[0] == '-' && buf[1] == '0') { - buf[0] = '0'; - l = 1; - } - break; - default: goto err; /* Invalid mode. */ - } - } - buf[l] = '\0'; - return l; -err: - /* force add Null termination */ - if (len > 0) - buf[0] = '\0'; - return 0; -} - -/* Get random bytes, attempts to get an initial seed from /dev/urandom and - * the uses a one way hash function in counter mode to generate a random - * stream. However if /dev/urandom is not available, a weaker seed is used. - * - * This function is not thread safe, since the state is global. */ -void getRandomBytes(unsigned char *p, size_t len) { - /* Global state. */ - static int seed_initialized = 0; - static unsigned char seed[64]; /* 512 bit internal block size. */ - static uint64_t counter = 0; /* The counter we hash with the seed. */ - - if (!seed_initialized) { - /* Initialize a seed and use SHA1 in counter mode, where we hash - * the same seed with a progressive counter. For the goals of this - * function we just need non-colliding strings, there are no - * cryptographic security needs. */ - FILE *fp = fopen("/dev/urandom","r"); - if (fp == NULL || fread(seed,sizeof(seed),1,fp) != 1) { - /* Revert to a weaker seed, and in this case reseed again - * at every call.*/ - for (unsigned int j = 0; j < sizeof(seed); j++) { - struct timeval tv; - gettimeofday(&tv,NULL); - pid_t pid = getpid(); - seed[j] = tv.tv_sec ^ tv.tv_usec ^ pid ^ (long)fp; - } - } else { - seed_initialized = 1; - } - if (fp) fclose(fp); - } - - while(len) { - /* This implements SHA256-HMAC. */ - unsigned char digest[SHA256_BLOCK_SIZE]; - unsigned char kxor[64]; - unsigned int copylen = - len > SHA256_BLOCK_SIZE ? SHA256_BLOCK_SIZE : len; - - /* IKEY: key xored with 0x36. */ - memcpy(kxor,seed,sizeof(kxor)); - for (unsigned int i = 0; i < sizeof(kxor); i++) kxor[i] ^= 0x36; - - /* Obtain HASH(IKEY||MESSAGE). */ - SHA256_CTX ctx; - sha256_init(&ctx); - sha256_update(&ctx,kxor,sizeof(kxor)); - sha256_update(&ctx,(unsigned char*)&counter,sizeof(counter)); - sha256_final(&ctx,digest); - - /* OKEY: key xored with 0x5c. */ - memcpy(kxor,seed,sizeof(kxor)); - for (unsigned int i = 0; i < sizeof(kxor); i++) kxor[i] ^= 0x5C; - - /* Obtain HASH(OKEY || HASH(IKEY||MESSAGE)). */ - sha256_init(&ctx); - sha256_update(&ctx,kxor,sizeof(kxor)); - sha256_update(&ctx,digest,SHA256_BLOCK_SIZE); - sha256_final(&ctx,digest); - - /* Increment the counter for the next iteration. */ - counter++; - - memcpy(p,digest,copylen); - len -= copylen; - p += copylen; - } -} - -/* Generate the Redis "Run ID", a SHA1-sized random number that identifies a - * given execution of Redis, so that if you are talking with an instance - * having run_id == A, and you reconnect and it has run_id == B, you can be - * sure that it is either a different instance or it was restarted. */ -void getRandomHexChars(char *p, size_t len) { - char *charset = "0123456789abcdef"; - size_t j; - - getRandomBytes((unsigned char*)p,len); - for (j = 0; j < len; j++) p[j] = charset[p[j] & 0x0F]; -} - -/* Given the filename, return the absolute path as an SDS string, or NULL - * if it fails for some reason. Note that "filename" may be an absolute path - * already, this will be detected and handled correctly. - * - * The function does not try to normalize everything, but only the obvious - * case of one or more "../" appearing at the start of "filename" - * relative path. */ -sds getAbsolutePath(char *filename) { - char cwd[1024]; - sds abspath; - sds relpath = sdsnew(filename); - - relpath = sdstrim(relpath," \r\n\t"); - if (relpath[0] == '/') return relpath; /* Path is already absolute. */ - - /* If path is relative, join cwd and relative path. */ - if (getcwd(cwd,sizeof(cwd)) == NULL) { - sdsfree(relpath); - return NULL; - } - abspath = sdsnew(cwd); - if (sdslen(abspath) && abspath[sdslen(abspath)-1] != '/') - abspath = sdscat(abspath,"/"); - - /* At this point we have the current path always ending with "/", and - * the trimmed relative path. Try to normalize the obvious case of - * trailing ../ elements at the start of the path. - * - * For every "../" we find in the filename, we remove it and also remove - * the last element of the cwd, unless the current cwd is "/". */ - while (sdslen(relpath) >= 3 && - relpath[0] == '.' && relpath[1] == '.' && relpath[2] == '/') - { - sdsrange(relpath,3,-1); - if (sdslen(abspath) > 1) { - char *p = abspath + sdslen(abspath)-2; - int trimlen = 1; - - while(*p != '/') { - p--; - trimlen++; - } - sdsrange(abspath,0,-(trimlen+1)); - } - } - - /* Finally glue the two parts together. */ - abspath = sdscatsds(abspath,relpath); - sdsfree(relpath); - return abspath; -} - -/* - * Gets the proper timezone in a more portable fashion - * i.e timezone variables are linux specific. - */ -long getTimeZone(void) { -#if defined(__linux__) || defined(__sun) - return timezone; -#else - struct timezone tz; - - gettimeofday(NULL, &tz); - - return tz.tz_minuteswest * 60L; -#endif -} - -/* Return true if the specified path is just a file basename without any - * relative or absolute path. This function just checks that no / or \ - * character exists inside the specified path, that's enough in the - * environments where Redis runs. */ -int pathIsBaseName(char *path) { - return strchr(path,'/') == NULL && strchr(path,'\\') == NULL; -} - -int fileExist(char *filename) { - struct stat statbuf; - return stat(filename, &statbuf) == 0 && S_ISREG(statbuf.st_mode); -} - -int dirExists(char *dname) { - struct stat statbuf; - return stat(dname, &statbuf) == 0 && S_ISDIR(statbuf.st_mode); -} - -int dirCreateIfMissing(char *dname) { - if (mkdir(dname, 0755) != 0) { - if (errno != EEXIST) { - return -1; - } else if (!dirExists(dname)) { - errno = ENOTDIR; - return -1; - } - } - return 0; -} - -int dirRemove(char *dname) { - DIR *dir; - struct stat stat_entry; - struct dirent *entry; - char full_path[PATH_MAX + 1]; - - if ((dir = opendir(dname)) == NULL) { - return -1; - } - - while ((entry = readdir(dir)) != NULL) { - if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) continue; - - snprintf(full_path, sizeof(full_path), "%s/%s", dname, entry->d_name); - - int fd = open(full_path, O_RDONLY|O_NONBLOCK); - if (fd == -1) { - closedir(dir); - return -1; - } - - if (fstat(fd, &stat_entry) == -1) { - close(fd); - closedir(dir); - return -1; - } - close(fd); - - if (S_ISDIR(stat_entry.st_mode) != 0) { - if (dirRemove(full_path) == -1) { - closedir(dir); - return -1; - } - continue; - } - - if (unlink(full_path) != 0) { - closedir(dir); - return -1; - } - } - - if (rmdir(dname) != 0) { - closedir(dir); - return -1; - } - - closedir(dir); - return 0; -} - -sds makePath(char *path, char *filename) { - return sdscatfmt(sdsempty(), "%s/%s", path, filename); -} - -/* Given the filename, sync the corresponding directory. - * - * Usually a portable and safe pattern to overwrite existing files would be like: - * 1. create a new temp file (on the same file system!) - * 2. write data to the temp file - * 3. fsync() the temp file - * 4. rename the temp file to the appropriate name - * 5. fsync() the containing directory */ -int fsyncFileDir(const char *filename) { -#ifdef _AIX - /* AIX is unable to fsync a directory */ - return 0; -#endif - char temp_filename[PATH_MAX + 1]; - char *dname; - int dir_fd; - - if (strlen(filename) > PATH_MAX) { - errno = ENAMETOOLONG; - return -1; - } - - /* In the glibc implementation dirname may modify their argument. */ - memcpy(temp_filename, filename, strlen(filename) + 1); - dname = dirname(temp_filename); - - dir_fd = open(dname, O_RDONLY); - if (dir_fd == -1) { - /* Some OSs don't allow us to open directories at all, just - * ignore the error in that case */ - if (errno == EISDIR) { - return 0; - } - return -1; - } - /* Some OSs don't allow us to fsync directories at all, so we can ignore - * those errors. */ - if (redis_fsync(dir_fd) == -1 && !(errno == EBADF || errno == EINVAL)) { - int save_errno = errno; - close(dir_fd); - errno = save_errno; - return -1; - } - - close(dir_fd); - return 0; -} - - /* free OS pages backed by file */ -int reclaimFilePageCache(int fd, size_t offset, size_t length) { -#ifdef HAVE_FADVISE - int ret = posix_fadvise(fd, offset, length, POSIX_FADV_DONTNEED); - if (ret) { - errno = ret; - return -1; - } - return 0; -#else - UNUSED(fd); - UNUSED(offset); - UNUSED(length); - return 0; -#endif -} - -/** An async signal safe version of fgets(). - * Has the same behaviour as standard fgets(): reads a line from fd and stores it into the dest buffer. - * It stops when either (buff_size-1) characters are read, the newline character is read, or the end-of-file is reached, - * whichever comes first. - * - * On success, the function returns the same dest parameter. If the End-of-File is encountered and no characters have - * been read, the contents of dest remain unchanged and a null pointer is returned. - * If an error occurs, a null pointer is returned. */ -char *fgets_async_signal_safe(char *dest, int buff_size, int fd) { - for (int i = 0; i < buff_size; i++) { - /* Read one byte */ - ssize_t bytes_read_count = read(fd, dest + i, 1); - /* On EOF or error return NULL */ - if (bytes_read_count < 1) { - return NULL; - } - /* we found the end of the line. */ - if (dest[i] == '\n') { - break; - } - } - return dest; -} - -static const char HEX[] = "0123456789abcdef"; - -static char *u2string_async_signal_safe(int _base, uint64_t val, char *buf) { - uint32_t base = (uint32_t) _base; - *buf-- = 0; - do { - *buf-- = HEX[val % base]; - } while ((val /= base) != 0); - return buf + 1; -} - -static char *i2string_async_signal_safe(int base, int64_t val, char *buf) { - 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 *check_longlong_async_signal_safe(const char *fmt, int32_t *have_longlong) { - *have_longlong = 0; - if (*fmt == 'l') { - fmt++; - if (*fmt != 'l') { - *have_longlong = (sizeof(long) == sizeof(int64_t)); - } else { - fmt++; - *have_longlong = 1; - } - } - return fmt; -} - -int vsnprintf_async_signal_safe(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 = 0; - if (*format != '%') { - if (to == end) { /* end of buffer */ - break; - } - *to++ = *format; /* copy ordinary char */ - continue; - } - ++format; /* skip '%' */ - - format = check_longlong_async_signal_safe(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') ? - u2string_async_signal_safe(base, uval, &buff[sizeof(buff) - 1]) : - i2string_async_signal_safe(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 snprintf_async_signal_safe(char *to, size_t n, const char *fmt, ...) { - int result; - va_list args; - va_start(args, fmt); - result = vsnprintf_async_signal_safe(to, n, fmt, args); - va_end(args); - return result; -} - -#ifdef REDIS_TEST -#include <assert.h> -#include <sys/mman.h> -#include "testhelp.h" - -static void test_string2ll(void) { - char buf[32]; - long long v; - - /* May not start with +. */ - redis_strlcpy(buf,"+1",sizeof(buf)); - assert(string2ll(buf,strlen(buf),&v) == 0); - - /* Leading space. */ - redis_strlcpy(buf," 1",sizeof(buf)); - assert(string2ll(buf,strlen(buf),&v) == 0); - - /* Trailing space. */ - redis_strlcpy(buf,"1 ",sizeof(buf)); - assert(string2ll(buf,strlen(buf),&v) == 0); - - /* May not start with 0. */ - redis_strlcpy(buf,"01",sizeof(buf)); - assert(string2ll(buf,strlen(buf),&v) == 0); - - redis_strlcpy(buf,"-1",sizeof(buf)); - assert(string2ll(buf,strlen(buf),&v) == 1); - assert(v == -1); - - redis_strlcpy(buf,"0",sizeof(buf)); - assert(string2ll(buf,strlen(buf),&v) == 1); - assert(v == 0); - - redis_strlcpy(buf,"1",sizeof(buf)); - assert(string2ll(buf,strlen(buf),&v) == 1); - assert(v == 1); - - redis_strlcpy(buf,"99",sizeof(buf)); - assert(string2ll(buf,strlen(buf),&v) == 1); - assert(v == 99); - - redis_strlcpy(buf,"-99",sizeof(buf)); - assert(string2ll(buf,strlen(buf),&v) == 1); - assert(v == -99); - - redis_strlcpy(buf,"-9223372036854775808",sizeof(buf)); - assert(string2ll(buf,strlen(buf),&v) == 1); - assert(v == LLONG_MIN); - - redis_strlcpy(buf,"-9223372036854775809",sizeof(buf)); /* overflow */ - assert(string2ll(buf,strlen(buf),&v) == 0); - - redis_strlcpy(buf,"9223372036854775807",sizeof(buf)); - assert(string2ll(buf,strlen(buf),&v) == 1); - assert(v == LLONG_MAX); - - redis_strlcpy(buf,"9223372036854775808",sizeof(buf)); /* overflow */ - assert(string2ll(buf,strlen(buf),&v) == 0); -} - -static void test_string2l(void) { - char buf[32]; - long v; - - /* May not start with +. */ - redis_strlcpy(buf,"+1",sizeof(buf)); - assert(string2l(buf,strlen(buf),&v) == 0); - - /* May not start with 0. */ - redis_strlcpy(buf,"01",sizeof(buf)); - assert(string2l(buf,strlen(buf),&v) == 0); - - redis_strlcpy(buf,"-1",sizeof(buf)); - assert(string2l(buf,strlen(buf),&v) == 1); - assert(v == -1); - - redis_strlcpy(buf,"0",sizeof(buf)); - assert(string2l(buf,strlen(buf),&v) == 1); - assert(v == 0); - - redis_strlcpy(buf,"1",sizeof(buf)); - assert(string2l(buf,strlen(buf),&v) == 1); - assert(v == 1); - - redis_strlcpy(buf,"99",sizeof(buf)); - assert(string2l(buf,strlen(buf),&v) == 1); - assert(v == 99); - - redis_strlcpy(buf,"-99",sizeof(buf)); - assert(string2l(buf,strlen(buf),&v) == 1); - assert(v == -99); - -#if LONG_MAX != LLONG_MAX - redis_strlcpy(buf,"-2147483648",sizeof(buf)); - assert(string2l(buf,strlen(buf),&v) == 1); - assert(v == LONG_MIN); - - redis_strlcpy(buf,"-2147483649",sizeof(buf)); /* overflow */ - assert(string2l(buf,strlen(buf),&v) == 0); - - redis_strlcpy(buf,"2147483647",sizeof(buf)); - assert(string2l(buf,strlen(buf),&v) == 1); - assert(v == LONG_MAX); - - redis_strlcpy(buf,"2147483648",sizeof(buf)); /* overflow */ - assert(string2l(buf,strlen(buf),&v) == 0); -#endif -} - -static void test_string2d(void) { - char buf[1024]; - double v; - - /* Valid hexadecimal value. */ - redis_strlcpy(buf,"0x0p+0",sizeof(buf)); - assert(string2d(buf,strlen(buf),&v) == 1); - assert(v == 0.0); - - redis_strlcpy(buf,"0x1p+0",sizeof(buf)); - assert(string2d(buf,strlen(buf),&v) == 1); - assert(v == 1.0); - - /* Valid floating-point numbers */ - redis_strlcpy(buf, "1.5", sizeof(buf)); - assert(string2d(buf, strlen(buf), &v) == 1); - assert(v == 1.5); - - redis_strlcpy(buf, "-3.14", sizeof(buf)); - assert(string2d(buf, strlen(buf), &v) == 1); - assert(v == -3.14); - - redis_strlcpy(buf, "2.0e10", sizeof(buf)); - assert(string2d(buf, strlen(buf), &v) == 1); - assert(v == 2.0e10); - - redis_strlcpy(buf, "1e-3", sizeof(buf)); - assert(string2d(buf, strlen(buf), &v) == 1); - assert(v == 0.001); - - /* Valid integer */ - redis_strlcpy(buf, "42", sizeof(buf)); - assert(string2d(buf, strlen(buf), &v) == 1); - assert(v == 42.0); - - /* Invalid cases */ - /* Empty. */ - redis_strlcpy(buf, "", sizeof(buf)); - assert(string2d(buf, strlen(buf), &v) == 0); - - /* Starting by space. */ - redis_strlcpy(buf, " 1.23", sizeof(buf)); - assert(string2d(buf, strlen(buf), &v) == 0); - - /* Invalid hexadecimal format. */ - redis_strlcpy(buf, "0x1.2g", sizeof(buf)); - assert(string2d(buf, strlen(buf), &v) == 0); - - /* Hexadecimal NaN */ - redis_strlcpy(buf, "0xNan", sizeof(buf)); - assert(string2d(buf, strlen(buf), &v) == 0); - - /* overflow. */ - redis_strlcpy(buf,"23456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789",sizeof(buf)); - assert(string2d(buf,strlen(buf),&v) == 0); -} - -static void test_ll2string(void) { - char buf[32]; - long long v; - int sz; - - v = 0; - sz = ll2string(buf, sizeof buf, v); - assert(sz == 1); - assert(!strcmp(buf, "0")); - - v = -1; - sz = ll2string(buf, sizeof buf, v); - assert(sz == 2); - assert(!strcmp(buf, "-1")); - - v = 99; - sz = ll2string(buf, sizeof buf, v); - assert(sz == 2); - assert(!strcmp(buf, "99")); - - v = -99; - sz = ll2string(buf, sizeof buf, v); - assert(sz == 3); - assert(!strcmp(buf, "-99")); - - v = -2147483648; - sz = ll2string(buf, sizeof buf, v); - assert(sz == 11); - assert(!strcmp(buf, "-2147483648")); - - v = LLONG_MIN; - sz = ll2string(buf, sizeof buf, v); - assert(sz == 20); - assert(!strcmp(buf, "-9223372036854775808")); - - v = LLONG_MAX; - sz = ll2string(buf, sizeof buf, v); - assert(sz == 19); - assert(!strcmp(buf, "9223372036854775807")); -} - -static void test_ld2string(void) { - char buf[32]; - long double v; - int sz; - - v = 0.0 / 0.0; - sz = ld2string(buf, sizeof(buf), v, LD_STR_AUTO); - assert(sz == 3); - assert(!strcmp(buf, "nan")); -} - -static void test_fixedpoint_d2string(void) { - char buf[32]; - double v; - int sz; - v = 0.0; - sz = fixedpoint_d2string(buf, sizeof buf, v, 4); - assert(sz == 6); - assert(!strcmp(buf, "0.0000")); - sz = fixedpoint_d2string(buf, sizeof buf, v, 1); - assert(sz == 3); - assert(!strcmp(buf, "0.0")); - /* set junk in buffer */ - memset(buf,'A',32); - v = 0.0001; - sz = fixedpoint_d2string(buf, sizeof buf, v, 4); - assert(sz == 6); - assert(buf[sz] == '\0'); - assert(!strcmp(buf, "0.0001")); - /* set junk in buffer */ - memset(buf,'A',32); - v = 6.0642951598391699e-05; - sz = fixedpoint_d2string(buf, sizeof buf, v, 4); - assert(sz == 6); - assert(buf[sz] == '\0'); - assert(!strcmp(buf, "0.0001")); - v = 0.01; - sz = fixedpoint_d2string(buf, sizeof buf, v, 4); - assert(sz == 6); - assert(!strcmp(buf, "0.0100")); - sz = fixedpoint_d2string(buf, sizeof buf, v, 1); - assert(sz == 3); - assert(!strcmp(buf, "0.0")); - v = -0.01; - sz = fixedpoint_d2string(buf, sizeof buf, v, 4); - assert(sz == 7); - assert(!strcmp(buf, "-0.0100")); - v = -0.1; - sz = fixedpoint_d2string(buf, sizeof buf, v, 1); - assert(sz == 4); - assert(!strcmp(buf, "-0.1")); - v = 0.1; - sz = fixedpoint_d2string(buf, sizeof buf, v, 1); - assert(sz == 3); - assert(!strcmp(buf, "0.1")); - v = 0.01; - sz = fixedpoint_d2string(buf, sizeof buf, v, 17); - assert(sz == 19); - assert(!strcmp(buf, "0.01000000000000000")); - v = 10.01; - sz = fixedpoint_d2string(buf, sizeof buf, v, 4); - assert(sz == 7); - assert(!strcmp(buf, "10.0100")); - /* negative tests */ - sz = fixedpoint_d2string(buf, sizeof buf, v, 18); - assert(sz == 0); - sz = fixedpoint_d2string(buf, sizeof buf, v, 0); - assert(sz == 0); - sz = fixedpoint_d2string(buf, 1, v, 1); - assert(sz == 0); -} - -#if defined(__linux__) -/* Since fadvise and mincore is only supported in specific platforms like - * Linux, we only verify the fadvise mechanism works in Linux */ -static int cache_exist(int fd) { - unsigned char flag; - void *m = mmap(NULL, 4096, PROT_READ, MAP_SHARED, fd, 0); - assert(m); - assert(mincore(m, 4096, &flag) == 0); - munmap(m, 4096); - /* the least significant bit of the byte will be set if the corresponding - * page is currently resident in memory */ - return flag&1; -} - -static void test_reclaimFilePageCache(void) { - char *tmpfile = "/tmp/redis-reclaim-cache-test"; - int fd = open(tmpfile, O_RDWR|O_CREAT, 0644); - assert(fd >= 0); - - /* test write file */ - char buf[4] = "foo"; - assert(write(fd, buf, sizeof(buf)) > 0); - assert(cache_exist(fd)); - assert(redis_fsync(fd) == 0); - assert(reclaimFilePageCache(fd, 0, 0) == 0); - assert(!cache_exist(fd)); - - /* test read file */ - assert(pread(fd, buf, sizeof(buf), 0) > 0); - assert(cache_exist(fd)); - assert(reclaimFilePageCache(fd, 0, 0) == 0); - assert(!cache_exist(fd)); - - unlink(tmpfile); - printf("reclaimFilePageCach test is ok\n"); -} -#endif - -int utilTest(int argc, char **argv, int flags) { - UNUSED(argc); - UNUSED(argv); - UNUSED(flags); - - test_string2ll(); - test_string2l(); - test_string2d(); - test_ll2string(); - test_ld2string(); - test_fixedpoint_d2string(); -#if defined(__linux__) - if (!(flags & REDIS_TEST_VALGRIND)) { - test_reclaimFilePageCache(); - } -#endif - printf("Done testing util\n"); - return 0; -} -#endif |
