1use git_zero_core::{Error, Result};
3mod names;
4mod url;
5use names::{Field, Key, Section};
6use url::Url;
7
8#[derive(Clone, Copy)]
9enum Phase {
10 Main,
11 Comment,
12 Base,
13 SectionSpace,
14 Name,
15 NameEscape,
16 SectionEnd,
17 Key,
18 KeySpace,
19 Value,
20 Halted,
21}
22enum ValueStep {
23 More,
24 Complete,
25 Malformed,
26}
27
28pub struct FsckModules {
33 remaining: u64,
34 normalized: u64,
35 pending_cr: bool,
36 eof_seen: bool,
37 limited: bool,
38 phase: Phase,
39 base_seen: bool,
40 section: Section,
41 key: Key,
42 value: Value,
43 done: bool,
44 failure: Option<Error>,
45}
46impl FsckModules {
47 pub fn new_received(size: u64, plain: bool) -> Result<Self> {
52 if plain && size > 512 * 1024 * 1024 {
53 return Err(Error::InvalidObject);
54 }
55 Ok(Self::new(size))
56 }
57
58 pub fn new(size: u64) -> Self {
59 Self {
60 remaining: size,
61 normalized: 0,
62 pending_cr: false,
63 eof_seen: false,
64 limited: false,
65 phase: Phase::Main,
66 base_seen: false,
67 section: Section::new(),
68 key: Key::default(),
69 value: Value::new(None),
70 done: false,
71 failure: None,
72 }
73 }
74 pub fn update(&mut self, bytes: &[u8]) -> Result<()> {
75 if let Some(error) = self.failure {
76 return Err(error);
77 }
78 let result = self.feed(bytes);
79 if let Err(error) = result {
80 self.failure = Some(error);
81 }
82 result
83 }
84 fn feed(&mut self, bytes: &[u8]) -> Result<()> {
85 self.remaining = self
86 .remaining
87 .checked_sub(bytes.len() as u64)
88 .ok_or(Error::InvalidObject)?;
89 for &byte in bytes {
90 if self.limited || matches!(self.phase, Phase::Halted) {
91 break;
92 }
93 if self.pending_cr {
94 self.pending_cr = false;
95 if byte == b'\n' {
96 self.character(b'\n')?;
97 continue;
98 }
99 self.character(b'\r')?;
100 if byte == 0xff {
101 continue;
102 }
103 }
104 if byte == b'\r' {
105 self.pending_cr = true;
106 } else {
107 self.character(byte)?;
108 }
109 }
110 Ok(())
111 }
112 fn character(&mut self, byte: u8) -> Result<()> {
113 if self.limited {
114 return Ok(());
115 }
116 if byte == 0xff {
120 self.eof_seen = true;
121 return self.consume(b'\n');
122 }
123 self.normalized += 1;
124 if self.normalized > i32::MAX as u64 {
128 self.limited = true;
129 self.eof_seen = true;
130 if matches!(self.phase, Phase::Value) {
131 self.consume(0)?;
132 } else {
133 self.phase = Phase::Halted;
134 }
135 return Ok(());
136 }
137 self.consume(byte)
138 }
139 fn boolean(&mut self) -> Result<()> {
140 if self.section.entry(self.key).is_some_and(|(bad, _)| bad) {
141 return Err(Error::InvalidObject);
142 }
143 self.phase = Phase::Main;
144 Ok(())
145 }
146 fn start_value(&mut self) {
147 self.value = Value::new(self.section.entry(self.key));
148 self.phase = Phase::Value;
149 }
150 fn consume(&mut self, byte: u8) -> Result<()> {
151 match self.phase {
152 Phase::Main => self.main(byte),
153 Phase::Comment => {
154 if byte == b'\n' {
155 self.phase = Phase::Main;
156 }
157 }
158 Phase::Base => self.base(byte),
159 Phase::SectionSpace => self.section_space(byte),
160 Phase::Name => match byte {
161 b'\n' => self.phase = Phase::Halted,
162 b'\\' => self.phase = Phase::NameEscape,
163 b'"' => self.phase = Phase::SectionEnd,
164 _ => self.section.name_byte(byte),
165 },
166 Phase::NameEscape => {
167 if byte == b'\n' {
168 self.phase = Phase::Halted;
169 } else {
170 self.section.name_byte(byte);
171 self.phase = Phase::Name;
172 }
173 }
174 Phase::SectionEnd => {
175 self.phase = if byte == b']' {
176 Phase::Main
177 } else {
178 Phase::Halted
179 }
180 }
181 Phase::Key | Phase::KeySpace => self.key(byte)?,
182 Phase::Value => self.value(byte)?,
183 Phase::Halted => {}
184 }
185 Ok(())
186 }
187 fn main(&mut self, byte: u8) {
188 match byte {
189 b'\n' if self.eof_seen => self.phase = Phase::Halted,
190 b'#' | b';' => self.phase = Phase::Comment,
191 b'[' => {
192 self.section = Section::new();
193 self.base_seen = false;
194 self.phase = Phase::Base;
195 }
196 _ if space(byte) => {}
197 _ if byte.is_ascii_alphabetic() => {
198 self.key = Key::default();
199 self.key.push(byte.to_ascii_lowercase());
200 self.phase = Phase::Key;
201 }
202 _ => self.phase = Phase::Halted,
203 }
204 }
205 fn base(&mut self, byte: u8) {
206 if self.eof_seen {
207 self.phase = Phase::Halted;
208 return;
209 }
210 match byte {
211 b']' if self.base_seen => self.phase = Phase::Main,
212 b' ' | b'\t' | b'\r' => self.phase = Phase::SectionSpace,
213 _ if key_char(byte) || byte == b'.' => {
214 self.base_seen = true;
215 self.section.base(byte);
216 }
217 _ => self.phase = Phase::Halted,
218 }
219 }
220 fn section_space(&mut self, byte: u8) {
221 match byte {
222 b' ' | b'\t' | b'\r' => {}
223 b'"' if self.base_seen => {
224 self.section.extended();
225 self.phase = Phase::Name;
226 }
227 _ => self.phase = Phase::Halted,
228 }
229 }
230 fn key(&mut self, byte: u8) -> Result<()> {
231 if matches!(self.phase, Phase::Key) && key_char(byte) && !self.eof_seen {
232 self.key.push(byte.to_ascii_lowercase());
233 return Ok(());
234 }
235 match byte {
236 b' ' | b'\t' => self.phase = Phase::KeySpace,
237 b'=' => self.start_value(),
238 b'\n' => self.boolean()?,
239 _ => self.phase = Phase::Halted,
240 }
241 Ok(())
242 }
243 fn value(&mut self, byte: u8) -> Result<()> {
244 match self.value.push(byte) {
245 ValueStep::More => {}
246 ValueStep::Malformed => self.phase = Phase::Halted,
247 ValueStep::Complete => {
248 if self.value.invalid() {
249 return Err(Error::InvalidObject);
250 }
251 self.phase = Phase::Main;
252 }
253 }
254 Ok(())
255 }
256 pub fn finish(&mut self) -> Result<()> {
257 if let Some(error) = self.failure {
258 return Err(error);
259 }
260 if self.done {
261 return Ok(());
262 }
263 let result = self.end();
264 if let Err(error) = result {
265 self.failure = Some(error);
266 }
267 result
268 }
269 fn end(&mut self) -> Result<()> {
270 if self.remaining != 0 {
271 return Err(Error::InvalidObject);
272 }
273 if self.pending_cr {
274 self.pending_cr = false;
275 self.character(b'\r')?;
276 }
277 self.consume(b'\n')?;
280 self.consume(b'\n')?;
281 self.done = true;
282 Ok(())
283 }
284}
285fn key_char(byte: u8) -> bool {
286 byte.is_ascii_alphanumeric() || byte == b'-'
287}
288fn space(byte: u8) -> bool {
289 matches!(byte, b' ' | b'\t' | b'\n' | b'\r')
290}
291
292struct Value {
293 field: Field,
294 bad_name: bool,
295 bad: bool,
296 actual: bool,
297 pending_space: bool,
298 quote: bool,
299 escape: bool,
300 comment: bool,
301 url: Url,
302}
303impl Value {
304 fn new(entry: Option<(bool, Field)>) -> Self {
305 let (bad_name, field) = entry.unwrap_or((false, Field::Other));
306 Self {
307 field,
308 bad_name,
309 bad: false,
310 actual: false,
311 pending_space: false,
312 quote: false,
313 escape: false,
314 comment: false,
315 url: Url::new(),
316 }
317 }
318 fn emit(&mut self, byte: u8) {
319 self.actual = true;
320 if self.field == Field::Other {
321 return;
322 }
323 if byte == 0 {
324 self.bad |= self.field == Field::Url && self.url.invalid();
325 self.field = Field::Other;
326 return;
327 }
328 match self.field {
329 Field::Path => {
330 self.bad |= byte == b'-';
331 self.field = Field::Other;
332 }
333 Field::Update => {
334 self.bad |= byte == b'!';
335 self.field = Field::Other;
336 }
337 Field::Url => {
338 self.url.push(byte);
339 if let Url::Other(bad) = self.url {
340 self.bad |= bad;
341 self.field = Field::Other;
342 }
343 }
344 Field::Other => {}
345 }
346 }
347 fn flush_space(&mut self) {
348 if self.pending_space && self.actual {
351 self.emit(b' ');
352 }
353 self.pending_space = false;
354 }
355 fn push(&mut self, byte: u8) -> ValueStep {
356 if self.comment {
357 return if byte == b'\n' {
358 ValueStep::Complete
359 } else {
360 ValueStep::More
361 };
362 }
363 if self.escape {
364 self.escape = false;
365 let decoded = match byte {
366 b'\n' => return ValueStep::More,
367 b't' => b'\t',
368 b'b' => 8,
369 b'n' => b'\n',
370 b'\\' | b'"' => byte,
371 _ => return ValueStep::Malformed,
372 };
373 self.emit(decoded);
374 return ValueStep::More;
375 }
376 if byte == b'\n' {
377 return if self.quote {
378 ValueStep::Malformed
379 } else {
380 ValueStep::Complete
381 };
382 }
383 if !self.quote && space(byte) {
384 self.pending_space = true;
385 return ValueStep::More;
386 }
387 if !self.quote && matches!(byte, b'#' | b';') {
388 self.comment = true;
389 return ValueStep::More;
390 }
391 self.flush_space();
392 match byte {
393 b'\\' => self.escape = true,
394 b'"' => self.quote = !self.quote,
395 _ => self.emit(byte),
396 }
397 ValueStep::More
398 }
399 fn invalid(&self) -> bool {
400 self.bad_name || self.bad || (self.field == Field::Url && self.url.invalid())
401 }
402}