mirror of
				https://github.com/NotAShelf/zid.git
				synced 2025-10-31 19:12:40 +00:00 
			
		
		
		
	Initial commit
This commit is contained in:
		
				commit
				
					
						3bc4b2b4a1
					
				
			
		
					 6 changed files with 285 additions and 0 deletions
				
			
		
							
								
								
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | |||
| zig-cache/ | ||||
							
								
								
									
										28
									
								
								LICENSE
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								LICENSE
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,28 @@ | |||
| BSD 3-Clause License | ||||
| 
 | ||||
| Copyright (c) 2024, raf | ||||
| 
 | ||||
| Redistribution and use in source and binary forms, with or without | ||||
| modification, are permitted provided that the following conditions are met: | ||||
| 
 | ||||
| 1. Redistributions of source code must retain the above copyright notice, this | ||||
|    list of conditions and the following disclaimer. | ||||
| 
 | ||||
| 2. Redistributions in binary form must reproduce the above copyright notice, | ||||
|    this list of conditions and the following disclaimer in the documentation | ||||
|    and/or other materials provided with the distribution. | ||||
| 
 | ||||
| 3. Neither the name of the copyright holder nor the names of its | ||||
|    contributors may be used to endorse or promote products derived from | ||||
|    this software without specific prior written permission. | ||||
| 
 | ||||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||
| AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||
| IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||
| DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||||
| FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||
| DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||
| SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||
| CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||||
| OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
							
								
								
									
										67
									
								
								README.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								README.md
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,67 @@ | |||
| # Zid | ||||
| 
 | ||||
| Zid is a Content Identifier (CID) Trie implementation in Zig. | ||||
| 
 | ||||
| ## Features | ||||
| 
 | ||||
| - Trie structure for fast access to identifiers based on their byte values. | ||||
| - Proper manages memory allocation and deallocation. | ||||
| - Custom error types for better error reporting and handling. | ||||
| 
 | ||||
| ## Building | ||||
| 
 | ||||
