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
use crate::Result;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use merkle_tree_stream::Node as NodeTrait;
use merkle_tree_stream::{NodeKind, NodeParts};
use pretty_hash::fmt as pretty_fmt;
use std::cmp::Ordering;
use std::convert::AsRef;
use std::fmt::{self, Display};
use std::io::Cursor;

use crate::crypto::Hash;

/// Nodes that are persisted to disk.
// TODO: replace `hash: Vec<u8>` with `hash: Hash`. This requires patching /
// rewriting the Blake2b crate to support `.from_bytes()` to serialize from
// disk.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Node {
    pub(crate) index: usize,
    pub(crate) hash: Vec<u8>,
    pub(crate) length: u64,
    pub(crate) parent: usize,
    pub(crate) data: Option<Vec<u8>>,
}

impl Node {
    /// Create a new instance.
    // TODO: ensure sizes are correct.
    pub fn new(index: usize, hash: Vec<u8>, length: usize) -> Self {
        Self {
            index,
            hash,
            length: length as u64,
            parent: flat_tree::parent(index),
            data: Some(Vec::with_capacity(0)),
        }
    }

    /// Convert a vector to a new instance.
    ///
    /// Requires the index at which the buffer was read to be passed.
    pub fn from_bytes(index: usize, buffer: &[u8]) -> Result<Self> {
        ensure!(buffer.len() == 40, "buffer should be 40 bytes");

        let parent = flat_tree::parent(index);
        let mut reader = Cursor::new(buffer);

        // TODO: subslice directly, move cursor forward.
        let capacity = 32;
        let mut hash = Vec::with_capacity(capacity);
        for _ in 0..capacity {
            hash.push(reader.read_u8()?);
        }

        let length = reader.read_u64::<BigEndian>()?;
        Ok(Self {
            hash,
            length,
            index,
            parent,
            data: Some(Vec::with_capacity(0)),
        })
    }

    /// Convert to a buffer that can be written to disk.
    pub fn to_bytes(&self) -> Result<Vec<u8>> {
        let mut writer = Vec::with_capacity(40);
        writer.extend_from_slice(&self.hash);
        writer.write_u64::<BigEndian>(self.length as u64)?;
        Ok(writer)
    }
}

impl NodeTrait for Node {
    #[inline]
    fn index(&self) -> usize {
        self.index
    }

    #[inline]
    fn hash(&self) -> &[u8] {
        &self.hash
    }

    #[inline]
    fn len(&self) -> usize {
        self.length as usize
    }

    #[inline]
    fn is_empty(&self) -> bool {
        self.length == 0
    }

    #[inline]
    fn parent(&self) -> usize {
        self.parent
    }
}

impl AsRef<Node> for Node {
    #[inline]
    fn as_ref(&self) -> &Self {
        self
    }
}

impl Display for Node {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "Node {{ index: {}, hash: {}, length: {} }}",
            self.index,
            pretty_fmt(&self.hash).unwrap(),
            self.length
        )
    }
}

impl PartialOrd for Node {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.index.cmp(&other.index))
    }
}

impl Ord for Node {
    fn cmp(&self, other: &Self) -> Ordering {
        self.index.cmp(&other.index)
    }
}

impl From<NodeParts<Hash>> for Node {
    fn from(parts: NodeParts<Hash>) -> Self {
        let partial = parts.node();
        let data = match partial.data() {
            NodeKind::Leaf(data) => Some(data.clone()),
            NodeKind::Parent => None,
        };

        Node {
            index: partial.index(),
            parent: partial.parent,
            length: partial.len() as u64,
            hash: parts.hash().as_bytes().into(),
            data,
        }
    }
}