1 /* SPDX-License-Identifier: GPL-2.0 */
2 #ifndef __EROFS_FLEX_ARRAY_H
3 #define __EROFS_FLEX_ARRAY_H
4
5 #ifdef __cplusplus
6 extern "C"
7 {
8 #endif
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <limits.h>
13 #include <stdint.h>
14
15 #include "defs.h"
16 #include "print.h"
17
18 /*
19 * flex-array.h
20 *
21 * Some notes to make sense of the code.
22 *
23 * Flex-arrays:
24 * - Flex-arrays became standard in C99 and are defined by "array[]" (at the
25 * end of a struct)
26 * - Pre-C99 flex-arrays can be accomplished by "array[1]"
27 * - There is a GNU extension where they are defined using "array[0]"
28 * Allegedly there is/was a bug in gcc whereby foo[1] generated incorrect
29 * code, so it's safest to use [0] (https://lkml.org/lkml/2015/2/18/407).
30 *
31 * For C89 and C90, __STDC__ is 1
32 * For later standards, __STDC_VERSION__ is defined according to the standard.
33 * For example: 199901L or 201112L
34 *
35 * Whilst we're on the subject, in version 5 of gcc, the default std was
36 * changed from gnu89 to gnu11. In jgmenu, CFLAGS therefore contains -std=gnu89
37 * You can check your default gcc std by doing:
38 * gcc -dM -E - </dev/null | grep '__STDC_VERSION__\|__STDC__'
39 *
40 * The code below is copied from git's git-compat-util.h in support of
41 * hashmap.c
42 */
43
44 #ifndef FLEX_ARRAY
45 #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \
46 (!defined(__SUNPRO_C) || (__SUNPRO_C > 0x580))
47 # define FLEX_ARRAY /* empty */
48 #elif defined(__GNUC__)
49 # if (__GNUC__ >= 3)
50 # define FLEX_ARRAY /* empty */
51 # else
52 # define FLEX_ARRAY 0 /* older GNU extension */
53 # endif
54 #endif
55
56 /* Otherwise, default to safer but a bit wasteful traditional style */
57 #ifndef FLEX_ARRAY
58 # define FLEX_ARRAY 1
59 #endif
60 #endif
61
62 #define bitsizeof(x) (CHAR_BIT * sizeof(x))
63
64 #define maximum_signed_value_of_type(a) \
65 (INTMAX_MAX >> (bitsizeof(intmax_t) - bitsizeof(a)))
66
67 #define maximum_unsigned_value_of_type(a) \
68 (UINTMAX_MAX >> (bitsizeof(uintmax_t) - bitsizeof(a)))
69
70 /*
71 * Signed integer overflow is undefined in C, so here's a helper macro
72 * to detect if the sum of two integers will overflow.
73 * Requires: a >= 0, typeof(a) equals typeof(b)
74 */
75 #define signed_add_overflows(a, b) \
76 ((b) > maximum_signed_value_of_type(a) - (a))
77
78 #define unsigned_add_overflows(a, b) \
79 ((b) > maximum_unsigned_value_of_type(a) - (a))
80
st_add(size_t a,size_t b)81 static inline size_t st_add(size_t a, size_t b)
82 {
83 if (unsigned_add_overflows(a, b)) {
84 erofs_err("size_t overflow: %llu + %llu", a | 0ULL, b | 0ULL);
85 BUG_ON(1);
86 return -1;
87 }
88 return a + b;
89 }
90
91 #define st_add3(a, b, c) st_add(st_add((a), (b)), (c))
92 #define st_add4(a, b, c, d) st_add(st_add3((a), (b), (c)), (d))
93
94 /*
95 * These functions help you allocate structs with flex arrays, and copy
96 * the data directly into the array. For example, if you had:
97 *
98 * struct foo {
99 * int bar;
100 * char name[FLEX_ARRAY];
101 * };
102 *
103 * you can do:
104 *
105 * struct foo *f;
106 * FLEX_ALLOC_MEM(f, name, src, len);
107 *
108 * to allocate a "foo" with the contents of "src" in the "name" field.
109 * The resulting struct is automatically zero'd, and the flex-array field
110 * is NUL-terminated (whether the incoming src buffer was or not).
111 *
112 * The FLEXPTR_* variants operate on structs that don't use flex-arrays,
113 * but do want to store a pointer to some extra data in the same allocated
114 * block. For example, if you have:
115 *
116 * struct foo {
117 * char *name;
118 * int bar;
119 * };
120 *
121 * you can do:
122 *
123 * struct foo *f;
124 * FLEXPTR_ALLOC_STR(f, name, src);
125 *
126 * and "name" will point to a block of memory after the struct, which will be
127 * freed along with the struct (but the pointer can be repointed anywhere).
128 *
129 * The *_STR variants accept a string parameter rather than a ptr/len
130 * combination.
131 *
132 * Note that these macros will evaluate the first parameter multiple
133 * times, and it must be assignable as an lvalue.
134 */
135 #define FLEX_ALLOC_MEM(x, flexname, buf, len) do { \
136 size_t flex_array_len_ = (len); \
137 (x) = calloc(1, st_add3(sizeof(*(x)), flex_array_len_, 1)); \
138 BUG_ON(!(x)); \
139 memcpy((void *)(x)->flexname, (buf), flex_array_len_); \
140 } while (0)
141 #define FLEXPTR_ALLOC_MEM(x, ptrname, buf, len) do { \
142 size_t flex_array_len_ = (len); \
143 (x) = xcalloc(1, st_add3(sizeof(*(x)), flex_array_len_, 1)); \
144 memcpy((x) + 1, (buf), flex_array_len_); \
145 (x)->ptrname = (void *)((x) + 1); \
146 } while (0)
147 #define FLEX_ALLOC_STR(x, flexname, str) \
148 FLEX_ALLOC_MEM((x), flexname, (str), strlen(str))
149 #define FLEXPTR_ALLOC_STR(x, ptrname, str) \
150 FLEXPTR_ALLOC_MEM((x), ptrname, (str), strlen(str))
151
152 #ifdef __cplusplus
153 }
154 #endif
155
156 #endif
157