<'
# Copyright (c) 2012-2015,2017,2019,2025-2026 LAAS/CNRS
# All rights reserved.
#
# Redistribution and use  in source  and binary  forms,  with or without
# modification, are permitted provided that the following conditions are
# met:
#
#   1. Redistributions of  source  code must retain the  above copyright
#      notice and this list of conditions.
#   2. Redistributions in binary form must reproduce the above copyright
#      notice and  this list of  conditions in the  documentation and/or
#      other materials provided with the distribution.
#
#                                      Anthony Mallet on Mon Apr  9 2012
#

args types
lang c

# expand types, uniquifying on mangled name
set tdict [dict create]
foreach t $types {
  foreach e [$t types] {
    dict set tdict [$e mangle] $e
  }
}
'>

/* C99 standard specifies that format macros must be explicitly requested */
#define __STDC_FORMAT_MACROS

#ifdef __cplusplus
# include <cerrno>
# include <cinttypes>
# include <climits>
# include <cmath>
# include <cstdint>
# include <cstdio>
# include <cstdlib>
# include <cstring>
using std::isnan; /* for some reason... (gcc-14, boost-1.87, NetBSD) */
#else
# include <errno.h>
# include <inttypes.h>
# include <limits.h>
# include <math.h>
# include <stdint.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
#endif

#include "typeops-c.h"


/* --- local data ---------------------------------------------------------- */

#define JSON_MINBUF	128

static int	genom_bufcat(char **buf, char **end, size_t *len, int quoted,
                        const char *data);
static int	genom_bufcat_uint64_t(char **buf, char **end, size_t *len,
                        int neg, uint64_t i) __attribute__ ((unused));

#define genom_json_skipspace(json) (json) += strspn(json, " \t\n\r")

<'dict for {m t} $tdict {'>
static inline int	genom_json_scan_<"$m">(
  <"[$t argument reference]">, const char **json);
static inline int	genom_json_print_<"$m">(
  char **json, char **end, size_t *len, <"[$t argument value]">);
<'}'>
static int		genom_json_scan_t_genom_unkex(
  genom_unkex_detail *data, const char **json);
static inline int	genom_json_print_t_genom_unkex(
  char **json, char **end, size_t *len, const genom_unkex_detail *data);
static int		genom_json_scan_t_genom_syserr(
  genom_syserr_detail *data, const char **json);
static inline int	genom_json_print_t_genom_syserr(
  char **json, char **end, size_t *len, const genom_syserr_detail *data);


/* === Exceptions ========================================================== */

/* Return the exception contained in a JSON serialized message. Any exception
 * detail is malloc'ed and must be freed be the caller.
 * The json pointer is advanced after the exception message if parsing is
 * successful. */
