2017-12-24 11:08:53 +08:00
|
|
|
const std = @import("index.zig");
|
|
|
|
const debug = std.debug;
|
|
|
|
const mem = std.mem;
|
2017-04-21 13:56:12 +08:00
|
|
|
const Allocator = mem.Allocator;
|
|
|
|
const assert = debug.assert;
|
2017-12-24 11:08:53 +08:00
|
|
|
const ArrayList = std.ArrayList;
|
2017-04-21 13:56:12 +08:00
|
|
|
|
2017-12-24 11:08:53 +08:00
|
|
|
const fmt = std.fmt;
|
2017-11-30 10:31:09 +08:00
|
|
|
|
2017-04-21 13:56:12 +08:00
|
|
|
/// A buffer that allocates memory and maintains a null byte at the end.
|
|
|
|
pub const Buffer = struct {
|
2017-05-05 02:05:06 +08:00
|
|
|
list: ArrayList(u8),
|
2017-04-21 13:56:12 +08:00
|
|
|
|
|
|
|
/// Must deinitialize with deinit.
|
2018-02-01 11:48:40 +08:00
|
|
|
pub fn init(allocator: &Allocator, m: []const u8) !Buffer {
|
2018-01-08 05:51:46 +08:00
|
|
|
var self = try initSize(allocator, m.len);
|
2017-04-21 13:56:12 +08:00
|
|
|
mem.copy(u8, self.list.items, m);
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Must deinitialize with deinit.
|
2018-02-01 11:48:40 +08:00
|
|
|
pub fn initSize(allocator: &Allocator, size: usize) !Buffer {
|
2017-04-21 13:56:12 +08:00
|
|
|
var self = initNull(allocator);
|
2018-01-08 05:51:46 +08:00
|
|
|
try self.resize(size);
|
2017-04-21 13:56:12 +08:00
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Must deinitialize with deinit.
|
|
|
|
/// None of the other operations are valid until you do one of these:
|
|
|
|
/// * ::replaceContents
|
|
|
|
/// * ::replaceContentsBuffer
|
|
|
|
/// * ::resize
|
2018-01-25 17:10:11 +08:00
|
|
|
pub fn initNull(allocator: &Allocator) Buffer {
|
2017-12-22 13:50:30 +08:00
|
|
|
return Buffer {
|
2017-05-05 02:05:06 +08:00
|
|
|
.list = ArrayList(u8).init(allocator),
|
2017-12-22 13:50:30 +08:00
|
|
|
};
|
2017-04-21 13:56:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Must deinitialize with deinit.
|
2018-02-01 11:48:40 +08:00
|
|
|
pub fn initFromBuffer(buffer: &const Buffer) !Buffer {
|
2017-04-21 13:56:12 +08:00
|
|
|
return Buffer.init(buffer.list.allocator, buffer.toSliceConst());
|
|
|
|
}
|
|
|
|
|
2017-10-11 22:16:13 +08:00
|
|
|
/// Buffer takes ownership of the passed in slice. The slice must have been
|
|
|
|
/// allocated with `allocator`.
|
|
|
|
/// Must deinitialize with deinit.
|
2018-01-25 17:10:11 +08:00
|
|
|
pub fn fromOwnedSlice(allocator: &Allocator, slice: []u8) Buffer {
|
2017-10-11 22:16:13 +08:00
|
|
|
var self = Buffer {
|
|
|
|
.list = ArrayList(u8).fromOwnedSlice(allocator, slice),
|
|
|
|
};
|
|
|
|
self.list.append(0);
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The caller owns the returned memory. The Buffer becomes null and
|
|
|
|
/// is safe to `deinit`.
|
2018-01-25 17:10:11 +08:00
|
|
|
pub fn toOwnedSlice(self: &Buffer) []u8 {
|
2017-10-11 22:16:13 +08:00
|
|
|
const allocator = self.list.allocator;
|
|
|
|
const result = allocator.shrink(u8, self.list.items, self.len());
|
|
|
|
*self = initNull(allocator);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-01-25 17:10:11 +08:00
|
|
|
pub fn deinit(self: &Buffer) void {
|
2017-04-21 13:56:12 +08:00
|
|
|
self.list.deinit();
|
|
|
|
}
|
|
|
|
|
2018-01-25 17:10:11 +08:00
|
|
|
pub fn toSlice(self: &Buffer) []u8 {
|
2017-05-19 22:39:59 +08:00
|
|
|
return self.list.toSlice()[0..self.len()];
|
2017-04-21 13:56:12 +08:00
|
|
|
}
|
|
|
|
|
2018-01-25 17:10:11 +08:00
|
|
|
pub fn toSliceConst(self: &const Buffer) []const u8 {
|
2017-05-19 22:39:59 +08:00
|
|
|
return self.list.toSliceConst()[0..self.len()];
|
2017-04-21 13:56:12 +08:00
|
|
|
}
|
|
|
|
|
2018-01-25 17:10:11 +08:00
|
|
|
pub fn shrink(self: &Buffer, new_len: usize) void {
|
2017-10-31 16:47:55 +08:00
|
|
|
assert(new_len <= self.len());
|
|
|
|
self.list.shrink(new_len + 1);
|
|
|
|
self.list.items[self.len()] = 0;
|
|
|
|
}
|
|
|
|
|
2018-02-01 11:48:40 +08:00
|
|
|
pub fn resize(self: &Buffer, new_len: usize) !void {
|
2018-01-08 05:51:46 +08:00
|
|
|
try self.list.resize(new_len + 1);
|
2017-04-21 13:56:12 +08:00
|
|
|
self.list.items[self.len()] = 0;
|
|
|
|
}
|
|
|
|
|
2018-01-25 17:10:11 +08:00
|
|
|
pub fn isNull(self: &const Buffer) bool {
|
2017-04-21 13:56:12 +08:00
|
|
|
return self.list.len == 0;
|
|
|
|
}
|
|
|
|
|
2018-01-25 17:10:11 +08:00
|
|
|
pub fn len(self: &const Buffer) usize {
|
2017-04-21 13:56:12 +08:00
|
|
|
return self.list.len - 1;
|
|
|
|
}
|
|
|
|
|
2018-02-01 11:48:40 +08:00
|
|
|
pub fn append(self: &Buffer, m: []const u8) !void {
|
2017-04-21 13:56:12 +08:00
|
|
|
const old_len = self.len();
|
2018-01-08 05:51:46 +08:00
|
|
|
try self.resize(old_len + m.len);
|
2017-05-19 22:39:59 +08:00
|
|
|
mem.copy(u8, self.list.toSlice()[old_len..], m);
|
2017-04-21 13:56:12 +08:00
|
|
|
}
|
|
|
|
|
2017-12-11 10:26:28 +08:00
|
|
|
// TODO: remove, use OutStream for this
|
2018-02-01 11:48:40 +08:00
|
|
|
pub fn appendFormat(self: &Buffer, comptime format: []const u8, args: ...) !void {
|
2017-11-30 10:31:09 +08:00
|
|
|
return fmt.format(self, append, format, args);
|
|
|
|
}
|
|
|
|
|
2017-12-11 10:26:28 +08:00
|
|
|
// TODO: remove, use OutStream for this
|
2018-02-01 11:48:40 +08:00
|
|
|
pub fn appendByte(self: &Buffer, byte: u8) !void {
|
2017-10-13 21:31:03 +08:00
|
|
|
return self.appendByteNTimes(byte, 1);
|
|
|
|
}
|
|
|
|
|
2017-12-11 10:26:28 +08:00
|
|
|
// TODO: remove, use OutStream for this
|
2018-02-01 11:48:40 +08:00
|
|
|
pub fn appendByteNTimes(self: &Buffer, byte: u8, count: usize) !void {
|
2017-10-13 21:31:03 +08:00
|
|
|
var prev_size: usize = self.len();
|
2018-01-03 17:55:16 +08:00
|
|
|
const new_size = prev_size + count;
|
2018-01-08 05:51:46 +08:00
|
|
|
try self.resize(new_size);
|
2017-10-13 21:31:03 +08:00
|
|
|
|
2018-01-03 17:55:16 +08:00
|
|
|
var i: usize = prev_size;
|
|
|
|
while (i < new_size) : (i += 1) {
|
|
|
|
self.list.items[i] = byte;
|
2017-10-13 21:31:03 +08:00
|
|
|
}
|
2017-04-21 13:56:12 +08:00
|
|
|
}
|
|
|
|
|
2018-01-25 17:10:11 +08:00
|
|
|
pub fn eql(self: &const Buffer, m: []const u8) bool {
|
2017-12-22 13:50:30 +08:00
|
|
|
return mem.eql(u8, self.toSliceConst(), m);
|
2017-04-21 13:56:12 +08:00
|
|
|
}
|
|
|
|
|
2018-01-25 17:10:11 +08:00
|
|
|
pub fn startsWith(self: &const Buffer, m: []const u8) bool {
|
2017-04-21 13:56:12 +08:00
|
|
|
if (self.len() < m.len) return false;
|
2017-05-19 22:39:59 +08:00
|
|
|
return mem.eql(u8, self.list.items[0..m.len], m);
|
2017-04-21 13:56:12 +08:00
|
|
|
}
|
|
|
|
|
2018-01-25 17:10:11 +08:00
|
|
|
pub fn endsWith(self: &const Buffer, m: []const u8) bool {
|
2017-04-21 13:56:12 +08:00
|
|
|
const l = self.len();
|
|
|
|
if (l < m.len) return false;
|
|
|
|
const start = l - m.len;
|
2017-09-08 21:19:02 +08:00
|
|
|
return mem.eql(u8, self.list.items[start..l], m);
|
2017-04-21 13:56:12 +08:00
|
|
|
}
|
|
|
|
|
2018-02-01 11:48:40 +08:00
|
|
|
pub fn replaceContents(self: &const Buffer, m: []const u8) !void {
|
2018-01-08 05:51:46 +08:00
|
|
|
try self.resize(m.len);
|
2017-04-21 13:56:12 +08:00
|
|
|
mem.copy(u8, self.list.toSlice(), m);
|
|
|
|
}
|
2017-12-23 13:29:39 +08:00
|
|
|
|
|
|
|
/// For passing to C functions.
|
2018-01-25 17:10:11 +08:00
|
|
|
pub fn ptr(self: &const Buffer) &u8 {
|
2017-12-23 13:29:39 +08:00
|
|
|
return self.list.items.ptr;
|
|
|
|
}
|
2017-04-21 13:56:12 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
test "simple Buffer" {
|
|
|
|
const cstr = @import("cstr.zig");
|
|
|
|
|
2018-01-09 13:07:01 +08:00
|
|
|
var buf = try Buffer.init(debug.global_allocator, "");
|
2017-04-21 13:56:12 +08:00
|
|
|
assert(buf.len() == 0);
|
2018-01-09 13:07:01 +08:00
|
|
|
try buf.append("hello");
|
|
|
|
try buf.appendByte(' ');
|
|
|
|
try buf.append("world");
|
2017-04-21 13:56:12 +08:00
|
|
|
assert(buf.eql("hello world"));
|
|
|
|
assert(mem.eql(u8, cstr.toSliceConst(buf.toSliceConst().ptr), buf.toSliceConst()));
|
|
|
|
|
2018-01-09 13:07:01 +08:00
|
|
|
var buf2 = try Buffer.initFromBuffer(&buf);
|
2017-04-21 13:56:12 +08:00
|
|
|
assert(buf.eql(buf2.toSliceConst()));
|
|
|
|
|
|
|
|
assert(buf.startsWith("hell"));
|
2017-09-08 21:19:02 +08:00
|
|
|
assert(buf.endsWith("orld"));
|
2017-04-21 13:56:12 +08:00
|
|
|
|
2018-01-09 13:07:01 +08:00
|
|
|
try buf2.resize(4);
|
2017-04-21 13:56:12 +08:00
|
|
|
assert(buf.startsWith(buf2.toSliceConst()));
|
|
|
|
}
|