Application Context Implementation #4
							
								
								
									
										106
									
								
								src/core/io.zig
									
									
									
									
									
								
							
							
						
						
									
										106
									
								
								src/core/io.zig
									
									
									
									
									
								
							| @ -3,66 +3,46 @@ const meta = @import("./meta.zig"); | ||||
| const stack = @import("./stack.zig"); | ||||
| const testing = @import("./testing.zig"); | ||||
| 
 | ||||
| /// | ||||
| /// | ||||
| /// | ||||
| pub const Allocation = struct { | ||||
|     existing: ?[*]u8, | ||||
|     size: usize | ||||
| }; | ||||
| 
 | ||||
| /// | ||||
| /// Dynamic memory allocation interface. | ||||
| /// | ||||
| pub const Allocator = union (enum) { | ||||
|     bound: struct { | ||||
|         alloc: fn (usize) ?[*]u8, | ||||
|         dealloc: fn ([*]u8) void, | ||||
|     }, | ||||
| pub const Allocator = meta.Function(Allocation, ?[*]u8); | ||||
| 
 | ||||
|     arena: struct { | ||||
|         buffer: []u8, | ||||
|         cursor: usize = 0, | ||||
|     }, | ||||
| /// | ||||
| /// | ||||
| /// | ||||
| pub const ArenaAllocator = struct { | ||||
|     region: []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"), | ||||
|     pub fn allocator(arena_allocator: *ArenaAllocator) Allocator { | ||||
|         return Allocator.fromClosure(arena_allocator, struct { | ||||
|             fn call(context: *ArenaAllocator, allocation: Allocation) ?[*]u8 { | ||||
|                 _ = allocation; | ||||
|                 _ = context; | ||||
| 
 | ||||
|             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 => {}, | ||||
|         } | ||||
|                 return null; | ||||
|             } | ||||
|         }.call); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
|     /// | ||||
|     /// 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)); | ||||
|             }, | ||||
|         } | ||||
|     } | ||||
| /// | ||||
| /// | ||||
| /// | ||||
| pub const MakeError = error { | ||||
|     OutOfMemory, | ||||
| }; | ||||
| 
 | ||||
| /// | ||||
| @ -337,6 +317,24 @@ test "Find first of sequence" { | ||||
|     try testing.expect(findFirstOf([]const u8, haystack, &.{"baz", "bar"}, testEquality) == null); | ||||
| } | ||||
| 
 | ||||
| /// | ||||
| /// | ||||
| /// | ||||
| pub fn free(allocator: Allocator, allocated_memory: anytype) void { | ||||
|     if (allocator.call(.{ | ||||
|         .existing = @ptrCast([*]u8, switch (@typeInfo(@TypeOf(allocated_memory))) { | ||||
|             .Pointer => |info| switch (info.size) { | ||||
|                 .One, .Many, .C => allocated_memory, | ||||
|                 .Slice => allocated_memory.ptr, | ||||
|             }, | ||||
| 
 | ||||
|             else => @compileError("`allocated_memory` must be a pointer"), | ||||
|         }), | ||||
| 
 | ||||
|         .size = 0, | ||||
|     }) != null) unreachable; | ||||
| } | ||||
| 
 | ||||
| /// | ||||
| /// Returns a deterministic hash code compiled from each byte in `bytes`. | ||||
| /// | ||||
| @ -357,6 +355,16 @@ test "Hashing bytes" { | ||||
|     try testing.expect(hashBytes(bytes_sequence) != hashBytes(&.{69, 42})); | ||||
| } | ||||
| 
 | ||||
| /// | ||||
| /// | ||||
| /// | ||||
| pub fn makeMany(comptime Element: type, allocator: Allocator, size: usize) MakeError![*]Element { | ||||
|     return @ptrCast([*]Element, @alignCast(@alignOf(Element), allocator.call(.{ | ||||
|         .existing = null, | ||||
|         .size = size, | ||||
|     }) orelse return error.OutOfMemory)); | ||||
| } | ||||
| 
 | ||||
| /// | ||||
| /// Swaps the `Data` in `this` with `that`. | ||||
| /// | ||||
|  | ||||
| @ -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.MakeError; | ||||
| pub const PushError = io.MakeError; | ||||
| 
 | ||||
| /// | ||||
| /// Returns an [io.Writer] wrapping `fixed_stack`. | ||||
|  | ||||
| @ -11,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, | ||||
| @ -37,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.ptr); | ||||
|             io.free(self.allocator, self.buckets); | ||||
| 
 | ||||
|             self.buckets = &.{}; | ||||
|         } | ||||
| @ -45,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.MakeError] if initializing failed. | ||||
|         /// Returns a new [Self] value or an [io.MakeError] if initializing failed. | ||||
|         /// | ||||
|         pub fn init(allocator: *Allocator) Allocator.MakeError!Self { | ||||
|         pub fn init(allocator: Allocator) io.MakeError!Self { | ||||
|             const initial_capacity = 4; | ||||
| 
 | ||||
|             return Self{ | ||||
|                 .buckets = (try allocator.make(Bucket, initial_capacity))[0 .. initial_capacity], | ||||
|                 .buckets = (try io.makeMany(Bucket, allocator, initial_capacity))[0 .. initial_capacity], | ||||
|                 .filled = 0, | ||||
|                 .allocator = allocator, | ||||
|                 .load_limit = 0.75, | ||||
| @ -99,11 +99,12 @@ 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.ptr); | ||||
|                 defer io.free(self.allocator, old_buckets); | ||||
| 
 | ||||
|                 const bucket_count = old_buckets.len * 2; | ||||
| 
 | ||||
|                 self.buckets = (try self.allocator.make(Bucket, bucket_count))[0 .. bucket_count]; | ||||
|                 self.buckets = (try io.makeMany(Bucket, self.allocator, | ||||
|                     bucket_count))[0 .. bucket_count]; | ||||
| 
 | ||||
|                 for (old_buckets) |bucket, index| self.buckets[index] = bucket; | ||||
|             } | ||||
| @ -163,7 +164,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.MakeError || error { | ||||
| pub const InsertError = io.MakeError || error { | ||||
|     KeyExists, | ||||
| }; | ||||
| 
 | ||||
| @ -197,8 +198,10 @@ pub const string_literal_context = KeyContext([]const u8){ | ||||
| 
 | ||||
| test "Hash table manipulation with string literal context" { | ||||
|     var buffer = [_]u8{0} ** 1024; | ||||
|     var allocator = io.Allocator{.arena = .{.buffer = &buffer}}; | ||||
|     var table = try Hashed([]const u8, u32, string_literal_context).init(&allocator); | ||||
|     var arena_allocator = io.ArenaAllocator{.region = &buffer}; | ||||
| 
 | ||||
|     var table = | ||||
|         try Hashed([]const u8, u32, string_literal_context).init(arena_allocator.allocator()); | ||||
| 
 | ||||
|     defer table.deinit(); | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user