master
h 844 lines 19.1 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #ifndef NETDATA_INLINED_H
4 #define NETDATA_INLINED_H 1
5
6 #include "libnetdata.h"
7
8 #ifdef KERNEL_32BIT
9 typedef uint32_t kernel_uint_t;
10 #define str2kernel_uint_t(string) str2uint32_t(string, NULL)
11 #define KERNEL_UINT_FORMAT "%u"
12 #else
13 typedef uint64_t kernel_uint_t;
14 #define str2kernel_uint_t(string) str2uint64_t(string, NULL)
15 #define KERNEL_UINT_FORMAT "%" PRIu64
16 #endif
17
18 #define str2pid_t(string) str2uint32_t(string, NULL)
19
20
21 // for faster execution, allow the compiler to inline
22 // these functions that are called thousands of times per second
23
24 static inline uint32_t djb2_hash32(const char* name) {
25 unsigned char *s = (unsigned char *) name;
26 uint32_t hash = 5381;
27 while (*s)
28 hash = ((hash << 5) + hash) + (uint32_t) *s++; // hash * 33 + char
29 return hash;
30 }
31
32 static inline uint32_t pluginsd_parser_hash32(const char *name) {
33 unsigned char *s = (unsigned char *) name;
34 uint32_t hash = 0;
35 while (*s) {
36 hash <<= 5;
37 hash += *s++ - ' ';
38 }
39 return hash;
40 }
41
42 // https://stackoverflow.com/a/107657
43 static inline uint32_t larson_hash32(const char *name) {
44 unsigned char *s = (unsigned char *) name;
45 uint32_t hash = 0;
46 while (*s)
47 hash = hash * 101 + (uint32_t) *s++;
48 return hash;
49 }
50
51 // http://isthe.com/chongo/tech/comp/fnv/
52 static inline uint32_t fnv1_hash32(const char *name) {
53 unsigned char *s = (unsigned char *) name;
54 uint32_t hash = 0x811c9dc5;
55 while (*s) {
56 hash *= 0x01000193; // 16777619
57 hash ^= (uint32_t) *s++;
58 }
59 return hash;
60 }
61
62 // http://isthe.com/chongo/tech/comp/fnv/
63 static inline uint32_t fnv1a_hash32(const char *name) {
64 unsigned char *s = (unsigned char *) name;
65 uint32_t hash = 0x811c9dc5;
66 while (*s) {
67 hash ^= (uint32_t) *s++;
68 hash *= 0x01000193; // 16777619
69 }
70 return hash;
71 }
72
73 static inline uint32_t fnv1a_uhash32(const char *name) {
74 unsigned char *s = (unsigned char *) name;
75 uint32_t hash = 0x811c9dc5, c;
76 while ((c = *s++)) {
77 if (unlikely(c >= 'A' && c <= 'Z')) c += 'a' - 'A';
78 hash ^= c;
79 hash *= 0x01000193; // 16777619
80 }
81 return hash;
82 }
83
84 #define simple_hash(s) fnv1a_hash32(s)
85 #define simple_uhash(s) fnv1a_uhash32(s)
86
87 static inline uint64_t fnv1a_hash_bin64(const void *data, size_t len) {
88 const uint8_t *bytes = (const uint8_t *)data;
89 uint64_t hash = 14695981039346656037ULL; // FNV offset basis for 64-bit
90 for (size_t i = 0; i < len; i++) {
91 hash ^= (uint64_t)bytes[i];
92 hash *= 1099511628211ULL; // FNV prime for 64-bit
93 }
94 return hash;
95 }
96
97 static uint32_t murmur32(uint32_t k) __attribute__((const));
98 static inline uint32_t murmur32(uint32_t k) {
99 k ^= k >> 16;
100 k *= 0x85ebca6b;
101 k ^= k >> 13;
102 k *= 0xc2b2ae35;
103 k ^= k >> 16;
104
105 return k;
106 }
107
108 static uint64_t murmur64(uint64_t k) __attribute__((const));
109 static inline uint64_t murmur64(uint64_t k) {
110 k ^= k >> 33;
111 k *= 0xff51afd7ed558ccdUL;
112 k ^= k >> 33;
113 k *= 0xc4ceb9fe1a85ec53UL;
114 k ^= k >> 33;
115
116 return k;
117 }
118
119 ALWAYS_INLINE
120 static unsigned int str2u(const char *s) {
121 while(isspace((uint8_t)*s))
122 s++;
123
124 unsigned int n = 0;
125 while(*s >= '0' && *s <= '9')
126 n = n * 10 + (*s++ - '0');
127
128 return n;
129 }
130
131 ALWAYS_INLINE
132 static int str2i(const char *s) {
133 while(isspace((uint8_t)*s))
134 s++;
135
136 if(unlikely(*s == '-')) {
137 s++;
138 return -(int) str2u(s);
139 }
140 else {
141 if(unlikely(*s == '+')) s++;
142 return (int) str2u(s);
143 }
144 }
145
146 ALWAYS_INLINE
147 static unsigned long str2ul(const char *s) {
148 while(isspace((uint8_t)*s))
149 s++;
150
151 unsigned long n = 0;
152 while(*s >= '0' && *s <= '9')
153 n = n * 10 + (*s++ - '0');
154
155 return n;
156 }
157
158 ALWAYS_INLINE
159 static long str2l(const char *s) {
160 while(isspace((uint8_t)*s))
161 s++;
162
163 if(unlikely(*s == '-')) {
164 s++;
165 return -(long) str2ul(s);
166 }
167 else {
168 if(unlikely(*s == '+')) s++;
169 return (long) str2ul(s);
170 }
171 }
172
173 ALWAYS_INLINE
174 static uint32_t str2uint32_t(const char *s, char **endptr) {
175 while(isspace((uint8_t)*s))
176 s++;
177
178 uint32_t n = 0;
179
180 while(*s >= '0' && *s <= '9')
181 n = n * 10 + (*s++ - '0');
182
183 if(unlikely(endptr))
184 *endptr = (char *)s;
185
186 return n;
187 }
188
189 ALWAYS_INLINE
190 static uint64_t str2uint64_t(const char *s, char **endptr) {
191 while(isspace((uint8_t)*s))
192 s++;
193
194 uint64_t n = 0;
195
196 #ifdef ENV32BIT
197 unsigned long n32 = 0;
198 while (*s >= '0' && *s <= '9' && n32 < (ULONG_MAX / 10))
199 n32 = n32 * 10 + (*s++ - '0');
200
201 n = n32;
202 #endif
203
204 while(*s >= '0' && *s <= '9')
205 n = n * 10 + (*s++ - '0');
206
207 if(unlikely(endptr))
208 *endptr = (char *)s;
209
210 return n;
211 }
212
213 ALWAYS_INLINE
214 static unsigned long long int str2ull(const char *s, char **endptr) {
215 return str2uint64_t(s, endptr);
216 }
217
218 ALWAYS_INLINE
219 static long long str2ll(const char *s, char **endptr) {
220 while(isspace((uint8_t)*s))
221 s++;
222
223 if(unlikely(*s == '-')) {
224 s++;
225 return -(long long) str2uint64_t(s, endptr);
226 }
227 else {
228 if(unlikely(*s == '+')) s++;
229 return (long long) str2uint64_t(s, endptr);
230 }
231 }
232
233 ALWAYS_INLINE
234 static uint32_t str2uint32_hex(const char *src, char **endptr) {
235 while(isspace((uint8_t)*src))
236 src++;
237
238 uint32_t num = 0;
239 const unsigned char *s = (const unsigned char *)src;
240 unsigned char c;
241
242 while ((c = hex_value_from_ascii[(uint8_t)*s]) != 255) {
243 num = (num << 4) | c;
244 s++;
245 }
246
247 if(endptr)
248 *endptr = (char *)s;
249
250 return num;
251 }
252
253 ALWAYS_INLINE
254 static uint64_t str2uint64_hex(const char *src, char **endptr) {
255 while(isspace((uint8_t)*src))
256 src++;
257
258 uint64_t num = 0;
259 const unsigned char *s = (const unsigned char *)src;
260 unsigned char c;
261
262 while ((c = hex_value_from_ascii[(uint8_t)*s]) != 255) {
263 num = (num << 4) | c;
264 s++;
265 }
266
267 if(endptr)
268 *endptr = (char *)s;
269
270 return num;
271 }
272
273 ALWAYS_INLINE
274 static uint64_t str2uint64_base64(const char *src, char **endptr) {
275 while(isspace((uint8_t)*src))
276 src++;
277
278 uint64_t num = 0;
279 const unsigned char *s = (const unsigned char *)src;
280 unsigned char c;
281
282 while ((c = base64_value_from_ascii[*s]) != 255) {
283 num = (num << 6) | c;
284 s++;
285 }
286
287 if(endptr)
288 *endptr = (char *)s;
289
290 return num;
291 }
292
293 ALWAYS_INLINE
294 static NETDATA_DOUBLE str2ndd_parse_double_decimal_digits_internal(const char *src, int *digits) {
295 while(isspace((uint8_t)*src))
296 src++;
297
298 const char *s = src;
299 NETDATA_DOUBLE n = 0.0;
300
301 while(*s >= '0' && *s <= '9') {
302
303 // this works for both 32-bit and 64-bit systems
304 unsigned long ni = 0;
305 unsigned exponent = 0;
306 while (*s >= '0' && *s <= '9' && ni < (ULONG_MAX / 10)) {
307 ni = (ni * 10) + (*s++ - '0');
308 exponent++;
309 }
310
311 n = n * powndd(10.0, exponent) + (NETDATA_DOUBLE)ni;
312 }
313
314 *digits = (int)(s - src);
315 return n;
316 }
317
318 ALWAYS_INLINE
319 static NETDATA_DOUBLE str2ndd(const char *src, char **endptr) {
320 while(isspace((uint8_t)*src))
321 src++;
322
323 const char *s = src;
324
325 NETDATA_DOUBLE sign = 1.0;
326 NETDATA_DOUBLE result;
327 int integral_digits = 0;
328
329 NETDATA_DOUBLE fractional = 0.0;
330 int fractional_digits = 0;
331
332 NETDATA_DOUBLE exponent = 0.0;
333 int exponent_digits = 0;
334
335 switch(*s) {
336 case '-':
337 s++;
338 sign = -1.0;
339 break;
340
341 case '+':
342 s++;
343 break;
344
345 case 'n':
346 if(s[1] == 'a' && s[2] == 'n') {
347 if(endptr) *endptr = (char *)&s[3];
348 return NAN;
349 }
350 if(s[1] == 'u' && s[2] == 'l' && s[3] == 'l') {
351 if(endptr) *endptr = (char *)&s[3];
352 return NAN;
353 }
354 break;
355
356 case 'i':
357 if(s[1] == 'n' && s[2] == 'f') {
358 if(endptr) *endptr = (char *)&s[3];
359 return INFINITY;
360 }
361 break;
362
363 default:
364 break;
365 }
366
367 result = str2ndd_parse_double_decimal_digits_internal(s, &integral_digits);
368 s += integral_digits;
369
370 if(unlikely(*s == '.')) {
371 s++;
372 fractional = str2ndd_parse_double_decimal_digits_internal(s, &fractional_digits);
373 s += fractional_digits;
374 }
375
376 if (unlikely(*s == 'e' || *s == 'E')) {
377 const char *e_ptr = s;
378 s++;
379
380 int exponent_sign = 1;
381 if (*s == '-') {
382 exponent_sign = -1;
383 s++;
384 }
385 else if(*s == '+')
386 s++;
387
388 exponent = str2ndd_parse_double_decimal_digits_internal(s, &exponent_digits);
389 if(unlikely(!exponent_digits)) {
390 exponent = 0;
391 s = e_ptr;
392 }
393 else {
394 s += exponent_digits;
395 exponent *= exponent_sign;
396 }
397 }
398
399 if(unlikely(endptr))
400 *endptr = (char *)s;
401
402 if (unlikely(exponent_digits))
403 result *= powndd(10.0, exponent);
404
405 if (unlikely(fractional_digits))
406 result += fractional / powndd(10.0, fractional_digits) * (exponent_digits ? powndd(10.0, exponent) : 1.0);
407
408 return sign * result;
409 }
410
411 ALWAYS_INLINE
412 static unsigned long long str2ull_encoded(const char *s) {
413 if(*s == IEEE754_UINT64_B64_PREFIX[0])
414 return str2uint64_base64(s + sizeof(IEEE754_UINT64_B64_PREFIX) - 1, NULL);
415
416 if(s[0] == HEX_PREFIX[0] && s[1] == HEX_PREFIX[1])
417 return str2uint64_hex(s + 2, NULL);
418
419 return str2uint64_t(s, NULL);
420 }
421
422 ALWAYS_INLINE
423 static long long str2ll_encoded(const char *s) {
424 if(*s == '-')
425 return -(long long) str2ull_encoded(&s[1]);
426 else
427 return (long long) str2ull_encoded(s);
428 }
429
430 ALWAYS_INLINE
431 static NETDATA_DOUBLE str2ndd_encoded(const char *src, char **endptr) {
432 if (*src == IEEE754_DOUBLE_B64_PREFIX[0]) {
433 // double parsing from base64
434 uint64_t n = str2uint64_base64(src + sizeof(IEEE754_DOUBLE_B64_PREFIX) - 1, endptr);
435 NETDATA_DOUBLE *ptr = (NETDATA_DOUBLE *) (&n);
436 return *ptr;
437 }
438
439 if (*src == IEEE754_DOUBLE_HEX_PREFIX[0]) {
440 // double parsing from hex
441 uint64_t n = str2uint64_hex(src + sizeof(IEEE754_DOUBLE_HEX_PREFIX) - 1, endptr);
442 NETDATA_DOUBLE *ptr = (NETDATA_DOUBLE *) (&n);
443 return *ptr;
444 }
445
446 double sign = 1.0;
447
448 if(*src == '-') {
449 sign = -1.0;
450 src++;
451 }
452
453 if(unlikely(*src == IEEE754_UINT64_B64_PREFIX[0]))
454 return (NETDATA_DOUBLE) str2uint64_base64(src + sizeof(IEEE754_UINT64_B64_PREFIX) - 1, endptr) * sign;
455
456 if(unlikely(*src == HEX_PREFIX[0] && src[1] == HEX_PREFIX[1]))
457 return (NETDATA_DOUBLE) str2uint64_hex(src + sizeof(HEX_PREFIX) - 1, endptr) * sign;
458
459 return str2ndd(src, endptr) * sign;
460 }
461
462 ALWAYS_INLINE
463 static char *strncpyz(char *dst, const char *src, size_t dst_size_minus_1) {
464 char *p = dst;
465
466 while (*src && dst_size_minus_1--)
467 *dst++ = *src++;
468
469 *dst = '\0';
470
471 return p;
472 }
473
474 // append src to dst, but only if there is space for it
475 // dst is always null terminated
476 ALWAYS_INLINE
477 static size_t strcatz(char *dst, size_t len, const char *src, size_t size) {
478 // If starting offset is out of bounds, do nothing.
479 if (unlikely(len >= size)) {
480 if(size > 0)
481 dst[size - 1] = '\0';
482
483 return len;
484 }
485
486 // If the source is not valid or empty, do nothing.
487 if(unlikely(!src || !*src)) {
488 dst[len] = '\0';
489 return len;
490 }
491
492 // Move pointer to the end of the current string.
493 char *dest = dst + len;
494
495 // Reserve one byte for the null terminator.
496 size_t space = size - len - 1;
497 size_t initial_space = space;
498
499 // Append src into dst using pointer operations.
500 while (*src && space > 0) {
501 *dest++ = *src++;
502 space--;
503 }
504
505 // Null-terminate the string.
506 *dest = '\0';
507
508 // Return the new length.
509 return len + (initial_space - space);
510 }
511
512 ALWAYS_INLINE
513 static void sanitize_json_string(char *dst, const char *src, size_t dst_size) {
514 while (*src != '\0' && dst_size > 1) {
515 if (*src < 0x1F) {
516 *dst++ = '_';
517 src++;
518 dst_size--;
519 }
520 else if (*src == '\\' || *src == '\"') {
521 *dst++ = '\\';
522 *dst++ = *src++;
523 dst_size -= 2;
524 }
525 else {
526 *dst++ = *src++;
527 dst_size--;
528 }
529 }
530 *dst = '\0';
531 }
532
533 ALWAYS_INLINE
534 static bool sanitize_command_argument_string(char *dst, const char *src, size_t dst_size) {
535 if(dst_size)
536 *dst = '\0';
537
538 // skip leading dashes
539 while (*src == '-')
540 src++;
541
542 while (*src != '\0') {
543 if (dst_size < 1)
544 return false;
545
546 if (iscntrl((uint8_t)*src) || *src == '$') {
547 // remove control characters and characters that are expanded by bash
548 *dst++ = '_';
549 dst_size--;
550 }
551 else if (*src == '\'' || *src == '`') {
552 // escape single quotes
553 if (dst_size < 4)
554 return false;
555
556 dst[0] = '\''; dst[1] = '\\'; dst[2] = '\''; dst[3] = '\'';
557
558 dst += 4;
559 dst_size -= 4;
560 }
561 else {
562 *dst++ = *src;
563 dst_size--;
564 }
565
566 src++;
567 }
568
569 // make sure we have space to terminate the string
570 if (dst_size == 0)
571 return false;
572
573 *dst = '\0';
574
575 return true;
576 }
577
578 ALWAYS_INLINE
579 static int read_txt_file(const char *filename, char *buffer, size_t size) {
580 if(unlikely(!size)) return 3;
581
582 int fd = open(filename, O_RDONLY | O_CLOEXEC, 0666);
583 if(unlikely(fd == -1)) {
584 buffer[0] = '\0';
585 return 1;
586 }
587
588 ssize_t r = read(fd, buffer, size - 1); // leave space of the final zero
589 if(unlikely(r == -1)) {
590 buffer[0] = '\0';
591 close(fd);
592 return 2;
593 }
594 buffer[r] = '\0';
595
596 close(fd);
597 return 0;
598 }
599
600 ALWAYS_INLINE
601 static bool read_txt_file_to_buffer(const char *filename, BUFFER *wb, size_t max_size) {
602 // Open the file
603 int fd = open(filename, O_RDONLY | O_CLOEXEC);
604 if (fd == -1)
605 return false;
606
607 // Get the file size
608 struct stat st;
609 if (fstat(fd, &st) == -1) {
610 close(fd);
611 return false;
612 }
613
614 size_t file_size = st.st_size;
615
616 // Check if the file size exceeds the maximum allowed size
617 if (file_size > max_size) {
618 close(fd);
619 return false; // File size too large
620 }
621
622 buffer_need_bytes(wb, file_size + 1);
623
624 // Read the file contents into the buffer
625 ssize_t r = read(fd, &wb->buffer[wb->len], file_size);
626 if (r != (ssize_t)file_size) {
627 close(fd);
628 return false; // Read error
629 }
630 wb->len = r;
631
632 // Close the file descriptor
633 close(fd);
634
635 return true; // Success
636 }
637
638 ALWAYS_INLINE
639 static int read_proc_cmdline(const char *filename, char *buffer, size_t size) {
640 if (unlikely(!size)) return 3;
641
642 int fd = open(filename, O_RDONLY | O_CLOEXEC, 0666);
643 if (unlikely(fd == -1)) {
644 buffer[0] = '\0';
645 return 1;
646 }
647
648 ssize_t r = read(fd, buffer, size - 1); // Leave space for final null character
649 if (unlikely(r == -1)) {
650 buffer[0] = '\0';
651 close(fd);
652 return 2;
653 }
654
655 if (r > 0) {
656 // Replace null characters with spaces, except for the last one
657 for (ssize_t i = 0; i < r - 1; i++) {
658 if (buffer[i] == '\0') {
659 buffer[i] = ' ';
660 }
661 }
662 buffer[r] = '\0'; // Null-terminate the string
663 }
664 else {
665 buffer[0] = '\0'; // Empty cmdline
666 }
667
668 close(fd);
669 return 0;
670 }
671
672 ALWAYS_INLINE
673 static int read_single_number_file(const char *filename, unsigned long long *result) {
674 char buffer[30 + 1];
675
676 int ret = read_txt_file(filename, buffer, sizeof(buffer));
677 if(unlikely(ret)) {
678 *result = 0;
679 return ret;
680 }
681
682 buffer[30] = '\0';
683 *result = str2ull(buffer, NULL);
684 return 0;
685 }
686
687 ALWAYS_INLINE
688 static int read_single_signed_number_file(const char *filename, long long *result) {
689 char buffer[30 + 1];
690
691 int ret = read_txt_file(filename, buffer, sizeof(buffer));
692 if(unlikely(ret)) {
693 *result = 0;
694 return ret;
695 }
696
697 buffer[30] = '\0';
698 *result = atoll(buffer);
699 return 0;
700 }
701
702 ALWAYS_INLINE
703 static int read_single_base64_or_hex_number_file(const char *filename, unsigned long long *result) {
704 char buffer[30 + 1];
705
706 int ret = read_txt_file(filename, buffer, sizeof(buffer));
707 if(unlikely(ret)) {
708 *result = 0;
709 return ret;
710 }
711
712 buffer[30] = '\0';
713
714 if(likely(buffer[0])){
715 *result = str2ull_encoded(buffer);
716 return 0;
717 }
718 else {
719 *result = 0;
720 return -1;
721 }
722 }
723
724 ALWAYS_INLINE
725 static char *strsep_skip_consecutive_separators(char **ptr, char *s) {
726 char *p = (char *)"";
727 while (p && !p[0] && *ptr) p = strsep(ptr, s);
728 return (p);
729 }
730
731 // remove leading and trailing spaces; may return NULL
732 ALWAYS_INLINE
733 static char *trim(char *s) {
734 char *buf = s;
735
736 // skip leading spaces
737 while (*s && isspace((uint8_t)*s)) s++;
738 if (!*s) {
739 *buf = '\0';
740 return NULL;
741 }
742
743 // skip tailing spaces
744 // this way is way faster. Writes only one NUL char.
745 ssize_t l = (ssize_t)strlen(s);
746 if (--l >= 0) {
747 char *p = s + l;
748 while (p > s && isspace((uint8_t)*p)) p--;
749 *++p = '\0';
750 }
751
752 if (!*s) {
753 *buf = '\0';
754 return NULL;
755 }
756
757 return s;
758 }
759
760 // like trim(), but also remove duplicate spaces inside the string
761 ALWAYS_INLINE
762 static char *trim_all(char *buffer) {
763 char *d = buffer, *s = buffer;
764
765 // skip spaces
766 while(isspace((uint8_t)*s))
767 s++;
768
769 while(*s) {
770 // copy the non-space part
771 while(*s && !isspace((uint8_t)*s))
772 *d++ = *s++;
773
774 // add a space if we have to
775 if(*s && isspace((uint8_t)*s)) {
776 *d++ = ' ';
777 s++;
778 }
779
780 // skip spaces
781 while(isspace((uint8_t)*s)) s++;
782 }
783
784 *d = '\0';
785
786 if(d > buffer) {
787 d--;
788 if(isspace((uint8_t)*d)) *d = '\0';
789 }
790
791 return buffer;
792 }
793
794 ALWAYS_INLINE
795 static bool streq(const char *a, const char *b) {
796 if (a == b)
797 return true;
798
799 if (a == NULL || b == NULL)
800 return false;
801
802 return strcmp(a, b) == 0;
803 }
804
805 ALWAYS_INLINE
806 static bool strstartswith(const char *string, const char *prefix) {
807 if (string == NULL || prefix == NULL)
808 return false;
809
810 size_t string_len = strlen(string);
811 size_t prefix_len = strlen(prefix);
812
813 if (prefix_len > string_len)
814 return false;
815
816 return strncmp(string, prefix, prefix_len) == 0;
817 }
818
819 ALWAYS_INLINE
820 static bool strendswith(const char *string, const char *suffix) {
821 if (string == NULL || suffix == NULL)
822 return false;
823
824 size_t string_len = strlen(string);
825 size_t suffix_len = strlen(suffix);
826
827 if (suffix_len > string_len)
828 return false;
829
830 return strcmp(string + string_len - suffix_len, suffix) == 0;
831 }
832
833 ALWAYS_INLINE
834 static bool strendswith_lengths(const char *string, size_t string_len, const char *suffix, size_t suffix_len) {
835 if (string == NULL || suffix == NULL)
836 return false;
837
838 if (suffix_len > string_len)
839 return false;
840
841 return strcmp(string + string_len - suffix_len, suffix) == 0;
842 }
843
844 #endif //NETDATA_INLINED_H