以下是stage分配器代码的相关部分:
#ifdef DEBUG
#include <string.h>
#define D(x) x
#else
#define D(x) (void) 0
#endif
/* Allocates a pointer from `arena`.
*
* The allocated pointer is at least aligned to `alignment`.
*
* `alignment` must be a power of 2.
*
* `size` must be a multiple of `alignment`.
*
* If a request can not be entertained, i.e. would overflow, or `arena` is full,
* the function returns `nullptr`. The function also returns a `nullptr` if the
* requested `size` or `alignment` is 0 or if `alignment` is not a power of 2,
* or if `size` is not a multiple of `alignment`.
*
* Any allocations made prior to this call are not freed on failure, and remain
* valid until the arena is either reset or destroyed. */
void *arena_alloc(Arena *arena, size_t alignment, size_t size)
{
if (size == 0
|| alignment == 0 || (alignment != 1 && !is_power_of_two(alignment))
|| !is_multiple_of(size, alignment)) {
return nullptr;
}
M_Pool *curr_pool = arena->pools[arena->current - 1];
uint8_t *const p = curr_pool->buf + curr_pool->offset;
const uintptr_t original = ((uintptr_t) p);
if (original > UINTPTR_MAX - alignment) {
return nullptr;
}
const uintptr_t remain = original & (alignment - 1);
const uintptr_t aligned =
remain != 0 ? original + (alignment - remain) : original;
const size_t offset = aligned - original;
if (size > SIZE_MAX - offset) {
return nullptr;
}
size += offset;
if (size > curr_pool->buf_len - curr_pool->offset) {
return nullptr;
}
/* Set the optional padding for alignment immediately before a user block,
* and the bytes immediately following such a block to non-zero.
* The intent is to trigger OBOB failures to inappropiate app use of
* strlen()/strnlen(), which keep forging ahead till encountering ascii NUL. */
D(
/* 0xA5 is used in FreeBSD's PHK malloc for debugging purposes. */
if (remain) {
memset(p + (alignment - remain), 0xA5, alignment - remain);
}
);
curr_pool->offset += size;
D(memset(curr_pool->buf + curr_pool->offset, 0xA5,
curr_pool->buf_len - curr_pool->offset));
arena->last_alloc_size = size;
/* Equal to "aligned", but preserves provenance. */
return p + offset;
}
以下是该功能的单元测试:
static void test_arena_alloc(void)
{
Arena *const arena = arena_new(nullptr, 100);
TEST_ASSERT(arena);
TEST_CHECK(arena_alloc(arena, 1, 112) == nullptr);
TEST_CHECK(arena_alloc(arena, 0, 1) == nullptr);
TEST_CHECK(arena_alloc(arena, 1, 0) == nullptr);
TEST_CHECK(arena_alloc(arena, 2, 5) == nullptr);
TEST_CHECK(arena_alloc(arena, 3, 5) == nullptr);
TEST_CHECK(arena_alloc(arena, 1, 95));
uint8_t *const curr_pool = arena->pools[0]->buf;
TEST_CHECK(curr_pool[96] == 0xA5 && curr_pool[97] == 0xA5
&& curr_pool[98] == 0xA5 && curr_pool[99] == 0xA5);
arena_reset(arena);
#ifdef HAVE_STDALIGN_H
const int *const a = arena_alloc(arena, alignof (int), 5 * sizeof *a);
const double *const b = arena_alloc(arena, alignof (double), 2 * sizeof *b);
const char *const c = arena_alloc(arena, 1, 10);
const short *const d = arena_alloc(arena, alignof (short), 5 * sizeof *d);
TEST_CHECK(a && is_aligned(a, alignof (int)));
TEST_CHECK(b && is_aligned(b, alignof (double)));
TEST_CHECK(c && is_aligned(c, 1));
TEST_CHECK(d && is_aligned(d, alignof (short)));
#endif
arena_destroy(arena);
}
在Linux(Ubuntu和Linux Mint)、Windows 10和Windows 11以及MacOS(macos最新版本,Github Operations使用的任何版本)上,所有这些都可以顺利通过.
cc -std=c11 -fPIC -Wall -Wextra -Werror -Wwrite-strings -Wno-unused-variable -Wno-parentheses -Wpedantic -Warray-bounds -Wno-unused-function -Wstrict-prototypes -Wdeprecated -DDEBUG tests.c -o tests
./tests arena_alloc --verbose=3
Test arena_alloc:
tests.c:92: arena... ok
tests.c:94: arena_alloc(arena, 1, 112) == nullptr... ok
tests.c:95: arena_alloc(arena, 0, 1) == nullptr... ok
tests.c:96: arena_alloc(arena, 1, 0) == nullptr... ok
tests.c:97: arena_alloc(arena, 2, 5) == nullptr... ok
tests.c:98: arena_alloc(arena, 3, 5) == nullptr... ok
tests.c:100: arena_alloc(arena, 1, 95)... ok
tests.c:103: curr_pool[96] == 0xA5 && curr_pool[97] == 0xA5 && curr_pool[98] == 0xA5 && curr_pool[99] == 0xA5... ok
tests.c:125: a && is_aligned(a, alignof (int))... ok
tests.c:126: b && is_aligned(b, alignof (double))... ok
tests.c:127: c && is_aligned(c, 1)... ok
tests.c:128: d && is_aligned(d, alignof (short))... ok
SUCCESS: All conditions have passed.
Summary:
Count of run unit tests: 1
Count of successful unit tests: 1
Count of failed unit tests: 0
SUCCESS: No unit tests have failed.
然而,如果我在FreeBSD上编译此代码,一个测试会失败:
Test arena_alloc:
tests.c:92: arena... ok
tests.c:94: arena_alloc(arena, 1, 112) == nullptr... ok
tests.c:95: arena_alloc(arena, 0, 1) == nullptr... ok
tests.c:96: arena_alloc(arena, 1, 0) == nullptr... ok
tests.c:97: arena_alloc(arena, 2, 5) == nullptr... ok
tests.c:98: arena_alloc(arena, 3, 5) == nullptr... ok
tests.c:100: arena_alloc(arena, 1, 95)... ok
tests.c:104: curr_pool[96] == 0xA5 && curr_pool[97] == 0xA5 && curr_pool[98] == 0xA5 && curr_pool[99] == 0xA5... failed
tests.c:114: a && is_aligned(a, alignof (int))... ok
tests.c:115: b && is_aligned(b, alignof (double))... ok
tests.c:116: c && is_aligned(c, 1)... ok
tests.c:117: d && is_aligned(d, alignof (short))... ok
FAILED: 1 condition has failed.
如果我打印出curr_pool[96]
-curr_pool[99]
的值,则所有值都是0,而不是0xA 5.
这是我正在使用的Makeline,它定义了DEBUG
标志:
CFLAGS += -std=c11
CFLAGS += -fPIC
CFLAGS += -Wall
CFLAGS += -Wextra
CFLAGS += -Werror
CFLAGS += -Wwrite-strings
CFLAGS += -Wno-unused-variable
CFLAGS += -Wno-parentheses
CFLAGS += -Wpedantic
CFLAGS += -Warray-bounds
CFLAGS += -Wno-unused-function
CFLAGS += -Wstrict-prototypes
CFLAGS += -Wdeprecated
TARGET := arena
TEST_TARGET := tests
SLIB_TARGET := libarena.a
DLIB_TARGET := libarena.so
RM := /bin/rm -f
release: CFLAGS += -O2 -s -DTEST_MAIN
release: $(TARGET)
debug: CFLAGS += -DTEST_MAIN -DDEBUG -g3 -ggdb -fsanitize=address,leak,undefined
debug: $(TARGET)
static: $(SLIB_TARGET)
$(SLIB_TARGET): $(TARGET).o
$(AR) rcs $@ $^
shared: $(DLIB_TARGET)
shared: LDFLAGS += -shared
$(DLIB_TARGET): $(TARGET).o
$(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS)
test: CFLAGS += -DDEBUG
test: $(TEST_TARGET)
./$(TEST_TARGET) --verbose=3
clean:
$(RM) $(TARGET) $(TEST_TARGET) $(TARGET).o $(SLIB_TARGET) $(DLIB_TARGET)
.PHONY: release debug static shared test clean
.DELETE_ON_ERROR:
这些测试是由以下内容编制的:
make test
如果缺少任何内容或需要更多代码,请在 comments 中询问. 不过,假设有人想要编译它,整个代码位于:arena处.
uname -a
显示FreeBSD 14.0.