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