1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
use std::fmt::Display;

use crate::{digest::PageDigest, Page};

/// A serialised representation of the range of keys contained within the
/// sub-tree rooted at a given [`Page`], and the associated [`PageDigest`].
///
/// A [`PageRange`] contains all the information needed to perform a tree
/// difference calculation, used as the input to the [`diff()`] function.
///
/// The contents of this type can be serialised and transmitted over the
/// network, and reconstructed by the receiver by calling [`PageRange::new()`]
/// with the serialised values.
///
/// # Exchanging Between Peers
///
/// Exchange the ordered sets of [`PageRange`] between peers by serialising
/// their content, accessed through the accessor methods:
///
/// ```rust
/// # use std::ops::Deref;
/// # use merkle_search_tree::{*, diff::PageRange};
/// #
/// /// A network wire representation used by the application.
/// struct NetworkPage {
///     start_bounds: String,
///     end_bounds: String,
///     hash: [u8; 16],
/// }
///
/// let mut t = MerkleSearchTree::default();
/// t.upsert("bananas".to_string(), &"platanos".to_string());
/// t.root_hash();
///
/// let network_request: Vec<NetworkPage> = t
///     .serialise_page_ranges()
///     .unwrap()
///     .into_iter()
///     .map(|page| NetworkPage {
///         start_bounds: page.start().clone(),
///         end_bounds: page.end().clone(),
///         hash: *page.hash().as_bytes(),
///     })
///     .collect();
///
/// // Send network_request to a peer over the network
/// ```
///
/// And reconstruct the [`PageRange`] on the receiver:
///
/// ```rust
/// # use merkle_search_tree::diff::PageRange;
/// # use merkle_search_tree::digest::*;
/// #
/// # struct NetworkPage {
/// #     start_bounds: String,
/// #     end_bounds: String,
/// #     hash: [u8; 16],
/// # }
/// #
/// # let network_request: Vec<NetworkPage> = vec![];
/// // Receive network_request from a peer over the network
///
/// // PageRange construction is zero-copy for the page keys, borrowing the keys
/// // from the underlying network request.
/// let page_refs = network_request
///     .iter()
///     .map(|p| {
///         // If this request is coming from an untrusted source, validate that
///         // start <= end to avoid the PageRange constructor panic.
///         PageRange::new(&p.start_bounds, &p.end_bounds, PageDigest::new(p.hash))
///     })
///     .collect::<Vec<_>>();
///
/// // Feed page_refs into diff()
/// ```
///
/// # Borrowed vs. Owned
///
/// A [`PageRange`] borrows the keys from the tree to avoid unnecessary clones,
/// retaining an immutable reference to the tree.
///
/// If an owned / long-lived set of [`PageRange`] is desired (avoiding the
/// immutable reference to the tree), generate a [`PageRangeSnapshot`] from the
/// set of [`PageRange`].
///
/// [`diff()`]: crate::diff::diff
/// [`PageRangeSnapshot`]: crate::diff::PageRangeSnapshot
#[derive(Debug, PartialEq)]
pub struct PageRange<'a, K> {
    /// The inclusive start & end key bounds of this range.
    start: &'a K,
    end: &'a K,

    /// The hash of this page, and the sub-tree rooted at it.
    hash: PageDigest,
}

impl<'a, K> Display for PageRange<'a, K>
where
    K: Display,
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_fmt(format_args!("({}, {})", self.start, self.end))
    }
}

impl<'a, K> Clone for PageRange<'a, K> {
    fn clone(&self) -> Self {
        Self {
            start: self.start,
            end: self.end,
            hash: self.hash.clone(),
        }
    }
}

impl<'a, const N: usize, K> From<&'a Page<N, K>> for PageRange<'a, K> {
    #[inline(always)]
    fn from(page: &'a Page<N, K>) -> Self {
        PageRange {
            start: page.min_subtree_key(),
            end: page.max_subtree_key(),
            hash: page
                .hash()
                .expect("page visitor requires prior hash regeneration")
                .clone(),
        }
    }
}

impl<'a, K> PageRange<'a, K> {
    /// Construct a [`PageRange`] for the given key interval and [`PageDigest`].
    ///
    /// # Panics
    ///
    /// If `start` is greater than `end`, this method panics.
    pub fn new(start: &'a K, end: &'a K, hash: PageDigest) -> Self
    where
        K: PartialOrd,
    {
        assert!(start <= end);
        Self { start, end, hash }
    }

    /// Returns the inclusive start of this [`PageRange`].
    pub fn start(&self) -> &'a K {
        self.start
    }

    /// Returns the inclusive end of this [`PageRange`]
    pub fn end(&self) -> &'a K {
        self.end
    }

    /// Returns true if the range within `self` overlaps any portion of the
    /// range within `p`.
    pub(crate) fn overlaps(&self, p: &Self) -> bool
    where
        K: PartialOrd,
    {
        //   0 1 2 3 4 5 6 7 8 9
        // A |       |
        // B       |   |
        let leading_edge = self.start <= p.start && self.end >= p.start;
        let trailing_edge = p.start <= self.end && p.end >= self.end;
        leading_edge || trailing_edge
    }

    /// Returns true if `self` is a superset of `other` (not a strict superset -
    /// equal ranges are treated as supersets of each other).
    pub(crate) fn is_superset_of(&self, other: &Self) -> bool
    where
        K: PartialOrd,
    {
        self.start <= other.start && self.end >= other.end
    }

    /// Returns the [`PageDigest`] of this page, representing the content of the
    /// page and all pages within the sub-tree rooted at it.
    pub fn hash(&self) -> &PageDigest {
        &self.hash
    }

    /// Consume this [`PageRange`], returning the [`PageDigest`] that covers the
    /// subtree rooted at this page.
    pub fn into_hash(self) -> PageDigest {
        self.hash
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{digest::Digest, MerkleSearchTree};

    /// Ensure the public API allows for serialisation & deserialisation of
    /// [`PageRange`].
    #[test]
    fn test_round_trip_api() {
        struct NetworkPage {
            start_bounds: String,
            end_bounds: String,
            hash: [u8; 16],
        }

        let mut t = MerkleSearchTree::default();
        t.upsert("bananas".to_string(), &"platanos".to_string());
        t.root_hash();

        let page_ranges = t.serialise_page_ranges().unwrap();

        // Serialise
        let network_pages: Vec<NetworkPage> = page_ranges
            .iter()
            .map(|v| NetworkPage {
                start_bounds: v.start().clone(),
                end_bounds: v.end().clone(),
                hash: *v.hash().as_bytes(),
            })
            .collect();

        // Deserialise
        let got: Vec<PageRange<'_, String>> = network_pages
            .iter()
            .map(|v| PageRange::new(&v.start_bounds, &v.end_bounds, PageDigest::new(v.hash)))
            .collect();

        assert_eq!(page_ranges, got);
    }

    #[test]
    #[should_panic(expected = "start <= end")]
    fn test_start_gt_end_panic() {
        let _p = PageRange::new(
            &42,
            &24,
            PageDigest::from(Digest::new([
                1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
            ])),
        );
    }
}