From df5b6365bd690815311f1ae158c6aa8942592c54 Mon Sep 17 00:00:00 2001 From: Jordan Lee Date: Sat, 15 Dec 2012 00:01:59 +0000 Subject: [PATCH] (trunk, libT) faster JSON parsing for tr_variant. This mostly helps the Qt client, which makes heavy use of the JSON-based RPC calls. --- daemon/remote.c | 2 +- libtransmission/makemeta.c | 4 +- libtransmission/resume.c | 3 +- libtransmission/rpcimpl.c | 6 +- libtransmission/variant-json.c | 114 ++++++++++++++++++++------------- libtransmission/variant.c | 32 ++++----- libtransmission/variant.h | 3 +- 7 files changed, 96 insertions(+), 68 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 3f6d3160b..c860d1f93 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -563,7 +563,7 @@ addIdArg (tr_variant * args, const char * id) if (!isdigit (*pch)) isNum = false; if (isNum || isList) - tr_rpc_parse_list_str (tr_variantDictAdd (args, "ids"), id, strlen (id)); + tr_rpc_parse_list_str (tr_variantDictAdd (args, "ids", 3), id, strlen (id)); else tr_variantDictAddStr (args, "ids", id); /* it's a torrent sha hash */ } diff --git a/libtransmission/makemeta.c b/libtransmission/makemeta.c index a9e4d9e3e..9e646274f 100644 --- a/libtransmission/makemeta.c +++ b/libtransmission/makemeta.c @@ -336,8 +336,8 @@ makeInfoDict (tr_variant * dict, for (i = 0; i < builder->fileCount; ++i) { tr_variant * d = tr_variantListAddDict (list, 2); - tr_variant * length = tr_variantDictAdd (d, "length"); - tr_variant * pathVal = tr_variantDictAdd (d, "path"); + tr_variant * length = tr_variantDictAdd (d, "length", 6); + tr_variant * pathVal = tr_variantDictAdd (d, "path", 4); getFileInfo (builder->top, &builder->files[i], length, pathVal); } } diff --git a/libtransmission/resume.c b/libtransmission/resume.c index db4a94d52..16fdb7766 100644 --- a/libtransmission/resume.c +++ b/libtransmission/resume.c @@ -66,6 +66,7 @@ #define KEY_PROGRESS_MTIMES "mtimes" #define KEY_PROGRESS_BITFIELD "bitfield" #define KEY_PROGRESS_BLOCKS "blocks" +#define KEY_PROGRESS_BLOCKS_STRLEN 6 #define KEY_PROGRESS_HAVE "have" enum @@ -480,7 +481,7 @@ saveProgress (tr_variant * dict, tr_torrent * tor) /* add the blocks bitfield */ bitfieldToBenc (&tor->completion.blockBitfield, - tr_variantDictAdd (prog, KEY_PROGRESS_BLOCKS)); + tr_variantDictAdd (prog, KEY_PROGRESS_BLOCKS, KEY_PROGRESS_BLOCKS_STRLEN)); } static uint64_t diff --git a/libtransmission/rpcimpl.c b/libtransmission/rpcimpl.c index 144add9a4..fe0e6ddd8 100644 --- a/libtransmission/rpcimpl.c +++ b/libtransmission/rpcimpl.c @@ -630,7 +630,7 @@ addField (tr_torrent * const tor, else if (tr_streq (key, keylen, "peer-limit")) tr_variantDictAddInt (d, key, tr_torrentGetPeerLimit (tor)); else if (tr_streq (key, keylen, "peers")) - addPeers (tor, tr_variantDictAdd (d, key)); + addPeers (tor, tr_variantDictAdd (d, key, keylen)); else if (tr_streq (key, keylen, "peersConnected")) tr_variantDictAddInt (d, key, st->peersConnected); else if (tr_streq (key, keylen, "peersFrom")) @@ -1332,7 +1332,7 @@ addTorrentImpl (struct tr_rpc_idle_data * data, tr_ctor * ctor) tr_variantListAddStr (&fields, "id"); tr_variantListAddStr (&fields, "name"); tr_variantListAddStr (&fields, "hashString"); - addInfo (tor, tr_variantDictAdd (data->args_out, "torrent-added"), &fields); + addInfo (tor, tr_variantDictAdd (data->args_out, "torrent-added", 13), &fields); notify (data->session, TR_RPC_TORRENT_ADDED, tor); tr_variantFree (&fields); } @@ -1970,7 +1970,7 @@ tr_rpc_request_exec_uri (tr_session * session, char * key = tr_strndup (pch, delim - pch); int isArg = strcmp (key, "method") && strcmp (key, "tag"); tr_variant * parent = isArg ? args : ⊤ - tr_rpc_parse_list_str (tr_variantDictAdd (parent, key), + tr_rpc_parse_list_str (tr_variantDictAdd (parent, key, delim-pch), delim + 1, next ? (size_t)( next - diff --git a/libtransmission/variant-json.c b/libtransmission/variant-json.c index 75f7fb8ca..01bdb47c6 100644 --- a/libtransmission/variant-json.c +++ b/libtransmission/variant-json.c @@ -43,7 +43,10 @@ struct json_wrapper_data int error; bool has_content; tr_variant * top; - char * key; + const char * key; + size_t keylen; + struct evbuffer * keybuf; + struct evbuffer * strbuf; const char * source; tr_ptrArray stack; }; @@ -69,9 +72,10 @@ get_node (struct jsonsl_st * jsn) } else if (tr_variantIsDict (parent) && (data->key!=NULL)) { - node = tr_variantDictAdd (parent, data->key); - tr_free (data->key); + node = tr_variantDictAdd (parent, data->key, data->keylen); + data->key = NULL; + data->keylen = 0; } return node; @@ -167,46 +171,35 @@ decode_hex_string (const char * in, unsigned int * setme) } static char* -extract_string (jsonsl_t jsn, struct jsonsl_state_st * state, size_t * len) +extract_escaped_string (const char * in, size_t in_len, size_t * len, struct evbuffer * buf) { - const char * in_begin; - const char * in_end; - const char * in_it; - size_t out_buflen; - char * out_buf; - char * out_it; + const char * const in_end = in + in_len; - in_begin = jsn->base + state->pos_begin; - if (*in_begin == '"') - in_begin++; - in_end = jsn->base + state->pos_cur; + evbuffer_drain (buf, evbuffer_get_length (buf)); - out_buflen = (in_end-in_begin)*3 + 1; - out_buf = tr_new0 (char, out_buflen); - out_it = out_buf; - - for (in_it=in_begin; in_it!=in_end;) + while (in < in_end) { bool unescaped = false; - if (*in_it=='\\' && in_end-in_it>=2) + if (*in=='\\' && in_end-in>=2) { - switch (in_it[1]) + switch (in[1]) { - case 'b' : *out_it++ = '\b'; in_it+=2; unescaped = true; break; - case 'f' : *out_it++ = '\f'; in_it+=2; unescaped = true; break; - case 'n' : *out_it++ = '\n'; in_it+=2; unescaped = true; break; - case 'r' : *out_it++ = '\r'; in_it+=2; unescaped = true; break; - case 't' : *out_it++ = '\t'; in_it+=2; unescaped = true; break; - case '/' : *out_it++ = '/' ; in_it+=2; unescaped = true; break; - case '"' : *out_it++ = '"' ; in_it+=2; unescaped = true; break; - case '\\': *out_it++ = '\\'; in_it+=2; unescaped = true; break; + case 'b' : evbuffer_add (buf, "\b", 1); in+=2; unescaped = true; break; + case 'f' : evbuffer_add (buf, "\f", 1); in+=2; unescaped = true; break; + case 'n' : evbuffer_add (buf, "\n", 1); in+=2; unescaped = true; break; + case 'r' : evbuffer_add (buf, "\r", 1); in+=2; unescaped = true; break; + case 't' : evbuffer_add (buf, "\t", 1); in+=2; unescaped = true; break; + case '/' : evbuffer_add (buf, "/" , 1); in+=2; unescaped = true; break; + case '"' : evbuffer_add (buf, "\"" , 1); in+=2; unescaped = true; break; + case '\\': evbuffer_add (buf, "\\", 1); in+=2; unescaped = true; break; case 'u': { - if (in_end - in_it >= 6) + if (in_end - in >= 6) { unsigned int val = 0; - if (decode_hex_string (in_it, &val)) + + if (decode_hex_string (in, &val)) { UTF32 str32_buf[2] = { val, 0 }; const UTF32 * str32_walk = str32_buf; @@ -214,16 +207,15 @@ extract_string (jsonsl_t jsn, struct jsonsl_state_st * state, size_t * len) UTF8 str8_buf[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; UTF8 * str8_walk = str8_buf; UTF8 * str8_end = str8_buf + 8; - + if (ConvertUTF32toUTF8 (&str32_walk, str32_end, &str8_walk, str8_end, 0) == 0) { const size_t len = str8_walk - str8_buf; - memcpy (out_it, str8_buf, len); - out_it += len; + evbuffer_add (buf, str8_buf, len); unescaped = true; } - - in_it += 6; + + in += 6; break; } } @@ -232,13 +224,43 @@ extract_string (jsonsl_t jsn, struct jsonsl_state_st * state, size_t * len) } if (!unescaped) - *out_it++ = *in_it++; + { + evbuffer_add (buf, in, 1); + ++in; + } } - if (len != NULL) - *len = out_it - out_buf; + *len = evbuffer_get_length (buf); + return (char*) evbuffer_pullup (buf, -1); +} - return out_buf; +static const char* +extract_string (jsonsl_t jsn, struct jsonsl_state_st * state, size_t * len, struct evbuffer * buf) +{ + const char * ret; + const char * in_begin; + const char * in_end; + size_t in_len; + + /* figure out where the string is */ + in_begin = jsn->base + state->pos_begin; + if (*in_begin == '"') + in_begin++; + in_end = jsn->base + state->pos_cur; + in_len = in_end - in_begin; + + if (memchr (in_begin, '\\', in_len) == NULL) + { + /* it's not escaped */ + ret = in_begin; + *len = in_len; + } + else + { + ret = extract_escaped_string (in_begin, in_len, len, buf); + } + + return ret; } static void @@ -251,17 +273,15 @@ action_callback_POP (jsonsl_t jsn, if (state->type == JSONSL_T_STRING) { - size_t len = 0; - char * str = extract_string (jsn, state, &len); + size_t len; + const char * str = extract_string (jsn, state, &len, data->strbuf); tr_variantInitStr (get_node (jsn), str, len); data->has_content = true; - tr_free (str); } else if (state->type == JSONSL_T_HKEY) { - char * str = extract_string (jsn, state, NULL); data->has_content = true; - data->key = str; + data->key = extract_string (jsn, state, &data->keylen, data->keybuf); } else if ((state->type == JSONSL_T_LIST) || (state->type == JSONSL_T_OBJECT)) { @@ -319,6 +339,8 @@ tr_jsonParse (const char * source, data.top = setme_benc; data.stack = TR_PTR_ARRAY_INIT; data.source = source; + data.keybuf = evbuffer_new (); + data.strbuf = evbuffer_new (); /* parse it */ jsonsl_feed (jsn, vbuf, len); @@ -333,6 +355,8 @@ tr_jsonParse (const char * source, /* cleanup */ error = data.error; + evbuffer_free (data.keybuf); + evbuffer_free (data.strbuf); tr_ptrArrayDestruct (&data.stack, NULL); jsonsl_destroy (jsn); return error; diff --git a/libtransmission/variant.c b/libtransmission/variant.c index 4513059f4..b5e997b09 100644 --- a/libtransmission/variant.c +++ b/libtransmission/variant.c @@ -88,17 +88,18 @@ dictIndexOf (const tr_variant * dict, const char * key) { if (tr_variantIsDict (dict)) { - size_t i; + const tr_variant * walk; + const tr_variant * const begin = dict->val.l.vals; + const tr_variant * const end = begin + dict->val.l.count; const size_t len = strlen (key); - for (i=0; (i+1) < dict->val.l.count; i += 2) - { - const tr_variant * child = dict->val.l.vals + i; - if ((child->type == TR_VARIANT_TYPE_STR) - && (child->val.s.len == len) - && !memcmp (getStr (child), key, len)) - return i; - } + for (walk=begin; walk!=end; walk+=2) + { + assert (walk->type == TR_VARIANT_TYPE_STR); + + if ((walk->val.s.len==len) && !memcmp (getStr(walk), key, len)) + return walk - begin; + } } return -1; @@ -527,7 +528,8 @@ tr_variantListAddDict (tr_variant * list, tr_variant * tr_variantDictAdd (tr_variant * dict, - const char * key) + const char * key, + int keylen) { tr_variant * child_key; tr_variant * child_val; @@ -537,7 +539,7 @@ tr_variantDictAdd (tr_variant * dict, containerReserve (dict, 2); child_key = dict->val.l.vals + dict->val.l.count++; - tr_variantInitStr (child_key, key, -1); + tr_variantInitStr (child_key, key, keylen); child_val = dict->val.l.vals + dict->val.l.count++; tr_variantInit (child_val, TR_VARIANT_TYPE_INT); @@ -561,7 +563,7 @@ dictFindOrAdd (tr_variant * dict, const char * key, int type) /* if it doesn't exist, create it */ if (child == NULL) - child = tr_variantDictAdd (dict, key); + child = tr_variantDictAdd (dict, key, -1); return child; } @@ -614,7 +616,7 @@ dictRecycleOrAdd (tr_variant * dict, const char * key) /* if it doesn't exist, create it */ if (child == NULL) - child = tr_variantDictAdd (dict, key); + child = tr_variantDictAdd (dict, key, -1); return child; } @@ -644,7 +646,7 @@ tr_variantDictAddList (tr_variant * dict, const char * key, size_t reserve_count) { - tr_variant * child = tr_variantDictAdd (dict, key); + tr_variant * child = tr_variantDictAdd (dict, key, -1); tr_variantInitList (child, reserve_count); return child; } @@ -654,7 +656,7 @@ tr_variantDictAddDict (tr_variant * dict, const char * key, size_t reserve_count) { - tr_variant * child = tr_variantDictAdd (dict, key); + tr_variant * child = tr_variantDictAdd (dict, key, -1); tr_variantInitDict (child, reserve_count); return child; } diff --git a/libtransmission/variant.h b/libtransmission/variant.h index 065cb750e..07dcc8bbd 100644 --- a/libtransmission/variant.h +++ b/libtransmission/variant.h @@ -320,7 +320,8 @@ int tr_variantDictRemove (tr_variant * dict, const char * key); tr_variant * tr_variantDictAdd (tr_variant * dict, - const char * key); + const char * key, + int keylen); tr_variant * tr_variantDictAddReal (tr_variant * dict, const char * key,