static inline genom_event
genom_json_scanex(const char **json, void **exdetail, size_t *exsize)
{
  genom_event ex = genom_ok;
  void *detail = NULL;
  size_t size = 0;
  const char *p, *e;

  p = *json;
  genom_json_skipspace(p);
  if (*(p++) != '{') goto bad;
  genom_json_skipspace(p);
  while(*p != '}') {
    if (*(p++) != '"') goto bad;

    if (!strncmp(p, "ex\"", sizeof("ex"))) {
      /* look for exception name */
      p += sizeof("ex")-1 +1/* skipping quote too */;
      genom_json_skipspace(p);
      if (*(p++) != ':') goto bad;
      genom_json_skipspace(p);
      if (*p != '"') goto bad;
      e = ++p;
      while (*p != '"') { if (*p == '\\') p++; p++; }

      if (sizeof(genom_syserr_id) == p - e + 1 &&
          !strncmp(genom_syserr_id, e, sizeof(genom_syserr_id)-1)) {
        ex = genom_syserr_id;
        size = sizeof(genom_syserr_detail);
      } else if (sizeof(genom_unkex_id) == p - e + 1 &&
                 !strncmp(genom_unkex_id, e, sizeof(genom_unkex_id)-1)) {
        ex = genom_unkex_id;
        size = sizeof(genom_unkex_detail);
      }
<'dict for {m t} $tdict {'>
<'  if {[$t kind] != "exception"} continue'>
      else if (sizeof(<"[$t cname]">_id) == p - e + 1 &&
               !strncmp(<"[$t cname]">_id, e, sizeof(<"[$t cname]">_id)-1)) {
        ex = <"[$t cname]">_id;
<'  if {[llength [$t members]]} {'>
        size = sizeof(<"[$t declarator]">);
<'  }'>
      }
<'}'>
      else goto bad; /* unknown exception id */

      p++;
    } else if (!strncmp(p, "detail\"", sizeof("detail"))) {
      /* look for exception detail */
      p += sizeof("detail")-1 +1/* skipping quote too */;
      genom_json_skipspace(p);
      if (*(p++) != ':') goto bad;

      if (size) {
        detail = malloc(size);
        if (!detail) { ex = genom_fatal; goto done; }
      }

      if (ex == genom_syserr_id) {
        if (genom_json_scan_t_genom_syserr(
              (genom_syserr_detail *)&detail, &p)) goto bad;
      }
      else if (ex == genom_unkex_id) {
        if (genom_json_scan_t_genom_unkex(
              (genom_unkex_detail *)&detail, &p)) goto bad;
      }
<'dict for {m t} $tdict {'>
<'  if {[$t kind] != "exception"} continue'>
<'  if {![llength [$t members]]} continue'>
      else if (ex == <"[$t cname]">_id) {
        <"[$t declarator *d]"> = (<"[$t declarator *]">)detail;
        genom_tinit_<"$m">(d);
        if (genom_json_scan_<"$m">(d, &p)) goto bad;
      }
<'}'>
      else goto bad; /* exception with unexpected detail */
    } else goto bad; /* unknown json tag */

    genom_json_skipspace(p);
    if (*p == ',') { p++; genom_json_skipspace(p); }
  }
  *json = ++p;
  goto done;

bad:
  ex = genom_unkex_id;
  detail = malloc(sizeof(genom_unkex_detail));
  if (detail) {
    genom_unkex_detail *d = (genom_unkex_detail *)detail;
    snprintf(d->what, sizeof(d->what), "%s", *json);
  } else
    ex = genom_fatal;

done:
  if (exdetail) *exdetail = detail;
  if (exsize) *exsize = size;
  return ex;
}

static inline int
genom_json_printex(char **json, genom_event ex, const void *exdetail)
{
  int (*pdetail)(char **, char **, size_t *, const void *data);
  size_t len;
  char *end;
  int s;

  len = JSON_MINBUF;
  *json = end = (char *)malloc(len);
  if (!*json) return ENOMEM;

  s = genom_bufcat(json, &end, &len, 0, "{\"ex\":\"");
  if (s) goto done;
  s = genom_bufcat(json, &end, &len, 0, ex);
  if (s) goto done;
  s = genom_bufcat(json, &end, &len, 0, "\"");
  if (s) goto done;

  pdetail = NULL;
  if (ex == genom_syserr_id)
    pdetail =
      (int (*)(char **, char **, size_t *, const void *data))
      genom_json_print_t_genom_syserr;
  else if (ex == genom_unkex_id)
    pdetail =
      (int (*)(char **, char **, size_t *, const void *data))
      genom_json_print_t_genom_unkex;
<'dict for {m t} $tdict {'>
<'  if {[$t kind] != "exception"} continue'>
<'  if {[llength [$t members]]} {'>
  else if (ex == <"[$t cname]">_id)
    pdetail =
      (int (*)(char **, char **, size_t *, const void *data))
      genom_json_print_<"$m">;
<'  } else {'>
  else if (ex == <"[$t cname]">_id);
<'  }'>
<'}'>
  else assert(!"unknown exception id");

  if (pdetail) {
    s = genom_bufcat(json, &end, &len, 0, ",\"detail\":");
    if (s) goto done;
    s = pdetail(json, &end, &len, exdetail);
    if (s) goto done;
  }
  s = genom_bufcat(json, &end, &len, 0, "}");
done:
  if (s) free(*json);
  return s;
}

