(trunk, libT) more tr_variant revision: faster serialization, dictionaries use less space

This commit is contained in:
Jordan Lee 2012-12-24 22:38:41 +00:00
parent e19fd611c7
commit 94b5e88d36
6 changed files with 315 additions and 461 deletions

View File

@ -11,12 +11,10 @@
*/
#include <assert.h>
#include <ctype.h> /* isdigit () */
#include <ctype.h> /* isdigit() */
#include <errno.h>
#include <stdlib.h> /* strtoul (), strtod (), realloc (), qsort (), mkstemp () */
#include <string.h> /* strlen (), memchr () */
#include <locale.h> /* setlocale () */
#include <stdlib.h> /* strtoul(), strtod(), realloc(), qsort(), mkstemp() */
#include <string.h> /* strlen(), memchr() */
#include <event2/buffer.h>
@ -25,13 +23,13 @@
#define __LIBTRANSMISSION_VARIANT_MODULE___
#include "transmission.h"
#include "ptrarray.h"
#include "utils.h" /* tr_strlcpy () */
#include "utils.h" /* tr_snprintf() */
#include "variant.h"
#include "variant-common.h"
/***
**** tr_variantParse ()
**** tr_variantLoad ()
**** tr_variantParse()
**** tr_variantLoad()
***/
/**
@ -117,68 +115,35 @@ tr_bencParseStr (const uint8_t * buf,
return 0;
}
static int
makeroom (tr_variant * container, size_t count)
static tr_variant*
get_node (tr_ptrArray * stack, tr_quark * key, tr_variant * top, int * err)
{
const size_t needed = container->val.l.count + count;
tr_variant * node = NULL;
assert (tr_variantIsContainer (container));
if (needed > container->val.l.alloc)
if (tr_ptrArrayEmpty (stack))
{
size_t n;
void * tmp;
node = top;
}
else
{
tr_variant * parent = tr_ptrArrayBack (stack);
/* scale the alloc size in powers-of-2 */
n = container->val.l.alloc ? container->val.l.alloc : 8;
while (n < needed)
n *= 2u;
tmp = realloc (container->val.l.vals, n * sizeof (tr_variant));
if (tmp == NULL)
return 1;
container->val.l.alloc = n;
container->val.l.vals = tmp;
if (tr_variantIsList (parent))
{
node = tr_variantListAdd (parent);
}
else if (*key && tr_variantIsDict (parent))
{
node = tr_variantDictAdd (parent, *key);
*key = 0;
}
else
{
*err = EILSEQ;
}
}
return 0;
}
static inline bool
isReadyForDictKey (tr_ptrArray * parentStack)
{
tr_variant * parent = tr_ptrArrayBack (parentStack);
return (parent != NULL)
&& (tr_variantIsDict(parent))
&& ((parent->val.l.count%2)==0);
}
static tr_variant*
getNode (tr_variant * top,
tr_ptrArray * parentStack,
int type)
{
tr_variant * parent;
assert (top);
assert (parentStack);
if (tr_ptrArrayEmpty (parentStack))
return top;
parent = tr_ptrArrayBack (parentStack);
assert (parent);
/* dictionary keys must be strings */
if ((parent->type == TR_VARIANT_TYPE_DICT)
&& (type != TR_VARIANT_TYPE_STR)
&& (! (parent->val.l.count % 2)))
return NULL;
makeroom (parent, 1);
return parent->val.l.vals + parent->val.l.count++;
return node;
}
/**
@ -186,137 +151,113 @@ getNode (tr_variant * top,
* easier to read, but was vulnerable to a smash-stacking
* attack via maliciously-crafted bencoded data. (#667)
*/
static int
tr_variantParseImpl (const void * buf_in,
int
tr_variantParseBenc (const void * buf_in,
const void * bufend_in,
tr_variant * top,
tr_ptrArray * parentStack,
const char ** setme_end)
{
int err;
int err = 0;
const uint8_t * buf = buf_in;
const uint8_t * bufend = bufend_in;
tr_ptrArray stack = TR_PTR_ARRAY_INIT;
tr_quark key = 0;
tr_variantInit (top, 0);
while (buf != bufend)
{
if (buf > bufend) /* no more text to parse... */
return 1;
err = EILSEQ;
if (err)
break;
if (*buf == 'i') /* int */
{
int64_t val;
const uint8_t * end;
tr_variant * node;
tr_variant * v;
if ((err = tr_bencParseInt (buf, bufend, &end, &val)))
return err;
node = getNode (top, parentStack, TR_VARIANT_TYPE_INT);
if (!node)
return EILSEQ;
tr_variantInitInt (node, val);
break;
buf = end;
if (tr_ptrArrayEmpty (parentStack))
break;
if ((v = get_node (&stack, &key, top, &err)))
tr_variantInitInt (v, val);
}
else if (*buf == 'l') /* list */
{
tr_variant * node = getNode (top, parentStack, TR_VARIANT_TYPE_LIST);
if (!node)
return EILSEQ;
tr_variant * v;
tr_variantInit (node, TR_VARIANT_TYPE_LIST);
tr_ptrArrayAppend (parentStack, node);
++buf;
if ((v = get_node (&stack, &key, top, &err)))
{
tr_variantInitList (v, 0);
tr_ptrArrayAppend (&stack, v);
}
}
else if (*buf == 'd') /* dict */
{
tr_variant * node = getNode (top, parentStack, TR_VARIANT_TYPE_DICT);
if (!node)
return EILSEQ;
tr_variant * v;
tr_variantInit (node, TR_VARIANT_TYPE_DICT);
tr_ptrArrayAppend (parentStack, node);
++buf;
if ((v = get_node (&stack, &key, top, &err)))
{
tr_variantInitDict (v, 0);
tr_ptrArrayAppend (&stack, v);
}
}
else if (*buf == 'e') /* end of list or dict */
{
tr_variant * node;
++buf;
if (tr_ptrArrayEmpty (parentStack))
return EILSEQ;
node = tr_ptrArrayBack (parentStack);
if (tr_variantIsDict (node) && (node->val.l.count % 2))
if (tr_ptrArrayEmpty (&stack) || (key != 0))
{
/* odd # of children in dict */
tr_variantFree (&node->val.l.vals[--node->val.l.count]);
return EILSEQ;
err = EILSEQ;
break;
}
else
{
tr_ptrArrayPop (&stack);
if (tr_ptrArrayEmpty (&stack))
break;
}
tr_ptrArrayPop (parentStack);
if (tr_ptrArrayEmpty (parentStack))
break;
}
else if (isdigit (*buf)) /* string? */
{
tr_variant * v;
const uint8_t * end;
const uint8_t * str;
size_t str_len;
tr_variant * node;
const bool is_key = isReadyForDictKey (parentStack);
if ((err = tr_bencParseStr (buf, bufend, &end, &str, &str_len)))
return err;
node = getNode (top, parentStack, TR_VARIANT_TYPE_STR);
if (!node)
return EILSEQ;
if (is_key)
tr_variantInitQuark (node, tr_quark_new (str, str_len));
else
tr_variantInitStr (node, str, str_len);
break;
buf = end;
if (tr_ptrArrayEmpty (parentStack))
break;
if (!key && !tr_ptrArrayEmpty(&stack) && tr_variantIsDict(tr_ptrArrayBack(&stack)))
key = tr_quark_new (str, str_len);
else if ((v = get_node (&stack, &key, top, &err)))
tr_variantInitStr (v, str, str_len);
}
else /* invalid bencoded text... march past it */
{
++buf;
}
if (tr_ptrArrayEmpty (&stack))
break;
}
err = !tr_variantIsSomething (top) || !tr_ptrArrayEmpty (parentStack);
if (!err)
err = !top->type || !tr_ptrArrayEmpty(&stack);
if (!err && setme_end)
*setme_end = (const char*) buf;
return err;
}
int
tr_variantParseBenc (const void * buf,
const void * end,
tr_variant * top,
const char ** setme_end)
{
int err;
tr_ptrArray parentStack = TR_PTR_ARRAY_INIT;
top->type = 0; /* set to `uninitialized' */
err = tr_variantParseImpl (buf, end, top, &parentStack, setme_end);
if (err)
tr_variantFree (top);
tr_ptrArrayDestruct (&parentStack, NULL);
tr_ptrArrayDestruct (&stack, NULL);
return err;
}
@ -342,18 +283,11 @@ saveBoolFunc (const tr_variant * val, void * evbuf)
static void
saveRealFunc (const tr_variant * val, void * evbuf)
{
int len;
char buf[128];
char locale[128];
size_t len;
/* always use a '.' decimal point s.t. locale-hopping doesn't bite us */
tr_strlcpy (locale, setlocale (LC_NUMERIC, NULL), sizeof (locale));
setlocale (LC_NUMERIC, "POSIX");
tr_snprintf (buf, sizeof (buf), "%f", val->val.d);
setlocale (LC_NUMERIC, locale);
len = strlen (buf);
evbuffer_add_printf (evbuf, "%lu:", (unsigned long)len);
len = tr_snprintf (buf, sizeof (buf), "%f", val->val.d);
evbuffer_add_printf (evbuf, "%d:", len);
evbuffer_add (evbuf, buf, len);
}

View File

@ -40,10 +40,6 @@ void tr_variantToBufJson (const tr_variant * top, struct evbuffer * buf, bool le
void tr_variantToBufBenc (const tr_variant * top, struct evbuffer * buf);
bool tr_variantIsSomething (const tr_variant * v);
bool tr_variantIsContainer (const tr_variant * v);
void tr_variantInit (tr_variant * v, char type);
int tr_jsonParse (const char * source, /* Such as a filename. Only when logging an error */

View File

@ -17,8 +17,6 @@
#include <string.h>
#include <errno.h> /* EILSEQ, EINVAL */
#include <locale.h> /* setlocale() */
#include <event2/buffer.h> /* evbuffer_add() */
#include <event2/util.h> /* evutil_strtoll () */
@ -389,15 +387,15 @@ struct jsonWalk
static void
jsonIndent (struct jsonWalk * data)
{
if (data->doIndent)
static char buf[1024] = { '\0' };
if (!*buf)
{
char buf[1024];
const int width = tr_list_size (data->parents) * 4;
memset (buf, ' ', sizeof(buf));
buf[0] = '\n';
memset (buf+1, ' ', width);
evbuffer_add (data->out, buf, 1+width);
}
if (data->doIndent)
evbuffer_add (data->out, buf, tr_list_size(data->parents)*4 + 1);
}
static void
@ -455,6 +453,9 @@ jsonPushParent (struct jsonWalk * data,
pstate->variantType = v->type;
pstate->childIndex = 0;
pstate->childCount = v->val.l.count;
if (tr_variantIsDict (v))
pstate->childCount *= 2;
tr_list_prepend (&data->parents, pstate);
}
@ -620,18 +621,13 @@ static const struct VariantWalkFuncs walk_funcs = { jsonIntFunc,
void
tr_variantToBufJson (const tr_variant * top, struct evbuffer * buf, bool lean)
{
char lc_numeric[128];
struct jsonWalk data;
data.doIndent = !lean;
data.out = buf;
data.parents = NULL;
/* json requires a '.' decimal point regardless of locale */
tr_strlcpy (lc_numeric, setlocale (LC_NUMERIC, NULL), sizeof (lc_numeric));
setlocale (LC_NUMERIC, "POSIX");
tr_variantWalk (top, &walk_funcs, &data, true);
setlocale (LC_NUMERIC, lc_numeric);
if (evbuffer_get_length (buf))
evbuffer_add_printf (buf, "\n");

View File

@ -12,27 +12,27 @@
#include <assert.h>
#include <errno.h>
#include <stdio.h> /* rename () */
#include <stdlib.h> /* strtoul (), strtod (), realloc (), qsort (), mkstemp () */
#include <stdio.h> /* rename() */
#include <stdlib.h> /* strtoul(), strtod(), realloc(), qsort(), mkstemp() */
#include <string.h>
#ifdef WIN32 /* tr_mkstemp () */
#ifdef WIN32 /* tr_mkstemp() */
#include <fcntl.h>
#define _S_IREAD 256
#define _S_IWRITE 128
#endif
#include <locale.h> /* setlocale () */
#include <unistd.h> /* write (), unlink () */
#include <locale.h> /* setlocale() */
#include <unistd.h> /* write(), unlink() */
#include <event2/buffer.h>
#define __LIBTRANSMISSION_VARIANT_MODULE___
#include "transmission.h"
#include "ConvertUTF.h"
#include "fdlimit.h" /* tr_close_file () */
#include "fdlimit.h" /* tr_close_file() */
#include "platform.h" /* TR_PATH_MAX */
#include "utils.h" /* tr_new (), tr_free () */
#include "utils.h" /* tr_new(), tr_free() */
#include "variant.h"
#include "variant-common.h"
@ -40,13 +40,13 @@
***
**/
bool
static bool
tr_variantIsContainer (const tr_variant * v)
{
return tr_variantIsList (v) || tr_variantIsDict (v);
}
bool
static bool
tr_variantIsSomething (const tr_variant * v)
{
return tr_variantIsContainer (v)
@ -59,34 +59,14 @@ tr_variantIsSomething (const tr_variant * v)
void
tr_variantInit (tr_variant * v, char type)
{
memset (v, 0, sizeof (*v));
v->type = type;
memset (&v->val, 0, sizeof(v->val));
}
/***
****
***/
/*
zzz
typedef enum
{
TR_STRING_TYPE_KEY,
TR_STRING_TYPE_HEAP,
TR_STRING_TYPE_BUF
}
tr_string_type;
struct tr_variant_string
{
tr_key key;
tr_string_type type;
size_t len;
union { char buf[8]; char * ptr; } str;
};
*/
static const struct tr_variant_string STRING_INIT =
{
.type = TR_STRING_TYPE_QUARK,
@ -122,7 +102,8 @@ tr_variant_string_get_string (const struct tr_variant_string * str)
}
static void
tr_variant_string_set_quark (struct tr_variant_string * str, const tr_quark quark)
tr_variant_string_set_quark (struct tr_variant_string * str,
const tr_quark quark)
{
tr_variant_string_clear (str);
@ -132,7 +113,9 @@ tr_variant_string_set_quark (struct tr_variant_string * str, const tr_quark quar
}
static void
tr_variant_string_set_string (struct tr_variant_string * str, const char * bytes, int len)
tr_variant_string_set_string (struct tr_variant_string * str,
const char * bytes,
int len)
{
tr_quark quark;
tr_variant_string_clear (str);
@ -188,14 +171,9 @@ dictIndexOf (const tr_variant * dict, const tr_quark key)
const tr_variant * const begin = dict->val.l.vals;
const tr_variant * const end = begin + dict->val.l.count;
for (walk=begin; walk!=end; walk+=2)
{
assert (walk->type == TR_VARIANT_TYPE_STR);
assert (walk->val.s.type == TR_STRING_TYPE_QUARK);
if (walk->val.s.quark == key)
for (walk=begin; walk!=end; ++walk)
if (walk->key == key)
return walk - begin;
}
}
return -1;
@ -206,7 +184,7 @@ tr_variantDictFind (tr_variant * dict, const tr_quark key)
{
const int i = dictIndexOf (dict, key);
return i < 0 ? NULL : &dict->val.l.vals[i + 1];
return i < 0 ? NULL : dict->val.l.vals+i;
}
static bool
@ -225,104 +203,97 @@ tr_variantListSize (const tr_variant * list)
}
tr_variant*
tr_variantListChild (tr_variant * val,
size_t i)
tr_variantListChild (tr_variant * v,
size_t i)
{
tr_variant * ret = NULL;
if (tr_variantIsList (val) && (i < val->val.l.count))
ret = val->val.l.vals + i;
if (tr_variantIsList (v) && (i < v->val.l.count))
ret = v->val.l.vals + i;
return ret;
}
int
bool
tr_variantListRemove (tr_variant * list, size_t i)
{
bool removed = false;
if (tr_variantIsList (list) && (i < list->val.l.count))
{
removed = true;
tr_variantFree (&list->val.l.vals[i]);
tr_removeElementFromArray (list->val.l.vals, i,
sizeof (tr_variant),
list->val.l.count--);
return 1;
}
return 0;
}
static void
tr_variant_warning (const char * err)
{
fprintf (stderr, "warning: %s\n", err);
return removed;
}
bool
tr_variantGetInt (const tr_variant * val,
tr_variantGetInt (const tr_variant * v,
int64_t * setme)
{
bool success = false;
if (!success && ((success = tr_variantIsInt (val))))
if (!success && ((success = tr_variantIsInt (v))))
if (setme)
*setme = val->val.i;
*setme = v->val.i;
if (!success && ((success = tr_variantIsBool (val))))
{
tr_variant_warning ("reading bool as an int");
if (setme)
*setme = val->val.b ? 1 : 0;
}
if (!success && ((success = tr_variantIsBool (v))))
if (setme)
*setme = v->val.b ? 1 : 0;
return success;
}
bool
tr_variantGetStr (const tr_variant * val,
tr_variantGetStr (const tr_variant * v,
const char ** setme,
size_t * len)
{
const bool success = tr_variantIsString (val);
const bool success = tr_variantIsString (v);
if (success)
*setme = getStr (val);
*setme = getStr (v);
if (len != NULL)
*len = success ? val->val.s.len : 0;
*len = success ? v->val.s.len : 0;
return success;
}
bool
tr_variantGetRaw (const tr_variant * val,
tr_variantGetRaw (const tr_variant * v,
const uint8_t ** setme_raw,
size_t * setme_len)
{
const bool success = tr_variantIsString (val);
const bool success = tr_variantIsString (v);
if (success)
{
*setme_raw = (uint8_t*) getStr (val);
*setme_len = val->val.s.len;
*setme_raw = (uint8_t*) getStr (v);
*setme_len = v->val.s.len;
}
return success;
}
bool
tr_variantGetBool (const tr_variant * val, bool * setme)
tr_variantGetBool (const tr_variant * v, bool * setme)
{
const char * str;
bool success = false;
if ((success = tr_variantIsBool (val)))
*setme = val->val.b;
if ((success = tr_variantIsBool (v)))
*setme = v->val.b;
if (!success && tr_variantIsInt (val))
if ((success = (val->val.i==0 || val->val.i==1)))
*setme = val->val.i!=0;
if (!success && tr_variantIsInt (v))
if ((success = (v->val.i==0 || v->val.i==1)))
*setme = v->val.i!=0;
if (!success && tr_variantGetStr (val, &str, NULL))
if (!success && tr_variantGetStr (v, &str, NULL))
if ((success = (!strcmp (str,"true") || !strcmp (str,"false"))))
*setme = !strcmp (str,"true");
@ -330,17 +301,17 @@ tr_variantGetBool (const tr_variant * val, bool * setme)
}
bool
tr_variantGetReal (const tr_variant * val, double * setme)
tr_variantGetReal (const tr_variant * v, double * setme)
{
bool success = false;
if (!success && ((success = tr_variantIsReal (val))))
*setme = val->val.d;
if (!success && ((success = tr_variantIsReal (v))))
*setme = v->val.d;
if (!success && ((success = tr_variantIsInt (val))))
*setme = val->val.i;
if (!success && ((success = tr_variantIsInt (v))))
*setme = v->val.i;
if (!success && tr_variantIsString (val))
if (!success && tr_variantIsString (v))
{
char * endptr;
char locale[128];
@ -349,10 +320,10 @@ tr_variantGetReal (const tr_variant * val, double * setme)
/* the json spec requires a '.' decimal point regardless of locale */
tr_strlcpy (locale, setlocale (LC_NUMERIC, NULL), sizeof (locale));
setlocale (LC_NUMERIC, "POSIX");
d = strtod (getStr (val), &endptr);
d = strtod (getStr (v), &endptr);
setlocale (LC_NUMERIC, locale);
if ((success = (getStr (val) != endptr) && !*endptr))
if ((success = (getStr (v) != endptr) && !*endptr))
*setme = d;
}
@ -401,10 +372,7 @@ tr_variantDictFindList (tr_variant * dict,
const tr_quark key,
tr_variant ** setme)
{
return tr_variantDictFindType (dict,
key,
TR_VARIANT_TYPE_LIST,
setme);
return tr_variantDictFindType (dict, key, TR_VARIANT_TYPE_LIST, setme);
}
bool
@ -412,10 +380,7 @@ tr_variantDictFindDict (tr_variant * dict,
const tr_quark key,
tr_variant ** setme)
{
return tr_variantDictFindType (dict,
key,
TR_VARIANT_TYPE_DICT,
setme);
return tr_variantDictFindType (dict, key, TR_VARIANT_TYPE_DICT, setme);
}
bool
@ -454,81 +419,72 @@ tr_variantInitStr (tr_variant * v, const void * str, int len)
}
void
tr_variantInitBool (tr_variant * variant, bool value)
tr_variantInitBool (tr_variant * v, bool value)
{
tr_variantInit (variant, TR_VARIANT_TYPE_BOOL);
variant->val.b = value != 0;
tr_variantInit (v, TR_VARIANT_TYPE_BOOL);
v->val.b = value != 0;
}
void
tr_variantInitReal (tr_variant * b, double value)
tr_variantInitReal (tr_variant * v, double value)
{
tr_variantInit (b, TR_VARIANT_TYPE_REAL);
b->val.d = value;
tr_variantInit (v, TR_VARIANT_TYPE_REAL);
v->val.d = value;
}
void
tr_variantInitInt (tr_variant * variant, int64_t value)
tr_variantInitInt (tr_variant * v, int64_t value)
{
tr_variantInit (variant, TR_VARIANT_TYPE_INT);
variant->val.i = value;
tr_variantInit (v, TR_VARIANT_TYPE_INT);
v->val.i = value;
}
int
tr_variantInitList (tr_variant * variant, size_t reserve_count)
void
tr_variantInitList (tr_variant * v, size_t reserve_count)
{
tr_variantInit (variant, TR_VARIANT_TYPE_LIST);
return tr_variantListReserve (variant, reserve_count);
tr_variantInit (v, TR_VARIANT_TYPE_LIST);
tr_variantListReserve (v, reserve_count);
}
static int
containerReserve (tr_variant * container, size_t count)
static void
containerReserve (tr_variant * v, size_t count)
{
const size_t needed = container->val.l.count + count;
const size_t needed = v->val.l.count + count;
assert (tr_variantIsContainer (container));
assert (tr_variantIsContainer (v));
if (needed > container->val.l.alloc)
if (needed > v->val.l.alloc)
{
size_t n;
void * tmp;
/* scale the alloc size in powers-of-2 */
n = container->val.l.alloc ? container->val.l.alloc : 8;
size_t n = v->val.l.alloc ? v->val.l.alloc : 8;
while (n < needed)
n *= 2u;
tmp = tr_renew (tr_variant, container->val.l.vals, n);
if (tmp == NULL)
return 1;
container->val.l.alloc = n;
container->val.l.vals = tmp;
v->val.l.vals = tr_renew (tr_variant, v->val.l.vals, n);
v->val.l.alloc = n;
}
return 0;
}
int
void
tr_variantListReserve (tr_variant * list, size_t count)
{
assert (tr_variantIsList (list));
return containerReserve (list, count);
containerReserve (list, count);
}
int
tr_variantInitDict (tr_variant * variant, size_t reserve_count)
void
tr_variantInitDict (tr_variant * v, size_t reserve_count)
{
tr_variantInit (variant, TR_VARIANT_TYPE_DICT);
return tr_variantDictReserve (variant, reserve_count);
tr_variantInit (v, TR_VARIANT_TYPE_DICT);
tr_variantDictReserve (v, reserve_count);
}
int
void
tr_variantDictReserve (tr_variant * dict,
size_t reserve_count)
{
assert (tr_variantIsDict (dict));
return containerReserve (dict, reserve_count * 2);
containerReserve (dict, reserve_count);
}
tr_variant *
@ -540,6 +496,7 @@ tr_variantListAdd (tr_variant * list)
containerReserve (list, 1);
child = &list->val.l.vals[list->val.l.count++];
child->key = 0;
tr_variantInit (child, TR_VARIANT_TYPE_INT);
return child;
@ -622,19 +579,16 @@ tr_variant *
tr_variantDictAdd (tr_variant * dict,
const tr_quark key)
{
tr_variant * child_key;
tr_variant * child_val;
tr_variant * val;
assert (tr_variantIsDict (dict));
containerReserve (dict, 2);
containerReserve (dict, 1);
child_key = dict->val.l.vals + dict->val.l.count++;
tr_variantInitQuark (child_key, key);
child_val = dict->val.l.vals + dict->val.l.count++;
tr_variantInit (child_val, TR_VARIANT_TYPE_INT);
return child_val;
val = dict->val.l.vals + dict->val.l.count++;
tr_variantInit (val, TR_VARIANT_TYPE_INT);
val->key = key;
return val;
}
static tr_variant*
@ -674,7 +628,9 @@ tr_variantDictAddInt (tr_variant * dict,
}
tr_variant*
tr_variantDictAddBool (tr_variant * dict, const tr_quark key, bool val)
tr_variantDictAddBool (tr_variant * dict,
const tr_quark key,
bool val)
{
tr_variant * child = dictFindOrAdd (dict, key, TR_VARIANT_TYPE_BOOL);
tr_variantInitBool (child, val);
@ -682,7 +638,9 @@ tr_variantDictAddBool (tr_variant * dict, const tr_quark key, bool val)
}
tr_variant*
tr_variantDictAddReal (tr_variant * dict, const tr_quark key, double val)
tr_variantDictAddReal (tr_variant * dict,
const tr_quark key,
double val)
{
tr_variant * child = dictFindOrAdd (dict, key, TR_VARIANT_TYPE_REAL);
tr_variantInitReal (child, val);
@ -690,7 +648,9 @@ tr_variantDictAddReal (tr_variant * dict, const tr_quark key, double val)
}
tr_variant*
tr_variantDictAddQuark (tr_variant * dict, const tr_quark key, const tr_quark val)
tr_variantDictAddQuark (tr_variant * dict,
const tr_quark key,
const tr_quark val)
{
tr_variant * child = dictFindOrAdd (dict, key, TR_VARIANT_TYPE_STR);
tr_variantInitQuark (child, val);
@ -698,7 +658,9 @@ tr_variantDictAddQuark (tr_variant * dict, const tr_quark key, const tr_quark va
}
tr_variant*
tr_variantDictAddStr (tr_variant * dict, const tr_quark key, const char * val)
tr_variantDictAddStr (tr_variant * dict,
const tr_quark key,
const char * val)
{
tr_variant * child = dictFindOrAdd (dict, key, TR_VARIANT_TYPE_STR);
tr_variantInitStr (child, val, -1);
@ -736,26 +698,28 @@ tr_variantDictAddDict (tr_variant * dict,
return child;
}
int
bool
tr_variantDictRemove (tr_variant * dict,
const tr_quark key)
{
bool removed = false;
const int i = dictIndexOf (dict, key);
if (i >= 0)
{
const int n = dict->val.l.count;
const int last = dict->val.l.count - 1;
tr_variantFree (&dict->val.l.vals[i]);
tr_variantFree (&dict->val.l.vals[i + 1]);
if (i + 2 < n)
{
dict->val.l.vals[i] = dict->val.l.vals[n - 2];
dict->val.l.vals[i + 1] = dict->val.l.vals[n - 1];
}
dict->val.l.count -= 2;
if (i != last)
dict->val.l.vals[i] = dict->val.l.vals[last];
--dict->val.l.count;
removed = true;
}
return i >= 0; /* return true if found */
return removed;
}
/***
@ -764,8 +728,8 @@ tr_variantDictRemove (tr_variant * dict,
struct KeyIndex
{
const char * key;
int index;
const char * keystr;
tr_variant * val;
};
static int
@ -774,105 +738,61 @@ compareKeyIndex (const void * va, const void * vb)
const struct KeyIndex * a = va;
const struct KeyIndex * b = vb;
return strcmp (a->key, b->key);
return strcmp (a->keystr, b->keystr);
}
struct SaveNode
{
const tr_variant * val;
int valIsVisited;
int childCount;
int childIndex;
int * children;
const tr_variant * v;
tr_variant sorted;
size_t childIndex;
bool isVisited;
};
static void
nodeInitDict (struct SaveNode * node,
const tr_variant * val,
bool sort_dicts)
nodeConstruct (struct SaveNode * node,
const tr_variant * v,
bool sort_dicts)
{
const int n = val->val.l.count;
const int nKeys = n / 2;
node->isVisited = false;
node->childIndex = 0;
assert (tr_variantIsDict (val));
node->val = val;
node->children = tr_new0 (int, n);
if (sort_dicts)
if (sort_dicts && tr_variantIsDict(v))
{
int i, j;
struct KeyIndex * indices = tr_new (struct KeyIndex, nKeys);
/* make node->sorted a sorted version of this dictionary */
for (i=j=0; i<n; i+=2, ++j)
size_t i;
const size_t n = v->val.l.count;
struct KeyIndex * tmp = tr_new (struct KeyIndex, n);
for (i=0; i<n; i++)
{
indices[j].key = getStr (&val->val.l.vals[i]);
indices[j].index = i;
tmp[i].val = v->val.l.vals+i;
tmp[i].keystr = tr_quark_get_string (tmp[i].val->key, NULL);
}
qsort (indices, j, sizeof (struct KeyIndex), compareKeyIndex);
for (i=0; i<j; ++i)
{
const int index = indices[i].index;
node->children[node->childCount++] = index;
node->children[node->childCount++] = index + 1;
}
tr_free (indices);
}
else
{
int i;
qsort (tmp, n, sizeof (struct KeyIndex), compareKeyIndex);
tr_variantInitDict (&node->sorted, n);
for (i=0; i<n; ++i)
node->children[node->childCount++] = i;
node->sorted.val.l.vals[i] = *tmp[i].val;
node->sorted.val.l.count = n;
tr_free (tmp);
node->v = &node->sorted;
}
assert (node->childCount == n);
}
static void
nodeInitList (struct SaveNode * node,
const tr_variant * val)
{
int i;
int n;
assert (tr_variantIsList (val));
n = val->val.l.count;
node->val = val;
node->childCount = n;
node->children = tr_new0 (int, n);
for (i=0; i<n; ++i) /* a list's children don't need to be reordered */
node->children[i] = i;
}
static void
nodeInitLeaf (struct SaveNode * node,
const tr_variant * variant)
{
assert (!tr_variantIsContainer (variant));
node->val = variant;
}
static void
nodeInit (struct SaveNode * node,
const tr_variant * variant,
bool sort_dicts)
{
static const struct SaveNode INIT_NODE = { NULL, 0, 0, 0, NULL };
*node = INIT_NODE;
if (tr_variantIsList (variant))
nodeInitList (node, variant);
else if (tr_variantIsDict (variant))
nodeInitDict (node, variant, sort_dicts);
else
nodeInitLeaf (node, variant);
{
node->v = v;
}
}
static void
nodeDestruct (struct SaveNode * node)
{
if (node->v == &node->sorted)
tr_free (node->sorted.val.l.vals);
}
/**
@ -881,63 +801,75 @@ nodeInit (struct SaveNode * node,
* attack via maliciously-crafted data. (#667)
*/
void
tr_variantWalk (const tr_variant * top,
tr_variantWalk (const tr_variant * v,
const struct VariantWalkFuncs * walkFuncs,
void * user_data,
bool sort_dicts)
{
int stackSize = 0;
int stackAlloc = 64;
char lc_numeric[128];
struct SaveNode * stack = tr_new (struct SaveNode, stackAlloc);
nodeInit (&stack[stackSize++], top, sort_dicts);
/* always use a '.' decimal point s.t. locale-hopping doesn't bite us */
tr_strlcpy (lc_numeric, setlocale (LC_NUMERIC, NULL), sizeof (lc_numeric));
setlocale (LC_NUMERIC, "POSIX");
nodeConstruct (&stack[stackSize++], v, sort_dicts);
while (stackSize > 0)
{
struct SaveNode * node = &stack[stackSize-1];
const tr_variant * val;
const tr_variant * v;
if (!node->valIsVisited)
if (!node->isVisited)
{
val = node->val;
node->valIsVisited = true;
v = node->v;
node->isVisited = true;
}
else if (node->childIndex < node->childCount)
else if (tr_variantIsContainer(node->v) && (node->childIndex < node->v->val.l.count))
{
const int index = node->children[node->childIndex++];
val = node->val->val.l.vals + index;
const int index = node->childIndex++;
v = node->v->val.l.vals + index;
if (tr_variantIsDict (node->v))
{
tr_variant tmp;
tr_variantInitQuark (&tmp, v->key);
walkFuncs->stringFunc (&tmp, user_data);
}
}
else /* done with this node */
{
if (tr_variantIsContainer (node->val))
walkFuncs->containerEndFunc (node->val, user_data);
--stackSize;
tr_free (node->children);
continue;
if (tr_variantIsContainer (node->v))
walkFuncs->containerEndFunc (node->v, user_data);
--stackSize;
nodeDestruct (node);
continue;
}
if (val) switch (val->type)
if (v) switch (v->type)
{
case TR_VARIANT_TYPE_INT:
walkFuncs->intFunc (val, user_data);
walkFuncs->intFunc (v, user_data);
break;
case TR_VARIANT_TYPE_BOOL:
walkFuncs->boolFunc (val, user_data);
walkFuncs->boolFunc (v, user_data);
break;
case TR_VARIANT_TYPE_REAL:
walkFuncs->realFunc (val, user_data);
walkFuncs->realFunc (v, user_data);
break;
case TR_VARIANT_TYPE_STR:
walkFuncs->stringFunc (val, user_data);
walkFuncs->stringFunc (v, user_data);
break;
case TR_VARIANT_TYPE_LIST:
if (val == node->val)
if (v == node->v)
{
walkFuncs->listBeginFunc (val, user_data);
walkFuncs->listBeginFunc (v, user_data);
}
else
{
@ -946,14 +878,14 @@ tr_variantWalk (const tr_variant * top,
stackAlloc *= 2;
stack = tr_renew (struct SaveNode, stack, stackAlloc);
}
nodeInit (&stack[stackSize++], val, sort_dicts);
nodeConstruct (&stack[stackSize++], v, sort_dicts);
}
break;
case TR_VARIANT_TYPE_DICT:
if (val == node->val)
if (v == node->v)
{
walkFuncs->dictBeginFunc (val, user_data);
walkFuncs->dictBeginFunc (v, user_data);
}
else
{
@ -962,7 +894,7 @@ tr_variantWalk (const tr_variant * top,
stackAlloc *= 2;
stack = tr_renew (struct SaveNode, stack, stackAlloc);
}
nodeInit (&stack[stackSize++], val, sort_dicts);
nodeConstruct (&stack[stackSize++], v, sort_dicts);
}
break;
@ -973,6 +905,9 @@ tr_variantWalk (const tr_variant * top,
}
}
/* restore the locale... */
setlocale (LC_NUMERIC, lc_numeric);
tr_free (stack);
}
@ -1066,12 +1001,7 @@ tr_variantListCopy (tr_variant * target, const tr_variant * src)
static size_t
tr_variantDictSize (const tr_variant * dict)
{
size_t count = 0;
if (tr_variantIsDict (dict))
count = dict->val.l.count / 2;
return count;
return tr_variantIsDict (dict) ? dict->val.l.count : 0;
}
bool
@ -1084,16 +1014,11 @@ tr_variantDictChild (tr_variant * dict,
assert (tr_variantIsDict (dict));
if (tr_variantIsDict (dict) && (n*2)+1 <= dict->val.l.count)
if (tr_variantIsDict (dict) && (n<dict->val.l.count))
{
tr_variant * k = dict->val.l.vals + (n*2);
tr_variant * v = dict->val.l.vals + (n*2) + 1;
if ((k->val.s.type == TR_STRING_TYPE_QUARK) && tr_variantIsSomething (v))
{
*key = k->val.s.quark;
*val = v;
success = true;
}
*key = dict->val.l.vals[n].key;
*val = dict->val.l.vals+n;
success = true;
}
return success;
@ -1175,24 +1100,24 @@ tr_variantMergeDicts (tr_variant * target, const tr_variant * source)
***/
struct evbuffer *
tr_variantToBuf (const tr_variant * top, tr_variant_fmt fmt)
tr_variantToBuf (const tr_variant * v, tr_variant_fmt fmt)
{
struct evbuffer * buf = evbuffer_new ();
struct evbuffer * buf = evbuffer_new();
evbuffer_expand (buf, 4096); /* alloc a little memory to start off with */
switch (fmt)
{
case TR_VARIANT_FMT_BENC:
tr_variantToBufBenc (top, buf);
tr_variantToBufBenc (v, buf);
break;
case TR_VARIANT_FMT_JSON:
tr_variantToBufJson (top, buf, false);
tr_variantToBufJson (v, buf, false);
break;
case TR_VARIANT_FMT_JSON_LEAN:
tr_variantToBufJson (top, buf, true);
tr_variantToBufJson (v, buf, true);
break;
}
@ -1200,9 +1125,9 @@ tr_variantToBuf (const tr_variant * top, tr_variant_fmt fmt)
}
char*
tr_variantToStr (const tr_variant * top, tr_variant_fmt fmt, int * len)
tr_variantToStr (const tr_variant * v, tr_variant_fmt fmt, int * len)
{
struct evbuffer * buf = tr_variantToBuf (top, fmt);
struct evbuffer * buf = tr_variantToBuf (v, fmt);
const size_t n = evbuffer_get_length (buf);
char * ret = evbuffer_free_to_str (buf);
if (len != NULL)
@ -1210,7 +1135,7 @@ tr_variantToStr (const tr_variant * top, tr_variant_fmt fmt, int * len)
return ret;
}
/* portability wrapper for mkstemp (). */
/* portability wrapper for mkstemp(). */
static int
tr_mkstemp (char * template)
{
@ -1225,7 +1150,7 @@ tr_mkstemp (char * template)
}
int
tr_variantToFile (const tr_variant * top,
tr_variantToFile (const tr_variant * v,
tr_variant_fmt fmt,
const char * filename)
{
@ -1235,7 +1160,7 @@ tr_variantToFile (const tr_variant * top,
char buf[TR_PATH_MAX];
/* follow symlinks to find the "real" file, to make sure the temporary
* we build with tr_mkstemp () is created on the right partition */
* we build with tr_mkstemp() is created on the right partition */
if (tr_realpath (filename, buf) != NULL)
filename = buf;
@ -1249,7 +1174,7 @@ tr_variantToFile (const tr_variant * top,
/* save the variant to a temporary file */
{
struct evbuffer * buf = tr_variantToBuf (top, fmt);
struct evbuffer * buf = tr_variantToBuf (v, fmt);
const char * walk = (const char *) evbuffer_pullup (buf, -1);
nleft = evbuffer_get_length (buf);

View File

@ -74,6 +74,8 @@ typedef struct tr_variant
{
char type;
tr_quark key;
union
{
bool b;
@ -86,9 +88,9 @@ typedef struct tr_variant
struct
{
struct tr_variant * vals; /* nodes */
size_t alloc; /* nodes allocated */
size_t count; /* nodes used */
size_t alloc;
size_t count;
struct tr_variant * vals;
} l;
}
val;
@ -275,10 +277,10 @@ tr_variantIsList (const tr_variant * v)
return (v != NULL) && (v->type == TR_VARIANT_TYPE_LIST);
}
int tr_variantInitList (tr_variant * list,
void tr_variantInitList (tr_variant * list,
size_t reserve_count);
int tr_variantListReserve (tr_variant * list,
void tr_variantListReserve (tr_variant * list,
size_t reserve_count);
tr_variant * tr_variantListAdd (tr_variant * list);
@ -311,7 +313,7 @@ tr_variant * tr_variantListAddDict (tr_variant * list,
tr_variant * tr_variantListChild (tr_variant * list,
size_t pos);
int tr_variantListRemove (tr_variant * list,
bool tr_variantListRemove (tr_variant * list,
size_t pos);
size_t tr_variantListSize (const tr_variant * list);
@ -327,13 +329,13 @@ tr_variantIsDict (const tr_variant * v)
return (v != NULL) && (v->type == TR_VARIANT_TYPE_DICT);
}
int tr_variantInitDict (tr_variant * initme,
void tr_variantInitDict (tr_variant * initme,
size_t reserve_count);
int tr_variantDictReserve (tr_variant * dict,
void tr_variantDictReserve (tr_variant * dict,
size_t reserve_count);
int tr_variantDictRemove (tr_variant * dict,
bool tr_variantDictRemove (tr_variant * dict,
const tr_quark key);
tr_variant * tr_variantDictAdd (tr_variant * dict,

View File

@ -1,2 +1,3 @@
#/bin/sh
valgrind --tool=cachegrind ./transmission-qt
valgrind --tool=cachegrind ./transmission-qt 2>&1 | tee runlog
#valgrind --tool=memcheck --leak-check=full --leak-resolution=high --num-callers=48 --log-file=x-valgrind --show-reachable=no ./transmission-qt 2>&1 | tee runlog