diff --git a/rust/hg-core/src/revlog/index.rs b/rust/hg-core/src/revlog/index.rs --- a/rust/hg-core/src/revlog/index.rs +++ b/rust/hg-core/src/revlog/index.rs @@ -2,13 +2,17 @@ use std::fmt::Debug; use std::ops::Deref; use byteorder::{BigEndian, ByteOrder}; +use bytes_cast::{unaligned, BytesCast}; +use super::REVIDX_KNOWN_FLAGS; use crate::errors::HgError; +use crate::node::{NODE_BYTES_LENGTH, STORED_NODE_ID_BYTES}; use crate::revlog::node::Node; use crate::revlog::{Revision, NULL_REVISION}; -use crate::{Graph, GraphError, RevlogIndex, UncheckedRevision}; +use crate::{Graph, GraphError, RevlogError, RevlogIndex, UncheckedRevision}; pub const INDEX_ENTRY_SIZE: usize = 64; +pub const COMPRESSION_MODE_INLINE: u8 = 2; pub struct IndexHeader { header_bytes: [u8; 4], @@ -120,6 +124,81 @@ impl std::ops::Index; +} + +impl RevisionDataParams { + pub fn validate(&self) -> Result<(), RevlogError> { + if self.flags & !REVIDX_KNOWN_FLAGS != 0 { + return Err(RevlogError::corrupted(format!( + "unknown revlog index flags: {}", + self.flags + ))); + } + if self.data_compression_mode != COMPRESSION_MODE_INLINE { + return Err(RevlogError::corrupted(format!( + "invalid data compression mode: {}", + self.data_compression_mode + ))); + } + // FIXME isn't this only for v2 or changelog v2? + if self._sidedata_compression_mode != COMPRESSION_MODE_INLINE { + return Err(RevlogError::corrupted(format!( + "invalid sidedata compression mode: {}", + self._sidedata_compression_mode + ))); + } + Ok(()) + } + + pub fn into_v1(self) -> RevisionDataV1 { + let data_offset_or_flags = self.data_offset << 16 | self.flags as u64; + let mut node_id = [0; STORED_NODE_ID_BYTES]; + node_id[..NODE_BYTES_LENGTH].copy_from_slice(&self.node_id); + RevisionDataV1 { + data_offset_or_flags: data_offset_or_flags.into(), + data_compressed_length: self.data_compressed_length.into(), + data_uncompressed_length: self.data_uncompressed_length.into(), + data_delta_base: self.data_delta_base.into(), + link_rev: self.link_rev.into(), + parent_rev_1: self.parent_rev_1.into(), + parent_rev_2: self.parent_rev_2.into(), + node_id, + } + } +} + /// A Revlog index pub struct Index { bytes: IndexData, @@ -283,6 +362,20 @@ impl Index { offset_override, } } + + /// TODO move this to the trait probably, along with other things + pub fn append( + &mut self, + revision_data: RevisionDataParams, + ) -> Result<(), RevlogError> { + revision_data.validate()?; + let new_offset = self.bytes.len(); + if let Some(offsets) = self.offsets.as_mut() { + offsets.push(new_offset) + } + self.bytes.added.extend(revision_data.into_v1().as_bytes()); + Ok(()) + } } impl super::RevlogIndex for Index { diff --git a/rust/hg-core/src/revlog/node.rs b/rust/hg-core/src/revlog/node.rs --- a/rust/hg-core/src/revlog/node.rs +++ b/rust/hg-core/src/revlog/node.rs @@ -20,6 +20,10 @@ use std::fmt; /// the future. pub const NODE_BYTES_LENGTH: usize = 20; +/// The length in bytes set aside on disk for a `Node`. Revlog up to v1 only +/// use 20 out of those 32. +pub const STORED_NODE_ID_BYTES: usize = 32; + /// Id of the null node. /// /// Used to indicate the absence of node.