From cb4b43e2e8140051218d44d84bb5accee0b1ce53 Mon Sep 17 00:00:00 2001 From: Patrick Pelissier Date: Sat, 13 Jun 2020 17:26:07 +0200 Subject: [PATCH] * Add length param for seralization write_string to increase a little bit the performance for big strings. * Change the format of storing strings for serial-bin. --- README.md | 4 +-- doc/API.txt | 2 +- m-core.h | 17 +++++++-- m-serial-bin.h | 95 ++++++++++++++++++++++++++++++++++++++++++------- m-serial-json.h | 10 +++--- m-string.h | 4 +-- 6 files changed, 108 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 3e39f723..f8e83a0c 100644 --- a/README.md +++ b/README.md @@ -5869,7 +5869,7 @@ the following fields with the following definition: Write the float 'data' of 'size_of_type' bytes into the serial stream 'serial'. Return M\_SERIAL\_OK\_DONE if it succeeds, M\_SERIAL\_FAIL otherwise */ * write\_string: - Write the null-terminated string 'data'into the serial stream 'serial'. + Write the null-terminated string 'data' of 'length' characters into the serial stream 'serial'. Return M\_SERIAL\_OK\_DONE if it succeeds, M\_SERIAL\_FAIL otherwise */ * write\_array\_start: Start writing an array of 'number_of_elements' objects into the serial stream 'serial'. @@ -5981,7 +5981,7 @@ The full C definition are: m_serial_return_code_t (*write_boolean)(m_serial_write_t serial, const bool b); m_serial_return_code_t (*write_integer)(m_serial_write_t serial, const long long i, const size_t size_of_type); m_serial_return_code_t (*write_float)(m_serial_write_t serial, const long double f, const size_t size_of_type); - m_serial_return_code_t (*write_string)(m_serial_write_t serial, const char s[]); + m_serial_return_code_t (*write_string)(m_serial_write_t serial, const char s[], size_t length); m_serial_return_code_t (*write_array_start)(m_serial_local_t local, m_serial_write_t serial, const size_t number_of_elements); m_serial_return_code_t (*write_array_next)(m_serial_local_t local, m_serial_write_t serial); m_serial_return_code_t (*write_array_end)(m_serial_local_t local, m_serial_write_t serial); diff --git a/doc/API.txt b/doc/API.txt index 33a95553..12b05809 100644 --- a/doc/API.txt +++ b/doc/API.txt @@ -50,4 +50,4 @@ V0.4.*: * The order of a list initialized using M_LET is reversed to match what is done for ARRAY (and the C++ forward_list). * Rename M_SKIP, M_KEEP & M_MID into M_SKIP_ARGS, M_KEEP_ARGS & M_MID_ARGS due to conflict with glibc malloc.h (that defines M_KEEP for compatibility). - + * Method write_string of m_serial expects a new argument (number of characters) diff --git a/m-core.h b/m-core.h index 8cd14c91..5d49ace4 100644 --- a/m-core.h +++ b/m-core.h @@ -2995,7 +2995,7 @@ typedef struct m_serial_write_interface_s { m_serial_return_code_t (*write_boolean)(m_serial_write_t,const bool data); m_serial_return_code_t (*write_integer)(m_serial_write_t,const long long data, const size_t size_of_type); m_serial_return_code_t (*write_float)(m_serial_write_t, const long double data, const size_t size_of_type); - m_serial_return_code_t (*write_string)(m_serial_write_t,const char data[]); + m_serial_return_code_t (*write_string)(m_serial_write_t,const char data[], size_t len); m_serial_return_code_t (*write_array_start)(m_serial_local_t, m_serial_write_t, const size_t number_of_elements); m_serial_return_code_t (*write_array_next)(m_serial_local_t, m_serial_write_t); m_serial_return_code_t (*write_array_end)(m_serial_local_t, m_serial_write_t); @@ -3031,8 +3031,8 @@ typedef struct m_serial_write_interface_s { float: (serial)->m_interface->write_float(serial, M_AS_TYPE(float,(x)), sizeof (x)), \ double: (serial)->m_interface->write_float(serial, M_AS_TYPE(double,(x)), sizeof (x)), \ long double: (serial)->m_interface->write_float(serial, M_AS_TYPE(long double,(x)), sizeof (x)), \ - const char *: (serial)->m_interface->write_string(serial, M_AS_TYPE(const char *,(x))), \ - char *: (serial)->m_interface->write_string(serial, M_AS_TYPE(char *,(x))), \ + const char *: (serial)->m_interface->write_string(serial, M_AS_TYPE(const char *,(x)), m_core_out_serial_strlen(M_AS_TYPE(const char *,(x))) ), \ + char *: (serial)->m_interface->write_string(serial, M_AS_TYPE(char *,(x)), m_core_out_serial_strlen(M_AS_TYPE(const char *,(x))) ), \ const void *: M_SERIAL_FAIL /* unsupported */, \ void *: M_SERIAL_FAIL /* unsupported */) @@ -3089,4 +3089,15 @@ M_IN_SERIAL_DEFAULT_TYPE_DEF(m_core_in_serial_float, float, read_float, long dou M_IN_SERIAL_DEFAULT_TYPE_DEF(m_core_in_serial_double, double, read_float, long double) M_IN_SERIAL_DEFAULT_TYPE_DEF(m_core_in_serial_ldouble, long double, read_float, long double) +/* Encapsulation of strlen to avoid warnings in M_OUT_SERIAL_DEFAULT_ARG + * because of expanded code will call strlen with NULL (which is illegal) + * However, the branch where it is called is unreachable, so the warning + * is not justified. */ +static size_t +m_core_out_serial_strlen(const char s[]) +{ + assert(s != NULL); + return strlen(s); +} + #endif diff --git a/m-serial-bin.h b/m-serial-bin.h index f0dc1e1f..01f6184e 100644 --- a/m-serial-bin.h +++ b/m-serial-bin.h @@ -30,6 +30,66 @@ #include "m-core.h" #include "m-string.h" +/* Internal service: + * Write size_t in the stream in a compact form to reduce consumption + * (and I/O bandwidth) + */ +static inline bool +m_serial_bin_write_size(FILE *f, const size_t size) +{ + bool b; + if (M_LIKELY(size < 253)) + { + b = EOF != fputc((unsigned char) size, f); + } else if (size < 1ULL << 16) { + b = EOF != fputc(253, f); // Save 16 bits encoding + b &= EOF != fputc((unsigned char) (size >> 8), f); + b &= EOF != fputc((unsigned char) size, f); + } else if (size < 1ULL<< 32) { + b = EOF != fputc(254, f); // Save 32 bits encoding + b &= EOF != fputc((unsigned char) (size >> 24), f); + b &= EOF != fputc((unsigned char) (size >> 16), f); + b &= EOF != fputc((unsigned char) (size >> 8), f); + b &= EOF != fputc((unsigned char) size, f); + } else { + b = EOF != fputc(255, f); // Save 64 bits encoding + b &= EOF != fputc((unsigned char) (size >> 56), f); + b &= EOF != fputc((unsigned char) (size >> 48), f); + b &= EOF != fputc((unsigned char) (size >> 40), f); + b &= EOF != fputc((unsigned char) (size >> 32), f); + b &= EOF != fputc((unsigned char) (size >> 24), f); + b &= EOF != fputc((unsigned char) (size >> 16), f); + b &= EOF != fputc((unsigned char) (size >> 8), f); + b &= EOF != fputc((unsigned char) size, f); + } + return b; +} + +/* Internal service: + * Read size_t in the stream from a compact form to reduce consumption + * (and I/O bandwidth) + */ +static inline bool +m_serial_bin_read_size(FILE *f, size_t *size) +{ + int c; + c = fgetc(f); + if (M_UNLIKELY(c == EOF)) return false; + if (M_LIKELY(c < 253)) { + *size = (size_t) c; + return true; + } + size_t s = 0; + int l = (c = 255) ? 8 : (c == 254) ? 4 : 2; + for(int i = 0; i < l; i++) { + c = fgetc(f); + if (M_UNLIKELY(c == EOF)) return false; + s = (s << 8) | (size_t) c; + } + *size = s; + return true; +} + /* Write the boolean 'data' into the serial stream 'serial'. Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ static inline m_serial_return_code_t @@ -105,12 +165,17 @@ m_serial_bin_write_float(m_serial_write_t serial, const long double data, const /* Write the null-terminated string 'data'into the serial stream 'serial'. Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ static inline m_serial_return_code_t -m_serial_bin_write_string(m_serial_write_t serial, const char data[]) +m_serial_bin_write_string(m_serial_write_t serial, const char data[], size_t length) { + STRINGI_ASSUME(length == strlen(data) ); FILE *f = (FILE *)serial->data[0].p; - size_t l = strlen(data); - size_t n = fwrite (M_ASSIGN_CAST(const void*, data), 1, l+1, f); - return n == l+1 ? M_SERIAL_OK_DONE : M_SERIAL_FAIL; + assert(f != NULL && data != NULL); + // Write first the number of (non null) characters + if (m_serial_bin_write_size(f, length) != true) return M_SERIAL_FAIL; + // Write the characters (excluding the final null char) + // NOTE: fwrite supports length == 0. + size_t n = fwrite (M_ASSIGN_CAST(const void*, data), 1, length, f); + return (n == length) ? M_SERIAL_OK_DONE : M_SERIAL_FAIL; } /* Start writing an array of 'number_of_elements' objects into the serial stream 'serial'. @@ -345,14 +410,20 @@ m_serial_bin_read_float(m_serial_read_t serial, long double *r, const size_t siz static inline m_serial_return_code_t m_serial_bin_read_string(m_serial_read_t serial, struct string_s *s){ FILE *f = (FILE*) serial->data[0].p; - string_clean(s); - int c; - c = fgetc(f); - while (c != 0 && c != EOF) { - string_push_back(s, c); - c = fgetc(f); - } - return c != EOF ? M_SERIAL_OK_DONE : M_SERIAL_FAIL; + assert(f != NULL && s != NULL); + // First read the number of non null characters + size_t length; + if (m_serial_bin_read_size(f, &length) != true) return M_SERIAL_FAIL; + // Use of internal string interface to dimension the string + string_reserve(s, length + 1); + char *p = stringi_get_str(s); + stringi_set_size(s, length); + // Read the characters excluding the final null one. + // NOTE: fread supports length == 0. + size_t n = fread(M_ASSIGN_CAST(void*, p), 1, length, f); + // Force the final null character + p[length] = 0; + return (n == length) ? M_SERIAL_OK_DONE : M_SERIAL_FAIL; } /* Start reading from the stream 'serial' an array. diff --git a/m-serial-json.h b/m-serial-json.h index cc0f905a..66cacf39 100644 --- a/m-serial-json.h +++ b/m-serial-json.h @@ -63,14 +63,16 @@ m_serial_json_write_float(m_serial_write_t serial, const long double data, const /* Write the null-terminated string 'data'into the serial stream 'serial'. Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ static inline m_serial_return_code_t -m_serial_json_write_string(m_serial_write_t serial, const char data[]) +m_serial_json_write_string(m_serial_write_t serial, const char data[], size_t length) { + STRINGI_ASSUME(length == strlen(data) ); FILE *f = (FILE *)serial->data[0].p; - /* Build dummy string to reuse string_out_str */ + assert(f != NULL && data != NULL); + /* HACK: Build dummy string to reuse string_out_str */ string_t v2; uintptr_t ptr = (uintptr_t) data; - v2->u.heap.size = strlen(data); - v2->u.heap.alloc = v2->u.heap.size + 1; + v2->u.heap.size = length; + v2->u.heap.alloc = length + 1; v2->ptr = (char*)ptr; string_out_str(f, v2); return M_SERIAL_OK_DONE; diff --git a/m-string.h b/m-string.h index d1c33171..10479ce8 100644 --- a/m-string.h +++ b/m-string.h @@ -1241,7 +1241,7 @@ static inline m_serial_return_code_t string_out_serial(m_serial_write_t serial, const string_t v) { assert (serial != NULL && serial->m_interface != NULL); - return serial->m_interface->write_string(serial, string_get_cstr(v)); + return serial->m_interface->write_string(serial, string_get_cstr(v), string_size(v) ); } /* Read the formatted string from the serializer @@ -2047,7 +2047,7 @@ namespace m_string { { \ BOUNDED_STRINGI_CONTRACT(v, max_size); \ assert (serial != NULL && serial->m_interface != NULL); \ - return serial->m_interface->write_string(serial, v->s); \ + return serial->m_interface->write_string(serial, v->s, strlen(v->s) ); \ } \ \ static inline m_serial_return_code_t \