@@ -18,9 +18,216 @@ pub mod self_profile;
1818
1919pub use self_profile:: { QueryData , SelfProfile } ;
2020
21+ #[ derive( Copy , Clone , PartialEq , Eq , PartialOrd , Ord , Hash ) ]
22+ pub enum Sha {
23+ /// Straight-up bytes of the 40-long hex-encoded sha
24+ Hex ( [ u8 ; 20 ] ) ,
25+ /// Arbitrary bytes, usually a string ID provided by the user.
26+ /// Encodes 30 characters by restricting to 5 bits per character
27+ /// (enough for 32 different characters, so `a-z`, `-`, and `_`).
28+ Raw { length : u8 , bytes : [ u8 ; 19 ] } ,
29+ }
30+
31+ impl PartialEq < str > for Sha {
32+ fn eq ( & self , other : & str ) -> bool {
33+ self . to_string ( ) == other
34+ }
35+ }
36+
37+ fn hex_decode ( s : & str ) -> Option < [ u8 ; 20 ] > {
38+ let mut in_progress = 0 ;
39+ let mut v = [ 0 ; 20 ] ;
40+ for ( idx, ch) in s. chars ( ) . enumerate ( ) {
41+ let offset = if idx % 2 == 0 { 4 } else { 0 } ;
42+ in_progress |= ( ch. to_digit ( 16 ) ? as u8 ) << offset;
43+ if idx % 2 != 0 {
44+ v[ idx / 2 ] = in_progress;
45+ in_progress = 0 ;
46+ }
47+ }
48+ Some ( v)
49+ }
50+
51+ struct BitView {
52+ bytes : [ u8 ; 19 ] ,
53+ bit_offset : usize ,
54+ }
55+
56+ impl BitView {
57+ fn new ( ) -> BitView {
58+ BitView {
59+ bytes : [ 0 ; 19 ] ,
60+ bit_offset : 0 ,
61+ }
62+ }
63+
64+ fn with_bytes ( bytes : [ u8 ; 19 ] ) -> BitView {
65+ BitView {
66+ bytes,
67+ bit_offset : 0 ,
68+ }
69+ }
70+
71+ fn read ( & mut self ) -> bool {
72+ let r = ( self . bytes [ self . bit_offset / 8 ] & ( 1 << ( self . bit_offset % 8 ) ) ) != 0 ;
73+ self . bit_offset += 1 ;
74+
75+ if self . bit_offset > 19 * 8 {
76+ panic ! ( "pushed past limit of 152 bits" ) ;
77+ }
78+
79+ r
80+ }
81+
82+ fn read5 ( & mut self ) -> u8 {
83+ let mut v = 0 ;
84+ v |= ( self . read ( ) as u8 ) << 0 ;
85+ v |= ( self . read ( ) as u8 ) << 1 ;
86+ v |= ( self . read ( ) as u8 ) << 2 ;
87+ v |= ( self . read ( ) as u8 ) << 3 ;
88+ v |= ( self . read ( ) as u8 ) << 4 ;
89+ v
90+ }
91+
92+ fn push ( & mut self , b : bool ) {
93+ if b {
94+ self . bytes [ self . bit_offset / 8 ] |= 1 << ( self . bit_offset % 8 ) ;
95+ }
96+ self . bit_offset += 1 ;
97+
98+ if self . bit_offset > 19 * 8 {
99+ panic ! ( "pushed past limit of 152 bits" ) ;
100+ }
101+ }
102+
103+ fn push5 ( & mut self , v : u8 ) {
104+ assert ! (
105+ v <= 32 ,
106+ "`{}` must be less than 32 (i.e., no more than 5 bits)" ,
107+ v
108+ ) ;
109+
110+ self . push ( v & 0b10000 != 0 ) ;
111+ self . push ( v & 0b01000 != 0 ) ;
112+ self . push ( v & 0b00100 != 0 ) ;
113+ self . push ( v & 0b00010 != 0 ) ;
114+ self . push ( v & 0b00001 != 0 ) ;
115+ }
116+ }
117+
118+ impl < ' a > From < & ' a str > for Sha {
119+ fn from ( s : & ' a str ) -> Sha {
120+ if let Some ( v) = hex_decode ( s) {
121+ return Sha :: Hex ( v) ;
122+ }
123+
124+ assert ! (
125+ s. len( ) <= 30 ,
126+ "`{}` is too long ({}), can be at most 30 bytes" ,
127+ s,
128+ s. len( ) ,
129+ ) ;
130+
131+ let mut v = BitView :: new ( ) ;
132+ for b in s. as_bytes ( ) . iter ( ) {
133+ let b = * b;
134+ match b {
135+ b'-' => {
136+ v. push5 ( 0 ) ;
137+ }
138+ b'4' => {
139+ v. push5 ( 1 ) ;
140+ }
141+ b'1' => {
142+ v. push5 ( 2 ) ;
143+ }
144+ b'2' => {
145+ v. push5 ( 3 ) ;
146+ }
147+ b'6' => {
148+ v. push5 ( 4 ) ;
149+ }
150+ b'8' => {
151+ v. push5 ( 5 ) ;
152+ }
153+ b'a' ..=b'z' => {
154+ v. push5 ( b - b'a' + 6 ) ;
155+ }
156+ _ => panic ! (
157+ "`{}` is not a valid character for SHA-like IDs, must be in a-z, or -, or _, in `{}`." ,
158+ b as char ,
159+ s,
160+ ) ,
161+ }
162+ }
163+ Sha :: Raw {
164+ length : s. len ( ) as u8 ,
165+ bytes : v. bytes ,
166+ }
167+ }
168+ }
169+
170+ impl Serialize for Sha {
171+ fn serialize < S > ( & self , serializer : S ) -> Result < S :: Ok , S :: Error >
172+ where
173+ S : serde:: ser:: Serializer ,
174+ {
175+ serializer. collect_str ( & self )
176+ }
177+ }
178+
179+ impl < ' de > Deserialize < ' de > for Sha {
180+ fn deserialize < D > ( deserializer : D ) -> Result < Self , D :: Error >
181+ where
182+ D : serde:: de:: Deserializer < ' de > ,
183+ {
184+ let s: & ' de str = <& ' de str >:: deserialize ( deserializer) ?;
185+ Ok ( s. into ( ) )
186+ }
187+ }
188+
189+ impl fmt:: Debug for Sha {
190+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
191+ write ! ( f, "{}" , self )
192+ }
193+ }
194+
195+ impl fmt:: Display for Sha {
196+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
197+ match * self {
198+ Sha :: Hex ( hex) => {
199+ for & b in hex. iter ( ) {
200+ write ! ( f, "{:x}{:x}" , b >> 4 , b & 0xf ) ?;
201+ }
202+ }
203+ Sha :: Raw { length, bytes } => {
204+ let mut v = BitView :: with_bytes ( bytes) ;
205+ let mut decoded = [ 0 ; 19 ] ;
206+ for idx in 0 ..length as usize {
207+ decoded[ idx] = match v. read5 ( ) {
208+ 0 => b'-' ,
209+ 1 => b'4' ,
210+ 2 => b'1' ,
211+ 3 => b'2' ,
212+ 4 => b'6' ,
213+ 5 => b'8' ,
214+ other => other - 6 + b'a' ,
215+ } ;
216+ }
217+ write ! (
218+ f,
219+ "{}" ,
220+ std:: str :: from_utf8( & decoded[ ..length as usize ] ) . unwrap( )
221+ ) ?;
222+ }
223+ }
224+ Ok ( ( ) )
225+ }
226+ }
227+
21228#[ derive( Debug , Clone , serde:: Deserialize , serde:: Serialize ) ]
22229pub struct Commit {
23- pub sha : String ,
230+ pub sha : Sha ,
24231 pub date : Date ,
25232}
26233
0 commit comments