You can also consider this usage of memmove
seen in Git 2.14.x (Q3 2017)
See commit 168e635 (16 Jul 2017), and commit 1773664, commit f331ab9, commit 5783980 (15 Jul 2017) by René Scharfe (rscharfe
).
(Merged by Junio C Hamano -- gitster
-- in commit 32f9025, 11 Aug 2017)
It uses an helper macro MOVE_ARRAY
which calculates the size
based on the specified number of elements for us and supports NULL
pointers when that number is zero.
Raw memmove(3)
calls with NULL
can
cause the compiler to (over-eagerly) optimize out later NULL
checks.
MOVE_ARRAY
adds a safe and convenient helper for moving potentially overlapping ranges of array entries.
It infers the element size, multiplies automatically and safely to get the size in bytes, does a basic type safety check by comparing element sizes and unlike memmove(3)
it supports NULL
pointers iff 0 elements are to be moved.
#define MOVE_ARRAY(dst, src, n) move_array((dst), (src), (n), sizeof(*(dst)) + \
BUILD_ASSERT_OR_ZERO(sizeof(*(dst)) == sizeof(*(src))))
static inline void move_array(void *dst, const void *src, size_t n, size_t size)
{
if (n)
memmove(dst, src, st_mult(size, n));
}
Examples:
- memmove(dst, src, (n) * sizeof(*dst));
+ MOVE_ARRAY(dst, src, 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))