2018-12-15 16:11:41 -05:00
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
|
|
|
|
|
2018-12-15 12:42:37 -05:00
|
|
|
#include <string.h>
|
|
|
|
|
2019-03-18 18:34:59 -04:00
|
|
|
#include <test.h>
|
|
|
|
|
2018-12-15 13:47:21 -05:00
|
|
|
#include "compiler.h"
|
2018-12-15 12:42:37 -05:00
|
|
|
#include "string_utils.h"
|
|
|
|
#include "utils.h"
|
|
|
|
|
2019-02-01 20:58:41 -05:00
|
|
|
#pragma GCC diagnostic push
|
|
|
|
|
|
|
|
// gcc warns about legitimate strncpy in mstrjoin and mstrextend
|
|
|
|
// strncpy(str, src1, len1) intentional truncates the null byte from src1.
|
|
|
|
// strncpy(str+len1, src2, len2) uses bound depends on the source argument,
|
|
|
|
// but str is allocated with len1+len2+1, so this strncpy can't overflow
|
2019-02-01 21:12:01 -05:00
|
|
|
#pragma GCC diagnostic ignored "-Wpragmas"
|
2019-02-01 20:58:41 -05:00
|
|
|
#pragma GCC diagnostic ignored "-Wstringop-truncation"
|
|
|
|
#pragma GCC diagnostic ignored "-Wstringop-overflow"
|
|
|
|
|
2018-12-15 12:42:37 -05:00
|
|
|
/**
|
|
|
|
* Allocate the space and join two strings.
|
|
|
|
*/
|
|
|
|
char *mstrjoin(const char *src1, const char *src2) {
|
2019-01-29 19:12:12 -05:00
|
|
|
auto len1 = strlen(src1);
|
|
|
|
auto len2 = strlen(src2);
|
|
|
|
auto len = len1 + len2 + 1;
|
|
|
|
auto str = ccalloc(len, char);
|
2018-12-15 12:42:37 -05:00
|
|
|
|
2019-01-29 19:12:12 -05:00
|
|
|
strncpy(str, src1, len1);
|
|
|
|
strncpy(str + len1, src2, len2);
|
|
|
|
str[len - 1] = '\0';
|
2018-12-15 12:42:37 -05:00
|
|
|
|
2019-01-29 19:12:12 -05:00
|
|
|
return str;
|
2018-12-15 12:42:37 -05:00
|
|
|
}
|
|
|
|
|
2019-03-18 18:34:59 -04:00
|
|
|
TEST_CASE(mstrjoin) {
|
|
|
|
char *str = mstrjoin("asdf", "qwer");
|
|
|
|
TEST_STREQUAL(str, "asdfqwer");
|
|
|
|
free(str);
|
|
|
|
|
|
|
|
str = mstrjoin("", "qwer");
|
|
|
|
TEST_STREQUAL(str, "qwer");
|
|
|
|
free(str);
|
|
|
|
|
|
|
|
str = mstrjoin("asdf", "");
|
|
|
|
TEST_STREQUAL(str, "asdf");
|
|
|
|
free(str);
|
|
|
|
}
|
|
|
|
|
2018-12-15 12:42:37 -05:00
|
|
|
/**
|
|
|
|
* Concatenate a string on heap with another string.
|
|
|
|
*/
|
|
|
|
void mstrextend(char **psrc1, const char *src2) {
|
2019-01-29 19:12:12 -05:00
|
|
|
if (!*psrc1) {
|
|
|
|
*psrc1 = strdup(src2);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto len1 = strlen(*psrc1);
|
|
|
|
auto len2 = strlen(src2);
|
|
|
|
auto len = len1 + len2 + 1;
|
|
|
|
*psrc1 = crealloc(*psrc1, len);
|
|
|
|
|
|
|
|
strncpy(*psrc1 + len1, src2, len2);
|
|
|
|
(*psrc1)[len - 1] = '\0';
|
2018-12-15 12:42:37 -05:00
|
|
|
}
|
Parse number locale-independently
Previously we were using glibc's strtod function to parse floating point
numbers. The problem is, strtod is locale dependent. Meaning 7,7 might
be parsed as two numbers (7 and 7) in one locale, and parsed as one
number (7 point 7) in another locale. This is undesirable.
We need to set the locale to a value we know to make number parsing
consistently. We could use setlocale(), but that is not thread-safe. We
can also use uselocale(), which is thread-safe, but doesn't cover strtod
(Yeah, some of the locale-aware functions only acknowledge the global
locale, not the thread local one).
So in frustration, I just wrote a simple floating point number parser
myself. This parser obviously doesn't cover all cases strtod covers, but
is good enough for our needs.
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2019-02-01 20:22:41 -05:00
|
|
|
|
2019-03-18 18:34:59 -04:00
|
|
|
TEST_CASE(mstrextend) {
|
|
|
|
char *str1 = NULL;
|
|
|
|
mstrextend(&str1, "asdf");
|
|
|
|
TEST_STREQUAL(str1, "asdf");
|
|
|
|
|
|
|
|
mstrextend(&str1, "asd");
|
|
|
|
TEST_STREQUAL(str1, "asdfasd");
|
|
|
|
|
|
|
|
mstrextend(&str1, "");
|
|
|
|
TEST_STREQUAL(str1, "asdfasd");
|
|
|
|
free(str1);
|
|
|
|
}
|
|
|
|
|
2019-02-01 20:58:41 -05:00
|
|
|
#pragma GCC diagnostic pop
|
|
|
|
|
Parse number locale-independently
Previously we were using glibc's strtod function to parse floating point
numbers. The problem is, strtod is locale dependent. Meaning 7,7 might
be parsed as two numbers (7 and 7) in one locale, and parsed as one
number (7 point 7) in another locale. This is undesirable.
We need to set the locale to a value we know to make number parsing
consistently. We could use setlocale(), but that is not thread-safe. We
can also use uselocale(), which is thread-safe, but doesn't cover strtod
(Yeah, some of the locale-aware functions only acknowledge the global
locale, not the thread local one).
So in frustration, I just wrote a simple floating point number parser
myself. This parser obviously doesn't cover all cases strtod covers, but
is good enough for our needs.
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2019-02-01 20:22:41 -05:00
|
|
|
/// Parse a floating point number of form (+|-)?[0-9]*(\.[0-9]*)
|
|
|
|
double strtod_simple(const char *src, const char **end) {
|
2019-03-10 08:34:37 -04:00
|
|
|
double neg = 1;
|
|
|
|
if (*src == '-') {
|
|
|
|
neg = -1;
|
|
|
|
src++;
|
|
|
|
} else if (*src == '+') {
|
|
|
|
src++;
|
|
|
|
}
|
Parse number locale-independently
Previously we were using glibc's strtod function to parse floating point
numbers. The problem is, strtod is locale dependent. Meaning 7,7 might
be parsed as two numbers (7 and 7) in one locale, and parsed as one
number (7 point 7) in another locale. This is undesirable.
We need to set the locale to a value we know to make number parsing
consistently. We could use setlocale(), but that is not thread-safe. We
can also use uselocale(), which is thread-safe, but doesn't cover strtod
(Yeah, some of the locale-aware functions only acknowledge the global
locale, not the thread local one).
So in frustration, I just wrote a simple floating point number parser
myself. This parser obviously doesn't cover all cases strtod covers, but
is good enough for our needs.
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2019-02-01 20:22:41 -05:00
|
|
|
|
2019-03-10 08:34:37 -04:00
|
|
|
double ret = 0;
|
|
|
|
while (*src >= '0' && *src <= '9') {
|
|
|
|
ret = ret * 10 + (*src - '0');
|
|
|
|
src++;
|
|
|
|
}
|
Parse number locale-independently
Previously we were using glibc's strtod function to parse floating point
numbers. The problem is, strtod is locale dependent. Meaning 7,7 might
be parsed as two numbers (7 and 7) in one locale, and parsed as one
number (7 point 7) in another locale. This is undesirable.
We need to set the locale to a value we know to make number parsing
consistently. We could use setlocale(), but that is not thread-safe. We
can also use uselocale(), which is thread-safe, but doesn't cover strtod
(Yeah, some of the locale-aware functions only acknowledge the global
locale, not the thread local one).
So in frustration, I just wrote a simple floating point number parser
myself. This parser obviously doesn't cover all cases strtod covers, but
is good enough for our needs.
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2019-02-01 20:22:41 -05:00
|
|
|
|
2019-03-10 08:34:37 -04:00
|
|
|
if (*src == '.') {
|
|
|
|
double frac = 0, mult = 0.1;
|
|
|
|
src++;
|
|
|
|
while (*src >= '0' && *src <= '9') {
|
|
|
|
frac += mult * (*src - '0');
|
|
|
|
mult *= 0.1;
|
|
|
|
src++;
|
|
|
|
}
|
|
|
|
ret += frac;
|
|
|
|
}
|
Parse number locale-independently
Previously we were using glibc's strtod function to parse floating point
numbers. The problem is, strtod is locale dependent. Meaning 7,7 might
be parsed as two numbers (7 and 7) in one locale, and parsed as one
number (7 point 7) in another locale. This is undesirable.
We need to set the locale to a value we know to make number parsing
consistently. We could use setlocale(), but that is not thread-safe. We
can also use uselocale(), which is thread-safe, but doesn't cover strtod
(Yeah, some of the locale-aware functions only acknowledge the global
locale, not the thread local one).
So in frustration, I just wrote a simple floating point number parser
myself. This parser obviously doesn't cover all cases strtod covers, but
is good enough for our needs.
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2019-02-01 20:22:41 -05:00
|
|
|
|
2019-03-10 08:34:37 -04:00
|
|
|
*end = src;
|
|
|
|
return ret * neg;
|
Parse number locale-independently
Previously we were using glibc's strtod function to parse floating point
numbers. The problem is, strtod is locale dependent. Meaning 7,7 might
be parsed as two numbers (7 and 7) in one locale, and parsed as one
number (7 point 7) in another locale. This is undesirable.
We need to set the locale to a value we know to make number parsing
consistently. We could use setlocale(), but that is not thread-safe. We
can also use uselocale(), which is thread-safe, but doesn't cover strtod
(Yeah, some of the locale-aware functions only acknowledge the global
locale, not the thread local one).
So in frustration, I just wrote a simple floating point number parser
myself. This parser obviously doesn't cover all cases strtod covers, but
is good enough for our needs.
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2019-02-01 20:22:41 -05:00
|
|
|
}
|
2019-03-18 18:34:59 -04:00
|
|
|
|
|
|
|
TEST_CASE(strtod_simple) {
|
|
|
|
const char *end;
|
|
|
|
double result = strtod_simple("1.0", &end);
|
|
|
|
TEST_EQUAL(result, 1);
|
|
|
|
TEST_EQUAL(*end, '\0');
|
|
|
|
|
|
|
|
result = strtod_simple("-1.0", &end);
|
|
|
|
TEST_EQUAL(result, -1);
|
|
|
|
TEST_EQUAL(*end, '\0');
|
|
|
|
|
|
|
|
result = strtod_simple("+.5", &end);
|
|
|
|
TEST_EQUAL(result, 0.5);
|
|
|
|
TEST_EQUAL(*end, '\0');
|
|
|
|
}
|
2022-07-17 10:30:42 -04:00
|
|
|
|
|
|
|
const char *trim_both(const char *src, size_t *length) {
|
|
|
|
size_t i = 0;
|
|
|
|
while (isspace(src[i])) {
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
size_t j = strlen(src) - 1;
|
|
|
|
while (j > i && isspace(src[j])) {
|
|
|
|
j--;
|
|
|
|
}
|
|
|
|
*length = j - i + 1;
|
|
|
|
return src + i;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_CASE(trim_both) {
|
|
|
|
size_t length;
|
|
|
|
const char *str = trim_both(" \t\n\r\f", &length);
|
|
|
|
TEST_EQUAL(length, 0);
|
|
|
|
TEST_EQUAL(*str, '\0');
|
|
|
|
|
|
|
|
str = trim_both(" asdfas ", &length);
|
|
|
|
TEST_EQUAL(length, 6);
|
|
|
|
TEST_STRNEQUAL(str, "asdfas", length);
|
|
|
|
|
|
|
|
str = trim_both(" asdf asdf ", &length);
|
|
|
|
TEST_EQUAL(length, 9);
|
|
|
|
TEST_STRNEQUAL(str, "asdf asdf", length);
|
|
|
|
}
|