Show More
@@ -1,630 +1,655 b'' | |||||
1 | use sha1::{Digest, Sha1}; |
|
1 | use sha1::{Digest, Sha1}; | |
2 |
|
2 | |||
3 | #[derive(PartialEq, Debug)] |
|
3 | #[derive(PartialEq, Debug)] | |
4 | #[allow(non_camel_case_types)] |
|
4 | #[allow(non_camel_case_types)] | |
5 | #[allow(clippy::upper_case_acronyms)] |
|
5 | #[allow(clippy::upper_case_acronyms)] | |
6 | enum path_state { |
|
6 | enum path_state { | |
7 | START, /* first byte of a path component */ |
|
7 | START, /* first byte of a path component */ | |
8 | A, /* "AUX" */ |
|
8 | A, /* "AUX" */ | |
9 | AU, |
|
9 | AU, | |
10 | THIRD, /* third of a 3-byte sequence, e.g. "AUX", "NUL" */ |
|
10 | THIRD, /* third of a 3-byte sequence, e.g. "AUX", "NUL" */ | |
11 | C, /* "CON" or "COMn" */ |
|
11 | C, /* "CON" or "COMn" */ | |
12 | CO, |
|
12 | CO, | |
13 | COMLPT, /* "COM" or "LPT" */ |
|
13 | COMLPT, /* "COM" or "LPT" */ | |
14 | COMLPTn, |
|
14 | COMLPTn, | |
15 | L, |
|
15 | L, | |
16 | LP, |
|
16 | LP, | |
17 | N, |
|
17 | N, | |
18 | NU, |
|
18 | NU, | |
19 | P, /* "PRN" */ |
|
19 | P, /* "PRN" */ | |
20 | PR, |
|
20 | PR, | |
21 | LDOT, /* leading '.' */ |
|
21 | LDOT, /* leading '.' */ | |
22 | DOT, /* '.' in a non-leading position */ |
|
22 | DOT, /* '.' in a non-leading position */ | |
23 | H, /* ".h" */ |
|
23 | H, /* ".h" */ | |
24 | HGDI, /* ".hg", ".d", or ".i" */ |
|
24 | HGDI, /* ".hg", ".d", or ".i" */ | |
25 | SPACE, |
|
25 | SPACE, | |
26 | DEFAULT, /* byte of a path component after the first */ |
|
26 | DEFAULT, /* byte of a path component after the first */ | |
27 | } |
|
27 | } | |
28 |
|
28 | |||
29 | /* state machine for dir-encoding */ |
|
29 | /* state machine for dir-encoding */ | |
30 | #[allow(non_camel_case_types)] |
|
30 | #[allow(non_camel_case_types)] | |
31 | #[allow(clippy::upper_case_acronyms)] |
|
31 | #[allow(clippy::upper_case_acronyms)] | |
32 | enum dir_state { |
|
32 | enum dir_state { | |
33 | DDOT, |
|
33 | DDOT, | |
34 | DH, |
|
34 | DH, | |
35 | DHGDI, |
|
35 | DHGDI, | |
36 | DDEFAULT, |
|
36 | DDEFAULT, | |
37 | } |
|
37 | } | |
38 |
|
38 | |||
39 | trait Sink { |
|
39 | trait Sink { | |
40 | fn write_byte(&mut self, c: u8); |
|
40 | fn write_byte(&mut self, c: u8); | |
41 | fn write_bytes(&mut self, c: &[u8]); |
|
41 | fn write_bytes(&mut self, c: &[u8]); | |
42 | } |
|
42 | } | |
43 |
|
43 | |||
44 | fn inset(bitset: &[u32; 8], c: u8) -> bool { |
|
44 | fn inset(bitset: &[u32; 8], c: u8) -> bool { | |
45 | bitset[(c as usize) >> 5] & (1 << (c & 31)) != 0 |
|
45 | bitset[(c as usize) >> 5] & (1 << (c & 31)) != 0 | |
46 | } |
|
46 | } | |
47 |
|
47 | |||
|
48 | const MAXENCODE: usize = 4096 * 4; | |||
|
49 | ||||
|
50 | struct DestArr { | |||
|
51 | buf: [u8; MAXENCODE], | |||
|
52 | pub len: usize, | |||
|
53 | } | |||
|
54 | ||||
|
55 | impl DestArr { | |||
|
56 | pub fn create() -> Self { | |||
|
57 | DestArr { | |||
|
58 | buf: [0; MAXENCODE], | |||
|
59 | len: 0, | |||
|
60 | } | |||
|
61 | } | |||
|
62 | ||||
|
63 | pub fn contents(&self) -> &[u8] { | |||
|
64 | &self.buf[..self.len] | |||
|
65 | } | |||
|
66 | } | |||
|
67 | ||||
|
68 | impl Sink for DestArr { | |||
|
69 | fn write_byte(&mut self, c: u8) { | |||
|
70 | self.buf[self.len] = c; | |||
|
71 | self.len += 1; | |||
|
72 | } | |||
|
73 | ||||
|
74 | fn write_bytes(&mut self, src: &[u8]) { | |||
|
75 | self.buf[self.len..self.len + src.len()].copy_from_slice(src); | |||
|
76 | self.len += src.len(); | |||
|
77 | } | |||
|
78 | } | |||
|
79 | ||||
48 | struct Dest<'a> { |
|
80 | struct Dest<'a> { | |
49 | dest: Option<&'a mut [u8]>, |
|
81 | dest: Option<&'a mut [u8]>, | |
50 | pub len: usize, |
|
82 | pub len: usize, | |
51 | } |
|
83 | } | |
52 |
|
84 | |||
53 | impl<'a> Dest<'a> { |
|
85 | impl<'a> Dest<'a> { | |
54 | pub fn create(buf: &'a mut [u8]) -> Dest<'a> { |
|
86 | pub fn create(buf: &'a mut [u8]) -> Dest<'a> { | |
55 | Dest { |
|
87 | Dest { | |
56 | dest: Some(buf), |
|
88 | dest: Some(buf), | |
57 | len: 0, |
|
89 | len: 0, | |
58 | } |
|
90 | } | |
59 | } |
|
91 | } | |
60 |
|
92 | |||
61 | pub fn create_measure() -> Dest<'a> { |
|
93 | pub fn create_measure() -> Dest<'a> { | |
62 | Dest { dest: None, len: 0 } |
|
94 | Dest { dest: None, len: 0 } | |
63 | } |
|
95 | } | |
64 | } |
|
96 | } | |
65 |
|
97 | |||
66 | fn rewrap_option<'a, 'b: 'a>( |
|
98 | fn rewrap_option<'a, 'b: 'a>( | |
67 | x: &'a mut Option<&'b mut [u8]>, |
|
99 | x: &'a mut Option<&'b mut [u8]>, | |
68 | ) -> Option<&'a mut [u8]> { |
|
100 | ) -> Option<&'a mut [u8]> { | |
69 | match x { |
|
101 | match x { | |
70 | None => None, |
|
102 | None => None, | |
71 | Some(y) => Some(y), |
|
103 | Some(y) => Some(y), | |
72 | } |
|
104 | } | |
73 | } |
|
105 | } | |
74 |
|
106 | |||
75 | impl<'a> Sink for Dest<'a> { |
|
107 | impl<'a> Sink for Dest<'a> { | |
76 | fn write_byte(&mut self, c: u8) { |
|
108 | fn write_byte(&mut self, c: u8) { | |
77 | if let Some(slice) = rewrap_option(&mut self.dest) { |
|
109 | if let Some(slice) = rewrap_option(&mut self.dest) { | |
78 | slice[self.len] = c |
|
110 | slice[self.len] = c | |
79 | } |
|
111 | } | |
80 | self.len += 1 |
|
112 | self.len += 1 | |
81 | } |
|
113 | } | |
82 |
|
114 | |||
83 | fn write_bytes(&mut self, src: &[u8]) { |
|
115 | fn write_bytes(&mut self, src: &[u8]) { | |
84 | if let Some(slice) = rewrap_option(&mut self.dest) { |
|
116 | if let Some(slice) = rewrap_option(&mut self.dest) { | |
85 | slice[self.len..self.len + src.len()].copy_from_slice(src) |
|
117 | slice[self.len..self.len + src.len()].copy_from_slice(src) | |
86 | } |
|
118 | } | |
87 | self.len += src.len(); |
|
119 | self.len += src.len(); | |
88 | } |
|
120 | } | |
89 | } |
|
121 | } | |
90 |
|
122 | |||
91 | fn hexencode(dest: &mut impl Sink, c: u8) { |
|
123 | fn hexencode(dest: &mut impl Sink, c: u8) { | |
92 | let hexdigit = b"0123456789abcdef"; |
|
124 | let hexdigit = b"0123456789abcdef"; | |
93 | dest.write_byte(hexdigit[(c as usize) >> 4]); |
|
125 | dest.write_byte(hexdigit[(c as usize) >> 4]); | |
94 | dest.write_byte(hexdigit[(c as usize) & 15]); |
|
126 | dest.write_byte(hexdigit[(c as usize) & 15]); | |
95 | } |
|
127 | } | |
96 |
|
128 | |||
97 | /* 3-byte escape: tilde followed by two hex digits */ |
|
129 | /* 3-byte escape: tilde followed by two hex digits */ | |
98 | fn escape3(dest: &mut impl Sink, c: u8) { |
|
130 | fn escape3(dest: &mut impl Sink, c: u8) { | |
99 | dest.write_byte(b'~'); |
|
131 | dest.write_byte(b'~'); | |
100 | hexencode(dest, c); |
|
132 | hexencode(dest, c); | |
101 | } |
|
133 | } | |
102 |
|
134 | |||
103 | fn encode_dir(dest: &mut impl Sink, src: &[u8]) { |
|
135 | fn encode_dir(dest: &mut impl Sink, src: &[u8]) { | |
104 | let mut state = dir_state::DDEFAULT; |
|
136 | let mut state = dir_state::DDEFAULT; | |
105 | let mut i = 0; |
|
137 | let mut i = 0; | |
106 |
|
138 | |||
107 | while i < src.len() { |
|
139 | while i < src.len() { | |
108 | match state { |
|
140 | match state { | |
109 | dir_state::DDOT => match src[i] { |
|
141 | dir_state::DDOT => match src[i] { | |
110 | b'd' | b'i' => { |
|
142 | b'd' | b'i' => { | |
111 | state = dir_state::DHGDI; |
|
143 | state = dir_state::DHGDI; | |
112 | dest.write_byte(src[i]); |
|
144 | dest.write_byte(src[i]); | |
113 | i += 1; |
|
145 | i += 1; | |
114 | } |
|
146 | } | |
115 | b'h' => { |
|
147 | b'h' => { | |
116 | state = dir_state::DH; |
|
148 | state = dir_state::DH; | |
117 | dest.write_byte(src[i]); |
|
149 | dest.write_byte(src[i]); | |
118 | i += 1; |
|
150 | i += 1; | |
119 | } |
|
151 | } | |
120 | _ => { |
|
152 | _ => { | |
121 | state = dir_state::DDEFAULT; |
|
153 | state = dir_state::DDEFAULT; | |
122 | } |
|
154 | } | |
123 | }, |
|
155 | }, | |
124 | dir_state::DH => { |
|
156 | dir_state::DH => { | |
125 | if src[i] == b'g' { |
|
157 | if src[i] == b'g' { | |
126 | state = dir_state::DHGDI; |
|
158 | state = dir_state::DHGDI; | |
127 | dest.write_byte(src[i]); |
|
159 | dest.write_byte(src[i]); | |
128 | i += 1; |
|
160 | i += 1; | |
129 | } else { |
|
161 | } else { | |
130 | state = dir_state::DDEFAULT; |
|
162 | state = dir_state::DDEFAULT; | |
131 | } |
|
163 | } | |
132 | } |
|
164 | } | |
133 | dir_state::DHGDI => { |
|
165 | dir_state::DHGDI => { | |
134 | if src[i] == b'/' { |
|
166 | if src[i] == b'/' { | |
135 | dest.write_bytes(b".hg"); |
|
167 | dest.write_bytes(b".hg"); | |
136 | dest.write_byte(src[i]); |
|
168 | dest.write_byte(src[i]); | |
137 | i += 1; |
|
169 | i += 1; | |
138 | } |
|
170 | } | |
139 | state = dir_state::DDEFAULT; |
|
171 | state = dir_state::DDEFAULT; | |
140 | } |
|
172 | } | |
141 | dir_state::DDEFAULT => { |
|
173 | dir_state::DDEFAULT => { | |
142 | if src[i] == b'.' { |
|
174 | if src[i] == b'.' { | |
143 | state = dir_state::DDOT |
|
175 | state = dir_state::DDOT | |
144 | } |
|
176 | } | |
145 | dest.write_byte(src[i]); |
|
177 | dest.write_byte(src[i]); | |
146 | i += 1; |
|
178 | i += 1; | |
147 | } |
|
179 | } | |
148 | } |
|
180 | } | |
149 | } |
|
181 | } | |
150 | } |
|
182 | } | |
151 |
|
183 | |||
152 | fn _encode( |
|
184 | fn _encode( | |
153 | twobytes: &[u32; 8], |
|
185 | twobytes: &[u32; 8], | |
154 | onebyte: &[u32; 8], |
|
186 | onebyte: &[u32; 8], | |
155 | dest: &mut impl Sink, |
|
187 | dest: &mut impl Sink, | |
156 | src: &[u8], |
|
188 | src: &[u8], | |
157 | encodedir: bool, |
|
189 | encodedir: bool, | |
158 | ) { |
|
190 | ) { | |
159 | let mut state = path_state::START; |
|
191 | let mut state = path_state::START; | |
160 | let mut i = 0; |
|
192 | let mut i = 0; | |
161 | let len = src.len(); |
|
193 | let len = src.len(); | |
162 |
|
194 | |||
163 | while i < len { |
|
195 | while i < len { | |
164 | match state { |
|
196 | match state { | |
165 | path_state::START => match src[i] { |
|
197 | path_state::START => match src[i] { | |
166 | b'/' => { |
|
198 | b'/' => { | |
167 | dest.write_byte(src[i]); |
|
199 | dest.write_byte(src[i]); | |
168 | i += 1; |
|
200 | i += 1; | |
169 | } |
|
201 | } | |
170 | b'.' => { |
|
202 | b'.' => { | |
171 | state = path_state::LDOT; |
|
203 | state = path_state::LDOT; | |
172 | escape3(dest, src[i]); |
|
204 | escape3(dest, src[i]); | |
173 | i += 1; |
|
205 | i += 1; | |
174 | } |
|
206 | } | |
175 | b' ' => { |
|
207 | b' ' => { | |
176 | state = path_state::DEFAULT; |
|
208 | state = path_state::DEFAULT; | |
177 | escape3(dest, src[i]); |
|
209 | escape3(dest, src[i]); | |
178 | i += 1; |
|
210 | i += 1; | |
179 | } |
|
211 | } | |
180 | b'a' => { |
|
212 | b'a' => { | |
181 | state = path_state::A; |
|
213 | state = path_state::A; | |
182 | dest.write_byte(src[i]); |
|
214 | dest.write_byte(src[i]); | |
183 | i += 1; |
|
215 | i += 1; | |
184 | } |
|
216 | } | |
185 | b'c' => { |
|
217 | b'c' => { | |
186 | state = path_state::C; |
|
218 | state = path_state::C; | |
187 | dest.write_byte(src[i]); |
|
219 | dest.write_byte(src[i]); | |
188 | i += 1; |
|
220 | i += 1; | |
189 | } |
|
221 | } | |
190 | b'l' => { |
|
222 | b'l' => { | |
191 | state = path_state::L; |
|
223 | state = path_state::L; | |
192 | dest.write_byte(src[i]); |
|
224 | dest.write_byte(src[i]); | |
193 | i += 1; |
|
225 | i += 1; | |
194 | } |
|
226 | } | |
195 | b'n' => { |
|
227 | b'n' => { | |
196 | state = path_state::N; |
|
228 | state = path_state::N; | |
197 | dest.write_byte(src[i]); |
|
229 | dest.write_byte(src[i]); | |
198 | i += 1; |
|
230 | i += 1; | |
199 | } |
|
231 | } | |
200 | b'p' => { |
|
232 | b'p' => { | |
201 | state = path_state::P; |
|
233 | state = path_state::P; | |
202 | dest.write_byte(src[i]); |
|
234 | dest.write_byte(src[i]); | |
203 | i += 1; |
|
235 | i += 1; | |
204 | } |
|
236 | } | |
205 | _ => { |
|
237 | _ => { | |
206 | state = path_state::DEFAULT; |
|
238 | state = path_state::DEFAULT; | |
207 | } |
|
239 | } | |
208 | }, |
|
240 | }, | |
209 | path_state::A => { |
|
241 | path_state::A => { | |
210 | if src[i] == b'u' { |
|
242 | if src[i] == b'u' { | |
211 | state = path_state::AU; |
|
243 | state = path_state::AU; | |
212 | dest.write_byte(src[i]); |
|
244 | dest.write_byte(src[i]); | |
213 | i += 1; |
|
245 | i += 1; | |
214 | } else { |
|
246 | } else { | |
215 | state = path_state::DEFAULT; |
|
247 | state = path_state::DEFAULT; | |
216 | } |
|
248 | } | |
217 | } |
|
249 | } | |
218 | path_state::AU => { |
|
250 | path_state::AU => { | |
219 | if src[i] == b'x' { |
|
251 | if src[i] == b'x' { | |
220 | state = path_state::THIRD; |
|
252 | state = path_state::THIRD; | |
221 | i += 1; |
|
253 | i += 1; | |
222 | } else { |
|
254 | } else { | |
223 | state = path_state::DEFAULT; |
|
255 | state = path_state::DEFAULT; | |
224 | } |
|
256 | } | |
225 | } |
|
257 | } | |
226 | path_state::THIRD => { |
|
258 | path_state::THIRD => { | |
227 | state = path_state::DEFAULT; |
|
259 | state = path_state::DEFAULT; | |
228 | match src[i] { |
|
260 | match src[i] { | |
229 | b'.' | b'/' | b'\0' => escape3(dest, src[i - 1]), |
|
261 | b'.' | b'/' | b'\0' => escape3(dest, src[i - 1]), | |
230 | _ => i -= 1, |
|
262 | _ => i -= 1, | |
231 | } |
|
263 | } | |
232 | } |
|
264 | } | |
233 | path_state::C => { |
|
265 | path_state::C => { | |
234 | if src[i] == b'o' { |
|
266 | if src[i] == b'o' { | |
235 | state = path_state::CO; |
|
267 | state = path_state::CO; | |
236 | dest.write_byte(src[i]); |
|
268 | dest.write_byte(src[i]); | |
237 | i += 1; |
|
269 | i += 1; | |
238 | } else { |
|
270 | } else { | |
239 | state = path_state::DEFAULT; |
|
271 | state = path_state::DEFAULT; | |
240 | } |
|
272 | } | |
241 | } |
|
273 | } | |
242 | path_state::CO => { |
|
274 | path_state::CO => { | |
243 | if src[i] == b'm' { |
|
275 | if src[i] == b'm' { | |
244 | state = path_state::COMLPT; |
|
276 | state = path_state::COMLPT; | |
245 | i += 1; |
|
277 | i += 1; | |
246 | } else if src[i] == b'n' { |
|
278 | } else if src[i] == b'n' { | |
247 | state = path_state::THIRD; |
|
279 | state = path_state::THIRD; | |
248 | i += 1; |
|
280 | i += 1; | |
249 | } else { |
|
281 | } else { | |
250 | state = path_state::DEFAULT; |
|
282 | state = path_state::DEFAULT; | |
251 | } |
|
283 | } | |
252 | } |
|
284 | } | |
253 | path_state::COMLPT => { |
|
285 | path_state::COMLPT => { | |
254 | if src[i] >= b'1' && src[i] <= b'9' { |
|
286 | if src[i] >= b'1' && src[i] <= b'9' { | |
255 | state = path_state::COMLPTn; |
|
287 | state = path_state::COMLPTn; | |
256 | i += 1; |
|
288 | i += 1; | |
257 | } else { |
|
289 | } else { | |
258 | state = path_state::DEFAULT; |
|
290 | state = path_state::DEFAULT; | |
259 | dest.write_byte(src[i - 1]); |
|
291 | dest.write_byte(src[i - 1]); | |
260 | } |
|
292 | } | |
261 | } |
|
293 | } | |
262 | path_state::COMLPTn => { |
|
294 | path_state::COMLPTn => { | |
263 | state = path_state::DEFAULT; |
|
295 | state = path_state::DEFAULT; | |
264 | match src[i] { |
|
296 | match src[i] { | |
265 | b'.' | b'/' | b'\0' => { |
|
297 | b'.' | b'/' | b'\0' => { | |
266 | escape3(dest, src[i - 2]); |
|
298 | escape3(dest, src[i - 2]); | |
267 | dest.write_byte(src[i - 1]); |
|
299 | dest.write_byte(src[i - 1]); | |
268 | } |
|
300 | } | |
269 | _ => { |
|
301 | _ => { | |
270 | dest.write_bytes(&src[i - 2..i]); |
|
302 | dest.write_bytes(&src[i - 2..i]); | |
271 | } |
|
303 | } | |
272 | } |
|
304 | } | |
273 | } |
|
305 | } | |
274 | path_state::L => { |
|
306 | path_state::L => { | |
275 | if src[i] == b'p' { |
|
307 | if src[i] == b'p' { | |
276 | state = path_state::LP; |
|
308 | state = path_state::LP; | |
277 | dest.write_byte(src[i]); |
|
309 | dest.write_byte(src[i]); | |
278 | i += 1; |
|
310 | i += 1; | |
279 | } else { |
|
311 | } else { | |
280 | state = path_state::DEFAULT; |
|
312 | state = path_state::DEFAULT; | |
281 | } |
|
313 | } | |
282 | } |
|
314 | } | |
283 | path_state::LP => { |
|
315 | path_state::LP => { | |
284 | if src[i] == b't' { |
|
316 | if src[i] == b't' { | |
285 | state = path_state::COMLPT; |
|
317 | state = path_state::COMLPT; | |
286 | i += 1; |
|
318 | i += 1; | |
287 | } else { |
|
319 | } else { | |
288 | state = path_state::DEFAULT; |
|
320 | state = path_state::DEFAULT; | |
289 | } |
|
321 | } | |
290 | } |
|
322 | } | |
291 | path_state::N => { |
|
323 | path_state::N => { | |
292 | if src[i] == b'u' { |
|
324 | if src[i] == b'u' { | |
293 | state = path_state::NU; |
|
325 | state = path_state::NU; | |
294 | dest.write_byte(src[i]); |
|
326 | dest.write_byte(src[i]); | |
295 | i += 1; |
|
327 | i += 1; | |
296 | } else { |
|
328 | } else { | |
297 | state = path_state::DEFAULT; |
|
329 | state = path_state::DEFAULT; | |
298 | } |
|
330 | } | |
299 | } |
|
331 | } | |
300 | path_state::NU => { |
|
332 | path_state::NU => { | |
301 | if src[i] == b'l' { |
|
333 | if src[i] == b'l' { | |
302 | state = path_state::THIRD; |
|
334 | state = path_state::THIRD; | |
303 | i += 1; |
|
335 | i += 1; | |
304 | } else { |
|
336 | } else { | |
305 | state = path_state::DEFAULT; |
|
337 | state = path_state::DEFAULT; | |
306 | } |
|
338 | } | |
307 | } |
|
339 | } | |
308 | path_state::P => { |
|
340 | path_state::P => { | |
309 | if src[i] == b'r' { |
|
341 | if src[i] == b'r' { | |
310 | state = path_state::PR; |
|
342 | state = path_state::PR; | |
311 | dest.write_byte(src[i]); |
|
343 | dest.write_byte(src[i]); | |
312 | i += 1; |
|
344 | i += 1; | |
313 | } else { |
|
345 | } else { | |
314 | state = path_state::DEFAULT; |
|
346 | state = path_state::DEFAULT; | |
315 | } |
|
347 | } | |
316 | } |
|
348 | } | |
317 | path_state::PR => { |
|
349 | path_state::PR => { | |
318 | if src[i] == b'n' { |
|
350 | if src[i] == b'n' { | |
319 | state = path_state::THIRD; |
|
351 | state = path_state::THIRD; | |
320 | i += 1; |
|
352 | i += 1; | |
321 | } else { |
|
353 | } else { | |
322 | state = path_state::DEFAULT; |
|
354 | state = path_state::DEFAULT; | |
323 | } |
|
355 | } | |
324 | } |
|
356 | } | |
325 | path_state::LDOT => match src[i] { |
|
357 | path_state::LDOT => match src[i] { | |
326 | b'd' | b'i' => { |
|
358 | b'd' | b'i' => { | |
327 | state = path_state::HGDI; |
|
359 | state = path_state::HGDI; | |
328 | dest.write_byte(src[i]); |
|
360 | dest.write_byte(src[i]); | |
329 | i += 1; |
|
361 | i += 1; | |
330 | } |
|
362 | } | |
331 | b'h' => { |
|
363 | b'h' => { | |
332 | state = path_state::H; |
|
364 | state = path_state::H; | |
333 | dest.write_byte(src[i]); |
|
365 | dest.write_byte(src[i]); | |
334 | i += 1; |
|
366 | i += 1; | |
335 | } |
|
367 | } | |
336 | _ => { |
|
368 | _ => { | |
337 | state = path_state::DEFAULT; |
|
369 | state = path_state::DEFAULT; | |
338 | } |
|
370 | } | |
339 | }, |
|
371 | }, | |
340 | path_state::DOT => match src[i] { |
|
372 | path_state::DOT => match src[i] { | |
341 | b'/' | b'\0' => { |
|
373 | b'/' | b'\0' => { | |
342 | state = path_state::START; |
|
374 | state = path_state::START; | |
343 | dest.write_bytes(b"~2e"); |
|
375 | dest.write_bytes(b"~2e"); | |
344 | dest.write_byte(src[i]); |
|
376 | dest.write_byte(src[i]); | |
345 | i += 1; |
|
377 | i += 1; | |
346 | } |
|
378 | } | |
347 | b'd' | b'i' => { |
|
379 | b'd' | b'i' => { | |
348 | state = path_state::HGDI; |
|
380 | state = path_state::HGDI; | |
349 | dest.write_byte(b'.'); |
|
381 | dest.write_byte(b'.'); | |
350 | dest.write_byte(src[i]); |
|
382 | dest.write_byte(src[i]); | |
351 | i += 1; |
|
383 | i += 1; | |
352 | } |
|
384 | } | |
353 | b'h' => { |
|
385 | b'h' => { | |
354 | state = path_state::H; |
|
386 | state = path_state::H; | |
355 | dest.write_bytes(b".h"); |
|
387 | dest.write_bytes(b".h"); | |
356 | i += 1; |
|
388 | i += 1; | |
357 | } |
|
389 | } | |
358 | _ => { |
|
390 | _ => { | |
359 | state = path_state::DEFAULT; |
|
391 | state = path_state::DEFAULT; | |
360 | dest.write_byte(b'.'); |
|
392 | dest.write_byte(b'.'); | |
361 | } |
|
393 | } | |
362 | }, |
|
394 | }, | |
363 | path_state::H => { |
|
395 | path_state::H => { | |
364 | if src[i] == b'g' { |
|
396 | if src[i] == b'g' { | |
365 | state = path_state::HGDI; |
|
397 | state = path_state::HGDI; | |
366 | dest.write_byte(src[i]); |
|
398 | dest.write_byte(src[i]); | |
367 | i += 1; |
|
399 | i += 1; | |
368 | } else { |
|
400 | } else { | |
369 | state = path_state::DEFAULT; |
|
401 | state = path_state::DEFAULT; | |
370 | } |
|
402 | } | |
371 | } |
|
403 | } | |
372 | path_state::HGDI => { |
|
404 | path_state::HGDI => { | |
373 | if src[i] == b'/' { |
|
405 | if src[i] == b'/' { | |
374 | state = path_state::START; |
|
406 | state = path_state::START; | |
375 | if encodedir { |
|
407 | if encodedir { | |
376 | dest.write_bytes(b".hg"); |
|
408 | dest.write_bytes(b".hg"); | |
377 | } |
|
409 | } | |
378 | dest.write_byte(src[i]); |
|
410 | dest.write_byte(src[i]); | |
379 | i += 1 |
|
411 | i += 1 | |
380 | } else { |
|
412 | } else { | |
381 | state = path_state::DEFAULT; |
|
413 | state = path_state::DEFAULT; | |
382 | } |
|
414 | } | |
383 | } |
|
415 | } | |
384 | path_state::SPACE => match src[i] { |
|
416 | path_state::SPACE => match src[i] { | |
385 | b'/' | b'\0' => { |
|
417 | b'/' | b'\0' => { | |
386 | state = path_state::START; |
|
418 | state = path_state::START; | |
387 | dest.write_bytes(b"~20"); |
|
419 | dest.write_bytes(b"~20"); | |
388 | dest.write_byte(src[i]); |
|
420 | dest.write_byte(src[i]); | |
389 | i += 1; |
|
421 | i += 1; | |
390 | } |
|
422 | } | |
391 | _ => { |
|
423 | _ => { | |
392 | state = path_state::DEFAULT; |
|
424 | state = path_state::DEFAULT; | |
393 | dest.write_byte(b' '); |
|
425 | dest.write_byte(b' '); | |
394 | } |
|
426 | } | |
395 | }, |
|
427 | }, | |
396 | path_state::DEFAULT => { |
|
428 | path_state::DEFAULT => { | |
397 | while i != len && inset(onebyte, src[i]) { |
|
429 | while i != len && inset(onebyte, src[i]) { | |
398 | dest.write_byte(src[i]); |
|
430 | dest.write_byte(src[i]); | |
399 | i += 1; |
|
431 | i += 1; | |
400 | } |
|
432 | } | |
401 | if i == len { |
|
433 | if i == len { | |
402 | break; |
|
434 | break; | |
403 | } |
|
435 | } | |
404 | match src[i] { |
|
436 | match src[i] { | |
405 | b'.' => { |
|
437 | b'.' => { | |
406 | state = path_state::DOT; |
|
438 | state = path_state::DOT; | |
407 | i += 1 |
|
439 | i += 1 | |
408 | } |
|
440 | } | |
409 | b' ' => { |
|
441 | b' ' => { | |
410 | state = path_state::SPACE; |
|
442 | state = path_state::SPACE; | |
411 | i += 1 |
|
443 | i += 1 | |
412 | } |
|
444 | } | |
413 | b'/' => { |
|
445 | b'/' => { | |
414 | state = path_state::START; |
|
446 | state = path_state::START; | |
415 | dest.write_byte(b'/'); |
|
447 | dest.write_byte(b'/'); | |
416 | i += 1; |
|
448 | i += 1; | |
417 | } |
|
449 | } | |
418 | _ => { |
|
450 | _ => { | |
419 | if inset(onebyte, src[i]) { |
|
451 | if inset(onebyte, src[i]) { | |
420 | loop { |
|
452 | loop { | |
421 | dest.write_byte(src[i]); |
|
453 | dest.write_byte(src[i]); | |
422 | i += 1; |
|
454 | i += 1; | |
423 | if !(i < len && inset(onebyte, src[i])) { |
|
455 | if !(i < len && inset(onebyte, src[i])) { | |
424 | break; |
|
456 | break; | |
425 | } |
|
457 | } | |
426 | } |
|
458 | } | |
427 | } else if inset(twobytes, src[i]) { |
|
459 | } else if inset(twobytes, src[i]) { | |
428 | let c = src[i]; |
|
460 | let c = src[i]; | |
429 | i += 1; |
|
461 | i += 1; | |
430 | dest.write_byte(b'_'); |
|
462 | dest.write_byte(b'_'); | |
431 | dest.write_byte(if c == b'_' { |
|
463 | dest.write_byte(if c == b'_' { | |
432 | b'_' |
|
464 | b'_' | |
433 | } else { |
|
465 | } else { | |
434 | c + 32 |
|
466 | c + 32 | |
435 | }); |
|
467 | }); | |
436 | } else { |
|
468 | } else { | |
437 | escape3(dest, src[i]); |
|
469 | escape3(dest, src[i]); | |
438 | i += 1; |
|
470 | i += 1; | |
439 | } |
|
471 | } | |
440 | } |
|
472 | } | |
441 | } |
|
473 | } | |
442 | } |
|
474 | } | |
443 | } |
|
475 | } | |
444 | } |
|
476 | } | |
445 | match state { |
|
477 | match state { | |
446 | path_state::START => (), |
|
478 | path_state::START => (), | |
447 | path_state::A => (), |
|
479 | path_state::A => (), | |
448 | path_state::AU => (), |
|
480 | path_state::AU => (), | |
449 | path_state::THIRD => escape3(dest, src[i - 1]), |
|
481 | path_state::THIRD => escape3(dest, src[i - 1]), | |
450 | path_state::C => (), |
|
482 | path_state::C => (), | |
451 | path_state::CO => (), |
|
483 | path_state::CO => (), | |
452 | path_state::COMLPT => dest.write_byte(src[i - 1]), |
|
484 | path_state::COMLPT => dest.write_byte(src[i - 1]), | |
453 | path_state::COMLPTn => { |
|
485 | path_state::COMLPTn => { | |
454 | escape3(dest, src[i - 2]); |
|
486 | escape3(dest, src[i - 2]); | |
455 | dest.write_byte(src[i - 1]); |
|
487 | dest.write_byte(src[i - 1]); | |
456 | } |
|
488 | } | |
457 | path_state::L => (), |
|
489 | path_state::L => (), | |
458 | path_state::LP => (), |
|
490 | path_state::LP => (), | |
459 | path_state::N => (), |
|
491 | path_state::N => (), | |
460 | path_state::NU => (), |
|
492 | path_state::NU => (), | |
461 | path_state::P => (), |
|
493 | path_state::P => (), | |
462 | path_state::PR => (), |
|
494 | path_state::PR => (), | |
463 | path_state::LDOT => (), |
|
495 | path_state::LDOT => (), | |
464 | path_state::DOT => { |
|
496 | path_state::DOT => { | |
465 | dest.write_bytes(b"~2e"); |
|
497 | dest.write_bytes(b"~2e"); | |
466 | } |
|
498 | } | |
467 | path_state::H => (), |
|
499 | path_state::H => (), | |
468 | path_state::HGDI => (), |
|
500 | path_state::HGDI => (), | |
469 | path_state::SPACE => { |
|
501 | path_state::SPACE => { | |
470 | dest.write_bytes(b"~20"); |
|
502 | dest.write_bytes(b"~20"); | |
471 | } |
|
503 | } | |
472 | path_state::DEFAULT => (), |
|
504 | path_state::DEFAULT => (), | |
473 | } |
|
505 | } | |
474 | } |
|
506 | } | |
475 |
|
507 | |||
476 | fn basic_encode(dest: &mut impl Sink, src: &[u8]) { |
|
508 | fn basic_encode(dest: &mut impl Sink, src: &[u8]) { | |
477 | let twobytes: [u32; 8] = [0, 0, 0x87ff_fffe, 0, 0, 0, 0, 0]; |
|
509 | let twobytes: [u32; 8] = [0, 0, 0x87ff_fffe, 0, 0, 0, 0, 0]; | |
478 | let onebyte: [u32; 8] = |
|
510 | let onebyte: [u32; 8] = | |
479 | [1, 0x2bff_3bfa, 0x6800_0001, 0x2fff_ffff, 0, 0, 0, 0]; |
|
511 | [1, 0x2bff_3bfa, 0x6800_0001, 0x2fff_ffff, 0, 0, 0, 0]; | |
480 | _encode(&twobytes, &onebyte, dest, src, true) |
|
512 | _encode(&twobytes, &onebyte, dest, src, true) | |
481 | } |
|
513 | } | |
482 |
|
514 | |||
483 | const MAXSTOREPATHLEN: usize = 120; |
|
515 | const MAXSTOREPATHLEN: usize = 120; | |
484 |
|
516 | |||
485 | fn lower_encode(dest: &mut impl Sink, src: &[u8]) { |
|
517 | fn lower_encode(dest: &mut impl Sink, src: &[u8]) { | |
486 | let onebyte: [u32; 8] = |
|
518 | let onebyte: [u32; 8] = | |
487 | [1, 0x2bff_fbfb, 0xe800_0001, 0x2fff_ffff, 0, 0, 0, 0]; |
|
519 | [1, 0x2bff_fbfb, 0xe800_0001, 0x2fff_ffff, 0, 0, 0, 0]; | |
488 | let lower: [u32; 8] = [0, 0, 0x07ff_fffe, 0, 0, 0, 0, 0]; |
|
520 | let lower: [u32; 8] = [0, 0, 0x07ff_fffe, 0, 0, 0, 0, 0]; | |
489 | for c in src { |
|
521 | for c in src { | |
490 | if inset(&onebyte, *c) { |
|
522 | if inset(&onebyte, *c) { | |
491 | dest.write_byte(*c) |
|
523 | dest.write_byte(*c) | |
492 | } else if inset(&lower, *c) { |
|
524 | } else if inset(&lower, *c) { | |
493 | dest.write_byte(*c + 32) |
|
525 | dest.write_byte(*c + 32) | |
494 | } else { |
|
526 | } else { | |
495 | escape3(dest, *c) |
|
527 | escape3(dest, *c) | |
496 | } |
|
528 | } | |
497 | } |
|
529 | } | |
498 | } |
|
530 | } | |
499 |
|
531 | |||
500 | fn aux_encode(dest: &mut impl Sink, src: &[u8]) { |
|
532 | fn aux_encode(dest: &mut impl Sink, src: &[u8]) { | |
501 | let twobytes = [0; 8]; |
|
533 | let twobytes = [0; 8]; | |
502 | let onebyte: [u32; 8] = [!0, 0xffff_3ffe, !0, !0, !0, !0, !0, !0]; |
|
534 | let onebyte: [u32; 8] = [!0, 0xffff_3ffe, !0, !0, !0, !0, !0, !0]; | |
503 | _encode(&twobytes, &onebyte, dest, src, false) |
|
535 | _encode(&twobytes, &onebyte, dest, src, false) | |
504 | } |
|
536 | } | |
505 |
|
537 | |||
506 | fn hash_mangle(src: &[u8], sha: &[u8]) -> Vec<u8> { |
|
538 | fn hash_mangle(src: &[u8], sha: &[u8]) -> Vec<u8> { | |
507 | let dirprefixlen = 8; |
|
539 | let dirprefixlen = 8; | |
508 | let maxshortdirslen = 68; |
|
540 | let maxshortdirslen = 68; | |
509 |
|
541 | |||
510 | let last_slash = src.iter().rposition(|b| *b == b'/'); |
|
542 | let last_slash = src.iter().rposition(|b| *b == b'/'); | |
511 | let last_dot: Option<usize> = { |
|
543 | let last_dot: Option<usize> = { | |
512 | let s = last_slash.unwrap_or(0); |
|
544 | let s = last_slash.unwrap_or(0); | |
513 | src[s..].iter().rposition(|b| *b == b'.').map(|i| i + s) |
|
545 | src[s..].iter().rposition(|b| *b == b'.').map(|i| i + s) | |
514 | }; |
|
546 | }; | |
515 |
|
547 | |||
516 | let mut dest_vec = vec![0; MAXSTOREPATHLEN]; |
|
548 | let mut dest_vec = vec![0; MAXSTOREPATHLEN]; | |
517 | let mut dest = Dest::create(&mut dest_vec); |
|
549 | let mut dest = Dest::create(&mut dest_vec); | |
518 | dest.write_bytes(b"dh/"); |
|
550 | dest.write_bytes(b"dh/"); | |
519 |
|
551 | |||
520 | if let Some(last_slash) = last_slash { |
|
552 | if let Some(last_slash) = last_slash { | |
521 | for slice in src[..last_slash].split(|b| *b == b'/') { |
|
553 | for slice in src[..last_slash].split(|b| *b == b'/') { | |
522 | let slice = &slice[..std::cmp::min(slice.len(), dirprefixlen)]; |
|
554 | let slice = &slice[..std::cmp::min(slice.len(), dirprefixlen)]; | |
523 | if dest.len + slice.len() > maxshortdirslen + 3 { |
|
555 | if dest.len + slice.len() > maxshortdirslen + 3 { | |
524 | break; |
|
556 | break; | |
525 | } else { |
|
557 | } else { | |
526 | dest.write_bytes(slice); |
|
558 | dest.write_bytes(slice); | |
527 | } |
|
559 | } | |
528 | dest.write_byte(b'/'); |
|
560 | dest.write_byte(b'/'); | |
529 | } |
|
561 | } | |
530 | } |
|
562 | } | |
531 |
|
563 | |||
532 | let used = dest.len + 40 + { |
|
564 | let used = dest.len + 40 + { | |
533 | if let Some(l) = last_dot { |
|
565 | if let Some(l) = last_dot { | |
534 | src.len() - l |
|
566 | src.len() - l | |
535 | } else { |
|
567 | } else { | |
536 | 0 |
|
568 | 0 | |
537 | } |
|
569 | } | |
538 | }; |
|
570 | }; | |
539 |
|
571 | |||
540 | if MAXSTOREPATHLEN > used { |
|
572 | if MAXSTOREPATHLEN > used { | |
541 | let slop = MAXSTOREPATHLEN - used; |
|
573 | let slop = MAXSTOREPATHLEN - used; | |
542 | let basenamelen = match last_slash { |
|
574 | let basenamelen = match last_slash { | |
543 | Some(l) => src.len() - l - 1, |
|
575 | Some(l) => src.len() - l - 1, | |
544 | None => src.len(), |
|
576 | None => src.len(), | |
545 | }; |
|
577 | }; | |
546 | let basenamelen = std::cmp::min(basenamelen, slop); |
|
578 | let basenamelen = std::cmp::min(basenamelen, slop); | |
547 | if basenamelen > 0 { |
|
579 | if basenamelen > 0 { | |
548 | let start = match last_slash { |
|
580 | let start = match last_slash { | |
549 | Some(l) => l + 1, |
|
581 | Some(l) => l + 1, | |
550 | None => 0, |
|
582 | None => 0, | |
551 | }; |
|
583 | }; | |
552 | dest.write_bytes(&src[start..][..basenamelen]) |
|
584 | dest.write_bytes(&src[start..][..basenamelen]) | |
553 | } |
|
585 | } | |
554 | } |
|
586 | } | |
555 | for c in sha { |
|
587 | for c in sha { | |
556 | hexencode(&mut dest, *c); |
|
588 | hexencode(&mut dest, *c); | |
557 | } |
|
589 | } | |
558 | if let Some(l) = last_dot { |
|
590 | if let Some(l) = last_dot { | |
559 | dest.write_bytes(&src[l..]); |
|
591 | dest.write_bytes(&src[l..]); | |
560 | } |
|
592 | } | |
561 | let destlen = dest.len; |
|
593 | let destlen = dest.len; | |
562 | if destlen == dest_vec.len() { |
|
594 | if destlen == dest_vec.len() { | |
563 | dest_vec |
|
595 | dest_vec | |
564 | } else { |
|
596 | } else { | |
565 | // sometimes the path are shorter than MAXSTOREPATHLEN |
|
597 | // sometimes the path are shorter than MAXSTOREPATHLEN | |
566 | dest_vec[..destlen].to_vec() |
|
598 | dest_vec[..destlen].to_vec() | |
567 | } |
|
599 | } | |
568 | } |
|
600 | } | |
569 |
|
601 | |||
570 | const MAXENCODE: usize = 4096 * 4; |
|
|||
571 | fn hash_encode(src: &[u8]) -> Vec<u8> { |
|
602 | fn hash_encode(src: &[u8]) -> Vec<u8> { | |
572 | let dired = &mut [0; MAXENCODE]; |
|
603 | let mut dired = DestArr::create(); | |
573 |
let mut |
|
604 | let mut lowered = DestArr::create(); | |
574 | let lowered = &mut [0; MAXENCODE]; |
|
605 | let mut auxed = DestArr::create(); | |
575 | let mut lowered_dest = Dest::create(lowered); |
|
|||
576 | let auxed = &mut [0; MAXENCODE]; |
|
|||
577 | let mut auxed_dest = Dest::create(auxed); |
|
|||
578 | let baselen = (src.len() - 5) * 3; |
|
606 | let baselen = (src.len() - 5) * 3; | |
579 | if baselen >= MAXENCODE { |
|
607 | if baselen >= MAXENCODE { | |
580 | panic!("path_encode::hash_encore: string too long: {}", baselen) |
|
608 | panic!("path_encode::hash_encore: string too long: {}", baselen) | |
581 | }; |
|
609 | }; | |
582 |
encode_dir(&mut dired |
|
610 | encode_dir(&mut dired, src); | |
583 | let dirlen = dired_dest.len; |
|
611 | let sha = Sha1::digest(dired.contents()); | |
584 | let sha = Sha1::digest(&dired[..dirlen]); |
|
612 | lower_encode(&mut lowered, &dired.contents()[5..]); | |
585 | lower_encode(&mut lowered_dest, &dired[..dirlen][5..]); |
|
613 | aux_encode(&mut auxed, lowered.contents()); | |
586 | let lowerlen = lowered_dest.len; |
|
614 | hash_mangle(auxed.contents(), &sha) | |
587 | aux_encode(&mut auxed_dest, &lowered[..lowerlen]); |
|
|||
588 | let auxlen = auxed_dest.len; |
|
|||
589 | hash_mangle(&auxed[..auxlen], &sha) |
|
|||
590 | } |
|
615 | } | |
591 |
|
616 | |||
592 | pub fn path_encode(path: &[u8]) -> Vec<u8> { |
|
617 | pub fn path_encode(path: &[u8]) -> Vec<u8> { | |
593 | let newlen = if path.len() <= MAXSTOREPATHLEN { |
|
618 | let newlen = if path.len() <= MAXSTOREPATHLEN { | |
594 | let mut measure = Dest::create_measure(); |
|
619 | let mut measure = Dest::create_measure(); | |
595 | basic_encode(&mut measure, path); |
|
620 | basic_encode(&mut measure, path); | |
596 | measure.len |
|
621 | measure.len | |
597 | } else { |
|
622 | } else { | |
598 | MAXSTOREPATHLEN + 1 |
|
623 | MAXSTOREPATHLEN + 1 | |
599 | }; |
|
624 | }; | |
600 | if newlen <= MAXSTOREPATHLEN { |
|
625 | if newlen <= MAXSTOREPATHLEN { | |
601 | if newlen == path.len() { |
|
626 | if newlen == path.len() { | |
602 | path.to_vec() |
|
627 | path.to_vec() | |
603 | } else { |
|
628 | } else { | |
604 | let mut res = vec![0; newlen]; |
|
629 | let mut res = vec![0; newlen]; | |
605 | let mut dest = Dest::create(&mut res); |
|
630 | let mut dest = Dest::create(&mut res); | |
606 | basic_encode(&mut dest, path); |
|
631 | basic_encode(&mut dest, path); | |
607 | assert!(dest.len == newlen); |
|
632 | assert!(dest.len == newlen); | |
608 | res |
|
633 | res | |
609 | } |
|
634 | } | |
610 | } else { |
|
635 | } else { | |
611 | hash_encode(path) |
|
636 | hash_encode(path) | |
612 | } |
|
637 | } | |
613 | } |
|
638 | } | |
614 |
|
639 | |||
615 | #[cfg(test)] |
|
640 | #[cfg(test)] | |
616 | mod tests { |
|
641 | mod tests { | |
617 | use super::*; |
|
642 | use super::*; | |
618 | use crate::utils::hg_path::HgPathBuf; |
|
643 | use crate::utils::hg_path::HgPathBuf; | |
619 |
|
644 | |||
620 | #[test] |
|
645 | #[test] | |
621 | fn test_long_filename_at_root() { |
|
646 | fn test_long_filename_at_root() { | |
622 | let input = b"data/ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJ.i"; |
|
647 | let input = b"data/ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJ.i"; | |
623 | let expected = b"dh/abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij.i708243a2237a7afae259ea3545a72a2ef11c247b.i"; |
|
648 | let expected = b"dh/abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij.i708243a2237a7afae259ea3545a72a2ef11c247b.i"; | |
624 | let res = path_encode(input); |
|
649 | let res = path_encode(input); | |
625 | assert_eq!( |
|
650 | assert_eq!( | |
626 | HgPathBuf::from_bytes(&res), |
|
651 | HgPathBuf::from_bytes(&res), | |
627 | HgPathBuf::from_bytes(expected) |
|
652 | HgPathBuf::from_bytes(expected) | |
628 | ); |
|
653 | ); | |
629 | } |
|
654 | } | |
630 | } |
|
655 | } |
General Comments 0
You need to be logged in to leave comments.
Login now