Skip to content
This repository has been archived by the owner on Feb 25, 2018. It is now read-only.

Commit

Permalink
use a custom size-prefix parser; now faster than ujson in the shootout
Browse files Browse the repository at this point in the history
  • Loading branch information
rfk committed Apr 2, 2011
1 parent 91cfc14 commit 8da0a34
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 14 deletions.
55 changes: 49 additions & 6 deletions tnetstring/tns_core.c
Expand Up @@ -27,9 +27,14 @@ static int tns_outbuf_puts(tns_outbuf *outbuf, const char *data, size_t len);
static int tns_outbuf_clamp(tns_outbuf *outbuf, size_t orig_size);
static char* tns_outbuf_finalize(tns_outbuf *outbuf, size_t *len);
static void tns_outbuf_free(tns_outbuf *outbuf);
static size_t tns_strtosz(const char *data, size_t len, size_t *sz, char **end);

#include "tns_outbuf_back.c"

#define STR_EQ_TRUE(s) (s[0]=='t' && s[1]=='r' && s[2]=='u' && s[3]=='e')
#define STR_EQ_FALSE(s) (s[0]=='f' && s[1]=='a' && s[2]=='l' \
&& s[3]=='s' && s[4] == 'e')

static void* tns_parse(const char *data, size_t len, char **remain)
{
void *val = NULL;
Expand All @@ -38,11 +43,12 @@ static void* tns_parse(const char *data, size_t len, char **remain)
size_t vallen = 0;

// Read the length of the value, and verify that it ends in a colon.
vallen = strtol(data, &valstr, 10);
check(valstr != data, "Not a tnetstring: no length prefix.");
check((valstr+vallen+1) < (data+len), "Not a tnetstring: bad length prefix.");
check(*valstr == ':', "Not a tnetstring: bad length prefix.");
check(tns_strtosz(data, len, &vallen, &valstr) != -1,
"Not a tnetstring: invalid length prefix.");
check(*valstr == ':', "Not a tnetstring: invalid length prefix.");
valstr++;
check((valstr+vallen) < (data+len),
"Not a tnetstring: invalid length prefix.");

// Grab the type tag from the end of the value.
type = valstr[vallen];
Expand All @@ -68,9 +74,9 @@ static void* tns_parse(const char *data, size_t len, char **remain)
// Primitive type: a boolean.
// The only acceptable values are "true" and "false".
case tns_tag_bool:
if(vallen == 4 && strncmp(valstr,"true",4) == 0) {
if(vallen == 4 && STR_EQ_TRUE(valstr)) {
val = tns_get_true();
} else if(vallen == 5 && strncmp(valstr,"false",5) == 0) {
} else if(vallen == 5 && STR_EQ_FALSE(valstr)) {
val = tns_get_false();
} else {
sentinel("Not a tnetstring: invalid boolean literal.");
Expand Down Expand Up @@ -109,6 +115,9 @@ static void* tns_parse(const char *data, size_t len, char **remain)
return NULL;
}

#undef STR_EQ_TRUE
#undef STR_EQ_FALSE


static char* tns_render(void *val, size_t *len)
{
Expand Down Expand Up @@ -239,3 +248,37 @@ static int tns_parse_dict(void *val, const char *data, size_t len)
}



static inline size_t
tns_strtosz(const char *data, size_t len, size_t *sz, char **end)
{
char *pos, *eod, c;
size_t value = 0;

pos = data;
eod = data + len;

// The first character must be a digit.
c = *pos;
if(c < '0' || c > '9') {
return -1;
}
value = c - '0';
pos++;

// Consume all other digits.
while(pos < eod) {
c = *pos;
if(c < '0' || c > '9') {
*sz = value;
*end = pos;
return 0;
}
value = (value * 10) + (c - '0');
pos++;
}

// If we consume the entire string, that's an error.
return -1;
}

16 changes: 8 additions & 8 deletions tools/shootout.py
Expand Up @@ -28,27 +28,27 @@ def add_test(v):

def thrash_tnetstring():
for obj, tns, json in TESTS:
# tnetstring.dumps(obj)
tnetstring.dumps(obj)
assert tnetstring.loads(tns) == obj
# assert tnetstring.loads(tnetstring.dumps(obj)) == obj
assert tnetstring.loads(tnetstring.dumps(obj)) == obj

def thrash_cjson():
for obj, tns, json in TESTS:
# cjson.encode(obj)
cjson.encode(obj)
assert cjson.decode(json) == obj
# assert cjson.decode(cjson.encode(obj)) == obj
assert cjson.decode(cjson.encode(obj)) == obj

def thrash_yajl():
for obj, tns, json in TESTS:
# yajl.dumps(obj)
yajl.dumps(obj)
assert yajl.loads(json) == obj
# assert yajl.loads(yajl.dumps(obj)) == obj
assert yajl.loads(yajl.dumps(obj)) == obj

def thrash_ujson():
for obj, tns, json in TESTS:
# ujson.dumps(obj)
ujson.dumps(obj)
assert ujson.loads(json) == obj
# assert ujson.loads(ujson.dumps(obj)) == obj
assert ujson.loads(ujson.dumps(obj)) == obj


if __name__ == "__main__":
Expand Down

0 comments on commit 8da0a34

Please sign in to comment.