Created
November 28, 2018 14:58
-
-
Save Hejsil/442d8fa0c4c715b4b6f9db29f96a1684 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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