Commit 2e806682f4

Andrew Kelley <andrew@ziglang.org>
2020-04-01 18:44:45
(breaking) std.Buffer => std.ArrayListSentineled(u8, 0)
This new name (and the fact that it is a function returning a type) will make it more clear which use cases are better suited for ArrayList and which are better suited for ArrayListSentineled. Also for consistency with ArrayList, * `append` => `appendSlice` * `appendByte` => `append` Thanks daurnimator for pointing out the confusion of std.Buffer.
1 parent 553f0e0
lib/std/io/in_stream.zig
@@ -3,7 +3,6 @@ const builtin = std.builtin;
 const math = std.math;
 const assert = std.debug.assert;
 const mem = std.mem;
-const Buffer = std.Buffer;
 const testing = std.testing;
 
 pub fn InStream(
lib/std/array_list_sentineled.zig
@@ -0,0 +1,224 @@
+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: var) !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: var) @TypeOf(self.list.items[0 .. self.list.len - 1 :sentinel]) {
+            return self.list.span()[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.len == 0;
+        }
+
+        pub fn len(self: Self) usize {
+            return self.list.len - 1;
+        }
+
+        pub fn capacity(self: Self) usize {
+            return if (self.list.items.len > 0)
+                self.list.items.len - 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.span()[old_len..], m);
+        }
+
+        pub fn append(self: *Self, byte: T) !void {
+            const old_len = self.len();
+            try self.resize(old_len + 1);
+            self.list.span()[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.span(), 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"));
+}
lib/std/buffer.zig
@@ -1,218 +0,0 @@
-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 buffer that allocates memory and maintains a null byte at the end.
-pub const Buffer = struct {
-    list: ArrayList(u8),
-
-    /// Must deinitialize with deinit.
-    pub fn init(allocator: *Allocator, m: []const u8) !Buffer {
-        var self = try initSize(allocator, m.len);
-        mem.copy(u8, 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) !Buffer {
-        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) !Buffer {
-        var self = Buffer{ .list = try ArrayList(u8).initCapacity(allocator, num + 1) };
-        self.list.appendAssumeCapacity(0);
-        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) Buffer {
-        return Buffer{ .list = ArrayList(u8).init(allocator) };
-    }
-
-    /// Must deinitialize with deinit.
-    pub fn initFromBuffer(buffer: Buffer) !Buffer {
-        return Buffer.init(buffer.list.allocator, buffer.span());
-    }
-
-    /// Buffer 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: []u8) !Buffer {
-        var self = Buffer{ .list = ArrayList(u8).fromOwnedSlice(allocator, slice) };
-        try self.list.append(0);
-        return self;
-    }
-
-    /// The caller owns the returned memory. The Buffer becomes null and
-    /// is safe to `deinit`.
-    pub fn toOwnedSlice(self: *Buffer) [:0]u8 {
-        const allocator = self.list.allocator;
-        const result = self.list.toOwnedSlice();
-        self.* = initNull(allocator);
-        return result[0 .. result.len - 1 :0];
-    }
-
-    pub fn allocPrint(allocator: *Allocator, comptime format: []const u8, args: var) !Buffer {
-        const size = std.math.cast(usize, std.fmt.count(format, args)) catch |err| switch (err) {
-            error.Overflow => return error.OutOfMemory,
-        };
-        var self = try Buffer.initSize(allocator, size);
-        assert((std.fmt.bufPrint(self.list.items, format, args) catch unreachable).len == size);
-        return self;
-    }
-
-    pub fn deinit(self: *Buffer) void {
-        self.list.deinit();
-    }
-
-    pub fn span(self: var) @TypeOf(self.list.items[0 .. self.list.len - 1 :0]) {
-        return self.list.span()[0..self.len() :0];
-    }
-
-    pub const toSlice = @compileError("deprecated; use span()");
-    pub const toSliceConst = @compileError("deprecated; use span()");
-
-    pub fn shrink(self: *Buffer, new_len: usize) void {
-        assert(new_len <= self.len());
-        self.list.shrink(new_len + 1);
-        self.list.items[self.len()] = 0;
-    }
-
-    pub fn resize(self: *Buffer, new_len: usize) !void {
-        try self.list.resize(new_len + 1);
-        self.list.items[self.len()] = 0;
-    }
-
-    pub fn isNull(self: Buffer) bool {
-        return self.list.len == 0;
-    }
-
-    pub fn len(self: Buffer) usize {
-        return self.list.len - 1;
-    }
-
-    pub fn capacity(self: Buffer) usize {
-        return if (self.list.items.len > 0)
-            self.list.items.len - 1
-        else
-            0;
-    }
-
-    pub fn append(self: *Buffer, m: []const u8) !void {
-        const old_len = self.len();
-        try self.resize(old_len + m.len);
-        mem.copy(u8, self.list.span()[old_len..], m);
-    }
-
-    pub fn appendByte(self: *Buffer, byte: u8) !void {
-        const old_len = self.len();
-        try self.resize(old_len + 1);
-        self.list.span()[old_len] = byte;
-    }
-
-    pub fn eql(self: Buffer, m: []const u8) bool {
-        return mem.eql(u8, self.span(), m);
-    }
-
-    pub fn startsWith(self: 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: 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..l], m);
-    }
-
-    pub fn replaceContents(self: *Buffer, m: []const u8) !void {
-        try self.resize(m.len);
-        mem.copy(u8, self.list.span(), m);
-    }
-
-    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;
-    }
-};
-
-test "simple Buffer" {
-    var buf = try Buffer.init(testing.allocator, "");
-    defer buf.deinit();
-
-    testing.expect(buf.len() == 0);
-    try buf.append("hello");
-    try buf.append(" ");
-    try buf.append("world");
-    testing.expect(buf.eql("hello world"));
-    testing.expect(mem.eql(u8, mem.spanZ(buf.span().ptr), buf.span()));
-
-    var buf2 = try Buffer.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 "Buffer.initSize" {
-    var buf = try Buffer.initSize(testing.allocator, 3);
-    defer buf.deinit();
-    testing.expect(buf.len() == 3);
-    try buf.append("hello");
-    testing.expect(mem.eql(u8, buf.span()[3..], "hello"));
-}
-
-test "Buffer.initCapacity" {
-    var buf = try Buffer.initCapacity(testing.allocator, 10);
-    defer buf.deinit();
-    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.span(), "hello"));
-}
-
-test "Buffer.print" {
-    var buf = try Buffer.init(testing.allocator, "");
-    defer buf.deinit();
-
-    try buf.outStream().print("Hello {} the {}", .{ 2, "world" });
-    testing.expect(buf.eql("Hello 2 the world"));
-}
-
-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.span(), "x: 42\ny: 1234\n"));
-}
lib/std/child_process.zig
@@ -10,7 +10,7 @@ const windows = os.windows;
 const mem = std.mem;
 const debug = std.debug;
 const BufMap = std.BufMap;
