authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-08-05 16:24:28-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-08-05 16:24:28-07:00
logd8cecffe314230f1ff42391be98622ac8098833c
tree791ccaa082bd52364d68a1aeb630747a8fa69f90
parent3914eaf3571949718bcd986ab8129b3c9f39b1d0
parent8c11ada66caa011523e5c1019f9bb23c2db89231
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #24699 from ziglang/bounded

remove RingBuffer; remove BoundedArray; use `@memmove`

17 files changed, 258 insertions(+), 807 deletions(-)

doc/langref/test_switch_dispatch_loop.zig+5-3
......@@ -8,20 +8,22 @@ const Instruction = enum {
88};
99
1010fn evaluate(initial_stack: []const i32, code: []const Instruction) !i32 {
11 var stack = try std.BoundedArray(i32, 8).fromSlice(initial_stack);
11 var buffer: [8]i32 = undefined;
12 var stack = std.ArrayListUnmanaged(i32).initBuffer(&buffer);
13 try stack.appendSliceBounded(initial_stack);
1214 var ip: usize = 0;
1315
1416 return vm: switch (code[ip]) {
1517 // Because all code after `continue` is unreachable, this branch does
1618 // not provide a result.
1719 .add => {
18 try stack.append(stack.pop().? + stack.pop().?);
20 try stack.appendBounded(stack.pop().? + stack.pop().?);
1921
2022 ip += 1;
2123 continue :vm code[ip];
2224 },
2325 .mul => {
24 try stack.append(stack.pop().? * stack.pop().?);
26 try stack.appendBounded(stack.pop().? * stack.pop().?);
2527
2628 ip += 1;
2729 continue :vm code[ip];
lib/docs/wasm/markdown/Parser.zig+40-26
......@@ -29,13 +29,14 @@ const Node = Document.Node;
2929const ExtraIndex = Document.ExtraIndex;
3030const ExtraData = Document.ExtraData;
3131const StringIndex = Document.StringIndex;
32const ArrayList = std.ArrayListUnmanaged;
3233
3334nodes: Node.List = .{},
34extra: std.ArrayListUnmanaged(u32) = .empty,
35scratch_extra: std.ArrayListUnmanaged(u32) = .empty,
36string_bytes: std.ArrayListUnmanaged(u8) = .empty,
37scratch_string: std.ArrayListUnmanaged(u8) = .empty,
38pending_blocks: std.ArrayListUnmanaged(Block) = .empty,
35extra: ArrayList(u32) = .empty,
36scratch_extra: ArrayList(u32) = .empty,
37string_bytes: ArrayList(u8) = .empty,
38scratch_string: ArrayList(u8) = .empty,
39pending_blocks: ArrayList(Block) = .empty,
3940allocator: Allocator,
4041
4142const Parser = @This();
......@@ -86,7 +87,8 @@ const Block = struct {
8687 continuation_indent: usize,
8788 },
8889 table: struct {
89 column_alignments: std.BoundedArray(Node.TableCellAlignment, max_table_columns) = .{},
90 column_alignments_buffer: [max_table_columns]Node.TableCellAlignment,
91 column_alignments_len: usize,
9092 },
9193 heading: struct {
9294 /// Between 1 and 6, inclusive.
......@@ -354,7 +356,8 @@ const BlockStart = struct {
354356 continuation_indent: usize,
355357 },
356358 table_row: struct {
357 cells: std.BoundedArray([]const u8, max_table_columns),
359 cells_buffer: [max_table_columns][]const u8,
360 cells_len: usize,
358361 },
359362 heading: struct {
360363 /// Between 1 and 6, inclusive.
......@@ -422,7 +425,8 @@ fn appendBlockStart(p: *Parser, block_start: BlockStart) !void {
422425 try p.pending_blocks.append(p.allocator, .{
423426 .tag = .table,
424427 .data = .{ .table = .{
425 .column_alignments = .{},
428 .column_alignments_buffer = undefined,
429 .column_alignments_len = 0,
426430 } },
427431 .string_start = p.scratch_string.items.len,
428432 .extra_start = p.scratch_extra.items.len,
......@@ -431,15 +435,19 @@ fn appendBlockStart(p: *Parser, block_start: BlockStart) !void {
431435
432436 const current_row = p.scratch_extra.items.len - p.pending_blocks.getLast().extra_start;
433437 if (current_row <= 1) {
434 if (parseTableHeaderDelimiter(block_start.data.table_row.cells)) |alignments| {
435 p.pending_blocks.items[p.pending_blocks.items.len - 1].data.table.column_alignments = alignments;
438 var buffer: [max_table_columns]Node.TableCellAlignment = undefined;
439 const table_row = &block_start.data.table_row;
440 if (parseTableHeaderDelimiter(table_row.cells_buffer[0..table_row.cells_len], &buffer)) |alignments| {
441 const table = &p.pending_blocks.items[p.pending_blocks.items.len - 1].data.table;
442 @memcpy(table.column_alignments_buffer[0..alignments.len], alignments);
443 table.column_alignments_len = alignments.len;
436444 if (current_row == 1) {
437445 // We need to go back and mark the header row and its column
438446 // alignments.
439447 const datas = p.nodes.items(.data);
440448 const header_data = datas[p.scratch_extra.getLast()];
441449 for (p.extraChildren(header_data.container.children), 0..) |header_cell, i| {
442 const alignment = if (i < alignments.len) alignments.buffer[i] else .unset;
450 const alignment = if (i < alignments.len) alignments[i] else .unset;
443451 const cell_data = &datas[@intFromEnum(header_cell)].table_cell;
444452 cell_data.info.alignment = alignment;
445453 cell_data.info.header = true;
......@@ -480,8 +488,10 @@ fn appendBlockStart(p: *Parser, block_start: BlockStart) !void {
480488 // available in the BlockStart. We can immediately parse and append
481489 // these children now.
482490 const containing_table = p.pending_blocks.items[p.pending_blocks.items.len - 2];
483 const column_alignments = containing_table.data.table.column_alignments.slice();
484 for (block_start.data.table_row.cells.slice(), 0..) |cell_content, i| {
491 const table = &containing_table.data.table;
492 const column_alignments = table.column_alignments_buffer[0..table.column_alignments_len];
493 const table_row = &block_start.data.table_row;
494 for (table_row.cells_buffer[0..table_row.cells_len], 0..) |cell_content, i| {
485495 const cell_children = try p.parseInlines(cell_content);
486496 const alignment = if (i < column_alignments.len) column_alignments[i] else .unset;
487497 const cell = try p.addNode(.{
......@@ -523,7 +533,8 @@ fn startBlock(p: *Parser, line: []const u8) !?BlockStart {
523533 return .{
524534 .tag = .table_row,
525535 .data = .{ .table_row = .{
526 .cells = table_row.cells,
536 .cells_buffer = table_row.cells_buffer,
537 .cells_len = table_row.cells_len,
527538 } },
528539 .rest = "",
529540 };
......@@ -606,7 +617,8 @@ fn startListItem(unindented_line: []const u8) ?ListItemStart {
606617}
607618
608619const TableRowStart = struct {
609 cells: std.BoundedArray([]const u8, max_table_columns),
620 cells_buffer: [max_table_columns][]const u8,
621 cells_len: usize,
610622};
611623
612624fn startTableRow(unindented_line: []const u8) ?TableRowStart {
......@@ -615,7 +627,8 @@ fn startTableRow(unindented_line: []const u8) ?TableRowStart {
615627 mem.endsWith(u8, unindented_line, "\\|") or
616628 !mem.endsWith(u8, unindented_line, "|")) return null;
617629
618 var cells: std.BoundedArray([]const u8, max_table_columns) = .{};
630 var cells_buffer: [max_table_columns][]const u8 = undefined;
631 var cells: ArrayList([]const u8) = .initBuffer(&cells_buffer);
619632 const table_row_content = unindented_line[1 .. unindented_line.len - 1];
620633 var cell_start: usize = 0;
621634 var i: usize = 0;
......@@ -623,7 +636,7 @@ fn startTableRow(unindented_line: []const u8) ?TableRowStart {
623636 switch (table_row_content[i]) {
624637 '\\' => i += 1,
625638 '|' => {
626 cells.append(table_row_content[cell_start..i]) catch return null;
639 cells.appendBounded(table_row_content[cell_start..i]) catch return null;
627640 cell_start = i + 1;
628641 },
629642 '`' => {
......@@ -641,20 +654,21 @@ fn startTableRow(unindented_line: []const u8) ?TableRowStart {
641654 else => {},
642655 }
643656 }
644 cells.append(table_row_content[cell_start..]) catch return null;
657 cells.appendBounded(table_row_content[cell_start..]) catch return null;
645658
646 return .{ .cells = cells };
659 return .{ .cells_buffer = cells_buffer, .cells_len = cells.items.len };
647660}
648661
649662fn parseTableHeaderDelimiter(
650 row_cells: std.BoundedArray([]const u8, max_table_columns),
651) ?std.BoundedArray(Node.TableCellAlignment, max_table_columns) {
652 var alignments: std.BoundedArray(Node.TableCellAlignment, max_table_columns) = .{};
653 for (row_cells.slice()) |content| {
663 row_cells: []const []const u8,
664 buffer: []Node.TableCellAlignment,
665) ?[]Node.TableCellAlignment {
666 var alignments: ArrayList(Node.TableCellAlignment) = .initBuffer(buffer);
667 for (row_cells) |content| {
654668 const alignment = parseTableHeaderDelimiterCell(content) orelse return null;
655669 alignments.appendAssumeCapacity(alignment);
656670 }
657 return alignments;
671 return alignments.items;
658672}
659673
660674fn parseTableHeaderDelimiterCell(content: []const u8) ?Node.TableCellAlignment {
......@@ -928,8 +942,8 @@ const InlineParser = struct {
928942 parent: *Parser,
929943 content: []const u8,
930944 pos: usize = 0,
931 pending_inlines: std.ArrayListUnmanaged(PendingInline) = .empty,
932 completed_inlines: std.ArrayListUnmanaged(CompletedInline) = .empty,
945 pending_inlines: ArrayList(PendingInline) = .empty,
946 completed_inlines: ArrayList(CompletedInline) = .empty,
933947
934948 const PendingInline = struct {
935949 tag: Tag,
lib/std/Io.zig-15
......@@ -231,21 +231,6 @@ pub fn GenericReader(
231231 return @errorCast(self.any().readBytesNoEof(num_bytes));
232232 }
233233
234 pub inline fn readIntoBoundedBytes(
235 self: Self,
236 comptime num_bytes: usize,
237 bounded: *std.BoundedArray(u8, num_bytes),
238 ) Error!void {
239 return @errorCast(self.any().readIntoBoundedBytes(num_bytes, bounded));
240 }
241
242 pub inline fn readBoundedBytes(
243 self: Self,
244 comptime num_bytes: usize,
245 ) Error!std.BoundedArray(u8, num_bytes) {
246 return @errorCast(self.any().readBoundedBytes(num_bytes));
247 }
248
249234 pub inline fn readInt(self: Self, comptime T: type, endian: std.builtin.Endian) NoEofError!T {
250235 return @errorCast(self.any().readInt(T, endian));
251236 }
lib/std/Io/DeprecatedReader.zig-27
......@@ -249,33 +249,6 @@ pub fn readBytesNoEof(self: Self, comptime num_bytes: usize) anyerror![num_bytes
249249 return bytes;
250250}
251251
252/// Reads bytes until `bounded.len` is equal to `num_bytes`,
253/// or the stream ends.
254///
255/// * it is assumed that `num_bytes` will not exceed `bounded.capacity()`
256pub fn readIntoBoundedBytes(
257 self: Self,
258 comptime num_bytes: usize,
259 bounded: *std.BoundedArray(u8, num_bytes),
260) anyerror!void {
261 while (bounded.len < num_bytes) {
262 // get at most the number of bytes free in the bounded array
263 const bytes_read = try self.read(bounded.unusedCapacitySlice());
264 if (bytes_read == 0) return;
265
266 // bytes_read will never be larger than @TypeOf(bounded.len)
267 // due to `self.read` being bounded by `bounded.unusedCapacitySlice()`
268 bounded.len += @as(@TypeOf(bounded.len), @intCast(bytes_read));
269 }
270}
271
272/// Reads at most `num_bytes` and returns as a bounded array.
273pub fn readBoundedBytes(self: Self, comptime num_bytes: usize) anyerror!std.BoundedArray(u8, num_bytes) {
274 var result = std.BoundedArray(u8, num_bytes){};
275 try self.readIntoBoundedBytes(num_bytes, &result);
276 return result;
277}
278
279252pub inline fn readInt(self: Self, comptime T: type, endian: std.builtin.Endian) anyerror!T {
280253 const bytes = try self.readBytesNoEof(@divExact(@typeInfo(T).int.bits, 8));
281254 return mem.readInt(T, &bytes, endian);
lib/std/Io/Reader/test.zig-21
......@@ -349,24 +349,3 @@ test "streamUntilDelimiter writes all bytes without delimiter to the output" {
349349
350350 try std.testing.expectError(error.StreamTooLong, reader.streamUntilDelimiter(writer, '!', 5));
351351}
352
353test "readBoundedBytes correctly reads into a new bounded array" {
354 const test_string = "abcdefg";
355 var fis = std.io.fixedBufferStream(test_string);
356 const reader = fis.reader();
357
358 var array = try reader.readBoundedBytes(10000);
359 try testing.expectEqualStrings(array.slice(), test_string);
360}
361
362test "readIntoBoundedBytes correctly reads into a provided bounded array" {
363 const test_string = "abcdefg";
364 var fis = std.io.fixedBufferStream(test_string);
365 const reader = fis.reader();
366
367 var bounded_array = std.BoundedArray(u8, 10000){};
368
369 // compile time error if the size is not the same at the provided `bounded.capacity()`
370 try reader.readIntoBoundedBytes(10000, &bounded_array);
371 try testing.expectEqualStrings(bounded_array.slice(), test_string);
372}
lib/std/Progress.zig+1-1
......@@ -1006,7 +1006,7 @@ fn serializeIpc(start_serialized_len: usize, serialized_buffer: *Serialized.Buff
10061006 continue;
10071007 }
10081008 const src = pipe_buf[m.remaining_read_trash_bytes..n];
1009 std.mem.copyForwards(u8, &pipe_buf, src);
1009 @memmove(pipe_buf[0..src.len], src);
10101010 m.remaining_read_trash_bytes = 0;
10111011 bytes_read = src.len;
10121012 continue;
lib/std/RingBuffer.zig deleted-230
......@@ -1,230 +0,0 @@
1//! This ring buffer stores read and write indices while being able to utilise
2//! the full backing slice by incrementing the indices modulo twice the slice's
3//! length and reducing indices modulo the slice's length on slice access. This
4//! means that whether the ring buffer is full or empty can be distinguished by
5//! looking at the difference between the read and write indices without adding
6//! an extra boolean flag or having to reserve a slot in the buffer.
7//!
8//! This ring buffer has not been implemented with thread safety in mind, and
9//! therefore should not be assumed to be suitable for use cases involving
10//! separate reader and writer threads.
11
12const Allocator = @import("std").mem.Allocator;
13const assert = @import("std").debug.assert;
14const copyForwards = @import("std").mem.copyForwards;
15
16const RingBuffer = @This();
17
18data: []u8,
19read_index: usize,
20write_index: usize,
21
22pub const Error = error{ Full, ReadLengthInvalid };
23
24/// Allocate a new `RingBuffer`; `deinit()` should be called to free the buffer.
25pub fn init(allocator: Allocator, capacity: usize) Allocator.Error!RingBuffer {
26 const bytes = try allocator.alloc(u8, capacity);
27 return RingBuffer{
28 .data = bytes,
29 .write_index = 0,
30 .read_index = 0,
31 };
32}
33
34/// Free the data backing a `RingBuffer`; must be passed the same `Allocator` as
35/// `init()`.
36pub fn deinit(self: *RingBuffer, allocator: Allocator) void {
37 allocator.free(self.data);
38 self.* = undefined;
39}
40
41/// Returns `index` modulo the length of the backing slice.
42pub fn mask(self: RingBuffer, index: usize) usize {
43 return index % self.data.len;
44}
45
46/// Returns `index` modulo twice the length of the backing slice.
47pub fn mask2(self: RingBuffer, index: usize) usize {
48 return index % (2 * self.data.len);
49}
50
51/// Write `byte` into the ring buffer. Returns `error.Full` if the ring
52/// buffer is full.
53pub fn write(self: *RingBuffer, byte: u8) Error!void {
54 if (self.isFull()) return error.Full;
55 self.writeAssumeCapacity(byte);
56}
57
58/// Write `byte` into the ring buffer. If the ring buffer is full, the
59/// oldest byte is overwritten.
60pub fn writeAssumeCapacity(self: *RingBuffer, byte: u8) void {
61 self.data[self.mask(self.write_index)] = byte;
62 self.write_index = self.mask2(self.write_index + 1);
63}
64
65/// Write `bytes` into the ring buffer. Returns `error.Full` if the ring
66/// buffer does not have enough space, without writing any data.
67/// Uses memcpy and so `bytes` must not overlap ring buffer data.
68pub fn writeSlice(self: *RingBuffer, bytes: []const u8) Error!void {
69 if (self.len() + bytes.len > self.data.len) return error.Full;
70 self.writeSliceAssumeCapacity(bytes);
71}
72
73/// Write `bytes` into the ring buffer. If there is not enough space, older
74/// bytes will be overwritten.
75/// Uses memcpy and so `bytes` must not overlap ring buffer data.
76pub fn writeSliceAssumeCapacity(self: *RingBuffer, bytes: []const u8) void {
77 assert(bytes.len <= self.data.len);
78 const data_start = self.mask(self.write_index);
79 const part1_data_end = @min(data_start + bytes.len, self.data.len);
80 const part1_len = part1_data_end - data_start;
81 @memcpy(self.data[data_start..part1_data_end], bytes[0..part1_len]);
82
83 const remaining = bytes.len - part1_len;
84 const to_write = @min(remaining, remaining % self.data.len + self.data.len);
85 const part2_bytes_start = bytes.len - to_write;
86 const part2_bytes_end = @min(part2_bytes_start + self.data.len, bytes.len);
87 const part2_len = part2_bytes_end - part2_bytes_start;
88 @memcpy(self.data[0..part2_len], bytes[part2_bytes_start..part2_bytes_end]);
89 if (part2_bytes_end != bytes.len) {
90 const part3_len = bytes.len - part2_bytes_end;
91 @memcpy(self.data[0..part3_len], bytes[part2_bytes_end..bytes.len]);
92 }
93 self.write_index = self.mask2(self.write_index + bytes.len);
94}
95
96/// Write `bytes` into the ring buffer. Returns `error.Full` if the ring
97/// buffer does not have enough space, without writing any data.
98/// Uses copyForwards and can write slices from this RingBuffer into itself.
99pub fn writeSliceForwards(self: *RingBuffer, bytes: []const u8) Error!void {
100 if (self.len() + bytes.len > self.data.len) return error.Full;
101 self.writeSliceForwardsAssumeCapacity(bytes);
102}
103
104/// Write `bytes` into the ring buffer. If there is not enough space, older
105/// bytes will be overwritten.
106/// Uses copyForwards and can write slices from this RingBuffer into itself.
107pub fn writeSliceForwardsAssumeCapacity(self: *RingBuffer, bytes: []const u8) void {
108 assert(bytes.len <= self.data.len);
109 const data_start = self.mask(self.write_index);
110 const part1_data_end = @min(data_start + bytes.len, self.data.len);
111 const part1_len = part1_data_end - data_start;
112 copyForwards(u8, self.data[data_start..], bytes[0..part1_len]);
113
114 const remaining = bytes.len - part1_len;
115 const to_write = @min(remaining, remaining % self.data.len + self.data.len);
116 const part2_bytes_start = bytes.len - to_write;
117 const part2_bytes_end = @min(part2_bytes_start + self.data.len, bytes.len);
118 copyForwards(u8, self.data[0..], bytes[part2_bytes_start..part2_bytes_end]);
119 if (part2_bytes_end != bytes.len)
120 copyForwards(u8, self.data[0..], bytes[part2_bytes_end..bytes.len]);
121 self.write_index = self.mask2(self.write_index + bytes.len);
122}
123
124/// Consume a byte from the ring buffer and return it. Returns `null` if the
125/// ring buffer is empty.
126pub fn read(self: *RingBuffer) ?u8 {
127 if (self.isEmpty()) return null;
128 return self.readAssumeLength();
129}
130
131/// Consume a byte from the ring buffer and return it; asserts that the buffer
132/// is not empty.
133pub fn readAssumeLength(self: *RingBuffer) u8 {
134 assert(!self.isEmpty());
135 const byte = self.data[self.mask(self.read_index)];
136 self.read_index = self.mask2(self.read_index + 1);
137 return byte;
138}
139
140/// Reads first `length` bytes written to the ring buffer into `dest`; Returns
141/// Error.ReadLengthInvalid if length greater than ring or dest length
142/// Uses memcpy and so `dest` must not overlap ring buffer data.
143pub fn readFirst(self: *RingBuffer, dest: []u8, length: usize) Error!void {
144 if (length > self.len() or length > dest.len) return error.ReadLengthInvalid;
145 self.readFirstAssumeLength(dest, length);
146}
147
148/// Reads first `length` bytes written to the ring buffer into `dest`;
149/// Asserts that length not greater than ring buffer or dest length
150/// Uses memcpy and so `dest` must not overlap ring buffer data.
151pub fn readFirstAssumeLength(self: *RingBuffer, dest: []u8, length: usize) void {
152 assert(length <= self.len() and length <= dest.len);
153 const slice = self.sliceAt(self.read_index, length);
154 slice.copyTo(dest);
155 self.read_index = self.mask2(self.read_index + length);
156}
157
158/// Reads last `length` bytes written to the ring buffer into `dest`; Returns
159/// Error.ReadLengthInvalid if length greater than ring or dest length
160/// Uses memcpy and so `dest` must not overlap ring buffer data.
161/// Reduces write index by `length`.
162pub fn readLast(self: *RingBuffer, dest: []u8, length: usize) Error!void {
163 if (length > self.len() or length > dest.len) return error.ReadLengthInvalid;
164 self.readLastAssumeLength(dest, length);
165}
166
167/// Reads last `length` bytes written to the ring buffer into `dest`;
168/// Asserts that length not greater than ring buffer or dest length
169/// Uses memcpy and so `dest` must not overlap ring buffer data.
170/// Reduces write index by `length`.
171pub fn readLastAssumeLength(self: *RingBuffer, dest: []u8, length: usize) void {
172 assert(length <= self.len() and length <= dest.len);
173 const slice = self.sliceLast(length);
174 slice.copyTo(dest);
175 self.write_index = if (self.write_index >= self.data.len)
176 self.write_index - length
177 else
178 self.mask(self.write_index + self.data.len - length);
179}
180
181/// Returns `true` if the ring buffer is empty and `false` otherwise.
182pub fn isEmpty(self: RingBuffer) bool {
183 return self.write_index == self.read_index;
184}
185
186/// Returns `true` if the ring buffer is full and `false` otherwise.
187pub fn isFull(self: RingBuffer) bool {
188 return self.mask2(self.write_index + self.data.len) == self.read_index;
189}
190
191/// Returns the length of data available for reading
192pub fn len(self: RingBuffer) usize {
193 const wrap_offset = 2 * self.data.len * @intFromBool(self.write_index < self.read_index);
194 const adjusted_write_index = self.write_index + wrap_offset;
195 return adjusted_write_index - self.read_index;
196}
197
198/// A `Slice` represents a region of a ring buffer. The region is split into two
199/// sections as the ring buffer data will not be contiguous if the desired
200/// region wraps to the start of the backing slice.
201pub const Slice = struct {
202 first: []u8,
203 second: []u8,
204
205 /// Copy data from `self` into `dest`
206 pub fn copyTo(self: Slice, dest: []u8) void {
207 @memcpy(dest[0..self.first.len], self.first);
208 @memcpy(dest[self.first.len..][0..self.second.len], self.second);
209 }
210};
211
212/// Returns a `Slice` for the region of the ring buffer starting at
213/// `self.mask(start_unmasked)` with the specified length.
214pub fn sliceAt(self: RingBuffer, start_unmasked: usize, length: usize) Slice {
215 assert(length <= self.data.len);
216 const slice1_start = self.mask(start_unmasked);
217 const slice1_end = @min(self.data.len, slice1_start + length);
218 const slice1 = self.data[slice1_start..slice1_end];
219 const slice2 = self.data[0 .. length - slice1.len];
220 return Slice{
221 .first = slice1,
222 .second = slice2,
223 };
224}
225
226/// Returns a `Slice` for the last `length` bytes written to the ring buffer.
227/// Does not check that any bytes have been written into the region.
228pub fn sliceLast(self: RingBuffer, length: usize) Slice {
229 return self.sliceAt(self.write_index + self.data.len - length, length);
230}
lib/std/array_list.zig+174-16
......@@ -158,7 +158,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty
158158 assert(self.items.len < self.capacity);
159159 self.items.len += 1;
160160
161 mem.copyBackwards(T, self.items[i + 1 .. self.items.len], self.items[i .. self.items.len - 1]);
161 @memmove(self.items[i + 1 .. self.items.len], self.items[i .. self.items.len - 1]);
162162 self.items[i] = item;
163163 }
164164
......@@ -216,7 +216,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty
216216 assert(self.capacity >= new_len);
217217 const to_move = self.items[index..];
218218 self.items.len = new_len;
219 mem.copyBackwards(T, self.items[index + count ..], to_move);
219 @memmove(self.items[index + count ..][0..to_move.len], to_move);
220220 const result = self.items[index..][0..count];
221221 @memset(result, undefined);
222222 return result;
......@@ -657,6 +657,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
657657
658658 /// Initialize with externally-managed memory. The buffer determines the
659659 /// capacity, and the length is set to zero.
660 ///
660661 /// When initialized this way, all functions that accept an Allocator
661662 /// argument cause illegal behavior.
662663 pub fn initBuffer(buffer: Slice) Self {
......@@ -738,18 +739,37 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
738739 }
739740
740741 /// Insert `item` at index `i`. Moves `list[i .. list.len]` to higher indices to make room.
741 /// If in` is equal to the length of the list this operation is equivalent to append.
742 ///
743 /// If `i` is equal to the length of the list this operation is equivalent to append.
744 ///
742745 /// This operation is O(N).
746 ///
743747 /// Asserts that the list has capacity for one additional item.
748 ///
744749 /// Asserts that the index is in bounds or equal to the length.
745750 pub fn insertAssumeCapacity(self: *Self, i: usize, item: T) void {
746751 assert(self.items.len < self.capacity);
747752 self.items.len += 1;
748753
749 mem.copyBackwards(T, self.items[i + 1 .. self.items.len], self.items[i .. self.items.len - 1]);
754 @memmove(self.items[i + 1 .. self.items.len], self.items[i .. self.items.len - 1]);
750755 self.items[i] = item;
751756 }
752757
758 /// Insert `item` at index `i`, moving `list[i .. list.len]` to higher indices to make room.
759 ///
760 /// If `i` is equal to the length of the list this operation is equivalent to append.
761 ///
762 /// This operation is O(N).
763 ///
764 /// If the list lacks unused capacity for the additional item, returns
765 /// `error.OutOfMemory`.
766 ///
767 /// Asserts that the index is in bounds or equal to the length.
768 pub fn insertBounded(self: *Self, i: usize, item: T) error{OutOfMemory}!void {
769 if (self.capacity - self.items.len == 0) return error.OutOfMemory;
770 return insertAssumeCapacity(self, i, item);
771 }
772
753773 /// Add `count` new elements at position `index`, which have
754774 /// `undefined` values. Returns a slice pointing to the newly allocated
755775 /// elements, which becomes invalid after various `ArrayList`
......@@ -782,12 +802,29 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
782802 assert(self.capacity >= new_len);
783803 const to_move = self.items[index..];
784804 self.items.len = new_len;
785 mem.copyBackwards(T, self.items[index + count ..], to_move);
805 @memmove(self.items[index + count ..][0..to_move.len], to_move);
786806 const result = self.items[index..][0..count];
787807 @memset(result, undefined);
788808 return result;
789809 }
790810
811 /// Add `count` new elements at position `index`, which have
812 /// `undefined` values, returning a slice pointing to the newly
813 /// allocated elements, which becomes invalid after various `ArrayList`
814 /// operations.
815 ///
816 /// Invalidates pre-existing pointers to elements at and after `index`, but
817 /// does not invalidate any before that.
818 ///
819 /// If the list lacks unused capacity for the additional items, returns
820 /// `error.OutOfMemory`.
821 ///
822 /// Asserts that the index is in bounds or equal to the length.
823 pub fn addManyAtBounded(self: *Self, index: usize, count: usize) error{OutOfMemory}![]T {
824 if (self.capacity - self.items.len < count) return error.OutOfMemory;
825 return addManyAtAssumeCapacity(self, index, count);
826 }
827
791828 /// Insert slice `items` at index `i` by moving `list[i .. list.len]` to make room.
792829 /// This operation is O(N).
793830 /// Invalidates pre-existing pointers to elements at and after `index`.
......@@ -831,7 +868,9 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
831868 }
832869
833870 /// Grows or shrinks the list as necessary.
871 ///
834872 /// Never invalidates element pointers.
873 ///
835874 /// Asserts the capacity is enough for additional items.
836875 pub fn replaceRangeAssumeCapacity(self: *Self, start: usize, len: usize, new_items: []const T) void {
837876 const after_range = start + len;
......@@ -848,16 +887,24 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
848887 } else {
849888 const extra = range.len - new_items.len;
850889 @memcpy(range[0..new_items.len], new_items);
851 std.mem.copyForwards(
852 T,
853 self.items[after_range - extra ..],
854 self.items[after_range..],
855 );
890 const src = self.items[after_range..];
891 @memmove(self.items[after_range - extra ..][0..src.len], src);
856892 @memset(self.items[self.items.len - extra ..], undefined);
857893 self.items.len -= extra;
858894 }
859895 }
860896
897 /// Grows or shrinks the list as necessary.
898 ///
899 /// Never invalidates element pointers.
900 ///
901 /// If the unused capacity is insufficient for additional items,
902 /// returns `error.OutOfMemory`.
903 pub fn replaceRangeBounded(self: *Self, start: usize, len: usize, new_items: []const T) error{OutOfMemory}!void {
904 if (self.capacity - self.items.len < new_items.len -| len) return error.OutOfMemory;
905 return replaceRangeAssumeCapacity(self, start, len, new_items);
906 }
907
861908 /// Extend the list by 1 element. Allocates more memory as necessary.
862909 /// Invalidates element pointers if additional memory is needed.
863910 pub fn append(self: *Self, gpa: Allocator, item: T) Allocator.Error!void {
......@@ -866,12 +913,25 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
866913 }
867914
868915 /// Extend the list by 1 element.
916 ///
869917 /// Never invalidates element pointers.
918 ///
870919 /// Asserts that the list can hold one additional item.
871920 pub fn appendAssumeCapacity(self: *Self, item: T) void {
872921 self.addOneAssumeCapacity().* = item;
873922 }
874923
924 /// Extend the list by 1 element.
925 ///
926 /// Never invalidates element pointers.
927 ///
928 /// If the list lacks unused capacity for the additional item, returns
929 /// `error.OutOfMemory`.
930 pub fn appendBounded(self: *Self, item: T) error{OutOfMemory}!void {
931 if (self.capacity - self.items.len == 0) return error.OutOfMemory;
932 return appendAssumeCapacity(self, item);
933 }
934
875935 /// Remove the element at index `i` from the list and return its value.
876936 /// Invalidates pointers to the last element.
877937 /// This operation is O(N).
......@@ -906,6 +966,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
906966 }
907967
908968 /// Append the slice of items to the list.
969 ///
909970 /// Asserts that the list can hold the additional items.
910971 pub fn appendSliceAssumeCapacity(self: *Self, items: []const T) void {
911972 const old_len = self.items.len;
......@@ -915,6 +976,14 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
915976 @memcpy(self.items[old_len..][0..items.len], items);
916977 }
917978
979 /// Append the slice of items to the list.
980 ///
981 /// If the list lacks unused capacity for the additional items, returns `error.OutOfMemory`.
982 pub fn appendSliceBounded(self: *Self, items: []const T) error{OutOfMemory}!void {
983 if (self.capacity - self.items.len < items.len) return error.OutOfMemory;
984 return appendSliceAssumeCapacity(self, items);
985 }
986
918987 /// Append the slice of items to the list. Allocates more
919988 /// memory as necessary. Only call this function if a call to `appendSlice` instead would
920989 /// be a compile error.
......@@ -925,8 +994,10 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
925994 }
926995
927996 /// Append an unaligned slice of items to the list.
928 /// Only call this function if a call to `appendSliceAssumeCapacity`
929 /// instead would be a compile error.
997 ///
998 /// Intended to be used only when `appendSliceAssumeCapacity` would be
999 /// a compile error.
1000 ///
9301001 /// Asserts that the list can hold the additional items.
9311002 pub fn appendUnalignedSliceAssumeCapacity(self: *Self, items: []align(1) const T) void {
9321003 const old_len = self.items.len;
......@@ -936,6 +1007,18 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
9361007 @memcpy(self.items[old_len..][0..items.len], items);
9371008 }
9381009
1010 /// Append an unaligned slice of items to the list.
1011 ///
1012 /// Intended to be used only when `appendSliceAssumeCapacity` would be
1013 /// a compile error.
1014 ///
1015 /// If the list lacks unused capacity for the additional items, returns
1016 /// `error.OutOfMemory`.
1017 pub fn appendUnalignedSliceBounded(self: *Self, items: []align(1) const T) error{OutOfMemory}!void {
1018 if (self.capacity - self.items.len < items.len) return error.OutOfMemory;
1019 return appendUnalignedSliceAssumeCapacity(self, items);
1020 }
1021
9391022 pub fn print(self: *Self, gpa: Allocator, comptime fmt: []const u8, args: anytype) error{OutOfMemory}!void {
9401023 comptime assert(T == u8);
9411024 try self.ensureUnusedCapacity(gpa, fmt.len);
......@@ -953,6 +1036,13 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
9531036 self.items.len += w.end;
9541037 }
9551038
1039 pub fn printBounded(self: *Self, comptime fmt: []const u8, args: anytype) error{OutOfMemory}!void {
1040 comptime assert(T == u8);
1041 var w: std.io.Writer = .fixed(self.unusedCapacitySlice());
1042 w.print(fmt, args) catch return error.OutOfMemory;
1043 self.items.len += w.end;
1044 }
1045
9561046 /// Deprecated in favor of `print` or `std.io.Writer.Allocating`.
9571047 pub const WriterContext = struct {
9581048 self: *Self,
......@@ -1007,9 +1097,12 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
10071097 }
10081098
10091099 /// Append a value to the list `n` times.
1100 ///
10101101 /// Never invalidates element pointers.
1102 ///
10111103 /// The function is inline so that a comptime-known `value` parameter will
10121104 /// have better memset codegen in case it has a repeated byte pattern.
1105 ///
10131106 /// Asserts that the list can hold the additional items.
10141107 pub inline fn appendNTimesAssumeCapacity(self: *Self, value: T, n: usize) void {
10151108 const new_len = self.items.len + n;
......@@ -1018,6 +1111,22 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
10181111 self.items.len = new_len;
10191112 }
10201113
1114 /// Append a value to the list `n` times.
1115 ///
1116 /// Never invalidates element pointers.
1117 ///
1118 /// The function is inline so that a comptime-known `value` parameter will
1119 /// have better memset codegen in case it has a repeated byte pattern.
1120 ///
1121 /// If the list lacks unused capacity for the additional items, returns
1122 /// `error.OutOfMemory`.
1123 pub inline fn appendNTimesBounded(self: *Self, value: T, n: usize) error{OutOfMemory}!void {
1124 const new_len = self.items.len + n;
1125 if (self.capacity < new_len) return error.OutOfMemory;
1126 @memset(self.items.ptr[self.items.len..new_len], value);
1127 self.items.len = new_len;
1128 }
1129
10211130 /// Adjust the list length to `new_len`.
10221131 /// Additional elements contain the value `undefined`.
10231132 /// Invalidates element pointers if additional memory is needed.
......@@ -1143,8 +1252,11 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
11431252 }
11441253
11451254 /// Increase length by 1, returning pointer to the new item.
1255 ///
11461256 /// Never invalidates element pointers.
1257 ///
11471258 /// The returned element pointer becomes invalid when the list is resized.
1259 ///
11481260 /// Asserts that the list can hold one additional item.
11491261 pub fn addOneAssumeCapacity(self: *Self) *T {
11501262 assert(self.items.len < self.capacity);
......@@ -1153,6 +1265,18 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
11531265 return &self.items[self.items.len - 1];
11541266 }
11551267
1268 /// Increase length by 1, returning pointer to the new item.
1269 ///
1270 /// Never invalidates element pointers.
1271 ///
1272 /// The returned element pointer becomes invalid when the list is resized.
1273 ///
1274 /// If the list lacks unused capacity for the additional item, returns `error.OutOfMemory`.
1275 pub fn addOneBounded(self: *Self) error{OutOfMemory}!*T {
1276 if (self.capacity - self.items.len < 1) return error.OutOfMemory;
1277 return addOneAssumeCapacity(self);
1278 }
1279
11561280 /// Resize the array, adding `n` new elements, which have `undefined` values.
11571281 /// The return value is an array pointing to the newly allocated elements.
11581282 /// The returned pointer becomes invalid when the list is resized.
......@@ -1163,9 +1287,13 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
11631287 }
11641288
11651289 /// Resize the array, adding `n` new elements, which have `undefined` values.
1290 ///
11661291 /// The return value is an array pointing to the newly allocated elements.
1292 ///
11671293 /// Never invalidates element pointers.
1294 ///
11681295 /// The returned pointer becomes invalid when the list is resized.
1296 ///
11691297 /// Asserts that the list can hold the additional items.
11701298 pub fn addManyAsArrayAssumeCapacity(self: *Self, comptime n: usize) *[n]T {
11711299 assert(self.items.len + n <= self.capacity);
......@@ -1174,6 +1302,21 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
11741302 return self.items[prev_len..][0..n];
11751303 }
11761304
1305 /// Resize the array, adding `n` new elements, which have `undefined` values.
1306 ///
1307 /// The return value is an array pointing to the newly allocated elements.
1308 ///
1309 /// Never invalidates element pointers.
1310 ///
1311 /// The returned pointer becomes invalid when the list is resized.
1312 ///
1313 /// If the list lacks unused capacity for the additional items, returns
1314 /// `error.OutOfMemory`.
1315 pub fn addManyAsArrayBounded(self: *Self, comptime n: usize) error{OutOfMemory}!*[n]T {
1316 if (self.capacity - self.items.len < n) return error.OutOfMemory;
1317 return addManyAsArrayAssumeCapacity(self, n);
1318 }
1319
11771320 /// Resize the array, adding `n` new elements, which have `undefined` values.
11781321 /// The return value is a slice pointing to the newly allocated elements.
11791322 /// The returned pointer becomes invalid when the list is resized.
......@@ -1184,10 +1327,12 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
11841327 return self.items[prev_len..][0..n];
11851328 }
11861329
1187 /// Resize the array, adding `n` new elements, which have `undefined` values.
1188 /// The return value is a slice pointing to the newly allocated elements.
1189 /// Never invalidates element pointers.
1190 /// The returned pointer becomes invalid when the list is resized.
1330 /// Resizes the array, adding `n` new elements, which have `undefined`
1331 /// values, returning a slice pointing to the newly allocated elements.
1332 ///
1333 /// Never invalidates element pointers. The returned pointer becomes
1334 /// invalid when the list is resized.
1335 ///
11911336 /// Asserts that the list can hold the additional items.
11921337 pub fn addManyAsSliceAssumeCapacity(self: *Self, n: usize) []T {
11931338 assert(self.items.len + n <= self.capacity);
......@@ -1196,6 +1341,19 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
11961341 return self.items[prev_len..][0..n];
11971342 }
11981343
1344 /// Resizes the array, adding `n` new elements, which have `undefined`
1345 /// values, returning a slice pointing to the newly allocated elements.
1346 ///
1347 /// Never invalidates element pointers. The returned pointer becomes
1348 /// invalid when the list is resized.
1349 ///
1350 /// If the list lacks unused capacity for the additional items, returns
1351 /// `error.OutOfMemory`.
1352 pub fn addManyAsSliceBounded(self: *Self, n: usize) error{OutOfMemory}![]T {
1353 if (self.capacity - self.items.len < n) return error.OutOfMemory;
1354 return addManyAsSliceAssumeCapacity(self, n);
1355 }
1356
11991357 /// Remove and return the last element from the list.
12001358 /// If the list is empty, returns `null`.
12011359 /// Invalidates pointers to last element.
lib/std/base64.zig+6-26
......@@ -118,22 +118,6 @@ pub const Base64Encoder = struct {
118118 }
119119 }
120120
121 // destWriter must be compatible with std.io.GenericWriter's writeAll interface
122 // sourceReader must be compatible with `std.io.GenericReader` read interface
123 pub fn encodeFromReaderToWriter(encoder: *const Base64Encoder, destWriter: anytype, sourceReader: anytype) !void {
124 while (true) {
125 var tempSource: [3]u8 = undefined;
126 const bytesRead = try sourceReader.read(&tempSource);
127 if (bytesRead == 0) {
128 break;
129 }
130
131 var temp: [5]u8 = undefined;
132 const s = encoder.encode(&temp, tempSource[0..bytesRead]);
133 try destWriter.writeAll(s);
134 }
135 }
136
137121 /// dest.len must at least be what you get from ::calcSize.
138122 pub fn encode(encoder: *const Base64Encoder, dest: []u8, source: []const u8) []const u8 {
139123 const out_len = encoder.calcSize(source.len);
......@@ -517,17 +501,13 @@ fn testAllApis(codecs: Codecs, expected_decoded: []const u8, expected_encoded: [
517501 var buffer: [0x100]u8 = undefined;
518502 const encoded = codecs.Encoder.encode(&buffer, expected_decoded);
519503 try testing.expectEqualSlices(u8, expected_encoded, encoded);
520
504 }
505 {
521506 // stream encode
522 var list = try std.BoundedArray(u8, 0x100).init(0);
523 try codecs.Encoder.encodeWriter(list.writer(), expected_decoded);
524 try testing.expectEqualSlices(u8, expected_encoded, list.slice());
525
526 // reader to writer encode
527 var stream = std.io.fixedBufferStream(expected_decoded);
528 list = try std.BoundedArray(u8, 0x100).init(0);
529 try codecs.Encoder.encodeFromReaderToWriter(list.writer(), stream.reader());
530 try testing.expectEqualSlices(u8, expected_encoded, list.slice());
507 var buffer: [0x100]u8 = undefined;
508 var writer: std.Io.Writer = .fixed(&buffer);
509 try codecs.Encoder.encodeWriter(&writer, expected_decoded);
510 try testing.expectEqualSlices(u8, expected_encoded, writer.buffered());
531511 }
532512
533513 // Base64Decoder
lib/std/bounded_array.zig deleted-412
......@@ -1,412 +0,0 @@
1const std = @import("std.zig");
2const assert = std.debug.assert;
3const mem = std.mem;
4const testing = std.testing;
5const Alignment = std.mem.Alignment;
6
7/// A structure with an array and a length, that can be used as a slice.
8///
9/// Useful to pass around small arrays whose exact size is only known at
10/// runtime, but whose maximum size is known at comptime, without requiring
11/// an `Allocator`.
12///
13/// ```zig
14/// var actual_size = 32;
15/// var a = try BoundedArray(u8, 64).init(actual_size);
16/// var slice = a.slice(); // a slice of the 64-byte array
17/// var a_clone = a; // creates a copy - the structure doesn't use any internal pointers
18/// ```
19pub fn BoundedArray(comptime T: type, comptime buffer_capacity: usize) type {
20 return BoundedArrayAligned(T, .of(T), buffer_capacity);
21}
22
23/// A structure with an array, length and alignment, that can be used as a
24/// slice.
25///
26/// Useful to pass around small explicitly-aligned arrays whose exact size is
27/// only known at runtime, but whose maximum size is known at comptime, without
28/// requiring an `Allocator`.
29/// ```zig
30// var a = try BoundedArrayAligned(u8, 16, 2).init(0);
31// try a.append(255);
32// try a.append(255);
33// const b = @ptrCast(*const [1]u16, a.constSlice().ptr);
34// try testing.expectEqual(@as(u16, 65535), b[0]);
35/// ```
36pub fn BoundedArrayAligned(
37 comptime T: type,
38 comptime alignment: Alignment,
39 comptime buffer_capacity: usize,
40) type {
41 return struct {
42 const Self = @This();
43 buffer: [buffer_capacity]T align(alignment.toByteUnits()) = undefined,
44 len: usize = 0,
45
46 /// Set the actual length of the slice.
47 /// Returns error.Overflow if it exceeds the length of the backing array.
48 pub fn init(len: usize) error{Overflow}!Self {
49 if (len > buffer_capacity) return error.Overflow;
50 return Self{ .len = len };
51 }
52
53 /// View the internal array as a slice whose size was previously set.
54 pub fn slice(self: anytype) switch (@TypeOf(&self.buffer)) {
55 *align(alignment.toByteUnits()) [buffer_capacity]T => []align(alignment.toByteUnits()) T,
56 *align(alignment.toByteUnits()) const [buffer_capacity]T => []align(alignment.toByteUnits()) const T,
57 else => unreachable,
58 } {
59 return self.buffer[0..self.len];
60 }
61
62 /// View the internal array as a constant slice whose size was previously set.
63 pub fn constSlice(self: *const Self) []align(alignment.toByteUnits()) const T {
64 return self.slice();
65 }
66
67 /// Adjust the slice's length to `len`.
68 /// Does not initialize added items if any.
69 pub fn resize(self: *Self, len: usize) error{Overflow}!void {
70 if (len > buffer_capacity) return error.Overflow;
71 self.len = len;
72 }
73
74 /// Remove all elements from the slice.
75 pub fn clear(self: *Self) void {
76 self.len = 0;
77 }
78
79 /// Copy the content of an existing slice.
80 pub fn fromSlice(m: []const T) error{Overflow}!Self {
81 var list = try init(m.len);
82 @memcpy(list.slice(), m);
83 return list;
84 }
85
86 /// Return the element at index `i` of the slice.
87 pub fn get(self: Self, i: usize) T {
88 return self.constSlice()[i];
89 }
90
91 /// Set the value of the element at index `i` of the slice.
92 pub fn set(self: *Self, i: usize, item: T) void {
93 self.slice()[i] = item;
94 }
95
96 /// Return the maximum length of a slice.
97 pub fn capacity(self: Self) usize {
98 return self.buffer.len;
99 }
100
101 /// Check that the slice can hold at least `additional_count` items.
102 pub fn ensureUnusedCapacity(self: Self, additional_count: usize) error{Overflow}!void {
103 if (self.len + additional_count > buffer_capacity) {
104 return error.Overflow;
105 }
106 }
107
108 /// Increase length by 1, returning a pointer to the new item.
109 pub fn addOne(self: *Self) error{Overflow}!*T {
110 try self.ensureUnusedCapacity(1);
111 return self.addOneAssumeCapacity();
112 }
113
114 /// Increase length by 1, returning pointer to the new item.
115 /// Asserts that there is space for the new item.
116 pub fn addOneAssumeCapacity(self: *Self) *T {
117 assert(self.len < buffer_capacity);
118 self.len += 1;
119 return &self.slice()[self.len - 1];
120 }
121
122 /// Resize the slice, adding `n` new elements, which have `undefined` values.
123 /// The return value is a pointer to the array of uninitialized elements.
124 pub fn addManyAsArray(self: *Self, comptime n: usize) error{Overflow}!*align(alignment.toByteUnits()) [n]T {
125 const prev_len = self.len;
126 try self.resize(self.len + n);
127 return self.slice()[prev_len..][0..n];
128 }
129
130 /// Resize the slice, adding `n` new elements, which have `undefined` values.
131 /// The return value is a slice pointing to the uninitialized elements.
132 pub fn addManyAsSlice(self: *Self, n: usize) error{Overflow}![]align(alignment.toByteUnits()) T {
133 const prev_len = self.len;
134 try self.resize(self.len + n);
135 return self.slice()[prev_len..][0..n];
136 }
137
138 /// Remove and return the last element from the slice, or return `null` if the slice is empty.
139 pub fn pop(self: *Self) ?T {
140 if (self.len == 0) return null;
141 const item = self.get(self.len - 1);
142 self.len -= 1;
143 return item;
144 }
145
146 /// Return a slice of only the extra capacity after items.
147 /// This can be useful for writing directly into it.
148 /// Note that such an operation must be followed up with a
149 /// call to `resize()`
150 pub fn unusedCapacitySlice(self: *Self) []align(alignment.toByteUnits()) T {
151 return self.buffer[self.len..];
152 }
153
154 /// Insert `item` at index `i` by moving `slice[n .. slice.len]` to make room.
155 /// This operation is O(N).
156 pub fn insert(
157 self: *Self,
158 i: usize,
159 item: T,
160 ) error{Overflow}!void {
161 if (i > self.len) {
162 return error.Overflow;
163 }
164 _ = try self.addOne();
165 var s = self.slice();
166 mem.copyBackwards(T, s[i + 1 .. s.len], s[i .. s.len - 1]);
167 self.buffer[i] = item;
168 }
169
170 /// Insert slice `items` at index `i` by moving `slice[i .. slice.len]` to make room.
171 /// This operation is O(N).
172 pub fn insertSlice(self: *Self, i: usize, items: []const T) error{Overflow}!void {
173 try self.ensureUnusedCapacity(items.len);
174 self.len += items.len;
175 mem.copyBackwards(T, self.slice()[i + items.len .. self.len], self.constSlice()[i .. self.len - items.len]);
176 @memcpy(self.slice()[i..][0..items.len], items);
177 }
178
179 /// Replace range of elements `slice[start..][0..len]` with `new_items`.
180 /// Grows slice if `len < new_items.len`.
181 /// Shrinks slice if `len > new_items.len`.
182 pub fn replaceRange(
183 self: *Self,
184 start: usize,
185 len: usize,
186 new_items: []const T,
187 ) error{Overflow}!void {
188 const after_range = start + len;
189 var range = self.slice()[start..after_range];
190
191 if (range.len == new_items.len) {
192 @memcpy(range[0..new_items.len], new_items);
193 } else if (range.len < new_items.len) {
194 const first = new_items[0..range.len];
195 const rest = new_items[range.len..];
196 @memcpy(range[0..first.len], first);
197 try self.insertSlice(after_range, rest);
198 } else {
199 @memcpy(range[0..new_items.len], new_items);
200 const after_subrange = start + new_items.len;
201 for (self.constSlice()[after_range..], 0..) |item, i| {
202 self.slice()[after_subrange..][i] = item;
203 }
204 self.len -= len - new_items.len;
205 }
206 }
207
208 /// Extend the slice by 1 element.
209 pub fn append(self: *Self, item: T) error{Overflow}!void {
210 const new_item_ptr = try self.addOne();
211 new_item_ptr.* = item;
212 }
213
214 /// Extend the slice by 1 element, asserting the capacity is already
215 /// enough to store the new item.
216 pub fn appendAssumeCapacity(self: *Self, item: T) void {
217 const new_item_ptr = self.addOneAssumeCapacity();
218 new_item_ptr.* = item;
219 }
220
221 /// Remove the element at index `i`, shift elements after index
222 /// `i` forward, and return the removed element.
223 /// Asserts the slice has at least one item.
224 /// This operation is O(N).
225 pub fn orderedRemove(self: *Self, i: usize) T {
226 const newlen = self.len - 1;
227 if (newlen == i) return self.pop().?;
228 const old_item = self.get(i);
229 for (self.slice()[i..newlen], 0..) |*b, j| b.* = self.get(i + 1 + j);
230 self.set(newlen, undefined);
231 self.len = newlen;
232 return old_item;
233 }
234
235 /// Remove the element at the specified index and return it.
236 /// The empty slot is filled from the end of the slice.
237 /// This operation is O(1).
238 pub fn swapRemove(self: *Self, i: usize) T {
239 if (self.len - 1 == i) return self.pop().?;
240 const old_item = self.get(i);
241 self.set(i, self.pop().?);
242 return old_item;
243 }
244
245 /// Append the slice of items to the slice.
246 pub fn appendSlice(self: *Self, items: []const T) error{Overflow}!void {
247 try self.ensureUnusedCapacity(items.len);
248 self.appendSliceAssumeCapacity(items);
249 }
250
251 /// Append the slice of items to the slice, asserting the capacity is already
252 /// enough to store the new items.
253 pub fn appendSliceAssumeCapacity(self: *Self, items: []const T) void {
254 const old_len = self.len;
255 self.len += items.len;
256 @memcpy(self.slice()[old_len..][0..items.len], items);
257 }
258
259 /// Append a value to the slice `n` times.
260 /// Allocates more memory as necessary.
261 pub fn appendNTimes(self: *Self, value: T, n: usize) error{Overflow}!void {
262 const old_len = self.len;
263 try self.resize(old_len + n);
264 @memset(self.slice()[old_len..self.len], value);
265 }
266
267 /// Append a value to the slice `n` times.
268 /// Asserts the capacity is enough.
269 pub fn appendNTimesAssumeCapacity(self: *Self, value: T, n: usize) void {
270 const old_len = self.len;
271 self.len += n;
272 assert(self.len <= buffer_capacity);
273 @memset(self.slice()[old_len..self.len], value);
274 }
275
276 pub const Writer = if (T != u8)
277 @compileError("The Writer interface is only defined for BoundedArray(u8, ...) " ++
278 "but the given type is BoundedArray(" ++ @typeName(T) ++ ", ...)")
279 else
280 std.io.GenericWriter(*Self, error{Overflow}, appendWrite);
281
282 /// Initializes a writer which will write into the array.
283 pub fn writer(self: *Self) Writer {
284 return .{ .context = self };
285 }
286
287 /// Same as `appendSlice` except it returns the number of bytes written, which is always the same
288 /// as `m.len`. The purpose of this function existing is to match `std.io.GenericWriter` API.
289 fn appendWrite(self: *Self, m: []const u8) error{Overflow}!usize {
290 try self.appendSlice(m);
291 return m.len;
292 }
293 };
294}
295
296test BoundedArray {
297 var a = try BoundedArray(u8, 64).init(32);
298
299 try testing.expectEqual(a.capacity(), 64);
300 try testing.expectEqual(a.slice().len, 32);
301 try testing.expectEqual(a.constSlice().len, 32);
302
303 try a.resize(48);
304 try testing.expectEqual(a.len, 48);
305
306 const x = [_]u8{1} ** 10;
307 a = try BoundedArray(u8, 64).fromSlice(&x);
308 try testing.expectEqualSlices(u8, &x, a.constSlice());
309
310 var a2 = a;
311 try testing.expectEqualSlices(u8, a.constSlice(), a2.constSlice());
312 a2.set(0, 0);
313 try testing.expect(a.get(0) != a2.get(0));
314
315 try testing.expectError(error.Overflow, a.resize(100));
316 try testing.expectError(error.Overflow, BoundedArray(u8, x.len - 1).fromSlice(&x));
317
318 try a.resize(0);
319 try a.ensureUnusedCapacity(a.capacity());
320 (try a.addOne()).* = 0;
321 try a.ensureUnusedCapacity(a.capacity() - 1);
322 try testing.expectEqual(a.len, 1);
323
324 const uninitialized = try a.addManyAsArray(4);
325 try testing.expectEqual(uninitialized.len, 4);
326 try testing.expectEqual(a.len, 5);
327
328 try a.append(0xff);
329 try testing.expectEqual(a.len, 6);
330 try testing.expectEqual(a.pop(), 0xff);
331
332 a.appendAssumeCapacity(0xff);
333 try testing.expectEqual(a.len, 6);
334 try testing.expectEqual(a.pop(), 0xff);
335
336 try a.resize(1);
337 try testing.expectEqual(a.pop(), 0);
338 try testing.expectEqual(a.pop(), null);
339 var unused = a.unusedCapacitySlice();
340 @memset(unused[0..8], 2);
341 unused[8] = 3;
342 unused[9] = 4;
343 try testing.expectEqual(unused.len, a.capacity());
344 try a.resize(10);
345
346 try a.insert(5, 0xaa);
347 try testing.expectEqual(a.len, 11);
348 try testing.expectEqual(a.get(5), 0xaa);
349 try testing.expectEqual(a.get(9), 3);
350 try testing.expectEqual(a.get(10), 4);
351
352 try a.insert(11, 0xbb);
353 try testing.expectEqual(a.len, 12);
354 try testing.expectEqual(a.pop(), 0xbb);
355
356 try a.appendSlice(&x);
357 try testing.expectEqual(a.len, 11 + x.len);
358
359 try a.appendNTimes(0xbb, 5);
360 try testing.expectEqual(a.len, 11 + x.len + 5);
361 try testing.expectEqual(a.pop(), 0xbb);
362
363 a.appendNTimesAssumeCapacity(0xcc, 5);
364 try testing.expectEqual(a.len, 11 + x.len + 5 - 1 + 5);
365 try testing.expectEqual(a.pop(), 0xcc);
366
367 try testing.expectEqual(a.len, 29);
368 try a.replaceRange(1, 20, &x);
369 try testing.expectEqual(a.len, 29 + x.len - 20);
370
371 try a.insertSlice(0, &x);
372 try testing.expectEqual(a.len, 29 + x.len - 20 + x.len);
373
374 try a.replaceRange(1, 5, &x);
375 try testing.expectEqual(a.len, 29 + x.len - 20 + x.len + x.len - 5);
376
377 try a.append(10);
378 try testing.expectEqual(a.pop(), 10);
379
380 try a.append(20);
381 const removed = a.orderedRemove(5);
382 try testing.expectEqual(removed, 1);
383 try testing.expectEqual(a.len, 34);
384
385 a.set(0, 0xdd);
386 a.set(a.len - 1, 0xee);
387 const swapped = a.swapRemove(0);
388 try testing.expectEqual(swapped, 0xdd);
389 try testing.expectEqual(a.get(0), 0xee);
390
391 const added_slice = try a.addManyAsSlice(3);
392 try testing.expectEqual(added_slice.len, 3);
393 try testing.expectEqual(a.len, 36);
394
395 while (a.pop()) |_| {}
396 const w = a.writer();
397 const s = "hello, this is a test string";
398 try w.writeAll(s);
399 try testing.expectEqualStrings(s, a.constSlice());
400}
401
402test "BoundedArrayAligned" {
403 var a = try BoundedArrayAligned(u8, .@"16", 4).init(0);
404 try a.append(0);
405 try a.append(0);
406 try a.append(255);
407 try a.append(255);
408
409 const b = @as(*const [2]u16, @ptrCast(a.constSlice().ptr));
410 try testing.expectEqual(@as(u16, 0), b[0]);
411 try testing.expectEqual(@as(u16, 65535), b[1]);
412}
lib/std/math/big/int.zig+3-5
......@@ -1710,7 +1710,7 @@ pub const Mutable = struct {
17101710
17111711 if (xy_trailing != 0 and r.limbs[r.len - 1] != 0) {
17121712 // Manually shift here since we know its limb aligned.
1713 mem.copyBackwards(Limb, r.limbs[xy_trailing..], r.limbs[0..r.len]);
1713 @memmove(r.limbs[xy_trailing..][0..r.len], r.limbs[0..r.len]);
17141714 @memset(r.limbs[0..xy_trailing], 0);
17151715 r.len += xy_trailing;
17161716 }
......@@ -3836,8 +3836,7 @@ fn llshl(r: []Limb, a: []const Limb, shift: usize) usize {
38363836 std.debug.assert(@intFromPtr(r.ptr) >= @intFromPtr(a.ptr));
38373837
38383838 if (shift == 0) {
3839 if (a.ptr != r.ptr)
3840 std.mem.copyBackwards(Limb, r[0..a.len], a);
3839 if (a.ptr != r.ptr) @memmove(r[0..a.len], a);
38413840 return a.len;
38423841 }
38433842 if (shift >= limb_bits) {
......@@ -3891,8 +3890,7 @@ fn llshr(r: []Limb, a: []const Limb, shift: usize) usize {
38913890 if (shift == 0) {
38923891 std.debug.assert(r.len >= a.len);
38933892
3894 if (a.ptr != r.ptr)
3895 std.mem.copyForwards(Limb, r[0..a.len], a);
3893 if (a.ptr != r.ptr) @memmove(r[0..a.len], a);
38963894 return a.len;
38973895 }
38983896 if (shift >= limb_bits) {
lib/std/os/windows.zig+3-8
......@@ -1332,7 +1332,7 @@ pub fn GetFinalPathNameByHandle(
13321332 // dropping the \Device\Mup\ and making sure the path begins with \\
13331333 if (mem.eql(u16, device_name_u16, std.unicode.utf8ToUtf16LeStringLiteral("Mup"))) {
13341334 out_buffer[0] = '\\';
1335 mem.copyForwards(u16, out_buffer[1..][0..file_name_u16.len], file_name_u16);
1335 @memmove(out_buffer[1..][0..file_name_u16.len], file_name_u16);
13361336 return out_buffer[0 .. 1 + file_name_u16.len];
13371337 }
13381338
......@@ -1400,7 +1400,7 @@ pub fn GetFinalPathNameByHandle(
14001400 if (out_buffer.len < drive_letter.len + file_name_u16.len) return error.NameTooLong;
14011401
14021402 @memcpy(out_buffer[0..drive_letter.len], drive_letter);
1403 mem.copyForwards(u16, out_buffer[drive_letter.len..][0..file_name_u16.len], file_name_u16);
1403 @memmove(out_buffer[drive_letter.len..][0..file_name_u16.len], file_name_u16);
14041404 const total_len = drive_letter.len + file_name_u16.len;
14051405
14061406 // Validate that DOS does not contain any spurious nul bytes.
......@@ -1449,12 +1449,7 @@ pub fn GetFinalPathNameByHandle(
14491449 // to copy backwards. We also need to do this before copying the volume path because
14501450 // it could overwrite the file_name_u16 memory.
14511451 const file_name_dest = out_buffer[volume_path.len..][0..file_name_u16.len];
1452 const file_name_byte_offset = @intFromPtr(file_name_u16.ptr) - @intFromPtr(out_buffer.ptr);
1453 const file_name_index = file_name_byte_offset / @sizeOf(u16);
1454 if (volume_path.len > file_name_index)
1455 mem.copyBackwards(u16, file_name_dest, file_name_u16)
1456 else
1457 mem.copyForwards(u16, file_name_dest, file_name_u16);
1452 @memmove(file_name_dest, file_name_u16);
14581453 @memcpy(out_buffer[0..volume_path.len], volume_path);
14591454 const total_len = volume_path.len + file_name_u16.len;
14601455
lib/std/std.zig-3
......@@ -9,8 +9,6 @@ pub const AutoArrayHashMapUnmanaged = array_hash_map.AutoArrayHashMapUnmanaged;
99pub const AutoHashMap = hash_map.AutoHashMap;
1010pub const AutoHashMapUnmanaged = hash_map.AutoHashMapUnmanaged;
1111pub const BitStack = @import("BitStack.zig");
12pub const BoundedArray = @import("bounded_array.zig").BoundedArray;
13pub const BoundedArrayAligned = @import("bounded_array.zig").BoundedArrayAligned;
1412pub const Build = @import("Build.zig");
1513pub const BufMap = @import("buf_map.zig").BufMap;
1614pub const BufSet = @import("buf_set.zig").BufSet;
......@@ -31,7 +29,6 @@ pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue;
3129pub const PriorityDequeue = @import("priority_dequeue.zig").PriorityDequeue;
3230pub const Progress = @import("Progress.zig");
3331pub const Random = @import("Random.zig");
34pub const RingBuffer = @import("RingBuffer.zig");
3532pub const SegmentedList = @import("segmented_list.zig").SegmentedList;
3633pub const SemanticVersion = @import("SemanticVersion.zig");
3734pub const SinglyLinkedList = @import("SinglyLinkedList.zig");
src/Compilation.zig+12-6
......@@ -2103,6 +2103,8 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
21032103 .local_zir_cache = local_zir_cache,
21042104 .error_limit = error_limit,
21052105 .llvm_object = null,
2106 .analysis_roots_buffer = undefined,
2107 .analysis_roots_len = 0,
21062108 };
21072109 try zcu.init(options.thread_pool.getIdCount());
21082110 break :blk zcu;
......@@ -2933,22 +2935,26 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
29332935 try comp.appendFileSystemInput(embed_file.path);
29342936 }
29352937
2936 zcu.analysis_roots.clear();
2938 zcu.analysis_roots_len = 0;
29372939
2938 zcu.analysis_roots.appendAssumeCapacity(zcu.std_mod);
2940 zcu.analysis_roots_buffer[zcu.analysis_roots_len] = zcu.std_mod;
2941 zcu.analysis_roots_len += 1;
29392942
29402943 // Normally we rely on importing std to in turn import the root source file in the start code.
29412944 // However, the main module is distinct from the root module in tests, so that won't happen there.
29422945 if (comp.config.is_test and zcu.main_mod != zcu.std_mod) {
2943 zcu.analysis_roots.appendAssumeCapacity(zcu.main_mod);
2946 zcu.analysis_roots_buffer[zcu.analysis_roots_len] = zcu.main_mod;
2947 zcu.analysis_roots_len += 1;
29442948 }
29452949
29462950 if (zcu.root_mod.deps.get("compiler_rt")) |compiler_rt_mod| {
2947 zcu.analysis_roots.appendAssumeCapacity(compiler_rt_mod);
2951 zcu.analysis_roots_buffer[zcu.analysis_roots_len] = compiler_rt_mod;
2952 zcu.analysis_roots_len += 1;
29482953 }
29492954
29502955 if (zcu.root_mod.deps.get("ubsan_rt")) |ubsan_rt_mod| {
2951 zcu.analysis_roots.appendAssumeCapacity(ubsan_rt_mod);
2956 zcu.analysis_roots_buffer[zcu.analysis_roots_len] = ubsan_rt_mod;
2957 zcu.analysis_roots_len += 1;
29522958 }
29532959 }
29542960
......@@ -4745,7 +4751,7 @@ fn performAllTheWork(
47454751 try zcu.flushRetryableFailures();
47464752
47474753 // It's analysis time! Queue up our initial analysis.
4748 for (zcu.analysis_roots.slice()) |mod| {
4754 for (zcu.analysisRoots()) |mod| {
47494755 try comp.queueJob(.{ .analyze_mod = mod });
47504756 }
47514757
src/Sema.zig+3-3
......@@ -2631,7 +2631,7 @@ fn reparentOwnedErrorMsg(
26312631
26322632 const orig_notes = msg.notes.len;
26332633 msg.notes = try sema.gpa.realloc(msg.notes, orig_notes + 1);
2634 std.mem.copyBackwards(Zcu.ErrorMsg, msg.notes[1..], msg.notes[0..orig_notes]);
2634 @memmove(msg.notes[1..][0..orig_notes], msg.notes[0..orig_notes]);
26352635 msg.notes[0] = .{
26362636 .src_loc = msg.src_loc,
26372637 .msg = msg.msg,
......@@ -14464,8 +14464,8 @@ fn analyzeTupleMul(
1446414464 }
1446514465 }
1446614466 for (0..factor) |i| {
14467 mem.copyForwards(InternPool.Index, types[tuple_len * i ..], types[0..tuple_len]);
14468 mem.copyForwards(InternPool.Index, values[tuple_len * i ..], values[0..tuple_len]);
14467 @memmove(types[tuple_len * i ..][0..tuple_len], types[0..tuple_len]);
14468 @memmove(values[tuple_len * i ..][0..tuple_len], values[0..tuple_len]);
1446914469 }
1447014470 break :rs runtime_src;
1447114471 };
src/Zcu.zig+8-3
......@@ -268,7 +268,8 @@ nav_val_analysis_queued: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, voi
268268
269269/// These are the modules which we initially queue for analysis in `Compilation.update`.
270270/// `resolveReferences` will use these as the root of its reachability traversal.
271analysis_roots: std.BoundedArray(*Package.Module, 4) = .{},
271analysis_roots_buffer: [4]*Package.Module,
272analysis_roots_len: usize = 0,
272273/// This is the cached result of `Zcu.resolveReferences`. It is computed on-demand, and
273274/// reset to `null` when any semantic analysis occurs (since this invalidates the data).
274275/// Allocated into `gpa`.
......@@ -4013,8 +4014,8 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
40134014 // This is not a sufficient size, but a lower bound.
40144015 try result.ensureTotalCapacity(gpa, @intCast(zcu.reference_table.count()));
40154016
4016 try type_queue.ensureTotalCapacity(gpa, zcu.analysis_roots.len);
4017 for (zcu.analysis_roots.slice()) |mod| {
4017 try type_queue.ensureTotalCapacity(gpa, zcu.analysis_roots_len);
4018 for (zcu.analysisRoots()) |mod| {
40184019 const file = zcu.module_roots.get(mod).?.unwrap() orelse continue;
40194020 const root_ty = zcu.fileRootType(file);
40204021 if (root_ty == .none) continue;
......@@ -4202,6 +4203,10 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
42024203 return result;
42034204}
42044205
4206pub fn analysisRoots(zcu: *Zcu) []*Package.Module {
4207 return zcu.analysis_roots_buffer[0..zcu.analysis_roots_len];
4208}
4209
42054210pub fn fileByIndex(zcu: *const Zcu, file_index: File.Index) *File {
42064211 return zcu.intern_pool.filePtr(file_index);
42074212}
src/Zcu/PerThread.zig+3-2
......@@ -2116,8 +2116,9 @@ pub fn computeAliveFiles(pt: Zcu.PerThread) Allocator.Error!bool {
21162116 // multi-threaded environment (where things like file indices could differ between compiler runs).
21172117
21182118 // The roots of our file liveness analysis will be the analysis roots.
2119 try zcu.alive_files.ensureTotalCapacity(gpa, zcu.analysis_roots.len);
2120 for (zcu.analysis_roots.slice()) |mod| {
2119 const analysis_roots = zcu.analysisRoots();
2120 try zcu.alive_files.ensureTotalCapacity(gpa, analysis_roots.len);
2121 for (analysis_roots) |mod| {
21212122 const file_index = zcu.module_roots.get(mod).?.unwrap() orelse continue;
21222123 const file = zcu.fileByIndex(file_index);
21232124