From 7b04e9f30b320e251b257b8211a3855f419c6991 Mon Sep 17 00:00:00 2001
From: Anthony Mallet <anthony.mallet@laas.fr>
Date: Tue, 8 Jul 2014 14:54:21 +0200
Subject: [PATCH] [wip/genom3-ros] [PATCH] more efficient JSON serialization
 code

Import upstream patch fixing an important performance issue until HEAD is
released.

Bump PKGREVISION.
---
 genom3-ros/Makefile         |   1 +
 genom3-ros/distinfo         |   1 +
 genom3-ros/patches/patch-aa | 528 ++++++++++++++++++++++++++++++++++++
 3 files changed, 530 insertions(+)
 create mode 100644 genom3-ros/patches/patch-aa

diff --git a/genom3-ros/Makefile b/genom3-ros/Makefile
index 9152dbe4..eeed6bbe 100644
--- a/genom3-ros/Makefile
+++ b/genom3-ros/Makefile
@@ -2,6 +2,7 @@
 # Created:			Anthony Mallet on Fri, 19 Oct 2012
 #
 
+PKGREVISION=	1
 DISTNAME=	genom3-ros-1.8
 CATEGORIES=	architecture
 MASTER_SITES=	${MASTER_SITE_OPENROBOTS:=genom3-ros/}
diff --git a/genom3-ros/distinfo b/genom3-ros/distinfo
index 1a266727..226d65e6 100644
--- a/genom3-ros/distinfo
+++ b/genom3-ros/distinfo
@@ -1,3 +1,4 @@
 SHA1 (genom3-ros-1.8.tar.gz) = ef33520b71b000867b45b9ef025175074e4a5cd2
 RMD160 (genom3-ros-1.8.tar.gz) = bf8655b5f9291f597baa089a3ce10ca760f350c9
 Size (genom3-ros-1.8.tar.gz) = 383458 bytes
