mirror of
https://github.com/ziglang/zig.git
synced 2026-01-20 14:25:16 +00:00
move ArrayListSentineled to std lib orphanage
This commit is contained in:
parent
51a904677c
commit
e5aab62228
@ -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 {
|
||||
|
||||
@ -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"));
|
||||
}
|
||||
@ -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 {
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user