-const Buffer = std.Buffer;
+const ArrayListSentineled = std.ArrayListSentineled;
 const builtin = @import("builtin");
 const Os = builtin.Os;
 const TailQueue = std.TailQueue;
@@ -758,7 +758,7 @@ 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 Buffer.initSize(allocator, 0);
+    var buf = try ArrayListSentineled(u8, 0).initSize(allocator, 0);
     defer buf.deinit();
     const buf_stream = buf.outStream();
 
lib/std/fs.zig
@@ -1416,13 +1416,14 @@ pub const readLinkC = @compileError("deprecated; use Dir.readLinkZ or readLinkAb
 
 pub const Walker = struct {
     stack: std.ArrayList(StackItem),
-    name_buffer: std.Buffer,
+    name_buffer: std.ArrayList(u8),
 
     pub const Entry = struct {
         /// The containing directory. This can be used to operate directly on `basename`
         /// rather than `path`, avoiding `error.NameTooLong` for deeply nested paths.
         /// The directory remains open until `next` or `deinit` is called.
         dir: Dir,
+        /// TODO make this null terminated for API convenience
         basename: []const u8,
 
         path: []const u8,
@@ -1445,8 +1446,8 @@ pub const Walker = struct {
             const dirname_len = top.dirname_len;
             if (try top.dir_it.next()) |base| {
                 self.name_buffer.shrink(dirname_len);
-                try self.name_buffer.appendByte(path.sep);
-                try self.name_buffer.append(base.name);
+                try self.name_buffer.append(path.sep);
+                try self.name_buffer.appendSlice(base.name);
                 if (base.kind == .Directory) {
                     var new_dir = top.dir_it.dir.openDir(base.name, .{ .iterate = true }) catch |err| switch (err) {
                         error.NameTooLong => unreachable, // no path sep in base.name
@@ -1456,7 +1457,7 @@ pub const Walker = struct {
                         errdefer new_dir.close();
                         try self.stack.append(StackItem{
                             .dir_it = new_dir.iterate(),
-                            .dirname_len = self.name_buffer.len(),
+                            .dirname_len = self.name_buffer.len,
                         });
                     }
                 }
@@ -1489,9 +1490,11 @@ pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {
     var dir = try cwd().openDir(dir_path, .{ .iterate = true });
     errdefer dir.close();
 
-    var name_buffer = try std.Buffer.init(allocator, dir_path);
+    var name_buffer = std.ArrayList(u8).init(allocator);
     errdefer name_buffer.deinit();
 
+    try name_buffer.appendSlice(dir_path);
+
     var walker = Walker{
         .stack = std.ArrayList(Walker.StackItem).init(allocator),
         .name_buffer = name_buffer,
lib/std/net.zig
@@ -504,7 +504,7 @@ 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.Buffer.initNull(arena);
+        var canon = std.ArrayListSentineled(u8, 0).initNull(arena);
         defer canon.deinit();
 
         try linuxLookupName(&lookup_addrs, &canon, name, family, flags, port);
@@ -539,7 +539,7 @@ const DAS_ORDER_SHIFT = 0;
 
 fn linuxLookupName(
     addrs: *std.ArrayList(LookupAddr),
-    canon: *std.Buffer,
+    canon: *std.ArrayListSentineled(u8, 0),
     opt_name: ?[]const u8,
     family: os.sa_family_t,
     flags: u32,
@@ -798,7 +798,7 @@ fn linuxLookupNameFromNull(
 
 fn linuxLookupNameFromHosts(
     addrs: *std.ArrayList(LookupAddr),
-    canon: *std.Buffer,
+    canon: *std.ArrayListSentineled(u8, 0),
     name: []const u8,
     family: os.sa_family_t,
     port: u16,
@@ -868,7 +868,7 @@ pub fn isValidHostName(hostname: []const u8) bool {
 
 fn linuxLookupNameFromDnsSearch(
     addrs: *std.ArrayList(LookupAddr),
-    canon: *std.Buffer,
+    canon: *std.ArrayListSentineled(u8, 0),
     name: []const u8,
     family: os.sa_family_t,
     port: u16,
@@ -901,12 +901,12 @@ fn linuxLookupNameFromDnsSearch(
     // the full requested name to name_from_dns.
     try canon.resize(canon_name.len);
     mem.copy(u8, canon.span(), canon_name);
-    try canon.appendByte('.');
+    try canon.append('.');
 
     var tok_it = mem.tokenize(search, " \t");
     while (tok_it.next()) |tok| {
         canon.shrink(canon_name.len + 1);
-        try canon.append(tok);
+        try canon.appendSlice(tok);
         try linuxLookupNameFromDns(addrs, canon, canon.span(), family, rc, port);
         if (addrs.len != 0) return;
     }
@@ -917,13 +917,13 @@ fn linuxLookupNameFromDnsSearch(
 
 const dpc_ctx = struct {
     addrs: *std.ArrayList(LookupAddr),
-    canon: *std.Buffer,
+    canon: *std.ArrayListSentineled(u8, 0),
     port: u16,
 };
 
 fn linuxLookupNameFromDns(
     addrs: *std.ArrayList(LookupAddr),
-    canon: *std.Buffer,
+    canon: *std.ArrayListSentineled(u8, 0),
     name: []const u8,
     family: os.sa_family_t,
     rc: ResolvConf,
@@ -978,7 +978,7 @@ const ResolvConf = struct {
     attempts: u32,
     ndots: u32,
     timeout: u32,
-    search: std.Buffer,
+    search: std.ArrayListSentineled(u8, 0),
     ns: std.ArrayList(LookupAddr),
 
     fn deinit(rc: *ResolvConf) void {
@@ -993,7 +993,7 @@ const ResolvConf = struct {
 fn getResolvConf(allocator: *mem.Allocator, rc: *ResolvConf) !void {
     rc.* = ResolvConf{
         .ns = std.ArrayList(LookupAddr).init(allocator),
-        .search = std.Buffer.initNull(allocator),
+        .search = std.ArrayListSentineled(u8, 0).initNull(allocator),
         .ndots = 1,
         .timeout = 5,
         .attempts = 2,
lib/std/std.zig
@@ -1,10 +1,10 @@
 pub const AlignedArrayList = @import("array_list.zig").AlignedArrayList;
 pub const ArrayList = @import("array_list.zig").ArrayList;
+pub const ArrayListSentineled = @import("array_list_sentineled.zig").ArrayListSentineled;
 pub const AutoHashMap = @import("hash_map.zig").AutoHashMap;
 pub const BloomFilter = @import("bloom_filter.zig").BloomFilter;
 pub const BufMap = @import("buf_map.zig").BufMap;
 pub const BufSet = @import("buf_set.zig").BufSet;
-pub const Buffer = @import("buffer.zig").Buffer;
 pub const ChildProcess = @import("child_process.zig").ChildProcess;
 pub const DynLib = @import("dynamic_library.zig").DynLib;
 pub const HashMap = @import("hash_map.zig").HashMap;
src-self-hosted/codegen.zig
@@ -45,7 +45,7 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
 
     // Don't use ZIG_VERSION_STRING here. LLVM misparses it when it includes
     // the git revision.
-    const producer = try std.Buffer.allocPrint(&code.arena.allocator, "zig {}.{}.{}", .{
+    const producer = try std.fmt.allocPrintZ(&code.arena.allocator, "zig {}.{}.{}", .{
         @as(u32, c.ZIG_VERSION_MAJOR),
         @as(u32, c.ZIG_VERSION_MINOR),
         @as(u32, c.ZIG_VERSION_PATCH),
@@ -62,7 +62,7 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
         dibuilder,
         DW.LANG_C99,
         compile_unit_file,
-        producer.span(),
+        producer,
         is_optimized,
         flags,
         runtime_version,
src-self-hosted/compilation.zig
@@ -2,7 +2,7 @@ const std = @import("std");
 const io = std.io;
 const mem = std.mem;
 const Allocator = mem.Allocator;
-const Buffer = std.Buffer;
+const ArrayListSentineled = std.ArrayListSentineled;
 const llvm = @import("llvm.zig");
 const c = @import("c.zig");
 const builtin = std.builtin;
@@ -123,8 +123,8 @@ pub const LlvmHandle = struct {
 
 pub const Compilation = struct {
     zig_compiler: *ZigCompiler,
-    name: Buffer,
-    llvm_triple: Buffer,
+    name: ArrayListSentineled(u8, 0),
+    llvm_triple: ArrayListSentineled(u8, 0),
     root_src_path: ?[]const u8,
     target: std.Target,
     llvm_target: *llvm.Target,
@@ -444,7 +444,7 @@ pub const Compilation = struct {
             comp.arena_allocator.deinit();
         }
 
-        comp.name = try Buffer.init(comp.arena(), name);
+        comp.name = try ArrayListSentineled(u8, 0).init(comp.arena(), name);
         comp.llvm_triple = try util.getLLVMTriple(comp.arena(), target);
         comp.llvm_target = try util.llvmTargetFromTriple(comp.llvm_triple);
         comp.zig_std_dir = try fs.path.join(comp.arena(), &[_][]const u8{ zig_lib_dir, "std" });
@@ -1151,7 +1151,7 @@ pub const Compilation = struct {
 
     /// If the temporary directory for this compilation has not been created, it creates it.
     /// Then it creates a random file name in that dir and returns it.
-    pub fn createRandomOutputPath(self: *Compilation, suffix: []const u8) !Buffer {
+    pub fn createRandomOutputPath(self: *Compilation, suffix: []const u8) !ArrayListSentineled(u8, 0) {
         const tmp_dir = try self.getTmpDir();
         const file_prefix = self.getRandomFileName();
 
@@ -1161,7 +1161,7 @@ pub const Compilation = struct {
         const full_path = try fs.path.join(self.gpa(), &[_][]const u8{ tmp_dir, file_name[0..] });
         errdefer self.gpa().free(full_path);
 
-        return Buffer.fromOwnedSlice(self.gpa(), full_path);
+        return ArrayListSentineled(u8, 0).fromOwnedSlice(self.gpa(), full_path);
     }
 
     /// If the temporary directory for this Compilation has not been created, creates it.
@@ -1279,7 +1279,7 @@ fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
     const fn_type = try analyzeFnType(comp, tree_scope, fn_decl.base.parent_scope, fn_decl.fn_proto);
     defer fn_type.base.base.deref(comp);
 
-    var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name);
+    var symbol_name = try std.ArrayListSentineled(u8, 0).init(comp.gpa(), fn_decl.base.name);
     var symbol_name_consumed = false;
     errdefer if (!symbol_name_consumed) symbol_name.deinit();
 
@@ -1426,7 +1426,7 @@ fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void {
     );
     defer fn_type.base.base.deref(comp);
 
-    var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name);
+    var symbol_name = try std.ArrayListSentineled(u8, 0).init(comp.gpa(), fn_decl.base.name);
     var symbol_name_consumed = false;
     defer if (!symbol_name_consumed) symbol_name.deinit();
 
src-self-hosted/dep_tokenizer.zig
@@ -33,7 +33,7 @@ pub const Tokenizer = struct {
                             break; // advance
                         },
                         else => {
-                            self.state = State{ .target = try std.Buffer.initSize(&self.arena.allocator, 0) };
+                            self.state = State{ .target = try std.ArrayListSentineled(u8, 0).initSize(&self.arena.allocator, 0) };
                         },
                     },
                     .target => |*target| switch (char) {
@@ -53,7 +53,7 @@ pub const Tokenizer = struct {
                             break; // advance
                         },
                         else => {
-                            try target.appendByte(char);
+                            try target.append(char);
                             break; // advance
                         },
                     },
@@ -62,24 +62,24 @@ pub const Tokenizer = struct {
                             return self.errorIllegalChar(self.index, char, "bad target escape", .{});
                         },
                         ' ', '#', '\\' => {
-                            try target.appendByte(char);
+                            try target.append(char);
                             self.state = State{ .target = target.* };
                             break; // advance
                         },
                         '$' => {
-                            try target.append(self.bytes[self.index - 1 .. self.index]);
+                            try target.appendSlice(self.bytes[self.index - 1 .. self.index]);
                             self.state = State{ .target_dollar_sign = target.* };
                             break; // advance
                         },
                         else => {
-                            try target.append(self.bytes[self.index - 1 .. self.index + 1]);
+                            try target.appendSlice(self.bytes[self.index - 1 .. self.index + 1]);
                             self.state = State{ .target = target.* };
                             break; // advance
                         },
                     },
                     .target_dollar_sign => |*target| switch (char) {
                         '$' => {
-                            try target.appendByte(char);
+                            try target.append(char);
                             self.state = State{ .target = target.* };
                             break; // advance
                         },
@@ -125,7 +125,7 @@ pub const Tokenizer = struct {
                             continue;
                         },
                         else => {
-                            try target.append(self.bytes[self.index - 2 .. self.index + 1]);
+                            try target.appendSlice(self.bytes[self.index - 2 .. self.index + 1]);
                             self.state = State{ .target = target.* };
                             break;
                         },
@@ -144,11 +144,11 @@ pub const Tokenizer = struct {
                             break; // advance
                         },
                         '"' => {
-                            self.state = State{ .prereq_quote = try std.Buffer.initSize(&self.arena.allocator, 0) };
+                            self.state = State{ .prereq_quote = try std.ArrayListSentineled(u8, 0).initSize(&self.arena.allocator, 0) };
                             break; // advance
                         },
                         else => {
-                            self.state = State{ .prereq = try std.Buffer.initSize(&self.arena.allocator, 0) };
+                            self.state = State{ .prereq = try std.ArrayListSentineled(u8, 0).initSize(&self.arena.allocator, 0) };
                         },
                     },
                     .rhs_continuation => switch (char) {
@@ -181,7 +181,7 @@ pub const Tokenizer = struct {
                             return Token{ .id = .prereq, .bytes = bytes };
                         },
                         else => {
-                            try prereq.appendByte(char);
+                            try prereq.append(char);
                             break; // advance
                         },
                     },
@@ -201,7 +201,7 @@ pub const Tokenizer = struct {
                             break; // advance
                         },
                         else => {
-                            try prereq.appendByte(char);
+                            try prereq.append(char);
                             break; // advance
                         },
                     },
@@ -218,7 +218,7 @@ pub const Tokenizer = struct {
                         },
                         else => {
                             // not continuation
-                            try prereq.append(self.bytes[self.index - 1 .. self.index + 1]);
+                            try prereq.appendSlice(self.bytes[self.index - 1 .. self.index + 1]);
                             self.state = State{ .prereq = prereq.* };
                             break; // advance
                         },
@@ -300,25 +300,25 @@ pub const Tokenizer = struct {
     }
 
     fn errorf(self: *Tokenizer, comptime fmt: []const u8, args: var) Error {
-        self.error_text = (try std.Buffer.allocPrint(&self.arena.allocator, fmt, args)).span();
+        self.error_text = try std.fmt.allocPrintZ(&self.arena.allocator, fmt, args);
         return Error.InvalidInput;
     }
 
     fn errorPosition(self: *Tokenizer, position: usize, bytes: []const u8, comptime fmt: []const u8, args: var) Error {
-        var buffer = try std.Buffer.initSize(&self.arena.allocator, 0);
+        var buffer = try std.ArrayListSentineled(u8, 0).initSize(&self.arena.allocator, 0);
         try buffer.outStream().print(fmt, args);
-        try buffer.append(" '");
-        var out = makeOutput(std.Buffer.append, &buffer);
+        try buffer.appendSlice(" '");
+        var out = makeOutput(std.ArrayListSentineled(u8, 0).appendSlice, &buffer);
         try printCharValues(&out, bytes);
-        try buffer.append("'");
+        try buffer.appendSlice("'");
         try buffer.outStream().print(" at position {}", .{position - (bytes.len - 1)});
         self.error_text = buffer.span();
         return Error.InvalidInput;
     }
 
     fn errorIllegalChar(self: *Tokenizer, position: usize, char: u8, comptime fmt: []const u8, args: var) Error {
-        var buffer = try std.Buffer.initSize(&self.arena.allocator, 0);
-        try buffer.append("illegal char ");
+        var buffer = try std.ArrayListSentineled(u8, 0).initSize(&self.arena.allocator, 0);
+        try buffer.appendSlice("illegal char ");
         try printUnderstandableChar(&buffer, char);
         try buffer.outStream().print(" at position {}", .{position});
         if (fmt.len != 0) try buffer.outStream().print(": " ++ fmt, args);
@@ -333,18 +333,18 @@ pub const Tokenizer = struct {
 
     const State = union(enum) {
         lhs: void,
-        target: std.Buffer,
-        target_reverse_solidus: std.Buffer,
-        target_dollar_sign: std.Buffer,
-        target_colon: std.Buffer,
-        target_colon_reverse_solidus: std.Buffer,
+        target: std.ArrayListSentineled(u8, 0),
+        target_reverse_solidus: std.ArrayListSentineled(u8, 0),
+        target_dollar_sign: std.ArrayListSentineled(u8, 0),
+        target_colon: std.ArrayListSentineled(u8, 0),
+        target_colon_reverse_solidus: std.ArrayListSentineled(u8, 0),
         rhs: void,
         rhs_continuation: void,
         rhs_continuation_linefeed: void,
-        prereq_quote: std.Buffer,
-        prereq: std.Buffer,
-        prereq_continuation: std.Buffer,
-        prereq_continuation_linefeed: std.Buffer,
+        prereq_quote: std.ArrayListSentineled(u8, 0),
+        prereq: std.ArrayListSentineled(u8, 0),
+        prereq_continuation: std.ArrayListSentineled(u8, 0),
+        prereq_continuation_linefeed: std.ArrayListSentineled(u8, 0),
     };
 
     const Token = struct {
@@ -841,28 +841,28 @@ fn depTokenizer(input: []const u8, expect: []const u8) !void {
     defer arena_allocator.deinit();
 
     var it = Tokenizer.init(arena, input);
-    var buffer = try std.Buffer.initSize(arena, 0);
+    var buffer = try std.ArrayListSentineled(u8, 0).initSize(arena, 0);
     var i: usize = 0;
     while (true) {
         const r = it.next() catch |err| {
             switch (err) {
                 Tokenizer.Error.InvalidInput => {
-                    if (i != 0) try buffer.append("\n");
-                    try buffer.append("ERROR: ");
-                    try buffer.append(it.error_text);
+                    if (i != 0) try buffer.appendSlice("\n");
+                    try buffer.appendSlice("ERROR: ");
+                    try buffer.appendSlice(it.error_text);
                 },
                 else => return err,
             }
             break;
         };
         const token = r orelse break;
-        if (i != 0) try buffer.append("\n");
-        try buffer.append(@tagName(token.id));
-        try buffer.append(" = {");
+        if (i != 0) try buffer.appendSlice("\n");
+        try buffer.appendSlice(@tagName(token.id));
+        try buffer.appendSlice(" = {");
         for (token.bytes) |b| {
-            try buffer.appendByte(printable_char_tab[b]);
+            try buffer.append(printable_char_tab[b]);
         }
-        try buffer.append("}");
+        try buffer.appendSlice("}");
         i += 1;
     }
     const got: []const u8 = buffer.span();
@@ -995,13 +995,13 @@ fn printCharValues(out: var, bytes: []const u8) !void {
     }
 }
 
-fn printUnderstandableChar(buffer: *std.Buffer, char: u8) !void {
+fn printUnderstandableChar(buffer: *std.ArrayListSentineled(u8, 0), char: u8) !void {
     if (!std.ascii.isPrint(char) or char == ' ') {
         try buffer.outStream().print("\\x{X:2}", .{char});
     } else {
-        try buffer.append("'");
-        try buffer.appendByte(printable_char_tab[char]);
-        try buffer.append("'");
+        try buffer.appendSlice("'");
+        try buffer.append(printable_char_tab[char]);
+        try buffer.appendSlice("'");
     }
 }
 
src-self-hosted/link.zig
@@ -15,10 +15,10 @@ const Context = struct {
     link_in_crt: bool,
 
     link_err: error{OutOfMemory}!void,
-    link_msg: std.Buffer,
+    link_msg: std.ArrayListSentineled(u8, 0),
 
     libc: *LibCInstallation,
-    out_file_path: std.Buffer,
+    out_file_path: std.ArrayListSentineled(u8, 0),
 };
 
 pub fn link(comp: *Compilation) !void {
@@ -34,9 +34,9 @@ pub fn link(comp: *Compilation) !void {
     };
     defer ctx.arena.deinit();
     ctx.args = std.ArrayList([*:0]const u8).init(&ctx.arena.allocator);
-    ctx.link_msg = std.Buffer.initNull(&ctx.arena.allocator);
+    ctx.link_msg = std.ArrayListSentineled(u8, 0).initNull(&ctx.arena.allocator);
 
-    ctx.out_file_path = try std.Buffer.init(&ctx.arena.allocator, comp.name.span());
+    ctx.out_file_path = try std.ArrayListSentineled(u8, 0).init(&ctx.arena.allocator, comp.name.span());
     switch (comp.kind) {
         .Exe => {
             try ctx.out_file_path.append(comp.target.exeFileExt());
src-self-hosted/package.zig
@@ -1,11 +1,11 @@
 const std = @import("std");
 const mem = std.mem;
 const assert = std.debug.assert;
-const Buffer = std.Buffer;
+const ArrayListSentineled = std.ArrayListSentineled;
 
 pub const Package = struct {
-    root_src_dir: Buffer,
-    root_src_path: Buffer,
+    root_src_dir: ArrayListSentineled(u8, 0),
+    root_src_path: ArrayListSentineled(u8, 0),
 
     /// relative to root_src_dir
     table: Table,
@@ -17,8 +17,8 @@ pub const Package = struct {
     pub fn create(allocator: *mem.Allocator, root_src_dir: []const u8, root_src_path: []const u8) !*Package {
         const ptr = try allocator.create(Package);
         ptr.* = Package{
-            .root_src_dir = try Buffer.init(allocator, root_src_dir),
-            .root_src_path = try Buffer.init(allocator, root_src_path),
+            .root_src_dir = try ArrayListSentineled(u8, 0).init(allocator, root_src_dir),
+            .root_src_path = try ArrayListSentineled(u8, 0).init(allocator, root_src_path),
             .table = Table.init(allocator),
         };
         return ptr;
src-self-hosted/stage2.zig
@@ -8,7 +8,7 @@ const fs = std.fs;
 const process = std.process;
 const Allocator = mem.Allocator;
 const ArrayList = std.ArrayList;
-const Buffer = std.Buffer;
+const ArrayListSentineled = std.ArrayListSentineled;
 const Target = std.Target;
 const CrossTarget = std.zig.CrossTarget;
 const self_hosted_main = @import("main.zig");
@@ -449,7 +449,7 @@ export fn stage2_DepTokenizer_deinit(self: *stage2_DepTokenizer) void {
 
 export fn stage2_DepTokenizer_next(self: *stage2_DepTokenizer) stage2_DepNextResult {
     const otoken = self.handle.next() catch {
-        const textz = std.Buffer.init(&self.handle.arena.allocator, self.handle.error_text) catch @panic("failed to create .d tokenizer error text");
+        const textz = std.ArrayListSentineled(u8, 0).init(&self.handle.arena.allocator, self.handle.error_text) catch @panic("failed to create .d tokenizer error text");
         return stage2_DepNextResult{
             .type_id = .error_,
             .textz = textz.span().ptr,
@@ -461,7 +461,7 @@ export fn stage2_DepTokenizer_next(self: *stage2_DepTokenizer) stage2_DepNextRes
             .textz = undefined,
         };
     };
-    const textz = std.Buffer.init(&self.handle.arena.allocator, token.bytes) catch @panic("failed to create .d tokenizer token text");
+    const textz = std.ArrayListSentineled(u8, 0).init(&self.handle.arena.allocator, token.bytes) catch @panic("failed to create .d tokenizer token text");
     return stage2_DepNextResult{
         .type_id = switch (token.id) {
             .target => .target,
@@ -924,14 +924,14 @@ const Stage2Target = extern struct {
         var dynamic_linker: ?[*:0]u8 = null;
         const target = try crossTargetToTarget(cross_target, &dynamic_linker);
 
-        var cache_hash = try std.Buffer.allocPrint(allocator, "{}\n{}\n", .{
+        var cache_hash = try std.ArrayListSentineled(u8, 0).allocPrint(allocator, "{}\n{}\n", .{
             target.cpu.model.name,
             target.cpu.features.asBytes(),
         });
         defer cache_hash.deinit();
 
         const generic_arch_name = target.cpu.arch.genericName();
-        var cpu_builtin_str_buffer = try std.Buffer.allocPrint(allocator,
+        var cpu_builtin_str_buffer = try std.ArrayListSentineled(u8, 0).allocPrint(allocator,
             \\Cpu{{
             \\    .arch = .{},
             \\    .model = &Target.{}.cpu.{},
@@ -946,7 +946,7 @@ const Stage2Target = extern struct {
         });
         defer cpu_builtin_str_buffer.deinit();
 
-        var llvm_features_buffer = try std.Buffer.initSize(allocator, 0);
+        var llvm_features_buffer = try std.ArrayListSentineled(u8, 0).initSize(allocator, 0);
         defer llvm_features_buffer.deinit();
 
         // Unfortunately we have to do the work twice, because Clang does not support
@@ -961,17 +961,17 @@ const Stage2Target = extern struct {
 
             if (feature.llvm_name) |llvm_name| {
                 const plus_or_minus = "-+"[@boolToInt(is_enabled)];
-                try llvm_features_buffer.appendByte(plus_or_minus);
-                try llvm_features_buffer.append(llvm_name);
-                try llvm_features_buffer.append(",");
+                try llvm_features_buffer.append(plus_or_minus);
+                try llvm_features_buffer.appendSlice(llvm_name);
+                try llvm_features_buffer.appendSlice(",");
             }
 
             if (is_enabled) {
                 // TODO some kind of "zig identifier escape" function rather than
                 // unconditionally using @"" syntax
-                try cpu_builtin_str_buffer.append("        .@\"");
-                try cpu_builtin_str_buffer.append(feature.name);
-                try cpu_builtin_str_buffer.append("\",\n");
+                try cpu_builtin_str_buffer.appendSlice("        .@\"");
+                try cpu_builtin_str_buffer.appendSlice(feature.name);
+                try cpu_builtin_str_buffer.appendSlice("\",\n");
             }
         }
 
@@ -990,7 +990,7 @@ const Stage2Target = extern struct {
             },
         }
 
-        try cpu_builtin_str_buffer.append(
+        try cpu_builtin_str_buffer.appendSlice(
             \\    }),
             \\};
             \\
@@ -999,7 +999,7 @@ const Stage2Target = extern struct {
         assert(mem.endsWith(u8, llvm_features_buffer.span(), ","));
         llvm_features_buffer.shrink(llvm_features_buffer.len() - 1);
 
-        var os_builtin_str_buffer = try std.Buffer.allocPrint(allocator,
+        var os_builtin_str_buffer = try std.ArrayListSentineled(u8, 0).allocPrint(allocator,
             \\Os{{
             \\    .tag = .{},
             \\    .version_range = .{{
@@ -1042,7 +1042,7 @@ const Stage2Target = extern struct {
             .emscripten,
             .uefi,
             .other,
-            => try os_builtin_str_buffer.append(" .none = {} }\n"),
+            => try os_builtin_str_buffer.appendSlice(" .none = {} }\n"),
 
             .freebsd,
             .macosx,
@@ -1118,9 +1118,9 @@ const Stage2Target = extern struct {
                 @tagName(target.os.version_range.windows.max),
             }),
         }
-        try os_builtin_str_buffer.append("};\n");
+        try os_builtin_str_buffer.appendSlice("};\n");
 
-        try cache_hash.append(
+        try cache_hash.appendSlice(
             os_builtin_str_buffer.span()[os_builtin_str_ver_start_index..os_builtin_str_buffer.len()],
         );
 
src-self-hosted/translate_c.zig
@@ -275,7 +275,7 @@ pub fn translate(
 
         const tree = try tree_arena.allocator.create(ast.Tree);
         tree.* = ast.Tree{
-            .source = undefined, // need to use Buffer.toOwnedSlice later
+            .source = undefined, // need to use toOwnedSlice later
             .root_node = undefined,
             .arena_allocator = tree_arena,
             .tokens = undefined, // can't reference the allocator yet
src-self-hosted/util.zig
@@ -16,11 +16,11 @@ pub fn getDarwinArchString(self: Target) [:0]const u8 {
     }
 }
 
-pub fn llvmTargetFromTriple(triple: std.Buffer) !*llvm.Target {
+pub fn llvmTargetFromTriple(triple: [:0]const u8) !*llvm.Target {
     var result: *llvm.Target = undefined;
     var err_msg: [*:0]u8 = undefined;
-    if (llvm.GetTargetFromTriple(triple.span(), &result, &err_msg) != 0) {
-        std.debug.warn("triple: {s} error: {s}\n", .{ triple.span(), err_msg });
+    if (llvm.GetTargetFromTriple(triple, &result, &err_msg) != 0) {
+        std.debug.warn("triple: {s} error: {s}\n", .{ triple, err_msg });
         return error.UnsupportedTarget;
     }
     return result;
@@ -34,14 +34,14 @@ pub fn initializeAllTargets() void {
     llvm.InitializeAllAsmParsers();
 }
 
-pub fn getLLVMTriple(allocator: *std.mem.Allocator, target: std.Target) !std.Buffer {
-    var result = try std.Buffer.initSize(allocator, 0);
-    errdefer result.deinit();
+pub fn getLLVMTriple(allocator: *std.mem.Allocator, target: std.Target) ![:0]u8 {
+    var result = try std.ArrayListSentineled(u8, 0).initSize(allocator, 0);
+    defer result.deinit();
 
     try result.outStream().print(
         "{}-unknown-{}-{}",
         .{ @tagName(target.cpu.arch), @tagName(target.os.tag), @tagName(target.abi) },
     );
 
-    return result;
+    return result.toOwnedSlice();
 }
src-self-hosted/value.zig
@@ -3,7 +3,7 @@ const Scope = @import("scope.zig").Scope;
 const Compilation = @import("compilation.zig").Compilation;
 const ObjectFile = @import("codegen.zig").ObjectFile;
 const llvm = @import("llvm.zig");
-const Buffer = std.Buffer;
+const ArrayListSentineled = std.ArrayListSentineled;
 const assert = std.debug.assert;
 
 /// Values are ref-counted, heap-allocated, and copy-on-write
@@ -131,9 +131,9 @@ pub const Value = struct {
 
         /// The main external name that is used in the .o file.
         /// TODO https://github.com/ziglang/zig/issues/265
-        symbol_name: Buffer,
+        symbol_name: ArrayListSentineled(u8, 0),
 
-        pub fn create(comp: *Compilation, fn_type: *Type.Fn, symbol_name: Buffer) !*FnProto {
+        pub fn create(comp: *Compilation, fn_type: *Type.Fn, symbol_name: ArrayListSentineled(u8, 0)) !*FnProto {
             const self = try comp.gpa().create(FnProto);
             self.* = FnProto{
                 .base = Value{
@@ -171,7 +171,7 @@ pub const Value = struct {
 
         /// The main external name that is used in the .o file.
         /// TODO https://github.com/ziglang/zig/issues/265
-        symbol_name: Buffer,
+        symbol_name: ArrayListSentineled(u8, 0),
 
         /// parent should be the top level decls or container decls
         fndef_scope: *Scope.FnDef,
@@ -183,13 +183,13 @@ pub const Value = struct {
         block_scope: ?*Scope.Block,
 
         /// Path to the object file that contains this function
-        containing_object: Buffer,
+        containing_object: ArrayListSentineled(u8, 0),
 
         link_set_node: *std.TailQueue(?*Value.Fn).Node,
 
         /// Creates a Fn value with 1 ref
         /// Takes ownership of symbol_name
-        pub fn create(comp: *Compilation, fn_type: *Type.Fn, fndef_scope: *Scope.FnDef, symbol_name: Buffer) !*Fn {
+        pub fn create(comp: *Compilation, fn_type: *Type.Fn, fndef_scope: *Scope.FnDef, symbol_name: ArrayListSentineled(u8, 0)) !*Fn {
             const link_set_node = try comp.gpa().create(Compilation.FnLinkSet.Node);
             link_set_node.* = Compilation.FnLinkSet.Node{
                 .data = null,
@@ -209,7 +209,7 @@ pub const Value = struct {
                 .child_scope = &fndef_scope.base,
                 .block_scope = null,
                 .symbol_name = symbol_name,
-                .containing_object = Buffer.initNull(comp.gpa()),
+                .containing_object = ArrayListSentineled(u8, 0).initNull(comp.gpa()),
                 .link_set_node = link_set_node,
             };
             fn_type.base.base.ref();
test/standalone/brace_expansion/main.zig
@@ -4,7 +4,7 @@ const mem = std.mem;
 const debug = std.debug;
 const assert = debug.assert;
 const testing = std.testing;
-const Buffer = std.Buffer;
+const ArrayListSentineled = std.ArrayListSentineled;
 const ArrayList = std.ArrayList;
 const maxInt = std.math.maxInt;
 
@@ -111,7 +111,7 @@ fn parse(tokens: *const ArrayList(Token), token_index: *usize) ParseError!Node {
     }
 }
 
-fn expandString(input: []const u8, output: *Buffer) !void {
+fn expandString(input: []const u8, output: *ArrayListSentineled(u8, 0)) !void {
     const tokens = try tokenize(input);
     if (tokens.len == 1) {
         return output.resize(0);
@@ -125,7 +125,7 @@ fn expandString(input: []const u8, output: *Buffer) !void {
         else => return error.InvalidInput,
     }
 
-    var result_list = ArrayList(Buffer).init(global_allocator);
+    var result_list = ArrayList(ArrayListSentineled(u8, 0)).init(global_allocator);
     defer result_list.deinit();
 
     try expandNode(root, &result_list);
@@ -133,41 +133,41 @@ fn expandString(input: []const u8, output: *Buffer) !void {
     try output.resize(0);
     for (result_list.span()) |buf, i| {
         if (i != 0) {
-            try output.appendByte(' ');
+            try output.append(' ');
         }
-        try output.append(buf.span());
+        try output.appendSlice(buf.span());
     }
 }
 
 const ExpandNodeError = error{OutOfMemory};
 
-fn expandNode(node: Node, output: *ArrayList(Buffer)) ExpandNodeError!void {
+fn expandNode(node: Node, output: *ArrayList(ArrayListSentineled(u8, 0))) ExpandNodeError!void {
     assert(output.len == 0);
     switch (node) {
         Node.Scalar => |scalar| {
-            try output.append(try Buffer.init(global_allocator, scalar));
+            try output.append(try ArrayListSentineled(u8, 0).init(global_allocator, scalar));
         },
         Node.Combine => |pair| {
             const a_node = pair[0];
             const b_node = pair[1];
 
-            var child_list_a = ArrayList(Buffer).init(global_allocator);
+            var child_list_a = ArrayList(ArrayListSentineled(u8, 0)).init(global_allocator);
             try expandNode(a_node, &child_list_a);
 
-            var child_list_b = ArrayList(Buffer).init(global_allocator);
+            var child_list_b = ArrayList(ArrayListSentineled(u8, 0)).init(global_allocator);
             try expandNode(b_node, &child_list_b);
 
             for (child_list_a.span()) |buf_a| {
                 for (child_list_b.span()) |buf_b| {
-                    var combined_buf = try Buffer.initFromBuffer(buf_a);
-                    try combined_buf.append(buf_b.span());
+                    var combined_buf = try ArrayListSentineled(u8, 0).initFromBuffer(buf_a);
+                    try combined_buf.appendSlice(buf_b.span());
                     try output.append(combined_buf);
                 }
             }
         },
         Node.List => |list| {
             for (list.span()) |child_node| {
-                var child_list = ArrayList(Buffer).init(global_allocator);
+                var child_list = ArrayList(ArrayListSentineled(u8, 0)).init(global_allocator);
                 try expandNode(child_node, &child_list);
 
                 for (child_list.span()) |buf| {
@@ -187,13 +187,13 @@ pub fn main() !void {
 
     global_allocator = &arena.allocator;
 
-    var stdin_buf = try Buffer.initSize(global_allocator, 0);
+    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 Buffer.initSize(global_allocator, 0);
+    var result_buf = try ArrayListSentineled(u8, 0).initSize(global_allocator, 0);
     defer result_buf.deinit();
 
     try expandString(stdin_buf.span(), &result_buf);
@@ -218,7 +218,7 @@ test "invalid inputs" {
 }
 
 fn expectError(test_input: []const u8, expected_err: anyerror) void {
-    var output_buf = Buffer.initSize(global_allocator, 0) catch unreachable;
+    var output_buf = ArrayListSentineled(u8, 0).initSize(global_allocator, 0) catch unreachable;
     defer output_buf.deinit();
 
     testing.expectError(expected_err, expandString(test_input, &output_buf));
@@ -251,7 +251,7 @@ test "valid inputs" {
 }
 
 fn expectExpansion(test_input: []const u8, expected_result: []const u8) void {
-    var result = Buffer.initSize(global_allocator, 0) catch unreachable;
+    var result = ArrayListSentineled(u8, 0).initSize(global_allocator, 0) catch unreachable;
     defer result.deinit();
 
     expandString(test_input, &result) catch unreachable;
test/tests.zig
@@ -4,7 +4,6 @@ const debug = std.debug;
 const warn = debug.warn;
 const build = std.build;
 const CrossTarget = std.zig.CrossTarget;
-const Buffer = std.Buffer;
 const io = std.io;
 const fs = std.fs;
 const mem = std.mem;
@@ -640,7 +639,7 @@ pub const StackTracesContext = struct {
             // - replace address with symbolic string
             // - skip empty lines
             const got: []const u8 = got_result: {
-                var buf = try Buffer.initSize(b.allocator, 0);
+                var buf = ArrayList(u8).init(b.allocator);
                 defer buf.deinit();
                 if (stderr.len != 0 and stderr[stderr.len - 1] == '\n') stderr = stderr[0 .. stderr.len - 1];
                 var it = mem.separate(stderr, "\n");
@@ -652,21 +651,21 @@ pub const StackTracesContext = struct {
                     var pos: usize = if (std.Target.current.os.tag == .windows) 2 else 0;
                     for (delims) |delim, i| {
                         marks[i] = mem.indexOfPos(u8, line, pos, delim) orelse {
-                            try buf.append(line);
-                            try buf.append("\n");
+                            try buf.appendSlice(line);
+                            try buf.appendSlice("\n");
                             continue :process_lines;
                         };
                         pos = marks[i] + delim.len;
                     }
                     pos = mem.lastIndexOfScalar(u8, line[0..marks[0]], fs.path.sep) orelse {
-                        try buf.append(line);
-                        try buf.append("\n");
+                        try buf.appendSlice(line);
+                        try buf.appendSlice("\n");
                         continue :process_lines;
                     };
-                    try buf.append(line[pos + 1 .. marks[2] + delims[2].len]);
-                    try buf.append(" [address]");
-                    try buf.append(line[marks[3]..]);
-                    try buf.append("\n");
+                    try buf.appendSlice(line[pos + 1 .. marks[2] + delims[2].len]);
+                    try buf.appendSlice(" [address]");
+                    try buf.appendSlice(line[marks[3]..]);
+                    try buf.appendSlice("\n");
                 }
                 break :got_result buf.toOwnedSlice();
             };