Skip to content

Instantly share code, notes, and snippets.

@Hejsil
Created November 28, 2018 14:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Hejsil/442d8fa0c4c715b4b6f9db29f96a1684 to your computer and use it in GitHub Desktop.
Save Hejsil/442d8fa0c4c715b4b6f9db29f96a1684 to your computer and use it in GitHub Desktop.
const std = @import("std");
const math = std.math;
const assert = std.debug.assert;
// All interaces are now a vtable + a ptr to self.
pub const Allocator = struct {
pub const Error = error{OutOfMemory};
pub const VTable = struct {
alloc: fn (self: *c_void, byte_count: usize, alignment: u29) Error![]u8,
};
vtable: *const VTable,
self: *c_void,
pub fn alloc(self: Allocator, comptime T: type, n: usize) ![]T {
return self.alignedAlloc(T, @alignOf(T), n);
}
pub fn alignedAlloc(self: Allocator, comptime T: type, comptime alignment: u29, n: usize) ![]align(alignment) T {
if (n == 0) {
return ([*]align(alignment) T)(undefined)[0..0];
}
const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;
const byte_slice = try self.vtable.alloc(self.self, byte_count, alignment);
assert(byte_slice.len == byte_count);
// This loop gets optimized out in ReleaseFast mode
for (byte_slice) |*byte| {
byte.* = undefined;
}
return @bytesToSlice(T, @alignCast(alignment, byte_slice));
}
};
// No need for implementation to contain any interface code.
pub fn getAllocator(fba: *FixedBufferAllocator) Allocator {
const Impl = struct {
const vtable = Allocator.VTable{
.alloc = alloc,
};
fn alloc(self: *c_void, n: usize, alignment: u29) ![]u8 {
const allocator = @ptrCast(*FixedBufferAllocator, @alignCast(8, self));
return try allocator.alloc(n, alignment);
}
};
return Allocator{
.vtable = &Impl.vtable,
.self = @ptrCast(*c_void, fba),
};
}
pub const FixedBufferAllocator = struct {
end_index: usize,
buffer: []u8,
pub fn init(buffer: []u8) FixedBufferAllocator {
return FixedBufferAllocator{
.buffer = buffer,
.end_index = 0,
};
}
pub fn alloc(self: *FixedBufferAllocator, n: usize, alignment: u29) ![]u8 {
const addr = @ptrToInt(self.buffer.ptr) + self.end_index;
const rem = @rem(addr, alignment);
const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
const adjusted_index = self.end_index + march_forward_bytes;
const new_end_index = adjusted_index + n;
if (new_end_index > self.buffer.len) {
return error.OutOfMemory;
}
const result = self.buffer[adjusted_index..new_end_index];
self.end_index = new_end_index;
return result;
}
};
test "New way to interface" {
var buf: [100]u8 = undefined;
var fba = FixedBufferAllocator.init(buf[0..]);
// You can never copy the interface struct out of it impl,
// as the impl does not contain the interface.
const allocator = getAllocator(&fba);
_ = allocator.alloc(u64, 2);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment