rust/hg-core/src/revlog/nodemap_docket.rs
changeset 46090 9eb07ab3f2d4
child 46167 8a4914397d02
equal deleted inserted replaced
46089:8ff2d8359d0f 46090:9eb07ab3f2d4
       
     1 use memmap::Mmap;
       
     2 use std::convert::TryInto;
       
     3 use std::path::{Path, PathBuf};
       
     4 
       
     5 use super::revlog::{mmap_open, RevlogError};
       
     6 use crate::utils::strip_suffix;
       
     7 
       
     8 const ONDISK_VERSION: u8 = 1;
       
     9 
       
    10 pub(super) struct NodeMapDocket {
       
    11     pub data_length: usize,
       
    12     // TODO: keep here more of the data from `parse()` when we need it
       
    13 }
       
    14 
       
    15 impl NodeMapDocket {
       
    16     /// Return `Ok(None)` when the caller should proceed without a persistent
       
    17     /// nodemap:
       
    18     ///
       
    19     /// * This revlog does not have a `.n` docket file (it is not generated for
       
    20     ///   small revlogs), or
       
    21     /// * The docket has an unsupported version number (repositories created by
       
    22     ///   later hg, maybe that should be a requirement instead?), or
       
    23     /// * The docket file points to a missing (likely deleted) data file (this
       
    24     ///   can happen in a rare race condition).
       
    25     pub fn read_from_file(
       
    26         index_path: &Path,
       
    27     ) -> Result<Option<(Self, Mmap)>, RevlogError> {
       
    28         let docket_path = index_path.with_extension("n");
       
    29         let docket_bytes = match std::fs::read(&docket_path) {
       
    30             Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
       
    31                 return Ok(None)
       
    32             }
       
    33             Err(e) => return Err(RevlogError::IoError(e)),
       
    34             Ok(bytes) => bytes,
       
    35         };
       
    36 
       
    37         let mut input = if let Some((&ONDISK_VERSION, rest)) =
       
    38             docket_bytes.split_first()
       
    39         {
       
    40             rest
       
    41         } else {
       
    42             return Ok(None);
       
    43         };
       
    44         let input = &mut input;
       
    45 
       
    46         let uid_size = read_u8(input)? as usize;
       
    47         let _tip_rev = read_be_u64(input)?;
       
    48         // TODO: do we care about overflow for 4 GB+ nodemap files on 32-bit
       
    49         // systems?
       
    50         let data_length = read_be_u64(input)? as usize;
       
    51         let _data_unused = read_be_u64(input)?;
       
    52         let tip_node_size = read_be_u64(input)? as usize;
       
    53         let uid = read_bytes(input, uid_size)?;
       
    54         let _tip_node = read_bytes(input, tip_node_size)?;
       
    55 
       
    56         let uid =
       
    57             std::str::from_utf8(uid).map_err(|_| RevlogError::Corrupted)?;
       
    58         let docket = NodeMapDocket { data_length };
       
    59 
       
    60         let data_path = rawdata_path(&docket_path, uid);
       
    61         // TODO: use `std::fs::read` here when the `persistent-nodemap.mmap`
       
    62         // config is false?
       
    63         match mmap_open(&data_path) {
       
    64             Ok(mmap) => {
       
    65                 if mmap.len() >= data_length {
       
    66                     Ok(Some((docket, mmap)))
       
    67                 } else {
       
    68                     Err(RevlogError::Corrupted)
       
    69                 }
       
    70             }
       
    71             Err(error) => {
       
    72                 if error.kind() == std::io::ErrorKind::NotFound {
       
    73                     Ok(None)
       
    74                 } else {
       
    75                     Err(RevlogError::IoError(error))
       
    76                 }
       
    77             }
       
    78         }
       
    79     }
       
    80 }
       
    81 
       
    82 fn read_bytes<'a>(
       
    83     input: &mut &'a [u8],
       
    84     count: usize,
       
    85 ) -> Result<&'a [u8], RevlogError> {
       
    86     if let Some(start) = input.get(..count) {
       
    87         *input = &input[count..];
       
    88         Ok(start)
       
    89     } else {
       
    90         Err(RevlogError::Corrupted)
       
    91     }
       
    92 }
       
    93 
       
    94 fn read_u8<'a>(input: &mut &[u8]) -> Result<u8, RevlogError> {
       
    95     Ok(read_bytes(input, 1)?[0])
       
    96 }
       
    97 
       
    98 fn read_be_u64<'a>(input: &mut &[u8]) -> Result<u64, RevlogError> {
       
    99     let array = read_bytes(input, std::mem::size_of::<u64>())?
       
   100         .try_into()
       
   101         .unwrap();
       
   102     Ok(u64::from_be_bytes(array))
       
   103 }
       
   104 
       
   105 fn rawdata_path(docket_path: &Path, uid: &str) -> PathBuf {
       
   106     let docket_name = docket_path
       
   107         .file_name()
       
   108         .expect("expected a base name")
       
   109         .to_str()
       
   110         .expect("expected an ASCII file name in the store");
       
   111     let prefix = strip_suffix(docket_name, ".n.a")
       
   112         .or_else(|| strip_suffix(docket_name, ".n"))
       
   113         .expect("expected docket path in .n or .n.a");
       
   114     let name = format!("{}-{}.nd", prefix, uid);
       
   115     docket_path
       
   116         .parent()
       
   117         .expect("expected a non-root path")
       
   118         .join(name)
       
   119 }