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.capacity = (int)size;
70 buf.data = calloc(1, 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.capacity = (int)size;
87 buf.data = (uint8_t *)gc_malloc(size, false, gc);
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.capacity = buf->capacity;
104 ret.offset = buf->offset;
105 ret.len = buf->len;
106#ifdef BUF_INIT_TRACKING
107 ret.debug_file = buf->debug_file;
108 ret.debug_line = buf->debug_line;
109#endif
110 ret.data = (uint8_t *)malloc(buf->capacity);
112 memcpy(BPTR(&ret), BPTR(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, BPTR(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 ((uint8_t *)(*e + 1) == 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 int ret = false;
229 if (buf_defined(buf))
230 {
232 uint8_t *ptr = BEND(buf);
233 int cap = buf_forward_capacity(buf);
234
235 if (cap > 0)
236 {
237 int stat;
239 stat = vsnprintf((char *)ptr, cap, format, arglist);
241 *(buf->data + buf->capacity - 1) = 0; /* windows vsnprintf needs this */
242 buf->len += (int)strlen((char *)ptr);
243 if (stat >= 0 && stat < cap)
244 {
245 ret = true;
246 }
247 }
248 }
249 return ret;
250}
251
252bool
253buf_puts(struct buffer *buf, const char *str)
254{
255 int ret = false;
256 uint8_t *ptr = BEND(buf);
257 int cap = buf_forward_capacity(buf);
258 if (cap > 0)
259 {
260 strncpynt((char *)ptr, str, cap);
261 *(buf->data + buf->capacity - 1) = 0; /* windows vsnprintf needs this */
262 buf->len += (int)strlen((char *)ptr);
263 ret = true;
264 }
265 return ret;
266}
267
268/*
269 * write a string to the end of a buffer that was
270 * truncated by buf_printf
271 */
272void
273buf_catrunc(struct buffer *buf, const char *str)
274{
275 if (buf_forward_capacity(buf) <= 1)
276 {
277 size_t len = strlen(str) + 1;
279 {
280 memcpy(buf->data + buf->capacity - len, str, len);
281 }
282 }
283}
284
285bool
286buffer_write_file(const char *filename, const struct buffer *buf)
287{
288 bool ret = false;
289 int fd = platform_open(filename, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR);
290 if (fd == -1)
291 {
292 msg(M_ERRNO, "Cannot open file '%s' for write", filename);
293 return false;
294 }
295
296 const ssize_t size = write(fd, BPTR(buf), (unsigned int)BLEN(buf));
297 if (size != BLEN(buf))
298 {
299 msg(M_ERRNO, "Write error on file '%s'", filename);
300 goto cleanup;
301 }
302
303 ret = true;
304cleanup:
305 if (close(fd) < 0)
306 {
307 msg(M_ERRNO, "Close error on file %s", filename);
308 ret = false;
309 }
310 return ret;
311}
312
313/*
314 * Garbage collection
315 */
316
317void *
318gc_malloc(size_t size, bool clear, struct gc_arena *a)
319{
320 void *ret;
321 if (a)
322 {
323 struct gc_entry *e;
324 e = (struct gc_entry *)malloc(size + sizeof(struct gc_entry));
326 ret = (char *)e + sizeof(struct gc_entry);
327 e->next = a->list;
328 a->list = e;
329 }
330 else
331 {
332 ret = malloc(size);
334 }
335#ifndef ZERO_BUFFER_ON_ALLOC
336 if (clear)
337#endif
338 memset(ret, 0, size);
339 return ret;
340}
341
342void *
343gc_realloc(void *ptr, size_t size, struct gc_arena *a)
344{
345 void *ret = realloc(ptr, size);
347 if (a)
348 {
349 if (ptr && ptr != ret)
350 {
351 /* find the old entry and modify it if realloc changed
352 * the pointer */
353 struct gc_entry_special *e = NULL;
354 for (e = a->list_special; e != NULL; e = e->next)
355 {
356 if (e->addr == ptr)
357 {
358 break;
359 }
360 }
361 ASSERT(e);
362 ASSERT(e->addr == ptr);
363 e->addr = ret;
364 }
365 else if (!ptr)
366 {
367 /* sets e->addr to newptr */
368 gc_addspecial(ret, free, a);
369 }
370 }
371
372 return ret;
373}
374
375void
377{
378 struct gc_entry *e;
379 e = a->list;
380 a->list = NULL;
381
382 while (e != NULL)
383 {
384 struct gc_entry *next = e->next;
385 free(e);
386 e = next;
387 }
388}
389
390/*
391 * Functions to handle special objects in gc_entries
392 */
393
394void
396{
397 struct gc_entry_special *e;
398 e = a->list_special;
399 a->list_special = NULL;
400
401 while (e != NULL)
402 {
403 struct gc_entry_special *next = e->next;
404 e->free_fnc(e->addr);
405 free(e);
406 e = next;
407 }
408}
409
410void
411gc_addspecial(void *addr, void (*free_function)(void *), struct gc_arena *a)
412{
413 ASSERT(a);
414 struct gc_entry_special *e;
415 e = (struct gc_entry_special *)malloc(sizeof(struct gc_entry_special));
417 e->free_fnc = free_function;
418 e->addr = addr;
419
420 e->next = a->list_special;
421 a->list_special = e;
422}
423
424
425/*
426 * Transfer src arena to dest, resetting src to an empty arena.
427 */
428void
429gc_transfer(struct gc_arena *dest, struct gc_arena *src)
430{
431 if (dest && src)
432 {
433 struct gc_entry *e = src->list;
434 if (e)
435 {
436 while (e->next != NULL)
437 {
438 e = e->next;
439 }
440 e->next = dest->list;
441 dest->list = src->list;
442 src->list = NULL;
443 }
444 }
445}
446
447/*
448 * Hex dump -- Output a binary buffer to a hex string and return it.
449 */
450
451char *
452format_hex_ex(const uint8_t *data, size_t size, size_t maxoutput, unsigned int space_break_flags,
453 const char *separator, struct gc_arena *gc)
454{
455 const size_t bytes_per_hexblock = space_break_flags & FHE_SPACE_BREAK_MASK;
456 const size_t separator_len = separator ? strlen(separator) : 0;
457 const size_t out_len = maxoutput > 0
458 ? maxoutput
459 : ((size * 2) + ((size / bytes_per_hexblock) * separator_len) + 2);
460
461 struct buffer out = alloc_buf_gc(out_len, gc);
462 for (size_t i = 0; i < size; ++i)
463 {
464 if (separator && i && !(i % bytes_per_hexblock))
465 {
466 buf_printf(&out, "%s", separator);
467 }
469 {
470 buf_printf(&out, "%02X", data[i]);
471 }
472 else
473 {
474 buf_printf(&out, "%02x", data[i]);
475 }
476 }
477 buf_catrunc(&out, "[more...]");
478 return (char *)out.data;
479}
480
481/*
482 * remove specific trailing character
483 */
484
485void
487{
488 uint8_t *cp = BLAST(buf);
489 if (cp && *cp == remove)
490 {
491 *cp = '\0';
492 --buf->len;
493 }
494}
495
496/*
497 * force a null termination even it requires
498 * truncation of the last char.
499 */
500void
502{
503 const uint8_t *last = BLAST(buf);
504 if (last && *last == '\0') /* already terminated? */
505 {
506 return;
507 }
508
509 if (!buf_safe(buf, 1)) /* make space for trailing null */
510 {
511 buf_inc_len(buf, -1);
512 }
513
514 buf_write_u8(buf, 0);
515}
516
517/*
518 * Remove trailing \r and \n chars and ensure
519 * null termination.
520 */
521void
522buf_chomp(struct buffer *buf)
523{
524 while (true)
525 {
526 const uint8_t *last = BLAST(buf);
527 if (!last)
528 {
529 break;
530 }
531 if (char_class(*last, CC_CRLF | CC_NULL))
532 {
533 if (!buf_inc_len(buf, -1))
534 {
535 break;
536 }
537 }
538 else
539 {
540 break;
541 }
542 }
544}
545
546const char *
548{
549 while (*str)
550 {
551 const char c = *str;
552 if (!(c == ' ' || c == '\t'))
553 {
554 break;
555 }
556 ++str;
557 }
558 return str;
559}
560
561#ifdef _WIN32
562/*
563 * like buf_null_terminate, but operate on strings
564 */
565void
567{
569 if (len < capacity)
570 {
571 *(str + len) = '\0';
572 }
573 else if (len == capacity)
574 {
575 *(str + len - 1) = '\0';
576 }
577}
578#endif
579
580/*
581 * Remove trailing \r and \n chars.
582 */
583void
584chomp(char *str)
585{
586 rm_trailing_chars(str, "\r\n");
587}
588
589/*
590 * Remove trailing chars
591 */
592void
594{
595 bool modified;
596 do
597 {
598 const size_t len = strlen(str);
599 modified = false;
600 if (len > 0)
601 {
602 char *cp = str + (len - 1);
603 if (strchr(what_to_delete, *cp) != NULL)
604 {
605 *cp = '\0';
606 modified = true;
607 }
608 }
609 } while (modified);
610}
611
612/*
613 * Allocate a string
614 */
615char *
616string_alloc(const char *str, struct gc_arena *gc)
617{
618 if (str)
619 {
620 const size_t n = strlen(str) + 1;
621 char *ret;
622
623 if (gc)
624 {
625 ret = (char *)gc_malloc(n, false, gc);
626 }
627 else
628 {
629 /* If there are no garbage collector available, it's expected
630 * that the caller cleans up afterwards. This is coherent with the
631 * earlier behaviour when gc_malloc() would be called with gc == NULL
632 */
633 ret = calloc(1, n);
635 }
636 memcpy(ret, str, n);
637 return ret;
638 }
639 else
640 {
641 return NULL;
642 }
643}
644
645/*
646 * Erase all characters in a string
647 */
648void
650{
651 if (str)
652 {
654 }
655}
656
657/*
658 * Return the length of a string array
659 */
660int
661string_array_len(const char **array)
662{
663 int i = 0;
664 if (array)
665 {
666 while (array[i])
667 {
668 ++i;
669 }
670 }
671 return i;
672}
673
674char *
675print_argv(const char **p, struct gc_arena *gc, const unsigned int flags)
676{
677 struct buffer out = alloc_buf_gc(256, gc);
678 int i = 0;
679 for (;;)
680 {
681 const char *cp = *p++;
682 if (!cp)
683 {
684 break;
685 }
686 if (i)
687 {
688 buf_printf(&out, " ");
689 }
690 if (flags & PA_BRACKET)
691 {
692 buf_printf(&out, "[%s]", cp);
693 }
694 else
695 {
696 buf_printf(&out, "%s", cp);
697 }
698 ++i;
699 }
700 return BSTR(&out);
701}
702
703/*
704 * Allocate a string inside a buffer
705 */
706struct buffer
708{
709 struct buffer buf;
710
711 ASSERT(str);
712
713 buf_set_read(&buf, (uint8_t *)string_alloc(str, gc), strlen(str) + 1);
714
715 if (buf.len > 0) /* Don't count trailing '\0' as part of length */
716 {
717 --buf.len;
718 }
719
720 return buf;
721}
722
723/*
724 * String comparison
725 */
726
727bool
728buf_string_match_head_str(const struct buffer *src, const char *match)
729{
730 const size_t size = strlen(match);
731 if (!buf_size_valid(size) || (int)size > src->len)
732 {
733 return false;
734 }
735 return memcmp(BPTR(src), match, size) == 0;
736}
737
738bool
740{
742 {
744 return true;
745 }
746 else
747 {
748 return false;
749 }
750}
751
752int
753buf_substring_len(const struct buffer *buf, int delim)
754{
755 int i = 0;
756 struct buffer tmp = *buf;
757 int c;
758
759 while ((c = buf_read_u8(&tmp)) >= 0)
760 {
761 ++i;
762 if (c == delim)
763 {
764 return i;
765 }
766 }
767 return -1;
768}
769
770/*
771 * String parsing
772 */
773
774bool
775buf_parse(struct buffer *buf, const int delim, char *line, const int size)
776{
777 bool eol = false;
778 int n = 0;
779 int c;
780
781 ASSERT(size > 0);
782
783 do
784 {
785 c = buf_peek_u8(buf);
786 if (c < 0)
787 {
788 eol = true;
789 line[n] = 0;
790 break;
791 }
792 if (c == delim)
793 {
794 buf_advance(buf, 1);
795 line[n] = 0;
796 break;
797 }
798 if (n >= (size - 1))
799 {
800 break;
801 }
802 buf_advance(buf, 1);
803 line[n++] = (char)c;
804 } while (c);
805
806 line[size - 1] = '\0';
807 return !(eol && !strlen(line));
808}
809
810/*
811 * Print a string which might be NULL
812 */
813const char *
814np(const char *str)
815{
816 if (str)
817 {
818 return str;
819 }
820 else
821 {
822 return "[NULL]";
823 }
824}
825
826/*
827 * Classify and mutate strings based on character types.
828 */
829
830/* Note 1: This functions depends on getting an unsigned
831 char. Both the is*() functions and our own checks expect it
832 this way.
833 Note 2: For CC_PRINT we just accept everything >= 32, so
834 if we ingest non-ASCII UTF-8 we will classify it as
835 printable since it will be >= 128. Other encodings are
836 not officially supported.
837*/
838bool
839char_class(const unsigned char c, const unsigned int flags)
840{
841 if (!flags)
842 {
843 return false;
844 }
845 if (flags & CC_ANY)
846 {
847 return true;
848 }
849
850 if ((flags & CC_NULL) && c == '\0')
851 {
852 return true;
853 }
854
855 if ((flags & CC_ALNUM) && isalnum(c))
856 {
857 return true;
858 }
859 if ((flags & CC_ALPHA) && isalpha(c))
860 {
861 return true;
862 }
863 if ((flags & CC_ASCII) && isascii(c))
864 {
865 return true;
866 }
867 if ((flags & CC_CNTRL) && iscntrl(c))
868 {
869 return true;
870 }
871 if ((flags & CC_DIGIT) && isdigit(c))
872 {
873 return true;
874 }
875 /* allow ascii non-control and UTF-8, consider DEL to be a control */
876 if ((flags & CC_PRINT) && (c >= 32 && c != 127))
877 {
878 return true;
879 }
880 if ((flags & CC_PUNCT) && ispunct(c))
881 {
882 return true;
883 }
884 if ((flags & CC_SPACE) && isspace(c))
885 {
886 return true;
887 }
888 if ((flags & CC_XDIGIT) && isxdigit(c))
889 {
890 return true;
891 }
892
893 if ((flags & CC_BLANK) && (c == ' ' || c == '\t'))
894 {
895 return true;
896 }
897 if ((flags & CC_NEWLINE) && c == '\n')
898 {
899 return true;
900 }
901 if ((flags & CC_CR) && c == '\r')
902 {
903 return true;
904 }
905
906 if ((flags & CC_BACKSLASH) && c == '\\')
907 {
908 return true;
909 }
910 if ((flags & CC_UNDERBAR) && c == '_')
911 {
912 return true;
913 }
914 if ((flags & CC_DASH) && c == '-')
915 {
916 return true;
917 }
918 if ((flags & CC_DOT) && c == '.')
919 {
920 return true;
921 }
922 if ((flags & CC_COMMA) && c == ',')
923 {
924 return true;
925 }
926 if ((flags & CC_COLON) && c == ':')
927 {
928 return true;
929 }
930 if ((flags & CC_SLASH) && c == '/')
931 {
932 return true;
933 }
934 if ((flags & CC_SINGLE_QUOTE) && c == '\'')
935 {
936 return true;
937 }
938 if ((flags & CC_DOUBLE_QUOTE) && c == '\"')
939 {
940 return true;
941 }
942 if ((flags & CC_REVERSE_QUOTE) && c == '`')
943 {
944 return true;
945 }
946 if ((flags & CC_PERCENT) && c == '%')
947 {
948 return true;
949 }
950 if ((flags & CC_EXCLAMATION) && c == '!')
951 {
952 return true;
953 }
954 if ((flags & CC_LESS_THAN) && c == '<')
955 {
956 return true;
957 }
958 if ((flags & CC_GREATER_THAN) && c == '>')
959 {
960 return true;
961 }
962 if ((flags & CC_PIPE) && c == '|')
963 {
964 return true;
965 }
966 if ((flags & CC_QUESTION_MARK) && c == '?')
967 {
968 return true;
969 }
970 if ((flags & CC_ASTERISK) && c == '*')
971 {
972 return true;
973 }
974
975 return false;
976}
977
978static inline bool
979char_inc_exc(const char c, const unsigned int inclusive, const unsigned int exclusive)
980{
981 return char_class((unsigned char)c, inclusive)
982 && !char_class((unsigned char)c, exclusive);
983}
984
985bool
986string_class(const char *str, const unsigned int inclusive, const unsigned int exclusive)
987{
988 char c;
989 ASSERT(str);
990 while ((c = *str++))
991 {
993 {
994 return false;
995 }
996 }
997 return true;
998}
999
1000/*
1001 * Modify string in place.
1002 * Guaranteed to not increase string length.
1003 */
1004bool
1005string_mod(char *str, const unsigned int inclusive, const unsigned int exclusive,
1006 const char replace)
1007{
1008 const char *in = str;
1009 bool ret = true;
1010
1011 ASSERT(str);
1012
1013 while (true)
1014 {
1015 char c = *in++;
1016 if (c)
1017 {
1019 {
1020 c = replace;
1021 ret = false;
1022 }
1023 if (c)
1024 {
1025 *str++ = c;
1026 }
1027 }
1028 else
1029 {
1030 *str = '\0';
1031 break;
1032 }
1033 }
1034 return ret;
1035}
1036
1037bool
1038string_check_buf(struct buffer *buf, const unsigned int inclusive, const unsigned int exclusive)
1039{
1040 ASSERT(buf);
1041
1042 for (int i = 0; i < BLEN(buf); i++)
1043 {
1044 char c = BSTR(buf)[i];
1045
1047 {
1048 return false;
1049 }
1050 }
1051 return true;
1052}
1053
1054const char *
1055string_mod_const(const char *str, const unsigned int inclusive, const unsigned int exclusive,
1056 const char replace, struct gc_arena *gc)
1057{
1058 if (str)
1059 {
1060 char *buf = string_alloc(str, gc);
1062 return buf;
1063 }
1064 else
1065 {
1066 return NULL;
1067 }
1068}
1069
1070void
1071string_replace_leading(char *str, const char match, const char replace)
1072{
1073 ASSERT(match != '\0');
1074 while (*str)
1075 {
1076 if (*str == match)
1077 {
1078 *str = replace;
1079 }
1080 else
1081 {
1082 break;
1083 }
1084 ++str;
1085 }
1086}
1087
1088bool
1089string_defined_equal(const char *s1, const char *s2)
1090{
1091 if (s1 && s2)
1092 {
1093 return !strcmp(s1, s2);
1094 }
1095 else
1096 {
1097 return false;
1098 }
1099}
1100
1101char *
1102string_substitute(const char *src, char from, char to, struct gc_arena *gc)
1103{
1104 char *ret = (char *)gc_malloc(strlen(src) + 1, true, gc);
1105 char *dest = ret;
1106 char c;
1107
1108 do
1109 {
1110 c = *src++;
1111 if (c == from)
1112 {
1113 c = to;
1114 }
1115 *dest++ = c;
1116 } while (c);
1117 return ret;
1118}
1119
1120bool
1121checked_snprintf(char *str, size_t size, const char *format, ...)
1122{
1125 ASSERT(size < INT_MAX);
1126 int len = vsnprintf(str, size, format, arglist);
1127 va_end(arglist);
1128 return (len >= 0 && len < (ssize_t)size);
1129}
1130
1131#ifdef VERIFY_ALIGNMENT
1132void
1133valign4(const struct buffer *buf, const char *file, const int line)
1134{
1135 if (buf && buf->len)
1136 {
1137 msglvl_t msglevel = D_ALIGN_DEBUG;
1138 const uintptr_t u = (uintptr_t)BPTR(buf);
1139
1140 if (u & (PAYLOAD_ALIGN - 1))
1141 {
1142 msglevel = D_ALIGN_ERRORS;
1143 }
1144
1145 msg(msglevel, "%sAlignment at %s/%d ptr=" ptr_format " OLC=%d/%d/%d I=%s/%d",
1146 (msglevel == D_ALIGN_ERRORS) ? "ERROR: " : "", file, line, (ptr_type)buf->data,
1147 buf->offset, buf->len, buf->capacity, buf_debug_file(buf), buf_debug_line(buf));
1148 }
1149}
1150#endif /* ifdef VERIFY_ALIGNMENT */
1151
1152/*
1153 * struct buffer_list
1154 */
1155struct buffer_list *
1157{
1158 struct buffer_list *ret;
1159 ALLOC_OBJ_CLEAR(ret, struct buffer_list);
1160 ret->size = 0;
1161 return ret;
1162}
1163
1164void
1166{
1167 if (ol)
1168 {
1170 free(ol);
1171 }
1172}
1173
1174bool
1176{
1177 return ol && ol->head != NULL && ol->size > 0;
1178}
1179
1180void
1182{
1183 struct buffer_entry *e = ol->head;
1184 while (e)
1185 {
1186 struct buffer_entry *next = e->next;
1187 free_buf(&e->buf);
1188 free(e);
1189 e = next;
1190 }
1191 ol->head = ol->tail = NULL;
1192 ol->size = 0;
1193}
1194
1195void
1196buffer_list_push(struct buffer_list *ol, const char *str)
1197{
1198 if (str)
1199 {
1200 const size_t len = strlen((const char *)str);
1201 struct buffer_entry *e = buffer_list_push_data(ol, str, len + 1);
1202 if (e)
1203 {
1204 e->buf.len--; /* Don't count trailing '\0' as part of length */
1205 }
1206 }
1207}
1208
1209struct buffer_entry *
1210buffer_list_push_data(struct buffer_list *ol, const void *data, size_t size)
1211{
1212 struct buffer_entry *e = NULL;
1213 if (data)
1214 {
1215 ALLOC_OBJ_CLEAR(e, struct buffer_entry);
1216
1217 ++ol->size;
1218 if (ol->tail)
1219 {
1220 ASSERT(ol->head);
1221 ol->tail->next = e;
1222 }
1223 else
1224 {
1225 ASSERT(!ol->head);
1226 ol->head = e;
1227 }
1228 e->buf = alloc_buf(size);
1229 memcpy(e->buf.data, data, size);
1230 /* Note: size implicitly checked by alloc_buf */
1231 e->buf.len = (int)size;
1232 ol->tail = e;
1233 }
1234 return e;
1235}
1236
1237struct buffer *
1239{
1240 if (ol && ol->head)
1241 {
1242 return &ol->head->buf;
1243 }
1244 else
1245 {
1246 return NULL;
1247 }
1248}
1249
1250void
1251buffer_list_aggregate_separator(struct buffer_list *bl, const size_t max_len, const char *sep)
1252{
1253 const size_t sep_len = strlen(sep);
1254 struct buffer_entry *more = bl->head;
1255 size_t size = 0;
1256 size_t count = 0;
1257 for (; more; ++count)
1258 {
1259 size_t extra_len = BLENZ(&more->buf) + sep_len;
1260 if (size + extra_len > max_len)
1261 {
1262 break;
1263 }
1264
1265 size += extra_len;
1266 more = more->next;
1267 }
1268
1269 if (count >= 2)
1270 {
1271 struct buffer_entry *f;
1272 ALLOC_OBJ_CLEAR(f, struct buffer_entry);
1273 f->buf = alloc_buf(size + 1); /* prevent 0-byte malloc */
1274
1275 struct buffer_entry *e = bl->head;
1276 for (size_t i = 0; e && i < count; ++i)
1277 {
1278 struct buffer_entry *next = e->next;
1279 buf_copy(&f->buf, &e->buf);
1280 buf_write(&f->buf, sep, sep_len);
1281 free_buf(&e->buf);
1282 free(e);
1283 e = next;
1284 }
1285 bl->head = f;
1286 bl->size -= count - 1;
1287 f->next = more;
1288 if (!more)
1289 {
1290 bl->tail = f;
1291 }
1292 }
1293}
1294
1295void
1296buffer_list_aggregate(struct buffer_list *bl, const size_t max)
1297{
1299}
1300
1301void
1303{
1304 if (buffer_list_defined(ol))
1305 {
1306 struct buffer_entry *e = ol->head->next;
1307 free_buf(&ol->head->buf);
1308 free(ol->head);
1309 ol->head = e;
1310 --ol->size;
1311 if (!e)
1312 {
1313 ol->tail = NULL;
1314 }
1315 }
1316}
1317
1318void
1319buffer_list_advance(struct buffer_list *ol, ssize_t n)
1320{
1321 if (ol->head)
1322 {
1323 struct buffer *buf = &ol->head->buf;
1324 ASSERT(buf_advance(buf, n));
1325 if (!BLEN(buf))
1326 {
1328 }
1329 }
1330}
1331
1332struct buffer_list *
1333buffer_list_file(const char *fn, int max_line_len)
1334{
1335 FILE *fp = platform_fopen(fn, "r");
1336 struct buffer_list *bl = NULL;
1337
1338 if (fp)
1339 {
1340 char *line = (char *)malloc(max_line_len);
1341 if (line)
1342 {
1343 bl = buffer_list_new();
1344 while (fgets(line, max_line_len, fp) != NULL)
1345 {
1346 buffer_list_push(bl, line);
1347 }
1348 free(line);
1349 }
1350 fclose(fp);
1351 }
1352 return bl;
1353}
1354
1355struct buffer
1357{
1358 struct buffer ret = { 0 };
1359
1360 platform_stat_t file_stat = { 0 };
1361 if (platform_stat(filename, &file_stat) < 0)
1362 {
1363 return ret;
1364 }
1365
1366 FILE *fp = platform_fopen(filename, "r");
1367 if (!fp)
1368 {
1369 return ret;
1370 }
1371
1372 const size_t size = file_stat.st_size;
1373 ret = alloc_buf_gc(size + 1, gc); /* space for trailing \0 */
1374 size_t read_size = fread(BPTR(&ret), 1, size, fp);
1375 if (read_size == 0)
1376 {
1377 free_buf_gc(&ret, gc);
1378 goto cleanup;
1379 }
1382
1383cleanup:
1384 fclose(fp);
1385 return ret;
1386}
1387
1388char *
1389buf_extract_field(struct buffer *buf, char sep, struct gc_arena *gc)
1390{
1391 if (!buf_valid(buf))
1392 {
1393 return NULL;
1394 }
1395
1396 const uint8_t *seppos = memchr(BPTR(buf), sep, buf_len(buf));
1397 if (!seppos)
1398 {
1399 return NULL;
1400 }
1401 size_t field_len = seppos - BPTR(buf);
1402
1403 char *field = gc_malloc(field_len + 1, false, gc);
1404
1405 memcpy(field, BPTR(buf), field_len);
1406 field[field_len] = 0;
1407
1408 buf_advance(buf, field_len + 1);
1409 return field;
1410}
bool buffer_list_defined(const struct buffer_list *ol)
Checks if the list is valid and non-empty.
Definition buffer.c:1175
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:739
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:1210
void rm_trailing_chars(char *str, const char *what_to_delete)
Remove all trailing characters that appear in a given set.
Definition buffer.c:593
void string_null_terminate(char *str, int len, int capacity)
Null-terminate a fixed-length string buffer.
Definition buffer.c:566
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:1251
static bool char_inc_exc(const char c, const unsigned int inclusive, const unsigned int exclusive)
Definition buffer.c:979
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:1181
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:547
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:1296
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:395
bool buffer_write_file(const char *filename, const struct buffer *buf)
Write buffer contents to file.
Definition buffer.c:286
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:273
#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:1302
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:1071
bool buf_puts(struct buffer *buf, const char *str)
Append a string to a buffer with overflow check.
Definition buffer.c:253
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:1389
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:1333
void string_clear(char *str)
Securely clear a null-terminated string.
Definition buffer.c:649
#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:429
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:986
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:675
void buf_null_terminate(struct buffer *buf)
Force a null terminator at the end of the buffer content.
Definition buffer.c:501
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:343
struct buffer_list * buffer_list_new(void)
Allocate an empty buffer list of capacity max_size.
Definition buffer.c:1156
void chomp(char *str)
Remove trailing newline and carriage-return characters from a string.
Definition buffer.c:584
struct buffer * buffer_list_peek(struct buffer_list *ol)
Retrieve the head buffer.
Definition buffer.c:1238
const char * np(const char *str)
Return a printable representation of a string that might be NULL.
Definition buffer.c:814
bool string_check_buf(struct buffer *buf, const unsigned int inclusive, const unsigned int exclusive)
Check a buffer if it only consists of allowed characters.
Definition buffer.c:1038
bool string_defined_equal(const char *s1, const char *s2)
Definition buffer.c:1089
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:1165
void * gc_malloc(size_t size, bool clear, struct gc_arena *a)
Allocate memory and, optionally, zero it.
Definition buffer.c:318
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:452
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:1005
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:1055
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:411
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:1319
int string_array_len(const char **array)
Return the number of elements in a NULL-terminated array of strings.
Definition buffer.c:661
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:1356
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:486
char * string_substitute(const char *src, char from, char to, struct gc_arena *gc)
Definition buffer.c:1102
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:775
bool checked_snprintf(char *str, size_t size, const char *format,...)
Like snprintf() but returns an boolean.
Definition buffer.c:1121
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:616
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:707
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:1196
void x_gc_free(struct gc_arena *a)
Free all plain allocations in a garbage collection arena.
Definition buffer.c:376
void buf_chomp(struct buffer *buf)
Remove trailing newline and carriage-return characters from a buffer.
Definition buffer.c:522
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:839
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:728
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:753
Buffer management functions and garbage collection.
#define CC_EXCLAMATION
exclamation mark
Definition buffer.h:1664
#define CC_PERCENT
percent sign
Definition buffer.h:1663
#define CC_COMMA
comma
Definition buffer.h:1657
#define BEND(buf)
Return a pointer one past the end of the buffer content.
Definition buffer.h:141
#define CC_DASH
dash
Definition buffer.h:1655
static bool buf_size_valid(const size_t size)
Return true iff size is within the allowed buffer size range.
Definition buffer.h:492
#define CC_DOUBLE_QUOTE
double quote
Definition buffer.h:1661
#define BLAST(buf)
Return a pointer to the last byte of the buffer content, or NULL if empty.
Definition buffer.h:143
#define CC_BLANK
space or tab
Definition buffer.h:1649
static bool buf_init_dowork(struct buffer *buf, int offset)
Initialise a buffer with a given initial offset.
Definition buffer.h:571
#define CC_PIPE
pipe
Definition buffer.h:1667
#define BSTR(buf)
Return the buffer content pointer cast to char *.
Definition buffer.h:151
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:1301
#define CC_ANY
any character
Definition buffer.h:1636
#define BPTR(buf)
Return a pointer to the start of the buffer content.
Definition buffer.h:139
static int buf_peek_u8(struct buffer *buf)
Return the first byte of the buffer without consuming it.
Definition buffer.h:1430
#define CC_XDIGIT
hex digit isxdigit()
Definition buffer.h:1647
#define CC_COLON
colon
Definition buffer.h:1658
#define CC_SINGLE_QUOTE
single quote
Definition buffer.h:1660
static bool buf_inc_len(struct buffer *buf, int inc)
Increase or decrease the length of a buffer.
Definition buffer.h:1078
#define CC_DIGIT
digit isdigit()
Definition buffer.h:1643
static bool buf_valid(const struct buffer *buf)
Return true iff buf is valid.
Definition buffer.h:404
#define PA_BRACKET
Flag for print_argv(): wrap each argument in square brackets.
Definition buffer.h:221
#define CC_DOT
dot
Definition buffer.h:1656
#define CC_CRLF
carriage return or newline
Definition buffer.h:1673
static bool buf_safe(const struct buffer *buf, size_t len)
Check whether len bytes can be appended to a buffer.
Definition buffer.h:953
#define ALLOC_SIZE_MAX
Maximum size for a single array allocation (checked by array_mult_safe()).
Definition buffer.h:1949
#define CC_ASTERISK
asterisk
Definition buffer.h:1669
#define CC_ALPHA
alphabetic isalpha()
Definition buffer.h:1640
#define CC_NEWLINE
newline
Definition buffer.h:1650
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:1101
static int buf_len(const struct buffer *buf)
Return the length of the buffer content.
Definition buffer.h:438
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:623
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:997
static void secure_memzero(void *data, size_t len)
Securely zeroise memory.
Definition buffer.h:705
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:1148
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:1124
#define CC_REVERSE_QUOTE
reverse quote
Definition buffer.h:1662
#define CC_SPACE
whitespace isspace()
Definition buffer.h:1646
#define CC_ASCII
ASCII character.
Definition buffer.h:1641
static bool buf_write(struct buffer *dest, const void *src, size_t size)
Append data to a buffer.
Definition buffer.h:1198
#define CC_BACKSLASH
backslash
Definition buffer.h:1653
#define CC_CNTRL
control character iscntrl()
Definition buffer.h:1642
#define CC_LESS_THAN
less than sign
Definition buffer.h:1665
static bool buf_write_u8(struct buffer *dest, uint8_t data)
Append a uint8_t to a buffer.
Definition buffer.h:1242
static int buf_read_u8(struct buffer *buf)
Read and consume a uint8_t from the front of a buffer.
Definition buffer.h:1449
#define BLEN(buf)
Return the length of the buffer content in bytes.
Definition buffer.h:145
#define CC_CR
carriage return
Definition buffer.h:1651
#define CC_SLASH
slash
Definition buffer.h:1659
#define CC_UNDERBAR
underscore
Definition buffer.h:1654
#define CC_GREATER_THAN
greater than sign
Definition buffer.h:1666
#define BLENZ(buf)
Return the length of the buffer content as a size_t.
Definition buffer.h:147
void buf_size_error(const size_t size)
Report a buffer size error and abort.
Definition buffer.c:54
static void strncpynt(char *dest, const char *src, size_t maxlen)
Like strncpy() but always null-terminates the destination.
Definition buffer.h:646
static void check_malloc_return(void *p)
Abort if a memory allocation returned NULL.
Definition buffer.h:2082
#define CC_NULL
null character \0
Definition buffer.h:1637
#define CC_PRINT
printable (>= 32, != 127)
Definition buffer.h:1644
#define CC_QUESTION_MARK
question mark
Definition buffer.h:1668
#define ALLOC_OBJ_CLEAR(dptr, type)
Allocate and zero-initialise memory for a single object of the given type.
Definition buffer.h:1974
static bool buf_defined(const struct buffer *buf)
Return true iff buf has a non-NULL data pointer.
Definition buffer.h:390
#define CC_ALNUM
alphanumeric isalnum()
Definition buffer.h:1639
#define buf_init(buf, offset)
Definition buffer.h:356
#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:884
#define CC_PUNCT
punctuation ispunct()
Definition buffer.h:1645
#define FHE_CAPS
Flag for format_hex_ex(): output hex digits in upper case.
Definition buffer.h:886
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:1026
unsigned long ptr_type
Definition common.h:59
#define ptr_format
Definition common.h:50
#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:219
#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:2099
struct buffer_entry * next
Pointer to the next node, or NULL.
Definition buffer.h:2101
struct buffer buf
The buffer stored in this list node.
Definition buffer.h:2100
A singly-linked list of buffers, with head/tail pointers for O(1) push.
Definition buffer.h:2106
size_t size
Current number of entries.
Definition buffer.h:2109
struct buffer_entry * tail
Last item pushed.
Definition buffer.h:2108
struct buffer_entry * head
Next item to pop/peek.
Definition buffer.h:2107
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:122