| 1. Get the Zig compiler appropriate to your distribution (Generally, if you are | ||||
|    sane, `nix-shell -p zig`) will do the trick.) | ||||
| 2. Clone the project | ||||
| 3. `zig build` or `zig build-exe src/main.zig` (former is recommended, but | ||||
|    doesn't matter) | ||||
| 
 | ||||
| ## Usage | ||||
| 
 | ||||
| <!-- deno-fmt-ignore-start --> | ||||
| 
 | ||||
| > [!CAUTION] | ||||
| > Zid, for the time being, should be considered highly unstable and must be | ||||
| > avoided in actual projects, unless you know what you are doing. | ||||
| 
 | ||||
| <!-- deno-fmt-ignore-end --> | ||||
| 
 | ||||
| ### Adding Identifiers | ||||
| 
 | ||||
| To add an identifier to the trie, use the `add` method. Pass the identifier as a | ||||
| byte slice (i.e. `[]u8`): | ||||
| 
 | ||||
| ```zig | ||||
| try trie.add("your_identifier_here"); | ||||
| ``` | ||||
| 
 | ||||
| ### Searching for Identifiers | ||||
| 
 | ||||
| To search for an identifier in the trie, use the lookup method. Again, pass the | ||||
| identifier as a byte slice: | ||||
| 
 | ||||
| ```zig | ||||
| const result = try trie.lookup("identifier_to_search"); | ||||
| ``` | ||||
| 
 | ||||
| ## FAQ | ||||
| 
 | ||||
| [IPFS docs]: https://docs.ipfs.tech/concepts/content-addressing/#what-is-a-cid | ||||
| 
 | ||||
| ### What the hell is a CID? | ||||
| 
 | ||||
| See [IPFS docs]. My interpretation of a CID Trie is a tree-like data structure | ||||
| used for _efficiently_ storing and retrieving identifiers, with efficiency being | ||||
| the primary goal. | ||||
| 
 | ||||
| ### Why doesn't this build? | ||||
| 
 | ||||
| It's my first time building a Zig project. Call it a minor friction. | ||||
| 
 | ||||
| ## TODO | ||||
| 
 | ||||
| - Support for Multiple Tries | ||||
| - Batch Operations | ||||
| - Dynamic MaxCIDLen (it's currently a runtime constant) | ||||
| - Persistence Layer | ||||
| - Concurrency | ||||
							
								
								
									
										91
									
								
								build.zig
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								build.zig
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,91 @@ | |||
| const std = @import("std"); | ||||
| 
 | ||||
| // Although this function looks imperative, note that its job is to | ||||
| // declaratively construct a build graph that will be executed by an external | ||||
| // runner. | ||||
| 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.addStaticLibrary(.{ | ||||
|         .name = "zid", | ||||
|         // In this case the main source file is merely a path, however, in more | ||||
|         // complicated build scripts, this could be a generated file. | ||||
|         .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); | ||||
| 
 | ||||
|     const exe = b.addExecutable(.{ | ||||
|         .name = "zid", | ||||
|         .root_source_file = b.path("src/main.zig"), | ||||
|         .target = target, | ||||
|         .optimize = optimize, | ||||
|     }); | ||||
| 
 | ||||
|     // This declares intent for the executable to be installed into the | ||||
|     // standard location when the user invokes the "install" step (the default | ||||
|     // step when running `zig build`). | ||||
|     b.installArtifact(exe); | ||||
| 
 | ||||
|     // This *creates* a Run step in the build graph, to be executed when another | ||||
|     // step is evaluated that depends on it. The next line below will establish | ||||
|     // such a dependency. | ||||
|     const run_cmd = b.addRunArtifact(exe); | ||||
| 
 | ||||
|     // By making the run step depend on the install step, it will be run from the | ||||
|     // installation directory rather than directly from within the cache directory. | ||||
|     // This is not necessary, however, if the application depends on other installed | ||||
|     // files, this ensures they will be present and in the expected location. | ||||
|     run_cmd.step.dependOn(b.getInstallStep()); | ||||
| 
 | ||||
|     // This allows the user to pass arguments to the application in the build | ||||
|     // command itself, like this: `zig build run -- arg1 arg2 etc` | ||||
|     if (b.args) |args| { | ||||
|         run_cmd.addArgs(args); | ||||
|     } | ||||
| 
 | ||||
|     // This creates a build step. It will be visible in the `zig build --help` menu, | ||||
|     // and can be selected like this: `zig build run` | ||||
|     // This will evaluate the `run` step rather than the default, which is "install". | ||||
|     const run_step = b.step("run", "Run the app"); | ||||
|     run_step.dependOn(&run_cmd.step); | ||||
| 
 | ||||
|     // 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("src/root.zig"), | ||||
|         .target = target, | ||||
|         .optimize = optimize, | ||||
|     }); | ||||
| 
 | ||||
|     const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests); | ||||
| 
 | ||||
|     const exe_unit_tests = b.addTest(.{ | ||||
|         .root_source_file = b.path("src/main.zig"), | ||||
|         .target = target, | ||||
|         .optimize = optimize, | ||||
|     }); | ||||
| 
 | ||||
|     const run_exe_unit_tests = b.addRunArtifact(exe_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); | ||||
|     test_step.dependOn(&run_exe_unit_tests.step); | ||||
| } | ||||
							
								
								
									
										13
									
								
								build.zig.zon
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								build.zig.zon
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,13 @@ | |||
| .{ | ||||
|     .name = "zid", | ||||
|     .version = "0.1.0", | ||||
|     .minimum_zig_version = "0.12.0", // not a hard-requirement, but I've built this package with 0.12 only. | ||||
| 
 | ||||
|     .paths = .{ | ||||
|         "build.zig", | ||||
|         "build.zig.zon", | ||||
|         "src", | ||||
|         "LICENSE", | ||||
|         "README.md", | ||||
|     }, | ||||
| } | ||||
							
								
								
									
										85
									
								
								src/main.zig
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								src/main.zig
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,85 @@ | |||
| const std = @import("std"); | ||||
| 
 | ||||
