Show More
@@ -1,98 +1,88 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::path::PathBuf; |
|
10 | use std::path::PathBuf; | |
11 |
|
11 | |||
12 | /// A specialized `Revlog` to work with file data logs. |
|
12 | /// A specialized `Revlog` to work with file data logs. | |
13 | pub struct Filelog { |
|
13 | pub struct Filelog { | |
14 | /// The generic `revlog` format. |
|
14 | /// The generic `revlog` format. | |
15 | revlog: Revlog, |
|
15 | revlog: Revlog, | |
16 | } |
|
16 | } | |
17 |
|
17 | |||
18 | impl Filelog { |
|
18 | impl Filelog { | |
19 | pub fn open(repo: &Repo, file_path: &HgPath) -> Result<Self, HgError> { |
|
19 | pub fn open(repo: &Repo, file_path: &HgPath) -> Result<Self, HgError> { | |
20 | let index_path = store_path(file_path, b".i"); |
|
20 | let index_path = store_path(file_path, b".i"); | |
21 | let data_path = store_path(file_path, b".d"); |
|
21 | let data_path = store_path(file_path, b".d"); | |
22 | let revlog = Revlog::open(repo, index_path, Some(&data_path))?; |
|
22 | let revlog = Revlog::open(repo, index_path, Some(&data_path))?; | |
23 | Ok(Self { revlog }) |
|
23 | Ok(Self { revlog }) | |
24 | } |
|
24 | } | |
25 |
|
25 | |||
26 | /// The given node ID is that of the file as found in a manifest, not of a |
|
26 | /// The given node ID is that of the file as found in a manifest, not of a | |
27 | /// changeset. |
|
27 | /// changeset. | |
28 | pub fn data_for_node( |
|
28 | pub fn data_for_node( | |
29 | &self, |
|
29 | &self, | |
30 | file_node: impl Into<NodePrefix>, |
|
30 | file_node: impl Into<NodePrefix>, | |
31 | ) -> Result<FilelogEntry, RevlogError> { |
|
31 | ) -> Result<FilelogEntry, RevlogError> { | |
32 | let file_rev = self.revlog.rev_from_node(file_node.into())?; |
|
32 | let file_rev = self.revlog.rev_from_node(file_node.into())?; | |
33 | self.data_for_rev(file_rev) |
|
33 | self.data_for_rev(file_rev) | |
34 | } |
|
34 | } | |
35 |
|
35 | |||
36 | /// The given revision is that of the file as found in a manifest, not of a |
|
36 | /// The given revision is that of the file as found in a manifest, not of a | |
37 | /// changeset. |
|
37 | /// changeset. | |
38 | pub fn data_for_rev( |
|
38 | pub fn data_for_rev( | |
39 | &self, |
|
39 | &self, | |
40 | file_rev: Revision, |
|
40 | file_rev: Revision, | |
41 | ) -> Result<FilelogEntry, RevlogError> { |
|
41 | ) -> Result<FilelogEntry, RevlogError> { | |
42 | let data: Vec<u8> = self.revlog.get_rev_data(file_rev)?; |
|
42 | let data: Vec<u8> = self.revlog.get_rev_data(file_rev)?; | |
43 | Ok(FilelogEntry(data.into())) |
|
43 | Ok(FilelogEntry(data.into())) | |
44 | } |
|
44 | } | |
45 | } |
|
45 | } | |
46 |
|
46 | |||
47 | fn store_path(hg_path: &HgPath, suffix: &[u8]) -> PathBuf { |
|
47 | fn store_path(hg_path: &HgPath, suffix: &[u8]) -> PathBuf { | |
48 | let encoded_bytes = |
|
48 | let encoded_bytes = | |
49 | path_encode(&[b"data/", hg_path.as_bytes(), suffix].concat()); |
|
49 | path_encode(&[b"data/", hg_path.as_bytes(), suffix].concat()); | |
50 | get_path_from_bytes(&encoded_bytes).into() |
|
50 | get_path_from_bytes(&encoded_bytes).into() | |
51 | } |
|
51 | } | |
52 |
|
52 | |||
53 | pub struct FilelogEntry(Vec<u8>); |
|
53 | pub struct FilelogEntry(Vec<u8>); | |
54 |
|
54 | |||
55 | impl FilelogEntry { |
|
55 | impl FilelogEntry { | |
56 | /// Split into metadata and data |
|
56 | /// Split into metadata and data | |
57 | /// Returns None if there is no metadata, so the entire entry is data. |
|
57 | pub fn split(&self) -> Result<(Option<&[u8]>, &[u8]), HgError> { | |
58 | fn split_metadata(&self) -> Result<Option<(&[u8], &[u8])>, HgError> { |
|
|||
59 | const DELIMITER: &[u8; 2] = &[b'\x01', b'\n']; |
|
58 | const DELIMITER: &[u8; 2] = &[b'\x01', b'\n']; | |
60 |
|
59 | |||
61 | if let Some(rest) = self.0.drop_prefix(DELIMITER) { |
|
60 | if let Some(rest) = self.0.drop_prefix(DELIMITER) { | |
62 | if let Some((metadata, data)) = rest.split_2_by_slice(DELIMITER) { |
|
61 | if let Some((metadata, data)) = rest.split_2_by_slice(DELIMITER) { | |
63 |
Ok(Some( |
|
62 | Ok((Some(metadata), data)) | |
64 | } else { |
|
63 | } else { | |
65 | Err(HgError::corrupted( |
|
64 | Err(HgError::corrupted( | |
66 | "Missing metadata end delimiter in filelog entry", |
|
65 | "Missing metadata end delimiter in filelog entry", | |
67 | )) |
|
66 | )) | |
68 | } |
|
67 | } | |
69 | } else { |
|
68 | } else { | |
70 | Ok(None) |
|
|||
71 | } |
|
|||
72 | } |
|
|||
73 |
|
||||
74 | /// Split into metadata and data |
|
|||
75 | pub fn split(&self) -> Result<(Option<&[u8]>, &[u8]), HgError> { |
|
|||
76 | if let Some((metadata, data)) = self.split_metadata()? { |
|
|||
77 | Ok((Some(metadata), data)) |
|
|||
78 | } else { |
|
|||
79 | Ok((None, &self.0)) |
|
69 | Ok((None, &self.0)) | |
80 | } |
|
70 | } | |
81 | } |
|
71 | } | |
82 |
|
72 | |||
83 | /// Returns the file contents at this revision, stripped of any metadata |
|
73 | /// Returns the file contents at this revision, stripped of any metadata | |
84 | pub fn data(&self) -> Result<&[u8], HgError> { |
|
74 | pub fn data(&self) -> Result<&[u8], HgError> { | |
85 | let (_metadata, data) = self.split()?; |
|
75 | let (_metadata, data) = self.split()?; | |
86 | Ok(data) |
|
76 | Ok(data) | |
87 | } |
|
77 | } | |
88 |
|
78 | |||
89 | /// Consume the entry, and convert it into data, discarding any metadata, |
|
79 | /// Consume the entry, and convert it into data, discarding any metadata, | |
90 | /// if present. |
|
80 | /// if present. | |
91 | pub fn into_data(self) -> Result<Vec<u8>, HgError> { |
|
81 | pub fn into_data(self) -> Result<Vec<u8>, HgError> { | |
92 |
if let Some( |
|
82 | if let (Some(_metadata), data) = self.split()? { | |
93 | Ok(data.to_owned()) |
|
83 | Ok(data.to_owned()) | |
94 | } else { |
|
84 | } else { | |
95 | Ok(self.0) |
|
85 | Ok(self.0) | |
96 | } |
|
86 | } | |
97 | } |
|
87 | } | |
98 | } |
|
88 | } |
General Comments 0
You need to be logged in to leave comments.
Login now