diff --git a/Cargo.lock b/Cargo.lock index 7ca6560..d2c3426 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -109,6 +109,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" +[[package]] +name = "difference" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" + [[package]] name = "digest" version = "0.8.1" @@ -944,6 +950,20 @@ name = "serde" version = "1.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "serde_json" @@ -1370,7 +1390,9 @@ dependencies = [ name = "yuurei" version = "0.1.0" dependencies = [ + "difference", "futures", + "serde", "tokio", "warp", ] diff --git a/Cargo.toml b/Cargo.toml index 3776af0..0e36122 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,13 @@ [package] name = "yuurei" -description = "ghost text messaging" +description = "ghost text liveboard" version = "0.1.0" authors = ["Avril "] edition = "2018" [dependencies] +difference = "2.0.0" futures = "0.3.8" +serde = {version = "1.0.118", features=["derive"]} tokio = {version = "0.2", features=["full"] } warp = "0.2.5" diff --git a/src/delta.rs b/src/delta.rs new file mode 100644 index 0000000..bcbb83a --- /dev/null +++ b/src/delta.rs @@ -0,0 +1,257 @@ +//! Deltas and applying them +use super::*; +use difference::{ + Changeset, + Difference, +}; + +const MAX_SINGLE_DELTA_SIZE: usize = 14; +const DELTA_BREAK: &str = ""; + +/// Infer all deltas needed to be sequentially applied to `orig` to transform it to `new`, return the number inserted into `output`. +pub fn infer_deltas + ?Sized>(output: &mut T, orig: &str, new: &str) -> usize +{ + let set = Changeset::new(orig, new, DELTA_BREAK); + + let mut done=0; + let mut position =0; + for diff in set.diffs.into_iter() { + match diff { + Difference::Same(string) => { + position += string.len(); + }, + Difference::Rem(string) => { + output.push_back(Delta { + location: position, + kind: DeltaKind::RemoveAhead{span_len: string.len()}, + }); + done+=1; + }, + Difference::Add(string) => { + let mut passer = BackInsertPass::new(|(span, span_len)| { + output.push_back(Delta { + location: position, + kind: DeltaKind::Insert { + span, span_len, + }, + }); + position+= usize::from(span_len); + done+=1; + }); + delta_span_many(&mut passer, string.chars()); + }, + } + } + done +} + +/// Create a delta span from an input iterator. +/// +/// This function can take no more than `min(255, MAX_SINGLE_DELTA_SIZE)` chars from the input. The number of chars inserted is also returned as `u8`. +pub(super) fn delta_span(from: I) -> ([char; MAX_SINGLE_DELTA_SIZE], u8) +where I: IntoIterator +{ + let mut output: [char; MAX_SINGLE_DELTA_SIZE] = Default::default(); + let mut sz: u8 = 0; + + for (d, s) in output.iter_mut().zip(from.into_iter().take(usize::from(u8::MAX))) + { + *d = s; + sz += 1; + } + + (output, sz) +} + +/// Create as many delta spans needed from an input iterator. +/// +/// This function can take as many inputs as needed, and outputs the needed amount of spans into `into`, and then returns the number added. +pub(super) fn delta_span_many + ?Sized, I>(into: &mut T, from: I) -> usize +where I: IntoIterator +{ + let mut iter = from.into_iter(); + let mut added=0; + loop { + match delta_span(&mut iter) { + (_, 0) => break, + other => into.push_back(other), + } + added+=1; + } + added +} + +/// Information about the delta to be applied in `Delta`. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)] +pub enum DeltaKind +{ + /// Append to the end of body. Equivilant to `Insert` with a location at `karada.scape.len()` (the end of the buffer). + Append{ + span: [char; MAX_SINGLE_DELTA_SIZE], + span_len: u8, + }, + /// Insert `span_len` chars from `span` into body starting *at* `location` and moving ahead + Insert{ + span: [char; MAX_SINGLE_DELTA_SIZE], + span_len: u8, + }, + /// Remove `span_len` chars from `location`. + RemoveAhead{ + span_len: usize, + }, + /// Remove `span_len` chars up to but not including `location`. + RemoveBehind{ + span_len: usize, + }, + /// Remove everything from `location` to the end. + Truncate, + /// Remove everything from 0 to `location`. + Shift, + /// Remove char at `location` + RemoveSingle, + /// Remove entire post body + Clear, +} + +/// A delta to apply to `Karada`. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)] +pub struct Delta +{ + /// Location to insert into. This is the INclusive range of: 0..=(karada.scape.len()). + /// + /// Insertions off the end of the buffer are to be appened instead. + location: usize, + /// The kind of delta to insert + kind: DeltaKind, +} + +/// Static assertion: `MAX_SINGLE_DELTA_SIZE` can fit into `u8`. +const _: [u8;(MAX_SINGLE_DELTA_SIZE < (!0u8 as usize)) as usize] = [0]; + +impl Delta +{ + /// Apply this delta to a message + pub fn insert>>(&self, inserter: &mut T) + { + let inserter = inserter.as_mut(); + match self.kind { + DeltaKind::Append{span, span_len} => { + inserter.extend_from_slice(&span[..usize::from(span_len)]); + }, + DeltaKind::Insert{span, span_len} => { + let span = &span[..usize::from(span_len)]; + if self.location == inserter.len() { + inserter.extend_from_slice(span); + } else if span.len() == 1 { + inserter.insert(self.location, span[0]); + } else if span.len() > 1 { + inserter.insert_exact(self.location, span.iter().copied()); + } + }, + DeltaKind::RemoveAhead{span_len} => { + inserter.drain(self.location..(self.location+span_len)); + }, + DeltaKind::RemoveBehind{span_len} => { + let span_st = self.location.checked_sub(span_len).unwrap_or(0); + inserter.drain(span_st..self.location); + }, + DeltaKind::RemoveSingle => { + inserter.remove(self.location); + }, + DeltaKind::Clear => inserter.clear(), + DeltaKind::Truncate => inserter.truncate(self.location), + DeltaKind::Shift => ((),inserter.drain(..self.location)).0, + } + } +} + +#[cfg(test)] +mod tests +{ + use super::*; + + #[test] + fn infer_and_insert() + { + let orig = "the quick brown fox jumped over the lazy dog !!!!"; + let new = "the quick brown dog jumped over the lazy fox twice"; + + let mut deltas = Vec::new(); + infer_deltas(&mut deltas, orig, new); + + println!("{:#?}", deltas); + let output: String = { + let mut scape: Vec<_> = orig.chars().collect(); + for delta in deltas.into_iter() { + delta.insert(&mut scape); + } + scape.into_iter().collect() + }; + + assert_eq!(&output[..], &new[..]); + } + + macro_rules! insert { + ($expects:literal; $start:literal, $ins:literal, $where:expr) => { + { + + let mut message: Vec = $start.chars().collect(); + + let delta = { + let (span, span_len) = delta_span($ins.chars()); + Delta { + location: $where, + kind: DeltaKind::Insert{span, span_len}, + } + }; + + println!("from: {:?}", message); + println!("delta: {:?}", delta); + delta.insert(&mut message); + + assert_eq!(&message.into_iter().collect::()[..], $expects); + } + }; + } + + #[test] + fn insert_body() + { + insert!("123456789"; "126789", "345", 2); + } + #[test] + fn insert_end() + { + insert!("123456789"; "1289", "34567", 2); + } + #[test] + fn insert_end_rev() + { + insert!("123456789"; "1234569", "78", 6); + } + #[test] + fn insert_begin_rev() + { + insert!("123456789"; "1456789", "23", 1); + } + #[test] + fn insert_begin() + { + insert!("123456789"; "789", "123456", 0); + } + #[test] + fn insert_end_f() + { + insert!("123456789"; "123", "456789", 3); + } + #[test] + fn insert_end_f_rev() + { + insert!("123456789"; "1234567", "89", 7); + } + #[test] + fn insert_single() + { + insert!("123456789"; "12346789", "5",4); + } +} diff --git a/src/ext.rs b/src/ext.rs new file mode 100644 index 0000000..926b936 --- /dev/null +++ b/src/ext.rs @@ -0,0 +1,332 @@ +//! Extensions +use std::{ + marker::PhantomData, + fmt, + ops, +}; + +/// Wrapper to derive debug for types that don't implement it. +#[repr(transparent)] +#[derive(Clone, PartialEq, Eq, Ord,PartialOrd, Hash)] +pub struct OpaqueDebug(T); + +impl OpaqueDebug +{ + /// Create a new wrapper + #[inline] pub const fn new(value: T) -> Self + { + Self(value) + } + + /// Consume into the value + #[inline] pub fn into_inner(self) -> T + { + self.0 + } +} + +impl AsRef for OpaqueDebug +{ + #[inline] fn as_ref(&self) -> &T + { + &self.0 + } +} + +impl AsMut for OpaqueDebug +{ + #[inline] fn as_mut(&mut self) -> &mut T + { + &mut self.0 + } +} + +impl ops::Deref for OpaqueDebug +{ + type Target = T; + #[inline] fn deref(&self) -> &Self::Target + { + &self.0 + } +} + +impl ops::DerefMut for OpaqueDebug +{ + #[inline] fn deref_mut(&mut self) -> &mut Self::Target + { + &mut self.0 + } +} + +impl fmt::Debug for OpaqueDebug +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + write!(f, "") + } +} + + +/// A trait for types that can insert objects at their end. +pub trait BackInserter +{ + /// Insert an object at the end of this container + fn push_back(&mut self, value: T); +} + +impl BackInserter for Vec +{ + #[inline] + fn push_back(&mut self, value: T) + { + self.push(value) + } +} + +/// Absracts a closure for `BackInserter`. +pub struct BackInsertPass(F, PhantomData) +where F: FnMut(T); + +impl BackInsertPass +{ + /// Create a new instance with this closure + #[inline] pub fn new(func: F) -> Self + { + Self(func, PhantomData) + } +} + +impl BackInserter for BackInsertPass +{ + #[inline] fn push_back(&mut self, value: T) + { + self.0(value) + } +} + +/// A `BackInserter` that will only add a max capacity of items before it starts dropping input to its `push_back` function. +pub struct CappedBackInserter<'a, T>(&'a mut T, usize, usize) +where T: BackInserter; + +impl<'a, T> CappedBackInserter<'a, T> +where T: BackInserter +{ + /// Create a new instance with this max capacity + #[inline] pub fn new(from: &'a mut T, cap: usize) -> Self + { + Self(from, 0, cap) + } + + /// The number of elements pushed so far + #[inline] pub fn len(&self) -> usize { + self.1 + } + + /// The max number of elemnts allowed to be pushed + #[inline] pub fn cap(&self) -> usize { + self.2 + } +} + +impl<'a, T> BackInserter for CappedBackInserter<'a, T> +where T: BackInserter +{ + #[inline] fn push_back(&mut self, value: T) + { + if self.1 < self.2 { + self.0.push_back(value); + self.1+=1; + } + } +} + +pub trait VecExt +{ + /// Insert many elements with exact size iterator + fn insert_exact>(&mut self, location: usize, slice: I) + where Ex: ExactSizeIterator; + + /// Insert many elements + fn insert_many>(&mut self, location: usize, slice: I); +} + + +impl VecExt for Vec +{ + #[cfg(not(feature="experimental_inserter"))] + #[inline(always)] + fn insert_exact>(&mut self, location: usize, slice: I) + where Ex: ExactSizeIterator + { + self.insert_many(location, slice) + } + #[cfg(feature="experimental_inserter")] + fn insert_exact>(&mut self, location: usize, slice: I) + where Ex: ExactSizeIterator, + { + #[inline(never)] + #[cold] + fn panic_len(l1: usize, l2: usize) -> ! + { + panic!("Location must be in range 0..{}, got {}", l1,l2) + } + + #[inline(never)] + #[cold] + fn inv_sz() -> ! + { + panic!("ExactSizeIterator returned invalid size"); + } + + if location >= self.len() { + panic_len(self.len(), location); + } + let mut slice = slice.into_iter(); + + let slen = slice.len(); + match slen { + 0 => return, + 1 => { + self.insert(location, slice.next().unwrap()); + return + }, + _ => (), + }; + self.reserve(slice.len()); + + unsafe { + let this = self.as_mut_ptr().add(location); + let len = self.len(); + let rest = std::mem::size_of::() * (location..len).len(); + + libc::memmove(this.add(slen) as *mut libc::c_void, this as *mut libc::c_void, rest); + let mut sent=0; + match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + let mut this = this; + for item in slice { + if sent >= slen { + inv_sz(); + } + + this.write(item); + this = this.add(1); + sent+=1; + } + if sent != slen { + inv_sz(); + } + })) { + Err(e) => { + // memory at (location+sent)..slen is now invalid, move the old one back before allowing unwind to contine + libc::memmove(this.add(sent) as *mut libc::c_void, this.add(slen) as *mut libc::c_void, rest); + self.set_len(len + sent); + std::panic::resume_unwind(e) + }, + _ => (), + } + self.set_len(len + sent); + } + } + #[inline] + fn insert_many>(&mut self, location: usize, slice: I) + { + let slice = slice.into_iter(); + match slice.size_hint() { + (0, Some(0)) | (0, None) => (), + (_, Some(bound)) | (bound, _) => self.reserve(bound), + }; + + self.splice(location..location, slice); + + //let splice = self.split_off(location); + //self.extend(slice.chain(splice.into_iter())); + + /* + // shift everything across, replacing with the new values + let splice: Vec<_> = self.splice(location.., slice).collect(); + // ^ -- this allocation bugs me, but we violate aliasing rules if we don't somehow collect it before adding it back in so... + // add tail back + self.extend(splice);*/ + } +} + +#[cfg(test)] +mod tests +{ + use super::*; + #[test] + fn vec_insert_exact() + { + let mut vec = vec![0,1,2,8,9,10]; + vec.insert_exact(3, [3,4,5,6, 7].iter().copied()); + + assert_eq!(&vec[..], + &[0,1,2,3,4,5,6,7,8,9,10] + ); + } + #[test] + fn vec_insert_exact_nt() + { + macro_rules! string { + ($str:literal) => (String::from($str)); + } + let mut vec = vec![ + string!("Hello"), + string!("world"), + string!("foo"), + string!("uhh"), + ]; + let vec2 = vec![ + string!("Hello"), + string!("world"), + string!("hi"), + string!("hello"), + string!("foo"), + string!("uhh"), + ]; + + vec.insert_exact(2, vec![string!("hi"), string!("hello")]); + + assert_eq!(&vec[..], &vec2[..]); + } + + #[cfg(nightly)] + mod benchmatks + { + use super::super::*; + use test::{ + Bencher, black_box, + }; + #[cfg(not(feature="experimental_inserter"))] + #[bench] + fn move_exact(b: &mut Bencher) + { + let mut vec = vec![0,10,11,12]; + let span = [0,1,2,3]; + b.iter(|| { + black_box(vec.insert_exact(vec.len()/2, span.iter().copied())); + }); + } + #[bench] + fn move_via_splice(b: &mut Bencher) + { + let mut vec = vec![0,10,11,12]; + let span = [0,1,2,3]; + + b.iter(|| { + black_box(vec.insert_many(vec.len()/2, span.iter().copied())); + }); + } + + #[cfg(feature="experimental_inserter")] + #[bench] + fn move_via_unsafe(b: &mut Bencher) + { + let mut vec = vec![0,10,11,12]; + let span = [0,1,2,3]; + b.iter(|| { + black_box(vec.insert_exact(vec.len()/2, span.iter().copied())); + }); + } + } +} diff --git a/src/main.rs b/src/main.rs index 592ea2c..bf783ae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,12 @@ +#![allow(dead_code)] + +#[macro_use] extern crate serde; + +#[macro_use] mod ext; use ext::*; + +mod delta; + #[tokio::main] async fn main() { println!("Hello, world!");