Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wire: remove towire_double() #3535

Merged
merged 3 commits into from
Feb 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 39 additions & 10 deletions common/json.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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)
{
char *end;
bool json_to_millionths(const char *buffer, const jsmntok_t *tok,
u64 *millionths)
{
int decimal_places = -1;
bool has_digits = 0;

*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;
}

*num = strtod(buffer + tok->start, &end);
if (end != buffer + tok->end)
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;
}

Expand Down Expand Up @@ -547,11 +581,6 @@ void json_add_num(struct json_stream *result, const char *fieldname, unsigned in
json_add_member(result, fieldname, false, "%u", value);
}

void json_add_double(struct json_stream *result, const char *fieldname, double value)
{
json_add_member(result, fieldname, false, "%f", value);
}

void json_add_u64(struct json_stream *result, const char *fieldname,
uint64_t value)
{
Expand Down
12 changes: 7 additions & 5 deletions common/json.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -185,9 +190,6 @@ void json_add_escaped_string(struct json_stream *result,
void json_add_literal(struct json_stream *result, const char *fieldname,
const char *literal, int len);
/* '"fieldname" : value' or 'value' if fieldname is NULL */
void json_add_double(struct json_stream *result, const char *fieldname,
double value);
/* '"fieldname" : value' or 'value' if fieldname is NULL */
void json_add_num(struct json_stream *result, const char *fieldname,
unsigned int value);
/* '"fieldname" : value' or 'value' if fieldname is NULL */
Expand Down
30 changes: 9 additions & 21 deletions common/json_tok.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,18 @@ struct command_result *param_bool(struct command *cmd, const char *name,
name, tok->end - tok->start, buffer + tok->start);
}

struct command_result *param_double(struct command *cmd, const char *name,
const char *buffer, const jsmntok_t *tok,
double **num)
struct command_result *param_millionths(struct command *cmd, const char *name,
const char *buffer,
const jsmntok_t *tok, uint64_t **num)
{
*num = tal(cmd, double);
if (json_to_double(buffer, tok, *num))
*num = tal(cmd, uint64_t);
if (json_to_millionths(buffer, tok, *num))
return NULL;

return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"'%s' should be a double, not '%.*s'",
name, tok->end - tok->start, buffer + tok->start);
return command_fail(
cmd, JSONRPC2_INVALID_PARAMS,
"'%s' should be a non-negative floating-point number, not '%.*s'",
name, tok->end - tok->start, buffer + tok->start);
}

struct command_result *param_escaped_string(struct command *cmd,
Expand Down Expand Up @@ -128,19 +129,6 @@ struct command_result *param_sha256(struct command *cmd, const char *name,
name, tok->end - tok->start, buffer + tok->start);
}

struct command_result *param_percent(struct command *cmd, const char *name,
const char *buffer, const jsmntok_t *tok,
double **num)
{
*num = tal(cmd, double);
if (json_to_double(buffer, tok, *num) && **num >= 0.0)
return NULL;

return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"'%s' should be a positive double, not '%.*s'",
name, tok->end - tok->start, buffer + tok->start);
}

struct command_result *param_u64(struct command *cmd, const char *name,
const char *buffer, const jsmntok_t *tok,
uint64_t **num)
Expand Down
17 changes: 8 additions & 9 deletions common/json_tok.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,14 @@ struct command_result *param_bool(struct command *cmd, const char *name,
const char *buffer, const jsmntok_t *tok,
bool **b);

