Tidy up coral library
This commit is contained in:
		
							parent
							
								
									d5d5b69f54
								
							
						
					
					
						commit
						54e716e4cf
					
				| @ -1,69 +0,0 @@ | ||||
| const io = @import("./io.zig"); | ||||
| 
 | ||||
| const math = @import("./math.zig"); | ||||
| 
 | ||||
| pub const Fixed = struct { | ||||
| 	data: []u8, | ||||
| 	write_index: usize = 0, | ||||
| 
 | ||||
| 	pub fn as_writer(self: *Fixed) io.Writer { | ||||
| 		return io.Writer.bind(self, struct { | ||||
| 			fn fallible_write(fixed: *Fixed, buffer: []const u8) io.WriteError!usize { | ||||
| 				return fixed.write(buffer); | ||||
| 			} | ||||
| 		}.fallible_write); | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn remaining(self: Fixed) usize { | ||||
| 		return self.data.len - self.write_index; | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn is_empty(self: Fixed) bool { | ||||
| 		return !self.is_full(); | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn is_full(self: Fixed) bool { | ||||
| 		return self.write_index == self.data.len; | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn write(self: *Fixed, buffer: []const u8) usize { | ||||
| 		const range = math.min(usize, buffer.len, self.remaining()); | ||||
| 
 | ||||
| 		io.copy(self.data[self.write_index ..], buffer[0 .. range]); | ||||
| 
 | ||||
| 		self.write_index += range; | ||||
| 
 | ||||
| 		return range; | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| pub const Ring = struct { | ||||
| 	data: []u8, | ||||
| 	read_index: usize = 0, | ||||
| 	write_index: usize = 0, | ||||
| 
 | ||||
| 	pub fn as_reader(self: *Ring) io.Reader { | ||||
| 		return io.Reader.bind(self, Ring); | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn as_writer(self: *Ring) io.Writer { | ||||
| 		return io.Writer.bind(self, Ring); | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn filled(self: Ring) usize { | ||||
| 		return (self.write_index + (2 * self.data.len * | ||||
| 			@boolToInt(self.write_index < self.read_index))) - self.read_index; | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn is_empty(self: Ring) bool { | ||||
| 		return self.write_index == self.read_index; | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn is_full(self: Ring) bool { | ||||
| 		return ((self.write_index + self.data.len) % (self.data.len * 2)) != self.read_index; | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn remaining(self: Ring) usize { | ||||
| 		return self.data.len - self.filled(); | ||||
| 	} | ||||
| }; | ||||
| @ -1,19 +1,44 @@ | ||||
| pub const buffer = @import("./buffer.zig"); | ||||
| 
 | ||||
| /// | ||||
| /// Debug build-only utilities and sanity-checkers. | ||||
| /// | ||||
| pub const debug = @import("./debug.zig"); | ||||
| 
 | ||||
| pub const format = @import("./format.zig"); | ||||
| 
 | ||||
| /// | ||||
| /// Heap memory allocation strategies. | ||||
| /// | ||||
| pub const heap = @import("./heap.zig"); | ||||
| 
 | ||||
| /// | ||||
| /// Platform-agnostic data input and output operations. | ||||
| /// | ||||
| pub const io = @import("./io.zig"); | ||||
| 
 | ||||
| /// | ||||
| /// Data structures and utilities for sequential, "list-like" collections. | ||||
| /// | ||||
| pub const list = @import("./list.zig"); | ||||
| 
 | ||||
| /// | ||||
| /// Types and functions designed for mathematics in interactive media applications. | ||||
| /// | ||||
| pub const math = @import("./math.zig"); | ||||
| 
 | ||||
| /// | ||||
| /// Data structures and utilities for fragmented, "heap-like" collections. | ||||
| /// | ||||
| pub const slab = @import("./slab.zig"); | ||||
| 
 | ||||
| /// | ||||
| /// Data structures and utilities for the highly-specialized "slotmap" collection. | ||||
| /// | ||||
| pub const slots = @import("./slots.zig"); | ||||
| 
 | ||||
| pub const stack = @import("./stack.zig"); | ||||
| 
 | ||||
| /// | ||||
| /// Data structures and utilities for associative, "table-like" collections. | ||||
| /// | ||||
| pub const table = @import("./table.zig"); | ||||
| 
 | ||||
| /// | ||||
| /// Converters, parsers, and validators for sequences of bytes treated as UTF8 unicode strings. | ||||
| /// | ||||
| pub const utf8 = @import("./utf8.zig"); | ||||
|  | ||||
| @ -1,4 +1,10 @@ | ||||
| 
 | ||||
| /// | ||||
| /// Active code comment to assert that `condition` should always be true. | ||||
| /// | ||||
| /// Safety-checked behavior is invoked where `condition` evaluates to false. | ||||
| /// | ||||
| pub fn assert(condition: bool) void { | ||||
| 	if (!condition) unreachable; | ||||
| 	if (!condition) { | ||||
| 		unreachable; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -1,46 +0,0 @@ | ||||
| const io = @import("./io.zig"); | ||||
| 
 | ||||
| pub const Value = union(enum) { | ||||
| 	newline: void, | ||||
| 	string: []const u8, | ||||
| 	unsigned: u128, | ||||
| 	signed: i128, | ||||
| }; | ||||
| 
 | ||||
| pub fn print(writer: io.Writer, values: []const Value) io.WriteError!usize { | ||||
| 	var written: usize = 0; | ||||
| 
 | ||||
| 	for (values) |value| written += switch (value) { | ||||
| 		.newline => try writer.invoke("\n"), | ||||
| 		.string => |string| try writer.invoke(string), | ||||
| 		.unsigned => |unsigned| try print_unsigned(writer, unsigned), | ||||
| 	}; | ||||
| 
 | ||||
| 	return written; | ||||
| } | ||||
| 
 | ||||
| pub fn print_unsigned(writer: io.Writer, value: u128) io.WriteError!usize { | ||||
| 	if (value == 0) return writer.invoke("0"); | ||||
| 
 | ||||
| 	var buffer = [_]u8{0} ** 39; | ||||
| 	var buffer_count: usize = 0; | ||||
| 	var split_value = value; | ||||
| 
 | ||||
| 	while (split_value != 0) : (buffer_count += 1) { | ||||
| 		const radix = 10; | ||||
| 
 | ||||
| 		buffer[buffer_count] = @intCast(u8, (split_value % radix) + '0'); | ||||
| 		split_value = (split_value / radix); | ||||
| 	} | ||||
| 
 | ||||
| 	{ | ||||
| 		const half_buffer_count = buffer_count / 2; | ||||
| 		var index: usize = 0; | ||||
| 
 | ||||
| 		while (index < half_buffer_count) : (index += 1) { | ||||
| 			io.swap(u8, &buffer[index], &buffer[buffer_count - index - 1]); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return writer.invoke(buffer[0 .. buffer_count]); | ||||
| } | ||||
| @ -13,6 +13,9 @@ pub const AllocationOptions = struct { | ||||
| 
 | ||||
| pub const Allocator = Functor(?[]u8, AllocationOptions); | ||||
| 
 | ||||
| /// | ||||
| /// Function pointer coupled with a state context for providing dynamic dispatch over a given `Input` and `Output`. | ||||
| /// | ||||
| pub fn Functor(comptime Output: type, comptime Input: type) type { | ||||
| 	return struct { | ||||
| 		context: *anyopaque, | ||||
| @ -20,20 +23,19 @@ pub fn Functor(comptime Output: type, comptime Input: type) type { | ||||
| 
 | ||||
| 		const Self = @This(); | ||||
| 
 | ||||
| 		pub fn bind(state: anytype, comptime invoker: fn (capture: @TypeOf(state), input: Input) Output) Self { | ||||
| 			const State = @TypeOf(state); | ||||
| 			const state_info = @typeInfo(State); | ||||
| 
 | ||||
| 			if (state_info != .Pointer) { | ||||
| 				@compileError("`@typeOf(state)` must be a pointer type"); | ||||
| 			} | ||||
| 
 | ||||
| 		pub fn bind(comptime State: type, state: *State, comptime invoker: fn (capture: *State, input: Input) Output) Self { | ||||
| 			return .{ | ||||
| 				.context = state, | ||||
| 
 | ||||
| 				.invoker = struct { | ||||
| 					fn invoke_opaque(context: *anyopaque, input: Input) Output { | ||||
| 						return invoker(@ptrCast(State, @alignCast(@alignOf(state_info.Pointer.child), context)), input); | ||||
| 						const state_alignment = @alignOf(State); | ||||
| 
 | ||||
| 						if (state_alignment == 0) { | ||||
| 							return invoker(@ptrCast(*State, context), input); | ||||
| 						} | ||||
| 
 | ||||
| 						return invoker(@ptrCast(*State, @alignCast(state_alignment, context)), input); | ||||
| 					} | ||||
| 				}.invoke_opaque, | ||||
| 			}; | ||||
| @ -59,6 +61,39 @@ pub fn Tag(comptime Element: type) type { | ||||
| 	}; | ||||
| } | ||||
| 
 | ||||
| pub const WritableMemory = struct { | ||||
| 	slice: []u8, | ||||
| 
 | ||||
| 	pub fn as_writer(self: *WritableMemory) Writer { | ||||
| 		return Writer.bind(self, struct { | ||||
| 			fn write(writable_memory: *WritableMemory, data: []const u8) WriteError!usize { | ||||
| 				return writable_memory.write(data); | ||||
| 			} | ||||
| 		}.write); | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn put(self: *WritableMemory, byte: u8) bool { | ||||
| 		if (self.slice.len == 0) { | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		self.slice[0] = byte; | ||||
| 		self.slice = self.slice[1 ..]; | ||||
| 
 | ||||
| 		return true; | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn write(self: *WritableMemory, bytes: []const u8) usize { | ||||
| 		const writable = math.min(self.slice.len, bytes.len); | ||||
| 
 | ||||
| 		copy(self.slice, bytes); | ||||
| 
 | ||||
| 		self.slice = self.slice[writable ..]; | ||||
| 
 | ||||
| 		return writable; | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| pub const WriteError = error{ | ||||
| 	IoUnavailable, | ||||
| }; | ||||
| @ -98,13 +133,15 @@ pub fn bytes_of(value: anytype) []const u8 { | ||||
| } | ||||
| 
 | ||||
| pub fn compare(this: []const u8, that: []const u8) isize { | ||||
| 	const range = math.min(usize, this.len, that.len); | ||||
| 	const range = math.min(this.len, that.len); | ||||
| 	var index: usize = 0; | ||||
| 
 | ||||
| 	while (index < range) : (index += 1) { | ||||
| 		const difference = @intCast(isize, this[index]) - @intCast(isize, that[index]); | ||||
| 
 | ||||
| 		if (difference != 0) return difference; | ||||
| 		if (difference != 0) { | ||||
| 			return difference; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return @intCast(isize, this.len) - @intCast(isize, that.len); | ||||
| @ -132,6 +169,18 @@ pub fn deallocate(allocator: Allocator, allocation: anytype) void { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| pub fn bytes_to(comptime Type: type, source_bytes: []const u8) ?Type { | ||||
| 	const type_size = @sizeOf(Type); | ||||
| 
 | ||||
| 	if (source_bytes.len != type_size) return null; | ||||
| 
 | ||||
| 	var target_bytes = @as([type_size]u8, undefined); | ||||
| 
 | ||||
| 	copy(&target_bytes, source_bytes); | ||||
| 
 | ||||
| 	return @bitCast(Type, target_bytes); | ||||
| } | ||||
| 
 | ||||
| pub fn copy(target: []u8, source: []const u8) void { | ||||
| 	var index: usize = 0; | ||||
| 
 | ||||
| @ -185,18 +234,6 @@ pub fn overlaps(pointer: [*]u8, memory_range: []u8) bool { | ||||
| 	return (pointer >= memory_range.ptr) and (pointer < (memory_range.ptr + memory_range.len)); | ||||
| } | ||||
| 
 | ||||
| pub fn bytes_to(comptime Type: type, source_bytes: []const u8) ?Type { | ||||
| 	const type_size = @sizeOf(Type); | ||||
| 
 | ||||
| 	if (source_bytes.len != type_size) return null; | ||||
| 
 | ||||
| 	var target_bytes = @as([type_size]u8, undefined); | ||||
| 
 | ||||
| 	copy(&target_bytes, source_bytes); | ||||
| 
 | ||||
| 	return @bitCast(Type, target_bytes); | ||||
| } | ||||
| 
 | ||||
| pub fn reallocate(allocator: Allocator, allocation: anytype, amount: usize) AllocationError![]@typeInfo(@TypeOf(allocation)).Pointer.child { | ||||
| 	const pointer_info = @typeInfo(@TypeOf(allocation)).Pointer; | ||||
| 	const Element = pointer_info.child; | ||||
| @ -211,12 +248,12 @@ pub fn reallocate(allocator: Allocator, allocation: anytype, amount: usize) Allo | ||||
| 	}) orelse return error.OutOfMemory).ptr))[0 .. amount]; | ||||
| } | ||||
| 
 | ||||
| pub fn slice_sentineled(comptime element: type, comptime sentinel: element, sequence: [*:sentinel]const element) []const element { | ||||
| 	var length: usize = 0; | ||||
| pub fn sentinel_index(comptime element: type, comptime sentinel: element, sequence: [*:sentinel]const element) usize { | ||||
| 	var index: usize = 0; | ||||
| 
 | ||||
| 	while (sequence[length] != sentinel) : (length += 1) {} | ||||
| 	while (sequence[index] != sentinel) : (index += 1) {} | ||||
| 
 | ||||
| 	return sequence[0..length]; | ||||
| 	return index; | ||||
| } | ||||
| 
 | ||||
| pub fn stream(output: Writer, input: Reader, buffer: []u8) (ReadError || WriteError)!u64 { | ||||
|  | ||||
							
								
								
									
										225
									
								
								source/coral/list.zig
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										225
									
								
								source/coral/list.zig
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,225 @@ | ||||
| const debug = @import("./debug.zig"); | ||||
| 
 | ||||
| const io = @import("./io.zig"); | ||||
| 
 | ||||
| const math = @import("./math.zig"); | ||||
| 
 | ||||
| /// | ||||
| /// Returns a dynamically sized stack capable of holding `Element`. | ||||
| /// | ||||
| pub fn Stack(comptime Element: type) type { | ||||
| 	return struct { | ||||
| 		capacity: usize, | ||||
| 		values: []Element, | ||||
| 
 | ||||
| 		const Self = @This(); | ||||
| 
 | ||||
| 		/// | ||||
| 		/// Clears all elements from `self` while preserving the current internal buffer. | ||||
| 		/// | ||||
| 		/// To clean up memory allocations made by the stack and deinitialize it, see [deinit] instead. | ||||
| 		/// | ||||
| 		pub fn clear(self: *Self) void { | ||||
| 			self.values = self.values[0 .. 0]; | ||||
| 		} | ||||
| 
 | ||||
| 		/// | ||||
| 		/// Deinitializes `self` and sets it to an invalid state, freeing all memory allocated by `allocator`. | ||||
| 		/// | ||||
| 		/// To clear all items from the stack while preserving the current internal buffer, see [clear] instead. | ||||
| 		/// | ||||
| 		/// *Note* `allocator` must reference the same allocation strategy as the one originally used to initialize | ||||
| 		/// `self`. | ||||
| 		/// | ||||
| 		pub fn deinit(self: *Self, allocator: io.Allocator) void { | ||||
| 			io.deallocate(allocator, self.values); | ||||
| 
 | ||||
| 			self.capacity = 0; | ||||
| 		} | ||||
| 
 | ||||
| 		/// | ||||
| 		/// Attempts to remove `amount` number of `Element`s from the stack, returning `bool` if it was successful, | ||||
| 		/// otherwise `false` if the stack contains fewer elements than `amount`. | ||||
| 		/// | ||||
| 		pub fn drop(self: *Self, amount: usize) bool { | ||||
| 			if (amount > self.values.len) return false; | ||||
| 
 | ||||
| 			self.values = self.values[0 .. self.values.len - amount]; | ||||
| 
 | ||||
| 			return true; | ||||
| 		} | ||||
| 
 | ||||
| 		/// | ||||
| 		/// Attempts to grow the internal buffer of `self` by `growth_amount` using `allocator`. | ||||
| 		/// | ||||
| 		/// The function returns [AllocatorError] instead if `allocator` cannot commit the memory required to grow the | ||||
| 		/// internal buffer by `growth_amount`, leaving `self` in the same state that it was in prior to starting the | ||||
| 		/// grow. | ||||
| 		/// | ||||
| 		/// Growing ahead of pushing operations is useful when the upper bound of pushes is well-understood, as it can | ||||
| 		/// reduce the number of allocations required per push. | ||||
| 		/// | ||||
| 		/// *Note* `allocator` must reference the same allocation strategy as the one originally used to initialize | ||||
| 		/// `self`. | ||||
| 		/// | ||||
| 		pub fn grow(self: *Self, allocator: io.Allocator, growth_amount: usize) io.AllocationError!void { | ||||
| 			const grown_capacity = self.capacity + growth_amount; | ||||
| 			const values = (try io.allocate_many(Element, grown_capacity, allocator))[0 .. self.values.len]; | ||||
| 
 | ||||
| 			errdefer io.deallocate(allocator, values); | ||||
| 
 | ||||
| 			{ | ||||
| 				var index: usize = 0; | ||||
| 
 | ||||
| 				while (index < self.values.len) { | ||||
| 					values[index] = self.values[index]; | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			io.deallocate(allocator, self.values); | ||||
| 
 | ||||
| 			self.values = values; | ||||
| 			self.capacity = grown_capacity; | ||||
| 		} | ||||
| 
 | ||||
| 		/// | ||||
| 		/// Attempts to allocate and return an empty stack with an internal buffer of `initial_capacity` size using | ||||
| 		/// `allocator` as the memory allocation strategy. | ||||
| 		/// | ||||
| 		/// The function returns [AllocationError] instead if `allocator` cannot commit the memory required for an | ||||
| 		/// internal buffer of `initial_capacity` size. | ||||
| 		/// | ||||
| 		pub fn init(allocator: io.Allocator, initial_capacity: usize) !Self { | ||||
| 			const values = try io.allocate_many(Element, initial_capacity, allocator); | ||||
| 
 | ||||
| 			errdefer io.deallocate(values); | ||||
| 
 | ||||
| 			return Self{ | ||||
| 				.capacity = initial_capacity, | ||||
| 				.values = values[0 .. 0], | ||||
| 			}; | ||||
| 		} | ||||
| 
 | ||||
| 		/// | ||||
| 		/// Attempts to push every `Element` in `values` to `self` using `allocator` to grow the internal buffer as | ||||
| 		/// necessary. | ||||
| 		/// | ||||
| 		/// The function returns [AllocationError] instead if `allocator` cannot commit the memory required to grow the | ||||
| 		/// internal buffer of `self` when necessary. | ||||
| 		/// | ||||
| 		/// *Note* `allocator` must reference the same allocation strategy as the one originally used to initialize | ||||
| 		/// `self`. | ||||
| 		/// | ||||
| 		pub fn push_all(self: *Self, allocator: io.Allocator, values: []const Element) io.AllocationError!void { | ||||
| 			const new_length = self.values.len + values.len; | ||||
| 
 | ||||
| 			if (new_length >= self.capacity) { | ||||
| 				try self.grow(allocator, math.min(new_length, self.capacity)); | ||||
| 			} | ||||
| 
 | ||||
| 			const offset_index = self.values.len; | ||||
| 
 | ||||
| 			self.values = self.values.ptr[0 .. new_length]; | ||||
| 
 | ||||
| 			{ | ||||
| 				var index: usize = 0; | ||||
| 
 | ||||
| 				while (index < values.len) : (index += 1) self.values[offset_index + index] = values[index]; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		/// | ||||
| 		/// Attempts to push the `Element` in `value` to `self` by `amount` number of times using `allocator` to grow | ||||
| 		/// the internal buffer as necessary. | ||||
| 		/// | ||||
| 		/// The function returns [AllocationError] instead if `allocator` cannot commit the memory required to grow the | ||||
| 		/// internal buffer of `self` when necessary. | ||||
| 		/// | ||||
| 		/// *Note* `allocator` must reference the same allocation strategy as the one originally used to initialize | ||||
| 		/// `self`. | ||||
| 		/// | ||||
| 		pub fn push_many(self: *Self, allocator: io.Allocator, value: Element, amount: usize) io.AllocationError!void { | ||||
| 			const new_length = self.values.len + amount; | ||||
| 
 | ||||
| 			if (new_length >= self.capacity) { | ||||
| 				try self.grow(allocator, math.max(usize, new_length, self.capacity)); | ||||
| 			} | ||||
| 
 | ||||
| 			const offset_index = self.values.len; | ||||
| 
 | ||||
| 			self.values = self.values.ptr[0 .. new_length]; | ||||
| 
 | ||||
| 			{ | ||||
| 				var index: usize = 0; | ||||
| 
 | ||||
| 				while (index < amount) : (index += 1) self.values[offset_index + index] = value; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		/// | ||||
| 		/// Attempts to push the `Element` in `value` to `self` using `allocator` to grow the internal buffer as | ||||
| 		/// necessary. | ||||
| 		/// | ||||
| 		/// The function returns [AllocationError] instead if `allocator` cannot commit the memory required to grow the | ||||
| 		/// internal buffer of `self` when necessary. | ||||
| 		/// | ||||
| 		/// *Note* `allocator` must reference the same allocation strategy as the one originally used to initialize | ||||
| 		/// `self`. | ||||
| 		/// | ||||
| 		pub fn push_one(self: *Self, allocator: io.Allocator, value: Element) io.AllocationError!void { | ||||
| 			if (self.values.len == self.capacity) { | ||||
| 				try self.grow(allocator, math.max(1, self.capacity)); | ||||
| 			} | ||||
| 
 | ||||
| 			const offset_index = self.values.len; | ||||
| 
 | ||||
| 			self.values = self.values.ptr[0 .. self.values.len + 1]; | ||||
| 
 | ||||
| 			self.values[offset_index] = value; | ||||
| 		} | ||||
| 	}; | ||||
| } | ||||
| 
 | ||||
| /// | ||||
| /// Binds `stack` to a [io.Allocator], returning it. | ||||
| /// | ||||
| pub fn stack_as_allocator(stack: *Stack(u8)) io.Allocator { | ||||
| 	return io.Allocator.bind(stack, struct { | ||||
| 		pub fn reallocate(writable_stack: *Stack(u8), existing_allocation: ?[*]u8, allocation_size: usize) ?[*]u8 { | ||||
| 			if (allocation_size == 0) return null; | ||||
| 
 | ||||
| 			writable_stack.push_all(io.bytes_of(&allocation_size)) catch return null; | ||||
| 
 | ||||
| 			const usize_size = @sizeOf(usize); | ||||
| 
 | ||||
| 			errdefer debug.assert(writable_stack.drop(usize_size)); | ||||
| 
 | ||||
| 			const allocation_index = writable_stack.values.len; | ||||
| 
 | ||||
| 			if (existing_allocation) |allocation| { | ||||
| 				const existing_allocation_size = @intToPtr(*const usize, @ptrToInt(allocation) - usize_size).*; | ||||
| 
 | ||||
| 				writable_stack.push_all(allocation[0 .. existing_allocation_size]) catch return null; | ||||
| 			} else { | ||||
| 				writable_stack.push_many(0, allocation_size) catch return null; | ||||
| 			} | ||||
| 
 | ||||
| 			return @ptrCast([*]u8, &writable_stack.values[allocation_index]); | ||||
| 		} | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| /// | ||||
| /// Binds `stack` to a [io.Writer], returning it. | ||||
| /// | ||||
| pub fn stack_as_writer(stack: *Stack(u8)) io.Writer { | ||||
| 	return io.Writer.bind(stack, struct { | ||||
| 		pub fn write(writable_stack: *Stack(u8), buffer: []const u8) io.WriteError!usize { | ||||
| 			writable_stack.push_all(buffer) catch |grow_error| switch (grow_error) { | ||||
| 				error.OutOfMemory => return error.IoUnavailable, | ||||
| 			}; | ||||
| 
 | ||||
| 			return buffer.len; | ||||
| 		} | ||||
| 	}); | ||||
| } | ||||
| @ -1,3 +1,8 @@ | ||||
| const std = @import("std"); | ||||
| 
 | ||||
| /// | ||||
| /// Errors that may occur during checked integer arithmetic operations. | ||||
| /// | ||||
| pub const CheckedArithmeticError = error { | ||||
| 	IntOverflow, | ||||
| }; | ||||
| @ -20,54 +25,139 @@ pub fn Unsigned(comptime bits: comptime_int) type { | ||||
| 	}}); | ||||
| } | ||||
| 
 | ||||
| /// | ||||
| /// Two-dimensional vector type. | ||||
| /// | ||||
| pub const Vector2 = extern struct { | ||||
| 	x: f32, | ||||
| 	y: f32, | ||||
| 
 | ||||
| 	/// | ||||
| 	/// A [Vector2] with a value of `0` assigned to all of the components. | ||||
| 	/// | ||||
| 	pub const zero = Vector2{.x = 0, .y = 0}; | ||||
| }; | ||||
| 
 | ||||
| /// | ||||
| /// Attempts to perform a checked addition between `a` and `b`, returning the result or [CheckedArithmeticError] if the | ||||
| /// operation tried to invoke safety-checked behavior. | ||||
| /// | ||||
| /// `checked_add` can be seen as an alternative to the language-native addition operator (+) that exposes the safety- | ||||
| /// checked behavior in the form of an error type that may be caught or tried on. | ||||
| /// | ||||
| pub fn checked_add(a: anytype, b: anytype) CheckedArithmeticError!@TypeOf(a + b) { | ||||
| 	const result = @addWithOverflow(a, b); | ||||
| 
 | ||||
| 	if (result.@"1" != 0) return error.IntOverflow; | ||||
| 	if (result.@"1" != 0) { | ||||
| 		return error.IntOverflow; | ||||
| 	} | ||||
| 
 | ||||
| 	return result.@"0"; | ||||
| } | ||||
| 
 | ||||
| /// | ||||
| /// Attempts to perform a checked integer cast to `Int` on `value`, returning the result or [CheckedArithmeticError] if | ||||
| /// the operation tried to invoke safety-checked behavior. | ||||
| /// | ||||
| /// `checked_cast` can be seen as an alternative to the language-native `@intCast` builtin that exposes the safety- | ||||
| /// checked behavior in the form of an error type that may be caught or tried on. | ||||
| /// | ||||
| pub fn checked_cast(comptime Int: type, value: anytype) CheckedArithmeticError!Int { | ||||
| 	const int_type_info = @typeInfo(Int); | ||||
| 
 | ||||
| 	if (int_type_info != .Int) { | ||||
| 		@compileError("`Int` must be of type int"); | ||||
| 	} | ||||
| 
 | ||||
| 	if ((value < min_int(int_type_info.Int)) or (value > max_int(int_type_info.Int))) { | ||||
| 		return error.IntOverflow; | ||||
| 	} | ||||
| 
 | ||||
| 	return @intCast(Int, value); | ||||
| } | ||||
| 
 | ||||
| /// | ||||
| /// Attempts to perform a checked multiplication between `a` and `b`, returning the result or [CheckedArithmeticError] | ||||
| /// if the operation tried to invoke safety-checked behavior. | ||||
| /// | ||||
| /// `checked_mul` can be seen as an alternative to the language-native multiplication operator (*) that exposes the | ||||
| /// safety-checked behavior in the form of an error type that may be caught or tried on. | ||||
| /// | ||||
| pub fn checked_mul(a: anytype, b: anytype) CheckedArithmeticError!@TypeOf(a * b) { | ||||
| 	const result = @mulWithOverflow(a, b); | ||||
| 
 | ||||
| 	if (result.@"1" != 0) return error.IntOverflow; | ||||
| 	if (result.@"1" != 0) { | ||||
| 		return error.IntOverflow; | ||||
| 	} | ||||
| 
 | ||||
| 	return result.@"0"; | ||||
| } | ||||
| 
 | ||||
| /// | ||||
| /// Attempts to perform a checked subtraction between `a` and `b`, returning the result or [CheckedArithmeticError] if | ||||
| /// the operation tried to invoke safety-checked behavior. | ||||
| /// | ||||
| /// `checked_sub` can be seen as an alternative to the language-native subtraction operator (-) that exposes the safety- | ||||
| /// checked behavior in the form of an error type that may be caught or tried on. | ||||
| /// | ||||
| pub fn checked_sub(a: anytype, b: anytype) CheckedArithmeticError!@TypeOf(a - b) { | ||||
| 	const result = @subWithOverflow(a, b); | ||||
| 
 | ||||
| 	if (result.@"1" != 0) return error.IntOverflow; | ||||
| 	if (result.@"1" != 0) { | ||||
| 		return error.IntOverflow; | ||||
| 	} | ||||
| 
 | ||||
| 	return result.@"0"; | ||||
| } | ||||
| 
 | ||||
| pub fn clamp(comptime Scalar: type, value: Scalar, min_value: Scalar, max_value: Scalar) Scalar { | ||||
| 	return max(Scalar, min_value, min(Scalar, max_value, value)); | ||||
| /// | ||||
| /// Returns `value` clamped between the inclusive bounds of `lower` and `upper`. | ||||
| /// | ||||
| pub fn clamp(value: anytype, lower: anytype, upper: anytype) @TypeOf(value, lower, upper) { | ||||
| 	return max(lower, min(upper, value)); | ||||
| } | ||||
| 
 | ||||
| pub fn max(comptime Scalar: type, this: Scalar, that: Scalar) Scalar { | ||||
| 	return @max(this, that); | ||||
| /// | ||||
| /// Returns `true` if `value` is clamped within the inclusive bounds of `lower` and `upper`. | ||||
| /// | ||||
| pub fn is_clamped(value: anytype, lower: anytype, upper: anytype) bool { | ||||
| 	return (value >= lower) and (value <= upper); | ||||
| } | ||||
| 
 | ||||
| pub fn max_int(comptime Int: type) Int { | ||||
| 	const info = @typeInfo(Int); | ||||
| 	const bit_count = info.Int.bits; | ||||
| /// | ||||
| /// Returns the maximum value between `a` and `b`. | ||||
| /// | ||||
| pub fn max(a: anytype, b: anytype) @TypeOf(a, b) { | ||||
| 	return @max(a, b); | ||||
| } | ||||
| 
 | ||||
| /// | ||||
| /// Returns the maximum value that the integer described by `int` may express. | ||||
| /// | ||||
| pub fn max_int(comptime int: std.builtin.Type.Int) comptime_int { | ||||
| 	const bit_count = int.bits; | ||||
| 
 | ||||
| 	if (bit_count == 0) return 0; | ||||
| 
 | ||||
| 	return (1 << (bit_count - @boolToInt(info.Int.signedness == .signed))) - 1; | ||||
| 	return (1 << (bit_count - @boolToInt(int.signedness == .signed))) - 1; | ||||
| } | ||||
| 
 | ||||
| pub fn min(comptime Scalar: type, this: Scalar, that: Scalar) Scalar { | ||||
| 	return @min(this, that); | ||||
| /// | ||||
| /// Returns the minimum value between `a` and `b`. | ||||
| /// | ||||
| pub fn min(a: anytype, b: anytype) @TypeOf(a, b) { | ||||
| 	return @min(a, b); | ||||
| } | ||||
| 
 | ||||
| /// | ||||
| /// Returns the minimum value that the integer described by `int` may express. | ||||
| /// | ||||
| pub fn min_int(comptime int: std.builtin.Type.Int) comptime_int { | ||||
|     if (int.signedness == .unsigned) return 0; | ||||
| 
 | ||||
| 	const bit_count = int.bits; | ||||
| 
 | ||||
| 	if (bit_count == 0) return 0; | ||||
| 
 | ||||
|     return -(1 << (bit_count - 1)); | ||||
| } | ||||
|  | ||||
| @ -4,22 +4,20 @@ const io = @import("./io.zig"); | ||||
| 
 | ||||
| const math = @import("./math.zig"); | ||||
| 
 | ||||
| const stack = @import("./stack.zig"); | ||||
| 
 | ||||
| /// | ||||
| /// Retruns a set of dense slots that may store `Element`s indexable by a [Slot], where `key` defines how many bits the | ||||
| /// Retruns a dense mapping slots that may store `Element`s indexable by a [Slot], where `key` defines how many bits the | ||||
| /// [Slot] used is made from. | ||||
| /// | ||||
| pub fn Dense(comptime key: Key, comptime Element: type) type { | ||||
| pub fn Map(comptime key: Key, comptime Element: type) type { | ||||
| 	const KeySlot = Slot(key); | ||||
| 	const Index = math.Unsigned(key.index_bits); | ||||
| 
 | ||||
| 	return struct { | ||||
| 		capacity: usize = 0, | ||||
| 		values: []Element = &.{}, | ||||
| 		slots: ?[*]KeySlot = null, | ||||
| 		erase: ?[*]Index = null, | ||||
| 		next_free: Index = 0, | ||||
| 		capacity: usize, | ||||
| 		values: []Element, | ||||
| 		slots: [*]KeySlot, | ||||
| 		erase: [*]Index, | ||||
| 		next_free: Index, | ||||
| 
 | ||||
| 		const Self = @This(); | ||||
| 
 | ||||
| @ -120,6 +118,34 @@ pub fn Dense(comptime key: Key, comptime Element: type) type { | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		/// | ||||
| 		/// Attempts to return an initialized slot map with an initial capacity of `initial_capacity` and `allocator` as | ||||
| 		/// the memory allocation strategy. | ||||
| 		/// | ||||
| 		/// Upon failure, a [io.AllocationError] is returned instead. | ||||
| 		/// | ||||
| 		pub fn init(allocator: io.Allocator, initial_capacity: usize) io.AllocationError!Self { | ||||
| 			const values = try io.allocate_many(Element, initial_capacity, allocator); | ||||
| 
 | ||||
| 			errdefer io.deallocate(allocator, values); | ||||
| 
 | ||||
| 			const slots = try io.allocate_many(KeySlot, initial_capacity, allocator); | ||||
| 
 | ||||
| 			errdefer io.deallocate(allocator, slots); | ||||
| 
 | ||||
| 			const erase = try io.allocate_many(Index, initial_capacity, allocator); | ||||
| 
 | ||||
| 			errdefer io.deallocate(allocator, erase); | ||||
| 
 | ||||
| 			return Self{ | ||||
| 				.capacity = initial_capacity, | ||||
| 				.values = values[0 .. 0], | ||||
| 				.slots = slots.ptr, | ||||
| 				.erase = erase.ptr, | ||||
| 				.next_free = 0, | ||||
| 			}; | ||||
| 		} | ||||
| 
 | ||||
| 		/// | ||||
| 		/// Attempts to insert `value` into `self`, growing the internal buffer with `allocator` if it is full and | ||||
| 		/// returning a `Slot` of `key` referencing the inserted element or a [io.AllocationError] if it failed. | ||||
|  | ||||
| @ -1,123 +0,0 @@ | ||||
| const debug = @import("./debug.zig"); | ||||
| 
 | ||||
| const io = @import("./io.zig"); | ||||
| 
 | ||||
| const math = @import("./math.zig"); | ||||
| 
 | ||||
| pub fn Dense(comptime Element: type) type { | ||||
| 	return struct { | ||||
| 		capacity: usize = 0, | ||||
| 		values: []Element = &.{}, | ||||
| 
 | ||||
| 		const Self = @This(); | ||||
| 
 | ||||
| 		pub fn clear(self: *Self) void { | ||||
| 			self.values = self.values[0 .. 0]; | ||||
| 		} | ||||
| 
 | ||||
| 		pub fn deinit(self: *Self, allocator: io.Allocator) void { | ||||
| 			io.deallocate(allocator, self.values); | ||||
| 		} | ||||
| 
 | ||||
| 		pub fn drop(self: *Self, amount: usize) bool { | ||||
| 			if (amount > self.values.len) return false; | ||||
| 
 | ||||
| 			self.values = self.values[0 .. self.values.len - amount]; | ||||
| 
 | ||||
| 			return true; | ||||
| 		} | ||||
| 
 | ||||
| 		pub fn grow(self: *Self, allocator: io.Allocator, growth_amount: usize) io.AllocationError!void { | ||||
| 			const grown_capacity = self.capacity + growth_amount; | ||||
| 
 | ||||
| 			self.values = (try io.reallocate(allocator, self.values, grown_capacity))[0 .. self.values.len]; | ||||
| 			self.capacity = grown_capacity; | ||||
| 		} | ||||
| 
 | ||||
| 		pub fn push_all(self: *Self, allocator: io.Allocator, values: []const Element) io.AllocationError!void { | ||||
| 			const new_length = self.values.len + values.len; | ||||
| 
 | ||||
| 			if (new_length >= self.capacity) { | ||||
| 				try self.grow(allocator, math.min(usize, new_length, self.capacity)); | ||||
| 			} | ||||
| 
 | ||||
| 			const offset_index = self.values.len; | ||||
| 
 | ||||
| 			self.values = self.values.ptr[0 .. new_length]; | ||||
| 
 | ||||
| 			{ | ||||
| 				var index: usize = 0; | ||||
| 
 | ||||
| 				while (index < values.len) : (index += 1) self.values[offset_index + index] = values[index]; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		pub fn push_many(self: *Self, allocator: io.Allocator, value: Element, amount: usize) io.AllocationError!void { | ||||
| 			const new_length = self.values.len + amount; | ||||
| 
 | ||||
| 			if (new_length >= self.capacity) { | ||||
| 				try self.grow(allocator, math.max(usize, new_length, self.capacity)); | ||||
| 			} | ||||
| 
 | ||||
| 			const offset_index = self.values.len; | ||||
| 
 | ||||
| 			self.values = self.values.ptr[0 .. new_length]; | ||||
| 
 | ||||
| 			{ | ||||
| 				var index: usize = 0; | ||||
| 
 | ||||
| 				while (index < amount) : (index += 1) self.values[offset_index + index] = value; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		pub fn push_one(self: *Self, allocator: io.MemoryAllocator, value: Element) io.AllocationError!void { | ||||
| 			if (self.values.len == self.capacity) { | ||||
| 				try self.grow(allocator, math.max(usize, 1, self.capacity)); | ||||
| 			} | ||||
| 
 | ||||
| 			const offset_index = self.values.len; | ||||
| 
 | ||||
| 			self.values = self.values.ptr[0 .. self.values.len + 1]; | ||||
| 
 | ||||
| 			self.values[offset_index] = value; | ||||
| 		} | ||||
| 	}; | ||||
| } | ||||
| 
 | ||||
| pub fn as_dense_allocator(stack: *Dense(u8)) io.Allocator { | ||||
| 	return io.Allocator.bind(stack, struct { | ||||
| 		pub fn reallocate(writable_stack: *Dense(u8), existing_allocation: ?[*]u8, allocation_size: usize) ?[*]u8 { | ||||
| 			if (allocation_size == 0) return null; | ||||
| 
 | ||||
| 			writable_stack.push_all(io.bytes_of(&allocation_size)) catch return null; | ||||
| 
 | ||||
| 			const usize_size = @sizeOf(usize); | ||||
| 
 | ||||
| 			errdefer debug.assert(writable_stack.drop(usize_size)); | ||||
| 
 | ||||
| 			const allocation_index = writable_stack.values.len; | ||||
| 
 | ||||
| 			if (existing_allocation) |allocation| { | ||||
| 				const existing_allocation_size = @intToPtr(*const usize, @ptrToInt(allocation) - usize_size).*; | ||||
| 
 | ||||
| 				writable_stack.push_all(allocation[0 .. existing_allocation_size]) catch return null; | ||||
| 			} else { | ||||
| 				writable_stack.push_many(0, allocation_size) catch return null; | ||||
| 			} | ||||
| 
 | ||||
| 			return @ptrCast([*]u8, &writable_stack.values[allocation_index]); | ||||
| 		} | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| pub fn as_dense_writer(stack: *Dense(u8)) io.Writer { | ||||
| 	return io.Writer.bind(stack, struct { | ||||
| 		pub fn write(writable_stack: *Dense(u8), buffer: []const u8) io.WriteError!usize { | ||||
| 			writable_stack.push_all(buffer) catch |grow_error| switch (grow_error) { | ||||
| 				error.OutOfMemory => return error.IoUnavailable, | ||||
| 			}; | ||||
| 
 | ||||
| 			return buffer.len; | ||||
| 		} | ||||
| 	}); | ||||
| } | ||||
| @ -1,3 +1,5 @@ | ||||
| const io = @import("./io.zig"); | ||||
| 
 | ||||
| const math = @import("./math.zig"); | ||||
| 
 | ||||
| pub const IntParseError = math.CheckedArithmeticError || ParseError; | ||||
| @ -99,3 +101,29 @@ pub fn parse_unsigned(comptime bits: comptime_int, utf8: []const u8) IntParseErr | ||||
| 
 | ||||
| 	return result; | ||||
| } | ||||
| 
 | ||||
| pub fn print_unsigned(comptime bit_size: comptime_int, writer: io.Writer, value: math.Unsigned(bit_size)) io.WriteError!usize { | ||||
| 	if (value == 0) return writer.invoke("0"); | ||||
| 
 | ||||
| 	var buffer = [_]u8{0} ** 39; | ||||
| 	var buffer_count: usize = 0; | ||||
| 	var split_value = value; | ||||
| 
 | ||||
| 	while (split_value != 0) : (buffer_count += 1) { | ||||
| 		const radix = 10; | ||||
| 
 | ||||
| 		buffer[buffer_count] = @intCast(u8, (split_value % radix) + '0'); | ||||
| 		split_value = (split_value / radix); | ||||
| 	} | ||||
| 
 | ||||
| 	{ | ||||
| 		const half_buffer_count = buffer_count / 2; | ||||
| 		var index: usize = 0; | ||||
| 
 | ||||
| 		while (index < half_buffer_count) : (index += 1) { | ||||
| 			io.swap(u8, &buffer[index], &buffer[buffer_count - index - 1]); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return writer.invoke(buffer[0 .. buffer_count]); | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user