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()