
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, in8db127d43f
(reftable: avoid leaks on realloc error, 2024-12-28) and in2cca185e85
(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>
202 lines
4.5 KiB
C
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();
|
|
}
|