157 lines
4.4 KiB
C
157 lines
4.4 KiB
C
/* 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
|