diff options
Diffstat (limited to 'libnetdata/buffer')
-rw-r--r-- | libnetdata/buffer/Makefile.am | 8 | ||||
-rw-r--r-- | libnetdata/buffer/README.md | 20 | ||||
-rw-r--r-- | libnetdata/buffer/buffer.c | 503 | ||||
-rw-r--r-- | libnetdata/buffer/buffer.h | 1228 |
4 files changed, 0 insertions, 1759 deletions
diff --git a/libnetdata/buffer/Makefile.am b/libnetdata/buffer/Makefile.am deleted file mode 100644 index 161784b8f..000000000 --- a/libnetdata/buffer/Makefile.am +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-License-Identifier: GPL-3.0-or-later - -AUTOMAKE_OPTIONS = subdir-objects -MAINTAINERCLEANFILES = $(srcdir)/Makefile.in - -dist_noinst_DATA = \ - README.md \ - $(NULL) diff --git a/libnetdata/buffer/README.md b/libnetdata/buffer/README.md deleted file mode 100644 index 2937ae141..000000000 --- a/libnetdata/buffer/README.md +++ /dev/null @@ -1,20 +0,0 @@ -<!-- -title: "BUFFER" -custom_edit_url: https://github.com/netdata/netdata/edit/master/libnetdata/buffer/README.md -sidebar_label: "BUFFER library" -learn_status: "Published" -learn_topic_type: "Tasks" -learn_rel_path: "Developers/libnetdata" ---> - -# BUFFER - -`BUFFER` is a convenience library for working with strings in `C`. -Mainly, `BUFFER`s eliminate the need for tracking the string length, thus providing -a safe alternative for string operations. - -Also, they are super fast in printing and appending data to the string and its `buffer_strlen()` -is just a lookup (it does not traverse the string). - -Netdata uses `BUFFER`s for preparing web responses and buffering data to be sent upstream or -to external databases. diff --git a/libnetdata/buffer/buffer.c b/libnetdata/buffer/buffer.c deleted file mode 100644 index 64f9cce47..000000000 --- a/libnetdata/buffer/buffer.c +++ /dev/null @@ -1,503 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "../libnetdata.h" - -static inline void buffer_overflow_init(BUFFER *b) -{ - b->buffer[b->size] = '\0'; - strcpy(&b->buffer[b->size + 1], BUFFER_OVERFLOW_EOF); -} - -void buffer_reset(BUFFER *wb) { - buffer_flush(wb); - - wb->content_type = CT_TEXT_PLAIN; - wb->options = 0; - wb->date = 0; - wb->expires = 0; - buffer_no_cacheable(wb); - - buffer_overflow_check(wb); -} - -void buffer_char_replace(BUFFER *wb, char from, char to) { - char *s = wb->buffer, *end = &wb->buffer[wb->len]; - - while(s != end) { - if(*s == from) *s = to; - s++; - } - - buffer_overflow_check(wb); -} - -void buffer_print_sn_flags(BUFFER *wb, SN_FLAGS flags, bool send_anomaly_bit) { - if(unlikely(flags == SN_EMPTY_SLOT)) { - buffer_fast_strcat(wb, "E", 1); - return; - } - - size_t printed = 0; - if(likely(send_anomaly_bit && (flags & SN_FLAG_NOT_ANOMALOUS))) { - buffer_fast_strcat(wb, "A", 1); - printed++; - } - - if(unlikely(flags & SN_FLAG_RESET)) { - buffer_fast_strcat(wb, "R", 1); - printed++; - } - - if(!printed) - buffer_fast_strcat(wb, "''", 2); -} - -void buffer_strcat_htmlescape(BUFFER *wb, const char *txt) -{ - while(*txt) { - switch(*txt) { - case '&': buffer_strcat(wb, "&"); break; - case '<': buffer_strcat(wb, "<"); break; - case '>': buffer_strcat(wb, ">"); break; - case '"': buffer_strcat(wb, """); break; - case '/': buffer_strcat(wb, "/"); break; - case '\'': buffer_strcat(wb, "'"); break; - default: { - buffer_need_bytes(wb, 1); - wb->buffer[wb->len++] = *txt; - } - } - txt++; - } - - buffer_overflow_check(wb); -} - -void buffer_snprintf(BUFFER *wb, size_t len, const char *fmt, ...) -{ - if(unlikely(!fmt || !*fmt)) return; - - buffer_need_bytes(wb, len + 1); - - va_list args; - va_start(args, fmt); - // vsnprintfz() returns the number of bytes actually written - after possible truncation - wb->len += vsnprintfz(&wb->buffer[wb->len], len, fmt, args); - va_end(args); - - buffer_overflow_check(wb); - - // the buffer is \0 terminated by vsnprintfz -} - -inline void buffer_vsprintf(BUFFER *wb, const char *fmt, va_list args) { - if(unlikely(!fmt || !*fmt)) return; - - size_t full_size_bytes = 0, need = 2, space_remaining = 0; - - do { - need += full_size_bytes + 2; - - buffer_need_bytes(wb, need); - - space_remaining = wb->size - wb->len - 1; - - // Use the copy of va_list for vsnprintf - va_list args_copy; - va_copy(args_copy, args); - // vsnprintf() returns the number of bytes required, even if bigger than the buffer provided - full_size_bytes = (size_t) vsnprintf(&wb->buffer[wb->len], space_remaining, fmt, args_copy); - va_end(args_copy); - - } while(full_size_bytes >= space_remaining); - - wb->len += full_size_bytes; - - wb->buffer[wb->len] = '\0'; - buffer_overflow_check(wb); -} - -void buffer_sprintf(BUFFER *wb, const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - buffer_vsprintf(wb, fmt, args); - va_end(args); -} - -// generate a javascript date, the fastest possible way... -void buffer_jsdate(BUFFER *wb, int year, int month, int day, int hours, int minutes, int seconds) -{ - // 10 20 30 = 35 - // 01234567890123456789012345678901234 - // Date(2014,04,01,03,28,20) - - buffer_need_bytes(wb, 30); - - char *b = &wb->buffer[wb->len], *p; - unsigned int *q = (unsigned int *)b; - - #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - *q++ = 0x65746144; // "Date" backwards. - #else - *q++ = 0x44617465; // "Date" - #endif - p = (char *)q; - - *p++ = '('; - *p++ = '0' + year / 1000; year %= 1000; - *p++ = '0' + year / 100; year %= 100; - *p++ = '0' + year / 10; - *p++ = '0' + year % 10; - *p++ = ','; - *p = '0' + month / 10; if (*p != '0') p++; - *p++ = '0' + month % 10; - *p++ = ','; - *p = '0' + day / 10; if (*p != '0') p++; - *p++ = '0' + day % 10; - *p++ = ','; - *p = '0' + hours / 10; if (*p != '0') p++; - *p++ = '0' + hours % 10; - *p++ = ','; - *p = '0' + minutes / 10; if (*p != '0') p++; - *p++ = '0' + minutes % 10; - *p++ = ','; - *p = '0' + seconds / 10; if (*p != '0') p++; - *p++ = '0' + seconds % 10; - - unsigned short *r = (unsigned short *)p; - -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - *r++ = 0x0029; // ")\0" backwards. - #else - *r++ = 0x2900; // ")\0" - #endif - - wb->len += (size_t)((char *)r - b - 1); - - // terminate it - wb->buffer[wb->len] = '\0'; - buffer_overflow_check(wb); -} - -// generate a date, the fastest possible way... -void buffer_date(BUFFER *wb, int year, int month, int day, int hours, int minutes, int seconds) -{ - // 10 20 30 = 35 - // 01234567890123456789012345678901234 - // 2014-04-01 03:28:20 - - buffer_need_bytes(wb, 36); - - char *b = &wb->buffer[wb->len]; - char *p = b; - - *p++ = '0' + year / 1000; year %= 1000; - *p++ = '0' + year / 100; year %= 100; - *p++ = '0' + year / 10; - *p++ = '0' + year % 10; - *p++ = '-'; - *p++ = '0' + month / 10; - *p++ = '0' + month % 10; - *p++ = '-'; - *p++ = '0' + day / 10; - *p++ = '0' + day % 10; - *p++ = ' '; - *p++ = '0' + hours / 10; - *p++ = '0' + hours % 10; - *p++ = ':'; - *p++ = '0' + minutes / 10; - *p++ = '0' + minutes % 10; - *p++ = ':'; - *p++ = '0' + seconds / 10; - *p++ = '0' + seconds % 10; - *p = '\0'; - - wb->len += (size_t)(p - b); - - // terminate it - wb->buffer[wb->len] = '\0'; - buffer_overflow_check(wb); -} - -BUFFER *buffer_create(size_t size, size_t *statistics) -{ - BUFFER *b; - - netdata_log_debug(D_WEB_BUFFER, "Creating new web buffer of size %zu.", size); - - b = callocz(1, sizeof(BUFFER)); - b->buffer = mallocz(size + sizeof(BUFFER_OVERFLOW_EOF) + 2); - b->buffer[0] = '\0'; - b->size = size; - b->content_type = CT_TEXT_PLAIN; - b->statistics = statistics; - buffer_no_cacheable(b); - buffer_overflow_init(b); - buffer_overflow_check(b); - - if(b->statistics) - __atomic_add_fetch(b->statistics, b->size + sizeof(BUFFER) + sizeof(BUFFER_OVERFLOW_EOF) + 2, __ATOMIC_RELAXED); - - return(b); -} - -void buffer_free(BUFFER *b) { - if(unlikely(!b)) return; - - buffer_overflow_check(b); - - netdata_log_debug(D_WEB_BUFFER, "Freeing web buffer of size %zu.", b->size); - - if(b->statistics) - __atomic_sub_fetch(b->statistics, b->size + sizeof(BUFFER) + sizeof(BUFFER_OVERFLOW_EOF) + 2, __ATOMIC_RELAXED); - - freez(b->buffer); - freez(b); -} - -void buffer_increase(BUFFER *b, size_t free_size_required) { - buffer_overflow_check(b); - - size_t left = b->size - b->len; - if(left >= free_size_required) return; - - size_t wanted = free_size_required - left; - size_t minimum = WEB_DATA_LENGTH_INCREASE_STEP; - if(minimum > wanted) wanted = minimum; - - size_t optimal = (b->size > 5*1024*1024) ? b->size / 2 : b->size; - if(optimal > wanted) wanted = optimal; - - netdata_log_debug(D_WEB_BUFFER, "Increasing data buffer from size %zu to %zu.", b->size, b->size + wanted); - - b->buffer = reallocz(b->buffer, b->size + wanted + sizeof(BUFFER_OVERFLOW_EOF) + 2); - b->size += wanted; - - if(b->statistics) - __atomic_add_fetch(b->statistics, wanted, __ATOMIC_RELAXED); - - buffer_overflow_init(b); - buffer_overflow_check(b); -} - -// ---------------------------------------------------------------------------- - -void buffer_json_initialize(BUFFER *wb, const char *key_quote, const char *value_quote, int depth, - bool add_anonymous_object, BUFFER_JSON_OPTIONS options) { - strncpyz(wb->json.key_quote, key_quote, BUFFER_QUOTE_MAX_SIZE); - strncpyz(wb->json.value_quote, value_quote, BUFFER_QUOTE_MAX_SIZE); - - wb->json.depth = (int8_t)(depth - 1); - _buffer_json_depth_push(wb, BUFFER_JSON_OBJECT); - - if(add_anonymous_object) - buffer_fast_strcat(wb, "{", 1); - else - options |= BUFFER_JSON_OPTIONS_NON_ANONYMOUS; - - wb->json.options = options; - - wb->content_type = CT_APPLICATION_JSON; - buffer_no_cacheable(wb); -} - -void buffer_json_finalize(BUFFER *wb) { - while(wb->json.depth >= 0) { - switch(wb->json.stack[wb->json.depth].type) { - case BUFFER_JSON_OBJECT: - if (wb->json.depth == 0) - if (!(wb->json.options & BUFFER_JSON_OPTIONS_NON_ANONYMOUS)) - buffer_json_object_close(wb); - else - _buffer_json_depth_pop(wb); - else - buffer_json_object_close(wb); - break; - case BUFFER_JSON_ARRAY: - buffer_json_array_close(wb); - break; - - default: - internal_fatal(true, "BUFFER: unknown json member type in stack"); - break; - } - } - - if(!(wb->json.options & BUFFER_JSON_OPTIONS_MINIFY)) - buffer_fast_strcat(wb, "\n", 1); -} - -// ---------------------------------------------------------------------------- - -const char hex_digits[16] = "0123456789ABCDEF"; -const char base64_digits[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -unsigned char hex_value_from_ascii[256]; -unsigned char base64_value_from_ascii[256]; - -__attribute__((constructor)) void initialize_ascii_maps(void) { - for(size_t i = 0 ; i < 256 ; i++) { - hex_value_from_ascii[i] = 255; - base64_value_from_ascii[i] = 255; - } - - for(size_t i = 0; i < 16 ; i++) - hex_value_from_ascii[(int)hex_digits[i]] = i; - - for(size_t i = 0; i < 64 ; i++) - base64_value_from_ascii[(int)base64_digits[i]] = i; -} - -// ---------------------------------------------------------------------------- -// unit test - -static int buffer_expect(BUFFER *wb, const char *expected) { - const char *generated = buffer_tostring(wb); - - if(strcmp(generated, expected) != 0) { - netdata_log_error("BUFFER: mismatch.\nGenerated:\n%s\nExpected:\n%s\n", - generated, expected); - return 1; - } - - return 0; -} - -static int buffer_uint64_roundtrip(BUFFER *wb, NUMBER_ENCODING encoding, uint64_t value, const char *expected) { - int errors = 0; - buffer_flush(wb); - buffer_print_uint64_encoded(wb, encoding, value); - - if(expected) - errors += buffer_expect(wb, expected); - - uint64_t v = str2ull_encoded(buffer_tostring(wb)); - if(v != value) { - netdata_log_error("BUFFER: string '%s' does resolves to %llu, expected %llu", - buffer_tostring(wb), (unsigned long long)v, (unsigned long long)value); - errors++; - } - buffer_flush(wb); - return errors; -} - -static int buffer_int64_roundtrip(BUFFER *wb, NUMBER_ENCODING encoding, int64_t value, const char *expected) { - int errors = 0; - buffer_flush(wb); - buffer_print_int64_encoded(wb, encoding, value); - - if(expected) - errors += buffer_expect(wb, expected); - - int64_t v = str2ll_encoded(buffer_tostring(wb)); - if(v != value) { - netdata_log_error("BUFFER: string '%s' does resolves to %lld, expected %lld", - buffer_tostring(wb), (long long)v, (long long)value); - errors++; - } - buffer_flush(wb); - return errors; -} - -static int buffer_double_roundtrip(BUFFER *wb, NUMBER_ENCODING encoding, NETDATA_DOUBLE value, const char *expected) { - int errors = 0; - buffer_flush(wb); - buffer_print_netdata_double_encoded(wb, encoding, value); - - if(expected) - errors += buffer_expect(wb, expected); - - NETDATA_DOUBLE v = str2ndd_encoded(buffer_tostring(wb), NULL); - if(v != value) { - netdata_log_error("BUFFER: string '%s' does resolves to %.12f, expected %.12f", - buffer_tostring(wb), v, value); - errors++; - } - buffer_flush(wb); - return errors; -} - -int buffer_unittest(void) { - int errors = 0; - BUFFER *wb = buffer_create(0, NULL); - - buffer_uint64_roundtrip(wb, NUMBER_ENCODING_DECIMAL, 0, "0"); - buffer_uint64_roundtrip(wb, NUMBER_ENCODING_HEX, 0, "0x0"); - buffer_uint64_roundtrip(wb, NUMBER_ENCODING_BASE64, 0, "#A"); - - buffer_uint64_roundtrip(wb, NUMBER_ENCODING_DECIMAL, 1676071986, "1676071986"); - buffer_uint64_roundtrip(wb, NUMBER_ENCODING_HEX, 1676071986, "0x63E6D432"); - buffer_uint64_roundtrip(wb, NUMBER_ENCODING_BASE64, 1676071986, "#Bj5tQy"); - - buffer_uint64_roundtrip(wb, NUMBER_ENCODING_DECIMAL, 18446744073709551615ULL, "18446744073709551615"); - buffer_uint64_roundtrip(wb, NUMBER_ENCODING_HEX, 18446744073709551615ULL, "0xFFFFFFFFFFFFFFFF"); - buffer_uint64_roundtrip(wb, NUMBER_ENCODING_BASE64, 18446744073709551615ULL, "#P//////////"); - - buffer_int64_roundtrip(wb, NUMBER_ENCODING_DECIMAL, 0, "0"); - buffer_int64_roundtrip(wb, NUMBER_ENCODING_HEX, 0, "0x0"); - buffer_int64_roundtrip(wb, NUMBER_ENCODING_BASE64, 0, "#A"); - - buffer_int64_roundtrip(wb, NUMBER_ENCODING_DECIMAL, -1676071986, "-1676071986"); - buffer_int64_roundtrip(wb, NUMBER_ENCODING_HEX, -1676071986, "-0x63E6D432"); - buffer_int64_roundtrip(wb, NUMBER_ENCODING_BASE64, -1676071986, "-#Bj5tQy"); - - buffer_int64_roundtrip(wb, NUMBER_ENCODING_DECIMAL, (int64_t)-9223372036854775807ULL, "-9223372036854775807"); - buffer_int64_roundtrip(wb, NUMBER_ENCODING_HEX, (int64_t)-9223372036854775807ULL, "-0x7FFFFFFFFFFFFFFF"); - buffer_int64_roundtrip(wb, NUMBER_ENCODING_BASE64, (int64_t)-9223372036854775807ULL, "-#H//////////"); - - buffer_double_roundtrip(wb, NUMBER_ENCODING_DECIMAL, 0, "0"); - buffer_double_roundtrip(wb, NUMBER_ENCODING_HEX, 0, "%0"); - buffer_double_roundtrip(wb, NUMBER_ENCODING_BASE64, 0, "@A"); - - buffer_double_roundtrip(wb, NUMBER_ENCODING_DECIMAL, 1.5, "1.5"); - buffer_double_roundtrip(wb, NUMBER_ENCODING_HEX, 1.5, "%3FF8000000000000"); - buffer_double_roundtrip(wb, NUMBER_ENCODING_BASE64, 1.5, "@D/4AAAAAAAA"); - - buffer_double_roundtrip(wb, NUMBER_ENCODING_DECIMAL, 1.23e+14, "123000000000000"); - buffer_double_roundtrip(wb, NUMBER_ENCODING_HEX, 1.23e+14, "%42DBF78AD3AC0000"); - buffer_double_roundtrip(wb, NUMBER_ENCODING_BASE64, 1.23e+14, "@ELb94rTrAAA"); - - buffer_double_roundtrip(wb, NUMBER_ENCODING_DECIMAL, 9.12345678901234567890123456789e+45, "9.123456789012346128e+45"); - buffer_double_roundtrip(wb, NUMBER_ENCODING_HEX, 9.12345678901234567890123456789e+45, "%497991C25C9E4309"); - buffer_double_roundtrip(wb, NUMBER_ENCODING_BASE64, 9.12345678901234567890123456789e+45, "@El5kcJcnkMJ"); - - buffer_flush(wb); - - { - char buf[1024 + 1]; - for(size_t i = 0; i < 1024 ;i++) - buf[i] = (char)(i % 26) + 'A'; - buf[1024] = '\0'; - - buffer_strcat(wb, buf); - errors += buffer_expect(wb, buf); - } - - buffer_flush(wb); - - buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_DEFAULT); - buffer_json_finalize(wb); - errors += buffer_expect(wb, "{\n}\n"); - - buffer_flush(wb); - - buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_DEFAULT); - buffer_json_member_add_string(wb, "hello", "world"); - buffer_json_member_add_string(wb, "alpha", "this: \" is a double quote"); - buffer_json_member_add_object(wb, "object1"); - buffer_json_member_add_string(wb, "hello", "world"); - buffer_json_finalize(wb); - errors += buffer_expect(wb, "{\n \"hello\":\"world\",\n \"alpha\":\"this: \\\" is a double quote\",\n \"object1\":{\n \"hello\":\"world\"\n }\n}\n"); - - buffer_free(wb); - return errors; -} - -#ifdef ENABLE_H2O -h2o_iovec_t buffer_to_h2o_iovec(BUFFER *wb) { - h2o_iovec_t ret; - ret.base = wb->buffer; - ret.len = wb->len; - return ret; -} -#endif diff --git a/libnetdata/buffer/buffer.h b/libnetdata/buffer/buffer.h deleted file mode 100644 index 88d3f0282..000000000 --- a/libnetdata/buffer/buffer.h +++ /dev/null @@ -1,1228 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -#ifndef NETDATA_WEB_BUFFER_H -#define NETDATA_WEB_BUFFER_H 1 - -#include "../string/utf8.h" -#include "../libnetdata.h" - -#ifdef ENABLE_H2O -#include "h2o/memory.h" -#endif - -#define WEB_DATA_LENGTH_INCREASE_STEP 1024 - -#define BUFFER_JSON_MAX_DEPTH 32 // max is 255 - -extern const char hex_digits[16]; -extern const char base64_digits[64]; -extern unsigned char hex_value_from_ascii[256]; -extern unsigned char base64_value_from_ascii[256]; - -typedef enum __attribute__ ((__packed__)) { - BUFFER_JSON_EMPTY = 0, - BUFFER_JSON_OBJECT, - BUFFER_JSON_ARRAY, -} BUFFER_JSON_NODE_TYPE; - -typedef struct web_buffer_json_node { - BUFFER_JSON_NODE_TYPE type; - uint32_t count:24; -} BUFFER_JSON_NODE; - -#define BUFFER_QUOTE_MAX_SIZE 7 - -typedef enum __attribute__ ((__packed__)) { - WB_CONTENT_CACHEABLE = (1 << 0), - WB_CONTENT_NO_CACHEABLE = (1 << 1), -} BUFFER_OPTIONS; - -typedef enum __attribute__ ((__packed__)) { - CT_NONE = 0, - CT_APPLICATION_JSON, - CT_TEXT_PLAIN, - CT_TEXT_HTML, - CT_APPLICATION_X_JAVASCRIPT, - CT_TEXT_CSS, - CT_TEXT_XML, - CT_APPLICATION_XML, - CT_TEXT_XSL, - CT_APPLICATION_OCTET_STREAM, - CT_APPLICATION_X_FONT_TRUETYPE, - CT_APPLICATION_X_FONT_OPENTYPE, - CT_APPLICATION_FONT_WOFF, - CT_APPLICATION_FONT_WOFF2, - CT_APPLICATION_VND_MS_FONTOBJ, - CT_IMAGE_SVG_XML, - CT_IMAGE_PNG, - CT_IMAGE_JPG, - CT_IMAGE_GIF, - CT_IMAGE_XICON, - CT_IMAGE_ICNS, - CT_IMAGE_BMP, - CT_PROMETHEUS, - CT_AUDIO_MPEG, - CT_AUDIO_OGG, - CT_VIDEO_MP4, - CT_APPLICATION_PDF, - CT_APPLICATION_ZIP, -} HTTP_CONTENT_TYPE; - -typedef enum __attribute__ ((__packed__)) { - BUFFER_JSON_OPTIONS_DEFAULT = 0, - BUFFER_JSON_OPTIONS_MINIFY = (1 << 0), - BUFFER_JSON_OPTIONS_NEWLINE_ON_ARRAY_ITEMS = (1 << 1), - BUFFER_JSON_OPTIONS_NON_ANONYMOUS = (1 << 2), -} BUFFER_JSON_OPTIONS; - -typedef struct web_buffer { - size_t size; // allocation size of buffer, in bytes - size_t len; // current data length in buffer, in bytes - char *buffer; // the buffer itself - HTTP_CONTENT_TYPE content_type; // the content type of the data in the buffer - BUFFER_OPTIONS options; // options related to the content - time_t date; // the timestamp this content has been generated - time_t expires; // the timestamp this content expires - size_t *statistics; - - struct { - char key_quote[BUFFER_QUOTE_MAX_SIZE + 1]; - char value_quote[BUFFER_QUOTE_MAX_SIZE + 1]; - int8_t depth; - BUFFER_JSON_OPTIONS options; - BUFFER_JSON_NODE stack[BUFFER_JSON_MAX_DEPTH]; - } json; -} BUFFER; - -#define CLEAN_BUFFER _cleanup_(buffer_freep) BUFFER - -#define buffer_cacheable(wb) do { (wb)->options |= WB_CONTENT_CACHEABLE; if((wb)->options & WB_CONTENT_NO_CACHEABLE) (wb)->options &= ~WB_CONTENT_NO_CACHEABLE; } while(0) -#define buffer_no_cacheable(wb) do { (wb)->options |= WB_CONTENT_NO_CACHEABLE; if((wb)->options & WB_CONTENT_CACHEABLE) (wb)->options &= ~WB_CONTENT_CACHEABLE; (wb)->expires = 0; } while(0) - -#define buffer_strlen(wb) ((wb)->len) - -#define BUFFER_OVERFLOW_EOF "EOF" - -#ifdef NETDATA_INTERNAL_CHECKS -#define buffer_overflow_check(b) _buffer_overflow_check(b) -#else -#define buffer_overflow_check(b) -#endif - -static inline void _buffer_overflow_check(BUFFER *b) { - assert(b->len <= b->size && - "BUFFER: length is above buffer size."); - - assert(!(b->buffer && (b->buffer[b->size] != '\0' || strcmp(&b->buffer[b->size + 1], BUFFER_OVERFLOW_EOF) != 0)) && - "BUFFER: detected overflow."); -} - -static inline void buffer_flush(BUFFER *wb) { - wb->len = 0; - - wb->json.depth = 0; - wb->json.stack[0].type = BUFFER_JSON_EMPTY; - wb->json.stack[0].count = 0; - - if(wb->buffer) - wb->buffer[0] = '\0'; -} - -void buffer_reset(BUFFER *wb); - -void buffer_date(BUFFER *wb, int year, int month, int day, int hours, int minutes, int seconds); -void buffer_jsdate(BUFFER *wb, int year, int month, int day, int hours, int minutes, int seconds); - -BUFFER *buffer_create(size_t size, size_t *statistics); -void buffer_free(BUFFER *b); -void buffer_increase(BUFFER *b, size_t free_size_required); - -static inline void buffer_freep(BUFFER **bp) { - if(bp) buffer_free(*bp); -} - -void buffer_snprintf(BUFFER *wb, size_t len, const char *fmt, ...) PRINTFLIKE(3, 4); -void buffer_vsprintf(BUFFER *wb, const char *fmt, va_list args); -void buffer_sprintf(BUFFER *wb, const char *fmt, ...) PRINTFLIKE(2,3); -void buffer_strcat_htmlescape(BUFFER *wb, const char *txt); - -void buffer_char_replace(BUFFER *wb, char from, char to); - -void buffer_print_sn_flags(BUFFER *wb, SN_FLAGS flags, bool send_anomaly_bit); - -#ifdef ENABLE_H2O -h2o_iovec_t buffer_to_h2o_iovec(BUFFER *wb); -#endif - -static inline void buffer_need_bytes(BUFFER *buffer, size_t needed_free_size) { - if(unlikely(buffer->len + needed_free_size >= buffer->size)) - buffer_increase(buffer, needed_free_size + 1); -} - -void buffer_json_initialize(BUFFER *wb, const char *key_quote, const char *value_quote, int depth, - bool add_anonymous_object, BUFFER_JSON_OPTIONS options); - -void buffer_json_finalize(BUFFER *wb); - -static const char *buffer_tostring(BUFFER *wb) -{ - buffer_need_bytes(wb, 1); - wb->buffer[wb->len] = '\0'; - - buffer_overflow_check(wb); - - return(wb->buffer); -} - -static inline void _buffer_json_depth_push(BUFFER *wb, BUFFER_JSON_NODE_TYPE type) { -#ifdef NETDATA_INTERNAL_CHECKS - assert(wb->json.depth <= BUFFER_JSON_MAX_DEPTH && "BUFFER JSON: max nesting reached"); -#endif - wb->json.depth++; - wb->json.stack[wb->json.depth].count = 0; - wb->json.stack[wb->json.depth].type = type; -} - -static inline void _buffer_json_depth_pop(BUFFER *wb) { - wb->json.depth--; -} - -static inline void buffer_fast_charcat(BUFFER *wb, const char c) { - - buffer_need_bytes(wb, 2); - *(&wb->buffer[wb->len]) = c; - wb->len += 1; - wb->buffer[wb->len] = '\0'; - - buffer_overflow_check(wb); -} - -static inline void buffer_fast_rawcat(BUFFER *wb, const char *txt, size_t len) { - if(unlikely(!txt || !*txt || !len)) return; - - buffer_need_bytes(wb, len + 1); - - const char *t = txt; - const char *e = &txt[len]; - - char *d = &wb->buffer[wb->len]; - - while(t != e) - *d++ = *t++; - - wb->len += len; - wb->buffer[wb->len] = '\0'; - - buffer_overflow_check(wb); -} - -static inline void buffer_putc(BUFFER *wb, char c) { - buffer_need_bytes(wb, 2); - wb->buffer[wb->len++] = c; - wb->buffer[wb->len] = '\0'; - buffer_overflow_check(wb); -} - -static inline void buffer_fast_strcat(BUFFER *wb, const char *txt, size_t len) { - if(unlikely(!txt || !*txt || !len)) return; - - buffer_need_bytes(wb, len + 1); - - const char *t = txt; - const char *e = &txt[len]; - - char *d = &wb->buffer[wb->len]; - - while(t != e -#ifdef NETDATA_INTERNAL_CHECKS - && *t -#endif - ) - *d++ = *t++; - -#ifdef NETDATA_INTERNAL_CHECKS - assert(!(t != e && !*t) && "BUFFER: source string is shorter than the length given."); -#endif - - wb->len += len; - wb->buffer[wb->len] = '\0'; - - buffer_overflow_check(wb); -} - -static inline void buffer_strcat(BUFFER *wb, const char *txt) { - if(unlikely(!txt || !*txt)) return; - - const char *t = txt; - while(*t) { - buffer_need_bytes(wb, 100); - char *s = &wb->buffer[wb->len]; - char *d = s; - const char *e = &wb->buffer[wb->size]; - - while(*t && d < e) - *d++ = *t++; - - wb->len += d - s; - } - - buffer_need_bytes(wb, 1); - wb->buffer[wb->len] = '\0'; - - buffer_overflow_check(wb); -} - -static inline void buffer_contents_replace(BUFFER *wb, const char *txt, size_t len) { - wb->len = 0; - buffer_need_bytes(wb, len + 1); - - memcpy(wb->buffer, txt, len); - wb->len = len; - wb->buffer[wb->len] = '\0'; - - buffer_overflow_check(wb); -} - -static inline void buffer_strncat(BUFFER *wb, const char *txt, size_t len) { - if(unlikely(!txt || !*txt)) return; - - buffer_need_bytes(wb, len + 1); - - memcpy(&wb->buffer[wb->len], txt, len); - - wb->len += len; - wb->buffer[wb->len] = '\0'; - - buffer_overflow_check(wb); -} - -static inline void buffer_memcat(BUFFER *wb, const void *mem, size_t bytes) { - if(unlikely(!mem)) return; - - buffer_need_bytes(wb, bytes + 1); - - memcpy(&wb->buffer[wb->len], mem, bytes); - - wb->len += bytes; - wb->buffer[wb->len] = '\0'; - - buffer_overflow_check(wb); -} - -static inline void buffer_json_strcat(BUFFER *wb, const char *txt) { - if(unlikely(!txt || !*txt)) return; - - const unsigned char *t = (const unsigned char *)txt; - while(*t) { - buffer_need_bytes(wb, 110); - unsigned char *s = (unsigned char *)&wb->buffer[wb->len]; - unsigned char *d = s; - const unsigned char *e = (unsigned char *)&wb->buffer[wb->size - 10]; // make room for the max escape sequence - - while(*t && d < e) { -#ifdef BUFFER_JSON_ESCAPE_UTF - if(unlikely(IS_UTF8_STARTBYTE(*t) && IS_UTF8_BYTE(t[1]))) { - // UTF-8 multi-byte encoded character - - // find how big this character is (2-4 bytes) - size_t utf_character_size = 2; - while(utf_character_size < 4 && t[utf_character_size] && IS_UTF8_BYTE(t[utf_character_size]) && !IS_UTF8_STARTBYTE(t[utf_character_size])) - utf_character_size++; - - uint32_t code_point = 0; - for (size_t i = 0; i < utf_character_size; i++) { - code_point <<= 6; - code_point |= (t[i] & 0x3F); - } - - t += utf_character_size; - - // encode as \u escape sequence - *d++ = '\\'; - *d++ = 'u'; - *d++ = hex_digits[(code_point >> 12) & 0xf]; - *d++ = hex_digits[(code_point >> 8) & 0xf]; - *d++ = hex_digits[(code_point >> 4) & 0xf]; - *d++ = hex_digits[code_point & 0xf]; - } - else -#endif - if(unlikely(*t < ' ')) { - uint32_t v = *t++; - *d++ = '\\'; - *d++ = 'u'; - *d++ = hex_digits[(v >> 12) & 0xf]; - *d++ = hex_digits[(v >> 8) & 0xf]; - *d++ = hex_digits[(v >> 4) & 0xf]; - *d++ = hex_digits[v & 0xf]; - } - else { - if (unlikely(*t == '\\' || *t == '\"')) - *d++ = '\\'; - - *d++ = *t++; - } - } - - wb->len += d - s; - } - - buffer_need_bytes(wb, 1); - wb->buffer[wb->len] = '\0'; - - buffer_overflow_check(wb); -} - -static inline void buffer_json_quoted_strcat(BUFFER *wb, const char *txt) { - if(unlikely(!txt || !*txt)) return; - - if(*txt == '"') - txt++; - - const char *t = txt; - while(*t) { - buffer_need_bytes(wb, 100); - char *s = &wb->buffer[wb->len]; - char *d = s; - const char *e = &wb->buffer[wb->size - 1]; // remove 1 to make room for the escape character - - while(*t && d < e) { - if(unlikely(*t == '"' && !t[1])) { - t++; - continue; - } - - if(unlikely(*t == '\\' || *t == '"')) - *d++ = '\\'; - - *d++ = *t++; - } - - wb->len += d - s; - } - - buffer_need_bytes(wb, 1); - wb->buffer[wb->len] = '\0'; - - buffer_overflow_check(wb); -} - -// This trick seems to give an 80% speed increase in 32bit systems -// print_number_llu_r() will just print the digits up to the -// point the remaining value fits in 32 bits, and then calls -// print_number_lu_r() to print the rest with 32 bit arithmetic. - -static inline char *print_uint32_reversed(char *dst, uint32_t value) { - char *d = dst; - do *d++ = (char)('0' + (value % 10)); while((value /= 10)); - return d; -} - -static inline char *print_uint64_reversed(char *dst, uint64_t value) { -#ifdef ENV32BIT - if(value <= (uint64_t)0xffffffff) - return print_uint32_reversed(dst, value); - - char *d = dst; - do *d++ = (char)('0' + (value % 10)); while((value /= 10) && value > (uint64_t)0xffffffff); - if(value) return print_uint32_reversed(d, value); - return d; -#else - char *d = dst; - do *d++ = (char)('0' + (value % 10)); while((value /= 10)); - return d; -#endif -} - -static inline char *print_uint32_hex_reversed(char *dst, uint32_t value) { - static const char *digits = "0123456789ABCDEF"; - char *d = dst; - do *d++ = digits[value & 0xf]; while((value >>= 4)); - return d; -} - -static inline char *print_uint64_hex_reversed(char *dst, uint64_t value) { -#ifdef ENV32BIT - if(value <= (uint64_t)0xffffffff) - return print_uint32_hex_reversed(dst, value); - - char *d = dst; - do *d++ = hex_digits[value & 0xf]; while((value >>= 4) && value > (uint64_t)0xffffffff); - if(value) return print_uint32_hex_reversed(d, value); - return d; -#else - char *d = dst; - do *d++ = hex_digits[value & 0xf]; while((value >>= 4)); - return d; -#endif -} - -static inline char *print_uint64_base64_reversed(char *dst, uint64_t value) { - char *d = dst; - do *d++ = base64_digits[value & 63]; while ((value >>= 6)); - return d; -} - -static inline void char_array_reverse(char *from, char *to) { - // from and to are inclusive - char *begin = from, *end = to, aux; - while (end > begin) aux = *end, *end-- = *begin, *begin++ = aux; -} - -static inline int print_netdata_double(char *dst, NETDATA_DOUBLE value) { - char *s = dst; - - if(unlikely(value < 0)) { - *s++ = '-'; - value = fabsndd(value); - } - - uint64_t fractional_precision = 10000000ULL; // fractional part 7 digits - int fractional_wanted_digits = 7; - int exponent = 0; - if(unlikely(value >= (NETDATA_DOUBLE)(UINT64_MAX / 10))) { - // the number is too big to print using 64bit numbers - // so, let's convert it to exponential notation - exponent = (int)(floorndd(log10ndd(value))); - value /= powndd(10, exponent); - - // the max precision we can support is 18 digits - // (UINT64_MAX is 20, but the first is 1) - fractional_precision = 1000000000000000000ULL; // fractional part 18 digits - fractional_wanted_digits = 18; - } - - char *d = s; - NETDATA_DOUBLE integral_d, fractional_d; - fractional_d = modfndd(value, &integral_d); - - // get the integral and the fractional parts as 64-bit integers - uint64_t integral = (uint64_t)integral_d; - uint64_t fractional = (uint64_t)llrintndd(fractional_d * (NETDATA_DOUBLE)fractional_precision); - if(unlikely(fractional >= fractional_precision)) { - integral++; - fractional -= fractional_precision; - } - - // convert the integral part to string (reversed) - d = print_uint64_reversed(d, integral); - char_array_reverse(s, d - 1); // copy reversed the integral string - - if(likely(fractional != 0)) { - *d++ = '.'; // add the dot - - // convert the fractional part to string (reversed) - d = print_uint64_reversed(s = d, fractional); - - while(d - s < fractional_wanted_digits) *d++ = '0'; // prepend zeros to reach precision - char_array_reverse(s, d - 1); // copy reversed the fractional string - - // remove trailing zeros from the fractional part - while(*(d - 1) == '0') d--; - } - - if(unlikely(exponent != 0)) { - *d++ = 'e'; - *d++ = '+'; - d = print_uint32_reversed(s = d, exponent); - char_array_reverse(s, d - 1); - } - - *d = '\0'; - return (int)(d - dst); -} - -static inline void buffer_print_uint64(BUFFER *wb, uint64_t value) { - buffer_need_bytes(wb, 50); - - char *s = &wb->buffer[wb->len]; - char *d = print_uint64_reversed(s, value); - char_array_reverse(s, d - 1); - *d = '\0'; - wb->len += d - s; - - buffer_overflow_check(wb); -} - -static inline void buffer_print_int64(BUFFER *wb, int64_t value) { - buffer_need_bytes(wb, 50); - - if(value < 0) { - buffer_fast_strcat(wb, "-", 1); - value = -value; - } - - buffer_print_uint64(wb, (uint64_t)value); - - buffer_overflow_check(wb); -} - -static inline void buffer_print_uint64_hex(BUFFER *wb, uint64_t value) { - buffer_need_bytes(wb, sizeof(uint64_t) * 2 + 2 + 1); - - buffer_fast_strcat(wb, HEX_PREFIX, sizeof(HEX_PREFIX) - 1); - - char *s = &wb->buffer[wb->len]; - char *d = print_uint64_hex_reversed(s, value); - char_array_reverse(s, d - 1); - *d = '\0'; - wb->len += d - s; - - buffer_overflow_check(wb); -} - -static inline void buffer_print_uint64_base64(BUFFER *wb, uint64_t value) { - buffer_need_bytes(wb, sizeof(uint64_t) * 2 + 2 + 1); - - buffer_fast_strcat(wb, IEEE754_UINT64_B64_PREFIX, sizeof(IEEE754_UINT64_B64_PREFIX) - 1); - - char *s = &wb->buffer[wb->len]; - char *d = print_uint64_base64_reversed(s, value); - char_array_reverse(s, d - 1); - *d = '\0'; - wb->len += d - s; - - buffer_overflow_check(wb); -} - -static inline void buffer_print_int64_hex(BUFFER *wb, int64_t value) { - buffer_need_bytes(wb, 2); - - if(value < 0) { - buffer_fast_strcat(wb, "-", 1); - value = -value; - } - - buffer_print_uint64_hex(wb, (uint64_t)value); - - buffer_overflow_check(wb); -} - -static inline void buffer_print_int64_base64(BUFFER *wb, int64_t value) { - buffer_need_bytes(wb, 2); - - if(value < 0) { - buffer_fast_strcat(wb, "-", 1); - value = -value; - } - - buffer_print_uint64_base64(wb, (uint64_t)value); - - buffer_overflow_check(wb); -} - -static inline void buffer_print_netdata_double(BUFFER *wb, NETDATA_DOUBLE value) { - buffer_need_bytes(wb, 512 + 2); - - if(isnan(value) || isinf(value)) { - buffer_fast_strcat(wb, "null", 4); - return; - } - else - wb->len += print_netdata_double(&wb->buffer[wb->len], value); - - // terminate it - buffer_need_bytes(wb, 1); - wb->buffer[wb->len] = '\0'; - - buffer_overflow_check(wb); -} - -static inline void buffer_print_netdata_double_hex(BUFFER *wb, NETDATA_DOUBLE value) { - buffer_need_bytes(wb, sizeof(uint64_t) * 2 + 2 + 1 + 1); - - uint64_t *ptr = (uint64_t *) (&value); - buffer_fast_strcat(wb, IEEE754_DOUBLE_HEX_PREFIX, sizeof(IEEE754_DOUBLE_HEX_PREFIX) - 1); - - char *s = &wb->buffer[wb->len]; - char *d = print_uint64_hex_reversed(s, *ptr); - char_array_reverse(s, d - 1); - *d = '\0'; - wb->len += d - s; - - buffer_overflow_check(wb); -} - -static inline void buffer_print_netdata_double_base64(BUFFER *wb, NETDATA_DOUBLE value) { - buffer_need_bytes(wb, sizeof(uint64_t) * 2 + 2 + 1 + 1); - - uint64_t *ptr = (uint64_t *) (&value); - buffer_fast_strcat(wb, IEEE754_DOUBLE_B64_PREFIX, sizeof(IEEE754_DOUBLE_B64_PREFIX) - 1); - - char *s = &wb->buffer[wb->len]; - char *d = print_uint64_base64_reversed(s, *ptr); - char_array_reverse(s, d - 1); - *d = '\0'; - wb->len += d - s; - - buffer_overflow_check(wb); -} - -typedef enum { - NUMBER_ENCODING_DECIMAL, - NUMBER_ENCODING_HEX, - NUMBER_ENCODING_BASE64, -} NUMBER_ENCODING; - -static inline void buffer_print_int64_encoded(BUFFER *wb, NUMBER_ENCODING encoding, int64_t value) { - if(encoding == NUMBER_ENCODING_BASE64) - return buffer_print_int64_base64(wb, value); - - if(encoding == NUMBER_ENCODING_HEX) - return buffer_print_int64_hex(wb, value); - - return buffer_print_int64(wb, value); -} - -static inline void buffer_print_uint64_encoded(BUFFER *wb, NUMBER_ENCODING encoding, uint64_t value) { - if(encoding == NUMBER_ENCODING_BASE64) - return buffer_print_uint64_base64(wb, value); - - if(encoding == NUMBER_ENCODING_HEX) - return buffer_print_uint64_hex(wb, value); - - return buffer_print_uint64(wb, value); -} - -static inline void buffer_print_netdata_double_encoded(BUFFER *wb, NUMBER_ENCODING encoding, NETDATA_DOUBLE value) { - if(encoding == NUMBER_ENCODING_BASE64) - return buffer_print_netdata_double_base64(wb, value); - - if(encoding == NUMBER_ENCODING_HEX) - return buffer_print_netdata_double_hex(wb, value); - - return buffer_print_netdata_double(wb, value); -} - -static inline void buffer_print_spaces(BUFFER *wb, size_t spaces) { - buffer_need_bytes(wb, spaces * 4 + 1); - - char *d = &wb->buffer[wb->len]; - for(size_t i = 0; i < spaces; i++) { - *d++ = ' '; - *d++ = ' '; - *d++ = ' '; - *d++ = ' '; - } - - *d = '\0'; - wb->len += spaces * 4; - - buffer_overflow_check(wb); -} - -static inline void buffer_print_json_comma(BUFFER *wb) { - if(wb->json.stack[wb->json.depth].count) - buffer_fast_strcat(wb, ",", 1); -} - -static inline void buffer_print_json_comma_newline_spacing(BUFFER *wb) { - buffer_print_json_comma(wb); - - if((wb->json.options & BUFFER_JSON_OPTIONS_MINIFY) || - (wb->json.stack[wb->json.depth].type == BUFFER_JSON_ARRAY && !(wb->json.options & BUFFER_JSON_OPTIONS_NEWLINE_ON_ARRAY_ITEMS))) - return; - - buffer_fast_strcat(wb, "\n", 1); - buffer_print_spaces(wb, wb->json.depth + 1); -} - -static inline void buffer_print_json_key(BUFFER *wb, const char *key) { - buffer_strcat(wb, wb->json.key_quote); - buffer_json_strcat(wb, key); - buffer_strcat(wb, wb->json.key_quote); -} - -static inline void buffer_json_add_string_value(BUFFER *wb, const char *value) { - if(value) { - buffer_strcat(wb, wb->json.value_quote); - buffer_json_strcat(wb, value); - buffer_strcat(wb, wb->json.value_quote); - } - else - buffer_fast_strcat(wb, "null", 4); -} - -static inline void buffer_json_add_quoted_string_value(BUFFER *wb, const char *value) { - if(value) { - buffer_strcat(wb, wb->json.value_quote); - buffer_json_quoted_strcat(wb, value); - buffer_strcat(wb, wb->json.value_quote); - } - else - buffer_fast_strcat(wb, "null", 4); -} - -static inline void buffer_json_member_add_object(BUFFER *wb, const char *key) { - buffer_print_json_comma_newline_spacing(wb); - buffer_print_json_key(wb, key); - buffer_fast_strcat(wb, ":{", 2); - wb->json.stack[wb->json.depth].count++; - - _buffer_json_depth_push(wb, BUFFER_JSON_OBJECT); -} - -static inline void buffer_json_object_close(BUFFER *wb) { -#ifdef NETDATA_INTERNAL_CHECKS - assert(wb->json.depth >= 0 && "BUFFER JSON: nothing is open to close it"); - assert(wb->json.stack[wb->json.depth].type == BUFFER_JSON_OBJECT && "BUFFER JSON: an object is not open to close it"); -#endif - if(!(wb->json.options & BUFFER_JSON_OPTIONS_MINIFY)) { - buffer_fast_strcat(wb, "\n", 1); - buffer_print_spaces(wb, wb->json.depth); - } - buffer_fast_strcat(wb, "}", 1); - _buffer_json_depth_pop(wb); -} - -static inline void buffer_json_member_add_string(BUFFER *wb, const char *key, const char *value) { - buffer_print_json_comma_newline_spacing(wb); - buffer_print_json_key(wb, key); - buffer_fast_strcat(wb, ":", 1); - buffer_json_add_string_value(wb, value); - - wb->json.stack[wb->json.depth].count++; -} - -static inline void buffer_json_member_add_string_or_omit(BUFFER *wb, const char *key, const char *value) { - if(value && *value) - buffer_json_member_add_string(wb, key, value); -} - -static inline void buffer_json_member_add_string_or_empty(BUFFER *wb, const char *key, const char *value) { - if(!value) - value = ""; - - buffer_json_member_add_string(wb, key, value); -} - -static inline void buffer_json_member_add_quoted_string(BUFFER *wb, const char *key, const char *value) { - buffer_print_json_comma_newline_spacing(wb); - buffer_print_json_key(wb, key); - buffer_fast_strcat(wb, ":", 1); - - if(!value || strcmp(value, "null") == 0) - buffer_fast_strcat(wb, "null", 4); - else - buffer_json_add_quoted_string_value(wb, value); - - wb->json.stack[wb->json.depth].count++; -} - -static inline void buffer_json_member_add_uuid(BUFFER *wb, const char *key, uuid_t *value) { - buffer_print_json_comma_newline_spacing(wb); - buffer_print_json_key(wb, key); - buffer_fast_strcat(wb, ":", 1); - - if(value && !uuid_is_null(*value)) { - char uuid[GUID_LEN + 1]; - uuid_unparse_lower(*value, uuid); - buffer_json_add_string_value(wb, uuid); - } - else - buffer_json_add_string_value(wb, NULL); - - wb->json.stack[wb->json.depth].count++; -} - -static inline void buffer_json_member_add_boolean(BUFFER *wb, const char *key, bool value) { - buffer_print_json_comma_newline_spacing(wb); - buffer_print_json_key(wb, key); - buffer_fast_strcat(wb, ":", 1); - buffer_strcat(wb, value?"true":"false"); - - wb->json.stack[wb->json.depth].count++; -} - -static inline void buffer_json_member_add_array(BUFFER *wb, const char *key) { - buffer_print_json_comma_newline_spacing(wb); - if (key) { - buffer_print_json_key(wb, key); - buffer_fast_strcat(wb, ":[", 2); - } - else - buffer_fast_strcat(wb, "[", 1); - - wb->json.stack[wb->json.depth].count++; - - _buffer_json_depth_push(wb, BUFFER_JSON_ARRAY); -} - -static inline void buffer_json_add_array_item_array(BUFFER *wb) { - if(!(wb->json.options & BUFFER_JSON_OPTIONS_MINIFY) && wb->json.stack[wb->json.depth].type == BUFFER_JSON_ARRAY) { - // an array inside another array - buffer_print_json_comma(wb); - buffer_fast_strcat(wb, "\n", 1); - buffer_print_spaces(wb, wb->json.depth + 1); - } - else - buffer_print_json_comma_newline_spacing(wb); - - buffer_fast_strcat(wb, "[", 1); - wb->json.stack[wb->json.depth].count++; - - _buffer_json_depth_push(wb, BUFFER_JSON_ARRAY); -} - -static inline void buffer_json_add_array_item_string(BUFFER *wb, const char *value) { - buffer_print_json_comma_newline_spacing(wb); - - buffer_json_add_string_value(wb, value); - wb->json.stack[wb->json.depth].count++; -} - -static inline void buffer_json_add_array_item_double(BUFFER *wb, NETDATA_DOUBLE value) { - buffer_print_json_comma_newline_spacing(wb); - - buffer_print_netdata_double(wb, value); - wb->json.stack[wb->json.depth].count++; -} - -static inline void buffer_json_add_array_item_int64(BUFFER *wb, int64_t value) { - buffer_print_json_comma_newline_spacing(wb); - - buffer_print_int64(wb, value); - wb->json.stack[wb->json.depth].count++; -} - -static inline void buffer_json_add_array_item_uint64(BUFFER *wb, uint64_t value) { - buffer_print_json_comma_newline_spacing(wb); - - buffer_print_uint64(wb, value); - wb->json.stack[wb->json.depth].count++; -} - -static inline void buffer_json_add_array_item_boolean(BUFFER *wb, bool value) { - buffer_print_json_comma_newline_spacing(wb); - - buffer_strcat(wb, value ? "true" : "false"); - wb->json.stack[wb->json.depth].count++; -} - -static inline void buffer_json_add_array_item_time_t(BUFFER *wb, time_t value) { - buffer_print_json_comma_newline_spacing(wb); - - buffer_print_int64(wb, value); - wb->json.stack[wb->json.depth].count++; -} - -static inline void buffer_json_add_array_item_time_ms(BUFFER *wb, time_t value) { - buffer_print_json_comma_newline_spacing(wb); - - buffer_print_int64(wb, value); - buffer_fast_strcat(wb, "000", 3); - wb->json.stack[wb->json.depth].count++; -} - -static inline void buffer_json_add_array_item_time_t2ms(BUFFER *wb, time_t value) { - buffer_print_json_comma_newline_spacing(wb); - - buffer_print_int64(wb, value); - buffer_fast_strcat(wb, "000", 3); - wb->json.stack[wb->json.depth].count++; -} - -static inline void buffer_json_add_array_item_object(BUFFER *wb) { - buffer_print_json_comma_newline_spacing(wb); - - buffer_fast_strcat(wb, "{", 1); - wb->json.stack[wb->json.depth].count++; - - _buffer_json_depth_push(wb, BUFFER_JSON_OBJECT); -} - -static inline void buffer_json_member_add_time_t(BUFFER *wb, const char *key, time_t value) { - buffer_print_json_comma_newline_spacing(wb); - buffer_print_json_key(wb, key); - buffer_fast_strcat(wb, ":", 1); - buffer_print_int64(wb, value); - - wb->json.stack[wb->json.depth].count++; -} - -static inline void buffer_json_member_add_time_t2ms(BUFFER *wb, const char *key, time_t value) { - buffer_print_json_comma_newline_spacing(wb); - buffer_print_json_key(wb, key); - buffer_fast_strcat(wb, ":", 1); - buffer_print_int64(wb, value); - buffer_fast_strcat(wb, "000", 3); - - wb->json.stack[wb->json.depth].count++; -} - -static inline void buffer_json_member_add_uint64(BUFFER *wb, const char *key, uint64_t value) { - buffer_print_json_comma_newline_spacing(wb); - buffer_print_json_key(wb, key); - buffer_fast_strcat(wb, ":", 1); - buffer_print_uint64(wb, value); - - wb->json.stack[wb->json.depth].count++; -} - -static inline void buffer_json_member_add_int64(BUFFER *wb, const char *key, int64_t value) { - buffer_print_json_comma_newline_spacing(wb); - buffer_print_json_key(wb, key); - buffer_fast_strcat(wb, ":", 1); - buffer_print_int64(wb, value); - - wb->json.stack[wb->json.depth].count++; -} - -static inline void buffer_json_member_add_double(BUFFER *wb, const char *key, NETDATA_DOUBLE value) { - buffer_print_json_comma_newline_spacing(wb); - buffer_print_json_key(wb, key); - buffer_fast_strcat(wb, ":", 1); - buffer_print_netdata_double(wb, value); - - wb->json.stack[wb->json.depth].count++; -} - -static inline void buffer_json_array_close(BUFFER *wb) { -#ifdef NETDATA_INTERNAL_CHECKS - assert(wb->json.depth >= 0 && "BUFFER JSON: nothing is open to close it"); - assert(wb->json.stack[wb->json.depth].type == BUFFER_JSON_ARRAY && "BUFFER JSON: an array is not open to close it"); -#endif - if(wb->json.options & BUFFER_JSON_OPTIONS_NEWLINE_ON_ARRAY_ITEMS) { - buffer_fast_strcat(wb, "\n", 1); - buffer_print_spaces(wb, wb->json.depth); - } - - buffer_fast_strcat(wb, "]", 1); - _buffer_json_depth_pop(wb); -} - -typedef enum __attribute__((packed)) { - RRDF_FIELD_OPTS_NONE = 0, - RRDF_FIELD_OPTS_UNIQUE_KEY = (1 << 0), // the field is the unique key of the row - RRDF_FIELD_OPTS_VISIBLE = (1 << 1), // the field should be visible by default - RRDF_FIELD_OPTS_STICKY = (1 << 2), // the field should be sticky - RRDF_FIELD_OPTS_FULL_WIDTH = (1 << 3), // the field should get full width - RRDF_FIELD_OPTS_WRAP = (1 << 4), // the field should wrap - RRDF_FIELD_OPTS_DUMMY = (1 << 5), // not a presentable field - RRDF_FIELD_OPTS_EXPANDED_FILTER = (1 << 6), // show the filter expanded -} RRDF_FIELD_OPTIONS; - -typedef enum __attribute__((packed)) { - RRDF_FIELD_TYPE_NONE, - RRDF_FIELD_TYPE_INTEGER, - RRDF_FIELD_TYPE_BOOLEAN, - RRDF_FIELD_TYPE_STRING, - RRDF_FIELD_TYPE_DETAIL_STRING, - RRDF_FIELD_TYPE_BAR_WITH_INTEGER, - RRDF_FIELD_TYPE_DURATION, - RRDF_FIELD_TYPE_TIMESTAMP, - RRDF_FIELD_TYPE_ARRAY, -} RRDF_FIELD_TYPE; - -static inline const char *rrdf_field_type_to_string(RRDF_FIELD_TYPE type) { - switch(type) { - default: - case RRDF_FIELD_TYPE_NONE: - return "none"; - - case RRDF_FIELD_TYPE_INTEGER: - return "integer"; - - case RRDF_FIELD_TYPE_BOOLEAN: - return "boolean"; - - case RRDF_FIELD_TYPE_STRING: - return "string"; - - case RRDF_FIELD_TYPE_DETAIL_STRING: - return "detail-string"; - - case RRDF_FIELD_TYPE_BAR_WITH_INTEGER: - return "bar-with-integer"; - - case RRDF_FIELD_TYPE_DURATION: - return "duration"; - - case RRDF_FIELD_TYPE_TIMESTAMP: - return "timestamp"; - - case RRDF_FIELD_TYPE_ARRAY: - return "array"; - } -} - -typedef enum __attribute__((packed)) { - RRDF_FIELD_VISUAL_VALUE, // show the value, possibly applying a transformation - RRDF_FIELD_VISUAL_BAR, // show the value and a bar, respecting the max field to fill the bar at 100% - RRDF_FIELD_VISUAL_PILL, // - RRDF_FIELD_VISUAL_RICH, // - RRDR_FIELD_VISUAL_ROW_OPTIONS, // this is a dummy column that is used for row options -} RRDF_FIELD_VISUAL; - -static inline const char *rrdf_field_visual_to_string(RRDF_FIELD_VISUAL visual) { - switch(visual) { - default: - case RRDF_FIELD_VISUAL_VALUE: - return "value"; - - case RRDF_FIELD_VISUAL_BAR: - return "bar"; - - case RRDF_FIELD_VISUAL_PILL: - return "pill"; - - case RRDF_FIELD_VISUAL_RICH: - return "richValue"; - - case RRDR_FIELD_VISUAL_ROW_OPTIONS: - return "rowOptions"; - } -} - -typedef enum __attribute__((packed)) { - RRDF_FIELD_TRANSFORM_NONE, // show the value as-is - RRDF_FIELD_TRANSFORM_NUMBER, // show the value respecting the decimal_points - RRDF_FIELD_TRANSFORM_DURATION_S, // transform as duration in second to a human-readable duration - RRDF_FIELD_TRANSFORM_DATETIME_MS, // UNIX epoch timestamp in ms - RRDF_FIELD_TRANSFORM_DATETIME_USEC, // UNIX epoch timestamp in usec -} RRDF_FIELD_TRANSFORM; - -static inline const char *rrdf_field_transform_to_string(RRDF_FIELD_TRANSFORM transform) { - switch(transform) { - default: - case RRDF_FIELD_TRANSFORM_NONE: - return "none"; - - case RRDF_FIELD_TRANSFORM_NUMBER: - return "number"; - - case RRDF_FIELD_TRANSFORM_DURATION_S: - return "duration"; - - case RRDF_FIELD_TRANSFORM_DATETIME_MS: - return "datetime"; - - case RRDF_FIELD_TRANSFORM_DATETIME_USEC: - return "datetime_usec"; - } -} - -typedef enum __attribute__((packed)) { - RRDF_FIELD_SORT_ASCENDING = (1 << 0), - RRDF_FIELD_SORT_DESCENDING = (1 << 1), - - RRDF_FIELD_SORT_FIXED = (1 << 7), -} RRDF_FIELD_SORT; - -static inline const char *rrdf_field_sort_to_string(RRDF_FIELD_SORT sort) { - if(sort & RRDF_FIELD_SORT_DESCENDING) - return "descending"; - - else - return "ascending"; -} - -typedef enum __attribute__((packed)) { - RRDF_FIELD_SUMMARY_UNIQUECOUNT, // Finds the number of unique values of a group of rows - RRDF_FIELD_SUMMARY_SUM, // Sums the values of a group of rows - RRDF_FIELD_SUMMARY_MIN, // Finds the minimum value of a group of rows - RRDF_FIELD_SUMMARY_MAX, // Finds the maximum value of a group of rows - // RRDF_FIELD_SUMMARY_EXTENT, // Finds the minimum and maximum values of a group of rows - RRDF_FIELD_SUMMARY_MEAN, // Finds the mean/average value of a group of rows - RRDF_FIELD_SUMMARY_MEDIAN, // Finds the median value of a group of rows - // RRDF_FIELD_SUMMARY_UNIQUE, // Finds the unique values of a group of rows - RRDF_FIELD_SUMMARY_COUNT, // Calculates the number of rows in a group -} RRDF_FIELD_SUMMARY; - -static inline const char *rrdf_field_summary_to_string(RRDF_FIELD_SUMMARY summary) { - switch(summary) { - default: - case RRDF_FIELD_SUMMARY_COUNT: - return "count"; - - case RRDF_FIELD_SUMMARY_UNIQUECOUNT: - return "uniqueCount"; - - case RRDF_FIELD_SUMMARY_SUM: - return "sum"; - - case RRDF_FIELD_SUMMARY_MIN: - return "min"; - - case RRDF_FIELD_SUMMARY_MEAN: - return "mean"; - - case RRDF_FIELD_SUMMARY_MEDIAN: - return "median"; - - case RRDF_FIELD_SUMMARY_MAX: - return "max"; - } -} - -typedef enum __attribute__((packed)) { - RRDF_FIELD_FILTER_NONE = 0, - RRDF_FIELD_FILTER_RANGE, - RRDF_FIELD_FILTER_MULTISELECT, - RRDF_FIELD_FILTER_FACET, -} RRDF_FIELD_FILTER; - -static inline const char *rrdf_field_filter_to_string(RRDF_FIELD_FILTER filter) { - switch(filter) { - case RRDF_FIELD_FILTER_RANGE: - return "range"; - - case RRDF_FIELD_FILTER_MULTISELECT: - return "multiselect"; - - case RRDF_FIELD_FILTER_FACET: - return "facet"; - - default: - case RRDF_FIELD_FILTER_NONE: - return "none"; - } -} - -static inline void -buffer_rrdf_table_add_field(BUFFER *wb, size_t field_id, const char *key, const char *name, RRDF_FIELD_TYPE type, - RRDF_FIELD_VISUAL visual, RRDF_FIELD_TRANSFORM transform, size_t decimal_points, - const char *units, NETDATA_DOUBLE max, RRDF_FIELD_SORT sort, const char *pointer_to, - RRDF_FIELD_SUMMARY summary, RRDF_FIELD_FILTER filter, RRDF_FIELD_OPTIONS options, - const char *default_value) { - - buffer_json_member_add_object(wb, key); - { - buffer_json_member_add_uint64(wb, "index", field_id); - buffer_json_member_add_boolean(wb, "unique_key", options & RRDF_FIELD_OPTS_UNIQUE_KEY); - buffer_json_member_add_string(wb, "name", name); - buffer_json_member_add_boolean(wb, "visible", options & RRDF_FIELD_OPTS_VISIBLE); - buffer_json_member_add_string(wb, "type", rrdf_field_type_to_string(type)); - buffer_json_member_add_string_or_omit(wb, "units", units); - buffer_json_member_add_string(wb, "visualization", rrdf_field_visual_to_string(visual)); - - buffer_json_member_add_object(wb, "value_options"); - { - buffer_json_member_add_string_or_omit(wb, "units", units); - buffer_json_member_add_string(wb, "transform", rrdf_field_transform_to_string(transform)); - buffer_json_member_add_uint64(wb, "decimal_points", decimal_points); - buffer_json_member_add_string(wb, "default_value", default_value); - } - buffer_json_object_close(wb); - - if (!isnan((NETDATA_DOUBLE) (max))) - buffer_json_member_add_double(wb, "max", (NETDATA_DOUBLE) (max)); - - buffer_json_member_add_string_or_omit(wb, "pointer_to", pointer_to); - buffer_json_member_add_string(wb, "sort", rrdf_field_sort_to_string(sort)); - buffer_json_member_add_boolean(wb, "sortable", !(sort & RRDF_FIELD_SORT_FIXED)); - buffer_json_member_add_boolean(wb, "sticky", options & RRDF_FIELD_OPTS_STICKY); - buffer_json_member_add_string(wb, "summary", rrdf_field_summary_to_string(summary)); - buffer_json_member_add_string(wb, "filter", rrdf_field_filter_to_string(filter)); - - buffer_json_member_add_boolean(wb, "full_width", options & RRDF_FIELD_OPTS_FULL_WIDTH); - buffer_json_member_add_boolean(wb, "wrap", options & RRDF_FIELD_OPTS_WRAP); - buffer_json_member_add_boolean(wb, "default_expanded_filter", options & RRDF_FIELD_OPTS_EXPANDED_FILTER); - - if(options & RRDF_FIELD_OPTS_DUMMY) - buffer_json_member_add_boolean(wb, "dummy", true); - } - buffer_json_object_close(wb); -} - -#endif /* NETDATA_WEB_BUFFER_H */ |