<'dict for {m t} $tdict {'>

/* <"[--- [$t mangle] ===================================================]"> */

static inline int
genom_json_scan_<"$m">(<"[$t argument reference data]">, const char **json)
{
  int s = 0;
<'
  switch -glob -- [$t kind] {
    typedef - {* member} {'>
  if ((s = genom_json_scan_<"[[$t type] mangle]">(data, json))) return s;
<'    }'>
<'    struct - exception {'>
<'      if {![llength [$t members]]} {'>
  (void)data; /* fix -Wunused-parameter */
<'      }'>
  genom_json_skipspace(*json);
  if (*((*json)++) != '{') return EINVAL;
  genom_json_skipspace(*json);
  while(**json != '}') {
    if (*((*json)++) != '"') return EINVAL;
<'      foreach e [$t members] {'>
    if (!strncmp(*json, "<"[$e name]">\"", 1+<"[string length [$e name]]">)) {
      (*json)+= 1+<"[string length [$e name]]">;
      genom_json_skipspace(*json);
      if (*((*json)++) != ':') return EINVAL;
      if ((s = genom_json_scan_<"[[$e type] mangle]">(
             <"[$e pass reference data->[$e name]]">, json))) return s;
    } else
<'      }'>
      return EINVAL;

    genom_json_skipspace(*json);
    if (**json == ',') {
      (*json)++;
      genom_json_skipspace(*json);
    }
  }
  (*json)++;
<'    }'>
<'    union {'>
  genom_json_skipspace(*json);
  if (*((*json)++) != '{') return EINVAL;
  genom_json_skipspace(*json);
  while(**json != '}') {
    if (*((*json)++) != '"') return EINVAL;
    if (!strncmp(*json, "d\"", 2)) {
      (*json)+= 2;
      genom_json_skipspace(*json);
      if (*((*json)++) != ':') return EINVAL;
      if ((s = genom_json_scan_<"[[$t discr] mangle]">(
             <"[[$t discr] pass reference data->_d]">, json))) return s;
    } else if (!strncmp(*json, "u\"", 2)) {
      (*json)+= 2;
      genom_json_skipspace(*json);
      if (*((*json)++) != ':') return EINVAL;
      genom_json_skipspace(*json);
      if (*((*json)++) != '{') return EINVAL;
      genom_json_skipspace(*json);
      while(**json != '}') {
        if (*((*json)++) != '"') return EINVAL;
<'      foreach e [$t members] {'>
        if (!strncmp(*json, "<"[$e name]">\"", 1+<"[string length [$e name]]">)) {
          (*json)+= 1+<"[string length [$e name]]">;
          genom_json_skipspace(*json);
          if (*((*json)++) != ':') return EINVAL;
          if ((s = genom_json_scan_<"[[$e type] mangle]">(
                 <"[$e pass reference data->_u.[$e name]]">, json))) return s;
        } else
<'      }'>
          return EINVAL;

        genom_json_skipspace(*json);
        if (**json == ',') {
          (*json)++;
          genom_json_skipspace(*json);
        }
      }
      (*json)++;
    } else
      return EINVAL;

    genom_json_skipspace(*json);
    if (**json == ',') {
      (*json)++;
      genom_json_skipspace(*json);
    }
  }
  (*json)++;
<'    }'>
<'    sequence {'>
  long i;

  genom_json_skipspace(*json);
  switch (*((*json)++)) {
    case '[':
      genom_json_skipspace(*json);
      i = 0;
      while(**json != ']') {
<'      if {[catch {$t length} l]} {'>
        if ((s = genom_sequence_reserve(data, i+1))) return s;
<'      } else {'>
        if (i >= <"$l">) return EINVAL;
<'      }'>
        if ((s = genom_json_scan_<"[[$t type] mangle]">(
             <"[[$t type] pass reference data->_buffer\[i\]]">, json)))
          return s;
        data->_length = ++i;
        genom_json_skipspace(*json);
        if (**json == ',') {
          (*json)++;
          genom_json_skipspace(*json);
        }
      }
      break;

    case '{':
      genom_json_skipspace(*json);
      while(**json != '}') {
        char *d;

        if (*((*json)++) != '"') return EINVAL;
        i = strtol(*json, &d, 0);
        if (d == *json || *d != '"' || i<0) return EINVAL;
        *json = d+1;
        genom_json_skipspace(*json);
        if (*((*json)++) != ':') return EINVAL;
<'      if {[catch {$t length} l]} {'>
        if (data->_length < (unsigned long)i+1) {
          if ((s = genom_sequence_reserve(data, i+1))) return s;
        }
<'      } else {'>
        if (i >= <"$l">) return EINVAL;
<'      }'>
        if ((s = genom_json_scan_<"[[$t type] mangle]">(
             <"[[$t type] pass reference data->_buffer\[i\]]">, json)))
          return s;
        if (data->_length < (unsigned long)i+1) data->_length = i+1;
        genom_json_skipspace(*json);
        if (**json == ',') {
          (*json)++;
          genom_json_skipspace(*json);
        }
      }
      break;

    default: return EINVAL;
  }
  (*json)++;
<'    }'>
<'    optional {'>
  genom_json_skipspace(*json);
  data->_present = 0;
  if (!strncmp(*json, "null", 4)) {
    (*json) += 4;
    return 0;
  }
  if ((s = genom_json_scan_<"[[$t type] mangle]">(
         <"[[$t type] pass reference data->_value]">, json)))
    return s;
  data->_present = 1;
<'    }'>
<'    array {'>
  long i;

  genom_json_skipspace(*json);
  switch (*((*json)++)) {
    case '[':
      genom_json_skipspace(*json);
      i = 0;
      while(**json != ']') {
        if (i >= <"[$t length]">) return EINVAL;
        if ((s = genom_json_scan_<"[[$t type] mangle]">(
               <"[[$t type] pass reference data\[i\]]">, json))) return s;
        genom_json_skipspace(*json);
        if (**json == ',') {
          (*json)++;
          genom_json_skipspace(*json);
        }
        i++;
      }
      break;

    case '{':
      genom_json_skipspace(*json);
      while(**json != '}') {
        char *d;

        if (*((*json)++) != '"') return EINVAL;
        i = strtol(*json, &d, 0);
        if (d == *json || *d != '"' || i<0) return EINVAL;
        *json = d+1;
        genom_json_skipspace(*json);
        if (*((*json)++) != ':') return EINVAL;
        if (i >= <"[$t length]">) return EINVAL;
        if ((s = genom_json_scan_<"[[$t type] mangle]">(
               <"[[$t type] pass reference data\[i\]]">, json))) return s;
        genom_json_skipspace(*json);
        if (**json == ',') {
          (*json)++;
          genom_json_skipspace(*json);
        }
      }
      break;

    default: return EINVAL;
  }
  (*json)++;
<'    }'>
<'    char {'>
  genom_json_skipspace(*json);
  if (*((*json)++) != '"') return EINVAL;
  *data = *((*json)++);
  if (*data == '\\' && (**json == '"' || **json == '\\'))
    *data = *((*json)++);
  if (*((*json)++) != '"') return EINVAL;
<'    }'>
<'    string {'>
  const char *d;
  char *p;

  genom_json_skipspace(*json);
  if (**json != '"') return EINVAL;
  d = ++(*json);
  while (**json != '"') {
    if (**json == '\\') (*json)++;
    (*json)++;
  }
<'      if {[catch {$t length} l]} {'>
  if (*data) free(*data);
  *data = (char *)malloc(*json - d + 1);
  if (!*data) return ENOMEM;
  strncpy(*data, d, *json - d);
  (*data)[*json - d] = '\0';
  p = *data;
<'      } else {'>
  if (*json - d + 1 > <"$l">) return EINVAL;
  strncpy(data, d, *json - d);
  data[*json - d] = '\0';
  p = data;
<'      }'>
  while((p = strstr(p, "\\"))) {
    if (*p == '"' || *p == '\\')
      memmove(p, p+1, strlen(p)/*including final \0*/);
    p++;
  }
  (*json)++;
<'    }'>
<'    enum {'>
  genom_json_skipspace(*json);
  if (*((*json)++) != '"') return EINVAL;
<'      foreach e [$t members] {'>
  if (!strncmp(*json, "<"[$e fullname]">\"",
               1+<"[string length [$e fullname]]">)) {
    (*json)+= 1+<"[string length [$e fullname]]">;
    *data = <"[$e cname]">;
  } else
    if (!strncmp(*json, "<"[$e name]">\"", 1+<"[string length [$e name]]">)) {
    (*json)+= 1+<"[string length [$e name]]">;
    *data = <"[$e cname]">;
  } else
<'      }'>
    if (!strncmp(*json, "<invalid>\"", sizeof("<invalid>"))) {
    (*json)+= sizeof("<invalid>");
    *data = (<"[$t declarator]">)-1 /*any invalid value*/;
  } else
    return EINVAL;
<'
    }
    float - double {'>
  double v;

  if (!strncmp(*json, "null", 4)) {
    (*json) += 4;
    *data = (<"[$t declarator]">)nan("");
  } else {
    char *d;
    v = strtod(*json, &d);
    if (d == *json) return EINVAL;
    *json = d;
    *data = (<"[$t declarator]">)v;
  }
<'
    }
    default {'>
  long long v;
  char *d;

  v = strtoll(*json, &d, 0);
  if (d == *json) return EINVAL;
  *json = d;
  <"[$t dereference reference data]"> = (<"[$t declarator]">)v;
<'
    }
  }'>
  return s;
}

