2019-03-02 13:46:04 -08:00
|
|
|
const std = @import("std.zig");
|
2017-12-23 19:08:53 -08:00
|
|
|
const debug = std.debug;
|
|
|
|
const mem = std.mem;
|
2017-04-20 22:56:12 -07:00
|
|
|
const Allocator = mem.Allocator;
|
|
|
|
const assert = debug.assert;
|
2019-02-08 15:18:47 -08:00
|
|
|
const testing = std.testing;
|
2017-12-23 19:08:53 -08:00
|
|
|
const ArrayList = std.ArrayList;
|
2017-04-20 22:56:12 -07:00
|
|
|
|
|
|
|
/// A buffer that allocates memory and maintains a null byte at the end.
|
2018-11-13 05:08:37 -08:00
|
|
|
pub const Buffer = struct {
|
2017-05-04 11:05:06 -07:00
|
|
|
list: ArrayList(u8),
|
2017-04-20 22:56:12 -07:00
|
|
|
|
|
|
|
/// Must deinitialize with deinit.
|
2018-05-31 07:56:59 -07:00
|
|
|
pub fn init(allocator: *Allocator, m: []const u8) !Buffer {
|
2018-01-07 13:51:46 -08:00
|
|
|
var self = try initSize(allocator, m.len);
|
2017-04-20 22:56:12 -07:00
|
|
|
mem.copy(u8, self.list.items, m);
|
|
|
|
return self;
|
|
|
|
}
|
2019-12-08 19:53:51 -08:00
|
|
|
|
2019-11-23 20:08:33 -08:00
|
|
|
/// Initialize memory to size bytes of undefined values.
|
2017-04-20 22:56:12 -07:00
|
|
|
/// Must deinitialize with deinit.
|
2018-05-31 07:56:59 -07:00
|
|
|
pub fn initSize(allocator: *Allocator, size: usize) !Buffer {
|
2017-04-20 22:56:12 -07:00
|
|
|
var self = initNull(allocator);
|
2018-01-07 13:51:46 -08:00
|
|
|
try self.resize(size);
|
2017-04-20 22:56:12 -07:00
|
|
|
return self;
|
|
|
|
}
|
2019-12-08 19:53:51 -08:00
|
|
|
|
2019-11-23 20:08:33 -08:00
|
|
|
/// Initialize with capacity to hold at least num bytes.
|
|
|
|
/// Must deinitialize with deinit.
|
|
|
|
pub fn initCapacity(allocator: *Allocator, num: usize) !Buffer {
|
|
|
|
var self = Buffer{ .list = try ArrayList(u8).initCapacity(allocator, num + 1) };
|
|
|
|
self.list.appendAssumeCapacity(0);
|
|
|
|
return self;
|
|
|
|
}
|
2017-04-20 22:56:12 -07:00
|
|
|
|
|
|
|
/// Must deinitialize with deinit.
|
|
|
|
/// None of the other operations are valid until you do one of these:
|
|
|
|
/// * ::replaceContents
|
|
|
|
/// * ::resize
|
2018-05-31 07:56:59 -07:00
|
|
|
pub fn initNull(allocator: *Allocator) Buffer {
|
2018-11-13 05:08:37 -08:00
|
|
|
return Buffer{ .list = ArrayList(u8).init(allocator) };
|
2017-04-20 22:56:12 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Must deinitialize with deinit.
|
2018-10-15 15:23:47 -07:00
|
|
|
pub fn initFromBuffer(buffer: Buffer) !Buffer {
|
2017-04-20 22:56:12 -07:00
|
|
|
return Buffer.init(buffer.list.allocator, buffer.toSliceConst());
|
|
|
|
}
|
|
|
|
|
2017-10-11 07:16:13 -07:00
|
|
|
/// Buffer takes ownership of the passed in slice. The slice must have been
|
|
|
|
/// allocated with `allocator`.
|
|
|
|
/// Must deinitialize with deinit.
|
2018-06-08 16:24:48 -07:00
|
|
|
pub fn fromOwnedSlice(allocator: *Allocator, slice: []u8) !Buffer {
|
2018-11-13 05:08:37 -08:00
|
|
|
var self = Buffer{ .list = ArrayList(u8).fromOwnedSlice(allocator, slice) };
|
2018-06-08 16:24:48 -07:00
|
|
|
try self.list.append(0);
|
2017-10-11 07:16:13 -07:00
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The caller owns the returned memory. The Buffer becomes null and
|
|
|
|
/// is safe to `deinit`.
|
2020-01-18 23:40:35 -08:00
|
|
|
pub fn toOwnedSlice(self: *Buffer) [:0]u8 {
|
2017-10-11 07:16:13 -07:00
|
|
|
const allocator = self.list.allocator;
|
2020-01-19 18:06:41 -08:00
|
|
|
const result = self.list.toOwnedSlice();
|
2018-05-09 21:29:49 -07:00
|
|
|
self.* = initNull(allocator);
|
2020-01-18 23:40:35 -08:00
|
|
|
return result[0 .. result.len - 1 :0];
|
2017-10-11 07:16:13 -07:00
|
|
|
}
|
|
|
|
|
2019-12-08 19:53:51 -08:00
|
|
|
pub fn allocPrint(allocator: *Allocator, comptime format: []const u8, args: var) !Buffer {
|
2020-03-13 08:55:50 -07:00
|
|
|
const size = std.math.cast(usize, std.fmt.count(format, args)) catch |err| switch (err) {
|
2020-03-06 10:03:15 -08:00
|
|
|
error.Overflow => return error.OutOfMemory,
|
|
|
|
};
|
2018-07-16 17:52:50 -07:00
|
|
|
var self = try Buffer.initSize(allocator, size);
|
2020-03-06 14:59:21 -08:00
|
|
|
assert((std.fmt.bufPrint(self.list.items, format, args) catch unreachable).len == size);
|
2018-07-16 17:52:50 -07:00
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2018-05-31 07:56:59 -07:00
|
|
|
pub fn deinit(self: *Buffer) void {
|
2017-04-20 22:56:12 -07:00
|
|
|
self.list.deinit();
|
|
|
|
}
|
|
|
|
|
2020-03-06 15:01:20 -08:00
|
|
|
pub fn span(self: var) @TypeOf(self.list.items[0 .. self.list.len - 1 :0]) {
|
|
|
|
return self.list.span()[0..self.len() :0];
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Deprecated: use `span`
|
2019-11-24 18:12:01 -08:00
|
|
|
pub fn toSlice(self: Buffer) [:0]u8 {
|
2020-03-06 15:01:20 -08:00
|
|
|
return self.span();
|
2017-04-20 22:56:12 -07:00
|
|
|
}
|
|
|
|
|
2020-03-06 15:01:20 -08:00
|
|
|
/// Deprecated: use `span`
|
2019-11-24 18:12:01 -08:00
|
|
|
pub fn toSliceConst(self: Buffer) [:0]const u8 {
|
2020-03-06 15:01:20 -08:00
|
|
|
return self.span();
|
2017-04-20 22:56:12 -07:00
|
|
|
}
|
|
|
|
|
2018-05-31 07:56:59 -07:00
|
|
|
pub fn shrink(self: *Buffer, new_len: usize) void {
|
2017-10-31 01:47:55 -07:00
|
|
|
assert(new_len <= self.len());
|
|
|
|
self.list.shrink(new_len + 1);
|
|
|
|
self.list.items[self.len()] = 0;
|
|
|
|
}
|
|
|
|
|
2018-05-31 07:56:59 -07:00
|
|
|
pub fn resize(self: *Buffer, new_len: usize) !void {
|
2018-01-07 13:51:46 -08:00
|
|
|
try self.list.resize(new_len + 1);
|
2017-04-20 22:56:12 -07:00
|
|
|
self.list.items[self.len()] = 0;
|
|
|
|
}
|
|
|
|
|
2019-10-28 23:19:22 -07:00
|
|
|
pub fn isNull(self: Buffer) bool {
|
2017-04-20 22:56:12 -07:00
|
|
|
return self.list.len == 0;
|
|
|
|
}
|
|
|
|
|
2019-10-28 23:19:22 -07:00
|
|
|
pub fn len(self: Buffer) usize {
|
2017-04-20 22:56:12 -07:00
|
|
|
return self.list.len - 1;
|
|
|
|
}
|
2019-12-08 19:53:51 -08:00
|
|
|
|
2019-11-23 20:08:33 -08:00
|
|
|
pub fn capacity(self: Buffer) usize {
|
|
|
|
return if (self.list.items.len > 0)
|
|
|
|
self.list.items.len - 1
|
|
|
|
else
|
|
|
|
0;
|
|
|
|
}
|
2017-04-20 22:56:12 -07:00
|
|
|
|
2018-05-31 07:56:59 -07:00
|
|
|
pub fn append(self: *Buffer, m: []const u8) !void {
|
2017-04-20 22:56:12 -07:00
|
|
|
const old_len = self.len();
|
2018-01-07 13:51:46 -08:00
|
|
|
try self.resize(old_len + m.len);
|
2017-05-19 07:39:59 -07:00
|
|
|
mem.copy(u8, self.list.toSlice()[old_len..], m);
|
2017-04-20 22:56:12 -07:00
|
|
|
}
|
|
|
|
|
2018-05-31 07:56:59 -07:00
|
|
|
pub fn appendByte(self: *Buffer, byte: u8) !void {
|
2018-05-11 11:08:16 -07:00
|
|
|
const old_len = self.len();
|
|
|
|
try self.resize(old_len + 1);
|
|
|
|
self.list.toSlice()[old_len] = byte;
|
2017-04-20 22:56:12 -07:00
|
|
|
}
|
|
|
|
|
2019-10-28 23:19:22 -07:00
|
|
|
pub fn eql(self: Buffer, m: []const u8) bool {
|
2017-12-21 21:50:30 -08:00
|
|
|
return mem.eql(u8, self.toSliceConst(), m);
|
2017-04-20 22:56:12 -07:00
|
|
|
}
|
|
|
|
|
2019-10-28 23:19:22 -07:00
|
|
|
pub fn startsWith(self: Buffer, m: []const u8) bool {
|
2017-04-20 22:56:12 -07:00
|
|
|
if (self.len() < m.len) return false;
|
2020-03-06 15:01:20 -08:00
|
|
|
return mem.eql(u8, self.list.items[0..m.len], m);
|
2017-04-20 22:56:12 -07:00
|
|
|
}
|
|
|
|
|
2019-10-28 23:19:22 -07:00
|
|
|
pub fn endsWith(self: Buffer, m: []const u8) bool {
|
2017-04-20 22:56:12 -07:00
|
|
|
const l = self.len();
|
|
|
|
if (l < m.len) return false;
|
|
|
|
const start = l - m.len;
|
2017-09-08 06:19:02 -07:00
|
|
|
return mem.eql(u8, self.list.items[start..l], m);
|
2017-04-20 22:56:12 -07:00
|
|
|
}
|
|
|
|
|
2018-06-06 11:09:47 -07:00
|
|
|
pub fn replaceContents(self: *Buffer, m: []const u8) !void {
|
2018-01-07 13:51:46 -08:00
|
|
|
try self.resize(m.len);
|
2017-04-20 22:56:12 -07:00
|
|
|
mem.copy(u8, self.list.toSlice(), m);
|
|
|
|
}
|
2020-02-04 10:16:20 -08:00
|
|
|
|
2020-03-10 12:27:45 -07:00
|
|
|
pub fn outStream(self: *Buffer) std.io.OutStream(*Buffer, 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.
|
|
|
|
pub fn appendWrite(self: *Buffer, m: []const u8) !usize {
|
|
|
|
try self.append(m);
|
|
|
|
return m.len;
|
|
|
|
}
|
2017-04-20 22:56:12 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
test "simple Buffer" {
|
2020-01-29 19:22:01 -08:00
|
|
|
var buf = try Buffer.init(testing.allocator, "");
|
2020-01-29 11:18:04 -08:00
|
|
|
defer buf.deinit();
|
|
|
|
|
2019-02-08 15:18:47 -08:00
|
|
|
testing.expect(buf.len() == 0);
|
2018-01-08 21:07:01 -08:00
|
|
|
try buf.append("hello");
|
2018-05-11 11:08:16 -07:00
|
|
|
try buf.append(" ");
|
2018-01-08 21:07:01 -08:00
|
|
|
try buf.append("world");
|
2019-02-08 15:18:47 -08:00
|
|
|
testing.expect(buf.eql("hello world"));
|
2019-05-26 10:17:34 -07:00
|
|
|
testing.expect(mem.eql(u8, mem.toSliceConst(u8, buf.toSliceConst().ptr), buf.toSliceConst()));
|
2017-04-20 22:56:12 -07:00
|
|
|
|
2018-10-15 15:23:47 -07:00
|
|
|
var buf2 = try Buffer.initFromBuffer(buf);
|
2020-01-29 11:18:04 -08:00
|
|
|
defer buf2.deinit();
|
2019-02-08 15:18:47 -08:00
|
|
|
testing.expect(buf.eql(buf2.toSliceConst()));
|
2017-04-20 22:56:12 -07:00
|
|
|
|
2019-02-08 15:18:47 -08:00
|
|
|
testing.expect(buf.startsWith("hell"));
|
|
|
|
testing.expect(buf.endsWith("orld"));
|
2017-04-20 22:56:12 -07:00
|
|
|
|
2018-01-08 21:07:01 -08:00
|
|
|
try buf2.resize(4);
|
2019-02-08 15:18:47 -08:00
|
|
|
testing.expect(buf.startsWith(buf2.toSlice()));
|
2017-04-20 22:56:12 -07:00
|
|
|
}
|
2019-11-23 20:08:33 -08:00
|
|
|
|
|
|
|
test "Buffer.initSize" {
|
2020-01-29 19:22:01 -08:00
|
|
|
var buf = try Buffer.initSize(testing.allocator, 3);
|
2020-01-29 11:18:04 -08:00
|
|
|
defer buf.deinit();
|
2019-11-23 20:08:33 -08:00
|
|
|
testing.expect(buf.len() == 3);
|
|
|
|
try buf.append("hello");
|
|
|
|
testing.expect(mem.eql(u8, buf.toSliceConst()[3..], "hello"));
|
|
|
|
}
|
|
|
|
|
|
|
|
test "Buffer.initCapacity" {
|
2020-01-29 19:22:01 -08:00
|
|
|
var buf = try Buffer.initCapacity(testing.allocator, 10);
|
2020-01-29 11:18:04 -08:00
|
|
|
defer buf.deinit();
|
2019-11-23 20:08:33 -08:00
|
|
|
testing.expect(buf.len() == 0);
|
|
|
|
testing.expect(buf.capacity() >= 10);
|
|
|
|
const old_cap = buf.capacity();
|
|
|
|
try buf.append("hello");
|
|
|
|
testing.expect(buf.len() == 5);
|
|
|
|
testing.expect(buf.capacity() == old_cap);
|
|
|
|
testing.expect(mem.eql(u8, buf.toSliceConst(), "hello"));
|
|
|
|
}
|
2020-02-04 10:16:20 -08:00
|
|
|
|
|
|
|
test "Buffer.print" {
|
|
|
|
var buf = try Buffer.init(testing.allocator, "");
|
|
|
|
defer buf.deinit();
|
|
|
|
|
2020-03-13 08:55:50 -07:00
|
|
|
try buf.outStream().print("Hello {} the {}", .{ 2, "world" });
|
2020-02-04 10:16:20 -08:00
|
|
|
testing.expect(buf.eql("Hello 2 the world"));
|
|
|
|
}
|
2020-03-10 15:44:30 -07:00
|
|
|
|
|
|
|
test "Buffer.outStream" {
|
|
|
|
var buffer = try Buffer.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.toSlice(), "x: 42\ny: 1234\n"));
|
|
|
|
}
|