implement arena
This commit is contained in:
parent
5e7126ab93
commit
230acc6ce7
252
src/lib.zig
252
src/lib.zig
@ -1,9 +1,105 @@
|
|||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
extern "c" fn _errno() *c_int;
|
|
||||||
extern "c" fn ptrace(c_int, c_int, usize, usize) c_long;
|
|
||||||
extern "c" fn IsDebuggerPresent() bool;
|
extern "c" fn IsDebuggerPresent() bool;
|
||||||
|
extern "c" fn __errno_location() *c_int;
|
||||||
|
|
||||||
|
pub const KB = 1024;
|
||||||
|
pub const MB = 1024 * 1024;
|
||||||
|
pub const GB = 1024 * 1024 * 1024;
|
||||||
|
|
||||||
|
pub fn assert(ok: bool) void {
|
||||||
|
if (!ok) {
|
||||||
|
@branchHint(.unlikely);
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn errno() Error {
|
fn errno() Error {
|
||||||
return @enumFromInt(_errno().*);
|
return @enumFromInt(__errno_location().*);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn align_forward(T: type, value: T, alignment: T) T {
|
||||||
|
assert(alignment != 0);
|
||||||
|
const mask = alignment - 1;
|
||||||
|
const result = (value + mask) & ~mask;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn align_forward_u64(value: u64, alignment: u64) u64 {
|
||||||
|
return align_forward(u64, value, alignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn align_forward_u32(value: u32, alignment: u32) u32 {
|
||||||
|
return align_forward(u32, value, alignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ValueFromFlag = enum {
|
||||||
|
sub,
|
||||||
|
cmov,
|
||||||
|
};
|
||||||
|
const value_from_flag_kind = ValueFromFlag.sub;
|
||||||
|
|
||||||
|
pub fn value_from_flag(value: anytype, flag: anytype) @TypeOf(value) {
|
||||||
|
const flag_int: @TypeOf(value) = switch (@TypeOf(flag)) {
|
||||||
|
comptime_int => b: {
|
||||||
|
if (flag != 1 and flag != 0) {
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
break :b flag;
|
||||||
|
},
|
||||||
|
bool => @intFromBool(flag),
|
||||||
|
u1 => flag,
|
||||||
|
else => @compileError("Unhandled type: " ++ @typeName(@TypeOf(flag))),
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = switch (value_from_flag_kind) {
|
||||||
|
.cmov => {
|
||||||
|
@compileError("TODO");
|
||||||
|
},
|
||||||
|
.sub => value & (@as(@TypeOf(value), 0) -% flag_int),
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
test "value_from_flag" {
|
||||||
|
const std = @import("std");
|
||||||
|
const expect = std.testing.expect;
|
||||||
|
|
||||||
|
try expect(value_from_flag(1, 1) == 1);
|
||||||
|
try expect(value_from_flag(2, true) == 2);
|
||||||
|
try expect(value_from_flag(3, false) == 0);
|
||||||
|
try expect(value_from_flag(3, true) == 3);
|
||||||
|
try expect(value_from_flag(3, 1) == 3);
|
||||||
|
|
||||||
|
try expect(value_from_flag(0xffff, 1) == 0xffff);
|
||||||
|
try expect(value_from_flag(0xffff, 0) == 0);
|
||||||
|
try expect(value_from_flag(0xffff, true) == 0xffff);
|
||||||
|
try expect(value_from_flag(0xffff, false) == 0);
|
||||||
|
|
||||||
|
try expect(value_from_flag(0xffffffff, 1) == 0xffffffff);
|
||||||
|
try expect(value_from_flag(0xffffffff, 0) == 0);
|
||||||
|
try expect(value_from_flag(0xffffffff, true) == 0xffffffff);
|
||||||
|
try expect(value_from_flag(0xffffffff, false) == 0);
|
||||||
|
|
||||||
|
try expect(value_from_flag(0xffffffffffffffff, 1) == 0xffffffffffffffff);
|
||||||
|
try expect(value_from_flag(0xffffffffffffffff, 0) == 0);
|
||||||
|
try expect(value_from_flag(0xffffffffffffffff, true) == 0xffffffffffffffff);
|
||||||
|
try expect(value_from_flag(0xffffffffffffffff, false) == 0);
|
||||||
|
|
||||||
|
const a: u32 = 1235;
|
||||||
|
const b_true: bool = true;
|
||||||
|
const b_false: bool = false;
|
||||||
|
const u_true: u1 = 1;
|
||||||
|
const u_false: u1 = 0;
|
||||||
|
try expect(value_from_flag(a, b_true) == a);
|
||||||
|
try expect(value_from_flag(a, b_false) == 0);
|
||||||
|
try expect(value_from_flag(a, u_true) == a);
|
||||||
|
try expect(value_from_flag(a, u_false) == 0);
|
||||||
|
|
||||||
|
const b: u64 = 0xffffffffffffffff;
|
||||||
|
try expect(value_from_flag(b, b_true) == b);
|
||||||
|
try expect(value_from_flag(b, b_false) == 0);
|
||||||
|
try expect(value_from_flag(b, u_true) == b);
|
||||||
|
try expect(value_from_flag(b, u_false) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const Error = enum(c_int) {
|
pub const Error = enum(c_int) {
|
||||||
@ -11,13 +107,15 @@ pub const Error = enum(c_int) {
|
|||||||
PERM = 1,
|
PERM = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const u64_max = ~@as(u64, 0);
|
||||||
|
|
||||||
pub const os = struct {
|
pub const os = struct {
|
||||||
pub extern "c" fn exit(c_int) noreturn;
|
pub extern "c" fn exit(c_int) noreturn;
|
||||||
pub fn is_being_debugged() bool {
|
pub fn is_being_debugged() bool {
|
||||||
var result = false;
|
var result = false;
|
||||||
switch (builtin.os) {
|
switch (builtin.os.tag) {
|
||||||
.linux => {
|
.linux => {
|
||||||
if (ptrace(0, 0, 0, 0) == -1) {
|
if (linux.ptrace(0, 0, 0, 0) == -1) {
|
||||||
result = errno() == Error.PERM;
|
result = errno() == Error.PERM;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -27,4 +125,148 @@ pub const os = struct {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const linux = struct {
|
||||||
|
const PROT_READ: u32 = 1 << 0;
|
||||||
|
const PROT_WRITE: u32 = 1 << 1;
|
||||||
|
const PROT_EXEC: u32 = 1 << 2;
|
||||||
|
|
||||||
|
const MAP_PRIVATE: u32 = 1 << 1;
|
||||||
|
const MAP_ANONYMOUS: u32 = 1 << 5;
|
||||||
|
const MAP_NORESERVE: u32 = 1 << 14;
|
||||||
|
const MAP_POPULATE: u32 = 1 << 15;
|
||||||
|
|
||||||
|
extern "c" fn ptrace(c_int, c_int, usize, usize) c_long;
|
||||||
|
extern "c" fn mmap(usize, usize, u32, u32, c_int, isize) *align(4096) anyopaque;
|
||||||
|
extern "c" fn mprotect(*anyopaque, usize, u32) c_int;
|
||||||
|
|
||||||
|
fn protection_flags(protection: ProtectionFlags) u32 {
|
||||||
|
const result = value_from_flag(linux.PROT_READ, protection.read) | value_from_flag(linux.PROT_WRITE, protection.write) | value_from_flag(linux.PROT_EXEC, protection.execute);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const ProtectionFlags = packed struct {
|
||||||
|
read: u1,
|
||||||
|
write: u1,
|
||||||
|
execute: u1,
|
||||||
|
};
|
||||||
|
const MapFlags = packed struct {
|
||||||
|
private: u1,
|
||||||
|
anonymous: u1,
|
||||||
|
no_reserve: u1,
|
||||||
|
populate: u1,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn reserve(base: u64, size: u64, protection: ProtectionFlags, map: MapFlags) *align(4096) anyopaque {
|
||||||
|
switch (builtin.os.tag) {
|
||||||
|
.windows => @compileError("TODO"),
|
||||||
|
else => {
|
||||||
|
const protection_flags: u32 = value_from_flag(linux.PROT_READ, protection.read) | value_from_flag(linux.PROT_WRITE, protection.write) | value_from_flag(linux.PROT_EXEC, protection.execute);
|
||||||
|
const map_flags = value_from_flag(linux.MAP_ANONYMOUS, map.anonymous) | value_from_flag(linux.MAP_PRIVATE, map.private) | value_from_flag(linux.MAP_NORESERVE, map.no_reserve) | switch (builtin.os.tag) {
|
||||||
|
.linux => value_from_flag(linux.MAP_POPULATE, map.populate),
|
||||||
|
else => 0,
|
||||||
|
};
|
||||||
|
const address = linux.mmap(base, size, protection_flags, map_flags, -1, 0);
|
||||||
|
if (@intFromPtr(address) == u64_max) {
|
||||||
|
@branchHint(.unlikely);
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
return address;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn commit(address: *anyopaque, size: u64, protection: ProtectionFlags) void {
|
||||||
|
switch (builtin.os.tag) {
|
||||||
|
.windows => @compileError("TODO"),
|
||||||
|
else => {
|
||||||
|
const protection_flags = linux.protection_flags(protection);
|
||||||
|
const result = linux.mprotect(address, size, protection_flags);
|
||||||
|
if (result != 0) {
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Arena = struct {
|
||||||
|
reserved_size: u64,
|
||||||
|
position: u64,
|
||||||
|
os_position: u64,
|
||||||
|
granularity: u64,
|
||||||
|
reserved: [32]u8 = [1]u8{0} ** 32,
|
||||||
|
|
||||||
|
const minimum_position = @sizeOf(Arena);
|
||||||
|
|
||||||
|
const Initialization = struct {
|
||||||
|
reserved_size: u64,
|
||||||
|
granularity: u64,
|
||||||
|
initial_size: u64,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn initialize(initialization: Initialization) *Arena {
|
||||||
|
const protection_flags = os.ProtectionFlags{
|
||||||
|
.read = 1,
|
||||||
|
.write = 1,
|
||||||
|
.execute = 0,
|
||||||
|
};
|
||||||
|
const map_flags = os.MapFlags{
|
||||||
|
.private = 1,
|
||||||
|
.anonymous = 1,
|
||||||
|
.no_reserve = 1,
|
||||||
|
.populate = 0,
|
||||||
|
};
|
||||||
|
const arena: *Arena = @ptrCast(os.reserve(0, initialization.reserved_size, protection_flags, map_flags));
|
||||||
|
os.commit(arena, initialization.initial_size, .{
|
||||||
|
.read = 1,
|
||||||
|
.write = 1,
|
||||||
|
.execute = 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
arena.* = .{
|
||||||
|
.reserved_size = initialization.reserved_size,
|
||||||
|
.os_position = initialization.initial_size,
|
||||||
|
.position = minimum_position,
|
||||||
|
.granularity = initialization.granularity,
|
||||||
|
};
|
||||||
|
|
||||||
|
return arena;
|
||||||
|
}
|
||||||
|
|
||||||
|
const default_size = 4 * GB;
|
||||||
|
const minimum_granularity = 4 * KB;
|
||||||
|
|
||||||
|
pub fn initialize_default(initial_size: u64) *Arena {
|
||||||
|
const arena = initialize(.{
|
||||||
|
.reserved_size = default_size,
|
||||||
|
.granularity = minimum_granularity,
|
||||||
|
.initial_size = initial_size,
|
||||||
|
});
|
||||||
|
return arena;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn allocate_bytes(arena: *Arena, size: u64, alignment: u64) *u8 {
|
||||||
|
const aligned_offset = align_forward_u64(arena.position, alignment);
|
||||||
|
const aligned_size_after = aligned_offset + size;
|
||||||
|
|
||||||
|
if (aligned_size_after > arena.os_position) {
|
||||||
|
const target_committed_size = align_forward_u64(aligned_size_after, arena.granularity);
|
||||||
|
const size_to_commit = target_committed_size - arena.os_position;
|
||||||
|
const commit_pointer = @as(*anyopaque, @ptrFromInt(@intFromPtr(arena) + arena.os_position));
|
||||||
|
os.commit(commit_pointer, size_to_commit, .{
|
||||||
|
.read = 1,
|
||||||
|
.write = 1,
|
||||||
|
.execute = 0,
|
||||||
|
});
|
||||||
|
arena.os_position = target_committed_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = @as(*u8, @ptrFromInt(@intFromPtr(arena) + aligned_offset));
|
||||||
|
arena.position = aligned_size_after;
|
||||||
|
assert(arena.position <= arena.os_position);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@ -11,6 +11,8 @@ pub fn panic(message: []const u8, stack_trace: ?*anyopaque, return_address: ?usi
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() callconv(.C) c_int {
|
pub fn main() callconv(.C) c_int {
|
||||||
|
const arena = lib.Arena.initialize_default(2 * lib.MB);
|
||||||
|
_ = arena.allocate_bytes(1024, 1);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,3 +23,7 @@ comptime {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
_ = lib;
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user