static inline int
genom_json_print_<"$m">(
  char **json, char **end, size_t *len, <"[$t argument value data]">)
{
  int s;
<'  switch -glob -- [$t kind] {'>
<'    typedef - {* member} {'>
  if ((s = genom_json_print_<"[[$t type] mangle]">(json, end, len, data)))
    return s;
<'    }'>
<'    struct - exception {'>
<'      if {![llength [$t members]]} {'>
  (void)data; /* fix -Wunused-parameter */
<'      }'>
  if ((s = genom_bufcat(json, end, len, 0, "{"))) return s;
<'
      set sep {}
      foreach e [$t members] {'>
  if ((s = genom_bufcat(json, end, len, 0, "<"$sep">\"<"[$e name]">\":")))
    return s;
  if ((s = genom_json_print_<"[$e mangle]">(json, end, len,
         <"[$e pass value data->[$e name]]">))) return s;
<'
        set sep ,
      }'>
  if ((s = genom_bufcat(json, end, len, 0, "}"))) return s;
<'    }'>
<'    union {'>
  if ((s = genom_bufcat(json, end, len, 0, "{\"d\":"))) return s;
  if ((s = genom_json_print_<"[[$t discr] mangle]">(json, end, len,
         <"[[$t discr] pass value data->_d]">))) return s;
  if ((s = genom_bufcat(json, end, len, 0, ",\"u\":{"))) return s;
  switch(data->_d) {
<'      foreach e [$t members] {'>
<'        foreach v [$e value] {'>
<'          if {$v != ""} {'>
    case <"[language::cname $v]">:
<'          } else {'>
    default:
<'          }'>
<'        }'>
      if ((s = genom_bufcat(json, end, len, 0, "\"<"[$e name]">\":")))
        return s;
      if ((s = genom_json_print_<"[$e mangle]">(json, end, len,
             <"[$e pass value data->_u.[$e name]]">))) return s;
      break;
<'      }'>
  }
  if ((s = genom_bufcat(json, end, len, 0, "}}"))) return s;
<'    }'>
<'    sequence {'>
  size_t i;
  if ((s = genom_bufcat(json, end, len, 0, "["))) return s;
  for (i=0; i<data->_length; i++) {
    if (i && (s = genom_bufcat(json, end, len, 0, ","))) return s;
    if ((s = genom_json_print_<"[[$t type] mangle]">(json, end, len,
           <"[[$t type] pass value {data->_buffer[i]}]">))) return s;
  }
  if ((s = genom_bufcat(json, end, len, 0, "]"))) return s;
<'    }'>
<'    optional {'>
  if (data->_present) {
    if ((s = genom_json_print_<"[[$t type] mangle]">(json, end, len,
           <"[[$t type] pass value {data->_value}]">))) return s;
  } else {
    if ((s = genom_bufcat(json, end, len, 0, "null"))) return s;
  }
<'    }'>
<'    array {'>
  size_t i;
  if ((s = genom_bufcat(json, end, len, 0, "["))) return s;
  for (i=0; i<<"[$t length]">; i++) {
    if (i && (s = genom_bufcat(json, end, len, 0, ","))) return s;
    if ((s = genom_json_print_<"[[$t type] mangle]">(json, end, len,
           <"[[$t type] pass value {data[i]}]">))) return s;
  }
  if ((s = genom_bufcat(json, end, len, 0, "]"))) return s;
<'    }'>
<'    char {'>
  char buf[] = { data, 0 };
  if ((s = genom_bufcat(json, end, len, 0, "\""))) return s;
  if ((s = genom_bufcat(json, end, len, 1, buf))) return s;
  if ((s = genom_bufcat(json, end, len, 0, "\""))) return s;
<'    }'>
<'    string {'>
  if ((s = genom_bufcat(json, end, len, 0, "\""))) return s;
  if ((s = genom_bufcat(json, end, len, 1, data))) return s;
  if ((s = genom_bufcat(json, end, len, 0, "\""))) return s;
<'    }'>
<'    enum {'>
  switch(data) {
<'      foreach e [$t members] {'>
    case <"[$e cname]">:
      if ((s = genom_bufcat(json, end, len, 0, "\"<"[$e fullname]">\"")))
        return s;
      break;
<'      }'>
    default:
      if ((s = genom_bufcat(json, end, len, 0, "\"<invalid>\""))) return s;
      break;
  }
<'    }'>
<'    float {'>
  if (isnan(data)) {
    if ((s = genom_bufcat(json, end, len, 0, "null"))) return s;
  } else {
    char buf[16];
    sprintf(buf, "%.9g", data);
    if ((s = genom_bufcat(json, end, len, 0, buf))) return s;
  }
<'    }'>
<'    double {'>
  if (isnan(data)) {
    if ((s = genom_bufcat(json, end, len, 0, "null"))) return s;
  } else {
    char buf[32];
    sprintf(buf, "%.17g", data);
    if ((s = genom_bufcat(json, end, len, 0, buf))) return s;
  }
<'
    }
    {unsigned *} {'>
  uint64_t v = <"[$t dereference value data]">;
  if ((s = genom_bufcat_uint64_t(json, end, len, 0, v))) return s;
<'
    }
    default {'>
  int64_t v = <"[$t dereference value data]">;
  if ((s = genom_bufcat_uint64_t(json, end, len, v<0, (uint64_t)(v<0?-v:v))))
    return s;
<'
    }
  }'>
  return 0;
}
<'}'>

