Create minimal collections structure.

This commit is contained in:
Madeorsk 2024-10-11 11:16:13 +02:00
commit d3278532af
Signed by: Madeorsk
GPG key ID: 677E51CA765BB79F
10 changed files with 491 additions and 0 deletions

7
.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
# IntelliJ IDEA
*.iml
.idea/
# Zig
.zig-cache/
zig-out/

9
LICENSE Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View file

@ -0,0 +1 @@
pub const Collection = @import("collection.zig").Collection;

85
tests/collection.zig Normal file
View 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
View file

@ -0,0 +1,3 @@
comptime {
_ = @import("collection.zig");
}