diff --git a/lib/std/array_list.zig b/lib/std/array_list.zig index ab47510cb7..102dfe9fbc 100644 --- a/lib/std/array_list.zig +++ b/lib/std/array_list.zig @@ -91,6 +91,13 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { return result; } + /// The caller owns the returned memory. ArrayList becomes empty. + pub fn toOwnedSliceSentinel(self: *Self, comptime sentinel: T) ![:sentinel]T { + try self.append(sentinel); + const result = self.list.toOwnedSlice(); + return result[0 .. result.len - 1 :sentinel]; + } + /// Insert `item` at index `n` by moving `list[n .. list.len]` to make room. /// This operation is O(N). pub fn insert(self: *Self, n: usize, item: T) !void { @@ -389,6 +396,13 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ return result; } + /// The caller owns the returned memory. ArrayList becomes empty. + pub fn toOwnedSliceSentinel(self: *Self, allocator: *Allocator, comptime sentinel: T) ![:sentinel]T { + try self.append(allocator, sentinel); + const result = self.list.toOwnedSlice(allocator); + return result[0 .. result.len - 1 :sentinel]; + } + /// Insert `item` at index `n`. Moves `list[n .. list.len]` /// to make room. pub fn insert(self: *Self, allocator: *Allocator, n: usize, item: T) !void { diff --git a/lib/std/array_list_sentineled.zig b/lib/std/array_list_sentineled.zig deleted file mode 100644 index 31f8f4fd80..0000000000 --- a/lib/std/array_list_sentineled.zig +++ /dev/null @@ -1,229 +0,0 @@ -// SPDX-License-Identifier: MIT -// Copyright (c) 2015-2020 Zig Contributors -// This file is part of [zig](https://ziglang.org/), which is MIT licensed. -// The MIT license requires this copyright notice to be included in all copies -// and substantial portions of the software. -const std = @import("std.zig"); -const debug = std.debug; -const mem = std.mem; -const Allocator = mem.Allocator; -const assert = debug.assert; -const testing = std.testing; -const ArrayList = std.ArrayList; - -/// A contiguous, growable list of items in memory, with a sentinel after them. -/// The sentinel is maintained when appending, resizing, etc. -/// If you do not need a sentinel, consider using `ArrayList` instead. -pub fn ArrayListSentineled(comptime T: type, comptime sentinel: T) type { - return struct { - list: ArrayList(T), - - const Self = @This(); - - /// Must deinitialize with deinit. - pub fn init(allocator: *Allocator, m: []const T) !Self { - var self = try initSize(allocator, m.len); - mem.copy(T, self.list.items, m); - return self; - } - - /// Initialize memory to size bytes of undefined values. - /// Must deinitialize with deinit. - pub fn initSize(allocator: *Allocator, size: usize) !Self { - var self = initNull(allocator); - try self.resize(size); - return self; - } - - /// Initialize with capacity to hold at least num bytes. - /// Must deinitialize with deinit. - pub fn initCapacity(allocator: *Allocator, num: usize) !Self { - var self = Self{ .list = try ArrayList(T).initCapacity(allocator, num + 1) }; - self.list.appendAssumeCapacity(sentinel); - return self; - } - - /// Must deinitialize with deinit. - /// None of the other operations are valid until you do one of these: - /// * `replaceContents` - /// * `resize` - pub fn initNull(allocator: *Allocator) Self { - return Self{ .list = ArrayList(T).init(allocator) }; - } - - /// Must deinitialize with deinit. - pub fn initFromBuffer(buffer: Self) !Self { - return Self.init(buffer.list.allocator, buffer.span()); - } - - /// Takes ownership of the passed in slice. The slice must have been - /// allocated with `allocator`. - /// Must deinitialize with deinit. - pub fn fromOwnedSlice(allocator: *Allocator, slice: []T) !Self { - var self = Self{ .list = ArrayList(T).fromOwnedSlice(allocator, slice) }; - try self.list.append(sentinel); - return self; - } - - /// The caller owns the returned memory. The list becomes null and is safe to `deinit`. - pub fn toOwnedSlice(self: *Self) [:sentinel]T { - const allocator = self.list.allocator; - const result = self.list.toOwnedSlice(); - self.* = initNull(allocator); - return result[0 .. result.len - 1 :sentinel]; - } - - /// Only works when `T` is `u8`. - pub fn allocPrint(allocator: *Allocator, comptime format: []const u8, args: anytype) !Self { - const size = std.math.cast(usize, std.fmt.count(format, args)) catch |err| switch (err) { - error.Overflow => return error.OutOfMemory, - }; - var self = try Self.initSize(allocator, size); - assert((std.fmt.bufPrint(self.list.items, format, args) catch unreachable).len == size); - return self; - } - - pub fn deinit(self: *Self) void { - self.list.deinit(); - } - - pub fn span(self: anytype) @TypeOf(self.list.items[0..:sentinel]) { - return self.list.items[0..self.len() :sentinel]; - } - - pub fn shrink(self: *Self, new_len: usize) void { - assert(new_len <= self.len()); - self.list.shrink(new_len + 1); - self.list.items[self.len()] = sentinel; - } - - pub fn resize(self: *Self, new_len: usize) !void { - try self.list.resize(new_len + 1); - self.list.items[self.len()] = sentinel; - } - - pub fn isNull(self: Self) bool { - return self.list.items.len == 0; - } - - pub fn len(self: Self) usize { - return self.list.items.len - 1; - } - - pub fn capacity(self: Self) usize { - return if (self.list.capacity > 0) - self.list.capacity - 1 - else - 0; - } - - pub fn appendSlice(self: *Self, m: []const T) !void { - const old_len = self.len(); - try self.resize(old_len + m.len); - mem.copy(T, self.list.items[old_len..], m); - } - - pub fn append(self: *Self, byte: T) !void { - const old_len = self.len(); - try self.resize(old_len + 1); - self.list.items[old_len] = byte; - } - - pub fn eql(self: Self, m: []const T) bool { - return mem.eql(T, self.span(), m); - } - - pub fn startsWith(self: Self, m: []const T) bool { - if (self.len() < m.len) return false; - return mem.eql(T, self.list.items[0..m.len], m); - } - - pub fn endsWith(self: Self, m: []const T) bool { - const l = self.len(); - if (l < m.len) return false; - const start = l - m.len; - return mem.eql(T, self.list.items[start..l], m); - } - - pub fn replaceContents(self: *Self, m: []const T) !void { - try self.resize(m.len); - mem.copy(T, self.list.items, m); - } - - /// Initializes an OutStream which will append to the list. - /// This function may be called only when `T` is `u8`. - pub fn outStream(self: *Self) std.io.OutStream(*Self, error{OutOfMemory}, appendWrite) { - return .{ .context = self }; - } - - /// Same as `append` except it returns the number of bytes written, which is always the same - /// as `m.len`. The purpose of this function existing is to match `std.io.OutStream` API. - /// This function may be called only when `T` is `u8`. - pub fn appendWrite(self: *Self, m: []const u8) !usize { - try self.appendSlice(m); - return m.len; - } - }; -} - -test "simple" { - var buf = try ArrayListSentineled(u8, 0).init(testing.allocator, ""); - defer buf.deinit(); - - testing.expect(buf.len() == 0); - try buf.appendSlice("hello"); - try buf.appendSlice(" "); - try buf.appendSlice("world"); - testing.expect(buf.eql("hello world")); - testing.expect(mem.eql(u8, mem.spanZ(buf.span().ptr), buf.span())); - - var buf2 = try ArrayListSentineled(u8, 0).initFromBuffer(buf); - defer buf2.deinit(); - testing.expect(buf.eql(buf2.span())); - - testing.expect(buf.startsWith("hell")); - testing.expect(buf.endsWith("orld")); - - try buf2.resize(4); - testing.expect(buf.startsWith(buf2.span())); -} - -test "initSize" { - var buf = try ArrayListSentineled(u8, 0).initSize(testing.allocator, 3); - defer buf.deinit(); - testing.expect(buf.len() == 3); - try buf.appendSlice("hello"); - testing.expect(mem.eql(u8, buf.span()[3..], "hello")); -} - -test "initCapacity" { - var buf = try ArrayListSentineled(u8, 0).initCapacity(testing.allocator, 10); - defer buf.deinit(); - testing.expect(buf.len() == 0); - testing.expect(buf.capacity() >= 10); - const old_cap = buf.capacity(); - try buf.appendSlice("hello"); - testing.expect(buf.len() == 5); - testing.expect(buf.capacity() == old_cap); - testing.expect(mem.eql(u8, buf.span(), "hello")); -} - -test "print" { - var buf = try ArrayListSentineled(u8, 0).init(testing.allocator, ""); - defer buf.deinit(); - - try buf.outStream().print("Hello {} the {}", .{ 2, "world" }); - testing.expect(buf.eql("Hello 2 the world")); -} - -test "outStream" { - var buffer = try ArrayListSentineled(u8, 0).initSize(testing.allocator, 0); - defer buffer.deinit(); - const buf_stream = buffer.outStream(); - - const x: i32 = 42; - const y: i32 = 1234; - try buf_stream.print("x: {}\ny: {}\n", .{ x, y }); - - testing.expect(mem.eql(u8, buffer.span(), "x: 42\ny: 1234\n")); -} diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index b61fe9470d..2d7f547a9e 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -15,7 +15,6 @@ const windows = os.windows; const mem = std.mem; const debug = std.debug; const BufMap = std.BufMap; -const ArrayListSentineled = std.ArrayListSentineled; const builtin = @import("builtin"); const Os = builtin.Os; const TailQueue = std.TailQueue; @@ -749,38 +748,38 @@ fn windowsCreateProcess(app_name: [*:0]u16, cmd_line: [*:0]u16, envp_ptr: ?[*]u1 /// Caller must dealloc. fn windowsCreateCommandLine(allocator: *mem.Allocator, argv: []const []const u8) ![:0]u8 { - var buf = try ArrayListSentineled(u8, 0).initSize(allocator, 0); + var buf = try ArrayList(u8).init(allocator); defer buf.deinit(); - const buf_stream = buf.outStream(); + const buf_wi = buf.outStream(); for (argv) |arg, arg_i| { - if (arg_i != 0) try buf_stream.writeByte(' '); + if (arg_i != 0) try buf.append(' '); if (mem.indexOfAny(u8, arg, " \t\n\"") == null) { - try buf_stream.writeAll(arg); + try buf.appendSlice(arg); continue; } - try buf_stream.writeByte('"'); + try buf.append('"'); var backslash_count: usize = 0; for (arg) |byte| { switch (byte) { '\\' => backslash_count += 1, '"' => { - try buf_stream.writeByteNTimes('\\', backslash_count * 2 + 1); - try buf_stream.writeByte('"'); + try buf.appendNTimes('\\', backslash_count * 2 + 1); + try buf.append('"'); backslash_count = 0; }, else => { - try buf_stream.writeByteNTimes('\\', backslash_count); - try buf_stream.writeByte(byte); + try buf.appendNTimes('\\', backslash_count); + try buf.append(byte); backslash_count = 0; }, } } - try buf_stream.writeByteNTimes('\\', backslash_count * 2); - try buf_stream.writeByte('"'); + try buf.appendNTimes('\\', backslash_count * 2); + try buf.append('"'); } - return buf.toOwnedSlice(); + return buf.toOwnedSliceSentinel(0); } fn windowsDestroyPipe(rd: ?windows.HANDLE, wr: ?windows.HANDLE) void { diff --git a/lib/std/net.zig b/lib/std/net.zig index 7e285b1fac..16b38b2536 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -783,13 +783,13 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !* var lookup_addrs = std.ArrayList(LookupAddr).init(allocator); defer lookup_addrs.deinit(); - var canon = std.ArrayListSentineled(u8, 0).initNull(arena); + var canon = std.ArrayList(u8).init(arena); defer canon.deinit(); try linuxLookupName(&lookup_addrs, &canon, name, family, flags, port); result.addrs = try arena.alloc(Address, lookup_addrs.items.len); - if (!canon.isNull()) { + if (canon.items.len != 0) { result.canon_name = canon.toOwnedSlice(); } @@ -818,7 +818,7 @@ const DAS_ORDER_SHIFT = 0; fn linuxLookupName( addrs: *std.ArrayList(LookupAddr), - canon: *std.ArrayListSentineled(u8, 0), + canon: *std.ArrayList(u8), opt_name: ?[]const u8, family: os.sa_family_t, flags: u32, @@ -826,7 +826,8 @@ fn linuxLookupName( ) !void { if (opt_name) |name| { // reject empty name and check len so it fits into temp bufs - try canon.replaceContents(name); + canon.items.len = 0; + try canon.appendSlice(name); if (Address.parseExpectingFamily(name, family, port)) |addr| { try addrs.append(LookupAddr{ .addr = addr }); } else |name_err| if ((flags & std.c.AI_NUMERICHOST) != 0) { @@ -1091,7 +1092,7 @@ fn linuxLookupNameFromNull( fn linuxLookupNameFromHosts( addrs: *std.ArrayList(LookupAddr), - canon: *std.ArrayListSentineled(u8, 0), + canon: *std.ArrayList(u8), name: []const u8, family: os.sa_family_t, port: u16, @@ -1142,7 +1143,8 @@ fn linuxLookupNameFromHosts( // first name is canonical name const name_text = first_name_text.?; if (isValidHostName(name_text)) { - try canon.replaceContents(name_text); + canon.items.len = 0; + try canon.appendSlice(name_text); } } } @@ -1161,7 +1163,7 @@ pub fn isValidHostName(hostname: []const u8) bool { fn linuxLookupNameFromDnsSearch( addrs: *std.ArrayList(LookupAddr), - canon: *std.ArrayListSentineled(u8, 0), + canon: *std.ArrayList(u8), name: []const u8, family: os.sa_family_t, port: u16, @@ -1177,10 +1179,10 @@ fn linuxLookupNameFromDnsSearch( if (byte == '.') dots += 1; } - const search = if (rc.search.isNull() or dots >= rc.ndots or mem.endsWith(u8, name, ".")) + const search = if (dots >= rc.ndots or mem.endsWith(u8, name, ".")) "" else - rc.search.span(); + rc.search.items; var canon_name = name; @@ -1193,14 +1195,14 @@ fn linuxLookupNameFromDnsSearch( // name is not a CNAME record) and serves as a buffer for passing // the full requested name to name_from_dns. try canon.resize(canon_name.len); - mem.copy(u8, canon.span(), canon_name); + mem.copy(u8, canon.items, canon_name); try canon.append('.'); var tok_it = mem.tokenize(search, " \t"); while (tok_it.next()) |tok| { canon.shrink(canon_name.len + 1); try canon.appendSlice(tok); - try linuxLookupNameFromDns(addrs, canon, canon.span(), family, rc, port); + try linuxLookupNameFromDns(addrs, canon, canon.items, family, rc, port); if (addrs.items.len != 0) return; } @@ -1210,13 +1212,13 @@ fn linuxLookupNameFromDnsSearch( const dpc_ctx = struct { addrs: *std.ArrayList(LookupAddr), - canon: *std.ArrayListSentineled(u8, 0), + canon: *std.ArrayList(u8), port: u16, }; fn linuxLookupNameFromDns( addrs: *std.ArrayList(LookupAddr), - canon: *std.ArrayListSentineled(u8, 0), + canon: *std.ArrayList(u8), name: []const u8, family: os.sa_family_t, rc: ResolvConf, @@ -1271,7 +1273,7 @@ const ResolvConf = struct { attempts: u32, ndots: u32, timeout: u32, - search: std.ArrayListSentineled(u8, 0), + search: std.ArrayList(u8), ns: std.ArrayList(LookupAddr), fn deinit(rc: *ResolvConf) void { @@ -1286,7 +1288,7 @@ const ResolvConf = struct { fn getResolvConf(allocator: *mem.Allocator, rc: *ResolvConf) !void { rc.* = ResolvConf{ .ns = std.ArrayList(LookupAddr).init(allocator), - .search = std.ArrayListSentineled(u8, 0).initNull(allocator), + .search = std.ArrayList(u8).init(allocator), .ndots = 1, .timeout = 5, .attempts = 2, @@ -1338,7 +1340,8 @@ fn getResolvConf(allocator: *mem.Allocator, rc: *ResolvConf) !void { const ip_txt = line_it.next() orelse continue; try linuxLookupNameFromNumericUnspec(&rc.ns, ip_txt, 53); } else if (mem.eql(u8, token, "domain") or mem.eql(u8, token, "search")) { - try rc.search.replaceContents(line_it.rest()); + rc.search.items.len = 0; + try rc.search.appendSlice(line_it.rest()); } } @@ -1569,7 +1572,8 @@ fn dnsParseCallback(ctx: dpc_ctx, rr: u8, data: []const u8, packet: []const u8) _ = try os.dn_expand(packet, data, &tmp); const canon_name = mem.spanZ(std.meta.assumeSentinel(&tmp, 0)); if (isValidHostName(canon_name)) { - try ctx.canon.replaceContents(canon_name); + ctx.canon.items.len = 0; + try ctx.canon.appendSlice(canon_name); } }, else => return, diff --git a/lib/std/std.zig b/lib/std/std.zig index 5fbf2662b9..eda0950e4f 100644 --- a/lib/std/std.zig +++ b/lib/std/std.zig @@ -8,7 +8,6 @@ pub const ArrayHashMapUnmanaged = array_hash_map.ArrayHashMapUnmanaged; pub const ArrayList = @import("array_list.zig").ArrayList; pub const ArrayListAligned = @import("array_list.zig").ArrayListAligned; pub const ArrayListAlignedUnmanaged = @import("array_list.zig").ArrayListAlignedUnmanaged; -pub const ArrayListSentineled = @import("array_list_sentineled.zig").ArrayListSentineled; pub const ArrayListUnmanaged = @import("array_list.zig").ArrayListUnmanaged; pub const AutoArrayHashMap = array_hash_map.AutoArrayHashMap; pub const AutoArrayHashMapUnmanaged = array_hash_map.AutoArrayHashMapUnmanaged; diff --git a/src/DepTokenizer.zig b/src/DepTokenizer.zig index cc2211a1aa..99db6e4b3c 100644 --- a/src/DepTokenizer.zig +++ b/src/DepTokenizer.zig @@ -885,7 +885,7 @@ fn depTokenizer(input: []const u8, expect: []const u8) !void { defer arena_allocator.deinit(); var it: Tokenizer = .{ .bytes = input }; - var buffer = try std.ArrayListSentineled(u8, 0).initSize(arena, 0); + var buffer = std.ArrayList(u8).init(arena); var resolve_buf = std.ArrayList(u8).init(arena); var i: usize = 0; while (it.next()) |token| { @@ -916,9 +916,8 @@ fn depTokenizer(input: []const u8, expect: []const u8) !void { } i += 1; } - const got: []const u8 = buffer.span(); - if (std.mem.eql(u8, expect, got)) { + if (std.mem.eql(u8, expect, buffer.items)) { testing.expect(true); return; } diff --git a/test/standalone/brace_expansion/main.zig b/test/standalone/brace_expansion/main.zig index 572be0624e..73c0e6fdae 100644 --- a/test/standalone/brace_expansion/main.zig +++ b/test/standalone/brace_expansion/main.zig @@ -4,7 +4,6 @@ const mem = std.mem; const debug = std.debug; const assert = debug.assert; const testing = std.testing; -const ArrayListSentineled = std.ArrayListSentineled; const ArrayList = std.ArrayList; const maxInt = std.math.maxInt; @@ -16,7 +15,8 @@ const Token = union(enum) { Eof, }; -var global_allocator: *mem.Allocator = undefined; +var gpa = std.heap.GeneralPurposeAllocator(.{}){}; +var global_allocator = &gpa.allocator; fn tokenize(input: []const u8) !ArrayList(Token) { const State = enum { @@ -25,12 +25,13 @@ fn tokenize(input: []const u8) !ArrayList(Token) { }; var token_list = ArrayList(Token).init(global_allocator); + errdefer token_list.deinit(); var tok_begin: usize = undefined; var state = State.Start; for (input) |b, i| { switch (state) { - State.Start => switch (b) { + .Start => switch (b) { 'a'...'z', 'A'...'Z' => { state = State.Word; tok_begin = i; @@ -40,7 +41,7 @@ fn tokenize(input: []const u8) !ArrayList(Token) { ',' => try token_list.append(Token.Comma), else => return error.InvalidInput, }, - State.Word => switch (b) { + .Word => switch (b) { 'a'...'z', 'A'...'Z' => {}, '{', '}', ',' => { try token_list.append(Token{ .Word = input[tok_begin..i] }); @@ -68,6 +69,23 @@ const Node = union(enum) { Scalar: []const u8, List: ArrayList(Node), Combine: []Node, + + fn deinit(self: Node) void { + switch (self) { + .Scalar => {}, + .Combine => |pair| { + pair[0].deinit(); + pair[1].deinit(); + global_allocator.free(pair); + }, + .List => |list| { + for (list.items) |item| { + item.deinit(); + } + list.deinit(); + }, + } + } }; const ParseError = error{ @@ -80,9 +98,13 @@ fn parse(tokens: *const ArrayList(Token), token_index: *usize) ParseError!Node { token_index.* += 1; const result_node = switch (first_token) { - Token.Word => |word| Node{ .Scalar = word }, - Token.OpenBrace => blk: { + .Word => |word| Node{ .Scalar = word }, + .OpenBrace => blk: { var list = ArrayList(Node).init(global_allocator); + errdefer { + for (list.items) |node| node.deinit(); + list.deinit(); + } while (true) { try list.append(try parse(tokens, token_index)); @@ -90,8 +112,8 @@ fn parse(tokens: *const ArrayList(Token), token_index: *usize) ParseError!Node { token_index.* += 1; switch (token) { - Token.CloseBrace => break, - Token.Comma => continue, + .CloseBrace => break, + .Comma => continue, else => return error.InvalidInput, } } @@ -101,8 +123,9 @@ fn parse(tokens: *const ArrayList(Token), token_index: *usize) ParseError!Node { }; switch (tokens.items[token_index.*]) { - Token.Word, Token.OpenBrace => { + .Word, .OpenBrace => { const pair = try global_allocator.alloc(Node, 2); + errdefer global_allocator.free(pair); pair[0] = result_node; pair[1] = try parse(tokens, token_index); return Node{ .Combine = pair }; @@ -111,22 +134,27 @@ fn parse(tokens: *const ArrayList(Token), token_index: *usize) ParseError!Node { } } -fn expandString(input: []const u8, output: *ArrayListSentineled(u8, 0)) !void { +fn expandString(input: []const u8, output: *ArrayList(u8)) !void { const tokens = try tokenize(input); + defer tokens.deinit(); if (tokens.items.len == 1) { return output.resize(0); } var token_index: usize = 0; const root = try parse(&tokens, &token_index); + defer root.deinit(); const last_token = tokens.items[token_index]; switch (last_token) { Token.Eof => {}, else => return error.InvalidInput, } - var result_list = ArrayList(ArrayListSentineled(u8, 0)).init(global_allocator); - defer result_list.deinit(); + var result_list = ArrayList(ArrayList(u8)).init(global_allocator); + defer { + for (result_list.items) |*buf| buf.deinit(); + result_list.deinit(); + } try expandNode(root, &result_list); @@ -135,39 +163,56 @@ fn expandString(input: []const u8, output: *ArrayListSentineled(u8, 0)) !void { if (i != 0) { try output.append(' '); } - try output.appendSlice(buf.span()); + try output.appendSlice(buf.items); } } const ExpandNodeError = error{OutOfMemory}; -fn expandNode(node: Node, output: *ArrayList(ArrayListSentineled(u8, 0))) ExpandNodeError!void { +fn expandNode(node: Node, output: *ArrayList(ArrayList(u8))) ExpandNodeError!void { assert(output.items.len == 0); switch (node) { - Node.Scalar => |scalar| { - try output.append(try ArrayListSentineled(u8, 0).init(global_allocator, scalar)); + .Scalar => |scalar| { + var list = ArrayList(u8).init(global_allocator); + errdefer list.deinit(); + try list.appendSlice(scalar); + try output.append(list); }, - Node.Combine => |pair| { + .Combine => |pair| { const a_node = pair[0]; const b_node = pair[1]; - var child_list_a = ArrayList(ArrayListSentineled(u8, 0)).init(global_allocator); + var child_list_a = ArrayList(ArrayList(u8)).init(global_allocator); + defer { + for (child_list_a.items) |*buf| buf.deinit(); + child_list_a.deinit(); + } try expandNode(a_node, &child_list_a); - var child_list_b = ArrayList(ArrayListSentineled(u8, 0)).init(global_allocator); + var child_list_b = ArrayList(ArrayList(u8)).init(global_allocator); + defer { + for (child_list_b.items) |*buf| buf.deinit(); + child_list_b.deinit(); + } try expandNode(b_node, &child_list_b); for (child_list_a.items) |buf_a| { for (child_list_b.items) |buf_b| { - var combined_buf = try ArrayListSentineled(u8, 0).initFromBuffer(buf_a); - try combined_buf.appendSlice(buf_b.span()); + var combined_buf = ArrayList(u8).init(global_allocator); + errdefer combined_buf.deinit(); + + try combined_buf.appendSlice(buf_a.items); + try combined_buf.appendSlice(buf_b.items); try output.append(combined_buf); } } }, - Node.List => |list| { + .List => |list| { for (list.items) |child_node| { - var child_list = ArrayList(ArrayListSentineled(u8, 0)).init(global_allocator); + var child_list = ArrayList(ArrayList(u8)).init(global_allocator); + errdefer for (child_list.items) |*buf| buf.deinit(); + defer child_list.deinit(); + try expandNode(child_node, &child_list); for (child_list.items) |buf| { @@ -179,32 +224,22 @@ fn expandNode(node: Node, output: *ArrayList(ArrayListSentineled(u8, 0))) Expand } pub fn main() !void { + defer _ = gpa.deinit(); const stdin_file = io.getStdIn(); const stdout_file = io.getStdOut(); - var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); - defer arena.deinit(); + const stdin = try stdin_file.reader().readAllAlloc(global_allocator, std.math.maxInt(usize)); + defer global_allocator.free(stdin); - global_allocator = &arena.allocator; - - var stdin_buf = try ArrayListSentineled(u8, 0).initSize(global_allocator, 0); - defer stdin_buf.deinit(); - - var stdin_adapter = stdin_file.inStream(); - try stdin_adapter.stream.readAllBuffer(&stdin_buf, maxInt(usize)); - - var result_buf = try ArrayListSentineled(u8, 0).initSize(global_allocator, 0); + var result_buf = ArrayList(u8).init(global_allocator); defer result_buf.deinit(); - try expandString(stdin_buf.span(), &result_buf); - try stdout_file.write(result_buf.span()); + try expandString(stdin_buf.items, &result_buf); + try stdout_file.write(result_buf.items); } test "invalid inputs" { - var arena = std.heap.ArenaAllocator.init(std.testing.allocator); - defer arena.deinit(); - - global_allocator = &arena.allocator; + global_allocator = std.testing.allocator; expectError("}ABC", error.InvalidInput); expectError("{ABC", error.InvalidInput); @@ -218,17 +253,14 @@ test "invalid inputs" { } fn expectError(test_input: []const u8, expected_err: anyerror) void { - var output_buf = ArrayListSentineled(u8, 0).initSize(global_allocator, 0) catch unreachable; + var output_buf = ArrayList(u8).init(global_allocator); defer output_buf.deinit(); testing.expectError(expected_err, expandString(test_input, &output_buf)); } test "valid inputs" { - var arena = std.heap.ArenaAllocator.init(std.testing.allocator); - defer arena.deinit(); - - global_allocator = &arena.allocator; + global_allocator = std.testing.allocator; expectExpansion("{x,y,z}", "x y z"); expectExpansion("{A,B}{x,y}", "Ax Ay Bx By"); @@ -251,10 +283,10 @@ test "valid inputs" { } fn expectExpansion(test_input: []const u8, expected_result: []const u8) void { - var result = ArrayListSentineled(u8, 0).initSize(global_allocator, 0) catch unreachable; + var result = ArrayList(u8).init(global_allocator); defer result.deinit(); expandString(test_input, &result) catch unreachable; - testing.expectEqualSlices(u8, expected_result, result.span()); + testing.expectEqualSlices(u8, expected_result, result.items); }