/* === t_genom_unkex ======================================================= */

static inline int
genom_json_scan_t_genom_unkex(genom_unkex_detail *data, const char **json)
{
  const char *e;

  genom_json_skipspace(*json);
  if (*((*json)++) != '{') return EINVAL;
  genom_json_skipspace(*json);
  while(**json != '}') {
    if (*((*json)++) != '"') return EINVAL;

    if (!strncmp(*json, "what\"", sizeof("what"))) {
      (*json) += sizeof("what");
      genom_json_skipspace(*json);
      if (*((*json)++) != ':') return EINVAL;
      genom_json_skipspace(*json);
      if (**json != '"') return EINVAL;
      e = ++(*json);
      while (**json != '"') { if (**json == '\\') (*json)++; (*json)++; }
      if ((size_t)(*json - e) >= sizeof(data->what))
        e = *json - sizeof(data->what);
      snprintf(data->what, *json - e, "%s", e);

    } else return EINVAL;

    genom_json_skipspace(*json);
    if (**json == ',') {
      (*json)++;
      genom_json_skipspace(*json);
    }
  }

  (*json)++;
  return 0;
}

static inline int
genom_json_print_t_genom_unkex(
  char **json, char **end, size_t *len, const genom_unkex_detail *data)
{
  int s;

  s = genom_bufcat(json, end, len, 0, "{\"what\":\"");
  if (s) return s;
  s = genom_bufcat(json, end, len, 1, data->what);
  if (s) return s;
  s = genom_bufcat(json, end, len, 0, "\"}");
  return s;
}

