11const std = @import ("std" );
22const Allocator = std .mem .Allocator ;
3- const testing = std .testing ;
3+
4+ pub const Error = error {
5+ UnknownCodec ,
6+ InputTooShort ,
7+ ParsingError ,
8+ InvalidCidVersion ,
9+ InvalidCidV0Codec ,
10+ InvalidCidV0Multihash ,
11+ InvalidCidV0Base ,
12+ VarIntDecodeError ,
13+ InvalidExplicitCidV0 ,
14+ };
415
516pub const CidVersion = enum (u64 ) {
617 V0 = 0 ,
718 V1 = 1 ,
19+
20+ pub fn isV0Str (data : []const u8 ) bool {
21+ return data .len == 46 and std .mem .startsWith (u8 , data , "Qm" );
22+ }
23+
24+ pub fn isV0Binary (data : []const u8 ) bool {
25+ return data .len == 34 and std .mem .startsWith (u8 , data , &[_ ]u8 { 0x12 , 0x20 });
26+ }
827};
928
1029pub const Codec = enum (u64 ) {
1130 Raw = 0x55 ,
1231 DagPb = 0x70 ,
1332 DagCbor = 0x71 ,
33+
34+ pub fn fromInt (value : u64 ) Error ! Codec {
35+ return switch (value ) {
36+ 0x55 = > .Raw ,
37+ 0x70 = > .DagPb ,
38+ 0x71 = > .DagCbor ,
39+ else = > Error .UnknownCodec ,
40+ };
41+ }
1442};
1543
1644pub const Cid = struct {
@@ -20,6 +48,10 @@ pub const Cid = struct {
2048 allocator : Allocator ,
2149
2250 pub fn init (allocator : Allocator , version : CidVersion , codec : Codec , hash : []const u8 ) ! Cid {
51+ if (version == .V0 and codec != .DagPb ) {
52+ return Error .InvalidCidV0Codec ;
53+ }
54+
2355 const hash_copy = try allocator .dupe (u8 , hash );
2456 return Cid {
2557 .version = version ,
@@ -29,56 +61,144 @@ pub const Cid = struct {
2961 };
3062 }
3163
64+ pub fn fromBytes (allocator : Allocator , bytes : []const u8 ) ! Cid {
65+ if (CidVersion .isV0Binary (bytes )) {
66+ return Cid .init (allocator , .V0 , .DagPb , bytes [2.. ]);
67+ }
68+
69+ if (bytes .len < 2 ) {
70+ return Error .InputTooShort ;
71+ }
72+
73+ const version = try std .meta .intToEnum (CidVersion , bytes [0 ]);
74+ const codec = try Codec .fromInt (bytes [1 ]);
75+
76+ return Cid .init (allocator , version , codec , bytes [2.. ]);
77+ }
78+
3279 pub fn deinit (self : * Cid ) void {
3380 self .allocator .free (self .hash );
3481 }
3582
36- pub fn format (self : Cid , comptime fmt : []const u8 , options : std.fmt.FormatOptions , writer : anytype ) ! void {
37- _ = fmt ;
38- _ = options ;
83+ pub fn toBytes (self : Cid ) ! []u8 {
84+ var buf = try self .allocator .alloc (u8 , 2 + self .hash .len );
3985
4086 switch (self .version ) {
4187 .V0 = > {
42- if (self .codec != .DagPb ) {
43- return error .InvalidV0Codec ;
44- }
45- try writer .writeAll (try std .fmt .allocPrint (self .allocator , "Qm{}" , .{std .fmt .fmtSliceHexLower (self .hash )}));
88+ buf [0 ] = 0x12 ;
89+ buf [1 ] = 0x20 ;
4690 },
4791 .V1 = > {
48- try writer .writeAll (try std .fmt .allocPrint (
49- self .allocator ,
50- "b{}{}{}" ,
51- .{
52- @intFromEnum (self .version ),
53- @intFromEnum (self .codec ),
54- std .fmt .fmtSliceHexLower (self .hash ),
55- },
56- ));
92+ buf [0 ] = @as (u8 , @intCast (@intFromEnum (self .version )));
93+ buf [1 ] = @as (u8 , @intCast (@intFromEnum (self .codec )));
5794 },
5895 }
96+
97+ std .mem .copyForwards (u8 , buf [2.. ], self .hash );
98+ return buf ;
5999 }
60100};
61101
62- test "CID v0" {
102+ test "CID basic operations" {
103+ const testing = std .testing ;
63104 const allocator = testing .allocator ;
64105
65- var hash = [_ ]u8 { 1 , 2 , 3 , 4 , 5 };
66- var cid = try Cid .init (allocator , .V0 , .DagPb , & hash );
67- defer cid .deinit ();
106+ // Test CIDv0
107+ {
108+ var hash = [_ ]u8 { 0x12 , 0x20 } ++ [_ ]u8 {1 } ** 32 ;
109+ var cid = try Cid .init (allocator , .V0 , .DagPb , hash [2.. ]);
110+ defer cid .deinit ();
111+
112+ try testing .expect (cid .version == .V0 );
113+ try testing .expect (cid .codec == .DagPb );
114+ try testing .expectEqualSlices (u8 , hash [2.. ], cid .hash );
115+
116+ // Test toBytes
117+ const bytes = try cid .toBytes ();
118+ defer allocator .free (bytes );
119+ try testing .expectEqualSlices (u8 , & hash , bytes );
120+ }
121+
122+ // Test CIDv1
123+ {
124+ var hash = [_ ]u8 {1 } ** 32 ;
125+ var cid = try Cid .init (allocator , .V1 , .DagCbor , & hash );
126+ defer cid .deinit ();
68127
69- try testing .expect (cid .version == .V0 );
70- try testing .expect (cid .codec == .DagPb );
71- try testing .expectEqualSlices (u8 , & hash , cid .hash );
128+ try testing .expect (cid .version == .V1 );
129+ try testing .expect (cid .codec == .DagCbor );
130+ try testing .expectEqualSlices (u8 , & hash , cid .hash );
131+ }
72132}
73133
74- test "CID v1" {
134+ test "CID fromBytes" {
135+ const testing = std .testing ;
75136 const allocator = testing .allocator ;
76137
77- var hash = [_ ]u8 { 1 , 2 , 3 , 4 , 5 };
78- var cid = try Cid .init (allocator , .V1 , .DagCbor , & hash );
79- defer cid .deinit ();
138+ // Test CIDv0 parsing
139+ {
140+ var input = [_ ]u8 { 0x12 , 0x20 } ++ [_ ]u8 {1 } ** 32 ;
141+ var cid = try Cid .fromBytes (allocator , & input );
142+ defer cid .deinit ();
143+
144+ try testing .expect (cid .version == .V0 );
145+ try testing .expect (cid .codec == .DagPb );
146+ try testing .expectEqualSlices (u8 , input [2.. ], cid .hash );
147+ }
148+
149+ // Test CIDv1 parsing
150+ {
151+ var input = [_ ]u8 { 1 , @intFromEnum (Codec .DagCbor ) } ++ [_ ]u8 {1 } ** 32 ;
152+ var cid = try Cid .fromBytes (allocator , & input );
153+ defer cid .deinit ();
154+
155+ try testing .expect (cid .version == .V1 );
156+ try testing .expect (cid .codec == .DagCbor );
157+ try testing .expectEqualSlices (u8 , input [2.. ], cid .hash );
158+ }
159+ }
160+
161+ test "CID error cases" {
162+ const testing = std .testing ;
163+ const allocator = testing .allocator ;
164+
165+ // Test invalid V0 codec
166+ {
167+ var hash = [_ ]u8 {1 } ** 32 ;
168+ try testing .expectError (Error .InvalidCidV0Codec , Cid .init (allocator , .V0 , .DagCbor , & hash ));
169+ }
170+
171+ // Test input too short
172+ {
173+ var input = [_ ]u8 {1 };
174+ try testing .expectError (Error .InputTooShort , Cid .fromBytes (allocator , & input ));
175+ }
176+
177+ // Test unknown codec
178+ {
179+ var input = [_ ]u8 { 1 , 0xFF } ++ [_ ]u8 {1 } ** 32 ;
180+ try testing .expectError (Error .UnknownCodec , Cid .fromBytes (allocator , & input ));
181+ }
182+ }
183+
184+ test "CID version checks" {
185+ const testing = std .testing ;
80186
81- try testing .expect (cid .version == .V1 );
82- try testing .expect (cid .codec == .DagCbor );
83- try testing .expectEqualSlices (u8 , & hash , cid .hash );
187+ // Test V0 string detection
188+ {
189+ const v0_str = "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n" ;
190+ try testing .expect (CidVersion .isV0Str (v0_str ));
191+
192+ const invalid_str = "invalid" ;
193+ try testing .expect (! CidVersion .isV0Str (invalid_str ));
194+ }
195+
196+ // Test V0 binary detection
197+ {
198+ var valid_bytes = [_ ]u8 { 0x12 , 0x20 } ++ [_ ]u8 {1 } ** 32 ;
199+ try testing .expect (CidVersion .isV0Binary (& valid_bytes ));
200+
201+ var invalid_bytes = [_ ]u8 { 0x00 , 0x00 } ++ [_ ]u8 {1 } ** 32 ;
202+ try testing .expect (! CidVersion .isV0Binary (& invalid_bytes ));
203+ }
84204}
0 commit comments