@@ -27,6 +27,53 @@ pub(crate) fn looks_like_cid(bytes: &[u8]) -> bool {
2727 bytes. len ( ) == 34 && bytes[ 0 ] == 0x12 && bytes[ 1 ] == 0x20
2828}
2929
30+ // Minimal-encoding unsigned varint, as `unsigned_varint::decode::u64`:
31+ // ≤10 bytes, last byte of a multi-byte varint must be non-zero.
32+ #[ inline]
33+ fn read_varint ( bytes : & [ u8 ] , pos : & mut usize ) -> Option < u64 > {
34+ let mut n: u64 = 0 ;
35+ for i in 0 ..10 {
36+ let & b = bytes. get ( * pos + i) ?;
37+ n |= ( ( b & 0x7f ) as u64 ) << ( i * 7 ) ;
38+ if b & 0x80 == 0 {
39+ if b == 0 && i > 0 {
40+ return None ;
41+ }
42+ * pos += i + 1 ;
43+ return Some ( n) ;
44+ }
45+ }
46+ None
47+ }
48+
49+ /// Structural check that `bytes` starts with a valid binary CID; returns
50+ /// `(consumed, codec)`. Accepts exactly what `::cid::Cid::try_from` accepts
51+ /// (trailing bytes are the caller's concern) but skips the `Multihash`
52+ /// construction and its 64-byte digest copy.
53+ #[ inline]
54+ pub ( crate ) fn parse_cid_prefix ( bytes : & [ u8 ] ) -> Option < ( usize , u64 ) > {
55+ let mut pos = 0 ;
56+ let version = read_varint ( bytes, & mut pos) ?;
57+ let codec = read_varint ( bytes, & mut pos) ?;
58+
59+ // CIDv0: `0x12 0x20` + 32-byte sha2-256 digest; codec is implicitly dag-pb
60+ if ( version, codec) == ( 0x12 , 0x20 ) {
61+ let end = pos + 32 ;
62+ return ( bytes. len ( ) >= end) . then_some ( ( end, 0x70 ) ) ;
63+ }
64+ if version != 1 {
65+ return None ;
66+ }
67+
68+ read_varint ( bytes, & mut pos) ?; // multihash code
69+ let hash_size = read_varint ( bytes, & mut pos) ?;
70+ if hash_size > 64 {
71+ return None ;
72+ }
73+ let end = pos + hash_size as usize ;
74+ ( bytes. len ( ) >= end) . then_some ( ( end, codec) )
75+ }
76+
3077pub ( crate ) fn extract_cid ( data : & Bound < PyAny > ) -> PyResult < :: cid:: Cid > {
3178 let cid = if let Ok ( s) = data. cast :: < PyString > ( ) {
3279 :: cid:: Cid :: try_from ( s. to_str ( ) ?)
0 commit comments