Create minimal collections structure.
This commit is contained in:
commit
d3278532af
10 changed files with 491 additions and 0 deletions
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
# IntelliJ IDEA
|
||||
*.iml
|
||||
.idea/
|
||||
|
||||
# Zig
|
||||
.zig-cache/
|
||||
zig-out/
|
9
LICENSE
Normal file
9
LICENSE
Normal file
|
@ -0,0 +1,9 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2024 Zeptotech
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
141
README.md
Normal file
141
README.md
Normal file
|
@ -0,0 +1,141 @@
|
|||
<p align="center">
|
||||
<a href="https://code.zeptotech.net/zedd/zollections">
|
||||
<picture>
|
||||
<img alt="Zollections logo" width="150" src="https://code.zeptotech.net/zedd/zollections/raw/branch/main/logo.svg" />
|
||||
</picture>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<h1 align="center">
|
||||
Zollections
|
||||
</h1>
|
||||
|
||||
<h4 align="center">
|
||||
<a href="https://code.zeptotech.net/zedd/zollections">Documentation</a>
|
||||
|
|
||||
<a href="https://zedd.zeptotech.net/zollections/api">API</a>
|
||||
</h4>
|
||||
|
||||
<p align="center">
|
||||
Zig collections library
|
||||
</p>
|
||||
|
||||
Zollections is part of [_zedd_](https://code.zeptotech.net/zedd), a collection of useful libraries for zig.
|
||||
|
||||
## Zollections
|
||||
|
||||
_Zollections_ is a collections library for Zig. It's made to ease memory management of dynamically allocated slices and elements.
|
||||
|
||||
## Versions
|
||||
|
||||
Zollections 0.1.0 is made and tested with zig 0.13.0.
|
||||
|
||||
## How to use
|
||||
|
||||
### Install
|
||||
|
||||
In your project directory:
|
||||
|
||||
```shell
|
||||
$ zig fetch --save https://code.zeptotech.net/zedd/zollections/archive/v0.1.0.tar.gz
|
||||
```
|
||||
|
||||
In `build.zig`:
|
||||
|
||||
```zig
|
||||
// Add zollections dependency.
|
||||
const zollections = b.dependency("zollections", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
exe.root_module.addImport("zollections", zollections.module("zollections"));
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
These examples are taken from tests in [`tests/collection.zig`](https://code.zeptotech.net/zedd/zollections/src/branch/main/tests/collection.zig).
|
||||
|
||||
#### Simple collection
|
||||
|
||||
```zig
|
||||
// Allocate your slice.
|
||||
const slice = try allocator.alloc(*u8, 3);
|
||||
// Create your slice elements.
|
||||
slice[0] = try allocator.create(u8);
|
||||
slice[1] = try allocator.create(u8);
|
||||
slice[2] = try allocator.create(u8);
|
||||
|
||||
// Create a collection with your slice of elements.
|
||||
const collection = try zollections.Collection(u8).init(allocator, slice);
|
||||
// Free your collection: your slice and all your elements will be freed.
|
||||
defer collection.deinit();
|
||||
```
|
||||
|
||||
#### Recursive free
|
||||
|
||||
```zig
|
||||
// Create a pointer to a slice.
|
||||
const slicePointer = try allocator.create([]*u8);
|
||||
|
||||
// Allocate your slice in the pointed slice.
|
||||
slicePointer.* = try allocator.alloc(*u8, 3);
|
||||
// Create slice elements.
|
||||
slicePointer.*[0] = try allocator.create(u8);
|
||||
slicePointer.*[1] = try allocator.create(u8);
|
||||
slicePointer.*[2] = try allocator.create(u8);
|
||||
|
||||
// Allocate your slice or pointers to slices.
|
||||
const slice = try allocator.alloc(*[]*u8, 1);
|
||||
slice[0] = slicePointer;
|
||||
|
||||
// Create a collection with your slice of elements.
|
||||
const collection = try zollections.Collection([]*u8).init(allocator, slice);
|
||||
// Free your collection: your slice and all your slices and their elements will be freed.
|
||||
defer collection.deinit();
|
||||
```
|
||||
|
||||
#### Custom structure deinitialization
|
||||
|
||||
```zig
|
||||
|
||||
/// An example structure.
|
||||
const ExampleStruct = struct {
|
||||
const Self = @This();
|
||||
|
||||
allocator: std.mem.Allocator,
|
||||
buffer: []u8,
|
||||
|
||||
/// Initialize a new example struct.
|
||||
pub fn init(bufSiz: usize) !Self
|
||||
{
|
||||
const allocator = std.testing.allocator;
|
||||
|
||||
return .{
|
||||
.allocator = allocator,
|
||||
.buffer = try allocator.alloc(u8, bufSiz),
|
||||
};
|
||||
}
|
||||
|
||||
/// Deinitialize the example struct.
|
||||
pub fn deinit(self: *Self) void
|
||||
{
|
||||
self.allocator.free(self.buffer);
|
||||
}
|
||||
};
|
||||
|
||||
// Allocate your slice.
|
||||
const slice = try allocator.alloc(*ExampleStruct, 3);
|
||||
// Create your slice elements with custom structs and their inner init / deinit.
|
||||
slice[0] = try allocator.create(ExampleStruct);
|
||||
slice[0].* = try ExampleStruct.init(4);
|
||||
slice[1] = try allocator.create(ExampleStruct);
|
||||
slice[1].* = try ExampleStruct.init(8);
|
||||
slice[2] = try allocator.create(ExampleStruct);
|
||||
slice[2].* = try ExampleStruct.init(16);
|
||||
|
||||
// Create a collection with your slice of elements.
|
||||
const collection = try zollections.Collection(ExampleStruct).init(allocator, slice);
|
||||
// Free your collection: your slice and all your elements will be deinitialized and freed.
|
||||
defer collection.deinit();
|
||||
|
||||
```
|
45
build.zig
Normal file
45
build.zig
Normal file
|
@ -0,0 +1,45 @@
|
|||
const std = @import("std");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
// Standard target options allows the person running `zig build` to choose
|
||||
// what target to build for. Here we do not override the defaults, which
|
||||
// means any target is allowed, and the default is native. Other options
|
||||
// for restricting supported target set are available.
|
||||
const target = b.standardTargetOptions(.{});
|
||||
|
||||
// Standard optimization options allow the person running `zig build` to select
|
||||
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
|
||||
// set a preferred release mode, allowing the user to decide how to optimize.
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
const lib = b.addSharedLibrary(.{
|
||||
.name = "zollections",
|
||||
.root_source_file = b.path("src/root.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
// This declares intent for the library to be installed into the standard
|
||||
// location when the user invokes the "install" step (the default step when
|
||||
// running `zig build`).
|
||||
b.installArtifact(lib);
|
||||
|
||||
// Creates a step for unit testing. This only builds the test executable
|
||||
// but does not run it.
|
||||
const lib_unit_tests = b.addTest(.{
|
||||
.root_source_file = b.path("tests/root.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
// Add zouter dependency.
|
||||
lib_unit_tests.root_module.addImport("zollections", &lib.root_module);
|
||||
|
||||
const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);
|
||||
|
||||
// Similar to creating the run step earlier, this exposes a `test` step to
|
||||
// the `zig build --help` menu, providing a way for the user to request
|
||||
// running the unit tests.
|
||||
const test_step = b.step("test", "Run unit tests.");
|
||||
test_step.dependOn(&run_lib_unit_tests.step);
|
||||
}
|
15
build.zig.zon
Normal file
15
build.zig.zon
Normal file
|
@ -0,0 +1,15 @@
|
|||
.{
|
||||
.name = "zollections",
|
||||
.version = "0.1.0",
|
||||
|
||||
.minimum_zig_version = "0.13.0",
|
||||
|
||||
.dependencies = .{
|
||||
},
|
||||
|
||||
.paths = .{
|
||||
"build.zig",
|
||||
"build.zig.zon",
|
||||
"src",
|
||||
},
|
||||
}
|
69
logo.svg
Normal file
69
logo.svg
Normal file
|
@ -0,0 +1,69 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="512"
|
||||
height="512"
|
||||
viewBox="0 0 512 512"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<g
|
||||
id="layer1">
|
||||
<path
|
||||
id="rect2"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.779484;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 83.121094,12 v 135.16406 a 4.9900596,4.9900596 22.579412 0 0 8.50877,3.53827 l 5.238316,-5.20935 22.25586,-22.13281 a 12.066715,12.066715 0 0 1 17.01754,0 l 5.23832,5.20935 a 547869.11,547869.11 44.839916 0 0 17.01791,16.92308 l 5.23953,5.21011 a 4.9903685,4.9903685 157.41933 0 0 8.50914,-3.53865 V 12 a 12,12 45 0 0 -12,-12 H 95.121094 a 12,12 135 0 0 -12,12 z" />
|
||||
<path
|
||||
id="rect3"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.949016;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 0,24 v 2 412 50 a 24,24 45 0 0 24,24 h 464 a 24,24 135 0 0 24,-24 V 486 74 24 A 24,24 45 0 0 488,0 H 486 74 24 A 24,24 135 0 0 0,24 Z m 74,26 h 364 a 24,24 45 0 1 24,24 v 364 a 24,24 135 0 1 -24,24 H 74 A 24,24 45 0 1 50,438 V 74 A 24,24 135 0 1 74,50 Z" />
|
||||
<g
|
||||
id="g4"
|
||||
transform="translate(1.9073486e-6,-2.6678004)">
|
||||
<rect
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.96282;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect4"
|
||||
width="57.902035"
|
||||
height="229.66351"
|
||||
x="83.121094"
|
||||
y="194.49916"
|
||||
rx="10" />
|
||||
<rect
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.96282;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect4-6"
|
||||
width="57.902035"
|
||||
height="229.66351"
|
||||
x="155.08504"
|
||||
y="194.49916"
|
||||
rx="10" />
|
||||
<rect
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.817319;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect4-61"
|
||||
width="57.902035"
|
||||
height="165.49487"
|
||||
x="227.04898"
|
||||
y="258.66779"
|
||||
rx="10" />
|
||||
<rect
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.817319;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect4-2"
|
||||
width="57.902035"
|
||||
height="165.49489"
|
||||
x="299.01291"
|
||||
y="258.66779"
|
||||
rx="10" />
|
||||
<rect
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.73063;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect4-2-7"
|
||||
width="57.902035"
|
||||
height="132.25037"
|
||||
x="370.97687"
|
||||
y="291.91232"
|
||||
rx="10" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3 KiB |
116
src/collection.zig
Normal file
116
src/collection.zig
Normal file
|
@ -0,0 +1,116 @@
|
|||
const std = @import("std");
|
||||
|
||||
/// Collection of pointers of a certain type.
|
||||
/// A collection manages memory of the contained type.
|
||||
pub fn Collection(comptime T: anytype) type
|
||||
{
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
/// The used allocator.
|
||||
allocator: std.mem.Allocator,
|
||||
/// Items contained by the collection.
|
||||
items: []*T,
|
||||
|
||||
/// Initialize a new collection of values.
|
||||
/// Values are now owned by the collection and will free them when it is deinitialized.
|
||||
/// The allocator must be the one that manages the slice and its items.
|
||||
pub fn init(allocator: std.mem.Allocator, values: []*T) !*Self
|
||||
{
|
||||
const self = try allocator.create(Self);
|
||||
|
||||
self.* = .{
|
||||
.allocator = allocator,
|
||||
// Store given values in items slice.
|
||||
.items = values,
|
||||
};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/// Free any pointer value.
|
||||
fn freeAnyPointer(self: *Self, pointer: anytype) void
|
||||
{
|
||||
// Get type info of the current pointer.
|
||||
const pointedTypeInfo = @typeInfo(@TypeOf(pointer.*));
|
||||
|
||||
switch (pointedTypeInfo)
|
||||
{
|
||||
.Struct, .Enum, .Union, .Opaque => {
|
||||
// If type is a container with a deinit, run deinit.
|
||||
if (@hasDecl(@TypeOf(pointer.*), "deinit"))
|
||||
{ // The container has a specific deinit, running it.
|
||||
pointer.deinit();
|
||||
//TODO implement something like that.
|
||||
//switch (@TypeOf(pointer.deinit).@"fn".return_type)
|
||||
//{
|
||||
// .ErrorUnion => {
|
||||
// try pointer.deinit();
|
||||
// },
|
||||
// else => {
|
||||
// pointer.deinit();
|
||||
// },
|
||||
//}
|
||||
}
|
||||
},
|
||||
.Pointer => {
|
||||
// It's a simple pointer, freeing its value recursively.
|
||||
self.freeAnyValue(pointer.*);
|
||||
},
|
||||
else => {
|
||||
// Otherwise, we consider it as a simple value, there is nothing to free.
|
||||
},
|
||||
}
|
||||
|
||||
// Free the current pointer.
|
||||
self.allocator.destroy(pointer);
|
||||
}
|
||||
|
||||
/// Free any value.
|
||||
fn freeAnyValue(self: *Self, value: anytype) void
|
||||
{
|
||||
// Get type info of the current pointer.
|
||||
const typeInfo = @typeInfo(@TypeOf(value));
|
||||
|
||||
switch (typeInfo)
|
||||
{
|
||||
.Pointer => |pointerInfo| {
|
||||
// Can be a slice or a simple pointer.
|
||||
if (pointerInfo.size == .One)
|
||||
{ // It's a simple pointer, freeing its value recursively.
|
||||
self.freeAnyPointer(value);
|
||||
}
|
||||
else
|
||||
{ // It's a slice, free every item then free it.
|
||||
for (value) |item|
|
||||
{ // For each item, free it recursively.
|
||||
self.freeAnyValue(item);
|
||||
}
|
||||
|
||||
// Free the current pointer.
|
||||
self.allocator.free(value);
|
||||
}
|
||||
},
|
||||
else => {
|
||||
// Otherwise, we consider it as a simple value, nothing to free.
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Deinitialize the collection of values and all its values.
|
||||
pub fn deinit(self: *Self) void
|
||||
{
|
||||
// Deinitialize all items.
|
||||
for (self.items) |item|
|
||||
{ // For each items, try to free it.
|
||||
self.freeAnyPointer(item);
|
||||
}
|
||||
|
||||
// Free items slice.
|
||||
self.allocator.free(self.items);
|
||||
|
||||
// Destroy the current collection.
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
};
|
||||
}
|
1
src/root.zig
Normal file
1
src/root.zig
Normal file
|
@ -0,0 +1 @@
|
|||
pub const Collection = @import("collection.zig").Collection;
|
85
tests/collection.zig
Normal file
85
tests/collection.zig
Normal file
|
@ -0,0 +1,85 @@
|
|||
const std = @import("std");
|
||||
const zollections = @import("zollections");
|
||||
|
||||
/// An example structure.
|
||||
const ExampleStruct = struct {
|
||||
const Self = @This();
|
||||
|
||||
allocator: std.mem.Allocator,
|
||||
buffer: []u8,
|
||||
|
||||
/// Initialize a new example struct.
|
||||
pub fn init(bufSiz: usize) !Self
|
||||
{
|
||||
const allocator = std.testing.allocator;
|
||||
|
||||
return .{
|
||||
.allocator = allocator,
|
||||
.buffer = try allocator.alloc(u8, bufSiz),
|
||||
};
|
||||
}
|
||||
|
||||
/// Deinitialize the example struct.
|
||||
pub fn deinit(self: *Self) void
|
||||
{
|
||||
self.allocator.free(self.buffer);
|
||||
}
|
||||
};
|
||||
|
||||
test "simple collection" {
|
||||
const allocator = std.testing.allocator;
|
||||
|
||||
// Allocate your slice.
|
||||
const slice = try allocator.alloc(*u8, 3);
|
||||
// Create your slice elements.
|
||||
slice[0] = try allocator.create(u8);
|
||||
slice[1] = try allocator.create(u8);
|
||||
slice[2] = try allocator.create(u8);
|
||||
|
||||
// Create a collection with your slice of elements.
|
||||
const collection = try zollections.Collection(u8).init(allocator, slice);
|
||||
// Free your collection: your slice and all your elements will be freed.
|
||||
defer collection.deinit();
|
||||
}
|
||||
|
||||
test "recursive free" {
|
||||
const allocator = std.testing.allocator;
|
||||
|
||||
// Create a pointer to a slice.
|
||||
const slicePointer = try allocator.create([]*u8);
|
||||
|
||||
// Allocate your slice in the pointed slice.
|
||||
slicePointer.* = try allocator.alloc(*u8, 3);
|
||||
// Create slice elements.
|
||||
slicePointer.*[0] = try allocator.create(u8);
|
||||
slicePointer.*[1] = try allocator.create(u8);
|
||||
slicePointer.*[2] = try allocator.create(u8);
|
||||
|
||||
// Allocate your slice or pointers to slices.
|
||||
const slice = try allocator.alloc(*[]*u8, 1);
|
||||
slice[0] = slicePointer;
|
||||
|
||||
// Create a collection with your slice of elements.
|
||||
const collection = try zollections.Collection([]*u8).init(allocator, slice);
|
||||
// Free your collection: your slice and all your slices and their elements will be freed.
|
||||
defer collection.deinit();
|
||||
}
|
||||
|
||||
test "custom struct deinit" {
|
||||
const allocator = std.testing.allocator;
|
||||
|
||||
// Allocate your slice.
|
||||
const slice = try allocator.alloc(*ExampleStruct, 3);
|
||||
// Create your slice elements with custom structs and their inner init / deinit.
|
||||
slice[0] = try allocator.create(ExampleStruct);
|
||||
slice[0].* = try ExampleStruct.init(4);
|
||||
slice[1] = try allocator.create(ExampleStruct);
|
||||
slice[1].* = try ExampleStruct.init(8);
|
||||
slice[2] = try allocator.create(ExampleStruct);
|
||||
slice[2].* = try ExampleStruct.init(16);
|
||||
|
||||
// Create a collection with your slice of elements.
|
||||
const collection = try zollections.Collection(ExampleStruct).init(allocator, slice);
|
||||
// Free your collection: your slice and all your elements will be deinitialized and freed.
|
||||
defer collection.deinit();
|
||||
}
|
3
tests/root.zig
Normal file
3
tests/root.zig
Normal file
|
@ -0,0 +1,3 @@
|
|||
comptime {
|
||||
_ = @import("collection.zig");
|
||||
}
|
Loading…
Reference in a new issue