1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183 |
- // 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;
- 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 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);
- 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_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_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);
- 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_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_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
- RRDR_FIELD_OPTS_DUMMY = (1 << 5), // not a presentable field
- } RRDF_FIELD_OPTIONS;
- typedef enum __attribute__((packed)) {
- RRDF_FIELD_TYPE_NONE,
- RRDF_FIELD_TYPE_INTEGER,
- 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_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,
- 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);
- if(options & RRDR_FIELD_OPTS_DUMMY)
- buffer_json_member_add_boolean(wb, "dummy", true);
- }
- buffer_json_object_close(wb);
- }
- #endif /* NETDATA_WEB_BUFFER_H */
|