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