Initial work on allocator replacement
This commit is contained in:
parent
cfd3e85021
commit
d49608f7bb
|
@ -4,9 +4,66 @@ const stack = @import("./stack.zig");
|
|||
const testing = @import("./testing.zig");
|
||||
|
||||
///
|
||||
/// Dynamic memory allocation interface.
|
||||
///
|
||||
///
|
||||
pub const Allocator = @import("std").mem.Allocator;
|
||||
pub const Allocator = union (enum) {
|
||||
bound: struct {
|
||||
alloc: fn (usize) ?[*]u8,
|
||||
dealloc: fn ([*]u8) void,
|
||||
},
|
||||
|
||||
arena: struct {
|
||||
buffer: []u8,
|
||||
cursor: usize = 0,
|
||||
},
|
||||
|
||||
///
|
||||
/// [MakeError.OutOfMemory] is used to indicate there is not enough memory available for a given
|
||||
/// operation.
|
||||
///
|
||||
pub const MakeError = error {
|
||||
OutOfMemory,
|
||||
};
|
||||
|
||||
///
|
||||
/// Frees `allocation` using `allocator`.
|
||||
///
|
||||
pub fn free(allocator: *Allocator, allocation: anytype) void {
|
||||
switch (@typeInfo(@TypeOf(allocation))) {
|
||||
.Pointer => |pointer| if (pointer.size == .Slice)
|
||||
@compileError("`allocation` cannot be a slice"),
|
||||
|
||||
else => @compileError("`allocation` must be a pointer"),
|
||||
}
|
||||
|
||||
if (@typeInfo(@TypeOf(allocation)) != .Pointer)
|
||||
@compileError("`allocation` must be a pointer");
|
||||
|
||||
// TODO: Implement arena de-allocation.
|
||||
switch (allocator.*) {
|
||||
.bound => |bound| bound.dealloc(@ptrCast([*]u8, allocation)),
|
||||
.arena => {},
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Attempts to allocate `size` number of `Element`s using `allocator`.
|
||||
///
|
||||
/// Returns the allocation or a [MakeError] if it failed.
|
||||
///
|
||||
pub fn make(allocator: *Allocator, comptime Element: type, size: usize) MakeError![*]Element {
|
||||
switch (allocator.*) {
|
||||
.bound => |bound| return @ptrCast([*]Element, @alignCast(@alignOf(Element),
|
||||
bound.alloc(@sizeOf(Element) * size) orelse return error.OutOfMemory)),
|
||||
|
||||
.arena => |*stack| {
|
||||
defer stack.cursor += size;
|
||||
|
||||
return @ptrCast([*]Element, @alignCast(@alignOf(Element), stack.buffer.ptr));
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
/// Closure that captures a reference to readable resources like block devices, memory buffers,
|
||||
|
|
|
@ -113,7 +113,7 @@ test "Fixed stack of string literals" {
|
|||
///
|
||||
/// Potential errors that may occur while trying to push one or more elements into a stack.
|
||||
///
|
||||
pub const PushError = io.Allocator.Error;
|
||||
pub const PushError = io.Allocator.MakeError;
|
||||
|
||||
///
|
||||
/// Returns an [io.Writer] wrapping `fixed_stack`.
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const io = @import("./io.zig");
|
||||
const testing = @import("./testing.zig");
|
||||
|
||||
///
|
||||
/// Returns a hash-backed table type of `Value`s indexed by `Key` and using `key_context` as the key
|
||||
|
@ -10,7 +11,7 @@ pub fn Hashed(comptime Key: type, comptime Value: type,
|
|||
const Allocator = io.Allocator;
|
||||
|
||||
return struct {
|
||||
allocator: Allocator,
|
||||
allocator: *Allocator,
|
||||
load_limit: f32,
|
||||
buckets: []Bucket,
|
||||
filled: usize,
|
||||
|
@ -36,7 +37,7 @@ pub fn Hashed(comptime Key: type, comptime Value: type,
|
|||
/// Deinitializes `self`, preventing any further use.
|
||||
///
|
||||
pub fn deinit(self: *Self) void {
|
||||
self.allocator.free(self.buckets);
|
||||
self.allocator.free(self.buckets.ptr);
|
||||
|
||||
self.buckets = &.{};
|
||||
}
|
||||
|
@ -44,13 +45,13 @@ pub fn Hashed(comptime Key: type, comptime Value: type,
|
|||
///
|
||||
/// Initializes a [Self] using `allocator` as the memory allocation strategy.
|
||||
///
|
||||
/// Returns a new [Self] value or an [io.Allocator.Error] if initializing failed.
|
||||
/// Returns a new [Self] value or an [io.Allocator.MakeError] if initializing failed.
|
||||
///
|
||||
pub fn init(allocator: Allocator) Allocator.Error!Self {
|
||||
pub fn init(allocator: *Allocator) Allocator.MakeError!Self {
|
||||
const initial_capacity = 4;
|
||||
|
||||
return Self{
|
||||
.buckets = try allocator.alloc(Bucket, initial_capacity),
|
||||
.buckets = (try allocator.make(Bucket, initial_capacity))[0 .. initial_capacity],
|
||||
.filled = 0,
|
||||
.allocator = allocator,
|
||||
.load_limit = 0.75,
|
||||
|
@ -98,9 +99,11 @@ pub fn Hashed(comptime Key: type, comptime Value: type,
|
|||
if (self.loadFactor() >= self.load_limit) {
|
||||
const old_buckets = self.buckets;
|
||||
|
||||
defer self.allocator.free(old_buckets);
|
||||
defer self.allocator.free(old_buckets.ptr);
|
||||
|
||||
self.buckets = try self.allocator.alloc(Bucket, old_buckets.len * 2);
|
||||
const bucket_count = old_buckets.len * 2;
|
||||
|
||||
self.buckets = (try self.allocator.make(Bucket, bucket_count))[0 .. bucket_count];
|
||||
|
||||
for (old_buckets) |bucket, index| self.buckets[index] = bucket;
|
||||
}
|
||||
|
@ -160,7 +163,7 @@ pub fn Hashed(comptime Key: type, comptime Value: type,
|
|||
/// [InsertError.KeyExists] occurs when an insertion was attempted on a table with a matching key
|
||||
/// already present.
|
||||
///
|
||||
pub const InsertError = io.Allocator.Error || error {
|
||||
pub const InsertError = io.Allocator.MakeError || error {
|
||||
KeyExists,
|
||||
};
|
||||
|
||||
|
@ -193,16 +196,16 @@ pub const string_literal_context = KeyContext([]const u8){
|
|||
};
|
||||
|
||||
test "Hash table manipulation with string literal context" {
|
||||
const testing = @import("std").testing;
|
||||
|
||||
var table = try Hashed([]const u8, u32, string_literal_context).init(testing.allocator);
|
||||
var buffer = [_]u8{0} ** 1024;
|
||||
var allocator = io.Allocator{.arena = .{.buffer = &buffer}};
|
||||
var table = try Hashed([]const u8, u32, string_literal_context).init(&allocator);
|
||||
|
||||
defer table.deinit();
|
||||
|
||||
const foo = @as(u32, 69);
|
||||
|
||||
try testing.expectEqual(table.remove("foo"), null);
|
||||
try testing.expect(table.remove("foo") == null);
|
||||
try table.insert("foo", foo);
|
||||
try testing.expectEqual(table.remove("foo"), foo);
|
||||
try testing.expectEqual(table.remove("foo"), null);
|
||||
try testing.expect(table.remove("foo").? == foo);
|
||||
try testing.expect(table.remove("foo") == null);
|
||||
}
|
||||
|
|
|
@ -428,6 +428,19 @@ pub const RunError = error {
|
|||
InitFailure,
|
||||
};
|
||||
|
||||
///
|
||||
/// Returns a [core.io.Allocator] bound to the underlying system allocator.
|
||||
///
|
||||
pub fn allocator() core.io.Allocator {
|
||||
// TODO: Add leak detection.
|
||||
return .{
|
||||
.bound = .{
|
||||
.alloc = ext.SDL_alloc,
|
||||
.dealloc = ext.SDL_free,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
///
|
||||
/// Runs a graphical application referenced by `run` with `error` as its error set.
|
||||
///
|
||||
|
|
Loading…
Reference in New Issue