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"); | const testing = @import("./testing.zig"); | ||||||
| 
 | 
 | ||||||
| /// | /// | ||||||
|  | /// Dynamic memory allocation interface. | ||||||
| /// | /// | ||||||
| /// | pub const Allocator = union (enum) { | ||||||
| pub const Allocator = @import("std").mem.Allocator; |     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, | /// 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. | /// 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`. | /// Returns an [io.Writer] wrapping `fixed_stack`. | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| const io = @import("./io.zig"); | 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 | /// 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; |     const Allocator = io.Allocator; | ||||||
| 
 | 
 | ||||||
|     return struct { |     return struct { | ||||||
|         allocator: Allocator, |         allocator: *Allocator, | ||||||
|         load_limit: f32, |         load_limit: f32, | ||||||
|         buckets: []Bucket, |         buckets: []Bucket, | ||||||
|         filled: usize, |         filled: usize, | ||||||
| @ -36,7 +37,7 @@ pub fn Hashed(comptime Key: type, comptime Value: type, | |||||||
|         /// Deinitializes `self`, preventing any further use. |         /// Deinitializes `self`, preventing any further use. | ||||||
|         /// |         /// | ||||||
|         pub fn deinit(self: *Self) void { |         pub fn deinit(self: *Self) void { | ||||||
|             self.allocator.free(self.buckets); |             self.allocator.free(self.buckets.ptr); | ||||||
| 
 | 
 | ||||||
|             self.buckets = &.{}; |             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. |         /// 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; |             const initial_capacity = 4; | ||||||
| 
 | 
 | ||||||
|             return Self{ |             return Self{ | ||||||
|                 .buckets = try allocator.alloc(Bucket, initial_capacity), |                 .buckets = (try allocator.make(Bucket, initial_capacity))[0 .. initial_capacity], | ||||||
|                 .filled = 0, |                 .filled = 0, | ||||||
|                 .allocator = allocator, |                 .allocator = allocator, | ||||||
|                 .load_limit = 0.75, |                 .load_limit = 0.75, | ||||||
| @ -98,9 +99,11 @@ pub fn Hashed(comptime Key: type, comptime Value: type, | |||||||
|             if (self.loadFactor() >= self.load_limit) { |             if (self.loadFactor() >= self.load_limit) { | ||||||
|                 const old_buckets = self.buckets; |                 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; |                 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 | /// [InsertError.KeyExists] occurs when an insertion was attempted on a table with a matching key | ||||||
| /// already present. | /// already present. | ||||||
| /// | /// | ||||||
| pub const InsertError = io.Allocator.Error || error { | pub const InsertError = io.Allocator.MakeError || error { | ||||||
|     KeyExists, |     KeyExists, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -193,16 +196,16 @@ pub const string_literal_context = KeyContext([]const u8){ | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| test "Hash table manipulation with string literal context" { | test "Hash table manipulation with string literal context" { | ||||||
|     const testing = @import("std").testing; |     var buffer = [_]u8{0} ** 1024; | ||||||
| 
 |     var allocator = io.Allocator{.arena = .{.buffer = &buffer}}; | ||||||
|     var table = try Hashed([]const u8, u32, string_literal_context).init(testing.allocator); |     var table = try Hashed([]const u8, u32, string_literal_context).init(&allocator); | ||||||
| 
 | 
 | ||||||
|     defer table.deinit(); |     defer table.deinit(); | ||||||
| 
 | 
 | ||||||
|     const foo = @as(u32, 69); |     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 table.insert("foo", foo); | ||||||
|     try testing.expectEqual(table.remove("foo"), foo); |     try testing.expect(table.remove("foo").? == foo); | ||||||
|     try testing.expectEqual(table.remove("foo"), null); |     try testing.expect(table.remove("foo") == null); | ||||||
| } | } | ||||||
|  | |||||||
| @ -428,6 +428,19 @@ pub const RunError = error { | |||||||
|     InitFailure, |     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. | /// Runs a graphical application referenced by `run` with `error` as its error set. | ||||||
| /// | /// | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user