diff --git a/doc/langref/test_switch_dispatch_loop.zig b/doc/langref/test_switch_dispatch_loop.zig index e3b891595e..1756549094 100644 --- a/doc/langref/test_switch_dispatch_loop.zig +++ b/doc/langref/test_switch_dispatch_loop.zig @@ -8,20 +8,22 @@ const Instruction = enum { }; fn evaluate(initial_stack: []const i32, code: []const Instruction) !i32 { - var stack = try std.BoundedArray(i32, 8).fromSlice(initial_stack); + var buffer: [8]i32 = undefined; + var stack = std.ArrayListUnmanaged(i32).initBuffer(&buffer); + try stack.appendSliceBounded(initial_stack); var ip: usize = 0; return vm: switch (code[ip]) { // Because all code after `continue` is unreachable, this branch does // not provide a result. .add => { - try stack.append(stack.pop().? + stack.pop().?); + try stack.appendBounded(stack.pop().? + stack.pop().?); ip += 1; continue :vm code[ip]; }, .mul => { - try stack.append(stack.pop().? * stack.pop().?); + try stack.appendBounded(stack.pop().? * stack.pop().?); ip += 1; continue :vm code[ip]; diff --git a/lib/docs/wasm/markdown/Parser.zig b/lib/docs/wasm/markdown/Parser.zig index ce8db08d96..2c9fff2ae8 100644 --- a/lib/docs/wasm/markdown/Parser.zig +++ b/lib/docs/wasm/markdown/Parser.zig @@ -29,13 +29,14 @@ const Node = Document.Node; const ExtraIndex = Document.ExtraIndex; const ExtraData = Document.ExtraData; const StringIndex = Document.StringIndex; +const ArrayList = std.ArrayListUnmanaged; nodes: Node.List = .{}, -extra: std.ArrayListUnmanaged(u32) = .empty, -scratch_extra: std.ArrayListUnmanaged(u32) = .empty, -string_bytes: std.ArrayListUnmanaged(u8) = .empty, -scratch_string: std.ArrayListUnmanaged(u8) = .empty, -pending_blocks: std.ArrayListUnmanaged(Block) = .empty, +extra: ArrayList(u32) = .empty, +scratch_extra: ArrayList(u32) = .empty, +string_bytes: ArrayList(u8) = .empty, +scratch_string: ArrayList(u8) = .empty, +pending_blocks: ArrayList(Block) = .empty, allocator: Allocator, const Parser = @This(); @@ -86,7 +87,8 @@ const Block = struct { continuation_indent: usize, }, table: struct { - column_alignments: std.BoundedArray(Node.TableCellAlignment, max_table_columns) = .{}, + column_alignments_buffer: [max_table_columns]Node.TableCellAlignment, + column_alignments_len: usize, }, heading: struct { /// Between 1 and 6, inclusive. @@ -354,7 +356,8 @@ const BlockStart = struct { continuation_indent: usize, }, table_row: struct { - cells: std.BoundedArray([]const u8, max_table_columns), + cells_buffer: [max_table_columns][]const u8, + cells_len: usize, }, heading: struct { /// Between 1 and 6, inclusive. @@ -422,7 +425,8 @@ fn appendBlockStart(p: *Parser, block_start: BlockStart) !void { try p.pending_blocks.append(p.allocator, .{ .tag = .table, .data = .{ .table = .{ - .column_alignments = .{}, + .column_alignments_buffer = undefined, + .column_alignments_len = 0, } }, .string_start = p.scratch_string.items.len, .extra_start = p.scratch_extra.items.len, @@ -431,15 +435,19 @@ fn appendBlockStart(p: *Parser, block_start: BlockStart) !void { const current_row = p.scratch_extra.items.len - p.pending_blocks.getLast().extra_start; if (current_row <= 1) { - if (parseTableHeaderDelimiter(block_start.data.table_row.cells)) |alignments| { - p.pending_blocks.items[p.pending_blocks.items.len - 1].data.table.column_alignments = alignments; + var buffer: [max_table_columns]Node.TableCellAlignment = undefined; + const table_row = &block_start.data.table_row; + if (parseTableHeaderDelimiter(table_row.cells_buffer[0..table_row.cells_len], &buffer)) |alignments| { + const table = &p.pending_blocks.items[p.pending_blocks.items.len - 1].data.table; + @memcpy(table.column_alignments_buffer[0..alignments.len], alignments); + table.column_alignments_len = alignments.len; if (current_row == 1) { // We need to go back and mark the header row and its column // alignments. const datas = p.nodes.items(.data); const header_data = datas[p.scratch_extra.getLast()]; for (p.extraChildren(header_data.container.children), 0..) |header_cell, i| { - const alignment = if (i < alignments.len) alignments.buffer[i] else .unset; + const alignment = if (i < alignments.len) alignments[i] else .unset; const cell_data = &datas[@intFromEnum(header_cell)].table_cell; cell_data.info.alignment = alignment; cell_data.info.header = true; @@ -480,8 +488,10 @@ fn appendBlockStart(p: *Parser, block_start: BlockStart) !void { // available in the BlockStart. We can immediately parse and append // these children now. const containing_table = p.pending_blocks.items[p.pending_blocks.items.len - 2]; - const column_alignments = containing_table.data.table.column_alignments.slice(); - for (block_start.data.table_row.cells.slice(), 0..) |cell_content, i| { + const table = &containing_table.data.table; + const column_alignments = table.column_alignments_buffer[0..table.column_alignments_len]; + const table_row = &block_start.data.table_row; + for (table_row.cells_buffer[0..table_row.cells_len], 0..) |cell_content, i| { const cell_children = try p.parseInlines(cell_content); const alignment = if (i < column_alignments.len) column_alignments[i] else .unset; const cell = try p.addNode(.{ @@ -523,7 +533,8 @@ fn startBlock(p: *Parser, line: []const u8) !?BlockStart { return .{ .tag = .table_row, .data = .{ .table_row = .{ - .cells = table_row.cells, + .cells_buffer = table_row.cells_buffer, + .cells_len = table_row.cells_len, } }, .rest = "", }; @@ -606,7 +617,8 @@ fn startListItem(unindented_line: []const u8) ?ListItemStart { } const TableRowStart = struct { - cells: std.BoundedArray([]const u8, max_table_columns), + cells_buffer: [max_table_columns][]const u8, + cells_len: usize, }; fn startTableRow(unindented_line: []const u8) ?TableRowStart { @@ -615,7 +627,8 @@ fn startTableRow(unindented_line: []const u8) ?TableRowStart { mem.endsWith(u8, unindented_line, "\\|") or !mem.endsWith(u8, unindented_line, "|")) return null; - var cells: std.BoundedArray([]const u8, max_table_columns) = .{}; + var cells_buffer: [max_table_columns][]const u8 = undefined; + var cells: ArrayList([]const u8) = .initBuffer(&cells_buffer); const table_row_content = unindented_line[1 .. unindented_line.len - 1]; var cell_start: usize = 0; var i: usize = 0; @@ -623,7 +636,7 @@ fn startTableRow(unindented_line: []const u8) ?TableRowStart { switch (table_row_content[i]) { '\\' => i += 1, '|' => { - cells.append(table_row_content[cell_start..i]) catch return null; + cells.appendBounded(table_row_content[cell_start..i]) catch return null; cell_start = i + 1; }, '`' => { @@ -641,20 +654,21 @@ fn startTableRow(unindented_line: []const u8) ?TableRowStart { else => {}, } } - cells.append(table_row_content[cell_start..]) catch return null; + cells.appendBounded(table_row_content[cell_start..]) catch return null; - return .{ .cells = cells }; + return .{ .cells_buffer = cells_buffer, .cells_len = cells.items.len }; } fn parseTableHeaderDelimiter( - row_cells: std.BoundedArray([]const u8, max_table_columns), -) ?std.BoundedArray(Node.TableCellAlignment, max_table_columns) { - var alignments: std.BoundedArray(Node.TableCellAlignment, max_table_columns) = .{}; - for (row_cells.slice()) |content| { + row_cells: []const []const u8, + buffer: []Node.TableCellAlignment, +) ?[]Node.TableCellAlignment { + var alignments: ArrayList(Node.TableCellAlignment) = .initBuffer(buffer); + for (row_cells) |content| { const alignment = parseTableHeaderDelimiterCell(content) orelse return null; alignments.appendAssumeCapacity(alignment); } - return alignments; + return alignments.items; } fn parseTableHeaderDelimiterCell(content: []const u8) ?Node.TableCellAlignment { @@ -928,8 +942,8 @@ const InlineParser = struct { parent: *Parser, content: []const u8, pos: usize = 0, - pending_inlines: std.ArrayListUnmanaged(PendingInline) = .empty, - completed_inlines: std.ArrayListUnmanaged(CompletedInline) = .empty, + pending_inlines: ArrayList(PendingInline) = .empty, + completed_inlines: ArrayList(CompletedInline) = .empty, const PendingInline = struct { tag: Tag, diff --git a/lib/std/Io.zig b/lib/std/Io.zig index c55c28f177..688120c08b 100644 --- a/lib/std/Io.zig +++ b/lib/std/Io.zig @@ -231,21 +231,6 @@ pub fn GenericReader( return @errorCast(self.any().readBytesNoEof(num_bytes)); } - pub inline fn readIntoBoundedBytes( - self: Self, - comptime num_bytes: usize, - bounded: *std.BoundedArray(u8, num_bytes), - ) Error!void { - return @errorCast(self.any().readIntoBoundedBytes(num_bytes, bounded)); - } - - pub inline fn readBoundedBytes( - self: Self, - comptime num_bytes: usize, - ) Error!std.BoundedArray(u8, num_bytes) { - return @errorCast(self.any().readBoundedBytes(num_bytes)); - } - pub inline fn readInt(self: Self, comptime T: type, endian: std.builtin.Endian) NoEofError!T { return @errorCast(self.any().readInt(T, endian)); } diff --git a/lib/std/Io/DeprecatedReader.zig b/lib/std/Io/DeprecatedReader.zig index 59f163b39c..2e99328e42 100644 --- a/lib/std/Io/DeprecatedReader.zig +++ b/lib/std/Io/DeprecatedReader.zig @@ -249,33 +249,6 @@ pub fn readBytesNoEof(self: Self, comptime num_bytes: usize) anyerror![num_bytes return bytes; } -/// Reads bytes until `bounded.len` is equal to `num_bytes`, -/// or the stream ends. -/// -/// * it is assumed that `num_bytes` will not exceed `bounded.capacity()` -pub fn readIntoBoundedBytes( - self: Self, - comptime num_bytes: usize, - bounded: *std.BoundedArray(u8, num_bytes), -) anyerror!void { - while (bounded.len < num_bytes) { - // get at most the number of bytes free in the bounded array - const bytes_read = try self.read(bounded.unusedCapacitySlice()); - if (bytes_read == 0) return; - - // bytes_read will never be larger than @TypeOf(bounded.len) - // due to `self.read` being bounded by `bounded.unusedCapacitySlice()` - bounded.len += @as(@TypeOf(bounded.len), @intCast(bytes_read)); - } -} - -/// Reads at most `num_bytes` and returns as a bounded array. -pub fn readBoundedBytes(self: Self, comptime num_bytes: usize) anyerror!std.BoundedArray(u8, num_bytes) { - var result = std.BoundedArray(u8, num_bytes){}; - try self.readIntoBoundedBytes(num_bytes, &result); - return result; -} - pub inline fn readInt(self: Self, comptime T: type, endian: std.builtin.Endian) anyerror!T { const bytes = try self.readBytesNoEof(@divExact(@typeInfo(T).int.bits, 8)); return mem.readInt(T, &bytes, endian); diff --git a/lib/std/Io/Reader/test.zig b/lib/std/Io/Reader/test.zig index 30f0e1269c..69b7bcdbda 100644 --- a/lib/std/Io/Reader/test.zig +++ b/lib/std/Io/Reader/test.zig @@ -349,24 +349,3 @@ test "streamUntilDelimiter writes all bytes without delimiter to the output" { try std.testing.expectError(error.StreamTooLong, reader.streamUntilDelimiter(writer, '!', 5)); } - -test "readBoundedBytes correctly reads into a new bounded array" { - const test_string = "abcdefg"; - var fis = std.io.fixedBufferStream(test_string); - const reader = fis.reader(); - - var array = try reader.readBoundedBytes(10000); - try testing.expectEqualStrings(array.slice(), test_string); -} - -test "readIntoBoundedBytes correctly reads into a provided bounded array" { - const test_string = "abcdefg"; - var fis = std.io.fixedBufferStream(test_string); - const reader = fis.reader(); - - var bounded_array = std.BoundedArray(u8, 10000){}; - - // compile time error if the size is not the same at the provided `bounded.capacity()` - try reader.readIntoBoundedBytes(10000, &bounded_array); - try testing.expectEqualStrings(bounded_array.slice(), test_string); -} diff --git a/lib/std/array_list.zig b/lib/std/array_list.zig index c866a34e03..8af36a4a8e 100644 --- a/lib/std/array_list.zig +++ b/lib/std/array_list.zig @@ -657,6 +657,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig /// Initialize with externally-managed memory. The buffer determines the /// capacity, and the length is set to zero. + /// /// When initialized this way, all functions that accept an Allocator /// argument cause illegal behavior. pub fn initBuffer(buffer: Slice) Self { diff --git a/lib/std/base64.zig b/lib/std/base64.zig index a84f4a0b4f..37d3b70c20 100644 --- a/lib/std/base64.zig +++ b/lib/std/base64.zig @@ -517,17 +517,21 @@ fn testAllApis(codecs: Codecs, expected_decoded: []const u8, expected_encoded: [ var buffer: [0x100]u8 = undefined; const encoded = codecs.Encoder.encode(&buffer, expected_decoded); try testing.expectEqualSlices(u8, expected_encoded, encoded); - + } + { // stream encode - var list = try std.BoundedArray(u8, 0x100).init(0); - try codecs.Encoder.encodeWriter(list.writer(), expected_decoded); - try testing.expectEqualSlices(u8, expected_encoded, list.slice()); - + var buffer: [0x100]u8 = undefined; + var writer: std.Io.Writer = .fixed(&buffer); + try codecs.Encoder.encodeWriter(&writer, expected_decoded); + try testing.expectEqualSlices(u8, expected_encoded, writer.buffered()); + } + { // reader to writer encode - var stream = std.io.fixedBufferStream(expected_decoded); - list = try std.BoundedArray(u8, 0x100).init(0); - try codecs.Encoder.encodeFromReaderToWriter(list.writer(), stream.reader()); - try testing.expectEqualSlices(u8, expected_encoded, list.slice()); + var stream: std.Io.Reader = .fixed(expected_decoded); + var buffer: [0x100]u8 = undefined; + var writer: std.Io.Writer = .fixed(&buffer); + try codecs.Encoder.encodeFromReaderToWriter(&writer, &stream); + try testing.expectEqualSlices(u8, expected_encoded, writer.buffered()); } // Base64Decoder diff --git a/lib/std/bounded_array.zig b/lib/std/bounded_array.zig deleted file mode 100644 index 7864dfb775..0000000000 --- a/lib/std/bounded_array.zig +++ /dev/null @@ -1,412 +0,0 @@ -const std = @import("std.zig"); -const assert = std.debug.assert; -const mem = std.mem; -const testing = std.testing; -const Alignment = std.mem.Alignment; - -/// A structure with an array and a length, that can be used as a slice. -/// -/// Useful to pass around small arrays whose exact size is only known at -/// runtime, but whose maximum size is known at comptime, without requiring -/// an `Allocator`. -/// -/// ```zig -/// var actual_size = 32; -/// var a = try BoundedArray(u8, 64).init(actual_size); -/// var slice = a.slice(); // a slice of the 64-byte array -/// var a_clone = a; // creates a copy - the structure doesn't use any internal pointers -/// ``` -pub fn BoundedArray(comptime T: type, comptime buffer_capacity: usize) type { - return BoundedArrayAligned(T, .of(T), buffer_capacity); -} - -/// A structure with an array, length and alignment, that can be used as a -/// slice. -/// -/// Useful to pass around small explicitly-aligned arrays whose exact size is -/// only known at runtime, but whose maximum size is known at comptime, without -/// requiring an `Allocator`. -/// ```zig -// var a = try BoundedArrayAligned(u8, 16, 2).init(0); -// try a.append(255); -// try a.append(255); -// const b = @ptrCast(*const [1]u16, a.constSlice().ptr); -// try testing.expectEqual(@as(u16, 65535), b[0]); -/// ``` -pub fn BoundedArrayAligned( - comptime T: type, - comptime alignment: Alignment, - comptime buffer_capacity: usize, -) type { - return struct { - const Self = @This(); - buffer: [buffer_capacity]T align(alignment.toByteUnits()) = undefined, - len: usize = 0, - - /// Set the actual length of the slice. - /// Returns error.Overflow if it exceeds the length of the backing array. - pub fn init(len: usize) error{Overflow}!Self { - if (len > buffer_capacity) return error.Overflow; - return Self{ .len = len }; - } - - /// View the internal array as a slice whose size was previously set. - pub fn slice(self: anytype) switch (@TypeOf(&self.buffer)) { - *align(alignment.toByteUnits()) [buffer_capacity]T => []align(alignment.toByteUnits()) T, - *align(alignment.toByteUnits()) const [buffer_capacity]T => []align(alignment.toByteUnits()) const T, - else => unreachable, - } { - return self.buffer[0..self.len]; - } - - /// View the internal array as a constant slice whose size was previously set. - pub fn constSlice(self: *const Self) []align(alignment.toByteUnits()) const T { - return self.slice(); - } - - /// Adjust the slice's length to `len`. - /// Does not initialize added items if any. - pub fn resize(self: *Self, len: usize) error{Overflow}!void { - if (len > buffer_capacity) return error.Overflow; - self.len = len; - } - - /// Remove all elements from the slice. - pub fn clear(self: *Self) void { - self.len = 0; - } - - /// Copy the content of an existing slice. - pub fn fromSlice(m: []const T) error{Overflow}!Self { - var list = try init(m.len); - @memcpy(list.slice(), m); - return list; - } - - /// Return the element at index `i` of the slice. - pub fn get(self: Self, i: usize) T { - return self.constSlice()[i]; - } - - /// Set the value of the element at index `i` of the slice. - pub fn set(self: *Self, i: usize, item: T) void { - self.slice()[i] = item; - } - - /// Return the maximum length of a slice. - pub fn capacity(self: Self) usize { - return self.buffer.len; - } - - /// Check that the slice can hold at least `additional_count` items. - pub fn ensureUnusedCapacity(self: Self, additional_count: usize) error{Overflow}!void { - if (self.len + additional_count > buffer_capacity) { - return error.Overflow; - } - } - - /// Increase length by 1, returning a pointer to the new item. - pub fn addOne(self: *Self) error{Overflow}!*T { - try self.ensureUnusedCapacity(1); - return self.addOneAssumeCapacity(); - } - - /// Increase length by 1, returning pointer to the new item. - /// Asserts that there is space for the new item. - pub fn addOneAssumeCapacity(self: *Self) *T { - assert(self.len < buffer_capacity); - self.len += 1; - return &self.slice()[self.len - 1]; - } - - /// Resize the slice, adding `n` new elements, which have `undefined` values. - /// The return value is a pointer to the array of uninitialized elements. - pub fn addManyAsArray(self: *Self, comptime n: usize) error{Overflow}!*align(alignment.toByteUnits()) [n]T { - const prev_len = self.len; - try self.resize(self.len + n); - return self.slice()[prev_len..][0..n]; - } - - /// Resize the slice, adding `n` new elements, which have `undefined` values. - /// The return value is a slice pointing to the uninitialized elements. - pub fn addManyAsSlice(self: *Self, n: usize) error{Overflow}![]align(alignment.toByteUnits()) T { - const prev_len = self.len; - try self.resize(self.len + n); - return self.slice()[prev_len..][0..n]; - } - - /// Remove and return the last element from the slice, or return `null` if the slice is empty. - pub fn pop(self: *Self) ?T { - if (self.len == 0) return null; - const item = self.get(self.len - 1); - self.len -= 1; - return item; - } - - /// Return a slice of only the extra capacity after items. - /// This can be useful for writing directly into it. - /// Note that such an operation must be followed up with a - /// call to `resize()` - pub fn unusedCapacitySlice(self: *Self) []align(alignment.toByteUnits()) T { - return self.buffer[self.len..]; - } - - /// Insert `item` at index `i` by moving `slice[n .. slice.len]` to make room. - /// This operation is O(N). - pub fn insert( - self: *Self, - i: usize, - item: T, - ) error{Overflow}!void { - if (i > self.len) { - return error.Overflow; - } - _ = try self.addOne(); - var s = self.slice(); - mem.copyBackwards(T, s[i + 1 .. s.len], s[i .. s.len - 1]); - self.buffer[i] = item; - } - - /// Insert slice `items` at index `i` by moving `slice[i .. slice.len]` to make room. - /// This operation is O(N). - pub fn insertSlice(self: *Self, i: usize, items: []const T) error{Overflow}!void { - try self.ensureUnusedCapacity(items.len); - self.len += items.len; - mem.copyBackwards(T, self.slice()[i + items.len .. self.len], self.constSlice()[i .. self.len - items.len]); - @memcpy(self.slice()[i..][0..items.len], items); - } - - /// Replace range of elements `slice[start..][0..len]` with `new_items`. - /// Grows slice if `len < new_items.len`. - /// Shrinks slice if `len > new_items.len`. - pub fn replaceRange( - self: *Self, - start: usize, - len: usize, - new_items: []const T, - ) error{Overflow}!void { - const after_range = start + len; - var range = self.slice()[start..after_range]; - - if (range.len == new_items.len) { - @memcpy(range[0..new_items.len], new_items); - } else if (range.len < new_items.len) { - const first = new_items[0..range.len]; - const rest = new_items[range.len..]; - @memcpy(range[0..first.len], first); - try self.insertSlice(after_range, rest); - } else { - @memcpy(range[0..new_items.len], new_items); - const after_subrange = start + new_items.len; - for (self.constSlice()[after_range..], 0..) |item, i| { - self.slice()[after_subrange..][i] = item; - } - self.len -= len - new_items.len; - } - } - - /// Extend the slice by 1 element. - pub fn append(self: *Self, item: T) error{Overflow}!void { - const new_item_ptr = try self.addOne(); - new_item_ptr.* = item; - } - - /// Extend the slice by 1 element, asserting the capacity is already - /// enough to store the new item. - pub fn appendAssumeCapacity(self: *Self, item: T) void { - const new_item_ptr = self.addOneAssumeCapacity(); - new_item_ptr.* = item; - } - - /// Remove the element at index `i`, shift elements after index - /// `i` forward, and return the removed element. - /// Asserts the slice has at least one item. - /// This operation is O(N). - pub fn orderedRemove(self: *Self, i: usize) T { - const newlen = self.len - 1; - if (newlen == i) return self.pop().?; - const old_item = self.get(i); - for (self.slice()[i..newlen], 0..) |*b, j| b.* = self.get(i + 1 + j); - self.set(newlen, undefined); - self.len = newlen; - return old_item; - } - - /// Remove the element at the specified index and return it. - /// The empty slot is filled from the end of the slice. - /// This operation is O(1). - pub fn swapRemove(self: *Self, i: usize) T { - if (self.len - 1 == i) return self.pop().?; - const old_item = self.get(i); - self.set(i, self.pop().?); - return old_item; - } - - /// Append the slice of items to the slice. - pub fn appendSlice(self: *Self, items: []const T) error{Overflow}!void { - try self.ensureUnusedCapacity(items.len); - self.appendSliceAssumeCapacity(items); - } - - /// Append the slice of items to the slice, asserting the capacity is already - /// enough to store the new items. - pub fn appendSliceAssumeCapacity(self: *Self, items: []const T) void { - const old_len = self.len; - self.len += items.len; - @memcpy(self.slice()[old_len..][0..items.len], items); - } - - /// Append a value to the slice `n` times. - /// Allocates more memory as necessary. - pub fn appendNTimes(self: *Self, value: T, n: usize) error{Overflow}!void { - const old_len = self.len; - try self.resize(old_len + n); - @memset(self.slice()[old_len..self.len], value); - } - - /// Append a value to the slice `n` times. - /// Asserts the capacity is enough. - pub fn appendNTimesAssumeCapacity(self: *Self, value: T, n: usize) void { - const old_len = self.len; - self.len += n; - assert(self.len <= buffer_capacity); - @memset(self.slice()[old_len..self.len], value); - } - - pub const Writer = if (T != u8) - @compileError("The Writer interface is only defined for BoundedArray(u8, ...) " ++ - "but the given type is BoundedArray(" ++ @typeName(T) ++ ", ...)") - else - std.io.GenericWriter(*Self, error{Overflow}, appendWrite); - - /// Initializes a writer which will write into the array. - pub fn writer(self: *Self) Writer { - return .{ .context = self }; - } - - /// Same as `appendSlice` except it returns the number of bytes written, which is always the same - /// as `m.len`. The purpose of this function existing is to match `std.io.GenericWriter` API. - fn appendWrite(self: *Self, m: []const u8) error{Overflow}!usize { - try self.appendSlice(m); - return m.len; - } - }; -} - -test BoundedArray { - var a = try BoundedArray(u8, 64).init(32); - - try testing.expectEqual(a.capacity(), 64); - try testing.expectEqual(a.slice().len, 32); - try testing.expectEqual(a.constSlice().len, 32); - - try a.resize(48); - try testing.expectEqual(a.len, 48); - - const x = [_]u8{1} ** 10; - a = try BoundedArray(u8, 64).fromSlice(&x); - try testing.expectEqualSlices(u8, &x, a.constSlice()); - - var a2 = a; - try testing.expectEqualSlices(u8, a.constSlice(), a2.constSlice()); - a2.set(0, 0); - try testing.expect(a.get(0) != a2.get(0)); - - try testing.expectError(error.Overflow, a.resize(100)); - try testing.expectError(error.Overflow, BoundedArray(u8, x.len - 1).fromSlice(&x)); - - try a.resize(0); - try a.ensureUnusedCapacity(a.capacity()); - (try a.addOne()).* = 0; - try a.ensureUnusedCapacity(a.capacity() - 1); - try testing.expectEqual(a.len, 1); - - const uninitialized = try a.addManyAsArray(4); - try testing.expectEqual(uninitialized.len, 4); - try testing.expectEqual(a.len, 5); - - try a.append(0xff); - try testing.expectEqual(a.len, 6); - try testing.expectEqual(a.pop(), 0xff); - - a.appendAssumeCapacity(0xff); - try testing.expectEqual(a.len, 6); - try testing.expectEqual(a.pop(), 0xff); - - try a.resize(1); - try testing.expectEqual(a.pop(), 0); - try testing.expectEqual(a.pop(), null); - var unused = a.unusedCapacitySlice(); - @memset(unused[0..8], 2); - unused[8] = 3; - unused[9] = 4; - try testing.expectEqual(unused.len, a.capacity()); - try a.resize(10); - - try a.insert(5, 0xaa); - try testing.expectEqual(a.len, 11); - try testing.expectEqual(a.get(5), 0xaa); - try testing.expectEqual(a.get(9), 3); - try testing.expectEqual(a.get(10), 4); - - try a.insert(11, 0xbb); - try testing.expectEqual(a.len, 12); - try testing.expectEqual(a.pop(), 0xbb); - - try a.appendSlice(&x); - try testing.expectEqual(a.len, 11 + x.len); - - try a.appendNTimes(0xbb, 5); - try testing.expectEqual(a.len, 11 + x.len + 5); - try testing.expectEqual(a.pop(), 0xbb); - - a.appendNTimesAssumeCapacity(0xcc, 5); - try testing.expectEqual(a.len, 11 + x.len + 5 - 1 + 5); - try testing.expectEqual(a.pop(), 0xcc); - - try testing.expectEqual(a.len, 29); - try a.replaceRange(1, 20, &x); - try testing.expectEqual(a.len, 29 + x.len - 20); - - try a.insertSlice(0, &x); - try testing.expectEqual(a.len, 29 + x.len - 20 + x.len); - - try a.replaceRange(1, 5, &x); - try testing.expectEqual(a.len, 29 + x.len - 20 + x.len + x.len - 5); - - try a.append(10); - try testing.expectEqual(a.pop(), 10); - - try a.append(20); - const removed = a.orderedRemove(5); - try testing.expectEqual(removed, 1); - try testing.expectEqual(a.len, 34); - - a.set(0, 0xdd); - a.set(a.len - 1, 0xee); - const swapped = a.swapRemove(0); - try testing.expectEqual(swapped, 0xdd); - try testing.expectEqual(a.get(0), 0xee); - - const added_slice = try a.addManyAsSlice(3); - try testing.expectEqual(added_slice.len, 3); - try testing.expectEqual(a.len, 36); - - while (a.pop()) |_| {} - const w = a.writer(); - const s = "hello, this is a test string"; - try w.writeAll(s); - try testing.expectEqualStrings(s, a.constSlice()); -} - -test "BoundedArrayAligned" { - var a = try BoundedArrayAligned(u8, .@"16", 4).init(0); - try a.append(0); - try a.append(0); - try a.append(255); - try a.append(255); - - const b = @as(*const [2]u16, @ptrCast(a.constSlice().ptr)); - try testing.expectEqual(@as(u16, 0), b[0]); - try testing.expectEqual(@as(u16, 65535), b[1]); -} diff --git a/lib/std/std.zig b/lib/std/std.zig index 564b04c609..891c0bc256 100644 --- a/lib/std/std.zig +++ b/lib/std/std.zig @@ -9,8 +9,6 @@ pub const AutoArrayHashMapUnmanaged = array_hash_map.AutoArrayHashMapUnmanaged; pub const AutoHashMap = hash_map.AutoHashMap; pub const AutoHashMapUnmanaged = hash_map.AutoHashMapUnmanaged; pub const BitStack = @import("BitStack.zig"); -pub const BoundedArray = @import("bounded_array.zig").BoundedArray; -pub const BoundedArrayAligned = @import("bounded_array.zig").BoundedArrayAligned; pub const Build = @import("Build.zig"); pub const BufMap = @import("buf_map.zig").BufMap; pub const BufSet = @import("buf_set.zig").BufSet; diff --git a/src/Compilation.zig b/src/Compilation.zig index 419c56019f..4024e0a49e 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -2103,6 +2103,8 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil .local_zir_cache = local_zir_cache, .error_limit = error_limit, .llvm_object = null, + .analysis_roots_buffer = undefined, + .analysis_roots_len = 0, }; try zcu.init(options.thread_pool.getIdCount()); break :blk zcu; @@ -2933,22 +2935,26 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { try comp.appendFileSystemInput(embed_file.path); } - zcu.analysis_roots.clear(); + zcu.analysis_roots_len = 0; - zcu.analysis_roots.appendAssumeCapacity(zcu.std_mod); + zcu.analysis_roots_buffer[zcu.analysis_roots_len] = zcu.std_mod; + zcu.analysis_roots_len += 1; // Normally we rely on importing std to in turn import the root source file in the start code. // However, the main module is distinct from the root module in tests, so that won't happen there. if (comp.config.is_test and zcu.main_mod != zcu.std_mod) { - zcu.analysis_roots.appendAssumeCapacity(zcu.main_mod); + zcu.analysis_roots_buffer[zcu.analysis_roots_len] = zcu.main_mod; + zcu.analysis_roots_len += 1; } if (zcu.root_mod.deps.get("compiler_rt")) |compiler_rt_mod| { - zcu.analysis_roots.appendAssumeCapacity(compiler_rt_mod); + zcu.analysis_roots_buffer[zcu.analysis_roots_len] = compiler_rt_mod; + zcu.analysis_roots_len += 1; } if (zcu.root_mod.deps.get("ubsan_rt")) |ubsan_rt_mod| { - zcu.analysis_roots.appendAssumeCapacity(ubsan_rt_mod); + zcu.analysis_roots_buffer[zcu.analysis_roots_len] = ubsan_rt_mod; + zcu.analysis_roots_len += 1; } } @@ -4745,7 +4751,7 @@ fn performAllTheWork( try zcu.flushRetryableFailures(); // It's analysis time! Queue up our initial analysis. - for (zcu.analysis_roots.slice()) |mod| { + for (zcu.analysisRoots()) |mod| { try comp.queueJob(.{ .analyze_mod = mod }); } diff --git a/src/Zcu.zig b/src/Zcu.zig index c444d57bf6..d77a6edc31 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -268,7 +268,8 @@ nav_val_analysis_queued: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, voi /// These are the modules which we initially queue for analysis in `Compilation.update`. /// `resolveReferences` will use these as the root of its reachability traversal. -analysis_roots: std.BoundedArray(*Package.Module, 4) = .{}, +analysis_roots_buffer: [4]*Package.Module, +analysis_roots_len: usize = 0, /// This is the cached result of `Zcu.resolveReferences`. It is computed on-demand, and /// reset to `null` when any semantic analysis occurs (since this invalidates the data). /// Allocated into `gpa`. @@ -4013,8 +4014,8 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv // This is not a sufficient size, but a lower bound. try result.ensureTotalCapacity(gpa, @intCast(zcu.reference_table.count())); - try type_queue.ensureTotalCapacity(gpa, zcu.analysis_roots.len); - for (zcu.analysis_roots.slice()) |mod| { + try type_queue.ensureTotalCapacity(gpa, zcu.analysis_roots_len); + for (zcu.analysisRoots()) |mod| { const file = zcu.module_roots.get(mod).?.unwrap() orelse continue; const root_ty = zcu.fileRootType(file); if (root_ty == .none) continue; @@ -4202,6 +4203,10 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv return result; } +pub fn analysisRoots(zcu: *Zcu) []*Package.Module { + return zcu.analysis_roots_buffer[0..zcu.analysis_roots_len]; +} + pub fn fileByIndex(zcu: *const Zcu, file_index: File.Index) *File { return zcu.intern_pool.filePtr(file_index); } diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index de4be438f5..79ad9f14e9 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -2116,8 +2116,9 @@ pub fn computeAliveFiles(pt: Zcu.PerThread) Allocator.Error!bool { // multi-threaded environment (where things like file indices could differ between compiler runs). // The roots of our file liveness analysis will be the analysis roots. - try zcu.alive_files.ensureTotalCapacity(gpa, zcu.analysis_roots.len); - for (zcu.analysis_roots.slice()) |mod| { + const analysis_roots = zcu.analysisRoots(); + try zcu.alive_files.ensureTotalCapacity(gpa, analysis_roots.len); + for (analysis_roots) |mod| { const file_index = zcu.module_roots.get(mod).?.unwrap() orelse continue; const file = zcu.fileByIndex(file_index);