Permalink
Browse files

use a custom size-prefix parser; now faster than ujson in the shootout

  • Loading branch information...
1 parent 91cfc14 commit 8da0a34a398d93e82c7a78e37d3cfd7c02e49ecd @rfk committed Apr 2, 2011
Showing with 57 additions and 14 deletions.
  1. +49 −6 tnetstring/tns_core.c
  2. +8 −8 tools/shootout.py
View
55 tnetstring/tns_core.c
@@ -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;
@@ -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];
@@ -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.");
@@ -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)
{
@@ -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;
+}
+
View
16 tools/shootout.py
@@ -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__":

0 comments on commit 8da0a34

Please sign in to comment.