@@ -3,6 +3,7 @@ const Allocator = std.mem.Allocator;
33const Multicodec = @import ("multicodec.zig" ).Multicodec ;
44const Multihash = @import ("multihash.zig" ).Multihash ;
55const varint = @import ("unsigned_varint.zig" );
6+ const MultiBaseCodec = @import ("multibase.zig" ).MultiBaseCodec ;
67
78pub const Error = error {
89 UnknownCodec ,
@@ -48,29 +49,31 @@ pub fn Cid(comptime S: usize) type {
4849 hash : Multihash (S ),
4950 allocator : Allocator ,
5051
51- pub fn newV0 (allocator : Allocator , hash : Multihash (32 )) ! Cid {
52+ const Self = @This ();
53+
54+ pub fn newV0 (allocator : Allocator , hash : Multihash (32 )) ! Self {
5255 if (hash .getCode () != Multicodec .SHA2_256 or hash .getSize () != 32 ) {
5356 return Error .InvalidCidV0Multihash ;
5457 }
5558
56- return Cid {
59+ return Cid ( 32 ) {
5760 .version = .V0 ,
5861 .codec = Multicodec .DAG_PB .getCode (),
5962 .hash = hash ,
6063 .allocator = allocator ,
6164 };
6265 }
6366
64- pub fn newV1 (allocator : Allocator , codec : u64 , hash : Multihash (S )) ! Cid {
65- return Cid {
67+ pub fn newV1 (allocator : Allocator , codec : u64 , hash : Multihash (S )) ! Self {
68+ return Cid ( S ) {
6669 .version = .V1 ,
6770 .codec = codec ,
6871 .hash = hash ,
6972 .allocator = allocator ,
7073 };
7174 }
7275
73- pub fn init (allocator : Allocator , version : CidVersion , codec : u64 , hash : Multihash (S )) ! @This () {
76+ pub fn init (allocator : Allocator , version : CidVersion , codec : u64 , hash : Multihash (S )) ! Self {
7477 switch (version ) {
7578 .V0 = > {
7679 if (codec != Multicodec .DAG_PB .getCode ()) {
@@ -85,15 +88,43 @@ pub fn Cid(comptime S: usize) type {
8588 }
8689 }
8790
88- pub fn readBytes (allocator : Allocator , reader : anytype ) ! Cid {
91+ /// Checks if two CIDs are equal by comparing version, codec and hash
92+ pub fn isEqual (self : * const Self , other : * const Self ) bool {
93+ return self .version == other .version and
94+ self .codec == other .codec and
95+ std .mem .eql (u8 , self .hash .getDigest (), other .hash .getDigest ());
96+ }
97+
98+ pub fn writeBytesV1 (self : * const Self , writer : anytype ) ! usize {
99+ const version_written = try varint .encode_stream (writer , u64 , self .version .toInt ());
100+ const codec_written = try varint .encode_stream (writer , u64 , self .codec );
101+
102+ var written : usize = version_written + codec_written ;
103+ written += try self .hash .write (writer );
104+ return written ;
105+ }
106+
107+ pub fn intoV1 (self : Self ) ! Self {
108+ return switch (self .version ) {
109+ .V0 = > {
110+ if (self .codec != @intFromEnum (Multicodec .DAG_PB )) {
111+ return Error .InvalidCidV0Codec ;
112+ }
113+ return newV1 (self .allocator , self .codec , self .hash );
114+ },
115+ .V1 = > self ,
116+ };
117+ }
118+
119+ pub fn readBytes (allocator : Allocator , reader : anytype ) ! Self {
89120 const version = try varint .decode_stream (reader , u64 );
90121 const codec = try varint .decode_stream (reader , u64 );
91122
92- // CIDv0 has the fixed `0x12 0x20` prefix
93123 if (version == 0x12 and codec == 0x20 ) {
94124 var digest : [32 ]u8 = undefined ;
95125 try reader .readNoEof (& digest );
96- const mh = try Multihash (32 ).wrap (version , & digest );
126+ const version_codec = try Multicodec .fromCode (version );
127+ const mh = try Multihash (32 ).wrap (version_codec , & digest );
97128 return newV0 (allocator , mh );
98129 }
99130
@@ -102,123 +133,129 @@ pub fn Cid(comptime S: usize) type {
102133 .V0 = > return Error .InvalidExplicitCidV0 ,
103134 .V1 = > {
104135 const mh = try Multihash (32 ).read (reader );
105- return Cid .init (allocator , ver , codec , mh . getDigest () );
136+ return Self .init (allocator , ver , codec , mh );
106137 },
107138 }
108139 }
109140
110- fn writeBytesV1 (self : * const Cid , writer : anytype ) ! usize {
111- const version_written = try varint .encode_stream (writer ,u64 ,self .version .toInt ());
112- const codec_written = try varint .encode_stream (writer ,u64 ,self .codec );
141+ pub fn writeBytes (self : * const Self , writer : anytype ) ! usize {
142+ return switch (self .version ) {
143+ .V0 = > try self .hash .write (writer ),
144+ .V1 = > try self .writeBytesV1 (writer ),
145+ };
146+ }
113147
114- const written : usize = version_written + codec_written ;
148+ pub fn encodedLen (self : Self ) usize {
149+ return switch (self .version ) {
150+ .V0 = > self .hash .encodedLen (),
151+ .V1 = > {
152+ var version_buf : [varint .bufferSize (u64 )]u8 = undefined ;
153+ const version = varint .encode (u64 , self .version .toInt (), & version_buf );
115154
116- return written ;
155+ var codec_buf : [varint .bufferSize (u64 )]u8 = undefined ;
156+ const codec = varint .encode (u64 , self .codec , & codec_buf );
157+
158+ return version .len + codec .len + self .hash .encodedLen ();
159+ },
160+ };
161+ }
162+
163+ pub fn toBytes (self : * const Self ) ! []u8 {
164+ var bytes = std .ArrayList (u8 ).init (self .allocator );
165+ errdefer bytes .deinit ();
166+
167+ const written = try self .writeBytes (bytes .writer ());
168+ std .debug .assert (written == bytes .items .len );
169+
170+ return bytes .toOwnedSlice ();
171+ }
172+
173+ pub fn getHash (self : * const Self ) []const u8 {
174+ return self .hash .getDigest ();
117175 }
118176
177+ pub fn getCodec (self : * const Self ) u64 {
178+ return self .codec ;
179+ }
180+
181+ pub fn getVersion (self : * const Self ) CidVersion {
182+ return self .version ;
183+ }
184+
185+ fn toStringV0 (self : * const Self ) ! []const u8 {
186+ const hash_bytes = try self .hash .toBytes ();
187+ var bytes = std .ArrayList (u8 ).init (self .allocator );
188+ errdefer bytes .deinit ();
189+ return MultiBaseCodec .Base58Btc .encode (bytes .items , hash_bytes );
190+ }
191+
192+ fn to_string_v1 (self : * const Self ) ! []u8 {
193+ const bytes = try self .toBytes (self .allocator );
194+ defer self .allocator .free (bytes );
195+
196+ const dest = std .ArrayList (u8 ).init (self .allocator );
197+ return MultiBaseCodec .Base32Lower .encode (dest .items , bytes );
198+ }
119199 };
120200}
121201
122- // test "CID basic operations" {
123- // const testing = std.testing;
124- // const allocator = testing.allocator;
125- //
126- // // Test CIDv0
127- // {
128- // var hash = [_]u8{ 0x12, 0x20 } ++ [_]u8{1} ** 32;
129- // var cid = try Cid.init(allocator, .V0, .DagPb, hash[2..]);
130- // defer cid.deinit();
131- //
132- // try testing.expect(cid.version == .V0);
133- // try testing.expect(cid.codec == .DagPb);
134- // try testing.expectEqualSlices(u8, hash[2..], cid.hash);
135- //
136- // // Test toBytes
137- // const bytes = try cid.toBytes();
138- // defer allocator.free(bytes);
139- // try testing.expectEqualSlices(u8, &hash, bytes);
140- // }
141- //
142- // // Test CIDv1
143- // {
144- // var hash = [_]u8{1} ** 32;
145- // var cid = try Cid.init(allocator, .V1, .DagCbor, &hash);
146- // defer cid.deinit();
147- //
148- // try testing.expect(cid.version == .V1);
149- // try testing.expect(cid.codec == .DagCbor);
150- // try testing.expectEqualSlices(u8, &hash, cid.hash);
151- // }
152- // }
153- //
154- // test "CID fromBytes" {
155- // const testing = std.testing;
156- // const allocator = testing.allocator;
157- //
158- // // Test CIDv0 parsing
159- // {
160- // var input = [_]u8{ 0x12, 0x20 } ++ [_]u8{1} ** 32;
161- // var cid = try Cid.fromBytes(allocator, &input);
162- // defer cid.deinit();
163- //
164- // try testing.expect(cid.version == .V0);
165- // try testing.expect(cid.codec == .DagPb);
166- // try testing.expectEqualSlices(u8, input[2..], cid.hash);
167- // }
168- //
169- // // Test CIDv1 parsing
170- // {
171- // var input = [_]u8{ 1, @intFromEnum(Codec.DagCbor) } ++ [_]u8{1} ** 32;
172- // var cid = try Cid.fromBytes(allocator, &input);
173- // defer cid.deinit();
174- //
175- // try testing.expect(cid.version == .V1);
176- // try testing.expect(cid.codec == .DagCbor);
177- // try testing.expectEqualSlices(u8, input[2..], cid.hash);
178- // }
179- // }
180- //
181- // test "CID error cases" {
182- // const testing = std.testing;
183- // const allocator = testing.allocator;
184- //
185- // // Test invalid V0 codec
186- // {
187- // var hash = [_]u8{1} ** 32;
188- // try testing.expectError(Error.InvalidCidV0Codec, Cid.init(allocator, .V0, .DagCbor, &hash));
189- // }
190- //
191- // // Test input too short
192- // {
193- // var input = [_]u8{1};
194- // try testing.expectError(Error.InputTooShort, Cid.fromBytes(allocator, &input));
195- // }
196- //
197- // // Test unknown codec
198- // {
199- // var input = [_]u8{ 1, 0xFF } ++ [_]u8{1} ** 32;
200- // try testing.expectError(Error.UnknownCodec, Cid.fromBytes(allocator, &input));
201- // }
202- // }
203- //
204- // test "CID version checks" {
205- // const testing = std.testing;
206- //
207- // // Test V0 string detection
208- // {
209- // const v0_str = "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n";
210- // try testing.expect(CidVersion.isV0Str(v0_str));
211- //
212- // const invalid_str = "invalid";
213- // try testing.expect(!CidVersion.isV0Str(invalid_str));
214- // }
215- //
216- // // Test V0 binary detection
217- // {
218- // var valid_bytes = [_]u8{ 0x12, 0x20 } ++ [_]u8{1} ** 32;
219- // try testing.expect(CidVersion.isV0Binary(&valid_bytes));
220- //
221- // var invalid_bytes = [_]u8{ 0x00, 0x00 } ++ [_]u8{1} ** 32;
222- // try testing.expect(!CidVersion.isV0Binary(&invalid_bytes));
223- // }
224- // }
202+ test "Cid" {
203+ const testing = std .testing ;
204+ const allocator = testing .allocator ;
205+
206+ // Test CIDv0
207+ {
208+ const hash = try Multihash (32 ).wrap (Multicodec .SHA2_256 , &[_ ]u8 {0 } ** 32 );
209+ const cid = try Cid (32 ).newV0 (allocator , hash );
210+ try testing .expectEqual (cid .version , .V0 );
211+ try testing .expectEqual (cid .codec , Multicodec .DAG_PB .getCode ());
212+ }
213+
214+ // Test CIDv1
215+ {
216+ const hash = try Multihash (64 ).wrap (Multicodec .SHA2_256 , &[_ ]u8 {0 } ** 32 );
217+ const cid = try Cid (64 ).newV1 (allocator , Multicodec .RAW .getCode (), hash );
218+ try testing .expectEqual (cid .version , .V1 );
219+ try testing .expectEqual (cid .codec , Multicodec .RAW .getCode ());
220+ }
221+
222+ // Test encoding/decoding
223+ {
224+ const hash = try Multihash (32 ).wrap (Multicodec .SHA2_256 , &[_ ]u8 {0 } ** 32 );
225+ const original = try Cid (32 ).newV1 (allocator , Multicodec .RAW .getCode (), hash );
226+
227+ const bytes = try original .toBytes ();
228+ defer allocator .free (bytes );
229+
230+ var fbs = std .io .fixedBufferStream (bytes );
231+ const decoded = try Cid (32 ).readBytes (allocator , fbs .reader ());
232+
233+ try testing .expect (original .isEqual (& decoded ));
234+ }
235+ }
236+
237+ test "Cid conversion and comparison" {
238+ const testing = std .testing ;
239+ const allocator = testing .allocator ;
240+
241+ // Test V0 to V1 conversion
242+ {
243+ const hash = try Multihash (32 ).wrap (Multicodec .SHA2_256 , &[_ ]u8 {0 } ** 32 );
244+ const v0 = try Cid (32 ).newV0 (allocator , hash );
245+ const v1 = try v0 .intoV1 ();
246+
247+ try testing .expectEqual (v1 .version , .V1 );
248+ try testing .expectEqual (v1 .codec , v0 .codec );
249+ try testing .expect (std .mem .eql (u8 , v1 .getHash (), v0 .getHash ()));
250+ }
251+
252+ // Test encoded length
253+ {
254+ const hash = try Multihash (32 ).wrap (Multicodec .SHA2_256 , &[_ ]u8 {0 } ** 32 );
255+ const cid = try Cid (32 ).newV1 (allocator , Multicodec .RAW .getCode (), hash );
256+ const bytes = try cid .toBytes ();
257+ defer allocator .free (bytes );
258+
259+ try testing .expectEqual (cid .encodedLen (), bytes .len );
260+ }
261+ }
0 commit comments