}
END_TEST
+/*******************************************************************************
+ * memcpy_cond
+ */
+
+#define MEMCPY_COND_DEF "abcdef"
+static struct {
+ char *a;
+ size_t n;
+ uint8_t cond;
+ char *res;
+} memcpy_cond_data[] = {
+ {NULL, 0, 0, NULL},
+ {NULL, 0, 1, NULL},
+ {"foobar", 6, 0, MEMCPY_COND_DEF},
+ {"foobar", 6, 1, "foobar"},
+ {"foobar", 3, 0, MEMCPY_COND_DEF},
+ {"foobar", 3, 1, "foodef"},
+ {"\0bar", 4, 0, MEMCPY_COND_DEF},
+ {"\0bar", 4, 1, "\0baref"},
+ /* unpredictable results if condition is not 0 or 1 */
+ {"foobar", 6, 5, "bkkfev"},
+};
+
+START_TEST(test_memcpy_cond)
+{
+ char buf[BUF_LEN] = MEMCPY_COND_DEF;
+
+ memcpy_cond(buf, memcpy_cond_data[_i].a, memcpy_cond_data[_i].n,
+ memcpy_cond_data[_i].cond);
+ ck_assert(memeq(buf, memcpy_cond_data[_i].res ?: MEMCPY_COND_DEF,
+ sizeof(MEMCPY_COND_DEF)));
+}
+END_TEST
+
/*******************************************************************************
* memstr
*/
tcase_add_loop_test(tc, test_memeq_const, 0, countof(memeq_data));
suite_add_tcase(s, tc);
+ tc = tcase_create("memcpy_cond");
+ tcase_add_loop_test(tc, test_memcpy_cond, 0, countof(memcpy_cond_data));
+ suite_add_tcase(s, tc);
+
tc = tcase_create("memstr");
tcase_add_loop_test(tc, test_memstr, 0, countof(memstr_data));
suite_add_tcase(s, tc);
#endif
#define memcpy(d,s,n) memcpy_noop(d,s,n)
+/**
+ * Copies n bytes from src to dst if the given condition is 1, or leaves dst
+ * unchanged if it's 0 (but does write to it).
+ *
+ * Runs in constant time, so it's safe to conditionally copy data after a call
+ * to memeq_const() without any branching instructions.
+ */
+void memcpy_cond(void *dst, const void *src, size_t n, uint8_t cond);
+
/**
* Calling memmove() with NULL pointers, even with n == 0, results in undefined
* behavior according to the C standard. This version is guaranteed to not