/* === t_genom_syserr ====================================================== */

static int
genom_json_scan_t_genom_syserr(genom_syserr_detail *data, const char **json)
{
  long long v;
  char *d;

  genom_json_skipspace(*json);
  if (*((*json)++) != '{') return EINVAL;
  genom_json_skipspace(*json);
  while(**json != '}') {
    if (*((*json)++) != '"') return EINVAL;

    if (!strncmp(*json, "code\"", sizeof("code"))) {
      (*json) += sizeof("code");
      genom_json_skipspace(*json);
      if (*((*json)++) != ':') return EINVAL;
      genom_json_skipspace(*json);

      v = strtoll(*json, &d, 0);
      if (d == *json) return EINVAL;
      *json = d;
      data->code = (uint32_t)v;

    } else return EINVAL;

    genom_json_skipspace(*json);
    if (**json == ',') {
      (*json)++;
      genom_json_skipspace(*json);
    }
  }

  (*json)++;
  return 0;
}

static inline int
genom_json_print_t_genom_syserr(
  char **json, char **end, size_t *len, const genom_syserr_detail *data)
{
  int s;

  s = genom_bufcat(json, end, len, 0, "{\"code\":");
  if (s) return s;
  s = genom_bufcat_uint64_t(json, end, len, 0, data->code);
  if (s) return s;
  s = genom_bufcat(json, end, len, 0, "}");
  return s;
}

