mirror of
https://github.com/ElementsProject/lightning.git
synced 2024-11-19 09:54:16 +01:00
common: avoid locale dependent strtod(3)
Replace `json_to_double()` (which uses `strtod(3)`) with our own floating-point parsing function `json_to_millionths()` that specifically expects to receive such a number that can fit in a 64 bit integer after being multiplied by 1 million. The main piece of the code in this patch comes from https://github.com/ElementsProject/lightning/pull/3535#discussion_r381041419 Changelog-None
This commit is contained in:
parent
89ceb273f5
commit
73ad9b5c0a
@ -13,8 +13,10 @@
|
||||
#include <common/json.h>
|
||||
#include <common/json_stream.h>
|
||||
#include <common/node_id.h>
|
||||
#include <common/overflows.h>
|
||||
#include <common/utils.h>
|
||||
#include <common/wireaddr.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdarg.h>
|
||||
@ -97,13 +99,45 @@ bool json_to_s64(const char *buffer, const jsmntok_t *tok, s64 *num)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool json_to_double(const char *buffer, const jsmntok_t *tok, double *num)
|
||||
bool json_to_millionths(const char *buffer, const jsmntok_t *tok,
|
||||
u64 *millionths)
|
||||
{
|
||||
char *end;
|
||||
int decimal_places = -1;
|
||||
bool has_digits = 0;
|
||||
|
||||
*num = strtod(buffer + tok->start, &end);
|
||||
if (end != buffer + tok->end)
|
||||
*millionths = 0;
|
||||
for (int i = tok->start; i < tok->end; i++) {
|
||||
if (isdigit(buffer[i])) {
|
||||
has_digits = true;
|
||||
/* Ignore too much precision */
|
||||
if (decimal_places >= 0 && ++decimal_places > 6)
|
||||
continue;
|
||||
if (mul_overflows_u64(*millionths, 10))
|
||||
return false;
|
||||
*millionths *= 10;
|
||||
if (add_overflows_u64(*millionths, buffer[i] - '0'))
|
||||
return false;
|
||||
*millionths += buffer[i] - '0';
|
||||
} else if (buffer[i] == '.') {
|
||||
if (decimal_places != -1)
|
||||
return false;
|
||||
decimal_places = 0;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!has_digits)
|
||||
return false;
|
||||
|
||||
if (decimal_places == -1)
|
||||
decimal_places = 0;
|
||||
|
||||
while (decimal_places < 6) {
|
||||
if (mul_overflows_u64(*millionths, 10))
|
||||
return false;
|
||||
*millionths *= 10;
|
||||
decimal_places++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -69,8 +69,13 @@ bool json_to_u32(const char *buffer, const jsmntok_t *tok,
|
||||
bool json_to_u16(const char *buffer, const jsmntok_t *tok,
|
||||
uint16_t *num);
|
||||
|
||||
/* Extract double from this (must be a number literal) */
|
||||
bool json_to_double(const char *buffer, const jsmntok_t *tok, double *num);
|
||||
/*
|
||||
* Extract a non-negative (either 0 or positive) floating-point number from this
|
||||
* (must be a number literal), multiply it by 1 million and return it as an
|
||||
* integer. Any fraction smaller than 0.000001 is ignored.
|
||||
*/
|
||||
bool json_to_millionths(const char *buffer, const jsmntok_t *tok,
|
||||
u64 *millionths);
|
||||
|
||||
/* Extract signed integer from this (may be a string, or a number literal) */
|
||||
bool json_to_int(const char *buffer, const jsmntok_t *tok, int *num);
|
||||
|
@ -39,12 +39,9 @@ struct command_result *param_millionths(struct command *cmd, const char *name,
|
||||
const char *buffer,
|
||||
const jsmntok_t *tok, uint64_t **num)
|
||||
{
|
||||
double d;
|
||||
if (json_to_double(buffer, tok, &d) && d >= 0.0) {
|
||||
*num = tal(cmd, uint64_t);
|
||||
**num = (uint64_t)(d * 1000000);
|
||||
*num = tal(cmd, uint64_t);
|
||||
if (json_to_millionths(buffer, tok, *num))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return command_fail(
|
||||
cmd, JSONRPC2_INVALID_PARAMS,
|
||||
|
@ -46,6 +46,62 @@ static int test_json_tok_bitcoin_amount(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void do_json_tok_millionths(const char *val, bool ok, uint64_t expected)
|
||||
{
|
||||
uint64_t amount;
|
||||
jsmntok_t tok;
|
||||
|
||||
tok.start = 0;
|
||||
tok.end = strlen(val);
|
||||
|
||||
assert(json_to_millionths(val, &tok, &amount) == ok);
|
||||
if (ok)
|
||||
assert(amount == expected);
|
||||
}
|
||||
|
||||
static int test_json_tok_millionths(void)
|
||||
{
|
||||
do_json_tok_millionths("", false, 0);
|
||||
do_json_tok_millionths("0..0", false, 0);
|
||||
do_json_tok_millionths("0.0.", false, 0);
|
||||
do_json_tok_millionths(".", false, 0);
|
||||
do_json_tok_millionths("..", false, 0);
|
||||
|
||||
do_json_tok_millionths("0", true, 0);
|
||||
do_json_tok_millionths(".0", true, 0);
|
||||
do_json_tok_millionths("0.", true, 0);
|
||||
do_json_tok_millionths("100", true, 100 * 1000000);
|
||||
do_json_tok_millionths("100.0", true, 100 * 1000000);
|
||||
do_json_tok_millionths("100.", true, 100 * 1000000);
|
||||
do_json_tok_millionths("100.000001", true, 100 * 1000000 + 1);
|
||||
do_json_tok_millionths("100.0000001", true, 100 * 1000000);
|
||||
do_json_tok_millionths(".000009", true, 9);
|
||||
do_json_tok_millionths(".0000099", true, 9);
|
||||
do_json_tok_millionths("18446744073709.551615", true,
|
||||
18446744073709551615ULL);
|
||||
do_json_tok_millionths("18446744073709.551616", false, 0);
|
||||
do_json_tok_millionths("18446744073709.551625", false, 0);
|
||||
do_json_tok_millionths("18446744073709.551715", false, 0);
|
||||
do_json_tok_millionths("18446744073709.552615", false, 0);
|
||||
do_json_tok_millionths("18446744073709.561615", false, 0);
|
||||
do_json_tok_millionths("18446744073709.651615", false, 0);
|
||||
do_json_tok_millionths("18446744073710.551615", false, 0);
|
||||
do_json_tok_millionths("18446744073809.551615", false, 0);
|
||||
do_json_tok_millionths("18446744074709.551615", false, 0);
|
||||
do_json_tok_millionths("18446744083709.551615", false, 0);
|
||||
do_json_tok_millionths("18446744173709.551615", false, 0);
|
||||
do_json_tok_millionths("18446745073709.551615", false, 0);
|
||||
do_json_tok_millionths("18446754073709.551615", false, 0);
|
||||
do_json_tok_millionths("18446844073709.551615", false, 0);
|
||||
do_json_tok_millionths("18447744073709.551615", false, 0);
|
||||
do_json_tok_millionths("18456744073709.551615", false, 0);
|
||||
do_json_tok_millionths("18546744073709.551615", false, 0);
|
||||
do_json_tok_millionths("19446744073709.551615", false, 0);
|
||||
do_json_tok_millionths("28446744073709.551615", false, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void test_json_tok_size(void)
|
||||
{
|
||||
const jsmntok_t *toks;
|
||||
@ -190,6 +246,7 @@ int main(void)
|
||||
|
||||
test_json_tok_size();
|
||||
test_json_tok_bitcoin_amount();
|
||||
test_json_tok_millionths();
|
||||
test_json_delve();
|
||||
assert(!taken_any());
|
||||
take_cleanup();
|
||||
|
Loading…
Reference in New Issue
Block a user