json_escape: add fast-path for when we don't need to escape.
authorRusty Russell <rusty@rustcorp.com.au>
Tue, 21 May 2019 04:34:49 +0000 (14:04 +0930)
committerRusty Russell <rusty@rustcorp.com.au>
Tue, 21 May 2019 04:38:15 +0000 (14:08 +0930)
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ccan/json_escape/json_escape.c
ccan/json_escape/json_escape.h
ccan/json_escape/test/run-take.c [new file with mode: 0644]

index d344940de0a5d934da693b959857326da194fb55..ed4bfab944504a48db7c9e1b4cdd2825d8863bf2 100644 (file)
@@ -19,6 +19,18 @@ bool json_escape_eq(const struct json_escape *a, const struct json_escape *b)
        return streq(a->s, b->s);
 }
 
+bool json_escape_needed(const char *str, size_t len)
+{
+       for (size_t i = 0; i < len; i++) {
+               if ((unsigned)str[i] < ' '
+                   || str[i] == 127
+                   || str[i] == '"'
+                   || str[i] == '\\')
+                       return true;
+       }
+       return false;
+}
+
 static struct json_escape *escape(const tal_t *ctx,
                                  const char *str TAKES,
                                  size_t len,
@@ -27,6 +39,16 @@ static struct json_escape *escape(const tal_t *ctx,
        struct json_escape *esc;
        size_t i, n;
 
+       /* Fast path: can steal, and nothing to escape. */
+       if (is_taken(str)
+           && tal_count(str) > len
+           && !json_escape_needed(str, len)) {
+               taken(str);
+               esc = (struct json_escape *)tal_steal(ctx, str);
+               esc->s[len] = '\0';
+               return esc;
+       }
+
        /* Worst case: all \uXXXX */
        esc = (struct json_escape *)tal_arr(ctx, char, len * 6 + 1);
 
index b8e0dfca1d41e3042a1477343c8e3259760879c1..5b33432f8fce768dc53b77ebeaf068a03d567fc9 100644 (file)
@@ -27,7 +27,8 @@ struct json_escape *json_escape_len(const tal_t *ctx,
 struct json_escape *json_partial_escape(const tal_t *ctx,
                                         const char *str TAKES);
 
-/* Extract a JSON-escaped string. */
+/* Do we need to escape this str? */
+bool json_escape_needed(const char *str, size_t len);
 
 /* Are two escape json strings identical? */
 bool json_escape_eq(const struct json_escape *a,
diff --git a/ccan/json_escape/test/run-take.c b/ccan/json_escape/test/run-take.c
new file mode 100644 (file)
index 0000000..f3b62a7
--- /dev/null
@@ -0,0 +1,35 @@
+#include <ccan/json_escape/json_escape.h>
+/* Include the C files directly. */
+#include <ccan/json_escape/json_escape.c>
+#include <ccan/tap/tap.h>
+
+int main(void)
+{
+       const tal_t *ctx = tal(NULL, char);
+       struct json_escape *e;
+       char *p;
+
+       /* This is how many tests you plan to run */
+       plan_tests(5);
+
+       /* This should simply be tal_steal */
+       p = tal_dup_arr(NULL, char, "Hello", 6, 0);
+       e = json_escape(ctx, take(p));
+       ok1(!strcmp(e->s, "Hello"));
+       ok1((void *)e == (void *)p);
+       ok1(tal_parent(e) == ctx);
+
+       /* This can't be tal_steal, but still should be freed. */
+       p = tal_dup_arr(NULL, char,
+                       "\\\b\f\n\r\t\""
+                       "\\\\\\b\\f\\n\\r\\t\\\"", 22, 0);
+       e = json_escape(ctx, take(p));
+       ok1(tal_parent(e) == ctx);
+       ok1(!strcmp(e->s,
+                   "\\\\\\b\\f\\n\\r\\t\\\""
+                   "\\\\\\\\\\\\b\\\\f\\\\n\\\\r\\\\t\\\\\\\""));
+       tal_free(ctx);
+
+       /* This exits depending on whether all tests passed */
+       return exit_status();
+}