/* --- genom_bufcat -------------------------------------------------------- */

static int
genom_bufcat(char **buf, char **end, size_t *len, int quoted, const char *data)
{
  size_t avail;
  const char *s;
  char *d;

  s = data;
  while(*s) {
    avail = *len - (*end - *buf);
    if (quoted) {
      for(d = *end; *s && avail; avail--) {
        if (*s == '"' || *s == '\\') {
          if (avail < 2) {
            avail = 0;
            break;
          }
          *d++ = '\\';
          avail--;
        }
        *d++ = *s++;
      }
    } else {
      for(d = *end; *s && avail; avail--)
        *d++ = *s++;
    }

    if (!avail) {
      size_t l = d - *buf;
      char *newbuf = (char *)realloc(*buf, *len << 1);
      if (!*newbuf) return ENOMEM;

      d = newbuf + l;
      *buf = newbuf;
      *len <<= 1;
    }
    *d = 0;
    *end = d;
  }

  return 0;
}

static int
genom_bufcat_uint64_t(char **buf, char **end, size_t *len, int neg, uint64_t i)
{
  char a[32];
  char *d;

  d = a + sizeof(a);
  *--d = 0;
  do { *--d = '0' + i%10; } while (i /= 10);
  if (neg) *--d = '-';

  return genom_bufcat(buf, end, len, 0, d);
}
