From f6549a956d640c1de2dc99340582950f9dc18c60 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 21 Nov 2023 18:30:42 -0700 Subject: [PATCH 1/8] std.ArrayList: add initBuffer to the unmanaged array list This is useful when you want to have an array list backed by a fixed slice of memory and no Allocator will be used. It's an alternative to BoundedArray as you will see in the following commit. --- lib/std/array_list.zig | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/std/array_list.zig b/lib/std/array_list.zig index 24655046a9..570c65ae99 100644 --- a/lib/std/array_list.zig +++ b/lib/std/array_list.zig @@ -633,6 +633,17 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ return self; } + /// Initialize with externally-managed memory. The buffer determines the + /// capacity, and the length is set to zero. + /// When initialized this way, all methods that accept an Allocator + /// argument are illegal to call. + pub fn initBuffer(buffer: Slice) Self { + return .{ + .items = buffer[0..0], + .capacity = buffer.len, + }; + } + /// Release all allocated memory. pub fn deinit(self: *Self, allocator: Allocator) void { allocator.free(self.allocatedSlice()); From 7b3556a8cff1edb63a331c5068902254b0b2126c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 21 Nov 2023 18:32:01 -0700 Subject: [PATCH 2/8] std.fs: make deleteTree use ArrayList instead of BoundedArray We definitely want ArrayList in the standard library. Do we want BoundedArray? Maybe, maybe not. But that makes ArrayList a more stable dependency for std.fs. --- lib/std/fs.zig | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 8ae98b54f6..2bcf4ee309 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -2204,28 +2204,29 @@ pub const Dir = struct { name: []const u8, parent_dir: Dir, iter: IterableDir.Iterator, + + fn closeAll(items: []@This()) void { + for (items) |*item| item.iter.dir.close(); + } }; - var stack = std.BoundedArray(StackItem, 16){}; - defer { - for (stack.slice()) |*item| { - item.iter.dir.close(); - } - } + var stack_buffer: [16]StackItem = undefined; + var stack = std.ArrayListUnmanaged(StackItem).initBuffer(&stack_buffer); + defer StackItem.closeAll(stack.items); - stack.appendAssumeCapacity(StackItem{ + stack.appendAssumeCapacity(.{ .name = sub_path, .parent_dir = self, .iter = initial_iterable_dir.iterateAssumeFirstIteration(), }); - process_stack: while (stack.len != 0) { - var top = &(stack.slice()[stack.len - 1]); + process_stack: while (stack.items.len != 0) { + var top = &stack.items[stack.items.len - 1]; while (try top.iter.next()) |entry| { var treat_as_dir = entry.kind == .directory; handle_entry: while (true) { if (treat_as_dir) { - if (stack.ensureUnusedCapacity(1)) { + if (stack.unusedCapacitySlice().len >= 1) { var iterable_dir = top.iter.dir.openIterableDir(entry.name, .{ .no_follow = true }) catch |err| switch (err) { error.NotDir => { treat_as_dir = false; @@ -2251,13 +2252,13 @@ pub const Dir = struct { error.DeviceBusy, => |e| return e, }; - stack.appendAssumeCapacity(StackItem{ + stack.appendAssumeCapacity(.{ .name = entry.name, .parent_dir = top.iter.dir, .iter = iterable_dir.iterateAssumeFirstIteration(), }); continue :process_stack; - } else |_| { + } else { try top.iter.dir.deleteTreeMinStackSizeWithKindHint(entry.name, entry.kind); break :handle_entry; } @@ -2301,7 +2302,7 @@ pub const Dir = struct { // pop the value from the stack. const parent_dir = top.parent_dir; const name = top.name; - _ = stack.pop(); + stack.items.len -= 1; var need_to_retry: bool = false; parent_dir.deleteDir(name) catch |err| switch (err) { @@ -2374,7 +2375,7 @@ pub const Dir = struct { }; // We know there is room on the stack since we are just re-adding // the StackItem that we previously popped. - stack.appendAssumeCapacity(StackItem{ + stack.appendAssumeCapacity(.{ .name = name, .parent_dir = parent_dir, .iter = iterable_dir.iterateAssumeFirstIteration(), From 49d6dd3ecb0b5d0547f8a70b764e38af2f24f475 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 21 Nov 2023 20:21:57 -0700 Subject: [PATCH 3/8] std.crypto.ff: simplify implementation * Take advantage of multi-object for loops. * Remove use of BoundedArray since it had no meaningful impact on safety or readability. * Simplify some complex expressions, such as using `!` to invert a boolean value. --- lib/std/crypto/ff.zig | 177 ++++++++++++++++++++---------------------- 1 file changed, 86 insertions(+), 91 deletions(-) diff --git a/lib/std/crypto/ff.zig b/lib/std/crypto/ff.zig index 932575e936..90b1b9f441 100644 --- a/lib/std/crypto/ff.zig +++ b/lib/std/crypto/ff.zig @@ -12,7 +12,6 @@ const math = std.math; const mem = std.mem; const meta = std.meta; const testing = std.testing; -const BoundedArray = std.BoundedArray; const assert = std.debug.assert; const Endian = std.builtin.Endian; @@ -63,46 +62,54 @@ pub fn Uint(comptime max_bits: comptime_int) type { return struct { const Self = @This(); - const max_limbs_count = math.divCeil(usize, max_bits, t_bits) catch unreachable; - const Limbs = BoundedArray(Limb, max_limbs_count); - limbs: Limbs, + + limbs_buffer: [max_limbs_count]Limb, + /// The number of active limbs. + limbs_len: usize, /// Number of bytes required to serialize an integer. pub const encoded_bytes = math.divCeil(usize, max_bits, 8) catch unreachable; - // Returns the number of active limbs. - fn limbs_count(self: Self) usize { - return self.limbs.len; + /// Constant slice of active limbs. + fn limbsConst(self: *const Self) []const Limb { + return self.limbs_buffer[0..self.limbs_len]; + } + + /// Mutable slice of active limbs. + fn limbs(self: *Self) []Limb { + return self.limbs_buffer[0..self.limbs_len]; } // Removes limbs whose value is zero from the active limbs. fn normalize(self: Self) Self { var res = self; - if (self.limbs_count() < 2) { + if (self.limbs_len < 2) { return res; } - var i = self.limbs_count() - 1; - while (i > 0 and res.limbs.get(i) == 0) : (i -= 1) {} - res.limbs.resize(i + 1) catch unreachable; + var i = self.limbs_len - 1; + while (i > 0 and res.limbsConst()[i] == 0) : (i -= 1) {} + res.limbs_len = i + 1; + assert(res.limbs_len <= res.limbs_buffer.len); return res; } /// The zero integer. - pub const zero = zero: { - var limbs = Limbs.init(0) catch unreachable; - limbs.appendNTimesAssumeCapacity(0, max_limbs_count); - break :zero Self{ .limbs = limbs }; + pub const zero: Self = .{ + .limbs_buffer = [1]Limb{0} ** max_limbs_count, + .limbs_len = max_limbs_count, }; /// Creates a new big integer from a primitive type. /// This function may not run in constant time. - pub fn fromPrimitive(comptime T: type, x_: T) OverflowError!Self { - var x = x_; - var out = Self.zero; - for (0..out.limbs.capacity()) |i| { - const t = if (@bitSizeOf(T) > t_bits) @as(TLimb, @truncate(x)) else x; - out.limbs.set(i, t); + pub fn fromPrimitive(comptime T: type, init_value: T) OverflowError!Self { + var x = init_value; + var out: Self = .{ + .limbs_buffer = undefined, + .limbs_len = max_limbs_count, + }; + for (&out.limbs_buffer) |*limb| { + limb.* = if (@bitSizeOf(T) > t_bits) @as(TLimb, @truncate(x)) else x; x = math.shr(T, x, t_bits); } if (x != 0) { @@ -115,13 +122,13 @@ pub fn Uint(comptime max_bits: comptime_int) type { /// This function may not run in constant time. pub fn toPrimitive(self: Self, comptime T: type) OverflowError!T { var x: T = 0; - var i = self.limbs_count() - 1; + var i = self.limbs_len - 1; while (true) : (i -= 1) { if (@bitSizeOf(T) >= t_bits and math.shr(T, x, @bitSizeOf(T) - t_bits) != 0) { return error.Overflow; } x = math.shl(T, x, t_bits); - const v = math.cast(T, self.limbs.get(i)) orelse return error.Overflow; + const v = math.cast(T, self.limbsConst()[i]) orelse return error.Overflow; x |= v; if (i == 0) break; } @@ -140,9 +147,9 @@ pub fn Uint(comptime max_bits: comptime_int) type { .big => bytes.len - 1, .little => 0, }; - for (0..self.limbs.len) |i| { + for (0..self.limbs_len) |i| { var remaining_bits = t_bits; - var limb = self.limbs.get(i); + var limb = self.limbsConst()[i]; while (remaining_bits >= 8) { bytes[out_i] |= math.shl(u8, @as(u8, @truncate(limb)), shift); const consumed = 8 - shift; @@ -152,7 +159,7 @@ pub fn Uint(comptime max_bits: comptime_int) type { switch (endian) { .big => { if (out_i == 0) { - if (i != self.limbs.len - 1 or limb != 0) { + if (i != self.limbs_len - 1 or limb != 0) { return error.Overflow; } return; @@ -162,7 +169,7 @@ pub fn Uint(comptime max_bits: comptime_int) type { .little => { out_i += 1; if (out_i == bytes.len) { - if (i != self.limbs.len - 1 or limb != 0) { + if (i != self.limbs_len - 1 or limb != 0) { return error.Overflow; } return; @@ -187,20 +194,20 @@ pub fn Uint(comptime max_bits: comptime_int) type { }; while (true) { const bi = bytes[i]; - out.limbs.set(out_i, out.limbs.get(out_i) | math.shl(Limb, bi, shift)); + out.limbs()[out_i] |= math.shl(Limb, bi, shift); shift += 8; if (shift >= t_bits) { shift -= t_bits; - out.limbs.set(out_i, @as(TLimb, @truncate(out.limbs.get(out_i)))); + out.limbs()[out_i] = @as(TLimb, @truncate(out.limbs()[out_i])); const overflow = math.shr(Limb, bi, 8 - shift); out_i += 1; - if (out_i >= out.limbs.len) { + if (out_i >= out.limbs_len) { if (overflow != 0 or i != 0) { return error.Overflow; } break; } - out.limbs.set(out_i, overflow); + out.limbs()[out_i] = overflow; } switch (endian) { .big => { @@ -218,32 +225,31 @@ pub fn Uint(comptime max_bits: comptime_int) type { /// Returns `true` if both integers are equal. pub fn eql(x: Self, y: Self) bool { - return crypto.utils.timingSafeEql([max_limbs_count]Limb, x.limbs.buffer, y.limbs.buffer); + return crypto.utils.timingSafeEql([max_limbs_count]Limb, x.limbs_buffer, y.limbs_buffer); } /// Compares two integers. pub fn compare(x: Self, y: Self) math.Order { return crypto.utils.timingSafeCompare( Limb, - x.limbs.constSlice(), - y.limbs.constSlice(), + x.limbsConst(), + y.limbsConst(), .little, ); } /// Returns `true` if the integer is zero. pub fn isZero(x: Self) bool { - const x_limbs = x.limbs.constSlice(); var t: Limb = 0; - for (0..x.limbs_count()) |i| { - t |= x_limbs[i]; + for (x.limbsConst()) |elem| { + t |= elem; } return ct.eql(t, 0); } /// Returns `true` if the integer is odd. pub fn isOdd(x: Self) bool { - return @as(bool, @bitCast(@as(u1, @truncate(x.limbs.get(0))))); + return @as(u1, @truncate(x.limbsConst()[0])) != 0; } /// Adds `y` to `x`, and returns `true` if the operation overflowed. @@ -258,39 +264,31 @@ pub fn Uint(comptime max_bits: comptime_int) type { // Replaces the limbs of `x` with the limbs of `y` if `on` is `true`. fn cmov(x: *Self, on: bool, y: Self) void { - const x_limbs = x.limbs.slice(); - const y_limbs = y.limbs.constSlice(); - for (0..y.limbs_count()) |i| { - x_limbs[i] = ct.select(on, y_limbs[i], x_limbs[i]); + for (x.limbs(), y.limbsConst()) |*x_limb, y_limb| { + x_limb.* = ct.select(on, y_limb, x_limb.*); } } - // Adds `y` to `x` if `on` is `true`, and returns `true` if the operation overflowed. + // Adds `y` to `x` if `on` is `true`, and returns `true` if the + // operation overflowed. fn conditionalAddWithOverflow(x: *Self, on: bool, y: Self) u1 { - assert(x.limbs_count() == y.limbs_count()); // Operands must have the same size. - const x_limbs = x.limbs.slice(); - const y_limbs = y.limbs.constSlice(); - var carry: u1 = 0; - for (0..x.limbs_count()) |i| { - const res = x_limbs[i] + y_limbs[i] + carry; - x_limbs[i] = ct.select(on, @as(TLimb, @truncate(res)), x_limbs[i]); - carry = @as(u1, @truncate(res >> t_bits)); + for (x.limbs(), y.limbsConst()) |*x_limb, y_limb| { + const res = x_limb.* + y_limb + carry; + x_limb.* = ct.select(on, @as(TLimb, @truncate(res)), x_limb.*); + carry = @truncate(res >> t_bits); } return carry; } - // Subtracts `y` from `x` if `on` is `true`, and returns `true` if the operation overflowed. + // Subtracts `y` from `x` if `on` is `true`, and returns `true` if the + // operation overflowed. fn conditionalSubWithOverflow(x: *Self, on: bool, y: Self) u1 { - assert(x.limbs_count() == y.limbs_count()); // Operands must have the same size. - const x_limbs = x.limbs.slice(); - const y_limbs = y.limbs.constSlice(); - var borrow: u1 = 0; - for (0..x.limbs_count()) |i| { - const res = x_limbs[i] -% y_limbs[i] -% borrow; - x_limbs[i] = ct.select(on, @as(TLimb, @truncate(res)), x_limbs[i]); - borrow = @as(u1, @truncate(res >> t_bits)); + for (x.limbs(), y.limbsConst()) |*x_limb, y_limb| { + const res = x_limb.* -% y_limb -% borrow; + x_limb.* = ct.select(on, @as(TLimb, @truncate(res)), x_limb.*); + borrow = @truncate(res >> t_bits); } return borrow; } @@ -315,7 +313,7 @@ fn Fe_(comptime bits: comptime_int) type { // The number of active limbs to represent the field element. fn limbs_count(self: Self) usize { - return self.v.limbs_count(); + return self.v.limbs_len; } /// Creates a field element from a primitive. @@ -398,7 +396,7 @@ pub fn Modulus(comptime max_bits: comptime_int) type { // Number of active limbs in the modulus. fn limbs_count(self: Self) usize { - return self.v.limbs_count(); + return self.v.limbs_len; } /// Actual size of the modulus, in bits. @@ -409,7 +407,7 @@ pub fn Modulus(comptime max_bits: comptime_int) type { /// Returns the element `1`. pub fn one(self: Self) Fe { var fe = self.zero; - fe.v.limbs.set(0, 1); + fe.v.limbs()[0] = 1; return fe; } @@ -419,10 +417,10 @@ pub fn Modulus(comptime max_bits: comptime_int) type { if (!v_.isOdd()) return error.EvenModulus; var v = v_.normalize(); - const hi = v.limbs.get(v.limbs_count() - 1); - const lo = v.limbs.get(0); + const hi = v.limbsConst()[v.limbs_len - 1]; + const lo = v.limbsConst()[0]; - if (v.limbs_count() < 2 and lo < 3) { + if (v.limbs_len < 2 and lo < 3) { return error.ModulusTooSmall; } @@ -481,18 +479,19 @@ pub fn Modulus(comptime max_bits: comptime_int) type { const new_len = self.limbs_count(); if (fe.limbs_count() < new_len) return error.Overflow; var acc: Limb = 0; - for (fe.v.limbs.constSlice()[new_len..]) |limb| { + for (fe.v.limbsConst()[new_len..]) |limb| { acc |= limb; } if (acc != 0) return error.Overflow; - try fe.v.limbs.resize(new_len); + if (new_len > fe.v.limbs_buffer.len) return error.Overflow; + fe.v.limbs_len = new_len; } // Computes R^2 for the Montgomery representation. fn computeRR(self: *Self) void { self.rr = self.zero; const n = self.rr.limbs_count(); - self.rr.v.limbs.set(n - 1, 1); + self.rr.v.limbs()[n - 1] = 1; for ((n - 1)..(2 * n)) |_| { self.shiftIn(&self.rr, 0); } @@ -502,9 +501,9 @@ pub fn Modulus(comptime max_bits: comptime_int) type { /// Computes x << t_bits + y (mod m) fn shiftIn(self: Self, x: *Fe, y: Limb) void { var d = self.zero; - const x_limbs = x.v.limbs.slice(); - const d_limbs = d.v.limbs.slice(); - const m_limbs = self.v.limbs.constSlice(); + const x_limbs = x.v.limbs(); + const d_limbs = d.v.limbs(); + const m_limbs = self.v.limbsConst(); var need_sub = false; var i: usize = t_bits - 1; @@ -569,18 +568,18 @@ pub fn Modulus(comptime max_bits: comptime_int) type { /// Reduces an arbitrary `Uint`, converting it to a field element. pub fn reduce(self: Self, x: anytype) Fe { var out = self.zero; - var i = x.limbs_count() - 1; + var i = x.limbs_len - 1; if (self.limbs_count() >= 2) { const start = @min(i, self.limbs_count() - 2); var j = start; while (true) : (j -= 1) { - out.v.limbs.set(j, x.limbs.get(i)); + out.v.limbs()[j] = x.limbsConst()[i]; i -= 1; if (j == 0) break; } } while (true) : (i -= 1) { - self.shiftIn(&out, x.limbs.get(i)); + self.shiftIn(&out, x.limbsConst()[i]); if (i == 0) break; } return out; @@ -591,10 +590,10 @@ pub fn Modulus(comptime max_bits: comptime_int) type { assert(d.limbs_count() == y.limbs_count()); assert(d.limbs_count() == self.limbs_count()); - const a_limbs = x.v.limbs.constSlice(); - const b_limbs = y.v.limbs.constSlice(); - const d_limbs = d.v.limbs.slice(); - const m_limbs = self.v.limbs.constSlice(); + const a_limbs = x.v.limbsConst(); + const b_limbs = y.v.limbsConst(); + const d_limbs = d.v.limbs(); + const m_limbs = self.v.limbsConst(); var overflow: u1 = 0; for (0..self.limbs_count()) |i| { @@ -685,7 +684,7 @@ pub fn Modulus(comptime max_bits: comptime_int) type { const k: u1 = @truncate(b >> j); if (k != 0) { const t = self.montgomeryMul(out, x_m); - @memcpy(out.v.limbs.slice(), t.v.limbs.constSlice()); + @memcpy(out.v.limbs(), t.v.limbsConst()); } if (j == 0) break; } @@ -731,7 +730,7 @@ pub fn Modulus(comptime max_bits: comptime_int) type { } const t1 = self.montgomeryMul(out, t0); if (public) { - @memcpy(out.v.limbs.slice(), t1.v.limbs.constSlice()); + @memcpy(out.v.limbs(), t1.v.limbsConst()); } else { out.v.cmov(!ct.eql(k, 0), t1.v); } @@ -790,9 +789,9 @@ pub fn Modulus(comptime max_bits: comptime_int) type { pub fn powPublic(self: Self, x: Fe, e: Fe) NullExponentError!Fe { var e_normalized = Fe{ .v = e.v.normalize() }; var buf_: [Fe.encoded_bytes]u8 = undefined; - var buf = buf_[0 .. math.divCeil(usize, e_normalized.v.limbs_count() * t_bits, 8) catch unreachable]; + var buf = buf_[0 .. math.divCeil(usize, e_normalized.v.limbs_len * t_bits, 8) catch unreachable]; e_normalized.toBytes(buf, .little) catch unreachable; - const leading = @clz(e_normalized.v.limbs.get(e_normalized.v.limbs_count() - carry_bits)); + const leading = @clz(e_normalized.v.limbsConst()[e_normalized.v.limbs_len - carry_bits]); buf = buf[0 .. buf.len - leading / 8]; return self.powWithEncodedPublicExponent(x, buf, .little); } @@ -835,20 +834,16 @@ const ct_protected = struct { // Compares two big integers in constant time, returning true if x < y. fn limbsCmpLt(x: anytype, y: @TypeOf(x)) bool { - assert(x.limbs_count() == y.limbs_count()); - const x_limbs = x.limbs.constSlice(); - const y_limbs = y.limbs.constSlice(); - var c: u1 = 0; - for (0..x.limbs_count()) |i| { - c = @as(u1, @truncate((x_limbs[i] -% y_limbs[i] -% c) >> t_bits)); + for (x.limbsConst(), y.limbsConst()) |x_limb, y_limb| { + c = @truncate((x_limb -% y_limb -% c) >> t_bits); } - return @as(bool, @bitCast(c)); + return c != 0; } // Compares two big integers in constant time, returning true if x >= y. fn limbsCmpGeq(x: anytype, y: @TypeOf(x)) bool { - return @as(bool, @bitCast(1 - @intFromBool(ct.limbsCmpLt(x, y)))); + return !ct.limbsCmpLt(x, y); } // Multiplies two limbs and returns the result as a wide limb. From 46af8bd2ba723e4dbcff07e457d2786cd7c74440 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 21 Nov 2023 20:44:32 -0700 Subject: [PATCH 4/8] resinator: use ArrayList instead of BoundedArray In this case it improved maintainability because magic number `4` is no longer repeated 3 times, and there is no longer a redundant branch in the loop. --- src/resinator/parse.zig | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/resinator/parse.zig b/src/resinator/parse.zig index 68537b94f6..7d56b92134 100644 --- a/src/resinator/parse.zig +++ b/src/resinator/parse.zig @@ -1246,13 +1246,16 @@ pub const Parser = struct { self.nextToken(.normal) catch unreachable; switch (statement_type) { .file_version, .product_version => { - var parts = std.BoundedArray(*Node, 4){}; + var parts_buffer: [4]*Node = undefined; + var parts = std.ArrayListUnmanaged(*Node).initBuffer(&parts_buffer); - while (parts.len < 4) { + while (true) { const value = try self.parseExpression(.{ .allowed_types = .{ .number = true } }); parts.addOneAssumeCapacity().* = value; - if (parts.len == 4 or !(try self.parseOptionalToken(.comma))) { + if (parts.unusedCapacitySlice().len == 0 or + !(try self.parseOptionalToken(.comma))) + { break; } } @@ -1260,7 +1263,7 @@ pub const Parser = struct { const node = try self.state.arena.create(Node.VersionStatement); node.* = .{ .type = type_token, - .parts = try self.state.arena.dupe(*Node, parts.slice()), + .parts = try self.state.arena.dupe(*Node, parts.items), }; return &node.base; }, From ddd8d5918890177e0d6e5477c1be3701d5973908 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 21 Nov 2023 21:20:30 -0700 Subject: [PATCH 5/8] tools/gen_spirv_spec: fix wrong use of BoundedArray It incorrectly, was returning an error, when it actually wanted to assert that the array bounds were not exceeded. Fixed by using ArrayList instead. --- tools/gen_spirv_spec.zig | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/tools/gen_spirv_spec.zig b/tools/gen_spirv_spec.zig index 18fbd5cd81..4e163b3522 100644 --- a/tools/gen_spirv_spec.zig +++ b/tools/gen_spirv_spec.zig @@ -601,9 +601,8 @@ fn renderFieldName(writer: anytype, operands: []const g.Operand, field_index: us const operand = operands[field_index]; // Should be enough for all names - adjust as needed. - var name_buffer = std.BoundedArray(u8, 64){ - .buffer = undefined, - }; + var name_backing_buffer: [64]u8 = undefined; + var name_buffer = std.ArrayListUnmanaged(u8).initBuffer(&name_backing_buffer); derive_from_kind: { // Operand names are often in the json encoded as "'Name'" (with two sets of quotes). @@ -617,33 +616,33 @@ fn renderFieldName(writer: anytype, operands: []const g.Operand, field_index: us // Use the same loop to transform to snake-case. for (name) |c| { switch (c) { - 'a'...'z', '0'...'9' => try name_buffer.append(c), - 'A'...'Z' => try name_buffer.append(std.ascii.toLower(c)), - ' ', '~' => try name_buffer.append('_'), + 'a'...'z', '0'...'9' => name_buffer.appendAssumeCapacity(c), + 'A'...'Z' => name_buffer.appendAssumeCapacity(std.ascii.toLower(c)), + ' ', '~' => name_buffer.appendAssumeCapacity('_'), else => break :derive_from_kind, } } // Assume there are no duplicate 'name' fields. - try writer.print("{}", .{std.zig.fmtId(name_buffer.slice())}); + try writer.print("{}", .{std.zig.fmtId(name_buffer.items)}); return; } // Translate to snake case. - name_buffer.len = 0; + name_buffer.items.len = 0; for (operand.kind, 0..) |c, i| { switch (c) { - 'a'...'z', '0'...'9' => try name_buffer.append(c), + 'a'...'z', '0'...'9' => name_buffer.appendAssumeCapacity(c), 'A'...'Z' => if (i > 0 and std.ascii.isLower(operand.kind[i - 1])) { - try name_buffer.appendSlice(&[_]u8{ '_', std.ascii.toLower(c) }); + name_buffer.appendSliceAssumeCapacity(&[_]u8{ '_', std.ascii.toLower(c) }); } else { - try name_buffer.append(std.ascii.toLower(c)); + name_buffer.appendAssumeCapacity(std.ascii.toLower(c)); }, else => unreachable, // Assume that the name is valid C-syntax (and contains no underscores). } } - try writer.print("{}", .{std.zig.fmtId(name_buffer.slice())}); + try writer.print("{}", .{std.zig.fmtId(name_buffer.items)}); // For fields derived from type name, there could be any amount. // Simply check against all other fields, and if another similar one exists, add a number. From 9393a83323bfdfa37cda703997f4e0faf1ac2732 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 21 Nov 2023 21:22:48 -0700 Subject: [PATCH 6/8] aro: use std.fmt.bufPrint rather than BoundedArray This simplifies the logic. For example, in generateExactWidthType, it no longer has to save a previous length and then use defer to reset the length after mutating it. --- deps/aro/aro/Compilation.zig | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/deps/aro/aro/Compilation.zig b/deps/aro/aro/Compilation.zig index 00d9599501..8aba7880bc 100644 --- a/deps/aro/aro/Compilation.zig +++ b/deps/aro/aro/Compilation.zig @@ -582,10 +582,9 @@ fn generateFloatMacros(w: anytype, prefix: []const u8, semantics: target_util.FP }, ); - var defPrefix = std.BoundedArray(u8, 32).init(0) catch unreachable; - defPrefix.writer().print("__{s}_", .{prefix}) catch return error.OutOfMemory; - - const prefix_slice = defPrefix.constSlice(); + var def_prefix_buf: [32]u8 = undefined; + const prefix_slice = std.fmt.bufPrint(&def_prefix_buf, "__{s}_", .{prefix}) catch + return error.OutOfMemory; try w.print("#define {s}DENORM_MIN__ {s}{s}\n", .{ prefix_slice, denormMin, ext }); try w.print("#define {s}HAS_DENORM__\n", .{prefix_slice}); @@ -770,18 +769,18 @@ fn generateExactWidthType(comp: *const Compilation, w: anytype, mapper: StrInt.T ty = if (unsigned) comp.types.int64.makeIntegerUnsigned() else comp.types.int64; } - var prefix = std.BoundedArray(u8, 16).init(0) catch unreachable; - prefix.writer().print("{s}{d}", .{ if (unsigned) "__UINT" else "__INT", width }) catch return error.OutOfMemory; + var buffer: [16]u8 = undefined; + const suffix = "_TYPE__"; + const full = std.fmt.bufPrint(&buffer, "{s}{d}{s}", .{ + if (unsigned) "__UINT" else "__INT", width, suffix, + }) catch return error.OutOfMemory; - { - const len = prefix.len; - defer prefix.resize(len) catch unreachable; // restoring previous size - prefix.appendSliceAssumeCapacity("_TYPE__"); - try generateTypeMacro(w, mapper, prefix.constSlice(), ty, comp.langopts); - } + try generateTypeMacro(w, mapper, full, ty, comp.langopts); - try comp.generateFmt(prefix.constSlice(), w, ty); - try comp.generateSuffixMacro(prefix.constSlice(), w, ty); + const prefix = full[0 .. full.len - suffix.len]; // remove "_TYPE__" + + try comp.generateFmt(prefix, w, ty); + try comp.generateSuffixMacro(prefix, w, ty); } pub fn hasFloat128(comp: *const Compilation) bool { @@ -908,10 +907,12 @@ fn generateExactWidthIntMax(comp: *const Compilation, w: anytype, specifier: Typ ty = if (unsigned) comp.types.int64.makeIntegerUnsigned() else comp.types.int64; } - var name = std.BoundedArray(u8, 6).init(0) catch unreachable; - name.writer().print("{s}{d}", .{ if (unsigned) "UINT" else "INT", bit_count }) catch return error.OutOfMemory; + var name_buffer: [6]u8 = undefined; + const name = std.fmt.bufPrint(&name_buffer, "{s}{d}", .{ + if (unsigned) "UINT" else "INT", bit_count, + }) catch return error.OutOfMemory; - return comp.generateIntMax(w, name.constSlice(), ty); + return comp.generateIntMax(w, name, ty); } fn generateIntWidth(comp: *Compilation, w: anytype, name: []const u8, ty: Type) !void { From 44ae978523f759382d5724fac1ffd83a2d0eda2c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 21 Nov 2023 21:42:58 -0700 Subject: [PATCH 7/8] aro: avoid BoundedArray in text_literal I consider this change to be neutral. It inlines a bit of logic that previously was abstracted, however, it also eliminates an instance of `catch unreachable` as well as a clumsy failed pop / append in favor of writing directly to the appropriate array element. --- deps/aro/aro/Parser.zig | 4 ++-- deps/aro/aro/text_literal.zig | 24 ++++++++++++++++++------ 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/deps/aro/aro/Parser.zig b/deps/aro/aro/Parser.zig index 5fddcc9f88..13469da734 100644 --- a/deps/aro/aro/Parser.zig +++ b/deps/aro/aro/Parser.zig @@ -7796,7 +7796,7 @@ fn stringLiteral(p: *Parser) Error!Result { } }, }; - for (char_literal_parser.errors.constSlice()) |item| { + for (char_literal_parser.errors()) |item| { try p.errExtra(item.tag, p.tok_i, item.extra); } } @@ -7911,7 +7911,7 @@ fn charLiteral(p: *Parser) Error!Result { char_literal_parser.err(.char_lit_too_wide, .{ .none = {} }); } - for (char_literal_parser.errors.constSlice()) |item| { + for (char_literal_parser.errors()) |item| { try p.errExtra(item.tag, p.tok_i, item.extra); } } diff --git a/deps/aro/aro/text_literal.zig b/deps/aro/aro/text_literal.zig index 342a6b27d6..1c5d592982 100644 --- a/deps/aro/aro/text_literal.zig +++ b/deps/aro/aro/text_literal.zig @@ -157,7 +157,8 @@ pub const Parser = struct { max_codepoint: u21, /// We only want to issue a max of 1 error per char literal errored: bool = false, - errors: std.BoundedArray(CharDiagnostic, 4) = .{}, + errors_buffer: [4]CharDiagnostic, + errors_len: usize, comp: *const Compilation, pub fn init(literal: []const u8, kind: Kind, max_codepoint: u21, comp: *const Compilation) Parser { @@ -166,6 +167,8 @@ pub const Parser = struct { .comp = comp, .kind = kind, .max_codepoint = max_codepoint, + .errors_buffer = undefined, + .errors_len = 0, }; } @@ -178,19 +181,28 @@ pub const Parser = struct { }; } + pub fn errors(p: *Parser) []CharDiagnostic { + return p.errors_buffer[0..p.errors_len]; + } + pub fn err(self: *Parser, tag: Diagnostics.Tag, extra: Diagnostics.Message.Extra) void { if (self.errored) return; self.errored = true; const diagnostic = .{ .tag = tag, .extra = extra }; - self.errors.append(diagnostic) catch { - _ = self.errors.pop(); - self.errors.append(diagnostic) catch unreachable; - }; + if (self.errors_len == self.errors_buffer.len) { + self.errors_buffer[self.errors_buffer.len - 1] = diagnostic; + } else { + self.errors_buffer[self.errors_len] = diagnostic; + self.errors_len += 1; + } } pub fn warn(self: *Parser, tag: Diagnostics.Tag, extra: Diagnostics.Message.Extra) void { if (self.errored) return; - self.errors.append(.{ .tag = tag, .extra = extra }) catch {}; + if (self.errors_len < self.errors_buffer.len) { + self.errors_buffer[self.errors_len] = .{ .tag = tag, .extra = extra }; + self.errors_len += 1; + } } pub fn next(self: *Parser) ?Item { From a34a51ef6eb4bd8dfab14bd8bfe1193d5573eacf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 21 Nov 2023 21:45:09 -0700 Subject: [PATCH 8/8] aro: use ArrayList in GCCDetector instead of BoundedArray These arrays don't really all have an upper bound of 16; in fact they have different upper bounds. Presumably the reason 16 was used for all of them was to avoid code bloat with BoundedArray. Well, now even more code bloat has been eliminated because now it's using `ArrayList([]const u8)` which is certainly instantiated elsewhere. Furthermore, the different corrected upper bounds can be specified at each instance of the array list. --- deps/aro/aro/Driver/GCCDetector.zig | 41 +++++++++++++++++------------ 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/deps/aro/aro/Driver/GCCDetector.zig b/deps/aro/aro/Driver/GCCDetector.zig index d1fade2864..79576270cd 100644 --- a/deps/aro/aro/Driver/GCCDetector.zig +++ b/deps/aro/aro/Driver/GCCDetector.zig @@ -29,7 +29,7 @@ pub fn appendToolPath(self: *const GCCDetector, tc: *Toolchain) !void { }, .program); } -fn addDefaultGCCPrefixes(prefixes: *PathPrefixes, tc: *const Toolchain) !void { +fn addDefaultGCCPrefixes(prefixes: *std.ArrayListUnmanaged([]const u8), tc: *const Toolchain) !void { const sysroot = tc.getSysroot(); const target = tc.getTarget(); if (sysroot.len == 0 and target.os.tag == .linux and tc.filesystem.exists("/opt/rh")) { @@ -57,14 +57,12 @@ fn addDefaultGCCPrefixes(prefixes: *PathPrefixes, tc: *const Toolchain) !void { } } -const PathPrefixes = std.BoundedArray([]const u8, 16); - fn collectLibDirsAndTriples( tc: *Toolchain, - lib_dirs: *PathPrefixes, - triple_aliases: *PathPrefixes, - biarch_libdirs: *PathPrefixes, - biarch_triple_aliases: *PathPrefixes, + lib_dirs: *std.ArrayListUnmanaged([]const u8), + triple_aliases: *std.ArrayListUnmanaged([]const u8), + biarch_libdirs: *std.ArrayListUnmanaged([]const u8), + biarch_triple_aliases: *std.ArrayListUnmanaged([]const u8), ) !void { const AArch64LibDirs: [2][]const u8 = .{ "/lib64", "/lib" }; const AArch64Triples: [4][]const u8 = .{ "aarch64-none-linux-gnu", "aarch64-linux-gnu", "aarch64-redhat-linux", "aarch64-suse-linux" }; @@ -408,10 +406,18 @@ pub fn discover(self: *GCCDetector, tc: *Toolchain) !void { else target_util.get32BitArchVariant(target); - var candidate_lib_dirs: PathPrefixes = .{}; - var candidate_triple_aliases: PathPrefixes = .{}; - var candidate_biarch_lib_dirs: PathPrefixes = .{}; - var candidate_biarch_triple_aliases: PathPrefixes = .{}; + var candidate_lib_dirs_buffer: [16][]const u8 = undefined; + var candidate_lib_dirs = std.ArrayListUnmanaged([]const u8).initBuffer(&candidate_lib_dirs_buffer); + + var candidate_triple_aliases_buffer: [16][]const u8 = undefined; + var candidate_triple_aliases = std.ArrayListUnmanaged([]const u8).initBuffer(&candidate_triple_aliases_buffer); + + var candidate_biarch_lib_dirs_buffer: [16][]const u8 = undefined; + var candidate_biarch_lib_dirs = std.ArrayListUnmanaged([]const u8).initBuffer(&candidate_biarch_lib_dirs_buffer); + + var candidate_biarch_triple_aliases_buffer: [16][]const u8 = undefined; + var candidate_biarch_triple_aliases = std.ArrayListUnmanaged([]const u8).initBuffer(&candidate_biarch_triple_aliases_buffer); + try collectLibDirsAndTriples( tc, &candidate_lib_dirs, @@ -433,7 +439,8 @@ pub fn discover(self: *GCCDetector, tc: *Toolchain) !void { } } - var prefixes: PathPrefixes = .{}; + var prefixes_buf: [16][]const u8 = undefined; + var prefixes = std.ArrayListUnmanaged([]const u8).initBuffer(&prefixes_buf); const gcc_toolchain_dir = gccToolchainDir(tc); if (gcc_toolchain_dir.len != 0) { const adjusted = if (gcc_toolchain_dir[gcc_toolchain_dir.len - 1] == '/') @@ -455,10 +462,10 @@ pub fn discover(self: *GCCDetector, tc: *Toolchain) !void { } const v0 = GCCVersion.parse("0.0.0"); - for (prefixes.constSlice()) |prefix| { + for (prefixes.items) |prefix| { if (!tc.filesystem.exists(prefix)) continue; - for (candidate_lib_dirs.constSlice()) |suffix| { + for (candidate_lib_dirs.items) |suffix| { defer fib.reset(); const lib_dir = std.fs.path.join(fib.allocator(), &.{ prefix, suffix }) catch continue; if (!tc.filesystem.exists(lib_dir)) continue; @@ -467,17 +474,17 @@ pub fn discover(self: *GCCDetector, tc: *Toolchain) !void { const gcc_cross_dir_exists = tc.filesystem.joinedExists(&.{ lib_dir, "/gcc-cross" }); try self.scanLibDirForGCCTriple(tc, target, lib_dir, triple_str, false, gcc_dir_exists, gcc_cross_dir_exists); - for (candidate_triple_aliases.constSlice()) |candidate| { + for (candidate_triple_aliases.items) |candidate| { try self.scanLibDirForGCCTriple(tc, target, lib_dir, candidate, false, gcc_dir_exists, gcc_cross_dir_exists); } } - for (candidate_biarch_lib_dirs.constSlice()) |suffix| { + for (candidate_biarch_lib_dirs.items) |suffix| { const lib_dir = std.fs.path.join(fib.allocator(), &.{ prefix, suffix }) catch continue; if (!tc.filesystem.exists(lib_dir)) continue; const gcc_dir_exists = tc.filesystem.joinedExists(&.{ lib_dir, "/gcc" }); const gcc_cross_dir_exists = tc.filesystem.joinedExists(&.{ lib_dir, "/gcc-cross" }); - for (candidate_biarch_triple_aliases.constSlice()) |candidate| { + for (candidate_biarch_triple_aliases.items) |candidate| { try self.scanLibDirForGCCTriple(tc, target, lib_dir, candidate, true, gcc_dir_exists, gcc_cross_dir_exists); } }