diff --git a/Cargo.lock b/Cargo.lock index 4ba8d76e..d838a35b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -157,15 +157,6 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" -[[package]] -name = "bitmaps" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" -dependencies = [ - "typenum", -] - [[package]] name = "block-buffer" version = "0.10.3" @@ -276,17 +267,6 @@ dependencies = [ "termcolor", ] -[[package]] -name = "colored" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" -dependencies = [ - "atty", - "lazy_static", - "winapi", -] - [[package]] name = "console_error_panic_hook" version = "0.1.7" @@ -671,20 +651,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" -[[package]] -name = "im" -version = "15.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0acd33ff0285af998aaf9b57342af478078f53492322fafc47450e09397e0e9" -dependencies = [ - "bitmaps", - "rand_core", - "rand_xoshiro", - "sized-chunks", - "typenum", - "version_check", -] - [[package]] name = "indexmap" version = "1.9.1" @@ -766,9 +732,7 @@ dependencies = [ "arbitrary", "arbtest", "arref", - "bit-vec", "color-backtrace", - "colored", "crdt-list", "criterion", "ctor", @@ -777,11 +741,10 @@ dependencies = [ "enum-as-inner", "flate2", "fxhash", - "im", "js-sys", "num", "owning_ref", - "pin-project", + "postcard", "proptest", "proptest-derive", "rand", @@ -1055,26 +1018,6 @@ dependencies = [ "siphasher", ] -[[package]] -name = "pin-project" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" -dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", -] - [[package]] name = "plotters" version = "0.3.4" @@ -1268,15 +1211,6 @@ dependencies = [ "rand_core", ] -[[package]] -name = "rand_xoshiro" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" -dependencies = [ - "rand_core", -] - [[package]] name = "rayon" version = "1.5.3" @@ -1561,16 +1495,6 @@ version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" -[[package]] -name = "sized-chunks" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" -dependencies = [ - "bitmaps", - "typenum", -] - [[package]] name = "smallvec" version = "1.10.0" diff --git a/crates/loro-core/Cargo.toml b/crates/loro-core/Cargo.toml index 6e4108cd..3c9cda54 100644 --- a/crates/loro-core/Cargo.toml +++ b/crates/loro-core/Cargo.toml @@ -9,22 +9,19 @@ edition = "2021" string_cache = "0.8.3" rle = { path = "../rle" } smallvec = "1.8.0" -smartstring = "1.0.1" +smartstring = { version = "1.0.1" } fxhash = "0.2.1" ring = "0.16.20" -pin-project = "1.0.10" serde = { version = "1.0.140", features = ["derive"] } thiserror = "1.0.31" -im = "15.1.0" enum-as-inner = "0.5.1" num = "0.4.0" crdt-list = { version = "0.3.0" } owning_ref = "0.4.1" +postcard = "1.0.2" rand = { version = "0.8.5", optional = true } arbitrary = { version = "1.1.7", optional = true } tabled = { version = "0.10.0", optional = true } -colored = "2.0.0" -bit-vec = "0.6.3" wasm-bindgen = { version = "0.2.83", optional = true } serde-wasm-bindgen = { version = "0.4.5", optional = true } js-sys = { version = "0.3.60", optional = true } diff --git a/crates/loro-core/src/container.rs b/crates/loro-core/src/container.rs index 995102c3..b6d373d5 100644 --- a/crates/loro-core/src/container.rs +++ b/crates/loro-core/src/container.rs @@ -10,7 +10,7 @@ use crate::{ log_store::ImportContext, op::{InnerContent, RemoteContent, RichOp}, version::{IdSpanVector, VersionVector}, - InternalString, LogStore, LoroValue, ID, + InternalString, LoroValue, ID, }; use serde::{Deserialize, Serialize}; diff --git a/crates/loro-core/src/container/list/list_op.rs b/crates/loro-core/src/container/list/list_op.rs index e2201fe0..52ab32e2 100644 --- a/crates/loro-core/src/container/list/list_op.rs +++ b/crates/loro-core/src/container/list/list_op.rs @@ -2,10 +2,11 @@ use std::ops::Range; use enum_as_inner::EnumAsInner; use rle::{HasLength, Mergable, Sliceable}; +use serde::{Deserialize, Serialize}; use crate::container::text::text_content::{ListSlice, SliceRange}; -#[derive(EnumAsInner, Debug, Clone)] +#[derive(EnumAsInner, Debug, Clone, Serialize, Deserialize)] pub enum ListOp { Insert { slice: ListSlice, pos: usize }, Delete(DeleteSpan), @@ -23,7 +24,7 @@ pub enum InnerListOp { /// len cannot be zero; /// /// pos: 5, len: -3 eq a range of (2, 5] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub struct DeleteSpan { pub pos: isize, pub len: isize, diff --git a/crates/loro-core/src/container/map/map_content.rs b/crates/loro-core/src/container/map/map_content.rs index 9e67bfaa..e237a044 100644 --- a/crates/loro-core/src/container/map/map_content.rs +++ b/crates/loro-core/src/container/map/map_content.rs @@ -1,8 +1,9 @@ use rle::{HasLength, Mergable, Sliceable}; +use serde::{Deserialize, Serialize}; use crate::{ContentType, InsertContentTrait, InternalString, LoroValue}; -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct MapSet { pub(crate) key: InternalString, pub(crate) value: LoroValue, diff --git a/crates/loro-core/src/container/registry.rs b/crates/loro-core/src/container/registry.rs index b98b394f..b19ea7e5 100644 --- a/crates/loro-core/src/container/registry.rs +++ b/crates/loro-core/src/container/registry.rs @@ -17,7 +17,7 @@ use crate::{ log_store::ImportContext, op::{RemoteContent, RichOp}, version::IdSpanVector, - LogStore, LoroError, LoroValue, + LoroError, LoroValue, }; use super::{ diff --git a/crates/loro-core/src/container/text/text_content.rs b/crates/loro-core/src/container/text/text_content.rs index 6e52ebbf..cd81ec07 100644 --- a/crates/loro-core/src/container/text/text_content.rs +++ b/crates/loro-core/src/container/text/text_content.rs @@ -2,10 +2,11 @@ use std::ops::Range; use enum_as_inner::EnumAsInner; use rle::{HasLength, Mergable, Sliceable}; +use serde::{Deserialize, Serialize}; use crate::{smstring::SmString, LoroValue}; -#[derive(PartialEq, Debug, EnumAsInner, Clone)] +#[derive(PartialEq, Debug, EnumAsInner, Clone, Serialize, Deserialize)] pub enum ListSlice { // TODO: use Box<[LoroValue]> ? RawData(Vec), @@ -101,7 +102,7 @@ impl HasLength for ListSlice { impl Sliceable for ListSlice { fn slice(&self, from: usize, to: usize) -> Self { match self { - ListSlice::RawStr(s) => ListSlice::RawStr(s.0[from..to].into()), + ListSlice::RawStr(s) => ListSlice::RawStr(s[from..to].into()), ListSlice::Unknown(_) => ListSlice::Unknown(to - from), ListSlice::RawData(x) => ListSlice::RawData(x[from..to].to_vec()), } diff --git a/crates/loro-core/src/container/text/tracker.rs b/crates/loro-core/src/container/text/tracker.rs index f85e6ef5..db8970e5 100644 --- a/crates/loro-core/src/container/text/tracker.rs +++ b/crates/loro-core/src/container/text/tracker.rs @@ -1,4 +1,3 @@ -use colored::Colorize; use debug_log::debug_log; use rle::{rle_tree::UnsafeCursor, HasLength, Sliceable}; use smallvec::SmallVec; @@ -326,7 +325,7 @@ impl Tracker { let mut spans = self .content .get_active_id_spans(span.start() as usize, span.atom_len()); - debug_log!("DELETED SPANS={}", format!("{:#?}", &spans).red()); + debug_log!("DELETED SPANS={}", format!("{:#?}", &spans)); self.update_spans(&spans, StatusChange::Delete); if span.is_reversed() && span.atom_len() > 1 { diff --git a/crates/loro-core/src/dag.rs b/crates/loro-core/src/dag.rs index 8e476431..eed49f7c 100644 --- a/crates/loro-core/src/dag.rs +++ b/crates/loro-core/src/dag.rs @@ -12,8 +12,6 @@ use std::{ fmt::Debug, }; -#[allow(unused)] -use colored::Colorize; use fxhash::{FxHashMap, FxHashSet}; use rle::{HasLength, Sliceable}; use smallvec::{smallvec, SmallVec}; diff --git a/crates/loro-core/src/event.rs b/crates/loro-core/src/event.rs index 35e61489..d8e51955 100644 --- a/crates/loro-core/src/event.rs +++ b/crates/loro-core/src/event.rs @@ -1,5 +1,6 @@ use enum_as_inner::EnumAsInner; use fxhash::{FxHashMap, FxHashSet}; +use serde::{Deserialize, Serialize}; use crate::{container::ContainerID, delta::Delta, version::Frontiers, InternalString, LoroValue}; @@ -27,7 +28,7 @@ pub struct Event { pub type Path = Vec; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum Index { Key(InternalString), Seq(usize), diff --git a/crates/loro-core/src/log_store.rs b/crates/loro-core/src/log_store.rs index 9289a19a..8d1b7442 100644 --- a/crates/loro-core/src/log_store.rs +++ b/crates/loro-core/src/log_store.rs @@ -1,9 +1,11 @@ //! [LogStore] stores all the [Change]s and [Op]s. It's also a [DAG][crate::dag]; //! //! +mod encode_updates; mod encoding; mod import; mod iter; + use crate::LoroValue; pub(crate) use import::ImportContext; use std::{ @@ -13,7 +15,6 @@ use std::{ use fxhash::FxHashMap; -use crate::context::Context; use rle::{HasLength, RleVec, RleVecWithIndex, Sliceable}; use smallvec::SmallVec; @@ -51,7 +52,7 @@ impl Default for GcConfig { } type ClientChanges = FxHashMap>; -type RemoteClientChanges = FxHashMap, ChangeMergeCfg>>; +type RemoteClientChanges = FxHashMap>>; #[derive(Debug)] /// LogStore stores the full history of Loro @@ -101,21 +102,14 @@ impl LogStore { .map(|changes| changes.get(id.counter as usize).unwrap().element) } - pub fn export( - &self, - remote_vv: &VersionVector, - ) -> FxHashMap, ChangeMergeCfg>> { - let mut ans: FxHashMap, ChangeMergeCfg>> = - Default::default(); + pub fn export(&self, remote_vv: &VersionVector) -> FxHashMap>> { + let mut ans: FxHashMap>> = Default::default(); let self_vv = self.vv(); let diff = self_vv.diff(remote_vv); for span in diff.left.iter() { let changes = self.get_changes_slice(span.id_span()); for change in changes.iter() { - let vec = ans - .entry(change.id.client_id) - .or_insert_with(|| RleVecWithIndex::new_cfg(self.get_change_merge_cfg())); - + let vec = ans.entry(change.id.client_id).or_insert_with(|| Vec::new()); vec.push(self.change_to_export_format(change)); } } diff --git a/crates/loro-core/src/log_store/encode_updates.rs b/crates/loro-core/src/log_store/encode_updates.rs new file mode 100644 index 00000000..1f7ffe4f --- /dev/null +++ b/crates/loro-core/src/log_store/encode_updates.rs @@ -0,0 +1,162 @@ +use rle::{HasLength, RleVec}; +use serde::{Deserialize, Serialize}; +use smallvec::SmallVec; + +use crate::{ + change::{Change, Lamport, Timestamp}, + container::ContainerID, + id::{ClientID, Counter, ID}, + op::{RemoteContent, RemoteOp}, + LogStore, VersionVector, +}; + +use super::RemoteClientChanges; + +#[derive(Serialize, Deserialize)] +struct Updates { + changes: Vec, +} + +/// the continuous changes from the same client +#[derive(Serialize, Deserialize)] +struct EncodedClientChanges { + meta: FirstChangeInfo, + data: Vec, +} + +#[derive(Serialize, Deserialize)] +struct FirstChangeInfo { + pub(crate) client: ClientID, + pub(crate) counter: Counter, + pub(crate) lamport: Lamport, + pub(crate) timestamp: Timestamp, +} + +#[derive(Serialize, Deserialize)] +struct EncodedOp { + pub(crate) container: ContainerID, + pub(crate) contents: Vec, +} + +#[derive(Serialize, Deserialize)] +struct EncodedChange { + pub(crate) ops: Vec, + pub(crate) deps_except_self: Vec, + pub(crate) lamport_delta: u32, + pub(crate) timestamp_delta: i64, +} + +impl LogStore { + pub fn encode_updates(&self, from: &VersionVector) -> Result, postcard::Error> { + let changes = self.export(from); + let mut updates = Updates { + changes: Vec::with_capacity(changes.len()), + }; + for (_, changes) in changes { + let encoded = convert_changes_to_encoded(changes.into_iter()); + updates.changes.push(encoded); + } + + postcard::to_allocvec(&updates) + } + + pub fn decode_updates(&mut self, input: &[u8]) -> Result<(), postcard::Error> { + let updates: Updates = postcard::from_bytes(input)?; + let mut changes: RemoteClientChanges = Default::default(); + for encoded in updates.changes { + changes.insert(encoded.meta.client, convert_encoded_to_changes(encoded)); + } + + self.import(changes); + Ok(()) + } +} + +fn convert_changes_to_encoded(mut changes: I) -> EncodedClientChanges +where + I: Iterator>, +{ + let first_change = changes.next().unwrap(); + let this_client_id = first_change.id.client_id; + let mut data = Vec::with_capacity(changes.size_hint().0 + 1); + let mut last_change = first_change.clone(); + for change in changes { + data.push(EncodedChange { + ops: change + .ops + .iter() + .map(|op| EncodedOp { + container: op.container.clone(), + contents: op.contents.iter().cloned().collect(), + }) + .collect(), + deps_except_self: change + .deps + .iter() + .filter(|x| x.client_id != this_client_id) + .copied() + .collect(), + lamport_delta: change.lamport - last_change.lamport, + timestamp_delta: change.timestamp - last_change.timestamp, + }); + last_change = change; + } + + EncodedClientChanges { + meta: FirstChangeInfo { + client: this_client_id, + counter: first_change.id.counter, + lamport: first_change.lamport, + timestamp: first_change.timestamp, + }, + data, + } +} + +fn convert_encoded_to_changes(changes: EncodedClientChanges) -> Vec> { + let mut result = Vec::with_capacity(changes.data.len() + 1); + let mut last_lamport = changes.meta.lamport; + let mut last_timestamp = changes.meta.timestamp; + let mut counter: Counter = changes.meta.counter; + for encoded in changes.data { + let start_counter = counter; + let mut deps = SmallVec::with_capacity(encoded.deps_except_self.len() + 1); + if counter > 0 { + deps.push(ID { + client_id: changes.meta.client, + counter: changes.meta.counter - 1, + }); + } + + for dep in encoded.deps_except_self { + deps.push(dep); + } + + let mut ops = RleVec::with_capacity(encoded.ops.len()); + for op in encoded.ops { + let len: usize = op.contents.iter().map(|x| x.atom_len()).sum(); + ops.push(RemoteOp { + counter, + container: op.container, + contents: op.contents.into_iter().collect(), + }); + counter += len as Counter; + } + + let change = Change { + id: ID { + client_id: changes.meta.client, + counter: start_counter, + }, + lamport: last_lamport + encoded.lamport_delta, + timestamp: last_timestamp + encoded.timestamp_delta, + ops, + deps, + }; + last_lamport = change.lamport; + last_timestamp = change.timestamp; + result.push(change); + } + + result +} diff --git a/crates/loro-core/src/log_store/encoding.rs b/crates/loro-core/src/log_store/encoding.rs index e7a06683..4c45c8cc 100644 --- a/crates/loro-core/src/log_store/encoding.rs +++ b/crates/loro-core/src/log_store/encoding.rs @@ -19,7 +19,6 @@ use crate::{ dag::remove_included_frontiers, id::{ClientID, Counter, ID}, op::{Op, RemoteContent, RemoteOp}, - smstring::SmString, span::{HasIdSpan, HasLamportSpan}, ContainerType, InternalString, LogStore, LoroValue, VersionVector, }; @@ -154,7 +153,6 @@ fn encode_changes(store: &LogStore) -> Encoded { (span.pos as usize, 0, LoroValue::I32(span.len as i32)) } }, - crate::op::RemoteContent::Dyn(_) => unreachable!(), }; op_len += 1; ops.push(OpEncoding { @@ -271,7 +269,7 @@ fn decode_changes( }, _ => { let slice = match value { - LoroValue::String(s) => ListSlice::RawStr(SmString::from(&*s)), + LoroValue::String(s) => ListSlice::RawStr(s.into()), LoroValue::List(v) => ListSlice::RawData(*v), _ => unreachable!(), }; diff --git a/crates/loro-core/src/loro.rs b/crates/loro-core/src/loro.rs index 4c9e2309..a0e78360 100644 --- a/crates/loro-core/src/loro.rs +++ b/crates/loro-core/src/loro.rs @@ -72,18 +72,12 @@ impl LoroCore { Text::from_instance(instance, cid) } - pub fn export( - &self, - remote_vv: VersionVector, - ) -> FxHashMap, ChangeMergeCfg>> { + pub fn export(&self, remote_vv: VersionVector) -> FxHashMap>> { let store = self.log_store.read().unwrap(); store.export(&remote_vv) } - pub fn import( - &mut self, - changes: FxHashMap, ChangeMergeCfg>>, - ) { + pub fn import(&mut self, changes: FxHashMap>>) { let mut store = self.log_store.write().unwrap(); store.import(changes) } diff --git a/crates/loro-core/src/op/content.rs b/crates/loro-core/src/op/content.rs index 0c09a554..44d5bf35 100644 --- a/crates/loro-core/src/op/content.rs +++ b/crates/loro-core/src/op/content.rs @@ -2,6 +2,7 @@ use std::any::{Any, TypeId}; use enum_as_inner::EnumAsInner; use rle::{HasLength, Mergable, Sliceable}; +use serde::{Deserialize, Serialize}; use crate::container::{ list::list_op::{InnerListOp, ListOp}, @@ -24,11 +25,10 @@ pub enum InnerContent { Map(InnerMapSet), } -#[derive(EnumAsInner, Debug)] +#[derive(EnumAsInner, Debug, Serialize, Deserialize)] pub enum RemoteContent { Map(MapSet), List(ListOp), - Dyn(Box), } impl Clone for RemoteContent { @@ -36,7 +36,6 @@ impl Clone for RemoteContent { match self { Self::Map(arg0) => Self::Map(arg0.clone()), Self::List(arg0) => Self::List(arg0.clone()), - Self::Dyn(arg0) => Self::Dyn(arg0.clone_content()), } } } @@ -93,7 +92,6 @@ impl HasLength for RemoteContent { fn content_len(&self) -> usize { match self { RemoteContent::Map(x) => x.content_len(), - RemoteContent::Dyn(x) => x.content_len(), RemoteContent::List(x) => x.content_len(), } } @@ -103,7 +101,6 @@ impl Sliceable for RemoteContent { fn slice(&self, from: usize, to: usize) -> Self { match self { RemoteContent::Map(x) => RemoteContent::Map(x.slice(from, to)), - RemoteContent::Dyn(x) => RemoteContent::Dyn(x.slice_content(from, to)), RemoteContent::List(x) => RemoteContent::List(x.slice(from, to)), } } @@ -117,7 +114,6 @@ impl Mergable for RemoteContent { match (self, other) { (RemoteContent::Map(x), RemoteContent::Map(y)) => x.is_mergable(y, &()), (RemoteContent::List(x), RemoteContent::List(y)) => x.is_mergable(y, &()), - (RemoteContent::Dyn(x), RemoteContent::Dyn(y)) => x.is_mergable_content(&**y), _ => false, } } @@ -135,7 +131,6 @@ impl Mergable for RemoteContent { RemoteContent::List(y) => x.merge(y, &()), _ => unreachable!(), }, - RemoteContent::Dyn(x) => x.merge_content(&**_other.as_dyn().unwrap()), } } } diff --git a/crates/loro-core/src/smstring.rs b/crates/loro-core/src/smstring.rs index efbffba6..e42a5106 100644 --- a/crates/loro-core/src/smstring.rs +++ b/crates/loro-core/src/smstring.rs @@ -3,6 +3,7 @@ use std::ops::DerefMut; use std::ops::Deref; use rle::Mergable; +use serde::Deserialize; use serde::Serialize; use smartstring::LazyCompact; @@ -63,6 +64,13 @@ impl From for SmString { } } +impl From> for SmString { + fn from(s: Box) -> Self { + let s: &str = &s; + SmString(s.into()) + } +} + impl From<&str> for SmString { fn from(s: &str) -> Self { SmString(s.into()) @@ -77,3 +85,13 @@ impl Serialize for SmString { serializer.serialize_str(&self.0) } } + +impl<'de> Deserialize<'de> for SmString { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + Ok(s.into()) + } +} diff --git a/crates/loro-core/tests/test.rs b/crates/loro-core/tests/test.rs index 99621ac6..dece3bda 100644 --- a/crates/loro-core/tests/test.rs +++ b/crates/loro-core/tests/test.rs @@ -1,4 +1,3 @@ -use std::borrow::{Borrow, BorrowMut}; use std::cell::RefCell; use std::rc::Rc; diff --git a/crates/rle/src/rle_vec.rs b/crates/rle/src/rle_vec.rs index 22f53670..89550472 100644 --- a/crates/rle/src/rle_vec.rs +++ b/crates/rle/src/rle_vec.rs @@ -555,6 +555,31 @@ impl Deref for RleVecWithLen { } } +impl Sliceable for Vec { + fn slice(&self, start: usize, end: usize) -> Self { + if start >= end || self.is_empty() { + return Vec::new(); + } + + let mut ans = Vec::new(); + let mut index = 0; + for item in self { + if index >= end { + break; + } + + let len = item.atom_len(); + if start < index + len { + ans.push(item.slice(start.saturating_sub(index), (end - index).min(len))) + } + + index += len; + } + + ans + } +} + #[cfg(test)] mod test { use super::*; diff --git a/crates/rle/src/rle_vec_old.rs b/crates/rle/src/rle_vec_old.rs index 05b4cc94..090c8254 100644 --- a/crates/rle/src/rle_vec_old.rs +++ b/crates/rle/src/rle_vec_old.rs @@ -1,4 +1,7 @@ -use std::ops::{Deref, Range}; +use std::{ + ops::{Deref, Range}, + vec, +}; use num::Integer; @@ -213,6 +216,16 @@ impl RleVecWithIndex { } } +impl IntoIterator for RleVecWithIndex { + type Item = T; + + type IntoIter = vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.vec.into_iter() + } +} + impl Default for RleVecWithIndex { fn default() -> Self { Self::new()