aboutsummaryrefslogtreecommitdiff
path: root/std/buffer.zig
blob: c7f086135615606caa99902e46c60d76a1aa3206 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
const debug = @import("debug.zig");
const mem = @import("mem.zig");
const Allocator = mem.Allocator;
const assert = debug.assert;
const List = @import("list.zig").List;

/// A buffer that allocates memory and maintains a null byte at the end.
pub const Buffer = struct {
    list: List(u8),

    /// Must deinitialize with deinit.
    pub fn init(allocator: &Allocator, m: []const u8) -> %Buffer {
        var self = %return initSize(allocator, m.len);
        mem.copy(u8, self.list.items, m);
        return self;
    }

    /// Must deinitialize with deinit.
    pub fn initSize(allocator: &Allocator, size: usize) -> %Buffer {
        var self = initNull(allocator);
        %return self.resize(size);
        return self;
    }

    /// Must deinitialize with deinit.
    /// None of the other operations are valid until you do one of these:
    /// * ::replaceContents
    /// * ::replaceContentsBuffer
    /// * ::resize
    pub fn initNull(allocator: &Allocator) -> Buffer {
        Buffer {
            .list = List(u8).init(allocator),
        }
    }

    /// Must deinitialize with deinit.
    pub fn initFromBuffer(buffer: &const Buffer) -> %Buffer {
        return Buffer.init(buffer.list.allocator, buffer.toSliceConst());
    }

    pub fn deinit(self: &Buffer) {
        self.list.deinit();
    }

    pub fn toSlice(self: &Buffer) -> []u8 {
        return self.list.toSlice()[0...self.len()];
    }

    pub fn toSliceConst(self: &const Buffer) -> []const u8 {
        return self.list.toSliceConst()[0...self.len()];
    }

    pub fn resize(self: &Buffer, new_len: usize) -> %void {
        %return self.list.resize(new_len + 1);
        self.list.items[self.len()] = 0;
    }

    pub fn isNull(self: &const Buffer) -> bool {
        return self.list.len == 0;
    }

    pub fn len(self: &const Buffer) -> usize {
        return self.list.len - 1;
    }

    pub fn append(self: &Buffer, m: []const u8) -> %void {
        const old_len = self.len();
        %return self.resize(old_len + m.len);
        mem.copy(u8, self.list.toSlice()[old_len...], m);
    }

    pub fn appendByte(self: &Buffer, byte: u8) -> %void {
        %return self.resize(self.len() + 1);
        self.list.items[self.len() - 1] = byte;
    }

    pub fn eql(self: &const Buffer, m: []const u8) -> bool {
        mem.eql(u8, self.toSliceConst(), m)
    }

    pub fn startsWith(self: &const Buffer, m: []const u8) -> bool {
        if (self.len() < m.len) return false;
        return mem.eql(u8, self.list.items[0...m.len], m);
    }

    pub fn endsWith(self: &const Buffer, m: []const u8) -> bool {
        const l = self.len();
        if (l < m.len) return false;
        const start = l - m.len;
        return mem.eql(u8, self.list.items[start...], m);
    }

    pub fn replaceContents(self: &const Buffer, m: []const u8) -> %void {
        %return self.resize(m.len);
        mem.copy(u8, self.list.toSlice(), m);
    }
};

test "simple Buffer" {
    const cstr = @import("cstr.zig");

    var buf = %%Buffer.init(&debug.global_allocator, "");
    assert(buf.len() == 0);
    %%buf.append("hello");
    %%buf.appendByte(' ');
    %%buf.append("world");
    assert(buf.eql("hello world"));
    assert(mem.eql(u8, cstr.toSliceConst(buf.toSliceConst().ptr), buf.toSliceConst()));

    var buf2 = %%Buffer.initFromBuffer(&buf);
    assert(buf.eql(buf2.toSliceConst()));

    assert(buf.startsWith("hell"));

    %%buf2.resize(4);
    assert(buf.startsWith(buf2.toSliceConst()));
}