##// END OF EJS Templates
rust-index: implementation of __getitem__...
Raphaël Gomès -
r52098:002b4990 default
parent child Browse files
Show More
@@ -1,886 +1,938 b''
1 use std::fmt::Debug;
1 use std::fmt::Debug;
2 use std::ops::Deref;
2 use std::ops::Deref;
3 use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
3 use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
4
4
5 use byteorder::{BigEndian, ByteOrder};
5 use byteorder::{BigEndian, ByteOrder};
6 use bytes_cast::{unaligned, BytesCast};
6 use bytes_cast::{unaligned, BytesCast};
7
7
8 use super::REVIDX_KNOWN_FLAGS;
8 use super::REVIDX_KNOWN_FLAGS;
9 use crate::errors::HgError;
9 use crate::errors::HgError;
10 use crate::node::{NODE_BYTES_LENGTH, STORED_NODE_ID_BYTES};
10 use crate::node::{NODE_BYTES_LENGTH, STORED_NODE_ID_BYTES};
11 use crate::revlog::node::Node;
11 use crate::revlog::node::Node;
12 use crate::revlog::{Revision, NULL_REVISION};
12 use crate::revlog::{Revision, NULL_REVISION};
13 use crate::{Graph, GraphError, RevlogError, RevlogIndex, UncheckedRevision};
13 use crate::{Graph, GraphError, RevlogError, RevlogIndex, UncheckedRevision};
14
14
15 pub const INDEX_ENTRY_SIZE: usize = 64;
15 pub const INDEX_ENTRY_SIZE: usize = 64;
16 pub const COMPRESSION_MODE_INLINE: u8 = 2;
16 pub const COMPRESSION_MODE_INLINE: u8 = 2;
17
17
18 pub struct IndexHeader {
18 pub struct IndexHeader {
19 pub(super) header_bytes: [u8; 4],
19 pub(super) header_bytes: [u8; 4],
20 }
20 }
21
21
22 #[derive(Copy, Clone)]
22 #[derive(Copy, Clone)]
23 pub struct IndexHeaderFlags {
23 pub struct IndexHeaderFlags {
24 flags: u16,
24 flags: u16,
25 }
25 }
26
26
27 /// Corresponds to the high bits of `_format_flags` in python
27 /// Corresponds to the high bits of `_format_flags` in python
28 impl IndexHeaderFlags {
28 impl IndexHeaderFlags {
29 /// Corresponds to FLAG_INLINE_DATA in python
29 /// Corresponds to FLAG_INLINE_DATA in python
30 pub fn is_inline(self) -> bool {
30 pub fn is_inline(self) -> bool {
31 self.flags & 1 != 0
31 self.flags & 1 != 0
32 }
32 }
33 /// Corresponds to FLAG_GENERALDELTA in python
33 /// Corresponds to FLAG_GENERALDELTA in python
34 pub fn uses_generaldelta(self) -> bool {
34 pub fn uses_generaldelta(self) -> bool {
35 self.flags & 2 != 0
35 self.flags & 2 != 0
36 }
36 }
37 }
37 }
38
38
39 /// Corresponds to the INDEX_HEADER structure,
39 /// Corresponds to the INDEX_HEADER structure,
40 /// which is parsed as a `header` variable in `_loadindex` in `revlog.py`
40 /// which is parsed as a `header` variable in `_loadindex` in `revlog.py`
41 impl IndexHeader {
41 impl IndexHeader {
42 fn format_flags(&self) -> IndexHeaderFlags {
42 fn format_flags(&self) -> IndexHeaderFlags {
43 // No "unknown flags" check here, unlike in python. Maybe there should
43 // No "unknown flags" check here, unlike in python. Maybe there should
44 // be.
44 // be.
45 IndexHeaderFlags {
45 IndexHeaderFlags {
46 flags: BigEndian::read_u16(&self.header_bytes[0..2]),
46 flags: BigEndian::read_u16(&self.header_bytes[0..2]),
47 }
47 }
48 }
48 }
49
49
50 /// The only revlog version currently supported by rhg.
50 /// The only revlog version currently supported by rhg.
51 const REVLOGV1: u16 = 1;
51 const REVLOGV1: u16 = 1;
52
52
53 /// Corresponds to `_format_version` in Python.
53 /// Corresponds to `_format_version` in Python.
54 fn format_version(&self) -> u16 {
54 fn format_version(&self) -> u16 {
55 BigEndian::read_u16(&self.header_bytes[2..4])
55 BigEndian::read_u16(&self.header_bytes[2..4])
56 }
56 }
57
57
58 pub fn parse(index_bytes: &[u8]) -> Result<Option<IndexHeader>, HgError> {
58 pub fn parse(index_bytes: &[u8]) -> Result<Option<IndexHeader>, HgError> {
59 if index_bytes.is_empty() {
59 if index_bytes.is_empty() {
60 return Ok(None);
60 return Ok(None);
61 }
61 }
62 if index_bytes.len() < 4 {
62 if index_bytes.len() < 4 {
63 return Err(HgError::corrupted(
63 return Err(HgError::corrupted(
64 "corrupted revlog: can't read the index format header",
64 "corrupted revlog: can't read the index format header",
65 ));
65 ));
66 }
66 }
67 Ok(Some(IndexHeader {
67 Ok(Some(IndexHeader {
68 header_bytes: {
68 header_bytes: {
69 let bytes: [u8; 4] =
69 let bytes: [u8; 4] =
70 index_bytes[0..4].try_into().expect("impossible");
70 index_bytes[0..4].try_into().expect("impossible");
71 bytes
71 bytes
72 },
72 },
73 }))
73 }))
74 }
74 }
75 }
75 }
76
76
77 /// Abstracts the access to the index bytes since they can be spread between
77 /// Abstracts the access to the index bytes since they can be spread between
78 /// the immutable (bytes) part and the mutable (added) part if any appends
78 /// the immutable (bytes) part and the mutable (added) part if any appends
79 /// happened. This makes it transparent for the callers.
79 /// happened. This makes it transparent for the callers.
80 struct IndexData {
80 struct IndexData {
81 /// Immutable bytes, most likely taken from disk
81 /// Immutable bytes, most likely taken from disk
82 bytes: Box<dyn Deref<Target = [u8]> + Send>,
82 bytes: Box<dyn Deref<Target = [u8]> + Send>,
83 /// Used when stripping index contents, keeps track of the start of the
83 /// Used when stripping index contents, keeps track of the start of the
84 /// first stripped revision, which is used to give a slice of the
84 /// first stripped revision, which is used to give a slice of the
85 /// `bytes` field.
85 /// `bytes` field.
86 truncation: Option<usize>,
86 truncation: Option<usize>,
87 /// Bytes that were added after reading the index
87 /// Bytes that were added after reading the index
88 added: Vec<u8>,
88 added: Vec<u8>,
89 }
89 }
90
90
91 impl IndexData {
91 impl IndexData {
92 pub fn new(bytes: Box<dyn Deref<Target = [u8]> + Send>) -> Self {
92 pub fn new(bytes: Box<dyn Deref<Target = [u8]> + Send>) -> Self {
93 Self {
93 Self {
94 bytes,
94 bytes,
95 truncation: None,
95 truncation: None,
96 added: vec![],
96 added: vec![],
97 }
97 }
98 }
98 }
99
99
100 pub fn len(&self) -> usize {
100 pub fn len(&self) -> usize {
101 match self.truncation {
101 match self.truncation {
102 Some(truncation) => truncation + self.added.len(),
102 Some(truncation) => truncation + self.added.len(),
103 None => self.bytes.len() + self.added.len(),
103 None => self.bytes.len() + self.added.len(),
104 }
104 }
105 }
105 }
106
106
107 fn remove(
107 fn remove(
108 &mut self,
108 &mut self,
109 rev: Revision,
109 rev: Revision,
110 offsets: Option<&[usize]>,
110 offsets: Option<&[usize]>,
111 ) -> Result<(), RevlogError> {
111 ) -> Result<(), RevlogError> {
112 let rev = rev.0 as usize;
112 let rev = rev.0 as usize;
113 let truncation = if let Some(offsets) = offsets {
113 let truncation = if let Some(offsets) = offsets {
114 offsets[rev]
114 offsets[rev]
115 } else {
115 } else {
116 rev * INDEX_ENTRY_SIZE
116 rev * INDEX_ENTRY_SIZE
117 };
117 };
118 if truncation < self.bytes.len() {
118 if truncation < self.bytes.len() {
119 self.truncation = Some(truncation);
119 self.truncation = Some(truncation);
120 self.added.clear();
120 self.added.clear();
121 } else {
121 } else {
122 self.added.truncate(truncation - self.bytes.len());
122 self.added.truncate(truncation - self.bytes.len());
123 }
123 }
124 Ok(())
124 Ok(())
125 }
125 }
126
127 fn is_new(&self) -> bool {
128 self.bytes.is_empty()
129 }
126 }
130 }
127
131
128 impl std::ops::Index<std::ops::Range<usize>> for IndexData {
132 impl std::ops::Index<std::ops::Range<usize>> for IndexData {
129 type Output = [u8];
133 type Output = [u8];
130
134
131 fn index(&self, index: std::ops::Range<usize>) -> &Self::Output {
135 fn index(&self, index: std::ops::Range<usize>) -> &Self::Output {
132 let start = index.start;
136 let start = index.start;
133 let end = index.end;
137 let end = index.end;
134 let immutable_len = match self.truncation {
138 let immutable_len = match self.truncation {
135 Some(truncation) => truncation,
139 Some(truncation) => truncation,
136 None => self.bytes.len(),
140 None => self.bytes.len(),
137 };
141 };
138 if start < immutable_len {
142 if start < immutable_len {
139 if end > immutable_len {
143 if end > immutable_len {
140 panic!("index data cannot span existing and added ranges");
144 panic!("index data cannot span existing and added ranges");
141 }
145 }
142 &self.bytes[index]
146 &self.bytes[index]
143 } else {
147 } else {
144 &self.added[start - immutable_len..end - immutable_len]
148 &self.added[start - immutable_len..end - immutable_len]
145 }
149 }
146 }
150 }
147 }
151 }
148
152
153 #[derive(Debug, PartialEq, Eq)]
149 pub struct RevisionDataParams {
154 pub struct RevisionDataParams {
150 pub flags: u16,
155 pub flags: u16,
151 pub data_offset: u64,
156 pub data_offset: u64,
152 pub data_compressed_length: i32,
157 pub data_compressed_length: i32,
153 pub data_uncompressed_length: i32,
158 pub data_uncompressed_length: i32,
154 pub data_delta_base: i32,
159 pub data_delta_base: i32,
155 pub link_rev: i32,
160 pub link_rev: i32,
156 pub parent_rev_1: i32,
161 pub parent_rev_1: i32,
157 pub parent_rev_2: i32,
162 pub parent_rev_2: i32,
158 pub node_id: [u8; NODE_BYTES_LENGTH],
163 pub node_id: [u8; NODE_BYTES_LENGTH],
159 pub _sidedata_offset: u64,
164 pub _sidedata_offset: u64,
160 pub _sidedata_compressed_length: i32,
165 pub _sidedata_compressed_length: i32,
161 pub data_compression_mode: u8,
166 pub data_compression_mode: u8,
162 pub _sidedata_compression_mode: u8,
167 pub _sidedata_compression_mode: u8,
163 pub _rank: i32,
168 pub _rank: i32,
164 }
169 }
165
170
171 impl Default for RevisionDataParams {
172 fn default() -> Self {
173 Self {
174 flags: 0,
175 data_offset: 0,
176 data_compressed_length: 0,
177 data_uncompressed_length: 0,
178 data_delta_base: -1,
179 link_rev: -1,
180 parent_rev_1: -1,
181 parent_rev_2: -1,
182 node_id: [0; NODE_BYTES_LENGTH],
183 _sidedata_offset: 0,
184 _sidedata_compressed_length: 0,
185 data_compression_mode: COMPRESSION_MODE_INLINE,
186 _sidedata_compression_mode: COMPRESSION_MODE_INLINE,
187 _rank: -1,
188 }
189 }
190 }
191
166 #[derive(BytesCast)]
192 #[derive(BytesCast)]
167 #[repr(C)]
193 #[repr(C)]
168 pub struct RevisionDataV1 {
194 pub struct RevisionDataV1 {
169 data_offset_or_flags: unaligned::U64Be,
195 data_offset_or_flags: unaligned::U64Be,
170 data_compressed_length: unaligned::I32Be,
196 data_compressed_length: unaligned::I32Be,
171 data_uncompressed_length: unaligned::I32Be,
197 data_uncompressed_length: unaligned::I32Be,
172 data_delta_base: unaligned::I32Be,
198 data_delta_base: unaligned::I32Be,
173 link_rev: unaligned::I32Be,
199 link_rev: unaligned::I32Be,
174 parent_rev_1: unaligned::I32Be,
200 parent_rev_1: unaligned::I32Be,
175 parent_rev_2: unaligned::I32Be,
201 parent_rev_2: unaligned::I32Be,
176 node_id: [u8; STORED_NODE_ID_BYTES],
202 node_id: [u8; STORED_NODE_ID_BYTES],
177 }
203 }
178
204
179 fn _static_assert_size_of_revision_data_v1() {
205 fn _static_assert_size_of_revision_data_v1() {
180 let _ = std::mem::transmute::<RevisionDataV1, [u8; 64]>;
206 let _ = std::mem::transmute::<RevisionDataV1, [u8; 64]>;
181 }
207 }
182
208
183 impl RevisionDataParams {
209 impl RevisionDataParams {
184 pub fn validate(&self) -> Result<(), RevlogError> {
210 pub fn validate(&self) -> Result<(), RevlogError> {
185 if self.flags & !REVIDX_KNOWN_FLAGS != 0 {
211 if self.flags & !REVIDX_KNOWN_FLAGS != 0 {
186 return Err(RevlogError::corrupted(format!(
212 return Err(RevlogError::corrupted(format!(
187 "unknown revlog index flags: {}",
213 "unknown revlog index flags: {}",
188 self.flags
214 self.flags
189 )));
215 )));
190 }
216 }
191 if self.data_compression_mode != COMPRESSION_MODE_INLINE {
217 if self.data_compression_mode != COMPRESSION_MODE_INLINE {
192 return Err(RevlogError::corrupted(format!(
218 return Err(RevlogError::corrupted(format!(
193 "invalid data compression mode: {}",
219 "invalid data compression mode: {}",
194 self.data_compression_mode
220 self.data_compression_mode
195 )));
221 )));
196 }
222 }
197 // FIXME isn't this only for v2 or changelog v2?
223 // FIXME isn't this only for v2 or changelog v2?
198 if self._sidedata_compression_mode != COMPRESSION_MODE_INLINE {
224 if self._sidedata_compression_mode != COMPRESSION_MODE_INLINE {
199 return Err(RevlogError::corrupted(format!(
225 return Err(RevlogError::corrupted(format!(
200 "invalid sidedata compression mode: {}",
226 "invalid sidedata compression mode: {}",
201 self._sidedata_compression_mode
227 self._sidedata_compression_mode
202 )));
228 )));
203 }
229 }
204 Ok(())
230 Ok(())
205 }
231 }
206
232
207 pub fn into_v1(self) -> RevisionDataV1 {
233 pub fn into_v1(self) -> RevisionDataV1 {
208 let data_offset_or_flags = self.data_offset << 16 | self.flags as u64;
234 let data_offset_or_flags = self.data_offset << 16 | self.flags as u64;
209 let mut node_id = [0; STORED_NODE_ID_BYTES];
235 let mut node_id = [0; STORED_NODE_ID_BYTES];
210 node_id[..NODE_BYTES_LENGTH].copy_from_slice(&self.node_id);
236 node_id[..NODE_BYTES_LENGTH].copy_from_slice(&self.node_id);
211 RevisionDataV1 {
237 RevisionDataV1 {
212 data_offset_or_flags: data_offset_or_flags.into(),
238 data_offset_or_flags: data_offset_or_flags.into(),
213 data_compressed_length: self.data_compressed_length.into(),
239 data_compressed_length: self.data_compressed_length.into(),
214 data_uncompressed_length: self.data_uncompressed_length.into(),
240 data_uncompressed_length: self.data_uncompressed_length.into(),
215 data_delta_base: self.data_delta_base.into(),
241 data_delta_base: self.data_delta_base.into(),
216 link_rev: self.link_rev.into(),
242 link_rev: self.link_rev.into(),
217 parent_rev_1: self.parent_rev_1.into(),
243 parent_rev_1: self.parent_rev_1.into(),
218 parent_rev_2: self.parent_rev_2.into(),
244 parent_rev_2: self.parent_rev_2.into(),
219 node_id,
245 node_id,
220 }
246 }
221 }
247 }
222 }
248 }
223
249
224 /// A Revlog index
250 /// A Revlog index
225 pub struct Index {
251 pub struct Index {
226 bytes: IndexData,
252 bytes: IndexData,
227 /// Offsets of starts of index blocks.
253 /// Offsets of starts of index blocks.
228 /// Only needed when the index is interleaved with data.
254 /// Only needed when the index is interleaved with data.
229 offsets: RwLock<Option<Vec<usize>>>,
255 offsets: RwLock<Option<Vec<usize>>>,
230 uses_generaldelta: bool,
256 uses_generaldelta: bool,
231 is_inline: bool,
257 is_inline: bool,
232 }
258 }
233
259
234 impl Debug for Index {
260 impl Debug for Index {
235 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
261 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
236 f.debug_struct("Index")
262 f.debug_struct("Index")
237 .field("offsets", &self.offsets)
263 .field("offsets", &self.offsets)
238 .field("uses_generaldelta", &self.uses_generaldelta)
264 .field("uses_generaldelta", &self.uses_generaldelta)
239 .finish()
265 .finish()
240 }
266 }
241 }
267 }
242
268
243 impl Graph for Index {
269 impl Graph for Index {
244 fn parents(&self, rev: Revision) -> Result<[Revision; 2], GraphError> {
270 fn parents(&self, rev: Revision) -> Result<[Revision; 2], GraphError> {
245 let err = || GraphError::ParentOutOfRange(rev);
271 let err = || GraphError::ParentOutOfRange(rev);
246 match self.get_entry(rev) {
272 match self.get_entry(rev) {
247 Some(entry) => {
273 Some(entry) => {
248 // The C implementation checks that the parents are valid
274 // The C implementation checks that the parents are valid
249 // before returning
275 // before returning
250 Ok([
276 Ok([
251 self.check_revision(entry.p1()).ok_or_else(err)?,
277 self.check_revision(entry.p1()).ok_or_else(err)?,
252 self.check_revision(entry.p2()).ok_or_else(err)?,
278 self.check_revision(entry.p2()).ok_or_else(err)?,
253 ])
279 ])
254 }
280 }
255 None => Ok([NULL_REVISION, NULL_REVISION]),
281 None => Ok([NULL_REVISION, NULL_REVISION]),
256 }
282 }
257 }
283 }
258 }
284 }
259
285
260 impl Index {
286 impl Index {
261 /// Create an index from bytes.
287 /// Create an index from bytes.
262 /// Calculate the start of each entry when is_inline is true.
288 /// Calculate the start of each entry when is_inline is true.
263 pub fn new(
289 pub fn new(
264 bytes: Box<dyn Deref<Target = [u8]> + Send>,
290 bytes: Box<dyn Deref<Target = [u8]> + Send>,
265 default_header: IndexHeader,
291 default_header: IndexHeader,
266 ) -> Result<Self, HgError> {
292 ) -> Result<Self, HgError> {
267 let header =
293 let header =
268 IndexHeader::parse(bytes.as_ref())?.unwrap_or(default_header);
294 IndexHeader::parse(bytes.as_ref())?.unwrap_or(default_header);
269
295
270 if header.format_version() != IndexHeader::REVLOGV1 {
296 if header.format_version() != IndexHeader::REVLOGV1 {
271 // A proper new version should have had a repo/store
297 // A proper new version should have had a repo/store
272 // requirement.
298 // requirement.
273 return Err(HgError::corrupted("unsupported revlog version"));
299 return Err(HgError::corrupted("unsupported revlog version"));
274 }
300 }
275
301
276 // This is only correct because we know version is REVLOGV1.
302 // This is only correct because we know version is REVLOGV1.
277 // In v2 we always use generaldelta, while in v0 we never use
303 // In v2 we always use generaldelta, while in v0 we never use
278 // generaldelta. Similar for [is_inline] (it's only used in v1).
304 // generaldelta. Similar for [is_inline] (it's only used in v1).
279 let uses_generaldelta = header.format_flags().uses_generaldelta();
305 let uses_generaldelta = header.format_flags().uses_generaldelta();
280
306
281 if header.format_flags().is_inline() {
307 if header.format_flags().is_inline() {
282 let mut offset: usize = 0;
308 let mut offset: usize = 0;
283 let mut offsets = Vec::new();
309 let mut offsets = Vec::new();
284
310
285 while offset + INDEX_ENTRY_SIZE <= bytes.len() {
311 while offset + INDEX_ENTRY_SIZE <= bytes.len() {
286 offsets.push(offset);
312 offsets.push(offset);
287 let end = offset + INDEX_ENTRY_SIZE;
313 let end = offset + INDEX_ENTRY_SIZE;
288 let entry = IndexEntry {
314 let entry = IndexEntry {
289 bytes: &bytes[offset..end],
315 bytes: &bytes[offset..end],
290 offset_override: None,
316 offset_override: None,
291 };
317 };
292
318
293 offset += INDEX_ENTRY_SIZE + entry.compressed_len() as usize;
319 offset += INDEX_ENTRY_SIZE + entry.compressed_len() as usize;
294 }
320 }
295
321
296 if offset == bytes.len() {
322 if offset == bytes.len() {
297 Ok(Self {
323 Ok(Self {
298 bytes: IndexData::new(bytes),
324 bytes: IndexData::new(bytes),
299 offsets: RwLock::new(Some(offsets)),
325 offsets: RwLock::new(Some(offsets)),
300 uses_generaldelta,
326 uses_generaldelta,
301 is_inline: true,
327 is_inline: true,
302 })
328 })
303 } else {
329 } else {
304 Err(HgError::corrupted("unexpected inline revlog length"))
330 Err(HgError::corrupted("unexpected inline revlog length"))
305 }
331 }
306 } else {
332 } else {
307 Ok(Self {
333 Ok(Self {
308 bytes: IndexData::new(bytes),
334 bytes: IndexData::new(bytes),
309 offsets: RwLock::new(None),
335 offsets: RwLock::new(None),
310 uses_generaldelta,
336 uses_generaldelta,
311 is_inline: false,
337 is_inline: false,
312 })
338 })
313 }
339 }
314 }
340 }
315
341
316 pub fn uses_generaldelta(&self) -> bool {
342 pub fn uses_generaldelta(&self) -> bool {
317 self.uses_generaldelta
343 self.uses_generaldelta
318 }
344 }
319
345
320 /// Value of the inline flag.
346 /// Value of the inline flag.
321 pub fn is_inline(&self) -> bool {
347 pub fn is_inline(&self) -> bool {
322 self.is_inline
348 self.is_inline
323 }
349 }
324
350
325 /// Return a slice of bytes if `revlog` is inline. Panic if not.
351 /// Return a slice of bytes if `revlog` is inline. Panic if not.
326 pub fn data(&self, start: usize, end: usize) -> &[u8] {
352 pub fn data(&self, start: usize, end: usize) -> &[u8] {
327 if !self.is_inline() {
353 if !self.is_inline() {
328 panic!("tried to access data in the index of a revlog that is not inline");
354 panic!("tried to access data in the index of a revlog that is not inline");
329 }
355 }
330 &self.bytes[start..end]
356 &self.bytes[start..end]
331 }
357 }
332
358
333 /// Return number of entries of the revlog index.
359 /// Return number of entries of the revlog index.
334 pub fn len(&self) -> usize {
360 pub fn len(&self) -> usize {
335 if let Some(offsets) = &*self.get_offsets() {
361 if let Some(offsets) = &*self.get_offsets() {
336 offsets.len()
362 offsets.len()
337 } else {
363 } else {
338 self.bytes.len() / INDEX_ENTRY_SIZE
364 self.bytes.len() / INDEX_ENTRY_SIZE
339 }
365 }
340 }
366 }
341
367
342 pub fn get_offsets(&self) -> RwLockReadGuard<Option<Vec<usize>>> {
368 pub fn get_offsets(&self) -> RwLockReadGuard<Option<Vec<usize>>> {
343 if self.is_inline() {
369 if self.is_inline() {
344 {
370 {
345 // Wrap in a block to drop the read guard
371 // Wrap in a block to drop the read guard
346 // TODO perf?
372 // TODO perf?
347 let mut offsets = self.offsets.write().unwrap();
373 let mut offsets = self.offsets.write().unwrap();
348 if offsets.is_none() {
374 if offsets.is_none() {
349 offsets.replace(inline_scan(&self.bytes.bytes).1);
375 offsets.replace(inline_scan(&self.bytes.bytes).1);
350 }
376 }
351 }
377 }
352 }
378 }
353 self.offsets.read().unwrap()
379 self.offsets.read().unwrap()
354 }
380 }
355
381
356 pub fn get_offsets_mut(&mut self) -> RwLockWriteGuard<Option<Vec<usize>>> {
382 pub fn get_offsets_mut(&mut self) -> RwLockWriteGuard<Option<Vec<usize>>> {
357 let mut offsets = self.offsets.write().unwrap();
383 let mut offsets = self.offsets.write().unwrap();
358 if self.is_inline() && offsets.is_none() {
384 if self.is_inline() && offsets.is_none() {
359 offsets.replace(inline_scan(&self.bytes.bytes).1);
385 offsets.replace(inline_scan(&self.bytes.bytes).1);
360 }
386 }
361 offsets
387 offsets
362 }
388 }
363
389
364 /// Returns `true` if the `Index` has zero `entries`.
390 /// Returns `true` if the `Index` has zero `entries`.
365 pub fn is_empty(&self) -> bool {
391 pub fn is_empty(&self) -> bool {
366 self.len() == 0
392 self.len() == 0
367 }
393 }
368
394
369 /// Return the index entry corresponding to the given revision or `None`
395 /// Return the index entry corresponding to the given revision or `None`
370 /// for [`NULL_REVISION`]
396 /// for [`NULL_REVISION`]
371 ///
397 ///
372 /// The specified revision being of the checked type, it always exists
398 /// The specified revision being of the checked type, it always exists
373 /// if it was validated by this index.
399 /// if it was validated by this index.
374 pub fn get_entry(&self, rev: Revision) -> Option<IndexEntry> {
400 pub fn get_entry(&self, rev: Revision) -> Option<IndexEntry> {
375 if rev == NULL_REVISION {
401 if rev == NULL_REVISION {
376 return None;
402 return None;
377 }
403 }
378 Some(if let Some(offsets) = &*self.get_offsets() {
404 Some(if let Some(offsets) = &*self.get_offsets() {
379 self.get_entry_inline(rev, offsets.as_ref())
405 self.get_entry_inline(rev, offsets.as_ref())
380 } else {
406 } else {
381 self.get_entry_separated(rev)
407 self.get_entry_separated(rev)
382 })
408 })
383 }
409 }
384
410
385 /// Return the binary content of the index entry for the given revision
411 /// Return the binary content of the index entry for the given revision
386 ///
412 ///
387 /// See [get_entry()](`Self::get_entry()`) for cases when `None` is
413 /// See [get_entry()](`Self::get_entry()`) for cases when `None` is
388 /// returned.
414 /// returned.
389 pub fn entry_binary(&self, rev: Revision) -> Option<&[u8]> {
415 pub fn entry_binary(&self, rev: Revision) -> Option<&[u8]> {
390 self.get_entry(rev).map(|e| {
416 self.get_entry(rev).map(|e| {
391 let bytes = e.as_bytes();
417 let bytes = e.as_bytes();
392 if rev.0 == 0 {
418 if rev.0 == 0 {
393 &bytes[4..]
419 &bytes[4..]
394 } else {
420 } else {
395 bytes
421 bytes
396 }
422 }
397 })
423 })
398 }
424 }
399
425
426 pub fn entry_as_params(
427 &self,
428 rev: UncheckedRevision,
429 ) -> Option<RevisionDataParams> {
430 let rev = self.check_revision(rev)?;
431 self.get_entry(rev).map(|e| RevisionDataParams {
432 flags: e.flags(),
433 data_offset: if rev.0 == 0 && !self.bytes.is_new() {
434 e.flags() as u64
435 } else {
436 e.raw_offset()
437 },
438 data_compressed_length: e.compressed_len().try_into().unwrap(),
439 data_uncompressed_length: e.uncompressed_len(),
440 data_delta_base: e.base_revision_or_base_of_delta_chain().0,
441 link_rev: e.link_revision().0,
442 parent_rev_1: e.p1().0,
443 parent_rev_2: e.p2().0,
444 node_id: e.hash().as_bytes().try_into().unwrap(),
445 ..Default::default()
446 })
447 }
448
400 fn get_entry_inline(
449 fn get_entry_inline(
401 &self,
450 &self,
402 rev: Revision,
451 rev: Revision,
403 offsets: &[usize],
452 offsets: &[usize],
404 ) -> IndexEntry {
453 ) -> IndexEntry {
405 let start = offsets[rev.0 as usize];
454 let start = offsets[rev.0 as usize];
406 let end = start + INDEX_ENTRY_SIZE;
455 let end = start + INDEX_ENTRY_SIZE;
407 let bytes = &self.bytes[start..end];
456 let bytes = &self.bytes[start..end];
408
457
409 // See IndexEntry for an explanation of this override.
458 // See IndexEntry for an explanation of this override.
410 let offset_override = Some(end);
459 let offset_override = Some(end);
411
460
412 IndexEntry {
461 IndexEntry {
413 bytes,
462 bytes,
414 offset_override,
463 offset_override,
415 }
464 }
416 }
465 }
417
466
418 fn get_entry_separated(&self, rev: Revision) -> IndexEntry {
467 fn get_entry_separated(&self, rev: Revision) -> IndexEntry {
419 let start = rev.0 as usize * INDEX_ENTRY_SIZE;
468 let start = rev.0 as usize * INDEX_ENTRY_SIZE;
420 let end = start + INDEX_ENTRY_SIZE;
469 let end = start + INDEX_ENTRY_SIZE;
421 let bytes = &self.bytes[start..end];
470 let bytes = &self.bytes[start..end];
422
471
423 // Override the offset of the first revision as its bytes are used
472 // Override the offset of the first revision as its bytes are used
424 // for the index's metadata (saving space because it is always 0)
473 // for the index's metadata (saving space because it is always 0)
425 let offset_override = if rev == Revision(0) { Some(0) } else { None };
474 let offset_override = if rev == Revision(0) { Some(0) } else { None };
426
475
427 IndexEntry {
476 IndexEntry {
428 bytes,
477 bytes,
429 offset_override,
478 offset_override,
430 }
479 }
431 }
480 }
432
481
433 /// TODO move this to the trait probably, along with other things
482 /// TODO move this to the trait probably, along with other things
434 pub fn append(
483 pub fn append(
435 &mut self,
484 &mut self,
436 revision_data: RevisionDataParams,
485 revision_data: RevisionDataParams,
437 ) -> Result<(), RevlogError> {
486 ) -> Result<(), RevlogError> {
438 revision_data.validate()?;
487 revision_data.validate()?;
439 let new_offset = self.bytes.len();
488 let new_offset = self.bytes.len();
440 if let Some(offsets) = &mut *self.get_offsets_mut() {
489 if let Some(offsets) = &mut *self.get_offsets_mut() {
441 offsets.push(new_offset)
490 offsets.push(new_offset)
442 }
491 }
443 self.bytes.added.extend(revision_data.into_v1().as_bytes());
492 self.bytes.added.extend(revision_data.into_v1().as_bytes());
444 Ok(())
493 Ok(())
445 }
494 }
446
495
447 pub fn pack_header(&self, header: i32) -> [u8; 4] {
496 pub fn pack_header(&self, header: i32) -> [u8; 4] {
448 header.to_be_bytes()
497 header.to_be_bytes()
449 }
498 }
450
499
451 pub fn remove(&mut self, rev: Revision) -> Result<(), RevlogError> {
500 pub fn remove(&mut self, rev: Revision) -> Result<(), RevlogError> {
452 let offsets = self.get_offsets().clone();
501 let offsets = self.get_offsets().clone();
453 self.bytes.remove(rev, offsets.as_deref())?;
502 self.bytes.remove(rev, offsets.as_deref())?;
454 if let Some(offsets) = &mut *self.get_offsets_mut() {
503 if let Some(offsets) = &mut *self.get_offsets_mut() {
455 offsets.truncate(rev.0 as usize)
504 offsets.truncate(rev.0 as usize)
456 }
505 }
457 Ok(())
506 Ok(())
458 }
507 }
459
508
460 pub fn clear_caches(&mut self) {
509 pub fn clear_caches(&mut self) {
461 // We need to get the 'inline' value from Python at init and use this
510 // We need to get the 'inline' value from Python at init and use this
462 // instead of offsets to determine whether we're inline since we might
511 // instead of offsets to determine whether we're inline since we might
463 // clear caches. This implies re-populating the offsets on-demand.
512 // clear caches. This implies re-populating the offsets on-demand.
464 self.offsets = RwLock::new(None);
513 self.offsets = RwLock::new(None);
465 }
514 }
466 }
515 }
467
516
468 fn inline_scan(bytes: &[u8]) -> (usize, Vec<usize>) {
517 fn inline_scan(bytes: &[u8]) -> (usize, Vec<usize>) {
469 let mut offset: usize = 0;
518 let mut offset: usize = 0;
470 let mut offsets = Vec::new();
519 let mut offsets = Vec::new();
471
520
472 while offset + INDEX_ENTRY_SIZE <= bytes.len() {
521 while offset + INDEX_ENTRY_SIZE <= bytes.len() {
473 offsets.push(offset);
522 offsets.push(offset);
474 let end = offset + INDEX_ENTRY_SIZE;
523 let end = offset + INDEX_ENTRY_SIZE;
475 let entry = IndexEntry {
524 let entry = IndexEntry {
476 bytes: &bytes[offset..end],
525 bytes: &bytes[offset..end],
477 offset_override: None,
526 offset_override: None,
478 };
527 };
479
528
480 offset += INDEX_ENTRY_SIZE + entry.compressed_len() as usize;
529 offset += INDEX_ENTRY_SIZE + entry.compressed_len() as usize;
481 }
530 }
482 (offset, offsets)
531 (offset, offsets)
483 }
532 }
484
533
485 impl super::RevlogIndex for Index {
534 impl super::RevlogIndex for Index {
486 fn len(&self) -> usize {
535 fn len(&self) -> usize {
487 self.len()
536 self.len()
488 }
537 }
489
538
490 fn node(&self, rev: Revision) -> Option<&Node> {
539 fn node(&self, rev: Revision) -> Option<&Node> {
491 self.get_entry(rev).map(|entry| entry.hash())
540 self.get_entry(rev).map(|entry| entry.hash())
492 }
541 }
493 }
542 }
494
543
495 #[derive(Debug)]
544 #[derive(Debug)]
496 pub struct IndexEntry<'a> {
545 pub struct IndexEntry<'a> {
497 bytes: &'a [u8],
546 bytes: &'a [u8],
498 /// Allows to override the offset value of the entry.
547 /// Allows to override the offset value of the entry.
499 ///
548 ///
500 /// For interleaved index and data, the offset stored in the index
549 /// For interleaved index and data, the offset stored in the index
501 /// corresponds to the separated data offset.
550 /// corresponds to the separated data offset.
502 /// It has to be overridden with the actual offset in the interleaved
551 /// It has to be overridden with the actual offset in the interleaved
503 /// index which is just after the index block.
552 /// index which is just after the index block.
504 ///
553 ///
505 /// For separated index and data, the offset stored in the first index
554 /// For separated index and data, the offset stored in the first index
506 /// entry is mixed with the index headers.
555 /// entry is mixed with the index headers.
507 /// It has to be overridden with 0.
556 /// It has to be overridden with 0.
508 offset_override: Option<usize>,
557 offset_override: Option<usize>,
509 }
558 }
510
559
511 impl<'a> IndexEntry<'a> {
560 impl<'a> IndexEntry<'a> {
512 /// Return the offset of the data.
561 /// Return the offset of the data.
513 pub fn offset(&self) -> usize {
562 pub fn offset(&self) -> usize {
514 if let Some(offset_override) = self.offset_override {
563 if let Some(offset_override) = self.offset_override {
515 offset_override
564 offset_override
516 } else {
565 } else {
517 let mut bytes = [0; 8];
566 let mut bytes = [0; 8];
518 bytes[2..8].copy_from_slice(&self.bytes[0..=5]);
567 bytes[2..8].copy_from_slice(&self.bytes[0..=5]);
519 BigEndian::read_u64(&bytes[..]) as usize
568 BigEndian::read_u64(&bytes[..]) as usize
520 }
569 }
521 }
570 }
571 pub fn raw_offset(&self) -> u64 {
572 BigEndian::read_u64(&self.bytes[0..8])
573 }
522
574
523 pub fn flags(&self) -> u16 {
575 pub fn flags(&self) -> u16 {
524 BigEndian::read_u16(&self.bytes[6..=7])
576 BigEndian::read_u16(&self.bytes[6..=7])
525 }
577 }
526
578
527 /// Return the compressed length of the data.
579 /// Return the compressed length of the data.
528 pub fn compressed_len(&self) -> u32 {
580 pub fn compressed_len(&self) -> u32 {
529 BigEndian::read_u32(&self.bytes[8..=11])
581 BigEndian::read_u32(&self.bytes[8..=11])
530 }
582 }
531
583
532 /// Return the uncompressed length of the data.
584 /// Return the uncompressed length of the data.
533 pub fn uncompressed_len(&self) -> i32 {
585 pub fn uncompressed_len(&self) -> i32 {
534 BigEndian::read_i32(&self.bytes[12..=15])
586 BigEndian::read_i32(&self.bytes[12..=15])
535 }
587 }
536
588
537 /// Return the revision upon which the data has been derived.
589 /// Return the revision upon which the data has been derived.
538 pub fn base_revision_or_base_of_delta_chain(&self) -> UncheckedRevision {
590 pub fn base_revision_or_base_of_delta_chain(&self) -> UncheckedRevision {
539 // TODO Maybe return an Option when base_revision == rev?
591 // TODO Maybe return an Option when base_revision == rev?
540 // Requires to add rev to IndexEntry
592 // Requires to add rev to IndexEntry
541
593
542 BigEndian::read_i32(&self.bytes[16..]).into()
594 BigEndian::read_i32(&self.bytes[16..]).into()
543 }
595 }
544
596
545 pub fn link_revision(&self) -> UncheckedRevision {
597 pub fn link_revision(&self) -> UncheckedRevision {
546 BigEndian::read_i32(&self.bytes[20..]).into()
598 BigEndian::read_i32(&self.bytes[20..]).into()
547 }
599 }
548
600
549 pub fn p1(&self) -> UncheckedRevision {
601 pub fn p1(&self) -> UncheckedRevision {
550 BigEndian::read_i32(&self.bytes[24..]).into()
602 BigEndian::read_i32(&self.bytes[24..]).into()
551 }
603 }
552
604
553 pub fn p2(&self) -> UncheckedRevision {
605 pub fn p2(&self) -> UncheckedRevision {
554 BigEndian::read_i32(&self.bytes[28..]).into()
606 BigEndian::read_i32(&self.bytes[28..]).into()
555 }
607 }
556
608
557 /// Return the hash of revision's full text.
609 /// Return the hash of revision's full text.
558 ///
610 ///
559 /// Currently, SHA-1 is used and only the first 20 bytes of this field
611 /// Currently, SHA-1 is used and only the first 20 bytes of this field
560 /// are used.
612 /// are used.
561 pub fn hash(&self) -> &'a Node {
613 pub fn hash(&self) -> &'a Node {
562 (&self.bytes[32..52]).try_into().unwrap()
614 (&self.bytes[32..52]).try_into().unwrap()
563 }
615 }
564
616
565 pub fn as_bytes(&self) -> &'a [u8] {
617 pub fn as_bytes(&self) -> &'a [u8] {
566 self.bytes
618 self.bytes
567 }
619 }
568 }
620 }
569
621
570 #[cfg(test)]
622 #[cfg(test)]
571 mod tests {
623 mod tests {
572 use super::*;
624 use super::*;
573 use crate::node::NULL_NODE;
625 use crate::node::NULL_NODE;
574
626
575 #[cfg(test)]
627 #[cfg(test)]
576 #[derive(Debug, Copy, Clone)]
628 #[derive(Debug, Copy, Clone)]
577 pub struct IndexEntryBuilder {
629 pub struct IndexEntryBuilder {
578 is_first: bool,
630 is_first: bool,
579 is_inline: bool,
631 is_inline: bool,
580 is_general_delta: bool,
632 is_general_delta: bool,
581 version: u16,
633 version: u16,
582 offset: usize,
634 offset: usize,
583 compressed_len: usize,
635 compressed_len: usize,
584 uncompressed_len: usize,
636 uncompressed_len: usize,
585 base_revision_or_base_of_delta_chain: Revision,
637 base_revision_or_base_of_delta_chain: Revision,
586 link_revision: Revision,
638 link_revision: Revision,
587 p1: Revision,
639 p1: Revision,
588 p2: Revision,
640 p2: Revision,
589 node: Node,
641 node: Node,
590 }
642 }
591
643
592 #[cfg(test)]
644 #[cfg(test)]
593 impl IndexEntryBuilder {
645 impl IndexEntryBuilder {
594 #[allow(clippy::new_without_default)]
646 #[allow(clippy::new_without_default)]
595 pub fn new() -> Self {
647 pub fn new() -> Self {
596 Self {
648 Self {
597 is_first: false,
649 is_first: false,
598 is_inline: false,
650 is_inline: false,
599 is_general_delta: true,
651 is_general_delta: true,
600 version: 1,
652 version: 1,
601 offset: 0,
653 offset: 0,
602 compressed_len: 0,
654 compressed_len: 0,
603 uncompressed_len: 0,
655 uncompressed_len: 0,
604 base_revision_or_base_of_delta_chain: Revision(0),
656 base_revision_or_base_of_delta_chain: Revision(0),
605 link_revision: Revision(0),
657 link_revision: Revision(0),
606 p1: NULL_REVISION,
658 p1: NULL_REVISION,
607 p2: NULL_REVISION,
659 p2: NULL_REVISION,
608 node: NULL_NODE,
660 node: NULL_NODE,
609 }
661 }
610 }
662 }
611
663
612 pub fn is_first(&mut self, value: bool) -> &mut Self {
664 pub fn is_first(&mut self, value: bool) -> &mut Self {
613 self.is_first = value;
665 self.is_first = value;
614 self
666 self
615 }
667 }
616
668
617 pub fn with_inline(&mut self, value: bool) -> &mut Self {
669 pub fn with_inline(&mut self, value: bool) -> &mut Self {
618 self.is_inline = value;
670 self.is_inline = value;
619 self
671 self
620 }
672 }
621
673
622 pub fn with_general_delta(&mut self, value: bool) -> &mut Self {
674 pub fn with_general_delta(&mut self, value: bool) -> &mut Self {
623 self.is_general_delta = value;
675 self.is_general_delta = value;
624 self
676 self
625 }
677 }
626
678
627 pub fn with_version(&mut self, value: u16) -> &mut Self {
679 pub fn with_version(&mut self, value: u16) -> &mut Self {
628 self.version = value;
680 self.version = value;
629 self
681 self
630 }
682 }
631
683
632 pub fn with_offset(&mut self, value: usize) -> &mut Self {
684 pub fn with_offset(&mut self, value: usize) -> &mut Self {
633 self.offset = value;
685 self.offset = value;
634 self
686 self
635 }
687 }
636
688
637 pub fn with_compressed_len(&mut self, value: usize) -> &mut Self {
689 pub fn with_compressed_len(&mut self, value: usize) -> &mut Self {
638 self.compressed_len = value;
690 self.compressed_len = value;
639 self
691 self
640 }
692 }
641
693
642 pub fn with_uncompressed_len(&mut self, value: usize) -> &mut Self {
694 pub fn with_uncompressed_len(&mut self, value: usize) -> &mut Self {
643 self.uncompressed_len = value;
695 self.uncompressed_len = value;
644 self
696 self
645 }
697 }
646
698
647 pub fn with_base_revision_or_base_of_delta_chain(
699 pub fn with_base_revision_or_base_of_delta_chain(
648 &mut self,
700 &mut self,
649 value: Revision,
701 value: Revision,
650 ) -> &mut Self {
702 ) -> &mut Self {
651 self.base_revision_or_base_of_delta_chain = value;
703 self.base_revision_or_base_of_delta_chain = value;
652 self
704 self
653 }
705 }
654
706
655 pub fn with_link_revision(&mut self, value: Revision) -> &mut Self {
707 pub fn with_link_revision(&mut self, value: Revision) -> &mut Self {
656 self.link_revision = value;
708 self.link_revision = value;
657 self
709 self
658 }
710 }
659
711
660 pub fn with_p1(&mut self, value: Revision) -> &mut Self {
712 pub fn with_p1(&mut self, value: Revision) -> &mut Self {
661 self.p1 = value;
713 self.p1 = value;
662 self
714 self
663 }
715 }
664
716
665 pub fn with_p2(&mut self, value: Revision) -> &mut Self {
717 pub fn with_p2(&mut self, value: Revision) -> &mut Self {
666 self.p2 = value;
718 self.p2 = value;
667 self
719 self
668 }
720 }
669
721
670 pub fn with_node(&mut self, value: Node) -> &mut Self {
722 pub fn with_node(&mut self, value: Node) -> &mut Self {
671 self.node = value;
723 self.node = value;
672 self
724 self
673 }
725 }
674
726
675 pub fn build(&self) -> Vec<u8> {
727 pub fn build(&self) -> Vec<u8> {
676 let mut bytes = Vec::with_capacity(INDEX_ENTRY_SIZE);
728 let mut bytes = Vec::with_capacity(INDEX_ENTRY_SIZE);
677 if self.is_first {
729 if self.is_first {
678 bytes.extend(&match (self.is_general_delta, self.is_inline) {
730 bytes.extend(&match (self.is_general_delta, self.is_inline) {
679 (false, false) => [0u8, 0],
731 (false, false) => [0u8, 0],
680 (false, true) => [0u8, 1],
732 (false, true) => [0u8, 1],
681 (true, false) => [0u8, 2],
733 (true, false) => [0u8, 2],
682 (true, true) => [0u8, 3],
734 (true, true) => [0u8, 3],
683 });
735 });
684 bytes.extend(&self.version.to_be_bytes());
736 bytes.extend(&self.version.to_be_bytes());
685 // Remaining offset bytes.
737 // Remaining offset bytes.
686 bytes.extend(&[0u8; 2]);
738 bytes.extend(&[0u8; 2]);
687 } else {
739 } else {
688 // Offset stored on 48 bits (6 bytes)
740 // Offset stored on 48 bits (6 bytes)
689 bytes.extend(&(self.offset as u64).to_be_bytes()[2..]);
741 bytes.extend(&(self.offset as u64).to_be_bytes()[2..]);
690 }
742 }
691 bytes.extend(&[0u8; 2]); // Revision flags.
743 bytes.extend(&[0u8; 2]); // Revision flags.
692 bytes.extend(&(self.compressed_len as u32).to_be_bytes());
744 bytes.extend(&(self.compressed_len as u32).to_be_bytes());
693 bytes.extend(&(self.uncompressed_len as u32).to_be_bytes());
745 bytes.extend(&(self.uncompressed_len as u32).to_be_bytes());
694 bytes.extend(
746 bytes.extend(
695 &self.base_revision_or_base_of_delta_chain.0.to_be_bytes(),
747 &self.base_revision_or_base_of_delta_chain.0.to_be_bytes(),
696 );
748 );
697 bytes.extend(&self.link_revision.0.to_be_bytes());
749 bytes.extend(&self.link_revision.0.to_be_bytes());
698 bytes.extend(&self.p1.0.to_be_bytes());
750 bytes.extend(&self.p1.0.to_be_bytes());
699 bytes.extend(&self.p2.0.to_be_bytes());
751 bytes.extend(&self.p2.0.to_be_bytes());
700 bytes.extend(self.node.as_bytes());
752 bytes.extend(self.node.as_bytes());
701 bytes.extend(vec![0u8; 12]);
753 bytes.extend(vec![0u8; 12]);
702 bytes
754 bytes
703 }
755 }
704 }
756 }
705
757
706 pub fn is_inline(index_bytes: &[u8]) -> bool {
758 pub fn is_inline(index_bytes: &[u8]) -> bool {
707 IndexHeader::parse(index_bytes)
759 IndexHeader::parse(index_bytes)
708 .expect("too short")
760 .expect("too short")
709 .unwrap()
761 .unwrap()
710 .format_flags()
762 .format_flags()
711 .is_inline()
763 .is_inline()
712 }
764 }
713
765
714 pub fn uses_generaldelta(index_bytes: &[u8]) -> bool {
766 pub fn uses_generaldelta(index_bytes: &[u8]) -> bool {
715 IndexHeader::parse(index_bytes)
767 IndexHeader::parse(index_bytes)
716 .expect("too short")
768 .expect("too short")
717 .unwrap()
769 .unwrap()
718 .format_flags()
770 .format_flags()
719 .uses_generaldelta()
771 .uses_generaldelta()
720 }
772 }
721
773
722 pub fn get_version(index_bytes: &[u8]) -> u16 {
774 pub fn get_version(index_bytes: &[u8]) -> u16 {
723 IndexHeader::parse(index_bytes)
775 IndexHeader::parse(index_bytes)
724 .expect("too short")
776 .expect("too short")
725 .unwrap()
777 .unwrap()
726 .format_version()
778 .format_version()
727 }
779 }
728
780
729 #[test]
781 #[test]
730 fn flags_when_no_inline_flag_test() {
782 fn flags_when_no_inline_flag_test() {
731 let bytes = IndexEntryBuilder::new()
783 let bytes = IndexEntryBuilder::new()
732 .is_first(true)
784 .is_first(true)
733 .with_general_delta(false)
785 .with_general_delta(false)
734 .with_inline(false)
786 .with_inline(false)
735 .build();
787 .build();
736
788
737 assert!(!is_inline(&bytes));
789 assert!(!is_inline(&bytes));
738 assert!(!uses_generaldelta(&bytes));
790 assert!(!uses_generaldelta(&bytes));
739 }
791 }
740
792
741 #[test]
793 #[test]
742 fn flags_when_inline_flag_test() {
794 fn flags_when_inline_flag_test() {
743 let bytes = IndexEntryBuilder::new()
795 let bytes = IndexEntryBuilder::new()
744 .is_first(true)
796 .is_first(true)
745 .with_general_delta(false)
797 .with_general_delta(false)
746 .with_inline(true)
798 .with_inline(true)
747 .build();
799 .build();
748
800
749 assert!(is_inline(&bytes));
801 assert!(is_inline(&bytes));
750 assert!(!uses_generaldelta(&bytes));
802 assert!(!uses_generaldelta(&bytes));
751 }
803 }
752
804
753 #[test]
805 #[test]
754 fn flags_when_inline_and_generaldelta_flags_test() {
806 fn flags_when_inline_and_generaldelta_flags_test() {
755 let bytes = IndexEntryBuilder::new()
807 let bytes = IndexEntryBuilder::new()
756 .is_first(true)
808 .is_first(true)
757 .with_general_delta(true)
809 .with_general_delta(true)
758 .with_inline(true)
810 .with_inline(true)
759 .build();
811 .build();
760
812
761 assert!(is_inline(&bytes));
813 assert!(is_inline(&bytes));
762 assert!(uses_generaldelta(&bytes));
814 assert!(uses_generaldelta(&bytes));
763 }
815 }
764
816
765 #[test]
817 #[test]
766 fn test_offset() {
818 fn test_offset() {
767 let bytes = IndexEntryBuilder::new().with_offset(1).build();
819 let bytes = IndexEntryBuilder::new().with_offset(1).build();
768 let entry = IndexEntry {
820 let entry = IndexEntry {
769 bytes: &bytes,
821 bytes: &bytes,
770 offset_override: None,
822 offset_override: None,
771 };
823 };
772
824
773 assert_eq!(entry.offset(), 1)
825 assert_eq!(entry.offset(), 1)
774 }
826 }
775
827
776 #[test]
828 #[test]
777 fn test_with_overridden_offset() {
829 fn test_with_overridden_offset() {
778 let bytes = IndexEntryBuilder::new().with_offset(1).build();
830 let bytes = IndexEntryBuilder::new().with_offset(1).build();
779 let entry = IndexEntry {
831 let entry = IndexEntry {
780 bytes: &bytes,
832 bytes: &bytes,
781 offset_override: Some(2),
833 offset_override: Some(2),
782 };
834 };
783
835
784 assert_eq!(entry.offset(), 2)
836 assert_eq!(entry.offset(), 2)
785 }
837 }
786
838
787 #[test]
839 #[test]
788 fn test_compressed_len() {
840 fn test_compressed_len() {
789 let bytes = IndexEntryBuilder::new().with_compressed_len(1).build();
841 let bytes = IndexEntryBuilder::new().with_compressed_len(1).build();
790 let entry = IndexEntry {
842 let entry = IndexEntry {
791 bytes: &bytes,
843 bytes: &bytes,
792 offset_override: None,
844 offset_override: None,
793 };
845 };
794
846
795 assert_eq!(entry.compressed_len(), 1)
847 assert_eq!(entry.compressed_len(), 1)
796 }
848 }
797
849
798 #[test]
850 #[test]
799 fn test_uncompressed_len() {
851 fn test_uncompressed_len() {
800 let bytes = IndexEntryBuilder::new().with_uncompressed_len(1).build();
852 let bytes = IndexEntryBuilder::new().with_uncompressed_len(1).build();
801 let entry = IndexEntry {
853 let entry = IndexEntry {
802 bytes: &bytes,
854 bytes: &bytes,
803 offset_override: None,
855 offset_override: None,
804 };
856 };
805
857
806 assert_eq!(entry.uncompressed_len(), 1)
858 assert_eq!(entry.uncompressed_len(), 1)
807 }
859 }
808
860
809 #[test]
861 #[test]
810 fn test_base_revision_or_base_of_delta_chain() {
862 fn test_base_revision_or_base_of_delta_chain() {
811 let bytes = IndexEntryBuilder::new()
863 let bytes = IndexEntryBuilder::new()
812 .with_base_revision_or_base_of_delta_chain(Revision(1))
864 .with_base_revision_or_base_of_delta_chain(Revision(1))
813 .build();
865 .build();
814 let entry = IndexEntry {
866 let entry = IndexEntry {
815 bytes: &bytes,
867 bytes: &bytes,
816 offset_override: None,
868 offset_override: None,
817 };
869 };
818
870
819 assert_eq!(entry.base_revision_or_base_of_delta_chain(), 1.into())
871 assert_eq!(entry.base_revision_or_base_of_delta_chain(), 1.into())
820 }
872 }
821
873
822 #[test]
874 #[test]
823 fn link_revision_test() {
875 fn link_revision_test() {
824 let bytes = IndexEntryBuilder::new()
876 let bytes = IndexEntryBuilder::new()
825 .with_link_revision(Revision(123))
877 .with_link_revision(Revision(123))
826 .build();
878 .build();
827
879
828 let entry = IndexEntry {
880 let entry = IndexEntry {
829 bytes: &bytes,
881 bytes: &bytes,
830 offset_override: None,
882 offset_override: None,
831 };
883 };
832
884
833 assert_eq!(entry.link_revision(), 123.into());
885 assert_eq!(entry.link_revision(), 123.into());
834 }
886 }
835
887
836 #[test]
888 #[test]
837 fn p1_test() {
889 fn p1_test() {
838 let bytes = IndexEntryBuilder::new().with_p1(Revision(123)).build();
890 let bytes = IndexEntryBuilder::new().with_p1(Revision(123)).build();
839
891
840 let entry = IndexEntry {
892 let entry = IndexEntry {
841 bytes: &bytes,
893 bytes: &bytes,
842 offset_override: None,
894 offset_override: None,
843 };
895 };
844
896
845 assert_eq!(entry.p1(), 123.into());
897 assert_eq!(entry.p1(), 123.into());
846 }
898 }
847
899
848 #[test]
900 #[test]
849 fn p2_test() {
901 fn p2_test() {
850 let bytes = IndexEntryBuilder::new().with_p2(Revision(123)).build();
902 let bytes = IndexEntryBuilder::new().with_p2(Revision(123)).build();
851
903
852 let entry = IndexEntry {
904 let entry = IndexEntry {
853 bytes: &bytes,
905 bytes: &bytes,
854 offset_override: None,
906 offset_override: None,
855 };
907 };
856
908
857 assert_eq!(entry.p2(), 123.into());
909 assert_eq!(entry.p2(), 123.into());
858 }
910 }
859
911
860 #[test]
912 #[test]
861 fn node_test() {
913 fn node_test() {
862 let node = Node::from_hex("0123456789012345678901234567890123456789")
914 let node = Node::from_hex("0123456789012345678901234567890123456789")
863 .unwrap();
915 .unwrap();
864 let bytes = IndexEntryBuilder::new().with_node(node).build();
916 let bytes = IndexEntryBuilder::new().with_node(node).build();
865
917
866 let entry = IndexEntry {
918 let entry = IndexEntry {
867 bytes: &bytes,
919 bytes: &bytes,
868 offset_override: None,
920 offset_override: None,
869 };
921 };
870
922
871 assert_eq!(*entry.hash(), node);
923 assert_eq!(*entry.hash(), node);
872 }
924 }
873
925
874 #[test]
926 #[test]
875 fn version_test() {
927 fn version_test() {
876 let bytes = IndexEntryBuilder::new()
928 let bytes = IndexEntryBuilder::new()
877 .is_first(true)
929 .is_first(true)
878 .with_version(2)
930 .with_version(2)
879 .build();
931 .build();
880
932
881 assert_eq!(get_version(&bytes), 2)
933 assert_eq!(get_version(&bytes), 2)
882 }
934 }
883 }
935 }
884
936
885 #[cfg(test)]
937 #[cfg(test)]
886 pub use tests::IndexEntryBuilder;
938 pub use tests::IndexEntryBuilder;
@@ -1,680 +1,747 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 utils::{node_from_py_bytes, node_from_py_object},
10 utils::{node_from_py_bytes, node_from_py_object},
11 PyRevision,
11 PyRevision,
12 };
12 };
13 use cpython::{
13 use cpython::{
14 buffer::{Element, PyBuffer},
14 buffer::{Element, PyBuffer},
15 exc::{IndexError, ValueError},
15 exc::{IndexError, ValueError},
16 ObjectProtocol, PyBool, PyBytes, PyClone, PyDict, PyErr, PyInt, PyModule,
16 ObjectProtocol, PyBool, PyBytes, PyClone, PyDict, PyErr, PyInt, PyModule,
17 PyObject, PyResult, PyString, PyTuple, Python, PythonObject, ToPyObject,
17 PyObject, PyResult, PyString, PyTuple, Python, PythonObject, ToPyObject,
18 };
18 };
19 use hg::{
19 use hg::{
20 index::IndexHeader,
20 index::{IndexHeader, RevisionDataParams},
21 index::{RevisionDataParams, COMPRESSION_MODE_INLINE},
22 nodemap::{Block, NodeMapError, NodeTree},
21 nodemap::{Block, NodeMapError, NodeTree},
23 revlog::{nodemap::NodeMap, NodePrefix, RevlogIndex},
22 revlog::{nodemap::NodeMap, NodePrefix, RevlogIndex},
24 BaseRevision, Revision, UncheckedRevision,
23 BaseRevision, Revision, UncheckedRevision, NULL_REVISION,
25 };
24 };
26 use std::cell::RefCell;
25 use std::cell::RefCell;
27
26
28 /// Return a Struct implementing the Graph trait
27 /// Return a Struct implementing the Graph trait
29 pub(crate) fn pyindex_to_graph(
28 pub(crate) fn pyindex_to_graph(
30 py: Python,
29 py: Python,
31 index: PyObject,
30 index: PyObject,
32 ) -> PyResult<cindex::Index> {
31 ) -> PyResult<cindex::Index> {
33 match index.extract::<MixedIndex>(py) {
32 match index.extract::<MixedIndex>(py) {
34 Ok(midx) => Ok(midx.clone_cindex(py)),
33 Ok(midx) => Ok(midx.clone_cindex(py)),
35 Err(_) => cindex::Index::new(py, index),
34 Err(_) => cindex::Index::new(py, index),
36 }
35 }
37 }
36 }
38
37
39 py_class!(pub class MixedIndex |py| {
38 py_class!(pub class MixedIndex |py| {
40 data cindex: RefCell<cindex::Index>;
39 data cindex: RefCell<cindex::Index>;
41 data index: RefCell<hg::index::Index>;
40 data index: RefCell<hg::index::Index>;
42 data nt: RefCell<Option<NodeTree>>;
41 data nt: RefCell<Option<NodeTree>>;
43 data docket: RefCell<Option<PyObject>>;
42 data docket: RefCell<Option<PyObject>>;
44 // Holds a reference to the mmap'ed persistent nodemap data
43 // Holds a reference to the mmap'ed persistent nodemap data
45 data nodemap_mmap: RefCell<Option<PyBuffer>>;
44 data nodemap_mmap: RefCell<Option<PyBuffer>>;
46 // Holds a reference to the mmap'ed persistent index data
45 // Holds a reference to the mmap'ed persistent index data
47 data index_mmap: RefCell<Option<PyBuffer>>;
46 data index_mmap: RefCell<Option<PyBuffer>>;
48
47
49 def __new__(
48 def __new__(
50 _cls,
49 _cls,
51 cindex: PyObject,
50 cindex: PyObject,
52 data: PyObject,
51 data: PyObject,
53 default_header: u32,
52 default_header: u32,
54 ) -> PyResult<MixedIndex> {
53 ) -> PyResult<MixedIndex> {
55 Self::new(py, cindex, data, default_header)
54 Self::new(py, cindex, data, default_header)
56 }
55 }
57
56
58 /// Compatibility layer used for Python consumers needing access to the C index
57 /// Compatibility layer used for Python consumers needing access to the C index
59 ///
58 ///
60 /// Only use case so far is `scmutil.shortesthexnodeidprefix`,
59 /// Only use case so far is `scmutil.shortesthexnodeidprefix`,
61 /// that may need to build a custom `nodetree`, based on a specified revset.
60 /// that may need to build a custom `nodetree`, based on a specified revset.
62 /// With a Rust implementation of the nodemap, we will be able to get rid of
61 /// With a Rust implementation of the nodemap, we will be able to get rid of
63 /// this, by exposing our own standalone nodemap class,
62 /// this, by exposing our own standalone nodemap class,
64 /// ready to accept `MixedIndex`.
63 /// ready to accept `MixedIndex`.
65 def get_cindex(&self) -> PyResult<PyObject> {
64 def get_cindex(&self) -> PyResult<PyObject> {
66 Ok(self.cindex(py).borrow().inner().clone_ref(py))
65 Ok(self.cindex(py).borrow().inner().clone_ref(py))
67 }
66 }
68
67
69 // Index API involving nodemap, as defined in mercurial/pure/parsers.py
68 // Index API involving nodemap, as defined in mercurial/pure/parsers.py
70
69
71 /// Return Revision if found, raises a bare `error.RevlogError`
70 /// Return Revision if found, raises a bare `error.RevlogError`
72 /// in case of ambiguity, same as C version does
71 /// in case of ambiguity, same as C version does
73 def get_rev(&self, node: PyBytes) -> PyResult<Option<PyRevision>> {
72 def get_rev(&self, node: PyBytes) -> PyResult<Option<PyRevision>> {
74 let opt = self.get_nodetree(py)?.borrow();
73 let opt = self.get_nodetree(py)?.borrow();
75 let nt = opt.as_ref().unwrap();
74 let nt = opt.as_ref().unwrap();
76 let idx = &*self.cindex(py).borrow();
75 let idx = &*self.cindex(py).borrow();
77 let ridx = &*self.index(py).borrow();
76 let ridx = &*self.index(py).borrow();
78 let node = node_from_py_bytes(py, &node)?;
77 let node = node_from_py_bytes(py, &node)?;
79 let rust_rev =
78 let rust_rev =
80 nt.find_bin(ridx, node.into()).map_err(|e| nodemap_error(py, e))?;
79 nt.find_bin(ridx, node.into()).map_err(|e| nodemap_error(py, e))?;
81 let c_rev =
80 let c_rev =
82 nt.find_bin(idx, node.into()).map_err(|e| nodemap_error(py, e))?;
81 nt.find_bin(idx, node.into()).map_err(|e| nodemap_error(py, e))?;
83 assert_eq!(rust_rev, c_rev);
82 assert_eq!(rust_rev, c_rev);
84 Ok(rust_rev.map(Into::into))
83 Ok(rust_rev.map(Into::into))
85
84
86 }
85 }
87
86
88 /// same as `get_rev()` but raises a bare `error.RevlogError` if node
87 /// same as `get_rev()` but raises a bare `error.RevlogError` if node
89 /// is not found.
88 /// is not found.
90 ///
89 ///
91 /// No need to repeat `node` in the exception, `mercurial/revlog.py`
90 /// No need to repeat `node` in the exception, `mercurial/revlog.py`
92 /// will catch and rewrap with it
91 /// will catch and rewrap with it
93 def rev(&self, node: PyBytes) -> PyResult<PyRevision> {
92 def rev(&self, node: PyBytes) -> PyResult<PyRevision> {
94 self.get_rev(py, node)?.ok_or_else(|| revlog_error(py))
93 self.get_rev(py, node)?.ok_or_else(|| revlog_error(py))
95 }
94 }
96
95
97 /// return True if the node exist in the index
96 /// return True if the node exist in the index
98 def has_node(&self, node: PyBytes) -> PyResult<bool> {
97 def has_node(&self, node: PyBytes) -> PyResult<bool> {
99 // TODO OPTIM we could avoid a needless conversion here,
98 // TODO OPTIM we could avoid a needless conversion here,
100 // to do when scaffolding for pure Rust switch is removed,
99 // to do when scaffolding for pure Rust switch is removed,
101 // as `get_rev()` currently does the necessary assertions
100 // as `get_rev()` currently does the necessary assertions
102 self.get_rev(py, node).map(|opt| opt.is_some())
101 self.get_rev(py, node).map(|opt| opt.is_some())
103 }
102 }
104
103
105 /// find length of shortest hex nodeid of a binary ID
104 /// find length of shortest hex nodeid of a binary ID
106 def shortest(&self, node: PyBytes) -> PyResult<usize> {
105 def shortest(&self, node: PyBytes) -> PyResult<usize> {
107 let opt = self.get_nodetree(py)?.borrow();
106 let opt = self.get_nodetree(py)?.borrow();
108 let nt = opt.as_ref().unwrap();
107 let nt = opt.as_ref().unwrap();
109 let idx = &*self.cindex(py).borrow();
108 let idx = &*self.cindex(py).borrow();
110 match nt.unique_prefix_len_node(idx, &node_from_py_bytes(py, &node)?)
109 match nt.unique_prefix_len_node(idx, &node_from_py_bytes(py, &node)?)
111 {
110 {
112 Ok(Some(l)) => Ok(l),
111 Ok(Some(l)) => Ok(l),
113 Ok(None) => Err(revlog_error(py)),
112 Ok(None) => Err(revlog_error(py)),
114 Err(e) => Err(nodemap_error(py, e)),
113 Err(e) => Err(nodemap_error(py, e)),
115 }
114 }
116 }
115 }
117
116
118 def partialmatch(&self, node: PyObject) -> PyResult<Option<PyBytes>> {
117 def partialmatch(&self, node: PyObject) -> PyResult<Option<PyBytes>> {
119 let opt = self.get_nodetree(py)?.borrow();
118 let opt = self.get_nodetree(py)?.borrow();
120 let nt = opt.as_ref().unwrap();
119 let nt = opt.as_ref().unwrap();
121 let idx = &*self.cindex(py).borrow();
120 let idx = &*self.cindex(py).borrow();
122
121
123 let node_as_string = if cfg!(feature = "python3-sys") {
122 let node_as_string = if cfg!(feature = "python3-sys") {
124 node.cast_as::<PyString>(py)?.to_string(py)?.to_string()
123 node.cast_as::<PyString>(py)?.to_string(py)?.to_string()
125 }
124 }
126 else {
125 else {
127 let node = node.extract::<PyBytes>(py)?;
126 let node = node.extract::<PyBytes>(py)?;
128 String::from_utf8_lossy(node.data(py)).to_string()
127 String::from_utf8_lossy(node.data(py)).to_string()
129 };
128 };
130
129
131 let prefix = NodePrefix::from_hex(&node_as_string)
130 let prefix = NodePrefix::from_hex(&node_as_string)
132 .map_err(|_| PyErr::new::<ValueError, _>(
131 .map_err(|_| PyErr::new::<ValueError, _>(
133 py, format!("Invalid node or prefix '{}'", node_as_string))
132 py, format!("Invalid node or prefix '{}'", node_as_string))
134 )?;
133 )?;
135
134
136 nt.find_bin(idx, prefix)
135 nt.find_bin(idx, prefix)
137 // TODO make an inner API returning the node directly
136 // TODO make an inner API returning the node directly
138 .map(|opt| opt.map(
137 .map(|opt| opt.map(
139 |rev| PyBytes::new(py, idx.node(rev).unwrap().as_bytes())))
138 |rev| PyBytes::new(py, idx.node(rev).unwrap().as_bytes())))
140 .map_err(|e| nodemap_error(py, e))
139 .map_err(|e| nodemap_error(py, e))
141
140
142 }
141 }
143
142
144 /// append an index entry
143 /// append an index entry
145 def append(&self, tup: PyTuple) -> PyResult<PyObject> {
144 def append(&self, tup: PyTuple) -> PyResult<PyObject> {
146 if tup.len(py) < 8 {
145 if tup.len(py) < 8 {
147 // this is better than the panic promised by tup.get_item()
146 // this is better than the panic promised by tup.get_item()
148 return Err(
147 return Err(
149 PyErr::new::<IndexError, _>(py, "tuple index out of range"))
148 PyErr::new::<IndexError, _>(py, "tuple index out of range"))
150 }
149 }
151 let node_bytes = tup.get_item(py, 7).extract(py)?;
150 let node_bytes = tup.get_item(py, 7).extract(py)?;
152 let node = node_from_py_object(py, &node_bytes)?;
151 let node = node_from_py_object(py, &node_bytes)?;
153
152
154 let rev = self.len(py)? as BaseRevision;
153 let rev = self.len(py)? as BaseRevision;
155 let mut idx = self.cindex(py).borrow_mut();
154 let mut idx = self.cindex(py).borrow_mut();
156
155
157 // This is ok since we will just add the revision to the index
156 // This is ok since we will just add the revision to the index
158 let rev = Revision(rev);
157 let rev = Revision(rev);
159 idx.append(py, tup.clone_ref(py))?;
158 idx.append(py, tup.clone_ref(py))?;
160 self.index(py)
159 self.index(py)
161 .borrow_mut()
160 .borrow_mut()
162 .append(py_tuple_to_revision_data_params(py, tup)?)
161 .append(py_tuple_to_revision_data_params(py, tup)?)
163 .unwrap();
162 .unwrap();
164 self.get_nodetree(py)?.borrow_mut().as_mut().unwrap()
163 self.get_nodetree(py)?.borrow_mut().as_mut().unwrap()
165 .insert(&*idx, &node, rev)
164 .insert(&*idx, &node, rev)
166 .map_err(|e| nodemap_error(py, e))?;
165 .map_err(|e| nodemap_error(py, e))?;
167 Ok(py.None())
166 Ok(py.None())
168 }
167 }
169
168
170 def __delitem__(&self, key: PyObject) -> PyResult<()> {
169 def __delitem__(&self, key: PyObject) -> PyResult<()> {
171 // __delitem__ is both for `del idx[r]` and `del idx[r1:r2]`
170 // __delitem__ is both for `del idx[r]` and `del idx[r1:r2]`
172 self.cindex(py).borrow().inner().del_item(py, &key)?;
171 self.cindex(py).borrow().inner().del_item(py, &key)?;
173 let start = key.getattr(py, "start")?;
172 let start = key.getattr(py, "start")?;
174 let start = UncheckedRevision(start.extract(py)?);
173 let start = UncheckedRevision(start.extract(py)?);
175 let start = self.index(py)
174 let start = self.index(py)
176 .borrow()
175 .borrow()
177 .check_revision(start)
176 .check_revision(start)
178 .ok_or_else(|| {
177 .ok_or_else(|| {
179 nodemap_error(py, NodeMapError::RevisionNotInIndex(start))
178 nodemap_error(py, NodeMapError::RevisionNotInIndex(start))
180 })?;
179 })?;
181 self.index(py).borrow_mut().remove(start).unwrap();
180 self.index(py).borrow_mut().remove(start).unwrap();
182 let mut opt = self.get_nodetree(py)?.borrow_mut();
181 let mut opt = self.get_nodetree(py)?.borrow_mut();
183 let nt = opt.as_mut().unwrap();
182 let nt = opt.as_mut().unwrap();
184 nt.invalidate_all();
183 nt.invalidate_all();
185 self.fill_nodemap(py, nt)?;
184 self.fill_nodemap(py, nt)?;
186 Ok(())
185 Ok(())
187 }
186 }
188
187
189 //
188 //
190 // Reforwarded C index API
189 // Reforwarded C index API
191 //
190 //
192
191
193 // index_methods (tp_methods). Same ordering as in revlog.c
192 // index_methods (tp_methods). Same ordering as in revlog.c
194
193
195 /// return the gca set of the given revs
194 /// return the gca set of the given revs
196 def ancestors(&self, *args, **kw) -> PyResult<PyObject> {
195 def ancestors(&self, *args, **kw) -> PyResult<PyObject> {
197 self.call_cindex(py, "ancestors", args, kw)
196 self.call_cindex(py, "ancestors", args, kw)
198 }
197 }
199
198
200 /// return the heads of the common ancestors of the given revs
199 /// return the heads of the common ancestors of the given revs
201 def commonancestorsheads(&self, *args, **kw) -> PyResult<PyObject> {
200 def commonancestorsheads(&self, *args, **kw) -> PyResult<PyObject> {
202 self.call_cindex(py, "commonancestorsheads", args, kw)
201 self.call_cindex(py, "commonancestorsheads", args, kw)
203 }
202 }
204
203
205 /// Clear the index caches and inner py_class data.
204 /// Clear the index caches and inner py_class data.
206 /// It is Python's responsibility to call `update_nodemap_data` again.
205 /// It is Python's responsibility to call `update_nodemap_data` again.
207 def clearcaches(&self, *args, **kw) -> PyResult<PyObject> {
206 def clearcaches(&self, *args, **kw) -> PyResult<PyObject> {
208 self.nt(py).borrow_mut().take();
207 self.nt(py).borrow_mut().take();
209 self.docket(py).borrow_mut().take();
208 self.docket(py).borrow_mut().take();
210 self.nodemap_mmap(py).borrow_mut().take();
209 self.nodemap_mmap(py).borrow_mut().take();
211 self.index(py).borrow_mut().clear_caches();
210 self.index(py).borrow_mut().clear_caches();
212 self.call_cindex(py, "clearcaches", args, kw)
211 self.call_cindex(py, "clearcaches", args, kw)
213 }
212 }
214
213
215 /// return the raw binary string representing a revision
214 /// return the raw binary string representing a revision
216 def entry_binary(&self, *args, **kw) -> PyResult<PyObject> {
215 def entry_binary(&self, *args, **kw) -> PyResult<PyObject> {
217 let rindex = self.index(py).borrow();
216 let rindex = self.index(py).borrow();
218 let rev = UncheckedRevision(args.get_item(py, 0).extract(py)?);
217 let rev = UncheckedRevision(args.get_item(py, 0).extract(py)?);
219 let rust_bytes = rindex.check_revision(rev).and_then(
218 let rust_bytes = rindex.check_revision(rev).and_then(
220 |r| rindex.entry_binary(r))
219 |r| rindex.entry_binary(r))
221 .ok_or_else(|| rev_not_in_index(py, rev))?;
220 .ok_or_else(|| rev_not_in_index(py, rev))?;
222 let rust_res = PyBytes::new(py, rust_bytes).into_object();
221 let rust_res = PyBytes::new(py, rust_bytes).into_object();
223
222
224 let c_res = self.call_cindex(py, "entry_binary", args, kw)?;
223 let c_res = self.call_cindex(py, "entry_binary", args, kw)?;
225 assert_py_eq(py, "entry_binary", &rust_res, &c_res)?;
224 assert_py_eq(py, "entry_binary", &rust_res, &c_res)?;
226 Ok(rust_res)
225 Ok(rust_res)
227 }
226 }
228
227
229 /// return a binary packed version of the header
228 /// return a binary packed version of the header
230 def pack_header(&self, *args, **kw) -> PyResult<PyObject> {
229 def pack_header(&self, *args, **kw) -> PyResult<PyObject> {
231 let rindex = self.index(py).borrow();
230 let rindex = self.index(py).borrow();
232 let packed = rindex.pack_header(args.get_item(py, 0).extract(py)?);
231 let packed = rindex.pack_header(args.get_item(py, 0).extract(py)?);
233 let rust_res = PyBytes::new(py, &packed).into_object();
232 let rust_res = PyBytes::new(py, &packed).into_object();
234
233
235 let c_res = self.call_cindex(py, "pack_header", args, kw)?;
234 let c_res = self.call_cindex(py, "pack_header", args, kw)?;
236 assert_py_eq(py, "pack_header", &rust_res, &c_res)?;
235 assert_py_eq(py, "pack_header", &rust_res, &c_res)?;
237 Ok(rust_res)
236 Ok(rust_res)
238 }
237 }
239
238
240 /// get an index entry
241 def get(&self, *args, **kw) -> PyResult<PyObject> {
242 self.call_cindex(py, "get", args, kw)
243 }
244
245 /// compute phases
239 /// compute phases
246 def computephasesmapsets(&self, *args, **kw) -> PyResult<PyObject> {
240 def computephasesmapsets(&self, *args, **kw) -> PyResult<PyObject> {
247 self.call_cindex(py, "computephasesmapsets", args, kw)
241 self.call_cindex(py, "computephasesmapsets", args, kw)
248 }
242 }
249
243
250 /// reachableroots
244 /// reachableroots
251 def reachableroots2(&self, *args, **kw) -> PyResult<PyObject> {
245 def reachableroots2(&self, *args, **kw) -> PyResult<PyObject> {
252 self.call_cindex(py, "reachableroots2", args, kw)
246 self.call_cindex(py, "reachableroots2", args, kw)
253 }
247 }
254
248
255 /// get head revisions
249 /// get head revisions
256 def headrevs(&self, *args, **kw) -> PyResult<PyObject> {
250 def headrevs(&self, *args, **kw) -> PyResult<PyObject> {
257 self.call_cindex(py, "headrevs", args, kw)
251 self.call_cindex(py, "headrevs", args, kw)
258 }
252 }
259
253
260 /// get filtered head revisions
254 /// get filtered head revisions
261 def headrevsfiltered(&self, *args, **kw) -> PyResult<PyObject> {
255 def headrevsfiltered(&self, *args, **kw) -> PyResult<PyObject> {
262 self.call_cindex(py, "headrevsfiltered", args, kw)
256 self.call_cindex(py, "headrevsfiltered", args, kw)
263 }
257 }
264
258
265 /// True if the object is a snapshot
259 /// True if the object is a snapshot
266 def issnapshot(&self, *args, **kw) -> PyResult<PyObject> {
260 def issnapshot(&self, *args, **kw) -> PyResult<PyObject> {
267 self.call_cindex(py, "issnapshot", args, kw)
261 self.call_cindex(py, "issnapshot", args, kw)
268 }
262 }
269
263
270 /// Gather snapshot data in a cache dict
264 /// Gather snapshot data in a cache dict
271 def findsnapshots(&self, *args, **kw) -> PyResult<PyObject> {
265 def findsnapshots(&self, *args, **kw) -> PyResult<PyObject> {
272 self.call_cindex(py, "findsnapshots", args, kw)
266 self.call_cindex(py, "findsnapshots", args, kw)
273 }
267 }
274
268
275 /// determine revisions with deltas to reconstruct fulltext
269 /// determine revisions with deltas to reconstruct fulltext
276 def deltachain(&self, *args, **kw) -> PyResult<PyObject> {
270 def deltachain(&self, *args, **kw) -> PyResult<PyObject> {
277 self.call_cindex(py, "deltachain", args, kw)
271 self.call_cindex(py, "deltachain", args, kw)
278 }
272 }
279
273
280 /// slice planned chunk read to reach a density threshold
274 /// slice planned chunk read to reach a density threshold
281 def slicechunktodensity(&self, *args, **kw) -> PyResult<PyObject> {
275 def slicechunktodensity(&self, *args, **kw) -> PyResult<PyObject> {
282 self.call_cindex(py, "slicechunktodensity", args, kw)
276 self.call_cindex(py, "slicechunktodensity", args, kw)
283 }
277 }
284
278
285 /// stats for the index
279 /// stats for the index
286 def stats(&self, *args, **kw) -> PyResult<PyObject> {
280 def stats(&self, *args, **kw) -> PyResult<PyObject> {
287 self.call_cindex(py, "stats", args, kw)
281 self.call_cindex(py, "stats", args, kw)
288 }
282 }
289
283
290 // index_sequence_methods and index_mapping_methods.
284 // index_sequence_methods and index_mapping_methods.
291 //
285 //
292 // Since we call back through the high level Python API,
286 // Since we call back through the high level Python API,
293 // there's no point making a distinction between index_get
287 // there's no point making a distinction between index_get
294 // and index_getitem.
288 // and index_getitem.
289 // gracinet 2023: this above is no longer true for the pure Rust impl
295
290
296 def __len__(&self) -> PyResult<usize> {
291 def __len__(&self) -> PyResult<usize> {
297 self.len(py)
292 self.len(py)
298 }
293 }
299
294
300 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
295 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
296 let rust_res = self.inner_getitem(py, key.clone_ref(py))?;
297
301 // this conversion seems needless, but that's actually because
298 // this conversion seems needless, but that's actually because
302 // `index_getitem` does not handle conversion from PyLong,
299 // `index_getitem` does not handle conversion from PyLong,
303 // which expressions such as [e for e in index] internally use.
300 // which expressions such as [e for e in index] internally use.
304 // Note that we don't seem to have a direct way to call
301 // Note that we don't seem to have a direct way to call
305 // PySequence_GetItem (does the job), which would possibly be better
302 // PySequence_GetItem (does the job), which would possibly be better
306 // for performance
303 // for performance
307 let key = match key.extract::<i32>(py) {
304 // gracinet 2023: the above comment can be removed when we use
305 // the pure Rust impl only. Note also that `key` can be a binary
306 // node id.
307 let c_key = match key.extract::<BaseRevision>(py) {
308 Ok(rev) => rev.to_py_object(py).into_object(),
308 Ok(rev) => rev.to_py_object(py).into_object(),
309 Err(_) => key,
309 Err(_) => key,
310 };
310 };
311 self.cindex(py).borrow().inner().get_item(py, key)
311 let c_res = self.cindex(py).borrow().inner().get_item(py, c_key)?;
312
313 assert_py_eq(py, "__getitem__", &rust_res, &c_res)?;
314 Ok(rust_res)
312 }
315 }
313
316
314 def __contains__(&self, item: PyObject) -> PyResult<bool> {
317 def __contains__(&self, item: PyObject) -> PyResult<bool> {
315 // ObjectProtocol does not seem to provide contains(), so
318 // ObjectProtocol does not seem to provide contains(), so
316 // this is an equivalent implementation of the index_contains()
319 // this is an equivalent implementation of the index_contains()
317 // defined in revlog.c
320 // defined in revlog.c
318 let cindex = self.cindex(py).borrow();
321 let cindex = self.cindex(py).borrow();
319 match item.extract::<i32>(py) {
322 match item.extract::<i32>(py) {
320 Ok(rev) => {
323 Ok(rev) => {
321 Ok(rev >= -1 && rev < self.len(py)? as BaseRevision)
324 Ok(rev >= -1 && rev < self.len(py)? as BaseRevision)
322 }
325 }
323 Err(_) => {
326 Err(_) => {
324 cindex.inner().call_method(
327 cindex.inner().call_method(
325 py,
328 py,
326 "has_node",
329 "has_node",
327 PyTuple::new(py, &[item]),
330 PyTuple::new(py, &[item]),
328 None)?
331 None)?
329 .extract(py)
332 .extract(py)
330 }
333 }
331 }
334 }
332 }
335 }
333
336
334 def nodemap_data_all(&self) -> PyResult<PyBytes> {
337 def nodemap_data_all(&self) -> PyResult<PyBytes> {
335 self.inner_nodemap_data_all(py)
338 self.inner_nodemap_data_all(py)
336 }
339 }
337
340
338 def nodemap_data_incremental(&self) -> PyResult<PyObject> {
341 def nodemap_data_incremental(&self) -> PyResult<PyObject> {
339 self.inner_nodemap_data_incremental(py)
342 self.inner_nodemap_data_incremental(py)
340 }
343 }
341 def update_nodemap_data(
344 def update_nodemap_data(
342 &self,
345 &self,
343 docket: PyObject,
346 docket: PyObject,
344 nm_data: PyObject
347 nm_data: PyObject
345 ) -> PyResult<PyObject> {
348 ) -> PyResult<PyObject> {
346 self.inner_update_nodemap_data(py, docket, nm_data)
349 self.inner_update_nodemap_data(py, docket, nm_data)
347 }
350 }
348
351
349 @property
352 @property
350 def entry_size(&self) -> PyResult<PyInt> {
353 def entry_size(&self) -> PyResult<PyInt> {
351 self.cindex(py).borrow().inner().getattr(py, "entry_size")?.extract::<PyInt>(py)
354 self.cindex(py).borrow().inner().getattr(py, "entry_size")?.extract::<PyInt>(py)
352 }
355 }
353
356
354 @property
357 @property
355 def rust_ext_compat(&self) -> PyResult<PyInt> {
358 def rust_ext_compat(&self) -> PyResult<PyInt> {
356 self.cindex(py).borrow().inner().getattr(py, "rust_ext_compat")?.extract::<PyInt>(py)
359 self.cindex(py).borrow().inner().getattr(py, "rust_ext_compat")?.extract::<PyInt>(py)
357 }
360 }
358
361
359 });
362 });
360
363
361 /// Take a (potentially) mmap'ed buffer, and return the underlying Python
364 /// Take a (potentially) mmap'ed buffer, and return the underlying Python
362 /// buffer along with the Rust slice into said buffer. We need to keep the
365 /// buffer along with the Rust slice into said buffer. We need to keep the
363 /// Python buffer around, otherwise we'd get a dangling pointer once the buffer
366 /// Python buffer around, otherwise we'd get a dangling pointer once the buffer
364 /// is freed from Python's side.
367 /// is freed from Python's side.
365 ///
368 ///
366 /// # Safety
369 /// # Safety
367 ///
370 ///
368 /// The caller must make sure that the buffer is kept around for at least as
371 /// The caller must make sure that the buffer is kept around for at least as
369 /// long as the slice.
372 /// long as the slice.
370 #[deny(unsafe_op_in_unsafe_fn)]
373 #[deny(unsafe_op_in_unsafe_fn)]
371 unsafe fn mmap_keeparound(
374 unsafe fn mmap_keeparound(
372 py: Python,
375 py: Python,
373 data: PyObject,
376 data: PyObject,
374 ) -> PyResult<(
377 ) -> PyResult<(
375 PyBuffer,
378 PyBuffer,
376 Box<dyn std::ops::Deref<Target = [u8]> + Send + 'static>,
379 Box<dyn std::ops::Deref<Target = [u8]> + Send + 'static>,
377 )> {
380 )> {
378 let buf = PyBuffer::get(py, &data)?;
381 let buf = PyBuffer::get(py, &data)?;
379 let len = buf.item_count();
382 let len = buf.item_count();
380
383
381 // Build a slice from the mmap'ed buffer data
384 // Build a slice from the mmap'ed buffer data
382 let cbuf = buf.buf_ptr();
385 let cbuf = buf.buf_ptr();
383 let bytes = if std::mem::size_of::<u8>() == buf.item_size()
386 let bytes = if std::mem::size_of::<u8>() == buf.item_size()
384 && buf.is_c_contiguous()
387 && buf.is_c_contiguous()
385 && u8::is_compatible_format(buf.format())
388 && u8::is_compatible_format(buf.format())
386 {
389 {
387 unsafe { std::slice::from_raw_parts(cbuf as *const u8, len) }
390 unsafe { std::slice::from_raw_parts(cbuf as *const u8, len) }
388 } else {
391 } else {
389 return Err(PyErr::new::<ValueError, _>(
392 return Err(PyErr::new::<ValueError, _>(
390 py,
393 py,
391 "Nodemap data buffer has an invalid memory representation"
394 "Nodemap data buffer has an invalid memory representation"
392 .to_string(),
395 .to_string(),
393 ));
396 ));
394 };
397 };
395
398
396 Ok((buf, Box::new(bytes)))
399 Ok((buf, Box::new(bytes)))
397 }
400 }
398
401
399 fn py_tuple_to_revision_data_params(
402 fn py_tuple_to_revision_data_params(
400 py: Python,
403 py: Python,
401 tuple: PyTuple,
404 tuple: PyTuple,
402 ) -> PyResult<RevisionDataParams> {
405 ) -> PyResult<RevisionDataParams> {
403 if tuple.len(py) < 8 {
406 if tuple.len(py) < 8 {
404 // this is better than the panic promised by tup.get_item()
407 // this is better than the panic promised by tup.get_item()
405 return Err(PyErr::new::<IndexError, _>(
408 return Err(PyErr::new::<IndexError, _>(
406 py,
409 py,
407 "tuple index out of range",
410 "tuple index out of range",
408 ));
411 ));
409 }
412 }
410 let offset_or_flags: u64 = tuple.get_item(py, 0).extract(py)?;
413 let offset_or_flags: u64 = tuple.get_item(py, 0).extract(py)?;
411 let node_id = tuple
414 let node_id = tuple
412 .get_item(py, 7)
415 .get_item(py, 7)
413 .extract::<PyBytes>(py)?
416 .extract::<PyBytes>(py)?
414 .data(py)
417 .data(py)
415 .try_into()
418 .try_into()
416 .unwrap();
419 .unwrap();
417 let flags = (offset_or_flags & 0xFFFF) as u16;
420 let flags = (offset_or_flags & 0xFFFF) as u16;
418 let data_offset = offset_or_flags >> 16;
421 let data_offset = offset_or_flags >> 16;
419 Ok(RevisionDataParams {
422 Ok(RevisionDataParams {
420 flags,
423 flags,
421 data_offset,
424 data_offset,
422 data_compressed_length: tuple.get_item(py, 1).extract(py)?,
425 data_compressed_length: tuple.get_item(py, 1).extract(py)?,
423 data_uncompressed_length: tuple.get_item(py, 2).extract(py)?,
426 data_uncompressed_length: tuple.get_item(py, 2).extract(py)?,
424 data_delta_base: tuple.get_item(py, 3).extract(py)?,
427 data_delta_base: tuple.get_item(py, 3).extract(py)?,
425 link_rev: tuple.get_item(py, 4).extract(py)?,
428 link_rev: tuple.get_item(py, 4).extract(py)?,
426 parent_rev_1: tuple.get_item(py, 5).extract(py)?,
429 parent_rev_1: tuple.get_item(py, 5).extract(py)?,
427 parent_rev_2: tuple.get_item(py, 6).extract(py)?,
430 parent_rev_2: tuple.get_item(py, 6).extract(py)?,
428 node_id,
431 node_id,
429 _sidedata_offset: 0,
432 ..Default::default()
430 _sidedata_compressed_length: 0,
431 data_compression_mode: COMPRESSION_MODE_INLINE,
432 _sidedata_compression_mode: COMPRESSION_MODE_INLINE,
433 _rank: -1,
434 })
433 })
435 }
434 }
435 fn revision_data_params_to_py_tuple(
436 py: Python,
437 params: RevisionDataParams,
438 ) -> PyTuple {
439 PyTuple::new(
440 py,
441 &[
442 params.data_offset.into_py_object(py).into_object(),
443 params
444 .data_compressed_length
445 .into_py_object(py)
446 .into_object(),
447 params
448 .data_uncompressed_length
449 .into_py_object(py)
450 .into_object(),
451 params.data_delta_base.into_py_object(py).into_object(),
452 params.link_rev.into_py_object(py).into_object(),
453 params.parent_rev_1.into_py_object(py).into_object(),
454 params.parent_rev_2.into_py_object(py).into_object(),
455 PyBytes::new(py, &params.node_id)
456 .into_py_object(py)
457 .into_object(),
458 params._sidedata_offset.into_py_object(py).into_object(),
459 params
460 ._sidedata_compressed_length
461 .into_py_object(py)
462 .into_object(),
463 params
464 .data_compression_mode
465 .into_py_object(py)
466 .into_object(),
467 params
468 ._sidedata_compression_mode
469 .into_py_object(py)
470 .into_object(),
471 params._rank.into_py_object(py).into_object(),
472 ],
473 )
474 }
436
475
437 impl MixedIndex {
476 impl MixedIndex {
438 fn new(
477 fn new(
439 py: Python,
478 py: Python,
440 cindex: PyObject,
479 cindex: PyObject,
441 data: PyObject,
480 data: PyObject,
442 header: u32,
481 header: u32,
443 ) -> PyResult<MixedIndex> {
482 ) -> PyResult<MixedIndex> {
444 // Safety: we keep the buffer around inside the class as `index_mmap`
483 // Safety: we keep the buffer around inside the class as `index_mmap`
445 let (buf, bytes) = unsafe { mmap_keeparound(py, data)? };
484 let (buf, bytes) = unsafe { mmap_keeparound(py, data)? };
446
485
447 Self::create_instance(
486 Self::create_instance(
448 py,
487 py,
449 RefCell::new(cindex::Index::new(py, cindex)?),
488 RefCell::new(cindex::Index::new(py, cindex)?),
450 RefCell::new(
489 RefCell::new(
451 hg::index::Index::new(
490 hg::index::Index::new(
452 bytes,
491 bytes,
453 IndexHeader::parse(&header.to_be_bytes())
492 IndexHeader::parse(&header.to_be_bytes())
454 .expect("default header is broken")
493 .expect("default header is broken")
455 .unwrap(),
494 .unwrap(),
456 )
495 )
457 .unwrap(),
496 .unwrap(),
458 ),
497 ),
459 RefCell::new(None),
498 RefCell::new(None),
460 RefCell::new(None),
499 RefCell::new(None),
461 RefCell::new(None),
500 RefCell::new(None),
462 RefCell::new(Some(buf)),
501 RefCell::new(Some(buf)),
463 )
502 )
464 }
503 }
465
504
466 fn len(&self, py: Python) -> PyResult<usize> {
505 fn len(&self, py: Python) -> PyResult<usize> {
467 let rust_index_len = self.index(py).borrow().len();
506 let rust_index_len = self.index(py).borrow().len();
468 let cindex_len = self.cindex(py).borrow().inner().len(py)?;
507 let cindex_len = self.cindex(py).borrow().inner().len(py)?;
469 assert_eq!(rust_index_len, cindex_len);
508 assert_eq!(rust_index_len, cindex_len);
470 Ok(cindex_len)
509 Ok(cindex_len)
471 }
510 }
472
511
473 /// This is scaffolding at this point, but it could also become
512 /// This is scaffolding at this point, but it could also become
474 /// a way to start a persistent nodemap or perform a
513 /// a way to start a persistent nodemap or perform a
475 /// vacuum / repack operation
514 /// vacuum / repack operation
476 fn fill_nodemap(
515 fn fill_nodemap(
477 &self,
516 &self,
478 py: Python,
517 py: Python,
479 nt: &mut NodeTree,
518 nt: &mut NodeTree,
480 ) -> PyResult<PyObject> {
519 ) -> PyResult<PyObject> {
481 let index = self.cindex(py).borrow();
520 let index = self.cindex(py).borrow();
482 for r in 0..self.len(py)? {
521 for r in 0..self.len(py)? {
483 let rev = Revision(r as BaseRevision);
522 let rev = Revision(r as BaseRevision);
484 // in this case node() won't ever return None
523 // in this case node() won't ever return None
485 nt.insert(&*index, index.node(rev).unwrap(), rev)
524 nt.insert(&*index, index.node(rev).unwrap(), rev)
486 .map_err(|e| nodemap_error(py, e))?
525 .map_err(|e| nodemap_error(py, e))?
487 }
526 }
488 Ok(py.None())
527 Ok(py.None())
489 }
528 }
490
529
491 fn get_nodetree<'a>(
530 fn get_nodetree<'a>(
492 &'a self,
531 &'a self,
493 py: Python<'a>,
532 py: Python<'a>,
494 ) -> PyResult<&'a RefCell<Option<NodeTree>>> {
533 ) -> PyResult<&'a RefCell<Option<NodeTree>>> {
495 if self.nt(py).borrow().is_none() {
534 if self.nt(py).borrow().is_none() {
496 let readonly = Box::<Vec<_>>::default();
535 let readonly = Box::<Vec<_>>::default();
497 let mut nt = NodeTree::load_bytes(readonly, 0);
536 let mut nt = NodeTree::load_bytes(readonly, 0);
498 self.fill_nodemap(py, &mut nt)?;
537 self.fill_nodemap(py, &mut nt)?;
499 self.nt(py).borrow_mut().replace(nt);
538 self.nt(py).borrow_mut().replace(nt);
500 }
539 }
501 Ok(self.nt(py))
540 Ok(self.nt(py))
502 }
541 }
503
542
504 /// forward a method call to the underlying C index
543 /// forward a method call to the underlying C index
505 fn call_cindex(
544 fn call_cindex(
506 &self,
545 &self,
507 py: Python,
546 py: Python,
508 name: &str,
547 name: &str,
509 args: &PyTuple,
548 args: &PyTuple,
510 kwargs: Option<&PyDict>,
549 kwargs: Option<&PyDict>,
511 ) -> PyResult<PyObject> {
550 ) -> PyResult<PyObject> {
512 self.cindex(py)
551 self.cindex(py)
513 .borrow()
552 .borrow()
514 .inner()
553 .inner()
515 .call_method(py, name, args, kwargs)
554 .call_method(py, name, args, kwargs)
516 }
555 }
517
556
518 pub fn clone_cindex(&self, py: Python) -> cindex::Index {
557 pub fn clone_cindex(&self, py: Python) -> cindex::Index {
519 self.cindex(py).borrow().clone_ref(py)
558 self.cindex(py).borrow().clone_ref(py)
520 }
559 }
521
560
522 /// Returns the full nodemap bytes to be written as-is to disk
561 /// Returns the full nodemap bytes to be written as-is to disk
523 fn inner_nodemap_data_all(&self, py: Python) -> PyResult<PyBytes> {
562 fn inner_nodemap_data_all(&self, py: Python) -> PyResult<PyBytes> {
524 let nodemap = self.get_nodetree(py)?.borrow_mut().take().unwrap();
563 let nodemap = self.get_nodetree(py)?.borrow_mut().take().unwrap();
525 let (readonly, bytes) = nodemap.into_readonly_and_added_bytes();
564 let (readonly, bytes) = nodemap.into_readonly_and_added_bytes();
526
565
527 // If there's anything readonly, we need to build the data again from
566 // If there's anything readonly, we need to build the data again from
528 // scratch
567 // scratch
529 let bytes = if readonly.len() > 0 {
568 let bytes = if readonly.len() > 0 {
530 let mut nt = NodeTree::load_bytes(Box::<Vec<_>>::default(), 0);
569 let mut nt = NodeTree::load_bytes(Box::<Vec<_>>::default(), 0);
531 self.fill_nodemap(py, &mut nt)?;
570 self.fill_nodemap(py, &mut nt)?;
532
571
533 let (readonly, bytes) = nt.into_readonly_and_added_bytes();
572 let (readonly, bytes) = nt.into_readonly_and_added_bytes();
534 assert_eq!(readonly.len(), 0);
573 assert_eq!(readonly.len(), 0);
535
574
536 bytes
575 bytes
537 } else {
576 } else {
538 bytes
577 bytes
539 };
578 };
540
579
541 let bytes = PyBytes::new(py, &bytes);
580 let bytes = PyBytes::new(py, &bytes);
542 Ok(bytes)
581 Ok(bytes)
543 }
582 }
544
583
545 /// Returns the last saved docket along with the size of any changed data
584 /// Returns the last saved docket along with the size of any changed data
546 /// (in number of blocks), and said data as bytes.
585 /// (in number of blocks), and said data as bytes.
547 fn inner_nodemap_data_incremental(
586 fn inner_nodemap_data_incremental(
548 &self,
587 &self,
549 py: Python,
588 py: Python,
550 ) -> PyResult<PyObject> {
589 ) -> PyResult<PyObject> {
551 let docket = self.docket(py).borrow();
590 let docket = self.docket(py).borrow();
552 let docket = match docket.as_ref() {
591 let docket = match docket.as_ref() {
553 Some(d) => d,
592 Some(d) => d,
554 None => return Ok(py.None()),
593 None => return Ok(py.None()),
555 };
594 };
556
595
557 let node_tree = self.get_nodetree(py)?.borrow_mut().take().unwrap();
596 let node_tree = self.get_nodetree(py)?.borrow_mut().take().unwrap();
558 let masked_blocks = node_tree.masked_readonly_blocks();
597 let masked_blocks = node_tree.masked_readonly_blocks();
559 let (_, data) = node_tree.into_readonly_and_added_bytes();
598 let (_, data) = node_tree.into_readonly_and_added_bytes();
560 let changed = masked_blocks * std::mem::size_of::<Block>();
599 let changed = masked_blocks * std::mem::size_of::<Block>();
561
600
562 Ok((docket, changed, PyBytes::new(py, &data))
601 Ok((docket, changed, PyBytes::new(py, &data))
563 .to_py_object(py)
602 .to_py_object(py)
564 .into_object())
603 .into_object())
565 }
604 }
566
605
567 /// Update the nodemap from the new (mmaped) data.
606 /// Update the nodemap from the new (mmaped) data.
568 /// The docket is kept as a reference for later incremental calls.
607 /// The docket is kept as a reference for later incremental calls.
569 fn inner_update_nodemap_data(
608 fn inner_update_nodemap_data(
570 &self,
609 &self,
571 py: Python,
610 py: Python,
572 docket: PyObject,
611 docket: PyObject,
573 nm_data: PyObject,
612 nm_data: PyObject,
574 ) -> PyResult<PyObject> {
613 ) -> PyResult<PyObject> {
575 // Safety: we keep the buffer around inside the class as `nodemap_mmap`
614 // Safety: we keep the buffer around inside the class as `nodemap_mmap`
576 let (buf, bytes) = unsafe { mmap_keeparound(py, nm_data)? };
615 let (buf, bytes) = unsafe { mmap_keeparound(py, nm_data)? };
577 let len = buf.item_count();
616 let len = buf.item_count();
578 self.nodemap_mmap(py).borrow_mut().replace(buf);
617 self.nodemap_mmap(py).borrow_mut().replace(buf);
579
618
580 let mut nt = NodeTree::load_bytes(bytes, len);
619 let mut nt = NodeTree::load_bytes(bytes, len);
581
620
582 let data_tip = docket
621 let data_tip = docket
583 .getattr(py, "tip_rev")?
622 .getattr(py, "tip_rev")?
584 .extract::<BaseRevision>(py)?
623 .extract::<BaseRevision>(py)?
585 .into();
624 .into();
586 self.docket(py).borrow_mut().replace(docket.clone_ref(py));
625 self.docket(py).borrow_mut().replace(docket.clone_ref(py));
587 let idx = self.cindex(py).borrow();
626 let idx = self.cindex(py).borrow();
588 let data_tip = idx.check_revision(data_tip).ok_or_else(|| {
627 let data_tip = idx.check_revision(data_tip).ok_or_else(|| {
589 nodemap_error(py, NodeMapError::RevisionNotInIndex(data_tip))
628 nodemap_error(py, NodeMapError::RevisionNotInIndex(data_tip))
590 })?;
629 })?;
591 let current_tip = idx.len();
630 let current_tip = idx.len();
592
631
593 for r in (data_tip.0 + 1)..current_tip as BaseRevision {
632 for r in (data_tip.0 + 1)..current_tip as BaseRevision {
594 let rev = Revision(r);
633 let rev = Revision(r);
595 // in this case node() won't ever return None
634 // in this case node() won't ever return None
596 nt.insert(&*idx, idx.node(rev).unwrap(), rev)
635 nt.insert(&*idx, idx.node(rev).unwrap(), rev)
597 .map_err(|e| nodemap_error(py, e))?
636 .map_err(|e| nodemap_error(py, e))?
598 }
637 }
599
638
600 *self.nt(py).borrow_mut() = Some(nt);
639 *self.nt(py).borrow_mut() = Some(nt);
601
640
602 Ok(py.None())
641 Ok(py.None())
603 }
642 }
643
644 fn inner_getitem(&self, py: Python, key: PyObject) -> PyResult<PyObject> {
645 let idx = self.index(py).borrow();
646 Ok(match key.extract::<BaseRevision>(py) {
647 Ok(key_as_int) => {
648 let entry_params = if key_as_int == NULL_REVISION.0 {
649 RevisionDataParams::default()
650 } else {
651 let rev = UncheckedRevision(key_as_int);
652 match idx.entry_as_params(rev) {
653 Some(e) => e,
654 None => {
655 return Err(PyErr::new::<IndexError, _>(
656 py,
657 "revlog index out of range",
658 ));
659 }
660 }
661 };
662 revision_data_params_to_py_tuple(py, entry_params)
663 .into_object()
664 }
665 _ => self.get_rev(py, key.extract::<PyBytes>(py)?)?.map_or_else(
666 || py.None(),
667 |py_rev| py_rev.into_py_object(py).into_object(),
668 ),
669 })
670 }
604 }
671 }
605
672
606 fn revlog_error(py: Python) -> PyErr {
673 fn revlog_error(py: Python) -> PyErr {
607 match py
674 match py
608 .import("mercurial.error")
675 .import("mercurial.error")
609 .and_then(|m| m.get(py, "RevlogError"))
676 .and_then(|m| m.get(py, "RevlogError"))
610 {
677 {
611 Err(e) => e,
678 Err(e) => e,
612 Ok(cls) => PyErr::from_instance(
679 Ok(cls) => PyErr::from_instance(
613 py,
680 py,
614 cls.call(py, (py.None(),), None).ok().into_py_object(py),
681 cls.call(py, (py.None(),), None).ok().into_py_object(py),
615 ),
682 ),
616 }
683 }
617 }
684 }
618
685
619 fn nodemap_rev_not_in_index(py: Python, rev: UncheckedRevision) -> PyErr {
686 fn nodemap_rev_not_in_index(py: Python, rev: UncheckedRevision) -> PyErr {
620 PyErr::new::<ValueError, _>(
687 PyErr::new::<ValueError, _>(
621 py,
688 py,
622 format!(
689 format!(
623 "Inconsistency: Revision {} found in nodemap \
690 "Inconsistency: Revision {} found in nodemap \
624 is not in revlog index",
691 is not in revlog index",
625 rev
692 rev
626 ),
693 ),
627 )
694 )
628 }
695 }
629
696
630 fn rev_not_in_index(py: Python, rev: UncheckedRevision) -> PyErr {
697 fn rev_not_in_index(py: Python, rev: UncheckedRevision) -> PyErr {
631 PyErr::new::<ValueError, _>(
698 PyErr::new::<ValueError, _>(
632 py,
699 py,
633 format!("revlog index out of range: {}", rev),
700 format!("revlog index out of range: {}", rev),
634 )
701 )
635 }
702 }
636
703
637 /// Standard treatment of NodeMapError
704 /// Standard treatment of NodeMapError
638 fn nodemap_error(py: Python, err: NodeMapError) -> PyErr {
705 fn nodemap_error(py: Python, err: NodeMapError) -> PyErr {
639 match err {
706 match err {
640 NodeMapError::MultipleResults => revlog_error(py),
707 NodeMapError::MultipleResults => revlog_error(py),
641 NodeMapError::RevisionNotInIndex(r) => nodemap_rev_not_in_index(py, r),
708 NodeMapError::RevisionNotInIndex(r) => nodemap_rev_not_in_index(py, r),
642 }
709 }
643 }
710 }
644
711
645 fn assert_py_eq(
712 fn assert_py_eq(
646 py: Python,
713 py: Python,
647 method: &str,
714 method: &str,
648 rust: &PyObject,
715 rust: &PyObject,
649 c: &PyObject,
716 c: &PyObject,
650 ) -> PyResult<()> {
717 ) -> PyResult<()> {
651 let locals = PyDict::new(py);
718 let locals = PyDict::new(py);
652 locals.set_item(py, "rust".into_py_object(py).into_object(), rust)?;
719 locals.set_item(py, "rust".into_py_object(py).into_object(), rust)?;
653 locals.set_item(py, "c".into_py_object(py).into_object(), c)?;
720 locals.set_item(py, "c".into_py_object(py).into_object(), c)?;
654 let is_eq: PyBool =
721 let is_eq: PyBool =
655 py.eval("rust == c", None, Some(&locals))?.extract(py)?;
722 py.eval("rust == c", None, Some(&locals))?.extract(py)?;
656 assert!(
723 assert!(
657 is_eq.is_true(),
724 is_eq.is_true(),
658 "{} results differ. Rust: {:?} C: {:?}",
725 "{} results differ. Rust: {:?} C: {:?}",
659 method,
726 method,
660 rust,
727 rust,
661 c
728 c
662 );
729 );
663 Ok(())
730 Ok(())
664 }
731 }
665
732
666 /// Create the module, with __package__ given from parent
733 /// Create the module, with __package__ given from parent
667 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
734 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
668 let dotted_name = &format!("{}.revlog", package);
735 let dotted_name = &format!("{}.revlog", package);
669 let m = PyModule::new(py, dotted_name)?;
736 let m = PyModule::new(py, dotted_name)?;
670 m.add(py, "__package__", package)?;
737 m.add(py, "__package__", package)?;
671 m.add(py, "__doc__", "RevLog - Rust implementations")?;
738 m.add(py, "__doc__", "RevLog - Rust implementations")?;
672
739
673 m.add_class::<MixedIndex>(py)?;
740 m.add_class::<MixedIndex>(py)?;
674
741
675 let sys = PyModule::import(py, "sys")?;
742 let sys = PyModule::import(py, "sys")?;
676 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
743 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
677 sys_modules.set_item(py, dotted_name, &m)?;
744 sys_modules.set_item(py, dotted_name, &m)?;
678
745
679 Ok(m)
746 Ok(m)
680 }
747 }
General Comments 0
You need to be logged in to leave comments. Login now