| pub var errCollision = std.errors.New("collisions"); | ||||
| pub var errNotFound = std.errors.New("not found"); | ||||
| 
 | ||||
| pub const maxCIDLen = 20; | ||||
| pub const CIDTrie = struct { | ||||
|     leaves: [256]?*CIDTrie, | ||||
|     end: bool, | ||||
| 
 | ||||
|     pub fn add(self: *CIDTrie, cid: []u8) !void { | ||||
|         if (cid.len > maxCIDLen) return error.CIDTooLong; | ||||
|         var trie = self; | ||||
|         for (cid) |b| { | ||||
|             if (trie.leaves[b] == null) { | ||||
|                 trie.leaves[b] = try std.heap.page_allocator.alloc(CIDTrie); | ||||
|                 trie.leaves[b].?.end = false; | ||||
|             } else if (trie.leaves[b].?.end) { | ||||
|                 return errCollision; | ||||
|             } | ||||
|             if (cid.len == 1) { | ||||
|                 for (trie.leaves[b].?.leaves) |leaf| { | ||||
|                     if (leaf != null) return errCollision; | ||||
|                 } | ||||
|                 trie.leaves[b].?.end = true; | ||||
|             } | ||||
|             trie = trie.leaves[b]; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn lookup(self: *CIDTrie, data: []u8) ![]u8 { | ||||
|         if (data.len > maxCIDLen) return error.CIDTooLong; | ||||
|         if (self.leaves[data[0]] == null) return errNotFound; | ||||
|         var nextLevel = self; | ||||
|         var currentIndex: usize = 0; | ||||
| 
 | ||||
|         for (data) |b| { | ||||
|             nextLevel = nextLevel.leaves[b]; | ||||
|             if (nextLevel.? != null and nextLevel.?.end) { | ||||
|                 return data[0..currentIndex]; | ||||
|             } | ||||
|             currentIndex += 1; | ||||
|         } | ||||
|         return errNotFound; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| pub fn main() void { | ||||
|     var allocator = std.heap.page_allocator; | ||||
| 
 | ||||
|     const trie = allocator.create(CIDTrie) catch |err| { | ||||
|         std.debug.print("Failed to allocate memory: {}\n", .{err}); | ||||
|         return; | ||||
|     }; | ||||
|     defer allocator.destroy(trie); | ||||
| 
 | ||||
|     // add entries | ||||
|     const addResult = trie.add("hello"[0..]) catch |err| { | ||||
|         std.debug.print("Failed to add 'hello': {}\n", .{err}); | ||||
|         return; | ||||
|     }; | ||||
| 
 | ||||
|     std.debug.print("Added 'hello': {}\n", .{addResult}); // this assumes add returns a meaningful value, alternative is to remove | ||||
| 
 | ||||
|     // lookup entries | ||||
|     const lookupHelloResult = trie.lookup("hello"[0..]) catch |err| { | ||||
|         std.debug.print("Failed to lookup 'hello': {}\n", .{err}); | ||||
|         return; | ||||
|     }; | ||||
|     std.debug.print("Found 'hello': {}\n", .{lookupHelloResult}); | ||||
| 
 | ||||
|     const lookupWorldResult = trie.lookup("world"[0..]) catch |err| { | ||||
|         std.debug.print("Failed to lookup 'world': {}\n", .{err}); | ||||
|         return; | ||||
|     }; | ||||
|     std.debug.print("Found 'world': {}\n", .{lookupWorldResult}); | ||||
| 
 | ||||
|     // attempt to lookup a nonexistent entry | ||||
|     const lookupNonexistentResult = trie.lookup("nonexistent"[0..]); | ||||
|     if (lookupNonexistentResult) |result| { | ||||
|         std.debug.print("Unexpectedly found 'nonexistent': {}\n", .{result}); | ||||
|     } else |err| { | ||||
|         std.debug.print("Expected error for 'nonexistent': {}\n", .{err}); | ||||
|     } | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 raf
				raf