rust/hg-core/src/revlog/nodemap.rs
changeset 44227 63db6657d280
child 44258 e52401a95b94
equal deleted inserted replaced
44226:46c8f15fb2b4 44227:63db6657d280
       
     1 // Copyright 2018-2020 Georges Racinet <georges.racinet@octobus.net>
       
     2 //           and Mercurial contributors
       
     3 //
       
     4 // This software may be used and distributed according to the terms of the
       
     5 // GNU General Public License version 2 or any later version.
       
     6 //! Indexing facilities for fast retrieval of `Revision` from `Node`
       
     7 //!
       
     8 //! This provides a variation on the 16-ary radix tree that is
       
     9 //! provided as "nodetree" in revlog.c, ready for append-only persistence
       
    10 //! on disk.
       
    11 //!
       
    12 //! Following existing implicit conventions, the "nodemap" terminology
       
    13 //! is used in a more abstract context.
       
    14 
       
    15 use super::Revision;
       
    16 use std::fmt;
       
    17 
       
    18 /// Low level NodeTree [`Blocks`] elements
       
    19 ///
       
    20 /// These are exactly as for instance on persistent storage.
       
    21 type RawElement = i32;
       
    22 
       
    23 /// High level representation of values in NodeTree
       
    24 /// [`Blocks`](struct.Block.html)
       
    25 ///
       
    26 /// This is the high level representation that most algorithms should
       
    27 /// use.
       
    28 #[derive(Clone, Debug, Eq, PartialEq)]
       
    29 enum Element {
       
    30     Rev(Revision),
       
    31     Block(usize),
       
    32     None,
       
    33 }
       
    34 
       
    35 impl From<RawElement> for Element {
       
    36     /// Conversion from low level representation, after endianness conversion.
       
    37     ///
       
    38     /// See [`Block`](struct.Block.html) for explanation about the encoding.
       
    39     fn from(raw: RawElement) -> Element {
       
    40         if raw >= 0 {
       
    41             Element::Block(raw as usize)
       
    42         } else if raw == -1 {
       
    43             Element::None
       
    44         } else {
       
    45             Element::Rev(-raw - 2)
       
    46         }
       
    47     }
       
    48 }
       
    49 
       
    50 impl From<Element> for RawElement {
       
    51     fn from(element: Element) -> RawElement {
       
    52         match element {
       
    53             Element::None => 0,
       
    54             Element::Block(i) => i as RawElement,
       
    55             Element::Rev(rev) => -rev - 2,
       
    56         }
       
    57     }
       
    58 }
       
    59 
       
    60 /// A logical block of the `NodeTree`, packed with a fixed size.
       
    61 ///
       
    62 /// These are always used in container types implementing `Index<Block>`,
       
    63 /// such as `&Block`
       
    64 ///
       
    65 /// As an array of integers, its ith element encodes that the
       
    66 /// ith potential edge from the block, representing the ith hexadecimal digit
       
    67 /// (nybble) `i` is either:
       
    68 ///
       
    69 /// - absent (value -1)
       
    70 /// - another `Block` in the same indexable container (value ≥ 0)
       
    71 ///  - a `Revision` leaf (value ≤ -2)
       
    72 ///
       
    73 /// Endianness has to be fixed for consistency on shared storage across
       
    74 /// different architectures.
       
    75 ///
       
    76 /// A key difference with the C `nodetree` is that we need to be
       
    77 /// able to represent the [`Block`] at index 0, hence -1 is the empty marker
       
    78 /// rather than 0 and the `Revision` range upper limit of -2 instead of -1.
       
    79 ///
       
    80 /// Another related difference is that `NULL_REVISION` (-1) is not
       
    81 /// represented at all, because we want an immutable empty nodetree
       
    82 /// to be valid.
       
    83 
       
    84 #[derive(Clone, PartialEq)]
       
    85 pub struct Block([RawElement; 16]);
       
    86 
       
    87 impl Block {
       
    88     fn new() -> Self {
       
    89         Block([-1; 16])
       
    90     }
       
    91 
       
    92     fn get(&self, nybble: u8) -> Element {
       
    93         Element::from(RawElement::from_be(self.0[nybble as usize]))
       
    94     }
       
    95 
       
    96     fn set(&mut self, nybble: u8, element: Element) {
       
    97         self.0[nybble as usize] = RawElement::to_be(element.into())
       
    98     }
       
    99 }
       
   100 
       
   101 impl fmt::Debug for Block {
       
   102     /// sparse representation for testing and debugging purposes
       
   103     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
       
   104         f.debug_map()
       
   105             .entries((0..16).filter_map(|i| match self.get(i) {
       
   106                 Element::None => None,
       
   107                 element => Some((i, element)),
       
   108             }))
       
   109             .finish()
       
   110     }
       
   111 }
       
   112 
       
   113 #[cfg(test)]
       
   114 mod tests {
       
   115     use super::*;
       
   116 
       
   117     /// Creates a `Block` using a syntax close to the `Debug` output
       
   118     macro_rules! block {
       
   119         {$($nybble:tt : $variant:ident($val:tt)),*} => (
       
   120             {
       
   121                 let mut block = Block::new();
       
   122                 $(block.set($nybble, Element::$variant($val)));*;
       
   123                 block
       
   124             }
       
   125         )
       
   126     }
       
   127 
       
   128     #[test]
       
   129     fn test_block_debug() {
       
   130         let mut block = Block::new();
       
   131         block.set(1, Element::Rev(3));
       
   132         block.set(10, Element::Block(0));
       
   133         assert_eq!(format!("{:?}", block), "{1: Rev(3), 10: Block(0)}");
       
   134     }
       
   135 
       
   136     #[test]
       
   137     fn test_block_macro() {
       
   138         let block = block! {5: Block(2)};
       
   139         assert_eq!(format!("{:?}", block), "{5: Block(2)}");
       
   140 
       
   141         let block = block! {13: Rev(15), 5: Block(2)};
       
   142         assert_eq!(format!("{:?}", block), "{5: Block(2), 13: Rev(15)}");
       
   143     }
       
   144 
       
   145     #[test]
       
   146     fn test_raw_block() {
       
   147         let mut raw = [-1; 16];
       
   148         raw[0] = 0;
       
   149         raw[1] = RawElement::to_be(15);
       
   150         raw[2] = RawElement::to_be(-2);
       
   151         raw[3] = RawElement::to_be(-1);
       
   152         raw[4] = RawElement::to_be(-3);
       
   153         let block = Block(raw);
       
   154         assert_eq!(block.get(0), Element::Block(0));
       
   155         assert_eq!(block.get(1), Element::Block(15));
       
   156         assert_eq!(block.get(3), Element::None);
       
   157         assert_eq!(block.get(2), Element::Rev(0));
       
   158         assert_eq!(block.get(4), Element::Rev(1));
       
   159     }
       
   160 }