Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions src/crypto/wordcoding/WordCode.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
use std::collections::HashMap;

pub struct WordCode {
dict_name: String,
words: Vec<String>,
bits_per_word: f64,
normalizer: Box<dyn Fn(String) -> String>,
word_positions: Option<HashMap<String, usize>>,
}

impl WordCode {
pub fn new(
dict_name: String,
words: Vec<String>,
normalizer: Option<Box<dyn Fn(String) -> String>>,
) -> Self {
let bits_per_word = (words.len() as f64).log2();
let normalizer = normalizer.unwrap_or_else(|| Box::new(|x: String| x.to_lowercase()));

WordCode {
dict_name,
words,
bits_per_word,
normalizer,
word_positions: None,
}
}

fn fill_word_positions(&mut self) {
if self.word_positions.is_none() {
let mut word_positions = HashMap::new();
for (pos, word) in self.words.iter().enumerate() {
word_positions.insert((self.normalizer)(word.clone()), pos);
}
self.word_positions = Some(word_positions);
}
}

pub fn encode(&self, hex: &str) -> Result<Vec<String>, String> {
let nibbles_per_word = (self.bits_per_word / 4.0).ceil() as usize;

if hex.len() % nibbles_per_word != 0 {
return Err("Hex string length is not a multiple of the bits-per-word constant.".to_string());
}

let words = hex
.as_bytes()
.chunks(nibbles_per_word)
.map(|chunk| {
let chunk_str = String::from_utf8_lossy(chunk);
let pos = usize::from_str_radix(&chunk_str, 16).unwrap();
self.words.get(pos).unwrap().clone()
})
.collect();

Ok(words)
}

pub fn decode(&mut self, words: &[String]) -> Result<String, String> {
self.fill_word_positions();

let nibbles_per_word = (self.bits_per_word / 4.0).ceil() as usize;

let mut result = String::new();

for word in words {
let position = self
.word_positions
.as_ref()
.unwrap()
.get(&(self.normalizer)(word.clone()));

match position {
Some(pos) => {
result.push_str(&format!("{:0width$X}", pos, width = nibbles_per_word));
}
None => {
return Err(format!(
"Received a word that is not in the dictionary '{}': {}",
self.dict_name, word
));
}
}
}

Ok(result)
}

pub fn check(&mut self, word: &str) -> bool {
self.fill_word_positions();
self.word_positions.as_ref().unwrap().contains_key(&(self.normalizer)(word.to_string()))
}
}
57 changes: 57 additions & 0 deletions src/data/model/immutable/HashedObject.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use std::collections::{HashMap, HashSet};
use crate::storage::store::Store;
use crate::crypto::random::RNGImpl;
use crate::identity::Identity;
use crate::hashing::{Hashing, Hash};
use crate::serialization::Serialization;
use crate::immutable::{HashedSet, HashReference, HashedMap};
use crate::literals::{Context, LiteralContext};
use crate::mesh::service::Mesh;
use crate::spaces::spaces::Resources;
use crate::literals::literal_utils::{Literal, Dependency};
use crate::util::logging::{Logger, LogLevel};
use crate::literals::class_registry::ClassRegistry;
use crate::util::events::EventRelay;
use crate::mutable::MutationObserver;

const BITS_FOR_ID: u32 = 128;

pub trait HashedObject {
fn get_class_name(&self) -> String;
fn init(&mut self);
async fn validate(&self, references: &HashMap<Hash, Box<dyn HashedObject>>) -> bool;
fn get_id(&self) -> Option<String>;
fn set_id(&mut self, id: String);
fn set_random_id(&mut self);
fn has_id(&self) -> bool;
fn set_author(&mut self, author: Identity);
fn get_author(&self) -> Option<&Identity>;
fn has_author(&self) -> bool;
fn has_last_signature(&self) -> bool;
fn set_last_signature(&mut self, signature: String);
fn get_last_signature(&self) -> String;
fn override_children_id(&mut self);
fn override_id_for_path(&mut self, path: String, target: &mut dyn HashedObject);
fn has_store(&self) -> bool;
fn set_store(&mut self, store: Store);
fn get_store(&self) -> Store;
fn get_mesh(&self) -> Mesh;
fn has_last_literal(&self) -> bool;
fn get_last_literal(&self) -> Option<&Literal>;
fn set_last_literal(&mut self, literal: Literal);
fn should_sign_on_save(&self) -> bool;
fn has_last_hash(&self) -> bool;
fn get_last_hash(&self) -> Hash;
fn hash(&mut self, seed: Option<String>) -> Hash;
fn custom_hash(&self, seed: Option<String>) -> Option<Hash>;
fn create_reference(&self) -> HashReference<Self>
where
Self: Sized;
fn equals(&self, another: Option<&dyn HashedObject>) -> bool;
fn clone(&self) -> Box<dyn HashedObject>;
fn add_derived_field(&mut self, field_name: String, object: Option<Box<dyn HashedObject>>);
fn set_derived_field(&mut self, field_name: String, object: Box<dyn HashedObject>);
fn check_derived_field(&self, field_name: &str) -> bool;
fn get_derived_field_id(&self, field_name: &str) -> Hash;
fn set_resources(&mut self, resources: Resources);
}
121 changes: 121 additions & 0 deletions src/data/model/literals/Context.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
use std::collections::{HashMap, HashSet};
use crate::hashed_object::HashedObject;
use crate::hashing::{Hash, Literal};
use crate::spaces::Resources;

