You can also consider an helper function.
See commit 45ccef8, commit 60566cb (25 Sep 2016) by René Scharfe (rscharfe
).
(Merged by Junio C Hamano -- gitster
-- in commit b1f0a85, 03 Oct 2016)
It uses COPY_ARRAY
, a safe and convenient helper for copying arrays,
complementing ALLOC_ARRAY
and REALLOC_ARRAY
.
So instead of memcpy(temp, a, sizeof(a));
, you would use COPY_ARRAY(temp, a, 1);
Users just specify source, destination and the number of elements; the size of an element is inferred automatically.
It checks if the multiplication of size and element count overflows.
The inferred size is passed first to st_mult
, which allows the division
there to be done at compilation time.
As a basic type safety check it makes sure the sizes of source and
destination elements are the same. That's evaluated at compilation time
as well.
COPY_ARRAY
is safe to use with NULL
as source pointer iff 0 elements are
to be copied.
That convention is used in some cases for initializing arrays.
Raw memcpy(3)
does not support it -- compilers are allowed to
assume that only valid pointers are passed to it and can optimize away
NULL
checks after such a call.
#define COPY_ARRAY(dst, src, n) copy_array((dst), (src), (n), sizeof(*(dst)) + \
BUILD_ASSERT_OR_ZERO(sizeof(*(dst)) == sizeof(*(src))))
static inline void copy_array(void *dst, const void *src, size_t n, size_t size)
{
if (n)
memcpy(dst, src, st_mult(size, n));
}
It uses the macro BUILD_ASSERT_OR_ZERO
which asserts a build-time dependency, as an expression (with @cond
being the compile-time condition which must be true).
The compilation will fail if the condition isn't true, or can't be evaluated by the compiler.
#define BUILD_ASSERT_OR_ZERO(cond) \
(sizeof(char [1 - 2*!(cond)]) - 1)
Example:
#define foo_to_char(foo) \
((char *)(foo) \
+ BUILD_ASSERT_OR_ZERO(offsetof(struct foo, string) == 0))
The inline function st_mult
is introduced in commit 320d0b4
static inline size_t st_mult(size_t a, size_t b)
{
if (unsigned_mult_overflows(a, b))
die("size_t overflow: %"PRIuMAX" * %"PRIuMAX,
(uintmax_t)a, (uintmax_t)b);
return a * b;
}
st_mult
is part helper functions for detecting size_t
overflow, which include unsigned_mult_overflows
Performing computations on size_t
variables that we feed to xmalloc
and friends can be dangerous, as an integer overflow can cause us to allocate a much smaller chunk than we realized.
We already have unsigned_add_overflows()
, but let's add
unsigned_mult_overflows()
to that
/*
* Returns true if the multiplication of "a" and "b" will
* overflow. The types of "a" and "b" must match and must be unsigned.
* Note that this macro evaluates "a" twice!
*/
#define unsigned_mult_overflows(a, b) \
((a) && (b) > maximum_unsigned_value_of_type(a) / (a))
That uses maximum_unsigned_value_of_type
: helper for detecting unsigned overflow (from commit 1368f6)
The idiom (a + b < a)
works fine for detecting that an unsigned integer has overflowed, but a more explicit
unsigned_add_overflows(a, b)
might be easier to read.
Define such a macro, expanding roughly to ((a) < UINT_MAX - (b))
.
Because the expansion uses each argument only once outside of sizeof()
expressions, it is safe to use with arguments that have side effects.
#define bitsizeof(x) (CHAR_BIT * sizeof(x))
#define maximum_unsigned_value_of_type(a) \
(UINTMAX_MAX >> (bitsizeof(uintmax_t) - bitsizeof(a)))
with CHAR_BIT
being the number of bits in char (architecture-dependent)
You can see an example with Git 2.23 (Q3 2019)
See commit 921d49b, commit 177fbab (15 Jun 2019) by René Scharfe (rscharfe
).
(Merged by Junio C Hamano -- gitster
-- in commit e8d2590, 09 Jul 2019)
use COPY_ARRAY
for copying arrays
Convert calls of memcpy(3)
to use COPY_ARRAY
, which shortens and
simplifies the code a bit.