##// END OF EJS Templates
rust: Move lazy initialization of `Repo::dirstate_map` into a generic struct...
Simon Sapin -
r48772:fc208d6f default
parent child Browse files
Show More
@@ -1,339 +1,367 b''
1 use crate::config::{Config, ConfigError, ConfigParseError};
1 use crate::config::{Config, ConfigError, ConfigParseError};
2 use crate::dirstate::DirstateParents;
2 use crate::dirstate::DirstateParents;
3 use crate::dirstate_tree::dirstate_map::DirstateMap;
3 use crate::dirstate_tree::dirstate_map::DirstateMap;
4 use crate::dirstate_tree::owning::OwningDirstateMap;
4 use crate::dirstate_tree::owning::OwningDirstateMap;
5 use crate::errors::HgError;
5 use crate::errors::HgError;
6 use crate::errors::HgResultExt;
6 use crate::errors::HgResultExt;
7 use crate::exit_codes;
7 use crate::exit_codes;
8 use crate::requirements;
8 use crate::requirements;
9 use crate::utils::files::get_path_from_bytes;
9 use crate::utils::files::get_path_from_bytes;
10 use crate::utils::SliceExt;
10 use crate::utils::SliceExt;
11 use crate::vfs::{is_dir, is_file, Vfs};
11 use crate::vfs::{is_dir, is_file, Vfs};
12 use crate::DirstateError;
12 use crate::DirstateError;
13 use std::cell::{Cell, Ref, RefCell, RefMut};
13 use std::cell::{Cell, Ref, RefCell, RefMut};
14 use std::collections::HashSet;
14 use std::collections::HashSet;
15 use std::path::{Path, PathBuf};
15 use std::path::{Path, PathBuf};
16
16
17 /// A repository on disk
17 /// A repository on disk
18 pub struct Repo {
18 pub struct Repo {
19 working_directory: PathBuf,
19 working_directory: PathBuf,
20 dot_hg: PathBuf,
20 dot_hg: PathBuf,
21 store: PathBuf,
21 store: PathBuf,
22 requirements: HashSet<String>,
22 requirements: HashSet<String>,
23 config: Config,
23 config: Config,
24 // None means not known/initialized yet
24 // None means not known/initialized yet
25 dirstate_parents: Cell<Option<DirstateParents>>,
25 dirstate_parents: Cell<Option<DirstateParents>>,
26 dirstate_map: RefCell<Option<OwningDirstateMap>>,
26 dirstate_map: LazyCell<OwningDirstateMap, DirstateError>,
27 }
27 }
28
28
29 #[derive(Debug, derive_more::From)]
29 #[derive(Debug, derive_more::From)]
30 pub enum RepoError {
30 pub enum RepoError {
31 NotFound {
31 NotFound {
32 at: PathBuf,
32 at: PathBuf,
33 },
33 },
34 #[from]
34 #[from]
35 ConfigParseError(ConfigParseError),
35 ConfigParseError(ConfigParseError),
36 #[from]
36 #[from]
37 Other(HgError),
37 Other(HgError),
38 }
38 }
39
39
40 impl From<ConfigError> for RepoError {
40 impl From<ConfigError> for RepoError {
41 fn from(error: ConfigError) -> Self {
41 fn from(error: ConfigError) -> Self {
42 match error {
42 match error {
43 ConfigError::Parse(error) => error.into(),
43 ConfigError::Parse(error) => error.into(),
44 ConfigError::Other(error) => error.into(),
44 ConfigError::Other(error) => error.into(),
45 }
45 }
46 }
46 }
47 }
47 }
48
48
49 impl Repo {
49 impl Repo {
50 /// tries to find nearest repository root in current working directory or
50 /// tries to find nearest repository root in current working directory or
51 /// its ancestors
51 /// its ancestors
52 pub fn find_repo_root() -> Result<PathBuf, RepoError> {
52 pub fn find_repo_root() -> Result<PathBuf, RepoError> {
53 let current_directory = crate::utils::current_dir()?;
53 let current_directory = crate::utils::current_dir()?;
54 // ancestors() is inclusive: it first yields `current_directory`
54 // ancestors() is inclusive: it first yields `current_directory`
55 // as-is.
55 // as-is.
56 for ancestor in current_directory.ancestors() {
56 for ancestor in current_directory.ancestors() {
57 if is_dir(ancestor.join(".hg"))? {
57 if is_dir(ancestor.join(".hg"))? {
58 return Ok(ancestor.to_path_buf());
58 return Ok(ancestor.to_path_buf());
59 }
59 }
60 }
60 }
61 return Err(RepoError::NotFound {
61 return Err(RepoError::NotFound {
62 at: current_directory,
62 at: current_directory,
63 });
63 });
64 }
64 }
65
65
66 /// Find a repository, either at the given path (which must contain a `.hg`
66 /// Find a repository, either at the given path (which must contain a `.hg`
67 /// sub-directory) or by searching the current directory and its
67 /// sub-directory) or by searching the current directory and its
68 /// ancestors.
68 /// ancestors.
69 ///
69 ///
70 /// A method with two very different "modes" like this usually a code smell
70 /// A method with two very different "modes" like this usually a code smell
71 /// to make two methods instead, but in this case an `Option` is what rhg
71 /// to make two methods instead, but in this case an `Option` is what rhg
72 /// sub-commands get from Clap for the `-R` / `--repository` CLI argument.
72 /// sub-commands get from Clap for the `-R` / `--repository` CLI argument.
73 /// Having two methods would just move that `if` to almost all callers.
73 /// Having two methods would just move that `if` to almost all callers.
74 pub fn find(
74 pub fn find(
75 config: &Config,
75 config: &Config,
76 explicit_path: Option<PathBuf>,
76 explicit_path: Option<PathBuf>,
77 ) -> Result<Self, RepoError> {
77 ) -> Result<Self, RepoError> {
78 if let Some(root) = explicit_path {
78 if let Some(root) = explicit_path {
79 if is_dir(root.join(".hg"))? {
79 if is_dir(root.join(".hg"))? {
80 Self::new_at_path(root.to_owned(), config)
80 Self::new_at_path(root.to_owned(), config)
81 } else if is_file(&root)? {
81 } else if is_file(&root)? {
82 Err(HgError::unsupported("bundle repository").into())
82 Err(HgError::unsupported("bundle repository").into())
83 } else {
83 } else {
84 Err(RepoError::NotFound {
84 Err(RepoError::NotFound {
85 at: root.to_owned(),
85 at: root.to_owned(),
86 })
86 })
87 }
87 }
88 } else {
88 } else {
89 let root = Self::find_repo_root()?;
89 let root = Self::find_repo_root()?;
90 Self::new_at_path(root, config)
90 Self::new_at_path(root, config)
91 }
91 }
92 }
92 }
93
93
94 /// To be called after checking that `.hg` is a sub-directory
94 /// To be called after checking that `.hg` is a sub-directory
95 fn new_at_path(
95 fn new_at_path(
96 working_directory: PathBuf,
96 working_directory: PathBuf,
97 config: &Config,
97 config: &Config,
98 ) -> Result<Self, RepoError> {
98 ) -> Result<Self, RepoError> {
99 let dot_hg = working_directory.join(".hg");
99 let dot_hg = working_directory.join(".hg");
100
100
101 let mut repo_config_files = Vec::new();
101 let mut repo_config_files = Vec::new();
102 repo_config_files.push(dot_hg.join("hgrc"));
102 repo_config_files.push(dot_hg.join("hgrc"));
103 repo_config_files.push(dot_hg.join("hgrc-not-shared"));
103 repo_config_files.push(dot_hg.join("hgrc-not-shared"));
104
104
105 let hg_vfs = Vfs { base: &dot_hg };
105 let hg_vfs = Vfs { base: &dot_hg };
106 let mut reqs = requirements::load_if_exists(hg_vfs)?;
106 let mut reqs = requirements::load_if_exists(hg_vfs)?;
107 let relative =
107 let relative =
108 reqs.contains(requirements::RELATIVE_SHARED_REQUIREMENT);
108 reqs.contains(requirements::RELATIVE_SHARED_REQUIREMENT);
109 let shared =
109 let shared =
110 reqs.contains(requirements::SHARED_REQUIREMENT) || relative;
110 reqs.contains(requirements::SHARED_REQUIREMENT) || relative;
111
111
112 // From `mercurial/localrepo.py`:
112 // From `mercurial/localrepo.py`:
113 //
113 //
114 // if .hg/requires contains the sharesafe requirement, it means
114 // if .hg/requires contains the sharesafe requirement, it means
115 // there exists a `.hg/store/requires` too and we should read it
115 // there exists a `.hg/store/requires` too and we should read it
116 // NOTE: presence of SHARESAFE_REQUIREMENT imply that store requirement
116 // NOTE: presence of SHARESAFE_REQUIREMENT imply that store requirement
117 // is present. We never write SHARESAFE_REQUIREMENT for a repo if store
117 // is present. We never write SHARESAFE_REQUIREMENT for a repo if store
118 // is not present, refer checkrequirementscompat() for that
118 // is not present, refer checkrequirementscompat() for that
119 //
119 //
120 // However, if SHARESAFE_REQUIREMENT is not present, it means that the
120 // However, if SHARESAFE_REQUIREMENT is not present, it means that the
121 // repository was shared the old way. We check the share source
121 // repository was shared the old way. We check the share source
122 // .hg/requires for SHARESAFE_REQUIREMENT to detect whether the
122 // .hg/requires for SHARESAFE_REQUIREMENT to detect whether the
123 // current repository needs to be reshared
123 // current repository needs to be reshared
124 let share_safe = reqs.contains(requirements::SHARESAFE_REQUIREMENT);
124 let share_safe = reqs.contains(requirements::SHARESAFE_REQUIREMENT);
125
125
126 let store_path;
126 let store_path;
127 if !shared {
127 if !shared {
128 store_path = dot_hg.join("store");
128 store_path = dot_hg.join("store");
129 } else {
129 } else {
130 let bytes = hg_vfs.read("sharedpath")?;
130 let bytes = hg_vfs.read("sharedpath")?;
131 let mut shared_path =
131 let mut shared_path =
132 get_path_from_bytes(bytes.trim_end_matches(|b| b == b'\n'))
132 get_path_from_bytes(bytes.trim_end_matches(|b| b == b'\n'))
133 .to_owned();
133 .to_owned();
134 if relative {
134 if relative {
135 shared_path = dot_hg.join(shared_path)
135 shared_path = dot_hg.join(shared_path)
136 }
136 }
137 if !is_dir(&shared_path)? {
137 if !is_dir(&shared_path)? {
138 return Err(HgError::corrupted(format!(
138 return Err(HgError::corrupted(format!(
139 ".hg/sharedpath points to nonexistent directory {}",
139 ".hg/sharedpath points to nonexistent directory {}",
140 shared_path.display()
140 shared_path.display()
141 ))
141 ))
142 .into());
142 .into());
143 }
143 }
144
144
145 store_path = shared_path.join("store");
145 store_path = shared_path.join("store");
146
146
147 let source_is_share_safe =
147 let source_is_share_safe =
148 requirements::load(Vfs { base: &shared_path })?
148 requirements::load(Vfs { base: &shared_path })?
149 .contains(requirements::SHARESAFE_REQUIREMENT);
149 .contains(requirements::SHARESAFE_REQUIREMENT);
150
150
151 if share_safe && !source_is_share_safe {
151 if share_safe && !source_is_share_safe {
152 return Err(match config
152 return Err(match config
153 .get(b"share", b"safe-mismatch.source-not-safe")
153 .get(b"share", b"safe-mismatch.source-not-safe")
154 {
154 {
155 Some(b"abort") | None => HgError::abort(
155 Some(b"abort") | None => HgError::abort(
156 "abort: share source does not support share-safe requirement\n\
156 "abort: share source does not support share-safe requirement\n\
157 (see `hg help config.format.use-share-safe` for more information)",
157 (see `hg help config.format.use-share-safe` for more information)",
158 exit_codes::ABORT,
158 exit_codes::ABORT,
159 ),
159 ),
160 _ => HgError::unsupported("share-safe downgrade"),
160 _ => HgError::unsupported("share-safe downgrade"),
161 }
161 }
162 .into());
162 .into());
163 } else if source_is_share_safe && !share_safe {
163 } else if source_is_share_safe && !share_safe {
164 return Err(
164 return Err(
165 match config.get(b"share", b"safe-mismatch.source-safe") {
165 match config.get(b"share", b"safe-mismatch.source-safe") {
166 Some(b"abort") | None => HgError::abort(
166 Some(b"abort") | None => HgError::abort(
167 "abort: version mismatch: source uses share-safe \
167 "abort: version mismatch: source uses share-safe \
168 functionality while the current share does not\n\
168 functionality while the current share does not\n\
169 (see `hg help config.format.use-share-safe` for more information)",
169 (see `hg help config.format.use-share-safe` for more information)",
170 exit_codes::ABORT,
170 exit_codes::ABORT,
171 ),
171 ),
172 _ => HgError::unsupported("share-safe upgrade"),
172 _ => HgError::unsupported("share-safe upgrade"),
173 }
173 }
174 .into(),
174 .into(),
175 );
175 );
176 }
176 }
177
177
178 if share_safe {
178 if share_safe {
179 repo_config_files.insert(0, shared_path.join("hgrc"))
179 repo_config_files.insert(0, shared_path.join("hgrc"))
180 }
180 }
181 }
181 }
182 if share_safe {
182 if share_safe {
183 reqs.extend(requirements::load(Vfs { base: &store_path })?);
183 reqs.extend(requirements::load(Vfs { base: &store_path })?);
184 }
184 }
185
185
186 let repo_config = if std::env::var_os("HGRCSKIPREPO").is_none() {
186 let repo_config = if std::env::var_os("HGRCSKIPREPO").is_none() {
187 config.combine_with_repo(&repo_config_files)?
187 config.combine_with_repo(&repo_config_files)?
188 } else {
188 } else {
189 config.clone()
189 config.clone()
190 };
190 };
191
191
192 let repo = Self {
192 let repo = Self {
193 requirements: reqs,
193 requirements: reqs,
194 working_directory,
194 working_directory,
195 store: store_path,
195 store: store_path,
196 dot_hg,
196 dot_hg,
197 config: repo_config,
197 config: repo_config,
198 dirstate_parents: Cell::new(None),
198 dirstate_parents: Cell::new(None),
199 dirstate_map: RefCell::new(None),
199 dirstate_map: LazyCell::new(Self::new_dirstate_map),
200 };
200 };
201
201
202 requirements::check(&repo)?;
202 requirements::check(&repo)?;
203
203
204 Ok(repo)
204 Ok(repo)
205 }
205 }
206
206
207 pub fn working_directory_path(&self) -> &Path {
207 pub fn working_directory_path(&self) -> &Path {
208 &self.working_directory
208 &self.working_directory
209 }
209 }
210
210
211 pub fn requirements(&self) -> &HashSet<String> {
211 pub fn requirements(&self) -> &HashSet<String> {
212 &self.requirements
212 &self.requirements
213 }
213 }
214
214
215 pub fn config(&self) -> &Config {
215 pub fn config(&self) -> &Config {
216 &self.config
216 &self.config
217 }
217 }
218
218
219 /// For accessing repository files (in `.hg`), except for the store
219 /// For accessing repository files (in `.hg`), except for the store
220 /// (`.hg/store`).
220 /// (`.hg/store`).
221 pub fn hg_vfs(&self) -> Vfs<'_> {
221 pub fn hg_vfs(&self) -> Vfs<'_> {
222 Vfs { base: &self.dot_hg }
222 Vfs { base: &self.dot_hg }
223 }
223 }
224
224
225 /// For accessing repository store files (in `.hg/store`)
225 /// For accessing repository store files (in `.hg/store`)
226 pub fn store_vfs(&self) -> Vfs<'_> {
226 pub fn store_vfs(&self) -> Vfs<'_> {
227 Vfs { base: &self.store }
227 Vfs { base: &self.store }
228 }
228 }
229
229
230 /// For accessing the working copy
230 /// For accessing the working copy
231 pub fn working_directory_vfs(&self) -> Vfs<'_> {
231 pub fn working_directory_vfs(&self) -> Vfs<'_> {
232 Vfs {
232 Vfs {
233 base: &self.working_directory,
233 base: &self.working_directory,
234 }
234 }
235 }
235 }
236
236
237 pub fn has_dirstate_v2(&self) -> bool {
237 pub fn has_dirstate_v2(&self) -> bool {
238 self.requirements
238 self.requirements
239 .contains(requirements::DIRSTATE_V2_REQUIREMENT)
239 .contains(requirements::DIRSTATE_V2_REQUIREMENT)
240 }
240 }
241
241
242 fn dirstate_file_contents(&self) -> Result<Vec<u8>, HgError> {
242 fn dirstate_file_contents(&self) -> Result<Vec<u8>, HgError> {
243 Ok(self
243 Ok(self
244 .hg_vfs()
244 .hg_vfs()
245 .read("dirstate")
245 .read("dirstate")
246 .io_not_found_as_none()?
246 .io_not_found_as_none()?
247 .unwrap_or(Vec::new()))
247 .unwrap_or(Vec::new()))
248 }
248 }
249
249
250 pub fn dirstate_parents(&self) -> Result<DirstateParents, HgError> {
250 pub fn dirstate_parents(&self) -> Result<DirstateParents, HgError> {
251 if let Some(parents) = self.dirstate_parents.get() {
251 if let Some(parents) = self.dirstate_parents.get() {
252 return Ok(parents);
252 return Ok(parents);
253 }
253 }
254 let dirstate = self.dirstate_file_contents()?;
254 let dirstate = self.dirstate_file_contents()?;
255 let parents = if dirstate.is_empty() {
255 let parents = if dirstate.is_empty() {
256 DirstateParents::NULL
256 DirstateParents::NULL
257 } else if self.has_dirstate_v2() {
257 } else if self.has_dirstate_v2() {
258 crate::dirstate_tree::on_disk::read_docket(&dirstate)?.parents()
258 crate::dirstate_tree::on_disk::read_docket(&dirstate)?.parents()
259 } else {
259 } else {
260 crate::dirstate::parsers::parse_dirstate_parents(&dirstate)?
260 crate::dirstate::parsers::parse_dirstate_parents(&dirstate)?
261 .clone()
261 .clone()
262 };
262 };
263 self.dirstate_parents.set(Some(parents));
263 self.dirstate_parents.set(Some(parents));
264 Ok(parents)
264 Ok(parents)
265 }
265 }
266
266
267 fn new_dirstate_map(&self) -> Result<OwningDirstateMap, DirstateError> {
267 fn new_dirstate_map(&self) -> Result<OwningDirstateMap, DirstateError> {
268 let dirstate_file_contents = self.dirstate_file_contents()?;
268 let dirstate_file_contents = self.dirstate_file_contents()?;
269 if dirstate_file_contents.is_empty() {
269 if dirstate_file_contents.is_empty() {
270 self.dirstate_parents.set(Some(DirstateParents::NULL));
270 self.dirstate_parents.set(Some(DirstateParents::NULL));
271 Ok(OwningDirstateMap::new_empty(Vec::new()))
271 Ok(OwningDirstateMap::new_empty(Vec::new()))
272 } else if self.has_dirstate_v2() {
272 } else if self.has_dirstate_v2() {
273 let docket = crate::dirstate_tree::on_disk::read_docket(
273 let docket = crate::dirstate_tree::on_disk::read_docket(
274 &dirstate_file_contents,
274 &dirstate_file_contents,
275 )?;
275 )?;
276 self.dirstate_parents.set(Some(docket.parents()));
276 self.dirstate_parents.set(Some(docket.parents()));
277 let data_size = docket.data_size();
277 let data_size = docket.data_size();
278 let metadata = docket.tree_metadata();
278 let metadata = docket.tree_metadata();
279 let mut map = if let Some(data_mmap) = self
279 let mut map = if let Some(data_mmap) = self
280 .hg_vfs()
280 .hg_vfs()
281 .mmap_open(docket.data_filename())
281 .mmap_open(docket.data_filename())
282 .io_not_found_as_none()?
282 .io_not_found_as_none()?
283 {
283 {
284 OwningDirstateMap::new_empty(MmapWrapper(data_mmap))
284 OwningDirstateMap::new_empty(MmapWrapper(data_mmap))
285 } else {
285 } else {
286 OwningDirstateMap::new_empty(Vec::new())
286 OwningDirstateMap::new_empty(Vec::new())
287 };
287 };
288 let (on_disk, placeholder) = map.get_mut_pair();
288 let (on_disk, placeholder) = map.get_mut_pair();
289 *placeholder = DirstateMap::new_v2(on_disk, data_size, metadata)?;
289 *placeholder = DirstateMap::new_v2(on_disk, data_size, metadata)?;
290 Ok(map)
290 Ok(map)
291 } else {
291 } else {
292 let mut map = OwningDirstateMap::new_empty(dirstate_file_contents);
292 let mut map = OwningDirstateMap::new_empty(dirstate_file_contents);
293 let (on_disk, placeholder) = map.get_mut_pair();
293 let (on_disk, placeholder) = map.get_mut_pair();
294 let (inner, parents) = DirstateMap::new_v1(on_disk)?;
294 let (inner, parents) = DirstateMap::new_v1(on_disk)?;
295 self.dirstate_parents
295 self.dirstate_parents
296 .set(Some(parents.unwrap_or(DirstateParents::NULL)));
296 .set(Some(parents.unwrap_or(DirstateParents::NULL)));
297 *placeholder = inner;
297 *placeholder = inner;
298 Ok(map)
298 Ok(map)
299 }
299 }
300 }
300 }
301
301
302 pub fn dirstate_map(
302 pub fn dirstate_map(
303 &self,
303 &self,
304 ) -> Result<Ref<OwningDirstateMap>, DirstateError> {
304 ) -> Result<Ref<OwningDirstateMap>, DirstateError> {
305 let mut borrowed = self.dirstate_map.borrow();
305 self.dirstate_map.get_or_init(self)
306 }
307
308 pub fn dirstate_map_mut(
309 &self,
310 ) -> Result<RefMut<OwningDirstateMap>, DirstateError> {
311 self.dirstate_map.get_mut_or_init(self)
312 }
313 }
314
315 /// Lazily-initialized component of `Repo` with interior mutability
316 ///
317 /// This differs from `OnceCell` in that the value can still be "deinitialized"
318 /// later by setting its inner `Option` to `None`.
319 struct LazyCell<T, E> {
320 value: RefCell<Option<T>>,
321 // `Fn`s that don’t capture environment are zero-size, so this box does
322 // not allocate:
323 init: Box<dyn Fn(&Repo) -> Result<T, E>>,
324 }
325
326 impl<T, E> LazyCell<T, E> {
327 fn new(init: impl Fn(&Repo) -> Result<T, E> + 'static) -> Self {
328 Self {
329 value: RefCell::new(None),
330 init: Box::new(init),
331 }
332 }
333
334 fn get_or_init(&self, repo: &Repo) -> Result<Ref<T>, E> {
335 let mut borrowed = self.value.borrow();
306 if borrowed.is_none() {
336 if borrowed.is_none() {
307 drop(borrowed);
337 drop(borrowed);
308 // Only use `borrow_mut` if it is really needed to avoid panic in
338 // Only use `borrow_mut` if it is really needed to avoid panic in
309 // case there is another outstanding borrow but mutation is not
339 // case there is another outstanding borrow but mutation is not
310 // needed.
340 // needed.
311 *self.dirstate_map.borrow_mut() = Some(self.new_dirstate_map()?);
341 *self.value.borrow_mut() = Some((self.init)(repo)?);
312 borrowed = self.dirstate_map.borrow()
342 borrowed = self.value.borrow()
313 }
343 }
314 Ok(Ref::map(borrowed, |option| option.as_ref().unwrap()))
344 Ok(Ref::map(borrowed, |option| option.as_ref().unwrap()))
315 }
345 }
316
346
317 pub fn dirstate_map_mut(
347 pub fn get_mut_or_init(&self, repo: &Repo) -> Result<RefMut<T>, E> {
318 &self,
348 let mut borrowed = self.value.borrow_mut();
319 ) -> Result<RefMut<OwningDirstateMap>, DirstateError> {
320 let mut borrowed = self.dirstate_map.borrow_mut();
321 if borrowed.is_none() {
349 if borrowed.is_none() {
322 *borrowed = Some(self.new_dirstate_map()?);
350 *borrowed = Some((self.init)(repo)?);
323 }
351 }
324 Ok(RefMut::map(borrowed, |option| option.as_mut().unwrap()))
352 Ok(RefMut::map(borrowed, |option| option.as_mut().unwrap()))
325 }
353 }
326 }
354 }
327
355
328 // TODO: remove this when https://github.com/RazrFalcon/memmap2-rs/pull/22 is on crates.io
356 // TODO: remove this when https://github.com/RazrFalcon/memmap2-rs/pull/22 is on crates.io
329 struct MmapWrapper(memmap2::Mmap);
357 struct MmapWrapper(memmap2::Mmap);
330
358
331 impl std::ops::Deref for MmapWrapper {
359 impl std::ops::Deref for MmapWrapper {
332 type Target = [u8];
360 type Target = [u8];
333
361
334 fn deref(&self) -> &[u8] {
362 fn deref(&self) -> &[u8] {
335 self.0.deref()
363 self.0.deref()
336 }
364 }
337 }
365 }
338
366
339 unsafe impl stable_deref_trait::StableDeref for MmapWrapper {}
367 unsafe impl stable_deref_trait::StableDeref for MmapWrapper {}
General Comments 0
You need to be logged in to leave comments. Login now