OpenVPN
buffer.c
Go to the documentation of this file.
1/*
2 * OpenVPN -- An application to securely tunnel IP networks
3 * over a single UDP port, with support for SSL/TLS-based
4 * session authentication and key exchange,
5 * packet encryption, packet authentication, and
6 * packet compression.
7 *
8 * Copyright (C) 2002-2026 OpenVPN Inc <sales@openvpn.net>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2
12 * as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, see <https://www.gnu.org/licenses/>.
21 */
22
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27#include "syshead.h"
28
29#include "common.h"
30#include "buffer.h"
31#include "error.h"
32#include "mtu.h"
33#include "misc.h"
34
35#include "memdbg.h"
36
37#include <wchar.h>
38
39size_t
40array_mult_safe(const size_t m1, const size_t m2, const size_t extra)
41{
42 const size_t limit = ALLOC_SIZE_MAX;
43 unsigned long long res =
44 (unsigned long long)m1 * (unsigned long long)m2 + (unsigned long long)extra;
45 if (unlikely(m1 > limit) || unlikely(m2 > limit) || unlikely(extra > limit)
46 || unlikely(res > limit))
47 {
48 msg(M_FATAL, "attempted allocation of excessively large array");
49 }
50 return (size_t)res;
51}
52
53void
54buf_size_error(const size_t size)
55{
56 msg(M_FATAL, "fatal buffer size error, size=%zu", size);
57}
58
59struct buffer
61{
62 struct buffer buf;
63 CLEAR(buf);
64
65 if (!buf_size_valid(size))
66 {
67 buf_size_error(size);
68 }
69 buf.data = calloc(1, size);
70 buf.capacity = (int)size;
72
73 return buf;
74}
75
76struct buffer
78{
79 struct buffer buf;
80 CLEAR(buf);
81
82 if (!buf_size_valid(size))
83 {
84 buf_size_error(size);
85 }
86 buf.data = gc_malloc(size, false, gc);
87 buf.capacity = (int)size;
88 if (size)
89 {
90 *buf.data = 0;
91 }
92 return buf;
93}
94
95struct buffer
97clone_buf_debug(const struct buffer *buf, const char *file, int line)
98#else
99clone_buf(const struct buffer *buf)
100#endif
101{
102 struct buffer ret;
103 ret.data = malloc(buf->capacity);
104 ret.capacity = buf->capacity;
106 ret.offset = buf->offset;
107 ret.len = buf->len;
108#ifdef BUF_INIT_TRACKING
109 ret.debug_file = buf->debug_file;
110 ret.debug_line = buf->debug_line;
111#endif
112 memcpy(BPTR(&ret), CBPTR(buf), BLENZ(buf));
113 return ret;
114}
115
116#ifdef BUF_INIT_TRACKING
117
118bool
119buf_init_debug(struct buffer *buf, int offset, const char *file, int line)
120{
121 buf->debug_file = file;
122 buf->debug_line = line;
123 return buf_init_dowork(buf, offset);
124}
125
126#ifdef VERIFY_ALIGNMENT
127static inline int
128buf_debug_line(const struct buffer *buf)
129{
130 return buf->debug_line;
131}
132
133static const char *
134buf_debug_file(const struct buffer *buf)
135{
136 return buf->debug_file;
137}
138#endif
139
140#else /* ifdef BUF_INIT_TRACKING */
141
142#define buf_debug_line(buf) 0
143#define buf_debug_file(buf) "[UNDEF]"
144
145#endif /* ifdef BUF_INIT_TRACKING */
146
147void
148buf_clear(struct buffer *buf)
149{
150 if (buf->capacity > 0)
151 {
152 secure_memzero(buf->data, buf->capacity);
153 }
154 buf->len = 0;
155 buf->offset = 0;
156}
157
158bool
159buf_assign(struct buffer *dest, const struct buffer *src)
160{
161 if (!buf_init(dest, src->offset))
162 {
163 return false;
164 }
165 return buf_write(dest, CBPTR(src), BLENZ(src));
166}
167
168void
169free_buf(struct buffer *buf)
170{
171 free(buf->data);
172 CLEAR(*buf);
173}
174
175static void
176free_buf_gc(struct buffer *buf, struct gc_arena *gc)
177{
178 if (gc)
179 {
180 struct gc_entry **e = &gc->list;
181
182 while (*e)
183 {
184 /* check if this object is the one we want to delete */
185 if ((uintptr_t)*e + sizeof(struct gc_entry) == (uintptr_t)buf->data)
186 {
187 struct gc_entry *to_delete = *e;
188
189 /* remove element from linked list and free it */
190 *e = (*e)->next;
191 free(to_delete);
192
193 break;
194 }
195
196 e = &(*e)->next;
197 }
198 }
199
200 CLEAR(*buf);
201}
202
203/*
204 * Return a buffer for write that is a subset of another buffer
205 */
206struct buffer
207buf_sub(struct buffer *buf, int size, bool prepend)
208{
209 struct buffer ret;
210 uint8_t *data;
211
212 CLEAR(ret);
213 data = prepend ? buf_prepend(buf, size) : buf_write_alloc(buf, size);
214 if (data)
215 {
216 ret.capacity = size;
217 ret.data = data;
218 }
219 return ret;
220}
221
222/*
223 * printf append to a buffer with overflow check
224 */
225bool
226buf_printf(struct buffer *buf, const char *format, ...)
227{
228 bool ret = false;
229 if (buf_defined(buf))
230 {
231 char *ptr = (char *)BEND(buf);
232 int cap = buf_forward_capacity(buf);
233
234 if (cap > 1) /* can write more than \0 */
235 {
236 int stat;
239 stat = vsnprintf(ptr, cap, format, arglist);
241 buf->len += (int)strnlen(ptr, cap);
242 if (stat >= 0 && stat < cap)
243 {
244 ret = true;
245 }
246 }
247 }
248 return ret;
249}
250
251void
252buf_puts(struct buffer *buf, const char *str)
253{
254 char *ptr = (char *)BEND(buf);
255 int cap = buf_forward_capacity(buf);
256 if (cap > 1) /* can write more than \0 */
257 {
258 int str_len = (int)strlcpy(ptr, str, cap);
259 buf->len += (str_len < cap) ? str_len : cap - 1;
260 }
261}
262
263/*
264 * write a string to the end of a buffer that was
265 * truncated by buf_printf
266 */
267void
268buf_catrunc(struct buffer *buf, const char *str)
269{
270 if (buf_forward_capacity(buf) <= 1)
271 {
272 size_t len = strlen(str) + 1;
274 {
275 memcpy(buf->data + buf->capacity - len, str, len);
276 }
277 }
278}
279
280bool
281buffer_write_file(const char *filename, const struct buffer *buf)
282{
283 bool ret = false;
284 int fd = platform_open(filename, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR);
285 if (fd == -1)
286 {
287 msg(M_ERRNO, "Cannot open file '%s' for write", filename);
288 return false;
289 }
290
291 const ssize_t size = write(fd, CBPTR(buf), (unsigned int)BLEN(buf));
292 if (size != BLEN(buf))
293 {
294 msg(M_ERRNO, "Write error on file '%s'", filename);
295 goto cleanup;
296 }
297
298 ret = true;
299cleanup:
300 if (close(fd) < 0)
301 {
302 msg(M_ERRNO, "Close error on file %s", filename);
303 ret = false;
304 }
305 return ret;
306}
307
308/*
309 * Garbage collection
310 */
311
312void *
313gc_malloc(size_t size, bool clear, struct gc_arena *a)
314{
315 void *ret;
316 if (a)
317 {
318 struct gc_entry *e;
319 e = (struct gc_entry *)malloc(size + sizeof(struct gc_entry));
321 ret = (char *)e + sizeof(struct gc_entry);
322 e->next = a->list;
323 a->list = e;
324 }
325 else
326 {
327 ret = malloc(size);
329 }
330#ifndef ZERO_BUFFER_ON_ALLOC
331 if (clear)
332#endif
333 memset(ret, 0, size);
334 return ret;
335}
336
337void *
338gc_realloc(void *ptr, size_t size, struct gc_arena *a)
339{
340 void *ret = realloc(ptr, size);
342 if (a)
343 {
344 if (ptr && ptr != ret)
345 {
346 /* find the old entry and modify it if realloc changed
347 * the pointer */
348 struct gc_entry_special *e = NULL;
349 for (e = a->list_special; e != NULL; e = e->next)
350 {
351 if (e->addr == ptr)
352 {
353 break;
354 }
355 }
356 ASSERT(e);
357 ASSERT(e->addr == ptr);
358 e->addr = ret;
359 }
360 else if (!ptr)
361 {
362 /* sets e->addr to newptr */
363 gc_addspecial(ret, free, a);
364 }
365 }
366
367 return ret;
368}
369
370void
372{
373 struct gc_entry *e;
374 e = a->list;
375 a->list = NULL;
376
377 while (e != NULL)
378 {
379 struct gc_entry *next = e->next;
380 free(e);
381 e = next;
382 }
383}
384
385/*
386 * Functions to handle special objects in gc_entries
387 */
388
389void
391{
392 struct gc_entry_special *e;
393 e = a->list_special;
394 a->list_special = NULL;
395
396 while (e != NULL)
397 {
398 struct gc_entry_special *next = e->next;
399 e->free_fnc(e->addr);
400 free(e);
401 e = next;
402 }
403}
404
405void
406gc_addspecial(void *addr, void (*free_function)(void *), struct gc_arena *a)
407{
408 ASSERT(a);
409 struct gc_entry_special *e;
410 e = (struct gc_entry_special *)malloc(sizeof(struct gc_entry_special));
412 e->free_fnc = free_function;
413 e->addr = addr;
414
415 e->next = a->list_special;
416 a->list_special = e;
417}
418
419
420/*
421 * Transfer src arena to dest, resetting src to an empty arena.
422 */
423void
424gc_transfer(struct gc_arena *dest, struct gc_arena *src)
425{
426 if (dest && src)
427 {
428 struct gc_entry *e = src->list;
429 if (e)
430 {
431 while (e->next != NULL)
432 {
433 e = e->next;
434 }
435 e->next = dest->list;
436 dest->list = src->list;
437 src->list = NULL;
438 }
439 }
440}
441
442/*
443 * Hex dump -- Output a binary buffer to a hex string and return it.
444 */
445
446char *
447format_hex_ex(const uint8_t *data, size_t size, size_t maxoutput, unsigned int space_break_flags,
448 const char *separator, struct gc_arena *gc)
449{
450 const size_t bytes_per_hexblock = space_break_flags & FHE_SPACE_BREAK_MASK;
451 const size_t separator_len = separator ? strlen(separator) : 0;
452 const size_t out_len = maxoutput > 0
453 ? maxoutput
454 : ((size * 2) + ((size / bytes_per_hexblock) * separator_len) + 2);
455
456 struct buffer out = alloc_buf_gc(out_len, gc);
457 for (size_t i = 0; i < size; ++i)
458 {
459 if (separator && i && !(i % bytes_per_hexblock))
460 {
461 buf_printf(&out, "%s", separator);
462 }
464 {
465 buf_printf(&out, "%02X", data[i]);
466 }
467 else
468 {
469 buf_printf(&out, "%02x", data[i]);
470 }
471 }
472 buf_catrunc(&out, "[more...]");
473 return (char *)out.data;
474}
475
476/*
477 * remove specific trailing character
478 */
479
480void
482{
483 uint8_t *cp = BLAST(buf);
484 if (cp && *cp == remove)
485 {
486 *cp = '\0';
487 --buf->len;
488 }
489}
490
491/*
492 * force a null termination even it requires
493 * truncation of the last char.
494 */
495void
497{
498 const uint8_t *last = BLAST(buf);
499 if (last && *last == '\0') /* already terminated? */
500 {
501 return;
502 }
503
504 if (!buf_safe(buf, 1)) /* make space for trailing null */
505 {
506 buf_inc_len(buf, -1);
507 }
508
509 buf_write_u8(buf, 0);
510}
511
512/*
513 * Remove trailing \r and \n chars and ensure
514 * null termination.
515 */
516void
517buf_chomp(struct buffer *buf)
518{
519 while (true)
520 {
521 const uint8_t *last = BLAST(buf);
522 if (!last)
523 {
524 break;
525 }
526 if (char_class(*last, CC_CRLF | CC_NULL))
527 {
528 if (!buf_inc_len(buf, -1))
529 {
530 break;
531 }
532 }
533 else
534 {
535 break;
536 }
537 }
539}
540
541const char *
543{
544 while (*str)
545 {
546 const char c = *str;
547 if (!(c == ' ' || c == '\t'))
548 {
549 break;
550 }
551 ++str;
552 }
553 return str;
554}
555
556#ifdef _WIN32
557/*
558 * like buf_null_terminate, but operate on strings
559 */
560void
562{
564 if (len < capacity)
565 {
566 *(str + len) = '\0';
567 }
568 else if (len == capacity)
569 {
570 *(str + len - 1) = '\0';
571 }
572}
573#endif
574
575/*
576 * Remove trailing \r and \n chars.
577 */
578void
579chomp(char *str)
580{
581 rm_trailing_chars(str, "\r\n");
582}
583
584/*
585 * Remove trailing chars
586 */
587void
589{
590 size_t len = strlen(str);
591 while (len > 0 && strchr(what_to_delete, str[len - 1]) != NULL)
592 {
593 str[--len] = '\0';
594 }
595}
596
597/*
598 * Allocate a string
599 */
600char *
601string_alloc(const char *str, struct gc_arena *gc)
602{
603 if (str)
604 {
605 const size_t n = strlen(str) + 1;
606 char *ret;
607
608 if (gc)
609 {
610 ret = gc_malloc(n, false, gc);
611 }
612 else
613 {
614 /* If there are no garbage collector available, it's expected
615 * that the caller cleans up afterwards. This is coherent with the
616 * earlier behaviour when gc_malloc() would be called with gc == NULL
617 */
618 ret = calloc(1, n);
620 }
621 memcpy(ret, str, n);
622 return ret;
623 }
624 else
625 {
626 return NULL;
627 }
628}
629
630/*
631 * Erase all characters in a string
632 */
633void
635{
636 if (str)
637 {
639 }
640}
641
642/*
643 * Return the length of a string array
644 */
645int
646string_array_len(const char **array)
647{
648 int i = 0;
649 if (array)
650 {
651 for (; *array; ++array)
652 {
653 ++i;
654 }
655 }
656 return i;
657}
658
659char *
660print_argv(const char **p, struct gc_arena *gc, const unsigned int flags)
661{
662 struct buffer out = alloc_buf_gc(256, gc);
663 int i = 0;
664 for (; *p; ++p)
665 {
666 const char *cp = *p;
667 if (i)
668 {
669 buf_printf(&out, " ");
670 }
671 if (flags & PA_BRACKET)
672 {
673 buf_printf(&out, "[%s]", cp);
674 }
675 else
676 {
677 buf_printf(&out, "%s", cp);
678 }
679 ++i;
680 }
681 return BSTR(&out);
682}
683
684/*
685 * Allocate a string inside a buffer
686 */
687struct buffer
689{
690 struct buffer buf;
691
692 ASSERT(str);
693
694 const char *copy = string_alloc(str, gc);
695 buf_set_read(&buf, (const uint8_t *)copy, strlen(str) + 1);
696
697 if (buf.len > 0) /* Don't count trailing '\0' as part of length */
698 {
699 --buf.len;
700 }
701
702 return buf;
703}
704
705/*
706 * String comparison
707 */
708
709bool
710buf_string_match_head_str(const struct buffer *src, const char *match)
711{
712 const size_t size = strlen(match);
713 if (!buf_size_valid(size) || (int)size > src->len)
714 {
715 return false;
716 }
717 return memcmp(CBPTR(src), match, size) == 0;
718}
719
720bool
722{
724 {
726 return true;
727 }
728 else
729 {
730 return false;
731 }
732}
733
734int
735buf_substring_len(const struct buffer *buf, int delim)
736{
737 int i = 0;
738 struct buffer tmp = *buf;
739 int c;
740
741 while ((c = buf_read_u8(&tmp)) >= 0)
742 {
743 ++i;
744 if (c == delim)
745 {
746 return i;
747 }
748 }
749 return -1;
750}
751
752/*
753 * String parsing
754 */
755
756bool
757buf_parse(struct buffer *buf, const int delim, char *line, const int size)
758{
759 bool eol = false;
760 int n = 0;
761 int c;
762
763 ASSERT(size > 0);
764
765 do
766 {
767 c = buf_peek_u8(buf);
768 if (c < 0)
769 {
770 eol = true;
771 line[n] = 0;
772 break;
773 }
774 if (c == delim)
775 {
776 buf_advance(buf, 1);
777 line[n] = 0;
778 break;
779 }
780 if (n >= (size - 1))
781 {
782 break;
783 }
784 buf_advance(buf, 1);
785 line[n++] = (char)c;
786 } while (c);
787
788 line[size - 1] = '\0';
789 return !(eol && line[0] == '\0');
790}
791
792/*
793 * Print a string which might be NULL
794 */
795const char *
796np(const char *str)
797{
798 if (str)
799 {
800 return str;
801 }
802 else
803 {
804 return "[NULL]";
805 }
806}
807
808/*
809 * Classify and mutate strings based on character types.
810 */
811
812/* Note 1: This functions depends on getting an unsigned
813 char. Both the is*() functions and our own checks expect it
814 this way.
815 Note 2: For CC_PRINT we just accept everything >= 32, so
816 if we ingest non-ASCII UTF-8 we will classify it as
817 printable since it will be >= 128. Other encodings are
818 not officially supported.
819*/
820bool
821char_class(const unsigned char c, const unsigned int flags)
822{
823 if (!flags)
824 {
825 return false;
826 }
827 if (flags & CC_ANY)
828 {
829 return true;
830 }
831
832 if ((flags & CC_NULL) && c == '\0')
833 {
834 return true;
835 }
836
837 if ((flags & CC_ALNUM) && isalnum(c))
838 {
839 return true;
840 }
841 if ((flags & CC_ALPHA) && isalpha(c))
842 {
843 return true;
844 }
845 if ((flags & CC_ASCII) && isascii(c))
846 {
847 return true;
848 }
849 if ((flags & CC_CNTRL) && iscntrl(c))
850 {
851 return true;
852 }
853 if ((flags & CC_DIGIT) && isdigit(c))
854 {
855 return true;
856 }
857 /* allow ascii non-control and UTF-8, consider DEL to be a control */
858 if ((flags & CC_PRINT) && (c >= 32 && c != 127))
859 {
860 return true;
861 }
862 if ((flags & CC_PUNCT) && ispunct(c))
863 {
864 return true;
865 }
866 if ((flags & CC_SPACE) && isspace(c))
867 {
868 return true;
869 }
870 if ((flags & CC_XDIGIT) && isxdigit(c))
871 {
872 return true;
873 }
874
875 if ((flags & CC_BLANK) && (c == ' ' || c == '\t'))
876 {
877 return true;
878 }
879 if ((flags & CC_NEWLINE) && c == '\n')
880 {
881 return true;
882 }
883 if ((flags & CC_CR) && c == '\r')
884 {
885 return true;
886 }
887
888 if ((flags & CC_BACKSLASH) && c == '\\')
889 {
890 return true;
891 }
892 if ((flags & CC_UNDERBAR) && c == '_')
893 {
894 return true;
895 }
896 if ((flags & CC_DASH) && c == '-')
897 {
898 return true;
899 }
900 if ((flags & CC_DOT) && c == '.')
901 {
902 return true;
903 }
904 if ((flags & CC_COMMA) && c == ',')
905 {
906 return true;
907 }
908 if ((flags & CC_COLON) && c == ':')
909 {
910 return true;
911 }
912 if ((flags & CC_SLASH) && c == '/')
913 {
914 return true;
915 }
916 if ((flags & CC_SINGLE_QUOTE) && c == '\'')
917 {
918 return true;
919 }
920 if ((flags & CC_DOUBLE_QUOTE) && c == '\"')
921 {
922 return true;
923 }
924 if ((flags & CC_REVERSE_QUOTE) && c == '`')
925 {
926 return true;
927 }
928 if ((flags & CC_PERCENT) && c == '%')
929 {
930 return true;
931 }
932 if ((flags & CC_EXCLAMATION) && c == '!')
933 {
934 return true;
935 }
936 if ((flags & CC_LESS_THAN) && c == '<')
937 {
938 return true;
939 }
940 if ((flags & CC_GREATER_THAN) && c == '>')
941 {
942 return true;
943 }
944 if ((flags & CC_PIPE) && c == '|')
945 {
946 return true;
947 }
948 if ((flags & CC_QUESTION_MARK) && c == '?')
949 {
950 return true;
951 }
952 if ((flags & CC_ASTERISK) && c == '*')
953 {
954 return true;
955 }
956
957 return false;
958}
959
960static inline bool
961char_inc_exc(const char c, const unsigned int inclusive, const unsigned int exclusive)
962{
963 return char_class((unsigned char)c, inclusive)
964 && !char_class((unsigned char)c, exclusive);
965}
966
967bool
968string_class(const char *str, const unsigned int inclusive, const unsigned int exclusive)
969{
970 ASSERT(str);
971 for (; *str; ++str)
972 {
974 {
975 return false;
976 }
977 }
978 return true;
979}
980
981/*
982 * Modify string in place.
983 * Guaranteed to not increase string length.
984 */
985bool
986string_mod(char *str, const unsigned int inclusive, const unsigned int exclusive,
987 const char replace)
988{
989 ASSERT(str);
990
991 char *out = str;
992 const char *in = out;
993 bool ret = true;
994
995 while (true)
996 {
997 char c = *in++;
998 if (c)
999 {
1001 {
1002 c = replace;
1003 ret = false;
1004 }
1005 if (c)
1006 {
1007 *out++ = c;
1008 }
1009 }
1010 else
1011 {
1012 *out = '\0';
1013 break;
1014 }
1015 }
1016 return ret;
1017}
1018
1019bool
1020string_check_buf(const struct buffer *buf, const unsigned int inclusive, const unsigned int exclusive)
1021{
1022 ASSERT(buf);
1023
1024 for (int i = 0; i < BLEN(buf); i++)
1025 {
1026 char c = CBSTR(buf)[i];
1027
1029 {
1030 return false;
1031 }
1032 }
1033 return true;
1034}
1035
1036const char *
1037string_mod_const(const char *str, const unsigned int inclusive, const unsigned int exclusive,
1038 const char replace, struct gc_arena *gc)
1039{
1040 if (str)
1041 {
1042 char *buf = string_alloc(str, gc);
1044 return buf;
1045 }
1046 else
1047 {
1048 return NULL;
1049 }
1050}
1051
1052void
1053string_replace_leading(char *str, const char match, const char replace)
1054{
1055 ASSERT(match != '\0');
1056 while (*str)
1057 {
1058 if (*str == match)
1059 {
1060 *str = replace;
1061 }
1062 else
1063 {
1064 break;
1065 }
1066 ++str;
1067 }
1068}
1069
1070bool
1071string_defined_equal(const char *s1, const char *s2)
1072{
1073 if (s1 && s2)
1074 {
1075 return !strcmp(s1, s2);
1076 }
1077 else
1078 {
1079 return false;
1080 }
1081}
1082
1083bool
1084streqnull(const char *a, const char *b)
1085{
1086 if (a == NULL && b == NULL)
1087 {
1088 return true;
1089 }
1090 else if (a == NULL || b == NULL)
1091 {
1092 return false;
1093 }
1094 else
1095 {
1096 return streq(a, b);
1097 }
1098}
1099
1100char *
1101string_substitute(const char *src, char from, char to, struct gc_arena *gc)
1102{
1103 char *ret = gc_malloc(strlen(src) + 1, true, gc);
1104 char *dest = ret;
1105
1106 for (; *src; ++src)
1107 {
1108 *dest++ = (*src == from) ? to : *src;
1109 }
1110 *dest = '\0';
1111 return ret;
1112}
1113
1114bool
1115checked_snprintf(char *str, size_t size, const char *format, ...)
1116{
1119 ASSERT(size < INT_MAX);
1120 int len = vsnprintf(str, size, format, arglist);
1121 va_end(arglist);
1122 return (len >= 0 && len < (ssize_t)size);
1123}
1124
1125#ifdef VERIFY_ALIGNMENT
1126void
1127valign4(const struct buffer *buf, const char *file, const int line)
1128{
1129 if (buf && buf->len)
1130 {
1131 msglvl_t msglevel = D_ALIGN_DEBUG;
1132 const uintptr_t u = (uintptr_t)CBPTR(buf);
1133
1134 if (u & (PAYLOAD_ALIGN - 1))
1135 {
1136 msglevel = D_ALIGN_ERRORS;
1137 }
1138
1139 msg(msglevel, "%sAlignment at %s/%d ptr=" ptr_format " OLC=%d/%d/%d I=%s/%d",
1140 (msglevel == D_ALIGN_ERRORS) ? "ERROR: " : "", file, line, (ptr_type)buf->data,
1141 buf->offset, buf->len, buf->capacity, buf_debug_file(buf), buf_debug_line(buf));
1142 }
1143}
1144#endif /* ifdef VERIFY_ALIGNMENT */
1145
1146/*
1147 * struct buffer_list
1148 */
1149struct buffer_list *
1151{
1152 struct buffer_list *ret;
1153 ALLOC_OBJ_CLEAR(ret, struct buffer_list);
1154 ret->size = 0;
1155 return ret;
1156}
1157
1158void
1160{
1161 if (ol)
1162 {
1164 free(ol);
1165 }
1166}
1167
1168bool
1170{
1171 return ol && ol->head != NULL && ol->size > 0;
1172}
1173
1174void
1176{
1177 struct buffer_entry *e = ol->head;
1178 while (e)
1179 {
1180 struct buffer_entry *next = e->next;
1181 free_buf(&e->buf);
1182 free(e);
1183 e = next;
1184 }
1185 ol->head = ol->tail = NULL;
1186 ol->size = 0;
1187}
1188
1189void
1190buffer_list_push(struct buffer_list *ol, const char *str)
1191{
1192 if (str)
1193 {
1194 const size_t len = strlen((const char *)str);
1195 struct buffer_entry *e = buffer_list_push_data(ol, str, len + 1);
1196 if (e)
1197 {
1198 e->buf.len--; /* Don't count trailing '\0' as part of length */
1199 }
1200 }
1201}
1202
1203struct buffer_entry *
1204buffer_list_push_data(struct buffer_list *ol, const void *data, size_t size)
1205{
1206 struct buffer_entry *e = NULL;
1207 if (data)
1208 {
1209 ALLOC_OBJ_CLEAR(e, struct buffer_entry);
1210
1211 ++ol->size;
1212 if (ol->tail)
1213 {
1214 ASSERT(ol->head);
1215 ol->tail->next = e;
1216 }
1217 else
1218 {
1219 ASSERT(!ol->head);
1220 ol->head = e;
1221 }
1222 e->buf = alloc_buf(size);
1223 memcpy(e->buf.data, data, size);
1224 /* Note: size implicitly checked by alloc_buf */
1225 e->buf.len = (int)size;
1226 ol->tail = e;
1227 }
1228 return e;
1229}
1230
1231struct buffer *
1233{
1234 if (ol && ol->head)
1235 {
1236 return &ol->head->buf;
1237 }
1238 else
1239 {
1240 return NULL;
1241 }
1242}
1243
1244void
1245buffer_list_aggregate_separator(struct buffer_list *bl, const size_t max_len, const char *sep)
1246{
1247 const size_t sep_len = strlen(sep);
1248 struct buffer_entry *more = bl->head;
1249 size_t size = 0;
1250 size_t count = 0;
1251 for (; more; ++count)
1252 {
1253 size_t extra_len = BLENZ(&more->buf) + sep_len;
1254 if (size + extra_len > max_len)
1255 {
1256 break;
1257 }
1258
1259 size += extra_len;
1260 more = more->next;
1261 }
1262
1263 if (count >= 2)
1264 {
1265 struct buffer_entry *f;
1266 ALLOC_OBJ_CLEAR(f, struct buffer_entry);
1267 f->buf = alloc_buf(size + 1); /* prevent 0-byte malloc */
1268
1269 struct buffer_entry *e = bl->head;
1270 for (size_t i = 0; e && i < count; ++i)
1271 {
1272 struct buffer_entry *next = e->next;
1273 buf_copy(&f->buf, &e->buf);
1274 buf_write(&f->buf, sep, sep_len);
1275 free_buf(&e->buf);
1276 free(e);
1277 e = next;
1278 }
1279 bl->head = f;
1280 bl->size -= count - 1;
1281 f->next = more;
1282 if (!more)
1283 {
1284 bl->tail = f;
1285 }
1286 }
1287}
1288
1289void
1290buffer_list_aggregate(struct buffer_list *bl, const size_t max)
1291{
1293}
1294
1295void
1297{
1298 if (buffer_list_defined(ol))
1299 {
1300 struct buffer_entry *e = ol->head->next;
1301 free_buf(&ol->head->buf);
1302 free(ol->head);
1303 ol->head = e;
1304 --ol->size;
1305 if (!e)
1306 {
1307 ol->tail = NULL;
1308 }
1309 }
1310}
1311
1312void
1313buffer_list_advance(struct buffer_list *ol, ssize_t n)
1314{
1315 if (ol->head)
1316 {
1317 struct buffer *buf = &ol->head->buf;
1318 ASSERT(buf_advance(buf, n));
1319 if (!BLEN(buf))
1320 {
1322 }
1323 }
1324}
1325
1326struct buffer_list *
1327buffer_list_file(const char *fn, int max_line_len)
1328{
1329 FILE *fp = platform_fopen(fn, "r");
1330 struct buffer_list *bl = NULL;
1331
1332 if (fp)
1333 {
1334 char *line = (char *)malloc(max_line_len);
1335 if (line)
1336 {
1337 bl = buffer_list_new();
1338 while (fgets(line, max_line_len, fp) != NULL)
1339 {
1340 buffer_list_push(bl, line);
1341 }
1342 free(line);
1343 }
1344 fclose(fp);
1345 }
1346 return bl;
1347}
1348
1349struct buffer
1351{
1352 struct buffer ret = { 0 };
1353
1354 platform_stat_t file_stat = { 0 };
1355 if (platform_stat(filename, &file_stat) < 0)
1356 {
1357 return ret;
1358 }
1359
1360 FILE *fp = platform_fopen(filename, "r");
1361 if (!fp)
1362 {
1363 return ret;
1364 }
1365
1366 const size_t size = file_stat.st_size;
1367 ret = alloc_buf_gc(size + 1, gc); /* space for trailing \0 */
1368 size_t read_size = fread(BPTR(&ret), 1, size, fp);
1369 if (read_size == 0)
1370 {
1371 free_buf_gc(&ret, gc);
1372 goto cleanup;
1373 }
1376
1377cleanup:
1378 fclose(fp);
1379 return ret;
1380}
1381
1382char *
1383buf_extract_field(struct buffer *buf, char sep, struct gc_arena *gc)
1384{
1385 if (!buf_valid(buf))
1386 {
1387 return NULL;
1388 }
1389
1390 const uint8_t *seppos = memchr(CBPTR(buf), sep, buf_len(buf));
1391 if (!seppos)
1392 {
1393 return NULL;
1394 }
1395 size_t field_len = seppos - CBPTR(buf);
1396
1397 char *field = gc_malloc(field_len + 1, false, gc);
1398
1399 memcpy(field, CBPTR(buf), field_len);
1400 field[field_len] = 0;
1401
1402 buf_advance(buf, field_len + 1);
1403 return field;
1404}
bool buffer_list_defined(const struct buffer_list *ol)
Checks if the list is valid and non-empty.
Definition buffer.c:1169
bool buf_string_compare_advance(struct buffer *src, const char *match)
Compare the head of src with match and advance past it if equal.
Definition buffer.c:721
struct buffer_entry * buffer_list_push_data(struct buffer_list *ol, const void *data, size_t size)
Allocates and appends a new buffer containing data of length size.
Definition buffer.c:1204
void rm_trailing_chars(char *str, const char *what_to_delete)
Remove all trailing characters that appear in a given set.
Definition buffer.c:588
void string_null_terminate(char *str, int len, int capacity)
Null-terminate a fixed-length string buffer.
Definition buffer.c:561
void free_buf(struct buffer *buf)
Free the memory allocated for a buffer.
Definition buffer.c:169
void buffer_list_aggregate_separator(struct buffer_list *bl, const size_t max_len, const char *sep)
Aggregates as many buffers as possible from bl in a new buffer of maximum length max_len .
Definition buffer.c:1245
static bool char_inc_exc(const char c, const unsigned int inclusive, const unsigned int exclusive)
Definition buffer.c:961
void buf_clear(struct buffer *buf)
Zeroise and reset a buffer.
Definition buffer.c:148
void buffer_list_reset(struct buffer_list *ol)
Empty the list ol and frees all the contained buffers.
Definition buffer.c:1175
static void free_buf_gc(struct buffer *buf, struct gc_arena *gc)
Definition buffer.c:176
const char * skip_leading_whitespace(const char *str)
Return a pointer past any leading whitespace in a string.
Definition buffer.c:542
void buffer_list_aggregate(struct buffer_list *bl, const size_t max)
Aggregates as many buffers as possible from bl in a new buffer of maximum length max_len .
Definition buffer.c:1290
struct buffer clone_buf(const struct buffer *buf)
Duplicate a buffer, including its content.
Definition buffer.c:99
void x_gc_freespecial(struct gc_arena *a)
Free all specially-allocated entries in a garbage collection arena.
Definition buffer.c:390
bool buffer_write_file(const char *filename, const struct buffer *buf)
Write buffer contents to file.
Definition buffer.c:281
void buf_catrunc(struct buffer *buf, const char *str)
Append a string to the physical end of a buffer that was truncated by buf_printf().
Definition buffer.c:268
#define buf_debug_file(buf)
Definition buffer.c:143
void buffer_list_pop(struct buffer_list *ol)
Remove and free the head buffer of the list.
Definition buffer.c:1296
bool buf_printf(struct buffer *buf, const char *format,...)
printf-style append to a buffer with overflow check.
Definition buffer.c:226
void string_replace_leading(char *str, const char match, const char replace)
Replace all leading occurrences of a character in a string.
Definition buffer.c:1053
char * buf_extract_field(struct buffer *buf, char sep, struct gc_arena *gc)
Extract a field from buf that ends with the sep character.
Definition buffer.c:1383
struct buffer_list * buffer_list_file(const char *fn, int max_line_len)
Read a file into a buffer list, one buffer per line.
Definition buffer.c:1327
void string_clear(char *str)
Securely clear a null-terminated string.
Definition buffer.c:634
#define buf_debug_line(buf)
Definition buffer.c:142
void gc_transfer(struct gc_arena *dest, struct gc_arena *src)
Move all allocations from one garbage collection arena to another.
Definition buffer.c:424
bool string_class(const char *str, const unsigned int inclusive, const unsigned int exclusive)
Test whether all characters in a string satisfy a character class filter.
Definition buffer.c:968
char * print_argv(const char **p, struct gc_arena *gc, const unsigned int flags)
Format a NULL-terminated argument vector as a single string.
Definition buffer.c:660
void buf_null_terminate(struct buffer *buf)
Force a null terminator at the end of the buffer content.
Definition buffer.c:496
struct buffer buf_sub(struct buffer *buf, int size, bool prepend)
Return a sub-buffer of another buffer.
Definition buffer.c:207
void * gc_realloc(void *ptr, size_t size, struct gc_arena *a)
allows to realloc a pointer previously allocated by gc_malloc or gc_realloc
Definition buffer.c:338
struct buffer_list * buffer_list_new(void)
Allocate an empty buffer list of capacity max_size.
Definition buffer.c:1150
void chomp(char *str)
Remove trailing newline and carriage-return characters from a string.
Definition buffer.c:579
struct buffer * buffer_list_peek(struct buffer_list *ol)
Retrieve the head buffer.
Definition buffer.c:1232
const char * np(const char *str)
Return a printable representation of a string that might be NULL.
Definition buffer.c:796
bool string_defined_equal(const char *s1, const char *s2)
compare two strings which might be NULL, but NULL is never equal
Definition buffer.c:1071
size_t array_mult_safe(const size_t m1, const size_t m2, const size_t extra)
Safely compute the product of two sizes plus an extra amount.
Definition buffer.c:40
void buffer_list_free(struct buffer_list *ol)
Frees a buffer list and all the buffers in it.
Definition buffer.c:1159
void * gc_malloc(size_t size, bool clear, struct gc_arena *a)
Allocate memory and, optionally, zero it.
Definition buffer.c:313
void buf_puts(struct buffer *buf, const char *str)
Append a string to a buffer with overflow check.
Definition buffer.c:252
bool buf_assign(struct buffer *dest, const struct buffer *src)
Assign the content of one buffer to another.
Definition buffer.c:159
struct buffer alloc_buf_gc(size_t size, struct gc_arena *gc)
Allocate a buffer of the given size under garbage collection.
Definition buffer.c:77
char * format_hex_ex(const uint8_t *data, size_t size, size_t maxoutput, unsigned int space_break_flags, const char *separator, struct gc_arena *gc)
Format a binary buffer as a hex string.
Definition buffer.c:447
bool string_mod(char *str, const unsigned int inclusive, const unsigned int exclusive, const char replace)
Modifies a string in place by replacing certain classes of characters of it with a specified characte...
Definition buffer.c:986
struct buffer alloc_buf(size_t size)
Allocate a buffer of the given size.
Definition buffer.c:60
const char * string_mod_const(const char *str, const unsigned int inclusive, const unsigned int exclusive, const char replace, struct gc_arena *gc)
Returns a copy of a string with certain classes of characters of it replaced with a specified charact...
Definition buffer.c:1037
void gc_addspecial(void *addr, void(*free_function)(void *), struct gc_arena *a)
Register an address with a custom free function in a garbage collection arena.
Definition buffer.c:406
void buffer_list_advance(struct buffer_list *ol, ssize_t n)
Advance past n bytes in the head buffer, popping it if it becomes empty.
Definition buffer.c:1313
int string_array_len(const char **array)
Return the number of elements in a NULL-terminated array of strings.
Definition buffer.c:646
struct buffer buffer_read_from_file(const char *filename, struct gc_arena *gc)
buffer_read_from_file - copy the content of a file into a buffer
Definition buffer.c:1350
void buf_rmtail(struct buffer *buf, uint8_t remove)
Remove all occurrences of a specific byte from the end of a buffer.
Definition buffer.c:481
char * string_substitute(const char *src, char from, char to, struct gc_arena *gc)
Definition buffer.c:1101
bool buf_parse(struct buffer *buf, const int delim, char *line, const int size)
Extract the next token from a buffer, delimited by a given character.
Definition buffer.c:757
bool checked_snprintf(char *str, size_t size, const char *format,...)
Like snprintf() but returns an boolean.
Definition buffer.c:1115
void buf_size_error(const size_t size)
Report a buffer size error and abort.
Definition buffer.c:54
char * string_alloc(const char *str, struct gc_arena *gc)
Duplicate a string, allocating memory under garbage collection.
Definition buffer.c:601
struct buffer string_alloc_buf(const char *str, struct gc_arena *gc)
Allocate a buffer containing a copy of the given string.
Definition buffer.c:688
void buffer_list_push(struct buffer_list *ol, const char *str)
Allocates and appends a new buffer containing str as data to ol.
Definition buffer.c:1190
void x_gc_free(struct gc_arena *a)
Free all plain allocations in a garbage collection arena.
Definition buffer.c:371
void buf_chomp(struct buffer *buf)
Remove trailing newline and carriage-return characters from a buffer.
Definition buffer.c:517
bool string_check_buf(const struct buffer *buf, const unsigned int inclusive, const unsigned int exclusive)
Check a buffer if it only consists of allowed characters.
Definition buffer.c:1020
bool char_class(const unsigned char c, const unsigned int flags)
Test whether a character belongs to one or more character classes.
Definition buffer.c:821
bool buf_string_match_head_str(const struct buffer *src, const char *match)
Return true if the head of src matches the string match.
Definition buffer.c:710
int buf_substring_len(const struct buffer *buf, int delim)
Return the number of bytes in a buffer up to and including a delimiter.
Definition buffer.c:735
bool streqnull(const char *a, const char *b)
compare two strings which might be NULL
Definition buffer.c:1084
Buffer management functions and garbage collection.
#define CC_EXCLAMATION
exclamation mark
Definition buffer.h:1705
#define CC_PERCENT
percent sign
Definition buffer.h:1704
#define CC_COMMA
comma
Definition buffer.h:1698
#define BEND(buf)
Return a pointer one past the end of the buffer content.
Definition buffer.h:143
#define CC_DASH
dash
Definition buffer.h:1696
static bool buf_size_valid(const size_t size)
Return true iff size is within the allowed buffer size range.
Definition buffer.h:543
#define CC_DOUBLE_QUOTE
double quote
Definition buffer.h:1702
#define BLAST(buf)
Return a pointer to the last byte of the buffer content, or NULL if empty.
Definition buffer.h:147
#define CC_BLANK
space or tab
Definition buffer.h:1690
static bool buf_init_dowork(struct buffer *buf, int offset)
Initialise a buffer with a given initial offset.
Definition buffer.h:635
#define CC_PIPE
pipe
Definition buffer.h:1708
#define BSTR(buf)
Return the buffer content pointer cast to char *.
Definition buffer.h:157
static bool buf_copy(struct buffer *dest, const struct buffer *src)
Copy the content of one buffer to the end of another.
Definition buffer.h:1342
#define CC_ANY
any character
Definition buffer.h:1677
#define BPTR(buf)
Return a pointer to the start of the buffer content.
Definition buffer.h:139
#define CC_XDIGIT
hex digit isxdigit()
Definition buffer.h:1688
#define CC_COLON
colon
Definition buffer.h:1699
#define CC_SINGLE_QUOTE
single quote
Definition buffer.h:1701
#define streq(x, y)
helper to work around the semantics of the strcmp() return value
Definition buffer.h:1813
static bool buf_inc_len(struct buffer *buf, int inc)
Increase or decrease the length of a buffer.
Definition buffer.h:1119
#define CC_DIGIT
digit isdigit()
Definition buffer.h:1684
static bool buf_valid(const struct buffer *buf)
Return true iff buf is valid.
Definition buffer.h:414
#define PA_BRACKET
Flag for print_argv(): wrap each argument in square brackets.
Definition buffer.h:229
#define CC_DOT
dot
Definition buffer.h:1697
#define CC_CRLF
carriage return or newline
Definition buffer.h:1714
static bool buf_safe(const struct buffer *buf, size_t len)
Check whether len bytes can be appended to a buffer.
Definition buffer.h:994
#define ALLOC_SIZE_MAX
Maximum size for a single array allocation (checked by array_mult_safe()).
Definition buffer.h:2002
#define CC_ASTERISK
asterisk
Definition buffer.h:1710
#define CC_ALPHA
alphabetic isalpha()
Definition buffer.h:1681
#define CC_NEWLINE
newline
Definition buffer.h:1691
static uint8_t * buf_prepend(struct buffer *buf, ssize_t size)
Make space at the front of a buffer for prepending data.
Definition buffer.h:1142
static int buf_len(const struct buffer *buf)
Return the length of the buffer content.
Definition buffer.h:462
static void buf_set_read(struct buffer *buf, const uint8_t *data, size_t size)
Initialise a buffer with an externally provided read-only memory region.
Definition buffer.h:687
static int buf_forward_capacity(const struct buffer *buf)
Return the number of bytes that can still be appended to the buffer.
Definition buffer.h:1038
#define CBPTR(buf)
Return a const pointer to the start of the buffer content.
Definition buffer.h:141
static void secure_memzero(void *data, size_t len)
Securely zeroise memory.
Definition buffer.h:748
static uint8_t * buf_write_alloc(struct buffer *buf, size_t size)
Reserve space at the end of a buffer for writing.
Definition buffer.h:1189
static bool buf_advance(struct buffer *buf, ssize_t size)
Advance the content start of a buffer, consuming bytes from the front.
Definition buffer.h:1165
#define CC_REVERSE_QUOTE
reverse quote
Definition buffer.h:1703
static int buf_peek_u8(const struct buffer *buf)
Return the first byte of the buffer without consuming it.
Definition buffer.h:1471
#define CC_SPACE
whitespace isspace()
Definition buffer.h:1687
#define CC_ASCII
ASCII character.
Definition buffer.h:1682
static bool buf_write(struct buffer *dest, const void *src, size_t size)
Append data to a buffer.
Definition buffer.h:1239
#define CC_BACKSLASH
backslash
Definition buffer.h:1694
#define CC_CNTRL
control character iscntrl()
Definition buffer.h:1683
#define CC_LESS_THAN
less than sign
Definition buffer.h:1706
static bool buf_write_u8(struct buffer *dest, uint8_t data)
Append a uint8_t to a buffer.
Definition buffer.h:1283
static int buf_read_u8(struct buffer *buf)
Read and consume a uint8_t from the front of a buffer.
Definition buffer.h:1490
#define BLEN(buf)
Return the length of the buffer content in bytes.
Definition buffer.h:151
#define CC_CR
carriage return
Definition buffer.h:1692
#define CC_SLASH
slash
Definition buffer.h:1700
#define CC_UNDERBAR
underscore
Definition buffer.h:1695
#define CC_GREATER_THAN
greater than sign
Definition buffer.h:1707
#define BLENZ(buf)
Return the length of the buffer content as a size_t.
Definition buffer.h:153
void buf_size_error(const size_t size)
Report a buffer size error and abort.
Definition buffer.c:54
static void check_malloc_return(void *p)
Abort if a memory allocation returned NULL.
Definition buffer.h:2135
#define CC_NULL
null character \0
Definition buffer.h:1678
#define CBSTR(buf)
Return the buffer content pointer cast to const char *.
Definition buffer.h:159
#define CC_PRINT
printable (>= 32, != 127)
Definition buffer.h:1685
#define CC_QUESTION_MARK
question mark
Definition buffer.h:1709
#define ALLOC_OBJ_CLEAR(dptr, type)
Allocate and zero-initialise memory for a single object of the given type.
Definition buffer.h:2027
static bool buf_defined(const struct buffer *buf)
Return true iff buf has a non-NULL data pointer.
Definition buffer.h:399
#define CC_ALNUM
alphanumeric isalnum()
Definition buffer.h:1680
#define buf_init(buf, offset)
Definition buffer.h:364
#define FHE_SPACE_BREAK_MASK
Mask for the space_break_flags field of format_hex_ex(): number of bytes between separators (lower 8 ...
Definition buffer.h:925
#define CC_PUNCT
punctuation ispunct()
Definition buffer.h:1686
#define FHE_CAPS
Flag for format_hex_ex(): output hex digits in upper case.
Definition buffer.h:927
static int buf_forward_capacity_total(const struct buffer *buf)
Return the total number of bytes available from the current offset to the end of the allocated memory...
Definition buffer.h:1067
unsigned long ptr_type
Definition common.h:59
#define ptr_format
Definition common.h:50
size_t strlcpy(char *dest, const char *src, size_t maxlen)
#define D_ALIGN_ERRORS
Definition errlevel.h:69
#define D_ALIGN_DEBUG
Definition errlevel.h:141
@ write
#define PAYLOAD_ALIGN
Definition mtu.h:100
#define CLEAR(x)
Definition basic.h:32
#define M_FATAL
Definition error.h:90
#define msg(flags,...)
Definition error.h:152
unsigned int msglvl_t
Definition error.h:77
#define ASSERT(x)
Definition error.h:221
#define M_ERRNO
Definition error.h:95
FILE * platform_fopen(const char *path, const char *mode)
Definition platform.c:500
int platform_open(const char *path, int flags, int mode)
Definition platform.c:513
int platform_stat(const char *path, platform_stat_t *buf)
Definition platform.c:526
struct _stat platform_stat_t
Definition platform.h:118
One node in a buffer_list linked list.
Definition buffer.h:2152
struct buffer_entry * next
Pointer to the next node, or NULL.
Definition buffer.h:2154
struct buffer buf
The buffer stored in this list node.
Definition buffer.h:2153
A singly-linked list of buffers, with head/tail pointers for O(1) push.
Definition buffer.h:2159
size_t size
Current number of entries.
Definition buffer.h:2162
struct buffer_entry * tail
Last item pushed.
Definition buffer.h:2161
struct buffer_entry * head
Next item to pop/peek.
Definition buffer.h:2160
Wrapper structure for dynamically allocated memory.
Definition buffer.h:71
int capacity
Size in bytes of memory allocated by malloc().
Definition buffer.h:72
uint8_t * data
Pointer to the allocated memory.
Definition buffer.h:78
int len
Length in bytes of the actual content within the allocated memory.
Definition buffer.h:76
int offset
Offset in bytes of the actual content within the allocated memory.
Definition buffer.h:74
Garbage collection arena used to keep track of dynamically allocated memory.
Definition buffer.h:127
struct gc_entry_special * list_special
First element of the linked list of gc_entry_special structures for allocations requiring a custom fr...
Definition buffer.h:130
struct gc_entry * list
First element of the linked list of gc_entry structures.
Definition buffer.h:128
Garbage collection entry for a specially allocated structure that needs a custom free function to be ...
Definition buffer.h:109
void(* free_fnc)(void *)
Definition buffer.h:111
void * addr
Definition buffer.h:112
struct gc_entry_special * next
Definition buffer.h:110
Garbage collection entry for one dynamically allocated block of memory.
Definition buffer.h:98
struct gc_entry * next
Pointer to the next item in the linked list.
Definition buffer.h:99
#define unlikely(x)
Definition syshead.h:35
static int cleanup(void **state)
struct gc_arena gc
Definition test_ssl.c:138