Browse Source

Lwan: Enable multiple queries benchmark / misc. updates (#5405)

* Lwan: enable multiple queries benchmark

* Lwan: fix single/multiple query spurious errors

* Lwan: cherry-pick changes in benchmark harness from Lwan repo
Leandro A. F. Pereira 5 years ago
parent
commit
bb916116bd

+ 1 - 1
frameworks/C/lwan/benchmark_config.json

@@ -5,7 +5,7 @@
       "default": {
       "default": {
         "json_url": "/json",
         "json_url": "/json",
         "db_url": "/db",
         "db_url": "/db",
-        "queries_url": "/queries",
+        "query_url": "/queries?queries=",
         "plaintext_url": "/plaintext",
         "plaintext_url": "/plaintext",
         "fortune_url": "/fortunes",
         "fortune_url": "/fortunes",
         "port": 8080,
         "port": 8080,

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

@@ -11,7 +11,7 @@ WORKDIR /lwan
 RUN mkdir mimalloc && \
 RUN mkdir mimalloc && \
     wget https://github.com/microsoft/mimalloc/archive/acb03c54971c4b0a43a6d17ea55a9d5feb88972f.tar.gz -O - | tar xz --strip-components=1 -C 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 ../.. && \
     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/b52c9f5e17542800a762f19bc9073bd8b3b95cb3.tar.gz -O - | tar xz --strip-components=1 && \
+    wget https://github.com/lpereira/lwan/archive/bc5566a07b0dba029086ae97ab1550611f529ac0.tar.gz -O - | tar xz --strip-components=1 && \
     mkdir build && cd build && \
     mkdir build && cd build && \
     cmake /lwan -DCMAKE_BUILD_TYPE=Release -DUSE_ALTERNATIVE_MALLOC=mimalloc && \
     cmake /lwan -DCMAKE_BUILD_TYPE=Release -DUSE_ALTERNATIVE_MALLOC=mimalloc && \
     make lwan-static
     make lwan-static

+ 76 - 54
frameworks/C/lwan/src/database.c

@@ -18,10 +18,12 @@
  * USA.
  * USA.
  */
  */
 
 
+#include <string.h>
 #include <mysql.h>
 #include <mysql.h>
 #include <sqlite3.h>
 #include <sqlite3.h>
 #include <stddef.h>
 #include <stddef.h>
 #include <stdlib.h>
 #include <stdlib.h>
+#include <stdarg.h>
 
 
 #include "database.h"
 #include "database.h"
 #include "lwan-status.h"
 #include "lwan-status.h"
@@ -30,7 +32,7 @@ struct db_stmt {
     bool (*bind)(const struct db_stmt *stmt,
     bool (*bind)(const struct db_stmt *stmt,
                  struct db_row *rows,
                  struct db_row *rows,
                  size_t n_rows);
                  size_t n_rows);
-    bool (*step)(const struct db_stmt *stmt, struct db_row *row);
+    bool (*step)(const struct db_stmt *stmt, const char *signature, va_list ap);
     void (*finalize)(struct db_stmt *stmt);
     void (*finalize)(struct db_stmt *stmt);
 };
 };
 
 
@@ -66,25 +68,28 @@ static bool db_stmt_bind_mysql(const struct db_stmt *stmt,
 
 
     if (!stmt_mysql->param_bind) {
     if (!stmt_mysql->param_bind) {
         stmt_mysql->param_bind = calloc(n_rows, sizeof(MYSQL_BIND));
         stmt_mysql->param_bind = calloc(n_rows, sizeof(MYSQL_BIND));
-        if (!stmt_mysql->param_bind) {
+        if (!stmt_mysql->param_bind)
             return false;
             return false;
-        }
     } else {
     } else {
         mysql_stmt_reset(stmt_mysql->stmt);
         mysql_stmt_reset(stmt_mysql->stmt);
     }
     }
 
 
-    for (size_t row = 0; row < n_rows; row++) {
-        if (rows[row].kind == '\0')
-            break;
-
+    for (size_t row = 0; row < n_rows && rows[row].kind; row++) {
         MYSQL_BIND *param = &stmt_mysql->param_bind[row];
         MYSQL_BIND *param = &stmt_mysql->param_bind[row];
-        if (rows[row].kind == 's') {
+
+        switch (rows[row].kind) {
+        case 's':
             param->buffer_type = MYSQL_TYPE_STRING;
             param->buffer_type = MYSQL_TYPE_STRING;
             param->buffer = rows[row].u.s;
             param->buffer = rows[row].u.s;
-        } else if (rows[row].kind == 'i') {
+            break;
+        case 'i':
             param->buffer_type = MYSQL_TYPE_LONG;
             param->buffer_type = MYSQL_TYPE_LONG;
             param->buffer = &rows[row].u.i;
             param->buffer = &rows[row].u.i;
+            break;
+        default:
+            return false;
         }
         }
+
         param->is_null = false;
         param->is_null = false;
         param->length = 0;
         param->length = 0;
     }
     }
@@ -92,7 +97,9 @@ static bool db_stmt_bind_mysql(const struct db_stmt *stmt,
     return !mysql_stmt_bind_param(stmt_mysql->stmt, stmt_mysql->param_bind);
     return !mysql_stmt_bind_param(stmt_mysql->stmt, stmt_mysql->param_bind);
 }
 }
 
 
-static bool db_stmt_step_mysql(const struct db_stmt *stmt, struct db_row *row)
+static bool db_stmt_step_mysql(const struct db_stmt *stmt,
+                               const char *signature,
+                               va_list ap)
 {
 {
     struct db_stmt_mysql *stmt_mysql = (struct db_stmt_mysql *)stmt;
     struct db_stmt_mysql *stmt_mysql = (struct db_stmt_mysql *)stmt;
 
 
@@ -103,46 +110,48 @@ static bool db_stmt_step_mysql(const struct db_stmt *stmt, struct db_row *row)
     }
     }
 
 
     if (!stmt_mysql->result_bind) {
     if (!stmt_mysql->result_bind) {
-        size_t n_rows = 0;
-        for (struct db_row *r = row; r->kind != '\0'; r++)
-            n_rows++;
-
-        if (!n_rows)
+        if (*signature == '\0')
             return false;
             return false;
 
 
         stmt_mysql->result_bind =
         stmt_mysql->result_bind =
-            calloc(n_rows, sizeof(*stmt_mysql->result_bind));
+            calloc(strlen(signature), sizeof(*stmt_mysql->result_bind));
         if (!stmt_mysql->result_bind)
         if (!stmt_mysql->result_bind)
             return false;
             return false;
 
 
-        stmt_mysql->param_bind =
-            calloc(n_rows, sizeof(*stmt_mysql->param_bind));
-        if (!stmt_mysql->param_bind) {
-            free(stmt_mysql->result_bind);
-            return false;
-        }
+        free(stmt_mysql->param_bind);
+        stmt_mysql->param_bind = NULL;
 
 
         MYSQL_BIND *result = stmt_mysql->result_bind;
         MYSQL_BIND *result = stmt_mysql->result_bind;
-        for (size_t r = 0; r < n_rows; r++) {
-            if (row[r].kind == 's') {
+        for (size_t r = 0; signature[r]; r++) {
+            switch (signature[r]) {
+            case 's':
                 result[r].buffer_type = MYSQL_TYPE_STRING;
                 result[r].buffer_type = MYSQL_TYPE_STRING;
-                result[r].buffer = row[r].u.s;
-            } else if (row[r].kind == 'i') {
+                result[r].buffer = va_arg(ap, char *);
+                result[r].buffer_length = va_arg(ap, size_t);
+                break;
+            case 'i':
                 result[r].buffer_type = MYSQL_TYPE_LONG;
                 result[r].buffer_type = MYSQL_TYPE_LONG;
-                result[r].buffer = &row[r].u.i;
-            } else {
-                return false;
+                result[r].buffer = va_arg(ap, long *);
+                result[r].buffer_length = 0;
+                break;
+            default:
+                goto out;
             }
             }
 
 
             result[r].is_null = false;
             result[r].is_null = false;
-            result[r].buffer_length = row[r].buffer_length;
         }
         }
 
 
         if (mysql_stmt_bind_result(stmt_mysql->stmt, result))
         if (mysql_stmt_bind_result(stmt_mysql->stmt, result))
-            return false;
+            goto out;
     }
     }
 
 
     return mysql_stmt_fetch(stmt_mysql->stmt) == 0;
     return mysql_stmt_fetch(stmt_mysql->stmt) == 0;
+
+out:
+    free(stmt_mysql->result_bind);
+    stmt_mysql->result_bind = NULL;
+
+    return false;
 }
 }
 
 
 static void db_stmt_finalize_mysql(struct db_stmt *stmt)
 static void db_stmt_finalize_mysql(struct db_stmt *stmt)