/* Extract double from this (must be a number literal) */
struct command_result *param_double(struct command *cmd, const char *name,
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.
*/
struct command_result *param_millionths(struct command *cmd, const char *name,
const char *buffer,
const jsmntok_t *tok, uint64_t **num);

/* Extract an escaped string (and unescape it) */
struct command_result *param_escaped_string(struct command *cmd,
Expand Down Expand Up @@ -57,11 +61,6 @@ struct command_result *param_sha256(struct command *cmd, const char *name,
const char *buffer, const jsmntok_t *tok,
struct sha256 **hash);

/* Extract double in range [0.0, 100.0] */
struct command_result *param_percent(struct command *cmd, const char *name,
const char *buffer, const jsmntok_t *tok,
double **num);

/* Extract number from this (may be a string, or a number literal) */
struct command_result *param_u64(struct command *cmd, const char *name,
const char *buffer, const jsmntok_t *tok,
Expand Down
57 changes: 57 additions & 0 deletions common/test/run-json.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down
65 changes: 33 additions & 32 deletions common/test/run-param.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,17 +131,17 @@ struct sanity {
char *str;
bool failed;
int ival;
double dval;
u64 fpval; /* floating-point, multiplied by 1000000 */
char *fail_str;
};

struct sanity buffers[] = {
// pass
{"['42', '3.15']", false, 42, 3.15, NULL},
{"{ 'u64' : '42', 'double' : '3.15' }", false, 42, 3.15, NULL},
{"['42', '3.15']", false, 42, 3150000, NULL},
{"{ 'u64' : '42', 'fp' : '3.15' }", false, 42, 3150000, NULL},

// fail
{"{'u64':'42', 'double':'3.15', 'extra':'stuff'}", true, 0, 0,
{"{'u64':'42', 'fp':'3.15', 'extra':'stuff'}", true, 0, 0,
"unknown parameter"},
{"['42', '3.15', 'stuff']", true, 0, 0, "too many"},
{"['42', '3.15', 'null']", true, 0, 0, "too many"},
Expand All @@ -151,17 +151,17 @@ struct sanity buffers[] = {
{"['42']", true, 0, 0, "missing required"},

// fail wrong type
{"{'u64':'hello', 'double':'3.15'}", true, 0, 0, "be an unsigned 64"},
{"{'u64':'hello', 'fp':'3.15'}", true, 0, 0, "be an unsigned 64"},
{"['3.15', '3.15', 'stuff']", true, 0, 0, "integer"},
};

static void stest(const struct json *j, struct sanity *b)
{
u64 *ival;
double *dval;
u64 *fpval;
if (!param(cmd, j->buffer, j->toks,
p_req("u64", param_u64, &ival),
p_req("double", param_double, &dval), NULL)) {
p_req("fp", param_millionths, &fpval), NULL)) {
assert(check_fail());
assert(b->failed == true);
if (!strstr(fail_msg, b->fail_str)) {
Expand All @@ -172,7 +172,7 @@ static void stest(const struct json *j, struct sanity *b)
assert(!check_fail());
assert(b->failed == false);
assert(*ival == 42);
assert(*dval > 3.1499 && b->dval < 3.1501);
assert(*fpval > 3149900 && b->fpval < 3150100);
}
}

Expand Down Expand Up @@ -222,13 +222,13 @@ static void dup_names(void)
{
struct json *j =
json_parse(cmd,
"{ 'u64' : '42', 'u64' : '43', 'double' : '3.15' }");
"{ 'u64' : '42', 'u64' : '43', 'fp' : '3.15' }");

u64 *i;
double *d;
u64 *fp;
assert(!param(cmd, j->buffer, j->toks,
p_req("u64", param_u64, &i),
p_req("double", param_double, &d), NULL));
p_req("fp", param_millionths, &fp), NULL));
}

static void null_params(void)
Expand Down Expand Up @@ -290,28 +290,28 @@ static void bad_programmer(void)
{
u64 *ival;
u64 *ival2;
double *dval;
u64 *fpval;
struct json *j = json_parse(cmd, "[ '25', '546', '26' ]");

/* check for repeated names */
assert(!param(cmd, j->buffer, j->toks,
p_req("repeat", param_u64, &ival),
p_req("double", param_double, &dval),
p_req("fp", param_millionths, &fpval),
p_req("repeat", param_u64, &ival2), NULL));
assert(check_fail());
assert(strstr(fail_msg, "developer error"));

assert(!param(cmd, j->buffer, j->toks,
p_req("repeat", param_u64, &ival),
p_req("double", param_double, &dval),
p_req("fp", param_millionths, &fpval),
p_req("repeat", param_u64, &ival), NULL));
assert(check_fail());
assert(strstr(fail_msg, "developer error"));

assert(!param(cmd, j->buffer, j->toks,
p_req("u64", param_u64, &ival),
p_req("repeat", param_double, &dval),
p_req("repeat", param_double, &dval), NULL));
p_req("repeat", param_millionths, &fpval),
p_req("repeat", param_millionths, &fpval), NULL));
assert(check_fail());
assert(strstr(fail_msg, "developer error"));

Expand All @@ -330,12 +330,13 @@ static void bad_programmer(void)
/* Add required param after optional */
j = json_parse(cmd, "[ '25', '546', '26', '1.1' ]");
unsigned int *msatoshi;
double *riskfactor;
assert(!param(cmd, j->buffer, j->toks,
p_req("u64", param_u64, &ival),
p_req("double", param_double, &dval),
p_opt_def("msatoshi", param_number, &msatoshi, 100),
p_req("riskfactor", param_double, &riskfactor), NULL));
u64 *riskfactor_millionths;
assert(!param(
cmd, j->buffer, j->toks, p_req("u64", param_u64, &ival),
p_req("fp", param_millionths, &fpval),
p_opt_def("msatoshi", param_number, &msatoshi, 100),
p_req("riskfactor", param_millionths, &riskfactor_millionths),
NULL));
assert(*msatoshi);
assert(*msatoshi == 100);
assert(check_fail());
Expand Down Expand Up @@ -525,16 +526,16 @@ static void param_tests(void)
test_cb(param_bool, bool, "[ tru ]", false, false);
test_cb(param_bool, bool, "[ 1 ]", false, false);

test_cb(param_percent, double, "[ -0.01 ]", 0, false);
test_cb(param_percent, double, "[ 0.00 ]", 0, true);
test_cb(param_percent, double, "[ 1 ]", 1, true);
test_cb(param_percent, double, "[ 1.1 ]", 1.1, true);
test_cb(param_percent, double, "[ 1.01 ]", 1.01, true);
test_cb(param_percent, double, "[ 99.99 ]", 99.99, true);
test_cb(param_percent, double, "[ 100.0 ]", 100, true);
test_cb(param_percent, double, "[ 100.001 ]", 100.001, true);
test_cb(param_percent, double, "[ 1000 ]", 1000, true);
test_cb(param_percent, double, "[ 'wow' ]", 0, false);
test_cb(param_millionths, u64, "[ -0.01 ]", 0, false);
test_cb(param_millionths, u64, "[ 0.00 ]", 0, true);
test_cb(param_millionths, u64, "[ 1 ]", 1000000, true);
test_cb(param_millionths, u64, "[ 1.1 ]", 1100000, true);
test_cb(param_millionths, u64, "[ 1.01 ]", 1010000, true);
test_cb(param_millionths, u64, "[ 99.99 ]", 99990000, true);
test_cb(param_millionths, u64, "[ 100.0 ]", 100000000, true);
test_cb(param_millionths, u64, "[ 100.001 ]", 100001000, true);
test_cb(param_millionths, u64, "[ 1000 ]", 1000000000, true);
test_cb(param_millionths, u64, "[ 'wow' ]", 0, false);
}

static void test_invoice(struct command *cmd,
Expand Down
Loading