diff --git a/rust/Cargo.lock b/rust/Cargo.lock --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "adler" version = "0.2.3" @@ -396,6 +398,7 @@ "regex", "same-file", "sha-1", + "stable_deref_trait", "tempfile", "twox-hash", "zstd", @@ -411,6 +414,7 @@ "hg-core", "libc", "log", + "stable_deref_trait", ] [[package]] @@ -865,6 +869,12 @@ ] [[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/rust/hg-core/Cargo.toml b/rust/hg-core/Cargo.toml --- a/rust/hg-core/Cargo.toml +++ b/rust/hg-core/Cargo.toml @@ -24,6 +24,7 @@ sha-1 = "0.9.6" twox-hash = "1.5.0" same-file = "1.0.6" +stable_deref_trait = "1.2.0" tempfile = "3.1.0" crossbeam-channel = "0.4" micro-timer = "0.3.0" diff --git a/rust/hg-core/src/dirstate_tree.rs b/rust/hg-core/src/dirstate_tree.rs --- a/rust/hg-core/src/dirstate_tree.rs +++ b/rust/hg-core/src/dirstate_tree.rs @@ -1,5 +1,7 @@ pub mod dirstate_map; pub mod dispatch; pub mod on_disk; +pub mod owning; +mod owning_dispatch; pub mod path_with_basename; pub mod status; diff --git a/rust/hg-cpython/src/dirstate/owning.rs b/rust/hg-core/src/dirstate_tree/owning.rs rename from rust/hg-cpython/src/dirstate/owning.rs rename to rust/hg-core/src/dirstate_tree/owning.rs --- a/rust/hg-cpython/src/dirstate/owning.rs +++ b/rust/hg-core/src/dirstate_tree/owning.rs @@ -1,11 +1,9 @@ -use cpython::PyBytes; -use cpython::Python; -use hg::dirstate_tree::dirstate_map::DirstateMap; -use hg::DirstateError; -use hg::DirstateParents; +use super::dirstate_map::DirstateMap; +use stable_deref_trait::StableDeref; +use std::ops::Deref; /// Keep a `DirstateMap<'on_disk>` next to the `on_disk` buffer that it -/// borrows. This is similar to the owning-ref crate. +/// borrows. /// /// This is similar to [`OwningRef`] which is more limited because it /// represents exactly one `&T` reference next to the value it borrows, as @@ -13,11 +11,11 @@ /// arbitrarily-nested data structures. /// /// [`OwningRef`]: https://docs.rs/owning_ref/0.4.1/owning_ref/struct.OwningRef.html -pub(super) struct OwningDirstateMap { +pub struct OwningDirstateMap { /// Owned handle to a bytes buffer with a stable address. /// /// See . - on_disk: PyBytes, + on_disk: Box + Send>, /// Pointer for `Box>`, typed-erased because the /// language cannot represent a lifetime referencing a sibling field. @@ -28,12 +26,13 @@ } impl OwningDirstateMap { - pub fn new_v1( - py: Python, - on_disk: PyBytes, - ) -> Result<(Self, Option), DirstateError> { - let bytes: &'_ [u8] = on_disk.data(py); - let (map, parents) = DirstateMap::new_v1(bytes)?; + pub fn new_empty(on_disk: OnDisk) -> Self + where + OnDisk: Deref + StableDeref + Send + 'static, + { + let on_disk = Box::new(on_disk); + let bytes: &'_ [u8] = &on_disk; + let map = DirstateMap::empty(bytes); // Like in `bytes` above, this `'_` lifetime parameter borrows from // the bytes buffer owned by `on_disk`. @@ -42,30 +41,12 @@ // Erase the pointed type entirely in order to erase the lifetime. let ptr: *mut () = ptr.cast(); - Ok((Self { on_disk, ptr }, parents)) + Self { on_disk, ptr } } - pub fn new_v2( - py: Python, - on_disk: PyBytes, - data_size: usize, - tree_metadata: PyBytes, - ) -> Result { - let bytes: &'_ [u8] = on_disk.data(py); - let map = - DirstateMap::new_v2(bytes, data_size, tree_metadata.data(py))?; - - // Like in `bytes` above, this `'_` lifetime parameter borrows from - // the bytes buffer owned by `on_disk`. - let ptr: *mut DirstateMap<'_> = Box::into_raw(Box::new(map)); - - // Erase the pointed type entirely in order to erase the lifetime. - let ptr: *mut () = ptr.cast(); - - Ok(Self { on_disk, ptr }) - } - - pub fn get_mut<'a>(&'a mut self) -> &'a mut DirstateMap<'a> { + pub fn get_mut_pair<'a>( + &'a mut self, + ) -> (&'a [u8], &'a mut DirstateMap<'a>) { // SAFETY: We cast the type-erased pointer back to the same type it had // in `new`, except with a different lifetime parameter. This time we // connect the lifetime to that of `self`. This cast is valid because @@ -76,7 +57,11 @@ // SAFETY: we dereference that pointer, connecting the lifetime of the // new `&mut` to that of `self`. This is valid because the // raw pointer is to a boxed value, and `self` owns that box. - unsafe { &mut *ptr } + (&self.on_disk, unsafe { &mut *ptr }) + } + + pub fn get_mut<'a>(&'a mut self) -> &'a mut DirstateMap<'a> { + self.get_mut_pair().1 } pub fn get<'a>(&'a self) -> &'a DirstateMap<'a> { @@ -84,6 +69,10 @@ let ptr: *mut DirstateMap<'a> = self.ptr.cast(); unsafe { &*ptr } } + + pub fn on_disk<'a>(&'a self) -> &'a [u8] { + &self.on_disk + } } impl Drop for OwningDirstateMap { @@ -105,13 +94,12 @@ fn _static_assert_is_send() {} fn _static_assert_fields_are_send() { - _static_assert_is_send::(); _static_assert_is_send::>>(); } // SAFETY: we don’t get this impl implicitly because `*mut (): !Send` because // thread-safety of raw pointers is unknown in the general case. However this // particular raw pointer represents a `Box>` that we -// own. Since that `Box` and `PyBytes` are both `Send` as shown in above, it -// is sound to mark this struct as `Send` too. +// own. Since that `Box` is `Send` as shown in above, it is sound to mark +// this struct as `Send` too. unsafe impl Send for OwningDirstateMap {} diff --git a/rust/hg-cpython/src/dirstate/dispatch.rs b/rust/hg-core/src/dirstate_tree/owning_dispatch.rs rename from rust/hg-cpython/src/dirstate/dispatch.rs rename to rust/hg-core/src/dirstate_tree/owning_dispatch.rs --- a/rust/hg-cpython/src/dirstate/dispatch.rs +++ b/rust/hg-core/src/dirstate_tree/owning_dispatch.rs @@ -1,18 +1,18 @@ -use crate::dirstate::owning::OwningDirstateMap; -use hg::dirstate::parsers::Timestamp; -use hg::dirstate_tree::dispatch::DirstateMapMethods; -use hg::dirstate_tree::on_disk::DirstateV2ParseError; -use hg::matchers::Matcher; -use hg::utils::hg_path::{HgPath, HgPathBuf}; -use hg::CopyMapIter; -use hg::DirstateEntry; -use hg::DirstateError; -use hg::DirstateParents; -use hg::DirstateStatus; -use hg::PatternFileWarning; -use hg::StateMapIter; -use hg::StatusError; -use hg::StatusOptions; +use crate::dirstate::parsers::Timestamp; +use crate::dirstate_tree::dispatch::DirstateMapMethods; +use crate::dirstate_tree::on_disk::DirstateV2ParseError; +use crate::dirstate_tree::owning::OwningDirstateMap; +use crate::matchers::Matcher; +use crate::utils::hg_path::{HgPath, HgPathBuf}; +use crate::CopyMapIter; +use crate::DirstateEntry; +use crate::DirstateError; +use crate::DirstateParents; +use crate::DirstateStatus; +use crate::PatternFileWarning; +use crate::StateMapIter; +use crate::StatusError; +use crate::StatusOptions; use std::path::PathBuf; impl DirstateMapMethods for OwningDirstateMap { diff --git a/rust/hg-cpython/Cargo.toml b/rust/hg-cpython/Cargo.toml --- a/rust/hg-cpython/Cargo.toml +++ b/rust/hg-cpython/Cargo.toml @@ -26,6 +26,7 @@ libc = '*' log = "0.4.8" env_logger = "0.7.1" +stable_deref_trait = "1.2.0" [dependencies.cpython] version = "0.6.0" diff --git a/rust/hg-cpython/src/dirstate.rs b/rust/hg-cpython/src/dirstate.rs --- a/rust/hg-cpython/src/dirstate.rs +++ b/rust/hg-cpython/src/dirstate.rs @@ -12,9 +12,7 @@ mod copymap; mod dirs_multiset; mod dirstate_map; -mod dispatch; mod non_normal_entries; -mod owning; mod status; use crate::{ dirstate::{ diff --git a/rust/hg-cpython/src/dirstate/dirstate_map.rs b/rust/hg-cpython/src/dirstate/dirstate_map.rs --- a/rust/hg-cpython/src/dirstate/dirstate_map.rs +++ b/rust/hg-cpython/src/dirstate/dirstate_map.rs @@ -24,15 +24,17 @@ dirstate::non_normal_entries::{ NonNormalEntries, NonNormalEntriesIterator, }, - dirstate::owning::OwningDirstateMap, parsers::dirstate_parents_to_pytuple, + pybytes_deref::PyBytesDeref, }; use hg::{ dirstate::parsers::Timestamp, dirstate::MTIME_UNSET, dirstate::SIZE_NON_NORMAL, + dirstate_tree::dirstate_map::DirstateMap as TreeDirstateMap, dirstate_tree::dispatch::DirstateMapMethods, dirstate_tree::on_disk::DirstateV2ParseError, + dirstate_tree::owning::OwningDirstateMap, revlog::Node, utils::files::normalize_case, utils::hg_path::{HgPath, HgPathBuf}, @@ -62,8 +64,13 @@ on_disk: PyBytes, ) -> PyResult { let (inner, parents) = if use_dirstate_tree { - let (map, parents) = OwningDirstateMap::new_v1(py, on_disk) + let on_disk = PyBytesDeref::new(py, on_disk); + let mut map = OwningDirstateMap::new_empty(on_disk); + let (on_disk, map_placeholder) = map.get_mut_pair(); + + let (actual_map, parents) = TreeDirstateMap::new_v1(on_disk) .map_err(|e| dirstate_error(py, e))?; + *map_placeholder = actual_map; (Box::new(map) as _, parents) } else { let bytes = on_disk.data(py); @@ -86,10 +93,13 @@ let dirstate_error = |e: DirstateError| { PyErr::new::(py, format!("Dirstate error: {:?}", e)) }; - let inner = OwningDirstateMap::new_v2( - py, on_disk, data_size, tree_metadata, + let on_disk = PyBytesDeref::new(py, on_disk); + let mut map = OwningDirstateMap::new_empty(on_disk); + let (on_disk, map_placeholder) = map.get_mut_pair(); + *map_placeholder = TreeDirstateMap::new_v2( + on_disk, data_size, tree_metadata.data(py), ).map_err(dirstate_error)?; - let map = Self::create_instance(py, Box::new(inner))?; + let map = Self::create_instance(py, Box::new(map))?; Ok(map.into_object()) } diff --git a/rust/hg-cpython/src/pybytes_deref.rs b/rust/hg-cpython/src/pybytes_deref.rs --- a/rust/hg-cpython/src/pybytes_deref.rs +++ b/rust/hg-cpython/src/pybytes_deref.rs @@ -1,4 +1,5 @@ use cpython::{PyBytes, Python}; +use stable_deref_trait::StableDeref; /// Safe abstraction over a `PyBytes` together with the `&[u8]` slice /// that borrows it. Implements `Deref`. @@ -40,6 +41,8 @@ } } +unsafe impl StableDeref for PyBytesDeref {} + fn require_send() {} #[allow(unused)]