##// END OF EJS Templates
rust: Rename the `Revlog::get_node_rev` method to `rev_from_node`...
Simon Sapin -
r48782:6f579618 default
parent child Browse files
Show More
@@ -1,67 +1,67 b''
1 use crate::errors::HgError;
1 use crate::errors::HgError;
2 use crate::repo::Repo;
2 use crate::repo::Repo;
3 use crate::revlog::revlog::{Revlog, RevlogError};
3 use crate::revlog::revlog::{Revlog, RevlogError};
4 use crate::revlog::Revision;
4 use crate::revlog::Revision;
5 use crate::revlog::{Node, NodePrefix};
5 use crate::revlog::{Node, NodePrefix};
6
6
7 /// A specialized `Revlog` to work with `changelog` data format.
7 /// A specialized `Revlog` to work with `changelog` data format.
8 pub struct Changelog {
8 pub struct Changelog {
9 /// The generic `revlog` format.
9 /// The generic `revlog` format.
10 pub(crate) revlog: Revlog,
10 pub(crate) revlog: Revlog,
11 }
11 }
12
12
13 impl Changelog {
13 impl Changelog {
14 /// Open the `changelog` of a repository given by its root.
14 /// Open the `changelog` of a repository given by its root.
15 pub fn open(repo: &Repo) -> Result<Self, HgError> {
15 pub fn open(repo: &Repo) -> Result<Self, HgError> {
16 let revlog = Revlog::open(repo, "00changelog.i", None)?;
16 let revlog = Revlog::open(repo, "00changelog.i", None)?;
17 Ok(Self { revlog })
17 Ok(Self { revlog })
18 }
18 }
19
19
20 /// Return the `ChangelogEntry` a given node id.
20 /// Return the `ChangelogEntry` a given node id.
21 pub fn get_node(
21 pub fn get_node(
22 &self,
22 &self,
23 node: NodePrefix,
23 node: NodePrefix,
24 ) -> Result<ChangelogEntry, RevlogError> {
24 ) -> Result<ChangelogEntry, RevlogError> {
25 let rev = self.revlog.get_node_rev(node)?;
25 let rev = self.revlog.rev_from_node(node)?;
26 self.get_rev(rev)
26 self.get_rev(rev)
27 }
27 }
28
28
29 /// Return the `ChangelogEntry` of a given node revision.
29 /// Return the `ChangelogEntry` of a given node revision.
30 pub fn get_rev(
30 pub fn get_rev(
31 &self,
31 &self,
32 rev: Revision,
32 rev: Revision,
33 ) -> Result<ChangelogEntry, RevlogError> {
33 ) -> Result<ChangelogEntry, RevlogError> {
34 let bytes = self.revlog.get_rev_data(rev)?;
34 let bytes = self.revlog.get_rev_data(rev)?;
35 Ok(ChangelogEntry { bytes })
35 Ok(ChangelogEntry { bytes })
36 }
36 }
37
37
38 pub fn node_from_rev(&self, rev: Revision) -> Option<&Node> {
38 pub fn node_from_rev(&self, rev: Revision) -> Option<&Node> {
39 self.revlog.node_from_rev(rev)
39 self.revlog.node_from_rev(rev)
40 }
40 }
41 }
41 }
42
42
43 /// `Changelog` entry which knows how to interpret the `changelog` data bytes.
43 /// `Changelog` entry which knows how to interpret the `changelog` data bytes.
44 #[derive(Debug)]
44 #[derive(Debug)]
45 pub struct ChangelogEntry {
45 pub struct ChangelogEntry {
46 /// The data bytes of the `changelog` entry.
46 /// The data bytes of the `changelog` entry.
47 bytes: Vec<u8>,
47 bytes: Vec<u8>,
48 }
48 }
49
49
50 impl ChangelogEntry {
50 impl ChangelogEntry {
51 /// Return an iterator over the lines of the entry.
51 /// Return an iterator over the lines of the entry.
52 pub fn lines(&self) -> impl Iterator<Item = &[u8]> {
52 pub fn lines(&self) -> impl Iterator<Item = &[u8]> {
53 self.bytes
53 self.bytes
54 .split(|b| b == &b'\n')
54 .split(|b| b == &b'\n')
55 .filter(|line| !line.is_empty())
55 .filter(|line| !line.is_empty())
56 }
56 }
57
57
58 /// Return the node id of the `manifest` referenced by this `changelog`
58 /// Return the node id of the `manifest` referenced by this `changelog`
59 /// entry.
59 /// entry.
60 pub fn manifest_node(&self) -> Result<Node, HgError> {
60 pub fn manifest_node(&self) -> Result<Node, HgError> {
61 Node::from_hex_for_repo(
61 Node::from_hex_for_repo(
62 self.lines()
62 self.lines()
63 .next()
63 .next()
64 .ok_or_else(|| HgError::corrupted("empty changelog entry"))?,
64 .ok_or_else(|| HgError::corrupted("empty changelog entry"))?,
65 )
65 )
66 }
66 }
67 }
67 }
@@ -1,79 +1,79 b''
1 use crate::errors::HgError;
1 use crate::errors::HgError;
2 use crate::repo::Repo;
2 use crate::repo::Repo;
3 use crate::revlog::path_encode::path_encode;
3 use crate::revlog::path_encode::path_encode;
4 use crate::revlog::revlog::{Revlog, RevlogError};
4 use crate::revlog::revlog::{Revlog, RevlogError};
5 use crate::revlog::NodePrefix;
5 use crate::revlog::NodePrefix;
6 use crate::revlog::Revision;
6 use crate::revlog::Revision;
7 use crate::utils::files::get_path_from_bytes;
7 use crate::utils::files::get_path_from_bytes;
8 use crate::utils::hg_path::HgPath;
8 use crate::utils::hg_path::HgPath;
9 use crate::utils::SliceExt;
9 use crate::utils::SliceExt;
10 use std::borrow::Cow;
10 use std::borrow::Cow;
11 use std::path::PathBuf;
11 use std::path::PathBuf;
12
12
13 /// A specialized `Revlog` to work with file data logs.
13 /// A specialized `Revlog` to work with file data logs.
14 pub struct Filelog {
14 pub struct Filelog {
15 /// The generic `revlog` format.
15 /// The generic `revlog` format.
16 revlog: Revlog,
16 revlog: Revlog,
17 }
17 }
18
18
19 impl Filelog {
19 impl Filelog {
20 pub fn open(repo: &Repo, file_path: &HgPath) -> Result<Self, HgError> {
20 pub fn open(repo: &Repo, file_path: &HgPath) -> Result<Self, HgError> {
21 let index_path = store_path(file_path, b".i");
21 let index_path = store_path(file_path, b".i");
22 let data_path = store_path(file_path, b".d");
22 let data_path = store_path(file_path, b".d");
23 let revlog = Revlog::open(repo, index_path, Some(&data_path))?;
23 let revlog = Revlog::open(repo, index_path, Some(&data_path))?;
24 Ok(Self { revlog })
24 Ok(Self { revlog })
25 }
25 }
26
26
27 /// The given node ID is that of the file as found in a manifest, not of a
27 /// The given node ID is that of the file as found in a manifest, not of a
28 /// changeset.
28 /// changeset.
29 pub fn get_node(
29 pub fn get_node(
30 &self,
30 &self,
31 file_node: impl Into<NodePrefix>,
31 file_node: impl Into<NodePrefix>,
32 ) -> Result<FilelogEntry, RevlogError> {
32 ) -> Result<FilelogEntry, RevlogError> {
33 let file_rev = self.revlog.get_node_rev(file_node.into())?;
33 let file_rev = self.revlog.rev_from_node(file_node.into())?;
34 self.get_rev(file_rev)
34 self.get_rev(file_rev)
35 }
35 }
36
36
37 /// The given revision is that of the file as found in a manifest, not of a
37 /// The given revision is that of the file as found in a manifest, not of a
38 /// changeset.
38 /// changeset.
39 pub fn get_rev(
39 pub fn get_rev(
40 &self,
40 &self,
41 file_rev: Revision,
41 file_rev: Revision,
42 ) -> Result<FilelogEntry, RevlogError> {
42 ) -> Result<FilelogEntry, RevlogError> {
43 let data = self.revlog.get_rev_data(file_rev)?;
43 let data = self.revlog.get_rev_data(file_rev)?;
44 Ok(FilelogEntry(data.into()))
44 Ok(FilelogEntry(data.into()))
45 }
45 }
46 }
46 }
47
47
48 fn store_path(hg_path: &HgPath, suffix: &[u8]) -> PathBuf {
48 fn store_path(hg_path: &HgPath, suffix: &[u8]) -> PathBuf {
49 let encoded_bytes =
49 let encoded_bytes =
50 path_encode(&[b"data/", hg_path.as_bytes(), suffix].concat());
50 path_encode(&[b"data/", hg_path.as_bytes(), suffix].concat());
51 get_path_from_bytes(&encoded_bytes).into()
51 get_path_from_bytes(&encoded_bytes).into()
52 }
52 }
53
53
54 pub struct FilelogEntry<'filelog>(Cow<'filelog, [u8]>);
54 pub struct FilelogEntry<'filelog>(Cow<'filelog, [u8]>);
55
55
56 impl<'filelog> FilelogEntry<'filelog> {
56 impl<'filelog> FilelogEntry<'filelog> {
57 /// Split into metadata and data
57 /// Split into metadata and data
58 pub fn split(&self) -> Result<(Option<&[u8]>, &[u8]), HgError> {
58 pub fn split(&self) -> Result<(Option<&[u8]>, &[u8]), HgError> {
59 const DELIMITER: &[u8; 2] = &[b'\x01', b'\n'];
59 const DELIMITER: &[u8; 2] = &[b'\x01', b'\n'];
60
60
61 if let Some(rest) = self.0.drop_prefix(DELIMITER) {
61 if let Some(rest) = self.0.drop_prefix(DELIMITER) {
62 if let Some((metadata, data)) = rest.split_2_by_slice(DELIMITER) {
62 if let Some((metadata, data)) = rest.split_2_by_slice(DELIMITER) {
63 Ok((Some(metadata), data))
63 Ok((Some(metadata), data))
64 } else {
64 } else {
65 Err(HgError::corrupted(
65 Err(HgError::corrupted(
66 "Missing metadata end delimiter in filelog entry",
66 "Missing metadata end delimiter in filelog entry",
67 ))
67 ))
68 }
68 }
69 } else {
69 } else {
70 Ok((None, &self.0))
70 Ok((None, &self.0))
71 }
71 }
72 }
72 }
73
73
74 /// Returns the file contents at this revision, stripped of any metadata
74 /// Returns the file contents at this revision, stripped of any metadata
75 pub fn data(&self) -> Result<&[u8], HgError> {
75 pub fn data(&self) -> Result<&[u8], HgError> {
76 let (_metadata, data) = self.split()?;
76 let (_metadata, data) = self.split()?;
77 Ok(data)
77 Ok(data)
78 }
78 }
79 }
79 }
@@ -1,84 +1,84 b''
1 use crate::errors::HgError;
1 use crate::errors::HgError;
2 use crate::repo::Repo;
2 use crate::repo::Repo;
3 use crate::revlog::revlog::{Revlog, RevlogError};
3 use crate::revlog::revlog::{Revlog, RevlogError};
4 use crate::revlog::Revision;
4 use crate::revlog::Revision;
5 use crate::revlog::{Node, NodePrefix};
5 use crate::revlog::{Node, NodePrefix};
6 use crate::utils::hg_path::HgPath;
6 use crate::utils::hg_path::HgPath;
7
7
8 /// A specialized `Revlog` to work with `manifest` data format.
8 /// A specialized `Revlog` to work with `manifest` data format.
9 pub struct Manifestlog {
9 pub struct Manifestlog {
10 /// The generic `revlog` format.
10 /// The generic `revlog` format.
11 revlog: Revlog,
11 revlog: Revlog,
12 }
12 }
13
13
14 impl Manifestlog {
14 impl Manifestlog {
15 /// Open the `manifest` of a repository given by its root.
15 /// Open the `manifest` of a repository given by its root.
16 pub fn open(repo: &Repo) -> Result<Self, HgError> {
16 pub fn open(repo: &Repo) -> Result<Self, HgError> {
17 let revlog = Revlog::open(repo, "00manifest.i", None)?;
17 let revlog = Revlog::open(repo, "00manifest.i", None)?;
18 Ok(Self { revlog })
18 Ok(Self { revlog })
19 }
19 }
20
20
21 /// Return the `ManifestEntry` of a given node id.
21 /// Return the `ManifestEntry` of a given node id.
22 pub fn get_node(&self, node: NodePrefix) -> Result<Manifest, RevlogError> {
22 pub fn get_node(&self, node: NodePrefix) -> Result<Manifest, RevlogError> {
23 let rev = self.revlog.get_node_rev(node)?;
23 let rev = self.revlog.rev_from_node(node)?;
24 self.get_rev(rev)
24 self.get_rev(rev)
25 }
25 }
26
26
27 /// Return the `ManifestEntry` of a given node revision.
27 /// Return the `ManifestEntry` of a given node revision.
28 pub fn get_rev(&self, rev: Revision) -> Result<Manifest, RevlogError> {
28 pub fn get_rev(&self, rev: Revision) -> Result<Manifest, RevlogError> {
29 let bytes = self.revlog.get_rev_data(rev)?;
29 let bytes = self.revlog.get_rev_data(rev)?;
30 Ok(Manifest { bytes })
30 Ok(Manifest { bytes })
31 }
31 }
32 }
32 }
33
33
34 /// `Manifestlog` entry which knows how to interpret the `manifest` data bytes.
34 /// `Manifestlog` entry which knows how to interpret the `manifest` data bytes.
35 #[derive(Debug)]
35 #[derive(Debug)]
36 pub struct Manifest {
36 pub struct Manifest {
37 bytes: Vec<u8>,
37 bytes: Vec<u8>,
38 }
38 }
39
39
40 impl Manifest {
40 impl Manifest {
41 /// Return an iterator over the lines of the entry.
41 /// Return an iterator over the lines of the entry.
42 pub fn lines(&self) -> impl Iterator<Item = &[u8]> {
42 pub fn lines(&self) -> impl Iterator<Item = &[u8]> {
43 self.bytes
43 self.bytes
44 .split(|b| b == &b'\n')
44 .split(|b| b == &b'\n')
45 .filter(|line| !line.is_empty())
45 .filter(|line| !line.is_empty())
46 }
46 }
47
47
48 /// Return an iterator over the files of the entry.
48 /// Return an iterator over the files of the entry.
49 pub fn files(&self) -> impl Iterator<Item = &HgPath> {
49 pub fn files(&self) -> impl Iterator<Item = &HgPath> {
50 self.lines().filter(|line| !line.is_empty()).map(|line| {
50 self.lines().filter(|line| !line.is_empty()).map(|line| {
51 let pos = line
51 let pos = line
52 .iter()
52 .iter()
53 .position(|x| x == &b'\0')
53 .position(|x| x == &b'\0')
54 .expect("manifest line should contain \\0");
54 .expect("manifest line should contain \\0");
55 HgPath::new(&line[..pos])
55 HgPath::new(&line[..pos])
56 })
56 })
57 }
57 }
58
58
59 /// Return an iterator over the files of the entry.
59 /// Return an iterator over the files of the entry.
60 pub fn files_with_nodes(&self) -> impl Iterator<Item = (&HgPath, &[u8])> {
60 pub fn files_with_nodes(&self) -> impl Iterator<Item = (&HgPath, &[u8])> {
61 self.lines().filter(|line| !line.is_empty()).map(|line| {
61 self.lines().filter(|line| !line.is_empty()).map(|line| {
62 let pos = line
62 let pos = line
63 .iter()
63 .iter()
64 .position(|x| x == &b'\0')
64 .position(|x| x == &b'\0')
65 .expect("manifest line should contain \\0");
65 .expect("manifest line should contain \\0");
66 let hash_start = pos + 1;
66 let hash_start = pos + 1;
67 let hash_end = hash_start + 40;
67 let hash_end = hash_start + 40;
68 (HgPath::new(&line[..pos]), &line[hash_start..hash_end])
68 (HgPath::new(&line[..pos]), &line[hash_start..hash_end])
69 })
69 })
70 }
70 }
71
71
72 /// If the given path is in this manifest, return its filelog node ID
72 /// If the given path is in this manifest, return its filelog node ID
73 pub fn find_file(&self, path: &HgPath) -> Result<Option<Node>, HgError> {
73 pub fn find_file(&self, path: &HgPath) -> Result<Option<Node>, HgError> {
74 // TODO: use binary search instead of linear scan. This may involve
74 // TODO: use binary search instead of linear scan. This may involve
75 // building (and caching) an index of the byte indicex of each manifest
75 // building (and caching) an index of the byte indicex of each manifest
76 // line.
76 // line.
77 for (manifest_path, node) in self.files_with_nodes() {
77 for (manifest_path, node) in self.files_with_nodes() {
78 if manifest_path == path {
78 if manifest_path == path {
79 return Ok(Some(Node::from_hex_for_repo(node)?));
79 return Ok(Some(Node::from_hex_for_repo(node)?));
80 }
80 }
81 }
81 }
82 Ok(None)
82 Ok(None)
83 }
83 }
84 }
84 }
@@ -1,406 +1,406 b''
1 use std::borrow::Cow;
1 use std::borrow::Cow;
2 use std::io::Read;
2 use std::io::Read;
3 use std::ops::Deref;
3 use std::ops::Deref;
4 use std::path::Path;
4 use std::path::Path;
5
5
6 use byteorder::{BigEndian, ByteOrder};
6 use byteorder::{BigEndian, ByteOrder};
7 use flate2::read::ZlibDecoder;
7 use flate2::read::ZlibDecoder;
8 use micro_timer::timed;
8 use micro_timer::timed;
9 use sha1::{Digest, Sha1};
9 use sha1::{Digest, Sha1};
10 use zstd;
10 use zstd;
11
11
12 use super::index::Index;
12 use super::index::Index;
13 use super::node::{NodePrefix, NODE_BYTES_LENGTH, NULL_NODE};
13 use super::node::{NodePrefix, NODE_BYTES_LENGTH, NULL_NODE};
14 use super::nodemap;
14 use super::nodemap;
15 use super::nodemap::{NodeMap, NodeMapError};
15 use super::nodemap::{NodeMap, NodeMapError};
16 use super::nodemap_docket::NodeMapDocket;
16 use super::nodemap_docket::NodeMapDocket;
17 use super::patch;
17 use super::patch;
18 use crate::errors::HgError;
18 use crate::errors::HgError;
19 use crate::repo::Repo;
19 use crate::repo::Repo;
20 use crate::revlog::Revision;
20 use crate::revlog::Revision;
21 use crate::{Node, NULL_REVISION};
21 use crate::{Node, NULL_REVISION};
22
22
23 #[derive(derive_more::From)]
23 #[derive(derive_more::From)]
24 pub enum RevlogError {
24 pub enum RevlogError {
25 InvalidRevision,
25 InvalidRevision,
26 /// Working directory is not supported
26 /// Working directory is not supported
27 WDirUnsupported,
27 WDirUnsupported,
28 /// Found more than one entry whose ID match the requested prefix
28 /// Found more than one entry whose ID match the requested prefix
29 AmbiguousPrefix,
29 AmbiguousPrefix,
30 #[from]
30 #[from]
31 Other(HgError),
31 Other(HgError),
32 }
32 }
33
33
34 impl From<NodeMapError> for RevlogError {
34 impl From<NodeMapError> for RevlogError {
35 fn from(error: NodeMapError) -> Self {
35 fn from(error: NodeMapError) -> Self {
36 match error {
36 match error {
37 NodeMapError::MultipleResults => RevlogError::AmbiguousPrefix,
37 NodeMapError::MultipleResults => RevlogError::AmbiguousPrefix,
38 NodeMapError::RevisionNotInIndex(_) => RevlogError::corrupted(),
38 NodeMapError::RevisionNotInIndex(_) => RevlogError::corrupted(),
39 }
39 }
40 }
40 }
41 }
41 }
42
42
43 impl RevlogError {
43 impl RevlogError {
44 fn corrupted() -> Self {
44 fn corrupted() -> Self {
45 RevlogError::Other(HgError::corrupted("corrupted revlog"))
45 RevlogError::Other(HgError::corrupted("corrupted revlog"))
46 }
46 }
47 }
47 }
48
48
49 /// Read only implementation of revlog.
49 /// Read only implementation of revlog.
50 pub struct Revlog {
50 pub struct Revlog {
51 /// When index and data are not interleaved: bytes of the revlog index.
51 /// When index and data are not interleaved: bytes of the revlog index.
52 /// When index and data are interleaved: bytes of the revlog index and
52 /// When index and data are interleaved: bytes of the revlog index and
53 /// data.
53 /// data.
54 index: Index,
54 index: Index,
55 /// When index and data are not interleaved: bytes of the revlog data
55 /// When index and data are not interleaved: bytes of the revlog data
56 data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>>,
56 data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>>,
57 /// When present on disk: the persistent nodemap for this revlog
57 /// When present on disk: the persistent nodemap for this revlog
58 nodemap: Option<nodemap::NodeTree>,
58 nodemap: Option<nodemap::NodeTree>,
59 }
59 }
60
60
61 impl Revlog {
61 impl Revlog {
62 /// Open a revlog index file.
62 /// Open a revlog index file.
63 ///
63 ///
64 /// It will also open the associated data file if index and data are not
64 /// It will also open the associated data file if index and data are not
65 /// interleaved.
65 /// interleaved.
66 #[timed]
66 #[timed]
67 pub fn open(
67 pub fn open(
68 repo: &Repo,
68 repo: &Repo,
69 index_path: impl AsRef<Path>,
69 index_path: impl AsRef<Path>,
70 data_path: Option<&Path>,
70 data_path: Option<&Path>,
71 ) -> Result<Self, HgError> {
71 ) -> Result<Self, HgError> {
72 let index_path = index_path.as_ref();
72 let index_path = index_path.as_ref();
73 let index_mmap = repo.store_vfs().mmap_open(&index_path)?;
73 let index_mmap = repo.store_vfs().mmap_open(&index_path)?;
74
74
75 let version = get_version(&index_mmap);
75 let version = get_version(&index_mmap);
76 if version != 1 {
76 if version != 1 {
77 // A proper new version should have had a repo/store requirement.
77 // A proper new version should have had a repo/store requirement.
78 return Err(HgError::corrupted("corrupted revlog"));
78 return Err(HgError::corrupted("corrupted revlog"));
79 }
79 }
80
80
81 let index = Index::new(Box::new(index_mmap))?;
81 let index = Index::new(Box::new(index_mmap))?;
82
82
83 let default_data_path = index_path.with_extension("d");
83 let default_data_path = index_path.with_extension("d");
84
84
85 // type annotation required
85 // type annotation required
86 // won't recognize Mmap as Deref<Target = [u8]>
86 // won't recognize Mmap as Deref<Target = [u8]>
87 let data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>> =
87 let data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>> =
88 if index.is_inline() {
88 if index.is_inline() {
89 None
89 None
90 } else {
90 } else {
91 let data_path = data_path.unwrap_or(&default_data_path);
91 let data_path = data_path.unwrap_or(&default_data_path);
92 let data_mmap = repo.store_vfs().mmap_open(data_path)?;
92 let data_mmap = repo.store_vfs().mmap_open(data_path)?;
93 Some(Box::new(data_mmap))
93 Some(Box::new(data_mmap))
94 };
94 };
95
95
96 let nodemap = NodeMapDocket::read_from_file(repo, index_path)?.map(
96 let nodemap = NodeMapDocket::read_from_file(repo, index_path)?.map(
97 |(docket, data)| {
97 |(docket, data)| {
98 nodemap::NodeTree::load_bytes(
98 nodemap::NodeTree::load_bytes(
99 Box::new(data),
99 Box::new(data),
100 docket.data_length,
100 docket.data_length,
101 )
101 )
102 },
102 },
103 );
103 );
104
104
105 Ok(Revlog {
105 Ok(Revlog {
106 index,
106 index,
107 data_bytes,
107 data_bytes,
108 nodemap,
108 nodemap,
109 })
109 })
110 }
110 }
111
111
112 /// Return number of entries of the `Revlog`.
112 /// Return number of entries of the `Revlog`.
113 pub fn len(&self) -> usize {
113 pub fn len(&self) -> usize {
114 self.index.len()
114 self.index.len()
115 }
115 }
116
116
117 /// Returns `true` if the `Revlog` has zero `entries`.
117 /// Returns `true` if the `Revlog` has zero `entries`.
118 pub fn is_empty(&self) -> bool {
118 pub fn is_empty(&self) -> bool {
119 self.index.is_empty()
119 self.index.is_empty()
120 }
120 }
121
121
122 /// Returns the node ID for the given revision number, if it exists in this revlog
122 /// Returns the node ID for the given revision number, if it exists in this revlog
123 pub fn node_from_rev(&self, rev: Revision) -> Option<&Node> {
123 pub fn node_from_rev(&self, rev: Revision) -> Option<&Node> {
124 Some(self.index.get_entry(rev)?.hash())
124 Some(self.index.get_entry(rev)?.hash())
125 }
125 }
126
126
127 /// Return the full data associated to a node.
127 /// Return the revision number for the given node ID, if it exists in this revlog
128 #[timed]
128 #[timed]
129 pub fn get_node_rev(
129 pub fn rev_from_node(
130 &self,
130 &self,
131 node: NodePrefix,
131 node: NodePrefix,
132 ) -> Result<Revision, RevlogError> {
132 ) -> Result<Revision, RevlogError> {
133 if node.is_prefix_of(&NULL_NODE) {
133 if node.is_prefix_of(&NULL_NODE) {
134 return Ok(NULL_REVISION);
134 return Ok(NULL_REVISION);
135 }
135 }
136
136
137 if let Some(nodemap) = &self.nodemap {
137 if let Some(nodemap) = &self.nodemap {
138 return nodemap
138 return nodemap
139 .find_bin(&self.index, node)?
139 .find_bin(&self.index, node)?
140 .ok_or(RevlogError::InvalidRevision);
140 .ok_or(RevlogError::InvalidRevision);
141 }
141 }
142
142
143 // Fallback to linear scan when a persistent nodemap is not present.
143 // Fallback to linear scan when a persistent nodemap is not present.
144 // This happens when the persistent-nodemap experimental feature is not
144 // This happens when the persistent-nodemap experimental feature is not
145 // enabled, or for small revlogs.
145 // enabled, or for small revlogs.
146 //
146 //
147 // TODO: consider building a non-persistent nodemap in memory to
147 // TODO: consider building a non-persistent nodemap in memory to
148 // optimize these cases.
148 // optimize these cases.
149 let mut found_by_prefix = None;
149 let mut found_by_prefix = None;
150 for rev in (0..self.len() as Revision).rev() {
150 for rev in (0..self.len() as Revision).rev() {
151 let index_entry =
151 let index_entry =
152 self.index.get_entry(rev).ok_or(HgError::corrupted(
152 self.index.get_entry(rev).ok_or(HgError::corrupted(
153 "revlog references a revision not in the index",
153 "revlog references a revision not in the index",
154 ))?;
154 ))?;
155 if node == *index_entry.hash() {
155 if node == *index_entry.hash() {
156 return Ok(rev);
156 return Ok(rev);
157 }
157 }
158 if node.is_prefix_of(index_entry.hash()) {
158 if node.is_prefix_of(index_entry.hash()) {
159 if found_by_prefix.is_some() {
159 if found_by_prefix.is_some() {
160 return Err(RevlogError::AmbiguousPrefix);
160 return Err(RevlogError::AmbiguousPrefix);
161 }
161 }
162 found_by_prefix = Some(rev)
162 found_by_prefix = Some(rev)
163 }
163 }
164 }
164 }
165 found_by_prefix.ok_or(RevlogError::InvalidRevision)
165 found_by_prefix.ok_or(RevlogError::InvalidRevision)
166 }
166 }
167
167
168 /// Returns whether the given revision exists in this revlog.
168 /// Returns whether the given revision exists in this revlog.
169 pub fn has_rev(&self, rev: Revision) -> bool {
169 pub fn has_rev(&self, rev: Revision) -> bool {
170 self.index.get_entry(rev).is_some()
170 self.index.get_entry(rev).is_some()
171 }
171 }
172
172
173 /// Return the full data associated to a revision.
173 /// Return the full data associated to a revision.
174 ///
174 ///
175 /// All entries required to build the final data out of deltas will be
175 /// All entries required to build the final data out of deltas will be
176 /// retrieved as needed, and the deltas will be applied to the inital
176 /// retrieved as needed, and the deltas will be applied to the inital
177 /// snapshot to rebuild the final data.
177 /// snapshot to rebuild the final data.
178 #[timed]
178 #[timed]
179 pub fn get_rev_data(&self, rev: Revision) -> Result<Vec<u8>, RevlogError> {
179 pub fn get_rev_data(&self, rev: Revision) -> Result<Vec<u8>, RevlogError> {
180 // Todo return -> Cow
180 // Todo return -> Cow
181 let mut entry = self.get_entry(rev)?;
181 let mut entry = self.get_entry(rev)?;
182 let mut delta_chain = vec![];
182 let mut delta_chain = vec![];
183 while let Some(base_rev) = entry.base_rev {
183 while let Some(base_rev) = entry.base_rev {
184 delta_chain.push(entry);
184 delta_chain.push(entry);
185 entry = self
185 entry = self
186 .get_entry(base_rev)
186 .get_entry(base_rev)
187 .map_err(|_| RevlogError::corrupted())?;
187 .map_err(|_| RevlogError::corrupted())?;
188 }
188 }
189
189
190 // TODO do not look twice in the index
190 // TODO do not look twice in the index
191 let index_entry = self
191 let index_entry = self
192 .index
192 .index
193 .get_entry(rev)
193 .get_entry(rev)
194 .ok_or(RevlogError::InvalidRevision)?;
194 .ok_or(RevlogError::InvalidRevision)?;
195
195
196 let data: Vec<u8> = if delta_chain.is_empty() {
196 let data: Vec<u8> = if delta_chain.is_empty() {
197 entry.data()?.into()
197 entry.data()?.into()
198 } else {
198 } else {
199 Revlog::build_data_from_deltas(entry, &delta_chain)?
199 Revlog::build_data_from_deltas(entry, &delta_chain)?
200 };
200 };
201
201
202 if self.check_hash(
202 if self.check_hash(
203 index_entry.p1(),
203 index_entry.p1(),
204 index_entry.p2(),
204 index_entry.p2(),
205 index_entry.hash().as_bytes(),
205 index_entry.hash().as_bytes(),
206 &data,
206 &data,
207 ) {
207 ) {
208 Ok(data)
208 Ok(data)
209 } else {
209 } else {
210 Err(RevlogError::corrupted())
210 Err(RevlogError::corrupted())
211 }
211 }
212 }
212 }
213
213
214 /// Check the hash of some given data against the recorded hash.
214 /// Check the hash of some given data against the recorded hash.
215 pub fn check_hash(
215 pub fn check_hash(
216 &self,
216 &self,
217 p1: Revision,
217 p1: Revision,
218 p2: Revision,
218 p2: Revision,
219 expected: &[u8],
219 expected: &[u8],
220 data: &[u8],
220 data: &[u8],
221 ) -> bool {
221 ) -> bool {
222 let e1 = self.index.get_entry(p1);
222 let e1 = self.index.get_entry(p1);
223 let h1 = match e1 {
223 let h1 = match e1 {
224 Some(ref entry) => entry.hash(),
224 Some(ref entry) => entry.hash(),
225 None => &NULL_NODE,
225 None => &NULL_NODE,
226 };
226 };
227 let e2 = self.index.get_entry(p2);
227 let e2 = self.index.get_entry(p2);
228 let h2 = match e2 {
228 let h2 = match e2 {
229 Some(ref entry) => entry.hash(),
229 Some(ref entry) => entry.hash(),
230 None => &NULL_NODE,
230 None => &NULL_NODE,
231 };
231 };
232
232
233 &hash(data, h1.as_bytes(), h2.as_bytes()) == expected
233 &hash(data, h1.as_bytes(), h2.as_bytes()) == expected
234 }
234 }
235
235
236 /// Build the full data of a revision out its snapshot
236 /// Build the full data of a revision out its snapshot
237 /// and its deltas.
237 /// and its deltas.
238 #[timed]
238 #[timed]
239 fn build_data_from_deltas(
239 fn build_data_from_deltas(
240 snapshot: RevlogEntry,
240 snapshot: RevlogEntry,
241 deltas: &[RevlogEntry],
241 deltas: &[RevlogEntry],
242 ) -> Result<Vec<u8>, RevlogError> {
242 ) -> Result<Vec<u8>, RevlogError> {
243 let snapshot = snapshot.data()?;
243 let snapshot = snapshot.data()?;
244 let deltas = deltas
244 let deltas = deltas
245 .iter()
245 .iter()
246 .rev()
246 .rev()
247 .map(RevlogEntry::data)
247 .map(RevlogEntry::data)
248 .collect::<Result<Vec<Cow<'_, [u8]>>, RevlogError>>()?;
248 .collect::<Result<Vec<Cow<'_, [u8]>>, RevlogError>>()?;
249 let patches: Vec<_> =
249 let patches: Vec<_> =
250 deltas.iter().map(|d| patch::PatchList::new(d)).collect();
250 deltas.iter().map(|d| patch::PatchList::new(d)).collect();
251 let patch = patch::fold_patch_lists(&patches);
251 let patch = patch::fold_patch_lists(&patches);
252 Ok(patch.apply(&snapshot))
252 Ok(patch.apply(&snapshot))
253 }
253 }
254
254
255 /// Return the revlog data.
255 /// Return the revlog data.
256 fn data(&self) -> &[u8] {
256 fn data(&self) -> &[u8] {
257 match self.data_bytes {
257 match self.data_bytes {
258 Some(ref data_bytes) => &data_bytes,
258 Some(ref data_bytes) => &data_bytes,
259 None => panic!(
259 None => panic!(
260 "forgot to load the data or trying to access inline data"
260 "forgot to load the data or trying to access inline data"
261 ),
261 ),
262 }
262 }
263 }
263 }
264
264
265 /// Get an entry of the revlog.
265 /// Get an entry of the revlog.
266 fn get_entry(&self, rev: Revision) -> Result<RevlogEntry, RevlogError> {
266 fn get_entry(&self, rev: Revision) -> Result<RevlogEntry, RevlogError> {
267 let index_entry = self
267 let index_entry = self
268 .index
268 .index
269 .get_entry(rev)
269 .get_entry(rev)
270 .ok_or(RevlogError::InvalidRevision)?;
270 .ok_or(RevlogError::InvalidRevision)?;
271 let start = index_entry.offset();
271 let start = index_entry.offset();
272 let end = start + index_entry.compressed_len();
272 let end = start + index_entry.compressed_len();
273 let data = if self.index.is_inline() {
273 let data = if self.index.is_inline() {
274 self.index.data(start, end)
274 self.index.data(start, end)
275 } else {
275 } else {
276 &self.data()[start..end]
276 &self.data()[start..end]
277 };
277 };
278 let entry = RevlogEntry {
278 let entry = RevlogEntry {
279 rev,
279 rev,
280 bytes: data,
280 bytes: data,
281 compressed_len: index_entry.compressed_len(),
281 compressed_len: index_entry.compressed_len(),
282 uncompressed_len: index_entry.uncompressed_len(),
282 uncompressed_len: index_entry.uncompressed_len(),
283 base_rev: if index_entry.base_revision() == rev {
283 base_rev: if index_entry.base_revision() == rev {
284 None
284 None
285 } else {
285 } else {
286 Some(index_entry.base_revision())
286 Some(index_entry.base_revision())
287 },
287 },
288 };
288 };
289 Ok(entry)
289 Ok(entry)
290 }
290 }
291 }
291 }
292
292
293 /// The revlog entry's bytes and the necessary informations to extract
293 /// The revlog entry's bytes and the necessary informations to extract
294 /// the entry's data.
294 /// the entry's data.
295 #[derive(Debug)]
295 #[derive(Debug)]
296 pub struct RevlogEntry<'a> {
296 pub struct RevlogEntry<'a> {
297 rev: Revision,
297 rev: Revision,
298 bytes: &'a [u8],
298 bytes: &'a [u8],
299 compressed_len: usize,
299 compressed_len: usize,
300 uncompressed_len: usize,
300 uncompressed_len: usize,
301 base_rev: Option<Revision>,
301 base_rev: Option<Revision>,
302 }
302 }
303
303
304 impl<'a> RevlogEntry<'a> {
304 impl<'a> RevlogEntry<'a> {
305 /// Extract the data contained in the entry.
305 /// Extract the data contained in the entry.
306 pub fn data(&self) -> Result<Cow<'_, [u8]>, RevlogError> {
306 pub fn data(&self) -> Result<Cow<'_, [u8]>, RevlogError> {
307 if self.bytes.is_empty() {
307 if self.bytes.is_empty() {
308 return Ok(Cow::Borrowed(&[]));
308 return Ok(Cow::Borrowed(&[]));
309 }
309 }
310 match self.bytes[0] {
310 match self.bytes[0] {
311 // Revision data is the entirety of the entry, including this
311 // Revision data is the entirety of the entry, including this
312 // header.
312 // header.
313 b'\0' => Ok(Cow::Borrowed(self.bytes)),
313 b'\0' => Ok(Cow::Borrowed(self.bytes)),
314 // Raw revision data follows.
314 // Raw revision data follows.
315 b'u' => Ok(Cow::Borrowed(&self.bytes[1..])),
315 b'u' => Ok(Cow::Borrowed(&self.bytes[1..])),
316 // zlib (RFC 1950) data.
316 // zlib (RFC 1950) data.
317 b'x' => Ok(Cow::Owned(self.uncompressed_zlib_data()?)),
317 b'x' => Ok(Cow::Owned(self.uncompressed_zlib_data()?)),
318 // zstd data.
318 // zstd data.
319 b'\x28' => Ok(Cow::Owned(self.uncompressed_zstd_data()?)),
319 b'\x28' => Ok(Cow::Owned(self.uncompressed_zstd_data()?)),
320 // A proper new format should have had a repo/store requirement.
320 // A proper new format should have had a repo/store requirement.
321 _format_type => Err(RevlogError::corrupted()),
321 _format_type => Err(RevlogError::corrupted()),
322 }
322 }
323 }
323 }
324
324
325 fn uncompressed_zlib_data(&self) -> Result<Vec<u8>, RevlogError> {
325 fn uncompressed_zlib_data(&self) -> Result<Vec<u8>, RevlogError> {
326 let mut decoder = ZlibDecoder::new(self.bytes);
326 let mut decoder = ZlibDecoder::new(self.bytes);
327 if self.is_delta() {
327 if self.is_delta() {
328 let mut buf = Vec::with_capacity(self.compressed_len);
328 let mut buf = Vec::with_capacity(self.compressed_len);
329 decoder
329 decoder
330 .read_to_end(&mut buf)
330 .read_to_end(&mut buf)
331 .map_err(|_| RevlogError::corrupted())?;
331 .map_err(|_| RevlogError::corrupted())?;
332 Ok(buf)
332 Ok(buf)
333 } else {
333 } else {
334 let mut buf = vec![0; self.uncompressed_len];
334 let mut buf = vec![0; self.uncompressed_len];
335 decoder
335 decoder
336 .read_exact(&mut buf)
336 .read_exact(&mut buf)
337 .map_err(|_| RevlogError::corrupted())?;
337 .map_err(|_| RevlogError::corrupted())?;
338 Ok(buf)
338 Ok(buf)
339 }
339 }
340 }
340 }
341
341
342 fn uncompressed_zstd_data(&self) -> Result<Vec<u8>, RevlogError> {
342 fn uncompressed_zstd_data(&self) -> Result<Vec<u8>, RevlogError> {
343 if self.is_delta() {
343 if self.is_delta() {
344 let mut buf = Vec::with_capacity(self.compressed_len);
344 let mut buf = Vec::with_capacity(self.compressed_len);
345 zstd::stream::copy_decode(self.bytes, &mut buf)
345 zstd::stream::copy_decode(self.bytes, &mut buf)
346 .map_err(|_| RevlogError::corrupted())?;
346 .map_err(|_| RevlogError::corrupted())?;
347 Ok(buf)
347 Ok(buf)
348 } else {
348 } else {
349 let mut buf = vec![0; self.uncompressed_len];
349 let mut buf = vec![0; self.uncompressed_len];
350 let len = zstd::block::decompress_to_buffer(self.bytes, &mut buf)
350 let len = zstd::block::decompress_to_buffer(self.bytes, &mut buf)
351 .map_err(|_| RevlogError::corrupted())?;
351 .map_err(|_| RevlogError::corrupted())?;
352 if len != self.uncompressed_len {
352 if len != self.uncompressed_len {
353 Err(RevlogError::corrupted())
353 Err(RevlogError::corrupted())
354 } else {
354 } else {
355 Ok(buf)
355 Ok(buf)
356 }
356 }
357 }
357 }
358 }
358 }
359
359
360 /// Tell if the entry is a snapshot or a delta
360 /// Tell if the entry is a snapshot or a delta
361 /// (influences on decompression).
361 /// (influences on decompression).
362 fn is_delta(&self) -> bool {
362 fn is_delta(&self) -> bool {
363 self.base_rev.is_some()
363 self.base_rev.is_some()
364 }
364 }
365 }
365 }
366
366
367 /// Format version of the revlog.
367 /// Format version of the revlog.
368 pub fn get_version(index_bytes: &[u8]) -> u16 {
368 pub fn get_version(index_bytes: &[u8]) -> u16 {
369 BigEndian::read_u16(&index_bytes[2..=3])
369 BigEndian::read_u16(&index_bytes[2..=3])
370 }
370 }
371
371
372 /// Calculate the hash of a revision given its data and its parents.
372 /// Calculate the hash of a revision given its data and its parents.
373 fn hash(
373 fn hash(
374 data: &[u8],
374 data: &[u8],
375 p1_hash: &[u8],
375 p1_hash: &[u8],
376 p2_hash: &[u8],
376 p2_hash: &[u8],
377 ) -> [u8; NODE_BYTES_LENGTH] {
377 ) -> [u8; NODE_BYTES_LENGTH] {
378 let mut hasher = Sha1::new();
378 let mut hasher = Sha1::new();
379 let (a, b) = (p1_hash, p2_hash);
379 let (a, b) = (p1_hash, p2_hash);
380 if a > b {
380 if a > b {
381 hasher.update(b);
381 hasher.update(b);
382 hasher.update(a);
382 hasher.update(a);
383 } else {
383 } else {
384 hasher.update(a);
384 hasher.update(a);
385 hasher.update(b);
385 hasher.update(b);
386 }
386 }
387 hasher.update(data);
387 hasher.update(data);
388 *hasher.finalize().as_ref()
388 *hasher.finalize().as_ref()
389 }
389 }
390
390
391 #[cfg(test)]
391 #[cfg(test)]
392 mod tests {
392 mod tests {
393 use super::*;
393 use super::*;
394
394
395 use super::super::index::IndexEntryBuilder;
395 use super::super::index::IndexEntryBuilder;
396
396
397 #[test]
397 #[test]
398 fn version_test() {
398 fn version_test() {
399 let bytes = IndexEntryBuilder::new()
399 let bytes = IndexEntryBuilder::new()
400 .is_first(true)
400 .is_first(true)
401 .with_version(1)
401 .with_version(1)
402 .build();
402 .build();
403
403
404 assert_eq!(get_version(&bytes), 1)
404 assert_eq!(get_version(&bytes), 1)
405 }
405 }
406 }
406 }
@@ -1,67 +1,67 b''
1 //! The revset query language
1 //! The revset query language
2 //!
2 //!
3 //! <https://www.mercurial-scm.org/repo/hg/help/revsets>
3 //! <https://www.mercurial-scm.org/repo/hg/help/revsets>
4
4
5 use crate::errors::HgError;
5 use crate::errors::HgError;
6 use crate::repo::Repo;
6 use crate::repo::Repo;
7 use crate::revlog::revlog::{Revlog, RevlogError};
7 use crate::revlog::revlog::{Revlog, RevlogError};
8 use crate::revlog::NodePrefix;
8 use crate::revlog::NodePrefix;
9 use crate::revlog::{Revision, NULL_REVISION, WORKING_DIRECTORY_HEX};
9 use crate::revlog::{Revision, NULL_REVISION, WORKING_DIRECTORY_HEX};
10 use crate::Node;
10 use crate::Node;
11
11
12 /// Resolve a query string into a single revision.
12 /// Resolve a query string into a single revision.
13 ///
13 ///
14 /// Only some of the revset language is implemented yet.
14 /// Only some of the revset language is implemented yet.
15 pub fn resolve_single(
15 pub fn resolve_single(
16 input: &str,
16 input: &str,
17 repo: &Repo,
17 repo: &Repo,
18 ) -> Result<Revision, RevlogError> {
18 ) -> Result<Revision, RevlogError> {
19 let changelog = repo.changelog()?;
19 let changelog = repo.changelog()?;
20
20
21 match resolve_rev_number_or_hex_prefix(input, &changelog.revlog) {
21 match resolve_rev_number_or_hex_prefix(input, &changelog.revlog) {
22 Err(RevlogError::InvalidRevision) => {} // Try other syntax
22 Err(RevlogError::InvalidRevision) => {} // Try other syntax
23 result => return result,
23 result => return result,
24 }
24 }
25
25
26 if input == "null" {
26 if input == "null" {
27 return Ok(NULL_REVISION);
27 return Ok(NULL_REVISION);
28 }
28 }
29
29
30 // TODO: support for the rest of the language here.
30 // TODO: support for the rest of the language here.
31
31
32 Err(
32 Err(
33 HgError::unsupported(format!("cannot parse revset '{}'", input))
33 HgError::unsupported(format!("cannot parse revset '{}'", input))
34 .into(),
34 .into(),
35 )
35 )
36 }
36 }
37
37
38 /// Resolve the small subset of the language suitable for revlogs other than
38 /// Resolve the small subset of the language suitable for revlogs other than
39 /// the changelog, such as in `hg debugdata --manifest` CLI argument.
39 /// the changelog, such as in `hg debugdata --manifest` CLI argument.
40 ///
40 ///
41 /// * A non-negative decimal integer for a revision number, or
41 /// * A non-negative decimal integer for a revision number, or
42 /// * An hexadecimal string, for the unique node ID that starts with this
42 /// * An hexadecimal string, for the unique node ID that starts with this
43 /// prefix
43 /// prefix
44 pub fn resolve_rev_number_or_hex_prefix(
44 pub fn resolve_rev_number_or_hex_prefix(
45 input: &str,
45 input: &str,
46 revlog: &Revlog,
46 revlog: &Revlog,
47 ) -> Result<Revision, RevlogError> {
47 ) -> Result<Revision, RevlogError> {
48 // The Python equivalent of this is part of `revsymbol` in
48 // The Python equivalent of this is part of `revsymbol` in
49 // `mercurial/scmutil.py`
49 // `mercurial/scmutil.py`
50
50
51 if let Ok(integer) = input.parse::<i32>() {
51 if let Ok(integer) = input.parse::<i32>() {
52 if integer.to_string() == input
52 if integer.to_string() == input
53 && integer >= 0
53 && integer >= 0
54 && revlog.has_rev(integer)
54 && revlog.has_rev(integer)
55 {
55 {
56 return Ok(integer);
56 return Ok(integer);
57 }
57 }
58 }
58 }
59 if let Ok(prefix) = NodePrefix::from_hex(input) {
59 if let Ok(prefix) = NodePrefix::from_hex(input) {
60 if prefix.is_prefix_of(&Node::from_hex(WORKING_DIRECTORY_HEX).unwrap())
60 if prefix.is_prefix_of(&Node::from_hex(WORKING_DIRECTORY_HEX).unwrap())
61 {
61 {
62 return Err(RevlogError::WDirUnsupported);
62 return Err(RevlogError::WDirUnsupported);
63 }
63 }
64 return revlog.get_node_rev(prefix);
64 return revlog.rev_from_node(prefix);
65 }
65 }
66 Err(RevlogError::InvalidRevision)
66 Err(RevlogError::InvalidRevision)
67 }
67 }
General Comments 0
You need to be logged in to leave comments. Login now