##// END OF EJS Templates
rust-index: add support for `_slicechunktodensity`
Raphaël Gomès -
r52111:0112803e default
parent child Browse files
Show More
@@ -1,1173 +1,1343 b''
1 use std::collections::hash_map::RandomState;
1 use std::collections::hash_map::RandomState;
2 use std::collections::HashSet;
2 use std::collections::HashSet;
3 use std::fmt::Debug;
3 use std::fmt::Debug;
4 use std::ops::Deref;
4 use std::ops::Deref;
5 use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
5 use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
6
6
7 use byteorder::{BigEndian, ByteOrder};
7 use byteorder::{BigEndian, ByteOrder};
8 use bytes_cast::{unaligned, BytesCast};
8 use bytes_cast::{unaligned, BytesCast};
9
9
10 use super::REVIDX_KNOWN_FLAGS;
10 use super::REVIDX_KNOWN_FLAGS;
11 use crate::errors::HgError;
11 use crate::errors::HgError;
12 use crate::node::{NODE_BYTES_LENGTH, NULL_NODE, STORED_NODE_ID_BYTES};
12 use crate::node::{NODE_BYTES_LENGTH, NULL_NODE, STORED_NODE_ID_BYTES};
13 use crate::revlog::node::Node;
13 use crate::revlog::node::Node;
14 use crate::revlog::{Revision, NULL_REVISION};
14 use crate::revlog::{Revision, NULL_REVISION};
15 use crate::{
15 use crate::{
16 dagops, BaseRevision, FastHashMap, Graph, GraphError, RevlogError,
16 dagops, BaseRevision, FastHashMap, Graph, GraphError, RevlogError,
17 RevlogIndex, UncheckedRevision,
17 RevlogIndex, UncheckedRevision,
18 };
18 };
19
19
20 pub const INDEX_ENTRY_SIZE: usize = 64;
20 pub const INDEX_ENTRY_SIZE: usize = 64;
21 pub const COMPRESSION_MODE_INLINE: u8 = 2;
21 pub const COMPRESSION_MODE_INLINE: u8 = 2;
22
22
23 #[derive(Debug)]
23 pub struct IndexHeader {
24 pub struct IndexHeader {
24 pub(super) header_bytes: [u8; 4],
25 pub(super) header_bytes: [u8; 4],
25 }
26 }
26
27
27 #[derive(Copy, Clone)]
28 #[derive(Copy, Clone)]
28 pub struct IndexHeaderFlags {
29 pub struct IndexHeaderFlags {
29 flags: u16,
30 flags: u16,
30 }
31 }
31
32
32 /// Corresponds to the high bits of `_format_flags` in python
33 /// Corresponds to the high bits of `_format_flags` in python
33 impl IndexHeaderFlags {
34 impl IndexHeaderFlags {
34 /// Corresponds to FLAG_INLINE_DATA in python
35 /// Corresponds to FLAG_INLINE_DATA in python
35 pub fn is_inline(self) -> bool {
36 pub fn is_inline(self) -> bool {
36 self.flags & 1 != 0
37 self.flags & 1 != 0
37 }
38 }
38 /// Corresponds to FLAG_GENERALDELTA in python
39 /// Corresponds to FLAG_GENERALDELTA in python
39 pub fn uses_generaldelta(self) -> bool {
40 pub fn uses_generaldelta(self) -> bool {
40 self.flags & 2 != 0
41 self.flags & 2 != 0
41 }
42 }
42 }
43 }
43
44
44 /// Corresponds to the INDEX_HEADER structure,
45 /// Corresponds to the INDEX_HEADER structure,
45 /// which is parsed as a `header` variable in `_loadindex` in `revlog.py`
46 /// which is parsed as a `header` variable in `_loadindex` in `revlog.py`
46 impl IndexHeader {
47 impl IndexHeader {
47 fn format_flags(&self) -> IndexHeaderFlags {
48 fn format_flags(&self) -> IndexHeaderFlags {
48 // No "unknown flags" check here, unlike in python. Maybe there should
49 // No "unknown flags" check here, unlike in python. Maybe there should
49 // be.
50 // be.
50 IndexHeaderFlags {
51 IndexHeaderFlags {
51 flags: BigEndian::read_u16(&self.header_bytes[0..2]),
52 flags: BigEndian::read_u16(&self.header_bytes[0..2]),
52 }
53 }
53 }
54 }
54
55
55 /// The only revlog version currently supported by rhg.
56 /// The only revlog version currently supported by rhg.
56 const REVLOGV1: u16 = 1;
57 const REVLOGV1: u16 = 1;
57
58
58 /// Corresponds to `_format_version` in Python.
59 /// Corresponds to `_format_version` in Python.
59 fn format_version(&self) -> u16 {
60 fn format_version(&self) -> u16 {
60 BigEndian::read_u16(&self.header_bytes[2..4])
61 BigEndian::read_u16(&self.header_bytes[2..4])
61 }
62 }
62
63
63 pub fn parse(index_bytes: &[u8]) -> Result<Option<IndexHeader>, HgError> {
64 pub fn parse(index_bytes: &[u8]) -> Result<Option<IndexHeader>, HgError> {
64 if index_bytes.is_empty() {
65 if index_bytes.is_empty() {
65 return Ok(None);
66 return Ok(None);
66 }
67 }
67 if index_bytes.len() < 4 {
68 if index_bytes.len() < 4 {
68 return Err(HgError::corrupted(
69 return Err(HgError::corrupted(
69 "corrupted revlog: can't read the index format header",
70 "corrupted revlog: can't read the index format header",
70 ));
71 ));
71 }
72 }
72 Ok(Some(IndexHeader {
73 Ok(Some(IndexHeader {
73 header_bytes: {
74 header_bytes: {
74 let bytes: [u8; 4] =
75 let bytes: [u8; 4] =
75 index_bytes[0..4].try_into().expect("impossible");
76 index_bytes[0..4].try_into().expect("impossible");
76 bytes
77 bytes
77 },
78 },
78 }))
79 }))
79 }
80 }
80 }
81 }
81
82
82 /// Abstracts the access to the index bytes since they can be spread between
83 /// Abstracts the access to the index bytes since they can be spread between
83 /// the immutable (bytes) part and the mutable (added) part if any appends
84 /// the immutable (bytes) part and the mutable (added) part if any appends
84 /// happened. This makes it transparent for the callers.
85 /// happened. This makes it transparent for the callers.
85 struct IndexData {
86 struct IndexData {
86 /// Immutable bytes, most likely taken from disk
87 /// Immutable bytes, most likely taken from disk
87 bytes: Box<dyn Deref<Target = [u8]> + Send>,
88 bytes: Box<dyn Deref<Target = [u8]> + Send>,
88 /// Used when stripping index contents, keeps track of the start of the
89 /// Used when stripping index contents, keeps track of the start of the
89 /// first stripped revision, which is used to give a slice of the
90 /// first stripped revision, which is used to give a slice of the
90 /// `bytes` field.
91 /// `bytes` field.
91 truncation: Option<usize>,
92 truncation: Option<usize>,
92 /// Bytes that were added after reading the index
93 /// Bytes that were added after reading the index
93 added: Vec<u8>,
94 added: Vec<u8>,
94 }
95 }
95
96
96 impl IndexData {
97 impl IndexData {
97 pub fn new(bytes: Box<dyn Deref<Target = [u8]> + Send>) -> Self {
98 pub fn new(bytes: Box<dyn Deref<Target = [u8]> + Send>) -> Self {
98 Self {
99 Self {
99 bytes,
100 bytes,
100 truncation: None,
101 truncation: None,
101 added: vec![],
102 added: vec![],
102 }
103 }
103 }
104 }
104
105
105 pub fn len(&self) -> usize {
106 pub fn len(&self) -> usize {
106 match self.truncation {
107 match self.truncation {
107 Some(truncation) => truncation + self.added.len(),
108 Some(truncation) => truncation + self.added.len(),
108 None => self.bytes.len() + self.added.len(),
109 None => self.bytes.len() + self.added.len(),
109 }
110 }
110 }
111 }
111
112
112 fn remove(
113 fn remove(
113 &mut self,
114 &mut self,
114 rev: Revision,
115 rev: Revision,
115 offsets: Option<&[usize]>,
116 offsets: Option<&[usize]>,
116 ) -> Result<(), RevlogError> {
117 ) -> Result<(), RevlogError> {
117 let rev = rev.0 as usize;
118 let rev = rev.0 as usize;
118 let truncation = if let Some(offsets) = offsets {
119 let truncation = if let Some(offsets) = offsets {
119 offsets[rev]
120 offsets[rev]
120 } else {
121 } else {
121 rev * INDEX_ENTRY_SIZE
122 rev * INDEX_ENTRY_SIZE
122 };
123 };
123 if truncation < self.bytes.len() {
124 if truncation < self.bytes.len() {
124 self.truncation = Some(truncation);
125 self.truncation = Some(truncation);
125 self.added.clear();
126 self.added.clear();
126 } else {
127 } else {
127 self.added.truncate(truncation - self.bytes.len());
128 self.added.truncate(truncation - self.bytes.len());
128 }
129 }
129 Ok(())
130 Ok(())
130 }
131 }
131
132
132 fn is_new(&self) -> bool {
133 fn is_new(&self) -> bool {
133 self.bytes.is_empty()
134 self.bytes.is_empty()
134 }
135 }
135 }
136 }
136
137
137 impl std::ops::Index<std::ops::Range<usize>> for IndexData {
138 impl std::ops::Index<std::ops::Range<usize>> for IndexData {
138 type Output = [u8];
139 type Output = [u8];
139
140
140 fn index(&self, index: std::ops::Range<usize>) -> &Self::Output {
141 fn index(&self, index: std::ops::Range<usize>) -> &Self::Output {
141 let start = index.start;
142 let start = index.start;
142 let end = index.end;
143 let end = index.end;
143 let immutable_len = match self.truncation {
144 let immutable_len = match self.truncation {
144 Some(truncation) => truncation,
145 Some(truncation) => truncation,
145 None => self.bytes.len(),
146 None => self.bytes.len(),
146 };
147 };
147 if start < immutable_len {
148 if start < immutable_len {
148 if end > immutable_len {
149 if end > immutable_len {
149 panic!("index data cannot span existing and added ranges");
150 panic!("index data cannot span existing and added ranges");
150 }
151 }
151 &self.bytes[index]
152 &self.bytes[index]
152 } else {
153 } else {
153 &self.added[start - immutable_len..end - immutable_len]
154 &self.added[start - immutable_len..end - immutable_len]
154 }
155 }
155 }
156 }
156 }
157 }
157
158
158 #[derive(Debug, PartialEq, Eq)]
159 #[derive(Debug, PartialEq, Eq)]
159 pub struct RevisionDataParams {
160 pub struct RevisionDataParams {
160 pub flags: u16,
161 pub flags: u16,
161 pub data_offset: u64,
162 pub data_offset: u64,
162 pub data_compressed_length: i32,
163 pub data_compressed_length: i32,
163 pub data_uncompressed_length: i32,
164 pub data_uncompressed_length: i32,
164 pub data_delta_base: i32,
165 pub data_delta_base: i32,
165 pub link_rev: i32,
166 pub link_rev: i32,
166 pub parent_rev_1: i32,
167 pub parent_rev_1: i32,
167 pub parent_rev_2: i32,
168 pub parent_rev_2: i32,
168 pub node_id: [u8; NODE_BYTES_LENGTH],
169 pub node_id: [u8; NODE_BYTES_LENGTH],
169 pub _sidedata_offset: u64,
170 pub _sidedata_offset: u64,
170 pub _sidedata_compressed_length: i32,
171 pub _sidedata_compressed_length: i32,
171 pub data_compression_mode: u8,
172 pub data_compression_mode: u8,
172 pub _sidedata_compression_mode: u8,
173 pub _sidedata_compression_mode: u8,
173 pub _rank: i32,
174 pub _rank: i32,
174 }
175 }
175
176
176 impl Default for RevisionDataParams {
177 impl Default for RevisionDataParams {
177 fn default() -> Self {
178 fn default() -> Self {
178 Self {
179 Self {
179 flags: 0,
180 flags: 0,
180 data_offset: 0,
181 data_offset: 0,
181 data_compressed_length: 0,
182 data_compressed_length: 0,
182 data_uncompressed_length: 0,
183 data_uncompressed_length: 0,
183 data_delta_base: -1,
184 data_delta_base: -1,
184 link_rev: -1,
185 link_rev: -1,
185 parent_rev_1: -1,
186 parent_rev_1: -1,
186 parent_rev_2: -1,
187 parent_rev_2: -1,
187 node_id: [0; NODE_BYTES_LENGTH],
188 node_id: [0; NODE_BYTES_LENGTH],
188 _sidedata_offset: 0,
189 _sidedata_offset: 0,
189 _sidedata_compressed_length: 0,
190 _sidedata_compressed_length: 0,
190 data_compression_mode: COMPRESSION_MODE_INLINE,
191 data_compression_mode: COMPRESSION_MODE_INLINE,
191 _sidedata_compression_mode: COMPRESSION_MODE_INLINE,
192 _sidedata_compression_mode: COMPRESSION_MODE_INLINE,
192 _rank: -1,
193 _rank: -1,
193 }
194 }
194 }
195 }
195 }
196 }
196
197
197 #[derive(BytesCast)]
198 #[derive(BytesCast)]
198 #[repr(C)]
199 #[repr(C)]
199 pub struct RevisionDataV1 {
200 pub struct RevisionDataV1 {
200 data_offset_or_flags: unaligned::U64Be,
201 data_offset_or_flags: unaligned::U64Be,
201 data_compressed_length: unaligned::I32Be,
202 data_compressed_length: unaligned::I32Be,
202 data_uncompressed_length: unaligned::I32Be,
203 data_uncompressed_length: unaligned::I32Be,
203 data_delta_base: unaligned::I32Be,
204 data_delta_base: unaligned::I32Be,
204 link_rev: unaligned::I32Be,
205 link_rev: unaligned::I32Be,
205 parent_rev_1: unaligned::I32Be,
206 parent_rev_1: unaligned::I32Be,
206 parent_rev_2: unaligned::I32Be,
207 parent_rev_2: unaligned::I32Be,
207 node_id: [u8; STORED_NODE_ID_BYTES],
208 node_id: [u8; STORED_NODE_ID_BYTES],
208 }
209 }
209
210
210 fn _static_assert_size_of_revision_data_v1() {
211 fn _static_assert_size_of_revision_data_v1() {
211 let _ = std::mem::transmute::<RevisionDataV1, [u8; 64]>;
212 let _ = std::mem::transmute::<RevisionDataV1, [u8; 64]>;
212 }
213 }
213
214
214 impl RevisionDataParams {
215 impl RevisionDataParams {
215 pub fn validate(&self) -> Result<(), RevlogError> {
216 pub fn validate(&self) -> Result<(), RevlogError> {
216 if self.flags & !REVIDX_KNOWN_FLAGS != 0 {
217 if self.flags & !REVIDX_KNOWN_FLAGS != 0 {
217 return Err(RevlogError::corrupted(format!(
218 return Err(RevlogError::corrupted(format!(
218 "unknown revlog index flags: {}",
219 "unknown revlog index flags: {}",
219 self.flags
220 self.flags
220 )));
221 )));
221 }
222 }
222 if self.data_compression_mode != COMPRESSION_MODE_INLINE {
223 if self.data_compression_mode != COMPRESSION_MODE_INLINE {
223 return Err(RevlogError::corrupted(format!(
224 return Err(RevlogError::corrupted(format!(
224 "invalid data compression mode: {}",
225 "invalid data compression mode: {}",
225 self.data_compression_mode
226 self.data_compression_mode
226 )));
227 )));
227 }
228 }
228 // FIXME isn't this only for v2 or changelog v2?
229 // FIXME isn't this only for v2 or changelog v2?
229 if self._sidedata_compression_mode != COMPRESSION_MODE_INLINE {
230 if self._sidedata_compression_mode != COMPRESSION_MODE_INLINE {
230 return Err(RevlogError::corrupted(format!(
231 return Err(RevlogError::corrupted(format!(
231 "invalid sidedata compression mode: {}",
232 "invalid sidedata compression mode: {}",
232 self._sidedata_compression_mode
233 self._sidedata_compression_mode
233 )));
234 )));
234 }
235 }
235 Ok(())
236 Ok(())
236 }
237 }
237
238
238 pub fn into_v1(self) -> RevisionDataV1 {
239 pub fn into_v1(self) -> RevisionDataV1 {
239 let data_offset_or_flags = self.data_offset << 16 | self.flags as u64;
240 let data_offset_or_flags = self.data_offset << 16 | self.flags as u64;
240 let mut node_id = [0; STORED_NODE_ID_BYTES];
241 let mut node_id = [0; STORED_NODE_ID_BYTES];
241 node_id[..NODE_BYTES_LENGTH].copy_from_slice(&self.node_id);
242 node_id[..NODE_BYTES_LENGTH].copy_from_slice(&self.node_id);
242 RevisionDataV1 {
243 RevisionDataV1 {
243 data_offset_or_flags: data_offset_or_flags.into(),
244 data_offset_or_flags: data_offset_or_flags.into(),
244 data_compressed_length: self.data_compressed_length.into(),
245 data_compressed_length: self.data_compressed_length.into(),
245 data_uncompressed_length: self.data_uncompressed_length.into(),
246 data_uncompressed_length: self.data_uncompressed_length.into(),
246 data_delta_base: self.data_delta_base.into(),
247 data_delta_base: self.data_delta_base.into(),
247 link_rev: self.link_rev.into(),
248 link_rev: self.link_rev.into(),
248 parent_rev_1: self.parent_rev_1.into(),
249 parent_rev_1: self.parent_rev_1.into(),
249 parent_rev_2: self.parent_rev_2.into(),
250 parent_rev_2: self.parent_rev_2.into(),
250 node_id,
251 node_id,
251 }
252 }
252 }
253 }
253 }
254 }
254
255
255 /// A Revlog index
256 /// A Revlog index
256 pub struct Index {
257 pub struct Index {
257 bytes: IndexData,
258 bytes: IndexData,
258 /// Offsets of starts of index blocks.
259 /// Offsets of starts of index blocks.
259 /// Only needed when the index is interleaved with data.
260 /// Only needed when the index is interleaved with data.
260 offsets: RwLock<Option<Vec<usize>>>,
261 offsets: RwLock<Option<Vec<usize>>>,
261 uses_generaldelta: bool,
262 uses_generaldelta: bool,
262 is_inline: bool,
263 is_inline: bool,
263 /// Cache of the head revisions in this index, kept in sync. Should
264 /// Cache of the head revisions in this index, kept in sync. Should
264 /// be accessed via the [`Self::head_revs`] method.
265 /// be accessed via the [`Self::head_revs`] method.
265 head_revs: Vec<Revision>,
266 head_revs: Vec<Revision>,
266 /// Cache of the last filtered revisions in this index, used to make sure
267 /// Cache of the last filtered revisions in this index, used to make sure
267 /// we haven't changed filters when returning the cached `head_revs`.
268 /// we haven't changed filters when returning the cached `head_revs`.
268 filtered_revs: HashSet<Revision>,
269 filtered_revs: HashSet<Revision>,
269 }
270 }
270
271
271 impl Debug for Index {
272 impl Debug for Index {
272 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
273 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
273 f.debug_struct("Index")
274 f.debug_struct("Index")
274 .field("offsets", &self.offsets)
275 .field("offsets", &self.offsets)
275 .field("uses_generaldelta", &self.uses_generaldelta)
276 .field("uses_generaldelta", &self.uses_generaldelta)
276 .finish()
277 .finish()
277 }
278 }
278 }
279 }
279
280
280 impl Graph for Index {
281 impl Graph for Index {
281 fn parents(&self, rev: Revision) -> Result<[Revision; 2], GraphError> {
282 fn parents(&self, rev: Revision) -> Result<[Revision; 2], GraphError> {
282 let err = || GraphError::ParentOutOfRange(rev);
283 let err = || GraphError::ParentOutOfRange(rev);
283 match self.get_entry(rev) {
284 match self.get_entry(rev) {
284 Some(entry) => {
285 Some(entry) => {
285 // The C implementation checks that the parents are valid
286 // The C implementation checks that the parents are valid
286 // before returning
287 // before returning
287 Ok([
288 Ok([
288 self.check_revision(entry.p1()).ok_or_else(err)?,
289 self.check_revision(entry.p1()).ok_or_else(err)?,
289 self.check_revision(entry.p2()).ok_or_else(err)?,
290 self.check_revision(entry.p2()).ok_or_else(err)?,
290 ])
291 ])
291 }
292 }
292 None => Ok([NULL_REVISION, NULL_REVISION]),
293 None => Ok([NULL_REVISION, NULL_REVISION]),
293 }
294 }
294 }
295 }
295 }
296 }
296
297
297 /// A cache suitable for find_snapshots
298 /// A cache suitable for find_snapshots
298 ///
299 ///
299 /// Logically equivalent to a mapping whose keys are [`BaseRevision`] and
300 /// Logically equivalent to a mapping whose keys are [`BaseRevision`] and
300 /// values sets of [`BaseRevision`]
301 /// values sets of [`BaseRevision`]
301 ///
302 ///
302 /// TODO the dubious part is insisting that errors must be RevlogError
303 /// TODO the dubious part is insisting that errors must be RevlogError
303 /// we would probably need to sprinkle some magic here, such as an associated
304 /// we would probably need to sprinkle some magic here, such as an associated
304 /// type that would be Into<RevlogError> but even that would not be
305 /// type that would be Into<RevlogError> but even that would not be
305 /// satisfactory, as errors potentially have nothing to do with the revlog.
306 /// satisfactory, as errors potentially have nothing to do with the revlog.
306 pub trait SnapshotsCache {
307 pub trait SnapshotsCache {
307 fn insert_for(
308 fn insert_for(
308 &mut self,
309 &mut self,
309 rev: BaseRevision,
310 rev: BaseRevision,
310 value: BaseRevision,
311 value: BaseRevision,
311 ) -> Result<(), RevlogError>;
312 ) -> Result<(), RevlogError>;
312 }
313 }
313
314
314 impl SnapshotsCache for FastHashMap<BaseRevision, HashSet<BaseRevision>> {
315 impl SnapshotsCache for FastHashMap<BaseRevision, HashSet<BaseRevision>> {
315 fn insert_for(
316 fn insert_for(
316 &mut self,
317 &mut self,
317 rev: BaseRevision,
318 rev: BaseRevision,
318 value: BaseRevision,
319 value: BaseRevision,
319 ) -> Result<(), RevlogError> {
320 ) -> Result<(), RevlogError> {
320 let all_values = self.entry(rev).or_insert_with(HashSet::new);
321 let all_values = self.entry(rev).or_insert_with(HashSet::new);
321 all_values.insert(value);
322 all_values.insert(value);
322 Ok(())
323 Ok(())
323 }
324 }
324 }
325 }
325
326
326 impl Index {
327 impl Index {
327 /// Create an index from bytes.
328 /// Create an index from bytes.
328 /// Calculate the start of each entry when is_inline is true.
329 /// Calculate the start of each entry when is_inline is true.
329 pub fn new(
330 pub fn new(
330 bytes: Box<dyn Deref<Target = [u8]> + Send>,
331 bytes: Box<dyn Deref<Target = [u8]> + Send>,
331 default_header: IndexHeader,
332 default_header: IndexHeader,
332 ) -> Result<Self, HgError> {
333 ) -> Result<Self, HgError> {
333 let header =
334 let header =
334 IndexHeader::parse(bytes.as_ref())?.unwrap_or(default_header);
335 IndexHeader::parse(bytes.as_ref())?.unwrap_or(default_header);
335
336
336 if header.format_version() != IndexHeader::REVLOGV1 {
337 if header.format_version() != IndexHeader::REVLOGV1 {
337 // A proper new version should have had a repo/store
338 // A proper new version should have had a repo/store
338 // requirement.
339 // requirement.
339 return Err(HgError::corrupted("unsupported revlog version"));
340 return Err(HgError::corrupted("unsupported revlog version"));
340 }
341 }
341
342
342 // This is only correct because we know version is REVLOGV1.
343 // This is only correct because we know version is REVLOGV1.
343 // In v2 we always use generaldelta, while in v0 we never use
344 // In v2 we always use generaldelta, while in v0 we never use
344 // generaldelta. Similar for [is_inline] (it's only used in v1).
345 // generaldelta. Similar for [is_inline] (it's only used in v1).
345 let uses_generaldelta = header.format_flags().uses_generaldelta();
346 let uses_generaldelta = header.format_flags().uses_generaldelta();
346
347
347 if header.format_flags().is_inline() {
348 if header.format_flags().is_inline() {
348 let mut offset: usize = 0;
349 let mut offset: usize = 0;
349 let mut offsets = Vec::new();
350 let mut offsets = Vec::new();
350
351
351 while offset + INDEX_ENTRY_SIZE <= bytes.len() {
352 while offset + INDEX_ENTRY_SIZE <= bytes.len() {
352 offsets.push(offset);
353 offsets.push(offset);
353 let end = offset + INDEX_ENTRY_SIZE;
354 let end = offset + INDEX_ENTRY_SIZE;
354 let entry = IndexEntry {
355 let entry = IndexEntry {
355 bytes: &bytes[offset..end],
356 bytes: &bytes[offset..end],
356 offset_override: None,
357 offset_override: None,
357 };
358 };
358
359
359 offset += INDEX_ENTRY_SIZE + entry.compressed_len() as usize;
360 offset += INDEX_ENTRY_SIZE + entry.compressed_len() as usize;
360 }
361 }
361
362
362 if offset == bytes.len() {
363 if offset == bytes.len() {
363 Ok(Self {
364 Ok(Self {
364 bytes: IndexData::new(bytes),
365 bytes: IndexData::new(bytes),
365 offsets: RwLock::new(Some(offsets)),
366 offsets: RwLock::new(Some(offsets)),
366 uses_generaldelta,
367 uses_generaldelta,
367 is_inline: true,
368 is_inline: true,
368 head_revs: vec![],
369 head_revs: vec![],
369 filtered_revs: HashSet::new(),
370 filtered_revs: HashSet::new(),
370 })
371 })
371 } else {
372 } else {
372 Err(HgError::corrupted("unexpected inline revlog length"))
373 Err(HgError::corrupted("unexpected inline revlog length"))
373 }
374 }
374 } else {
375 } else {
375 Ok(Self {
376 Ok(Self {
376 bytes: IndexData::new(bytes),
377 bytes: IndexData::new(bytes),
377 offsets: RwLock::new(None),
378 offsets: RwLock::new(None),
378 uses_generaldelta,
379 uses_generaldelta,
379 is_inline: false,
380 is_inline: false,
380 head_revs: vec![],
381 head_revs: vec![],
381 filtered_revs: HashSet::new(),
382 filtered_revs: HashSet::new(),
382 })
383 })
383 }
384 }
384 }
385 }
385
386
386 pub fn uses_generaldelta(&self) -> bool {
387 pub fn uses_generaldelta(&self) -> bool {
387 self.uses_generaldelta
388 self.uses_generaldelta
388 }
389 }
389
390
390 /// Value of the inline flag.
391 /// Value of the inline flag.
391 pub fn is_inline(&self) -> bool {
392 pub fn is_inline(&self) -> bool {
392 self.is_inline
393 self.is_inline
393 }
394 }
394
395
395 /// Return a slice of bytes if `revlog` is inline. Panic if not.
396 /// Return a slice of bytes if `revlog` is inline. Panic if not.
396 pub fn data(&self, start: usize, end: usize) -> &[u8] {
397 pub fn data(&self, start: usize, end: usize) -> &[u8] {
397 if !self.is_inline() {
398 if !self.is_inline() {
398 panic!("tried to access data in the index of a revlog that is not inline");
399 panic!("tried to access data in the index of a revlog that is not inline");
399 }
400 }
400 &self.bytes[start..end]
401 &self.bytes[start..end]
401 }
402 }
402
403
403 /// Return number of entries of the revlog index.
404 /// Return number of entries of the revlog index.
404 pub fn len(&self) -> usize {
405 pub fn len(&self) -> usize {
405 if let Some(offsets) = &*self.get_offsets() {
406 if let Some(offsets) = &*self.get_offsets() {
406 offsets.len()
407 offsets.len()
407 } else {
408 } else {
408 self.bytes.len() / INDEX_ENTRY_SIZE
409 self.bytes.len() / INDEX_ENTRY_SIZE
409 }
410 }
410 }
411 }
411
412
412 pub fn get_offsets(&self) -> RwLockReadGuard<Option<Vec<usize>>> {
413 pub fn get_offsets(&self) -> RwLockReadGuard<Option<Vec<usize>>> {
413 if self.is_inline() {
414 if self.is_inline() {
414 {
415 {
415 // Wrap in a block to drop the read guard
416 // Wrap in a block to drop the read guard
416 // TODO perf?
417 // TODO perf?
417 let mut offsets = self.offsets.write().unwrap();
418 let mut offsets = self.offsets.write().unwrap();
418 if offsets.is_none() {
419 if offsets.is_none() {
419 offsets.replace(inline_scan(&self.bytes.bytes).1);
420 offsets.replace(inline_scan(&self.bytes.bytes).1);
420 }
421 }
421 }
422 }
422 }
423 }
423 self.offsets.read().unwrap()
424 self.offsets.read().unwrap()
424 }
425 }
425
426
426 pub fn get_offsets_mut(&mut self) -> RwLockWriteGuard<Option<Vec<usize>>> {
427 pub fn get_offsets_mut(&mut self) -> RwLockWriteGuard<Option<Vec<usize>>> {
427 let mut offsets = self.offsets.write().unwrap();
428 let mut offsets = self.offsets.write().unwrap();
428 if self.is_inline() && offsets.is_none() {
429 if self.is_inline() && offsets.is_none() {
429 offsets.replace(inline_scan(&self.bytes.bytes).1);
430 offsets.replace(inline_scan(&self.bytes.bytes).1);
430 }
431 }
431 offsets
432 offsets
432 }
433 }
433
434
434 /// Returns `true` if the `Index` has zero `entries`.
435 /// Returns `true` if the `Index` has zero `entries`.
435 pub fn is_empty(&self) -> bool {
436 pub fn is_empty(&self) -> bool {
436 self.len() == 0
437 self.len() == 0
437 }
438 }
438
439
439 /// Return the index entry corresponding to the given revision or `None`
440 /// Return the index entry corresponding to the given revision or `None`
440 /// for [`NULL_REVISION`]
441 /// for [`NULL_REVISION`]
441 ///
442 ///
442 /// The specified revision being of the checked type, it always exists
443 /// The specified revision being of the checked type, it always exists
443 /// if it was validated by this index.
444 /// if it was validated by this index.
444 pub fn get_entry(&self, rev: Revision) -> Option<IndexEntry> {
445 pub fn get_entry(&self, rev: Revision) -> Option<IndexEntry> {
445 if rev == NULL_REVISION {
446 if rev == NULL_REVISION {
446 return None;
447 return None;
447 }
448 }
448 Some(if let Some(offsets) = &*self.get_offsets() {
449 Some(if let Some(offsets) = &*self.get_offsets() {
449 self.get_entry_inline(rev, offsets.as_ref())
450 self.get_entry_inline(rev, offsets.as_ref())
450 } else {
451 } else {
451 self.get_entry_separated(rev)
452 self.get_entry_separated(rev)
452 })
453 })
453 }
454 }
454
455
455 /// Return the binary content of the index entry for the given revision
456 /// Return the binary content of the index entry for the given revision
456 ///
457 ///
457 /// See [get_entry()](`Self::get_entry()`) for cases when `None` is
458 /// See [get_entry()](`Self::get_entry()`) for cases when `None` is
458 /// returned.
459 /// returned.
459 pub fn entry_binary(&self, rev: Revision) -> Option<&[u8]> {
460 pub fn entry_binary(&self, rev: Revision) -> Option<&[u8]> {
460 self.get_entry(rev).map(|e| {
461 self.get_entry(rev).map(|e| {
461 let bytes = e.as_bytes();
462 let bytes = e.as_bytes();
462 if rev.0 == 0 {
463 if rev.0 == 0 {
463 &bytes[4..]
464 &bytes[4..]
464 } else {
465 } else {
465 bytes
466 bytes
466 }
467 }
467 })
468 })
468 }
469 }
469
470
470 pub fn entry_as_params(
471 pub fn entry_as_params(
471 &self,
472 &self,
472 rev: UncheckedRevision,
473 rev: UncheckedRevision,
473 ) -> Option<RevisionDataParams> {
474 ) -> Option<RevisionDataParams> {
474 let rev = self.check_revision(rev)?;
475 let rev = self.check_revision(rev)?;
475 self.get_entry(rev).map(|e| RevisionDataParams {
476 self.get_entry(rev).map(|e| RevisionDataParams {
476 flags: e.flags(),
477 flags: e.flags(),
477 data_offset: if rev.0 == 0 && !self.bytes.is_new() {
478 data_offset: if rev.0 == 0 && !self.bytes.is_new() {
478 e.flags() as u64
479 e.flags() as u64
479 } else {
480 } else {
480 e.raw_offset()
481 e.raw_offset()
481 },
482 },
482 data_compressed_length: e.compressed_len().try_into().unwrap(),
483 data_compressed_length: e.compressed_len().try_into().unwrap(),
483 data_uncompressed_length: e.uncompressed_len(),
484 data_uncompressed_length: e.uncompressed_len(),
484 data_delta_base: e.base_revision_or_base_of_delta_chain().0,
485 data_delta_base: e.base_revision_or_base_of_delta_chain().0,
485 link_rev: e.link_revision().0,
486 link_rev: e.link_revision().0,
486 parent_rev_1: e.p1().0,
487 parent_rev_1: e.p1().0,
487 parent_rev_2: e.p2().0,
488 parent_rev_2: e.p2().0,
488 node_id: e.hash().as_bytes().try_into().unwrap(),
489 node_id: e.hash().as_bytes().try_into().unwrap(),
489 ..Default::default()
490 ..Default::default()
490 })
491 })
491 }
492 }
492
493
493 fn get_entry_inline(
494 fn get_entry_inline(
494 &self,
495 &self,
495 rev: Revision,
496 rev: Revision,
496 offsets: &[usize],
497 offsets: &[usize],
497 ) -> IndexEntry {
498 ) -> IndexEntry {
498 let start = offsets[rev.0 as usize];
499 let start = offsets[rev.0 as usize];
499 let end = start + INDEX_ENTRY_SIZE;
500 let end = start + INDEX_ENTRY_SIZE;
500 let bytes = &self.bytes[start..end];
501 let bytes = &self.bytes[start..end];
501
502
502 // See IndexEntry for an explanation of this override.
503 // See IndexEntry for an explanation of this override.
503 let offset_override = Some(end);
504 let offset_override = Some(end);
504
505
505 IndexEntry {
506 IndexEntry {
506 bytes,
507 bytes,
507 offset_override,
508 offset_override,
508 }
509 }
509 }
510 }
510
511
511 fn get_entry_separated(&self, rev: Revision) -> IndexEntry {
512 fn get_entry_separated(&self, rev: Revision) -> IndexEntry {
512 let start = rev.0 as usize * INDEX_ENTRY_SIZE;
513 let start = rev.0 as usize * INDEX_ENTRY_SIZE;
513 let end = start + INDEX_ENTRY_SIZE;
514 let end = start + INDEX_ENTRY_SIZE;
514 let bytes = &self.bytes[start..end];
515 let bytes = &self.bytes[start..end];
515
516
516 // Override the offset of the first revision as its bytes are used
517 // Override the offset of the first revision as its bytes are used
517 // for the index's metadata (saving space because it is always 0)
518 // for the index's metadata (saving space because it is always 0)
518 let offset_override = if rev == Revision(0) { Some(0) } else { None };
519 let offset_override = if rev == Revision(0) { Some(0) } else { None };
519
520
520 IndexEntry {
521 IndexEntry {
521 bytes,
522 bytes,
522 offset_override,
523 offset_override,
523 }
524 }
524 }
525 }
525
526
527 fn null_entry(&self) -> IndexEntry {
528 IndexEntry {
529 bytes: &[0; INDEX_ENTRY_SIZE],
530 offset_override: Some(0),
531 }
532 }
533
526 /// Return the head revisions of this index
534 /// Return the head revisions of this index
527 pub fn head_revs(&mut self) -> Result<Vec<Revision>, GraphError> {
535 pub fn head_revs(&mut self) -> Result<Vec<Revision>, GraphError> {
528 self.head_revs_filtered(&HashSet::new())
536 self.head_revs_filtered(&HashSet::new())
529 }
537 }
530
538
531 /// Return the head revisions of this index
539 /// Return the head revisions of this index
532 pub fn head_revs_filtered(
540 pub fn head_revs_filtered(
533 &mut self,
541 &mut self,
534 filtered_revs: &HashSet<Revision>,
542 filtered_revs: &HashSet<Revision>,
535 ) -> Result<Vec<Revision>, GraphError> {
543 ) -> Result<Vec<Revision>, GraphError> {
536 if !self.head_revs.is_empty() && filtered_revs == &self.filtered_revs {
544 if !self.head_revs.is_empty() && filtered_revs == &self.filtered_revs {
537 return Ok(self.head_revs.to_owned());
545 return Ok(self.head_revs.to_owned());
538 }
546 }
539 let mut revs: HashSet<Revision, RandomState> =
547 let mut revs: HashSet<Revision, RandomState> =
540 if filtered_revs.is_empty() {
548 if filtered_revs.is_empty() {
541 (0..self.len())
549 (0..self.len())
542 .into_iter()
550 .into_iter()
543 .map(|i| Revision(i as BaseRevision))
551 .map(|i| Revision(i as BaseRevision))
544 .collect()
552 .collect()
545 } else {
553 } else {
546 (0..self.len())
554 (0..self.len())
547 .into_iter()
555 .into_iter()
548 .filter_map(|i| {
556 .filter_map(|i| {
549 let r = Revision(i as BaseRevision);
557 let r = Revision(i as BaseRevision);
550 if filtered_revs.contains(&r) {
558 if filtered_revs.contains(&r) {
551 None
559 None
552 } else {
560 } else {
553 Some(r)
561 Some(r)
554 }
562 }
555 })
563 })
556 .collect()
564 .collect()
557 };
565 };
558 dagops::retain_heads(self, &mut revs)?;
566 dagops::retain_heads(self, &mut revs)?;
559 if self.is_empty() {
567 if self.is_empty() {
560 revs.insert(NULL_REVISION);
568 revs.insert(NULL_REVISION);
561 }
569 }
562 let mut as_vec: Vec<Revision> =
570 let mut as_vec: Vec<Revision> =
563 revs.into_iter().map(Into::into).collect();
571 revs.into_iter().map(Into::into).collect();
564 as_vec.sort_unstable();
572 as_vec.sort_unstable();
565 self.head_revs = as_vec.to_owned();
573 self.head_revs = as_vec.to_owned();
566 self.filtered_revs = filtered_revs.to_owned();
574 self.filtered_revs = filtered_revs.to_owned();
567 Ok(as_vec)
575 Ok(as_vec)
568 }
576 }
569
577
570 /// Obtain the delta chain for a revision.
578 /// Obtain the delta chain for a revision.
571 ///
579 ///
572 /// `stop_rev` specifies a revision to stop at. If not specified, we
580 /// `stop_rev` specifies a revision to stop at. If not specified, we
573 /// stop at the base of the chain.
581 /// stop at the base of the chain.
574 ///
582 ///
575 /// Returns a 2-tuple of (chain, stopped) where `chain` is a vec of
583 /// Returns a 2-tuple of (chain, stopped) where `chain` is a vec of
576 /// revs in ascending order and `stopped` is a bool indicating whether
584 /// revs in ascending order and `stopped` is a bool indicating whether
577 /// `stoprev` was hit.
585 /// `stoprev` was hit.
578 pub fn delta_chain(
586 pub fn delta_chain(
579 &self,
587 &self,
580 rev: Revision,
588 rev: Revision,
581 stop_rev: Option<Revision>,
589 stop_rev: Option<Revision>,
582 ) -> Result<(Vec<Revision>, bool), HgError> {
590 ) -> Result<(Vec<Revision>, bool), HgError> {
583 let mut current_rev = rev;
591 let mut current_rev = rev;
584 let mut entry = self.get_entry(rev).unwrap();
592 let mut entry = self.get_entry(rev).unwrap();
585 let mut chain = vec![];
593 let mut chain = vec![];
586 while current_rev.0 != entry.base_revision_or_base_of_delta_chain().0
594 while current_rev.0 != entry.base_revision_or_base_of_delta_chain().0
587 && stop_rev.map(|r| r != current_rev).unwrap_or(true)
595 && stop_rev.map(|r| r != current_rev).unwrap_or(true)
588 {
596 {
589 chain.push(current_rev);
597 chain.push(current_rev);
590 let new_rev = if self.uses_generaldelta() {
598 let new_rev = if self.uses_generaldelta() {
591 entry.base_revision_or_base_of_delta_chain()
599 entry.base_revision_or_base_of_delta_chain()
592 } else {
600 } else {
593 UncheckedRevision(current_rev.0 - 1)
601 UncheckedRevision(current_rev.0 - 1)
594 };
602 };
595 if new_rev.0 == NULL_REVISION.0 {
596 break;
597 }
598 current_rev = self.check_revision(new_rev).ok_or_else(|| {
603 current_rev = self.check_revision(new_rev).ok_or_else(|| {
599 HgError::corrupted(format!("Revision {new_rev} out of range"))
604 HgError::corrupted(format!("Revision {new_rev} out of range"))
600 })?;
605 })?;
606 if current_rev.0 == NULL_REVISION.0 {
607 break;
608 }
601 entry = self.get_entry(current_rev).unwrap()
609 entry = self.get_entry(current_rev).unwrap()
602 }
610 }
603
611
604 let stopped = if stop_rev.map(|r| current_rev == r).unwrap_or(false) {
612 let stopped = if stop_rev.map(|r| current_rev == r).unwrap_or(false) {
605 true
613 true
606 } else {
614 } else {
607 chain.push(current_rev);
615 chain.push(current_rev);
608 false
616 false
609 };
617 };
610 chain.reverse();
618 chain.reverse();
611 Ok((chain, stopped))
619 Ok((chain, stopped))
612 }
620 }
613
621
614 pub fn find_snapshots(
622 pub fn find_snapshots(
615 &self,
623 &self,
616 start_rev: UncheckedRevision,
624 start_rev: UncheckedRevision,
617 end_rev: UncheckedRevision,
625 end_rev: UncheckedRevision,
618 cache: &mut impl SnapshotsCache,
626 cache: &mut impl SnapshotsCache,
619 ) -> Result<(), RevlogError> {
627 ) -> Result<(), RevlogError> {
620 let mut start_rev = start_rev.0;
628 let mut start_rev = start_rev.0;
621 let mut end_rev = end_rev.0;
629 let mut end_rev = end_rev.0;
622 end_rev += 1;
630 end_rev += 1;
623 let len = self.len().try_into().unwrap();
631 let len = self.len().try_into().unwrap();
624 if end_rev > len {
632 if end_rev > len {
625 end_rev = len;
633 end_rev = len;
626 }
634 }
627 if start_rev < 0 {
635 if start_rev < 0 {
628 start_rev = 0;
636 start_rev = 0;
629 }
637 }
630 for rev in start_rev..end_rev {
638 for rev in start_rev..end_rev {
631 if !self.is_snapshot_unchecked(Revision(rev))? {
639 if !self.is_snapshot_unchecked(Revision(rev))? {
632 continue;
640 continue;
633 }
641 }
634 let mut base = self
642 let mut base = self
635 .get_entry(Revision(rev))
643 .get_entry(Revision(rev))
636 .unwrap()
644 .unwrap()
637 .base_revision_or_base_of_delta_chain();
645 .base_revision_or_base_of_delta_chain();
638 if base.0 == rev {
646 if base.0 == rev {
639 base = NULL_REVISION.into();
647 base = NULL_REVISION.into();
640 }
648 }
641 cache.insert_for(base.0, rev)?;
649 cache.insert_for(base.0, rev)?;
642 }
650 }
643 Ok(())
651 Ok(())
644 }
652 }
645
653
646 /// TODO move this to the trait probably, along with other things
654 /// TODO move this to the trait probably, along with other things
647 pub fn append(
655 pub fn append(
648 &mut self,
656 &mut self,
649 revision_data: RevisionDataParams,
657 revision_data: RevisionDataParams,
650 ) -> Result<(), RevlogError> {
658 ) -> Result<(), RevlogError> {
651 revision_data.validate()?;
659 revision_data.validate()?;
652 let new_offset = self.bytes.len();
660 let new_offset = self.bytes.len();
653 if let Some(offsets) = &mut *self.get_offsets_mut() {
661 if let Some(offsets) = &mut *self.get_offsets_mut() {
654 offsets.push(new_offset)
662 offsets.push(new_offset)
655 }
663 }
656 self.bytes.added.extend(revision_data.into_v1().as_bytes());
664 self.bytes.added.extend(revision_data.into_v1().as_bytes());
657 self.head_revs.clear();
665 self.head_revs.clear();
658 Ok(())
666 Ok(())
659 }
667 }
660
668
661 pub fn pack_header(&self, header: i32) -> [u8; 4] {
669 pub fn pack_header(&self, header: i32) -> [u8; 4] {
662 header.to_be_bytes()
670 header.to_be_bytes()
663 }
671 }
664
672
665 pub fn remove(&mut self, rev: Revision) -> Result<(), RevlogError> {
673 pub fn remove(&mut self, rev: Revision) -> Result<(), RevlogError> {
666 let offsets = self.get_offsets().clone();
674 let offsets = self.get_offsets().clone();
667 self.bytes.remove(rev, offsets.as_deref())?;
675 self.bytes.remove(rev, offsets.as_deref())?;
668 if let Some(offsets) = &mut *self.get_offsets_mut() {
676 if let Some(offsets) = &mut *self.get_offsets_mut() {
669 offsets.truncate(rev.0 as usize)
677 offsets.truncate(rev.0 as usize)
670 }
678 }
671 self.head_revs.clear();
679 self.head_revs.clear();
672 Ok(())
680 Ok(())
673 }
681 }
674
682
675 pub fn clear_caches(&mut self) {
683 pub fn clear_caches(&mut self) {
676 // We need to get the 'inline' value from Python at init and use this
684 // We need to get the 'inline' value from Python at init and use this
677 // instead of offsets to determine whether we're inline since we might
685 // instead of offsets to determine whether we're inline since we might
678 // clear caches. This implies re-populating the offsets on-demand.
686 // clear caches. This implies re-populating the offsets on-demand.
679 self.offsets = RwLock::new(None);
687 self.offsets = RwLock::new(None);
680 self.head_revs.clear();
688 self.head_revs.clear();
681 }
689 }
682
690
683 /// Unchecked version of `is_snapshot`.
691 /// Unchecked version of `is_snapshot`.
684 /// Assumes the caller checked that `rev` is within a valid revision range.
692 /// Assumes the caller checked that `rev` is within a valid revision range.
685 pub fn is_snapshot_unchecked(
693 pub fn is_snapshot_unchecked(
686 &self,
694 &self,
687 mut rev: Revision,
695 mut rev: Revision,
688 ) -> Result<bool, RevlogError> {
696 ) -> Result<bool, RevlogError> {
689 while rev.0 >= 0 {
697 while rev.0 >= 0 {
690 let entry = self.get_entry(rev).unwrap();
698 let entry = self.get_entry(rev).unwrap();
691 let mut base = entry.base_revision_or_base_of_delta_chain().0;
699 let mut base = entry.base_revision_or_base_of_delta_chain().0;
692 if base == rev.0 {
700 if base == rev.0 {
693 base = NULL_REVISION.0;
701 base = NULL_REVISION.0;
694 }
702 }
695 if base == NULL_REVISION.0 {
703 if base == NULL_REVISION.0 {
696 return Ok(true);
704 return Ok(true);
697 }
705 }
698 let [mut p1, mut p2] = self
706 let [mut p1, mut p2] = self
699 .parents(rev)
707 .parents(rev)
700 .map_err(|_| RevlogError::InvalidRevision)?;
708 .map_err(|_| RevlogError::InvalidRevision)?;
701 while let Some(p1_entry) = self.get_entry(p1) {
709 while let Some(p1_entry) = self.get_entry(p1) {
702 if p1_entry.compressed_len() != 0 || p1.0 == 0 {
710 if p1_entry.compressed_len() != 0 || p1.0 == 0 {
703 break;
711 break;
704 }
712 }
705 let parent_base =
713 let parent_base =
706 p1_entry.base_revision_or_base_of_delta_chain();
714 p1_entry.base_revision_or_base_of_delta_chain();
707 if parent_base.0 == p1.0 {
715 if parent_base.0 == p1.0 {
708 break;
716 break;
709 }
717 }
710 p1 = self
718 p1 = self
711 .check_revision(parent_base)
719 .check_revision(parent_base)
712 .ok_or(RevlogError::InvalidRevision)?;
720 .ok_or(RevlogError::InvalidRevision)?;
713 }
721 }
714 while let Some(p2_entry) = self.get_entry(p2) {
722 while let Some(p2_entry) = self.get_entry(p2) {
715 if p2_entry.compressed_len() != 0 || p2.0 == 0 {
723 if p2_entry.compressed_len() != 0 || p2.0 == 0 {
716 break;
724 break;
717 }
725 }
718 let parent_base =
726 let parent_base =
719 p2_entry.base_revision_or_base_of_delta_chain();
727 p2_entry.base_revision_or_base_of_delta_chain();
720 if parent_base.0 == p2.0 {
728 if parent_base.0 == p2.0 {
721 break;
729 break;
722 }
730 }
723 p2 = self
731 p2 = self
724 .check_revision(parent_base)
732 .check_revision(parent_base)
725 .ok_or(RevlogError::InvalidRevision)?;
733 .ok_or(RevlogError::InvalidRevision)?;
726 }
734 }
727 if base == p1.0 || base == p2.0 {
735 if base == p1.0 || base == p2.0 {
728 return Ok(false);
736 return Ok(false);
729 }
737 }
730 rev = self
738 rev = self
731 .check_revision(base.into())
739 .check_revision(base.into())
732 .ok_or(RevlogError::InvalidRevision)?;
740 .ok_or(RevlogError::InvalidRevision)?;
733 }
741 }
734 Ok(rev == NULL_REVISION)
742 Ok(rev == NULL_REVISION)
735 }
743 }
736
744
737 /// Return whether the given revision is a snapshot. Returns an error if
745 /// Return whether the given revision is a snapshot. Returns an error if
738 /// `rev` is not within a valid revision range.
746 /// `rev` is not within a valid revision range.
739 pub fn is_snapshot(
747 pub fn is_snapshot(
740 &self,
748 &self,
741 rev: UncheckedRevision,
749 rev: UncheckedRevision,
742 ) -> Result<bool, RevlogError> {
750 ) -> Result<bool, RevlogError> {
743 let rev = self
751 let rev = self
744 .check_revision(rev)
752 .check_revision(rev)
745 .ok_or_else(|| RevlogError::corrupted("test"))?;
753 .ok_or_else(|| RevlogError::corrupted("test"))?;
746 self.is_snapshot_unchecked(rev)
754 self.is_snapshot_unchecked(rev)
747 }
755 }
756
757 /// Slice revs to reduce the amount of unrelated data to be read from disk.
758 ///
759 /// The index is sliced into groups that should be read in one time.
760 ///
761 /// The initial chunk is sliced until the overall density
762 /// (payload/chunks-span ratio) is above `target_density`.
763 /// No gap smaller than `min_gap_size` is skipped.
764 pub fn slice_chunk_to_density(
765 &self,
766 revs: &[Revision],
767 target_density: f64,
768 min_gap_size: usize,
769 ) -> Vec<Vec<Revision>> {
770 if revs.is_empty() {
771 return vec![];
772 }
773 if revs.len() == 1 {
774 return vec![revs.to_owned()];
775 }
776 let delta_chain_span = self.segment_span(revs);
777 if delta_chain_span < min_gap_size {
778 return vec![revs.to_owned()];
779 }
780 let entries: Vec<_> = revs
781 .iter()
782 .map(|r| {
783 (*r, self.get_entry(*r).unwrap_or_else(|| self.null_entry()))
784 })
785 .collect();
786
787 let mut read_data = delta_chain_span;
788 let chain_payload: u32 =
789 entries.iter().map(|(_r, e)| e.compressed_len()).sum();
790 let mut density = if delta_chain_span > 0 {
791 chain_payload as f64 / delta_chain_span as f64
792 } else {
793 1.0
794 };
795
796 if density >= target_density {
797 return vec![revs.to_owned()];
798 }
799
800 // Store the gaps in a heap to have them sorted by decreasing size
801 let mut gaps = Vec::new();
802 let mut previous_end = None;
803
804 for (i, (_rev, entry)) in entries.iter().enumerate() {
805 let start = entry.c_start() as usize;
806 let length = entry.compressed_len();
807
808 // Skip empty revisions to form larger holes
809 if length == 0 {
810 continue;
811 }
812
813 if let Some(end) = previous_end {
814 let gap_size = start - end;
815 // Only consider holes that are large enough
816 if gap_size > min_gap_size {
817 gaps.push((gap_size, i));
818 }
819 }
820 previous_end = Some(start + length as usize);
821 }
822 if gaps.is_empty() {
823 return vec![revs.to_owned()];
824 }
825 // sort the gaps to pop them from largest to small
826 gaps.sort_unstable();
827
828 // Collect the indices of the largest holes until
829 // the density is acceptable
830 let mut selected = vec![];
831 while let Some((gap_size, gap_id)) = gaps.pop() {
832 if density >= target_density {
833 break;
834 }
835 selected.push(gap_id);
836
837 // The gap sizes are stored as negatives to be sorted decreasingly
838 // by the heap
839 read_data -= gap_size;
840 density = if read_data > 0 {
841 chain_payload as f64 / read_data as f64
842 } else {
843 1.0
844 };
845 if density >= target_density {
846 break;
847 }
848 }
849 selected.sort_unstable();
850 selected.push(revs.len());
851
852 // Cut the revs at collected indices
853 let mut previous_idx = 0;
854 let mut chunks = vec![];
855 for idx in selected {
856 let chunk = self.trim_chunk(&entries, previous_idx, idx);
857 if !chunk.is_empty() {
858 chunks.push(chunk.iter().map(|(rev, _entry)| *rev).collect());
859 }
860 previous_idx = idx;
861 }
862 let chunk = self.trim_chunk(&entries, previous_idx, entries.len());
863 if !chunk.is_empty() {
864 chunks.push(chunk.iter().map(|(rev, _entry)| *rev).collect());
865 }
866
867 chunks
868 }
869
870 /// Get the byte span of a segment of sorted revisions.
871 ///
872 /// Occurrences of [`NULL_REVISION`] are ignored at the beginning of
873 /// the `revs` segment.
874 ///
875 /// panics:
876 /// - if `revs` is empty or only made of `NULL_REVISION`
877 /// - if cannot retrieve entry for the last or first not null element of
878 /// `revs`.
879 fn segment_span(&self, revs: &[Revision]) -> usize {
880 if revs.is_empty() {
881 return 0;
882 }
883 let last_entry = &self.get_entry(revs[revs.len() - 1]).unwrap();
884 let end = last_entry.c_start() + last_entry.compressed_len() as u64;
885 let first_rev = revs.iter().find(|r| r.0 != NULL_REVISION.0).unwrap();
886 let start = if (*first_rev).0 == 0 {
887 0
888 } else {
889 self.get_entry(*first_rev).unwrap().c_start()
890 };
891 (end - start) as usize
892 }
893
894 /// Returns `&revs[startidx..endidx]` without empty trailing revs
895 fn trim_chunk<'a>(
896 &'a self,
897 revs: &'a [(Revision, IndexEntry)],
898 start: usize,
899 mut end: usize,
900 ) -> &'a [(Revision, IndexEntry)] {
901 // Trim empty revs at the end, except the very first rev of a chain
902 let last_rev = revs[end - 1].0;
903 if last_rev.0 < self.len() as BaseRevision {
904 while end > 1
905 && end > start
906 && revs[end - 1].1.compressed_len() == 0
907 {
908 end -= 1
909 }
910 }
911 &revs[start..end]
912 }
748 }
913 }
749 fn inline_scan(bytes: &[u8]) -> (usize, Vec<usize>) {
914 fn inline_scan(bytes: &[u8]) -> (usize, Vec<usize>) {
750 let mut offset: usize = 0;
915 let mut offset: usize = 0;
751 let mut offsets = Vec::new();
916 let mut offsets = Vec::new();
752
917
753 while offset + INDEX_ENTRY_SIZE <= bytes.len() {
918 while offset + INDEX_ENTRY_SIZE <= bytes.len() {
754 offsets.push(offset);
919 offsets.push(offset);
755 let end = offset + INDEX_ENTRY_SIZE;
920 let end = offset + INDEX_ENTRY_SIZE;
756 let entry = IndexEntry {
921 let entry = IndexEntry {
757 bytes: &bytes[offset..end],
922 bytes: &bytes[offset..end],
758 offset_override: None,
923 offset_override: None,
759 };
924 };
760
925
761 offset += INDEX_ENTRY_SIZE + entry.compressed_len() as usize;
926 offset += INDEX_ENTRY_SIZE + entry.compressed_len() as usize;
762 }
927 }
763 (offset, offsets)
928 (offset, offsets)
764 }
929 }
765
930
766 impl super::RevlogIndex for Index {
931 impl super::RevlogIndex for Index {
767 fn len(&self) -> usize {
932 fn len(&self) -> usize {
768 self.len()
933 self.len()
769 }
934 }
770
935
771 fn node(&self, rev: Revision) -> Option<&Node> {
936 fn node(&self, rev: Revision) -> Option<&Node> {
772 if rev == NULL_REVISION {
937 if rev == NULL_REVISION {
773 return Some(&NULL_NODE);
938 return Some(&NULL_NODE);
774 }
939 }
775 self.get_entry(rev).map(|entry| entry.hash())
940 self.get_entry(rev).map(|entry| entry.hash())
776 }
941 }
777 }
942 }
778
943
779 #[derive(Debug)]
944 #[derive(Debug)]
780 pub struct IndexEntry<'a> {
945 pub struct IndexEntry<'a> {
781 bytes: &'a [u8],
946 bytes: &'a [u8],
782 /// Allows to override the offset value of the entry.
947 /// Allows to override the offset value of the entry.
783 ///
948 ///
784 /// For interleaved index and data, the offset stored in the index
949 /// For interleaved index and data, the offset stored in the index
785 /// corresponds to the separated data offset.
950 /// corresponds to the separated data offset.
786 /// It has to be overridden with the actual offset in the interleaved
951 /// It has to be overridden with the actual offset in the interleaved
787 /// index which is just after the index block.
952 /// index which is just after the index block.
788 ///
953 ///
789 /// For separated index and data, the offset stored in the first index
954 /// For separated index and data, the offset stored in the first index
790 /// entry is mixed with the index headers.
955 /// entry is mixed with the index headers.
791 /// It has to be overridden with 0.
956 /// It has to be overridden with 0.
792 offset_override: Option<usize>,
957 offset_override: Option<usize>,
793 }
958 }
794
959
795 impl<'a> IndexEntry<'a> {
960 impl<'a> IndexEntry<'a> {
796 /// Return the offset of the data.
961 /// Return the offset of the data.
797 pub fn offset(&self) -> usize {
962 pub fn offset(&self) -> usize {
798 if let Some(offset_override) = self.offset_override {
963 if let Some(offset_override) = self.offset_override {
799 offset_override
964 offset_override
800 } else {
965 } else {
801 let mut bytes = [0; 8];
966 let mut bytes = [0; 8];
802 bytes[2..8].copy_from_slice(&self.bytes[0..=5]);
967 bytes[2..8].copy_from_slice(&self.bytes[0..=5]);
803 BigEndian::read_u64(&bytes[..]) as usize
968 BigEndian::read_u64(&bytes[..]) as usize
804 }
969 }
805 }
970 }
806 pub fn raw_offset(&self) -> u64 {
971 pub fn raw_offset(&self) -> u64 {
807 BigEndian::read_u64(&self.bytes[0..8])
972 BigEndian::read_u64(&self.bytes[0..8])
808 }
973 }
809
974
975 /// Same result (except potentially for rev 0) as C `index_get_start()`
976 fn c_start(&self) -> u64 {
977 self.raw_offset() >> 16
978 }
979
810 pub fn flags(&self) -> u16 {
980 pub fn flags(&self) -> u16 {
811 BigEndian::read_u16(&self.bytes[6..=7])
981 BigEndian::read_u16(&self.bytes[6..=7])
812 }
982 }
813
983
814 /// Return the compressed length of the data.
984 /// Return the compressed length of the data.
815 pub fn compressed_len(&self) -> u32 {
985 pub fn compressed_len(&self) -> u32 {
816 BigEndian::read_u32(&self.bytes[8..=11])
986 BigEndian::read_u32(&self.bytes[8..=11])
817 }
987 }
818
988
819 /// Return the uncompressed length of the data.
989 /// Return the uncompressed length of the data.
820 pub fn uncompressed_len(&self) -> i32 {
990 pub fn uncompressed_len(&self) -> i32 {
821 BigEndian::read_i32(&self.bytes[12..=15])
991 BigEndian::read_i32(&self.bytes[12..=15])
822 }
992 }
823
993
824 /// Return the revision upon which the data has been derived.
994 /// Return the revision upon which the data has been derived.
825 pub fn base_revision_or_base_of_delta_chain(&self) -> UncheckedRevision {
995 pub fn base_revision_or_base_of_delta_chain(&self) -> UncheckedRevision {
826 // TODO Maybe return an Option when base_revision == rev?
996 // TODO Maybe return an Option when base_revision == rev?
827 // Requires to add rev to IndexEntry
997 // Requires to add rev to IndexEntry
828
998
829 BigEndian::read_i32(&self.bytes[16..]).into()
999 BigEndian::read_i32(&self.bytes[16..]).into()
830 }
1000 }
831
1001
832 pub fn link_revision(&self) -> UncheckedRevision {
1002 pub fn link_revision(&self) -> UncheckedRevision {
833 BigEndian::read_i32(&self.bytes[20..]).into()
1003 BigEndian::read_i32(&self.bytes[20..]).into()
834 }
1004 }
835
1005
836 pub fn p1(&self) -> UncheckedRevision {
1006 pub fn p1(&self) -> UncheckedRevision {
837 BigEndian::read_i32(&self.bytes[24..]).into()
1007 BigEndian::read_i32(&self.bytes[24..]).into()
838 }
1008 }
839
1009
840 pub fn p2(&self) -> UncheckedRevision {
1010 pub fn p2(&self) -> UncheckedRevision {
841 BigEndian::read_i32(&self.bytes[28..]).into()
1011 BigEndian::read_i32(&self.bytes[28..]).into()
842 }
1012 }
843
1013
844 /// Return the hash of revision's full text.
1014 /// Return the hash of revision's full text.
845 ///
1015 ///
846 /// Currently, SHA-1 is used and only the first 20 bytes of this field
1016 /// Currently, SHA-1 is used and only the first 20 bytes of this field
847 /// are used.
1017 /// are used.
848 pub fn hash(&self) -> &'a Node {
1018 pub fn hash(&self) -> &'a Node {
849 (&self.bytes[32..52]).try_into().unwrap()
1019 (&self.bytes[32..52]).try_into().unwrap()
850 }
1020 }
851
1021
852 pub fn as_bytes(&self) -> &'a [u8] {
1022 pub fn as_bytes(&self) -> &'a [u8] {
853 self.bytes
1023 self.bytes
854 }
1024 }
855 }
1025 }
856
1026
857 #[cfg(test)]
1027 #[cfg(test)]
858 mod tests {
1028 mod tests {
859 use super::*;
1029 use super::*;
860 use crate::node::NULL_NODE;
1030 use crate::node::NULL_NODE;
861
1031
862 #[cfg(test)]
1032 #[cfg(test)]
863 #[derive(Debug, Copy, Clone)]
1033 #[derive(Debug, Copy, Clone)]
864 pub struct IndexEntryBuilder {
1034 pub struct IndexEntryBuilder {
865 is_first: bool,
1035 is_first: bool,
866 is_inline: bool,
1036 is_inline: bool,
867 is_general_delta: bool,
1037 is_general_delta: bool,
868 version: u16,
1038 version: u16,
869 offset: usize,
1039 offset: usize,
870 compressed_len: usize,
1040 compressed_len: usize,
871 uncompressed_len: usize,
1041 uncompressed_len: usize,
872 base_revision_or_base_of_delta_chain: Revision,
1042 base_revision_or_base_of_delta_chain: Revision,
873 link_revision: Revision,
1043 link_revision: Revision,
874 p1: Revision,
1044 p1: Revision,
875 p2: Revision,
1045 p2: Revision,
876 node: Node,
1046 node: Node,
877 }
1047 }
878
1048
879 #[cfg(test)]
1049 #[cfg(test)]
880 impl IndexEntryBuilder {
1050 impl IndexEntryBuilder {
881 #[allow(clippy::new_without_default)]
1051 #[allow(clippy::new_without_default)]
882 pub fn new() -> Self {
1052 pub fn new() -> Self {
883 Self {
1053 Self {
884 is_first: false,
1054 is_first: false,
885 is_inline: false,
1055 is_inline: false,
886 is_general_delta: true,
1056 is_general_delta: true,
887 version: 1,
1057 version: 1,
888 offset: 0,
1058 offset: 0,
889 compressed_len: 0,
1059 compressed_len: 0,
890 uncompressed_len: 0,
1060 uncompressed_len: 0,
891 base_revision_or_base_of_delta_chain: Revision(0),
1061 base_revision_or_base_of_delta_chain: Revision(0),
892 link_revision: Revision(0),
1062 link_revision: Revision(0),
893 p1: NULL_REVISION,
1063 p1: NULL_REVISION,
894 p2: NULL_REVISION,
1064 p2: NULL_REVISION,
895 node: NULL_NODE,
1065 node: NULL_NODE,
896 }
1066 }
897 }
1067 }
898
1068
899 pub fn is_first(&mut self, value: bool) -> &mut Self {
1069 pub fn is_first(&mut self, value: bool) -> &mut Self {
900 self.is_first = value;
1070 self.is_first = value;
901 self
1071 self
902 }
1072 }
903
1073
904 pub fn with_inline(&mut self, value: bool) -> &mut Self {
1074 pub fn with_inline(&mut self, value: bool) -> &mut Self {
905 self.is_inline = value;
1075 self.is_inline = value;
906 self
1076 self
907 }
1077 }
908
1078
909 pub fn with_general_delta(&mut self, value: bool) -> &mut Self {
1079 pub fn with_general_delta(&mut self, value: bool) -> &mut Self {
910 self.is_general_delta = value;
1080 self.is_general_delta = value;
911 self
1081 self
912 }
1082 }
913
1083
914 pub fn with_version(&mut self, value: u16) -> &mut Self {
1084 pub fn with_version(&mut self, value: u16) -> &mut Self {
915 self.version = value;
1085 self.version = value;
916 self
1086 self
917 }
1087 }
918
1088
919 pub fn with_offset(&mut self, value: usize) -> &mut Self {
1089 pub fn with_offset(&mut self, value: usize) -> &mut Self {
920 self.offset = value;
1090 self.offset = value;
921 self
1091 self
922 }
1092 }
923
1093
924 pub fn with_compressed_len(&mut self, value: usize) -> &mut Self {
1094 pub fn with_compressed_len(&mut self, value: usize) -> &mut Self {
925 self.compressed_len = value;
1095 self.compressed_len = value;
926 self
1096 self
927 }
1097 }
928
1098
929 pub fn with_uncompressed_len(&mut self, value: usize) -> &mut Self {
1099 pub fn with_uncompressed_len(&mut self, value: usize) -> &mut Self {
930 self.uncompressed_len = value;
1100 self.uncompressed_len = value;
931 self
1101 self
932 }
1102 }
933
1103
934 pub fn with_base_revision_or_base_of_delta_chain(
1104 pub fn with_base_revision_or_base_of_delta_chain(
935 &mut self,
1105 &mut self,
936 value: Revision,
1106 value: Revision,
937 ) -> &mut Self {
1107 ) -> &mut Self {
938 self.base_revision_or_base_of_delta_chain = value;
1108 self.base_revision_or_base_of_delta_chain = value;
939 self
1109 self
940 }
1110 }
941
1111
942 pub fn with_link_revision(&mut self, value: Revision) -> &mut Self {
1112 pub fn with_link_revision(&mut self, value: Revision) -> &mut Self {
943 self.link_revision = value;
1113 self.link_revision = value;
944 self
1114 self
945 }
1115 }
946
1116
947 pub fn with_p1(&mut self, value: Revision) -> &mut Self {
1117 pub fn with_p1(&mut self, value: Revision) -> &mut Self {
948 self.p1 = value;
1118 self.p1 = value;
949 self
1119 self
950 }
1120 }
951
1121
952 pub fn with_p2(&mut self, value: Revision) -> &mut Self {
1122 pub fn with_p2(&mut self, value: Revision) -> &mut Self {
953 self.p2 = value;
1123 self.p2 = value;
954 self
1124 self
955 }
1125 }
956
1126
957 pub fn with_node(&mut self, value: Node) -> &mut Self {
1127 pub fn with_node(&mut self, value: Node) -> &mut Self {
958 self.node = value;
1128 self.node = value;
959 self
1129 self
960 }
1130 }
961
1131
962 pub fn build(&self) -> Vec<u8> {
1132 pub fn build(&self) -> Vec<u8> {
963 let mut bytes = Vec::with_capacity(INDEX_ENTRY_SIZE);
1133 let mut bytes = Vec::with_capacity(INDEX_ENTRY_SIZE);
964 if self.is_first {
1134 if self.is_first {
965 bytes.extend(&match (self.is_general_delta, self.is_inline) {
1135 bytes.extend(&match (self.is_general_delta, self.is_inline) {
966 (false, false) => [0u8, 0],
1136 (false, false) => [0u8, 0],
967 (false, true) => [0u8, 1],
1137 (false, true) => [0u8, 1],
968 (true, false) => [0u8, 2],
1138 (true, false) => [0u8, 2],
969 (true, true) => [0u8, 3],
1139 (true, true) => [0u8, 3],
970 });
1140 });
971 bytes.extend(&self.version.to_be_bytes());
1141 bytes.extend(&self.version.to_be_bytes());
972 // Remaining offset bytes.
1142 // Remaining offset bytes.
973 bytes.extend(&[0u8; 2]);
1143 bytes.extend(&[0u8; 2]);
974 } else {
1144 } else {
975 // Offset stored on 48 bits (6 bytes)
1145 // Offset stored on 48 bits (6 bytes)
976 bytes.extend(&(self.offset as u64).to_be_bytes()[2..]);
1146 bytes.extend(&(self.offset as u64).to_be_bytes()[2..]);
977 }
1147 }
978 bytes.extend(&[0u8; 2]); // Revision flags.
1148 bytes.extend(&[0u8; 2]); // Revision flags.
979 bytes.extend(&(self.compressed_len as u32).to_be_bytes());
1149 bytes.extend(&(self.compressed_len as u32).to_be_bytes());
980 bytes.extend(&(self.uncompressed_len as u32).to_be_bytes());
1150 bytes.extend(&(self.uncompressed_len as u32).to_be_bytes());
981 bytes.extend(
1151 bytes.extend(
982 &self.base_revision_or_base_of_delta_chain.0.to_be_bytes(),
1152 &self.base_revision_or_base_of_delta_chain.0.to_be_bytes(),
983 );
1153 );
984 bytes.extend(&self.link_revision.0.to_be_bytes());
1154 bytes.extend(&self.link_revision.0.to_be_bytes());
985 bytes.extend(&self.p1.0.to_be_bytes());
1155 bytes.extend(&self.p1.0.to_be_bytes());
986 bytes.extend(&self.p2.0.to_be_bytes());
1156 bytes.extend(&self.p2.0.to_be_bytes());
987 bytes.extend(self.node.as_bytes());
1157 bytes.extend(self.node.as_bytes());
988 bytes.extend(vec![0u8; 12]);
1158 bytes.extend(vec![0u8; 12]);
989 bytes
1159 bytes
990 }
1160 }
991 }
1161 }
992
1162
993 pub fn is_inline(index_bytes: &[u8]) -> bool {
1163 pub fn is_inline(index_bytes: &[u8]) -> bool {
994 IndexHeader::parse(index_bytes)
1164 IndexHeader::parse(index_bytes)
995 .expect("too short")
1165 .expect("too short")
996 .unwrap()
1166 .unwrap()
997 .format_flags()
1167 .format_flags()
998 .is_inline()
1168 .is_inline()
999 }
1169 }
1000
1170
1001 pub fn uses_generaldelta(index_bytes: &[u8]) -> bool {
1171 pub fn uses_generaldelta(index_bytes: &[u8]) -> bool {
1002 IndexHeader::parse(index_bytes)
1172 IndexHeader::parse(index_bytes)
1003 .expect("too short")
1173 .expect("too short")
1004 .unwrap()
1174 .unwrap()
1005 .format_flags()
1175 .format_flags()
1006 .uses_generaldelta()
1176 .uses_generaldelta()
1007 }
1177 }
1008
1178
1009 pub fn get_version(index_bytes: &[u8]) -> u16 {
1179 pub fn get_version(index_bytes: &[u8]) -> u16 {
1010 IndexHeader::parse(index_bytes)
1180 IndexHeader::parse(index_bytes)
1011 .expect("too short")
1181 .expect("too short")
1012 .unwrap()
1182 .unwrap()
1013 .format_version()
1183 .format_version()
1014 }
1184 }
1015
1185
1016 #[test]
1186 #[test]
1017 fn flags_when_no_inline_flag_test() {
1187 fn flags_when_no_inline_flag_test() {
1018 let bytes = IndexEntryBuilder::new()
1188 let bytes = IndexEntryBuilder::new()
1019 .is_first(true)
1189 .is_first(true)
1020 .with_general_delta(false)
1190 .with_general_delta(false)
1021 .with_inline(false)
1191 .with_inline(false)
1022 .build();
1192 .build();
1023
1193
1024 assert!(!is_inline(&bytes));
1194 assert!(!is_inline(&bytes));
1025 assert!(!uses_generaldelta(&bytes));
1195 assert!(!uses_generaldelta(&bytes));
1026 }
1196 }
1027
1197
1028 #[test]
1198 #[test]
1029 fn flags_when_inline_flag_test() {
1199 fn flags_when_inline_flag_test() {
1030 let bytes = IndexEntryBuilder::new()
1200 let bytes = IndexEntryBuilder::new()
1031 .is_first(true)
1201 .is_first(true)
1032 .with_general_delta(false)
1202 .with_general_delta(false)
1033 .with_inline(true)
1203 .with_inline(true)
1034 .build();
1204 .build();
1035
1205
1036 assert!(is_inline(&bytes));
1206 assert!(is_inline(&bytes));
1037 assert!(!uses_generaldelta(&bytes));
1207 assert!(!uses_generaldelta(&bytes));
1038 }
1208 }
1039
1209
1040 #[test]
1210 #[test]
1041 fn flags_when_inline_and_generaldelta_flags_test() {
1211 fn flags_when_inline_and_generaldelta_flags_test() {
1042 let bytes = IndexEntryBuilder::new()
1212 let bytes = IndexEntryBuilder::new()
1043 .is_first(true)
1213 .is_first(true)
1044 .with_general_delta(true)
1214 .with_general_delta(true)
1045 .with_inline(true)
1215 .with_inline(true)
1046 .build();
1216 .build();
1047
1217
1048 assert!(is_inline(&bytes));
1218 assert!(is_inline(&bytes));
1049 assert!(uses_generaldelta(&bytes));
1219 assert!(uses_generaldelta(&bytes));
1050 }
1220 }
1051
1221
1052 #[test]
1222 #[test]
1053 fn test_offset() {
1223 fn test_offset() {
1054 let bytes = IndexEntryBuilder::new().with_offset(1).build();
1224 let bytes = IndexEntryBuilder::new().with_offset(1).build();
1055 let entry = IndexEntry {
1225 let entry = IndexEntry {
1056 bytes: &bytes,
1226 bytes: &bytes,
1057 offset_override: None,
1227 offset_override: None,
1058 };
1228 };
1059
1229
1060 assert_eq!(entry.offset(), 1)
1230 assert_eq!(entry.offset(), 1)
1061 }
1231 }
1062
1232
1063 #[test]
1233 #[test]
1064 fn test_with_overridden_offset() {
1234 fn test_with_overridden_offset() {
1065 let bytes = IndexEntryBuilder::new().with_offset(1).build();
1235 let bytes = IndexEntryBuilder::new().with_offset(1).build();
1066 let entry = IndexEntry {
1236 let entry = IndexEntry {
1067 bytes: &bytes,
1237 bytes: &bytes,
1068 offset_override: Some(2),
1238 offset_override: Some(2),
1069 };
1239 };
1070
1240
1071 assert_eq!(entry.offset(), 2)
1241 assert_eq!(entry.offset(), 2)
1072 }
1242 }
1073
1243
1074 #[test]
1244 #[test]
1075 fn test_compressed_len() {
1245 fn test_compressed_len() {
1076 let bytes = IndexEntryBuilder::new().with_compressed_len(1).build();
1246 let bytes = IndexEntryBuilder::new().with_compressed_len(1).build();
1077 let entry = IndexEntry {
1247 let entry = IndexEntry {
1078 bytes: &bytes,
1248 bytes: &bytes,
1079 offset_override: None,
1249 offset_override: None,
1080 };
1250 };
1081
1251
1082 assert_eq!(entry.compressed_len(), 1)
1252 assert_eq!(entry.compressed_len(), 1)
1083 }
1253 }
1084
1254
1085 #[test]
1255 #[test]
1086 fn test_uncompressed_len() {
1256 fn test_uncompressed_len() {
1087 let bytes = IndexEntryBuilder::new().with_uncompressed_len(1).build();
1257 let bytes = IndexEntryBuilder::new().with_uncompressed_len(1).build();
1088 let entry = IndexEntry {
1258 let entry = IndexEntry {
1089 bytes: &bytes,
1259 bytes: &bytes,
1090 offset_override: None,
1260 offset_override: None,
1091 };
1261 };
1092
1262
1093 assert_eq!(entry.uncompressed_len(), 1)
1263 assert_eq!(entry.uncompressed_len(), 1)
1094 }
1264 }
1095
1265
1096 #[test]
1266 #[test]
1097 fn test_base_revision_or_base_of_delta_chain() {
1267 fn test_base_revision_or_base_of_delta_chain() {
1098 let bytes = IndexEntryBuilder::new()
1268 let bytes = IndexEntryBuilder::new()
1099 .with_base_revision_or_base_of_delta_chain(Revision(1))
1269 .with_base_revision_or_base_of_delta_chain(Revision(1))
1100 .build();
1270 .build();
1101 let entry = IndexEntry {
1271 let entry = IndexEntry {
1102 bytes: &bytes,
1272 bytes: &bytes,
1103 offset_override: None,
1273 offset_override: None,
1104 };
1274 };
1105
1275
1106 assert_eq!(entry.base_revision_or_base_of_delta_chain(), 1.into())
1276 assert_eq!(entry.base_revision_or_base_of_delta_chain(), 1.into())
1107 }
1277 }
1108
1278
1109 #[test]
1279 #[test]
1110 fn link_revision_test() {
1280 fn link_revision_test() {
1111 let bytes = IndexEntryBuilder::new()
1281 let bytes = IndexEntryBuilder::new()
1112 .with_link_revision(Revision(123))
1282 .with_link_revision(Revision(123))
1113 .build();
1283 .build();
1114
1284
1115 let entry = IndexEntry {
1285 let entry = IndexEntry {
1116 bytes: &bytes,
1286 bytes: &bytes,
1117 offset_override: None,
1287 offset_override: None,
1118 };
1288 };
1119
1289
1120 assert_eq!(entry.link_revision(), 123.into());
1290 assert_eq!(entry.link_revision(), 123.into());
1121 }
1291 }
1122
1292
1123 #[test]
1293 #[test]
1124 fn p1_test() {
1294 fn p1_test() {
1125 let bytes = IndexEntryBuilder::new().with_p1(Revision(123)).build();
1295 let bytes = IndexEntryBuilder::new().with_p1(Revision(123)).build();
1126
1296
1127 let entry = IndexEntry {
1297 let entry = IndexEntry {
1128 bytes: &bytes,
1298 bytes: &bytes,
1129 offset_override: None,
1299 offset_override: None,
1130 };
1300 };
1131
1301
1132 assert_eq!(entry.p1(), 123.into());
1302 assert_eq!(entry.p1(), 123.into());
1133 }
1303 }
1134
1304
1135 #[test]
1305 #[test]
1136 fn p2_test() {
1306 fn p2_test() {
1137 let bytes = IndexEntryBuilder::new().with_p2(Revision(123)).build();
1307 let bytes = IndexEntryBuilder::new().with_p2(Revision(123)).build();
1138
1308
1139 let entry = IndexEntry {
1309 let entry = IndexEntry {
1140 bytes: &bytes,
1310 bytes: &bytes,
1141 offset_override: None,
1311 offset_override: None,
1142 };
1312 };
1143
1313
1144 assert_eq!(entry.p2(), 123.into());
1314 assert_eq!(entry.p2(), 123.into());
1145 }
1315 }
1146
1316
1147 #[test]
1317 #[test]
1148 fn node_test() {
1318 fn node_test() {
1149 let node = Node::from_hex("0123456789012345678901234567890123456789")
1319 let node = Node::from_hex("0123456789012345678901234567890123456789")
1150 .unwrap();
1320 .unwrap();
1151 let bytes = IndexEntryBuilder::new().with_node(node).build();
1321 let bytes = IndexEntryBuilder::new().with_node(node).build();
1152
1322
1153 let entry = IndexEntry {
1323 let entry = IndexEntry {
1154 bytes: &bytes,
1324 bytes: &bytes,
1155 offset_override: None,
1325 offset_override: None,
1156 };
1326 };
1157
1327
1158 assert_eq!(*entry.hash(), node);
1328 assert_eq!(*entry.hash(), node);
1159 }
1329 }
1160
1330
1161 #[test]
1331 #[test]
1162 fn version_test() {
1332 fn version_test() {
1163 let bytes = IndexEntryBuilder::new()
1333 let bytes = IndexEntryBuilder::new()
1164 .is_first(true)
1334 .is_first(true)
1165 .with_version(2)
1335 .with_version(2)
1166 .build();
1336 .build();
1167
1337
1168 assert_eq!(get_version(&bytes), 2)
1338 assert_eq!(get_version(&bytes), 2)
1169 }
1339 }
1170 }
1340 }
1171
1341
1172 #[cfg(test)]
1342 #[cfg(test)]
1173 pub use tests::IndexEntryBuilder;
1343 pub use tests::IndexEntryBuilder;
@@ -1,928 +1,970 b''
1 // revlog.rs
1 // revlog.rs
2 //
2 //
3 // Copyright 2019-2020 Georges Racinet <georges.racinet@octobus.net>
3 // Copyright 2019-2020 Georges Racinet <georges.racinet@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::{
8 use crate::{
9 cindex,
9 cindex,
10 conversion::rev_pyiter_collect,
10 conversion::rev_pyiter_collect,
11 utils::{node_from_py_bytes, node_from_py_object},
11 utils::{node_from_py_bytes, node_from_py_object},
12 PyRevision,
12 PyRevision,
13 };
13 };
14 use cpython::{
14 use cpython::{
15 buffer::{Element, PyBuffer},
15 buffer::{Element, PyBuffer},
16 exc::{IndexError, ValueError},
16 exc::{IndexError, ValueError},
17 ObjectProtocol, PyBool, PyBytes, PyClone, PyDict, PyErr, PyInt, PyList,
17 ObjectProtocol, PyBool, PyBytes, PyClone, PyDict, PyErr, PyInt, PyList,
18 PyModule, PyObject, PyResult, PySet, PyString, PyTuple, Python,
18 PyModule, PyObject, PyResult, PySet, PyString, PyTuple, Python,
19 PythonObject, ToPyObject,
19 PythonObject, ToPyObject,
20 };
20 };
21 use hg::{
21 use hg::{
22 errors::HgError,
22 errors::HgError,
23 index::{IndexHeader, RevisionDataParams, SnapshotsCache},
23 index::{IndexHeader, RevisionDataParams, SnapshotsCache},
24 nodemap::{Block, NodeMapError, NodeTree},
24 nodemap::{Block, NodeMapError, NodeTree},
25 revlog::{nodemap::NodeMap, NodePrefix, RevlogError, RevlogIndex},
25 revlog::{nodemap::NodeMap, NodePrefix, RevlogError, RevlogIndex},
26 BaseRevision, Revision, UncheckedRevision, NULL_REVISION,
26 BaseRevision, Revision, UncheckedRevision, NULL_REVISION,
27 };
27 };
28 use std::cell::RefCell;
28 use std::cell::RefCell;
29
29
30 /// Return a Struct implementing the Graph trait
30 /// Return a Struct implementing the Graph trait
31 pub(crate) fn pyindex_to_graph(
31 pub(crate) fn pyindex_to_graph(
32 py: Python,
32 py: Python,
33 index: PyObject,
33 index: PyObject,
34 ) -> PyResult<cindex::Index> {
34 ) -> PyResult<cindex::Index> {
35 match index.extract::<MixedIndex>(py) {
35 match index.extract::<MixedIndex>(py) {
36 Ok(midx) => Ok(midx.clone_cindex(py)),
36 Ok(midx) => Ok(midx.clone_cindex(py)),
37 Err(_) => cindex::Index::new(py, index),
37 Err(_) => cindex::Index::new(py, index),
38 }
38 }
39 }
39 }
40
40
41 py_class!(pub class MixedIndex |py| {
41 py_class!(pub class MixedIndex |py| {
42 data cindex: RefCell<cindex::Index>;
42 data cindex: RefCell<cindex::Index>;
43 data index: RefCell<hg::index::Index>;
43 data index: RefCell<hg::index::Index>;
44 data nt: RefCell<Option<NodeTree>>;
44 data nt: RefCell<Option<NodeTree>>;
45 data docket: RefCell<Option<PyObject>>;
45 data docket: RefCell<Option<PyObject>>;
46 // Holds a reference to the mmap'ed persistent nodemap data
46 // Holds a reference to the mmap'ed persistent nodemap data
47 data nodemap_mmap: RefCell<Option<PyBuffer>>;
47 data nodemap_mmap: RefCell<Option<PyBuffer>>;
48 // Holds a reference to the mmap'ed persistent index data
48 // Holds a reference to the mmap'ed persistent index data
49 data index_mmap: RefCell<Option<PyBuffer>>;
49 data index_mmap: RefCell<Option<PyBuffer>>;
50
50
51 def __new__(
51 def __new__(
52 _cls,
52 _cls,
53 cindex: PyObject,
53 cindex: PyObject,
54 data: PyObject,
54 data: PyObject,
55 default_header: u32,
55 default_header: u32,
56 ) -> PyResult<MixedIndex> {
56 ) -> PyResult<MixedIndex> {
57 Self::new(py, cindex, data, default_header)
57 Self::new(py, cindex, data, default_header)
58 }
58 }
59
59
60 /// Compatibility layer used for Python consumers needing access to the C index
60 /// Compatibility layer used for Python consumers needing access to the C index
61 ///
61 ///
62 /// Only use case so far is `scmutil.shortesthexnodeidprefix`,
62 /// Only use case so far is `scmutil.shortesthexnodeidprefix`,
63 /// that may need to build a custom `nodetree`, based on a specified revset.
63 /// that may need to build a custom `nodetree`, based on a specified revset.
64 /// With a Rust implementation of the nodemap, we will be able to get rid of
64 /// With a Rust implementation of the nodemap, we will be able to get rid of
65 /// this, by exposing our own standalone nodemap class,
65 /// this, by exposing our own standalone nodemap class,
66 /// ready to accept `MixedIndex`.
66 /// ready to accept `MixedIndex`.
67 def get_cindex(&self) -> PyResult<PyObject> {
67 def get_cindex(&self) -> PyResult<PyObject> {
68 Ok(self.cindex(py).borrow().inner().clone_ref(py))
68 Ok(self.cindex(py).borrow().inner().clone_ref(py))
69 }
69 }
70
70
71 // Index API involving nodemap, as defined in mercurial/pure/parsers.py
71 // Index API involving nodemap, as defined in mercurial/pure/parsers.py
72
72
73 /// Return Revision if found, raises a bare `error.RevlogError`
73 /// Return Revision if found, raises a bare `error.RevlogError`
74 /// in case of ambiguity, same as C version does
74 /// in case of ambiguity, same as C version does
75 def get_rev(&self, node: PyBytes) -> PyResult<Option<PyRevision>> {
75 def get_rev(&self, node: PyBytes) -> PyResult<Option<PyRevision>> {
76 let opt = self.get_nodetree(py)?.borrow();
76 let opt = self.get_nodetree(py)?.borrow();
77 let nt = opt.as_ref().unwrap();
77 let nt = opt.as_ref().unwrap();
78 let idx = &*self.cindex(py).borrow();
78 let idx = &*self.cindex(py).borrow();
79 let ridx = &*self.index(py).borrow();
79 let ridx = &*self.index(py).borrow();
80 let node = node_from_py_bytes(py, &node)?;
80 let node = node_from_py_bytes(py, &node)?;
81 let rust_rev =
81 let rust_rev =
82 nt.find_bin(ridx, node.into()).map_err(|e| nodemap_error(py, e))?;
82 nt.find_bin(ridx, node.into()).map_err(|e| nodemap_error(py, e))?;
83 let c_rev =
83 let c_rev =
84 nt.find_bin(idx, node.into()).map_err(|e| nodemap_error(py, e))?;
84 nt.find_bin(idx, node.into()).map_err(|e| nodemap_error(py, e))?;
85 assert_eq!(rust_rev, c_rev);
85 assert_eq!(rust_rev, c_rev);
86 Ok(rust_rev.map(Into::into))
86 Ok(rust_rev.map(Into::into))
87
87
88 }
88 }
89
89
90 /// same as `get_rev()` but raises a bare `error.RevlogError` if node
90 /// same as `get_rev()` but raises a bare `error.RevlogError` if node
91 /// is not found.
91 /// is not found.
92 ///
92 ///
93 /// No need to repeat `node` in the exception, `mercurial/revlog.py`
93 /// No need to repeat `node` in the exception, `mercurial/revlog.py`
94 /// will catch and rewrap with it
94 /// will catch and rewrap with it
95 def rev(&self, node: PyBytes) -> PyResult<PyRevision> {
95 def rev(&self, node: PyBytes) -> PyResult<PyRevision> {
96 self.get_rev(py, node)?.ok_or_else(|| revlog_error(py))
96 self.get_rev(py, node)?.ok_or_else(|| revlog_error(py))
97 }
97 }
98
98
99 /// return True if the node exist in the index
99 /// return True if the node exist in the index
100 def has_node(&self, node: PyBytes) -> PyResult<bool> {
100 def has_node(&self, node: PyBytes) -> PyResult<bool> {
101 // TODO OPTIM we could avoid a needless conversion here,
101 // TODO OPTIM we could avoid a needless conversion here,
102 // to do when scaffolding for pure Rust switch is removed,
102 // to do when scaffolding for pure Rust switch is removed,
103 // as `get_rev()` currently does the necessary assertions
103 // as `get_rev()` currently does the necessary assertions
104 self.get_rev(py, node).map(|opt| opt.is_some())
104 self.get_rev(py, node).map(|opt| opt.is_some())
105 }
105 }
106
106
107 /// find length of shortest hex nodeid of a binary ID
107 /// find length of shortest hex nodeid of a binary ID
108 def shortest(&self, node: PyBytes) -> PyResult<usize> {
108 def shortest(&self, node: PyBytes) -> PyResult<usize> {
109 let opt = self.get_nodetree(py)?.borrow();
109 let opt = self.get_nodetree(py)?.borrow();
110 let nt = opt.as_ref().unwrap();
110 let nt = opt.as_ref().unwrap();
111 let idx = &*self.index(py).borrow();
111 let idx = &*self.index(py).borrow();
112 match nt.unique_prefix_len_node(idx, &node_from_py_bytes(py, &node)?)
112 match nt.unique_prefix_len_node(idx, &node_from_py_bytes(py, &node)?)
113 {
113 {
114 Ok(Some(l)) => Ok(l),
114 Ok(Some(l)) => Ok(l),
115 Ok(None) => Err(revlog_error(py)),
115 Ok(None) => Err(revlog_error(py)),
116 Err(e) => Err(nodemap_error(py, e)),
116 Err(e) => Err(nodemap_error(py, e)),
117 }
117 }
118 }
118 }
119
119
120 def partialmatch(&self, node: PyObject) -> PyResult<Option<PyBytes>> {
120 def partialmatch(&self, node: PyObject) -> PyResult<Option<PyBytes>> {
121 let opt = self.get_nodetree(py)?.borrow();
121 let opt = self.get_nodetree(py)?.borrow();
122 let nt = opt.as_ref().unwrap();
122 let nt = opt.as_ref().unwrap();
123 let idx = &*self.index(py).borrow();
123 let idx = &*self.index(py).borrow();
124
124
125 let node_as_string = if cfg!(feature = "python3-sys") {
125 let node_as_string = if cfg!(feature = "python3-sys") {
126 node.cast_as::<PyString>(py)?.to_string(py)?.to_string()
126 node.cast_as::<PyString>(py)?.to_string(py)?.to_string()
127 }
127 }
128 else {
128 else {
129 let node = node.extract::<PyBytes>(py)?;
129 let node = node.extract::<PyBytes>(py)?;
130 String::from_utf8_lossy(node.data(py)).to_string()
130 String::from_utf8_lossy(node.data(py)).to_string()
131 };
131 };
132
132
133 let prefix = NodePrefix::from_hex(&node_as_string)
133 let prefix = NodePrefix::from_hex(&node_as_string)
134 .map_err(|_| PyErr::new::<ValueError, _>(
134 .map_err(|_| PyErr::new::<ValueError, _>(
135 py, format!("Invalid node or prefix '{}'", node_as_string))
135 py, format!("Invalid node or prefix '{}'", node_as_string))
136 )?;
136 )?;
137
137
138 nt.find_bin(idx, prefix)
138 nt.find_bin(idx, prefix)
139 // TODO make an inner API returning the node directly
139 // TODO make an inner API returning the node directly
140 .map(|opt| opt.map(
140 .map(|opt| opt.map(
141 |rev| PyBytes::new(py, idx.node(rev).unwrap().as_bytes())))
141 |rev| PyBytes::new(py, idx.node(rev).unwrap().as_bytes())))
142 .map_err(|e| nodemap_error(py, e))
142 .map_err(|e| nodemap_error(py, e))
143
143
144 }
144 }
145
145
146 /// append an index entry
146 /// append an index entry
147 def append(&self, tup: PyTuple) -> PyResult<PyObject> {
147 def append(&self, tup: PyTuple) -> PyResult<PyObject> {
148 if tup.len(py) < 8 {
148 if tup.len(py) < 8 {
149 // this is better than the panic promised by tup.get_item()
149 // this is better than the panic promised by tup.get_item()
150 return Err(
150 return Err(
151 PyErr::new::<IndexError, _>(py, "tuple index out of range"))
151 PyErr::new::<IndexError, _>(py, "tuple index out of range"))
152 }
152 }
153 let node_bytes = tup.get_item(py, 7).extract(py)?;
153 let node_bytes = tup.get_item(py, 7).extract(py)?;
154 let node = node_from_py_object(py, &node_bytes)?;
154 let node = node_from_py_object(py, &node_bytes)?;
155
155
156 let rev = self.len(py)? as BaseRevision;
156 let rev = self.len(py)? as BaseRevision;
157 let mut idx = self.cindex(py).borrow_mut();
157 let mut idx = self.cindex(py).borrow_mut();
158
158
159 // This is ok since we will just add the revision to the index
159 // This is ok since we will just add the revision to the index
160 let rev = Revision(rev);
160 let rev = Revision(rev);
161 idx.append(py, tup.clone_ref(py))?;
161 idx.append(py, tup.clone_ref(py))?;
162 self.index(py)
162 self.index(py)
163 .borrow_mut()
163 .borrow_mut()
164 .append(py_tuple_to_revision_data_params(py, tup)?)
164 .append(py_tuple_to_revision_data_params(py, tup)?)
165 .unwrap();
165 .unwrap();
166 self.get_nodetree(py)?.borrow_mut().as_mut().unwrap()
166 self.get_nodetree(py)?.borrow_mut().as_mut().unwrap()
167 .insert(&*idx, &node, rev)
167 .insert(&*idx, &node, rev)
168 .map_err(|e| nodemap_error(py, e))?;
168 .map_err(|e| nodemap_error(py, e))?;
169 Ok(py.None())
169 Ok(py.None())
170 }
170 }
171
171
172 def __delitem__(&self, key: PyObject) -> PyResult<()> {
172 def __delitem__(&self, key: PyObject) -> PyResult<()> {
173 // __delitem__ is both for `del idx[r]` and `del idx[r1:r2]`
173 // __delitem__ is both for `del idx[r]` and `del idx[r1:r2]`
174 self.cindex(py).borrow().inner().del_item(py, &key)?;
174 self.cindex(py).borrow().inner().del_item(py, &key)?;
175 let start = key.getattr(py, "start")?;
175 let start = key.getattr(py, "start")?;
176 let start = UncheckedRevision(start.extract(py)?);
176 let start = UncheckedRevision(start.extract(py)?);
177 let start = self.index(py)
177 let start = self.index(py)
178 .borrow()
178 .borrow()
179 .check_revision(start)
179 .check_revision(start)
180 .ok_or_else(|| {
180 .ok_or_else(|| {
181 nodemap_error(py, NodeMapError::RevisionNotInIndex(start))
181 nodemap_error(py, NodeMapError::RevisionNotInIndex(start))
182 })?;
182 })?;
183 self.index(py).borrow_mut().remove(start).unwrap();
183 self.index(py).borrow_mut().remove(start).unwrap();
184 let mut opt = self.get_nodetree(py)?.borrow_mut();
184 let mut opt = self.get_nodetree(py)?.borrow_mut();
185 let nt = opt.as_mut().unwrap();
185 let nt = opt.as_mut().unwrap();
186 nt.invalidate_all();
186 nt.invalidate_all();
187 self.fill_nodemap(py, nt)?;
187 self.fill_nodemap(py, nt)?;
188 Ok(())
188 Ok(())
189 }
189 }
190
190
191 //
191 //
192 // Reforwarded C index API
192 // Reforwarded C index API
193 //
193 //
194
194
195 // index_methods (tp_methods). Same ordering as in revlog.c
195 // index_methods (tp_methods). Same ordering as in revlog.c
196
196
197 /// return the gca set of the given revs
197 /// return the gca set of the given revs
198 def ancestors(&self, *args, **kw) -> PyResult<PyObject> {
198 def ancestors(&self, *args, **kw) -> PyResult<PyObject> {
199 self.call_cindex(py, "ancestors", args, kw)
199 self.call_cindex(py, "ancestors", args, kw)
200 }
200 }
201
201
202 /// return the heads of the common ancestors of the given revs
202 /// return the heads of the common ancestors of the given revs
203 def commonancestorsheads(&self, *args, **kw) -> PyResult<PyObject> {
203 def commonancestorsheads(&self, *args, **kw) -> PyResult<PyObject> {
204 self.call_cindex(py, "commonancestorsheads", args, kw)
204 self.call_cindex(py, "commonancestorsheads", args, kw)
205 }
205 }
206
206
207 /// Clear the index caches and inner py_class data.
207 /// Clear the index caches and inner py_class data.
208 /// It is Python's responsibility to call `update_nodemap_data` again.
208 /// It is Python's responsibility to call `update_nodemap_data` again.
209 def clearcaches(&self, *args, **kw) -> PyResult<PyObject> {
209 def clearcaches(&self, *args, **kw) -> PyResult<PyObject> {
210 self.nt(py).borrow_mut().take();
210 self.nt(py).borrow_mut().take();
211 self.docket(py).borrow_mut().take();
211 self.docket(py).borrow_mut().take();
212 self.nodemap_mmap(py).borrow_mut().take();
212 self.nodemap_mmap(py).borrow_mut().take();
213 self.index(py).borrow_mut().clear_caches();
213 self.index(py).borrow_mut().clear_caches();
214 self.call_cindex(py, "clearcaches", args, kw)
214 self.call_cindex(py, "clearcaches", args, kw)
215 }
215 }
216
216
217 /// return the raw binary string representing a revision
217 /// return the raw binary string representing a revision
218 def entry_binary(&self, *args, **kw) -> PyResult<PyObject> {
218 def entry_binary(&self, *args, **kw) -> PyResult<PyObject> {
219 let rindex = self.index(py).borrow();
219 let rindex = self.index(py).borrow();
220 let rev = UncheckedRevision(args.get_item(py, 0).extract(py)?);
220 let rev = UncheckedRevision(args.get_item(py, 0).extract(py)?);
221 let rust_bytes = rindex.check_revision(rev).and_then(
221 let rust_bytes = rindex.check_revision(rev).and_then(
222 |r| rindex.entry_binary(r))
222 |r| rindex.entry_binary(r))
223 .ok_or_else(|| rev_not_in_index(py, rev))?;
223 .ok_or_else(|| rev_not_in_index(py, rev))?;
224 let rust_res = PyBytes::new(py, rust_bytes).into_object();
224 let rust_res = PyBytes::new(py, rust_bytes).into_object();
225
225
226 let c_res = self.call_cindex(py, "entry_binary", args, kw)?;
226 let c_res = self.call_cindex(py, "entry_binary", args, kw)?;
227 assert_py_eq(py, "entry_binary", &rust_res, &c_res)?;
227 assert_py_eq(py, "entry_binary", &rust_res, &c_res)?;
228 Ok(rust_res)
228 Ok(rust_res)
229 }
229 }
230
230
231 /// return a binary packed version of the header
231 /// return a binary packed version of the header
232 def pack_header(&self, *args, **kw) -> PyResult<PyObject> {
232 def pack_header(&self, *args, **kw) -> PyResult<PyObject> {
233 let rindex = self.index(py).borrow();
233 let rindex = self.index(py).borrow();
234 let packed = rindex.pack_header(args.get_item(py, 0).extract(py)?);
234 let packed = rindex.pack_header(args.get_item(py, 0).extract(py)?);
235 let rust_res = PyBytes::new(py, &packed).into_object();
235 let rust_res = PyBytes::new(py, &packed).into_object();
236
236
237 let c_res = self.call_cindex(py, "pack_header", args, kw)?;
237 let c_res = self.call_cindex(py, "pack_header", args, kw)?;
238 assert_py_eq(py, "pack_header", &rust_res, &c_res)?;
238 assert_py_eq(py, "pack_header", &rust_res, &c_res)?;
239 Ok(rust_res)
239 Ok(rust_res)
240 }
240 }
241
241
242 /// compute phases
242 /// compute phases
243 def computephasesmapsets(&self, *args, **kw) -> PyResult<PyObject> {
243 def computephasesmapsets(&self, *args, **kw) -> PyResult<PyObject> {
244 self.call_cindex(py, "computephasesmapsets", args, kw)
244 self.call_cindex(py, "computephasesmapsets", args, kw)
245 }
245 }
246
246
247 /// reachableroots
247 /// reachableroots
248 def reachableroots2(&self, *args, **kw) -> PyResult<PyObject> {
248 def reachableroots2(&self, *args, **kw) -> PyResult<PyObject> {
249 self.call_cindex(py, "reachableroots2", args, kw)
249 self.call_cindex(py, "reachableroots2", args, kw)
250 }
250 }
251
251
252 /// get head revisions
252 /// get head revisions
253 def headrevs(&self, *args, **kw) -> PyResult<PyObject> {
253 def headrevs(&self, *args, **kw) -> PyResult<PyObject> {
254 let rust_res = self.inner_headrevs(py)?;
254 let rust_res = self.inner_headrevs(py)?;
255
255
256 let c_res = self.call_cindex(py, "headrevs", args, kw)?;
256 let c_res = self.call_cindex(py, "headrevs", args, kw)?;
257 assert_py_eq(py, "headrevs", &rust_res, &c_res)?;
257 assert_py_eq(py, "headrevs", &rust_res, &c_res)?;
258 Ok(rust_res)
258 Ok(rust_res)
259 }
259 }
260
260
261 /// get filtered head revisions
261 /// get filtered head revisions
262 def headrevsfiltered(&self, *args, **kw) -> PyResult<PyObject> {
262 def headrevsfiltered(&self, *args, **kw) -> PyResult<PyObject> {
263 let rust_res = self.inner_headrevsfiltered(py, &args.get_item(py, 0))?;
263 let rust_res = self.inner_headrevsfiltered(py, &args.get_item(py, 0))?;
264 let c_res = self.call_cindex(py, "headrevsfiltered", args, kw)?;
264 let c_res = self.call_cindex(py, "headrevsfiltered", args, kw)?;
265
265
266 assert_py_eq(py, "headrevsfiltered", &rust_res, &c_res)?;
266 assert_py_eq(py, "headrevsfiltered", &rust_res, &c_res)?;
267 Ok(rust_res)
267 Ok(rust_res)
268 }
268 }
269
269
270 /// True if the object is a snapshot
270 /// True if the object is a snapshot
271 def issnapshot(&self, *args, **kw) -> PyResult<bool> {
271 def issnapshot(&self, *args, **kw) -> PyResult<bool> {
272 let index = self.index(py).borrow();
272 let index = self.index(py).borrow();
273 let result = index
273 let result = index
274 .is_snapshot(UncheckedRevision(args.get_item(py, 0).extract(py)?))
274 .is_snapshot(UncheckedRevision(args.get_item(py, 0).extract(py)?))
275 .map_err(|e| {
275 .map_err(|e| {
276 PyErr::new::<cpython::exc::ValueError, _>(py, e.to_string())
276 PyErr::new::<cpython::exc::ValueError, _>(py, e.to_string())
277 })?;
277 })?;
278 let cresult = self.call_cindex(py, "issnapshot", args, kw)?;
278 let cresult = self.call_cindex(py, "issnapshot", args, kw)?;
279 assert_eq!(result, cresult.extract(py)?);
279 assert_eq!(result, cresult.extract(py)?);
280 Ok(result)
280 Ok(result)
281 }
281 }
282
282
283 /// Gather snapshot data in a cache dict
283 /// Gather snapshot data in a cache dict
284 def findsnapshots(&self, *args, **kw) -> PyResult<PyObject> {
284 def findsnapshots(&self, *args, **kw) -> PyResult<PyObject> {
285 let index = self.index(py).borrow();
285 let index = self.index(py).borrow();
286 let cache: PyDict = args.get_item(py, 0).extract(py)?;
286 let cache: PyDict = args.get_item(py, 0).extract(py)?;
287 // this methods operates by setting new values in the cache,
287 // this methods operates by setting new values in the cache,
288 // hence we will compare results by letting the C implementation
288 // hence we will compare results by letting the C implementation
289 // operate over a deepcopy of the cache, and finally compare both
289 // operate over a deepcopy of the cache, and finally compare both
290 // caches.
290 // caches.
291 let c_cache = PyDict::new(py);
291 let c_cache = PyDict::new(py);
292 for (k, v) in cache.items(py) {
292 for (k, v) in cache.items(py) {
293 c_cache.set_item(py, k, PySet::new(py, v)?)?;
293 c_cache.set_item(py, k, PySet::new(py, v)?)?;
294 }
294 }
295
295
296 let start_rev = UncheckedRevision(args.get_item(py, 1).extract(py)?);
296 let start_rev = UncheckedRevision(args.get_item(py, 1).extract(py)?);
297 let end_rev = UncheckedRevision(args.get_item(py, 2).extract(py)?);
297 let end_rev = UncheckedRevision(args.get_item(py, 2).extract(py)?);
298 let mut cache_wrapper = PySnapshotsCache{ py, dict: cache };
298 let mut cache_wrapper = PySnapshotsCache{ py, dict: cache };
299 index.find_snapshots(
299 index.find_snapshots(
300 start_rev,
300 start_rev,
301 end_rev,
301 end_rev,
302 &mut cache_wrapper,
302 &mut cache_wrapper,
303 ).map_err(|_| revlog_error(py))?;
303 ).map_err(|_| revlog_error(py))?;
304
304
305 let c_args = PyTuple::new(
305 let c_args = PyTuple::new(
306 py,
306 py,
307 &[
307 &[
308 c_cache.clone_ref(py).into_object(),
308 c_cache.clone_ref(py).into_object(),
309 args.get_item(py, 1),
309 args.get_item(py, 1),
310 args.get_item(py, 2)
310 args.get_item(py, 2)
311 ]
311 ]
312 );
312 );
313 self.call_cindex(py, "findsnapshots", &c_args, kw)?;
313 self.call_cindex(py, "findsnapshots", &c_args, kw)?;
314 assert_py_eq(py, "findsnapshots cache",
314 assert_py_eq(py, "findsnapshots cache",
315 &cache_wrapper.into_object(),
315 &cache_wrapper.into_object(),
316 &c_cache.into_object())?;
316 &c_cache.into_object())?;
317 Ok(py.None())
317 Ok(py.None())
318 }
318 }
319
319
320 /// determine revisions with deltas to reconstruct fulltext
320 /// determine revisions with deltas to reconstruct fulltext
321 def deltachain(&self, *args, **kw) -> PyResult<PyObject> {
321 def deltachain(&self, *args, **kw) -> PyResult<PyObject> {
322 let index = self.index(py).borrow();
322 let index = self.index(py).borrow();
323 let rev = args.get_item(py, 0).extract::<BaseRevision>(py)?.into();
323 let rev = args.get_item(py, 0).extract::<BaseRevision>(py)?.into();
324 let stop_rev =
324 let stop_rev =
325 args.get_item(py, 1).extract::<Option<BaseRevision>>(py)?;
325 args.get_item(py, 1).extract::<Option<BaseRevision>>(py)?;
326 let rev = index.check_revision(rev).ok_or_else(|| {
326 let rev = index.check_revision(rev).ok_or_else(|| {
327 nodemap_error(py, NodeMapError::RevisionNotInIndex(rev))
327 nodemap_error(py, NodeMapError::RevisionNotInIndex(rev))
328 })?;
328 })?;
329 let stop_rev = if let Some(stop_rev) = stop_rev {
329 let stop_rev = if let Some(stop_rev) = stop_rev {
330 let stop_rev = UncheckedRevision(stop_rev);
330 let stop_rev = UncheckedRevision(stop_rev);
331 Some(index.check_revision(stop_rev).ok_or_else(|| {
331 Some(index.check_revision(stop_rev).ok_or_else(|| {
332 nodemap_error(py, NodeMapError::RevisionNotInIndex(stop_rev))
332 nodemap_error(py, NodeMapError::RevisionNotInIndex(stop_rev))
333 })?)
333 })?)
334 } else {None};
334 } else {None};
335 let (chain, stopped) = index.delta_chain(rev, stop_rev).map_err(|e| {
335 let (chain, stopped) = index.delta_chain(rev, stop_rev).map_err(|e| {
336 PyErr::new::<cpython::exc::ValueError, _>(py, e.to_string())
336 PyErr::new::<cpython::exc::ValueError, _>(py, e.to_string())
337 })?;
337 })?;
338
338
339 let cresult = self.call_cindex(py, "deltachain", args, kw)?;
339 let cresult = self.call_cindex(py, "deltachain", args, kw)?;
340 let cchain: Vec<BaseRevision> =
340 let cchain: Vec<BaseRevision> =
341 cresult.get_item(py, 0)?.extract::<Vec<BaseRevision>>(py)?;
341 cresult.get_item(py, 0)?.extract::<Vec<BaseRevision>>(py)?;
342 let chain: Vec<_> = chain.into_iter().map(|r| r.0).collect();
342 let chain: Vec<_> = chain.into_iter().map(|r| r.0).collect();
343 assert_eq!(chain, cchain);
343 assert_eq!(chain, cchain);
344 assert_eq!(stopped, cresult.get_item(py, 1)?.extract(py)?);
344 assert_eq!(stopped, cresult.get_item(py, 1)?.extract(py)?);
345
345
346 Ok(
346 Ok(
347 PyTuple::new(
347 PyTuple::new(
348 py,
348 py,
349 &[
349 &[
350 chain.into_py_object(py).into_object(),
350 chain.into_py_object(py).into_object(),
351 stopped.into_py_object(py).into_object()
351 stopped.into_py_object(py).into_object()
352 ]
352 ]
353 ).into_object()
353 ).into_object()
354 )
354 )
355
355
356 }
356 }
357
357
358 /// slice planned chunk read to reach a density threshold
358 /// slice planned chunk read to reach a density threshold
359 def slicechunktodensity(&self, *args, **kw) -> PyResult<PyObject> {
359 def slicechunktodensity(&self, *args, **kw) -> PyResult<PyObject> {
360 self.call_cindex(py, "slicechunktodensity", args, kw)
360 let rust_res = self.inner_slicechunktodensity(
361 py,
362 args.get_item(py, 0),
363 args.get_item(py, 1).extract(py)?,
364 args.get_item(py, 2).extract(py)?
365 )?;
366
367 let c_res = self.call_cindex(py, "slicechunktodensity", args, kw)?;
368 assert_eq!(
369 rust_res.len(),
370 c_res.len(py)?,
371 "chunks differ {:?} {}",
372 rust_res, c_res
373 );
374 for (i, chunk) in rust_res.iter().enumerate() {
375 let c_chunk = c_res.get_item(py, i)?;
376 assert_eq!(
377 chunk.len(),
378 c_chunk.len(py)?,
379 "chunk {} length differ {:?} {}",
380 i,
381 chunk,
382 c_res
383 );
384 for (j, rev) in chunk.iter().enumerate() {
385 let c_chunk: BaseRevision
386 = c_chunk.get_item(py, j)?.extract(py)?;
387 assert_eq!(c_chunk, rev.0);
388 }
389 }
390 Ok(c_res)
361 }
391 }
362
392
363 /// stats for the index
393 /// stats for the index
364 def stats(&self, *args, **kw) -> PyResult<PyObject> {
394 def stats(&self, *args, **kw) -> PyResult<PyObject> {
365 self.call_cindex(py, "stats", args, kw)
395 self.call_cindex(py, "stats", args, kw)
366 }
396 }
367
397
368 // index_sequence_methods and index_mapping_methods.
398 // index_sequence_methods and index_mapping_methods.
369 //
399 //
370 // Since we call back through the high level Python API,
400 // Since we call back through the high level Python API,
371 // there's no point making a distinction between index_get
401 // there's no point making a distinction between index_get
372 // and index_getitem.
402 // and index_getitem.
373 // gracinet 2023: this above is no longer true for the pure Rust impl
403 // gracinet 2023: this above is no longer true for the pure Rust impl
374
404
375 def __len__(&self) -> PyResult<usize> {
405 def __len__(&self) -> PyResult<usize> {
376 self.len(py)
406 self.len(py)
377 }
407 }
378
408
379 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
409 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
380 let rust_res = self.inner_getitem(py, key.clone_ref(py))?;
410 let rust_res = self.inner_getitem(py, key.clone_ref(py))?;
381
411
382 // this conversion seems needless, but that's actually because
412 // this conversion seems needless, but that's actually because
383 // `index_getitem` does not handle conversion from PyLong,
413 // `index_getitem` does not handle conversion from PyLong,
384 // which expressions such as [e for e in index] internally use.
414 // which expressions such as [e for e in index] internally use.
385 // Note that we don't seem to have a direct way to call
415 // Note that we don't seem to have a direct way to call
386 // PySequence_GetItem (does the job), which would possibly be better
416 // PySequence_GetItem (does the job), which would possibly be better
387 // for performance
417 // for performance
388 // gracinet 2023: the above comment can be removed when we use
418 // gracinet 2023: the above comment can be removed when we use
389 // the pure Rust impl only. Note also that `key` can be a binary
419 // the pure Rust impl only. Note also that `key` can be a binary
390 // node id.
420 // node id.
391 let c_key = match key.extract::<BaseRevision>(py) {
421 let c_key = match key.extract::<BaseRevision>(py) {
392 Ok(rev) => rev.to_py_object(py).into_object(),
422 Ok(rev) => rev.to_py_object(py).into_object(),
393 Err(_) => key,
423 Err(_) => key,
394 };
424 };
395 let c_res = self.cindex(py).borrow().inner().get_item(py, c_key)?;
425 let c_res = self.cindex(py).borrow().inner().get_item(py, c_key)?;
396
426
397 assert_py_eq(py, "__getitem__", &rust_res, &c_res)?;
427 assert_py_eq(py, "__getitem__", &rust_res, &c_res)?;
398 Ok(rust_res)
428 Ok(rust_res)
399 }
429 }
400
430
401 def __contains__(&self, item: PyObject) -> PyResult<bool> {
431 def __contains__(&self, item: PyObject) -> PyResult<bool> {
402 // ObjectProtocol does not seem to provide contains(), so
432 // ObjectProtocol does not seem to provide contains(), so
403 // this is an equivalent implementation of the index_contains()
433 // this is an equivalent implementation of the index_contains()
404 // defined in revlog.c
434 // defined in revlog.c
405 let cindex = self.cindex(py).borrow();
435 let cindex = self.cindex(py).borrow();
406 match item.extract::<i32>(py) {
436 match item.extract::<i32>(py) {
407 Ok(rev) => {
437 Ok(rev) => {
408 Ok(rev >= -1 && rev < self.len(py)? as BaseRevision)
438 Ok(rev >= -1 && rev < self.len(py)? as BaseRevision)
409 }
439 }
410 Err(_) => {
440 Err(_) => {
411 let item_bytes: PyBytes = item.extract(py)?;
441 let item_bytes: PyBytes = item.extract(py)?;
412 let rust_res = self.has_node(py, item_bytes)?;
442 let rust_res = self.has_node(py, item_bytes)?;
413
443
414 let c_res = cindex.inner().call_method(
444 let c_res = cindex.inner().call_method(
415 py,
445 py,
416 "has_node",
446 "has_node",
417 PyTuple::new(py, &[item.clone_ref(py)]),
447 PyTuple::new(py, &[item.clone_ref(py)]),
418 None)?
448 None)?
419 .extract(py)?;
449 .extract(py)?;
420
450
421 assert_eq!(rust_res, c_res);
451 assert_eq!(rust_res, c_res);
422 Ok(rust_res)
452 Ok(rust_res)
423 }
453 }
424 }
454 }
425 }
455 }
426
456
427 def nodemap_data_all(&self) -> PyResult<PyBytes> {
457 def nodemap_data_all(&self) -> PyResult<PyBytes> {
428 self.inner_nodemap_data_all(py)
458 self.inner_nodemap_data_all(py)
429 }
459 }
430
460
431 def nodemap_data_incremental(&self) -> PyResult<PyObject> {
461 def nodemap_data_incremental(&self) -> PyResult<PyObject> {
432 self.inner_nodemap_data_incremental(py)
462 self.inner_nodemap_data_incremental(py)
433 }
463 }
434 def update_nodemap_data(
464 def update_nodemap_data(
435 &self,
465 &self,
436 docket: PyObject,
466 docket: PyObject,
437 nm_data: PyObject
467 nm_data: PyObject
438 ) -> PyResult<PyObject> {
468 ) -> PyResult<PyObject> {
439 self.inner_update_nodemap_data(py, docket, nm_data)
469 self.inner_update_nodemap_data(py, docket, nm_data)
440 }
470 }
441
471
442 @property
472 @property
443 def entry_size(&self) -> PyResult<PyInt> {
473 def entry_size(&self) -> PyResult<PyInt> {
444 self.cindex(py).borrow().inner().getattr(py, "entry_size")?.extract::<PyInt>(py)
474 self.cindex(py).borrow().inner().getattr(py, "entry_size")?.extract::<PyInt>(py)
445 }
475 }
446
476
447 @property
477 @property
448 def rust_ext_compat(&self) -> PyResult<PyInt> {
478 def rust_ext_compat(&self) -> PyResult<PyInt> {
449 self.cindex(py).borrow().inner().getattr(py, "rust_ext_compat")?.extract::<PyInt>(py)
479 self.cindex(py).borrow().inner().getattr(py, "rust_ext_compat")?.extract::<PyInt>(py)
450 }
480 }
451
481
452 });
482 });
453
483
454 /// Take a (potentially) mmap'ed buffer, and return the underlying Python
484 /// Take a (potentially) mmap'ed buffer, and return the underlying Python
455 /// buffer along with the Rust slice into said buffer. We need to keep the
485 /// buffer along with the Rust slice into said buffer. We need to keep the
456 /// Python buffer around, otherwise we'd get a dangling pointer once the buffer
486 /// Python buffer around, otherwise we'd get a dangling pointer once the buffer
457 /// is freed from Python's side.
487 /// is freed from Python's side.
458 ///
488 ///
459 /// # Safety
489 /// # Safety
460 ///
490 ///
461 /// The caller must make sure that the buffer is kept around for at least as
491 /// The caller must make sure that the buffer is kept around for at least as
462 /// long as the slice.
492 /// long as the slice.
463 #[deny(unsafe_op_in_unsafe_fn)]
493 #[deny(unsafe_op_in_unsafe_fn)]
464 unsafe fn mmap_keeparound(
494 unsafe fn mmap_keeparound(
465 py: Python,
495 py: Python,
466 data: PyObject,
496 data: PyObject,
467 ) -> PyResult<(
497 ) -> PyResult<(
468 PyBuffer,
498 PyBuffer,
469 Box<dyn std::ops::Deref<Target = [u8]> + Send + 'static>,
499 Box<dyn std::ops::Deref<Target = [u8]> + Send + 'static>,
470 )> {
500 )> {
471 let buf = PyBuffer::get(py, &data)?;
501 let buf = PyBuffer::get(py, &data)?;
472 let len = buf.item_count();
502 let len = buf.item_count();
473
503
474 // Build a slice from the mmap'ed buffer data
504 // Build a slice from the mmap'ed buffer data
475 let cbuf = buf.buf_ptr();
505 let cbuf = buf.buf_ptr();
476 let bytes = if std::mem::size_of::<u8>() == buf.item_size()
506 let bytes = if std::mem::size_of::<u8>() == buf.item_size()
477 && buf.is_c_contiguous()
507 && buf.is_c_contiguous()
478 && u8::is_compatible_format(buf.format())
508 && u8::is_compatible_format(buf.format())
479 {
509 {
480 unsafe { std::slice::from_raw_parts(cbuf as *const u8, len) }
510 unsafe { std::slice::from_raw_parts(cbuf as *const u8, len) }
481 } else {
511 } else {
482 return Err(PyErr::new::<ValueError, _>(
512 return Err(PyErr::new::<ValueError, _>(
483 py,
513 py,
484 "Nodemap data buffer has an invalid memory representation"
514 "Nodemap data buffer has an invalid memory representation"
485 .to_string(),
515 .to_string(),
486 ));
516 ));
487 };
517 };
488
518
489 Ok((buf, Box::new(bytes)))
519 Ok((buf, Box::new(bytes)))
490 }
520 }
491
521
492 fn py_tuple_to_revision_data_params(
522 fn py_tuple_to_revision_data_params(
493 py: Python,
523 py: Python,
494 tuple: PyTuple,
524 tuple: PyTuple,
495 ) -> PyResult<RevisionDataParams> {
525 ) -> PyResult<RevisionDataParams> {
496 if tuple.len(py) < 8 {
526 if tuple.len(py) < 8 {
497 // this is better than the panic promised by tup.get_item()
527 // this is better than the panic promised by tup.get_item()
498 return Err(PyErr::new::<IndexError, _>(
528 return Err(PyErr::new::<IndexError, _>(
499 py,
529 py,
500 "tuple index out of range",
530 "tuple index out of range",
501 ));
531 ));
502 }
532 }
503 let offset_or_flags: u64 = tuple.get_item(py, 0).extract(py)?;
533 let offset_or_flags: u64 = tuple.get_item(py, 0).extract(py)?;
504 let node_id = tuple
534 let node_id = tuple
505 .get_item(py, 7)
535 .get_item(py, 7)
506 .extract::<PyBytes>(py)?
536 .extract::<PyBytes>(py)?
507 .data(py)
537 .data(py)
508 .try_into()
538 .try_into()
509 .unwrap();
539 .unwrap();
510 let flags = (offset_or_flags & 0xFFFF) as u16;
540 let flags = (offset_or_flags & 0xFFFF) as u16;
511 let data_offset = offset_or_flags >> 16;
541 let data_offset = offset_or_flags >> 16;
512 Ok(RevisionDataParams {
542 Ok(RevisionDataParams {
513 flags,
543 flags,
514 data_offset,
544 data_offset,
515 data_compressed_length: tuple.get_item(py, 1).extract(py)?,
545 data_compressed_length: tuple.get_item(py, 1).extract(py)?,
516 data_uncompressed_length: tuple.get_item(py, 2).extract(py)?,
546 data_uncompressed_length: tuple.get_item(py, 2).extract(py)?,
517 data_delta_base: tuple.get_item(py, 3).extract(py)?,
547 data_delta_base: tuple.get_item(py, 3).extract(py)?,
518 link_rev: tuple.get_item(py, 4).extract(py)?,
548 link_rev: tuple.get_item(py, 4).extract(py)?,
519 parent_rev_1: tuple.get_item(py, 5).extract(py)?,
549 parent_rev_1: tuple.get_item(py, 5).extract(py)?,
520 parent_rev_2: tuple.get_item(py, 6).extract(py)?,
550 parent_rev_2: tuple.get_item(py, 6).extract(py)?,
521 node_id,
551 node_id,
522 ..Default::default()
552 ..Default::default()
523 })
553 })
524 }
554 }
525 fn revision_data_params_to_py_tuple(
555 fn revision_data_params_to_py_tuple(
526 py: Python,
556 py: Python,
527 params: RevisionDataParams,
557 params: RevisionDataParams,
528 ) -> PyTuple {
558 ) -> PyTuple {
529 PyTuple::new(
559 PyTuple::new(
530 py,
560 py,
531 &[
561 &[
532 params.data_offset.into_py_object(py).into_object(),
562 params.data_offset.into_py_object(py).into_object(),
533 params
563 params
534 .data_compressed_length
564 .data_compressed_length
535 .into_py_object(py)
565 .into_py_object(py)
536 .into_object(),
566 .into_object(),
537 params
567 params
538 .data_uncompressed_length
568 .data_uncompressed_length
539 .into_py_object(py)
569 .into_py_object(py)
540 .into_object(),
570 .into_object(),
541 params.data_delta_base.into_py_object(py).into_object(),
571 params.data_delta_base.into_py_object(py).into_object(),
542 params.link_rev.into_py_object(py).into_object(),
572 params.link_rev.into_py_object(py).into_object(),
543 params.parent_rev_1.into_py_object(py).into_object(),
573 params.parent_rev_1.into_py_object(py).into_object(),
544 params.parent_rev_2.into_py_object(py).into_object(),
574 params.parent_rev_2.into_py_object(py).into_object(),
545 PyBytes::new(py, &params.node_id)
575 PyBytes::new(py, &params.node_id)
546 .into_py_object(py)
576 .into_py_object(py)
547 .into_object(),
577 .into_object(),
548 params._sidedata_offset.into_py_object(py).into_object(),
578 params._sidedata_offset.into_py_object(py).into_object(),
549 params
579 params
550 ._sidedata_compressed_length
580 ._sidedata_compressed_length
551 .into_py_object(py)
581 .into_py_object(py)
552 .into_object(),
582 .into_object(),
553 params
583 params
554 .data_compression_mode
584 .data_compression_mode
555 .into_py_object(py)
585 .into_py_object(py)
556 .into_object(),
586 .into_object(),
557 params
587 params
558 ._sidedata_compression_mode
588 ._sidedata_compression_mode
559 .into_py_object(py)
589 .into_py_object(py)
560 .into_object(),
590 .into_object(),
561 params._rank.into_py_object(py).into_object(),
591 params._rank.into_py_object(py).into_object(),
562 ],
592 ],
563 )
593 )
564 }
594 }
565
595
566 struct PySnapshotsCache<'p> {
596 struct PySnapshotsCache<'p> {
567 py: Python<'p>,
597 py: Python<'p>,
568 dict: PyDict,
598 dict: PyDict,
569 }
599 }
570
600
571 impl<'p> PySnapshotsCache<'p> {
601 impl<'p> PySnapshotsCache<'p> {
572 fn into_object(self) -> PyObject {
602 fn into_object(self) -> PyObject {
573 self.dict.into_object()
603 self.dict.into_object()
574 }
604 }
575 }
605 }
576
606
577 impl<'p> SnapshotsCache for PySnapshotsCache<'p> {
607 impl<'p> SnapshotsCache for PySnapshotsCache<'p> {
578 fn insert_for(
608 fn insert_for(
579 &mut self,
609 &mut self,
580 rev: BaseRevision,
610 rev: BaseRevision,
581 value: BaseRevision,
611 value: BaseRevision,
582 ) -> Result<(), RevlogError> {
612 ) -> Result<(), RevlogError> {
583 let pyvalue = value.into_py_object(self.py).into_object();
613 let pyvalue = value.into_py_object(self.py).into_object();
584 match self.dict.get_item(self.py, rev) {
614 match self.dict.get_item(self.py, rev) {
585 Some(obj) => obj
615 Some(obj) => obj
586 .extract::<PySet>(self.py)
616 .extract::<PySet>(self.py)
587 .and_then(|set| set.add(self.py, pyvalue)),
617 .and_then(|set| set.add(self.py, pyvalue)),
588 None => PySet::new(self.py, vec![pyvalue])
618 None => PySet::new(self.py, vec![pyvalue])
589 .and_then(|set| self.dict.set_item(self.py, rev, set)),
619 .and_then(|set| self.dict.set_item(self.py, rev, set)),
590 }
620 }
591 .map_err(|_| {
621 .map_err(|_| {
592 RevlogError::Other(HgError::unsupported(
622 RevlogError::Other(HgError::unsupported(
593 "Error in Python caches handling",
623 "Error in Python caches handling",
594 ))
624 ))
595 })
625 })
596 }
626 }
597 }
627 }
598
628
599 impl MixedIndex {
629 impl MixedIndex {
600 fn new(
630 fn new(
601 py: Python,
631 py: Python,
602 cindex: PyObject,
632 cindex: PyObject,
603 data: PyObject,
633 data: PyObject,
604 header: u32,
634 header: u32,
605 ) -> PyResult<MixedIndex> {
635 ) -> PyResult<MixedIndex> {
606 // Safety: we keep the buffer around inside the class as `index_mmap`
636 // Safety: we keep the buffer around inside the class as `index_mmap`
607 let (buf, bytes) = unsafe { mmap_keeparound(py, data)? };
637 let (buf, bytes) = unsafe { mmap_keeparound(py, data)? };
608
638
609 Self::create_instance(
639 Self::create_instance(
610 py,
640 py,
611 RefCell::new(cindex::Index::new(py, cindex)?),
641 RefCell::new(cindex::Index::new(py, cindex)?),
612 RefCell::new(
642 RefCell::new(
613 hg::index::Index::new(
643 hg::index::Index::new(
614 bytes,
644 bytes,
615 IndexHeader::parse(&header.to_be_bytes())
645 IndexHeader::parse(&header.to_be_bytes())
616 .expect("default header is broken")
646 .expect("default header is broken")
617 .unwrap(),
647 .unwrap(),
618 )
648 )
619 .unwrap(),
649 .unwrap(),
620 ),
650 ),
621 RefCell::new(None),
651 RefCell::new(None),
622 RefCell::new(None),
652 RefCell::new(None),
623 RefCell::new(None),
653 RefCell::new(None),
624 RefCell::new(Some(buf)),
654 RefCell::new(Some(buf)),
625 )
655 )
626 }
656 }
627
657
628 fn len(&self, py: Python) -> PyResult<usize> {
658 fn len(&self, py: Python) -> PyResult<usize> {
629 let rust_index_len = self.index(py).borrow().len();
659 let rust_index_len = self.index(py).borrow().len();
630 let cindex_len = self.cindex(py).borrow().inner().len(py)?;
660 let cindex_len = self.cindex(py).borrow().inner().len(py)?;
631 assert_eq!(rust_index_len, cindex_len);
661 assert_eq!(rust_index_len, cindex_len);
632 Ok(cindex_len)
662 Ok(cindex_len)
633 }
663 }
634
664
635 /// This is scaffolding at this point, but it could also become
665 /// This is scaffolding at this point, but it could also become
636 /// a way to start a persistent nodemap or perform a
666 /// a way to start a persistent nodemap or perform a
637 /// vacuum / repack operation
667 /// vacuum / repack operation
638 fn fill_nodemap(
668 fn fill_nodemap(
639 &self,
669 &self,
640 py: Python,
670 py: Python,
641 nt: &mut NodeTree,
671 nt: &mut NodeTree,
642 ) -> PyResult<PyObject> {
672 ) -> PyResult<PyObject> {
643 let index = self.index(py).borrow();
673 let index = self.index(py).borrow();
644 for r in 0..self.len(py)? {
674 for r in 0..self.len(py)? {
645 let rev = Revision(r as BaseRevision);
675 let rev = Revision(r as BaseRevision);
646 // in this case node() won't ever return None
676 // in this case node() won't ever return None
647 nt.insert(&*index, index.node(rev).unwrap(), rev)
677 nt.insert(&*index, index.node(rev).unwrap(), rev)
648 .map_err(|e| nodemap_error(py, e))?
678 .map_err(|e| nodemap_error(py, e))?
649 }
679 }
650 Ok(py.None())
680 Ok(py.None())
651 }
681 }
652
682
653 fn get_nodetree<'a>(
683 fn get_nodetree<'a>(
654 &'a self,
684 &'a self,
655 py: Python<'a>,
685 py: Python<'a>,
656 ) -> PyResult<&'a RefCell<Option<NodeTree>>> {
686 ) -> PyResult<&'a RefCell<Option<NodeTree>>> {
657 if self.nt(py).borrow().is_none() {
687 if self.nt(py).borrow().is_none() {
658 let readonly = Box::<Vec<_>>::default();
688 let readonly = Box::<Vec<_>>::default();
659 let mut nt = NodeTree::load_bytes(readonly, 0);
689 let mut nt = NodeTree::load_bytes(readonly, 0);
660 self.fill_nodemap(py, &mut nt)?;
690 self.fill_nodemap(py, &mut nt)?;
661 self.nt(py).borrow_mut().replace(nt);
691 self.nt(py).borrow_mut().replace(nt);
662 }
692 }
663 Ok(self.nt(py))
693 Ok(self.nt(py))
664 }
694 }
665
695
666 /// forward a method call to the underlying C index
696 /// forward a method call to the underlying C index
667 fn call_cindex(
697 fn call_cindex(
668 &self,
698 &self,
669 py: Python,
699 py: Python,
670 name: &str,
700 name: &str,
671 args: &PyTuple,
701 args: &PyTuple,
672 kwargs: Option<&PyDict>,
702 kwargs: Option<&PyDict>,
673 ) -> PyResult<PyObject> {
703 ) -> PyResult<PyObject> {
674 self.cindex(py)
704 self.cindex(py)
675 .borrow()
705 .borrow()
676 .inner()
706 .inner()
677 .call_method(py, name, args, kwargs)
707 .call_method(py, name, args, kwargs)
678 }
708 }
679
709
680 pub fn clone_cindex(&self, py: Python) -> cindex::Index {
710 pub fn clone_cindex(&self, py: Python) -> cindex::Index {
681 self.cindex(py).borrow().clone_ref(py)
711 self.cindex(py).borrow().clone_ref(py)
682 }
712 }
683
713
684 /// Returns the full nodemap bytes to be written as-is to disk
714 /// Returns the full nodemap bytes to be written as-is to disk
685 fn inner_nodemap_data_all(&self, py: Python) -> PyResult<PyBytes> {
715 fn inner_nodemap_data_all(&self, py: Python) -> PyResult<PyBytes> {
686 let nodemap = self.get_nodetree(py)?.borrow_mut().take().unwrap();
716 let nodemap = self.get_nodetree(py)?.borrow_mut().take().unwrap();
687 let (readonly, bytes) = nodemap.into_readonly_and_added_bytes();
717 let (readonly, bytes) = nodemap.into_readonly_and_added_bytes();
688
718
689 // If there's anything readonly, we need to build the data again from
719 // If there's anything readonly, we need to build the data again from
690 // scratch
720 // scratch
691 let bytes = if readonly.len() > 0 {
721 let bytes = if readonly.len() > 0 {
692 let mut nt = NodeTree::load_bytes(Box::<Vec<_>>::default(), 0);
722 let mut nt = NodeTree::load_bytes(Box::<Vec<_>>::default(), 0);
693 self.fill_nodemap(py, &mut nt)?;
723 self.fill_nodemap(py, &mut nt)?;
694
724
695 let (readonly, bytes) = nt.into_readonly_and_added_bytes();
725 let (readonly, bytes) = nt.into_readonly_and_added_bytes();
696 assert_eq!(readonly.len(), 0);
726 assert_eq!(readonly.len(), 0);
697
727
698 bytes
728 bytes
699 } else {
729 } else {
700 bytes
730 bytes
701 };
731 };
702
732
703 let bytes = PyBytes::new(py, &bytes);
733 let bytes = PyBytes::new(py, &bytes);
704 Ok(bytes)
734 Ok(bytes)
705 }
735 }
706
736
707 /// Returns the last saved docket along with the size of any changed data
737 /// Returns the last saved docket along with the size of any changed data
708 /// (in number of blocks), and said data as bytes.
738 /// (in number of blocks), and said data as bytes.
709 fn inner_nodemap_data_incremental(
739 fn inner_nodemap_data_incremental(
710 &self,
740 &self,
711 py: Python,
741 py: Python,
712 ) -> PyResult<PyObject> {
742 ) -> PyResult<PyObject> {
713 let docket = self.docket(py).borrow();
743 let docket = self.docket(py).borrow();
714 let docket = match docket.as_ref() {
744 let docket = match docket.as_ref() {
715 Some(d) => d,
745 Some(d) => d,
716 None => return Ok(py.None()),
746 None => return Ok(py.None()),
717 };
747 };
718
748
719 let node_tree = self.get_nodetree(py)?.borrow_mut().take().unwrap();
749 let node_tree = self.get_nodetree(py)?.borrow_mut().take().unwrap();
720 let masked_blocks = node_tree.masked_readonly_blocks();
750 let masked_blocks = node_tree.masked_readonly_blocks();
721 let (_, data) = node_tree.into_readonly_and_added_bytes();
751 let (_, data) = node_tree.into_readonly_and_added_bytes();
722 let changed = masked_blocks * std::mem::size_of::<Block>();
752 let changed = masked_blocks * std::mem::size_of::<Block>();
723
753
724 Ok((docket, changed, PyBytes::new(py, &data))
754 Ok((docket, changed, PyBytes::new(py, &data))
725 .to_py_object(py)
755 .to_py_object(py)
726 .into_object())
756 .into_object())
727 }
757 }
728
758
729 /// Update the nodemap from the new (mmaped) data.
759 /// Update the nodemap from the new (mmaped) data.
730 /// The docket is kept as a reference for later incremental calls.
760 /// The docket is kept as a reference for later incremental calls.
731 fn inner_update_nodemap_data(
761 fn inner_update_nodemap_data(
732 &self,
762 &self,
733 py: Python,
763 py: Python,
734 docket: PyObject,
764 docket: PyObject,
735 nm_data: PyObject,
765 nm_data: PyObject,
736 ) -> PyResult<PyObject> {
766 ) -> PyResult<PyObject> {
737 // Safety: we keep the buffer around inside the class as `nodemap_mmap`
767 // Safety: we keep the buffer around inside the class as `nodemap_mmap`
738 let (buf, bytes) = unsafe { mmap_keeparound(py, nm_data)? };
768 let (buf, bytes) = unsafe { mmap_keeparound(py, nm_data)? };
739 let len = buf.item_count();
769 let len = buf.item_count();
740 self.nodemap_mmap(py).borrow_mut().replace(buf);
770 self.nodemap_mmap(py).borrow_mut().replace(buf);
741
771
742 let mut nt = NodeTree::load_bytes(bytes, len);
772 let mut nt = NodeTree::load_bytes(bytes, len);
743
773
744 let data_tip = docket
774 let data_tip = docket
745 .getattr(py, "tip_rev")?
775 .getattr(py, "tip_rev")?
746 .extract::<BaseRevision>(py)?
776 .extract::<BaseRevision>(py)?
747 .into();
777 .into();
748 self.docket(py).borrow_mut().replace(docket.clone_ref(py));
778 self.docket(py).borrow_mut().replace(docket.clone_ref(py));
749 let idx = self.index(py).borrow();
779 let idx = self.index(py).borrow();
750 let data_tip = idx.check_revision(data_tip).ok_or_else(|| {
780 let data_tip = idx.check_revision(data_tip).ok_or_else(|| {
751 nodemap_error(py, NodeMapError::RevisionNotInIndex(data_tip))
781 nodemap_error(py, NodeMapError::RevisionNotInIndex(data_tip))
752 })?;
782 })?;
753 let current_tip = idx.len();
783 let current_tip = idx.len();
754
784
755 for r in (data_tip.0 + 1)..current_tip as BaseRevision {
785 for r in (data_tip.0 + 1)..current_tip as BaseRevision {
756 let rev = Revision(r);
786 let rev = Revision(r);
757 // in this case node() won't ever return None
787 // in this case node() won't ever return None
758 nt.insert(&*idx, idx.node(rev).unwrap(), rev)
788 nt.insert(&*idx, idx.node(rev).unwrap(), rev)
759 .map_err(|e| nodemap_error(py, e))?
789 .map_err(|e| nodemap_error(py, e))?
760 }
790 }
761
791
762 *self.nt(py).borrow_mut() = Some(nt);
792 *self.nt(py).borrow_mut() = Some(nt);
763
793
764 Ok(py.None())
794 Ok(py.None())
765 }
795 }
766
796
767 fn inner_getitem(&self, py: Python, key: PyObject) -> PyResult<PyObject> {
797 fn inner_getitem(&self, py: Python, key: PyObject) -> PyResult<PyObject> {
768 let idx = self.index(py).borrow();
798 let idx = self.index(py).borrow();
769 Ok(match key.extract::<BaseRevision>(py) {
799 Ok(match key.extract::<BaseRevision>(py) {
770 Ok(key_as_int) => {
800 Ok(key_as_int) => {
771 let entry_params = if key_as_int == NULL_REVISION.0 {
801 let entry_params = if key_as_int == NULL_REVISION.0 {
772 RevisionDataParams::default()
802 RevisionDataParams::default()
773 } else {
803 } else {
774 let rev = UncheckedRevision(key_as_int);
804 let rev = UncheckedRevision(key_as_int);
775 match idx.entry_as_params(rev) {
805 match idx.entry_as_params(rev) {
776 Some(e) => e,
806 Some(e) => e,
777 None => {
807 None => {
778 return Err(PyErr::new::<IndexError, _>(
808 return Err(PyErr::new::<IndexError, _>(
779 py,
809 py,
780 "revlog index out of range",
810 "revlog index out of range",
781 ));
811 ));
782 }
812 }
783 }
813 }
784 };
814 };
785 revision_data_params_to_py_tuple(py, entry_params)
815 revision_data_params_to_py_tuple(py, entry_params)
786 .into_object()
816 .into_object()
787 }
817 }
788 _ => self.get_rev(py, key.extract::<PyBytes>(py)?)?.map_or_else(
818 _ => self.get_rev(py, key.extract::<PyBytes>(py)?)?.map_or_else(
789 || py.None(),
819 || py.None(),
790 |py_rev| py_rev.into_py_object(py).into_object(),
820 |py_rev| py_rev.into_py_object(py).into_object(),
791 ),
821 ),
792 })
822 })
793 }
823 }
794
824
795 fn inner_headrevs(&self, py: Python) -> PyResult<PyObject> {
825 fn inner_headrevs(&self, py: Python) -> PyResult<PyObject> {
796 let index = &mut *self.index(py).borrow_mut();
826 let index = &mut *self.index(py).borrow_mut();
797 let as_vec: Vec<PyObject> = index
827 let as_vec: Vec<PyObject> = index
798 .head_revs()
828 .head_revs()
799 .map_err(|e| graph_error(py, e))?
829 .map_err(|e| graph_error(py, e))?
800 .iter()
830 .iter()
801 .map(|r| PyRevision::from(*r).into_py_object(py).into_object())
831 .map(|r| PyRevision::from(*r).into_py_object(py).into_object())
802 .collect();
832 .collect();
803 Ok(PyList::new(py, &as_vec).into_object())
833 Ok(PyList::new(py, &as_vec).into_object())
804 }
834 }
805
835
806 fn inner_headrevsfiltered(
836 fn inner_headrevsfiltered(
807 &self,
837 &self,
808 py: Python,
838 py: Python,
809 filtered_revs: &PyObject,
839 filtered_revs: &PyObject,
810 ) -> PyResult<PyObject> {
840 ) -> PyResult<PyObject> {
811 let index = &mut *self.index(py).borrow_mut();
841 let index = &mut *self.index(py).borrow_mut();
812 let filtered_revs = rev_pyiter_collect(py, filtered_revs, index)?;
842 let filtered_revs = rev_pyiter_collect(py, filtered_revs, index)?;
813
843
814 let as_vec: Vec<PyObject> = index
844 let as_vec: Vec<PyObject> = index
815 .head_revs_filtered(&filtered_revs)
845 .head_revs_filtered(&filtered_revs)
816 .map_err(|e| graph_error(py, e))?
846 .map_err(|e| graph_error(py, e))?
817 .iter()
847 .iter()
818 .map(|r| PyRevision::from(*r).into_py_object(py).into_object())
848 .map(|r| PyRevision::from(*r).into_py_object(py).into_object())
819 .collect();
849 .collect();
820 Ok(PyList::new(py, &as_vec).into_object())
850 Ok(PyList::new(py, &as_vec).into_object())
821 }
851 }
852
853 fn inner_slicechunktodensity(
854 &self,
855 py: Python,
856 revs: PyObject,
857 target_density: f64,
858 min_gap_size: usize,
859 ) -> PyResult<Vec<Vec<Revision>>> {
860 let index = &mut *self.index(py).borrow_mut();
861 let revs: Vec<_> = rev_pyiter_collect(py, &revs, index)?;
862 Ok(index.slice_chunk_to_density(&revs, target_density, min_gap_size))
863 }
822 }
864 }
823
865
824 fn revlog_error(py: Python) -> PyErr {
866 fn revlog_error(py: Python) -> PyErr {
825 match py
867 match py
826 .import("mercurial.error")
868 .import("mercurial.error")
827 .and_then(|m| m.get(py, "RevlogError"))
869 .and_then(|m| m.get(py, "RevlogError"))
828 {
870 {
829 Err(e) => e,
871 Err(e) => e,
830 Ok(cls) => PyErr::from_instance(
872 Ok(cls) => PyErr::from_instance(
831 py,
873 py,
832 cls.call(py, (py.None(),), None).ok().into_py_object(py),
874 cls.call(py, (py.None(),), None).ok().into_py_object(py),
833 ),
875 ),
834 }
876 }
835 }
877 }
836
878
837 fn graph_error(py: Python, _err: hg::GraphError) -> PyErr {
879 fn graph_error(py: Python, _err: hg::GraphError) -> PyErr {
838 // ParentOutOfRange is currently the only alternative
880 // ParentOutOfRange is currently the only alternative
839 // in `hg::GraphError`. The C index always raises this simple ValueError.
881 // in `hg::GraphError`. The C index always raises this simple ValueError.
840 PyErr::new::<ValueError, _>(py, "parent out of range")
882 PyErr::new::<ValueError, _>(py, "parent out of range")
841 }
883 }
842
884
843 fn nodemap_rev_not_in_index(py: Python, rev: UncheckedRevision) -> PyErr {
885 fn nodemap_rev_not_in_index(py: Python, rev: UncheckedRevision) -> PyErr {
844 PyErr::new::<ValueError, _>(
886 PyErr::new::<ValueError, _>(
845 py,
887 py,
846 format!(
888 format!(
847 "Inconsistency: Revision {} found in nodemap \
889 "Inconsistency: Revision {} found in nodemap \
848 is not in revlog index",
890 is not in revlog index",
849 rev
891 rev
850 ),
892 ),
851 )
893 )
852 }
894 }
853
895
854 fn rev_not_in_index(py: Python, rev: UncheckedRevision) -> PyErr {
896 fn rev_not_in_index(py: Python, rev: UncheckedRevision) -> PyErr {
855 PyErr::new::<ValueError, _>(
897 PyErr::new::<ValueError, _>(
856 py,
898 py,
857 format!("revlog index out of range: {}", rev),
899 format!("revlog index out of range: {}", rev),
858 )
900 )
859 }
901 }
860
902
861 /// Standard treatment of NodeMapError
903 /// Standard treatment of NodeMapError
862 fn nodemap_error(py: Python, err: NodeMapError) -> PyErr {
904 fn nodemap_error(py: Python, err: NodeMapError) -> PyErr {
863 match err {
905 match err {
864 NodeMapError::MultipleResults => revlog_error(py),
906 NodeMapError::MultipleResults => revlog_error(py),
865 NodeMapError::RevisionNotInIndex(r) => nodemap_rev_not_in_index(py, r),
907 NodeMapError::RevisionNotInIndex(r) => nodemap_rev_not_in_index(py, r),
866 }
908 }
867 }
909 }
868
910
869 /// assert two Python objects to be equal from a Python point of view
911 /// assert two Python objects to be equal from a Python point of view
870 ///
912 ///
871 /// `method` is a label for the assertion error message, intended to be the
913 /// `method` is a label for the assertion error message, intended to be the
872 /// name of the caller.
914 /// name of the caller.
873 /// `normalizer` is a function that takes a Python variable name and returns
915 /// `normalizer` is a function that takes a Python variable name and returns
874 /// an expression that the conparison will actually use.
916 /// an expression that the conparison will actually use.
875 /// Foe example: `|v| format!("sorted({})", v)`
917 /// Foe example: `|v| format!("sorted({})", v)`
876 fn assert_py_eq_normalized(
918 fn assert_py_eq_normalized(
877 py: Python,
919 py: Python,
878 method: &str,
920 method: &str,
879 rust: &PyObject,
921 rust: &PyObject,
880 c: &PyObject,
922 c: &PyObject,
881 normalizer: impl FnOnce(&str) -> String + Copy,
923 normalizer: impl FnOnce(&str) -> String + Copy,
882 ) -> PyResult<()> {
924 ) -> PyResult<()> {
883 let locals = PyDict::new(py);
925 let locals = PyDict::new(py);
884 locals.set_item(py, "rust".into_py_object(py).into_object(), rust)?;
926 locals.set_item(py, "rust".into_py_object(py).into_object(), rust)?;
885 locals.set_item(py, "c".into_py_object(py).into_object(), c)?;
927 locals.set_item(py, "c".into_py_object(py).into_object(), c)?;
886 // let lhs = format!(normalizer_fmt, "rust");
928 // let lhs = format!(normalizer_fmt, "rust");
887 // let rhs = format!(normalizer_fmt, "c");
929 // let rhs = format!(normalizer_fmt, "c");
888 let is_eq: PyBool = py
930 let is_eq: PyBool = py
889 .eval(
931 .eval(
890 &format!("{} == {}", &normalizer("rust"), &normalizer("c")),
932 &format!("{} == {}", &normalizer("rust"), &normalizer("c")),
891 None,
933 None,
892 Some(&locals),
934 Some(&locals),
893 )?
935 )?
894 .extract(py)?;
936 .extract(py)?;
895 assert!(
937 assert!(
896 is_eq.is_true(),
938 is_eq.is_true(),
897 "{} results differ. Rust: {:?} C: {:?} (before any normalization)",
939 "{} results differ. Rust: {:?} C: {:?} (before any normalization)",
898 method,
940 method,
899 rust,
941 rust,
900 c
942 c
901 );
943 );
902 Ok(())
944 Ok(())
903 }
945 }
904
946
905 fn assert_py_eq(
947 fn assert_py_eq(
906 py: Python,
948 py: Python,
907 method: &str,
949 method: &str,
908 rust: &PyObject,
950 rust: &PyObject,
909 c: &PyObject,
951 c: &PyObject,
910 ) -> PyResult<()> {
952 ) -> PyResult<()> {
911 assert_py_eq_normalized(py, method, rust, c, |v| v.to_owned())
953 assert_py_eq_normalized(py, method, rust, c, |v| v.to_owned())
912 }
954 }
913
955
914 /// Create the module, with __package__ given from parent
956 /// Create the module, with __package__ given from parent
915 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
957 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
916 let dotted_name = &format!("{}.revlog", package);
958 let dotted_name = &format!("{}.revlog", package);
917 let m = PyModule::new(py, dotted_name)?;
959 let m = PyModule::new(py, dotted_name)?;
918 m.add(py, "__package__", package)?;
960 m.add(py, "__package__", package)?;
919 m.add(py, "__doc__", "RevLog - Rust implementations")?;
961 m.add(py, "__doc__", "RevLog - Rust implementations")?;
920
962
921 m.add_class::<MixedIndex>(py)?;
963 m.add_class::<MixedIndex>(py)?;
922
964
923 let sys = PyModule::import(py, "sys")?;
965 let sys = PyModule::import(py, "sys")?;
924 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
966 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
925 sys_modules.set_item(py, dotted_name, &m)?;
967 sys_modules.set_item(py, dotted_name, &m)?;
926
968
927 Ok(m)
969 Ok(m)
928 }
970 }
General Comments 0
You need to be logged in to leave comments. Login now