diff -r c7c23bb036c9 -r 2097f63575a5 rust/hg-core/src/dirstate_tree/on_disk.rs --- a/rust/hg-core/src/dirstate_tree/on_disk.rs Thu Dec 02 12:05:36 2021 +0100 +++ b/rust/hg-core/src/dirstate_tree/on_disk.rs Thu Dec 02 15:10:03 2021 +0100 @@ -14,8 +14,10 @@ use bytes_cast::unaligned::{U16Be, U32Be}; use bytes_cast::BytesCast; use format_bytes::format_bytes; +use rand::Rng; use std::borrow::Cow; use std::convert::{TryFrom, TryInto}; +use std::fmt::Write; /// Added at the start of `.hg/dirstate` when the "v2" format is used. /// This a redundant sanity check more than an actual "magic number" since @@ -68,7 +70,7 @@ /// section of `mercurial/helptext/internals/dirstate-v2.txt` #[derive(BytesCast)] #[repr(C)] -struct TreeMetadata { +pub struct TreeMetadata { root_nodes: ChildNodes, nodes_with_entry_count: Size, nodes_with_copy_source_count: Size, @@ -186,7 +188,51 @@ } } +impl TreeMetadata { + pub fn as_bytes(&self) -> &[u8] { + BytesCast::as_bytes(self) + } +} + impl<'on_disk> Docket<'on_disk> { + /// Generate the identifier for a new data file + /// + /// TODO: support the `HGTEST_UUIDFILE` environment variable. + /// See `mercurial/revlogutils/docket.py` + pub fn new_uid() -> String { + const ID_LENGTH: usize = 8; + let mut id = String::with_capacity(ID_LENGTH); + let mut rng = rand::thread_rng(); + for _ in 0..ID_LENGTH { + // One random hexadecimal digit. + // `unwrap` never panics because `impl Write for String` + // never returns an error. + write!(&mut id, "{:x}", rng.gen_range(0, 16)).unwrap(); + } + id + } + + pub fn serialize( + parents: DirstateParents, + tree_metadata: TreeMetadata, + data_size: u64, + uuid: &[u8], + ) -> Result, std::num::TryFromIntError> { + let header = DocketHeader { + marker: *V2_FORMAT_MARKER, + parent_1: parents.p1.pad_to_256_bits(), + parent_2: parents.p2.pad_to_256_bits(), + metadata: tree_metadata, + data_size: u32::try_from(data_size)?.into(), + uuid_size: uuid.len().try_into()?, + }; + let header = header.as_bytes(); + let mut docket = Vec::with_capacity(header.len() + uuid.len()); + docket.extend_from_slice(header); + docket.extend_from_slice(uuid); + Ok(docket) + } + pub fn parents(&self) -> DirstateParents { use crate::Node; let p1 = Node::try_from(&self.header.parent_1[..USED_NODE_ID_BYTES]) @@ -555,7 +601,7 @@ pub(super) fn write( dirstate_map: &DirstateMap, can_append: bool, -) -> Result<(Vec, Vec, bool), DirstateError> { +) -> Result<(Vec, TreeMetadata, bool), DirstateError> { let append = can_append && dirstate_map.write_should_append(); // This ignores the space for paths, and for nodes without an entry. @@ -581,7 +627,7 @@ unused: [0; 4], ignore_patterns_hash: dirstate_map.ignore_patterns_hash, }; - Ok((writer.out, meta.as_bytes().to_vec(), append)) + Ok((writer.out, meta, append)) } struct Writer<'dmap, 'on_disk> {