##// END OF EJS Templates
hg-core: use `.or(Err(Error))` not `.map_err(|_| Error)` (D9100#inline-15067)...
Antoine cezar -
r46177:be951ca9 default
parent child Browse files
Show More
@@ -1,188 +1,187 b''
1 // list_tracked_files.rs
1 // list_tracked_files.rs
2 //
2 //
3 // Copyright 2020 Antoine Cezar <antoine.cezar@octobus.net>
3 // Copyright 2020 Antoine Cezar <antoine.cezar@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::dirstate::parsers::parse_dirstate;
8 use crate::dirstate::parsers::parse_dirstate;
9 use crate::revlog::changelog::Changelog;
9 use crate::revlog::changelog::Changelog;
10 use crate::revlog::manifest::{Manifest, ManifestEntry};
10 use crate::revlog::manifest::{Manifest, ManifestEntry};
11 use crate::revlog::revlog::RevlogError;
11 use crate::revlog::revlog::RevlogError;
12 use crate::revlog::Revision;
12 use crate::revlog::Revision;
13 use crate::utils::hg_path::HgPath;
13 use crate::utils::hg_path::HgPath;
14 use crate::{DirstateParseError, EntryState};
14 use crate::{DirstateParseError, EntryState};
15 use rayon::prelude::*;
15 use rayon::prelude::*;
16 use std::convert::From;
16 use std::convert::From;
17 use std::fs;
17 use std::fs;
18 use std::path::PathBuf;
18 use std::path::PathBuf;
19
19
20 /// Kind of error encountered by `ListDirstateTrackedFiles`
20 /// Kind of error encountered by `ListDirstateTrackedFiles`
21 #[derive(Debug)]
21 #[derive(Debug)]
22 pub enum ListDirstateTrackedFilesErrorKind {
22 pub enum ListDirstateTrackedFilesErrorKind {
23 /// Error when reading the `dirstate` file
23 /// Error when reading the `dirstate` file
24 IoError(std::io::Error),
24 IoError(std::io::Error),
25 /// Error when parsing the `dirstate` file
25 /// Error when parsing the `dirstate` file
26 ParseError(DirstateParseError),
26 ParseError(DirstateParseError),
27 }
27 }
28
28
29 /// A `ListDirstateTrackedFiles` error
29 /// A `ListDirstateTrackedFiles` error
30 #[derive(Debug)]
30 #[derive(Debug)]
31 pub struct ListDirstateTrackedFilesError {
31 pub struct ListDirstateTrackedFilesError {
32 /// Kind of error encountered by `ListDirstateTrackedFiles`
32 /// Kind of error encountered by `ListDirstateTrackedFiles`
33 pub kind: ListDirstateTrackedFilesErrorKind,
33 pub kind: ListDirstateTrackedFilesErrorKind,
34 }
34 }
35
35
36 impl From<ListDirstateTrackedFilesErrorKind>
36 impl From<ListDirstateTrackedFilesErrorKind>
37 for ListDirstateTrackedFilesError
37 for ListDirstateTrackedFilesError
38 {
38 {
39 fn from(kind: ListDirstateTrackedFilesErrorKind) -> Self {
39 fn from(kind: ListDirstateTrackedFilesErrorKind) -> Self {
40 ListDirstateTrackedFilesError { kind }
40 ListDirstateTrackedFilesError { kind }
41 }
41 }
42 }
42 }
43
43
44 impl From<std::io::Error> for ListDirstateTrackedFilesError {
44 impl From<std::io::Error> for ListDirstateTrackedFilesError {
45 fn from(err: std::io::Error) -> Self {
45 fn from(err: std::io::Error) -> Self {
46 let kind = ListDirstateTrackedFilesErrorKind::IoError(err);
46 let kind = ListDirstateTrackedFilesErrorKind::IoError(err);
47 ListDirstateTrackedFilesError { kind }
47 ListDirstateTrackedFilesError { kind }
48 }
48 }
49 }
49 }
50
50
51 /// List files under Mercurial control in the working directory
51 /// List files under Mercurial control in the working directory
52 /// by reading the dirstate
52 /// by reading the dirstate
53 pub struct ListDirstateTrackedFiles {
53 pub struct ListDirstateTrackedFiles {
54 /// The `dirstate` content.
54 /// The `dirstate` content.
55 content: Vec<u8>,
55 content: Vec<u8>,
56 }
56 }
57
57
58 impl ListDirstateTrackedFiles {
58 impl ListDirstateTrackedFiles {
59 pub fn new(root: &PathBuf) -> Result<Self, ListDirstateTrackedFilesError> {
59 pub fn new(root: &PathBuf) -> Result<Self, ListDirstateTrackedFilesError> {
60 let dirstate = root.join(".hg/dirstate");
60 let dirstate = root.join(".hg/dirstate");
61 let content = fs::read(&dirstate)?;
61 let content = fs::read(&dirstate)?;
62 Ok(Self { content })
62 Ok(Self { content })
63 }
63 }
64
64
65 pub fn run(
65 pub fn run(
66 &mut self,
66 &mut self,
67 ) -> Result<Vec<&HgPath>, ListDirstateTrackedFilesError> {
67 ) -> Result<Vec<&HgPath>, ListDirstateTrackedFilesError> {
68 let (_, entries, _) = parse_dirstate(&self.content)
68 let (_, entries, _) = parse_dirstate(&self.content)
69 .map_err(ListDirstateTrackedFilesErrorKind::ParseError)?;
69 .map_err(ListDirstateTrackedFilesErrorKind::ParseError)?;
70 let mut files: Vec<&HgPath> = entries
70 let mut files: Vec<&HgPath> = entries
71 .into_iter()
71 .into_iter()
72 .filter_map(|(path, entry)| match entry.state {
72 .filter_map(|(path, entry)| match entry.state {
73 EntryState::Removed => None,
73 EntryState::Removed => None,
74 _ => Some(path),
74 _ => Some(path),
75 })
75 })
76 .collect();
76 .collect();
77 files.par_sort_unstable();
77 files.par_sort_unstable();
78 Ok(files)
78 Ok(files)
79 }
79 }
80 }
80 }
81
81
82 /// Kind of error encountered by `ListRevTrackedFiles`
82 /// Kind of error encountered by `ListRevTrackedFiles`
83 #[derive(Debug)]
83 #[derive(Debug)]
84 pub enum ListRevTrackedFilesErrorKind {
84 pub enum ListRevTrackedFilesErrorKind {
85 /// Error when reading a `revlog` file.
85 /// Error when reading a `revlog` file.
86 IoError(std::io::Error),
86 IoError(std::io::Error),
87 /// The revision has not been found.
87 /// The revision has not been found.
88 InvalidRevision,
88 InvalidRevision,
89 /// A `revlog` file is corrupted.
89 /// A `revlog` file is corrupted.
90 CorruptedRevlog,
90 CorruptedRevlog,
91 /// The `revlog` format version is not supported.
91 /// The `revlog` format version is not supported.
92 UnsuportedRevlogVersion(u16),
92 UnsuportedRevlogVersion(u16),
93 /// The `revlog` data format is not supported.
93 /// The `revlog` data format is not supported.
94 UnknowRevlogDataFormat(u8),
94 UnknowRevlogDataFormat(u8),
95 }
95 }
96
96
97 /// A `ListRevTrackedFiles` error
97 /// A `ListRevTrackedFiles` error
98 #[derive(Debug)]
98 #[derive(Debug)]
99 pub struct ListRevTrackedFilesError {
99 pub struct ListRevTrackedFilesError {
100 /// Kind of error encountered by `ListRevTrackedFiles`
100 /// Kind of error encountered by `ListRevTrackedFiles`
101 pub kind: ListRevTrackedFilesErrorKind,
101 pub kind: ListRevTrackedFilesErrorKind,
102 }
102 }
103
103
104 impl From<ListRevTrackedFilesErrorKind> for ListRevTrackedFilesError {
104 impl From<ListRevTrackedFilesErrorKind> for ListRevTrackedFilesError {
105 fn from(kind: ListRevTrackedFilesErrorKind) -> Self {
105 fn from(kind: ListRevTrackedFilesErrorKind) -> Self {
106 ListRevTrackedFilesError { kind }
106 ListRevTrackedFilesError { kind }
107 }
107 }
108 }
108 }
109
109
110 impl From<RevlogError> for ListRevTrackedFilesError {
110 impl From<RevlogError> for ListRevTrackedFilesError {
111 fn from(err: RevlogError) -> Self {
111 fn from(err: RevlogError) -> Self {
112 match err {
112 match err {
113 RevlogError::IoError(err) => {
113 RevlogError::IoError(err) => {
114 ListRevTrackedFilesErrorKind::IoError(err)
114 ListRevTrackedFilesErrorKind::IoError(err)
115 }
115 }
116 RevlogError::UnsuportedVersion(version) => {
116 RevlogError::UnsuportedVersion(version) => {
117 ListRevTrackedFilesErrorKind::UnsuportedRevlogVersion(version)
117 ListRevTrackedFilesErrorKind::UnsuportedRevlogVersion(version)
118 }
118 }
119 RevlogError::InvalidRevision => {
119 RevlogError::InvalidRevision => {
120 ListRevTrackedFilesErrorKind::InvalidRevision
120 ListRevTrackedFilesErrorKind::InvalidRevision
121 }
121 }
122 RevlogError::Corrupted => {
122 RevlogError::Corrupted => {
123 ListRevTrackedFilesErrorKind::CorruptedRevlog
123 ListRevTrackedFilesErrorKind::CorruptedRevlog
124 }
124 }
125 RevlogError::UnknowDataFormat(format) => {
125 RevlogError::UnknowDataFormat(format) => {
126 ListRevTrackedFilesErrorKind::UnknowRevlogDataFormat(format)
126 ListRevTrackedFilesErrorKind::UnknowRevlogDataFormat(format)
127 }
127 }
128 }
128 }
129 .into()
129 .into()
130 }
130 }
131 }
131 }
132
132
133 /// List files under Mercurial control at a given revision.
133 /// List files under Mercurial control at a given revision.
134 pub struct ListRevTrackedFiles<'a> {
134 pub struct ListRevTrackedFiles<'a> {
135 /// The revision to list the files from.
135 /// The revision to list the files from.
136 rev: &'a str,
136 rev: &'a str,
137 /// The changelog file
137 /// The changelog file
138 changelog: Changelog,
138 changelog: Changelog,
139 /// The manifest file
139 /// The manifest file
140 manifest: Manifest,
140 manifest: Manifest,
141 /// The manifest entry corresponding to the revision.
141 /// The manifest entry corresponding to the revision.
142 ///
142 ///
143 /// Used to hold the owner of the returned references.
143 /// Used to hold the owner of the returned references.
144 manifest_entry: Option<ManifestEntry>,
144 manifest_entry: Option<ManifestEntry>,
145 }
145 }
146
146
147 impl<'a> ListRevTrackedFiles<'a> {
147 impl<'a> ListRevTrackedFiles<'a> {
148 pub fn new(
148 pub fn new(
149 root: &PathBuf,
149 root: &PathBuf,
150 rev: &'a str,
150 rev: &'a str,
151 ) -> Result<Self, ListRevTrackedFilesError> {
151 ) -> Result<Self, ListRevTrackedFilesError> {
152 let changelog = Changelog::open(&root)?;
152 let changelog = Changelog::open(&root)?;
153 let manifest = Manifest::open(&root)?;
153 let manifest = Manifest::open(&root)?;
154
154
155 Ok(Self {
155 Ok(Self {
156 rev,
156 rev,
157 changelog,
157 changelog,
158 manifest,
158 manifest,
159 manifest_entry: None,
159 manifest_entry: None,
160 })
160 })
161 }
161 }
162
162
163 pub fn run(
163 pub fn run(
164 &mut self,
164 &mut self,
165 ) -> Result<impl Iterator<Item = &HgPath>, ListRevTrackedFilesError> {
165 ) -> Result<impl Iterator<Item = &HgPath>, ListRevTrackedFilesError> {
166 let changelog_entry = match self.rev.parse::<Revision>() {
166 let changelog_entry = match self.rev.parse::<Revision>() {
167 Ok(rev) => self.changelog.get_rev(rev)?,
167 Ok(rev) => self.changelog.get_rev(rev)?,
168 _ => {
168 _ => {
169 let changelog_node = hex::decode(&self.rev).map_err(|_| {
169 let changelog_node = hex::decode(&self.rev)
170 ListRevTrackedFilesErrorKind::InvalidRevision
170 .or(Err(ListRevTrackedFilesErrorKind::InvalidRevision))?;
171 })?;
172 self.changelog.get_node(&changelog_node)?
171 self.changelog.get_node(&changelog_node)?
173 }
172 }
174 };
173 };
175 let manifest_node = hex::decode(&changelog_entry.manifest_node()?)
174 let manifest_node = hex::decode(&changelog_entry.manifest_node()?)
176 .map_err(|_| ListRevTrackedFilesErrorKind::CorruptedRevlog)?;
175 .or(Err(ListRevTrackedFilesErrorKind::CorruptedRevlog))?;
177
176
178 self.manifest_entry = Some(self.manifest.get_node(&manifest_node)?);
177 self.manifest_entry = Some(self.manifest.get_node(&manifest_node)?);
179
178
180 if let Some(ref manifest_entry) = self.manifest_entry {
179 if let Some(ref manifest_entry) = self.manifest_entry {
181 Ok(manifest_entry.files())
180 Ok(manifest_entry.files())
182 } else {
181 } else {
183 panic!(
182 panic!(
184 "manifest entry should have been stored in self.manifest_node to ensure its lifetime since references are returned from it"
183 "manifest entry should have been stored in self.manifest_node to ensure its lifetime since references are returned from it"
185 )
184 )
186 }
185 }
187 }
186 }
188 }
187 }
@@ -1,332 +1,331 b''
1 use std::borrow::Cow;
1 use std::borrow::Cow;
2 use std::fs::File;
2 use std::fs::File;
3 use std::io::Read;
3 use std::io::Read;
4 use std::ops::Deref;
4 use std::ops::Deref;
5 use std::path::Path;
5 use std::path::Path;
6
6
7 use byteorder::{BigEndian, ByteOrder};
7 use byteorder::{BigEndian, ByteOrder};
8 use crypto::digest::Digest;
8 use crypto::digest::Digest;
9 use crypto::sha1::Sha1;
9 use crypto::sha1::Sha1;
10 use flate2::read::ZlibDecoder;
10 use flate2::read::ZlibDecoder;
11 use memmap::{Mmap, MmapOptions};
11 use memmap::{Mmap, MmapOptions};
12 use micro_timer::timed;
12 use micro_timer::timed;
13 use zstd;
13 use zstd;
14
14
15 use super::index::Index;
15 use super::index::Index;
16 use super::node::{NODE_BYTES_LENGTH, NULL_NODE_ID};
16 use super::node::{NODE_BYTES_LENGTH, NULL_NODE_ID};
17 use super::patch;
17 use super::patch;
18 use crate::revlog::Revision;
18 use crate::revlog::Revision;
19
19
20 pub enum RevlogError {
20 pub enum RevlogError {
21 IoError(std::io::Error),
21 IoError(std::io::Error),
22 UnsuportedVersion(u16),
22 UnsuportedVersion(u16),
23 InvalidRevision,
23 InvalidRevision,
24 Corrupted,
24 Corrupted,
25 UnknowDataFormat(u8),
25 UnknowDataFormat(u8),
26 }
26 }
27
27
28 fn mmap_open(path: &Path) -> Result<Mmap, std::io::Error> {
28 fn mmap_open(path: &Path) -> Result<Mmap, std::io::Error> {
29 let file = File::open(path)?;
29 let file = File::open(path)?;
30 let mmap = unsafe { MmapOptions::new().map(&file) }?;
30 let mmap = unsafe { MmapOptions::new().map(&file) }?;
31 Ok(mmap)
31 Ok(mmap)
32 }
32 }
33
33
34 /// Read only implementation of revlog.
34 /// Read only implementation of revlog.
35 pub struct Revlog {
35 pub struct Revlog {
36 /// When index and data are not interleaved: bytes of the revlog index.
36 /// When index and data are not interleaved: bytes of the revlog index.
37 /// When index and data are interleaved: bytes of the revlog index and
37 /// When index and data are interleaved: bytes of the revlog index and
38 /// data.
38 /// data.
39 index: Index,
39 index: Index,
40 /// When index and data are not interleaved: bytes of the revlog data
40 /// When index and data are not interleaved: bytes of the revlog data
41 data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>>,
41 data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>>,
42 }
42 }
43
43
44 impl Revlog {
44 impl Revlog {
45 /// Open a revlog index file.
45 /// Open a revlog index file.
46 ///
46 ///
47 /// It will also open the associated data file if index and data are not
47 /// It will also open the associated data file if index and data are not
48 /// interleaved.
48 /// interleaved.
49 #[timed]
49 #[timed]
50 pub fn open(index_path: &Path) -> Result<Self, RevlogError> {
50 pub fn open(index_path: &Path) -> Result<Self, RevlogError> {
51 let index_mmap =
51 let index_mmap =
52 mmap_open(&index_path).map_err(RevlogError::IoError)?;
52 mmap_open(&index_path).map_err(RevlogError::IoError)?;
53
53
54 let version = get_version(&index_mmap);
54 let version = get_version(&index_mmap);
55 if version != 1 {
55 if version != 1 {
56 return Err(RevlogError::UnsuportedVersion(version));
56 return Err(RevlogError::UnsuportedVersion(version));
57 }
57 }
58
58
59 let index = Index::new(Box::new(index_mmap))?;
59 let index = Index::new(Box::new(index_mmap))?;
60
60
61 // TODO load data only when needed //
61 // TODO load data only when needed //
62 // type annotation required
62 // type annotation required
63 // won't recognize Mmap as Deref<Target = [u8]>
63 // won't recognize Mmap as Deref<Target = [u8]>
64 let data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>> =
64 let data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>> =
65 if index.is_inline() {
65 if index.is_inline() {
66 None
66 None
67 } else {
67 } else {
68 let data_path = index_path.with_extension("d");
68 let data_path = index_path.with_extension("d");
69 let data_mmap =
69 let data_mmap =
70 mmap_open(&data_path).map_err(RevlogError::IoError)?;
70 mmap_open(&data_path).map_err(RevlogError::IoError)?;
71 Some(Box::new(data_mmap))
71 Some(Box::new(data_mmap))
72 };
72 };
73
73
74 Ok(Revlog { index, data_bytes })
74 Ok(Revlog { index, data_bytes })
75 }
75 }
76
76
77 /// Return number of entries of the `Revlog`.
77 /// Return number of entries of the `Revlog`.
78 pub fn len(&self) -> usize {
78 pub fn len(&self) -> usize {
79 self.index.len()
79 self.index.len()
80 }
80 }
81
81
82 /// Returns `true` if the `Revlog` has zero `entries`.
82 /// Returns `true` if the `Revlog` has zero `entries`.
83 pub fn is_empty(&self) -> bool {
83 pub fn is_empty(&self) -> bool {
84 self.index.is_empty()
84 self.index.is_empty()
85 }
85 }
86
86
87 /// Return the full data associated to a node.
87 /// Return the full data associated to a node.
88 #[timed]
88 #[timed]
89 pub fn get_node_rev(&self, node: &[u8]) -> Result<Revision, RevlogError> {
89 pub fn get_node_rev(&self, node: &[u8]) -> Result<Revision, RevlogError> {
90 // This is brute force. But it is fast enough for now.
90 // This is brute force. But it is fast enough for now.
91 // Optimization will come later.
91 // Optimization will come later.
92 for rev in (0..self.len() as Revision).rev() {
92 for rev in (0..self.len() as Revision).rev() {
93 let index_entry =
93 let index_entry =
94 self.index.get_entry(rev).ok_or(RevlogError::Corrupted)?;
94 self.index.get_entry(rev).ok_or(RevlogError::Corrupted)?;
95 if node == index_entry.hash() {
95 if node == index_entry.hash() {
96 return Ok(rev);
96 return Ok(rev);
97 }
97 }
98 }
98 }
99 Err(RevlogError::InvalidRevision)
99 Err(RevlogError::InvalidRevision)
100 }
100 }
101
101
102 /// Return the full data associated to a revision.
102 /// Return the full data associated to a revision.
103 ///
103 ///
104 /// All entries required to build the final data out of deltas will be
104 /// All entries required to build the final data out of deltas will be
105 /// retrieved as needed, and the deltas will be applied to the inital
105 /// retrieved as needed, and the deltas will be applied to the inital
106 /// snapshot to rebuild the final data.
106 /// snapshot to rebuild the final data.
107 #[timed]
107 #[timed]
108 pub fn get_rev_data(&self, rev: Revision) -> Result<Vec<u8>, RevlogError> {
108 pub fn get_rev_data(&self, rev: Revision) -> Result<Vec<u8>, RevlogError> {
109 // Todo return -> Cow
109 // Todo return -> Cow
110 let mut entry = self.get_entry(rev)?;
110 let mut entry = self.get_entry(rev)?;
111 let mut delta_chain = vec![];
111 let mut delta_chain = vec![];
112 while let Some(base_rev) = entry.base_rev {
112 while let Some(base_rev) = entry.base_rev {
113 delta_chain.push(entry);
113 delta_chain.push(entry);
114 entry = self
114 entry =
115 .get_entry(base_rev)
115 self.get_entry(base_rev).or(Err(RevlogError::Corrupted))?;
116 .map_err(|_| RevlogError::Corrupted)?;
117 }
116 }
118
117
119 // TODO do not look twice in the index
118 // TODO do not look twice in the index
120 let index_entry = self
119 let index_entry = self
121 .index
120 .index
122 .get_entry(rev)
121 .get_entry(rev)
123 .ok_or(RevlogError::InvalidRevision)?;
122 .ok_or(RevlogError::InvalidRevision)?;
124
123
125 let data: Vec<u8> = if delta_chain.is_empty() {
124 let data: Vec<u8> = if delta_chain.is_empty() {
126 entry.data()?.into()
125 entry.data()?.into()
127 } else {
126 } else {
128 Revlog::build_data_from_deltas(entry, &delta_chain)?
127 Revlog::build_data_from_deltas(entry, &delta_chain)?
129 };
128 };
130
129
131 if self.check_hash(
130 if self.check_hash(
132 index_entry.p1(),
131 index_entry.p1(),
133 index_entry.p2(),
132 index_entry.p2(),
134 index_entry.hash(),
133 index_entry.hash(),
135 &data,
134 &data,
136 ) {
135 ) {
137 Ok(data)
136 Ok(data)
138 } else {
137 } else {
139 Err(RevlogError::Corrupted)
138 Err(RevlogError::Corrupted)
140 }
139 }
141 }
140 }
142
141
143 /// Check the hash of some given data against the recorded hash.
142 /// Check the hash of some given data against the recorded hash.
144 pub fn check_hash(
143 pub fn check_hash(
145 &self,
144 &self,
146 p1: Revision,
145 p1: Revision,
147 p2: Revision,
146 p2: Revision,
148 expected: &[u8],
147 expected: &[u8],
149 data: &[u8],
148 data: &[u8],
150 ) -> bool {
149 ) -> bool {
151 let e1 = self.index.get_entry(p1);
150 let e1 = self.index.get_entry(p1);
152 let h1 = match e1 {
151 let h1 = match e1 {
153 Some(ref entry) => entry.hash(),
152 Some(ref entry) => entry.hash(),
154 None => &NULL_NODE_ID,
153 None => &NULL_NODE_ID,
155 };
154 };
156 let e2 = self.index.get_entry(p2);
155 let e2 = self.index.get_entry(p2);
157 let h2 = match e2 {
156 let h2 = match e2 {
158 Some(ref entry) => entry.hash(),
157 Some(ref entry) => entry.hash(),
159 None => &NULL_NODE_ID,
158 None => &NULL_NODE_ID,
160 };
159 };
161
160
162 hash(data, &h1, &h2).as_slice() == expected
161 hash(data, &h1, &h2).as_slice() == expected
163 }
162 }
164
163
165 /// Build the full data of a revision out its snapshot
164 /// Build the full data of a revision out its snapshot
166 /// and its deltas.
165 /// and its deltas.
167 #[timed]
166 #[timed]
168 fn build_data_from_deltas(
167 fn build_data_from_deltas(
169 snapshot: RevlogEntry,
168 snapshot: RevlogEntry,
170 deltas: &[RevlogEntry],
169 deltas: &[RevlogEntry],
171 ) -> Result<Vec<u8>, RevlogError> {
170 ) -> Result<Vec<u8>, RevlogError> {
172 let snapshot = snapshot.data()?;
171 let snapshot = snapshot.data()?;
173 let deltas = deltas
172 let deltas = deltas
174 .iter()
173 .iter()
175 .rev()
174 .rev()
176 .map(RevlogEntry::data)
175 .map(RevlogEntry::data)
177 .collect::<Result<Vec<Cow<'_, [u8]>>, RevlogError>>()?;
176 .collect::<Result<Vec<Cow<'_, [u8]>>, RevlogError>>()?;
178 let patches: Vec<_> =
177 let patches: Vec<_> =
179 deltas.iter().map(|d| patch::PatchList::new(d)).collect();
178 deltas.iter().map(|d| patch::PatchList::new(d)).collect();
180 let patch = patch::fold_patch_lists(&patches);
179 let patch = patch::fold_patch_lists(&patches);
181 Ok(patch.apply(&snapshot))
180 Ok(patch.apply(&snapshot))
182 }
181 }
183
182
184 /// Return the revlog data.
183 /// Return the revlog data.
185 fn data(&self) -> &[u8] {
184 fn data(&self) -> &[u8] {
186 match self.data_bytes {
185 match self.data_bytes {
187 Some(ref data_bytes) => &data_bytes,
186 Some(ref data_bytes) => &data_bytes,
188 None => panic!(
187 None => panic!(
189 "forgot to load the data or trying to access inline data"
188 "forgot to load the data or trying to access inline data"
190 ),
189 ),
191 }
190 }
192 }
191 }
193
192
194 /// Get an entry of the revlog.
193 /// Get an entry of the revlog.
195 fn get_entry(&self, rev: Revision) -> Result<RevlogEntry, RevlogError> {
194 fn get_entry(&self, rev: Revision) -> Result<RevlogEntry, RevlogError> {
196 let index_entry = self
195 let index_entry = self
197 .index
196 .index
198 .get_entry(rev)
197 .get_entry(rev)
199 .ok_or(RevlogError::InvalidRevision)?;
198 .ok_or(RevlogError::InvalidRevision)?;
200 let start = index_entry.offset();
199 let start = index_entry.offset();
201 let end = start + index_entry.compressed_len();
200 let end = start + index_entry.compressed_len();
202 let data = if self.index.is_inline() {
201 let data = if self.index.is_inline() {
203 self.index.data(start, end)
202 self.index.data(start, end)
204 } else {
203 } else {
205 &self.data()[start..end]
204 &self.data()[start..end]
206 };
205 };
207 let entry = RevlogEntry {
206 let entry = RevlogEntry {
208 rev,
207 rev,
209 bytes: data,
208 bytes: data,
210 compressed_len: index_entry.compressed_len(),
209 compressed_len: index_entry.compressed_len(),
211 uncompressed_len: index_entry.uncompressed_len(),
210 uncompressed_len: index_entry.uncompressed_len(),
212 base_rev: if index_entry.base_revision() == rev {
211 base_rev: if index_entry.base_revision() == rev {
213 None
212 None
214 } else {
213 } else {
215 Some(index_entry.base_revision())
214 Some(index_entry.base_revision())
216 },
215 },
217 };
216 };
218 Ok(entry)
217 Ok(entry)
219 }
218 }
220 }
219 }
221
220
222 /// The revlog entry's bytes and the necessary informations to extract
221 /// The revlog entry's bytes and the necessary informations to extract
223 /// the entry's data.
222 /// the entry's data.
224 #[derive(Debug)]
223 #[derive(Debug)]
225 pub struct RevlogEntry<'a> {
224 pub struct RevlogEntry<'a> {
226 rev: Revision,
225 rev: Revision,
227 bytes: &'a [u8],
226 bytes: &'a [u8],
228 compressed_len: usize,
227 compressed_len: usize,
229 uncompressed_len: usize,
228 uncompressed_len: usize,
230 base_rev: Option<Revision>,
229 base_rev: Option<Revision>,
231 }
230 }
232
231
233 impl<'a> RevlogEntry<'a> {
232 impl<'a> RevlogEntry<'a> {
234 /// Extract the data contained in the entry.
233 /// Extract the data contained in the entry.
235 pub fn data(&self) -> Result<Cow<'_, [u8]>, RevlogError> {
234 pub fn data(&self) -> Result<Cow<'_, [u8]>, RevlogError> {
236 if self.bytes.is_empty() {
235 if self.bytes.is_empty() {
237 return Ok(Cow::Borrowed(&[]));
236 return Ok(Cow::Borrowed(&[]));
238 }
237 }
239 match self.bytes[0] {
238 match self.bytes[0] {
240 // Revision data is the entirety of the entry, including this
239 // Revision data is the entirety of the entry, including this
241 // header.
240 // header.
242 b'\0' => Ok(Cow::Borrowed(self.bytes)),
241 b'\0' => Ok(Cow::Borrowed(self.bytes)),
243 // Raw revision data follows.
242 // Raw revision data follows.
244 b'u' => Ok(Cow::Borrowed(&self.bytes[1..])),
243 b'u' => Ok(Cow::Borrowed(&self.bytes[1..])),
245 // zlib (RFC 1950) data.
244 // zlib (RFC 1950) data.
246 b'x' => Ok(Cow::Owned(self.uncompressed_zlib_data()?)),
245 b'x' => Ok(Cow::Owned(self.uncompressed_zlib_data()?)),
247 // zstd data.
246 // zstd data.
248 b'\x28' => Ok(Cow::Owned(self.uncompressed_zstd_data()?)),
247 b'\x28' => Ok(Cow::Owned(self.uncompressed_zstd_data()?)),
249 format_type => Err(RevlogError::UnknowDataFormat(format_type)),
248 format_type => Err(RevlogError::UnknowDataFormat(format_type)),
250 }
249 }
251 }
250 }
252
251
253 fn uncompressed_zlib_data(&self) -> Result<Vec<u8>, RevlogError> {
252 fn uncompressed_zlib_data(&self) -> Result<Vec<u8>, RevlogError> {
254 let mut decoder = ZlibDecoder::new(self.bytes);
253 let mut decoder = ZlibDecoder::new(self.bytes);
255 if self.is_delta() {
254 if self.is_delta() {
256 let mut buf = Vec::with_capacity(self.compressed_len);
255 let mut buf = Vec::with_capacity(self.compressed_len);
257 decoder
256 decoder
258 .read_to_end(&mut buf)
257 .read_to_end(&mut buf)
259 .or(Err(RevlogError::Corrupted))?;
258 .or(Err(RevlogError::Corrupted))?;
260 Ok(buf)
259 Ok(buf)
261 } else {
260 } else {
262 let mut buf = vec![0; self.uncompressed_len];
261 let mut buf = vec![0; self.uncompressed_len];
263 decoder
262 decoder
264 .read_exact(&mut buf)
263 .read_exact(&mut buf)
265 .or(Err(RevlogError::Corrupted))?;
264 .or(Err(RevlogError::Corrupted))?;
266 Ok(buf)
265 Ok(buf)
267 }
266 }
268 }
267 }
269
268
270 fn uncompressed_zstd_data(&self) -> Result<Vec<u8>, RevlogError> {
269 fn uncompressed_zstd_data(&self) -> Result<Vec<u8>, RevlogError> {
271 if self.is_delta() {
270 if self.is_delta() {
272 let mut buf = Vec::with_capacity(self.compressed_len);
271 let mut buf = Vec::with_capacity(self.compressed_len);
273 zstd::stream::copy_decode(self.bytes, &mut buf)
272 zstd::stream::copy_decode(self.bytes, &mut buf)
274 .or(Err(RevlogError::Corrupted))?;
273 .or(Err(RevlogError::Corrupted))?;
275 Ok(buf)
274 Ok(buf)
276 } else {
275 } else {
277 let mut buf = vec![0; self.uncompressed_len];
276 let mut buf = vec![0; self.uncompressed_len];
278 let len = zstd::block::decompress_to_buffer(self.bytes, &mut buf)
277 let len = zstd::block::decompress_to_buffer(self.bytes, &mut buf)
279 .or(Err(RevlogError::Corrupted))?;
278 .or(Err(RevlogError::Corrupted))?;
280 if len != self.uncompressed_len {
279 if len != self.uncompressed_len {
281 Err(RevlogError::Corrupted)
280 Err(RevlogError::Corrupted)
282 } else {
281 } else {
283 Ok(buf)
282 Ok(buf)
284 }
283 }
285 }
284 }
286 }
285 }
287
286
288 /// Tell if the entry is a snapshot or a delta
287 /// Tell if the entry is a snapshot or a delta
289 /// (influences on decompression).
288 /// (influences on decompression).
290 fn is_delta(&self) -> bool {
289 fn is_delta(&self) -> bool {
291 self.base_rev.is_some()
290 self.base_rev.is_some()
292 }
291 }
293 }
292 }
294
293
295 /// Format version of the revlog.
294 /// Format version of the revlog.
296 pub fn get_version(index_bytes: &[u8]) -> u16 {
295 pub fn get_version(index_bytes: &[u8]) -> u16 {
297 BigEndian::read_u16(&index_bytes[2..=3])
296 BigEndian::read_u16(&index_bytes[2..=3])
298 }
297 }
299
298
300 /// Calculate the hash of a revision given its data and its parents.
299 /// Calculate the hash of a revision given its data and its parents.
301 fn hash(data: &[u8], p1_hash: &[u8], p2_hash: &[u8]) -> Vec<u8> {
300 fn hash(data: &[u8], p1_hash: &[u8], p2_hash: &[u8]) -> Vec<u8> {
302 let mut hasher = Sha1::new();
301 let mut hasher = Sha1::new();
303 let (a, b) = (p1_hash, p2_hash);
302 let (a, b) = (p1_hash, p2_hash);
304 if a > b {
303 if a > b {
305 hasher.input(b);
304 hasher.input(b);
306 hasher.input(a);
305 hasher.input(a);
307 } else {
306 } else {
308 hasher.input(a);
307 hasher.input(a);
309 hasher.input(b);
308 hasher.input(b);
310 }
309 }
311 hasher.input(data);
310 hasher.input(data);
312 let mut hash = vec![0; NODE_BYTES_LENGTH];
311 let mut hash = vec![0; NODE_BYTES_LENGTH];
313 hasher.result(&mut hash);
312 hasher.result(&mut hash);
314 hash
313 hash
315 }
314 }
316
315
317 #[cfg(test)]
316 #[cfg(test)]
318 mod tests {
317 mod tests {
319 use super::*;
318 use super::*;
320
319
321 use super::super::index::IndexEntryBuilder;
320 use super::super::index::IndexEntryBuilder;
322
321
323 #[test]
322 #[test]
324 fn version_test() {
323 fn version_test() {
325 let bytes = IndexEntryBuilder::new()
324 let bytes = IndexEntryBuilder::new()
326 .is_first(true)
325 .is_first(true)
327 .with_version(1)
326 .with_version(1)
328 .build();
327 .build();
329
328
330 assert_eq!(get_version(&bytes), 1)
329 assert_eq!(get_version(&bytes), 1)
331 }
330 }
332 }
331 }
General Comments 0
You need to be logged in to leave comments. Login now