mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-04-27 19:09:47 +03:00
166 lines
5.4 KiB
Zig
166 lines
5.4 KiB
Zig
//! An allocator that attempts to allocate from the given buffer, falling back to
|
|
//! `fallback_allocator` if this fails.
|
|
|
|
const std = @import("../std.zig");
|
|
const heap = std.heap;
|
|
const testing = std.testing;
|
|
|
|
const Alignment = std.mem.Alignment;
|
|
const Allocator = std.mem.Allocator;
|
|
const FixedBufferAllocator = std.heap.FixedBufferAllocator;
|
|
|
|
const BufferFirstAllocator = @This();
|
|
|
|
fallback_allocator: Allocator,
|
|
fixed_buffer_allocator: FixedBufferAllocator,
|
|
|
|
pub fn init(buffer: []u8, fallback_allocator: Allocator) BufferFirstAllocator {
|
|
return .{
|
|
.fallback_allocator = fallback_allocator,
|
|
.fixed_buffer_allocator = .init(buffer),
|
|
};
|
|
}
|
|
|
|
pub fn allocator(self: *BufferFirstAllocator) Allocator {
|
|
return .{
|
|
.ptr = self,
|
|
.vtable = &.{
|
|
.alloc = alloc,
|
|
.resize = resize,
|
|
.remap = remap,
|
|
.free = free,
|
|
},
|
|
};
|
|
}
|
|
|
|
fn alloc(
|
|
ctx: *anyopaque,
|
|
len: usize,
|
|
alignment: Alignment,
|
|
ra: usize,
|
|
) ?[*]u8 {
|
|
const self: *BufferFirstAllocator = @ptrCast(@alignCast(ctx));
|
|
return FixedBufferAllocator.alloc(&self.fixed_buffer_allocator, len, alignment, ra) orelse
|
|
return self.fallback_allocator.rawAlloc(len, alignment, ra);
|
|
}
|
|
|
|
fn resize(
|
|
ctx: *anyopaque,
|
|
buf: []u8,
|
|
alignment: Alignment,
|
|
new_len: usize,
|
|
ra: usize,
|
|
) bool {
|
|
const self: *BufferFirstAllocator = @ptrCast(@alignCast(ctx));
|
|
if (self.fixed_buffer_allocator.ownsPtr(buf.ptr)) {
|
|
return FixedBufferAllocator.resize(&self.fixed_buffer_allocator, buf, alignment, new_len, ra);
|
|
} else {
|
|
return self.fallback_allocator.rawResize(buf, alignment, new_len, ra);
|
|
}
|
|
}
|
|
|
|
fn remap(
|
|
context: *anyopaque,
|
|
memory: []u8,
|
|
alignment: Alignment,
|
|
new_len: usize,
|
|
return_address: usize,
|
|
) ?[*]u8 {
|
|
const self: *BufferFirstAllocator = @ptrCast(@alignCast(context));
|
|
if (self.fixed_buffer_allocator.ownsPtr(memory.ptr)) {
|
|
return FixedBufferAllocator.remap(&self.fixed_buffer_allocator, memory, alignment, new_len, return_address);
|
|
} else {
|
|
return self.fallback_allocator.rawRemap(memory, alignment, new_len, return_address);
|
|
}
|
|
}
|
|
|
|
fn free(
|
|
ctx: *anyopaque,
|
|
buf: []u8,
|
|
alignment: Alignment,
|
|
ra: usize,
|
|
) void {
|
|
const self: *BufferFirstAllocator = @ptrCast(@alignCast(ctx));
|
|
if (self.fixed_buffer_allocator.ownsPtr(buf.ptr)) {
|
|
return FixedBufferAllocator.free(&self.fixed_buffer_allocator, buf, alignment, ra);
|
|
} else {
|
|
return self.fallback_allocator.rawFree(buf, alignment, ra);
|
|
}
|
|
}
|
|
|
|
test "BufferFirstAllocator" {
|
|
// Buffer first specific tests
|
|
{
|
|
var buffer: [10]u8 = undefined;
|
|
var bfa_state: BufferFirstAllocator = .init(&buffer, std.testing.allocator);
|
|
const bfa = bfa_state.allocator();
|
|
|
|
// We're under the limit, so we should be allocated in the buffer
|
|
const txt0 = "hellowrld";
|
|
const buf0 = try bfa.create(@TypeOf(txt0.*));
|
|
buf0.* = txt0.*;
|
|
try testing.expect(bfa_state.fixed_buffer_allocator.ownsPtr(buf0.ptr));
|
|
|
|
// We're now over the limit, so we should be allocated from the fallback
|
|
const txt1 = "test!";
|
|
const buf1 = try bfa.create(@TypeOf(txt1.*));
|
|
buf1.* = txt1.*;
|
|
try testing.expect(!bfa_state.fixed_buffer_allocator.ownsPtr(buf1.ptr));
|
|
|
|
// Free the allocation that took up space in the buffer
|
|
try testing.expectEqualStrings(txt0, buf0);
|
|
bfa.destroy(buf0);
|
|
|
|
// The next allocation would go in the buffer, but it's too big so it doesn't
|
|
const txt2 = "qwertyqwerty";
|
|
const buf2 = try bfa.create(@TypeOf(txt2.*));
|
|
buf2.* = txt2.*;
|
|
try testing.expect(!bfa_state.fixed_buffer_allocator.ownsPtr(buf2.ptr));
|
|
|
|
// The next allocation is smaller and fits in the buffer
|
|
const txt3 = "dvorak";
|
|
const buf3 = try bfa.create(@TypeOf(txt3.*));
|
|
buf3.* = txt3.*;
|
|
try testing.expect(bfa_state.fixed_buffer_allocator.ownsPtr(buf3.ptr));
|
|
|
|
// The remainder in the buffer is too small for the following allocation so it falls back
|
|
const txt4 = "moretext";
|
|
const buf4 = try bfa.create(@TypeOf(txt4.*));
|
|
buf4.* = txt4.*;
|
|
try testing.expect(!bfa_state.fixed_buffer_allocator.ownsPtr(buf4.ptr));
|
|
|
|
// Check equality on the remaining buffers and free them
|
|
try testing.expectEqualStrings(txt1, buf1);
|
|
bfa.destroy(buf1);
|
|
try testing.expectEqualStrings(txt2, buf2);
|
|
bfa.destroy(buf2);
|
|
try testing.expectEqualStrings(txt3, buf3);
|
|
bfa.destroy(buf3);
|
|
try testing.expectEqualStrings(txt4, buf4);
|
|
bfa.destroy(buf4);
|
|
|
|
try testing.expectEqual(0, bfa_state.fixed_buffer_allocator.end_index);
|
|
}
|
|
|
|
// Standard allocator tests
|
|
{
|
|
var buf: [4096]u8 = undefined;
|
|
{
|
|
var bfa: BufferFirstAllocator = .init(&buf, std.testing.allocator);
|
|
try heap.testAllocator(bfa.allocator());
|
|
}
|
|
{
|
|
var bfa: BufferFirstAllocator = .init(&buf, std.testing.allocator);
|
|
try heap.testAllocatorAligned(bfa.allocator());
|
|
}
|
|
{
|
|
var bfa: BufferFirstAllocator = .init(&buf, std.testing.allocator);
|
|
try heap.testAllocatorLargeAlignment(bfa.allocator());
|
|
}
|
|
{
|
|
var bfa: BufferFirstAllocator = .init(&buf, std.testing.allocator);
|
|
try heap.testAllocatorAlignedShrink(bfa.allocator());
|
|
}
|
|
}
|
|
}
|