Mercurial > hg
changeset 47954:4afd6cc447b9
rust: Make OwningDirstateMap generic and move it into hg-core
This will enable using it in rhg too.
The `OwningDirstateMap::new_empty` constructor is generic and accepts a value
of any type that gives acces to a bytes buffer. That buffer must stay valid
as long as the value hasn’t been dropped, and must keep its memory address
even if the value is moved. The `StableDeref` marker trait encodes those
constraints. Previously no trait was needed because the value was always
of type `PyBytes` which we know satisfies those constraints.
The buffer type is ereased in the struct itself through boxing and
dynamic dispatch, in order to simplify other signatures that mention
`OwningDirstateMap`.
Differential Revision: https://phab.mercurial-scm.org/D11396
author | Simon Sapin <simon.sapin@octobus.net> |
---|---|
date | Thu, 09 Sep 2021 18:07:40 +0200 |
parents | 8f031a274cd6 |
children | e834b79def74 |
files | rust/Cargo.lock rust/hg-core/Cargo.toml rust/hg-core/src/dirstate_tree.rs rust/hg-core/src/dirstate_tree/owning.rs rust/hg-core/src/dirstate_tree/owning_dispatch.rs rust/hg-cpython/Cargo.toml rust/hg-cpython/src/dirstate.rs rust/hg-cpython/src/dirstate/dirstate_map.rs rust/hg-cpython/src/dirstate/dispatch.rs rust/hg-cpython/src/dirstate/owning.rs rust/hg-cpython/src/pybytes_deref.rs |
diffstat | 11 files changed, 377 insertions(+), 364 deletions(-) [+] |
line wrap: on
line diff
--- a/rust/Cargo.lock Mon Sep 06 13:39:54 2021 +0200 +++ b/rust/Cargo.lock Thu Sep 09 18:07:40 2021 +0200 @@ -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"
--- a/rust/hg-core/Cargo.toml Mon Sep 06 13:39:54 2021 +0200 +++ b/rust/hg-core/Cargo.toml Thu Sep 09 18:07:40 2021 +0200 @@ -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"
--- a/rust/hg-core/src/dirstate_tree.rs Mon Sep 06 13:39:54 2021 +0200 +++ b/rust/hg-core/src/dirstate_tree.rs Thu Sep 09 18:07:40 2021 +0200 @@ -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;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/hg-core/src/dirstate_tree/owning.rs Thu Sep 09 18:07:40 2021 +0200 @@ -0,0 +1,105 @@ +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 [`OwningRef`] which is more limited because it +/// represents exactly one `&T` reference next to the value it borrows, as +/// opposed to a struct that may contain an arbitrary number of references in +/// arbitrarily-nested data structures. +/// +/// [`OwningRef`]: https://docs.rs/owning_ref/0.4.1/owning_ref/struct.OwningRef.html +pub struct OwningDirstateMap { + /// Owned handle to a bytes buffer with a stable address. + /// + /// See <https://docs.rs/owning_ref/0.4.1/owning_ref/trait.StableAddress.html>. + on_disk: Box<dyn Deref<Target = [u8]> + Send>, + + /// Pointer for `Box<DirstateMap<'on_disk>>`, typed-erased because the + /// language cannot represent a lifetime referencing a sibling field. + /// This is not quite a self-referencial struct (moving this struct is not + /// a problem as it doesn’t change the address of the bytes buffer owned + /// by `PyBytes`) but touches similar borrow-checker limitations. + ptr: *mut (), +} + +impl OwningDirstateMap { + pub fn new_empty<OnDisk>(on_disk: OnDisk) -> Self + where + OnDisk: Deref<Target = [u8]> + 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`. + 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(); + + Self { on_disk, ptr } + } + + 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 + // `self` owns the same `PyBytes` whose buffer `DirstateMap` + // references. That buffer has a stable memory address because the byte + // string value of a `PyBytes` is immutable. + let ptr: *mut DirstateMap<'a> = self.ptr.cast(); + // 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. + (&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> { + // SAFETY: same reasoning as in `get_mut` above. + 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 { + fn drop(&mut self) { + // Silence a "field is never read" warning, and demonstrate that this + // value is still alive. + let _ = &self.on_disk; + // SAFETY: this cast is the same as in `get_mut`, and is valid for the + // same reason. `self.on_disk` still exists at this point, drop glue + // will drop it implicitly after this `drop` method returns. + let ptr: *mut DirstateMap<'_> = self.ptr.cast(); + // SAFETY: `Box::from_raw` takes ownership of the box away from `self`. + // This is fine because drop glue does nothig for `*mut ()` and we’re + // in `drop`, so `get` and `get_mut` cannot be called again. + unsafe { drop(Box::from_raw(ptr)) } + } +} + +fn _static_assert_is_send<T: Send>() {} + +fn _static_assert_fields_are_send() { + _static_assert_is_send::<Box<DirstateMap<'_>>>(); +} + +// 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<DirstateMap<'on_disk>>` that we +// 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 {}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/hg-core/src/dirstate_tree/owning_dispatch.rs Thu Sep 09 18:07:40 2021 +0200 @@ -0,0 +1,240 @@ +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 { + fn clear(&mut self) { + self.get_mut().clear() + } + + fn set_v1(&mut self, filename: &HgPath, entry: DirstateEntry) { + self.get_mut().set_v1(filename, entry) + } + + fn add_file( + &mut self, + filename: &HgPath, + entry: DirstateEntry, + added: bool, + merged: bool, + from_p2: bool, + possibly_dirty: bool, + ) -> Result<(), DirstateError> { + self.get_mut().add_file( + filename, + entry, + added, + merged, + from_p2, + possibly_dirty, + ) + } + + fn remove_file( + &mut self, + filename: &HgPath, + in_merge: bool, + ) -> Result<(), DirstateError> { + self.get_mut().remove_file(filename, in_merge) + } + + fn drop_file(&mut self, filename: &HgPath) -> Result<bool, DirstateError> { + self.get_mut().drop_file(filename) + } + + fn clear_ambiguous_times( + &mut self, + filenames: Vec<HgPathBuf>, + now: i32, + ) -> Result<(), DirstateV2ParseError> { + self.get_mut().clear_ambiguous_times(filenames, now) + } + + fn non_normal_entries_contains( + &mut self, + key: &HgPath, + ) -> Result<bool, DirstateV2ParseError> { + self.get_mut().non_normal_entries_contains(key) + } + + fn non_normal_entries_remove(&mut self, key: &HgPath) -> bool { + self.get_mut().non_normal_entries_remove(key) + } + + fn non_normal_entries_add(&mut self, key: &HgPath) { + self.get_mut().non_normal_entries_add(key) + } + + fn non_normal_or_other_parent_paths( + &mut self, + ) -> Box<dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + '_> + { + self.get_mut().non_normal_or_other_parent_paths() + } + + fn set_non_normal_other_parent_entries(&mut self, force: bool) { + self.get_mut().set_non_normal_other_parent_entries(force) + } + + fn iter_non_normal_paths( + &mut self, + ) -> Box< + dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + Send + '_, + > { + self.get_mut().iter_non_normal_paths() + } + + fn iter_non_normal_paths_panic( + &self, + ) -> Box< + dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + Send + '_, + > { + self.get().iter_non_normal_paths_panic() + } + + fn iter_other_parent_paths( + &mut self, + ) -> Box< + dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + Send + '_, + > { + self.get_mut().iter_other_parent_paths() + } + + fn has_tracked_dir( + &mut self, + directory: &HgPath, + ) -> Result<bool, DirstateError> { + self.get_mut().has_tracked_dir(directory) + } + + fn has_dir(&mut self, directory: &HgPath) -> Result<bool, DirstateError> { + self.get_mut().has_dir(directory) + } + + fn pack_v1( + &mut self, + parents: DirstateParents, + now: Timestamp, + ) -> Result<Vec<u8>, DirstateError> { + self.get_mut().pack_v1(parents, now) + } + + fn pack_v2( + &mut self, + now: Timestamp, + can_append: bool, + ) -> Result<(Vec<u8>, Vec<u8>, bool), DirstateError> { + self.get_mut().pack_v2(now, can_append) + } + + fn status<'a>( + &'a mut self, + matcher: &'a (dyn Matcher + Sync), + root_dir: PathBuf, + ignore_files: Vec<PathBuf>, + options: StatusOptions, + ) -> Result<(DirstateStatus<'a>, Vec<PatternFileWarning>), StatusError> + { + self.get_mut() + .status(matcher, root_dir, ignore_files, options) + } + + fn copy_map_len(&self) -> usize { + self.get().copy_map_len() + } + + fn copy_map_iter(&self) -> CopyMapIter<'_> { + self.get().copy_map_iter() + } + + fn copy_map_contains_key( + &self, + key: &HgPath, + ) -> Result<bool, DirstateV2ParseError> { + self.get().copy_map_contains_key(key) + } + + fn copy_map_get( + &self, + key: &HgPath, + ) -> Result<Option<&HgPath>, DirstateV2ParseError> { + self.get().copy_map_get(key) + } + + fn copy_map_remove( + &mut self, + key: &HgPath, + ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> { + self.get_mut().copy_map_remove(key) + } + + fn copy_map_insert( + &mut self, + key: HgPathBuf, + value: HgPathBuf, + ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> { + self.get_mut().copy_map_insert(key, value) + } + + fn len(&self) -> usize { + self.get().len() + } + + fn contains_key( + &self, + key: &HgPath, + ) -> Result<bool, DirstateV2ParseError> { + self.get().contains_key(key) + } + + fn get( + &self, + key: &HgPath, + ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> { + self.get().get(key) + } + + fn iter(&self) -> StateMapIter<'_> { + self.get().iter() + } + + fn iter_tracked_dirs( + &mut self, + ) -> Result< + Box< + dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + + Send + + '_, + >, + DirstateError, + > { + self.get_mut().iter_tracked_dirs() + } + + fn debug_iter( + &self, + ) -> Box< + dyn Iterator< + Item = Result< + (&HgPath, (u8, i32, i32, i32)), + DirstateV2ParseError, + >, + > + Send + + '_, + > { + self.get().debug_iter() + } +}
--- a/rust/hg-cpython/Cargo.toml Mon Sep 06 13:39:54 2021 +0200 +++ b/rust/hg-cpython/Cargo.toml Thu Sep 09 18:07:40 2021 +0200 @@ -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"
--- a/rust/hg-cpython/src/dirstate.rs Mon Sep 06 13:39:54 2021 +0200 +++ b/rust/hg-cpython/src/dirstate.rs Thu Sep 09 18:07:40 2021 +0200 @@ -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::{
--- a/rust/hg-cpython/src/dirstate/dirstate_map.rs Mon Sep 06 13:39:54 2021 +0200 +++ b/rust/hg-cpython/src/dirstate/dirstate_map.rs Thu Sep 09 18:07:40 2021 +0200 @@ -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<PyObject> { 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::<exc::OSError, _>(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()) }
--- a/rust/hg-cpython/src/dirstate/dispatch.rs Mon Sep 06 13:39:54 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,240 +0,0 @@ -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 std::path::PathBuf; - -impl DirstateMapMethods for OwningDirstateMap { - fn clear(&mut self) { - self.get_mut().clear() - } - - fn set_v1(&mut self, filename: &HgPath, entry: DirstateEntry) { - self.get_mut().set_v1(filename, entry) - } - - fn add_file( - &mut self, - filename: &HgPath, - entry: DirstateEntry, - added: bool, - merged: bool, - from_p2: bool, - possibly_dirty: bool, - ) -> Result<(), DirstateError> { - self.get_mut().add_file( - filename, - entry, - added, - merged, - from_p2, - possibly_dirty, - ) - } - - fn remove_file( - &mut self, - filename: &HgPath, - in_merge: bool, - ) -> Result<(), DirstateError> { - self.get_mut().remove_file(filename, in_merge) - } - - fn drop_file(&mut self, filename: &HgPath) -> Result<bool, DirstateError> { - self.get_mut().drop_file(filename) - } - - fn clear_ambiguous_times( - &mut self, - filenames: Vec<HgPathBuf>, - now: i32, - ) -> Result<(), DirstateV2ParseError> { - self.get_mut().clear_ambiguous_times(filenames, now) - } - - fn non_normal_entries_contains( - &mut self, - key: &HgPath, - ) -> Result<bool, DirstateV2ParseError> { - self.get_mut().non_normal_entries_contains(key) - } - - fn non_normal_entries_remove(&mut self, key: &HgPath) -> bool { - self.get_mut().non_normal_entries_remove(key) - } - - fn non_normal_entries_add(&mut self, key: &HgPath) { - self.get_mut().non_normal_entries_add(key) - } - - fn non_normal_or_other_parent_paths( - &mut self, - ) -> Box<dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + '_> - { - self.get_mut().non_normal_or_other_parent_paths() - } - - fn set_non_normal_other_parent_entries(&mut self, force: bool) { - self.get_mut().set_non_normal_other_parent_entries(force) - } - - fn iter_non_normal_paths( - &mut self, - ) -> Box< - dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + Send + '_, - > { - self.get_mut().iter_non_normal_paths() - } - - fn iter_non_normal_paths_panic( - &self, - ) -> Box< - dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + Send + '_, - > { - self.get().iter_non_normal_paths_panic() - } - - fn iter_other_parent_paths( - &mut self, - ) -> Box< - dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + Send + '_, - > { - self.get_mut().iter_other_parent_paths() - } - - fn has_tracked_dir( - &mut self, - directory: &HgPath, - ) -> Result<bool, DirstateError> { - self.get_mut().has_tracked_dir(directory) - } - - fn has_dir(&mut self, directory: &HgPath) -> Result<bool, DirstateError> { - self.get_mut().has_dir(directory) - } - - fn pack_v1( - &mut self, - parents: DirstateParents, - now: Timestamp, - ) -> Result<Vec<u8>, DirstateError> { - self.get_mut().pack_v1(parents, now) - } - - fn pack_v2( - &mut self, - now: Timestamp, - can_append: bool, - ) -> Result<(Vec<u8>, Vec<u8>, bool), DirstateError> { - self.get_mut().pack_v2(now, can_append) - } - - fn status<'a>( - &'a mut self, - matcher: &'a (dyn Matcher + Sync), - root_dir: PathBuf, - ignore_files: Vec<PathBuf>, - options: StatusOptions, - ) -> Result<(DirstateStatus<'a>, Vec<PatternFileWarning>), StatusError> - { - self.get_mut() - .status(matcher, root_dir, ignore_files, options) - } - - fn copy_map_len(&self) -> usize { - self.get().copy_map_len() - } - - fn copy_map_iter(&self) -> CopyMapIter<'_> { - self.get().copy_map_iter() - } - - fn copy_map_contains_key( - &self, - key: &HgPath, - ) -> Result<bool, DirstateV2ParseError> { - self.get().copy_map_contains_key(key) - } - - fn copy_map_get( - &self, - key: &HgPath, - ) -> Result<Option<&HgPath>, DirstateV2ParseError> { - self.get().copy_map_get(key) - } - - fn copy_map_remove( - &mut self, - key: &HgPath, - ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> { - self.get_mut().copy_map_remove(key) - } - - fn copy_map_insert( - &mut self, - key: HgPathBuf, - value: HgPathBuf, - ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> { - self.get_mut().copy_map_insert(key, value) - } - - fn len(&self) -> usize { - self.get().len() - } - - fn contains_key( - &self, - key: &HgPath, - ) -> Result<bool, DirstateV2ParseError> { - self.get().contains_key(key) - } - - fn get( - &self, - key: &HgPath, - ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> { - self.get().get(key) - } - - fn iter(&self) -> StateMapIter<'_> { - self.get().iter() - } - - fn iter_tracked_dirs( - &mut self, - ) -> Result< - Box< - dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> - + Send - + '_, - >, - DirstateError, - > { - self.get_mut().iter_tracked_dirs() - } - - fn debug_iter( - &self, - ) -> Box< - dyn Iterator< - Item = Result< - (&HgPath, (u8, i32, i32, i32)), - DirstateV2ParseError, - >, - > + Send - + '_, - > { - self.get().debug_iter() - } -}
--- a/rust/hg-cpython/src/dirstate/owning.rs Mon Sep 06 13:39:54 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,117 +0,0 @@ -use cpython::PyBytes; -use cpython::Python; -use hg::dirstate_tree::dirstate_map::DirstateMap; -use hg::DirstateError; -use hg::DirstateParents; - -/// Keep a `DirstateMap<'on_disk>` next to the `on_disk` buffer that it -/// borrows. This is similar to the owning-ref crate. -/// -/// This is similar to [`OwningRef`] which is more limited because it -/// represents exactly one `&T` reference next to the value it borrows, as -/// opposed to a struct that may contain an arbitrary number of references in -/// arbitrarily-nested data structures. -/// -/// [`OwningRef`]: https://docs.rs/owning_ref/0.4.1/owning_ref/struct.OwningRef.html -pub(super) struct OwningDirstateMap { - /// Owned handle to a bytes buffer with a stable address. - /// - /// See <https://docs.rs/owning_ref/0.4.1/owning_ref/trait.StableAddress.html>. - on_disk: PyBytes, - - /// Pointer for `Box<DirstateMap<'on_disk>>`, typed-erased because the - /// language cannot represent a lifetime referencing a sibling field. - /// This is not quite a self-referencial struct (moving this struct is not - /// a problem as it doesn’t change the address of the bytes buffer owned - /// by `PyBytes`) but touches similar borrow-checker limitations. - ptr: *mut (), -} - -impl OwningDirstateMap { - pub fn new_v1( - py: Python, - on_disk: PyBytes, - ) -> Result<(Self, Option<DirstateParents>), DirstateError> { - let bytes: &'_ [u8] = on_disk.data(py); - let (map, parents) = DirstateMap::new_v1(bytes)?; - - // 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 }, parents)) - } - - pub fn new_v2( - py: Python, - on_disk: PyBytes, - data_size: usize, - tree_metadata: PyBytes, - ) -> Result<Self, DirstateError> { - 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> { - // 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 - // `self` owns the same `PyBytes` whose buffer `DirstateMap` - // references. That buffer has a stable memory address because the byte - // string value of a `PyBytes` is immutable. - let ptr: *mut DirstateMap<'a> = self.ptr.cast(); - // 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 } - } - - pub fn get<'a>(&'a self) -> &'a DirstateMap<'a> { - // SAFETY: same reasoning as in `get_mut` above. - let ptr: *mut DirstateMap<'a> = self.ptr.cast(); - unsafe { &*ptr } - } -} - -impl Drop for OwningDirstateMap { - fn drop(&mut self) { - // Silence a "field is never read" warning, and demonstrate that this - // value is still alive. - let _ = &self.on_disk; - // SAFETY: this cast is the same as in `get_mut`, and is valid for the - // same reason. `self.on_disk` still exists at this point, drop glue - // will drop it implicitly after this `drop` method returns. - let ptr: *mut DirstateMap<'_> = self.ptr.cast(); - // SAFETY: `Box::from_raw` takes ownership of the box away from `self`. - // This is fine because drop glue does nothig for `*mut ()` and we’re - // in `drop`, so `get` and `get_mut` cannot be called again. - unsafe { drop(Box::from_raw(ptr)) } - } -} - -fn _static_assert_is_send<T: Send>() {} - -fn _static_assert_fields_are_send() { - _static_assert_is_send::<PyBytes>(); - _static_assert_is_send::<Box<DirstateMap<'_>>>(); -} - -// 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<DirstateMap<'on_disk>>` 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. -unsafe impl Send for OwningDirstateMap {}
--- a/rust/hg-cpython/src/pybytes_deref.rs Mon Sep 06 13:39:54 2021 +0200 +++ b/rust/hg-cpython/src/pybytes_deref.rs Thu Sep 09 18:07:40 2021 +0200 @@ -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<Target = [u8]>`. @@ -40,6 +41,8 @@ } } +unsafe impl StableDeref for PyBytesDeref {} + fn require_send<T: Send>() {} #[allow(unused)]