summaryrefslogtreecommitdiffstats
path: root/libnetdata/buffer
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--libnetdata/buffer/buffer.c61
-rw-r--r--libnetdata/buffer/buffer.h55
-rw-r--r--libnetdata/buffered_reader/Makefile.am8
-rw-r--r--libnetdata/buffered_reader/README.md0
-rw-r--r--libnetdata/buffered_reader/buffered_reader.c3
-rw-r--r--libnetdata/buffered_reader/buffered_reader.h146
6 files changed, 235 insertions, 38 deletions
diff --git a/libnetdata/buffer/buffer.c b/libnetdata/buffer/buffer.c
index feee72fcf..64f9cce47 100644
--- a/libnetdata/buffer/buffer.c
+++ b/libnetdata/buffer/buffer.c
@@ -81,6 +81,7 @@ void buffer_snprintf(BUFFER *wb, size_t len, const char *fmt, ...)
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);
@@ -89,53 +90,39 @@ void buffer_snprintf(BUFFER *wb, size_t len, const char *fmt, ...)
// the buffer is \0 terminated by vsnprintfz
}
-void buffer_vsprintf(BUFFER *wb, const char *fmt, va_list args)
-{
+inline void buffer_vsprintf(BUFFER *wb, const char *fmt, va_list args) {
if(unlikely(!fmt || !*fmt)) return;
- size_t wrote = 0, need = 2, space_remaining = 0;
+ size_t full_size_bytes = 0, need = 2, space_remaining = 0;
do {
- need += space_remaining * 2;
+ need += full_size_bytes + 2;
- netdata_log_debug(D_WEB_BUFFER, "web_buffer_sprintf(): increasing web_buffer at position %zu, size = %zu, by %zu bytes (wrote = %zu)\n", wb->len, wb->size, need, wrote);
buffer_need_bytes(wb, need);
space_remaining = wb->size - wb->len - 1;
- wrote = (size_t) vsnprintfz(&wb->buffer[wb->len], space_remaining, fmt, args);
+ // 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(wrote >= space_remaining);
+ } while(full_size_bytes >= space_remaining);
- wb->len += wrote;
+ wb->len += full_size_bytes;
- // the buffer is \0 terminated by vsnprintf
+ wb->buffer[wb->len] = '\0';
+ buffer_overflow_check(wb);
}
void buffer_sprintf(BUFFER *wb, const char *fmt, ...)
{
- if(unlikely(!fmt || !*fmt)) return;
-
va_list args;
- size_t wrote = 0, need = 2, space_remaining = 0;
-
- do {
- need += space_remaining * 2;
-
- netdata_log_debug(D_WEB_BUFFER, "web_buffer_sprintf(): increasing web_buffer at position %zu, size = %zu, by %zu bytes (wrote = %zu)\n", wb->len, wb->size, need, wrote);
- buffer_need_bytes(wb, need);
-
- space_remaining = wb->size - wb->len - 1;
-
- va_start(args, fmt);
- wrote = (size_t) vsnprintfz(&wb->buffer[wb->len], space_remaining, fmt, args);
- va_end(args);
-
- } while(wrote >= space_remaining);
-
- wb->len += wrote;
-
- // the buffer is \0 terminated by vsnprintf
+ va_start(args, fmt);
+ buffer_vsprintf(wb, fmt, args);
+ va_end(args);
}
// generate a javascript date, the fastest possible way...
@@ -301,12 +288,15 @@ void buffer_json_initialize(BUFFER *wb, const char *key_quote, const char *value
strncpyz(wb->json.key_quote, key_quote, BUFFER_QUOTE_MAX_SIZE);
strncpyz(wb->json.value_quote, value_quote, BUFFER_QUOTE_MAX_SIZE);
- wb->json.options = options;
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);
@@ -316,9 +306,14 @@ 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);
+ 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;
diff --git a/libnetdata/buffer/buffer.h b/libnetdata/buffer/buffer.h
index 26efe0070..88d3f0282 100644
--- a/libnetdata/buffer/buffer.h
+++ b/libnetdata/buffer/buffer.h
@@ -72,6 +72,7 @@ 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 {
@@ -93,6 +94,8 @@ typedef struct web_buffer {
} 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)
@@ -134,6 +137,10 @@ 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);
@@ -209,6 +216,13 @@ static inline void buffer_fast_rawcat(BUFFER *wb, const char *txt, size_t len) {
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;
@@ -282,6 +296,19 @@ static inline void buffer_strncat(BUFFER *wb, const char *txt, size_t len) {
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;
@@ -809,8 +836,13 @@ static inline void buffer_json_member_add_boolean(BUFFER *wb, const char *key, b
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);
+ 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);
@@ -860,6 +892,13 @@ static inline void buffer_json_add_array_item_uint64(BUFFER *wb, uint64_t 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);
@@ -959,12 +998,14 @@ typedef enum __attribute__((packed)) {
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_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,
@@ -982,6 +1023,9 @@ static inline const char *rrdf_field_type_to_string(RRDF_FIELD_TYPE type) {
case RRDF_FIELD_TYPE_INTEGER:
return "integer";
+ case RRDF_FIELD_TYPE_BOOLEAN:
+ return "boolean";
+
case RRDF_FIELD_TYPE_STRING:
return "string";
@@ -1112,7 +1156,7 @@ static inline const char *rrdf_field_summary_to_string(RRDF_FIELD_SUMMARY summar
}
typedef enum __attribute__((packed)) {
- RRDF_FIELD_FILTER_NONE,
+ RRDF_FIELD_FILTER_NONE = 0,
RRDF_FIELD_FILTER_RANGE,
RRDF_FIELD_FILTER_MULTISELECT,
RRDF_FIELD_FILTER_FACET,
@@ -1173,8 +1217,9 @@ buffer_rrdf_table_add_field(BUFFER *wb, size_t field_id, const char *key, const
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 & RRDR_FIELD_OPTS_DUMMY)
+ if(options & RRDF_FIELD_OPTS_DUMMY)
buffer_json_member_add_boolean(wb, "dummy", true);
}
buffer_json_object_close(wb);
diff --git a/libnetdata/buffered_reader/Makefile.am b/libnetdata/buffered_reader/Makefile.am
new file mode 100644
index 000000000..161784b8f
--- /dev/null
+++ b/libnetdata/buffered_reader/Makefile.am
@@ -0,0 +1,8 @@
+# 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/buffered_reader/README.md b/libnetdata/buffered_reader/README.md
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/libnetdata/buffered_reader/README.md
diff --git a/libnetdata/buffered_reader/buffered_reader.c b/libnetdata/buffered_reader/buffered_reader.c
new file mode 100644
index 000000000..7cd17abfe
--- /dev/null
+++ b/libnetdata/buffered_reader/buffered_reader.c
@@ -0,0 +1,3 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "../libnetdata.h"
diff --git a/libnetdata/buffered_reader/buffered_reader.h b/libnetdata/buffered_reader/buffered_reader.h
new file mode 100644
index 000000000..4db57cd29
--- /dev/null
+++ b/libnetdata/buffered_reader/buffered_reader.h
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "../libnetdata.h"
+
+#ifndef NETDATA_BUFFERED_READER_H
+#define NETDATA_BUFFERED_READER_H
+
+struct buffered_reader {
+ ssize_t read_len;
+ ssize_t pos;
+ char read_buffer[PLUGINSD_LINE_MAX + 1];
+};
+
+static inline void buffered_reader_init(struct buffered_reader *reader) {
+ reader->read_buffer[0] = '\0';
+ reader->read_len = 0;
+ reader->pos = 0;
+}
+
+typedef enum {
+ BUFFERED_READER_READ_OK = 0,
+ BUFFERED_READER_READ_FAILED = -1,
+ BUFFERED_READER_READ_BUFFER_FULL = -2,
+ BUFFERED_READER_READ_POLLERR = -3,
+ BUFFERED_READER_READ_POLLHUP = -4,
+ BUFFERED_READER_READ_POLLNVAL = -5,
+ BUFFERED_READER_READ_POLL_UNKNOWN = -6,
+ BUFFERED_READER_READ_POLL_TIMEOUT = -7,
+ BUFFERED_READER_READ_POLL_FAILED = -8,
+} buffered_reader_ret_t;
+
+
+static inline buffered_reader_ret_t buffered_reader_read(struct buffered_reader *reader, int fd) {
+#ifdef NETDATA_INTERNAL_CHECKS
+ if(reader->read_buffer[reader->read_len] != '\0')
+ fatal("read_buffer does not start with zero");
+#endif
+
+ char *read_at = reader->read_buffer + reader->read_len;
+ ssize_t remaining = sizeof(reader->read_buffer) - reader->read_len - 1;
+
+ if(unlikely(remaining <= 0))
+ return BUFFERED_READER_READ_BUFFER_FULL;
+
+ ssize_t bytes_read = read(fd, read_at, remaining);
+ if(unlikely(bytes_read <= 0))
+ return BUFFERED_READER_READ_FAILED;
+
+ reader->read_len += bytes_read;
+ reader->read_buffer[reader->read_len] = '\0';
+
+ return BUFFERED_READER_READ_OK;
+}
+
+static inline buffered_reader_ret_t buffered_reader_read_timeout(struct buffered_reader *reader, int fd, int timeout_ms, bool log_error) {
+ errno = 0;
+ struct pollfd fds[1];
+
+ fds[0].fd = fd;
+ fds[0].events = POLLIN;
+
+ int ret = poll(fds, 1, timeout_ms);
+
+ if (ret > 0) {
+ /* There is data to read */
+ if (fds[0].revents & POLLIN)
+ return buffered_reader_read(reader, fd);
+
+ else if(fds[0].revents & POLLERR) {
+ if(log_error)
+ netdata_log_error("PARSER: read failed: POLLERR.");
+ return BUFFERED_READER_READ_POLLERR;
+ }
+ else if(fds[0].revents & POLLHUP) {
+ if(log_error)
+ netdata_log_error("PARSER: read failed: POLLHUP.");
+ return BUFFERED_READER_READ_POLLHUP;
+ }
+ else if(fds[0].revents & POLLNVAL) {
+ if(log_error)
+ netdata_log_error("PARSER: read failed: POLLNVAL.");
+ return BUFFERED_READER_READ_POLLNVAL;
+ }
+
+ if(log_error)
+ netdata_log_error("PARSER: poll() returned positive number, but POLLIN|POLLERR|POLLHUP|POLLNVAL are not set.");
+ return BUFFERED_READER_READ_POLL_UNKNOWN;
+ }
+ else if (ret == 0) {
+ if(log_error)
+ netdata_log_error("PARSER: timeout while waiting for data.");
+ return BUFFERED_READER_READ_POLL_TIMEOUT;
+ }
+
+ if(log_error)
+ netdata_log_error("PARSER: poll() failed with code %d.", ret);
+ return BUFFERED_READER_READ_POLL_FAILED;
+}
+
+/* Produce a full line if one exists, statefully return where we start next time.
+ * When we hit the end of the buffer with a partial line move it to the beginning for the next fill.
+ */
+static inline bool buffered_reader_next_line(struct buffered_reader *reader, BUFFER *dst) {
+ buffer_need_bytes(dst, reader->read_len - reader->pos + 2);
+
+ size_t start = reader->pos;
+
+ char *ss = &reader->read_buffer[start];
+ char *se = &reader->read_buffer[reader->read_len];
+ char *ds = &dst->buffer[dst->len];
+ char *de = &ds[dst->size - dst->len - 2];
+
+ if(ss >= se) {
+ *ds = '\0';
+ reader->pos = 0;
+ reader->read_len = 0;
+ reader->read_buffer[reader->read_len] = '\0';
+ return false;
+ }
+
+ // copy all bytes to buffer
+ while(ss < se && ds < de && *ss != '\n') {
+ *ds++ = *ss++;
+ dst->len++;
+ }
+
+ // if we have a newline, return the buffer
+ if(ss < se && ds < de && *ss == '\n') {
+ // newline found in the r->read_buffer
+
+ *ds++ = *ss++; // copy the newline too
+ dst->len++;
+
+ *ds = '\0';
+
+ reader->pos = ss - reader->read_buffer;
+ return true;
+ }
+
+ reader->pos = 0;
+ reader->read_len = 0;
+ reader->read_buffer[reader->read_len] = '\0';
+ return false;
+}
+
+#endif //NETDATA_BUFFERED_READER_H