summaryrefslogtreecommitdiffstats
path: root/fluent-bit/lib/librdkafka-2.1.0/src/rdstring.c
diff options
context:
space:
mode:
Diffstat (limited to 'fluent-bit/lib/librdkafka-2.1.0/src/rdstring.c')
-rw-r--r--fluent-bit/lib/librdkafka-2.1.0/src/rdstring.c629
1 files changed, 629 insertions, 0 deletions
diff --git a/fluent-bit/lib/librdkafka-2.1.0/src/rdstring.c b/fluent-bit/lib/librdkafka-2.1.0/src/rdstring.c
new file mode 100644
index 000000000..6a18210c9
--- /dev/null
+++ b/fluent-bit/lib/librdkafka-2.1.0/src/rdstring.c
@@ -0,0 +1,629 @@
+/*
+ * librdkafka - The Apache Kafka C/C++ library
+ *
+ * Copyright (c) 2016 Magnus Edenhill
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#include "rd.h"
+#include "rdstring.h"
+#include "rdunittest.h"
+
+#include <ctype.h>
+
+
+/**
+ * @brief Render string \p template using \p callback for key lookups.
+ *
+ * Keys in template follow the %{keyname} syntax.
+ *
+ * The \p callback must not write more than \p size bytes to \p buf, must
+ * should return the number of bytes it wanted to write (which will indicate
+ * a truncated write).
+ * If the key is not found -1 should be returned (which fails the rendering).
+ *
+ * @returns number of written bytes to \p dest,
+ * or -1 on failure (errstr is written)
+ */
+char *rd_string_render(
+ const char *template,
+ char *errstr,
+ size_t errstr_size,
+ ssize_t (*callback)(const char *key, char *buf, size_t size, void *opaque),
+ void *opaque) {
+ const char *s = template;
+ const char *tend = template + strlen(template);
+ size_t size = 256;
+ char *buf;
+ size_t of = 0;
+
+ buf = rd_malloc(size);
+
+#define _remain() (size - of - 1)
+#define _assure_space(SZ) \
+ do { \
+ if (of + (SZ) + 1 >= size) { \
+ size = (size + (SZ) + 1) * 2; \
+ buf = rd_realloc(buf, size); \
+ } \
+ } while (0)
+
+#define _do_write(PTR, SZ) \
+ do { \
+ _assure_space(SZ); \
+ memcpy(buf + of, (PTR), (SZ)); \
+ of += (SZ); \
+ } while (0)
+
+
+
+ while (*s) {
+ const char *t;
+ size_t tof = (size_t)(s - template);
+
+ t = strstr(s, "%{");
+ if (t != s) {
+ /* Write "abc%{"
+ * ^^^ */
+ size_t len = (size_t)((t ? t : tend) - s);
+ if (len)
+ _do_write(s, len);
+ }
+
+ if (t) {
+ const char *te;
+ ssize_t r;
+ char *tmpkey;
+
+ /* Find "abc%{key}"
+ * ^ */
+ te = strchr(t + 2, '}');
+ if (!te) {
+ rd_snprintf(errstr, errstr_size,
+ "Missing close-brace } for "
+ "%.*s at %" PRIusz,
+ 15, t, tof);
+ rd_free(buf);
+ return NULL;
+ }
+
+ rd_strndupa(&tmpkey, t + 2, (int)(te - t - 2));
+
+ /* Query callback for length of key's value. */
+ r = callback(tmpkey, NULL, 0, opaque);
+ if (r == -1) {
+ rd_snprintf(errstr, errstr_size,
+ "Property not available: \"%s\"",
+ tmpkey);
+ rd_free(buf);
+ return NULL;
+ }
+
+ _assure_space(r);
+
+ /* Call again now providing a large enough buffer. */
+ r = callback(tmpkey, buf + of, _remain(), opaque);
+ if (r == -1) {
+ rd_snprintf(errstr, errstr_size,
+ "Property not available: "
+ "\"%s\"",
+ tmpkey);
+ rd_free(buf);
+ return NULL;
+ }
+
+ assert(r < (ssize_t)_remain());
+ of += r;
+ s = te + 1;
+
+ } else {
+ s = tend;
+ }
+ }
+
+ buf[of] = '\0';
+ return buf;
+}
+
+
+
+void rd_strtup_destroy(rd_strtup_t *strtup) {
+ rd_free(strtup);
+}
+
+void rd_strtup_free(void *strtup) {
+ rd_strtup_destroy((rd_strtup_t *)strtup);
+}
+
+rd_strtup_t *rd_strtup_new0(const char *name,
+ ssize_t name_len,
+ const char *value,
+ ssize_t value_len) {
+ rd_strtup_t *strtup;
+
+ /* Calculate lengths, if needed, and add space for \0 nul */
+
+ if (name_len == -1)
+ name_len = strlen(name);
+
+ if (!value)
+ value_len = 0;
+ else if (value_len == -1)
+ value_len = strlen(value);
+
+
+ strtup = rd_malloc(sizeof(*strtup) + name_len + 1 + value_len + 1 -
+ 1 /*name[1]*/);
+ memcpy(strtup->name, name, name_len);
+ strtup->name[name_len] = '\0';
+ if (value) {
+ strtup->value = &strtup->name[name_len + 1];
+ memcpy(strtup->value, value, value_len);
+ strtup->value[value_len] = '\0';
+ } else {
+ strtup->value = NULL;
+ }
+
+ return strtup;
+}
+
+rd_strtup_t *rd_strtup_new(const char *name, const char *value) {
+ return rd_strtup_new0(name, -1, value, -1);
+}
+
+
+/**
+ * @returns a new copy of \p src
+ */
+rd_strtup_t *rd_strtup_dup(const rd_strtup_t *src) {
+ return rd_strtup_new(src->name, src->value);
+}
+
+/**
+ * @brief Wrapper for rd_strtup_dup() suitable rd_list_copy*() use
+ */
+void *rd_strtup_list_copy(const void *elem, void *opaque) {
+ const rd_strtup_t *src = elem;
+ return (void *)rd_strtup_dup(src);
+}
+
+
+
+/**
+ * @brief Convert bit-flags in \p flags to human-readable CSV string
+ * use the bit-description strings in \p desc.
+ *
+ * \p desc array element N corresponds to bit (1<<N).
+ * \p desc MUST be terminated by a NULL array element.
+ * Empty descriptions are ignored even if the bit is set.
+ *
+ * @returns a null-terminated \p dst
+ */
+char *rd_flags2str(char *dst, size_t size, const char **desc, int flags) {
+ int bit = 0;
+ size_t of = 0;
+
+ for (; *desc; desc++, bit++) {
+ int r;
+
+ if (!(flags & (1 << bit)) || !*desc)
+ continue;
+
+ if (of >= size) {
+ /* Dest buffer too small, indicate truncation */
+ if (size > 3)
+ rd_snprintf(dst + (size - 3), 3, "..");
+ break;
+ }
+
+ r = rd_snprintf(dst + of, size - of, "%s%s", !of ? "" : ",",
+ *desc);
+
+ of += r;
+ }
+
+ if (of == 0 && size > 0)
+ *dst = '\0';
+
+ return dst;
+}
+
+
+
+/**
+ * @returns a djb2 hash of \p str.
+ *
+ * @param len If -1 the \p str will be hashed until nul is encountered,
+ * else up to the \p len.
+ */
+unsigned int rd_string_hash(const char *str, ssize_t len) {
+ unsigned int hash = 5381;
+ ssize_t i;
+
+ if (len == -1) {
+ for (i = 0; str[i] != '\0'; i++)
+ hash = ((hash << 5) + hash) + str[i];
+ } else {
+ for (i = 0; i < len; i++)
+ hash = ((hash << 5) + hash) + str[i];
+ }
+
+ return hash;
+}
+
+
+/**
+ * @brief Same as strcmp() but handles NULL values.
+ */
+int rd_strcmp(const char *a, const char *b) {
+ if (a == b)
+ return 0;
+ else if (!a && b)
+ return -1;
+ else if (!b)
+ return 1;
+ else
+ return strcmp(a, b);
+}
+
+
+
+/**
+ * @brief Case-insensitive strstr() for platforms where strcasestr()
+ * is not available.
+ */
+char *_rd_strcasestr(const char *haystack, const char *needle) {
+ const char *h_rem, *n_last;
+ size_t h_len = strlen(haystack);
+ size_t n_len = strlen(needle);
+
+
+ if (n_len == 0 || n_len > h_len)
+ return NULL;
+ else if (n_len == h_len)
+ return !rd_strcasecmp(haystack, needle) ? (char *)haystack
+ : NULL;
+
+ /*
+ * Scan inspired by Boyer-Moore:
+ *
+ * haystack = "this is a haystack"
+ * needle = "hays"
+ *
+ * "this is a haystack"
+ * ^ ^- h_last
+ * `-h (haystack + strlen(needle) - 1)
+ * `-h_rem
+ *
+ * "hays"
+ * ^-n
+ * ^-n_last
+ */
+ n_last = needle + n_len - 1;
+ h_rem = haystack + n_len - 1;
+
+ while (*h_rem) {
+ const char *h, *n = n_last;
+
+ /* Find first occurrence of last character in the needle
+ in the remaining haystack. */
+ for (h = h_rem; *h && tolower((int)*h) != tolower((int)*n); h++)
+ ;
+
+ if (!*h)
+ return NULL; /* No match */
+
+ /* Backtrack both needle and haystack as long as each character
+ * matches, if the start of the needle is found we have
+ * a full match, else start over from the remaining part of the
+ * haystack. */
+ do {
+ if (n == needle)
+ return (char *)h; /* Full match */
+
+ /* Rewind both n and h */
+ n--;
+ h--;
+
+ } while (tolower((int)*n) == tolower((int)*h));
+
+ /* Mismatch, start over at the next haystack position */
+ h_rem++;
+ }
+
+ return NULL;
+}
+
+
+
+/**
+ * @brief Unittests for rd_strcasestr()
+ */
+static int ut_strcasestr(void) {
+ static const struct {
+ const char *haystack;
+ const char *needle;
+ ssize_t exp;
+ } strs[] = {
+ {"this is a haystack", "hays", 10},
+ {"abc", "a", 0},
+ {"abc", "b", 1},
+ {"abc", "c", 2},
+ {"AbcaBcabC", "ABC", 0},
+ {"abcabcaBC", "BcA", 1},
+ {"abcabcABc", "cAB", 2},
+ {"need to estart stART the tart ReStArT!", "REsTaRt", 30},
+ {"need to estart stART the tart ReStArT!", "?sTaRt", -1},
+ {"aaaabaaAb", "ab", 3},
+ {"0A!", "a", 1},
+ {"a", "A", 0},
+ {".z", "Z", 1},
+ {"", "", -1},
+ {"", "a", -1},
+ {"a", "", -1},
+ {"peRfeCt", "peRfeCt", 0},
+ {"perfect", "perfect", 0},
+ {"PERFECT", "perfect", 0},
+ {NULL},
+ };
+ int i;
+
+ RD_UT_BEGIN();
+
+ for (i = 0; strs[i].haystack; i++) {
+ const char *ret;
+ ssize_t of = -1;
+
+ ret = _rd_strcasestr(strs[i].haystack, strs[i].needle);
+ if (ret)
+ of = ret - strs[i].haystack;
+ RD_UT_ASSERT(of == strs[i].exp,
+ "#%d: '%s' in '%s': expected offset %" PRIdsz
+ ", not %" PRIdsz " (%s)",
+ i, strs[i].needle, strs[i].haystack, strs[i].exp,
+ of, ret ? ret : "(NULL)");
+ }
+
+ RD_UT_PASS();
+}
+
+
+
+/**
+ * @brief Split a character-separated string into an array.
+ *
+ * @remark This is not CSV compliant as CSV uses " for escapes, but this here
+ * uses \.
+ *
+ * @param input Input string to parse.
+ * @param sep The separator character (typically ',')
+ * @param skip_empty Do not include empty fields in output array.
+ * @param cntp Will be set to number of elements in array.
+ *
+ * Supports "\" escapes.
+ * The array and the array elements will be allocated together and must be freed
+ * with a single rd_free(array) call.
+ * The array elements are copied and any "\" escapes are removed.
+ *
+ * @returns the parsed fields in an array. The number of elements in the
+ * array is returned in \p cntp
+ */
+char **rd_string_split(const char *input,
+ char sep,
+ rd_bool_t skip_empty,
+ size_t *cntp) {
+ size_t fieldcnt = 1;
+ rd_bool_t next_esc = rd_false;
+ const char *s;
+ char *p;
+ char **arr;
+ size_t inputlen;
+ size_t i = 0;
+ size_t elen = 0;
+
+ *cntp = 0;
+
+ /* First count the maximum number of fields so we know how large of
+ * an array we need to allocate. Escapes are ignored. */
+ for (s = input; *s; s++) {
+ if (*s == sep)
+ fieldcnt++;
+ }
+
+ inputlen = (size_t)(s - input);
+
+ /* Allocate array and memory for the copied elements in one go. */
+ arr = rd_malloc((sizeof(*arr) * fieldcnt) + inputlen + 1);
+ p = (char *)(&arr[fieldcnt]);
+
+ for (s = input;; s++) {
+ rd_bool_t at_end = *s == '\0';
+ rd_bool_t is_esc = next_esc;
+
+ /* If we've reached the end, jump to done to finish
+ * the current field. */
+ if (at_end)
+ goto done;
+
+ if (unlikely(!is_esc && *s == '\\')) {
+ next_esc = rd_true;
+ continue;
+ }
+
+ next_esc = rd_false;
+
+ /* Strip leading whitespaces for each element */
+ if (!is_esc && elen == 0 && isspace((int)*s))
+ continue;
+
+ if (likely(is_esc || *s != sep)) {
+ char c = *s;
+ if (is_esc) {
+ /* Perform some common escape substitions.
+ * If not known we'll just keep the escaped
+ * character as is (probably the separator). */
+ switch (c) {
+ case 't':
+ c = '\t';
+ break;
+ case 'n':
+ c = '\n';
+ break;
+ case 'r':
+ c = '\r';
+ break;
+ case '0':
+ c = '\0';
+ break;
+ }
+ }
+ p[elen++] = c;
+ continue;
+ }
+
+ done:
+ /* Strip trailing whitespaces */
+ while (elen > 0 && isspace((int)p[elen - 1]))
+ elen--;
+
+ /* End of field */
+ if (elen == 0 && skip_empty) {
+ if (at_end)
+ break;
+ continue;
+ }
+
+ rd_assert(i < fieldcnt);
+
+ /* Nul-terminate the element */
+ p[elen++] = '\0';
+ /* Assign element to array */
+ arr[i] = p;
+ /* Update next element pointer past the written bytes */
+ p += elen;
+ /* Reset element length */
+ elen = 0;
+ /* Advance array element index */
+ i++;
+
+ if (at_end)
+ break;
+ }
+
+ *cntp = i;
+
+ return arr;
+}
+
+/**
+ * @brief Unittest for rd_string_split()
+ */
+static int ut_string_split(void) {
+ static const struct {
+ const char *input;
+ const char sep;
+ rd_bool_t skip_empty;
+ size_t exp_cnt;
+ const char *exp[16];
+ } strs[] = {
+ {"just one field", ',', rd_true, 1, {"just one field"}},
+ /* Empty with skip_empty */
+ {"", ',', rd_true, 0},
+ /* Empty without skip_empty */
+ {"", ',', rd_false, 1, {""}},
+ {
+ ", a,b ,,c, d, e,f,ghijk, lmn,opq , r s t u, v",
+ ',',
+ rd_true,
+ 11,
+ {"a", "b", "c", "d", "e", "f", "ghijk", "lmn", "opq",
+ "r s t u", "v"},
+ },
+ {
+ ", a,b ,,c, d, e,f,ghijk, lmn,opq , r s t u, v",
+ ',',
+ rd_false,
+ 13,
+ {"", "a", "b", "", "c", "d", "e", "f", "ghijk", "lmn", "opq",
+ "r s t u", "v"},
+ },
+ {" this is an \\,escaped comma,\\,,\\\\, "
+ "and this is an unbalanced escape: \\\\\\\\\\\\\\",
+ ',',
+ rd_true,
+ 4,
+ {"this is an ,escaped comma", ",", "\\",
+ "and this is an unbalanced escape: \\\\\\"}},
+ {
+ "using|another ||\\|d|elimiter",
+ '|',
+ rd_false,
+ 5,
+ {"using", "another", "", "|d", "elimiter"},
+ },
+ {NULL},
+ };
+ size_t i;
+
+ RD_UT_BEGIN();
+
+ for (i = 0; strs[i].input; i++) {
+ char **ret;
+ size_t cnt = 12345;
+ size_t j;
+
+ ret = rd_string_split(strs[i].input, strs[i].sep,
+ strs[i].skip_empty, &cnt);
+ RD_UT_ASSERT(ret != NULL, "#%" PRIusz ": Did not expect NULL",
+ i);
+ RD_UT_ASSERT(cnt == strs[i].exp_cnt,
+ "#%" PRIusz
+ ": "
+ "Expected %" PRIusz " elements, got %" PRIusz,
+ i, strs[i].exp_cnt, cnt);
+
+ for (j = 0; j < cnt; j++)
+ RD_UT_ASSERT(!strcmp(strs[i].exp[j], ret[j]),
+ "#%" PRIusz ": Expected string %" PRIusz
+ " to be \"%s\", not \"%s\"",
+ i, j, strs[i].exp[j], ret[j]);
+
+ rd_free(ret);
+ }
+
+ RD_UT_PASS();
+}
+
+/**
+ * @brief Unittests for strings
+ */
+int unittest_string(void) {
+ int fails = 0;
+
+ fails += ut_strcasestr();
+ fails += ut_string_split();
+
+ return fails;
+}