+SHA1 (patch-aa) = e854744edf0dc643d0ce3c8919b9b0a13a5172f4
diff --git a/genom3-ros/patches/patch-aa b/genom3-ros/patches/patch-aa
new file mode 100644
index 00000000..4dc0654d
--- /dev/null
+++ b/genom3-ros/patches/patch-aa
@@ -0,0 +1,528 @@
+From a04e6142f5a42e1cd940e3879e2eb81aa02e5e97 Mon Sep 17 00:00:00 2001
+From: Anthony Mallet <anthony.mallet@laas.fr>
+Date: Thu, 26 Jun 2014 17:17:14 +0200
+Subject: [PATCH] Provide a more efficient implementation of the JSON
+ serialization code
+
+Replace the quick & dirty C -> JSON serialization of the C client by a
+better, more efficient implementation. Among the main improvements:
+
+- maintain a pointer on the current write position in the JSON string
+  instead of concatenatings strings with asprintf (biggest performance hit).
+- allocate the JSON buffer by exponentially growing chunks
+- replace useless "printf %s"-like constructs by direct string concatenation
+- implement a ad-hoc equivalent of itoa(3) instead of using "printf %d"
+  (but printf %g is still used when needed).
+- use JSON arrays instead of dictionaries with an integer key to serialize IDL
+  sequences and arrays. The deserialization code can parse both kind of
+  serialization, and all other clients should also be able to deal with both.
+
+All in all, this improves the serialization speed by a factor 80.
+---
+ client/c/client.configure.ac |    1 -
+ client/c/json.cc             |   91 ++++++++++++----------
+ common/json-common.h         |  176 ++++++++++++++++++++----------------------
+ server/json.cc               |   16 ++--
+ 4 files changed, 145 insertions(+), 139 deletions(-)
+
+diff --git client/c/client.configure.ac client/c/client.configure.ac
+index 69bcaea..0e9636c 100644
+--- client/c/client.configure.ac
++++ client/c/client.configure.ac
+@@ -55,7 +55,6 @@ lt_cv_deplibs_check_method=pass_all
+ 
+ LT_INIT
+ AC_PROG_CXX
+-AC_CHECK_FUNCS([vasprintf asprintf])
+ AX_PTHREAD(, [AC_MSG_ERROR([pthread support not available])])
+ 
+ dnl External software
+--- client/c/json.cc~	2014-02-19 13:50:42.000000000 +0100
++++ client/c/json.cc	2014-07-08 12:23:21.518249556 +0200
+@@ -26,15 +26,6 @@
+ '>
+ #include "autoconf/acheader.h"
+ 
+-#ifdef __linux__
+-# if defined(HAVE_VASPRINTF) && defined(HAVE_ASPRINTF)
+-/* Apparently we need _GNU_SOURCE defined to get access to vasprintf on Linux */
+-#  ifndef _GNU_SOURCE
+-#   define _GNU_SOURCE
+-#  endif
+-# endif
+-#endif
+-
+ #include <errno.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+@@ -91,10 +82,19 @@
+ genom_<"$comp">_client_<"[$s name]">_json_print(char **json,
+              const struct genom_<"$comp">_<"[$s name]">_output *out)
+ {
++  size_t len;
++  char *end;
+   int s = 0;
+   *json = NULL;
+ 
+-  if ((s = bufcat(json, "{"))) {
++  len = JSON_MINBUF;
++  *json = end = (char *)malloc(len);
++  if (!*json) {
++    genom_syserr_detail d = { ENOMEM };
++    return genom_syserr(&d);
++  }
++
++  if ((s = bufcat(json, &end, &len, 0, "{")))  {
+     if (*json) free(*json);
+     *json = NULL;
+     genom_syserr_detail d = { s };
+@@ -103,12 +103,12 @@
+ 
+ <'  set sep {}'>
+ <'  foreach p [$s parameters inout out] {'>
+-  if ((s = bufcat(json, "<"$sep">\"<"[$p name]">\":"))) {
++  if ((s = bufcat(json, &end, &len, 0, "<"$sep">\"<"[$p name]">\":"))) {
+     if (*json) { free(*json); *json = NULL; }
+     genom_syserr_detail d = { s };
+     return genom_syserr(&d);
+   }
+-  if ((s = json_print_<"[[$p type] mangle]">(json,
++  if ((s = json_print_<"[[$p type] mangle]">(json, &end, &len,
+          <"[[$p type] pass value out->[$p name]]">))) {
+     if (*json) { free(*json); *json = NULL; }
+     genom_syserr_detail d = { s };
+@@ -117,7 +117,7 @@
+ <'  set sep ,'>
+ <'    }'>
+ 
+-  if ((s = bufcat(json, "}"))) {
++  if ((s = bufcat(json, &end, &len, 0, "}"))) {
+     if (*json) free(*json);
+     *json = NULL;
+     genom_syserr_detail d = { s };
+@@ -137,27 +137,36 @@
+ genom_<"$comp">_client_<"[$p name]">_json_print(char **json,
+   <"[[$p datatype] argument value data]">)
+ {
+-  int s;
++  size_t len;
++  char *end;
++  int s = 0;
+   *json = NULL;
+ 
+-  if ((s = bufcat(json, "{"))) {
++  len = JSON_MINBUF;
++  *json = end = (char *)malloc(len);
++  if (!*json) {
++    genom_syserr_detail d = { ENOMEM };
++    return genom_syserr(&d);
++  }
++
++  if ((s = bufcat(json, &end, &len, 0, "{"))) {
+     if (*json) { free(*json); *json = NULL; }
+     genom_syserr_detail d = { s };
+     return genom_syserr(&d);
+   }
+ 
+-  if ((s = bufcat(json, "\"<"[$p name]">\":"))) {
++  if ((s = bufcat(json, &end, &len, 0, "\"<"[$p name]">\":"))) {
+     if (*json) { free(*json); *json = NULL; }
+     genom_syserr_detail d = { s };
+     return genom_syserr(&d);
+   }
+-  if ((s = json_print_<"[[$p datatype] mangle]">(json, data))) {
++  if ((s = json_print_<"[[$p datatype] mangle]">(json, &end, &len, data))) {
+     if (*json) { free(*json); *json = NULL; }
+     genom_syserr_detail d = { s };
+     return genom_syserr(&d);
+   }
+ 
+-  if ((s = bufcat(json, "}"))) {
++  if ((s = bufcat(json, &end, &len, 0, "}"))) {
+     if (*json) { free(*json); *json = NULL; }
+     genom_syserr_detail d = { s };
+     return genom_syserr(&d);
+@@ -174,45 +183,50 @@
+ char *
+ genom_<"$comp">_client_json_error(genom_event e, const void *detail)
+ {
+-  char *s = NULL;
++  char *s, *end;
++  size_t len;
++
++  len = JSON_MINBUF;
++  s = end = (char *)malloc(len);
++  if (!s) return NULL;
+ 
+   if (!e) detail = genom_thrown(&e);
+ 
+   if (e == genom_syserr_id) {
+-    if (detail) {
+-      uint32_t e = ((genom_syserr_detail *)detail)->code;
+-      if (bufcat(&s, "\"%s\"", strerror(e))) return NULL;
+-    } else {
+-      if (bufcat(&s, "\"%s\"", genom_syserr_id)) return NULL;
+-    }
++    if (bufcat(&s, &end, &len, 0, "\"")) return NULL;
++    if (bufcat(&s, &end, &len, 1,
++               strerror(((genom_syserr_detail *)detail)->code))) return NULL;
++    if (bufcat(&s, &end, &len, 0, "\"")) return NULL;
+     return s;
+-  } else if (e == genom_unkex_id && detail) {
++  } else if (e == genom_unkex_id) {
++    if (bufcat(&s, &end, &len, 0, "{\"ex\":\"")) return NULL;
++    if (bufcat(&s, &end, &len, 0, genom_unkex_id)) return NULL;
+     if (detail) {
+-      if (bufcat(&s, "{\"ex\":\"%s\",\"detail\":{\"what\":", genom_unkex_id))
+-        return NULL;
+-      if (bufcatquoted(&s, ((genom_unkex_detail *)detail)->what)) return NULL;
+-      if (bufcat(&s, "}}")) return NULL;
+-    } else {
+-      if (bufcat(&s, "{\"ex\":\"%s\"}", genom_unkex_id)) return NULL;
++      if (bufcat(&s, &end, &len, 0, "\",\"detail\":{\"what\":\"")) return NULL;
++      if (bufcat(&s, &end, &len, 1,
++                 ((genom_unkex_detail *)detail)->what)) return NULL;
+     }
++    if (bufcat(&s, &end, &len, 0, "\"}}")) return NULL;
+     return s;
+   }
+ <'foreach e [$component types public] {'>
+ <'  if {[$e kind] != "exception"} continue'>
+   else if (e == <"[$e cname]">_id) {
+ <'  if {[llength [$e members]]} {'>
++    if (bufcat(&s, &end, &len, 0, "{\"ex\":\"")) return NULL;
++    if (bufcat(&s, &end, &len, 0, <"[$e cname]">_id)) return NULL;
+     if (detail) {
+-      if (bufcat(&s, "{\"ex\":\"%s\",\"detail\":", <"[$e cname]">_id))
+-        return NULL;
+-      if (json_print_<"[$e mangle]">(&s, (const <"[$e declarator]"> *)detail))
+-        return NULL;
+-      if (bufcat(&s, "}")) return NULL;
+-      return s;
++      if (bufcat(&s, &end, &len, 0, "\",\"detail\":")) return NULL;
++      if (json_print_<"[$e mangle]">(&s, &end, &len,
++                      (const <"[$e declarator]"> *)detail)) return NULL;
++      if (bufcat(&s, &end, &len, 0, "}")) return NULL;
++      return s;
+     }
+ <'  }'>
+-    if (bufcat(&s, "{\"ex\":\"%s\"}", <"[$e cname]">_id))
+-      return NULL;
+-    return s;
++    if (bufcat(&s, &end, &len, 0, "{\"ex\":\"")) return NULL;
++    if (bufcat(&s, &end, &len, 0, <"[$e cname]">_id)) return NULL;
++    if (bufcat(&s, &end, &len, 0, "\"}")) return NULL;
++    return s;
+   }
+ <'}'>
+ 
+diff --git common/json-common.h common/json-common.h
+index 00ca051..e0db752 100644
+--- common/json-common.h
++++ common/json-common.h
+@@ -22,15 +22,6 @@ lang c
+ '>
+ #include "autoconf/acheader.h"
+ 
+-#ifdef __linux__
+-# if defined(HAVE_VASPRINTF) && defined(HAVE_ASPRINTF)
+-/* Apparently we need _GNU_SOURCE defined to get access to vasprintf on Linux */
+-#  ifndef _GNU_SOURCE
+-#   define _GNU_SOURCE
+-#  endif
+-# endif
+-#endif
+-
+ /* C99 standard specifies that format macros must be explicitly requested */
+ #define __STDC_FORMAT_MACROS
+ 
+@@ -48,13 +39,17 @@ lang c
+ 
+ /* --- local data ---------------------------------------------------------- */
+ 
+-static int	bufcat(char **buf, const char *fmt, ...);
+-static int	bufcatquoted(char **buf, const char *data);
++#define JSON_MINBUF	128
++
++static int	bufcat(char **buf, char **end, size_t *len, int quoted,
++                       const char *data);
++static int	bufcat_uint64_t(char **buf, char **end, size_t *len, int neg,
++                                uint64_t i) __attribute__ ((unused));
+ 
+ <'foreach t $types {'>
+ static __inline__ int	json_scan_<"[$t mangle]">(
+   <"[$t argument reference]">, const char **);
+-static __inline__ int	json_print_<"[$t mangle]">(char **,
++static __inline__ int	json_print_<"[$t mangle]">(char **, char **, size_t *,
+   <"[$t argument value]">);
+ <'}'>
+ 
+@@ -310,94 +305,100 @@ json_scan_<"[$t mangle]">(<"[$t argument reference data]">, const char **json)
+ }
+ 
+ static __inline__ int
+-json_print_<"[$t mangle]">(char **json, <"[$t argument value data]">)
++json_print_<"[$t mangle]">(char **json, char **end, size_t *len,
++  <"[$t argument value data]">)
+ {
+   int s;
+ <'
+   switch -glob -- [$t kind] {
+     typedef - {* member} {'>
+-  if ((s = json_print_<"[[$t type] mangle]">(json, data))) return s;
++  if ((s = json_print_<"[[$t type] mangle]">(json, end, len, data))) return s;
+ <'
+     }
+     struct - union - exception {'>
+-  if ((s = bufcat(json, "{"))) return s;
++  if ((s = bufcat(json, end, len, 0, "{"))) return s;
+ <'
+       set sep {}
+       foreach e [$t members] {'>
+-  if ((s = bufcat(json, "<"$sep">\"<"[$e name]">\":"))) return s;
+-  if ((s = json_print_<"[$e mangle]">(json,
++  if ((s = bufcat(json, end, len, 0, "<"$sep">\"<"[$e name]">\":"))) return s;
++  if ((s = json_print_<"[$e mangle]">(json, end, len,
+          <"[$e pass value data->[$e name]]">))) return s;
+ <'
+         set sep ,
+       }'>
+-  if ((s = bufcat(json, "}"))) return s;
++  if ((s = bufcat(json, end, len, 0, "}"))) return s;
+ <'
+     }
+     sequence {'>
+   size_t i;
+-  if ((s = bufcat(json, "{"))) return s;
++  if ((s = bufcat(json, end, len, 0, "["))) return s;
+   for (i=0; i<data->_length; i++) {
+-    if ((s = bufcat(json, "%s\"%d\":", i?",":"", i))) return s;
+-    if ((s = json_print_<"[[$t type] mangle]">(
+-           json, <"[[$t type] pass value {data->_buffer[i]}]">))) return s;
++    if (i && (s = bufcat(json, end, len, 0, ","))) return s;
++    if ((s = json_print_<"[[$t type] mangle]">(json, end, len,
++           <"[[$t type] pass value {data->_buffer[i]}]">))) return s;
+   }
+-  if ((s = bufcat(json, "}"))) return s;
++  if ((s = bufcat(json, end, len, 0, "]"))) return s;
+ <'    }'>
+ <'    optional {'>
+   if (data->_present) {
+-    if ((s = json_print_<"[[$t type] mangle]">(
+-           json, <"[[$t type] pass value {data->_value}]">))) return s;
++    if ((s = json_print_<"[[$t type] mangle]">(json, end, len,
++           <"[[$t type] pass value {data->_value}]">))) return s;
+   } else {
+-    if ((s = bufcat(json, "null"))) return s;
++    if ((s = bufcat(json, end, len, 0, "null"))) return s;
+   }
+ <'    }'>
+ <'    array {'>
+   size_t i;
+-  if ((s = bufcat(json, "{"))) return s;
++  if ((s = bufcat(json, end, len, 0, "["))) return s;
+   for (i=0; i<<"[$t length]">; i++) {
+-    if ((s = bufcat(json, "%s\"%d\":", i?",":"", i))) return s;
+-    if ((s = json_print_<"[[$t type] mangle]">(
+-           json, <"[[$t type] pass value {data[i]}]">))) return s;
++    if (i && (s = bufcat(json, end, len, 0, ","))) return s;
++    if ((s = json_print_<"[[$t type] mangle]">(json, end, len,
++           <"[[$t type] pass value {data[i]}]">))) return s;
+   }
+-  if ((s = bufcat(json, "}"))) return s;
++  if ((s = bufcat(json, end, len, 0, "]"))) return s;
+ <'    }'>
+ <'    char {'>
+-  if (*data == '"')
+-    if ((s = bufcat(json, "\"\\\"\""))) return s;
+-  else
+-    if ((s = bufcat(json, "\"%c\"", *data))) return s;
++  char buf[] = { '"', *data, '"', 0 };
++  if ((s = bufcat(json, end, len, 1, buf))) return s;
+ <'    }'>
+ <'    string {'>
+-  if ((s = bufcatquoted(json, data))) return s;
++  if ((s = bufcat(json, end, len, 0, "\""))) return s;
++  if ((s = bufcat(json, end, len, 1, data))) return s;
++  if ((s = bufcat(json, end, len, 0, "\""))) return s;
+ <'    }'>
+ <'    enum {'>
+   switch(data) {
+ <'      foreach e [$t members] {'>
+     case <"[$e cname]">:
+-      if ((s = bufcat(json, "\"<"[$e fullname]">\""))) return s; else break;
++      if ((s = bufcat(json, end, len, 0, "\"<"[$e fullname]">\""))) return s;
++      break;
+ <'      }'>
+     default:
+       assert(!"invalid value in enum <"[$e fullname]">");
+-      if ((s = bufcat(json, "\"<invalid>\""))) return s; else break;
++      if ((s = bufcat(json, end, len, 0, "\"<invalid>\""))) return s;
++      break;
+   }
+ <'
+     }
+     float - double {'>
+   if (isnan(data)) {
+-    if ((s = bufcat(json, "null"))) return s;
++    if ((s = bufcat(json, end, len, 0, "null"))) return s;
+   } else {
+-    if ((s = bufcat(json, "%g", data))) return s;
++    char buf[32];
++    sprintf(buf, "%g", data);
++    if ((s = bufcat(json, end, len, 0, buf))) return s;
+   }
+ <'
+     }
+     {unsigned *} {'>
+   uint64_t v = <"[$t dereference value data]">;
+-  if ((s = bufcat(json, "%" PRIu64, v))) return s;
++  if ((s = bufcat_uint64_t(json, end, len, 0, v))) return s;
+ <'
+     }
+     default {'>
+   int64_t v = <"[$t dereference value data]">;
+-  if ((s = bufcat(json, "%" PRIi64, v))) return s;
++  if ((s = bufcat_uint64_t(json, end, len, v<0, (uint64_t)(v<0?-v:v))))
++    return s;
+ <'
+     }
+   }'>
+@@ -408,64 +409,57 @@ json_print_<"[$t mangle]">(char **json, <"[$t argument value data]">)
+ /* --- bufcat -------------------------------------------------------------- */
+ 
+ static int
+-bufcat(char **buf, const char *fmt, ...)
++bufcat(char **buf, char **end, size_t *len, int quoted, const char *data)
+ {
+-  va_list ap;
+-  int s;
+-#if defined(HAVE_VASPRINTF) && defined(HAVE_ASPRINTF)
+-  char *buf1, *r = NULL;
+-
+-  va_start(ap, fmt);
+-  s = vasprintf(&r, fmt, ap);
+-  va_end(ap);
+-  if (s < 0) return ENOMEM;
+-
+-  if (*buf) {
+-    buf1 = *buf;
+-    s = asprintf(buf, "%s%s", buf1, r);
+-    free(r);
+-    free(buf1);
+-    if (s < 0) return ENOMEM;
+-  } else
+-    *buf = r;
+-#else
+-  static char buf1[1024];
++  size_t avail;
++  const char *s;
++  char *d;
+ 
+-  s = 1 + (*buf ? strlen(*buf) : 0);
++  s = data;
++  while(*s) {
++    avail = *len - (*end - *buf);
++    if (quoted) {
++      for(d = *end; *s && avail; avail--) {
++        if (*s == '"') {
++          if (avail < 2) {
++            avail = 0;
++            break;
++          }
++          *d++ = '\\';
++          avail--;
++        }
++        *d++ = *s++;
++      }
++    } else {
++      for(d = *end; *s && avail; avail--)
++        *d++ = *s++;
++    }
+ 
+-  va_start(ap, fmt);
+-  vsnprintf(buf1, sizeof(buf1), fmt, ap);
+-  va_end(ap);
+-  s += strlen(buf1);
++    if (!avail) {
++      char *newbuf = (char *)realloc(*buf, *len << 1);
++      if (!*newbuf) return ENOMEM;
+ 
+-  if (*buf)
+-    *buf = (char *)realloc(*buf, s);
+-  else
+-    *buf = (char *)calloc(s, 1);
+-  if (!*buf) return ENOMEM;
+-  strcat(*buf, buf1);
+-#endif
++      d = newbuf + (d - *buf);
++      *buf = newbuf;
++      *len <<= 1;
++    }
++    *d = 0;
++    *end = d;
++  }
+ 
+   return 0;
+ }
+ 
+ static int
+-bufcatquoted(char **buf, const char *data)
++bufcat_uint64_t(char **buf, char **end, size_t *len, int neg, uint64_t i)
+ {
+-  size_t l;
+-  int s;
++  char a[32];
++  char *d;
+ 
+-  if ((s = bufcat(buf, "\""))) return s;
+-  do {
+-    l = strcspn(data, "\"");
+-    if (l) {
+-      if ((s = bufcat(buf, "%.*s", l, data))) return s;
+-      data += l;
+-    }
+-    while (*data == '"') {
+-      if ((s = bufcat(buf, "\\\""))) return s;
+-      data++;
+-    }
+-  } while (*data);
+-  return bufcat(buf, "\"");
++  d = a + sizeof(a);
++  *--d = 0;
++  do { *--d = '0' + i%10; } while (i /= 10);
++  if (neg) *--d = '-';
++
++  return bufcat(buf, end, len, 0, d);
+ }
+diff --git server/json.cc server/json.cc
+index 0e19f4c..78e9ab3 100644
+--- server/json.cc
++++ server/json.cc
+@@ -50,12 +50,18 @@ genom_<"$comp">_<"[$t mangle]">_encodex(
+ {
+   json = "{\"ex\":\"<"[$t fullname]">\"";
+ <'  if {[llength [$t members]]} {'>
+-  char *jdetail = NULL;
+-  if (!json_print_<"[$t mangle]">(&jdetail, exdetail)) {
+-    json += ",\"detail\":";
+-    json += jdetail;
++  char *jdetail, *end;
++  size_t len;
++
++  len = JSON_MINBUF;
++  jdetail = end = (char *)malloc(len);
++  if (jdetail) {
++    if (!json_print_<"[$t mangle]">(&jdetail, &end, &len, exdetail)) {
++      json += ",\"detail\":";
++      json += jdetail;
++    }
++    if (jdetail) free(jdetail);
+   }
+-  if (jdetail) free(jdetail);
+ <'  }'>
+   json += "}";
+ }
+-- 
+1.7.9.5
+
-- 
GitLab