@@ -248,35 +257,36 @@ static bool db_stmt_bind_sqlite(const struct db_stmt *stmt,
 {
 {
     const struct db_stmt_sqlite *stmt_sqlite =
     const struct db_stmt_sqlite *stmt_sqlite =
         (const struct db_stmt_sqlite *)stmt;
         (const struct db_stmt_sqlite *)stmt;
-    const struct db_row *rows_1_based = rows - 1;
-    int ret;
 
 
     sqlite3_reset(stmt_sqlite->sqlite);
     sqlite3_reset(stmt_sqlite->sqlite);
     sqlite3_clear_bindings(stmt_sqlite->sqlite);
     sqlite3_clear_bindings(stmt_sqlite->sqlite);
 
 
     for (size_t row = 1; row <= n_rows; row++) {
     for (size_t row = 1; row <= n_rows; row++) {
-        const struct db_row *r = &rows_1_based[row];
-        if (r->kind == '\0')
-            break;
+        const struct db_row *r = &rows[row - 1];
+        int ret;
 
 
-        if (r->kind == 's') {
+        switch (r->kind) {
+        case 's':
             ret = sqlite3_bind_text(stmt_sqlite->sqlite, (int)row, r->u.s, -1,
             ret = sqlite3_bind_text(stmt_sqlite->sqlite, (int)row, r->u.s, -1,
                                     NULL);
                                     NULL);
-            if (ret != SQLITE_OK)
-                return false;
-        } else if (r->kind == 'i') {
+            break;
+        case 'i':
             ret = sqlite3_bind_int(stmt_sqlite->sqlite, (int)row, r->u.i);
             ret = sqlite3_bind_int(stmt_sqlite->sqlite, (int)row, r->u.i);
-            if (ret != SQLITE_OK)
-                return false;
-        } else {
+            break;
+        default:
             return false;
             return false;
         }
         }
+
+        if (ret != SQLITE_OK)
+            return false;
     }
     }
 
 
     return true;
     return true;
 }
 }
 
 
-static bool db_stmt_step_sqlite(const struct db_stmt *stmt, struct db_row *row)
+static bool db_stmt_step_sqlite(const struct db_stmt *stmt,
+                                const char *signature,
+                                va_list ap)
 {
 {
     const struct db_stmt_sqlite *stmt_sqlite =
     const struct db_stmt_sqlite *stmt_sqlite =
         (const struct db_stmt_sqlite *)stmt;
         (const struct db_stmt_sqlite *)stmt;
@@ -284,14 +294,18 @@ static bool db_stmt_step_sqlite(const struct db_stmt *stmt, struct db_row *row)
     if (sqlite3_step(stmt_sqlite->sqlite) != SQLITE_ROW)
     if (sqlite3_step(stmt_sqlite->sqlite) != SQLITE_ROW)
         return false;
         return false;
 
 
-    int column_id = 0;
-    for (struct db_row *r = row; r->kind != '\0'; r++, column_id++) {
-        if (r->kind == 'i') {
-            r->u.i = sqlite3_column_int(stmt_sqlite->sqlite, column_id);
-        } else if (r->kind == 's') {
-            r->u.s =
-                (char *)sqlite3_column_text(stmt_sqlite->sqlite, column_id);
-        } else {
+    for (int r = 0; signature[r]; r++) {
+        switch (signature[r]) {
+        case 'i':
+            *va_arg(ap, long *) = sqlite3_column_int(stmt_sqlite->sqlite, r);
+            break;
+        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);
+            break;
+        }
+        default:
             return false;
             return false;
         }
         }
     }
     }
@@ -372,9 +386,17 @@ db_stmt_bind(const struct db_stmt *stmt, struct db_row *rows, size_t n_rows)
     return stmt->bind(stmt, rows, n_rows);
     return stmt->bind(stmt, rows, n_rows);
 }
 }
 
 
-inline bool db_stmt_step(const struct db_stmt *stmt, struct db_row *row)
+inline bool
+db_stmt_step(const struct db_stmt *stmt, const char *signature, ...)
 {
 {
-    return stmt->step(stmt, row);
+    va_list ap;
+    bool ret;
+
+    va_start(ap, signature);
+    ret = stmt->step(stmt, signature, ap);
+    va_end(ap);
+
+    return ret;
 }
 }
 
 
 inline void db_stmt_finalize(struct db_stmt *stmt) { stmt->finalize(stmt); }
 inline void db_stmt_finalize(struct db_stmt *stmt) { stmt->finalize(stmt); }

+ 16 - 8
frameworks/C/lwan/src/database.h

@@ -14,7 +14,8 @@
  *
  *
  * You should have received a copy of the GNU General Public License
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
  */
  */
 
 
 #pragma once
 #pragma once
@@ -33,13 +34,20 @@ struct db_row {
     size_t buffer_length;
     size_t buffer_length;
 };
 };
 
 
-bool db_stmt_bind(const struct db_stmt *stmt, struct db_row *rows, size_t n_rows);
-bool db_stmt_step(const struct db_stmt *stmt, struct db_row *row);
+bool db_stmt_bind(const struct db_stmt *stmt,
+                  struct db_row *rows,
+                  size_t n_rows);
+
+bool db_stmt_step(const struct db_stmt *stmt, const char *signature, ...);
+
 void db_stmt_finalize(struct db_stmt *stmt);
 void db_stmt_finalize(struct db_stmt *stmt);
 void db_disconnect(struct db *db);
 void db_disconnect(struct db *db);
-struct db_stmt *db_prepare_stmt(const struct db *db, const char *sql,
-    const size_t sql_len);
-
-struct db *db_connect_sqlite(const char *path, bool read_only, const char *pragmas[]);
-struct db *db_connect_mysql(const char *host, const char *user, const char *pass, const char *database);
+struct db_stmt *
+db_prepare_stmt(const struct db *db, const char *sql, const size_t sql_len);
 
 
+struct db *
+db_connect_sqlite(const char *path, bool read_only, const char *pragmas[]);
+struct db *db_connect_mysql(const char *host,
+                            const char *user,
+                            const char *pass,
+                            const char *database);

+ 146 - 100
frameworks/C/lwan/src/json.c

@@ -15,6 +15,8 @@
 #include <string.h>
 #include <string.h>
 
 
 #include "json.h"
 #include "json.h"
