mirror of
				https://github.com/NotAShelf/zid.git
				synced 2025-10-31 11:02:38 +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