From 10e6cde083cf9ddd1ae72850d45c25b1258d0d7e Mon Sep 17 00:00:00 2001 From: MCRusher Date: Sat, 23 Nov 2019 22:54:33 -0500 Subject: [PATCH 1/3] Added initCapacity and relevant test Added ArrayList.initCapcity() as a way to preallocate a block of memory to reduce future allocations. Added a test "std.ArrayList.initCapacity" that ensures initCapacity adds no elements and increases capacity by at least the requested amount --- lib/std/array_list.zig | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lib/std/array_list.zig b/lib/std/array_list.zig index 59fd2a10e5..d7da5f7f8a 100644 --- a/lib/std/array_list.zig +++ b/lib/std/array_list.zig @@ -40,6 +40,14 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type { .allocator = allocator, }; } + + /// Initialize with capacity to hold at least num elements. + /// Deinitialize with `deinit` or use `toOwnedSlice`. + pub fn initCapacity(allocator: *Allocator, num: usize) !Self { + var self = Self.init(allocator); + try self.ensureCapacity(num); + return self; + } /// Release all allocated memory. pub fn deinit(self: Self) void { @@ -271,6 +279,15 @@ test "std.ArrayList.init" { testing.expect(list.capacity() == 0); } +test "std.ArrayList.initCapacity" { + var bytes: [1024]u8 = undefined; + const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator; + var list = try ArrayList(i8).initCapacity(allocator, 200); + defer list.deinit(); + testing.expect(list.count() == 0); + testing.expect(list.capacity() >= 200); +} + test "std.ArrayList.basic" { var bytes: [1024]u8 = undefined; const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator; From d49e0a7b90dabbf486902fa9d4a4306aede55087 Mon Sep 17 00:00:00 2001 From: MCRusher Date: Sat, 23 Nov 2019 23:08:33 -0500 Subject: [PATCH 2/3] Added initCapacity, capacity, and 2 tests. Added Buffer.initCapcity() to buffer to allow preallocation of a block of memory to reduce future allocations. Uses the added ArrayList.initCapacity() function to achieve this. Added Buffer.capacity() to track current usable allocation size, not counting null byte, and returning 0 if empty or created with Buffer.initNull() Added a test for initCapacity() that shows that no further allocation is performed for an append of size smaller than or equal to capacity when initCapacity is used. Added a test for initSize(), since it did not exist already. Also added a comment to better explain the difference between initSize() and initCapacity() note: forgot in the first commit but thanks to mikdusan for helping me brainstorm, through the process, and for drawing up a draft diff which I tweaked. --- lib/std/buffer.zig | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/lib/std/buffer.zig b/lib/std/buffer.zig index 24bd23fa74..7876f2197f 100644 --- a/lib/std/buffer.zig +++ b/lib/std/buffer.zig @@ -16,13 +16,22 @@ pub const Buffer = struct { 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: @@ -98,6 +107,13 @@ pub const Buffer = struct { 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(); @@ -156,3 +172,21 @@ test "simple Buffer" { try buf2.resize(4); testing.expect(buf.startsWith(buf2.toSlice())); } + +test "Buffer.initSize" { + var buf = try Buffer.initSize(debug.global_allocator, 3); + testing.expect(buf.len() == 3); + try buf.append("hello"); + testing.expect(mem.eql(u8, buf.toSliceConst()[3..], "hello")); +} + +test "Buffer.initCapacity" { + var buf = try Buffer.initCapacity(debug.global_allocator, 10); + 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")); +} From 17abb7ef7f75705c1195022076e95cdde6ab2243 Mon Sep 17 00:00:00 2001 From: MCRusher Date: Sat, 23 Nov 2019 23:49:55 -0500 Subject: [PATCH 3/3] Adds initCapacity() to buffer and arraylist array_list.zig: - adds ArrayList.initCapacity(*Allocator,usize) to allow preallocation of a block at initialization to reduce future allocations. - adds a test for ArrayList.initCapacity() that ensures ArrayList.len() is unchanged and that at least the requested amount is allocated for. buffer.zig: - adds Buffer.initCapacity(*Allocator,usize), based off of ArrayList.initCapacity(), to preallocate a buffer before use. note: contrary to Buffer.initSize(0) and then Buffer.list.ensureCapacity(200) (the presumed current method), this only allocates once instead of twice. - adds Buffer.capacity to check usable allocated space, not including the null byte. note: returns 0 when Buffer has only a null byte or when initNull() was used before without resize()/replaceContents(). - adds a test "Buffer.initCapacity" which ensures that Buffer.append()'s with [added size <= Buffer.capacity()-Buffer.len()] do not cause a reallocation to occur. - adds a test "Buffer.initSize" which ensures that Buffer.initSize() behaves as expected, also combined with Buffer.append(). - adds a doc comment to Buffer.initSize() that makes its function and distinction from Buffer.initCapacity() clearer.