+#include "lwan.h"
+#include "int-to-str.h"
 
 
 struct token {
 struct token {
     enum json_tokens type;
     enum json_tokens type;
@@ -108,7 +110,7 @@ static void *lexer_string(struct lexer *lexer)
     while (true) {
     while (true) {
         int chr = next(lexer);
         int chr = next(lexer);
 
 
-        if (chr == '\0') {
+        if (UNLIKELY(chr == '\0')) {
             emit(lexer, JSON_TOK_ERROR);
             emit(lexer, JSON_TOK_ERROR);
             return NULL;
             return NULL;
         }
         }
@@ -125,19 +127,19 @@ static void *lexer_string(struct lexer *lexer)
             case 't':
             case 't':
                 continue;
                 continue;
             case 'u':
             case 'u':
-                if (!isxdigit(next(lexer))) {
+                if (UNLIKELY(!isxdigit(next(lexer)))) {
                     goto error;
                     goto error;
                 }
                 }
 
 
-                if (!isxdigit(next(lexer))) {
+                if (UNLIKELY(!isxdigit(next(lexer)))) {
                     goto error;
                     goto error;
                 }
                 }
 
 
-                if (!isxdigit(next(lexer))) {
+                if (UNLIKELY(!isxdigit(next(lexer)))) {
                     goto error;
                     goto error;
                 }
                 }
 
 
-                if (!isxdigit(next(lexer))) {
+                if (UNLIKELY(!isxdigit(next(lexer)))) {
                     goto error;
                     goto error;
                 }
                 }
 
 
@@ -166,7 +168,7 @@ error:
 static int accept_run(struct lexer *lexer, const char *run)
 static int accept_run(struct lexer *lexer, const char *run)
 {
 {
     for (; *run; run++) {
     for (; *run; run++) {
-        if (next(lexer) != *run) {
+        if (UNLIKELY(next(lexer) != *run)) {
             return -EINVAL;
             return -EINVAL;
         }
         }
     }
     }
@@ -176,17 +178,17 @@ static int accept_run(struct lexer *lexer, const char *run)
 
 
 static void *lexer_boolean(struct lexer *lexer)
 static void *lexer_boolean(struct lexer *lexer)
 {
 {
-    backup(lexer);
+    /* Already matched either `t' or `f' at this point */
 
 
     switch (next(lexer)) {
     switch (next(lexer)) {
-    case 't':
-        if (!accept_run(lexer, "rue")) {
+    case 'r':
+        if (LIKELY(!accept_run(lexer, "ue"))) {
             emit(lexer, JSON_TOK_TRUE);
             emit(lexer, JSON_TOK_TRUE);
             return lexer_json;
             return lexer_json;
         }
         }
         break;
         break;
-    case 'f':
-        if (!accept_run(lexer, "alse")) {
+    case 'a':
+        if (LIKELY(!accept_run(lexer, "lse"))) {
             emit(lexer, JSON_TOK_FALSE);
             emit(lexer, JSON_TOK_FALSE);
             return lexer_json;
             return lexer_json;
         }
         }
@@ -199,7 +201,7 @@ static void *lexer_boolean(struct lexer *lexer)
 
 
 static void *lexer_null(struct lexer *lexer)
 static void *lexer_null(struct lexer *lexer)
 {
 {
-    if (accept_run(lexer, "ull") < 0) {
+    if (UNLIKELY(accept_run(lexer, "ull") < 0)) {
         emit(lexer, JSON_TOK_ERROR);
         emit(lexer, JSON_TOK_ERROR);
         return NULL;
         return NULL;
     }
     }
@@ -249,7 +251,7 @@ static void *lexer_json(struct lexer *lexer)
         case 'f':
         case 'f':
             return lexer_boolean;
             return lexer_boolean;
         case '-':
         case '-':
-            if (isdigit(peek(lexer))) {
+            if (LIKELY(isdigit(peek(lexer)))) {
                 return lexer_number;
                 return lexer_number;
             }
             }
 
 
@@ -260,7 +262,7 @@ static void *lexer_json(struct lexer *lexer)
                 continue;
                 continue;
             }
             }
 
 
-            if (isdigit(chr)) {
+            if (LIKELY(isdigit(chr))) {
                 return lexer_number;
                 return lexer_number;
             }
             }
 
 
@@ -285,11 +287,11 @@ static int obj_init(struct json_obj *json, char *data, size_t len)
 
 
     lexer_init(&json->lexer, data, len);
     lexer_init(&json->lexer, data, len);
 
 
-    if (!lexer_next(&json->lexer, &token)) {
+    if (UNLIKELY(!lexer_next(&json->lexer, &token))) {
         return -EINVAL;
         return -EINVAL;
     }
     }
 
 
-    if (token.type != JSON_TOK_OBJECT_START) {
+    if (UNLIKELY(token.type != JSON_TOK_OBJECT_START)) {
         return -EINVAL;
         return -EINVAL;
     }
     }
 
 
@@ -315,7 +317,7 @@ static int obj_next(struct json_obj *json, struct json_obj_key_value *kv)
 {
 {
     struct token token;
     struct token token;
 
 
-    if (!lexer_next(&json->lexer, &token)) {
+    if (UNLIKELY(!lexer_next(&json->lexer, &token))) {
         return -EINVAL;
         return -EINVAL;
     }
     }
 
 
@@ -328,11 +330,11 @@ static int obj_next(struct json_obj *json, struct json_obj_key_value *kv)
 
 
         return 0;
         return 0;
     case JSON_TOK_COMMA:
     case JSON_TOK_COMMA:
-        if (!lexer_next(&json->lexer, &token)) {
+        if (UNLIKELY(!lexer_next(&json->lexer, &token))) {
             return -EINVAL;
             return -EINVAL;
         }
         }
 
 
-        if (token.type != JSON_TOK_STRING) {
+        if (UNLIKELY(token.type != JSON_TOK_STRING)) {
             return -EINVAL;
             return -EINVAL;
         }
         }
 
 
@@ -346,16 +348,16 @@ static int obj_next(struct json_obj *json, struct json_obj_key_value *kv)
     }
     }
 
 
     /* Match : after key */
     /* Match : after key */
-    if (!lexer_next(&json->lexer, &token)) {
+    if (UNLIKELY(!lexer_next(&json->lexer, &token))) {
         return -EINVAL;
         return -EINVAL;
     }
     }
 
 
-    if (token.type != JSON_TOK_COLON) {
+    if (UNLIKELY(token.type != JSON_TOK_COLON)) {
         return -EINVAL;
         return -EINVAL;
     }
     }
 
 
     /* Match value */
     /* Match value */
-    if (!lexer_next(&json->lexer, &kv->value)) {
+    if (UNLIKELY(!lexer_next(&json->lexer, &kv->value))) {
         return -EINVAL;
         return -EINVAL;
     }
     }
 
 
@@ -364,7 +366,7 @@ static int obj_next(struct json_obj *json, struct json_obj_key_value *kv)
 
 
 static int arr_next(struct json_obj *json, struct token *value)
 static int arr_next(struct json_obj *json, struct token *value)
 {
 {
-    if (!lexer_next(&json->lexer, value)) {
+    if (UNLIKELY(!lexer_next(&json->lexer, value))) {
         return -EINVAL;
         return -EINVAL;
     }
     }
 
 
@@ -373,7 +375,7 @@ static int arr_next(struct json_obj *json, struct token *value)
     }
     }
 
 
     if (value->type == JSON_TOK_COMMA) {
     if (value->type == JSON_TOK_COMMA) {
-        if (!lexer_next(&json->lexer, value)) {
+        if (UNLIKELY(!lexer_next(&json->lexer, value))) {
             return -EINVAL;
             return -EINVAL;
         }
         }
     }
     }
@@ -496,7 +498,7 @@ static ptrdiff_t get_elem_size(const struct json_obj_descr *descr)
         for (i = 0; i < descr->object.sub_descr_len; i++) {
         for (i = 0; i < descr->object.sub_descr_len; i++) {
             ptrdiff_t s = get_elem_size(&descr->object.sub_descr[i]);
             ptrdiff_t s = get_elem_size(&descr->object.sub_descr[i]);
 
 
-            total += (ptrdiff_t)ROUND_UP(s, 1 << descr->object.sub_descr[i].align_shift);
+            total += (ptrdiff_t)ROUND_UP(s, descr->object.sub_descr[i].align);
         }
         }
 
 
         return total;
         return total;
@@ -526,11 +528,11 @@ static int arr_parse(struct json_obj *obj,
             return 0;
             return 0;
         }
         }
 
 
-        if (field == last_elem) {
+        if (UNLIKELY(field == last_elem)) {
             return -ENOSPC;
             return -ENOSPC;
         }
         }
 
 
-        if (decode_value(obj, elem_descr, &value, field, val) < 0) {
+        if (UNLIKELY(decode_value(obj, elem_descr, &value, field, val) < 0)) {
             return -EINVAL;
             return -EINVAL;
         }
         }
 
 
@@ -575,7 +577,7 @@ static int obj_parse(struct json_obj *obj,
 
 
             /* Store the decoded value */
             /* Store the decoded value */
             ret = decode_value(obj, &descr[i], &kv.value, decode_field, val);
             ret = decode_value(obj, &descr[i], &kv.value, decode_field, val);
-            if (ret < 0) {
+            if (UNLIKELY(UNLIKELY(ret < 0))) {
                 return ret;
                 return ret;
             }
             }
 
 
@@ -599,7 +601,7 @@ int json_obj_parse(char *payload,
     assert(descr_len < (sizeof(ret) * CHAR_BIT - 1));
     assert(descr_len < (sizeof(ret) * CHAR_BIT - 1));
 
 
     ret = obj_init(&obj, payload, len);
     ret = obj_init(&obj, payload, len);
-    if (ret < 0) {
+    if (UNLIKELY(ret < 0)) {
         return ret;
         return ret;
     }
     }
 
 
@@ -677,7 +679,7 @@ ssize_t json_escape(char *str, size_t *len, size_t buf_size)
         return 0;
         return 0;
     }
     }
 
 
-    if (escaped_len >= buf_size) {
+    if (UNLIKELY(escaped_len >= buf_size)) {
         return -ENOMEM;
         return -ENOMEM;
     }
     }
 
 
@@ -707,76 +709,89 @@ ssize_t json_escape(char *str, size_t *len, size_t buf_size)
 static int encode(const struct json_obj_descr *descr,
 static int encode(const struct json_obj_descr *descr,
                   const void *val,
                   const void *val,
                   json_append_bytes_t append_bytes,
                   json_append_bytes_t append_bytes,
-                  void *data);
+                  void *data,
+                  bool encode_key);
 
 
 static int arr_encode(const struct json_obj_descr *elem_descr,
 static int arr_encode(const struct json_obj_descr *elem_descr,
                       const void *field,
                       const void *field,
                       const void *val,
                       const void *val,
                       json_append_bytes_t append_bytes,
                       json_append_bytes_t append_bytes,
-                      void *data)
+                      void *data,
+                      bool encode_key)
 {
 {
     ptrdiff_t elem_size = get_elem_size(elem_descr);
     ptrdiff_t elem_size = get_elem_size(elem_descr);
     /*
     /*
-     * NOTE: Since an element descriptor's offset isn't meaningful
-     * (array elements occur at multiple offsets in `val'), we use
-     * its space in elem_descr to store the offset to the field
-     * containing the number of elements.
+     * NOTE: Since an element descriptor's offset isn't meaningful (array
+     * elements occur at multiple offsets in `val'), we use its space in
+     * elem_descr to store the offset to the field containing the number of
+     * elements.
      */
      */
     size_t n_elem = *(size_t *)((char *)val + elem_descr->offset);
     size_t n_elem = *(size_t *)((char *)val + elem_descr->offset);
     size_t i;
     size_t i;
     int ret;
     int ret;
 
 
+    if (UNLIKELY(!n_elem)) {
+        return append_bytes("[]", 2, data);
+    }
+
     ret = append_bytes("[", 1, data);
     ret = append_bytes("[", 1, data);
-    if (ret < 0) {
+    if (UNLIKELY(ret < 0)) {
         return ret;
         return ret;
     }
     }
 
 
+    n_elem--;
     for (i = 0; i < n_elem; i++) {
     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
+         * 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.
          * 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.
+         * 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,
         ret = encode(elem_descr, (char *)field - elem_descr->offset,
-                     append_bytes, data);
-        if (ret < 0) {
+                     append_bytes, data, encode_key);
+        if (UNLIKELY(ret < 0)) {
             return ret;
             return ret;
         }
         }
 
 
-        if (i < n_elem - 1) {
-            ret = append_bytes(",", 1, data);
-            if (ret < 0) {
-                return ret;
-            }
+        ret = append_bytes(",", 1, data);
+        if (UNLIKELY(ret < 0)) {
+            return ret;
         }
         }
 
 
         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;
+    }
+
     return append_bytes("]", 1, data);
     return append_bytes("]", 1, data);
 }
 }
 
 
 static int
 static int
-str_encode(const char **str, json_append_bytes_t append_bytes, void *data)
+str_encode(const char **str, json_append_bytes_t append_bytes, void *data, bool encode)
 {
 {
     int ret;
     int ret;
 
 
     ret = append_bytes("\"", 1, data);
     ret = append_bytes("\"", 1, data);
-    if (ret < 0) {
+    if (UNLIKELY(ret < 0)) {
         return ret;
         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) {
     if (!ret) {
         return append_bytes("\"", 1, data);
         return append_bytes("\"", 1, data);
     }
     }
@@ -787,18 +802,11 @@ str_encode(const char **str, json_append_bytes_t append_bytes, void *data)
 static int
 static int
 num_encode(const int32_t *num, json_append_bytes_t append_bytes, void *data)
 num_encode(const int32_t *num, json_append_bytes_t append_bytes, void *data)
 {
 {
-    char buf[3 * sizeof(int32_t)];
-    int ret;
+    char buf[INT_TO_STR_BUFFER_SIZE];
+    size_t len;
+    char *as_string = int_to_string(*num, buf, &len);
 
 
-    ret = snprintf(buf, sizeof(buf), "%d", *num);
-    if (ret < 0) {
-        return ret;
-    }
-    if (ret >= (int)sizeof(buf)) {
-        return -ENOMEM;
-    }
-
-    return append_bytes(buf, (size_t)ret, data);
+    return append_bytes(as_string, len, data);
 }
 }
 
 
 static int
 static int
@@ -811,10 +819,23 @@ bool_encode(const bool *value, json_append_bytes_t append_bytes, void *data)
     return append_bytes("false", 5, data);
     return append_bytes("false", 5, data);
 }
 }
 
 
+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)
+{
+    void *ptr = (char *)val + descr->offset;
+
+    return arr_encode(descr->array.element_descr, ptr, val, append_bytes, data,
+                      encode_key);
+}
+
 static int encode(const struct json_obj_descr *descr,
 static int encode(const struct json_obj_descr *descr,
                   const void *val,
                   const void *val,
                   json_append_bytes_t append_bytes,
                   json_append_bytes_t append_bytes,
-                  void *data)
+                  void *data,
+                  bool encode_key)
 {
 {
     void *ptr = (char *)val + descr->offset;
     void *ptr = (char *)val + descr->offset;
 
 
@@ -823,13 +844,14 @@ static int encode(const struct json_obj_descr *descr,
     case JSON_TOK_TRUE:
     case JSON_TOK_TRUE:
         return bool_encode(ptr, append_bytes, data);
         return bool_encode(ptr, append_bytes, data);
     case JSON_TOK_STRING:
     case JSON_TOK_STRING:
-        return str_encode(ptr, append_bytes, data);
+        return str_encode(ptr, append_bytes, data, true);
     case JSON_TOK_LIST_START:
     case JSON_TOK_LIST_START:
-        return arr_encode(descr->array.element_descr, ptr, val, append_bytes, data);
+        return arr_encode(descr->array.element_descr, ptr, val,
+                          append_bytes, data, encode_key);
     case JSON_TOK_OBJECT_START:
     case JSON_TOK_OBJECT_START:
-        return json_obj_encode(descr->object.sub_descr,
-                               descr->object.sub_descr_len, ptr, append_bytes,
-                               data);
+        return json_obj_encode_full(descr->object.sub_descr,
+                                    descr->object.sub_descr_len, ptr, append_bytes,
+                                    data, encode_key);
     case JSON_TOK_NUMBER:
     case JSON_TOK_NUMBER:
         return num_encode(ptr, append_bytes, data);
         return num_encode(ptr, append_bytes, data);
     default:
     default:
@@ -837,43 +859,67 @@ static int encode(const struct json_obj_descr *descr,
     }
     }
 }
 }
 
 
-int json_obj_encode(const struct json_obj_descr *descr,
-                    size_t descr_len,
-                    const void *val,
-                    json_append_bytes_t append_bytes,
-                    void *data)
+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)
+{
+    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;
+    }
+
+    return encode(descr, val, append_bytes, data, encode_key);
+}
+
+int json_obj_encode_full(const struct json_obj_descr *descr,
+                         size_t descr_len,
+                         const void *val,
+                         json_append_bytes_t append_bytes,
+                         void *data,
+                         bool encode_key)
 {
 {
     size_t i;
     size_t i;
     int ret;
     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);
     ret = append_bytes("{", 1, data);
-    if (ret < 0) {
+    if (UNLIKELY(ret < 0)) {
         return ret;
         return ret;
     }
     }
 
 
-    for (i = 0; i < descr_len; i++) {
-        ret =
-            str_encode((const char **)&descr[i].field_name, append_bytes, data);
-        if (ret < 0) {
-            return ret;
-        }
-
-        ret = append_bytes(":", 1, data);
-        if (ret < 0) {
+    /* 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;
             return ret;
         }
         }
 
 
-        ret = encode(&descr[i], val, append_bytes, data);
-        if (ret < 0) {
+        ret = append_bytes(",", 1, data);
+        if (UNLIKELY(ret < 0)) {
             return ret;
             return ret;
         }
         }
+    }
 
 
-        if (i < descr_len - 1) {
-            ret = append_bytes(",", 1, data);
-            if (ret < 0) {
-                return ret;
-            }
-        }
+    ret = encode_key_value(&descr[0], val, append_bytes, data, encode_key);
+    if (UNLIKELY(ret < 0)) {
+        return ret;
     }
     }
 
 
     return append_bytes("}", 1, data);
     return append_bytes("}", 1, data);
@@ -889,7 +935,7 @@ static int append_bytes_to_buf(const char *bytes, size_t len, void *data)
 {
 {
     struct appender *appender = data;
     struct appender *appender = data;
 
 
-    if (len > appender->size - appender->used) {
+    if (UNLIKELY(len > appender->size - appender->used)) {
         return -ENOMEM;
         return -ENOMEM;
     }
     }
 
 
@@ -930,7 +976,7 @@ ssize_t json_calc_encoded_len(const struct json_obj_descr *descr,
     int ret;
     int ret;
 
 
     ret = json_obj_encode(descr, descr_len, val, measure_bytes, &total);
     ret = json_obj_encode(descr, descr_len, val, measure_bytes, &total);
-    if (ret < 0) {
+    if (UNLIKELY(ret < 0)) {
         return ret;
         return ret;
     }
     }
 
 

+ 60 - 29
frameworks/C/lwan/src/json.h

@@ -55,8 +55,7 @@ enum json_tokens {
 struct json_obj_descr {
 struct json_obj_descr {
     const char *field_name;
     const char *field_name;
 
 
-
-    uint32_t align_shift;
+    uint32_t align;
     uint32_t field_name_len;
     uint32_t field_name_len;
     uint32_t type;
     uint32_t type;
     uint32_t offset;
     uint32_t offset;
@@ -87,11 +86,6 @@ struct json_obj_descr {
  */
  */
 typedef int (*json_append_bytes_t)(const char *bytes, size_t len, void *data);
 typedef int (*json_append_bytes_t)(const char *bytes, size_t len, void *data);
 
 
-#define Z_ALIGN_SHIFT(type)                                                    \
-    (__alignof__(type) == 1                                                    \
-         ? 0                                                                   \
-         : __alignof__(type) == 2 ? 1 : __alignof__(type) == 4 ? 2 : 3)
-
 /**
 /**
  * @brief Helper macro to declare a descriptor for supported primitive
  * @brief Helper macro to declare a descriptor for supported primitive
  * values.
  * values.
@@ -116,7 +110,7 @@ typedef int (*json_append_bytes_t)(const char *bytes, size_t len, void *data);
  */
  */
 #define JSON_OBJ_DESCR_PRIM(struct_, field_name_, type_)                       \
 #define JSON_OBJ_DESCR_PRIM(struct_, field_name_, type_)                       \
     {                                                                          \
     {                                                                          \
-        .field_name = (#field_name_), .align_shift = Z_ALIGN_SHIFT(struct_),   \
+        .field_name = (#field_name_), .align = __alignof__(struct_),           \
         .field_name_len = sizeof(#field_name_) - 1, .type = type_,             \
         .field_name_len = sizeof(#field_name_) - 1, .type = type_,             \
         .offset = offsetof(struct_, field_name_),                              \
         .offset = offsetof(struct_, field_name_),                              \
     }
     }
@@ -149,7 +143,7 @@ typedef int (*json_append_bytes_t)(const char *bytes, size_t len, void *data);
  */
  */
 #define JSON_OBJ_DESCR_OBJECT(struct_, field_name_, sub_descr_)                \
 #define JSON_OBJ_DESCR_OBJECT(struct_, field_name_, sub_descr_)                \
     {                                                                          \
     {                                                                          \
-        .field_name = (#field_name_), .align_shift = Z_ALIGN_SHIFT(struct_),   \
+        .field_name = (#field_name_), .align = __alignof__(struct_),           \
         .field_name_len = (sizeof(#field_name_) - 1),                          \
         .field_name_len = (sizeof(#field_name_) - 1),                          \
         .type = JSON_TOK_OBJECT_START,                                         \
         .type = JSON_TOK_OBJECT_START,                                         \
         .offset = offsetof(struct_, field_name_),                              \
         .offset = offsetof(struct_, field_name_),                              \
@@ -188,13 +182,13 @@ 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_,       \
 #define JSON_OBJ_DESCR_ARRAY(struct_, field_name_, max_len_, len_field_,       \
                              elem_type_)                                       \
                              elem_type_)                                       \
     {                                                                          \
     {                                                                          \
-        .field_name = (#field_name_), .align_shift = Z_ALIGN_SHIFT(struct_),   \
+        .field_name = (#field_name_), .align = __alignof__(struct_),           \
         .field_name_len = sizeof(#field_name_) - 1,                            \
         .field_name_len = sizeof(#field_name_) - 1,                            \
         .type = JSON_TOK_LIST_START, .offset = offsetof(struct_, field_name_), \
         .type = JSON_TOK_LIST_START, .offset = offsetof(struct_, field_name_), \
         .array = {                                                             \
         .array = {                                                             \
             .element_descr =                                                   \
             .element_descr =                                                   \
                 &(struct json_obj_descr){                                      \
                 &(struct json_obj_descr){                                      \
-                    .align_shift = Z_ALIGN_SHIFT(struct_),                     \
+                    .align = __alignof__(struct_),                             \
                     .type = elem_type_,                                        \
                     .type = elem_type_,                                        \
                     .offset = offsetof(struct_, len_field_),                   \
                     .offset = offsetof(struct_, len_field_),                   \
                 },                                                             \
                 },                                                             \
@@ -244,13 +238,13 @@ 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_,   \
 #define JSON_OBJ_DESCR_OBJ_ARRAY(struct_, field_name_, max_len_, len_field_,   \
                                  elem_descr_, elem_descr_len_)                 \
                                  elem_descr_, elem_descr_len_)                 \
     {                                                                          \
     {                                                                          \
-        .field_name = (#field_name_), .align_shift = Z_ALIGN_SHIFT(struct_),   \
+        .field_name = (#field_name_), .align = __alignof__(struct_),           \
         .field_name_len = sizeof(#field_name_) - 1,                            \
         .field_name_len = sizeof(#field_name_) - 1,                            \
         .type = JSON_TOK_LIST_START, .offset = offsetof(struct_, field_name_), \
         .type = JSON_TOK_LIST_START, .offset = offsetof(struct_, field_name_), \
         .array = {                                                             \
         .array = {                                                             \
             .element_descr =                                                   \
             .element_descr =                                                   \
                 &(struct json_obj_descr){                                      \
                 &(struct json_obj_descr){                                      \
-                    .align_shift = Z_ALIGN_SHIFT(struct_),                     \
+                    .align = __alignof__(struct_),                             \
                     .type = JSON_TOK_OBJECT_START,                             \
                     .type = JSON_TOK_OBJECT_START,                             \
                     .offset = offsetof(struct_, len_field_),                   \
                     .offset = offsetof(struct_, len_field_),                   \
                     .object =                                                  \
                     .object =                                                  \
@@ -314,13 +308,13 @@ 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_, \
 #define JSON_OBJ_DESCR_ARRAY_ARRAY(struct_, field_name_, max_len_, len_field_, \
                                    elem_descr_, elem_descr_len_)               \
                                    elem_descr_, elem_descr_len_)               \
     {                                                                          \
     {                                                                          \
-        .field_name = (#field_name_), .align_shift = Z_ALIGN_SHIFT(struct_),   \
+        .field_name = (#field_name_), .align = __alignof__(struct_),           \
         .field_name_len = sizeof(#field_name_) - 1,                            \
         .field_name_len = sizeof(#field_name_) - 1,                            \
         .type = JSON_TOK_LIST_START, .offset = offsetof(struct_, field_name_), \
         .type = JSON_TOK_LIST_START, .offset = offsetof(struct_, field_name_), \
         .array = {                                                             \
         .array = {                                                             \
             .element_descr =                                                   \
             .element_descr =                                                   \
                 &(struct json_obj_descr){                                      \
                 &(struct json_obj_descr){                                      \
-                    .align_shift = Z_ALIGN_SHIFT(struct_),                     \
+                    .align = __alignof__(struct_),                             \
                     .type = JSON_TOK_LIST_START,                               \
                     .type = JSON_TOK_LIST_START,                               \
                     .offset = offsetof(struct_, len_field_),                   \
                     .offset = offsetof(struct_, len_field_),                   \
                     .object =                                                  \
                     .object =                                                  \
@@ -353,8 +347,7 @@ typedef int (*json_append_bytes_t)(const char *bytes, size_t len, void *data);
 #define JSON_OBJ_DESCR_PRIM_NAMED(struct_, json_field_name_,                   \
 #define JSON_OBJ_DESCR_PRIM_NAMED(struct_, json_field_name_,                   \
                                   struct_field_name_, type_)                   \
                                   struct_field_name_, type_)                   \
     {                                                                          \
     {                                                                          \
-        .field_name = (json_field_name_),                                      \
-        .align_shift = Z_ALIGN_SHIFT(struct_),                                 \
+        .field_name = (json_field_name_), .align = __alignof__(struct_),       \
         .field_name_len = sizeof(json_field_name_) - 1, .type = type_,         \
         .field_name_len = sizeof(json_field_name_) - 1, .type = type_,         \
         .offset = offsetof(struct_, struct_field_name_),                       \
         .offset = offsetof(struct_, struct_field_name_),                       \
     }
     }
@@ -378,8 +371,7 @@ typedef int (*json_append_bytes_t)(const char *bytes, size_t len, void *data);
 #define JSON_OBJ_DESCR_OBJECT_NAMED(struct_, json_field_name_,                 \
 #define JSON_OBJ_DESCR_OBJECT_NAMED(struct_, json_field_name_,                 \
                                     struct_field_name_, sub_descr_)            \
                                     struct_field_name_, sub_descr_)            \
     {                                                                          \
     {                                                                          \
-        .field_name = (json_field_name_),                                      \
-        .align_shift = Z_ALIGN_SHIFT(struct_),                                 \
+        .field_name = (json_field_name_), .align = __alignof__(struct_),       \
         .field_name_len = (sizeof(json_field_name_) - 1),                      \
         .field_name_len = (sizeof(json_field_name_) - 1),                      \
         .type = JSON_TOK_OBJECT_START,                                         \
         .type = JSON_TOK_OBJECT_START,                                         \
         .offset = offsetof(struct_, struct_field_name_),                       \
         .offset = offsetof(struct_, struct_field_name_),                       \
@@ -414,15 +406,14 @@ typedef int (*json_append_bytes_t)(const char *bytes, size_t len, void *data);
                                    struct_field_name_, max_len_, len_field_,   \
                                    struct_field_name_, max_len_, len_field_,   \
                                    elem_type_)                                 \
                                    elem_type_)                                 \
     {                                                                          \
     {                                                                          \
-        .field_name = (json_field_name_),                                      \
-        .align_shift = Z_ALIGN_SHIFT(struct_),                                 \
+        .field_name = (json_field_name_), .align = __alignof__(struct_),       \
         .field_name_len = sizeof(json_field_name_) - 1,                        \
         .field_name_len = sizeof(json_field_name_) - 1,                        \
         .type = JSON_TOK_LIST_START,                                           \
         .type = JSON_TOK_LIST_START,                                           \
         .offset = offsetof(struct_, struct_field_name_),                       \
         .offset = offsetof(struct_, struct_field_name_),                       \
         .array = {                                                             \
         .array = {                                                             \
             .element_descr =                                                   \
             .element_descr =                                                   \
                 &(struct json_obj_descr){                                      \
                 &(struct json_obj_descr){                                      \
-                    .align_shift = Z_ALIGN_SHIFT(struct_),                     \
+                    .align = __alignof__(struct_),                             \
                     .type = elem_type_,                                        \
                     .type = elem_type_,                                        \
                     .offset = offsetof(struct_, len_field_),                   \
                     .offset = offsetof(struct_, len_field_),                   \
                 },                                                             \
                 },                                                             \
@@ -480,13 +471,13 @@ 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_,       \
     struct_, json_field_name_, struct_field_name_, max_len_, len_field_,       \
     elem_descr_, elem_descr_len_)                                              \
     elem_descr_, elem_descr_len_)                                              \
     {                                                                          \
     {                                                                          \
-        .field_name = json_field_name_, .align_shift = Z_ALIGN_SHIFT(struct_), \
+        .field_name = json_field_name_, .align = __alignof__(struct_),         \
         .field_name_len = sizeof(json_field_name_) - 1,                        \
         .field_name_len = sizeof(json_field_name_) - 1,                        \
         .type = JSON_TOK_LIST_START,                                           \
         .type = JSON_TOK_LIST_START,                                           \
         .offset = offsetof(struct_, struct_field_name_),                       \
         .offset = offsetof(struct_, struct_field_name_),                       \
         .element_descr =                                                       \
         .element_descr =                                                       \
             &(struct json_obj_descr){                                          \
             &(struct json_obj_descr){                                          \
-                .align_shift = Z_ALIGN_SHIFT(struct_),                         \
+                .align = __alignof__(struct_),                                 \
                 .type = JSON_TOK_OBJECT_START,                                 \
                 .type = JSON_TOK_OBJECT_START,                                 \
                 .offset = offsetof(struct_, len_field_),                       \
                 .offset = offsetof(struct_, len_field_),                       \
                 .object =                                                      \
                 .object =                                                      \
@@ -621,11 +612,51 @@ int json_obj_encode_buf(const struct json_obj_descr *descr,
  * @return 0 if object has been successfully encoded. A negative value
  * @return 0 if object has been successfully encoded. A negative value
  * indicates an error.
  * indicates an error.
  */
  */
-int json_obj_encode(const struct json_obj_descr *descr,
-                    size_t descr_len,
-                    const void *val,
-                    json_append_bytes_t append_bytes,
-                    void *data);
+int json_obj_encode_full(const struct json_obj_descr *descr,
+                         size_t descr_len,
+                         const void *val,
+                         json_append_bytes_t append_bytes,
+                         void *data,
+                         bool encode_key);
+static inline int json_obj_encode(const struct json_obj_descr *descr,
+                                  size_t descr_len,
+                                  const void *val,
+                                  json_append_bytes_t append_bytes,
+                                  void *data)
+{
+
+    return json_obj_encode_full(descr, descr_len, val, append_bytes, data,
+                                true);
+}
+/**
+ * @brief Encodes an array using an arbitrary writer function
+ *
+ * @param descr Pointer to the descriptor array
+ *
+ * @param descr_len Number of elements in the descriptor array
+ *
+ * @param val Struct holding the values
+ *
+ * @param append_bytes Function to append bytes to the output
+ *
+ * @param data Data pointer to be passed to the append_bytes callback
+ * function.
+ *
+ * @return 0 if object has been successfully encoded. A negative value
+ * indicates an error.
+ */
+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);
+static inline int json_arr_encode(const struct json_obj_descr *descr,
+                                  const void *val,
+                                  json_append_bytes_t append_bytes,
+                                  void *data)
+{
+    return json_arr_encode_full(descr, val, append_bytes, data, true);
+}
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }

+ 79 - 70
frameworks/C/lwan/src/techempower.c

@@ -31,7 +31,7 @@
 
 
 enum db_connect_type { DB_CONN_MYSQL, DB_CONN_SQLITE };
 enum db_connect_type { DB_CONN_MYSQL, DB_CONN_SQLITE };
 
 
-struct db_connection_params {
+static struct db_connection_params {
     enum db_connect_type type;
     enum db_connect_type type;
     union {
     union {
         struct {
         struct {
@@ -45,33 +45,7 @@ struct db_connection_params {
             const char **pragmas;
             const char **pragmas;
         } sqlite;
         } sqlite;
     };
     };
-};
-
-static struct db_connection_params db_connection_params;
-
-static struct db *get_db(void)
-{
-    static __thread struct db *database;
-
-    if (!database) {
-        switch (db_connection_params.type) {
-        case DB_CONN_MYSQL:
-            database = db_connect_mysql(db_connection_params.mysql.hostname,
-                                        db_connection_params.mysql.user,
-                                        db_connection_params.mysql.password,
-                                        db_connection_params.mysql.database);
-            break;
-        case DB_CONN_SQLITE:
-            database = db_connect_sqlite(db_connection_params.sqlite.path, true,
-                                         db_connection_params.sqlite.pragmas);
-            break;
-        }
-        if (!database)
-            lwan_status_critical("Could not connect to the database");
-    }
-
-    return database;
-}
+} db_connection_params;
 
 
 static const char hello_world[] = "Hello, World!";
 static const char hello_world[] = "Hello, World!";
 static const char random_number_query[] =
 static const char random_number_query[] =
@@ -106,14 +80,14 @@ static int fortune_list_generator(struct coro *coro, void *data);
 
 
 #undef TPL_STRUCT
 #undef TPL_STRUCT
 #define TPL_STRUCT struct Fortune
 #define TPL_STRUCT struct Fortune
-static const struct lwan_var_descriptor fortune_item_desc[] = {
-    TPL_VAR_INT(item.id),
-    TPL_VAR_STR_ESCAPE(item.message),
-    TPL_VAR_SENTINEL,
-};
-
 static const struct lwan_var_descriptor fortune_desc[] = {
 static const struct lwan_var_descriptor fortune_desc[] = {
-    TPL_VAR_SEQUENCE(item, fortune_list_generator, fortune_item_desc),
+    TPL_VAR_SEQUENCE(item,
+                     fortune_list_generator,
+                     ((const struct lwan_var_descriptor[]){
+                         TPL_VAR_INT(item.id),
+                         TPL_VAR_STR_ESCAPE(item.message),
+                         TPL_VAR_SENTINEL,
+                     })),
     TPL_VAR_SENTINEL,
     TPL_VAR_SENTINEL,
 };
 };
 
 
@@ -139,14 +113,37 @@ struct queries_json {
     struct db_json queries[500];
     struct db_json queries[500];
     size_t queries_len;
     size_t queries_len;
 };
 };
-static const struct json_obj_descr queries_json_desc[] = {
+static const struct json_obj_descr queries_array_desc =
     JSON_OBJ_DESCR_OBJ_ARRAY(struct queries_json,
     JSON_OBJ_DESCR_OBJ_ARRAY(struct queries_json,
                              queries,
                              queries,
                              500,
                              500,
                              queries_len,
                              queries_len,
                              db_json_desc,
                              db_json_desc,
-                             N_ELEMENTS(db_json_desc)),
-};
+                             N_ELEMENTS(db_json_desc));
+
+static struct db *get_db(void)
+{
+    static __thread struct db *database;
+
+    if (!database) {
+        switch (db_connection_params.type) {
+        case DB_CONN_MYSQL:
+            database = db_connect_mysql(db_connection_params.mysql.hostname,
+                                        db_connection_params.mysql.user,
+                                        db_connection_params.mysql.password,
+                                        db_connection_params.mysql.database);
+            break;
+        case DB_CONN_SQLITE:
+            database = db_connect_sqlite(db_connection_params.sqlite.path, true,
+                                         db_connection_params.sqlite.pragmas);
+            break;
+        }
+        if (!database)
+            lwan_status_critical("Could not connect to the database");
+    }
+
+    return database;
+}
 
 
 static int append_to_strbuf(const char *bytes, size_t len, void *data)
 static int append_to_strbuf(const char *bytes, size_t len, void *data)
 {
 {
@@ -155,15 +152,31 @@ static int append_to_strbuf(const char *bytes, size_t len, void *data)
     return lwan_strbuf_append_str(strbuf, bytes, len) ? 0 : -EINVAL;
     return lwan_strbuf_append_str(strbuf, bytes, len) ? 0 : -EINVAL;
 }
 }
 
 
-static enum lwan_http_status json_response(struct lwan_response *response,
-                                           const struct json_obj_descr *descr,
-                                           size_t descr_len,
-                                           const void *data)
+static enum lwan_http_status
+json_response_obj(struct lwan_response *response,
+                  const struct json_obj_descr *descr,
+                  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)
+        return HTTP_INTERNAL_ERROR;
+
+    response->mime_type = "application/json";
+    return HTTP_OK;
+}
+
+static enum lwan_http_status
+json_response_arr(struct lwan_response *response,
+                  const struct json_obj_descr *descr,
+                  const void *data)
 {
 {
     lwan_strbuf_grow_to(response->buffer, 128);
     lwan_strbuf_grow_to(response->buffer, 128);
 
 
-    if (json_obj_encode(descr, descr_len, data, append_to_strbuf,
-                        response->buffer) < 0)
+    if (json_arr_encode_full(descr, data, append_to_strbuf, response->buffer,
+                             false) < 0)
         return HTTP_INTERNAL_ERROR;
         return HTTP_INTERNAL_ERROR;
 
 
     response->mime_type = "application/json";
     response->mime_type = "application/json";
@@ -174,35 +187,37 @@ LWAN_HANDLER(json)
 {
 {
     struct hello_world_json j = {.message = hello_world};
     struct hello_world_json j = {.message = hello_world};
 
 
-    return json_response(response, hello_world_json_desc,
+    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,
 static bool db_query(struct db_stmt *stmt,
                      struct db_row rows[],
                      struct db_row rows[],
-                     struct db_row results[],
+                     size_t n_rows,
                      struct db_json *out)
                      struct db_json *out)
 {
 {
-    int id = rand() % 10000;
+    const int id = (rand() % 10000) + 1;
+
+    assert(n_rows >= 1);
 
 
     rows[0].u.i = id;
     rows[0].u.i = id;
 
 
-    if (UNLIKELY(!db_stmt_bind(stmt, rows, 1)))
+    if (UNLIKELY(!db_stmt_bind(stmt, rows, n_rows)))
         return false;
         return false;
 
 
-    if (UNLIKELY(!db_stmt_step(stmt, results)))
+    long random_number;
+    if (UNLIKELY(!db_stmt_step(stmt, "i", &random_number)))
         return false;
         return false;
 
 
     out->id = id;
     out->id = id;
-    out->randomNumber = results[0].u.i;
+    out->randomNumber = (int)random_number;
 
 
     return true;
     return true;
 }
 }
 
 
 LWAN_HANDLER(db)
 LWAN_HANDLER(db)
 {
 {
-    struct db_row rows[1] = {{.kind = 'i'}};
-    struct db_row results[] = {{.kind = 'i'}, {.kind = '\0'}};
+    struct db_row rows[] = {{.kind = 'i'}};
     struct db_stmt *stmt = db_prepare_stmt(get_db(), random_number_query,
     struct db_stmt *stmt = db_prepare_stmt(get_db(), random_number_query,
                                            sizeof(random_number_query) - 1);
                                            sizeof(random_number_query) - 1);
     struct db_json db_json;
     struct db_json db_json;
@@ -212,14 +227,14 @@ LWAN_HANDLER(db)
         return HTTP_INTERNAL_ERROR;
         return HTTP_INTERNAL_ERROR;
     }
     }
 
 
-    bool queried = db_query(stmt, rows, results, &db_json);
+    bool queried = db_query(stmt, rows, N_ELEMENTS(rows), &db_json);
 
 
     db_stmt_finalize(stmt);
     db_stmt_finalize(stmt);
 
 
     if (!queried)
     if (!queried)
         return HTTP_INTERNAL_ERROR;
         return HTTP_INTERNAL_ERROR;
 
 
-    return json_response(response, db_json_desc, N_ELEMENTS(db_json_desc),
+    return json_response_obj(response, db_json_desc, N_ELEMENTS(db_json_desc),
                          &db_json);
                          &db_json);
 }
 }
 
 
@@ -245,15 +260,13 @@ LWAN_HANDLER(queries)
         return HTTP_INTERNAL_ERROR;
         return HTTP_INTERNAL_ERROR;
 
 
     struct queries_json qj = {.queries_len = (size_t)queries};
     struct queries_json qj = {.queries_len = (size_t)queries};
-    struct db_row rows[1] = {{.kind = 'i'}};
-    struct db_row results[] = {{.kind = 'i'}, {.kind = '\0'}};
+    struct db_row results[] = {{.kind = 'i'}};
     for (long i = 0; i < queries; i++) {
     for (long i = 0; i < queries; i++) {
-        if (!db_query(stmt, rows, results, &qj.queries[i]))
+        if (!db_query(stmt, results, N_ELEMENTS(results), &qj.queries[i]))
             goto out;
             goto out;
     }
     }
 
 
-    ret = json_response(response, queries_json_desc,
-                        N_ELEMENTS(queries_json_desc), &qj);
+    ret = json_response_arr(response, &queries_array_desc, &qj);
 
 
 out:
 out:
     db_stmt_finalize(stmt);
     db_stmt_finalize(stmt);
@@ -311,7 +324,6 @@ static bool append_fortune(struct coro *coro,
 static int fortune_list_generator(struct coro *coro, void *data)
 static int fortune_list_generator(struct coro *coro, void *data)
 {
 {
     static const char fortune_query[] = "SELECT * FROM Fortune";
     static const char fortune_query[] = "SELECT * FROM Fortune";
-    char fortune_buffer[256];
     struct Fortune *fortune = data;
     struct Fortune *fortune = data;
     struct fortune_array fortunes;
     struct fortune_array fortunes;
     struct db_stmt *stmt;
     struct db_stmt *stmt;
@@ -322,13 +334,10 @@ static int fortune_list_generator(struct coro *coro, void *data)
 
 
     fortune_array_init(&fortunes);
     fortune_array_init(&fortunes);
 
 
-    struct db_row results[] = {{.kind = 'i'},
-                               {.kind = 's',
-                                .u.s = fortune_buffer,
-                                .buffer_length = sizeof(fortune_buffer)},
-                               {.kind = '\0'}};
-    while (db_stmt_step(stmt, results)) {
-        if (!append_fortune(coro, &fortunes, results[0].u.i, results[1].u.s))
+    long id;
+    char fortune_buffer[256];
+    while (db_stmt_step(stmt, "is", &id, &fortune_buffer, sizeof(fortune_buffer))) {
+        if (!append_fortune(coro, &fortunes, (int)id, fortune_buffer))
             goto out;
             goto out;
     }
     }
 
 
@@ -382,12 +391,12 @@ int main(void)
     srand((unsigned int)time(NULL));
     srand((unsigned int)time(NULL));
 
 
     if (getenv("USE_MYSQL")) {
     if (getenv("USE_MYSQL")) {
-        db_connection_params = (struct db_connection_params) {
+        db_connection_params = (struct db_connection_params){
             .type = DB_CONN_MYSQL,
             .type = DB_CONN_MYSQL,
             .mysql.user = getenv("MYSQL_USER"),
             .mysql.user = getenv("MYSQL_USER"),
             .mysql.password = getenv("MYSQL_PASS"),
             .mysql.password = getenv("MYSQL_PASS"),
             .mysql.hostname = getenv("MYSQL_HOST"),
             .mysql.hostname = getenv("MYSQL_HOST"),
-            .mysql.database = getenv("MYSQL_DB")
+            .mysql.database = getenv("MYSQL_DB"),
         };
         };
 
 
         if (!db_connection_params.mysql.user)
         if (!db_connection_params.mysql.user)
@@ -402,7 +411,7 @@ int main(void)
         static const char *pragmas[] = {"PRAGMA mmap_size=44040192",
         static const char *pragmas[] = {"PRAGMA mmap_size=44040192",
                                         "PRAGMA journal_mode=OFF",
                                         "PRAGMA journal_mode=OFF",
                                         "PRAGMA locking_mode=EXCLUSIVE", NULL};
                                         "PRAGMA locking_mode=EXCLUSIVE", NULL};
-        db_connection_params = (struct db_connection_params) {
+        db_connection_params = (struct db_connection_params){
             .type = DB_CONN_SQLITE,
             .type = DB_CONN_SQLITE,
             .sqlite.path = "techempower.db",
             .sqlite.path = "techempower.db",
             .sqlite.pragmas = pragmas,
             .sqlite.pragmas = pragmas,