move ArrayListSentineled to std lib orphanage

This commit is contained in:
Veikka Tuominen 2020-12-23 16:24:22 +02:00
parent 51a904677c
commit e5aab62228
No known key found for this signature in database
GPG Key ID: 59AEB8936E16A6AC
7 changed files with 128 additions and 310 deletions

View File

@ -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 {

View File

@ -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"));
}

View File

@ -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 {

View File

@ -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,

View File

@ -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;

View File

@ -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;
}

View File

@ -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);
}