libpijul_compat/backend/
key.rs

1use super::patch_id::*;
2use bs58;
3use sanakirja::{Alignment, Representable};
4use std;
5use Hash;
6
7const LINE_ID_SIZE: usize = 8;
8pub const KEY_SIZE: usize = PATCH_ID_SIZE + LINE_ID_SIZE;
9
10/// The node at the root of the repository graph.
11pub const ROOT_KEY: Key<PatchId> = Key {
12    patch: ROOT_PATCH_ID,
13    line: LineId([0; LINE_ID_SIZE]),
14};
15
16use hex::ToHex;
17use std::fmt::Write;
18impl ToHex for Key<PatchId> {
19    fn write_hex<W: Write>(&self, w: &mut W) -> std::fmt::Result {
20        self.patch.write_hex(w)?;
21        self.line.write_hex(w)
22    }
23    fn write_hex_upper<W: Write>(&self, w: &mut W) -> std::fmt::Result {
24        self.patch.write_hex(w)?;
25        self.line.write_hex(w)
26    }
27}
28
29impl Key<PatchId> {
30    pub fn to_base58(&self) -> String {
31        self.patch.to_base58() + &bs58::encode(&self.line.0).into_string()
32    }
33    pub fn to_hex(&self) -> String {
34        let mut s = String::new();
35        self.write_hex(&mut s).unwrap();
36        s
37    }
38}
39
40impl Key<Hash> {
41    pub fn to_base58(&self) -> String {
42        self.patch.to_base58() + &bs58::encode(&self.line.0).into_string()
43    }
44}
45
46impl Key<PatchId> {
47    /// Is this the root key? (the root key is all 0s).
48    pub fn is_root(&self) -> bool {
49        self == &ROOT_KEY
50    }
51
52    /// Decode this key from its hexadecimal representation.
53    pub fn from_hex(hex: &str) -> Option<Self> {
54        let mut s = [0; KEY_SIZE];
55        if super::from_hex(hex, &mut s) {
56            Some(unsafe { std::mem::transmute(s) })
57        } else {
58            None
59        }
60    }
61}
62
63// A LineId contains a counter encoded little-endian, so that it
64// can both be deterministically put into a Sanakirja database,
65// and passed to standard serializers.
66
67/// An index for file chunks within a patch.
68#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
69pub struct LineId([u8; LINE_ID_SIZE]);
70
71impl ToHex for LineId {
72    fn write_hex<W: Write>(&self, w: &mut W) -> std::fmt::Result {
73        let mut h = String::new();
74        self.0.write_hex(&mut h)?;
75        w.write_str(h.trim_right_matches('0'))
76    }
77    fn write_hex_upper<W: Write>(&self, w: &mut W) -> std::fmt::Result {
78        let mut h = String::new();
79        self.0.write_hex_upper(&mut h)?;
80        w.write_str(h.trim_right_matches('0'))
81    }
82}
83
84impl std::fmt::Debug for LineId {
85    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
86        write!(fmt, "LineId(0x{})", self.to_hex())
87    }
88}
89
90impl LineId {
91    /// Creates a new `LineId`, initialized to 0.
92    pub fn new() -> LineId {
93        LineId([0; LINE_ID_SIZE])
94    }
95    /// Is this line identifier all 0?
96    pub fn is_root(&self) -> bool {
97        self.0.iter().all(|x| *x == 0)
98    }
99    pub fn to_hex(&self) -> String {
100        let mut s = String::new();
101        self.write_hex(&mut s).unwrap();
102        s
103    }
104}
105use byteorder::{ByteOrder, LittleEndian};
106impl std::ops::Add<usize> for LineId {
107    type Output = LineId;
108    fn add(self, x: usize) -> Self::Output {
109        let a = LittleEndian::read_u64(&self.0);
110        let mut b = LineId::new();
111        LittleEndian::write_u64(&mut b.0, a + x as u64);
112        b
113    }
114}
115impl std::ops::AddAssign<usize> for LineId {
116    fn add_assign(&mut self, x: usize) {
117        *self = self.clone() + x
118    }
119}
120
121/// A node in the repository graph, made of a patch internal
122/// identifier, and a line identifier in that patch.
123#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
124pub struct Key<H> {
125    /// The patch that introduced this node.
126    pub patch: H,
127    /// The line identifier of the node in that patch. Here,
128    /// "line" does not imply anything on the contents of the
129    /// chunk.
130    pub line: LineId,
131}
132
133#[test]
134fn test_key_alignment() {
135    assert_eq!(std::mem::size_of::<Key<PatchId>>(), 16)
136}
137
138impl<T> AsRef<LineId> for Key<T> {
139    fn as_ref(&self) -> &LineId {
140        &self.line
141    }
142}
143
144impl<T: Clone> Key<Option<T>> {
145    pub fn unwrap_patch(&self) -> Key<T> {
146        Key {
147            patch: self.patch.as_ref().unwrap().clone(),
148            line: self.line.clone(),
149        }
150    }
151}
152
153impl Representable for Key<PatchId> {
154    fn alignment() -> Alignment {
155        Alignment::B1
156    }
157    fn onpage_size(&self) -> u16 {
158        (PATCH_ID_SIZE + LINE_ID_SIZE) as u16
159    }
160    unsafe fn write_value(&self, p: *mut u8) {
161        trace!("write_value {:?}", p);
162        std::ptr::copy(self as *const Key<PatchId> as *const u8, p, KEY_SIZE)
163    }
164    unsafe fn read_value(p: *const u8) -> Self {
165        trace!("read_value {:?}", p);
166        let mut k = std::mem::uninitialized();
167        std::ptr::copy(p, (&mut k) as *mut Key<PatchId> as *mut u8, 16);
168        k
169    }
170    unsafe fn cmp_value<T>(&self, _: &T, x: Self) -> std::cmp::Ordering {
171        self.cmp(&x)
172    }
173    type PageOffsets = std::iter::Empty<u64>;
174    fn page_offsets(&self) -> Self::PageOffsets {
175        std::iter::empty()
176    }
177}