##// END OF EJS Templates
rustdoc: fixed warnings about links...
Georges Racinet -
r51272:331a3cbe default
parent child Browse files
Show More
@@ -1,119 +1,119 b''
1 use std::fs;
1 use std::fs;
2 use std::io;
2 use std::io;
3 use std::os::unix::fs::{MetadataExt, PermissionsExt};
3 use std::os::unix::fs::{MetadataExt, PermissionsExt};
4 use std::path::Path;
4 use std::path::Path;
5
5
6 const EXECFLAGS: u32 = 0o111;
6 const EXECFLAGS: u32 = 0o111;
7
7
8 fn is_executable(path: impl AsRef<Path>) -> Result<bool, io::Error> {
8 fn is_executable(path: impl AsRef<Path>) -> Result<bool, io::Error> {
9 let metadata = fs::metadata(path)?;
9 let metadata = fs::metadata(path)?;
10 let mode = metadata.mode();
10 let mode = metadata.mode();
11 Ok(mode & EXECFLAGS != 0)
11 Ok(mode & EXECFLAGS != 0)
12 }
12 }
13
13
14 fn make_executable(path: impl AsRef<Path>) -> Result<(), io::Error> {
14 fn make_executable(path: impl AsRef<Path>) -> Result<(), io::Error> {
15 let mode = fs::metadata(path.as_ref())?.mode();
15 let mode = fs::metadata(path.as_ref())?.mode();
16 fs::set_permissions(
16 fs::set_permissions(
17 path,
17 path,
18 fs::Permissions::from_mode((mode & 0o777) | EXECFLAGS),
18 fs::Permissions::from_mode((mode & 0o777) | EXECFLAGS),
19 )?;
19 )?;
20 Ok(())
20 Ok(())
21 }
21 }
22
22
23 fn copy_mode(
23 fn copy_mode(
24 src: impl AsRef<Path>,
24 src: impl AsRef<Path>,
25 dst: impl AsRef<Path>,
25 dst: impl AsRef<Path>,
26 ) -> Result<(), io::Error> {
26 ) -> Result<(), io::Error> {
27 let mode = match fs::symlink_metadata(src) {
27 let mode = match fs::symlink_metadata(src) {
28 Ok(metadata) => metadata.mode(),
28 Ok(metadata) => metadata.mode(),
29 Err(e) if e.kind() == io::ErrorKind::NotFound =>
29 Err(e) if e.kind() == io::ErrorKind::NotFound =>
30 // copymode in python has a more complicated handling of FileNotFound
30 // copymode in python has a more complicated handling of FileNotFound
31 // error, which we don't need because all it does is applying
31 // error, which we don't need because all it does is applying
32 // umask, which the OS already does when we mkdir.
32 // umask, which the OS already does when we mkdir.
33 {
33 {
34 return Ok(())
34 return Ok(())
35 }
35 }
36 Err(e) => return Err(e),
36 Err(e) => return Err(e),
37 };
37 };
38 fs::set_permissions(dst, fs::Permissions::from_mode(mode))?;
38 fs::set_permissions(dst, fs::Permissions::from_mode(mode))?;
39 Ok(())
39 Ok(())
40 }
40 }
41
41
42 fn check_exec_impl(path: impl AsRef<Path>) -> Result<bool, io::Error> {
42 fn check_exec_impl(path: impl AsRef<Path>) -> Result<bool, io::Error> {
43 let basedir = path.as_ref().join(".hg");
43 let basedir = path.as_ref().join(".hg");
44 let cachedir = basedir.join("wcache");
44 let cachedir = basedir.join("wcache");
45 let storedir = basedir.join("store");
45 let storedir = basedir.join("store");
46
46
47 if !cachedir.exists() {
47 if !cachedir.exists() {
48 // we want to create the 'cache' directory, not the '.hg' one.
48 // we want to create the 'cache' directory, not the '.hg' one.
49 // Automatically creating '.hg' directory could silently spawn
49 // Automatically creating '.hg' directory could silently spawn
50 // invalid Mercurial repositories. That seems like a bad idea.
50 // invalid Mercurial repositories. That seems like a bad idea.
51 fs::create_dir(&cachedir)
51 fs::create_dir(&cachedir)
52 .and_then(|()| {
52 .and_then(|()| {
53 if storedir.exists() {
53 if storedir.exists() {
54 copy_mode(&storedir, &cachedir)
54 copy_mode(&storedir, &cachedir)
55 } else {
55 } else {
56 copy_mode(&basedir, &cachedir)
56 copy_mode(&basedir, &cachedir)
57 }
57 }
58 })
58 })
59 .ok();
59 .ok();
60 }
60 }
61
61
62 let leave_file: bool;
62 let leave_file: bool;
63 let checkdir: &Path;
63 let checkdir: &Path;
64 let checkisexec = cachedir.join("checkisexec");
64 let checkisexec = cachedir.join("checkisexec");
65 let checknoexec = cachedir.join("checknoexec");
65 let checknoexec = cachedir.join("checknoexec");
66 if cachedir.is_dir() {
66 if cachedir.is_dir() {
67 // Check if both files already exist in cache and have correct
67 // Check if both files already exist in cache and have correct
68 // permissions. if so, we assume that permissions work.
68 // permissions. if so, we assume that permissions work.
69 // If not, we delete the files and try again.
69 // If not, we delete the files and try again.
70 match is_executable(&checkisexec) {
70 match is_executable(&checkisexec) {
71 Err(e) if e.kind() == io::ErrorKind::NotFound => (),
71 Err(e) if e.kind() == io::ErrorKind::NotFound => (),
72 Err(e) => return Err(e),
72 Err(e) => return Err(e),
73 Ok(is_exec) => {
73 Ok(is_exec) => {
74 if is_exec {
74 if is_exec {
75 let noexec_is_exec = match is_executable(&checknoexec) {
75 let noexec_is_exec = match is_executable(&checknoexec) {
76 Err(e) if e.kind() == io::ErrorKind::NotFound => {
76 Err(e) if e.kind() == io::ErrorKind::NotFound => {
77 fs::write(&checknoexec, "")?;
77 fs::write(&checknoexec, "")?;
78 is_executable(&checknoexec)?
78 is_executable(&checknoexec)?
79 }
79 }
80 Err(e) => return Err(e),
80 Err(e) => return Err(e),
81 Ok(exec) => exec,
81 Ok(exec) => exec,
82 };
82 };
83 if !noexec_is_exec {
83 if !noexec_is_exec {
84 // check-exec is exec and check-no-exec is not exec
84 // check-exec is exec and check-no-exec is not exec
85 return Ok(true);
85 return Ok(true);
86 }
86 }
87 fs::remove_file(&checknoexec)?;
87 fs::remove_file(&checknoexec)?;
88 }
88 }
89 fs::remove_file(&checkisexec)?;
89 fs::remove_file(&checkisexec)?;
90 }
90 }
91 }
91 }
92 checkdir = &cachedir;
92 checkdir = &cachedir;
93 leave_file = true;
93 leave_file = true;
94 } else {
94 } else {
95 // no cache directory (probably because .hg doesn't exist):
95 // no cache directory (probably because .hg doesn't exist):
96 // check directly in `path` and don't leave the temp file behind
96 // check directly in `path` and don't leave the temp file behind
97 checkdir = path.as_ref();
97 checkdir = path.as_ref();
98 leave_file = false;
98 leave_file = false;
99 };
99 };
100
100
101 let tmp_file = tempfile::NamedTempFile::new_in(checkdir)?;
101 let tmp_file = tempfile::NamedTempFile::new_in(checkdir)?;
102 if !is_executable(tmp_file.path())? {
102 if !is_executable(tmp_file.path())? {
103 make_executable(tmp_file.path())?;
103 make_executable(tmp_file.path())?;
104 if is_executable(tmp_file.path())? {
104 if is_executable(tmp_file.path())? {
105 if leave_file {
105 if leave_file {
106 tmp_file.persist(checkisexec).ok();
106 tmp_file.persist(checkisexec).ok();
107 }
107 }
108 return Ok(true);
108 return Ok(true);
109 }
109 }
110 }
110 }
111
111
112 Ok(false)
112 Ok(false)
113 }
113 }
114
114
115 /// This function is a rust rewrite of [checkexec] function from [posix.py]
115 /// This function is a rust rewrite of `checkexec` function from `posix.py`
116 /// Returns true if the filesystem supports execute permissions.
116 /// Returns true if the filesystem supports execute permissions.
117 pub fn check_exec(path: impl AsRef<Path>) -> bool {
117 pub fn check_exec(path: impl AsRef<Path>) -> bool {
118 check_exec_impl(path).unwrap_or(false)
118 check_exec_impl(path).unwrap_or(false)
119 }
119 }
@@ -1,1069 +1,1069 b''
1 // Copyright 2018-2020 Georges Racinet <georges.racinet@octobus.net>
1 // Copyright 2018-2020 Georges Racinet <georges.racinet@octobus.net>
2 // and Mercurial contributors
2 // and Mercurial contributors
3 //
3 //
4 // This software may be used and distributed according to the terms of the
4 // This software may be used and distributed according to the terms of the
5 // GNU General Public License version 2 or any later version.
5 // GNU General Public License version 2 or any later version.
6 //! Indexing facilities for fast retrieval of `Revision` from `Node`
6 //! Indexing facilities for fast retrieval of `Revision` from `Node`
7 //!
7 //!
8 //! This provides a variation on the 16-ary radix tree that is
8 //! This provides a variation on the 16-ary radix tree that is
9 //! provided as "nodetree" in revlog.c, ready for append-only persistence
9 //! provided as "nodetree" in revlog.c, ready for append-only persistence
10 //! on disk.
10 //! on disk.
11 //!
11 //!
12 //! Following existing implicit conventions, the "nodemap" terminology
12 //! Following existing implicit conventions, the "nodemap" terminology
13 //! is used in a more abstract context.
13 //! is used in a more abstract context.
14
14
15 use super::{
15 use super::{
16 node::NULL_NODE, Node, NodePrefix, Revision, RevlogIndex, NULL_REVISION,
16 node::NULL_NODE, Node, NodePrefix, Revision, RevlogIndex, NULL_REVISION,
17 };
17 };
18
18
19 use bytes_cast::{unaligned, BytesCast};
19 use bytes_cast::{unaligned, BytesCast};
20 use std::cmp::max;
20 use std::cmp::max;
21 use std::fmt;
21 use std::fmt;
22 use std::mem::{self, align_of, size_of};
22 use std::mem::{self, align_of, size_of};
23 use std::ops::Deref;
23 use std::ops::Deref;
24 use std::ops::Index;
24 use std::ops::Index;
25
25
26 #[derive(Debug, PartialEq)]
26 #[derive(Debug, PartialEq)]
27 pub enum NodeMapError {
27 pub enum NodeMapError {
28 MultipleResults,
28 MultipleResults,
29 /// A `Revision` stored in the nodemap could not be found in the index
29 /// A `Revision` stored in the nodemap could not be found in the index
30 RevisionNotInIndex(Revision),
30 RevisionNotInIndex(Revision),
31 }
31 }
32
32
33 /// Mapping system from Mercurial nodes to revision numbers.
33 /// Mapping system from Mercurial nodes to revision numbers.
34 ///
34 ///
35 /// ## `RevlogIndex` and `NodeMap`
35 /// ## `RevlogIndex` and `NodeMap`
36 ///
36 ///
37 /// One way to think about their relationship is that
37 /// One way to think about their relationship is that
38 /// the `NodeMap` is a prefix-oriented reverse index of the `Node` information
38 /// the `NodeMap` is a prefix-oriented reverse index of the `Node` information
39 /// carried by a [`RevlogIndex`].
39 /// carried by a [`RevlogIndex`].
40 ///
40 ///
41 /// Many of the methods in this trait take a `RevlogIndex` argument
41 /// Many of the methods in this trait take a `RevlogIndex` argument
42 /// which is used for validation of their results. This index must naturally
42 /// which is used for validation of their results. This index must naturally
43 /// be the one the `NodeMap` is about, and it must be consistent.
43 /// be the one the `NodeMap` is about, and it must be consistent.
44 ///
44 ///
45 /// Notably, the `NodeMap` must not store
45 /// Notably, the `NodeMap` must not store
46 /// information about more `Revision` values than there are in the index.
46 /// information about more `Revision` values than there are in the index.
47 /// In these methods, an encountered `Revision` is not in the index, a
47 /// In these methods, an encountered `Revision` is not in the index, a
48 /// [`RevisionNotInIndex`] error is returned.
48 /// [`RevisionNotInIndex`] error is returned.
49 ///
49 ///
50 /// In insert operations, the rule is thus that the `NodeMap` must always
50 /// In insert operations, the rule is thus that the `NodeMap` must always
51 /// be updated after the `RevlogIndex`
51 /// be updated after the `RevlogIndex`
52 /// be updated first, and the `NodeMap` second.
52 /// be updated first, and the `NodeMap` second.
53 ///
53 ///
54 /// [`RevisionNotInIndex`]: enum.NodeMapError.html#variant.RevisionNotInIndex
54 /// [`RevisionNotInIndex`]: enum.NodeMapError.html#variant.RevisionNotInIndex
55 /// [`RevlogIndex`]: ../trait.RevlogIndex.html
55 /// [`RevlogIndex`]: ../trait.RevlogIndex.html
56 pub trait NodeMap {
56 pub trait NodeMap {
57 /// Find the unique `Revision` having the given `Node`
57 /// Find the unique `Revision` having the given `Node`
58 ///
58 ///
59 /// If no Revision matches the given `Node`, `Ok(None)` is returned.
59 /// If no Revision matches the given `Node`, `Ok(None)` is returned.
60 fn find_node(
60 fn find_node(
61 &self,
61 &self,
62 index: &impl RevlogIndex,
62 index: &impl RevlogIndex,
63 node: &Node,
63 node: &Node,
64 ) -> Result<Option<Revision>, NodeMapError> {
64 ) -> Result<Option<Revision>, NodeMapError> {
65 self.find_bin(index, node.into())
65 self.find_bin(index, node.into())
66 }
66 }
67
67
68 /// Find the unique Revision whose `Node` starts with a given binary prefix
68 /// Find the unique Revision whose `Node` starts with a given binary prefix
69 ///
69 ///
70 /// If no Revision matches the given prefix, `Ok(None)` is returned.
70 /// If no Revision matches the given prefix, `Ok(None)` is returned.
71 ///
71 ///
72 /// If several Revisions match the given prefix, a [`MultipleResults`]
72 /// If several Revisions match the given prefix, a
73 /// error is returned.
73 /// [MultipleResults](NodeMapError) error is returned.
74 fn find_bin(
74 fn find_bin(
75 &self,
75 &self,
76 idx: &impl RevlogIndex,
76 idx: &impl RevlogIndex,
77 prefix: NodePrefix,
77 prefix: NodePrefix,
78 ) -> Result<Option<Revision>, NodeMapError>;
78 ) -> Result<Option<Revision>, NodeMapError>;
79
79
80 /// Give the size of the shortest node prefix that determines
80 /// Give the size of the shortest node prefix that determines
81 /// the revision uniquely.
81 /// the revision uniquely.
82 ///
82 ///
83 /// From a binary node prefix, if it is matched in the node map, this
83 /// From a binary node prefix, if it is matched in the node map, this
84 /// returns the number of hexadecimal digits that would had sufficed
84 /// returns the number of hexadecimal digits that would had sufficed
85 /// to find the revision uniquely.
85 /// to find the revision uniquely.
86 ///
86 ///
87 /// Returns `None` if no `Revision` could be found for the prefix.
87 /// Returns `None` if no `Revision` could be found for the prefix.
88 ///
88 ///
89 /// If several Revisions match the given prefix, a [`MultipleResults`]
89 /// If several Revisions match the given prefix, a
90 /// error is returned.
90 /// [MultipleResults](NodeMapError) error is returned.
91 fn unique_prefix_len_bin(
91 fn unique_prefix_len_bin(
92 &self,
92 &self,
93 idx: &impl RevlogIndex,
93 idx: &impl RevlogIndex,
94 node_prefix: NodePrefix,
94 node_prefix: NodePrefix,
95 ) -> Result<Option<usize>, NodeMapError>;
95 ) -> Result<Option<usize>, NodeMapError>;
96
96
97 /// Same as `unique_prefix_len_bin`, with a full `Node` as input
97 /// Same as `unique_prefix_len_bin`, with a full `Node` as input
98 fn unique_prefix_len_node(
98 fn unique_prefix_len_node(
99 &self,
99 &self,
100 idx: &impl RevlogIndex,
100 idx: &impl RevlogIndex,
101 node: &Node,
101 node: &Node,
102 ) -> Result<Option<usize>, NodeMapError> {
102 ) -> Result<Option<usize>, NodeMapError> {
103 self.unique_prefix_len_bin(idx, node.into())
103 self.unique_prefix_len_bin(idx, node.into())
104 }
104 }
105 }
105 }
106
106
107 pub trait MutableNodeMap: NodeMap {
107 pub trait MutableNodeMap: NodeMap {
108 fn insert<I: RevlogIndex>(
108 fn insert<I: RevlogIndex>(
109 &mut self,
109 &mut self,
110 index: &I,
110 index: &I,
111 node: &Node,
111 node: &Node,
112 rev: Revision,
112 rev: Revision,
113 ) -> Result<(), NodeMapError>;
113 ) -> Result<(), NodeMapError>;
114 }
114 }
115
115
116 /// Low level NodeTree [`Blocks`] elements
116 /// Low level NodeTree [`Block`] elements
117 ///
117 ///
118 /// These are exactly as for instance on persistent storage.
118 /// These are exactly as for instance on persistent storage.
119 type RawElement = unaligned::I32Be;
119 type RawElement = unaligned::I32Be;
120
120
121 /// High level representation of values in NodeTree
121 /// High level representation of values in NodeTree
122 /// [`Blocks`](struct.Block.html)
122 /// [`Blocks`](struct.Block.html)
123 ///
123 ///
124 /// This is the high level representation that most algorithms should
124 /// This is the high level representation that most algorithms should
125 /// use.
125 /// use.
126 #[derive(Clone, Debug, Eq, PartialEq)]
126 #[derive(Clone, Debug, Eq, PartialEq)]
127 enum Element {
127 enum Element {
128 Rev(Revision),
128 Rev(Revision),
129 Block(usize),
129 Block(usize),
130 None,
130 None,
131 }
131 }
132
132
133 impl From<RawElement> for Element {
133 impl From<RawElement> for Element {
134 /// Conversion from low level representation, after endianness conversion.
134 /// Conversion from low level representation, after endianness conversion.
135 ///
135 ///
136 /// See [`Block`](struct.Block.html) for explanation about the encoding.
136 /// See [`Block`](struct.Block.html) for explanation about the encoding.
137 fn from(raw: RawElement) -> Element {
137 fn from(raw: RawElement) -> Element {
138 let int = raw.get();
138 let int = raw.get();
139 if int >= 0 {
139 if int >= 0 {
140 Element::Block(int as usize)
140 Element::Block(int as usize)
141 } else if int == -1 {
141 } else if int == -1 {
142 Element::None
142 Element::None
143 } else {
143 } else {
144 Element::Rev(-int - 2)
144 Element::Rev(-int - 2)
145 }
145 }
146 }
146 }
147 }
147 }
148
148
149 impl From<Element> for RawElement {
149 impl From<Element> for RawElement {
150 fn from(element: Element) -> RawElement {
150 fn from(element: Element) -> RawElement {
151 RawElement::from(match element {
151 RawElement::from(match element {
152 Element::None => 0,
152 Element::None => 0,
153 Element::Block(i) => i as i32,
153 Element::Block(i) => i as i32,
154 Element::Rev(rev) => -rev - 2,
154 Element::Rev(rev) => -rev - 2,
155 })
155 })
156 }
156 }
157 }
157 }
158
158
159 /// A logical block of the `NodeTree`, packed with a fixed size.
159 /// A logical block of the `NodeTree`, packed with a fixed size.
160 ///
160 ///
161 /// These are always used in container types implementing `Index<Block>`,
161 /// These are always used in container types implementing `Index<Block>`,
162 /// such as `&Block`
162 /// such as `&Block`
163 ///
163 ///
164 /// As an array of integers, its ith element encodes that the
164 /// As an array of integers, its ith element encodes that the
165 /// ith potential edge from the block, representing the ith hexadecimal digit
165 /// ith potential edge from the block, representing the ith hexadecimal digit
166 /// (nybble) `i` is either:
166 /// (nybble) `i` is either:
167 ///
167 ///
168 /// - absent (value -1)
168 /// - absent (value -1)
169 /// - another `Block` in the same indexable container (value β‰₯ 0)
169 /// - another `Block` in the same indexable container (value β‰₯ 0)
170 /// - a `Revision` leaf (value ≀ -2)
170 /// - a `Revision` leaf (value ≀ -2)
171 ///
171 ///
172 /// Endianness has to be fixed for consistency on shared storage across
172 /// Endianness has to be fixed for consistency on shared storage across
173 /// different architectures.
173 /// different architectures.
174 ///
174 ///
175 /// A key difference with the C `nodetree` is that we need to be
175 /// A key difference with the C `nodetree` is that we need to be
176 /// able to represent the [`Block`] at index 0, hence -1 is the empty marker
176 /// able to represent the [`Block`] at index 0, hence -1 is the empty marker
177 /// rather than 0 and the `Revision` range upper limit of -2 instead of -1.
177 /// rather than 0 and the `Revision` range upper limit of -2 instead of -1.
178 ///
178 ///
179 /// Another related difference is that `NULL_REVISION` (-1) is not
179 /// Another related difference is that `NULL_REVISION` (-1) is not
180 /// represented at all, because we want an immutable empty nodetree
180 /// represented at all, because we want an immutable empty nodetree
181 /// to be valid.
181 /// to be valid.
182
182
183 const ELEMENTS_PER_BLOCK: usize = 16; // number of different values in a nybble
183 const ELEMENTS_PER_BLOCK: usize = 16; // number of different values in a nybble
184
184
185 #[derive(Copy, Clone, BytesCast, PartialEq)]
185 #[derive(Copy, Clone, BytesCast, PartialEq)]
186 #[repr(transparent)]
186 #[repr(transparent)]
187 pub struct Block([RawElement; ELEMENTS_PER_BLOCK]);
187 pub struct Block([RawElement; ELEMENTS_PER_BLOCK]);
188
188
189 impl Block {
189 impl Block {
190 fn new() -> Self {
190 fn new() -> Self {
191 let absent_node = RawElement::from(-1);
191 let absent_node = RawElement::from(-1);
192 Block([absent_node; ELEMENTS_PER_BLOCK])
192 Block([absent_node; ELEMENTS_PER_BLOCK])
193 }
193 }
194
194
195 fn get(&self, nybble: u8) -> Element {
195 fn get(&self, nybble: u8) -> Element {
196 self.0[nybble as usize].into()
196 self.0[nybble as usize].into()
197 }
197 }
198
198
199 fn set(&mut self, nybble: u8, element: Element) {
199 fn set(&mut self, nybble: u8, element: Element) {
200 self.0[nybble as usize] = element.into()
200 self.0[nybble as usize] = element.into()
201 }
201 }
202 }
202 }
203
203
204 impl fmt::Debug for Block {
204 impl fmt::Debug for Block {
205 /// sparse representation for testing and debugging purposes
205 /// sparse representation for testing and debugging purposes
206 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
206 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
207 f.debug_map()
207 f.debug_map()
208 .entries((0..16).filter_map(|i| match self.get(i) {
208 .entries((0..16).filter_map(|i| match self.get(i) {
209 Element::None => None,
209 Element::None => None,
210 element => Some((i, element)),
210 element => Some((i, element)),
211 }))
211 }))
212 .finish()
212 .finish()
213 }
213 }
214 }
214 }
215
215
216 /// A mutable 16-radix tree with the root block logically at the end
216 /// A mutable 16-radix tree with the root block logically at the end
217 ///
217 ///
218 /// Because of the append only nature of our node trees, we need to
218 /// Because of the append only nature of our node trees, we need to
219 /// keep the original untouched and store new blocks separately.
219 /// keep the original untouched and store new blocks separately.
220 ///
220 ///
221 /// The mutable root `Block` is kept apart so that we don't have to rebump
221 /// The mutable root `Block` is kept apart so that we don't have to rebump
222 /// it on each insertion.
222 /// it on each insertion.
223 pub struct NodeTree {
223 pub struct NodeTree {
224 readonly: Box<dyn Deref<Target = [Block]> + Send>,
224 readonly: Box<dyn Deref<Target = [Block]> + Send>,
225 growable: Vec<Block>,
225 growable: Vec<Block>,
226 root: Block,
226 root: Block,
227 masked_inner_blocks: usize,
227 masked_inner_blocks: usize,
228 }
228 }
229
229
230 impl Index<usize> for NodeTree {
230 impl Index<usize> for NodeTree {
231 type Output = Block;
231 type Output = Block;
232
232
233 fn index(&self, i: usize) -> &Block {
233 fn index(&self, i: usize) -> &Block {
234 let ro_len = self.readonly.len();
234 let ro_len = self.readonly.len();
235 if i < ro_len {
235 if i < ro_len {
236 &self.readonly[i]
236 &self.readonly[i]
237 } else if i == ro_len + self.growable.len() {
237 } else if i == ro_len + self.growable.len() {
238 &self.root
238 &self.root
239 } else {
239 } else {
240 &self.growable[i - ro_len]
240 &self.growable[i - ro_len]
241 }
241 }
242 }
242 }
243 }
243 }
244
244
245 /// Return `None` unless the `Node` for `rev` has given prefix in `index`.
245 /// Return `None` unless the `Node` for `rev` has given prefix in `index`.
246 fn has_prefix_or_none(
246 fn has_prefix_or_none(
247 idx: &impl RevlogIndex,
247 idx: &impl RevlogIndex,
248 prefix: NodePrefix,
248 prefix: NodePrefix,
249 rev: Revision,
249 rev: Revision,
250 ) -> Result<Option<Revision>, NodeMapError> {
250 ) -> Result<Option<Revision>, NodeMapError> {
251 idx.node(rev)
251 idx.node(rev)
252 .ok_or(NodeMapError::RevisionNotInIndex(rev))
252 .ok_or(NodeMapError::RevisionNotInIndex(rev))
253 .map(|node| {
253 .map(|node| {
254 if prefix.is_prefix_of(node) {
254 if prefix.is_prefix_of(node) {
255 Some(rev)
255 Some(rev)
256 } else {
256 } else {
257 None
257 None
258 }
258 }
259 })
259 })
260 }
260 }
261
261
262 /// validate that the candidate's node starts indeed with given prefix,
262 /// validate that the candidate's node starts indeed with given prefix,
263 /// and treat ambiguities related to `NULL_REVISION`.
263 /// and treat ambiguities related to `NULL_REVISION`.
264 ///
264 ///
265 /// From the data in the NodeTree, one can only conclude that some
265 /// From the data in the NodeTree, one can only conclude that some
266 /// revision is the only one for a *subprefix* of the one being looked up.
266 /// revision is the only one for a *subprefix* of the one being looked up.
267 fn validate_candidate(
267 fn validate_candidate(
268 idx: &impl RevlogIndex,
268 idx: &impl RevlogIndex,
269 prefix: NodePrefix,
269 prefix: NodePrefix,
270 candidate: (Option<Revision>, usize),
270 candidate: (Option<Revision>, usize),
271 ) -> Result<(Option<Revision>, usize), NodeMapError> {
271 ) -> Result<(Option<Revision>, usize), NodeMapError> {
272 let (rev, steps) = candidate;
272 let (rev, steps) = candidate;
273 if let Some(nz_nybble) = prefix.first_different_nybble(&NULL_NODE) {
273 if let Some(nz_nybble) = prefix.first_different_nybble(&NULL_NODE) {
274 rev.map_or(Ok((None, steps)), |r| {
274 rev.map_or(Ok((None, steps)), |r| {
275 has_prefix_or_none(idx, prefix, r)
275 has_prefix_or_none(idx, prefix, r)
276 .map(|opt| (opt, max(steps, nz_nybble + 1)))
276 .map(|opt| (opt, max(steps, nz_nybble + 1)))
277 })
277 })
278 } else {
278 } else {
279 // the prefix is only made of zeros; NULL_REVISION always matches it
279 // the prefix is only made of zeros; NULL_REVISION always matches it
280 // and any other *valid* result is an ambiguity
280 // and any other *valid* result is an ambiguity
281 match rev {
281 match rev {
282 None => Ok((Some(NULL_REVISION), steps + 1)),
282 None => Ok((Some(NULL_REVISION), steps + 1)),
283 Some(r) => match has_prefix_or_none(idx, prefix, r)? {
283 Some(r) => match has_prefix_or_none(idx, prefix, r)? {
284 None => Ok((Some(NULL_REVISION), steps + 1)),
284 None => Ok((Some(NULL_REVISION), steps + 1)),
285 _ => Err(NodeMapError::MultipleResults),
285 _ => Err(NodeMapError::MultipleResults),
286 },
286 },
287 }
287 }
288 }
288 }
289 }
289 }
290
290
291 impl NodeTree {
291 impl NodeTree {
292 /// Initiate a NodeTree from an immutable slice-like of `Block`
292 /// Initiate a NodeTree from an immutable slice-like of `Block`
293 ///
293 ///
294 /// We keep `readonly` and clone its root block if it isn't empty.
294 /// We keep `readonly` and clone its root block if it isn't empty.
295 fn new(readonly: Box<dyn Deref<Target = [Block]> + Send>) -> Self {
295 fn new(readonly: Box<dyn Deref<Target = [Block]> + Send>) -> Self {
296 let root = readonly.last().cloned().unwrap_or_else(Block::new);
296 let root = readonly.last().cloned().unwrap_or_else(Block::new);
297 NodeTree {
297 NodeTree {
298 readonly,
298 readonly,
299 growable: Vec::new(),
299 growable: Vec::new(),
300 root,
300 root,
301 masked_inner_blocks: 0,
301 masked_inner_blocks: 0,
302 }
302 }
303 }
303 }
304
304
305 /// Create from an opaque bunch of bytes
305 /// Create from an opaque bunch of bytes
306 ///
306 ///
307 /// The created `NodeTreeBytes` from `buffer`,
307 /// The created `NodeTreeBytes` from `buffer`,
308 /// of which exactly `amount` bytes are used.
308 /// of which exactly `amount` bytes are used.
309 ///
309 ///
310 /// - `buffer` could be derived from `PyBuffer` and `Mmap` objects.
310 /// - `buffer` could be derived from `PyBuffer` and `Mmap` objects.
311 /// - `offset` allows for the final file format to include fixed data
311 /// - `offset` allows for the final file format to include fixed data
312 /// (generation number, behavioural flags)
312 /// (generation number, behavioural flags)
313 /// - `amount` is expressed in bytes, and is not automatically derived from
313 /// - `amount` is expressed in bytes, and is not automatically derived from
314 /// `bytes`, so that a caller that manages them atomically can perform
314 /// `bytes`, so that a caller that manages them atomically can perform
315 /// temporary disk serializations and still rollback easily if needed.
315 /// temporary disk serializations and still rollback easily if needed.
316 /// First use-case for this would be to support Mercurial shell hooks.
316 /// First use-case for this would be to support Mercurial shell hooks.
317 ///
317 ///
318 /// panics if `buffer` is smaller than `amount`
318 /// panics if `buffer` is smaller than `amount`
319 pub fn load_bytes(
319 pub fn load_bytes(
320 bytes: Box<dyn Deref<Target = [u8]> + Send>,
320 bytes: Box<dyn Deref<Target = [u8]> + Send>,
321 amount: usize,
321 amount: usize,
322 ) -> Self {
322 ) -> Self {
323 NodeTree::new(Box::new(NodeTreeBytes::new(bytes, amount)))
323 NodeTree::new(Box::new(NodeTreeBytes::new(bytes, amount)))
324 }
324 }
325
325
326 /// Retrieve added `Block` and the original immutable data
326 /// Retrieve added `Block` and the original immutable data
327 pub fn into_readonly_and_added(
327 pub fn into_readonly_and_added(
328 self,
328 self,
329 ) -> (Box<dyn Deref<Target = [Block]> + Send>, Vec<Block>) {
329 ) -> (Box<dyn Deref<Target = [Block]> + Send>, Vec<Block>) {
330 let mut vec = self.growable;
330 let mut vec = self.growable;
331 let readonly = self.readonly;
331 let readonly = self.readonly;
332 if readonly.last() != Some(&self.root) {
332 if readonly.last() != Some(&self.root) {
333 vec.push(self.root);
333 vec.push(self.root);
334 }
334 }
335 (readonly, vec)
335 (readonly, vec)
336 }
336 }
337
337
338 /// Retrieve added `Blocks` as bytes, ready to be written to persistent
338 /// Retrieve added `Blocks` as bytes, ready to be written to persistent
339 /// storage
339 /// storage
340 pub fn into_readonly_and_added_bytes(
340 pub fn into_readonly_and_added_bytes(
341 self,
341 self,
342 ) -> (Box<dyn Deref<Target = [Block]> + Send>, Vec<u8>) {
342 ) -> (Box<dyn Deref<Target = [Block]> + Send>, Vec<u8>) {
343 let (readonly, vec) = self.into_readonly_and_added();
343 let (readonly, vec) = self.into_readonly_and_added();
344 // Prevent running `v`'s destructor so we are in complete control
344 // Prevent running `v`'s destructor so we are in complete control
345 // of the allocation.
345 // of the allocation.
346 let vec = mem::ManuallyDrop::new(vec);
346 let vec = mem::ManuallyDrop::new(vec);
347
347
348 // Transmute the `Vec<Block>` to a `Vec<u8>`. Blocks are contiguous
348 // Transmute the `Vec<Block>` to a `Vec<u8>`. Blocks are contiguous
349 // bytes, so this is perfectly safe.
349 // bytes, so this is perfectly safe.
350 let bytes = unsafe {
350 let bytes = unsafe {
351 // Check for compatible allocation layout.
351 // Check for compatible allocation layout.
352 // (Optimized away by constant-folding + dead code elimination.)
352 // (Optimized away by constant-folding + dead code elimination.)
353 assert_eq!(size_of::<Block>(), 64);
353 assert_eq!(size_of::<Block>(), 64);
354 assert_eq!(align_of::<Block>(), 1);
354 assert_eq!(align_of::<Block>(), 1);
355
355
356 // /!\ Any use of `vec` after this is use-after-free.
356 // /!\ Any use of `vec` after this is use-after-free.
357 // TODO: use `into_raw_parts` once stabilized
357 // TODO: use `into_raw_parts` once stabilized
358 Vec::from_raw_parts(
358 Vec::from_raw_parts(
359 vec.as_ptr() as *mut u8,
359 vec.as_ptr() as *mut u8,
360 vec.len() * size_of::<Block>(),
360 vec.len() * size_of::<Block>(),
361 vec.capacity() * size_of::<Block>(),
361 vec.capacity() * size_of::<Block>(),
362 )
362 )
363 };
363 };
364 (readonly, bytes)
364 (readonly, bytes)
365 }
365 }
366
366
367 /// Total number of blocks
367 /// Total number of blocks
368 fn len(&self) -> usize {
368 fn len(&self) -> usize {
369 self.readonly.len() + self.growable.len() + 1
369 self.readonly.len() + self.growable.len() + 1
370 }
370 }
371
371
372 /// Implemented for completeness
372 /// Implemented for completeness
373 ///
373 ///
374 /// A `NodeTree` always has at least the mutable root block.
374 /// A `NodeTree` always has at least the mutable root block.
375 #[allow(dead_code)]
375 #[allow(dead_code)]
376 fn is_empty(&self) -> bool {
376 fn is_empty(&self) -> bool {
377 false
377 false
378 }
378 }
379
379
380 /// Main working method for `NodeTree` searches
380 /// Main working method for `NodeTree` searches
381 ///
381 ///
382 /// The first returned value is the result of analysing `NodeTree` data
382 /// The first returned value is the result of analysing `NodeTree` data
383 /// *alone*: whereas `None` guarantees that the given prefix is absent
383 /// *alone*: whereas `None` guarantees that the given prefix is absent
384 /// from the `NodeTree` data (but still could match `NULL_NODE`), with
384 /// from the `NodeTree` data (but still could match `NULL_NODE`), with
385 /// `Some(rev)`, it is to be understood that `rev` is the unique `Revision`
385 /// `Some(rev)`, it is to be understood that `rev` is the unique `Revision`
386 /// that could match the prefix. Actually, all that can be inferred from
386 /// that could match the prefix. Actually, all that can be inferred from
387 /// the `NodeTree` data is that `rev` is the revision with the longest
387 /// the `NodeTree` data is that `rev` is the revision with the longest
388 /// common node prefix with the given prefix.
388 /// common node prefix with the given prefix.
389 ///
389 ///
390 /// The second returned value is the size of the smallest subprefix
390 /// The second returned value is the size of the smallest subprefix
391 /// of `prefix` that would give the same result, i.e. not the
391 /// of `prefix` that would give the same result, i.e. not the
392 /// `MultipleResults` error variant (again, using only the data of the
392 /// `MultipleResults` error variant (again, using only the data of the
393 /// `NodeTree`).
393 /// `NodeTree`).
394 fn lookup(
394 fn lookup(
395 &self,
395 &self,
396 prefix: NodePrefix,
396 prefix: NodePrefix,
397 ) -> Result<(Option<Revision>, usize), NodeMapError> {
397 ) -> Result<(Option<Revision>, usize), NodeMapError> {
398 for (i, visit_item) in self.visit(prefix).enumerate() {
398 for (i, visit_item) in self.visit(prefix).enumerate() {
399 if let Some(opt) = visit_item.final_revision() {
399 if let Some(opt) = visit_item.final_revision() {
400 return Ok((opt, i + 1));
400 return Ok((opt, i + 1));
401 }
401 }
402 }
402 }
403 Err(NodeMapError::MultipleResults)
403 Err(NodeMapError::MultipleResults)
404 }
404 }
405
405
406 fn visit(&self, prefix: NodePrefix) -> NodeTreeVisitor {
406 fn visit(&self, prefix: NodePrefix) -> NodeTreeVisitor {
407 NodeTreeVisitor {
407 NodeTreeVisitor {
408 nt: self,
408 nt: self,
409 prefix,
409 prefix,
410 visit: self.len() - 1,
410 visit: self.len() - 1,
411 nybble_idx: 0,
411 nybble_idx: 0,
412 done: false,
412 done: false,
413 }
413 }
414 }
414 }
415 /// Return a mutable reference for `Block` at index `idx`.
415 /// Return a mutable reference for `Block` at index `idx`.
416 ///
416 ///
417 /// If `idx` lies in the immutable area, then the reference is to
417 /// If `idx` lies in the immutable area, then the reference is to
418 /// a newly appended copy.
418 /// a newly appended copy.
419 ///
419 ///
420 /// Returns (new_idx, glen, mut_ref) where
420 /// Returns (new_idx, glen, mut_ref) where
421 ///
421 ///
422 /// - `new_idx` is the index of the mutable `Block`
422 /// - `new_idx` is the index of the mutable `Block`
423 /// - `mut_ref` is a mutable reference to the mutable Block.
423 /// - `mut_ref` is a mutable reference to the mutable Block.
424 /// - `glen` is the new length of `self.growable`
424 /// - `glen` is the new length of `self.growable`
425 ///
425 ///
426 /// Note: the caller wouldn't be allowed to query `self.growable.len()`
426 /// Note: the caller wouldn't be allowed to query `self.growable.len()`
427 /// itself because of the mutable borrow taken with the returned `Block`
427 /// itself because of the mutable borrow taken with the returned `Block`
428 fn mutable_block(&mut self, idx: usize) -> (usize, &mut Block, usize) {
428 fn mutable_block(&mut self, idx: usize) -> (usize, &mut Block, usize) {
429 let ro_blocks = &self.readonly;
429 let ro_blocks = &self.readonly;
430 let ro_len = ro_blocks.len();
430 let ro_len = ro_blocks.len();
431 let glen = self.growable.len();
431 let glen = self.growable.len();
432 if idx < ro_len {
432 if idx < ro_len {
433 self.masked_inner_blocks += 1;
433 self.masked_inner_blocks += 1;
434 self.growable.push(ro_blocks[idx]);
434 self.growable.push(ro_blocks[idx]);
435 (glen + ro_len, &mut self.growable[glen], glen + 1)
435 (glen + ro_len, &mut self.growable[glen], glen + 1)
436 } else if glen + ro_len == idx {
436 } else if glen + ro_len == idx {
437 (idx, &mut self.root, glen)
437 (idx, &mut self.root, glen)
438 } else {
438 } else {
439 (idx, &mut self.growable[idx - ro_len], glen)
439 (idx, &mut self.growable[idx - ro_len], glen)
440 }
440 }
441 }
441 }
442
442
443 /// Main insertion method
443 /// Main insertion method
444 ///
444 ///
445 /// This will dive in the node tree to find the deepest `Block` for
445 /// This will dive in the node tree to find the deepest `Block` for
446 /// `node`, split it as much as needed and record `node` in there.
446 /// `node`, split it as much as needed and record `node` in there.
447 /// The method then backtracks, updating references in all the visited
447 /// The method then backtracks, updating references in all the visited
448 /// blocks from the root.
448 /// blocks from the root.
449 ///
449 ///
450 /// All the mutated `Block` are copied first to the growable part if
450 /// All the mutated `Block` are copied first to the growable part if
451 /// needed. That happens for those in the immutable part except the root.
451 /// needed. That happens for those in the immutable part except the root.
452 pub fn insert<I: RevlogIndex>(
452 pub fn insert<I: RevlogIndex>(
453 &mut self,
453 &mut self,
454 index: &I,
454 index: &I,
455 node: &Node,
455 node: &Node,
456 rev: Revision,
456 rev: Revision,
457 ) -> Result<(), NodeMapError> {
457 ) -> Result<(), NodeMapError> {
458 let ro_len = &self.readonly.len();
458 let ro_len = &self.readonly.len();
459
459
460 let mut visit_steps: Vec<_> = self.visit(node.into()).collect();
460 let mut visit_steps: Vec<_> = self.visit(node.into()).collect();
461 let read_nybbles = visit_steps.len();
461 let read_nybbles = visit_steps.len();
462 // visit_steps cannot be empty, since we always visit the root block
462 // visit_steps cannot be empty, since we always visit the root block
463 let deepest = visit_steps.pop().unwrap();
463 let deepest = visit_steps.pop().unwrap();
464
464
465 let (mut block_idx, mut block, mut glen) =
465 let (mut block_idx, mut block, mut glen) =
466 self.mutable_block(deepest.block_idx);
466 self.mutable_block(deepest.block_idx);
467
467
468 if let Element::Rev(old_rev) = deepest.element {
468 if let Element::Rev(old_rev) = deepest.element {
469 let old_node = index
469 let old_node = index
470 .node(old_rev)
470 .node(old_rev)
471 .ok_or(NodeMapError::RevisionNotInIndex(old_rev))?;
471 .ok_or(NodeMapError::RevisionNotInIndex(old_rev))?;
472 if old_node == node {
472 if old_node == node {
473 return Ok(()); // avoid creating lots of useless blocks
473 return Ok(()); // avoid creating lots of useless blocks
474 }
474 }
475
475
476 // Looping over the tail of nybbles in both nodes, creating
476 // Looping over the tail of nybbles in both nodes, creating
477 // new blocks until we find the difference
477 // new blocks until we find the difference
478 let mut new_block_idx = ro_len + glen;
478 let mut new_block_idx = ro_len + glen;
479 let mut nybble = deepest.nybble;
479 let mut nybble = deepest.nybble;
480 for nybble_pos in read_nybbles..node.nybbles_len() {
480 for nybble_pos in read_nybbles..node.nybbles_len() {
481 block.set(nybble, Element::Block(new_block_idx));
481 block.set(nybble, Element::Block(new_block_idx));
482
482
483 let new_nybble = node.get_nybble(nybble_pos);
483 let new_nybble = node.get_nybble(nybble_pos);
484 let old_nybble = old_node.get_nybble(nybble_pos);
484 let old_nybble = old_node.get_nybble(nybble_pos);
485
485
486 if old_nybble == new_nybble {
486 if old_nybble == new_nybble {
487 self.growable.push(Block::new());
487 self.growable.push(Block::new());
488 block = &mut self.growable[glen];
488 block = &mut self.growable[glen];
489 glen += 1;
489 glen += 1;
490 new_block_idx += 1;
490 new_block_idx += 1;
491 nybble = new_nybble;
491 nybble = new_nybble;
492 } else {
492 } else {
493 let mut new_block = Block::new();
493 let mut new_block = Block::new();
494 new_block.set(old_nybble, Element::Rev(old_rev));
494 new_block.set(old_nybble, Element::Rev(old_rev));
495 new_block.set(new_nybble, Element::Rev(rev));
495 new_block.set(new_nybble, Element::Rev(rev));
496 self.growable.push(new_block);
496 self.growable.push(new_block);
497 break;
497 break;
498 }
498 }
499 }
499 }
500 } else {
500 } else {
501 // Free slot in the deepest block: no splitting has to be done
501 // Free slot in the deepest block: no splitting has to be done
502 block.set(deepest.nybble, Element::Rev(rev));
502 block.set(deepest.nybble, Element::Rev(rev));
503 }
503 }
504
504
505 // Backtrack over visit steps to update references
505 // Backtrack over visit steps to update references
506 while let Some(visited) = visit_steps.pop() {
506 while let Some(visited) = visit_steps.pop() {
507 let to_write = Element::Block(block_idx);
507 let to_write = Element::Block(block_idx);
508 if visit_steps.is_empty() {
508 if visit_steps.is_empty() {
509 self.root.set(visited.nybble, to_write);
509 self.root.set(visited.nybble, to_write);
510 break;
510 break;
511 }
511 }
512 let (new_idx, block, _) = self.mutable_block(visited.block_idx);
512 let (new_idx, block, _) = self.mutable_block(visited.block_idx);
513 if block.get(visited.nybble) == to_write {
513 if block.get(visited.nybble) == to_write {
514 break;
514 break;
515 }
515 }
516 block.set(visited.nybble, to_write);
516 block.set(visited.nybble, to_write);
517 block_idx = new_idx;
517 block_idx = new_idx;
518 }
518 }
519 Ok(())
519 Ok(())
520 }
520 }
521
521
522 /// Make the whole `NodeTree` logically empty, without touching the
522 /// Make the whole `NodeTree` logically empty, without touching the
523 /// immutable part.
523 /// immutable part.
524 pub fn invalidate_all(&mut self) {
524 pub fn invalidate_all(&mut self) {
525 self.root = Block::new();
525 self.root = Block::new();
526 self.growable = Vec::new();
526 self.growable = Vec::new();
527 self.masked_inner_blocks = self.readonly.len();
527 self.masked_inner_blocks = self.readonly.len();
528 }
528 }
529
529
530 /// Return the number of blocks in the readonly part that are currently
530 /// Return the number of blocks in the readonly part that are currently
531 /// masked in the mutable part.
531 /// masked in the mutable part.
532 ///
532 ///
533 /// The `NodeTree` structure has no efficient way to know how many blocks
533 /// The `NodeTree` structure has no efficient way to know how many blocks
534 /// are already unreachable in the readonly part.
534 /// are already unreachable in the readonly part.
535 ///
535 ///
536 /// After a call to `invalidate_all()`, the returned number can be actually
536 /// After a call to `invalidate_all()`, the returned number can be actually
537 /// bigger than the whole readonly part, a conventional way to mean that
537 /// bigger than the whole readonly part, a conventional way to mean that
538 /// all the readonly blocks have been masked. This is what is really
538 /// all the readonly blocks have been masked. This is what is really
539 /// useful to the caller and does not require to know how many were
539 /// useful to the caller and does not require to know how many were
540 /// actually unreachable to begin with.
540 /// actually unreachable to begin with.
541 pub fn masked_readonly_blocks(&self) -> usize {
541 pub fn masked_readonly_blocks(&self) -> usize {
542 if let Some(readonly_root) = self.readonly.last() {
542 if let Some(readonly_root) = self.readonly.last() {
543 if readonly_root == &self.root {
543 if readonly_root == &self.root {
544 return 0;
544 return 0;
545 }
545 }
546 } else {
546 } else {
547 return 0;
547 return 0;
548 }
548 }
549 self.masked_inner_blocks + 1
549 self.masked_inner_blocks + 1
550 }
550 }
551 }
551 }
552
552
553 pub struct NodeTreeBytes {
553 pub struct NodeTreeBytes {
554 buffer: Box<dyn Deref<Target = [u8]> + Send>,
554 buffer: Box<dyn Deref<Target = [u8]> + Send>,
555 len_in_blocks: usize,
555 len_in_blocks: usize,
556 }
556 }
557
557
558 impl NodeTreeBytes {
558 impl NodeTreeBytes {
559 fn new(
559 fn new(
560 buffer: Box<dyn Deref<Target = [u8]> + Send>,
560 buffer: Box<dyn Deref<Target = [u8]> + Send>,
561 amount: usize,
561 amount: usize,
562 ) -> Self {
562 ) -> Self {
563 assert!(buffer.len() >= amount);
563 assert!(buffer.len() >= amount);
564 let len_in_blocks = amount / size_of::<Block>();
564 let len_in_blocks = amount / size_of::<Block>();
565 NodeTreeBytes {
565 NodeTreeBytes {
566 buffer,
566 buffer,
567 len_in_blocks,
567 len_in_blocks,
568 }
568 }
569 }
569 }
570 }
570 }
571
571
572 impl Deref for NodeTreeBytes {
572 impl Deref for NodeTreeBytes {
573 type Target = [Block];
573 type Target = [Block];
574
574
575 fn deref(&self) -> &[Block] {
575 fn deref(&self) -> &[Block] {
576 Block::slice_from_bytes(&self.buffer, self.len_in_blocks)
576 Block::slice_from_bytes(&self.buffer, self.len_in_blocks)
577 // `NodeTreeBytes::new` already asserted that `self.buffer` is
577 // `NodeTreeBytes::new` already asserted that `self.buffer` is
578 // large enough.
578 // large enough.
579 .unwrap()
579 .unwrap()
580 .0
580 .0
581 }
581 }
582 }
582 }
583
583
584 struct NodeTreeVisitor<'n> {
584 struct NodeTreeVisitor<'n> {
585 nt: &'n NodeTree,
585 nt: &'n NodeTree,
586 prefix: NodePrefix,
586 prefix: NodePrefix,
587 visit: usize,
587 visit: usize,
588 nybble_idx: usize,
588 nybble_idx: usize,
589 done: bool,
589 done: bool,
590 }
590 }
591
591
592 #[derive(Debug, PartialEq, Clone)]
592 #[derive(Debug, PartialEq, Clone)]
593 struct NodeTreeVisitItem {
593 struct NodeTreeVisitItem {
594 block_idx: usize,
594 block_idx: usize,
595 nybble: u8,
595 nybble: u8,
596 element: Element,
596 element: Element,
597 }
597 }
598
598
599 impl<'n> Iterator for NodeTreeVisitor<'n> {
599 impl<'n> Iterator for NodeTreeVisitor<'n> {
600 type Item = NodeTreeVisitItem;
600 type Item = NodeTreeVisitItem;
601
601
602 fn next(&mut self) -> Option<Self::Item> {
602 fn next(&mut self) -> Option<Self::Item> {
603 if self.done || self.nybble_idx >= self.prefix.nybbles_len() {
603 if self.done || self.nybble_idx >= self.prefix.nybbles_len() {
604 return None;
604 return None;
605 }
605 }
606
606
607 let nybble = self.prefix.get_nybble(self.nybble_idx);
607 let nybble = self.prefix.get_nybble(self.nybble_idx);
608 self.nybble_idx += 1;
608 self.nybble_idx += 1;
609
609
610 let visit = self.visit;
610 let visit = self.visit;
611 let element = self.nt[visit].get(nybble);
611 let element = self.nt[visit].get(nybble);
612 if let Element::Block(idx) = element {
612 if let Element::Block(idx) = element {
613 self.visit = idx;
613 self.visit = idx;
614 } else {
614 } else {
615 self.done = true;
615 self.done = true;
616 }
616 }
617
617
618 Some(NodeTreeVisitItem {
618 Some(NodeTreeVisitItem {
619 block_idx: visit,
619 block_idx: visit,
620 nybble,
620 nybble,
621 element,
621 element,
622 })
622 })
623 }
623 }
624 }
624 }
625
625
626 impl NodeTreeVisitItem {
626 impl NodeTreeVisitItem {
627 // Return `Some(opt)` if this item is final, with `opt` being the
627 // Return `Some(opt)` if this item is final, with `opt` being the
628 // `Revision` that it may represent.
628 // `Revision` that it may represent.
629 //
629 //
630 // If the item is not terminal, return `None`
630 // If the item is not terminal, return `None`
631 fn final_revision(&self) -> Option<Option<Revision>> {
631 fn final_revision(&self) -> Option<Option<Revision>> {
632 match self.element {
632 match self.element {
633 Element::Block(_) => None,
633 Element::Block(_) => None,
634 Element::Rev(r) => Some(Some(r)),
634 Element::Rev(r) => Some(Some(r)),
635 Element::None => Some(None),
635 Element::None => Some(None),
636 }
636 }
637 }
637 }
638 }
638 }
639
639
640 impl From<Vec<Block>> for NodeTree {
640 impl From<Vec<Block>> for NodeTree {
641 fn from(vec: Vec<Block>) -> Self {
641 fn from(vec: Vec<Block>) -> Self {
642 Self::new(Box::new(vec))
642 Self::new(Box::new(vec))
643 }
643 }
644 }
644 }
645
645
646 impl fmt::Debug for NodeTree {
646 impl fmt::Debug for NodeTree {
647 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
647 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
648 let readonly: &[Block] = &*self.readonly;
648 let readonly: &[Block] = &*self.readonly;
649 write!(
649 write!(
650 f,
650 f,
651 "readonly: {:?}, growable: {:?}, root: {:?}",
651 "readonly: {:?}, growable: {:?}, root: {:?}",
652 readonly, self.growable, self.root
652 readonly, self.growable, self.root
653 )
653 )
654 }
654 }
655 }
655 }
656
656
657 impl Default for NodeTree {
657 impl Default for NodeTree {
658 /// Create a fully mutable empty NodeTree
658 /// Create a fully mutable empty NodeTree
659 fn default() -> Self {
659 fn default() -> Self {
660 NodeTree::new(Box::new(Vec::new()))
660 NodeTree::new(Box::new(Vec::new()))
661 }
661 }
662 }
662 }
663
663
664 impl NodeMap for NodeTree {
664 impl NodeMap for NodeTree {
665 fn find_bin<'a>(
665 fn find_bin<'a>(
666 &self,
666 &self,
667 idx: &impl RevlogIndex,
667 idx: &impl RevlogIndex,
668 prefix: NodePrefix,
668 prefix: NodePrefix,
669 ) -> Result<Option<Revision>, NodeMapError> {
669 ) -> Result<Option<Revision>, NodeMapError> {
670 validate_candidate(idx, prefix, self.lookup(prefix)?)
670 validate_candidate(idx, prefix, self.lookup(prefix)?)
671 .map(|(opt, _shortest)| opt)
671 .map(|(opt, _shortest)| opt)
672 }
672 }
673
673
674 fn unique_prefix_len_bin<'a>(
674 fn unique_prefix_len_bin<'a>(
675 &self,
675 &self,
676 idx: &impl RevlogIndex,
676 idx: &impl RevlogIndex,
677 prefix: NodePrefix,
677 prefix: NodePrefix,
678 ) -> Result<Option<usize>, NodeMapError> {
678 ) -> Result<Option<usize>, NodeMapError> {
679 validate_candidate(idx, prefix, self.lookup(prefix)?)
679 validate_candidate(idx, prefix, self.lookup(prefix)?)
680 .map(|(opt, shortest)| opt.map(|_rev| shortest))
680 .map(|(opt, shortest)| opt.map(|_rev| shortest))
681 }
681 }
682 }
682 }
683
683
684 #[cfg(test)]
684 #[cfg(test)]
685 mod tests {
685 mod tests {
686 use super::NodeMapError::*;
686 use super::NodeMapError::*;
687 use super::*;
687 use super::*;
688 use crate::revlog::node::{hex_pad_right, Node};
688 use crate::revlog::node::{hex_pad_right, Node};
689 use std::collections::HashMap;
689 use std::collections::HashMap;
690
690
691 /// Creates a `Block` using a syntax close to the `Debug` output
691 /// Creates a `Block` using a syntax close to the `Debug` output
692 macro_rules! block {
692 macro_rules! block {
693 {$($nybble:tt : $variant:ident($val:tt)),*} => (
693 {$($nybble:tt : $variant:ident($val:tt)),*} => (
694 {
694 {
695 let mut block = Block::new();
695 let mut block = Block::new();
696 $(block.set($nybble, Element::$variant($val)));*;
696 $(block.set($nybble, Element::$variant($val)));*;
697 block
697 block
698 }
698 }
699 )
699 )
700 }
700 }
701
701
702 #[test]
702 #[test]
703 fn test_block_debug() {
703 fn test_block_debug() {
704 let mut block = Block::new();
704 let mut block = Block::new();
705 block.set(1, Element::Rev(3));
705 block.set(1, Element::Rev(3));
706 block.set(10, Element::Block(0));
706 block.set(10, Element::Block(0));
707 assert_eq!(format!("{:?}", block), "{1: Rev(3), 10: Block(0)}");
707 assert_eq!(format!("{:?}", block), "{1: Rev(3), 10: Block(0)}");
708 }
708 }
709
709
710 #[test]
710 #[test]
711 fn test_block_macro() {
711 fn test_block_macro() {
712 let block = block! {5: Block(2)};
712 let block = block! {5: Block(2)};
713 assert_eq!(format!("{:?}", block), "{5: Block(2)}");
713 assert_eq!(format!("{:?}", block), "{5: Block(2)}");
714
714
715 let block = block! {13: Rev(15), 5: Block(2)};
715 let block = block! {13: Rev(15), 5: Block(2)};
716 assert_eq!(format!("{:?}", block), "{5: Block(2), 13: Rev(15)}");
716 assert_eq!(format!("{:?}", block), "{5: Block(2), 13: Rev(15)}");
717 }
717 }
718
718
719 #[test]
719 #[test]
720 fn test_raw_block() {
720 fn test_raw_block() {
721 let mut raw = [255u8; 64];
721 let mut raw = [255u8; 64];
722
722
723 let mut counter = 0;
723 let mut counter = 0;
724 for val in [0_i32, 15, -2, -1, -3].iter() {
724 for val in [0_i32, 15, -2, -1, -3].iter() {
725 for byte in val.to_be_bytes().iter() {
725 for byte in val.to_be_bytes().iter() {
726 raw[counter] = *byte;
726 raw[counter] = *byte;
727 counter += 1;
727 counter += 1;
728 }
728 }
729 }
729 }
730 let (block, _) = Block::from_bytes(&raw).unwrap();
730 let (block, _) = Block::from_bytes(&raw).unwrap();
731 assert_eq!(block.get(0), Element::Block(0));
731 assert_eq!(block.get(0), Element::Block(0));
732 assert_eq!(block.get(1), Element::Block(15));
732 assert_eq!(block.get(1), Element::Block(15));
733 assert_eq!(block.get(3), Element::None);
733 assert_eq!(block.get(3), Element::None);
734 assert_eq!(block.get(2), Element::Rev(0));
734 assert_eq!(block.get(2), Element::Rev(0));
735 assert_eq!(block.get(4), Element::Rev(1));
735 assert_eq!(block.get(4), Element::Rev(1));
736 }
736 }
737
737
738 type TestIndex = HashMap<Revision, Node>;
738 type TestIndex = HashMap<Revision, Node>;
739
739
740 impl RevlogIndex for TestIndex {
740 impl RevlogIndex for TestIndex {
741 fn node(&self, rev: Revision) -> Option<&Node> {
741 fn node(&self, rev: Revision) -> Option<&Node> {
742 self.get(&rev)
742 self.get(&rev)
743 }
743 }
744
744
745 fn len(&self) -> usize {
745 fn len(&self) -> usize {
746 self.len()
746 self.len()
747 }
747 }
748 }
748 }
749
749
750 /// Pad hexadecimal Node prefix with zeros on the right
750 /// Pad hexadecimal Node prefix with zeros on the right
751 ///
751 ///
752 /// This avoids having to repeatedly write very long hexadecimal
752 /// This avoids having to repeatedly write very long hexadecimal
753 /// strings for test data, and brings actual hash size independency.
753 /// strings for test data, and brings actual hash size independency.
754 #[cfg(test)]
754 #[cfg(test)]
755 fn pad_node(hex: &str) -> Node {
755 fn pad_node(hex: &str) -> Node {
756 Node::from_hex(&hex_pad_right(hex)).unwrap()
756 Node::from_hex(&hex_pad_right(hex)).unwrap()
757 }
757 }
758
758
759 /// Pad hexadecimal Node prefix with zeros on the right, then insert
759 /// Pad hexadecimal Node prefix with zeros on the right, then insert
760 fn pad_insert(idx: &mut TestIndex, rev: Revision, hex: &str) {
760 fn pad_insert(idx: &mut TestIndex, rev: Revision, hex: &str) {
761 idx.insert(rev, pad_node(hex));
761 idx.insert(rev, pad_node(hex));
762 }
762 }
763
763
764 fn sample_nodetree() -> NodeTree {
764 fn sample_nodetree() -> NodeTree {
765 NodeTree::from(vec![
765 NodeTree::from(vec![
766 block![0: Rev(9)],
766 block![0: Rev(9)],
767 block![0: Rev(0), 1: Rev(9)],
767 block![0: Rev(0), 1: Rev(9)],
768 block![0: Block(1), 1:Rev(1)],
768 block![0: Block(1), 1:Rev(1)],
769 ])
769 ])
770 }
770 }
771
771
772 fn hex(s: &str) -> NodePrefix {
772 fn hex(s: &str) -> NodePrefix {
773 NodePrefix::from_hex(s).unwrap()
773 NodePrefix::from_hex(s).unwrap()
774 }
774 }
775
775
776 #[test]
776 #[test]
777 fn test_nt_debug() {
777 fn test_nt_debug() {
778 let nt = sample_nodetree();
778 let nt = sample_nodetree();
779 assert_eq!(
779 assert_eq!(
780 format!("{:?}", nt),
780 format!("{:?}", nt),
781 "readonly: \
781 "readonly: \
782 [{0: Rev(9)}, {0: Rev(0), 1: Rev(9)}, {0: Block(1), 1: Rev(1)}], \
782 [{0: Rev(9)}, {0: Rev(0), 1: Rev(9)}, {0: Block(1), 1: Rev(1)}], \
783 growable: [], \
783 growable: [], \
784 root: {0: Block(1), 1: Rev(1)}",
784 root: {0: Block(1), 1: Rev(1)}",
785 );
785 );
786 }
786 }
787
787
788 #[test]
788 #[test]
789 fn test_immutable_find_simplest() -> Result<(), NodeMapError> {
789 fn test_immutable_find_simplest() -> Result<(), NodeMapError> {
790 let mut idx: TestIndex = HashMap::new();
790 let mut idx: TestIndex = HashMap::new();
791 pad_insert(&mut idx, 1, "1234deadcafe");
791 pad_insert(&mut idx, 1, "1234deadcafe");
792
792
793 let nt = NodeTree::from(vec![block! {1: Rev(1)}]);
793 let nt = NodeTree::from(vec![block! {1: Rev(1)}]);
794 assert_eq!(nt.find_bin(&idx, hex("1"))?, Some(1));
794 assert_eq!(nt.find_bin(&idx, hex("1"))?, Some(1));
795 assert_eq!(nt.find_bin(&idx, hex("12"))?, Some(1));
795 assert_eq!(nt.find_bin(&idx, hex("12"))?, Some(1));
796 assert_eq!(nt.find_bin(&idx, hex("1234de"))?, Some(1));
796 assert_eq!(nt.find_bin(&idx, hex("1234de"))?, Some(1));
797 assert_eq!(nt.find_bin(&idx, hex("1a"))?, None);
797 assert_eq!(nt.find_bin(&idx, hex("1a"))?, None);
798 assert_eq!(nt.find_bin(&idx, hex("ab"))?, None);
798 assert_eq!(nt.find_bin(&idx, hex("ab"))?, None);
799
799
800 // and with full binary Nodes
800 // and with full binary Nodes
801 assert_eq!(nt.find_node(&idx, idx.get(&1).unwrap())?, Some(1));
801 assert_eq!(nt.find_node(&idx, idx.get(&1).unwrap())?, Some(1));
802 let unknown = Node::from_hex(&hex_pad_right("3d")).unwrap();
802 let unknown = Node::from_hex(&hex_pad_right("3d")).unwrap();
803 assert_eq!(nt.find_node(&idx, &unknown)?, None);
803 assert_eq!(nt.find_node(&idx, &unknown)?, None);
804 Ok(())
804 Ok(())
805 }
805 }
806
806
807 #[test]
807 #[test]
808 fn test_immutable_find_one_jump() {
808 fn test_immutable_find_one_jump() {
809 let mut idx = TestIndex::new();
809 let mut idx = TestIndex::new();
810 pad_insert(&mut idx, 9, "012");
810 pad_insert(&mut idx, 9, "012");
811 pad_insert(&mut idx, 0, "00a");
811 pad_insert(&mut idx, 0, "00a");
812
812
813 let nt = sample_nodetree();
813 let nt = sample_nodetree();
814
814
815 assert_eq!(nt.find_bin(&idx, hex("0")), Err(MultipleResults));
815 assert_eq!(nt.find_bin(&idx, hex("0")), Err(MultipleResults));
816 assert_eq!(nt.find_bin(&idx, hex("01")), Ok(Some(9)));
816 assert_eq!(nt.find_bin(&idx, hex("01")), Ok(Some(9)));
817 assert_eq!(nt.find_bin(&idx, hex("00")), Err(MultipleResults));
817 assert_eq!(nt.find_bin(&idx, hex("00")), Err(MultipleResults));
818 assert_eq!(nt.find_bin(&idx, hex("00a")), Ok(Some(0)));
818 assert_eq!(nt.find_bin(&idx, hex("00a")), Ok(Some(0)));
819 assert_eq!(nt.unique_prefix_len_bin(&idx, hex("00a")), Ok(Some(3)));
819 assert_eq!(nt.unique_prefix_len_bin(&idx, hex("00a")), Ok(Some(3)));
820 assert_eq!(nt.find_bin(&idx, hex("000")), Ok(Some(NULL_REVISION)));
820 assert_eq!(nt.find_bin(&idx, hex("000")), Ok(Some(NULL_REVISION)));
821 }
821 }
822
822
823 #[test]
823 #[test]
824 fn test_mutated_find() -> Result<(), NodeMapError> {
824 fn test_mutated_find() -> Result<(), NodeMapError> {
825 let mut idx = TestIndex::new();
825 let mut idx = TestIndex::new();
826 pad_insert(&mut idx, 9, "012");
826 pad_insert(&mut idx, 9, "012");
827 pad_insert(&mut idx, 0, "00a");
827 pad_insert(&mut idx, 0, "00a");
828 pad_insert(&mut idx, 2, "cafe");
828 pad_insert(&mut idx, 2, "cafe");
829 pad_insert(&mut idx, 3, "15");
829 pad_insert(&mut idx, 3, "15");
830 pad_insert(&mut idx, 1, "10");
830 pad_insert(&mut idx, 1, "10");
831
831
832 let nt = NodeTree {
832 let nt = NodeTree {
833 readonly: sample_nodetree().readonly,
833 readonly: sample_nodetree().readonly,
834 growable: vec![block![0: Rev(1), 5: Rev(3)]],
834 growable: vec![block![0: Rev(1), 5: Rev(3)]],
835 root: block![0: Block(1), 1:Block(3), 12: Rev(2)],
835 root: block![0: Block(1), 1:Block(3), 12: Rev(2)],
836 masked_inner_blocks: 1,
836 masked_inner_blocks: 1,
837 };
837 };
838 assert_eq!(nt.find_bin(&idx, hex("10"))?, Some(1));
838 assert_eq!(nt.find_bin(&idx, hex("10"))?, Some(1));
839 assert_eq!(nt.find_bin(&idx, hex("c"))?, Some(2));
839 assert_eq!(nt.find_bin(&idx, hex("c"))?, Some(2));
840 assert_eq!(nt.unique_prefix_len_bin(&idx, hex("c"))?, Some(1));
840 assert_eq!(nt.unique_prefix_len_bin(&idx, hex("c"))?, Some(1));
841 assert_eq!(nt.find_bin(&idx, hex("00")), Err(MultipleResults));
841 assert_eq!(nt.find_bin(&idx, hex("00")), Err(MultipleResults));
842 assert_eq!(nt.find_bin(&idx, hex("000"))?, Some(NULL_REVISION));
842 assert_eq!(nt.find_bin(&idx, hex("000"))?, Some(NULL_REVISION));
843 assert_eq!(nt.unique_prefix_len_bin(&idx, hex("000"))?, Some(3));
843 assert_eq!(nt.unique_prefix_len_bin(&idx, hex("000"))?, Some(3));
844 assert_eq!(nt.find_bin(&idx, hex("01"))?, Some(9));
844 assert_eq!(nt.find_bin(&idx, hex("01"))?, Some(9));
845 assert_eq!(nt.masked_readonly_blocks(), 2);
845 assert_eq!(nt.masked_readonly_blocks(), 2);
846 Ok(())
846 Ok(())
847 }
847 }
848
848
849 struct TestNtIndex {
849 struct TestNtIndex {
850 index: TestIndex,
850 index: TestIndex,
851 nt: NodeTree,
851 nt: NodeTree,
852 }
852 }
853
853
854 impl TestNtIndex {
854 impl TestNtIndex {
855 fn new() -> Self {
855 fn new() -> Self {
856 TestNtIndex {
856 TestNtIndex {
857 index: HashMap::new(),
857 index: HashMap::new(),
858 nt: NodeTree::default(),
858 nt: NodeTree::default(),
859 }
859 }
860 }
860 }
861
861
862 fn insert(
862 fn insert(
863 &mut self,
863 &mut self,
864 rev: Revision,
864 rev: Revision,
865 hex: &str,
865 hex: &str,
866 ) -> Result<(), NodeMapError> {
866 ) -> Result<(), NodeMapError> {
867 let node = pad_node(hex);
867 let node = pad_node(hex);
868 self.index.insert(rev, node);
868 self.index.insert(rev, node);
869 self.nt.insert(&self.index, &node, rev)?;
869 self.nt.insert(&self.index, &node, rev)?;
870 Ok(())
870 Ok(())
871 }
871 }
872
872
873 fn find_hex(
873 fn find_hex(
874 &self,
874 &self,
875 prefix: &str,
875 prefix: &str,
876 ) -> Result<Option<Revision>, NodeMapError> {
876 ) -> Result<Option<Revision>, NodeMapError> {
877 self.nt.find_bin(&self.index, hex(prefix))
877 self.nt.find_bin(&self.index, hex(prefix))
878 }
878 }
879
879
880 fn unique_prefix_len_hex(
880 fn unique_prefix_len_hex(
881 &self,
881 &self,
882 prefix: &str,
882 prefix: &str,
883 ) -> Result<Option<usize>, NodeMapError> {
883 ) -> Result<Option<usize>, NodeMapError> {
884 self.nt.unique_prefix_len_bin(&self.index, hex(prefix))
884 self.nt.unique_prefix_len_bin(&self.index, hex(prefix))
885 }
885 }
886
886
887 /// Drain `added` and restart a new one
887 /// Drain `added` and restart a new one
888 fn commit(self) -> Self {
888 fn commit(self) -> Self {
889 let mut as_vec: Vec<Block> =
889 let mut as_vec: Vec<Block> =
890 self.nt.readonly.iter().copied().collect();
890 self.nt.readonly.iter().copied().collect();
891 as_vec.extend(self.nt.growable);
891 as_vec.extend(self.nt.growable);
892 as_vec.push(self.nt.root);
892 as_vec.push(self.nt.root);
893
893
894 Self {
894 Self {
895 index: self.index,
895 index: self.index,
896 nt: NodeTree::from(as_vec),
896 nt: NodeTree::from(as_vec),
897 }
897 }
898 }
898 }
899 }
899 }
900
900
901 #[test]
901 #[test]
902 fn test_insert_full_mutable() -> Result<(), NodeMapError> {
902 fn test_insert_full_mutable() -> Result<(), NodeMapError> {
903 let mut idx = TestNtIndex::new();
903 let mut idx = TestNtIndex::new();
904 idx.insert(0, "1234")?;
904 idx.insert(0, "1234")?;
905 assert_eq!(idx.find_hex("1")?, Some(0));
905 assert_eq!(idx.find_hex("1")?, Some(0));
906 assert_eq!(idx.find_hex("12")?, Some(0));
906 assert_eq!(idx.find_hex("12")?, Some(0));
907
907
908 // let's trigger a simple split
908 // let's trigger a simple split
909 idx.insert(1, "1a34")?;
909 idx.insert(1, "1a34")?;
910 assert_eq!(idx.nt.growable.len(), 1);
910 assert_eq!(idx.nt.growable.len(), 1);
911 assert_eq!(idx.find_hex("12")?, Some(0));
911 assert_eq!(idx.find_hex("12")?, Some(0));
912 assert_eq!(idx.find_hex("1a")?, Some(1));
912 assert_eq!(idx.find_hex("1a")?, Some(1));
913
913
914 // reinserting is a no_op
914 // reinserting is a no_op
915 idx.insert(1, "1a34")?;
915 idx.insert(1, "1a34")?;
916 assert_eq!(idx.nt.growable.len(), 1);
916 assert_eq!(idx.nt.growable.len(), 1);
917 assert_eq!(idx.find_hex("12")?, Some(0));
917 assert_eq!(idx.find_hex("12")?, Some(0));
918 assert_eq!(idx.find_hex("1a")?, Some(1));
918 assert_eq!(idx.find_hex("1a")?, Some(1));
919
919
920 idx.insert(2, "1a01")?;
920 idx.insert(2, "1a01")?;
921 assert_eq!(idx.nt.growable.len(), 2);
921 assert_eq!(idx.nt.growable.len(), 2);
922 assert_eq!(idx.find_hex("1a"), Err(NodeMapError::MultipleResults));
922 assert_eq!(idx.find_hex("1a"), Err(NodeMapError::MultipleResults));
923 assert_eq!(idx.find_hex("12")?, Some(0));
923 assert_eq!(idx.find_hex("12")?, Some(0));
924 assert_eq!(idx.find_hex("1a3")?, Some(1));
924 assert_eq!(idx.find_hex("1a3")?, Some(1));
925 assert_eq!(idx.find_hex("1a0")?, Some(2));
925 assert_eq!(idx.find_hex("1a0")?, Some(2));
926 assert_eq!(idx.find_hex("1a12")?, None);
926 assert_eq!(idx.find_hex("1a12")?, None);
927
927
928 // now let's make it split and create more than one additional block
928 // now let's make it split and create more than one additional block
929 idx.insert(3, "1a345")?;
929 idx.insert(3, "1a345")?;
930 assert_eq!(idx.nt.growable.len(), 4);
930 assert_eq!(idx.nt.growable.len(), 4);
931 assert_eq!(idx.find_hex("1a340")?, Some(1));
931 assert_eq!(idx.find_hex("1a340")?, Some(1));
932 assert_eq!(idx.find_hex("1a345")?, Some(3));
932 assert_eq!(idx.find_hex("1a345")?, Some(3));
933 assert_eq!(idx.find_hex("1a341")?, None);
933 assert_eq!(idx.find_hex("1a341")?, None);
934
934
935 // there's no readonly block to mask
935 // there's no readonly block to mask
936 assert_eq!(idx.nt.masked_readonly_blocks(), 0);
936 assert_eq!(idx.nt.masked_readonly_blocks(), 0);
937 Ok(())
937 Ok(())
938 }
938 }
939
939
940 #[test]
940 #[test]
941 fn test_unique_prefix_len_zero_prefix() {
941 fn test_unique_prefix_len_zero_prefix() {
942 let mut idx = TestNtIndex::new();
942 let mut idx = TestNtIndex::new();
943 idx.insert(0, "00000abcd").unwrap();
943 idx.insert(0, "00000abcd").unwrap();
944
944
945 assert_eq!(idx.find_hex("000"), Err(NodeMapError::MultipleResults));
945 assert_eq!(idx.find_hex("000"), Err(NodeMapError::MultipleResults));
946 // in the nodetree proper, this will be found at the first nybble
946 // in the nodetree proper, this will be found at the first nybble
947 // yet the correct answer for unique_prefix_len is not 1, nor 1+1,
947 // yet the correct answer for unique_prefix_len is not 1, nor 1+1,
948 // but the first difference with `NULL_NODE`
948 // but the first difference with `NULL_NODE`
949 assert_eq!(idx.unique_prefix_len_hex("00000a"), Ok(Some(6)));
949 assert_eq!(idx.unique_prefix_len_hex("00000a"), Ok(Some(6)));
950 assert_eq!(idx.unique_prefix_len_hex("00000ab"), Ok(Some(6)));
950 assert_eq!(idx.unique_prefix_len_hex("00000ab"), Ok(Some(6)));
951
951
952 // same with odd result
952 // same with odd result
953 idx.insert(1, "00123").unwrap();
953 idx.insert(1, "00123").unwrap();
954 assert_eq!(idx.unique_prefix_len_hex("001"), Ok(Some(3)));
954 assert_eq!(idx.unique_prefix_len_hex("001"), Ok(Some(3)));
955 assert_eq!(idx.unique_prefix_len_hex("0012"), Ok(Some(3)));
955 assert_eq!(idx.unique_prefix_len_hex("0012"), Ok(Some(3)));
956
956
957 // these are unchanged of course
957 // these are unchanged of course
958 assert_eq!(idx.unique_prefix_len_hex("00000a"), Ok(Some(6)));
958 assert_eq!(idx.unique_prefix_len_hex("00000a"), Ok(Some(6)));
959 assert_eq!(idx.unique_prefix_len_hex("00000ab"), Ok(Some(6)));
959 assert_eq!(idx.unique_prefix_len_hex("00000ab"), Ok(Some(6)));
960 }
960 }
961
961
962 #[test]
962 #[test]
963 fn test_insert_extreme_splitting() -> Result<(), NodeMapError> {
963 fn test_insert_extreme_splitting() -> Result<(), NodeMapError> {
964 // check that the splitting loop is long enough
964 // check that the splitting loop is long enough
965 let mut nt_idx = TestNtIndex::new();
965 let mut nt_idx = TestNtIndex::new();
966 let nt = &mut nt_idx.nt;
966 let nt = &mut nt_idx.nt;
967 let idx = &mut nt_idx.index;
967 let idx = &mut nt_idx.index;
968
968
969 let node0_hex = hex_pad_right("444444");
969 let node0_hex = hex_pad_right("444444");
970 let mut node1_hex = hex_pad_right("444444");
970 let mut node1_hex = hex_pad_right("444444");
971 node1_hex.pop();
971 node1_hex.pop();
972 node1_hex.push('5');
972 node1_hex.push('5');
973 let node0 = Node::from_hex(&node0_hex).unwrap();
973 let node0 = Node::from_hex(&node0_hex).unwrap();
974 let node1 = Node::from_hex(&node1_hex).unwrap();
974 let node1 = Node::from_hex(&node1_hex).unwrap();
975
975
976 idx.insert(0, node0);
976 idx.insert(0, node0);
977 nt.insert(idx, &node0, 0)?;
977 nt.insert(idx, &node0, 0)?;
978 idx.insert(1, node1);
978 idx.insert(1, node1);
979 nt.insert(idx, &node1, 1)?;
979 nt.insert(idx, &node1, 1)?;
980
980
981 assert_eq!(nt.find_bin(idx, (&node0).into())?, Some(0));
981 assert_eq!(nt.find_bin(idx, (&node0).into())?, Some(0));
982 assert_eq!(nt.find_bin(idx, (&node1).into())?, Some(1));
982 assert_eq!(nt.find_bin(idx, (&node1).into())?, Some(1));
983 Ok(())
983 Ok(())
984 }
984 }
985
985
986 #[test]
986 #[test]
987 fn test_insert_partly_immutable() -> Result<(), NodeMapError> {
987 fn test_insert_partly_immutable() -> Result<(), NodeMapError> {
988 let mut idx = TestNtIndex::new();
988 let mut idx = TestNtIndex::new();
989 idx.insert(0, "1234")?;
989 idx.insert(0, "1234")?;
990 idx.insert(1, "1235")?;
990 idx.insert(1, "1235")?;
991 idx.insert(2, "131")?;
991 idx.insert(2, "131")?;
992 idx.insert(3, "cafe")?;
992 idx.insert(3, "cafe")?;
993 let mut idx = idx.commit();
993 let mut idx = idx.commit();
994 assert_eq!(idx.find_hex("1234")?, Some(0));
994 assert_eq!(idx.find_hex("1234")?, Some(0));
995 assert_eq!(idx.find_hex("1235")?, Some(1));
995 assert_eq!(idx.find_hex("1235")?, Some(1));
996 assert_eq!(idx.find_hex("131")?, Some(2));
996 assert_eq!(idx.find_hex("131")?, Some(2));
997 assert_eq!(idx.find_hex("cafe")?, Some(3));
997 assert_eq!(idx.find_hex("cafe")?, Some(3));
998 // we did not add anything since init from readonly
998 // we did not add anything since init from readonly
999 assert_eq!(idx.nt.masked_readonly_blocks(), 0);
999 assert_eq!(idx.nt.masked_readonly_blocks(), 0);
1000
1000
1001 idx.insert(4, "123A")?;
1001 idx.insert(4, "123A")?;
1002 assert_eq!(idx.find_hex("1234")?, Some(0));
1002 assert_eq!(idx.find_hex("1234")?, Some(0));
1003 assert_eq!(idx.find_hex("1235")?, Some(1));
1003 assert_eq!(idx.find_hex("1235")?, Some(1));
1004 assert_eq!(idx.find_hex("131")?, Some(2));
1004 assert_eq!(idx.find_hex("131")?, Some(2));
1005 assert_eq!(idx.find_hex("cafe")?, Some(3));
1005 assert_eq!(idx.find_hex("cafe")?, Some(3));
1006 assert_eq!(idx.find_hex("123A")?, Some(4));
1006 assert_eq!(idx.find_hex("123A")?, Some(4));
1007 // we masked blocks for all prefixes of "123", including the root
1007 // we masked blocks for all prefixes of "123", including the root
1008 assert_eq!(idx.nt.masked_readonly_blocks(), 4);
1008 assert_eq!(idx.nt.masked_readonly_blocks(), 4);
1009
1009
1010 eprintln!("{:?}", idx.nt);
1010 eprintln!("{:?}", idx.nt);
1011 idx.insert(5, "c0")?;
1011 idx.insert(5, "c0")?;
1012 assert_eq!(idx.find_hex("cafe")?, Some(3));
1012 assert_eq!(idx.find_hex("cafe")?, Some(3));
1013 assert_eq!(idx.find_hex("c0")?, Some(5));
1013 assert_eq!(idx.find_hex("c0")?, Some(5));
1014 assert_eq!(idx.find_hex("c1")?, None);
1014 assert_eq!(idx.find_hex("c1")?, None);
1015 assert_eq!(idx.find_hex("1234")?, Some(0));
1015 assert_eq!(idx.find_hex("1234")?, Some(0));
1016 // inserting "c0" is just splitting the 'c' slot of the mutable root,
1016 // inserting "c0" is just splitting the 'c' slot of the mutable root,
1017 // it doesn't mask anything
1017 // it doesn't mask anything
1018 assert_eq!(idx.nt.masked_readonly_blocks(), 4);
1018 assert_eq!(idx.nt.masked_readonly_blocks(), 4);
1019
1019
1020 Ok(())
1020 Ok(())
1021 }
1021 }
1022
1022
1023 #[test]
1023 #[test]
1024 fn test_invalidate_all() -> Result<(), NodeMapError> {
1024 fn test_invalidate_all() -> Result<(), NodeMapError> {
1025 let mut idx = TestNtIndex::new();
1025 let mut idx = TestNtIndex::new();
1026 idx.insert(0, "1234")?;
1026 idx.insert(0, "1234")?;
1027 idx.insert(1, "1235")?;
1027 idx.insert(1, "1235")?;
1028 idx.insert(2, "131")?;
1028 idx.insert(2, "131")?;
1029 idx.insert(3, "cafe")?;
1029 idx.insert(3, "cafe")?;
1030 let mut idx = idx.commit();
1030 let mut idx = idx.commit();
1031
1031
1032 idx.nt.invalidate_all();
1032 idx.nt.invalidate_all();
1033
1033
1034 assert_eq!(idx.find_hex("1234")?, None);
1034 assert_eq!(idx.find_hex("1234")?, None);
1035 assert_eq!(idx.find_hex("1235")?, None);
1035 assert_eq!(idx.find_hex("1235")?, None);
1036 assert_eq!(idx.find_hex("131")?, None);
1036 assert_eq!(idx.find_hex("131")?, None);
1037 assert_eq!(idx.find_hex("cafe")?, None);
1037 assert_eq!(idx.find_hex("cafe")?, None);
1038 // all the readonly blocks have been masked, this is the
1038 // all the readonly blocks have been masked, this is the
1039 // conventional expected response
1039 // conventional expected response
1040 assert_eq!(idx.nt.masked_readonly_blocks(), idx.nt.readonly.len() + 1);
1040 assert_eq!(idx.nt.masked_readonly_blocks(), idx.nt.readonly.len() + 1);
1041 Ok(())
1041 Ok(())
1042 }
1042 }
1043
1043
1044 #[test]
1044 #[test]
1045 fn test_into_added_empty() {
1045 fn test_into_added_empty() {
1046 assert!(sample_nodetree().into_readonly_and_added().1.is_empty());
1046 assert!(sample_nodetree().into_readonly_and_added().1.is_empty());
1047 assert!(sample_nodetree()
1047 assert!(sample_nodetree()
1048 .into_readonly_and_added_bytes()
1048 .into_readonly_and_added_bytes()
1049 .1
1049 .1
1050 .is_empty());
1050 .is_empty());
1051 }
1051 }
1052
1052
1053 #[test]
1053 #[test]
1054 fn test_into_added_bytes() -> Result<(), NodeMapError> {
1054 fn test_into_added_bytes() -> Result<(), NodeMapError> {
1055 let mut idx = TestNtIndex::new();
1055 let mut idx = TestNtIndex::new();
1056 idx.insert(0, "1234")?;
1056 idx.insert(0, "1234")?;
1057 let mut idx = idx.commit();
1057 let mut idx = idx.commit();
1058 idx.insert(4, "cafe")?;
1058 idx.insert(4, "cafe")?;
1059 let (_, bytes) = idx.nt.into_readonly_and_added_bytes();
1059 let (_, bytes) = idx.nt.into_readonly_and_added_bytes();
1060
1060
1061 // only the root block has been changed
1061 // only the root block has been changed
1062 assert_eq!(bytes.len(), size_of::<Block>());
1062 assert_eq!(bytes.len(), size_of::<Block>());
1063 // big endian for -2
1063 // big endian for -2
1064 assert_eq!(&bytes[4..2 * 4], [255, 255, 255, 254]);
1064 assert_eq!(&bytes[4..2 * 4], [255, 255, 255, 254]);
1065 // big endian for -6
1065 // big endian for -6
1066 assert_eq!(&bytes[12 * 4..13 * 4], [255, 255, 255, 250]);
1066 assert_eq!(&bytes[12 * 4..13 * 4], [255, 255, 255, 250]);
1067 Ok(())
1067 Ok(())
1068 }
1068 }
1069 }
1069 }
@@ -1,532 +1,532 b''
1 // utils module
1 // utils module
2 //
2 //
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 //
4 //
5 // This software may be used and distributed according to the terms of the
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 //! Contains useful functions, traits, structs, etc. for use in core.
8 //! Contains useful functions, traits, structs, etc. for use in core.
9
9
10 use crate::errors::{HgError, IoErrorContext};
10 use crate::errors::{HgError, IoErrorContext};
11 use crate::utils::hg_path::HgPath;
11 use crate::utils::hg_path::HgPath;
12 use im_rc::ordmap::DiffItem;
12 use im_rc::ordmap::DiffItem;
13 use im_rc::ordmap::OrdMap;
13 use im_rc::ordmap::OrdMap;
14 use std::cell::Cell;
14 use std::cell::Cell;
15 use std::fmt;
15 use std::fmt;
16 use std::{io::Write, ops::Deref};
16 use std::{io::Write, ops::Deref};
17
17
18 pub mod debug;
18 pub mod debug;
19 pub mod files;
19 pub mod files;
20 pub mod hg_path;
20 pub mod hg_path;
21 pub mod path_auditor;
21 pub mod path_auditor;
22
22
23 /// Useful until rust/issues/56345 is stable
23 /// Useful until rust/issues/56345 is stable
24 ///
24 ///
25 /// # Examples
25 /// # Examples
26 ///
26 ///
27 /// ```
27 /// ```
28 /// use crate::hg::utils::find_slice_in_slice;
28 /// use crate::hg::utils::find_slice_in_slice;
29 ///
29 ///
30 /// let haystack = b"This is the haystack".to_vec();
30 /// let haystack = b"This is the haystack".to_vec();
31 /// assert_eq!(find_slice_in_slice(&haystack, b"the"), Some(8));
31 /// assert_eq!(find_slice_in_slice(&haystack, b"the"), Some(8));
32 /// assert_eq!(find_slice_in_slice(&haystack, b"not here"), None);
32 /// assert_eq!(find_slice_in_slice(&haystack, b"not here"), None);
33 /// ```
33 /// ```
34 pub fn find_slice_in_slice<T>(slice: &[T], needle: &[T]) -> Option<usize>
34 pub fn find_slice_in_slice<T>(slice: &[T], needle: &[T]) -> Option<usize>
35 where
35 where
36 for<'a> &'a [T]: PartialEq,
36 for<'a> &'a [T]: PartialEq,
37 {
37 {
38 slice
38 slice
39 .windows(needle.len())
39 .windows(needle.len())
40 .position(|window| window == needle)
40 .position(|window| window == needle)
41 }
41 }
42
42
43 /// Replaces the `from` slice with the `to` slice inside the `buf` slice.
43 /// Replaces the `from` slice with the `to` slice inside the `buf` slice.
44 ///
44 ///
45 /// # Examples
45 /// # Examples
46 ///
46 ///
47 /// ```
47 /// ```
48 /// use crate::hg::utils::replace_slice;
48 /// use crate::hg::utils::replace_slice;
49 /// let mut line = b"I hate writing tests!".to_vec();
49 /// let mut line = b"I hate writing tests!".to_vec();
50 /// replace_slice(&mut line, b"hate", b"love");
50 /// replace_slice(&mut line, b"hate", b"love");
51 /// assert_eq!(
51 /// assert_eq!(
52 /// line,
52 /// line,
53 /// b"I love writing tests!".to_vec()
53 /// b"I love writing tests!".to_vec()
54 /// );
54 /// );
55 /// ```
55 /// ```
56 pub fn replace_slice<T>(buf: &mut [T], from: &[T], to: &[T])
56 pub fn replace_slice<T>(buf: &mut [T], from: &[T], to: &[T])
57 where
57 where
58 T: Clone + PartialEq,
58 T: Clone + PartialEq,
59 {
59 {
60 if buf.len() < from.len() || from.len() != to.len() {
60 if buf.len() < from.len() || from.len() != to.len() {
61 return;
61 return;
62 }
62 }
63 for i in 0..=buf.len() - from.len() {
63 for i in 0..=buf.len() - from.len() {
64 if buf[i..].starts_with(from) {
64 if buf[i..].starts_with(from) {
65 buf[i..(i + from.len())].clone_from_slice(to);
65 buf[i..(i + from.len())].clone_from_slice(to);
66 }
66 }
67 }
67 }
68 }
68 }
69
69
70 pub trait SliceExt {
70 pub trait SliceExt {
71 fn trim_end(&self) -> &Self;
71 fn trim_end(&self) -> &Self;
72 fn trim_start(&self) -> &Self;
72 fn trim_start(&self) -> &Self;
73 fn trim_end_matches(&self, f: impl FnMut(u8) -> bool) -> &Self;
73 fn trim_end_matches(&self, f: impl FnMut(u8) -> bool) -> &Self;
74 fn trim_start_matches(&self, f: impl FnMut(u8) -> bool) -> &Self;
74 fn trim_start_matches(&self, f: impl FnMut(u8) -> bool) -> &Self;
75 fn trim(&self) -> &Self;
75 fn trim(&self) -> &Self;
76 fn drop_prefix(&self, needle: &Self) -> Option<&Self>;
76 fn drop_prefix(&self, needle: &Self) -> Option<&Self>;
77 fn split_2(&self, separator: u8) -> Option<(&[u8], &[u8])>;
77 fn split_2(&self, separator: u8) -> Option<(&[u8], &[u8])>;
78 fn split_2_by_slice(&self, separator: &[u8]) -> Option<(&[u8], &[u8])>;
78 fn split_2_by_slice(&self, separator: &[u8]) -> Option<(&[u8], &[u8])>;
79 }
79 }
80
80
81 impl SliceExt for [u8] {
81 impl SliceExt for [u8] {
82 fn trim_end(&self) -> &[u8] {
82 fn trim_end(&self) -> &[u8] {
83 self.trim_end_matches(|byte| byte.is_ascii_whitespace())
83 self.trim_end_matches(|byte| byte.is_ascii_whitespace())
84 }
84 }
85
85
86 fn trim_start(&self) -> &[u8] {
86 fn trim_start(&self) -> &[u8] {
87 self.trim_start_matches(|byte| byte.is_ascii_whitespace())
87 self.trim_start_matches(|byte| byte.is_ascii_whitespace())
88 }
88 }
89
89
90 fn trim_end_matches(&self, mut f: impl FnMut(u8) -> bool) -> &Self {
90 fn trim_end_matches(&self, mut f: impl FnMut(u8) -> bool) -> &Self {
91 if let Some(last) = self.iter().rposition(|&byte| !f(byte)) {
91 if let Some(last) = self.iter().rposition(|&byte| !f(byte)) {
92 &self[..=last]
92 &self[..=last]
93 } else {
93 } else {
94 &[]
94 &[]
95 }
95 }
96 }
96 }
97
97
98 fn trim_start_matches(&self, mut f: impl FnMut(u8) -> bool) -> &Self {
98 fn trim_start_matches(&self, mut f: impl FnMut(u8) -> bool) -> &Self {
99 if let Some(first) = self.iter().position(|&byte| !f(byte)) {
99 if let Some(first) = self.iter().position(|&byte| !f(byte)) {
100 &self[first..]
100 &self[first..]
101 } else {
101 } else {
102 &[]
102 &[]
103 }
103 }
104 }
104 }
105
105
106 /// ```
106 /// ```
107 /// use hg::utils::SliceExt;
107 /// use hg::utils::SliceExt;
108 /// assert_eq!(
108 /// assert_eq!(
109 /// b" to trim ".trim(),
109 /// b" to trim ".trim(),
110 /// b"to trim"
110 /// b"to trim"
111 /// );
111 /// );
112 /// assert_eq!(
112 /// assert_eq!(
113 /// b"to trim ".trim(),
113 /// b"to trim ".trim(),
114 /// b"to trim"
114 /// b"to trim"
115 /// );
115 /// );
116 /// assert_eq!(
116 /// assert_eq!(
117 /// b" to trim".trim(),
117 /// b" to trim".trim(),
118 /// b"to trim"
118 /// b"to trim"
119 /// );
119 /// );
120 /// ```
120 /// ```
121 fn trim(&self) -> &[u8] {
121 fn trim(&self) -> &[u8] {
122 self.trim_start().trim_end()
122 self.trim_start().trim_end()
123 }
123 }
124
124
125 fn drop_prefix(&self, needle: &Self) -> Option<&Self> {
125 fn drop_prefix(&self, needle: &Self) -> Option<&Self> {
126 if self.starts_with(needle) {
126 if self.starts_with(needle) {
127 Some(&self[needle.len()..])
127 Some(&self[needle.len()..])
128 } else {
128 } else {
129 None
129 None
130 }
130 }
131 }
131 }
132
132
133 fn split_2(&self, separator: u8) -> Option<(&[u8], &[u8])> {
133 fn split_2(&self, separator: u8) -> Option<(&[u8], &[u8])> {
134 let mut iter = self.splitn(2, |&byte| byte == separator);
134 let mut iter = self.splitn(2, |&byte| byte == separator);
135 let a = iter.next()?;
135 let a = iter.next()?;
136 let b = iter.next()?;
136 let b = iter.next()?;
137 Some((a, b))
137 Some((a, b))
138 }
138 }
139
139
140 fn split_2_by_slice(&self, separator: &[u8]) -> Option<(&[u8], &[u8])> {
140 fn split_2_by_slice(&self, separator: &[u8]) -> Option<(&[u8], &[u8])> {
141 find_slice_in_slice(self, separator)
141 find_slice_in_slice(self, separator)
142 .map(|pos| (&self[..pos], &self[pos + separator.len()..]))
142 .map(|pos| (&self[..pos], &self[pos + separator.len()..]))
143 }
143 }
144 }
144 }
145
145
146 pub trait Escaped {
146 pub trait Escaped {
147 /// Return bytes escaped for display to the user
147 /// Return bytes escaped for display to the user
148 fn escaped_bytes(&self) -> Vec<u8>;
148 fn escaped_bytes(&self) -> Vec<u8>;
149 }
149 }
150
150
151 impl Escaped for u8 {
151 impl Escaped for u8 {
152 fn escaped_bytes(&self) -> Vec<u8> {
152 fn escaped_bytes(&self) -> Vec<u8> {
153 let mut acc = vec![];
153 let mut acc = vec![];
154 match self {
154 match self {
155 c @ b'\'' | c @ b'\\' => {
155 c @ b'\'' | c @ b'\\' => {
156 acc.push(b'\\');
156 acc.push(b'\\');
157 acc.push(*c);
157 acc.push(*c);
158 }
158 }
159 b'\t' => {
159 b'\t' => {
160 acc.extend(br"\\t");
160 acc.extend(br"\\t");
161 }
161 }
162 b'\n' => {
162 b'\n' => {
163 acc.extend(br"\\n");
163 acc.extend(br"\\n");
164 }
164 }
165 b'\r' => {
165 b'\r' => {
166 acc.extend(br"\\r");
166 acc.extend(br"\\r");
167 }
167 }
168 c if (*c < b' ' || *c >= 127) => {
168 c if (*c < b' ' || *c >= 127) => {
169 write!(acc, "\\x{:x}", self).unwrap();
169 write!(acc, "\\x{:x}", self).unwrap();
170 }
170 }
171 c => {
171 c => {
172 acc.push(*c);
172 acc.push(*c);
173 }
173 }
174 }
174 }
175 acc
175 acc
176 }
176 }
177 }
177 }
178
178
179 impl<'a, T: Escaped> Escaped for &'a [T] {
179 impl<'a, T: Escaped> Escaped for &'a [T] {
180 fn escaped_bytes(&self) -> Vec<u8> {
180 fn escaped_bytes(&self) -> Vec<u8> {
181 self.iter().flat_map(Escaped::escaped_bytes).collect()
181 self.iter().flat_map(Escaped::escaped_bytes).collect()
182 }
182 }
183 }
183 }
184
184
185 impl<T: Escaped> Escaped for Vec<T> {
185 impl<T: Escaped> Escaped for Vec<T> {
186 fn escaped_bytes(&self) -> Vec<u8> {
186 fn escaped_bytes(&self) -> Vec<u8> {
187 self.deref().escaped_bytes()
187 self.deref().escaped_bytes()
188 }
188 }
189 }
189 }
190
190
191 impl<'a> Escaped for &'a HgPath {
191 impl<'a> Escaped for &'a HgPath {
192 fn escaped_bytes(&self) -> Vec<u8> {
192 fn escaped_bytes(&self) -> Vec<u8> {
193 self.as_bytes().escaped_bytes()
193 self.as_bytes().escaped_bytes()
194 }
194 }
195 }
195 }
196
196
197 #[cfg(unix)]
197 #[cfg(unix)]
198 pub fn shell_quote(value: &[u8]) -> Vec<u8> {
198 pub fn shell_quote(value: &[u8]) -> Vec<u8> {
199 if value.iter().all(|&byte| {
199 if value.iter().all(|&byte| {
200 matches!(
200 matches!(
201 byte,
201 byte,
202 b'a'..=b'z'
202 b'a'..=b'z'
203 | b'A'..=b'Z'
203 | b'A'..=b'Z'
204 | b'0'..=b'9'
204 | b'0'..=b'9'
205 | b'.'
205 | b'.'
206 | b'_'
206 | b'_'
207 | b'/'
207 | b'/'
208 | b'+'
208 | b'+'
209 | b'-'
209 | b'-'
210 )
210 )
211 }) {
211 }) {
212 value.to_owned()
212 value.to_owned()
213 } else {
213 } else {
214 let mut quoted = Vec::with_capacity(value.len() + 2);
214 let mut quoted = Vec::with_capacity(value.len() + 2);
215 quoted.push(b'\'');
215 quoted.push(b'\'');
216 for &byte in value {
216 for &byte in value {
217 if byte == b'\'' {
217 if byte == b'\'' {
218 quoted.push(b'\\');
218 quoted.push(b'\\');
219 }
219 }
220 quoted.push(byte);
220 quoted.push(byte);
221 }
221 }
222 quoted.push(b'\'');
222 quoted.push(b'\'');
223 quoted
223 quoted
224 }
224 }
225 }
225 }
226
226
227 pub fn current_dir() -> Result<std::path::PathBuf, HgError> {
227 pub fn current_dir() -> Result<std::path::PathBuf, HgError> {
228 std::env::current_dir().map_err(|error| HgError::IoError {
228 std::env::current_dir().map_err(|error| HgError::IoError {
229 error,
229 error,
230 context: IoErrorContext::CurrentDir,
230 context: IoErrorContext::CurrentDir,
231 })
231 })
232 }
232 }
233
233
234 pub fn current_exe() -> Result<std::path::PathBuf, HgError> {
234 pub fn current_exe() -> Result<std::path::PathBuf, HgError> {
235 std::env::current_exe().map_err(|error| HgError::IoError {
235 std::env::current_exe().map_err(|error| HgError::IoError {
236 error,
236 error,
237 context: IoErrorContext::CurrentExe,
237 context: IoErrorContext::CurrentExe,
238 })
238 })
239 }
239 }
240
240
241 /// Expand `$FOO` and `${FOO}` environment variables in the given byte string
241 /// Expand `$FOO` and `${FOO}` environment variables in the given byte string
242 pub fn expand_vars(s: &[u8]) -> std::borrow::Cow<[u8]> {
242 pub fn expand_vars(s: &[u8]) -> std::borrow::Cow<[u8]> {
243 lazy_static::lazy_static! {
243 lazy_static::lazy_static! {
244 /// https://github.com/python/cpython/blob/3.9/Lib/posixpath.py#L301
244 /// https://github.com/python/cpython/blob/3.9/Lib/posixpath.py#L301
245 /// The `x` makes whitespace ignored.
245 /// The `x` makes whitespace ignored.
246 /// `-u` disables the Unicode flag, which makes `\w` like Python with the ASCII flag.
246 /// `-u` disables the Unicode flag, which makes `\w` like Python with the ASCII flag.
247 static ref VAR_RE: regex::bytes::Regex =
247 static ref VAR_RE: regex::bytes::Regex =
248 regex::bytes::Regex::new(r"(?x-u)
248 regex::bytes::Regex::new(r"(?x-u)
249 \$
249 \$
250 (?:
250 (?:
251 (\w+)
251 (\w+)
252 |
252 |
253 \{
253 \{
254 ([^}]*)
254 ([^}]*)
255 \}
255 \}
256 )
256 )
257 ").unwrap();
257 ").unwrap();
258 }
258 }
259 VAR_RE.replace_all(s, |captures: &regex::bytes::Captures| {
259 VAR_RE.replace_all(s, |captures: &regex::bytes::Captures| {
260 let var_name = files::get_os_str_from_bytes(
260 let var_name = files::get_os_str_from_bytes(
261 captures
261 captures
262 .get(1)
262 .get(1)
263 .or_else(|| captures.get(2))
263 .or_else(|| captures.get(2))
264 .expect("either side of `|` must participate in match")
264 .expect("either side of `|` must participate in match")
265 .as_bytes(),
265 .as_bytes(),
266 );
266 );
267 std::env::var_os(var_name)
267 std::env::var_os(var_name)
268 .map(files::get_bytes_from_os_str)
268 .map(files::get_bytes_from_os_str)
269 .unwrap_or_else(|| {
269 .unwrap_or_else(|| {
270 // Referencing an environment variable that does not exist.
270 // Referencing an environment variable that does not exist.
271 // Leave the $FOO reference as-is.
271 // Leave the $FOO reference as-is.
272 captures[0].to_owned()
272 captures[0].to_owned()
273 })
273 })
274 })
274 })
275 }
275 }
276
276
277 #[test]
277 #[test]
278 fn test_expand_vars() {
278 fn test_expand_vars() {
279 // Modifying process-global state in a test isn’t great,
279 // Modifying process-global state in a test isn’t great,
280 // but hopefully this won’t collide with anything.
280 // but hopefully this won’t collide with anything.
281 std::env::set_var("TEST_EXPAND_VAR", "1");
281 std::env::set_var("TEST_EXPAND_VAR", "1");
282 assert_eq!(
282 assert_eq!(
283 expand_vars(b"before/$TEST_EXPAND_VAR/after"),
283 expand_vars(b"before/$TEST_EXPAND_VAR/after"),
284 &b"before/1/after"[..]
284 &b"before/1/after"[..]
285 );
285 );
286 assert_eq!(
286 assert_eq!(
287 expand_vars(b"before${TEST_EXPAND_VAR}${TEST_EXPAND_VAR}${TEST_EXPAND_VAR}after"),
287 expand_vars(b"before${TEST_EXPAND_VAR}${TEST_EXPAND_VAR}${TEST_EXPAND_VAR}after"),
288 &b"before111after"[..]
288 &b"before111after"[..]
289 );
289 );
290 let s = b"before $SOME_LONG_NAME_THAT_WE_ASSUME_IS_NOT_AN_ACTUAL_ENV_VAR after";
290 let s = b"before $SOME_LONG_NAME_THAT_WE_ASSUME_IS_NOT_AN_ACTUAL_ENV_VAR after";
291 assert_eq!(expand_vars(s), &s[..]);
291 assert_eq!(expand_vars(s), &s[..]);
292 }
292 }
293
293
294 pub(crate) enum MergeResult<V> {
294 pub(crate) enum MergeResult<V> {
295 Left,
295 Left,
296 Right,
296 Right,
297 New(V),
297 New(V),
298 }
298 }
299
299
300 /// Return the union of the two given maps,
300 /// Return the union of the two given maps,
301 /// calling `merge(key, left_value, right_value)` to resolve keys that exist in
301 /// calling `merge(key, left_value, right_value)` to resolve keys that exist in
302 /// both.
302 /// both.
303 ///
303 ///
304 /// CC https://github.com/bodil/im-rs/issues/166
304 /// CC <https://github.com/bodil/im-rs/issues/166>
305 pub(crate) fn ordmap_union_with_merge<K, V>(
305 pub(crate) fn ordmap_union_with_merge<K, V>(
306 left: OrdMap<K, V>,
306 left: OrdMap<K, V>,
307 right: OrdMap<K, V>,
307 right: OrdMap<K, V>,
308 mut merge: impl FnMut(&K, &V, &V) -> MergeResult<V>,
308 mut merge: impl FnMut(&K, &V, &V) -> MergeResult<V>,
309 ) -> OrdMap<K, V>
309 ) -> OrdMap<K, V>
310 where
310 where
311 K: Clone + Ord,
311 K: Clone + Ord,
312 V: Clone + PartialEq,
312 V: Clone + PartialEq,
313 {
313 {
314 if left.ptr_eq(&right) {
314 if left.ptr_eq(&right) {
315 // One of the two maps is an unmodified clone of the other
315 // One of the two maps is an unmodified clone of the other
316 left
316 left
317 } else if left.len() / 2 > right.len() {
317 } else if left.len() / 2 > right.len() {
318 // When two maps have different sizes,
318 // When two maps have different sizes,
319 // their size difference is a lower bound on
319 // their size difference is a lower bound on
320 // how many keys of the larger map are not also in the smaller map.
320 // how many keys of the larger map are not also in the smaller map.
321 // This in turn is a lower bound on the number of differences in
321 // This in turn is a lower bound on the number of differences in
322 // `OrdMap::diff` and the "amount of work" that would be done
322 // `OrdMap::diff` and the "amount of work" that would be done
323 // by `ordmap_union_with_merge_by_diff`.
323 // by `ordmap_union_with_merge_by_diff`.
324 //
324 //
325 // Here `left` is more than twice the size of `right`,
325 // Here `left` is more than twice the size of `right`,
326 // so the number of differences is more than the total size of
326 // so the number of differences is more than the total size of
327 // `right`. Therefore an algorithm based on iterating `right`
327 // `right`. Therefore an algorithm based on iterating `right`
328 // is more efficient.
328 // is more efficient.
329 //
329 //
330 // This helps a lot when a tiny (or empty) map is merged
330 // This helps a lot when a tiny (or empty) map is merged
331 // with a large one.
331 // with a large one.
332 ordmap_union_with_merge_by_iter(left, right, merge)
332 ordmap_union_with_merge_by_iter(left, right, merge)
333 } else if left.len() < right.len() / 2 {
333 } else if left.len() < right.len() / 2 {
334 // Same as above but with `left` and `right` swapped
334 // Same as above but with `left` and `right` swapped
335 ordmap_union_with_merge_by_iter(right, left, |key, a, b| {
335 ordmap_union_with_merge_by_iter(right, left, |key, a, b| {
336 // Also swapped in `merge` arguments:
336 // Also swapped in `merge` arguments:
337 match merge(key, b, a) {
337 match merge(key, b, a) {
338 MergeResult::New(v) => MergeResult::New(v),
338 MergeResult::New(v) => MergeResult::New(v),
339 // … and swap back in `merge` result:
339 // … and swap back in `merge` result:
340 MergeResult::Left => MergeResult::Right,
340 MergeResult::Left => MergeResult::Right,
341 MergeResult::Right => MergeResult::Left,
341 MergeResult::Right => MergeResult::Left,
342 }
342 }
343 })
343 })
344 } else {
344 } else {
345 // For maps of similar size, use the algorithm based on `OrdMap::diff`
345 // For maps of similar size, use the algorithm based on `OrdMap::diff`
346 ordmap_union_with_merge_by_diff(left, right, merge)
346 ordmap_union_with_merge_by_diff(left, right, merge)
347 }
347 }
348 }
348 }
349
349
350 /// Efficient if `right` is much smaller than `left`
350 /// Efficient if `right` is much smaller than `left`
351 fn ordmap_union_with_merge_by_iter<K, V>(
351 fn ordmap_union_with_merge_by_iter<K, V>(
352 mut left: OrdMap<K, V>,
352 mut left: OrdMap<K, V>,
353 right: OrdMap<K, V>,
353 right: OrdMap<K, V>,
354 mut merge: impl FnMut(&K, &V, &V) -> MergeResult<V>,
354 mut merge: impl FnMut(&K, &V, &V) -> MergeResult<V>,
355 ) -> OrdMap<K, V>
355 ) -> OrdMap<K, V>
356 where
356 where
357 K: Clone + Ord,
357 K: Clone + Ord,
358 V: Clone,
358 V: Clone,
359 {
359 {
360 for (key, right_value) in right {
360 for (key, right_value) in right {
361 match left.get(&key) {
361 match left.get(&key) {
362 None => {
362 None => {
363 left.insert(key, right_value);
363 left.insert(key, right_value);
364 }
364 }
365 Some(left_value) => match merge(&key, left_value, &right_value) {
365 Some(left_value) => match merge(&key, left_value, &right_value) {
366 MergeResult::Left => {}
366 MergeResult::Left => {}
367 MergeResult::Right => {
367 MergeResult::Right => {
368 left.insert(key, right_value);
368 left.insert(key, right_value);
369 }
369 }
370 MergeResult::New(new_value) => {
370 MergeResult::New(new_value) => {
371 left.insert(key, new_value);
371 left.insert(key, new_value);
372 }
372 }
373 },
373 },
374 }
374 }
375 }
375 }
376 left
376 left
377 }
377 }
378
378
379 /// Fallback when both maps are of similar size
379 /// Fallback when both maps are of similar size
380 fn ordmap_union_with_merge_by_diff<K, V>(
380 fn ordmap_union_with_merge_by_diff<K, V>(
381 mut left: OrdMap<K, V>,
381 mut left: OrdMap<K, V>,
382 mut right: OrdMap<K, V>,
382 mut right: OrdMap<K, V>,
383 mut merge: impl FnMut(&K, &V, &V) -> MergeResult<V>,
383 mut merge: impl FnMut(&K, &V, &V) -> MergeResult<V>,
384 ) -> OrdMap<K, V>
384 ) -> OrdMap<K, V>
385 where
385 where
386 K: Clone + Ord,
386 K: Clone + Ord,
387 V: Clone + PartialEq,
387 V: Clone + PartialEq,
388 {
388 {
389 // (key, value) pairs that would need to be inserted in either map
389 // (key, value) pairs that would need to be inserted in either map
390 // in order to turn it into the union.
390 // in order to turn it into the union.
391 //
391 //
392 // TODO: if/when https://github.com/bodil/im-rs/pull/168 is accepted,
392 // TODO: if/when https://github.com/bodil/im-rs/pull/168 is accepted,
393 // change these from `Vec<(K, V)>` to `Vec<(&K, Cow<V>)>`
393 // change these from `Vec<(K, V)>` to `Vec<(&K, Cow<V>)>`
394 // with `left_updates` only borrowing from `right` and `right_updates` from
394 // with `left_updates` only borrowing from `right` and `right_updates` from
395 // `left`, and with `Cow::Owned` used for `MergeResult::New`.
395 // `left`, and with `Cow::Owned` used for `MergeResult::New`.
396 //
396 //
397 // This would allow moving all `.clone()` calls to after we’ve decided
397 // This would allow moving all `.clone()` calls to after we’ve decided
398 // which of `right_updates` or `left_updates` to use
398 // which of `right_updates` or `left_updates` to use
399 // (value ones becoming `Cow::into_owned`),
399 // (value ones becoming `Cow::into_owned`),
400 // and avoid making clones we don’t end up using.
400 // and avoid making clones we don’t end up using.
401 let mut left_updates = Vec::new();
401 let mut left_updates = Vec::new();
402 let mut right_updates = Vec::new();
402 let mut right_updates = Vec::new();
403
403
404 for difference in left.diff(&right) {
404 for difference in left.diff(&right) {
405 match difference {
405 match difference {
406 DiffItem::Add(key, value) => {
406 DiffItem::Add(key, value) => {
407 left_updates.push((key.clone(), value.clone()))
407 left_updates.push((key.clone(), value.clone()))
408 }
408 }
409 DiffItem::Remove(key, value) => {
409 DiffItem::Remove(key, value) => {
410 right_updates.push((key.clone(), value.clone()))
410 right_updates.push((key.clone(), value.clone()))
411 }
411 }
412 DiffItem::Update {
412 DiffItem::Update {
413 old: (key, left_value),
413 old: (key, left_value),
414 new: (_, right_value),
414 new: (_, right_value),
415 } => match merge(key, left_value, right_value) {
415 } => match merge(key, left_value, right_value) {
416 MergeResult::Left => {
416 MergeResult::Left => {
417 right_updates.push((key.clone(), left_value.clone()))
417 right_updates.push((key.clone(), left_value.clone()))
418 }
418 }
419 MergeResult::Right => {
419 MergeResult::Right => {
420 left_updates.push((key.clone(), right_value.clone()))
420 left_updates.push((key.clone(), right_value.clone()))
421 }
421 }
422 MergeResult::New(new_value) => {
422 MergeResult::New(new_value) => {
423 left_updates.push((key.clone(), new_value.clone()));
423 left_updates.push((key.clone(), new_value.clone()));
424 right_updates.push((key.clone(), new_value))
424 right_updates.push((key.clone(), new_value))
425 }
425 }
426 },
426 },
427 }
427 }
428 }
428 }
429 if left_updates.len() < right_updates.len() {
429 if left_updates.len() < right_updates.len() {
430 for (key, value) in left_updates {
430 for (key, value) in left_updates {
431 left.insert(key, value);
431 left.insert(key, value);
432 }
432 }
433 left
433 left
434 } else {
434 } else {
435 for (key, value) in right_updates {
435 for (key, value) in right_updates {
436 right.insert(key, value);
436 right.insert(key, value);
437 }
437 }
438 right
438 right
439 }
439 }
440 }
440 }
441
441
442 /// Join items of the iterable with the given separator, similar to Python’s
442 /// Join items of the iterable with the given separator, similar to Python’s
443 /// `separator.join(iter)`.
443 /// `separator.join(iter)`.
444 ///
444 ///
445 /// Formatting the return value consumes the iterator.
445 /// Formatting the return value consumes the iterator.
446 /// Formatting it again will produce an empty string.
446 /// Formatting it again will produce an empty string.
447 pub fn join_display(
447 pub fn join_display(
448 iter: impl IntoIterator<Item = impl fmt::Display>,
448 iter: impl IntoIterator<Item = impl fmt::Display>,
449 separator: impl fmt::Display,
449 separator: impl fmt::Display,
450 ) -> impl fmt::Display {
450 ) -> impl fmt::Display {
451 JoinDisplay {
451 JoinDisplay {
452 iter: Cell::new(Some(iter.into_iter())),
452 iter: Cell::new(Some(iter.into_iter())),
453 separator,
453 separator,
454 }
454 }
455 }
455 }
456
456
457 struct JoinDisplay<I, S> {
457 struct JoinDisplay<I, S> {
458 iter: Cell<Option<I>>,
458 iter: Cell<Option<I>>,
459 separator: S,
459 separator: S,
460 }
460 }
461
461
462 impl<I, T, S> fmt::Display for JoinDisplay<I, S>
462 impl<I, T, S> fmt::Display for JoinDisplay<I, S>
463 where
463 where
464 I: Iterator<Item = T>,
464 I: Iterator<Item = T>,
465 T: fmt::Display,
465 T: fmt::Display,
466 S: fmt::Display,
466 S: fmt::Display,
467 {
467 {
468 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
468 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
469 if let Some(mut iter) = self.iter.take() {
469 if let Some(mut iter) = self.iter.take() {
470 if let Some(first) = iter.next() {
470 if let Some(first) = iter.next() {
471 first.fmt(f)?;
471 first.fmt(f)?;
472 }
472 }
473 for value in iter {
473 for value in iter {
474 self.separator.fmt(f)?;
474 self.separator.fmt(f)?;
475 value.fmt(f)?;
475 value.fmt(f)?;
476 }
476 }
477 }
477 }
478 Ok(())
478 Ok(())
479 }
479 }
480 }
480 }
481
481
482 /// Like `Iterator::filter_map`, but over a fallible iterator of `Result`s.
482 /// Like `Iterator::filter_map`, but over a fallible iterator of `Result`s.
483 ///
483 ///
484 /// The callback is only called for incoming `Ok` values. Errors are passed
484 /// The callback is only called for incoming `Ok` values. Errors are passed
485 /// through as-is. In order to let it use the `?` operator the callback is
485 /// through as-is. In order to let it use the `?` operator the callback is
486 /// expected to return a `Result` of `Option`, instead of an `Option` of
486 /// expected to return a `Result` of `Option`, instead of an `Option` of
487 /// `Result`.
487 /// `Result`.
488 pub fn filter_map_results<'a, I, F, A, B, E>(
488 pub fn filter_map_results<'a, I, F, A, B, E>(
489 iter: I,
489 iter: I,
490 f: F,
490 f: F,
491 ) -> impl Iterator<Item = Result<B, E>> + 'a
491 ) -> impl Iterator<Item = Result<B, E>> + 'a
492 where
492 where
493 I: Iterator<Item = Result<A, E>> + 'a,
493 I: Iterator<Item = Result<A, E>> + 'a,
494 F: Fn(A) -> Result<Option<B>, E> + 'a,
494 F: Fn(A) -> Result<Option<B>, E> + 'a,
495 {
495 {
496 iter.filter_map(move |result| match result {
496 iter.filter_map(move |result| match result {
497 Ok(node) => f(node).transpose(),
497 Ok(node) => f(node).transpose(),
498 Err(e) => Some(Err(e)),
498 Err(e) => Some(Err(e)),
499 })
499 })
500 }
500 }
501
501
502 /// Force the global rayon threadpool to not exceed 16 concurrent threads
502 /// Force the global rayon threadpool to not exceed 16 concurrent threads
503 /// unless the user has specified a value.
503 /// unless the user has specified a value.
504 /// This is a stop-gap measure until we figure out why using more than 16
504 /// This is a stop-gap measure until we figure out why using more than 16
505 /// threads makes `status` slower for each additional thread.
505 /// threads makes `status` slower for each additional thread.
506 ///
506 ///
507 /// TODO find the underlying cause and fix it, then remove this.
507 /// TODO find the underlying cause and fix it, then remove this.
508 ///
508 ///
509 /// # Errors
509 /// # Errors
510 ///
510 ///
511 /// Returns an error if the global threadpool has already been initialized if
511 /// Returns an error if the global threadpool has already been initialized if
512 /// we try to initialize it.
512 /// we try to initialize it.
513 pub fn cap_default_rayon_threads() -> Result<(), rayon::ThreadPoolBuildError> {
513 pub fn cap_default_rayon_threads() -> Result<(), rayon::ThreadPoolBuildError> {
514 const THREAD_CAP: usize = 16;
514 const THREAD_CAP: usize = 16;
515
515
516 if std::env::var("RAYON_NUM_THREADS").is_err() {
516 if std::env::var("RAYON_NUM_THREADS").is_err() {
517 let available_parallelism = std::thread::available_parallelism()
517 let available_parallelism = std::thread::available_parallelism()
518 .map(usize::from)
518 .map(usize::from)
519 .unwrap_or(1);
519 .unwrap_or(1);
520 let new_thread_count = THREAD_CAP.min(available_parallelism);
520 let new_thread_count = THREAD_CAP.min(available_parallelism);
521 let res = rayon::ThreadPoolBuilder::new()
521 let res = rayon::ThreadPoolBuilder::new()
522 .num_threads(new_thread_count)
522 .num_threads(new_thread_count)
523 .build_global();
523 .build_global();
524 if res.is_ok() {
524 if res.is_ok() {
525 log::trace!(
525 log::trace!(
526 "Capped the rayon threadpool to {new_thread_count} threads",
526 "Capped the rayon threadpool to {new_thread_count} threads",
527 );
527 );
528 }
528 }
529 return res;
529 return res;
530 }
530 }
531 Ok(())
531 Ok(())
532 }
532 }
@@ -1,812 +1,812 b''
1 // hg_path.rs
1 // hg_path.rs
2 //
2 //
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 //
4 //
5 // This software may be used and distributed according to the terms of the
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 use crate::utils::SliceExt;
8 use crate::utils::SliceExt;
9 use std::borrow::Borrow;
9 use std::borrow::Borrow;
10 use std::borrow::Cow;
10 use std::borrow::Cow;
11 use std::ffi::{OsStr, OsString};
11 use std::ffi::{OsStr, OsString};
12 use std::fmt;
12 use std::fmt;
13 use std::ops::Deref;
13 use std::ops::Deref;
14 use std::path::{Path, PathBuf};
14 use std::path::{Path, PathBuf};
15
15
16 #[derive(Debug, Eq, PartialEq)]
16 #[derive(Debug, Eq, PartialEq)]
17 pub enum HgPathError {
17 pub enum HgPathError {
18 /// Bytes from the invalid `HgPath`
18 /// Bytes from the invalid `HgPath`
19 LeadingSlash(Vec<u8>),
19 LeadingSlash(Vec<u8>),
20 ConsecutiveSlashes {
20 ConsecutiveSlashes {
21 bytes: Vec<u8>,
21 bytes: Vec<u8>,
22 second_slash_index: usize,
22 second_slash_index: usize,
23 },
23 },
24 ContainsNullByte {
24 ContainsNullByte {
25 bytes: Vec<u8>,
25 bytes: Vec<u8>,
26 null_byte_index: usize,
26 null_byte_index: usize,
27 },
27 },
28 /// Bytes
28 /// Bytes
29 DecodeError(Vec<u8>),
29 DecodeError(Vec<u8>),
30 /// The rest come from audit errors
30 /// The rest come from audit errors
31 EndsWithSlash(HgPathBuf),
31 EndsWithSlash(HgPathBuf),
32 ContainsIllegalComponent(HgPathBuf),
32 ContainsIllegalComponent(HgPathBuf),
33 /// Path is inside the `.hg` folder
33 /// Path is inside the `.hg` folder
34 InsideDotHg(HgPathBuf),
34 InsideDotHg(HgPathBuf),
35 IsInsideNestedRepo {
35 IsInsideNestedRepo {
36 path: HgPathBuf,
36 path: HgPathBuf,
37 nested_repo: HgPathBuf,
37 nested_repo: HgPathBuf,
38 },
38 },
39 TraversesSymbolicLink {
39 TraversesSymbolicLink {
40 path: HgPathBuf,
40 path: HgPathBuf,
41 symlink: HgPathBuf,
41 symlink: HgPathBuf,
42 },
42 },
43 NotFsCompliant(HgPathBuf),
43 NotFsCompliant(HgPathBuf),
44 /// `path` is the smallest invalid path
44 /// `path` is the smallest invalid path
45 NotUnderRoot {
45 NotUnderRoot {
46 path: PathBuf,
46 path: PathBuf,
47 root: PathBuf,
47 root: PathBuf,
48 },
48 },
49 }
49 }
50
50
51 impl fmt::Display for HgPathError {
51 impl fmt::Display for HgPathError {
52 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
52 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
53 match self {
53 match self {
54 HgPathError::LeadingSlash(bytes) => {
54 HgPathError::LeadingSlash(bytes) => {
55 write!(f, "Invalid HgPath '{:?}': has a leading slash.", bytes)
55 write!(f, "Invalid HgPath '{:?}': has a leading slash.", bytes)
56 }
56 }
57 HgPathError::ConsecutiveSlashes {
57 HgPathError::ConsecutiveSlashes {
58 bytes,
58 bytes,
59 second_slash_index: pos,
59 second_slash_index: pos,
60 } => write!(
60 } => write!(
61 f,
61 f,
62 "Invalid HgPath '{:?}': consecutive slashes at pos {}.",
62 "Invalid HgPath '{:?}': consecutive slashes at pos {}.",
63 bytes, pos
63 bytes, pos
64 ),
64 ),
65 HgPathError::ContainsNullByte {
65 HgPathError::ContainsNullByte {
66 bytes,
66 bytes,
67 null_byte_index: pos,
67 null_byte_index: pos,
68 } => write!(
68 } => write!(
69 f,
69 f,
70 "Invalid HgPath '{:?}': contains null byte at pos {}.",
70 "Invalid HgPath '{:?}': contains null byte at pos {}.",
71 bytes, pos
71 bytes, pos
72 ),
72 ),
73 HgPathError::DecodeError(bytes) => write!(
73 HgPathError::DecodeError(bytes) => write!(
74 f,
74 f,
75 "Invalid HgPath '{:?}': could not be decoded.",
75 "Invalid HgPath '{:?}': could not be decoded.",
76 bytes
76 bytes
77 ),
77 ),
78 HgPathError::EndsWithSlash(path) => {
78 HgPathError::EndsWithSlash(path) => {
79 write!(f, "Audit failed for '{}': ends with a slash.", path)
79 write!(f, "Audit failed for '{}': ends with a slash.", path)
80 }
80 }
81 HgPathError::ContainsIllegalComponent(path) => write!(
81 HgPathError::ContainsIllegalComponent(path) => write!(
82 f,
82 f,
83 "Audit failed for '{}': contains an illegal component.",
83 "Audit failed for '{}': contains an illegal component.",
84 path
84 path
85 ),
85 ),
86 HgPathError::InsideDotHg(path) => write!(
86 HgPathError::InsideDotHg(path) => write!(
87 f,
87 f,
88 "Audit failed for '{}': is inside the '.hg' folder.",
88 "Audit failed for '{}': is inside the '.hg' folder.",
89 path
89 path
90 ),
90 ),
91 HgPathError::IsInsideNestedRepo {
91 HgPathError::IsInsideNestedRepo {
92 path,
92 path,
93 nested_repo: nested,
93 nested_repo: nested,
94 } => {
94 } => {
95 write!(f,
95 write!(f,
96 "Audit failed for '{}': is inside a nested repository '{}'.",
96 "Audit failed for '{}': is inside a nested repository '{}'.",
97 path, nested
97 path, nested
98 )
98 )
99 }
99 }
100 HgPathError::TraversesSymbolicLink { path, symlink } => write!(
100 HgPathError::TraversesSymbolicLink { path, symlink } => write!(
101 f,
101 f,
102 "Audit failed for '{}': traverses symbolic link '{}'.",
102 "Audit failed for '{}': traverses symbolic link '{}'.",
103 path, symlink
103 path, symlink
104 ),
104 ),
105 HgPathError::NotFsCompliant(path) => write!(
105 HgPathError::NotFsCompliant(path) => write!(
106 f,
106 f,
107 "Audit failed for '{}': cannot be turned into a \
107 "Audit failed for '{}': cannot be turned into a \
108 filesystem path.",
108 filesystem path.",
109 path
109 path
110 ),
110 ),
111 HgPathError::NotUnderRoot { path, root } => write!(
111 HgPathError::NotUnderRoot { path, root } => write!(
112 f,
112 f,
113 "Audit failed for '{}': not under root {}.",
113 "Audit failed for '{}': not under root {}.",
114 path.display(),
114 path.display(),
115 root.display()
115 root.display()
116 ),
116 ),
117 }
117 }
118 }
118 }
119 }
119 }
120
120
121 impl From<HgPathError> for std::io::Error {
121 impl From<HgPathError> for std::io::Error {
122 fn from(e: HgPathError) -> Self {
122 fn from(e: HgPathError) -> Self {
123 std::io::Error::new(std::io::ErrorKind::InvalidData, e.to_string())
123 std::io::Error::new(std::io::ErrorKind::InvalidData, e.to_string())
124 }
124 }
125 }
125 }
126
126
127 /// This is a repository-relative path (or canonical path):
127 /// This is a repository-relative path (or canonical path):
128 /// - no null characters
128 /// - no null characters
129 /// - `/` separates directories
129 /// - `/` separates directories
130 /// - no consecutive slashes
130 /// - no consecutive slashes
131 /// - no leading slash,
131 /// - no leading slash,
132 /// - no `.` nor `..` of special meaning
132 /// - no `.` nor `..` of special meaning
133 /// - stored in repository and shared across platforms
133 /// - stored in repository and shared across platforms
134 ///
134 ///
135 /// Note: there is no guarantee of any `HgPath` being well-formed at any point
135 /// Note: there is no guarantee of any `HgPath` being well-formed at any point
136 /// in its lifetime for performance reasons and to ease ergonomics. It is
136 /// in its lifetime for performance reasons and to ease ergonomics. It is
137 /// however checked using the `check_state` method before any file-system
137 /// however checked using the `check_state` method before any file-system
138 /// operation.
138 /// operation.
139 ///
139 ///
140 /// This allows us to be encoding-transparent as much as possible, until really
140 /// This allows us to be encoding-transparent as much as possible, until really
141 /// needed; `HgPath` can be transformed into a platform-specific path (`OsStr`
141 /// needed; `HgPath` can be transformed into a platform-specific path (`OsStr`
142 /// or `Path`) whenever more complex operations are needed:
142 /// or `Path`) whenever more complex operations are needed:
143 /// On Unix, it's just byte-to-byte conversion. On Windows, it has to be
143 /// On Unix, it's just byte-to-byte conversion. On Windows, it has to be
144 /// decoded from MBCS to WTF-8. If WindowsUTF8Plan is implemented, the source
144 /// decoded from MBCS to WTF-8. If WindowsUTF8Plan is implemented, the source
145 /// character encoding will be determined on a per-repository basis.
145 /// character encoding will be determined on a per-repository basis.
146 #[derive(Eq, Ord, PartialEq, PartialOrd, Hash)]
146 #[derive(Eq, Ord, PartialEq, PartialOrd, Hash)]
147 #[repr(transparent)]
147 #[repr(transparent)]
148 pub struct HgPath {
148 pub struct HgPath {
149 inner: [u8],
149 inner: [u8],
150 }
150 }
151
151
152 impl HgPath {
152 impl HgPath {
153 pub fn new<S: AsRef<[u8]> + ?Sized>(s: &S) -> &Self {
153 pub fn new<S: AsRef<[u8]> + ?Sized>(s: &S) -> &Self {
154 unsafe { &*(s.as_ref() as *const [u8] as *const Self) }
154 unsafe { &*(s.as_ref() as *const [u8] as *const Self) }
155 }
155 }
156 pub fn is_empty(&self) -> bool {
156 pub fn is_empty(&self) -> bool {
157 self.inner.is_empty()
157 self.inner.is_empty()
158 }
158 }
159 pub fn len(&self) -> usize {
159 pub fn len(&self) -> usize {
160 self.inner.len()
160 self.inner.len()
161 }
161 }
162 fn to_hg_path_buf(&self) -> HgPathBuf {
162 fn to_hg_path_buf(&self) -> HgPathBuf {
163 HgPathBuf {
163 HgPathBuf {
164 inner: self.inner.to_owned(),
164 inner: self.inner.to_owned(),
165 }
165 }
166 }
166 }
167 pub fn bytes(&self) -> std::slice::Iter<u8> {
167 pub fn bytes(&self) -> std::slice::Iter<u8> {
168 self.inner.iter()
168 self.inner.iter()
169 }
169 }
170 pub fn to_ascii_uppercase(&self) -> HgPathBuf {
170 pub fn to_ascii_uppercase(&self) -> HgPathBuf {
171 HgPathBuf::from(self.inner.to_ascii_uppercase())
171 HgPathBuf::from(self.inner.to_ascii_uppercase())
172 }
172 }
173 pub fn to_ascii_lowercase(&self) -> HgPathBuf {
173 pub fn to_ascii_lowercase(&self) -> HgPathBuf {
174 HgPathBuf::from(self.inner.to_ascii_lowercase())
174 HgPathBuf::from(self.inner.to_ascii_lowercase())
175 }
175 }
176 pub fn as_bytes(&self) -> &[u8] {
176 pub fn as_bytes(&self) -> &[u8] {
177 &self.inner
177 &self.inner
178 }
178 }
179 pub fn contains(&self, other: u8) -> bool {
179 pub fn contains(&self, other: u8) -> bool {
180 self.inner.contains(&other)
180 self.inner.contains(&other)
181 }
181 }
182 pub fn starts_with(&self, needle: impl AsRef<Self>) -> bool {
182 pub fn starts_with(&self, needle: impl AsRef<Self>) -> bool {
183 self.inner.starts_with(needle.as_ref().as_bytes())
183 self.inner.starts_with(needle.as_ref().as_bytes())
184 }
184 }
185 pub fn trim_trailing_slash(&self) -> &Self {
185 pub fn trim_trailing_slash(&self) -> &Self {
186 Self::new(if self.inner.last() == Some(&b'/') {
186 Self::new(if self.inner.last() == Some(&b'/') {
187 &self.inner[..self.inner.len() - 1]
187 &self.inner[..self.inner.len() - 1]
188 } else {
188 } else {
189 &self.inner[..]
189 &self.inner[..]
190 })
190 })
191 }
191 }
192 /// Returns a tuple of slices `(base, filename)` resulting from the split
192 /// Returns a tuple of slices `(base, filename)` resulting from the split
193 /// at the rightmost `/`, if any.
193 /// at the rightmost `/`, if any.
194 ///
194 ///
195 /// # Examples:
195 /// # Examples:
196 ///
196 ///
197 /// ```
197 /// ```
198 /// use hg::utils::hg_path::HgPath;
198 /// use hg::utils::hg_path::HgPath;
199 ///
199 ///
200 /// let path = HgPath::new(b"cool/hg/path").split_filename();
200 /// let path = HgPath::new(b"cool/hg/path").split_filename();
201 /// assert_eq!(path, (HgPath::new(b"cool/hg"), HgPath::new(b"path")));
201 /// assert_eq!(path, (HgPath::new(b"cool/hg"), HgPath::new(b"path")));
202 ///
202 ///
203 /// let path = HgPath::new(b"pathwithoutsep").split_filename();
203 /// let path = HgPath::new(b"pathwithoutsep").split_filename();
204 /// assert_eq!(path, (HgPath::new(b""), HgPath::new(b"pathwithoutsep")));
204 /// assert_eq!(path, (HgPath::new(b""), HgPath::new(b"pathwithoutsep")));
205 /// ```
205 /// ```
206 pub fn split_filename(&self) -> (&Self, &Self) {
206 pub fn split_filename(&self) -> (&Self, &Self) {
207 match &self.inner.iter().rposition(|c| *c == b'/') {
207 match &self.inner.iter().rposition(|c| *c == b'/') {
208 None => (HgPath::new(""), self),
208 None => (HgPath::new(""), self),
209 Some(size) => (
209 Some(size) => (
210 HgPath::new(&self.inner[..*size]),
210 HgPath::new(&self.inner[..*size]),
211 HgPath::new(&self.inner[*size + 1..]),
211 HgPath::new(&self.inner[*size + 1..]),
212 ),
212 ),
213 }
213 }
214 }
214 }
215
215
216 pub fn join(&self, path: &HgPath) -> HgPathBuf {
216 pub fn join(&self, path: &HgPath) -> HgPathBuf {
217 let mut buf = self.to_owned();
217 let mut buf = self.to_owned();
218 buf.push(path);
218 buf.push(path);
219 buf
219 buf
220 }
220 }
221
221
222 pub fn components(&self) -> impl Iterator<Item = &HgPath> {
222 pub fn components(&self) -> impl Iterator<Item = &HgPath> {
223 self.inner.split(|&byte| byte == b'/').map(HgPath::new)
223 self.inner.split(|&byte| byte == b'/').map(HgPath::new)
224 }
224 }
225
225
226 /// Returns the first (that is "root-most") slash-separated component of
226 /// Returns the first (that is "root-most") slash-separated component of
227 /// the path, and the rest after the first slash if there is one.
227 /// the path, and the rest after the first slash if there is one.
228 pub fn split_first_component(&self) -> (&HgPath, Option<&HgPath>) {
228 pub fn split_first_component(&self) -> (&HgPath, Option<&HgPath>) {
229 match self.inner.split_2(b'/') {
229 match self.inner.split_2(b'/') {
230 Some((a, b)) => (HgPath::new(a), Some(HgPath::new(b))),
230 Some((a, b)) => (HgPath::new(a), Some(HgPath::new(b))),
231 None => (self, None),
231 None => (self, None),
232 }
232 }
233 }
233 }
234
234
235 pub fn parent(&self) -> &Self {
235 pub fn parent(&self) -> &Self {
236 let inner = self.as_bytes();
236 let inner = self.as_bytes();
237 HgPath::new(match inner.iter().rposition(|b| *b == b'/') {
237 HgPath::new(match inner.iter().rposition(|b| *b == b'/') {
238 Some(pos) => &inner[..pos],
238 Some(pos) => &inner[..pos],
239 None => &[],
239 None => &[],
240 })
240 })
241 }
241 }
242 /// Given a base directory, returns the slice of `self` relative to the
242 /// Given a base directory, returns the slice of `self` relative to the
243 /// base directory. If `base` is not a directory (does not end with a
243 /// base directory. If `base` is not a directory (does not end with a
244 /// `b'/'`), returns `None`.
244 /// `b'/'`), returns `None`.
245 pub fn relative_to(&self, base: impl AsRef<Self>) -> Option<&Self> {
245 pub fn relative_to(&self, base: impl AsRef<Self>) -> Option<&Self> {
246 let base = base.as_ref();
246 let base = base.as_ref();
247 if base.is_empty() {
247 if base.is_empty() {
248 return Some(self);
248 return Some(self);
249 }
249 }
250 let is_dir = base.as_bytes().ends_with(b"/");
250 let is_dir = base.as_bytes().ends_with(b"/");
251 if is_dir && self.starts_with(base) {
251 if is_dir && self.starts_with(base) {
252 Some(Self::new(&self.inner[base.len()..]))
252 Some(Self::new(&self.inner[base.len()..]))
253 } else {
253 } else {
254 None
254 None
255 }
255 }
256 }
256 }
257
257
258 #[cfg(windows)]
258 #[cfg(windows)]
259 /// Copied from the Python stdlib's `os.path.splitdrive` implementation.
259 /// Copied from the Python stdlib's `os.path.splitdrive` implementation.
260 ///
260 ///
261 /// Split a pathname into drive/UNC sharepoint and relative path
261 /// Split a pathname into drive/UNC sharepoint and relative path
262 /// specifiers. Returns a 2-tuple (drive_or_unc, path); either part may
262 /// specifiers. Returns a 2-tuple (drive_or_unc, path); either part may
263 /// be empty.
263 /// be empty.
264 ///
264 ///
265 /// If you assign
265 /// If you assign
266 /// result = split_drive(p)
266 /// result = split_drive(p)
267 /// It is always true that:
267 /// It is always true that:
268 /// result[0] + result[1] == p
268 /// result[0] + result[1] == p
269 ///
269 ///
270 /// If the path contained a drive letter, drive_or_unc will contain
270 /// If the path contained a drive letter, drive_or_unc will contain
271 /// everything up to and including the colon.
271 /// everything up to and including the colon.
272 /// e.g. split_drive("c:/dir") returns ("c:", "/dir")
272 /// e.g. split_drive("c:/dir") returns ("c:", "/dir")
273 ///
273 ///
274 /// If the path contained a UNC path, the drive_or_unc will contain the
274 /// If the path contained a UNC path, the drive_or_unc will contain the
275 /// host name and share up to but not including the fourth directory
275 /// host name and share up to but not including the fourth directory
276 /// separator character.
276 /// separator character.
277 /// e.g. split_drive("//host/computer/dir") returns ("//host/computer",
277 /// e.g. split_drive("//host/computer/dir") returns ("//host/computer",
278 /// "/dir")
278 /// "/dir")
279 ///
279 ///
280 /// Paths cannot contain both a drive letter and a UNC path.
280 /// Paths cannot contain both a drive letter and a UNC path.
281 pub fn split_drive<'a>(&self) -> (&HgPath, &HgPath) {
281 pub fn split_drive<'a>(&self) -> (&HgPath, &HgPath) {
282 let bytes = self.as_bytes();
282 let bytes = self.as_bytes();
283 let is_sep = |b| std::path::is_separator(b as char);
283 let is_sep = |b| std::path::is_separator(b as char);
284
284
285 if self.len() < 2 {
285 if self.len() < 2 {
286 (HgPath::new(b""), &self)
286 (HgPath::new(b""), &self)
287 } else if is_sep(bytes[0])
287 } else if is_sep(bytes[0])
288 && is_sep(bytes[1])
288 && is_sep(bytes[1])
289 && (self.len() == 2 || !is_sep(bytes[2]))
289 && (self.len() == 2 || !is_sep(bytes[2]))
290 {
290 {
291 // Is a UNC path:
291 // Is a UNC path:
292 // vvvvvvvvvvvvvvvvvvvv drive letter or UNC path
292 // vvvvvvvvvvvvvvvvvvvv drive letter or UNC path
293 // \\machine\mountpoint\directory\etc\...
293 // \\machine\mountpoint\directory\etc\...
294 // directory ^^^^^^^^^^^^^^^
294 // directory ^^^^^^^^^^^^^^^
295
295
296 let machine_end_index = bytes[2..].iter().position(|b| is_sep(*b));
296 let machine_end_index = bytes[2..].iter().position(|b| is_sep(*b));
297 let mountpoint_start_index = if let Some(i) = machine_end_index {
297 let mountpoint_start_index = if let Some(i) = machine_end_index {
298 i + 2
298 i + 2
299 } else {
299 } else {
300 return (HgPath::new(b""), &self);
300 return (HgPath::new(b""), &self);
301 };
301 };
302
302
303 match bytes[mountpoint_start_index + 1..]
303 match bytes[mountpoint_start_index + 1..]
304 .iter()
304 .iter()
305 .position(|b| is_sep(*b))
305 .position(|b| is_sep(*b))
306 {
306 {
307 // A UNC path can't have two slashes in a row
307 // A UNC path can't have two slashes in a row
308 // (after the initial two)
308 // (after the initial two)
309 Some(0) => (HgPath::new(b""), &self),
309 Some(0) => (HgPath::new(b""), &self),
310 Some(i) => {
310 Some(i) => {
311 let (a, b) =
311 let (a, b) =
312 bytes.split_at(mountpoint_start_index + 1 + i);
312 bytes.split_at(mountpoint_start_index + 1 + i);
313 (HgPath::new(a), HgPath::new(b))
313 (HgPath::new(a), HgPath::new(b))
314 }
314 }
315 None => (&self, HgPath::new(b"")),
315 None => (&self, HgPath::new(b"")),
316 }
316 }
317 } else if bytes[1] == b':' {
317 } else if bytes[1] == b':' {
318 // Drive path c:\directory
318 // Drive path c:\directory
319 let (a, b) = bytes.split_at(2);
319 let (a, b) = bytes.split_at(2);
320 (HgPath::new(a), HgPath::new(b))
320 (HgPath::new(a), HgPath::new(b))
321 } else {
321 } else {
322 (HgPath::new(b""), &self)
322 (HgPath::new(b""), &self)
323 }
323 }
324 }
324 }
325
325
326 #[cfg(unix)]
326 #[cfg(unix)]
327 /// Split a pathname into drive and path. On Posix, drive is always empty.
327 /// Split a pathname into drive and path. On Posix, drive is always empty.
328 pub fn split_drive(&self) -> (&HgPath, &HgPath) {
328 pub fn split_drive(&self) -> (&HgPath, &HgPath) {
329 (HgPath::new(b""), self)
329 (HgPath::new(b""), self)
330 }
330 }
331
331
332 /// Checks for errors in the path, short-circuiting at the first one.
332 /// Checks for errors in the path, short-circuiting at the first one.
333 /// This generates fine-grained errors useful for debugging.
333 /// This generates fine-grained errors useful for debugging.
334 /// To simply check if the path is valid during tests, use `is_valid`.
334 /// To simply check if the path is valid during tests, use `is_valid`.
335 pub fn check_state(&self) -> Result<(), HgPathError> {
335 pub fn check_state(&self) -> Result<(), HgPathError> {
336 if self.is_empty() {
336 if self.is_empty() {
337 return Ok(());
337 return Ok(());
338 }
338 }
339 let bytes = self.as_bytes();
339 let bytes = self.as_bytes();
340 let mut previous_byte = None;
340 let mut previous_byte = None;
341
341
342 if bytes[0] == b'/' {
342 if bytes[0] == b'/' {
343 return Err(HgPathError::LeadingSlash(bytes.to_vec()));
343 return Err(HgPathError::LeadingSlash(bytes.to_vec()));
344 }
344 }
345 for (index, byte) in bytes.iter().enumerate() {
345 for (index, byte) in bytes.iter().enumerate() {
346 match byte {
346 match byte {
347 0 => {
347 0 => {
348 return Err(HgPathError::ContainsNullByte {
348 return Err(HgPathError::ContainsNullByte {
349 bytes: bytes.to_vec(),
349 bytes: bytes.to_vec(),
350 null_byte_index: index,
350 null_byte_index: index,
351 })
351 })
352 }
352 }
353 b'/' => {
353 b'/' => {
354 if previous_byte.is_some() && previous_byte == Some(b'/') {
354 if previous_byte.is_some() && previous_byte == Some(b'/') {
355 return Err(HgPathError::ConsecutiveSlashes {
355 return Err(HgPathError::ConsecutiveSlashes {
356 bytes: bytes.to_vec(),
356 bytes: bytes.to_vec(),
357 second_slash_index: index,
357 second_slash_index: index,
358 });
358 });
359 }
359 }
360 }
360 }
361 _ => (),
361 _ => (),
362 };
362 };
363 previous_byte = Some(*byte);
363 previous_byte = Some(*byte);
364 }
364 }
365 Ok(())
365 Ok(())
366 }
366 }
367
367
368 #[cfg(test)]
368 #[cfg(test)]
369 /// Only usable during tests to force developers to handle invalid states
369 /// Only usable during tests to force developers to handle invalid states
370 fn is_valid(&self) -> bool {
370 fn is_valid(&self) -> bool {
371 self.check_state().is_ok()
371 self.check_state().is_ok()
372 }
372 }
373 }
373 }
374
374
375 impl fmt::Debug for HgPath {
375 impl fmt::Debug for HgPath {
376 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
376 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
377 write!(f, "HgPath({:?})", String::from_utf8_lossy(&self.inner))
377 write!(f, "HgPath({:?})", String::from_utf8_lossy(&self.inner))
378 }
378 }
379 }
379 }
380
380
381 impl fmt::Display for HgPath {
381 impl fmt::Display for HgPath {
382 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
382 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
383 write!(f, "{}", String::from_utf8_lossy(&self.inner))
383 write!(f, "{}", String::from_utf8_lossy(&self.inner))
384 }
384 }
385 }
385 }
386
386
387 #[derive(
387 #[derive(
388 Default, Eq, Ord, Clone, PartialEq, PartialOrd, Hash, derive_more::From,
388 Default, Eq, Ord, Clone, PartialEq, PartialOrd, Hash, derive_more::From,
389 )]
389 )]
390 pub struct HgPathBuf {
390 pub struct HgPathBuf {
391 inner: Vec<u8>,
391 inner: Vec<u8>,
392 }
392 }
393
393
394 impl HgPathBuf {
394 impl HgPathBuf {
395 pub fn new() -> Self {
395 pub fn new() -> Self {
396 Default::default()
396 Default::default()
397 }
397 }
398
398
399 pub fn push<T: ?Sized + AsRef<HgPath>>(&mut self, other: &T) {
399 pub fn push<T: ?Sized + AsRef<HgPath>>(&mut self, other: &T) {
400 if !self.inner.is_empty() && self.inner.last() != Some(&b'/') {
400 if !self.inner.is_empty() && self.inner.last() != Some(&b'/') {
401 self.inner.push(b'/');
401 self.inner.push(b'/');
402 }
402 }
403 self.inner.extend(other.as_ref().bytes())
403 self.inner.extend(other.as_ref().bytes())
404 }
404 }
405
405
406 pub fn push_byte(&mut self, byte: u8) {
406 pub fn push_byte(&mut self, byte: u8) {
407 self.inner.push(byte);
407 self.inner.push(byte);
408 }
408 }
409 pub fn from_bytes(s: &[u8]) -> HgPathBuf {
409 pub fn from_bytes(s: &[u8]) -> HgPathBuf {
410 HgPath::new(s).to_owned()
410 HgPath::new(s).to_owned()
411 }
411 }
412 pub fn into_vec(self) -> Vec<u8> {
412 pub fn into_vec(self) -> Vec<u8> {
413 self.inner
413 self.inner
414 }
414 }
415 }
415 }
416
416
417 impl fmt::Debug for HgPathBuf {
417 impl fmt::Debug for HgPathBuf {
418 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
418 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
419 write!(f, "HgPathBuf({:?})", String::from_utf8_lossy(&self.inner))
419 write!(f, "HgPathBuf({:?})", String::from_utf8_lossy(&self.inner))
420 }
420 }
421 }
421 }
422
422
423 impl fmt::Display for HgPathBuf {
423 impl fmt::Display for HgPathBuf {
424 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
424 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
425 write!(f, "{}", String::from_utf8_lossy(&self.inner))
425 write!(f, "{}", String::from_utf8_lossy(&self.inner))
426 }
426 }
427 }
427 }
428
428
429 impl Deref for HgPathBuf {
429 impl Deref for HgPathBuf {
430 type Target = HgPath;
430 type Target = HgPath;
431
431
432 #[inline]
432 #[inline]
433 fn deref(&self) -> &HgPath {
433 fn deref(&self) -> &HgPath {
434 HgPath::new(&self.inner)
434 HgPath::new(&self.inner)
435 }
435 }
436 }
436 }
437
437
438 impl<T: ?Sized + AsRef<HgPath>> From<&T> for HgPathBuf {
438 impl<T: ?Sized + AsRef<HgPath>> From<&T> for HgPathBuf {
439 fn from(s: &T) -> HgPathBuf {
439 fn from(s: &T) -> HgPathBuf {
440 s.as_ref().to_owned()
440 s.as_ref().to_owned()
441 }
441 }
442 }
442 }
443
443
444 impl From<HgPathBuf> for Vec<u8> {
444 impl From<HgPathBuf> for Vec<u8> {
445 fn from(val: HgPathBuf) -> Self {
445 fn from(val: HgPathBuf) -> Self {
446 val.inner
446 val.inner
447 }
447 }
448 }
448 }
449
449
450 impl Borrow<HgPath> for HgPathBuf {
450 impl Borrow<HgPath> for HgPathBuf {
451 fn borrow(&self) -> &HgPath {
451 fn borrow(&self) -> &HgPath {
452 HgPath::new(self.as_bytes())
452 HgPath::new(self.as_bytes())
453 }
453 }
454 }
454 }
455
455
456 impl ToOwned for HgPath {
456 impl ToOwned for HgPath {
457 type Owned = HgPathBuf;
457 type Owned = HgPathBuf;
458
458
459 fn to_owned(&self) -> HgPathBuf {
459 fn to_owned(&self) -> HgPathBuf {
460 self.to_hg_path_buf()
460 self.to_hg_path_buf()
461 }
461 }
462 }
462 }
463
463
464 impl AsRef<HgPath> for HgPath {
464 impl AsRef<HgPath> for HgPath {
465 fn as_ref(&self) -> &HgPath {
465 fn as_ref(&self) -> &HgPath {
466 self
466 self
467 }
467 }
468 }
468 }
469
469
470 impl AsRef<HgPath> for HgPathBuf {
470 impl AsRef<HgPath> for HgPathBuf {
471 fn as_ref(&self) -> &HgPath {
471 fn as_ref(&self) -> &HgPath {
472 self
472 self
473 }
473 }
474 }
474 }
475
475
476 impl Extend<u8> for HgPathBuf {
476 impl Extend<u8> for HgPathBuf {
477 fn extend<T: IntoIterator<Item = u8>>(&mut self, iter: T) {
477 fn extend<T: IntoIterator<Item = u8>>(&mut self, iter: T) {
478 self.inner.extend(iter);
478 self.inner.extend(iter);
479 }
479 }
480 }
480 }
481
481
482 /// TODO: Once https://www.mercurial-scm.org/wiki/WindowsUTF8Plan is
482 /// TODO: Once <https://www.mercurial-scm.org/wiki/WindowsUTF8Plan> is
483 /// implemented, these conversion utils will have to work differently depending
483 /// implemented, these conversion utils will have to work differently depending
484 /// on the repository encoding: either `UTF-8` or `MBCS`.
484 /// on the repository encoding: either `UTF-8` or `MBCS`.
485
485
486 pub fn hg_path_to_os_string<P: AsRef<HgPath>>(
486 pub fn hg_path_to_os_string<P: AsRef<HgPath>>(
487 hg_path: P,
487 hg_path: P,
488 ) -> Result<OsString, HgPathError> {
488 ) -> Result<OsString, HgPathError> {
489 hg_path.as_ref().check_state()?;
489 hg_path.as_ref().check_state()?;
490 let os_str;
490 let os_str;
491 #[cfg(unix)]
491 #[cfg(unix)]
492 {
492 {
493 use std::os::unix::ffi::OsStrExt;
493 use std::os::unix::ffi::OsStrExt;
494 os_str = std::ffi::OsStr::from_bytes(hg_path.as_ref().as_bytes());
494 os_str = std::ffi::OsStr::from_bytes(hg_path.as_ref().as_bytes());
495 }
495 }
496 // TODO Handle other platforms
496 // TODO Handle other platforms
497 // TODO: convert from WTF8 to Windows MBCS (ANSI encoding).
497 // TODO: convert from WTF8 to Windows MBCS (ANSI encoding).
498 Ok(os_str.to_os_string())
498 Ok(os_str.to_os_string())
499 }
499 }
500
500
501 pub fn hg_path_to_path_buf<P: AsRef<HgPath>>(
501 pub fn hg_path_to_path_buf<P: AsRef<HgPath>>(
502 hg_path: P,
502 hg_path: P,
503 ) -> Result<PathBuf, HgPathError> {
503 ) -> Result<PathBuf, HgPathError> {
504 Ok(Path::new(&hg_path_to_os_string(hg_path)?).to_path_buf())
504 Ok(Path::new(&hg_path_to_os_string(hg_path)?).to_path_buf())
505 }
505 }
506
506
507 pub fn os_string_to_hg_path_buf<S: AsRef<OsStr>>(
507 pub fn os_string_to_hg_path_buf<S: AsRef<OsStr>>(
508 os_string: S,
508 os_string: S,
509 ) -> Result<HgPathBuf, HgPathError> {
509 ) -> Result<HgPathBuf, HgPathError> {
510 let buf;
510 let buf;
511 #[cfg(unix)]
511 #[cfg(unix)]
512 {
512 {
513 use std::os::unix::ffi::OsStrExt;
513 use std::os::unix::ffi::OsStrExt;
514 buf = HgPathBuf::from_bytes(os_string.as_ref().as_bytes());
514 buf = HgPathBuf::from_bytes(os_string.as_ref().as_bytes());
515 }
515 }
516 // TODO Handle other platforms
516 // TODO Handle other platforms
517 // TODO: convert from WTF8 to Windows MBCS (ANSI encoding).
517 // TODO: convert from WTF8 to Windows MBCS (ANSI encoding).
518
518
519 buf.check_state()?;
519 buf.check_state()?;
520 Ok(buf)
520 Ok(buf)
521 }
521 }
522
522
523 pub fn path_to_hg_path_buf<P: AsRef<Path>>(
523 pub fn path_to_hg_path_buf<P: AsRef<Path>>(
524 path: P,
524 path: P,
525 ) -> Result<HgPathBuf, HgPathError> {
525 ) -> Result<HgPathBuf, HgPathError> {
526 let buf;
526 let buf;
527 let os_str = path.as_ref().as_os_str();
527 let os_str = path.as_ref().as_os_str();
528 #[cfg(unix)]
528 #[cfg(unix)]
529 {
529 {
530 use std::os::unix::ffi::OsStrExt;
530 use std::os::unix::ffi::OsStrExt;
531 buf = HgPathBuf::from_bytes(os_str.as_bytes());
531 buf = HgPathBuf::from_bytes(os_str.as_bytes());
532 }
532 }
533 // TODO Handle other platforms
533 // TODO Handle other platforms
534 // TODO: convert from WTF8 to Windows MBCS (ANSI encoding).
534 // TODO: convert from WTF8 to Windows MBCS (ANSI encoding).
535
535
536 buf.check_state()?;
536 buf.check_state()?;
537 Ok(buf)
537 Ok(buf)
538 }
538 }
539
539
540 impl TryFrom<PathBuf> for HgPathBuf {
540 impl TryFrom<PathBuf> for HgPathBuf {
541 type Error = HgPathError;
541 type Error = HgPathError;
542 fn try_from(path: PathBuf) -> Result<Self, Self::Error> {
542 fn try_from(path: PathBuf) -> Result<Self, Self::Error> {
543 path_to_hg_path_buf(path)
543 path_to_hg_path_buf(path)
544 }
544 }
545 }
545 }
546
546
547 impl From<HgPathBuf> for Cow<'_, HgPath> {
547 impl From<HgPathBuf> for Cow<'_, HgPath> {
548 fn from(path: HgPathBuf) -> Self {
548 fn from(path: HgPathBuf) -> Self {
549 Cow::Owned(path)
549 Cow::Owned(path)
550 }
550 }
551 }
551 }
552
552
553 impl<'a> From<&'a HgPath> for Cow<'a, HgPath> {
553 impl<'a> From<&'a HgPath> for Cow<'a, HgPath> {
554 fn from(path: &'a HgPath) -> Self {
554 fn from(path: &'a HgPath) -> Self {
555 Cow::Borrowed(path)
555 Cow::Borrowed(path)
556 }
556 }
557 }
557 }
558
558
559 impl<'a> From<&'a HgPathBuf> for Cow<'a, HgPath> {
559 impl<'a> From<&'a HgPathBuf> for Cow<'a, HgPath> {
560 fn from(path: &'a HgPathBuf) -> Self {
560 fn from(path: &'a HgPathBuf) -> Self {
561 Cow::Borrowed(&**path)
561 Cow::Borrowed(&**path)
562 }
562 }
563 }
563 }
564
564
565 #[cfg(test)]
565 #[cfg(test)]
566 mod tests {
566 mod tests {
567 use super::*;
567 use super::*;
568 use pretty_assertions::assert_eq;
568 use pretty_assertions::assert_eq;
569
569
570 #[test]
570 #[test]
571 fn test_path_states() {
571 fn test_path_states() {
572 assert_eq!(
572 assert_eq!(
573 Err(HgPathError::LeadingSlash(b"/".to_vec())),
573 Err(HgPathError::LeadingSlash(b"/".to_vec())),
574 HgPath::new(b"/").check_state()
574 HgPath::new(b"/").check_state()
575 );
575 );
576 assert_eq!(
576 assert_eq!(
577 Err(HgPathError::ConsecutiveSlashes {
577 Err(HgPathError::ConsecutiveSlashes {
578 bytes: b"a/b//c".to_vec(),
578 bytes: b"a/b//c".to_vec(),
579 second_slash_index: 4
579 second_slash_index: 4
580 }),
580 }),
581 HgPath::new(b"a/b//c").check_state()
581 HgPath::new(b"a/b//c").check_state()
582 );
582 );
583 assert_eq!(
583 assert_eq!(
584 Err(HgPathError::ContainsNullByte {
584 Err(HgPathError::ContainsNullByte {
585 bytes: b"a/b/\0c".to_vec(),
585 bytes: b"a/b/\0c".to_vec(),
586 null_byte_index: 4
586 null_byte_index: 4
587 }),
587 }),
588 HgPath::new(b"a/b/\0c").check_state()
588 HgPath::new(b"a/b/\0c").check_state()
589 );
589 );
590 // TODO test HgPathError::DecodeError for the Windows implementation.
590 // TODO test HgPathError::DecodeError for the Windows implementation.
591 assert_eq!(true, HgPath::new(b"").is_valid());
591 assert_eq!(true, HgPath::new(b"").is_valid());
592 assert_eq!(true, HgPath::new(b"a/b/c").is_valid());
592 assert_eq!(true, HgPath::new(b"a/b/c").is_valid());
593 // Backslashes in paths are not significant, but allowed
593 // Backslashes in paths are not significant, but allowed
594 assert_eq!(true, HgPath::new(br"a\b/c").is_valid());
594 assert_eq!(true, HgPath::new(br"a\b/c").is_valid());
595 // Dots in paths are not significant, but allowed
595 // Dots in paths are not significant, but allowed
596 assert_eq!(true, HgPath::new(b"a/b/../c/").is_valid());
596 assert_eq!(true, HgPath::new(b"a/b/../c/").is_valid());
597 assert_eq!(true, HgPath::new(b"./a/b/../c/").is_valid());
597 assert_eq!(true, HgPath::new(b"./a/b/../c/").is_valid());
598 }
598 }
599
599
600 #[test]
600 #[test]
601 fn test_iter() {
601 fn test_iter() {
602 let path = HgPath::new(b"a");
602 let path = HgPath::new(b"a");
603 let mut iter = path.bytes();
603 let mut iter = path.bytes();
604 assert_eq!(Some(&b'a'), iter.next());
604 assert_eq!(Some(&b'a'), iter.next());
605 assert_eq!(None, iter.next_back());
605 assert_eq!(None, iter.next_back());
606 assert_eq!(None, iter.next());
606 assert_eq!(None, iter.next());
607
607
608 let path = HgPath::new(b"a");
608 let path = HgPath::new(b"a");
609 let mut iter = path.bytes();
609 let mut iter = path.bytes();
610 assert_eq!(Some(&b'a'), iter.next_back());
610 assert_eq!(Some(&b'a'), iter.next_back());
611 assert_eq!(None, iter.next_back());
611 assert_eq!(None, iter.next_back());
612 assert_eq!(None, iter.next());
612 assert_eq!(None, iter.next());
613
613
614 let path = HgPath::new(b"abc");
614 let path = HgPath::new(b"abc");
615 let mut iter = path.bytes();
615 let mut iter = path.bytes();
616 assert_eq!(Some(&b'a'), iter.next());
616 assert_eq!(Some(&b'a'), iter.next());
617 assert_eq!(Some(&b'c'), iter.next_back());
617 assert_eq!(Some(&b'c'), iter.next_back());
618 assert_eq!(Some(&b'b'), iter.next_back());
618 assert_eq!(Some(&b'b'), iter.next_back());
619 assert_eq!(None, iter.next_back());
619 assert_eq!(None, iter.next_back());
620 assert_eq!(None, iter.next());
620 assert_eq!(None, iter.next());
621
621
622 let path = HgPath::new(b"abc");
622 let path = HgPath::new(b"abc");
623 let mut iter = path.bytes();
623 let mut iter = path.bytes();
624 assert_eq!(Some(&b'a'), iter.next());
624 assert_eq!(Some(&b'a'), iter.next());
625 assert_eq!(Some(&b'b'), iter.next());
625 assert_eq!(Some(&b'b'), iter.next());
626 assert_eq!(Some(&b'c'), iter.next());
626 assert_eq!(Some(&b'c'), iter.next());
627 assert_eq!(None, iter.next_back());
627 assert_eq!(None, iter.next_back());
628 assert_eq!(None, iter.next());
628 assert_eq!(None, iter.next());
629
629
630 let path = HgPath::new(b"abc");
630 let path = HgPath::new(b"abc");
631 let iter = path.bytes();
631 let iter = path.bytes();
632 let mut vec = Vec::new();
632 let mut vec = Vec::new();
633 vec.extend(iter);
633 vec.extend(iter);
634 assert_eq!(vec![b'a', b'b', b'c'], vec);
634 assert_eq!(vec![b'a', b'b', b'c'], vec);
635
635
636 let path = HgPath::new(b"abc");
636 let path = HgPath::new(b"abc");
637 let mut iter = path.bytes();
637 let mut iter = path.bytes();
638 assert_eq!(Some(2), iter.rposition(|c| *c == b'c'));
638 assert_eq!(Some(2), iter.rposition(|c| *c == b'c'));
639
639
640 let path = HgPath::new(b"abc");
640 let path = HgPath::new(b"abc");
641 let mut iter = path.bytes();
641 let mut iter = path.bytes();
642 assert_eq!(None, iter.rposition(|c| *c == b'd'));
642 assert_eq!(None, iter.rposition(|c| *c == b'd'));
643 }
643 }
644
644
645 #[test]
645 #[test]
646 fn test_join() {
646 fn test_join() {
647 let path = HgPathBuf::from_bytes(b"a").join(HgPath::new(b"b"));
647 let path = HgPathBuf::from_bytes(b"a").join(HgPath::new(b"b"));
648 assert_eq!(b"a/b", path.as_bytes());
648 assert_eq!(b"a/b", path.as_bytes());
649
649
650 let path = HgPathBuf::from_bytes(b"a/").join(HgPath::new(b"b/c"));
650 let path = HgPathBuf::from_bytes(b"a/").join(HgPath::new(b"b/c"));
651 assert_eq!(b"a/b/c", path.as_bytes());
651 assert_eq!(b"a/b/c", path.as_bytes());
652
652
653 // No leading slash if empty before join
653 // No leading slash if empty before join
654 let path = HgPathBuf::new().join(HgPath::new(b"b/c"));
654 let path = HgPathBuf::new().join(HgPath::new(b"b/c"));
655 assert_eq!(b"b/c", path.as_bytes());
655 assert_eq!(b"b/c", path.as_bytes());
656
656
657 // The leading slash is an invalid representation of an `HgPath`, but
657 // The leading slash is an invalid representation of an `HgPath`, but
658 // it can happen. This creates another invalid representation of
658 // it can happen. This creates another invalid representation of
659 // consecutive bytes.
659 // consecutive bytes.
660 // TODO What should be done in this case? Should we silently remove
660 // TODO What should be done in this case? Should we silently remove
661 // the extra slash? Should we change the signature to a problematic
661 // the extra slash? Should we change the signature to a problematic
662 // `Result<HgPathBuf, HgPathError>`, or should we just keep it so and
662 // `Result<HgPathBuf, HgPathError>`, or should we just keep it so and
663 // let the error happen upon filesystem interaction?
663 // let the error happen upon filesystem interaction?
664 let path = HgPathBuf::from_bytes(b"a/").join(HgPath::new(b"/b"));
664 let path = HgPathBuf::from_bytes(b"a/").join(HgPath::new(b"/b"));
665 assert_eq!(b"a//b", path.as_bytes());
665 assert_eq!(b"a//b", path.as_bytes());
666 let path = HgPathBuf::from_bytes(b"a").join(HgPath::new(b"/b"));
666 let path = HgPathBuf::from_bytes(b"a").join(HgPath::new(b"/b"));
667 assert_eq!(b"a//b", path.as_bytes());
667 assert_eq!(b"a//b", path.as_bytes());
668 }
668 }
669
669
670 #[test]
670 #[test]
671 fn test_relative_to() {
671 fn test_relative_to() {
672 let path = HgPath::new(b"");
672 let path = HgPath::new(b"");
673 let base = HgPath::new(b"");
673 let base = HgPath::new(b"");
674 assert_eq!(Some(path), path.relative_to(base));
674 assert_eq!(Some(path), path.relative_to(base));
675
675
676 let path = HgPath::new(b"path");
676 let path = HgPath::new(b"path");
677 let base = HgPath::new(b"");
677 let base = HgPath::new(b"");
678 assert_eq!(Some(path), path.relative_to(base));
678 assert_eq!(Some(path), path.relative_to(base));
679
679
680 let path = HgPath::new(b"a");
680 let path = HgPath::new(b"a");
681 let base = HgPath::new(b"b");
681 let base = HgPath::new(b"b");
682 assert_eq!(None, path.relative_to(base));
682 assert_eq!(None, path.relative_to(base));
683
683
684 let path = HgPath::new(b"a/b");
684 let path = HgPath::new(b"a/b");
685 let base = HgPath::new(b"a");
685 let base = HgPath::new(b"a");
686 assert_eq!(None, path.relative_to(base));
686 assert_eq!(None, path.relative_to(base));
687
687
688 let path = HgPath::new(b"a/b");
688 let path = HgPath::new(b"a/b");
689 let base = HgPath::new(b"a/");
689 let base = HgPath::new(b"a/");
690 assert_eq!(Some(HgPath::new(b"b")), path.relative_to(base));
690 assert_eq!(Some(HgPath::new(b"b")), path.relative_to(base));
691
691
692 let path = HgPath::new(b"nested/path/to/b");
692 let path = HgPath::new(b"nested/path/to/b");
693 let base = HgPath::new(b"nested/path/");
693 let base = HgPath::new(b"nested/path/");
694 assert_eq!(Some(HgPath::new(b"to/b")), path.relative_to(base));
694 assert_eq!(Some(HgPath::new(b"to/b")), path.relative_to(base));
695
695
696 let path = HgPath::new(b"ends/with/dir/");
696 let path = HgPath::new(b"ends/with/dir/");
697 let base = HgPath::new(b"ends/");
697 let base = HgPath::new(b"ends/");
698 assert_eq!(Some(HgPath::new(b"with/dir/")), path.relative_to(base));
698 assert_eq!(Some(HgPath::new(b"with/dir/")), path.relative_to(base));
699 }
699 }
700
700
701 #[test]
701 #[test]
702 #[cfg(unix)]
702 #[cfg(unix)]
703 fn test_split_drive() {
703 fn test_split_drive() {
704 // Taken from the Python stdlib's tests
704 // Taken from the Python stdlib's tests
705 assert_eq!(
705 assert_eq!(
706 HgPath::new(br"/foo/bar").split_drive(),
706 HgPath::new(br"/foo/bar").split_drive(),
707 (HgPath::new(b""), HgPath::new(br"/foo/bar"))
707 (HgPath::new(b""), HgPath::new(br"/foo/bar"))
708 );
708 );
709 assert_eq!(
709 assert_eq!(
710 HgPath::new(br"foo:bar").split_drive(),
710 HgPath::new(br"foo:bar").split_drive(),
711 (HgPath::new(b""), HgPath::new(br"foo:bar"))
711 (HgPath::new(b""), HgPath::new(br"foo:bar"))
712 );
712 );
713 assert_eq!(
713 assert_eq!(
714 HgPath::new(br":foo:bar").split_drive(),
714 HgPath::new(br":foo:bar").split_drive(),
715 (HgPath::new(b""), HgPath::new(br":foo:bar"))
715 (HgPath::new(b""), HgPath::new(br":foo:bar"))
716 );
716 );
717 // Also try NT paths; should not split them
717 // Also try NT paths; should not split them
718 assert_eq!(
718 assert_eq!(
719 HgPath::new(br"c:\foo\bar").split_drive(),
719 HgPath::new(br"c:\foo\bar").split_drive(),
720 (HgPath::new(b""), HgPath::new(br"c:\foo\bar"))
720 (HgPath::new(b""), HgPath::new(br"c:\foo\bar"))
721 );
721 );
722 assert_eq!(
722 assert_eq!(
723 HgPath::new(b"c:/foo/bar").split_drive(),
723 HgPath::new(b"c:/foo/bar").split_drive(),
724 (HgPath::new(b""), HgPath::new(br"c:/foo/bar"))
724 (HgPath::new(b""), HgPath::new(br"c:/foo/bar"))
725 );
725 );
726 assert_eq!(
726 assert_eq!(
727 HgPath::new(br"\\conky\mountpoint\foo\bar").split_drive(),
727 HgPath::new(br"\\conky\mountpoint\foo\bar").split_drive(),
728 (
728 (
729 HgPath::new(b""),
729 HgPath::new(b""),
730 HgPath::new(br"\\conky\mountpoint\foo\bar")
730 HgPath::new(br"\\conky\mountpoint\foo\bar")
731 )
731 )
732 );
732 );
733 }
733 }
734
734
735 #[test]
735 #[test]
736 #[cfg(windows)]
736 #[cfg(windows)]
737 fn test_split_drive() {
737 fn test_split_drive() {
738 assert_eq!(
738 assert_eq!(
739 HgPath::new(br"c:\foo\bar").split_drive(),
739 HgPath::new(br"c:\foo\bar").split_drive(),
740 (HgPath::new(br"c:"), HgPath::new(br"\foo\bar"))
740 (HgPath::new(br"c:"), HgPath::new(br"\foo\bar"))
741 );
741 );
742 assert_eq!(
742 assert_eq!(
743 HgPath::new(b"c:/foo/bar").split_drive(),
743 HgPath::new(b"c:/foo/bar").split_drive(),
744 (HgPath::new(br"c:"), HgPath::new(br"/foo/bar"))
744 (HgPath::new(br"c:"), HgPath::new(br"/foo/bar"))
745 );
745 );
746 assert_eq!(
746 assert_eq!(
747 HgPath::new(br"\\conky\mountpoint\foo\bar").split_drive(),
747 HgPath::new(br"\\conky\mountpoint\foo\bar").split_drive(),
748 (
748 (
749 HgPath::new(br"\\conky\mountpoint"),
749 HgPath::new(br"\\conky\mountpoint"),
750 HgPath::new(br"\foo\bar")
750 HgPath::new(br"\foo\bar")
751 )
751 )
752 );
752 );
753 assert_eq!(
753 assert_eq!(
754 HgPath::new(br"//conky/mountpoint/foo/bar").split_drive(),
754 HgPath::new(br"//conky/mountpoint/foo/bar").split_drive(),
755 (
755 (
756 HgPath::new(br"//conky/mountpoint"),
756 HgPath::new(br"//conky/mountpoint"),
757 HgPath::new(br"/foo/bar")
757 HgPath::new(br"/foo/bar")
758 )
758 )
759 );
759 );
760 assert_eq!(
760 assert_eq!(
761 HgPath::new(br"\\\conky\mountpoint\foo\bar").split_drive(),
761 HgPath::new(br"\\\conky\mountpoint\foo\bar").split_drive(),
762 (
762 (
763 HgPath::new(br""),
763 HgPath::new(br""),
764 HgPath::new(br"\\\conky\mountpoint\foo\bar")
764 HgPath::new(br"\\\conky\mountpoint\foo\bar")
765 )
765 )
766 );
766 );
767 assert_eq!(
767 assert_eq!(
768 HgPath::new(br"///conky/mountpoint/foo/bar").split_drive(),
768 HgPath::new(br"///conky/mountpoint/foo/bar").split_drive(),
769 (
769 (
770 HgPath::new(br""),
770 HgPath::new(br""),
771 HgPath::new(br"///conky/mountpoint/foo/bar")
771 HgPath::new(br"///conky/mountpoint/foo/bar")
772 )
772 )
773 );
773 );
774 assert_eq!(
774 assert_eq!(
775 HgPath::new(br"\\conky\\mountpoint\foo\bar").split_drive(),
775 HgPath::new(br"\\conky\\mountpoint\foo\bar").split_drive(),
776 (
776 (
777 HgPath::new(br""),
777 HgPath::new(br""),
778 HgPath::new(br"\\conky\\mountpoint\foo\bar")
778 HgPath::new(br"\\conky\\mountpoint\foo\bar")
779 )
779 )
780 );
780 );
781 assert_eq!(
781 assert_eq!(
782 HgPath::new(br"//conky//mountpoint/foo/bar").split_drive(),
782 HgPath::new(br"//conky//mountpoint/foo/bar").split_drive(),
783 (
783 (
784 HgPath::new(br""),
784 HgPath::new(br""),
785 HgPath::new(br"//conky//mountpoint/foo/bar")
785 HgPath::new(br"//conky//mountpoint/foo/bar")
786 )
786 )
787 );
787 );
788 // UNC part containing U+0130
788 // UNC part containing U+0130
789 assert_eq!(
789 assert_eq!(
790 HgPath::new(b"//conky/MOUNTPO\xc4\xb0NT/foo/bar").split_drive(),
790 HgPath::new(b"//conky/MOUNTPO\xc4\xb0NT/foo/bar").split_drive(),
791 (
791 (
792 HgPath::new(b"//conky/MOUNTPO\xc4\xb0NT"),
792 HgPath::new(b"//conky/MOUNTPO\xc4\xb0NT"),
793 HgPath::new(br"/foo/bar")
793 HgPath::new(br"/foo/bar")
794 )
794 )
795 );
795 );
796 }
796 }
797
797
798 #[test]
798 #[test]
799 fn test_parent() {
799 fn test_parent() {
800 let path = HgPath::new(b"");
800 let path = HgPath::new(b"");
801 assert_eq!(path.parent(), path);
801 assert_eq!(path.parent(), path);
802
802
803 let path = HgPath::new(b"a");
803 let path = HgPath::new(b"a");
804 assert_eq!(path.parent(), HgPath::new(b""));
804 assert_eq!(path.parent(), HgPath::new(b""));
805
805
806 let path = HgPath::new(b"a/b");
806 let path = HgPath::new(b"a/b");
807 assert_eq!(path.parent(), HgPath::new(b"a"));
807 assert_eq!(path.parent(), HgPath::new(b"a"));
808
808
809 let path = HgPath::new(b"a/other/b");
809 let path = HgPath::new(b"a/other/b");
810 assert_eq!(path.parent(), HgPath::new(b"a/other"));
810 assert_eq!(path.parent(), HgPath::new(b"a/other"));
811 }
811 }
812 }
812 }
General Comments 0
You need to be logged in to leave comments. Login now