futriix/src/util.c
Viktor Söderqvist 6af51f5092
Prevent clang-format in certain places (#468)
This is a preparation for adding clang-format.

These comments prevent automatic formatting in some places. With these
exceptions, we will be able to run clang-format on the rest of the code.

This is a preparation for #323.

---------

Signed-off-by: Viktor Söderqvist <viktor.soderqvist@est.tech>
2024-05-08 20:58:53 +02:00

1380 lines
44 KiB
C

/*
* Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
* 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 <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))
/* Glob-style pattern matching. */
static int stringmatchlen_impl(const char *pattern, int patternLen,
const char *string, int stringLen, int nocase, int *skipLongerMatches)
{
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))
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 = pattern[0] == '^';
if (not) {
pattern++;
patternLen--;
}
match = 0;
while(1) {
if (pattern[0] == '\\' && patternLen >= 2) {
pattern++;
patternLen--;
if (pattern[0] == string[0])
match = 1;
} else if (pattern[0] == ']') {
break;
} else if (patternLen == 0) {
pattern--;
patternLen++;
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(*pattern == '*') {
pattern++;
patternLen--;
}
break;
}
}
if (patternLen == 0 && stringLen == 0)
return 1;
return 0;
}
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);
}
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
* Valkey: 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;
*dp = strtod(s, &eptr);
if (slen == 0 ||
isspace(((const char*)s)[0]) ||
(size_t)(eptr-(char*)s) != slen ||
(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 the server 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 server "Run ID", a SHA1-sized random number that identifies a
* given execution of the server, 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 the server 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 (valkey_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) {
/* clang-format 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;
}
/* clang-format 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;
}