Browse Source

Update Lwan (#5458)

* Lwan: Update to latest version

* Lwan: Update test harness to the latest version

This includes many tweaks in the JSON encoder.
Leandro A. F. Pereira 5 years ago
parent
commit
fd6739ee50

+ 1 - 1
frameworks/C/lwan/lwan.dockerfile

@@ -11,7 +11,7 @@ WORKDIR /lwan
 RUN mkdir mimalloc && \
     wget https://github.com/microsoft/mimalloc/archive/acb03c54971c4b0a43a6d17ea55a9d5feb88972f.tar.gz -O - | tar xz --strip-components=1 -C mimalloc && \
     cd mimalloc && mkdir build && cd build && CFLAGS="-flto -ffat-lto-objects" cmake .. -DCMAKE_BUILD_TYPE=Release -DMI_SECURE=OFF && make -j install && cd ../.. && \
-    wget https://github.com/lpereira/lwan/archive/bc5566a07b0dba029086ae97ab1550611f529ac0.tar.gz -O - | tar xz --strip-components=1 && \
+    wget https://github.com/lpereira/lwan/archive/e3c03b6a975a7206a1b6e5d78a99abf5e7be1647.tar.gz -O - | tar xz --strip-components=1 && \
     mkdir build && cd build && \
     cmake /lwan -DCMAKE_BUILD_TYPE=Release -DUSE_ALTERNATIVE_MALLOC=mimalloc && \
     make lwan-static

+ 6 - 2
frameworks/C/lwan/src/database.c

@@ -302,7 +302,11 @@ static bool db_stmt_step_sqlite(const struct db_stmt *stmt,
         case 's': {
             char *out = va_arg(ap, char *);
             size_t bufsize = va_arg(ap, size_t);
-            strncpy(out, sqlite3_column_text(stmt_sqlite->sqlite, r), bufsize);
+
+            strncpy(out,
+                    (const char *)sqlite3_column_text(stmt_sqlite->sqlite, r),
+                    bufsize);
+
             break;
         }
         default:
@@ -360,7 +364,7 @@ db_connect_sqlite(const char *path, bool read_only, const char *pragmas[])
     if (!db_sqlite)
         return NULL;
 
-    int flags = read_only ? SQLITE_OPEN_READONLY : 0;
+    int flags = read_only ? SQLITE_OPEN_READONLY : SQLITE_OPEN_READWRITE;
     int ret = sqlite3_open_v2(path, &db_sqlite->sqlite, flags, NULL);
     if (ret != SQLITE_OK) {
         free(db_sqlite);

+ 79 - 117
frameworks/C/lwan/src/json.c

@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2017 Intel Corporation
+ * Copyright (c) 2020 Leandro A. F. Pereira <[email protected]>
  *
  * SPDX-License-Identifier: Apache-2.0
  */
@@ -635,20 +636,27 @@ static int json_escape_internal(const char *str,
                                 void *data)
 {
     const char *cur;
+    const char *unescaped;
     int ret = 0;
 
-    for (cur = str; ret == 0 && *cur; cur++) {
+    for (cur = unescaped = str; *cur; cur++) {
         char escaped = escape_as(*cur);
 
         if (escaped) {
             char bytes[2] = {'\\', escaped};
 
-            ret = append_bytes(bytes, 2, data);
-        } else {
-            ret = append_bytes(cur, 1, data);
+            if (unescaped - cur) {
+                ret |= append_bytes(unescaped, (size_t)(cur - unescaped), data);
+                unescaped = cur + 1;
+            }
+
+            ret |= append_bytes(bytes, 2, data);
         }
     }
 
+    if (unescaped - cur)
+        return ret | append_bytes(unescaped, (size_t)(cur - unescaped), data);
+
     return ret;
 }
 
@@ -710,14 +718,14 @@ static int encode(const struct json_obj_descr *descr,
                   const void *val,
                   json_append_bytes_t append_bytes,
                   void *data,
-                  bool encode_key);
+                  bool escape_key);
 
 static int arr_encode(const struct json_obj_descr *elem_descr,
                       const void *field,
                       const void *val,
                       json_append_bytes_t append_bytes,
                       void *data,
-                      bool encode_key)
+                      bool escape_key)
 {
     ptrdiff_t elem_size = get_elem_size(elem_descr);
     /*
@@ -727,76 +735,48 @@ static int arr_encode(const struct json_obj_descr *elem_descr,
      * elements.
      */
     size_t n_elem = *(size_t *)((char *)val + elem_descr->offset);
-    size_t i;
-    int ret;
-
-    if (UNLIKELY(!n_elem)) {
-        return append_bytes("[]", 2, data);
-    }
-
-    ret = append_bytes("[", 1, data);
-    if (UNLIKELY(ret < 0)) {
-        return ret;
-    }
-
-    n_elem--;
-    for (i = 0; i < n_elem; i++) {
-        /*
-         * Though "field" points at the next element in the array which we
-         * need to encode, the value in elem_descr->offset is actually the
-         * offset of the length field in the "parent" struct containing the
-         * array.
-         *
-         * To patch things up, we lie to encode() about where the field is
-         * by exactly the amount it will offset it.  This is a size
-         * optimization for struct json_obj_descr: the alternative is to
-         * keep a separate field next to element_descr which is an offset to
-         * the length field in the parent struct, but that would add a
-         * size_t to every descriptor.
-         */
-        ret = encode(elem_descr, (char *)field - elem_descr->offset,
-                     append_bytes, data, encode_key);
-        if (UNLIKELY(ret < 0)) {
-            return ret;
-        }
-
-        ret = append_bytes(",", 1, data);
-        if (UNLIKELY(ret < 0)) {
-            return ret;
+    int ret = append_bytes("[", 1, data);
+
+    if (LIKELY(n_elem)) {
+        n_elem--;
+        for (size_t i = 0; i < n_elem; i++) {
+            /*
+             * Though "field" points at the next element in the array which we
+             * need to encode, the value in elem_descr->offset is actually the
+             * offset of the length field in the "parent" struct containing the
+             * array.
+             *
+             * To patch things up, we lie to encode() about where the field is
+             * by exactly the amount it will offset it.  This is a size
+             * optimization for struct json_obj_descr: the alternative is to
+             * keep a separate field next to element_descr which is an offset to
+             * the length field in the parent struct, but that would add a
+             * size_t to every descriptor.
+             */
+            ret |= encode(elem_descr, (char *)field - elem_descr->offset,
+                          append_bytes, data, escape_key);
+
+            ret |= append_bytes(",", 1, data);
+
+            field = (char *)field + elem_size;
         }
 
-        field = (char *)field + elem_size;
-    }
-
-    ret = encode(elem_descr, (char *)field - elem_descr->offset,
-                 append_bytes, data, encode_key);
-    if (UNLIKELY(ret < 0)) {
-        return ret;
+        ret |= encode(elem_descr, (char *)field - elem_descr->offset,
+                      append_bytes, data, escape_key);
     }
 
-    return append_bytes("]", 1, data);
+    return ret | append_bytes("]", 1, data);
 }
 
-static int
-str_encode(const char **str, json_append_bytes_t append_bytes, void *data, bool encode)
+static int str_encode(const char **str,
+                      json_append_bytes_t append_bytes,
+                      void *data)
 {
-    int ret;
+    int ret = append_bytes("\"", 1, data);
 
-    ret = append_bytes("\"", 1, data);
-    if (UNLIKELY(ret < 0)) {
-        return ret;
-    }
+    ret |= json_escape_internal(*str, append_bytes, data);
 
-    if (encode) {
-        ret = json_escape_internal(*str, append_bytes, data);
-    } else {
-        ret = append_bytes(*str, strlen(*str), data);
-    }
-    if (!ret) {
-        return append_bytes("\"", 1, data);
-    }
-
-    return ret;
+    return ret | append_bytes("\"", 1, data);
 }
 
 static int
@@ -823,19 +803,19 @@ int json_arr_encode_full(const struct json_obj_descr *descr,
                          const void *val,
                          json_append_bytes_t append_bytes,
                          void *data,
-                         bool encode_key)
+                         bool escape_key)
 {
     void *ptr = (char *)val + descr->offset;
 
     return arr_encode(descr->array.element_descr, ptr, val, append_bytes, data,
-                      encode_key);
+                      escape_key);
 }
 
 static int encode(const struct json_obj_descr *descr,
                   const void *val,
                   json_append_bytes_t append_bytes,
                   void *data,
-                  bool encode_key)
+                  bool escape_key)
 {
     void *ptr = (char *)val + descr->offset;
 
@@ -844,14 +824,14 @@ static int encode(const struct json_obj_descr *descr,
     case JSON_TOK_TRUE:
         return bool_encode(ptr, append_bytes, data);
     case JSON_TOK_STRING:
-        return str_encode(ptr, append_bytes, data, true);
+        return str_encode(ptr, append_bytes, data);
     case JSON_TOK_LIST_START:
         return arr_encode(descr->array.element_descr, ptr, val,
-                          append_bytes, data, encode_key);
+                          append_bytes, data, escape_key);
     case JSON_TOK_OBJECT_START:
         return json_obj_encode_full(descr->object.sub_descr,
                                     descr->object.sub_descr_len, ptr, append_bytes,
-                                    data, encode_key);
+                                    data, escape_key);
     case JSON_TOK_NUMBER:
         return num_encode(ptr, append_bytes, data);
     default:
@@ -863,21 +843,19 @@ static int encode_key_value(const struct json_obj_descr *descr,
                             const void *val,
                             json_append_bytes_t append_bytes,
                             void *data,
-                            bool encode_key)
+                            bool escape_key)
 {
     int ret;
 
-    ret = str_encode((const char **)&descr->field_name, append_bytes, data, encode_key);
-    if (UNLIKELY(ret < 0)) {
-        return ret;
-    }
-
-    ret = append_bytes(":", 1, data);
-    if (UNLIKELY(ret < 0)) {
-        return ret;
+    if (!escape_key) {
+        ret = append_bytes(descr->field_name + descr->field_name_len,
+                           descr->field_name_len + 3 /* 3=len('"":') */, data);
+    } else {
+        ret = str_encode((const char **)&descr->field_name, append_bytes, data);
+        ret |= append_bytes(":", 1, data);
     }
 
-    return encode(descr, val, append_bytes, data, encode_key);
+    return ret | encode(descr, val, append_bytes, data, escape_key);
 }
 
 int json_obj_encode_full(const struct json_obj_descr *descr,
@@ -885,44 +863,28 @@ int json_obj_encode_full(const struct json_obj_descr *descr,
                          const void *val,
                          json_append_bytes_t append_bytes,
                          void *data,
-                         bool encode_key)
-{
-    size_t i;
-    int ret;
-
-    if (UNLIKELY(!descr_len)) {
-        /* Code below assumes at least one descr, so return early. */
-        return append_bytes("{}", 2, data);
-    }
-
-    ret = append_bytes("{", 1, data);
-    if (UNLIKELY(ret < 0)) {
-        return ret;
-    }
-
-    /* To avoid checking if we're encoding the last element on each iteration of
-     * this loop, start at the second descriptor, and always write the comma.
-     * Then, after the loop, encode the first descriptor.  If the descriptor
-     * array has only 1 element, this loop won't run.  This is fine since order
-     * isn't important for objects, and we save some branches.  */
-    for (i = 1; i < descr_len; i++) {
-        ret = encode_key_value(&descr[i], val, append_bytes, data, encode_key);
-        if (UNLIKELY(ret < 0)) {
-            return ret;
-        }
-
-        ret = append_bytes(",", 1, data);
-        if (UNLIKELY(ret < 0)) {
-            return ret;
+                         bool escape_key)
+{
+    int ret = append_bytes("{", 1, data);
+
+    if (LIKELY(descr_len)) {
+        /* To avoid checking if we're encoding the last element on each
+         * iteration of this loop, start at the second descriptor, and always
+         * write the comma. Then, after the loop, encode the first descriptor.
+         * If the descriptor array has only 1 element, this loop won't run. This
+         * is fine since order isn't important for objects, and we save some
+         * branches.  */
+
+        for (size_t i = 1; i < descr_len; i++) {
+            ret |= encode_key_value(&descr[i], val, append_bytes, data,
+                                    escape_key);
+            ret |= append_bytes(",", 1, data);
         }
-    }
 
-    ret = encode_key_value(&descr[0], val, append_bytes, data, encode_key);
-    if (UNLIKELY(ret < 0)) {
-        return ret;
+        ret |= encode_key_value(&descr[0], val, append_bytes, data, escape_key);
     }
 
-    return append_bytes("}", 1, data);
+    return ret | append_bytes("}", 1, data);
 }
 
 struct appender {

+ 99 - 99
frameworks/C/lwan/src/json.h

@@ -86,6 +86,10 @@ struct json_obj_descr {
  */
 typedef int (*json_append_bytes_t)(const char *bytes, size_t len, void *data);
 
+#define JSON_FIELD_NAME(field_name_)                                           \
+    .field_name = #field_name_ "\"" #field_name_ "\":",                        \
+    .field_name_len = sizeof(#field_name_) - 1
+
 /**
  * @brief Helper macro to declare a descriptor for supported primitive
  * values.
@@ -110,9 +114,9 @@ typedef int (*json_append_bytes_t)(const char *bytes, size_t len, void *data);
  */
 #define JSON_OBJ_DESCR_PRIM(struct_, field_name_, type_)                       \
     {                                                                          \
-        .field_name = (#field_name_), .align = __alignof__(struct_),           \
-        .field_name_len = sizeof(#field_name_) - 1, .type = type_,             \
-        .offset = offsetof(struct_, field_name_),                              \
+        JSON_FIELD_NAME(field_name_),                                          \
+            .align = __alignof__(struct_), .type = type_,                      \
+            .offset = offsetof(struct_, field_name_),                          \
     }
 
 /**
@@ -143,14 +147,13 @@ typedef int (*json_append_bytes_t)(const char *bytes, size_t len, void *data);
  */
 #define JSON_OBJ_DESCR_OBJECT(struct_, field_name_, sub_descr_)                \
     {                                                                          \
-        .field_name = (#field_name_), .align = __alignof__(struct_),           \
-        .field_name_len = (sizeof(#field_name_) - 1),                          \
-        .type = JSON_TOK_OBJECT_START,                                         \
-        .offset = offsetof(struct_, field_name_),                              \
-        .object = {                                                            \
-            .sub_descr = sub_descr_,                                           \
-            .sub_descr_len = ARRAY_SIZE(sub_descr_),                           \
-        },                                                                     \
+        JSON_FIELD_NAME(field_name_),                                          \
+            .align = __alignof__(struct_), .type = JSON_TOK_OBJECT_START,      \
+            .offset = offsetof(struct_, field_name_),                          \
+            .object = {                                                        \
+                .sub_descr = sub_descr_,                                       \
+                .sub_descr_len = ARRAY_SIZE(sub_descr_),                       \
+            },                                                                 \
     }
 
 /**
@@ -182,18 +185,18 @@ typedef int (*json_append_bytes_t)(const char *bytes, size_t len, void *data);
 #define JSON_OBJ_DESCR_ARRAY(struct_, field_name_, max_len_, len_field_,       \
                              elem_type_)                                       \
     {                                                                          \
-        .field_name = (#field_name_), .align = __alignof__(struct_),           \
-        .field_name_len = sizeof(#field_name_) - 1,                            \
-        .type = JSON_TOK_LIST_START, .offset = offsetof(struct_, field_name_), \
-        .array = {                                                             \
-            .element_descr =                                                   \
-                &(struct json_obj_descr){                                      \
-                    .align = __alignof__(struct_),                             \
-                    .type = elem_type_,                                        \
-                    .offset = offsetof(struct_, len_field_),                   \
-                },                                                             \
-            .n_elements = (max_len_),                                          \
-        },                                                                     \
+        JSON_FIELD_NAME(field_name_),                                          \
+            .align = __alignof__(struct_), .type = JSON_TOK_LIST_START,        \
+            .offset = offsetof(struct_, field_name_),                          \
+            .array = {                                                         \
+                .element_descr =                                               \
+                    &(struct json_obj_descr){                                  \
+                        .align = __alignof__(struct_),                         \
+                        .type = elem_type_,                                    \
+                        .offset = offsetof(struct_, len_field_),               \
+                    },                                                         \
+                .n_elements = (max_len_),                                      \
+            },                                                                 \
     }
 
 /**
@@ -238,23 +241,23 @@ typedef int (*json_append_bytes_t)(const char *bytes, size_t len, void *data);
 #define JSON_OBJ_DESCR_OBJ_ARRAY(struct_, field_name_, max_len_, len_field_,   \
                                  elem_descr_, elem_descr_len_)                 \
     {                                                                          \
-        .field_name = (#field_name_), .align = __alignof__(struct_),           \
-        .field_name_len = sizeof(#field_name_) - 1,                            \
-        .type = JSON_TOK_LIST_START, .offset = offsetof(struct_, field_name_), \
-        .array = {                                                             \
-            .element_descr =                                                   \
-                &(struct json_obj_descr){                                      \
-                    .align = __alignof__(struct_),                             \
-                    .type = JSON_TOK_OBJECT_START,                             \
-                    .offset = offsetof(struct_, len_field_),                   \
-                    .object =                                                  \
-                        {                                                      \
-                            .sub_descr = elem_descr_,                          \
-                            .sub_descr_len = elem_descr_len_,                  \
-                        },                                                     \
-                },                                                             \
-            .n_elements = (max_len_),                                          \
-        },                                                                     \
+        JSON_FIELD_NAME(field_name_),                                          \
+            .align = __alignof__(struct_), .type = JSON_TOK_LIST_START,        \
+            .offset = offsetof(struct_, field_name_),                          \
+            .array = {                                                         \
+                .element_descr =                                               \
+                    &(struct json_obj_descr){                                  \
+                        .align = __alignof__(struct_),                         \
+                        .type = JSON_TOK_OBJECT_START,                         \
+                        .offset = offsetof(struct_, len_field_),               \
+                        .object =                                              \
+                            {                                                  \
+                                .sub_descr = elem_descr_,                      \
+                                .sub_descr_len = elem_descr_len_,              \
+                            },                                                 \
+                    },                                                         \
+                .n_elements = (max_len_),                                      \
+            },                                                                 \
     }
 
 /**
@@ -308,23 +311,23 @@ typedef int (*json_append_bytes_t)(const char *bytes, size_t len, void *data);
 #define JSON_OBJ_DESCR_ARRAY_ARRAY(struct_, field_name_, max_len_, len_field_, \
                                    elem_descr_, elem_descr_len_)               \
     {                                                                          \
-        .field_name = (#field_name_), .align = __alignof__(struct_),           \
-        .field_name_len = sizeof(#field_name_) - 1,                            \
-        .type = JSON_TOK_LIST_START, .offset = offsetof(struct_, field_name_), \
-        .array = {                                                             \
-            .element_descr =                                                   \
-                &(struct json_obj_descr){                                      \
-                    .align = __alignof__(struct_),                             \
-                    .type = JSON_TOK_LIST_START,                               \
-                    .offset = offsetof(struct_, len_field_),                   \
-                    .object =                                                  \
-                        {                                                      \
-                            .sub_descr = elem_descr_,                          \
-                            .sub_descr_len = elem_descr_len_,                  \
-                        },                                                     \
-                },                                                             \
-            .n_elements = (max_len_),                                          \
-        },                                                                     \
+        JSON_FIELD_NAME(field_name_),                                          \
+            .align = __alignof__(struct_), .type = JSON_TOK_LIST_START,        \
+            .offset = offsetof(struct_, field_name_),                          \
+            .array = {                                                         \
+                .element_descr =                                               \
+                    &(struct json_obj_descr){                                  \
+                        .align = __alignof__(struct_),                         \
+                        .type = JSON_TOK_LIST_START,                           \
+                        .offset = offsetof(struct_, len_field_),               \
+                        .object =                                              \
+                            {                                                  \
+                                .sub_descr = elem_descr_,                      \
+                                .sub_descr_len = elem_descr_len_,              \
+                            },                                                 \
+                    },                                                         \
+                .n_elements = (max_len_),                                      \
+            },                                                                 \
     }
 
 /**
@@ -347,9 +350,9 @@ typedef int (*json_append_bytes_t)(const char *bytes, size_t len, void *data);
 #define JSON_OBJ_DESCR_PRIM_NAMED(struct_, json_field_name_,                   \
                                   struct_field_name_, type_)                   \
     {                                                                          \
-        .field_name = (json_field_name_), .align = __alignof__(struct_),       \
-        .field_name_len = sizeof(json_field_name_) - 1, .type = type_,         \
-        .offset = offsetof(struct_, struct_field_name_),                       \
+        JSON_FIELD_NAME(json_field_name_),                                     \
+            .align = __alignof__(struct_), .type = type_,                      \
+            .offset = offsetof(struct_, struct_field_name_),                   \
     }
 
 /**
@@ -371,14 +374,13 @@ typedef int (*json_append_bytes_t)(const char *bytes, size_t len, void *data);
 #define JSON_OBJ_DESCR_OBJECT_NAMED(struct_, json_field_name_,                 \
                                     struct_field_name_, sub_descr_)            \
     {                                                                          \
-        .field_name = (json_field_name_), .align = __alignof__(struct_),       \
-        .field_name_len = (sizeof(json_field_name_) - 1),                      \
-        .type = JSON_TOK_OBJECT_START,                                         \
-        .offset = offsetof(struct_, struct_field_name_),                       \
-        .object = {                                                            \
-            .sub_descr = sub_descr_,                                           \
-            .sub_descr_len = ARRAY_SIZE(sub_descr_),                           \
-        },                                                                     \
+        JSON_FIELD_NAME(json_field_name_),                                     \
+            .align = __alignof__(struct_), .type = JSON_TOK_OBJECT_START,      \
+            .offset = offsetof(struct_, struct_field_name_),                   \
+            .object = {                                                        \
+                .sub_descr = sub_descr_,                                       \
+                .sub_descr_len = ARRAY_SIZE(sub_descr_),                       \
+            },                                                                 \
     }
 
 /**
@@ -406,19 +408,18 @@ typedef int (*json_append_bytes_t)(const char *bytes, size_t len, void *data);
                                    struct_field_name_, max_len_, len_field_,   \
                                    elem_type_)                                 \
     {                                                                          \
-        .field_name = (json_field_name_), .align = __alignof__(struct_),       \
-        .field_name_len = sizeof(json_field_name_) - 1,                        \
-        .type = JSON_TOK_LIST_START,                                           \
-        .offset = offsetof(struct_, struct_field_name_),                       \
-        .array = {                                                             \
-            .element_descr =                                                   \
-                &(struct json_obj_descr){                                      \
-                    .align = __alignof__(struct_),                             \
-                    .type = elem_type_,                                        \
-                    .offset = offsetof(struct_, len_field_),                   \
-                },                                                             \
-            .n_elements = (max_len_),                                          \
-        },                                                                     \
+        JSON_FIELD_NAME(json_field_name_),                                     \
+            .align = __alignof__(struct_), .type = JSON_TOK_LIST_START,        \
+            .offset = offsetof(struct_, struct_field_name_),                   \
+            .array = {                                                         \
+                .element_descr =                                               \
+                    &(struct json_obj_descr){                                  \
+                        .align = __alignof__(struct_),                         \
+                        .type = elem_type_,                                    \
+                        .offset = offsetof(struct_, len_field_),               \
+                    },                                                         \
+                .n_elements = (max_len_),                                      \
+            },                                                                 \
     }
 
 /**
@@ -471,22 +472,21 @@ typedef int (*json_append_bytes_t)(const char *bytes, size_t len, void *data);
     struct_, json_field_name_, struct_field_name_, max_len_, len_field_,       \
     elem_descr_, elem_descr_len_)                                              \
     {                                                                          \
-        .field_name = json_field_name_, .align = __alignof__(struct_),         \
-        .field_name_len = sizeof(json_field_name_) - 1,                        \
-        .type = JSON_TOK_LIST_START,                                           \
-        .offset = offsetof(struct_, struct_field_name_),                       \
-        .element_descr =                                                       \
-            &(struct json_obj_descr){                                          \
-                .align = __alignof__(struct_),                                 \
-                .type = JSON_TOK_OBJECT_START,                                 \
-                .offset = offsetof(struct_, len_field_),                       \
-                .object =                                                      \
-                    {                                                          \
-                        .sub_descr = elem_descr_,                              \
-                        .sub_descr_len = elem_descr_len_,                      \
-                    },                                                         \
-            },                                                                 \
-        .n_elements = (max_len_),                                              \
+        JSON_FIELD_NAME(json_field_name_),                                     \
+            .align = __alignof__(struct_), .type = JSON_TOK_LIST_START,        \
+            .offset = offsetof(struct_, struct_field_name_),                   \
+            .element_descr =                                                   \
+                &(struct json_obj_descr){                                      \
+                    .align = __alignof__(struct_),                             \
+                    .type = JSON_TOK_OBJECT_START,                             \
+                    .offset = offsetof(struct_, len_field_),                   \
+                    .object =                                                  \
+                        {                                                      \
+                            .sub_descr = elem_descr_,                          \
+                            .sub_descr_len = elem_descr_len_,                  \
+                        },                                                     \
+                },                                                             \
+            .n_elements = (max_len_),                                          \
     }
 
 /**
@@ -617,7 +617,7 @@ int json_obj_encode_full(const struct json_obj_descr *descr,
                          const void *val,
                          json_append_bytes_t append_bytes,
                          void *data,
-                         bool encode_key);
+                         bool escape_key);
 static inline int json_obj_encode(const struct json_obj_descr *descr,
                                   size_t descr_len,
                                   const void *val,
@@ -649,7 +649,7 @@ int json_arr_encode_full(const struct json_obj_descr *descr,
                          const void *val,
                          json_append_bytes_t append_bytes,
                          void *data,
-                         bool encode_key);
+                         bool escape_key);
 static inline int json_arr_encode(const struct json_obj_descr *descr,
                                   const void *val,
                                   json_append_bytes_t append_bytes,

+ 30 - 43
frameworks/C/lwan/src/techempower.c

@@ -22,7 +22,7 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include "lwan.h"
+#include "lwan-private.h"
 #include "lwan-config.h"
 #include "lwan-template.h"
 
@@ -149,7 +149,7 @@ static int append_to_strbuf(const char *bytes, size_t len, void *data)
 {
     struct lwan_strbuf *strbuf = data;
 
-    return lwan_strbuf_append_str(strbuf, bytes, len) ? 0 : -EINVAL;
+    return !lwan_strbuf_append_str(strbuf, bytes, len);
 }
 
 static enum lwan_http_status
@@ -158,10 +158,8 @@ json_response_obj(struct lwan_response *response,
                   size_t descr_len,
                   const void *data)
 {
-    lwan_strbuf_grow_to(response->buffer, 128);
-
     if (json_obj_encode_full(descr, descr_len, data, append_to_strbuf,
-                             response->buffer, false) < 0)
+                             response->buffer, false) != 0)
         return HTTP_INTERNAL_ERROR;
 
     response->mime_type = "application/json";
@@ -173,10 +171,8 @@ json_response_arr(struct lwan_response *response,
                   const struct json_obj_descr *descr,
                   const void *data)
 {
-    lwan_strbuf_grow_to(response->buffer, 128);
-
     if (json_arr_encode_full(descr, data, append_to_strbuf, response->buffer,
-                             false) < 0)
+                             false) != 0)
         return HTTP_INTERNAL_ERROR;
 
     response->mime_type = "application/json";
@@ -188,28 +184,20 @@ LWAN_HANDLER(json)
     struct hello_world_json j = {.message = hello_world};
 
     return json_response_obj(response, hello_world_json_desc,
-                         N_ELEMENTS(hello_world_json_desc), &j);
+                             N_ELEMENTS(hello_world_json_desc), &j);
 }
 
-static bool db_query(struct db_stmt *stmt,
-                     struct db_row rows[],
-                     size_t n_rows,
-                     struct db_json *out)
+static bool db_query(struct db_stmt *stmt, struct db_json *out)
 {
-    const int id = (rand() % 10000) + 1;
-
-    assert(n_rows >= 1);
-
-    rows[0].u.i = id;
-
-    if (UNLIKELY(!db_stmt_bind(stmt, rows, n_rows)))
+    struct db_row row = {.kind = 'i', .u.i = (rand() % 10000) + 1};
+    if (UNLIKELY(!db_stmt_bind(stmt, &row, 1)))
         return false;
 
     long random_number;
     if (UNLIKELY(!db_stmt_step(stmt, "i", &random_number)))
         return false;
 
-    out->id = id;
+    out->id = row.u.i;
     out->randomNumber = (int)random_number;
 
     return true;
@@ -217,7 +205,6 @@ static bool db_query(struct db_stmt *stmt,
 
 LWAN_HANDLER(db)
 {
-    struct db_row rows[] = {{.kind = 'i'}};
     struct db_stmt *stmt = db_prepare_stmt(get_db(), random_number_query,
                                            sizeof(random_number_query) - 1);
     struct db_json db_json;
@@ -227,7 +214,7 @@ LWAN_HANDLER(db)
         return HTTP_INTERNAL_ERROR;
     }
 
-    bool queried = db_query(stmt, rows, N_ELEMENTS(rows), &db_json);
+    bool queried = db_query(stmt, &db_json);
 
     db_stmt_finalize(stmt);
 
@@ -244,15 +231,9 @@ LWAN_HANDLER(queries)
     const char *queries_str = lwan_request_get_query_param(request, "queries");
     long queries;
 
-    if (LIKELY(queries_str)) {
-        queries = parse_long(queries_str, -1);
-        if (UNLIKELY(queries <= 0))
-            queries = 1;
-        else if (UNLIKELY(queries > 500))
-            queries = 500;
-    } else {
-        queries = 1;
-    }
+    queries = LIKELY(queries_str)
+                  ? LWAN_MIN(500, LWAN_MAX(1, parse_long(queries_str, -1)))
+                  : 1;
 
     struct db_stmt *stmt = db_prepare_stmt(get_db(), random_number_query,
                                            sizeof(random_number_query) - 1);
@@ -260,12 +241,16 @@ LWAN_HANDLER(queries)
         return HTTP_INTERNAL_ERROR;
 
     struct queries_json qj = {.queries_len = (size_t)queries};
-    struct db_row results[] = {{.kind = 'i'}};
     for (long i = 0; i < queries; i++) {
-        if (!db_query(stmt, results, N_ELEMENTS(results), &qj.queries[i]))
+        if (!db_query(stmt, &qj.queries[i]))
             goto out;
     }
 
+    /* Avoid reallocations/copies while building response.  Each response
+     * has ~32bytes.  500 queries (max) should be less than 16384 bytes,
+     * so this is a good approximation.  */
+    lwan_strbuf_grow_to(response->buffer, (size_t)(32l * queries));
+
     ret = json_response_arr(response, &queries_array_desc, &qj);
 
 out:
@@ -287,16 +272,8 @@ static int fortune_compare(const void *a, const void *b)
 {
     const struct Fortune *fortune_a = (const struct Fortune *)a;
     const struct Fortune *fortune_b = (const struct Fortune *)b;
-    size_t a_len = strlen(fortune_a->item.message);
-    size_t b_len = strlen(fortune_b->item.message);
-
-    if (!a_len || !b_len)
-        return a_len > b_len;
 
-    size_t min_len = a_len < b_len ? a_len : b_len;
-
-    int cmp = memcmp(fortune_a->item.message, fortune_b->item.message, min_len);
-    return cmp == 0 ? -(int)(ssize_t)min_len : cmp;
+    return strcmp(fortune_a->item.message, fortune_b->item.message);
 }
 
 static bool append_fortune(struct coro *coro,
@@ -374,14 +351,24 @@ LWAN_HANDLER(fortunes)
     return HTTP_OK;
 }
 
+LWAN_HANDLER(quit_lwan)
+{
+    exit(0);
+    return HTTP_OK;
+}
+
 int main(void)
 {
     static const struct lwan_url_map url_map[] = {
+        /* Routes for the TWFB benchmark: */
         {.prefix = "/json", .handler = LWAN_HANDLER_REF(json)},
         {.prefix = "/db", .handler = LWAN_HANDLER_REF(db)},
         {.prefix = "/queries", .handler = LWAN_HANDLER_REF(queries)},
         {.prefix = "/plaintext", .handler = LWAN_HANDLER_REF(plaintext)},
         {.prefix = "/fortunes", .handler = LWAN_HANDLER_REF(fortunes)},
+        /* Routes for the test harness: */
+        {.prefix = "/quit-lwan", .handler = LWAN_HANDLER_REF(quit_lwan)},
+        {.prefix = "/hello", .handler = LWAN_HANDLER_REF(plaintext)},
         {.prefix = NULL},
     };
     struct lwan l;