From eda5fbf34262ce09a5122c65509cdba15637d81a Mon Sep 17 00:00:00 2001 From: Jeremy Larkin Date: Tue, 27 May 2025 06:35:48 -0700 Subject: [PATCH 1/5] remove init, use empty --- src/root.zig | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/root.zig b/src/root.zig index 201498f..abd1930 100644 --- a/src/root.zig +++ b/src/root.zig @@ -120,11 +120,6 @@ pub fn SmallArrayListAlignedSized(comptime T: type, comptime alignment: ?u29, co pub const Slice = SliceType; pub const SliceConst = SliceConstType; - /// Initialized an empty SmallArrayList. Deinitialize with `deinit`. - pub fn init() Self { - return empty; - } - /// Initialize with capacity to hold `num` elements. /// The resulting capacity will equal `num` exactly. /// Deinitialize with `deinit`. From 2edb9fac0341f1a3e621e1bf9f1dd0c471e06d14 Mon Sep 17 00:00:00 2001 From: Jeremy Larkin Date: Tue, 27 May 2025 06:36:11 -0700 Subject: [PATCH 2/5] add writer --- src/root.zig | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/src/root.zig b/src/root.zig index abd1930..db43999 100644 --- a/src/root.zig +++ b/src/root.zig @@ -644,6 +644,47 @@ pub fn SmallArrayListAlignedSized(comptime T: type, comptime alignment: ?u29, co self.as.slice = new_mem; self.capacity = new_capacity; } + + pub const WriterContext = struct { + self: *Self, + allocator: Allocator, + }; + + pub const Writer = if (T != u8) + @compileError("The Writer interface is only defined for SmallArrayList(u8) " ++ + "but the given type is SmallArrayList(" ++ @typeName(T) ++ ")") + else + std.io.Writer(WriterContext, Allocator.Error, appendWrite); + + /// Initializes a Writer which will append to the list. + pub fn writer(self: *Self, gpa: Allocator) Writer { + return .{ .context = .{ .self = self, .allocator = gpa } }; + } + + /// 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.Writer` API. + /// Invalidates element pointers if additional memory is needed. + fn appendWrite(context: WriterContext, m: []const u8) Allocator.Error!usize { + try context.self.appendSlice(context.allocator, m); + return m.len; + } + + pub const FixedWriter = std.io.Writer(*Self, Allocator.Error, appendWriteFixed); + + /// Initializes a Writer which will append to the list but will return + /// `error.OutOfMemory` rather than increasing capacity. + pub fn fixedWriter(self: *Self) FixedWriter { + return .{ .context = self }; + } + + /// The purpose of this function existing is to match `std.io.Writer` API. + fn appendWriteFixed(self: *Self, m: []const u8) error{OutOfMemory}!usize { + const available_capacity = self.capacity - self.len; + if (m.len > available_capacity) return error.OutOfMemory; + self.appendSliceAssumeCapacity(m); + return m.len; + } }; } @@ -1180,3 +1221,30 @@ test "sized" { try testing.expect(list1.hasAllocation()); try testing.expect(!list2.hasAllocation()); } + +test "SmallArrayList(u8) implements writer" { + const a = testing.allocator; + + { + var buffer: SmallArrayList(u8) = .empty; + defer buffer.deinit(a); + + const x: i32 = 42; + const y: i32 = 1234; + try buffer.writer(a).print("x: {}\ny: {}\n", .{ x, y }); + + try testing.expectEqualSlices(u8, "x: 42\ny: 1234\n", buffer.items()); + } + { + var list: SmallArrayListAligned(u8, 2) = .empty; + defer list.deinit(a); + + const writer = list.writer(a); + try writer.writeAll("a"); + try writer.writeAll("bc"); + try writer.writeAll("d"); + try writer.writeAll("efg"); + + try testing.expectEqualSlices(u8, "abcdefg", list.items()); + } +} From 4df12740e1e9dbbdd693536e6c025286a1ec9e30 Mon Sep 17 00:00:00 2001 From: Jeremy Larkin Date: Tue, 27 May 2025 06:38:33 -0700 Subject: [PATCH 3/5] bump version --- README.md | 4 ++-- build.zig.zon | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8e6a831..bbbc25d 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ std.debug.print("len={} capacity={}\n", .{ list.len, list.capacity }); First, add the dependency to your `build.zig.zon`: ```sh -zig fetch --save git+https://github.com/kalamay/small-array-list#v0.1.1 +zig fetch --save git+https://github.com/kalamay/small-array-list#v0.2.0 ``` Next add the dependecy to your `build.zig`: @@ -103,7 +103,7 @@ native machine word size. Because a zig slice requires two machine words for storing it's `ptr` and `len`, this space can be used for direct item storage instead until the small capacity is exceeded. For any given type `T`, the small array list can store up to `2*@sizeOf(usize) / @sizeOf(T)` items before -requiring any internal allocation. +requiring any internal allocation. For example on a 64-bit processor will print out `smallCapacity=4 sizeOf=24`: diff --git a/build.zig.zon b/build.zig.zon index 88c052c..ad4805f 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,6 +1,6 @@ .{ .name = .small_array_list, - .version = "0.1.1", + .version = "0.2.0", .fingerprint = 0x378608764b63102b, .minimum_zig_version = "0.14.0", .paths = .{ From 9e13aa666076f3f3d4fcf5588ac6c4af36752377 Mon Sep 17 00:00:00 2001 From: Jeremy Larkin Date: Tue, 27 May 2025 07:27:30 -0700 Subject: [PATCH 4/5] use empty --- src/root.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/root.zig b/src/root.zig index db43999..92b6f91 100644 --- a/src/root.zig +++ b/src/root.zig @@ -124,7 +124,7 @@ pub fn SmallArrayListAlignedSized(comptime T: type, comptime alignment: ?u29, co /// The resulting capacity will equal `num` exactly. /// Deinitialize with `deinit`. pub fn initCapacity(gpa: Allocator, num: usize) Allocator.Error!Self { - var self = Self{}; + var self = Self.empty; try self.ensureTotalCapacityPrecise(gpa, num); return self; } From 0b0aaf13f5c312a2342f23753e9e2ef5ac050b6d Mon Sep 17 00:00:00 2001 From: Jeremy Larkin Date: Tue, 27 May 2025 07:27:54 -0700 Subject: [PATCH 5/5] add more tests --- src/root.zig | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/src/root.zig b/src/root.zig index 92b6f91..29beac6 100644 --- a/src/root.zig +++ b/src/root.zig @@ -725,14 +725,14 @@ test "init" { test "initCapacity" { { const a = testing.allocator; - var list = try SmallArrayList(i8).initCapacity(a, 200); + var list = try SmallArrayList(u8).initCapacity(a, 200); defer list.deinit(a); try testing.expect(list.len == 0); try testing.expect(list.capacity >= 200); } { const a = testing.allocator; - var list = try SmallArrayListSized(i8, 8).initCapacity(a, 6); + var list = try SmallArrayListSized(u8, 8).initCapacity(a, 6); try testing.expect(list.len == 0); try testing.expect(list.capacity >= 6); try testing.expect(!list.hasAllocation()); @@ -841,6 +841,20 @@ test "basic" { try testing.expect(list.pop() == 33); } +test "appendSlice" { + const a = testing.allocator; + var list: SmallArrayList(u8) = .empty; + defer list.deinit(a); + + try list.appendSlice(a, "abcdefg"); + try testing.expect(!list.hasAllocation()); + try list.appendSlice(a, "hijklmn"); + try testing.expect(!list.hasAllocation()); + try list.appendSlice(a, "opqrstu"); + try testing.expect(list.hasAllocation()); + try testing.expectEqualStrings("abcdefghijklmnopqrstu", list.items()); +} + test "appendNTimes" { const a = testing.allocator; var list: SmallArrayList(i32) = .empty; @@ -1222,6 +1236,41 @@ test "sized" { try testing.expect(!list2.hasAllocation()); } +test "expand and shrink" { + const List = SmallArrayList(u8); + var list: List = .empty; + try testing.expectEqual(list.len, 0); + try testing.expectEqual(list.capacity, List.smallCapacity); + + list.expandToCapacity(); + try testing.expectEqual(list.len, List.smallCapacity); + try testing.expectEqual(list.capacity, List.smallCapacity); + + list.shrinkRetainingCapacity(4); + try testing.expectEqual(list.len, 4); + try testing.expectEqual(list.capacity, List.smallCapacity); + + list.clearRetainingCapacity(); + try testing.expectEqual(list.len, 0); + try testing.expectEqual(list.capacity, List.smallCapacity); +} + +test "ensureTotalCapacity" { + const List = SmallArrayList(u8); + const a = testing.allocator; + var list: List = .empty; + + try list.ensureTotalCapacity(a, 100); + + try testing.expectEqual(list.len, 0); + try testing.expectEqual(list.capacity, 128); + + list.clearAndFree(a); + + try testing.expectEqual(list.len, 0); + try testing.expectEqual(list.capacity, List.smallCapacity); +} + test "SmallArrayList(u8) implements writer" { const a = testing.allocator; @@ -1248,3 +1297,14 @@ test "SmallArrayList(u8) implements writer" { try testing.expectEqualSlices(u8, "abcdefg", list.items()); } } + +test "SmallArrayList(u8) implements fixedWriter" { + var buffer: SmallArrayList(u8) = .empty; + + const x: i32 = 42; + const y: i32 = 1234; + try buffer.fixedWriter().print("x: {}\ny: {}\n", .{ x, y }); + + try testing.expectEqualSlices(u8, "x: 42\ny: 1234\n", buffer.items()); + try testing.expectError(error.OutOfMemory, buffer.fixedWriter().print("x: {}\ny: {}\n", .{ x, y })); +}