Skip to content

Commit

Permalink
simple_op_store: hash view/operation data directly
Browse files Browse the repository at this point in the history
  • Loading branch information
Ralith committed Nov 12, 2022
1 parent ade831b commit c7ed2e8
Show file tree
Hide file tree
Showing 7 changed files with 263 additions and 77 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ bytes = "1.2.1"
byteorder = "1.4.3"
chrono = { version = "0.4.22", default-features = false, features = ["std", "clock"] }
config = { version = "0.13.2", default-features = false, features = ["toml"] }
digest = "0.10.5"
git2 = "0.15.0"
hex = "0.4.3"
itertools = "0.10.5"
Expand Down
25 changes: 20 additions & 5 deletions lib/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,18 @@ use std::vec::Vec;

use thiserror::Error;

use crate::content_hash::ContentHash;
use crate::repo_path::{RepoPath, RepoPathComponent};

#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
pub struct CommitId(Vec<u8>);

impl ContentHash for CommitId {
fn hash(&self, state: &mut impl digest::Update) {
self.0.hash(state);
}
}

impl Debug for CommitId {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
f.debug_tuple("CommitId").field(&self.hex()).finish()
Expand Down Expand Up @@ -228,11 +235,19 @@ pub enum Phase {
#[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord)]
pub struct MillisSinceEpoch(pub i64);

#[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord)]
pub struct Timestamp {
pub timestamp: MillisSinceEpoch,
// time zone offset in minutes
pub tz_offset: i32,
impl ContentHash for MillisSinceEpoch {
fn hash(&self, state: &mut impl digest::Update) {
self.0.hash(state);
}
}

content_hash! {
#[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord)]
pub struct Timestamp {
pub timestamp: MillisSinceEpoch,
// time zone offset in minutes
pub tz_offset: i32,
}
}

impl Timestamp {
Expand Down
133 changes: 133 additions & 0 deletions lib/src/content_hash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
use itertools::Itertools as _;

/// Portable, stable hashing suitable for identifying values
pub trait ContentHash {
fn hash(&self, state: &mut impl digest::Update);
}

impl ContentHash for u8 {
fn hash(&self, state: &mut impl digest::Update) {
state.update(&[*self]);
}
}

impl ContentHash for i32 {
fn hash(&self, state: &mut impl digest::Update) {
state.update(&self.to_le_bytes());
}
}

impl ContentHash for i64 {
fn hash(&self, state: &mut impl digest::Update) {
state.update(&self.to_le_bytes());
}
}

// TODO: Specialize for [u8] once specialization exists
impl<T: ContentHash> ContentHash for [T] {
fn hash(&self, state: &mut impl digest::Update) {
state.update(&(self.len() as u64).to_le_bytes());
for x in self {
x.hash(state);
}
}
}

impl<T: ContentHash> ContentHash for Vec<T> {
fn hash(&self, state: &mut impl digest::Update) {
self.as_slice().hash(state)
}
}

impl ContentHash for String {
fn hash(&self, state: &mut impl digest::Update) {
self.as_bytes().hash(state);
}
}

impl<T: ContentHash> ContentHash for Option<T> {
fn hash(&self, state: &mut impl digest::Update) {
match *self {
None => state.update(&[0]),
Some(ref x) => {
state.update(&[1]);
x.hash(state)
}
}
}
}

impl<K, V> ContentHash for std::collections::HashMap<K, V>
where
K: ContentHash + Ord,
V: ContentHash + Ord,
{
fn hash(&self, state: &mut impl digest::Update) {
state.update(&(self.len() as u64).to_le_bytes());
for (k, v) in self.iter().sorted() {
k.hash(state);
v.hash(state);
}
}
}

impl<K> ContentHash for std::collections::HashSet<K>
where
K: ContentHash + Ord,
{
fn hash(&self, state: &mut impl digest::Update) {
state.update(&(self.len() as u64).to_le_bytes());
for k in self.iter().sorted() {
k.hash(state);
}
}
}

impl<K, V> ContentHash for std::collections::BTreeMap<K, V>
where
K: ContentHash,
V: ContentHash,
{
fn hash(&self, state: &mut impl digest::Update) {
state.update(&(self.len() as u64).to_le_bytes());
for (k, v) in self.iter() {
k.hash(state);
v.hash(state);
}
}
}

macro_rules! content_hash {
($(#[$meta:meta])* $vis:vis struct $name:ident { $($(#[$field_meta:meta])* $field_vis:vis $field:ident : $ty:ty),* $(,)? }) => {
$(#[$meta])*
$vis struct $name {
$($(#[$field_meta])* $field_vis $field : $ty),*
}

impl crate::content_hash::ContentHash for $name {
fn hash(&self, state: &mut impl digest::Update) {
$(<$ty as crate::content_hash::ContentHash>::hash(&self.$field, state);)*
}
}
};
($(#[$meta:meta])* $vis:vis enum $name:ident { $($variant:ident { $($field:ident : $ty:ty),* $(,)? }),* $(,)? }) => {
$(#[$meta])*
$vis enum $name {
$($variant { $($field : $ty),* }),*
}

impl crate::content_hash::ContentHash for $name {
fn hash(&self, state: &mut impl digest::Update) {
let mut counter: u32 = 0;
$(
if let Self::$variant { $(ref $field,)* } = *self {
state.update(&counter.to_le_bytes());
$(<$ty as crate::content_hash::ContentHash>::hash($field, state);)*
}
counter += 1;
)*
_ = counter;
}
}
};
}
3 changes: 3 additions & 0 deletions lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@

#![deny(unused_must_use)]

#[macro_use]
mod content_hash;

pub mod backend;
pub mod commit;
pub mod commit_builder;
Expand Down

0 comments on commit c7ed2e8

Please sign in to comment.