Show More
@@ -0,0 +1,79 b'' | |||||
|
1 | use crate::errors::HgError; | |||
|
2 | use crate::repo::Repo; | |||
|
3 | use crate::revlog::path_encode::path_encode; | |||
|
4 | use crate::revlog::revlog::{Revlog, RevlogError}; | |||
|
5 | use crate::revlog::NodePrefix; | |||
|
6 | use crate::revlog::Revision; | |||
|
7 | use crate::utils::files::get_path_from_bytes; | |||
|
8 | use crate::utils::hg_path::HgPath; | |||
|
9 | use crate::utils::SliceExt; | |||
|
10 | use std::borrow::Cow; | |||
|
11 | use std::path::PathBuf; | |||
|
12 | ||||
|
13 | /// A specialized `Revlog` to work with file data logs. | |||
|
14 | pub struct Filelog { | |||
|
15 | /// The generic `revlog` format. | |||
|
16 | revlog: Revlog, | |||
|
17 | } | |||
|
18 | ||||
|
19 | impl Filelog { | |||
|
20 | pub fn open(repo: &Repo, file_path: &HgPath) -> Result<Self, RevlogError> { | |||
|
21 | let index_path = store_path(file_path, b".i"); | |||
|
22 | let data_path = store_path(file_path, b".d"); | |||
|
23 | let revlog = Revlog::open(repo, index_path, Some(&data_path))?; | |||
|
24 | Ok(Self { revlog }) | |||
|
25 | } | |||
|
26 | ||||
|
27 | /// The given node ID is that of the file as found in a manifest, not of a | |||
|
28 | /// changeset. | |||
|
29 | pub fn get_node( | |||
|
30 | &self, | |||
|
31 | file_node: impl Into<NodePrefix>, | |||
|
32 | ) -> Result<FilelogEntry, RevlogError> { | |||
|
33 | let file_rev = self.revlog.get_node_rev(file_node.into())?; | |||
|
34 | self.get_rev(file_rev) | |||
|
35 | } | |||
|
36 | ||||
|
37 | /// The given revision is that of the file as found in a manifest, not of a | |||
|
38 | /// changeset. | |||
|
39 | pub fn get_rev( | |||
|
40 | &self, | |||
|
41 | file_rev: Revision, | |||
|
42 | ) -> Result<FilelogEntry, RevlogError> { | |||
|
43 | let data = self.revlog.get_rev_data(file_rev)?; | |||
|
44 | Ok(FilelogEntry(data.into())) | |||
|
45 | } | |||
|
46 | } | |||
|
47 | ||||
|
48 | fn store_path(hg_path: &HgPath, suffix: &[u8]) -> PathBuf { | |||
|
49 | let encoded_bytes = | |||
|
50 | path_encode(&[b"data/", hg_path.as_bytes(), suffix].concat()); | |||
|
51 | get_path_from_bytes(&encoded_bytes).into() | |||
|
52 | } | |||
|
53 | ||||
|
54 | pub struct FilelogEntry<'filelog>(Cow<'filelog, [u8]>); | |||
|
55 | ||||
|
56 | impl<'filelog> FilelogEntry<'filelog> { | |||
|
57 | /// Split into metadata and data | |||
|
58 | pub fn split(&self) -> Result<(Option<&[u8]>, &[u8]), HgError> { | |||
|
59 | const DELIMITER: &[u8; 2] = &[b'\x01', b'\n']; | |||
|
60 | ||||
|
61 | if let Some(rest) = self.0.drop_prefix(DELIMITER) { | |||
|
62 | if let Some((metadata, data)) = rest.split_2_by_slice(DELIMITER) { | |||
|
63 | Ok((Some(metadata), data)) | |||
|
64 | } else { | |||
|
65 | Err(HgError::corrupted( | |||
|
66 | "Missing metadata end delimiter in filelog entry", | |||
|
67 | )) | |||
|
68 | } | |||
|
69 | } else { | |||
|
70 | Ok((None, &self.0)) | |||
|
71 | } | |||
|
72 | } | |||
|
73 | ||||
|
74 | /// Returns the file contents at this revision, stripped of any metadata | |||
|
75 | pub fn data(&self) -> Result<&[u8], HgError> { | |||
|
76 | let (_metadata, data) = self.split()?; | |||
|
77 | Ok(data) | |||
|
78 | } | |||
|
79 | } |
@@ -5,15 +5,11 b'' | |||||
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 std::path::PathBuf; |
|
|||
9 |
|
||||
10 | use crate::repo::Repo; |
|
8 | use crate::repo::Repo; | |
11 | use crate::revlog::path_encode::path_encode; |
|
|||
12 | use crate::revlog::revlog::Revlog; |
|
|||
13 | use crate::revlog::revlog::RevlogError; |
|
9 | use crate::revlog::revlog::RevlogError; | |
14 | use crate::revlog::Node; |
|
10 | use crate::revlog::Node; | |
15 | use crate::utils::files::get_path_from_bytes; |
|
11 | ||
16 |
use crate::utils::hg_path:: |
|
12 | use crate::utils::hg_path::HgPathBuf; | |
17 |
|
13 | |||
18 | pub struct CatOutput { |
|
14 | pub struct CatOutput { | |
19 | /// Whether any file in the manifest matched the paths given as CLI |
|
15 | /// Whether any file in the manifest matched the paths given as CLI | |
@@ -27,8 +23,6 b' pub struct CatOutput {' | |||||
27 | pub node: Node, |
|
23 | pub node: Node, | |
28 | } |
|
24 | } | |
29 |
|
25 | |||
30 | const METADATA_DELIMITER: [u8; 2] = [b'\x01', b'\n']; |
|
|||
31 |
|
||||
32 | /// Output the given revision of files |
|
26 | /// Output the given revision of files | |
33 | /// |
|
27 | /// | |
34 | /// * `root`: Repository root |
|
28 | /// * `root`: Repository root | |
@@ -54,26 +48,10 b" pub fn cat<'a>(" | |||||
54 | if cat_file.as_bytes() == manifest_file.as_bytes() { |
|
48 | if cat_file.as_bytes() == manifest_file.as_bytes() { | |
55 | *is_matched = true; |
|
49 | *is_matched = true; | |
56 | found_any = true; |
|
50 | found_any = true; | |
57 |
let |
|
51 | let file_log = repo.filelog(manifest_file)?; | |
58 | let data_path = store_path(manifest_file, b".d"); |
|
|||
59 |
|
||||
60 | let file_log = |
|
|||
61 | Revlog::open(repo, &index_path, Some(&data_path))?; |
|
|||
62 | let file_node = Node::from_hex_for_repo(node_bytes)?; |
|
52 | let file_node = Node::from_hex_for_repo(node_bytes)?; | |
63 |
let |
|
53 | let entry = file_log.get_node(file_node)?; | |
64 | let data = file_log.get_rev_data(file_rev)?; |
|
54 | bytes.extend(entry.data()?) | |
65 | if data.starts_with(&METADATA_DELIMITER) { |
|
|||
66 | let end_delimiter_position = data |
|
|||
67 | [METADATA_DELIMITER.len()..] |
|
|||
68 | .windows(METADATA_DELIMITER.len()) |
|
|||
69 | .position(|bytes| bytes == METADATA_DELIMITER); |
|
|||
70 | if let Some(position) = end_delimiter_position { |
|
|||
71 | let offset = METADATA_DELIMITER.len() * 2; |
|
|||
72 | bytes.extend(data[position + offset..].iter()); |
|
|||
73 | } |
|
|||
74 | } else { |
|
|||
75 | bytes.extend(data); |
|
|||
76 | } |
|
|||
77 | } |
|
55 | } | |
78 | } |
|
56 | } | |
79 | } |
|
57 | } | |
@@ -91,9 +69,3 b" pub fn cat<'a>(" | |||||
91 | node, |
|
69 | node, | |
92 | }) |
|
70 | }) | |
93 | } |
|
71 | } | |
94 |
|
||||
95 | fn store_path(hg_path: &HgPath, suffix: &[u8]) -> PathBuf { |
|
|||
96 | let encoded_bytes = |
|
|||
97 | path_encode(&[b"data/", hg_path.as_bytes(), suffix].concat()); |
|
|||
98 | get_path_from_bytes(&encoded_bytes).into() |
|
|||
99 | } |
|
@@ -7,8 +7,10 b' use crate::errors::HgError;' | |||||
7 | use crate::errors::HgResultExt; |
|
7 | use crate::errors::HgResultExt; | |
8 | use crate::manifest::{Manifest, Manifestlog}; |
|
8 | use crate::manifest::{Manifest, Manifestlog}; | |
9 | use crate::requirements; |
|
9 | use crate::requirements; | |
|
10 | use crate::revlog::filelog::Filelog; | |||
10 | use crate::revlog::revlog::RevlogError; |
|
11 | use crate::revlog::revlog::RevlogError; | |
11 | use crate::utils::files::get_path_from_bytes; |
|
12 | use crate::utils::files::get_path_from_bytes; | |
|
13 | use crate::utils::hg_path::HgPath; | |||
12 | use crate::utils::SliceExt; |
|
14 | use crate::utils::SliceExt; | |
13 | use crate::vfs::{is_dir, is_file, Vfs}; |
|
15 | use crate::vfs::{is_dir, is_file, Vfs}; | |
14 | use crate::{exit_codes, Node}; |
|
16 | use crate::{exit_codes, Node}; | |
@@ -346,6 +348,10 b' impl Repo {' | |||||
346 | Node::from_hex_for_repo(&changelog_entry.manifest_node()?)?; |
|
348 | Node::from_hex_for_repo(&changelog_entry.manifest_node()?)?; | |
347 | manifest.get_node(manifest_node.into()) |
|
349 | manifest.get_node(manifest_node.into()) | |
348 | } |
|
350 | } | |
|
351 | ||||
|
352 | pub fn filelog(&self, path: &HgPath) -> Result<Filelog, RevlogError> { | |||
|
353 | Filelog::open(self, path) | |||
|
354 | } | |||
349 | } |
|
355 | } | |
350 |
|
356 | |||
351 | /// Lazily-initialized component of `Repo` with interior mutability |
|
357 | /// Lazily-initialized component of `Repo` with interior mutability |
@@ -11,6 +11,7 b' mod nodemap_docket;' | |||||
11 | pub mod path_encode; |
|
11 | pub mod path_encode; | |
12 | pub use node::{FromHexError, Node, NodePrefix}; |
|
12 | pub use node::{FromHexError, Node, NodePrefix}; | |
13 | pub mod changelog; |
|
13 | pub mod changelog; | |
|
14 | pub mod filelog; | |||
14 | pub mod index; |
|
15 | pub mod index; | |
15 | pub mod manifest; |
|
16 | pub mod manifest; | |
16 | pub mod patch; |
|
17 | pub mod patch; |
@@ -74,6 +74,7 b' pub trait SliceExt {' | |||||
74 | fn trim(&self) -> &Self; |
|
74 | fn trim(&self) -> &Self; | |
75 | fn drop_prefix(&self, needle: &Self) -> Option<&Self>; |
|
75 | fn drop_prefix(&self, needle: &Self) -> Option<&Self>; | |
76 | fn split_2(&self, separator: u8) -> Option<(&[u8], &[u8])>; |
|
76 | fn split_2(&self, separator: u8) -> Option<(&[u8], &[u8])>; | |
|
77 | fn split_2_by_slice(&self, separator: &[u8]) -> Option<(&[u8], &[u8])>; | |||
77 | } |
|
78 | } | |
78 |
|
79 | |||
79 | impl SliceExt for [u8] { |
|
80 | impl SliceExt for [u8] { | |
@@ -134,6 +135,14 b' impl SliceExt for [u8] {' | |||||
134 | let b = iter.next()?; |
|
135 | let b = iter.next()?; | |
135 | Some((a, b)) |
|
136 | Some((a, b)) | |
136 | } |
|
137 | } | |
|
138 | ||||
|
139 | fn split_2_by_slice(&self, separator: &[u8]) -> Option<(&[u8], &[u8])> { | |||
|
140 | if let Some(pos) = find_slice_in_slice(self, separator) { | |||
|
141 | Some((&self[..pos], &self[pos + separator.len()..])) | |||
|
142 | } else { | |||
|
143 | None | |||
|
144 | } | |||
|
145 | } | |||
137 | } |
|
146 | } | |
138 |
|
147 | |||
139 | pub trait Escaped { |
|
148 | pub trait Escaped { |
General Comments 0
You need to be logged in to leave comments.
Login now