pub type LiteralContext = (Vec<Hash>, HashMap<Hash, Literal>);

pub fn is_literal_context(obj: &dyn Any) -> bool {
obj.downcast_ref::<LiteralContext>().is_some()
}

pub struct Context {
pub root_hashes: Vec<Hash>,
pub objects: HashMap<Hash, HashedObject>,
pub literals: HashMap<Hash, Literal>,
pub resources: Option<Resources>,
}

impl Context {
pub fn new() -> Self {
Self {
root_hashes: Vec::new(),
objects: HashMap::new(),
literals: HashMap::new(),
resources: None,
}
}

pub fn has(&self, hash: &Hash) -> bool {
self.literals.contains_key(hash)
|| self.objects.contains_key(hash)
|| self
.resources
.as_ref()
.map(|r| r.aliasing.as_ref().map(|a| a.contains_key(hash)).unwrap_or(false))
.unwrap_or(false)
}

pub fn to_literal_context(&self) -> LiteralContext {
(
self.root_hashes.clone(),
self.literals.clone(),
)
}

pub fn from_literal_context(&mut self, literal_context: LiteralContext) {
self.root_hashes = literal_context.0;
self.literals = literal_context.1;
self.objects = HashMap::new();
}

pub fn merge(&mut self, other: &Context) {
let roots: HashSet<Hash> = self
.root_hashes
.iter()
.chain(other.root_hashes.iter())
.cloned()
.collect();
self.root_hashes = roots.into_iter().collect();

for (hash, literal) in &other.literals {
self.literals.entry(*hash).or_insert_with(|| literal.clone());
}

for (hash, obj) in &other.objects {
self.objects.entry(*hash).or_insert_with(|| obj.clone());
}

if let Some(ref mut resources) = self.resources {
if let Some(ref other_aliasing) = other.resources.as_ref().and_then(|r| r.aliasing.as_ref()) {
if resources.aliasing.is_none() {
resources.aliasing = Some(HashMap::new());
}
let aliasing = resources.aliasing.as_mut().unwrap();
for (hash, aliased) in other_aliasing {
aliasing.entry(*hash).or_insert_with(|| aliased.clone());
}
}
} else {
self.resources = other.resources.clone();
}
}

pub fn copy(&self) -> Context {
let mut another = Context::new();
another.merge(self);
another
}

// if a dependency is in more than one subobject, it will pick one of the shortest dep chains.
pub fn find_missing_deps(&self, hash: &Hash, chain: Option<Vec<Hash>>, missing: Option<HashMap<Hash, Vec<Hash>>>) -> HashMap<Hash, Vec<Hash>> {
let mut chain = chain.unwrap_or_default();
let mut missing = missing.unwrap_or_default();

if let Some(literal) = self.literals.get(hash) {
for dep in &literal.dependencies {
let mut new_chain = chain.clone();
new_chain.insert(0, *hash);
let new_missing = self.find_missing_deps(dep.hash, Some(new_chain), Some(missing));
missing = missing.into_iter().chain(new_missing).collect();
}
} else {
let prev_chain = missing.get(hash);
if prev_chain.is_none() || chain.len() < prev_chain.unwrap().len() {
missing.insert(*hash, chain);
}
}

missing
}

pub fn check_literal_hashes(&self) -> bool {
self.literals.iter().all(|(hash, literal)| {
hash == &literal.hash && literal.validate_hash()
})
}

pub fn check_root_hashes(&self) -> bool {
self.root_hashes.iter().all(|hash| self.literals.contains_key(hash))
}
}