Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`:
Expand Down Expand Up @@ -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`:

Expand Down
2 changes: 1 addition & 1 deletion build.zig.zon
Original file line number Diff line number Diff line change
@@ -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 = .{
Expand Down
139 changes: 131 additions & 8 deletions src/root.zig
Original file line number Diff line number Diff line change
Expand Up @@ -120,16 +120,11 @@ 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`.
pub fn initCapacity(gpa: Allocator, num: usize) Allocator.Error!Self {
var self = Self{};
var self = Self.empty;
try self.ensureTotalCapacityPrecise(gpa, num);
return self;
}
Expand Down Expand Up @@ -649,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;
}
};
}

Expand Down Expand Up @@ -689,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());
Expand Down Expand Up @@ -805,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;
Expand Down Expand Up @@ -1185,3 +1235,76 @@ test "sized" {
try testing.expect(list1.hasAllocation());
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;

{
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());
}
}

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 }));
}