Skip to content

Commit c2e8788

Browse files
committed
Merge branch 'daurnimator-less-buffer'
closes #4665
2 parents e8a1e2a + 2e80668 commit c2e8788

31 files changed

+513
-456
lines changed

doc/docgen.zig

+4-4
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {
321321
var last_action = Action.Open;
322322
var last_columns: ?u8 = null;
323323

324-
var toc_buf = try std.Buffer.initSize(allocator, 0);
324+
var toc_buf = std.ArrayList(u8).init(allocator);
325325
defer toc_buf.deinit();
326326

327327
var toc = toc_buf.outStream();
@@ -607,7 +607,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {
607607
}
608608

609609
fn urlize(allocator: *mem.Allocator, input: []const u8) ![]u8 {
610-
var buf = try std.Buffer.initSize(allocator, 0);
610+
var buf = std.ArrayList(u8).init(allocator);
611611
defer buf.deinit();
612612

613613
const out = buf.outStream();
@@ -626,7 +626,7 @@ fn urlize(allocator: *mem.Allocator, input: []const u8) ![]u8 {
626626
}
627627

628628
fn escapeHtml(allocator: *mem.Allocator, input: []const u8) ![]u8 {
629-
var buf = try std.Buffer.initSize(allocator, 0);
629+
var buf = std.ArrayList(u8).init(allocator);
630630
defer buf.deinit();
631631

632632
const out = buf.outStream();
@@ -672,7 +672,7 @@ test "term color" {
672672
}
673673

674674
fn termColor(allocator: *mem.Allocator, input: []const u8) ![]u8 {
675-
var buf = try std.Buffer.initSize(allocator, 0);
675+
var buf = std.ArrayList(u8).init(allocator);
676676
defer buf.deinit();
677677

678678
var out = buf.outStream();

lib/std/array_list.zig

+29-4
Original file line numberDiff line numberDiff line change
@@ -189,16 +189,30 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type {
189189
self.len += items.len;
190190
}
191191

192-
/// Append a value to the list `n` times. Allocates more memory
193-
/// as necessary.
192+
/// Same as `append` except it returns the number of bytes written, which is always the same
193+
/// as `m.len`. The purpose of this function existing is to match `std.io.OutStream` API.
194+
/// This function may be called only when `T` is `u8`.
195+
fn appendWrite(self: *Self, m: []const u8) !usize {
196+
try self.appendSlice(m);
197+
return m.len;
198+
}
199+
200+
/// Initializes an OutStream which will append to the list.
201+
/// This function may be called only when `T` is `u8`.
202+
pub fn outStream(self: *Self) std.io.OutStream(*Self, error{OutOfMemory}, appendWrite) {
203+
return .{ .context = self };
204+
}
205+
206+
/// Append a value to the list `n` times.
207+
/// Allocates more memory as necessary.
194208
pub fn appendNTimes(self: *Self, value: T, n: usize) !void {
195209
const old_len = self.len;
196210
try self.resize(self.len + n);
197211
mem.set(T, self.items[old_len..self.len], value);
198212
}
199213

200-
/// Adjust the list's length to `new_len`. Doesn't initialize
201-
/// added items if any.
214+
/// Adjust the list's length to `new_len`.
215+
/// Does not initialize added items if any.
202216
pub fn resize(self: *Self, new_len: usize) !void {
203217
try self.ensureCapacity(new_len);
204218
self.len = new_len;
@@ -479,3 +493,14 @@ test "std.ArrayList: ArrayList(T) of struct T" {
479493
try root.sub_items.append(Item{ .integer = 42, .sub_items = ArrayList(Item).init(testing.allocator) });
480494
testing.expect(root.sub_items.items[0].integer == 42);
481495
}
496+
497+
test "std.ArrayList(u8) implements outStream" {
498+
var buffer = ArrayList(u8).init(std.testing.allocator);
499+
defer buffer.deinit();
500+
501+
const x: i32 = 42;
502+
const y: i32 = 1234;
503+
try buffer.outStream().print("x: {}\ny: {}\n", .{ x, y });
504+
505+
testing.expectEqualSlices(u8, "x: 42\ny: 1234\n", buffer.span());
506+
}

lib/std/array_list_sentineled.zig

+224
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
const std = @import("std.zig");
2+
const debug = std.debug;
3+
const mem = std.mem;
4+
const Allocator = mem.Allocator;
5+
const assert = debug.assert;
6+
const testing = std.testing;
7+
const ArrayList = std.ArrayList;
8+
9+
/// A contiguous, growable list of items in memory, with a sentinel after them.
10+
/// The sentinel is maintained when appending, resizing, etc.
11+
/// If you do not need a sentinel, consider using `ArrayList` instead.
12+
pub fn ArrayListSentineled(comptime T: type, comptime sentinel: T) type {
13+
return struct {
14+
list: ArrayList(T),
15+
16+
const Self = @This();
17+
18+
/// Must deinitialize with deinit.
19+
pub fn init(allocator: *Allocator, m: []const T) !Self {
20+
var self = try initSize(allocator, m.len);
21+
mem.copy(T, self.list.items, m);
22+
return self;
23+
}
24+
25+
/// Initialize memory to size bytes of undefined values.
26+
/// Must deinitialize with deinit.
27+
pub fn initSize(allocator: *Allocator, size: usize) !Self {
28+
var self = initNull(allocator);
29+
try self.resize(size);
30+
return self;
31+
}
32+
33+
/// Initialize with capacity to hold at least num bytes.
34+
/// Must deinitialize with deinit.
35+
pub fn initCapacity(allocator: *Allocator, num: usize) !Self {
36+
var self = Self{ .list = try ArrayList(T).initCapacity(allocator, num + 1) };
37+
self.list.appendAssumeCapacity(sentinel);
38+
return self;
39+
}
40+
41+
/// Must deinitialize with deinit.
42+
/// None of the other operations are valid until you do one of these:
43+
/// * `replaceContents`
44+
/// * `resize`
45+
pub fn initNull(allocator: *Allocator) Self {
46+
return Self{ .list = ArrayList(T).init(allocator) };
47+
}
48+
49+
/// Must deinitialize with deinit.
50+
pub fn initFromBuffer(buffer: Self) !Self {
51+
return Self.init(buffer.list.allocator, buffer.span());
52+
}
53+
54+
/// Takes ownership of the passed in slice. The slice must have been
55+
/// allocated with `allocator`.
56+
/// Must deinitialize with deinit.
57+
pub fn fromOwnedSlice(allocator: *Allocator, slice: []T) !Self {
58+
var self = Self{ .list = ArrayList(T).fromOwnedSlice(allocator, slice) };
59+
try self.list.append(sentinel);
60+
return self;
61+
}
62+
63+
/// The caller owns the returned memory. The list becomes null and is safe to `deinit`.
64+
pub fn toOwnedSlice(self: *Self) [:sentinel]T {
65+
const allocator = self.list.allocator;
66+
const result = self.list.toOwnedSlice();
67+
self.* = initNull(allocator);
68+
return result[0 .. result.len - 1 :sentinel];
69+
}
70+
71+
/// Only works when `T` is `u8`.
72+
pub fn allocPrint(allocator: *Allocator, comptime format: []const u8, args: var) !Self {
73+
const size = std.math.cast(usize, std.fmt.count(format, args)) catch |err| switch (err) {
74+
error.Overflow => return error.OutOfMemory,
75+
};
76+
var self = try Self.initSize(allocator, size);
77+
assert((std.fmt.bufPrint(self.list.items, format, args) catch unreachable).len == size);
78+
return self;
79+
}
80+
81+
pub fn deinit(self: *Self) void {
82+
self.list.deinit();
83+
}
84+
85+
pub fn span(self: var) @TypeOf(self.list.items[0 .. self.list.len - 1 :sentinel]) {
86+
return self.list.span()[0..self.len() :sentinel];
87+
}
88+
89+
pub fn shrink(self: *Self, new_len: usize) void {
90+
assert(new_len <= self.len());
91+
self.list.shrink(new_len + 1);
92+
self.list.items[self.len()] = sentinel;
93+
}
94+
95+
pub fn resize(self: *Self, new_len: usize) !void {
96+
try self.list.resize(new_len + 1);
97+
self.list.items[self.len()] = sentinel;
98+
}
99+
100+
pub fn isNull(self: Self) bool {
101+
return self.list.len == 0;
102+
}
103+
104+
pub fn len(self: Self) usize {
105+
return self.list.len - 1;
106+
}
107+
108+
pub fn capacity(self: Self) usize {
109+
return if (self.list.items.len > 0)
110+
self.list.items.len - 1
111+
else
112+
0;
113+
}
114+
115+
pub fn appendSlice(self: *Self, m: []const T) !void {
116+
const old_len = self.len();
117+
try self.resize(old_len + m.len);
118+
mem.copy(T, self.list.span()[old_len..], m);
119+
}
120+
121+
pub fn append(self: *Self, byte: T) !void {
122+
const old_len = self.len();
123+
try self.resize(old_len + 1);
124+
self.list.span()[old_len] = byte;
125+
}
126+
127+
pub fn eql(self: Self, m: []const T) bool {
128+
return mem.eql(T, self.span(), m);
129+
}
130+
131+
pub fn startsWith(self: Self, m: []const T) bool {
132+
if (self.len() < m.len) return false;
133+
return mem.eql(T, self.list.items[0..m.len], m);
134+
}
135+
136+
pub fn endsWith(self: Self, m: []const T) bool {
137+
const l = self.len();
138+
if (l < m.len) return false;
139+
const start = l - m.len;
140+
return mem.eql(T, self.list.items[start..l], m);
141+
}
142+
143+
pub fn replaceContents(self: *Self, m: []const T) !void {
144+
try self.resize(m.len);
145+
mem.copy(T, self.list.span(), m);
146+
}
147+
148+
/// Initializes an OutStream which will append to the list.
149+
/// This function may be called only when `T` is `u8`.
150+
pub fn outStream(self: *Self) std.io.OutStream(*Self, error{OutOfMemory}, appendWrite) {
151+
return .{ .context = self };
152+
}
153+
154+
/// Same as `append` except it returns the number of bytes written, which is always the same
155+
/// as `m.len`. The purpose of this function existing is to match `std.io.OutStream` API.
156+
/// This function may be called only when `T` is `u8`.
157+
pub fn appendWrite(self: *Self, m: []const u8) !usize {
158+
try self.appendSlice(m);
159+
return m.len;
160+
}
161+
};
162+
}
163+
164+
test "simple" {
165+
var buf = try ArrayListSentineled(u8, 0).init(testing.allocator, "");
166+
defer buf.deinit();
167+
168+
testing.expect(buf.len() == 0);
169+
try buf.appendSlice("hello");
170+
try buf.appendSlice(" ");
171+
try buf.appendSlice("world");
172+
testing.expect(buf.eql("hello world"));
173+
testing.expect(mem.eql(u8, mem.spanZ(buf.span().ptr), buf.span()));
174+
175+
var buf2 = try ArrayListSentineled(u8, 0).initFromBuffer(buf);
176+
defer buf2.deinit();
177+
testing.expect(buf.eql(buf2.span()));
178+
179+
testing.expect(buf.startsWith("hell"));
180+
testing.expect(buf.endsWith("orld"));
181+
182+
try buf2.resize(4);
183+
testing.expect(buf.startsWith(buf2.span()));
184+
}
185+
186+
test "initSize" {
187+
var buf = try ArrayListSentineled(u8, 0).initSize(testing.allocator, 3);
188+
defer buf.deinit();
189+
testing.expect(buf.len() == 3);
190+
try buf.appendSlice("hello");
191+
testing.expect(mem.eql(u8, buf.span()[3..], "hello"));
192+
}
193+
194+
test "initCapacity" {
195+
var buf = try ArrayListSentineled(u8, 0).initCapacity(testing.allocator, 10);
196+
defer buf.deinit();
197+
testing.expect(buf.len() == 0);
198+
testing.expect(buf.capacity() >= 10);
199+
const old_cap = buf.capacity();
200+
try buf.appendSlice("hello");
201+
testing.expect(buf.len() == 5);
202+
testing.expect(buf.capacity() == old_cap);
203+
testing.expect(mem.eql(u8, buf.span(), "hello"));
204+
}
205+
206+
test "print" {
207+
var buf = try ArrayListSentineled(u8, 0).init(testing.allocator, "");
208+
defer buf.deinit();
209+
210+
try buf.outStream().print("Hello {} the {}", .{ 2, "world" });
211+
testing.expect(buf.eql("Hello 2 the world"));
212+
}
213+
214+
test "outStream" {
215+
var buffer = try ArrayListSentineled(u8, 0).initSize(testing.allocator, 0);
216+
defer buffer.deinit();
217+
const buf_stream = buffer.outStream();
218+
219+
const x: i32 = 42;
220+
const y: i32 = 1234;
221+
try buf_stream.print("x: {}\ny: {}\n", .{ x, y });
222+
223+
testing.expect(mem.eql(u8, buffer.span(), "x: 42\ny: 1234\n"));
224+
}

0 commit comments

Comments
 (0)