##// END OF EJS Templates
rust-parsers: use the same error message as with the higher-level code...
Raphaël Gomès -
r52945:db5c202e default
parent child Browse files
Show More
@@ -1,150 +1,155
1 1 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
2 2 //
3 3 // This software may be used and distributed according to the terms of the
4 4 // GNU General Public License version 2 or any later version.
5 5
6 6 use crate::errors::HgError;
7 7 use crate::utils::hg_path::HgPath;
8 8 use crate::{dirstate::EntryState, DirstateEntry, DirstateParents};
9 9 use byteorder::{BigEndian, WriteBytesExt};
10 10 use bytes_cast::{unaligned, BytesCast};
11 11
12 12 /// Parents are stored in the dirstate as byte hashes.
13 13 pub const PARENT_SIZE: usize = 20;
14 14 /// Dirstate entries have a static part of 8 + 32 + 32 + 32 + 32 bits.
15 15 const MIN_ENTRY_SIZE: usize = 17;
16 16
17 17 type ParseResult<'a> = (
18 18 &'a DirstateParents,
19 19 Vec<(&'a HgPath, DirstateEntry)>,
20 20 Vec<(&'a HgPath, &'a HgPath)>,
21 21 );
22 22
23 23 pub fn parse_dirstate_parents(
24 24 contents: &[u8],
25 25 ) -> Result<&DirstateParents, HgError> {
26 let (parents, _rest) = DirstateParents::from_bytes(contents)
27 .map_err(|_| HgError::corrupted("Too little data for dirstate."))?;
26 let contents_len = contents.len();
27 let (parents, _rest) =
28 DirstateParents::from_bytes(contents).map_err(|_| {
29 HgError::corrupted(format!(
30 "Too little data for dirstate: {contents_len} bytes.",
31 ))
32 })?;
28 33 Ok(parents)
29 34 }
30 35
31 36 #[logging_timer::time("trace")]
32 37 pub fn parse_dirstate(contents: &[u8]) -> Result<ParseResult, HgError> {
33 38 let mut copies = Vec::new();
34 39 let mut entries = Vec::new();
35 40 let parents =
36 41 parse_dirstate_entries(contents, |path, entry, copy_source| {
37 42 if let Some(source) = copy_source {
38 43 copies.push((path, source));
39 44 }
40 45 entries.push((path, *entry));
41 46 Ok(())
42 47 })?;
43 48 Ok((parents, entries, copies))
44 49 }
45 50
46 51 #[derive(BytesCast)]
47 52 #[repr(C)]
48 53 struct RawEntry {
49 54 state: u8,
50 55 mode: unaligned::I32Be,
51 56 size: unaligned::I32Be,
52 57 mtime: unaligned::I32Be,
53 58 length: unaligned::I32Be,
54 59 }
55 60
56 61 pub fn parse_dirstate_entries<'a>(
57 62 mut contents: &'a [u8],
58 63 mut each_entry: impl FnMut(
59 64 &'a HgPath,
60 65 &DirstateEntry,
61 66 Option<&'a HgPath>,
62 67 ) -> Result<(), HgError>,
63 68 ) -> Result<&'a DirstateParents, HgError> {
64 69 let mut entry_idx = 0;
65 70 let original_len = contents.len();
66 71 let (parents, rest) =
67 72 DirstateParents::from_bytes(contents).map_err(|_| {
68 73 HgError::corrupted(format!(
69 74 "Too little data for dirstate: {} bytes.",
70 75 original_len
71 76 ))
72 77 })?;
73 78 contents = rest;
74 79 while !contents.is_empty() {
75 80 let (raw_entry, rest) = RawEntry::from_bytes(contents)
76 81 .map_err(|_| HgError::corrupted(format!(
77 82 "dirstate corrupted: ran out of bytes at entry header {}, offset {}.",
78 83 entry_idx, original_len-contents.len())))?;
79 84
80 85 let entry = DirstateEntry::from_v1_data(
81 86 EntryState::try_from(raw_entry.state)?,
82 87 raw_entry.mode.get(),
83 88 raw_entry.size.get(),
84 89 raw_entry.mtime.get(),
85 90 );
86 91 let filename_len = raw_entry.length.get() as usize;
87 92 let (paths, rest) =
88 93 u8::slice_from_bytes(rest, filename_len)
89 94 .map_err(|_|
90 95 HgError::corrupted(format!(
91 96 "dirstate corrupted: ran out of bytes at entry {}, offset {} (expected {} bytes).",
92 97 entry_idx, original_len-contents.len(), filename_len))
93 98 )?;
94 99
95 100 // `paths` is either a single path, or two paths separated by a NULL
96 101 // byte
97 102 let mut iter = paths.splitn(2, |&byte| byte == b'\0');
98 103 let path = HgPath::new(
99 104 iter.next().expect("splitn always yields at least one item"),
100 105 );
101 106 let copy_source = iter.next().map(HgPath::new);
102 107 each_entry(path, &entry, copy_source)?;
103 108
104 109 entry_idx += 1;
105 110 contents = rest;
106 111 }
107 112 Ok(parents)
108 113 }
109 114
110 115 fn packed_filename_and_copy_source_size(
111 116 filename: &HgPath,
112 117 copy_source: Option<&HgPath>,
113 118 ) -> usize {
114 119 filename.len()
115 120 + if let Some(source) = copy_source {
116 121 b"\0".len() + source.len()
117 122 } else {
118 123 0
119 124 }
120 125 }
121 126
122 127 pub fn packed_entry_size(
123 128 filename: &HgPath,
124 129 copy_source: Option<&HgPath>,
125 130 ) -> usize {
126 131 MIN_ENTRY_SIZE
127 132 + packed_filename_and_copy_source_size(filename, copy_source)
128 133 }
129 134
130 135 pub fn pack_entry(
131 136 filename: &HgPath,
132 137 entry: &DirstateEntry,
133 138 copy_source: Option<&HgPath>,
134 139 packed: &mut Vec<u8>,
135 140 ) {
136 141 let length = packed_filename_and_copy_source_size(filename, copy_source);
137 142 let (state, mode, size, mtime) = entry.v1_data();
138 143
139 144 // Unwrapping because `impl std::io::Write for Vec<u8>` never errors
140 145 packed.write_u8(state).unwrap();
141 146 packed.write_i32::<BigEndian>(mode).unwrap();
142 147 packed.write_i32::<BigEndian>(size).unwrap();
143 148 packed.write_i32::<BigEndian>(mtime).unwrap();
144 149 packed.write_i32::<BigEndian>(length as i32).unwrap();
145 150 packed.extend(filename.as_bytes());
146 151 if let Some(source) = copy_source {
147 152 packed.push(b'\0');
148 153 packed.extend(source.as_bytes());
149 154 }
150 155 }
General Comments 0
You need to be logged in to leave comments. Login now