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
--- 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)]