Browse Source

json: add more efficient iterators for objects and arrays.

Christian points out that we can iterate by ->size rather than calling
json_next() to find the end (which traverses the entire object!).

Now ->size is reliable (since previous patch), this is OK.

Reported-by: @cdecker
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
pylightning-async
Rusty Russell 6 years ago
committed by Christian Decker
parent
commit
82ff580a66
  1. 12
      common/json.c
  2. 11
      common/json.h
  3. 49
      common/param.c
  4. 11
      lightningd/gossip_control.c
  5. 46
      lightningd/invoice.c
  6. 35
      lightningd/pay.c
  7. 34
      plugins/pay.c

12
common/json.c

@ -180,13 +180,13 @@ const jsmntok_t *json_next(const jsmntok_t *tok)
const jsmntok_t *json_get_member(const char *buffer, const jsmntok_t tok[], const jsmntok_t *json_get_member(const char *buffer, const jsmntok_t tok[],
const char *label) const char *label)
{ {
const jsmntok_t *t, *end; const jsmntok_t *t;
size_t i;
if (tok->type != JSMN_OBJECT) if (tok->type != JSMN_OBJECT)
return NULL; return NULL;
end = json_next(tok); json_for_each_obj(i, t, tok)
for (t = tok + 1; t < end; t = json_next(t+1))
if (json_tok_streq(buffer, t, label)) if (json_tok_streq(buffer, t, label))
return t + 1; return t + 1;
@ -195,13 +195,13 @@ const jsmntok_t *json_get_member(const char *buffer, const jsmntok_t tok[],
const jsmntok_t *json_get_arr(const jsmntok_t tok[], size_t index) const jsmntok_t *json_get_arr(const jsmntok_t tok[], size_t index)
{ {
const jsmntok_t *t, *end; const jsmntok_t *t;
size_t i;
if (tok->type != JSMN_ARRAY) if (tok->type != JSMN_ARRAY)
return NULL; return NULL;
end = json_next(tok); json_for_each_arr(i, t, tok) {
for (t = tok + 1; t < end; t = json_next(t)) {
if (index == 0) if (index == 0)
return t; return t;
index--; index--;

11
common/json.h

@ -51,7 +51,7 @@ bool json_tok_is_num(const char *buffer, const jsmntok_t *tok);
/* Is this the null primitive? */ /* Is this the null primitive? */
bool json_tok_is_null(const char *buffer, const jsmntok_t *tok); bool json_tok_is_null(const char *buffer, const jsmntok_t *tok);
/* Returns next token with same parent. */ /* Returns next token with same parent (WARNING: slow!). */
const jsmntok_t *json_next(const jsmntok_t *tok); const jsmntok_t *json_next(const jsmntok_t *tok);
/* Get top-level member. */ /* Get top-level member. */
@ -85,4 +85,13 @@ const jsmntok_t *json_delve(const char *buffer,
const jsmntok_t *tok, const jsmntok_t *tok,
const char *guide); const char *guide);
/* Iterator macro for array: i is counter, t is token ptr, arr is JSMN_ARRAY */
#define json_for_each_arr(i, t, arr) \
for (i = 0, t = (arr) + 1; i < (arr)->size; t = json_next(t), i++)
/* Iterator macro for object: i is counter, t is token ptr (t+1 is
* contents of obj member), obj is JSMN_OBJECT */
#define json_for_each_obj(i, t, obj) \
for (i = 0, t = (obj) + 1; i < (obj)->size; t = json_next(t+1), i++)
#endif /* LIGHTNING_COMMON_JSON_H */ #endif /* LIGHTNING_COMMON_JSON_H */

49
common/param.c

@ -68,27 +68,27 @@ static struct command_result *parse_by_position(struct command *cmd,
bool allow_extra) bool allow_extra)
{ {
struct command_result *res; struct command_result *res;
const jsmntok_t *tok = tokens + 1; const jsmntok_t *tok;
const jsmntok_t *end = json_next(tokens); size_t i;
struct param *first = params;
struct param *last = first + tal_count(params);
while (first != last && tok != end) {
if (!json_tok_is_null(buffer, tok)) {
res = make_callback(cmd, first, buffer, tok);
if (res)
return res;
}
tok = json_next(tok);
first++;
}
json_for_each_arr(i, tok, tokens) {
/* check for unexpected trailing params */ /* check for unexpected trailing params */
if (!allow_extra && tok != end) { if (i == tal_count(params)) {
if (!allow_extra) {
return command_fail(cmd, JSONRPC2_INVALID_PARAMS, return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"too many parameters:" "too many parameters:"
" got %u, expected %zu", " got %u, expected %zu",
tokens->size, tal_count(params)); tokens->size,
tal_count(params));
}
break;
}
if (!json_tok_is_null(buffer, tok)) {
res = make_callback(cmd, params+i, buffer, tok);
if (res)
return res;
}
} }
return post_check(cmd, params); return post_check(cmd, params);
@ -115,18 +115,18 @@ static struct command_result *parse_by_name(struct command *cmd,
const jsmntok_t tokens[], const jsmntok_t tokens[],
bool allow_extra) bool allow_extra)
{ {
const jsmntok_t *first = tokens + 1; size_t i;
const jsmntok_t *last = json_next(tokens); const jsmntok_t *t;
while (first != last) { json_for_each_obj(i, t, tokens) {
struct param *p = find_param(params, buffer + first->start, struct param *p = find_param(params, buffer + t->start,
first->end - first->start); t->end - t->start);
if (!p) { if (!p) {
if (!allow_extra) { if (!allow_extra) {
return command_fail(cmd, JSONRPC2_INVALID_PARAMS, return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"unknown parameter: '%.*s'", "unknown parameter: '%.*s'",
first->end - first->start, t->end - t->start,
buffer + first->start); buffer + t->start);
} }
} else { } else {
struct command_result *res; struct command_result *res;
@ -137,11 +137,10 @@ static struct command_result *parse_by_name(struct command *cmd,
p->name); p->name);
} }
res = make_callback(cmd, p, buffer, first + 1); res = make_callback(cmd, p, buffer, t + 1);
if (res) if (res)
return res; return res;
} }
first = json_next(first + 1);
} }
return post_check(cmd, params); return post_check(cmd, params);
} }

11
lightningd/gossip_control.c

@ -331,15 +331,13 @@ static struct command_result *json_getroute(struct command *cmd,
*fuzz = *fuzz / 100.0; *fuzz = *fuzz / 100.0;
if (excludetok) { if (excludetok) {
const jsmntok_t *t, *end = json_next(excludetok); const jsmntok_t *t;
size_t i; size_t i;
excluded = tal_arr(cmd, struct short_channel_id_dir, excluded = tal_arr(cmd, struct short_channel_id_dir,
excludetok->size); excludetok->size);
for (i = 0, t = excludetok + 1; json_for_each_arr(i, t, excludetok) {
t < end;
t = json_next(t), i++) {
if (!short_channel_id_dir_from_str(buffer + t->start, if (!short_channel_id_dir_from_str(buffer + t->start,
t->end - t->start, t->end - t->start,
&excluded[i])) { &excluded[i])) {
@ -486,7 +484,7 @@ static struct command_result *json_dev_query_scids(struct command *cmd,
{ {
u8 *msg; u8 *msg;
const jsmntok_t *scidstok; const jsmntok_t *scidstok;
const jsmntok_t *t, *end; const jsmntok_t *t;
struct pubkey *id; struct pubkey *id;
struct short_channel_id *scids; struct short_channel_id *scids;
size_t i; size_t i;
@ -498,8 +496,7 @@ static struct command_result *json_dev_query_scids(struct command *cmd,
return command_param_failed(); return command_param_failed();
scids = tal_arr(cmd, struct short_channel_id, scidstok->size); scids = tal_arr(cmd, struct short_channel_id, scidstok->size);
end = json_next(scidstok); json_for_each_arr(i, t, scidstok) {
for (i = 0, t = scidstok + 1; t < end; t = json_next(t), i++) {
if (!json_to_short_channel_id(buffer, t, &scids[i])) { if (!json_to_short_channel_id(buffer, t, &scids[i])) {
return command_fail(cmd, JSONRPC2_INVALID_PARAMS, return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"scid %zu '%.*s' is not an scid", "scid %zu '%.*s' is not an scid",

46
lightningd/invoice.c

@ -307,13 +307,13 @@ static struct route_info *unpack_route(const tal_t *ctx,
const char *buffer, const char *buffer,
const jsmntok_t *routetok) const jsmntok_t *routetok)
{ {
const jsmntok_t *t, *end; const jsmntok_t *t;
struct route_info *route = tal_arr(ctx, struct route_info, 0); size_t i;
struct route_info *route = tal_arr(ctx, struct route_info, routetok->size);
end = json_next(routetok); json_for_each_arr(i, t, routetok) {
for (t = routetok + 1; t < end; t = json_next(t)) {
const jsmntok_t *pubkey, *fee_base, *fee_prop, *scid, *cltv; const jsmntok_t *pubkey, *fee_base, *fee_prop, *scid, *cltv;
struct route_info r; struct route_info *r = &route[i];
u32 cltv_u32; u32 cltv_u32;
pubkey = json_get_member(buffer, t, "id"); pubkey = json_get_member(buffer, t, "id");
@ -323,17 +323,16 @@ static struct route_info *unpack_route(const tal_t *ctx,
"fee_proportional_millionths"); "fee_proportional_millionths");
cltv = json_get_member(buffer, t, "cltv_expiry_delta"); cltv = json_get_member(buffer, t, "cltv_expiry_delta");
if (!json_to_pubkey(buffer, pubkey, &r.pubkey) if (!json_to_pubkey(buffer, pubkey, &r->pubkey)
|| !json_to_short_channel_id(buffer, scid, || !json_to_short_channel_id(buffer, scid,
&r.short_channel_id) &r->short_channel_id)
|| !json_to_number(buffer, fee_base, &r.fee_base_msat) || !json_to_number(buffer, fee_base, &r->fee_base_msat)
|| !json_to_number(buffer, fee_prop, || !json_to_number(buffer, fee_prop,
&r.fee_proportional_millionths) &r->fee_proportional_millionths)
|| !json_to_number(buffer, cltv, &cltv_u32)) || !json_to_number(buffer, cltv, &cltv_u32))
abort(); abort();
/* We don't have a json_to_u16 */ /* We don't have a json_to_u16 */
r.cltv_expiry_delta = cltv_u32; r->cltv_expiry_delta = cltv_u32;
tal_arr_expand(&route, r);
} }
return route; return route;
} }
@ -343,17 +342,16 @@ static struct route_info **unpack_routes(const tal_t *ctx,
const jsmntok_t *routestok) const jsmntok_t *routestok)
{ {
struct route_info **routes; struct route_info **routes;
const jsmntok_t *t, *end; const jsmntok_t *t;
size_t i;
if (!routestok) if (!routestok)
return NULL; return NULL;
routes = tal_arr(ctx, struct route_info *, 0); routes = tal_arr(ctx, struct route_info *, routestok->size);
end = json_next(routestok); json_for_each_arr(i, t, routestok)
for (t = routestok + 1; t < end; t = json_next(t)) { routes[i] = unpack_route(routes, buffer, t);
struct route_info *r = unpack_route(routes, buffer, t);
tal_arr_expand(&routes, r);
}
return routes; return routes;
} }
#endif /* DEVELOPER */ #endif /* DEVELOPER */
@ -409,16 +407,16 @@ static struct command_result *json_invoice(struct command *cmd,
} }
if (fallbacks) { if (fallbacks) {
const jsmntok_t *i, *end = json_next(fallbacks); size_t i;
const jsmntok_t *t;
fallback_scripts = tal_arr(cmd, const u8 *, 0); fallback_scripts = tal_arr(cmd, const u8 *, fallbacks->size);
for (i = fallbacks + 1; i < end; i = json_next(i)) { json_for_each_arr(i, t, fallbacks) {
struct command_result *r; struct command_result *r;
const u8 *fs;
r = parse_fallback(cmd, buffer, i, &fs); r = parse_fallback(cmd, buffer, t, &fallback_scripts[i]);
if (r) if (r)
return r; return r;
tal_arr_expand(&fallback_scripts, fs);
} }
} }

35
lightningd/pay.c

@ -981,8 +981,8 @@ static struct command_result *json_sendpay(struct command *cmd,
const jsmntok_t *params) const jsmntok_t *params)
{ {
const jsmntok_t *routetok; const jsmntok_t *routetok;
const jsmntok_t *t, *end; const jsmntok_t *t;
size_t n_hops; size_t i;
struct sha256 *rhash; struct sha256 *rhash;
struct route_hop *route; struct route_hop *route;
u64 *msatoshi; u64 *msatoshi;
@ -996,11 +996,11 @@ static struct command_result *json_sendpay(struct command *cmd,
NULL)) NULL))
return command_param_failed(); return command_param_failed();
end = json_next(routetok); if (routetok->size == 0)
n_hops = 0; return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Empty route");
route = tal_arr(cmd, struct route_hop, n_hops);
for (t = routetok + 1; t < end; t = json_next(t)) { route = tal_arr(cmd, struct route_hop, routetok->size);
json_for_each_arr(i, t, routetok) {
u64 *amount; u64 *amount;
struct pubkey *id; struct pubkey *id;
struct short_channel_id *channel; struct short_channel_id *channel;
@ -1015,19 +1015,12 @@ static struct command_result *json_sendpay(struct command *cmd,
NULL)) NULL))
return command_param_failed(); return command_param_failed();
tal_resize(&route, n_hops + 1); route[i].amount = *amount;
route[i].nodeid = *id;
route[n_hops].amount = *amount; route[i].delay = *delay;
route[n_hops].nodeid = *id; route[i].channel_id = *channel;
route[n_hops].delay = *delay;
route[n_hops].channel_id = *channel;
/* FIXME: Actually ignored by sending code! */ /* FIXME: Actually ignored by sending code! */
route[n_hops].direction = direction ? *direction : 0; route[i].direction = direction ? *direction : 0;
n_hops++;
}
if (n_hops == 0) {
return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Empty route");
} }
/* The given msatoshi is the actual payment that the payee is /* The given msatoshi is the actual payment that the payee is
@ -1036,8 +1029,8 @@ static struct command_result *json_sendpay(struct command *cmd,
/* if not: msatoshi <= finalhop.amount <= 2 * msatoshi, fail. */ /* if not: msatoshi <= finalhop.amount <= 2 * msatoshi, fail. */
if (msatoshi) { if (msatoshi) {
if (!(*msatoshi <= route[n_hops-1].amount && if (!(*msatoshi <= route[routetok->size-1].amount &&
route[n_hops-1].amount <= 2 * *msatoshi)) { route[routetok->size-1].amount <= 2 * *msatoshi)) {
return command_fail(cmd, JSONRPC2_INVALID_PARAMS, return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"msatoshi %"PRIu64" out of range", "msatoshi %"PRIu64" out of range",
*msatoshi); *msatoshi);
@ -1045,7 +1038,7 @@ static struct command_result *json_sendpay(struct command *cmd,
} }
if (send_payment(cmd, cmd->ld, rhash, route, if (send_payment(cmd, cmd->ld, rhash, route,
msatoshi ? *msatoshi : route[n_hops-1].amount, msatoshi ? *msatoshi : route[routetok->size-1].amount,
description, description,
&json_sendpay_on_resolve, cmd)) &json_sendpay_on_resolve, cmd))
return command_still_pending(cmd); return command_still_pending(cmd);

34
plugins/pay.c

@ -345,15 +345,15 @@ static const jsmntok_t *find_worst_channel(const char *buf,
u64 final) u64 final)
{ {
u64 prev = final, worstval = 0; u64 prev = final, worstval = 0;
const jsmntok_t *worst = NULL, *end; const jsmntok_t *worst = NULL, *t;
size_t i;
end = json_next(route); json_for_each_arr(i, t, route) {
for (route = route + 1; route < end; route = json_next(route)) {
u64 val; u64 val;
json_to_u64(buf, json_get_member(buf, route, fieldname), &val); json_to_u64(buf, json_get_member(buf, t, fieldname), &val);
if (worst == NULL || val - prev > worstval) { if (worst == NULL || val - prev > worstval) {
worst = route; worst = t;
worstval = val - prev; worstval = val - prev;
} }
prev = val; prev = val;
@ -616,12 +616,12 @@ static struct command_result *add_shadow_route(struct command *cmd,
{ {
/* Use reservoir sampling across the capable channels. */ /* Use reservoir sampling across the capable channels. */
const jsmntok_t *channels = json_get_member(buf, result, "channels"); const jsmntok_t *channels = json_get_member(buf, result, "channels");
const jsmntok_t *chan, *end, *best = NULL; const jsmntok_t *chan, *best = NULL;
size_t i;
u64 sample; u64 sample;
u32 cltv, best_cltv; u32 cltv, best_cltv;
end = json_next(channels); json_for_each_arr(i, chan, channels) {
for (chan = channels + 1; chan < end; chan = json_next(chan)) {
u64 sats, v; u64 sats, v;
json_to_u64(buf, json_get_member(buf, chan, "satoshis"), &sats); json_to_u64(buf, json_get_member(buf, chan, "satoshis"), &sats);
@ -676,24 +676,24 @@ static struct command_result *listpeers_done(struct command *cmd,
const jsmntok_t *result, const jsmntok_t *result,
struct pay_command *pc) struct pay_command *pc)
{ {
const jsmntok_t *peer, *peers_end; const jsmntok_t *peers, *peer;
size_t i;
char *mods = tal_strdup(tmpctx, ""); char *mods = tal_strdup(tmpctx, "");
peer = json_get_member(buf, result, "peers"); peers = json_get_member(buf, result, "peers");
if (!peer) if (!peers)
plugin_err("listpeers gave no 'peers'? '%.*s'", plugin_err("listpeers gave no 'peers'? '%.*s'",
result->end - result->start, buf); result->end - result->start, buf);
peers_end = json_next(peer); json_for_each_arr(i, peer, peers) {
for (peer = peer + 1; peer < peers_end; peer = json_next(peer)) { const jsmntok_t *chans, *chan;
const jsmntok_t *chan, *chans_end;
bool connected; bool connected;
size_t j;
json_to_bool(buf, json_get_member(buf, peer, "connected"), json_to_bool(buf, json_get_member(buf, peer, "connected"),
&connected); &connected);
chan = json_get_member(buf, peer, "channels"); chans = json_get_member(buf, peer, "channels");
chans_end = json_next(chan); json_for_each_arr(j, chan, chans) {
for (chan = chan + 1; chan < chans_end; chan = json_next(chan)) {
const jsmntok_t *state, *scid, *dir; const jsmntok_t *state, *scid, *dir;
u64 spendable; u64 spendable;

Loading…
Cancel
Save