Files
git/t/unit-tests/t-reftable-basics.c
Johannes Schindelin d02c37c3e6 t-reftable-basics: allow for malloc to be #defined
As indicated by the `#undef malloc` line in `reftable/basics.h`, it is
quite common to use allocators other than the default one by defining
`malloc` constants and friends.

This pattern is used e.g. in Git for Windows, which uses the powerful
and performant `mimalloc` allocator.

Furthermore, in `reftable/basics.c` this `#undef malloc` is
_specifically_ disabled by virtue of defining the
`REFTABLE_ALLOW_BANNED_ALLOCATORS` constant before including
`reftable/basic.h`, to ensure that such a custom allocator is also used
in the reftable code.

However, in 8db127d43f (reftable: avoid leaks on realloc error,
2024-12-28) and in 2cca185e85 (reftable: fix allocation count on
realloc error, 2024-12-28), `reftable_set_alloc()` function calls were
introduced that pass `malloc`, `realloc` and `free` function pointers as
parameters _after_ `reftable/basics.h` ensured that they were no longer
`#define`d. This would override the custom allocator and re-set it to
the default allocator provided by, say, libc or MSVCRT.

This causes problems because those calls happen after the initial
allocator has already been used to initialize an array, which is
subsequently resized using the overridden default `realloc()` allocator.

You cannot mix and match allocators like that, which leads to a
`STATUS_HEAP_CORRUPTION` (C0000374) on Windows, and when running this
unit test through shell and/or `prove` (which only support 7-bit status
codes), it surfaces as exit code 127.

It is actually unnecessary to use those function pointers to
`malloc`/`realloc`/`free`, though: The `reftable` code goes out of its
way to fall back to the initial allocator when passing `NULL` parameters
instead. So let's do that instead of causing heap corruptions.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Acked-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-01-08 09:41:52 -08:00

202 lines
4.5 KiB
C

/*
Copyright 2020 Google LLC
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file or at
https://developers.google.com/open-source/licenses/bsd
*/
#include "test-lib.h"
#include "reftable/basics.h"
struct integer_needle_lesseq_args {
int needle;
int *haystack;
};
static int integer_needle_lesseq(size_t i, void *_args)
{
struct integer_needle_lesseq_args *args = _args;
return args->needle <= args->haystack[i];
}
static void *realloc_stub(void *p UNUSED, size_t size UNUSED)
{
return NULL;
}
int cmd_main(int argc UNUSED, const char *argv[] UNUSED)
{
if_test ("binary search with binsearch works") {
int haystack[] = { 2, 4, 6, 8, 10 };
struct {
int needle;
size_t expected_idx;
} testcases[] = {
{-9000, 0},
{-1, 0},
{0, 0},
{2, 0},
{3, 1},
{4, 1},
{7, 3},
{9, 4},
{10, 4},
{11, 5},
{9000, 5},
};
for (size_t i = 0; i < ARRAY_SIZE(testcases); i++) {
struct integer_needle_lesseq_args args = {
.haystack = haystack,
.needle = testcases[i].needle,
};
size_t idx;
idx = binsearch(ARRAY_SIZE(haystack),
&integer_needle_lesseq, &args);
check_int(idx, ==, testcases[i].expected_idx);
}
}
if_test ("names_length returns size of a NULL-terminated string array") {
const char *a[] = { "a", "b", NULL };
check_int(names_length(a), ==, 2);
}
if_test ("names_equal compares NULL-terminated string arrays") {
const char *a[] = { "a", "b", "c", NULL };
const char *b[] = { "a", "b", "d", NULL };
const char *c[] = { "a", "b", NULL };
check(names_equal(a, a));
check(!names_equal(a, b));
check(!names_equal(a, c));
}
if_test ("parse_names works for basic input") {
char in1[] = "line\n";
char in2[] = "a\nb\nc";
char **out = parse_names(in1, strlen(in1));
check(out != NULL);
check_str(out[0], "line");
check(!out[1]);
free_names(out);
out = parse_names(in2, strlen(in2));
check(out != NULL);
check_str(out[0], "a");
check_str(out[1], "b");
check_str(out[2], "c");
check(!out[3]);
free_names(out);
}
if_test ("parse_names drops empty string") {
char in[] = "a\n\nb\n";
char **out = parse_names(in, strlen(in));
check(out != NULL);
check_str(out[0], "a");
/* simply '\n' should be dropped as empty string */
check_str(out[1], "b");
check(!out[2]);
free_names(out);
}
if_test ("common_prefix_size works") {
struct reftable_buf a = REFTABLE_BUF_INIT;
struct reftable_buf b = REFTABLE_BUF_INIT;
struct {
const char *a, *b;
int want;
} cases[] = {
{"abcdef", "abc", 3},
{ "abc", "ab", 2 },
{ "", "abc", 0 },
{ "abc", "abd", 2 },
{ "abc", "pqr", 0 },
};
for (size_t i = 0; i < ARRAY_SIZE(cases); i++) {
check(!reftable_buf_addstr(&a, cases[i].a));
check(!reftable_buf_addstr(&b, cases[i].b));
check_int(common_prefix_size(&a, &b), ==, cases[i].want);
reftable_buf_reset(&a);
reftable_buf_reset(&b);
}
reftable_buf_release(&a);
reftable_buf_release(&b);
}
if_test ("put_be24 and get_be24 work") {
uint32_t in = 0x112233;
uint8_t dest[3];
uint32_t out;
put_be24(dest, in);
out = get_be24(dest);
check_int(in, ==, out);
}
if_test ("put_be16 and get_be16 work") {
uint32_t in = 0xfef1;
uint8_t dest[3];
uint32_t out;
put_be16(dest, in);
out = get_be16(dest);
check_int(in, ==, out);
}
if_test ("REFTABLE_ALLOC_GROW works") {
int *arr = NULL, *old_arr;
size_t alloc = 0, old_alloc;
check(!REFTABLE_ALLOC_GROW(arr, 1, alloc));
check(arr != NULL);
check_uint(alloc, >=, 1);
arr[0] = 42;
old_alloc = alloc;
old_arr = arr;
reftable_set_alloc(NULL, realloc_stub, NULL);
check(REFTABLE_ALLOC_GROW(arr, old_alloc + 1, alloc));
check(arr == old_arr);
check_uint(alloc, ==, old_alloc);
old_alloc = alloc;
reftable_set_alloc(NULL, NULL, NULL);
check(!REFTABLE_ALLOC_GROW(arr, old_alloc + 1, alloc));
check(arr != NULL);
check_uint(alloc, >, old_alloc);
arr[alloc - 1] = 42;
reftable_free(arr);
}
if_test ("REFTABLE_ALLOC_GROW_OR_NULL works") {
int *arr = NULL;
size_t alloc = 0, old_alloc;
REFTABLE_ALLOC_GROW_OR_NULL(arr, 1, alloc);
check(arr != NULL);
check_uint(alloc, >=, 1);
arr[0] = 42;
old_alloc = alloc;
REFTABLE_ALLOC_GROW_OR_NULL(arr, old_alloc + 1, alloc);
check(arr != NULL);
check_uint(alloc, >, old_alloc);
arr[alloc - 1] = 42;
old_alloc = alloc;
reftable_set_alloc(NULL, realloc_stub, NULL);
REFTABLE_ALLOC_GROW_OR_NULL(arr, old_alloc + 1, alloc);
check(arr == NULL);
check_uint(alloc, ==, 0);
reftable_set_alloc(NULL, NULL, NULL);
reftable_free(arr);
}
return test_done();
}