diff options
Diffstat (limited to 'libnetdata/buffer/buffer.c')
-rw-r--r-- | libnetdata/buffer/buffer.c | 466 |
1 files changed, 219 insertions, 247 deletions
diff --git a/libnetdata/buffer/buffer.c b/libnetdata/buffer/buffer.c index eeb28320..142fbca1 100644 --- a/libnetdata/buffer/buffer.c +++ b/libnetdata/buffer/buffer.c @@ -2,39 +2,16 @@ #include "../libnetdata.h" -#define BUFFER_OVERFLOW_EOF "EOF" - static inline void buffer_overflow_init(BUFFER *b) { b->buffer[b->size] = '\0'; strcpy(&b->buffer[b->size + 1], BUFFER_OVERFLOW_EOF); } -#ifdef NETDATA_INTERNAL_CHECKS -#define buffer_overflow_check(b) _buffer_overflow_check(b, __FILE__, __FUNCTION__, __LINE__) -#else -#define buffer_overflow_check(b) -#endif - -static inline void _buffer_overflow_check(BUFFER *b, const char *file, const char *function, const unsigned long line) -{ - if(b->len > b->size) { - error("BUFFER: length %zu is above size %zu, at line %lu, at function %s() of file '%s'.", b->len, b->size, line, function, file); - b->len = b->size; - } - - if(b->buffer[b->size] != '\0' || strcmp(&b->buffer[b->size + 1], BUFFER_OVERFLOW_EOF) != 0) { - error("BUFFER: detected overflow at line %lu, at function %s() of file '%s'.", line, function, file); - buffer_overflow_init(b); - } -} - - -void buffer_reset(BUFFER *wb) -{ +void buffer_reset(BUFFER *wb) { buffer_flush(wb); - wb->contenttype = CT_TEXT_PLAIN; + wb->content_type = CT_TEXT_PLAIN; wb->options = 0; wb->date = 0; wb->expires = 0; @@ -52,8 +29,7 @@ const char *buffer_tostring(BUFFER *wb) return(wb->buffer); } -void buffer_char_replace(BUFFER *wb, char from, char to) -{ +void buffer_char_replace(BUFFER *wb, char from, char to) { char *s = wb->buffer, *end = &wb->buffer[wb->len]; while(s != end) { @@ -64,212 +40,25 @@ void buffer_char_replace(BUFFER *wb, char from, char to) 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. - -inline char *print_number_lu_r(char *str, unsigned long uvalue) { - char *wstr = str; - - // print each digit - do *wstr++ = (char)('0' + (uvalue % 10)); while(uvalue /= 10); - return wstr; -} - -inline char *print_number_llu_r(char *str, unsigned long long uvalue) { - char *wstr = str; - - // print each digit - do *wstr++ = (char)('0' + (uvalue % 10)); while((uvalue /= 10) && uvalue > (unsigned long long)0xffffffff); - if(uvalue) return print_number_lu_r(wstr, uvalue); - return wstr; -} - -inline char *print_number_llu_r_smart(char *str, unsigned long long uvalue) { - switch (sizeof(void *)) { - case 4: - str = (uvalue > (unsigned long long) 0xffffffff) ? print_number_llu_r(str, uvalue) : - print_number_lu_r(str, uvalue); - break; - case 8: - do { - *str++ = (char) ('0' + (uvalue % 10)); - } while (uvalue /= 10); - break; - default: - fatal("Netdata supports only 32-bit & 64-bit systems."); - } - - return str; -} - -void buffer_print_llu(BUFFER *wb, unsigned long long uvalue) -{ - buffer_need_bytes(wb, 50); - - char *str = &wb->buffer[wb->len]; - char *wstr = str; - - switch (sizeof(void *)) { - case 4: - wstr = (uvalue > (unsigned long long) 0xffffffff) ? print_number_llu_r(wstr, uvalue) : - print_number_lu_r(wstr, uvalue); - break; - case 8: - do { - *wstr++ = (char) ('0' + (uvalue % 10)); - } while (uvalue /= 10); - break; - default: - fatal("Netdata supports only 32-bit & 64-bit systems."); +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; } - // terminate it - *wstr = '\0'; - - // reverse it - char *begin = str, *end = wstr - 1, aux; - while (end > begin) aux = *end, *end-- = *begin, *begin++ = aux; - - // return the buffer length - wb->len += wstr - str; -} - -void buffer_print_ll(BUFFER *wb, long long value) -{ - buffer_need_bytes(wb, 50); - - if(value < 0) { - buffer_fast_strcat(wb, "-", 1); - value = -value; + size_t printed = 0; + if(likely(send_anomaly_bit && (flags & SN_FLAG_NOT_ANOMALOUS))) { + buffer_fast_strcat(wb, "A", 1); + printed++; } - buffer_print_llu(wb, value); -} - -static unsigned char bits03_to_hex[16] = { - [0] = '0', - [1] = '1', - [2] = '2', - [3] = '3', - [4] = '4', - [5] = '5', - [6] = '6', - [7] = '7', - [8] = '8', - [9] = '9', - [10] = 'A', - [11] = 'B', - [12] = 'C', - [13] = 'D', - [14] = 'E', - [15] = 'F' -}; - -void buffer_print_llu_hex(BUFFER *wb, unsigned long long value) -{ - unsigned char buffer[sizeof(unsigned long long) * 2 + 2 + 1]; // 8 bytes * 2 + '0x' + '\0' - unsigned char *e = &buffer[sizeof(unsigned long long) * 2 + 2]; - unsigned char *p = e; - - *p-- = '\0'; - *p-- = bits03_to_hex[value & 0xF]; - value >>= 4; - if(value) { - *p-- = bits03_to_hex[value & 0xF]; - value >>= 4; - - while(value) { - *p-- = bits03_to_hex[value & 0xF]; - value >>= 4; - - if(value) { - *p-- = bits03_to_hex[value & 0xF]; - value >>= 4; - } - } + if(unlikely(flags & SN_FLAG_RESET)) { + buffer_fast_strcat(wb, "R", 1); + printed++; } - *p-- = 'x'; - *p = '0'; - buffer_fast_strcat(wb, (char *)p, e - p); -} - -void buffer_fast_strcat(BUFFER *wb, const char *txt, size_t len) { - if(unlikely(!txt || !*txt)) return; - - buffer_need_bytes(wb, len + 1); - - char *s = &wb->buffer[wb->len]; - const char *end = &txt[len + 1]; - - while(txt != end) - *s++ = *txt++; - - wb->len += len; - - // keep it NULL terminating - // not counting it at wb->len - wb->buffer[wb->len] = '\0'; -} - -void buffer_strcat(BUFFER *wb, const char *txt) -{ - // buffer_sprintf(wb, "%s", txt); - - if(unlikely(!txt || !*txt)) return; - - buffer_need_bytes(wb, 1); - - char *s = &wb->buffer[wb->len], *start, *end = &wb->buffer[wb->size]; - size_t len = wb->len; - - start = s; - while(*txt && s != end) - *s++ = *txt++; - - len += s - start; - - wb->len = len; - buffer_overflow_check(wb); - - if(*txt) { - debug(D_WEB_BUFFER, "strcat(): increasing web_buffer at position %zu, size = %zu\n", wb->len, wb->size); - len = strlen(txt); - buffer_fast_strcat(wb, txt, len); - } - else { - // terminate the string - // without increasing the length - buffer_need_bytes(wb, (size_t)1); - wb->buffer[wb->len] = '\0'; - } -} - -void buffer_strcat_jsonescape(BUFFER *wb, const char *txt) -{ - while(*txt) { - switch(*txt) { - case '\\': - buffer_need_bytes(wb, 2); - wb->buffer[wb->len++] = '\\'; - wb->buffer[wb->len++] = '\\'; - break; - case '"': - buffer_need_bytes(wb, 2); - wb->buffer[wb->len++] = '\\'; - wb->buffer[wb->len++] = '"'; - break; - default: { - buffer_need_bytes(wb, 1); - wb->buffer[wb->len++] = *txt; - } - } - txt++; - } - - buffer_overflow_check(wb); + if(!printed) + buffer_fast_strcat(wb, "''", 2); } void buffer_strcat_htmlescape(BUFFER *wb, const char *txt) @@ -358,25 +147,6 @@ void buffer_sprintf(BUFFER *wb, const char *fmt, ...) // the buffer is \0 terminated by vsnprintf } - -void buffer_rrd_value(BUFFER *wb, NETDATA_DOUBLE value) -{ - buffer_need_bytes(wb, 50); - - if(isnan(value) || isinf(value)) { - buffer_strcat(wb, "null"); - 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); -} - // 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) { @@ -482,7 +252,7 @@ BUFFER *buffer_create(size_t size, size_t *statistics) b->buffer = mallocz(size + sizeof(BUFFER_OVERFLOW_EOF) + 2); b->buffer[0] = '\0'; b->size = size; - b->contenttype = CT_TEXT_PLAIN; + b->content_type = CT_TEXT_PLAIN; b->statistics = statistics; buffer_overflow_init(b); buffer_overflow_check(b); @@ -531,3 +301,205 @@ void buffer_increase(BUFFER *b, size_t free_size_required) { 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, bool minify) { + strncpyz(wb->json.key_quote, key_quote, BUFFER_QUOTE_MAX_SIZE); + strncpyz(wb->json.value_quote, value_quote, BUFFER_QUOTE_MAX_SIZE); + + wb->json.minify = minify; + wb->json.depth = (int8_t)(depth - 1); + _buffer_json_depth_push(wb, BUFFER_JSON_OBJECT); + + if(add_anonymous_object) + buffer_fast_strcat(wb, "{", 1); +} + +void buffer_json_finalize(BUFFER *wb) { + while(wb->json.depth >= 0) { + switch(wb->json.stack[wb->json.depth].type) { + case BUFFER_JSON_OBJECT: + 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.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) { + 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) { + 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) { + 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) { + 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, false); + buffer_json_finalize(wb); + errors += buffer_expect(wb, "{\n}\n"); + + buffer_flush(wb); + + buffer_json_initialize(wb, "\"", "\"", 0, true, false); + 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; +} + |