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