mirror of
https://github.com/aljazceru/pubky-core.git
synced 2025-12-31 12:54:35 +01:00
wip: Mast zip tree
This commit is contained in:
58
Cargo.lock
generated
58
Cargo.lock
generated
@@ -130,6 +130,12 @@ dependencies = [
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "doc-comment"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
@@ -170,6 +176,15 @@ version = "0.2.150"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
|
||||
|
||||
[[package]]
|
||||
name = "mast"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"blake3",
|
||||
"bytes",
|
||||
"varu64",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "password-hash"
|
||||
version = "0.5.0"
|
||||
@@ -235,12 +250,44 @@ dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "snafu"
|
||||
version = "0.6.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eab12d3c261b2308b0d80c26fffb58d17eba81a4be97890101f416b478c79ca7"
|
||||
dependencies = [
|
||||
"doc-comment",
|
||||
"snafu-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "snafu-derive"
|
||||
version = "0.6.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1508efa03c362e23817f96cde18abed596a25219a8b2c66e8db33c03543d315b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.39"
|
||||
@@ -269,7 +316,7 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.39",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -284,6 +331,15 @@ version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "varu64"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd0b4bcb210ab3a048eda9a938508b072e474af2838994e77976c817e51af1e3"
|
||||
dependencies = [
|
||||
"snafu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"kytz",
|
||||
"mast",
|
||||
]
|
||||
|
||||
# See: https://github.com/rust-lang/rust/issues/90148#issuecomment-949194352
|
||||
|
||||
11
mast/Cargo.toml
Normal file
11
mast/Cargo.toml
Normal file
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "mast"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
blake3 = "1.5.0"
|
||||
bytes = "1.5.0"
|
||||
varu64 = "0.7.0"
|
||||
18
mast/src/lib.rs
Normal file
18
mast/src/lib.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
#![allow(unused)]
|
||||
|
||||
mod node;
|
||||
mod storage;
|
||||
mod tree;
|
||||
|
||||
use crate::node::Node;
|
||||
|
||||
// // TODO: maybe add nonce for encrypted trees.
|
||||
// // TODO: why add header in each node? or in the Mast commit?
|
||||
// // Would we need to read a node without traversing down from the Mast commit?
|
||||
// pub struct Mast {
|
||||
// /// The name of this Mast to be used as a prefix for all nodes
|
||||
// /// in the storage, seperating different Masts.
|
||||
// name: String,
|
||||
// root: Option<Hash>,
|
||||
// storage: MastStorage,
|
||||
// }
|
||||
3
mast/src/main.rs
Normal file
3
mast/src/main.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
||||
129
mast/src/node.rs
Normal file
129
mast/src/node.rs
Normal file
@@ -0,0 +1,129 @@
|
||||
//! Zip tree node.
|
||||
|
||||
use blake3::{Hash, Hasher};
|
||||
use bytes::{BufMut, Bytes, BytesMut};
|
||||
|
||||
pub const HASH_LEN: usize = blake3::OUT_LEN;
|
||||
pub const EMPTY_HASH: Hash = Hash::from_bytes([0_u8; HASH_LEN]);
|
||||
|
||||
#[derive(Debug)]
|
||||
/// A serialized node.
|
||||
pub struct Node {
|
||||
key: String,
|
||||
value: Hash,
|
||||
left: Option<Hash>,
|
||||
right: Option<Hash>,
|
||||
}
|
||||
|
||||
impl Node {
|
||||
pub fn new(key: String, value: Hash) -> Self {
|
||||
Self {
|
||||
key,
|
||||
value,
|
||||
left: None,
|
||||
right: None,
|
||||
}
|
||||
}
|
||||
|
||||
// === Getters ===
|
||||
|
||||
// pub fn key(&self) -> &String {
|
||||
// &self.key
|
||||
// }
|
||||
|
||||
// pub fn left(&self) -> Option<Hash> {
|
||||
// self.left
|
||||
// }
|
||||
|
||||
// === Public Methods ===
|
||||
//
|
||||
pub fn serialize(&self) -> Bytes {
|
||||
let mut bytes = BytesMut::new();
|
||||
|
||||
let mut header = 0_u8;
|
||||
if self.left.is_some() {
|
||||
header |= 0b0000_0010;
|
||||
}
|
||||
if self.right.is_some() {
|
||||
header |= 0b0000_0001;
|
||||
}
|
||||
bytes.put_u8(0_u8);
|
||||
|
||||
// Encode the key
|
||||
let mut key_len = [0_u8; 9];
|
||||
let size = varu64::encode(self.key.len() as u64, &mut key_len);
|
||||
bytes.extend_from_slice(&key_len[..size]);
|
||||
bytes.extend_from_slice(self.key.as_bytes());
|
||||
|
||||
// Encode the value
|
||||
bytes.extend_from_slice(self.value.as_bytes());
|
||||
|
||||
if let Some(left) = self.left {
|
||||
bytes.extend_from_slice(left.as_bytes());
|
||||
}
|
||||
if let Some(right) = self.right {
|
||||
bytes.extend_from_slice(right.as_bytes());
|
||||
}
|
||||
|
||||
bytes.freeze()
|
||||
}
|
||||
|
||||
// pub fn hash(&self) -> Hash {
|
||||
// let mut hasher = Hasher::new();
|
||||
// hasher.update(&self.serialize());
|
||||
// hasher.finalize().into()
|
||||
// }
|
||||
|
||||
pub fn deserialize(encoded: &Bytes) -> Result<Node, ()> {
|
||||
let header = encoded.first().ok_or(())?;
|
||||
|
||||
let (n, rest) = varu64::decode(&encoded[1..]).map_err(|_| ())?;
|
||||
let key_len = n as usize;
|
||||
let key = String::from_utf8(rest[..key_len].to_vec()).map_err(|_| ())?;
|
||||
let value = Hash::from_bytes(
|
||||
rest[key_len..key_len + HASH_LEN]
|
||||
.try_into()
|
||||
.map_err(|_| ())?,
|
||||
);
|
||||
|
||||
let mut left: Option<Hash> = None;
|
||||
let mut right: Option<Hash> = None;
|
||||
|
||||
if *header & 0b0000_0010 != 0 {
|
||||
let start = key_len + HASH_LEN;
|
||||
let end = start + HASH_LEN;
|
||||
|
||||
left = Some(Hash::from_bytes(
|
||||
rest[start..end].try_into().map_err(|_| ())?,
|
||||
))
|
||||
}
|
||||
if *header & 0b0000_0001 != 0 {
|
||||
let start = key_len + HASH_LEN + if left.is_some() { HASH_LEN } else { 0 };
|
||||
let end = start + HASH_LEN;
|
||||
|
||||
right = Some(Hash::from_bytes(
|
||||
rest[start..end].try_into().map_err(|_| ())?,
|
||||
))
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
key,
|
||||
value,
|
||||
left,
|
||||
right,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn encode() {
|
||||
let node = Node::new("key".to_string(), EMPTY_HASH);
|
||||
|
||||
let encoded = node.serialize();
|
||||
let decoded = Node::deserialize(&encoded);
|
||||
}
|
||||
}
|
||||
36
mast/src/storage/memory.rs
Normal file
36
mast/src/storage/memory.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
//! In memory Mast storage.
|
||||
|
||||
use blake3::{Hash, Hasher};
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use std::collections::HashMap;
|
||||
|
||||
// TODO: storage abstraction.
|
||||
#[derive(Debug)]
|
||||
pub struct Storage {
|
||||
storage: HashMap<Hash, Bytes>,
|
||||
}
|
||||
|
||||
impl Storage {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
storage: HashMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, hash: &Hash) -> Option<Bytes> {
|
||||
self.storage.get(hash).cloned()
|
||||
}
|
||||
|
||||
pub fn insert_bytes(&mut self, bytes: Bytes) -> Hash {
|
||||
let hash = Hasher::new().update(&bytes).finalize();
|
||||
// TODO: should I add a prefix here?
|
||||
self.storage.insert(hash, bytes);
|
||||
hash
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Storage {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
1
mast/src/storage/mod.rs
Normal file
1
mast/src/storage/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod memory;
|
||||
100
mast/src/tree.rs
Normal file
100
mast/src/tree.rs
Normal file
@@ -0,0 +1,100 @@
|
||||
//! Zip tree implementation.
|
||||
|
||||
use blake3::{Hash, Hasher};
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use std::collections::btree_map::BTreeMap;
|
||||
use std::fmt::{self, Debug, Display, Formatter};
|
||||
|
||||
use crate::node::Node;
|
||||
use crate::storage;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ZipTree {
|
||||
root: Option<Node>,
|
||||
storage: storage::memory::Storage,
|
||||
}
|
||||
|
||||
impl ZipTree {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
root: None,
|
||||
storage: storage::memory::Storage::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, key: &[u8], value: &[u8]) -> &mut Self {
|
||||
// let node = Node::new(key, value);
|
||||
//
|
||||
// let hash = node.hash();
|
||||
//
|
||||
// self.storage.insert(hash, node.serialize().into());
|
||||
// self.root = Some(hash);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
// pub fn node(&self, hash: &Hash) -> Option<Node> {
|
||||
// if let Some(encoded_node) = self.storage.get(hash) {
|
||||
// return Node::deserialize(encoded_node).ok().or(None);
|
||||
// };
|
||||
//
|
||||
// None
|
||||
// }
|
||||
}
|
||||
|
||||
// impl Display for ZipTree {
|
||||
// fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
// writeln!(f, "graph TD;");
|
||||
//
|
||||
// let mut stack: Vec<Node> = Vec::new();
|
||||
//
|
||||
// if let Some(root_id) = self.root {
|
||||
// if let Some(node) = self.node(&root_id) {
|
||||
// stack.push(node);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// while let Some(node) = stack.pop() {
|
||||
// let left = self.node(&node.left());
|
||||
// let right = self.node(&node.right());
|
||||
//
|
||||
// match (&left, &right) {
|
||||
// (None, None) => {
|
||||
// writeln!(f, " {:?}", node.key());
|
||||
// }
|
||||
// _ => {
|
||||
// if let Some(left) = left {
|
||||
// writeln!(f, " {:?} --> {:?}", node.key(), left.key());
|
||||
// }
|
||||
// if let Some(right) = right {
|
||||
// writeln!(f, " {:?} --> {:?}", node.key(), right.key());
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Ok(())
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl Debug for ZipTree {
|
||||
// fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
// write!(f, "{}", self);
|
||||
//
|
||||
// Ok(())
|
||||
// }
|
||||
// }
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_new() {
|
||||
let mut tree = ZipTree::new();
|
||||
|
||||
tree.insert(b"foo", b"bar");
|
||||
|
||||
dbg!(tree);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user