Mercurial > public > mercurial-scm > hg
comparison rust/hg-core/src/revlog/nodemap_docket.rs @ 46390:0800aa42bb4c
rust: use the bytes-cast crate to parse persistent nodemaps
This crate casts pointers to custom structs, with compile-time safety checks,
for easy and efficient binary data parsing.
See https://crates.io/crates/bytes-cast and
https://docs.rs/bytes-cast/0.1.0/bytes_cast/
Differential Revision: https://phab.mercurial-scm.org/D9788
author | Simon Sapin <simon.sapin@octobus.net> |
---|---|
date | Fri, 15 Jan 2021 16:11:54 +0100 |
parents | 8a4914397d02 |
children | 43d63979a75e |
comparison
equal
deleted
inserted
replaced
46389:38b9a63d3a13 | 46390:0800aa42bb4c |
---|---|
1 use bytes_cast::{unaligned, BytesCast}; | |
1 use memmap::Mmap; | 2 use memmap::Mmap; |
2 use std::convert::TryInto; | |
3 use std::path::{Path, PathBuf}; | 3 use std::path::{Path, PathBuf}; |
4 | 4 |
5 use super::revlog::RevlogError; | 5 use super::revlog::RevlogError; |
6 use crate::repo::Repo; | 6 use crate::repo::Repo; |
7 use crate::utils::strip_suffix; | 7 use crate::utils::strip_suffix; |
9 const ONDISK_VERSION: u8 = 1; | 9 const ONDISK_VERSION: u8 = 1; |
10 | 10 |
11 pub(super) struct NodeMapDocket { | 11 pub(super) struct NodeMapDocket { |
12 pub data_length: usize, | 12 pub data_length: usize, |
13 // TODO: keep here more of the data from `parse()` when we need it | 13 // TODO: keep here more of the data from `parse()` when we need it |
14 } | |
15 | |
16 #[derive(BytesCast)] | |
17 #[repr(C)] | |
18 struct DocketHeader { | |
19 uid_size: u8, | |
20 _tip_rev: unaligned::U64Be, | |
21 data_length: unaligned::U64Be, | |
22 _data_unused: unaligned::U64Be, | |
23 tip_node_size: unaligned::U64Be, | |
14 } | 24 } |
15 | 25 |
16 impl NodeMapDocket { | 26 impl NodeMapDocket { |
17 /// Return `Ok(None)` when the caller should proceed without a persistent | 27 /// Return `Ok(None)` when the caller should proceed without a persistent |
18 /// nodemap: | 28 /// nodemap: |
34 } | 44 } |
35 Err(e) => return Err(RevlogError::IoError(e)), | 45 Err(e) => return Err(RevlogError::IoError(e)), |
36 Ok(bytes) => bytes, | 46 Ok(bytes) => bytes, |
37 }; | 47 }; |
38 | 48 |
39 let mut input = if let Some((&ONDISK_VERSION, rest)) = | 49 let input = if let Some((&ONDISK_VERSION, rest)) = |
40 docket_bytes.split_first() | 50 docket_bytes.split_first() |
41 { | 51 { |
42 rest | 52 rest |
43 } else { | 53 } else { |
44 return Ok(None); | 54 return Ok(None); |
45 }; | 55 }; |
46 let input = &mut input; | |
47 | 56 |
48 let uid_size = read_u8(input)? as usize; | 57 let (header, rest) = DocketHeader::from_bytes(input)?; |
49 let _tip_rev = read_be_u64(input)?; | 58 let uid_size = header.uid_size as usize; |
50 // TODO: do we care about overflow for 4 GB+ nodemap files on 32-bit | 59 // TODO: do we care about overflow for 4 GB+ nodemap files on 32-bit |
51 // systems? | 60 // systems? |
52 let data_length = read_be_u64(input)? as usize; | 61 let tip_node_size = header.tip_node_size.get() as usize; |
53 let _data_unused = read_be_u64(input)?; | 62 let data_length = header.data_length.get() as usize; |
54 let tip_node_size = read_be_u64(input)? as usize; | 63 let (uid, rest) = u8::slice_from_bytes(rest, uid_size)?; |
55 let uid = read_bytes(input, uid_size)?; | 64 let (_tip_node, _rest) = u8::slice_from_bytes(rest, tip_node_size)?; |
56 let _tip_node = read_bytes(input, tip_node_size)?; | |
57 | |
58 let uid = | 65 let uid = |
59 std::str::from_utf8(uid).map_err(|_| RevlogError::Corrupted)?; | 66 std::str::from_utf8(uid).map_err(|_| RevlogError::Corrupted)?; |
60 let docket = NodeMapDocket { data_length }; | 67 let docket = NodeMapDocket { data_length }; |
61 | 68 |
62 let data_path = rawdata_path(&docket_path, uid); | 69 let data_path = rawdata_path(&docket_path, uid); |
79 } | 86 } |
80 } | 87 } |
81 } | 88 } |
82 } | 89 } |
83 | 90 |
84 fn read_bytes<'a>( | |
85 input: &mut &'a [u8], | |
86 count: usize, | |
87 ) -> Result<&'a [u8], RevlogError> { | |
88 if let Some(start) = input.get(..count) { | |
89 *input = &input[count..]; | |
90 Ok(start) | |
91 } else { | |
92 Err(RevlogError::Corrupted) | |
93 } | |
94 } | |
95 | |
96 fn read_u8<'a>(input: &mut &[u8]) -> Result<u8, RevlogError> { | |
97 Ok(read_bytes(input, 1)?[0]) | |
98 } | |
99 | |
100 fn read_be_u64<'a>(input: &mut &[u8]) -> Result<u64, RevlogError> { | |
101 let array = read_bytes(input, std::mem::size_of::<u64>())? | |
102 .try_into() | |
103 .unwrap(); | |
104 Ok(u64::from_be_bytes(array)) | |
105 } | |
106 | |
107 fn rawdata_path(docket_path: &Path, uid: &str) -> PathBuf { | 91 fn rawdata_path(docket_path: &Path, uid: &str) -> PathBuf { |
108 let docket_name = docket_path | 92 let docket_name = docket_path |
109 .file_name() | 93 .file_name() |
110 .expect("expected a base name") | 94 .expect("expected a base name") |
111 .to_str() | 95 .to_str() |