##// END OF EJS Templates
rust-index: add append method...
Raphaël Gomès -
r52082:b4d152a2 default
parent child Browse files
Show More
@@ -1,682 +1,775 b''
1 use std::fmt::Debug;
1 use std::fmt::Debug;
2 use std::ops::Deref;
2 use std::ops::Deref;
3
3
4 use byteorder::{BigEndian, ByteOrder};
4 use byteorder::{BigEndian, ByteOrder};
5 use bytes_cast::{unaligned, BytesCast};
5
6
7 use super::REVIDX_KNOWN_FLAGS;
6 use crate::errors::HgError;
8 use crate::errors::HgError;
9 use crate::node::{NODE_BYTES_LENGTH, STORED_NODE_ID_BYTES};
7 use crate::revlog::node::Node;
10 use crate::revlog::node::Node;
8 use crate::revlog::{Revision, NULL_REVISION};
11 use crate::revlog::{Revision, NULL_REVISION};
9 use crate::{Graph, GraphError, RevlogIndex, UncheckedRevision};
12 use crate::{Graph, GraphError, RevlogError, RevlogIndex, UncheckedRevision};
10
13
11 pub const INDEX_ENTRY_SIZE: usize = 64;
14 pub const INDEX_ENTRY_SIZE: usize = 64;
15 pub const COMPRESSION_MODE_INLINE: u8 = 2;
12
16
13 pub struct IndexHeader {
17 pub struct IndexHeader {
14 header_bytes: [u8; 4],
18 header_bytes: [u8; 4],
15 }
19 }
16
20
17 #[derive(Copy, Clone)]
21 #[derive(Copy, Clone)]
18 pub struct IndexHeaderFlags {
22 pub struct IndexHeaderFlags {
19 flags: u16,
23 flags: u16,
20 }
24 }
21
25
22 /// Corresponds to the high bits of `_format_flags` in python
26 /// Corresponds to the high bits of `_format_flags` in python
23 impl IndexHeaderFlags {
27 impl IndexHeaderFlags {
24 /// Corresponds to FLAG_INLINE_DATA in python
28 /// Corresponds to FLAG_INLINE_DATA in python
25 pub fn is_inline(self) -> bool {
29 pub fn is_inline(self) -> bool {
26 self.flags & 1 != 0
30 self.flags & 1 != 0
27 }
31 }
28 /// Corresponds to FLAG_GENERALDELTA in python
32 /// Corresponds to FLAG_GENERALDELTA in python
29 pub fn uses_generaldelta(self) -> bool {
33 pub fn uses_generaldelta(self) -> bool {
30 self.flags & 2 != 0
34 self.flags & 2 != 0
31 }
35 }
32 }
36 }
33
37
34 /// Corresponds to the INDEX_HEADER structure,
38 /// Corresponds to the INDEX_HEADER structure,
35 /// which is parsed as a `header` variable in `_loadindex` in `revlog.py`
39 /// which is parsed as a `header` variable in `_loadindex` in `revlog.py`
36 impl IndexHeader {
40 impl IndexHeader {
37 fn format_flags(&self) -> IndexHeaderFlags {
41 fn format_flags(&self) -> IndexHeaderFlags {
38 // No "unknown flags" check here, unlike in python. Maybe there should
42 // No "unknown flags" check here, unlike in python. Maybe there should
39 // be.
43 // be.
40 IndexHeaderFlags {
44 IndexHeaderFlags {
41 flags: BigEndian::read_u16(&self.header_bytes[0..2]),
45 flags: BigEndian::read_u16(&self.header_bytes[0..2]),
42 }
46 }
43 }
47 }
44
48
45 /// The only revlog version currently supported by rhg.
49 /// The only revlog version currently supported by rhg.
46 const REVLOGV1: u16 = 1;
50 const REVLOGV1: u16 = 1;
47
51
48 /// Corresponds to `_format_version` in Python.
52 /// Corresponds to `_format_version` in Python.
49 fn format_version(&self) -> u16 {
53 fn format_version(&self) -> u16 {
50 BigEndian::read_u16(&self.header_bytes[2..4])
54 BigEndian::read_u16(&self.header_bytes[2..4])
51 }
55 }
52
56
53 const EMPTY_INDEX_HEADER: IndexHeader = IndexHeader {
57 const EMPTY_INDEX_HEADER: IndexHeader = IndexHeader {
54 // We treat an empty file as a valid index with no entries.
58 // We treat an empty file as a valid index with no entries.
55 // Here we make an arbitrary choice of what we assume the format of the
59 // Here we make an arbitrary choice of what we assume the format of the
56 // index to be (V1, using generaldelta).
60 // index to be (V1, using generaldelta).
57 // This doesn't matter too much, since we're only doing read-only
61 // This doesn't matter too much, since we're only doing read-only
58 // access. but the value corresponds to the `new_header` variable in
62 // access. but the value corresponds to the `new_header` variable in
59 // `revlog.py`, `_loadindex`
63 // `revlog.py`, `_loadindex`
60 header_bytes: [0, 3, 0, 1],
64 header_bytes: [0, 3, 0, 1],
61 };
65 };
62
66
63 fn parse(index_bytes: &[u8]) -> Result<IndexHeader, HgError> {
67 fn parse(index_bytes: &[u8]) -> Result<IndexHeader, HgError> {
64 if index_bytes.is_empty() {
68 if index_bytes.is_empty() {
65 return Ok(IndexHeader::EMPTY_INDEX_HEADER);
69 return Ok(IndexHeader::EMPTY_INDEX_HEADER);
66 }
70 }
67 if index_bytes.len() < 4 {
71 if index_bytes.len() < 4 {
68 return Err(HgError::corrupted(
72 return Err(HgError::corrupted(
69 "corrupted revlog: can't read the index format header",
73 "corrupted revlog: can't read the index format header",
70 ));
74 ));
71 }
75 }
72 Ok(IndexHeader {
76 Ok(IndexHeader {
73 header_bytes: {
77 header_bytes: {
74 let bytes: [u8; 4] =
78 let bytes: [u8; 4] =
75 index_bytes[0..4].try_into().expect("impossible");
79 index_bytes[0..4].try_into().expect("impossible");
76 bytes
80 bytes
77 },
81 },
78 })
82 })
79 }
83 }
80 }
84 }
81
85
82 /// Abstracts the access to the index bytes since they can be spread between
86 /// Abstracts the access to the index bytes since they can be spread between
83 /// the immutable (bytes) part and the mutable (added) part if any appends
87 /// the immutable (bytes) part and the mutable (added) part if any appends
84 /// happened. This makes it transparent for the callers.
88 /// happened. This makes it transparent for the callers.
85 struct IndexData {
89 struct IndexData {
86 /// Immutable bytes, most likely taken from disk
90 /// Immutable bytes, most likely taken from disk
87 bytes: Box<dyn Deref<Target = [u8]> + Send>,
91 bytes: Box<dyn Deref<Target = [u8]> + Send>,
88 /// Bytes that were added after reading the index
92 /// Bytes that were added after reading the index
89 added: Vec<u8>,
93 added: Vec<u8>,
90 }
94 }
91
95
92 impl IndexData {
96 impl IndexData {
93 pub fn new(bytes: Box<dyn Deref<Target = [u8]> + Send>) -> Self {
97 pub fn new(bytes: Box<dyn Deref<Target = [u8]> + Send>) -> Self {
94 Self {
98 Self {
95 bytes,
99 bytes,
96 added: vec![],
100 added: vec![],
97 }
101 }
98 }
102 }
99
103
100 pub fn len(&self) -> usize {
104 pub fn len(&self) -> usize {
101 self.bytes.len() + self.added.len()
105 self.bytes.len() + self.added.len()
102 }
106 }
103 }
107 }
104
108
105 impl std::ops::Index<std::ops::Range<usize>> for IndexData {
109 impl std::ops::Index<std::ops::Range<usize>> for IndexData {
106 type Output = [u8];
110 type Output = [u8];
107
111
108 fn index(&self, index: std::ops::Range<usize>) -> &Self::Output {
112 fn index(&self, index: std::ops::Range<usize>) -> &Self::Output {
109 let start = index.start;
113 let start = index.start;
110 let end = index.end;
114 let end = index.end;
111 let immutable_len = self.bytes.len();
115 let immutable_len = self.bytes.len();
112 if start < immutable_len {
116 if start < immutable_len {
113 if end > immutable_len {
117 if end > immutable_len {
114 panic!("index data cannot span existing and added ranges");
118 panic!("index data cannot span existing and added ranges");
115 }
119 }
116 &self.bytes[index]
120 &self.bytes[index]
117 } else {
121 } else {
118 &self.added[start - immutable_len..end - immutable_len]
122 &self.added[start - immutable_len..end - immutable_len]
119 }
123 }
120 }
124 }
121 }
125 }
122
126
127 pub struct RevisionDataParams {
128 flags: u16,
129 data_offset: u64,
130 data_compressed_length: i32,
131 data_uncompressed_length: i32,
132 data_delta_base: i32,
133 link_rev: i32,
134 parent_rev_1: i32,
135 parent_rev_2: i32,
136 node_id: [u8; NODE_BYTES_LENGTH],
137 _sidedata_offset: u64,
138 _sidedata_compressed_length: i32,
139 data_compression_mode: u8,
140 _sidedata_compression_mode: u8,
141 _rank: i32,
142 }
143
144 #[derive(BytesCast)]
145 #[repr(C)]
146 pub struct RevisionDataV1 {
147 data_offset_or_flags: unaligned::U64Be,
148 data_compressed_length: unaligned::I32Be,
149 data_uncompressed_length: unaligned::I32Be,
150 data_delta_base: unaligned::I32Be,
151 link_rev: unaligned::I32Be,
152 parent_rev_1: unaligned::I32Be,
153 parent_rev_2: unaligned::I32Be,
154 node_id: [u8; STORED_NODE_ID_BYTES],
155 }
156
157 fn _static_assert_size_of_revision_data_v1() {
158 let _ = std::mem::transmute::<RevisionDataV1, [u8; 64]>;
159 }
160
161 impl RevisionDataParams {
162 pub fn validate(&self) -> Result<(), RevlogError> {
163 if self.flags & !REVIDX_KNOWN_FLAGS != 0 {
164 return Err(RevlogError::corrupted(format!(
165 "unknown revlog index flags: {}",
166 self.flags
167 )));
168 }
169 if self.data_compression_mode != COMPRESSION_MODE_INLINE {
170 return Err(RevlogError::corrupted(format!(
171 "invalid data compression mode: {}",
172 self.data_compression_mode
173 )));
174 }
175 // FIXME isn't this only for v2 or changelog v2?
176 if self._sidedata_compression_mode != COMPRESSION_MODE_INLINE {
177 return Err(RevlogError::corrupted(format!(
178 "invalid sidedata compression mode: {}",
179 self._sidedata_compression_mode
180 )));
181 }
182 Ok(())
183 }
184
185 pub fn into_v1(self) -> RevisionDataV1 {
186 let data_offset_or_flags = self.data_offset << 16 | self.flags as u64;
187 let mut node_id = [0; STORED_NODE_ID_BYTES];
188 node_id[..NODE_BYTES_LENGTH].copy_from_slice(&self.node_id);
189 RevisionDataV1 {
190 data_offset_or_flags: data_offset_or_flags.into(),
191 data_compressed_length: self.data_compressed_length.into(),
192 data_uncompressed_length: self.data_uncompressed_length.into(),
193 data_delta_base: self.data_delta_base.into(),
194 link_rev: self.link_rev.into(),
195 parent_rev_1: self.parent_rev_1.into(),
196 parent_rev_2: self.parent_rev_2.into(),
197 node_id,
198 }
199 }
200 }
201
123 /// A Revlog index
202 /// A Revlog index
124 pub struct Index {
203 pub struct Index {
125 bytes: IndexData,
204 bytes: IndexData,
126 /// Offsets of starts of index blocks.
205 /// Offsets of starts of index blocks.
127 /// Only needed when the index is interleaved with data.
206 /// Only needed when the index is interleaved with data.
128 offsets: Option<Vec<usize>>,
207 offsets: Option<Vec<usize>>,
129 uses_generaldelta: bool,
208 uses_generaldelta: bool,
130 }
209 }
131
210
132 impl Debug for Index {
211 impl Debug for Index {
133 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
212 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
134 f.debug_struct("Index")
213 f.debug_struct("Index")
135 .field("offsets", &self.offsets)
214 .field("offsets", &self.offsets)
136 .field("uses_generaldelta", &self.uses_generaldelta)
215 .field("uses_generaldelta", &self.uses_generaldelta)
137 .finish()
216 .finish()
138 }
217 }
139 }
218 }
140
219
141 impl Graph for Index {
220 impl Graph for Index {
142 fn parents(&self, rev: Revision) -> Result<[Revision; 2], GraphError> {
221 fn parents(&self, rev: Revision) -> Result<[Revision; 2], GraphError> {
143 let err = || GraphError::ParentOutOfRange(rev);
222 let err = || GraphError::ParentOutOfRange(rev);
144 match self.get_entry(rev) {
223 match self.get_entry(rev) {
145 Some(entry) => {
224 Some(entry) => {
146 // The C implementation checks that the parents are valid
225 // The C implementation checks that the parents are valid
147 // before returning
226 // before returning
148 Ok([
227 Ok([
149 self.check_revision(entry.p1()).ok_or_else(err)?,
228 self.check_revision(entry.p1()).ok_or_else(err)?,
150 self.check_revision(entry.p2()).ok_or_else(err)?,
229 self.check_revision(entry.p2()).ok_or_else(err)?,
151 ])
230 ])
152 }
231 }
153 None => Ok([NULL_REVISION, NULL_REVISION]),
232 None => Ok([NULL_REVISION, NULL_REVISION]),
154 }
233 }
155 }
234 }
156 }
235 }
157
236
158 impl Index {
237 impl Index {
159 /// Create an index from bytes.
238 /// Create an index from bytes.
160 /// Calculate the start of each entry when is_inline is true.
239 /// Calculate the start of each entry when is_inline is true.
161 pub fn new(
240 pub fn new(
162 bytes: Box<dyn Deref<Target = [u8]> + Send>,
241 bytes: Box<dyn Deref<Target = [u8]> + Send>,
163 ) -> Result<Self, HgError> {
242 ) -> Result<Self, HgError> {
164 let header = IndexHeader::parse(bytes.as_ref())?;
243 let header = IndexHeader::parse(bytes.as_ref())?;
165
244
166 if header.format_version() != IndexHeader::REVLOGV1 {
245 if header.format_version() != IndexHeader::REVLOGV1 {
167 // A proper new version should have had a repo/store
246 // A proper new version should have had a repo/store
168 // requirement.
247 // requirement.
169 return Err(HgError::corrupted("unsupported revlog version"));
248 return Err(HgError::corrupted("unsupported revlog version"));
170 }
249 }
171
250
172 // This is only correct because we know version is REVLOGV1.
251 // This is only correct because we know version is REVLOGV1.
173 // In v2 we always use generaldelta, while in v0 we never use
252 // In v2 we always use generaldelta, while in v0 we never use
174 // generaldelta. Similar for [is_inline] (it's only used in v1).
253 // generaldelta. Similar for [is_inline] (it's only used in v1).
175 let uses_generaldelta = header.format_flags().uses_generaldelta();
254 let uses_generaldelta = header.format_flags().uses_generaldelta();
176
255
177 if header.format_flags().is_inline() {
256 if header.format_flags().is_inline() {
178 let mut offset: usize = 0;
257 let mut offset: usize = 0;
179 let mut offsets = Vec::new();
258 let mut offsets = Vec::new();
180
259
181 while offset + INDEX_ENTRY_SIZE <= bytes.len() {
260 while offset + INDEX_ENTRY_SIZE <= bytes.len() {
182 offsets.push(offset);
261 offsets.push(offset);
183 let end = offset + INDEX_ENTRY_SIZE;
262 let end = offset + INDEX_ENTRY_SIZE;
184 let entry = IndexEntry {
263 let entry = IndexEntry {
185 bytes: &bytes[offset..end],
264 bytes: &bytes[offset..end],
186 offset_override: None,
265 offset_override: None,
187 };
266 };
188
267
189 offset += INDEX_ENTRY_SIZE + entry.compressed_len() as usize;
268 offset += INDEX_ENTRY_SIZE + entry.compressed_len() as usize;
190 }
269 }
191
270
192 if offset == bytes.len() {
271 if offset == bytes.len() {
193 Ok(Self {
272 Ok(Self {
194 bytes: IndexData::new(bytes),
273 bytes: IndexData::new(bytes),
195 offsets: Some(offsets),
274 offsets: Some(offsets),
196 uses_generaldelta,
275 uses_generaldelta,
197 })
276 })
198 } else {
277 } else {
199 Err(HgError::corrupted("unexpected inline revlog length"))
278 Err(HgError::corrupted("unexpected inline revlog length"))
200 }
279 }
201 } else {
280 } else {
202 Ok(Self {
281 Ok(Self {
203 bytes: IndexData::new(bytes),
282 bytes: IndexData::new(bytes),
204 offsets: None,
283 offsets: None,
205 uses_generaldelta,
284 uses_generaldelta,
206 })
285 })
207 }
286 }
208 }
287 }
209
288
210 pub fn uses_generaldelta(&self) -> bool {
289 pub fn uses_generaldelta(&self) -> bool {
211 self.uses_generaldelta
290 self.uses_generaldelta
212 }
291 }
213
292
214 /// Value of the inline flag.
293 /// Value of the inline flag.
215 pub fn is_inline(&self) -> bool {
294 pub fn is_inline(&self) -> bool {
216 self.offsets.is_some()
295 self.offsets.is_some()
217 }
296 }
218
297
219 /// Return a slice of bytes if `revlog` is inline. Panic if not.
298 /// Return a slice of bytes if `revlog` is inline. Panic if not.
220 pub fn data(&self, start: usize, end: usize) -> &[u8] {
299 pub fn data(&self, start: usize, end: usize) -> &[u8] {
221 if !self.is_inline() {
300 if !self.is_inline() {
222 panic!("tried to access data in the index of a revlog that is not inline");
301 panic!("tried to access data in the index of a revlog that is not inline");
223 }
302 }
224 &self.bytes[start..end]
303 &self.bytes[start..end]
225 }
304 }
226
305
227 /// Return number of entries of the revlog index.
306 /// Return number of entries of the revlog index.
228 pub fn len(&self) -> usize {
307 pub fn len(&self) -> usize {
229 if let Some(offsets) = &self.offsets {
308 if let Some(offsets) = &self.offsets {
230 offsets.len()
309 offsets.len()
231 } else {
310 } else {
232 self.bytes.len() / INDEX_ENTRY_SIZE
311 self.bytes.len() / INDEX_ENTRY_SIZE
233 }
312 }
234 }
313 }
235
314
236 /// Returns `true` if the `Index` has zero `entries`.
315 /// Returns `true` if the `Index` has zero `entries`.
237 pub fn is_empty(&self) -> bool {
316 pub fn is_empty(&self) -> bool {
238 self.len() == 0
317 self.len() == 0
239 }
318 }
240
319
241 /// Return the index entry corresponding to the given revision if it
320 /// Return the index entry corresponding to the given revision if it
242 /// exists.
321 /// exists.
243 pub fn get_entry(&self, rev: Revision) -> Option<IndexEntry> {
322 pub fn get_entry(&self, rev: Revision) -> Option<IndexEntry> {
244 if rev == NULL_REVISION {
323 if rev == NULL_REVISION {
245 return None;
324 return None;
246 }
325 }
247 Some(if let Some(offsets) = &self.offsets {
326 Some(if let Some(offsets) = &self.offsets {
248 self.get_entry_inline(rev, offsets)
327 self.get_entry_inline(rev, offsets)
249 } else {
328 } else {
250 self.get_entry_separated(rev)
329 self.get_entry_separated(rev)
251 })
330 })
252 }
331 }
253
332
254 fn get_entry_inline(
333 fn get_entry_inline(
255 &self,
334 &self,
256 rev: Revision,
335 rev: Revision,
257 offsets: &[usize],
336 offsets: &[usize],
258 ) -> IndexEntry {
337 ) -> IndexEntry {
259 let start = offsets[rev.0 as usize];
338 let start = offsets[rev.0 as usize];
260 let end = start + INDEX_ENTRY_SIZE;
339 let end = start + INDEX_ENTRY_SIZE;
261 let bytes = &self.bytes[start..end];
340 let bytes = &self.bytes[start..end];
262
341
263 // See IndexEntry for an explanation of this override.
342 // See IndexEntry for an explanation of this override.
264 let offset_override = Some(end);
343 let offset_override = Some(end);
265
344
266 IndexEntry {
345 IndexEntry {
267 bytes,
346 bytes,
268 offset_override,
347 offset_override,
269 }
348 }
270 }
349 }
271
350
272 fn get_entry_separated(&self, rev: Revision) -> IndexEntry {
351 fn get_entry_separated(&self, rev: Revision) -> IndexEntry {
273 let start = rev.0 as usize * INDEX_ENTRY_SIZE;
352 let start = rev.0 as usize * INDEX_ENTRY_SIZE;
274 let end = start + INDEX_ENTRY_SIZE;
353 let end = start + INDEX_ENTRY_SIZE;
275 let bytes = &self.bytes[start..end];
354 let bytes = &self.bytes[start..end];
276
355
277 // Override the offset of the first revision as its bytes are used
356 // Override the offset of the first revision as its bytes are used
278 // for the index's metadata (saving space because it is always 0)
357 // for the index's metadata (saving space because it is always 0)
279 let offset_override = if rev == Revision(0) { Some(0) } else { None };
358 let offset_override = if rev == Revision(0) { Some(0) } else { None };
280
359
281 IndexEntry {
360 IndexEntry {
282 bytes,
361 bytes,
283 offset_override,
362 offset_override,
284 }
363 }
285 }
364 }
365
366 /// TODO move this to the trait probably, along with other things
367 pub fn append(
368 &mut self,
369 revision_data: RevisionDataParams,
370 ) -> Result<(), RevlogError> {
371 revision_data.validate()?;
372 let new_offset = self.bytes.len();
373 if let Some(offsets) = self.offsets.as_mut() {
374 offsets.push(new_offset)
375 }
376 self.bytes.added.extend(revision_data.into_v1().as_bytes());
377 Ok(())
378 }
286 }
379 }
287
380
288 impl super::RevlogIndex for Index {
381 impl super::RevlogIndex for Index {
289 fn len(&self) -> usize {
382 fn len(&self) -> usize {
290 self.len()
383 self.len()
291 }
384 }
292
385
293 fn node(&self, rev: Revision) -> Option<&Node> {
386 fn node(&self, rev: Revision) -> Option<&Node> {
294 self.get_entry(rev).map(|entry| entry.hash())
387 self.get_entry(rev).map(|entry| entry.hash())
295 }
388 }
296 }
389 }
297
390
298 #[derive(Debug)]
391 #[derive(Debug)]
299 pub struct IndexEntry<'a> {
392 pub struct IndexEntry<'a> {
300 bytes: &'a [u8],
393 bytes: &'a [u8],
301 /// Allows to override the offset value of the entry.
394 /// Allows to override the offset value of the entry.
302 ///
395 ///
303 /// For interleaved index and data, the offset stored in the index
396 /// For interleaved index and data, the offset stored in the index
304 /// corresponds to the separated data offset.
397 /// corresponds to the separated data offset.
305 /// It has to be overridden with the actual offset in the interleaved
398 /// It has to be overridden with the actual offset in the interleaved
306 /// index which is just after the index block.
399 /// index which is just after the index block.
307 ///
400 ///
308 /// For separated index and data, the offset stored in the first index
401 /// For separated index and data, the offset stored in the first index
309 /// entry is mixed with the index headers.
402 /// entry is mixed with the index headers.
310 /// It has to be overridden with 0.
403 /// It has to be overridden with 0.
311 offset_override: Option<usize>,
404 offset_override: Option<usize>,
312 }
405 }
313
406
314 impl<'a> IndexEntry<'a> {
407 impl<'a> IndexEntry<'a> {
315 /// Return the offset of the data.
408 /// Return the offset of the data.
316 pub fn offset(&self) -> usize {
409 pub fn offset(&self) -> usize {
317 if let Some(offset_override) = self.offset_override {
410 if let Some(offset_override) = self.offset_override {
318 offset_override
411 offset_override
319 } else {
412 } else {
320 let mut bytes = [0; 8];
413 let mut bytes = [0; 8];
321 bytes[2..8].copy_from_slice(&self.bytes[0..=5]);
414 bytes[2..8].copy_from_slice(&self.bytes[0..=5]);
322 BigEndian::read_u64(&bytes[..]) as usize
415 BigEndian::read_u64(&bytes[..]) as usize
323 }
416 }
324 }
417 }
325
418
326 pub fn flags(&self) -> u16 {
419 pub fn flags(&self) -> u16 {
327 BigEndian::read_u16(&self.bytes[6..=7])
420 BigEndian::read_u16(&self.bytes[6..=7])
328 }
421 }
329
422
330 /// Return the compressed length of the data.
423 /// Return the compressed length of the data.
331 pub fn compressed_len(&self) -> u32 {
424 pub fn compressed_len(&self) -> u32 {
332 BigEndian::read_u32(&self.bytes[8..=11])
425 BigEndian::read_u32(&self.bytes[8..=11])
333 }
426 }
334
427
335 /// Return the uncompressed length of the data.
428 /// Return the uncompressed length of the data.
336 pub fn uncompressed_len(&self) -> i32 {
429 pub fn uncompressed_len(&self) -> i32 {
337 BigEndian::read_i32(&self.bytes[12..=15])
430 BigEndian::read_i32(&self.bytes[12..=15])
338 }
431 }
339
432
340 /// Return the revision upon which the data has been derived.
433 /// Return the revision upon which the data has been derived.
341 pub fn base_revision_or_base_of_delta_chain(&self) -> UncheckedRevision {
434 pub fn base_revision_or_base_of_delta_chain(&self) -> UncheckedRevision {
342 // TODO Maybe return an Option when base_revision == rev?
435 // TODO Maybe return an Option when base_revision == rev?
343 // Requires to add rev to IndexEntry
436 // Requires to add rev to IndexEntry
344
437
345 BigEndian::read_i32(&self.bytes[16..]).into()
438 BigEndian::read_i32(&self.bytes[16..]).into()
346 }
439 }
347
440
348 pub fn link_revision(&self) -> UncheckedRevision {
441 pub fn link_revision(&self) -> UncheckedRevision {
349 BigEndian::read_i32(&self.bytes[20..]).into()
442 BigEndian::read_i32(&self.bytes[20..]).into()
350 }
443 }
351
444
352 pub fn p1(&self) -> UncheckedRevision {
445 pub fn p1(&self) -> UncheckedRevision {
353 BigEndian::read_i32(&self.bytes[24..]).into()
446 BigEndian::read_i32(&self.bytes[24..]).into()
354 }
447 }
355
448
356 pub fn p2(&self) -> UncheckedRevision {
449 pub fn p2(&self) -> UncheckedRevision {
357 BigEndian::read_i32(&self.bytes[28..]).into()
450 BigEndian::read_i32(&self.bytes[28..]).into()
358 }
451 }
359
452
360 /// Return the hash of revision's full text.
453 /// Return the hash of revision's full text.
361 ///
454 ///
362 /// Currently, SHA-1 is used and only the first 20 bytes of this field
455 /// Currently, SHA-1 is used and only the first 20 bytes of this field
363 /// are used.
456 /// are used.
364 pub fn hash(&self) -> &'a Node {
457 pub fn hash(&self) -> &'a Node {
365 (&self.bytes[32..52]).try_into().unwrap()
458 (&self.bytes[32..52]).try_into().unwrap()
366 }
459 }
367 }
460 }
368
461
369 #[cfg(test)]
462 #[cfg(test)]
370 mod tests {
463 mod tests {
371 use super::*;
464 use super::*;
372 use crate::node::NULL_NODE;
465 use crate::node::NULL_NODE;
373
466
374 #[cfg(test)]
467 #[cfg(test)]
375 #[derive(Debug, Copy, Clone)]
468 #[derive(Debug, Copy, Clone)]
376 pub struct IndexEntryBuilder {
469 pub struct IndexEntryBuilder {
377 is_first: bool,
470 is_first: bool,
378 is_inline: bool,
471 is_inline: bool,
379 is_general_delta: bool,
472 is_general_delta: bool,
380 version: u16,
473 version: u16,
381 offset: usize,
474 offset: usize,
382 compressed_len: usize,
475 compressed_len: usize,
383 uncompressed_len: usize,
476 uncompressed_len: usize,
384 base_revision_or_base_of_delta_chain: Revision,
477 base_revision_or_base_of_delta_chain: Revision,
385 link_revision: Revision,
478 link_revision: Revision,
386 p1: Revision,
479 p1: Revision,
387 p2: Revision,
480 p2: Revision,
388 node: Node,
481 node: Node,
389 }
482 }
390
483
391 #[cfg(test)]
484 #[cfg(test)]
392 impl IndexEntryBuilder {
485 impl IndexEntryBuilder {
393 #[allow(clippy::new_without_default)]
486 #[allow(clippy::new_without_default)]
394 pub fn new() -> Self {
487 pub fn new() -> Self {
395 Self {
488 Self {
396 is_first: false,
489 is_first: false,
397 is_inline: false,
490 is_inline: false,
398 is_general_delta: true,
491 is_general_delta: true,
399 version: 1,
492 version: 1,
400 offset: 0,
493 offset: 0,
401 compressed_len: 0,
494 compressed_len: 0,
402 uncompressed_len: 0,
495 uncompressed_len: 0,
403 base_revision_or_base_of_delta_chain: Revision(0),
496 base_revision_or_base_of_delta_chain: Revision(0),
404 link_revision: Revision(0),
497 link_revision: Revision(0),
405 p1: NULL_REVISION,
498 p1: NULL_REVISION,
406 p2: NULL_REVISION,
499 p2: NULL_REVISION,
407 node: NULL_NODE,
500 node: NULL_NODE,
408 }
501 }
409 }
502 }
410
503
411 pub fn is_first(&mut self, value: bool) -> &mut Self {
504 pub fn is_first(&mut self, value: bool) -> &mut Self {
412 self.is_first = value;
505 self.is_first = value;
413 self
506 self
414 }
507 }
415
508
416 pub fn with_inline(&mut self, value: bool) -> &mut Self {
509 pub fn with_inline(&mut self, value: bool) -> &mut Self {
417 self.is_inline = value;
510 self.is_inline = value;
418 self
511 self
419 }
512 }
420
513
421 pub fn with_general_delta(&mut self, value: bool) -> &mut Self {
514 pub fn with_general_delta(&mut self, value: bool) -> &mut Self {
422 self.is_general_delta = value;
515 self.is_general_delta = value;
423 self
516 self
424 }
517 }
425
518
426 pub fn with_version(&mut self, value: u16) -> &mut Self {
519 pub fn with_version(&mut self, value: u16) -> &mut Self {
427 self.version = value;
520 self.version = value;
428 self
521 self
429 }
522 }
430
523
431 pub fn with_offset(&mut self, value: usize) -> &mut Self {
524 pub fn with_offset(&mut self, value: usize) -> &mut Self {
432 self.offset = value;
525 self.offset = value;
433 self
526 self
434 }
527 }
435
528
436 pub fn with_compressed_len(&mut self, value: usize) -> &mut Self {
529 pub fn with_compressed_len(&mut self, value: usize) -> &mut Self {
437 self.compressed_len = value;
530 self.compressed_len = value;
438 self
531 self
439 }
532 }
440
533
441 pub fn with_uncompressed_len(&mut self, value: usize) -> &mut Self {
534 pub fn with_uncompressed_len(&mut self, value: usize) -> &mut Self {
442 self.uncompressed_len = value;
535 self.uncompressed_len = value;
443 self
536 self
444 }
537 }
445
538
446 pub fn with_base_revision_or_base_of_delta_chain(
539 pub fn with_base_revision_or_base_of_delta_chain(
447 &mut self,
540 &mut self,
448 value: Revision,
541 value: Revision,
449 ) -> &mut Self {
542 ) -> &mut Self {
450 self.base_revision_or_base_of_delta_chain = value;
543 self.base_revision_or_base_of_delta_chain = value;
451 self
544 self
452 }
545 }
453
546
454 pub fn with_link_revision(&mut self, value: Revision) -> &mut Self {
547 pub fn with_link_revision(&mut self, value: Revision) -> &mut Self {
455 self.link_revision = value;
548 self.link_revision = value;
456 self
549 self
457 }
550 }
458
551
459 pub fn with_p1(&mut self, value: Revision) -> &mut Self {
552 pub fn with_p1(&mut self, value: Revision) -> &mut Self {
460 self.p1 = value;
553 self.p1 = value;
461 self
554 self
462 }
555 }
463
556
464 pub fn with_p2(&mut self, value: Revision) -> &mut Self {
557 pub fn with_p2(&mut self, value: Revision) -> &mut Self {
465 self.p2 = value;
558 self.p2 = value;
466 self
559 self
467 }
560 }
468
561
469 pub fn with_node(&mut self, value: Node) -> &mut Self {
562 pub fn with_node(&mut self, value: Node) -> &mut Self {
470 self.node = value;
563 self.node = value;
471 self
564 self
472 }
565 }
473
566
474 pub fn build(&self) -> Vec<u8> {
567 pub fn build(&self) -> Vec<u8> {
475 let mut bytes = Vec::with_capacity(INDEX_ENTRY_SIZE);
568 let mut bytes = Vec::with_capacity(INDEX_ENTRY_SIZE);
476 if self.is_first {
569 if self.is_first {
477 bytes.extend(&match (self.is_general_delta, self.is_inline) {
570 bytes.extend(&match (self.is_general_delta, self.is_inline) {
478 (false, false) => [0u8, 0],
571 (false, false) => [0u8, 0],
479 (false, true) => [0u8, 1],
572 (false, true) => [0u8, 1],
480 (true, false) => [0u8, 2],
573 (true, false) => [0u8, 2],
481 (true, true) => [0u8, 3],
574 (true, true) => [0u8, 3],
482 });
575 });
483 bytes.extend(&self.version.to_be_bytes());
576 bytes.extend(&self.version.to_be_bytes());
484 // Remaining offset bytes.
577 // Remaining offset bytes.
485 bytes.extend(&[0u8; 2]);
578 bytes.extend(&[0u8; 2]);
486 } else {
579 } else {
487 // Offset stored on 48 bits (6 bytes)
580 // Offset stored on 48 bits (6 bytes)
488 bytes.extend(&(self.offset as u64).to_be_bytes()[2..]);
581 bytes.extend(&(self.offset as u64).to_be_bytes()[2..]);
489 }
582 }
490 bytes.extend(&[0u8; 2]); // Revision flags.
583 bytes.extend(&[0u8; 2]); // Revision flags.
491 bytes.extend(&(self.compressed_len as u32).to_be_bytes());
584 bytes.extend(&(self.compressed_len as u32).to_be_bytes());
492 bytes.extend(&(self.uncompressed_len as u32).to_be_bytes());
585 bytes.extend(&(self.uncompressed_len as u32).to_be_bytes());
493 bytes.extend(
586 bytes.extend(
494 &self.base_revision_or_base_of_delta_chain.0.to_be_bytes(),
587 &self.base_revision_or_base_of_delta_chain.0.to_be_bytes(),
495 );
588 );
496 bytes.extend(&self.link_revision.0.to_be_bytes());
589 bytes.extend(&self.link_revision.0.to_be_bytes());
497 bytes.extend(&self.p1.0.to_be_bytes());
590 bytes.extend(&self.p1.0.to_be_bytes());
498 bytes.extend(&self.p2.0.to_be_bytes());
591 bytes.extend(&self.p2.0.to_be_bytes());
499 bytes.extend(self.node.as_bytes());
592 bytes.extend(self.node.as_bytes());
500 bytes.extend(vec![0u8; 12]);
593 bytes.extend(vec![0u8; 12]);
501 bytes
594 bytes
502 }
595 }
503 }
596 }
504
597
505 pub fn is_inline(index_bytes: &[u8]) -> bool {
598 pub fn is_inline(index_bytes: &[u8]) -> bool {
506 IndexHeader::parse(index_bytes)
599 IndexHeader::parse(index_bytes)
507 .expect("too short")
600 .expect("too short")
508 .format_flags()
601 .format_flags()
509 .is_inline()
602 .is_inline()
510 }
603 }
511
604
512 pub fn uses_generaldelta(index_bytes: &[u8]) -> bool {
605 pub fn uses_generaldelta(index_bytes: &[u8]) -> bool {
513 IndexHeader::parse(index_bytes)
606 IndexHeader::parse(index_bytes)
514 .expect("too short")
607 .expect("too short")
515 .format_flags()
608 .format_flags()
516 .uses_generaldelta()
609 .uses_generaldelta()
517 }
610 }
518
611
519 pub fn get_version(index_bytes: &[u8]) -> u16 {
612 pub fn get_version(index_bytes: &[u8]) -> u16 {
520 IndexHeader::parse(index_bytes)
613 IndexHeader::parse(index_bytes)
521 .expect("too short")
614 .expect("too short")
522 .format_version()
615 .format_version()
523 }
616 }
524
617
525 #[test]
618 #[test]
526 fn flags_when_no_inline_flag_test() {
619 fn flags_when_no_inline_flag_test() {
527 let bytes = IndexEntryBuilder::new()
620 let bytes = IndexEntryBuilder::new()
528 .is_first(true)
621 .is_first(true)
529 .with_general_delta(false)
622 .with_general_delta(false)
530 .with_inline(false)
623 .with_inline(false)
531 .build();
624 .build();
532
625
533 assert!(!is_inline(&bytes));
626 assert!(!is_inline(&bytes));
534 assert!(!uses_generaldelta(&bytes));
627 assert!(!uses_generaldelta(&bytes));
535 }
628 }
536
629
537 #[test]
630 #[test]
538 fn flags_when_inline_flag_test() {
631 fn flags_when_inline_flag_test() {
539 let bytes = IndexEntryBuilder::new()
632 let bytes = IndexEntryBuilder::new()
540 .is_first(true)
633 .is_first(true)
541 .with_general_delta(false)
634 .with_general_delta(false)
542 .with_inline(true)
635 .with_inline(true)
543 .build();
636 .build();
544
637
545 assert!(is_inline(&bytes));
638 assert!(is_inline(&bytes));
546 assert!(!uses_generaldelta(&bytes));
639 assert!(!uses_generaldelta(&bytes));
547 }
640 }
548
641
549 #[test]
642 #[test]
550 fn flags_when_inline_and_generaldelta_flags_test() {
643 fn flags_when_inline_and_generaldelta_flags_test() {
551 let bytes = IndexEntryBuilder::new()
644 let bytes = IndexEntryBuilder::new()
552 .is_first(true)
645 .is_first(true)
553 .with_general_delta(true)
646 .with_general_delta(true)
554 .with_inline(true)
647 .with_inline(true)
555 .build();
648 .build();
556
649
557 assert!(is_inline(&bytes));
650 assert!(is_inline(&bytes));
558 assert!(uses_generaldelta(&bytes));
651 assert!(uses_generaldelta(&bytes));
559 }
652 }
560
653
561 #[test]
654 #[test]
562 fn test_offset() {
655 fn test_offset() {
563 let bytes = IndexEntryBuilder::new().with_offset(1).build();
656 let bytes = IndexEntryBuilder::new().with_offset(1).build();
564 let entry = IndexEntry {
657 let entry = IndexEntry {
565 bytes: &bytes,
658 bytes: &bytes,
566 offset_override: None,
659 offset_override: None,
567 };
660 };
568
661
569 assert_eq!(entry.offset(), 1)
662 assert_eq!(entry.offset(), 1)
570 }
663 }
571
664
572 #[test]
665 #[test]
573 fn test_with_overridden_offset() {
666 fn test_with_overridden_offset() {
574 let bytes = IndexEntryBuilder::new().with_offset(1).build();
667 let bytes = IndexEntryBuilder::new().with_offset(1).build();
575 let entry = IndexEntry {
668 let entry = IndexEntry {
576 bytes: &bytes,
669 bytes: &bytes,
577 offset_override: Some(2),
670 offset_override: Some(2),
578 };
671 };
579
672
580 assert_eq!(entry.offset(), 2)
673 assert_eq!(entry.offset(), 2)
581 }
674 }
582
675
583 #[test]
676 #[test]
584 fn test_compressed_len() {
677 fn test_compressed_len() {
585 let bytes = IndexEntryBuilder::new().with_compressed_len(1).build();
678 let bytes = IndexEntryBuilder::new().with_compressed_len(1).build();
586 let entry = IndexEntry {
679 let entry = IndexEntry {
587 bytes: &bytes,
680 bytes: &bytes,
588 offset_override: None,
681 offset_override: None,
589 };
682 };
590
683
591 assert_eq!(entry.compressed_len(), 1)
684 assert_eq!(entry.compressed_len(), 1)
592 }
685 }
593
686
594 #[test]
687 #[test]
595 fn test_uncompressed_len() {
688 fn test_uncompressed_len() {
596 let bytes = IndexEntryBuilder::new().with_uncompressed_len(1).build();
689 let bytes = IndexEntryBuilder::new().with_uncompressed_len(1).build();
597 let entry = IndexEntry {
690 let entry = IndexEntry {
598 bytes: &bytes,
691 bytes: &bytes,
599 offset_override: None,
692 offset_override: None,
600 };
693 };
601
694
602 assert_eq!(entry.uncompressed_len(), 1)
695 assert_eq!(entry.uncompressed_len(), 1)
603 }
696 }
604
697
605 #[test]
698 #[test]
606 fn test_base_revision_or_base_of_delta_chain() {
699 fn test_base_revision_or_base_of_delta_chain() {
607 let bytes = IndexEntryBuilder::new()
700 let bytes = IndexEntryBuilder::new()
608 .with_base_revision_or_base_of_delta_chain(Revision(1))
701 .with_base_revision_or_base_of_delta_chain(Revision(1))
609 .build();
702 .build();
610 let entry = IndexEntry {
703 let entry = IndexEntry {
611 bytes: &bytes,
704 bytes: &bytes,
612 offset_override: None,
705 offset_override: None,
613 };
706 };
614
707
615 assert_eq!(entry.base_revision_or_base_of_delta_chain(), 1.into())
708 assert_eq!(entry.base_revision_or_base_of_delta_chain(), 1.into())
616 }
709 }
617
710
618 #[test]
711 #[test]
619 fn link_revision_test() {
712 fn link_revision_test() {
620 let bytes = IndexEntryBuilder::new()
713 let bytes = IndexEntryBuilder::new()
621 .with_link_revision(Revision(123))
714 .with_link_revision(Revision(123))
622 .build();
715 .build();
623
716
624 let entry = IndexEntry {
717 let entry = IndexEntry {
625 bytes: &bytes,
718 bytes: &bytes,
626 offset_override: None,
719 offset_override: None,
627 };
720 };
628
721
629 assert_eq!(entry.link_revision(), 123.into());
722 assert_eq!(entry.link_revision(), 123.into());
630 }
723 }
631
724
632 #[test]
725 #[test]
633 fn p1_test() {
726 fn p1_test() {
634 let bytes = IndexEntryBuilder::new().with_p1(Revision(123)).build();
727 let bytes = IndexEntryBuilder::new().with_p1(Revision(123)).build();
635
728
636 let entry = IndexEntry {
729 let entry = IndexEntry {
637 bytes: &bytes,
730 bytes: &bytes,
638 offset_override: None,
731 offset_override: None,
639 };
732 };
640
733
641 assert_eq!(entry.p1(), 123.into());
734 assert_eq!(entry.p1(), 123.into());
642 }
735 }
643
736
644 #[test]
737 #[test]
645 fn p2_test() {
738 fn p2_test() {
646 let bytes = IndexEntryBuilder::new().with_p2(Revision(123)).build();
739 let bytes = IndexEntryBuilder::new().with_p2(Revision(123)).build();
647
740
648 let entry = IndexEntry {
741 let entry = IndexEntry {
649 bytes: &bytes,
742 bytes: &bytes,
650 offset_override: None,
743 offset_override: None,
651 };
744 };
652
745
653 assert_eq!(entry.p2(), 123.into());
746 assert_eq!(entry.p2(), 123.into());
654 }
747 }
655
748
656 #[test]
749 #[test]
657 fn node_test() {
750 fn node_test() {
658 let node = Node::from_hex("0123456789012345678901234567890123456789")
751 let node = Node::from_hex("0123456789012345678901234567890123456789")
659 .unwrap();
752 .unwrap();
660 let bytes = IndexEntryBuilder::new().with_node(node).build();
753 let bytes = IndexEntryBuilder::new().with_node(node).build();
661
754
662 let entry = IndexEntry {
755 let entry = IndexEntry {
663 bytes: &bytes,
756 bytes: &bytes,
664 offset_override: None,
757 offset_override: None,
665 };
758 };
666
759
667 assert_eq!(*entry.hash(), node);
760 assert_eq!(*entry.hash(), node);
668 }
761 }
669
762
670 #[test]
763 #[test]
671 fn version_test() {
764 fn version_test() {
672 let bytes = IndexEntryBuilder::new()
765 let bytes = IndexEntryBuilder::new()
673 .is_first(true)
766 .is_first(true)
674 .with_version(2)
767 .with_version(2)
675 .build();
768 .build();
676
769
677 assert_eq!(get_version(&bytes), 2)
770 assert_eq!(get_version(&bytes), 2)
678 }
771 }
679 }
772 }
680
773
681 #[cfg(test)]
774 #[cfg(test)]
682 pub use tests::IndexEntryBuilder;
775 pub use tests::IndexEntryBuilder;
@@ -1,429 +1,433 b''
1 // Copyright 2019-2020 Georges Racinet <georges.racinet@octobus.net>
1 // Copyright 2019-2020 Georges Racinet <georges.racinet@octobus.net>
2 //
2 //
3 // This software may be used and distributed according to the terms of the
3 // This software may be used and distributed according to the terms of the
4 // GNU General Public License version 2 or any later version.
4 // GNU General Public License version 2 or any later version.
5
5
6 //! Definitions and utilities for Revision nodes
6 //! Definitions and utilities for Revision nodes
7 //!
7 //!
8 //! In Mercurial code base, it is customary to call "a node" the binary SHA
8 //! In Mercurial code base, it is customary to call "a node" the binary SHA
9 //! of a revision.
9 //! of a revision.
10
10
11 use crate::errors::HgError;
11 use crate::errors::HgError;
12 use bytes_cast::BytesCast;
12 use bytes_cast::BytesCast;
13 use std::fmt;
13 use std::fmt;
14
14
15 /// The length in bytes of a `Node`
15 /// The length in bytes of a `Node`
16 ///
16 ///
17 /// This constant is meant to ease refactors of this module, and
17 /// This constant is meant to ease refactors of this module, and
18 /// are private so that calling code does not expect all nodes have
18 /// are private so that calling code does not expect all nodes have
19 /// the same size, should we support several formats concurrently in
19 /// the same size, should we support several formats concurrently in
20 /// the future.
20 /// the future.
21 pub const NODE_BYTES_LENGTH: usize = 20;
21 pub const NODE_BYTES_LENGTH: usize = 20;
22
22
23 /// The length in bytes set aside on disk for a `Node`. Revlog up to v1 only
24 /// use 20 out of those 32.
25 pub const STORED_NODE_ID_BYTES: usize = 32;
26
23 /// Id of the null node.
27 /// Id of the null node.
24 ///
28 ///
25 /// Used to indicate the absence of node.
29 /// Used to indicate the absence of node.
26 pub const NULL_NODE_ID: [u8; NODE_BYTES_LENGTH] = [0u8; NODE_BYTES_LENGTH];
30 pub const NULL_NODE_ID: [u8; NODE_BYTES_LENGTH] = [0u8; NODE_BYTES_LENGTH];
27
31
28 /// The length in bytes of a `Node`
32 /// The length in bytes of a `Node`
29 ///
33 ///
30 /// see also `NODES_BYTES_LENGTH` about it being private.
34 /// see also `NODES_BYTES_LENGTH` about it being private.
31 const NODE_NYBBLES_LENGTH: usize = 2 * NODE_BYTES_LENGTH;
35 const NODE_NYBBLES_LENGTH: usize = 2 * NODE_BYTES_LENGTH;
32
36
33 /// Default for UI presentation
37 /// Default for UI presentation
34 const SHORT_PREFIX_DEFAULT_NYBBLES_LENGTH: u8 = 12;
38 const SHORT_PREFIX_DEFAULT_NYBBLES_LENGTH: u8 = 12;
35
39
36 /// Private alias for readability and to ease future change
40 /// Private alias for readability and to ease future change
37 type NodeData = [u8; NODE_BYTES_LENGTH];
41 type NodeData = [u8; NODE_BYTES_LENGTH];
38
42
39 /// Binary revision SHA
43 /// Binary revision SHA
40 ///
44 ///
41 /// ## Future changes of hash size
45 /// ## Future changes of hash size
42 ///
46 ///
43 /// To accomodate future changes of hash size, Rust callers
47 /// To accomodate future changes of hash size, Rust callers
44 /// should use the conversion methods at the boundaries (FFI, actual
48 /// should use the conversion methods at the boundaries (FFI, actual
45 /// computation of hashes and I/O) only, and only if required.
49 /// computation of hashes and I/O) only, and only if required.
46 ///
50 ///
47 /// All other callers outside of unit tests should just handle `Node` values
51 /// All other callers outside of unit tests should just handle `Node` values
48 /// and never make any assumption on the actual length, using [`nybbles_len`]
52 /// and never make any assumption on the actual length, using [`nybbles_len`]
49 /// if they need a loop boundary.
53 /// if they need a loop boundary.
50 ///
54 ///
51 /// All methods that create a `Node` either take a type that enforces
55 /// All methods that create a `Node` either take a type that enforces
52 /// the size or return an error at runtime.
56 /// the size or return an error at runtime.
53 ///
57 ///
54 /// [`nybbles_len`]: #method.nybbles_len
58 /// [`nybbles_len`]: #method.nybbles_len
55 #[derive(Copy, Clone, PartialEq, BytesCast, derive_more::From)]
59 #[derive(Copy, Clone, PartialEq, BytesCast, derive_more::From)]
56 #[repr(transparent)]
60 #[repr(transparent)]
57 pub struct Node {
61 pub struct Node {
58 data: NodeData,
62 data: NodeData,
59 }
63 }
60
64
61 impl fmt::Debug for Node {
65 impl fmt::Debug for Node {
62 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63 let n = format!("{:x?}", self.data);
67 let n = format!("{:x?}", self.data);
64 // We're using debug_tuple because it makes the output a little
68 // We're using debug_tuple because it makes the output a little
65 // more compact without losing data.
69 // more compact without losing data.
66 f.debug_tuple("Node").field(&n).finish()
70 f.debug_tuple("Node").field(&n).finish()
67 }
71 }
68 }
72 }
69
73
70 /// The node value for NULL_REVISION
74 /// The node value for NULL_REVISION
71 pub const NULL_NODE: Node = Node {
75 pub const NULL_NODE: Node = Node {
72 data: [0; NODE_BYTES_LENGTH],
76 data: [0; NODE_BYTES_LENGTH],
73 };
77 };
74
78
75 /// Return an error if the slice has an unexpected length
79 /// Return an error if the slice has an unexpected length
76 impl<'a> TryFrom<&'a [u8]> for &'a Node {
80 impl<'a> TryFrom<&'a [u8]> for &'a Node {
77 type Error = ();
81 type Error = ();
78
82
79 #[inline]
83 #[inline]
80 fn try_from(bytes: &'a [u8]) -> Result<Self, Self::Error> {
84 fn try_from(bytes: &'a [u8]) -> Result<Self, Self::Error> {
81 match Node::from_bytes(bytes) {
85 match Node::from_bytes(bytes) {
82 Ok((node, rest)) if rest.is_empty() => Ok(node),
86 Ok((node, rest)) if rest.is_empty() => Ok(node),
83 _ => Err(()),
87 _ => Err(()),
84 }
88 }
85 }
89 }
86 }
90 }
87
91
88 /// Return an error if the slice has an unexpected length
92 /// Return an error if the slice has an unexpected length
89 impl TryFrom<&'_ [u8]> for Node {
93 impl TryFrom<&'_ [u8]> for Node {
90 type Error = std::array::TryFromSliceError;
94 type Error = std::array::TryFromSliceError;
91
95
92 #[inline]
96 #[inline]
93 fn try_from(bytes: &'_ [u8]) -> Result<Self, Self::Error> {
97 fn try_from(bytes: &'_ [u8]) -> Result<Self, Self::Error> {
94 let data = bytes.try_into()?;
98 let data = bytes.try_into()?;
95 Ok(Self { data })
99 Ok(Self { data })
96 }
100 }
97 }
101 }
98
102
99 impl From<&'_ NodeData> for Node {
103 impl From<&'_ NodeData> for Node {
100 #[inline]
104 #[inline]
101 fn from(data: &'_ NodeData) -> Self {
105 fn from(data: &'_ NodeData) -> Self {
102 Self { data: *data }
106 Self { data: *data }
103 }
107 }
104 }
108 }
105
109
106 impl fmt::LowerHex for Node {
110 impl fmt::LowerHex for Node {
107 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
111 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
108 for &byte in &self.data {
112 for &byte in &self.data {
109 write!(f, "{:02x}", byte)?
113 write!(f, "{:02x}", byte)?
110 }
114 }
111 Ok(())
115 Ok(())
112 }
116 }
113 }
117 }
114
118
115 #[derive(Debug)]
119 #[derive(Debug)]
116 pub struct FromHexError;
120 pub struct FromHexError;
117
121
118 /// Low level utility function, also for prefixes
122 /// Low level utility function, also for prefixes
119 fn get_nybble(s: &[u8], i: usize) -> u8 {
123 fn get_nybble(s: &[u8], i: usize) -> u8 {
120 if i % 2 == 0 {
124 if i % 2 == 0 {
121 s[i / 2] >> 4
125 s[i / 2] >> 4
122 } else {
126 } else {
123 s[i / 2] & 0x0f
127 s[i / 2] & 0x0f
124 }
128 }
125 }
129 }
126
130
127 impl Node {
131 impl Node {
128 /// Retrieve the `i`th half-byte of the binary data.
132 /// Retrieve the `i`th half-byte of the binary data.
129 ///
133 ///
130 /// This is also the `i`th hexadecimal digit in numeric form,
134 /// This is also the `i`th hexadecimal digit in numeric form,
131 /// also called a [nybble](https://en.wikipedia.org/wiki/Nibble).
135 /// also called a [nybble](https://en.wikipedia.org/wiki/Nibble).
132 pub fn get_nybble(&self, i: usize) -> u8 {
136 pub fn get_nybble(&self, i: usize) -> u8 {
133 get_nybble(&self.data, i)
137 get_nybble(&self.data, i)
134 }
138 }
135
139
136 /// Length of the data, in nybbles
140 /// Length of the data, in nybbles
137 pub fn nybbles_len(&self) -> usize {
141 pub fn nybbles_len(&self) -> usize {
138 // public exposure as an instance method only, so that we can
142 // public exposure as an instance method only, so that we can
139 // easily support several sizes of hashes if needed in the future.
143 // easily support several sizes of hashes if needed in the future.
140 NODE_NYBBLES_LENGTH
144 NODE_NYBBLES_LENGTH
141 }
145 }
142
146
143 /// Convert from hexadecimal string representation
147 /// Convert from hexadecimal string representation
144 ///
148 ///
145 /// Exact length is required.
149 /// Exact length is required.
146 ///
150 ///
147 /// To be used in FFI and I/O only, in order to facilitate future
151 /// To be used in FFI and I/O only, in order to facilitate future
148 /// changes of hash format.
152 /// changes of hash format.
149 pub fn from_hex(hex: impl AsRef<[u8]>) -> Result<Node, FromHexError> {
153 pub fn from_hex(hex: impl AsRef<[u8]>) -> Result<Node, FromHexError> {
150 let prefix = NodePrefix::from_hex(hex)?;
154 let prefix = NodePrefix::from_hex(hex)?;
151 if prefix.nybbles_len() == NODE_NYBBLES_LENGTH {
155 if prefix.nybbles_len() == NODE_NYBBLES_LENGTH {
152 Ok(Self { data: prefix.data })
156 Ok(Self { data: prefix.data })
153 } else {
157 } else {
154 Err(FromHexError)
158 Err(FromHexError)
155 }
159 }
156 }
160 }
157
161
158 /// `from_hex`, but for input from an internal file of the repository such
162 /// `from_hex`, but for input from an internal file of the repository such
159 /// as a changelog or manifest entry.
163 /// as a changelog or manifest entry.
160 ///
164 ///
161 /// An error is treated as repository corruption.
165 /// An error is treated as repository corruption.
162 pub fn from_hex_for_repo(hex: impl AsRef<[u8]>) -> Result<Node, HgError> {
166 pub fn from_hex_for_repo(hex: impl AsRef<[u8]>) -> Result<Node, HgError> {
163 Self::from_hex(hex.as_ref()).map_err(|FromHexError| {
167 Self::from_hex(hex.as_ref()).map_err(|FromHexError| {
164 HgError::CorruptedRepository(format!(
168 HgError::CorruptedRepository(format!(
165 "Expected a full hexadecimal node ID, found {}",
169 "Expected a full hexadecimal node ID, found {}",
166 String::from_utf8_lossy(hex.as_ref())
170 String::from_utf8_lossy(hex.as_ref())
167 ))
171 ))
168 })
172 })
169 }
173 }
170
174
171 /// Provide access to binary data
175 /// Provide access to binary data
172 ///
176 ///
173 /// This is needed by FFI layers, for instance to return expected
177 /// This is needed by FFI layers, for instance to return expected
174 /// binary values to Python.
178 /// binary values to Python.
175 pub fn as_bytes(&self) -> &[u8] {
179 pub fn as_bytes(&self) -> &[u8] {
176 &self.data
180 &self.data
177 }
181 }
178
182
179 pub fn short(&self) -> NodePrefix {
183 pub fn short(&self) -> NodePrefix {
180 NodePrefix {
184 NodePrefix {
181 nybbles_len: SHORT_PREFIX_DEFAULT_NYBBLES_LENGTH,
185 nybbles_len: SHORT_PREFIX_DEFAULT_NYBBLES_LENGTH,
182 data: self.data,
186 data: self.data,
183 }
187 }
184 }
188 }
185
189
186 pub fn pad_to_256_bits(&self) -> [u8; 32] {
190 pub fn pad_to_256_bits(&self) -> [u8; 32] {
187 let mut bits = [0; 32];
191 let mut bits = [0; 32];
188 bits[..NODE_BYTES_LENGTH].copy_from_slice(&self.data);
192 bits[..NODE_BYTES_LENGTH].copy_from_slice(&self.data);
189 bits
193 bits
190 }
194 }
191 }
195 }
192
196
193 /// The beginning of a binary revision SHA.
197 /// The beginning of a binary revision SHA.
194 ///
198 ///
195 /// Since it can potentially come from an hexadecimal representation with
199 /// Since it can potentially come from an hexadecimal representation with
196 /// odd length, it needs to carry around whether the last 4 bits are relevant
200 /// odd length, it needs to carry around whether the last 4 bits are relevant
197 /// or not.
201 /// or not.
198 #[derive(Debug, PartialEq, Copy, Clone)]
202 #[derive(Debug, PartialEq, Copy, Clone)]
199 pub struct NodePrefix {
203 pub struct NodePrefix {
200 /// In `1..=NODE_NYBBLES_LENGTH`
204 /// In `1..=NODE_NYBBLES_LENGTH`
201 nybbles_len: u8,
205 nybbles_len: u8,
202 /// The first `4 * length_in_nybbles` bits are used (considering bits
206 /// The first `4 * length_in_nybbles` bits are used (considering bits
203 /// within a bytes in big-endian: most significant first), the rest
207 /// within a bytes in big-endian: most significant first), the rest
204 /// are zero.
208 /// are zero.
205 data: NodeData,
209 data: NodeData,
206 }
210 }
207
211
208 impl NodePrefix {
212 impl NodePrefix {
209 /// Convert from hexadecimal string representation
213 /// Convert from hexadecimal string representation
210 ///
214 ///
211 /// Similarly to `hex::decode`, can be used with Unicode string types
215 /// Similarly to `hex::decode`, can be used with Unicode string types
212 /// (`String`, `&str`) as well as bytes.
216 /// (`String`, `&str`) as well as bytes.
213 ///
217 ///
214 /// To be used in FFI and I/O only, in order to facilitate future
218 /// To be used in FFI and I/O only, in order to facilitate future
215 /// changes of hash format.
219 /// changes of hash format.
216 pub fn from_hex(hex: impl AsRef<[u8]>) -> Result<Self, FromHexError> {
220 pub fn from_hex(hex: impl AsRef<[u8]>) -> Result<Self, FromHexError> {
217 let hex = hex.as_ref();
221 let hex = hex.as_ref();
218 let len = hex.len();
222 let len = hex.len();
219 if len > NODE_NYBBLES_LENGTH || len == 0 {
223 if len > NODE_NYBBLES_LENGTH || len == 0 {
220 return Err(FromHexError);
224 return Err(FromHexError);
221 }
225 }
222
226
223 let mut data = [0; NODE_BYTES_LENGTH];
227 let mut data = [0; NODE_BYTES_LENGTH];
224 let mut nybbles_len = 0;
228 let mut nybbles_len = 0;
225 for &ascii_byte in hex {
229 for &ascii_byte in hex {
226 let nybble = match char::from(ascii_byte).to_digit(16) {
230 let nybble = match char::from(ascii_byte).to_digit(16) {
227 Some(digit) => digit as u8,
231 Some(digit) => digit as u8,
228 None => return Err(FromHexError),
232 None => return Err(FromHexError),
229 };
233 };
230 // Fill in the upper half of a byte first, then the lower half.
234 // Fill in the upper half of a byte first, then the lower half.
231 let shift = if nybbles_len % 2 == 0 { 4 } else { 0 };
235 let shift = if nybbles_len % 2 == 0 { 4 } else { 0 };
232 data[nybbles_len as usize / 2] |= nybble << shift;
236 data[nybbles_len as usize / 2] |= nybble << shift;
233 nybbles_len += 1;
237 nybbles_len += 1;
234 }
238 }
235 Ok(Self { data, nybbles_len })
239 Ok(Self { data, nybbles_len })
236 }
240 }
237
241
238 pub fn nybbles_len(&self) -> usize {
242 pub fn nybbles_len(&self) -> usize {
239 self.nybbles_len as _
243 self.nybbles_len as _
240 }
244 }
241
245
242 pub fn is_prefix_of(&self, node: &Node) -> bool {
246 pub fn is_prefix_of(&self, node: &Node) -> bool {
243 let full_bytes = self.nybbles_len() / 2;
247 let full_bytes = self.nybbles_len() / 2;
244 if self.data[..full_bytes] != node.data[..full_bytes] {
248 if self.data[..full_bytes] != node.data[..full_bytes] {
245 return false;
249 return false;
246 }
250 }
247 if self.nybbles_len() % 2 == 0 {
251 if self.nybbles_len() % 2 == 0 {
248 return true;
252 return true;
249 }
253 }
250 let last = self.nybbles_len() - 1;
254 let last = self.nybbles_len() - 1;
251 self.get_nybble(last) == node.get_nybble(last)
255 self.get_nybble(last) == node.get_nybble(last)
252 }
256 }
253
257
254 /// Retrieve the `i`th half-byte from the prefix.
258 /// Retrieve the `i`th half-byte from the prefix.
255 ///
259 ///
256 /// This is also the `i`th hexadecimal digit in numeric form,
260 /// This is also the `i`th hexadecimal digit in numeric form,
257 /// also called a [nybble](https://en.wikipedia.org/wiki/Nibble).
261 /// also called a [nybble](https://en.wikipedia.org/wiki/Nibble).
258 pub fn get_nybble(&self, i: usize) -> u8 {
262 pub fn get_nybble(&self, i: usize) -> u8 {
259 assert!(i < self.nybbles_len());
263 assert!(i < self.nybbles_len());
260 get_nybble(&self.data, i)
264 get_nybble(&self.data, i)
261 }
265 }
262
266
263 fn iter_nybbles(&self) -> impl Iterator<Item = u8> + '_ {
267 fn iter_nybbles(&self) -> impl Iterator<Item = u8> + '_ {
264 (0..self.nybbles_len()).map(move |i| get_nybble(&self.data, i))
268 (0..self.nybbles_len()).map(move |i| get_nybble(&self.data, i))
265 }
269 }
266
270
267 /// Return the index first nybble that's different from `node`
271 /// Return the index first nybble that's different from `node`
268 ///
272 ///
269 /// If the return value is `None` that means that `self` is
273 /// If the return value is `None` that means that `self` is
270 /// a prefix of `node`, but the current method is a bit slower
274 /// a prefix of `node`, but the current method is a bit slower
271 /// than `is_prefix_of`.
275 /// than `is_prefix_of`.
272 ///
276 ///
273 /// Returned index is as in `get_nybble`, i.e., starting at 0.
277 /// Returned index is as in `get_nybble`, i.e., starting at 0.
274 pub fn first_different_nybble(&self, node: &Node) -> Option<usize> {
278 pub fn first_different_nybble(&self, node: &Node) -> Option<usize> {
275 self.iter_nybbles()
279 self.iter_nybbles()
276 .zip(NodePrefix::from(*node).iter_nybbles())
280 .zip(NodePrefix::from(*node).iter_nybbles())
277 .position(|(a, b)| a != b)
281 .position(|(a, b)| a != b)
278 }
282 }
279 }
283 }
280
284
281 impl fmt::LowerHex for NodePrefix {
285 impl fmt::LowerHex for NodePrefix {
282 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
286 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
283 let full_bytes = self.nybbles_len() / 2;
287 let full_bytes = self.nybbles_len() / 2;
284 for &byte in &self.data[..full_bytes] {
288 for &byte in &self.data[..full_bytes] {
285 write!(f, "{:02x}", byte)?
289 write!(f, "{:02x}", byte)?
286 }
290 }
287 if self.nybbles_len() % 2 == 1 {
291 if self.nybbles_len() % 2 == 1 {
288 let last = self.nybbles_len() - 1;
292 let last = self.nybbles_len() - 1;
289 write!(f, "{:x}", self.get_nybble(last))?
293 write!(f, "{:x}", self.get_nybble(last))?
290 }
294 }
291 Ok(())
295 Ok(())
292 }
296 }
293 }
297 }
294
298
295 /// A shortcut for full `Node` references
299 /// A shortcut for full `Node` references
296 impl From<&'_ Node> for NodePrefix {
300 impl From<&'_ Node> for NodePrefix {
297 fn from(node: &'_ Node) -> Self {
301 fn from(node: &'_ Node) -> Self {
298 NodePrefix {
302 NodePrefix {
299 nybbles_len: node.nybbles_len() as _,
303 nybbles_len: node.nybbles_len() as _,
300 data: node.data,
304 data: node.data,
301 }
305 }
302 }
306 }
303 }
307 }
304
308
305 /// A shortcut for full `Node` references
309 /// A shortcut for full `Node` references
306 impl From<Node> for NodePrefix {
310 impl From<Node> for NodePrefix {
307 fn from(node: Node) -> Self {
311 fn from(node: Node) -> Self {
308 NodePrefix {
312 NodePrefix {
309 nybbles_len: node.nybbles_len() as _,
313 nybbles_len: node.nybbles_len() as _,
310 data: node.data,
314 data: node.data,
311 }
315 }
312 }
316 }
313 }
317 }
314
318
315 impl PartialEq<Node> for NodePrefix {
319 impl PartialEq<Node> for NodePrefix {
316 fn eq(&self, other: &Node) -> bool {
320 fn eq(&self, other: &Node) -> bool {
317 self.data == other.data && self.nybbles_len() == other.nybbles_len()
321 self.data == other.data && self.nybbles_len() == other.nybbles_len()
318 }
322 }
319 }
323 }
320
324
321 #[cfg(test)]
325 #[cfg(test)]
322 mod tests {
326 mod tests {
323 use super::*;
327 use super::*;
324
328
325 const SAMPLE_NODE_HEX: &str = "0123456789abcdeffedcba9876543210deadbeef";
329 const SAMPLE_NODE_HEX: &str = "0123456789abcdeffedcba9876543210deadbeef";
326 const SAMPLE_NODE: Node = Node {
330 const SAMPLE_NODE: Node = Node {
327 data: [
331 data: [
328 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba,
332 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba,
329 0x98, 0x76, 0x54, 0x32, 0x10, 0xde, 0xad, 0xbe, 0xef,
333 0x98, 0x76, 0x54, 0x32, 0x10, 0xde, 0xad, 0xbe, 0xef,
330 ],
334 ],
331 };
335 };
332
336
333 /// Pad an hexadecimal string to reach `NODE_NYBBLES_LENGTH`
337 /// Pad an hexadecimal string to reach `NODE_NYBBLES_LENGTH`
334 /// The padding is made with zeros.
338 /// The padding is made with zeros.
335 pub fn hex_pad_right(hex: &str) -> String {
339 pub fn hex_pad_right(hex: &str) -> String {
336 let mut res = hex.to_string();
340 let mut res = hex.to_string();
337 while res.len() < NODE_NYBBLES_LENGTH {
341 while res.len() < NODE_NYBBLES_LENGTH {
338 res.push('0');
342 res.push('0');
339 }
343 }
340 res
344 res
341 }
345 }
342
346
343 #[test]
347 #[test]
344 fn test_node_from_hex() {
348 fn test_node_from_hex() {
345 let not_hex = "012... oops";
349 let not_hex = "012... oops";
346 let too_short = "0123";
350 let too_short = "0123";
347 let too_long = format!("{}0", SAMPLE_NODE_HEX);
351 let too_long = format!("{}0", SAMPLE_NODE_HEX);
348 assert_eq!(Node::from_hex(SAMPLE_NODE_HEX).unwrap(), SAMPLE_NODE);
352 assert_eq!(Node::from_hex(SAMPLE_NODE_HEX).unwrap(), SAMPLE_NODE);
349 assert!(Node::from_hex(not_hex).is_err());
353 assert!(Node::from_hex(not_hex).is_err());
350 assert!(Node::from_hex(too_short).is_err());
354 assert!(Node::from_hex(too_short).is_err());
351 assert!(Node::from_hex(too_long).is_err());
355 assert!(Node::from_hex(too_long).is_err());
352 }
356 }
353
357
354 #[test]
358 #[test]
355 fn test_node_encode_hex() {
359 fn test_node_encode_hex() {
356 assert_eq!(format!("{:x}", SAMPLE_NODE), SAMPLE_NODE_HEX);
360 assert_eq!(format!("{:x}", SAMPLE_NODE), SAMPLE_NODE_HEX);
357 }
361 }
358
362
359 #[test]
363 #[test]
360 fn test_prefix_from_to_hex() -> Result<(), FromHexError> {
364 fn test_prefix_from_to_hex() -> Result<(), FromHexError> {
361 assert_eq!(format!("{:x}", NodePrefix::from_hex("0e1")?), "0e1");
365 assert_eq!(format!("{:x}", NodePrefix::from_hex("0e1")?), "0e1");
362 assert_eq!(format!("{:x}", NodePrefix::from_hex("0e1a")?), "0e1a");
366 assert_eq!(format!("{:x}", NodePrefix::from_hex("0e1a")?), "0e1a");
363 assert_eq!(
367 assert_eq!(
364 format!("{:x}", NodePrefix::from_hex(SAMPLE_NODE_HEX)?),
368 format!("{:x}", NodePrefix::from_hex(SAMPLE_NODE_HEX)?),
365 SAMPLE_NODE_HEX
369 SAMPLE_NODE_HEX
366 );
370 );
367 Ok(())
371 Ok(())
368 }
372 }
369
373
370 #[test]
374 #[test]
371 fn test_prefix_from_hex_errors() {
375 fn test_prefix_from_hex_errors() {
372 assert!(NodePrefix::from_hex("testgr").is_err());
376 assert!(NodePrefix::from_hex("testgr").is_err());
373 let mut long = format!("{:x}", NULL_NODE);
377 let mut long = format!("{:x}", NULL_NODE);
374 long.push('c');
378 long.push('c');
375 assert!(NodePrefix::from_hex(&long).is_err())
379 assert!(NodePrefix::from_hex(&long).is_err())
376 }
380 }
377
381
378 #[test]
382 #[test]
379 fn test_is_prefix_of() -> Result<(), FromHexError> {
383 fn test_is_prefix_of() -> Result<(), FromHexError> {
380 let mut node_data = [0; NODE_BYTES_LENGTH];
384 let mut node_data = [0; NODE_BYTES_LENGTH];
381 node_data[0] = 0x12;
385 node_data[0] = 0x12;
382 node_data[1] = 0xca;
386 node_data[1] = 0xca;
383 let node = Node::from(node_data);
387 let node = Node::from(node_data);
384 assert!(NodePrefix::from_hex("12")?.is_prefix_of(&node));
388 assert!(NodePrefix::from_hex("12")?.is_prefix_of(&node));
385 assert!(!NodePrefix::from_hex("1a")?.is_prefix_of(&node));
389 assert!(!NodePrefix::from_hex("1a")?.is_prefix_of(&node));
386 assert!(NodePrefix::from_hex("12c")?.is_prefix_of(&node));
390 assert!(NodePrefix::from_hex("12c")?.is_prefix_of(&node));
387 assert!(!NodePrefix::from_hex("12d")?.is_prefix_of(&node));
391 assert!(!NodePrefix::from_hex("12d")?.is_prefix_of(&node));
388 Ok(())
392 Ok(())
389 }
393 }
390
394
391 #[test]
395 #[test]
392 fn test_get_nybble() -> Result<(), FromHexError> {
396 fn test_get_nybble() -> Result<(), FromHexError> {
393 let prefix = NodePrefix::from_hex("dead6789cafe")?;
397 let prefix = NodePrefix::from_hex("dead6789cafe")?;
394 assert_eq!(prefix.get_nybble(0), 13);
398 assert_eq!(prefix.get_nybble(0), 13);
395 assert_eq!(prefix.get_nybble(7), 9);
399 assert_eq!(prefix.get_nybble(7), 9);
396 Ok(())
400 Ok(())
397 }
401 }
398
402
399 #[test]
403 #[test]
400 fn test_first_different_nybble_even_prefix() {
404 fn test_first_different_nybble_even_prefix() {
401 let prefix = NodePrefix::from_hex("12ca").unwrap();
405 let prefix = NodePrefix::from_hex("12ca").unwrap();
402 let mut node = Node::from([0; NODE_BYTES_LENGTH]);
406 let mut node = Node::from([0; NODE_BYTES_LENGTH]);
403 assert_eq!(prefix.first_different_nybble(&node), Some(0));
407 assert_eq!(prefix.first_different_nybble(&node), Some(0));
404 node.data[0] = 0x13;
408 node.data[0] = 0x13;
405 assert_eq!(prefix.first_different_nybble(&node), Some(1));
409 assert_eq!(prefix.first_different_nybble(&node), Some(1));
406 node.data[0] = 0x12;
410 node.data[0] = 0x12;
407 assert_eq!(prefix.first_different_nybble(&node), Some(2));
411 assert_eq!(prefix.first_different_nybble(&node), Some(2));
408 node.data[1] = 0xca;
412 node.data[1] = 0xca;
409 // now it is a prefix
413 // now it is a prefix
410 assert_eq!(prefix.first_different_nybble(&node), None);
414 assert_eq!(prefix.first_different_nybble(&node), None);
411 }
415 }
412
416
413 #[test]
417 #[test]
414 fn test_first_different_nybble_odd_prefix() {
418 fn test_first_different_nybble_odd_prefix() {
415 let prefix = NodePrefix::from_hex("12c").unwrap();
419 let prefix = NodePrefix::from_hex("12c").unwrap();
416 let mut node = Node::from([0; NODE_BYTES_LENGTH]);
420 let mut node = Node::from([0; NODE_BYTES_LENGTH]);
417 assert_eq!(prefix.first_different_nybble(&node), Some(0));
421 assert_eq!(prefix.first_different_nybble(&node), Some(0));
418 node.data[0] = 0x13;
422 node.data[0] = 0x13;
419 assert_eq!(prefix.first_different_nybble(&node), Some(1));
423 assert_eq!(prefix.first_different_nybble(&node), Some(1));
420 node.data[0] = 0x12;
424 node.data[0] = 0x12;
421 assert_eq!(prefix.first_different_nybble(&node), Some(2));
425 assert_eq!(prefix.first_different_nybble(&node), Some(2));
422 node.data[1] = 0xca;
426 node.data[1] = 0xca;
423 // now it is a prefix
427 // now it is a prefix
424 assert_eq!(prefix.first_different_nybble(&node), None);
428 assert_eq!(prefix.first_different_nybble(&node), None);
425 }
429 }
426 }
430 }
427
431
428 #[cfg(test)]
432 #[cfg(test)]
429 pub use tests::hex_pad_right;
433 pub use tests::hex_pad_right;
General Comments 0
You need to be logged in to leave comments. Login now