Use SmolStr in place of String (#182)
Most of the strings are small and so fit nicely in a SmolStr. When they don't it just reverts to using a normal String.
This commit is contained in:
parent
ff8b8613d5
commit
fc1b8f87fb
41 changed files with 260 additions and 205 deletions
automerge-backend
Cargo.toml
src
tests
automerge-frontend
automerge-protocol
automerge
perf
|
@ -25,6 +25,7 @@ itertools = "0.9.0"
|
|||
tracing = { version = "0.1.25", features = ["log"] }
|
||||
flate2 = "1.0.20"
|
||||
nonzero_ext = "^0.2.0"
|
||||
smol_str = "0.1.17"
|
||||
|
||||
[dependencies.web-sys]
|
||||
version = "0.3"
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use std::cmp::Ordering;
|
||||
|
||||
use automerge_protocol as amp;
|
||||
use smol_str::SmolStr;
|
||||
|
||||
use crate::{
|
||||
expanded_op::ExpandedOp,
|
||||
|
@ -13,7 +14,7 @@ pub(crate) struct ActorMap(Vec<amp::ActorId>);
|
|||
impl ActorMap {
|
||||
pub fn import_key(&mut self, key: &::Key) -> Key {
|
||||
match key {
|
||||
amp::Key::Map(string) => Key::Map(string.to_string()),
|
||||
amp::Key::Map(string) => Key::Map(string.clone()),
|
||||
amp::Key::Seq(eid) => Key::Seq(self.import_element_id(eid)),
|
||||
}
|
||||
}
|
||||
|
@ -94,18 +95,22 @@ impl ActorMap {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn opid_to_string(&self, id: &OpId) -> String {
|
||||
format!("{}@{}", id.0, self.export_actor(id.1).to_hex_string())
|
||||
pub fn opid_to_string(&self, id: &OpId) -> SmolStr {
|
||||
SmolStr::new(format!(
|
||||
"{}@{}",
|
||||
id.0,
|
||||
self.export_actor(id.1).to_hex_string()
|
||||
))
|
||||
}
|
||||
|
||||
pub fn elementid_to_string(&self, eid: &ElementId) -> String {
|
||||
pub fn elementid_to_string(&self, eid: &ElementId) -> SmolStr {
|
||||
match eid {
|
||||
ElementId::Head => "_head".into(),
|
||||
ElementId::Id(id) => self.opid_to_string(id),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn key_to_string(&self, key: &Key) -> String {
|
||||
pub fn key_to_string(&self, key: &Key) -> SmolStr {
|
||||
match &key {
|
||||
Key::Map(s) => s.clone(),
|
||||
Key::Seq(eid) => self.elementid_to_string(eid),
|
||||
|
|
|
@ -11,6 +11,7 @@ use std::{
|
|||
|
||||
use automerge_protocol as amp;
|
||||
use flate2::bufread::DeflateDecoder;
|
||||
use smol_str::SmolStr;
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::{
|
||||
|
@ -324,7 +325,7 @@ pub struct KeyIterator<'a> {
|
|||
pub(crate) actors: &'a [amp::ActorId],
|
||||
pub(crate) actor: RleDecoder<'a, usize>,
|
||||
pub(crate) ctr: DeltaDecoder<'a>,
|
||||
pub(crate) str: RleDecoder<'a, String>,
|
||||
pub(crate) str: RleDecoder<'a, SmolStr>,
|
||||
}
|
||||
|
||||
pub struct ValueIterator<'a> {
|
||||
|
@ -435,7 +436,7 @@ impl<'a> Iterator for ValueIterator<'a> {
|
|||
let len = v >> 4;
|
||||
let data = self.val_raw.read_bytes(len).ok()?;
|
||||
let s = str::from_utf8(data).ok()?;
|
||||
Some(amp::ScalarValue::Str(s.to_string()))
|
||||
Some(amp::ScalarValue::Str(SmolStr::new(s)))
|
||||
}
|
||||
v if v % 16 == VALUE_TYPE_BYTES => {
|
||||
let len = v >> 4;
|
||||
|
@ -643,7 +644,7 @@ impl ValEncoder {
|
|||
struct KeyEncoder {
|
||||
actor: RleEncoder<usize>,
|
||||
ctr: DeltaEncoder,
|
||||
str: RleEncoder<String>,
|
||||
str: RleEncoder<SmolStr>,
|
||||
}
|
||||
|
||||
impl KeyEncoder {
|
||||
|
@ -1347,7 +1348,7 @@ mod tests {
|
|||
let col_op = ColumnOp {
|
||||
action: InternalOpType::Set(ScalarValue::Null),
|
||||
obj: Cow::Owned(amp::ObjectId::Root),
|
||||
key: Cow::Owned(Key::Map("r".to_owned())),
|
||||
key: Cow::Owned(Key::Map("r".into())),
|
||||
pred: vec![actor.op_id_at(1), actor2.op_id_at(1)],
|
||||
insert: false,
|
||||
};
|
||||
|
@ -1359,7 +1360,7 @@ mod tests {
|
|||
let col_op2 = ColumnOp {
|
||||
action: InternalOpType::Set(ScalarValue::Null),
|
||||
obj: Cow::Owned(amp::ObjectId::Root),
|
||||
key: Cow::Owned(Key::Map("r".to_owned())),
|
||||
key: Cow::Owned(Key::Map("r".into())),
|
||||
pred: vec![actor2.op_id_at(1), actor.op_id_at(1)],
|
||||
insert: false,
|
||||
};
|
||||
|
|
|
@ -2,6 +2,7 @@ use core::fmt::Debug;
|
|||
use std::{borrow::Cow, convert::TryFrom, io, io::Read, str};
|
||||
|
||||
use automerge_protocol as amp;
|
||||
use smol_str::SmolStr;
|
||||
|
||||
/// The error type for decoding operations.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
|
@ -362,6 +363,17 @@ impl Decodable for Vec<u8> {
|
|||
Some(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for SmolStr {
|
||||
fn decode<R>(bytes: &mut R) -> Option<SmolStr>
|
||||
where
|
||||
R: Read,
|
||||
{
|
||||
let buffer = Vec::decode(bytes)?;
|
||||
str::from_utf8(&buffer).map(|t| t.into()).ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for String {
|
||||
fn decode<R>(bytes: &mut R) -> Option<String>
|
||||
where
|
||||
|
|
|
@ -7,6 +7,7 @@ use std::{
|
|||
|
||||
use automerge_protocol as amp;
|
||||
use flate2::{bufread::DeflateEncoder, Compression};
|
||||
use smol_str::SmolStr;
|
||||
|
||||
use crate::columnar::COLUMN_TYPE_DEFLATE;
|
||||
|
||||
|
@ -254,6 +255,15 @@ pub(crate) trait Encodable {
|
|||
fn encode<R: Write>(&self, buf: &mut R) -> io::Result<usize>;
|
||||
}
|
||||
|
||||
impl Encodable for SmolStr {
|
||||
fn encode<R: Write>(&self, buf: &mut R) -> io::Result<usize> {
|
||||
let bytes = self.as_bytes();
|
||||
let head = bytes.len().encode(buf)?;
|
||||
buf.write_all(bytes)?;
|
||||
Ok(head + bytes.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for String {
|
||||
fn encode<R: Write>(&self, buf: &mut R) -> io::Result<usize> {
|
||||
let bytes = self.as_bytes();
|
||||
|
|
|
@ -204,8 +204,8 @@ mod tests {
|
|||
},
|
||||
Op {
|
||||
action: OpType::MultiSet(vec![
|
||||
ScalarValue::Str("hi ".to_owned()),
|
||||
ScalarValue::Str("world".to_owned()),
|
||||
ScalarValue::Str("hi ".into()),
|
||||
ScalarValue::Str("world".into()),
|
||||
]),
|
||||
obj: ObjectId::Id(OpId(1, actor.clone())),
|
||||
key: Key::Seq(ElementId::Id(OpId(4, actor.clone()))),
|
||||
|
@ -239,14 +239,14 @@ mod tests {
|
|||
insert: true
|
||||
},
|
||||
ExpandedOp {
|
||||
action: InternalOpType::Set(ScalarValue::Str("hi ".to_owned())),
|
||||
action: InternalOpType::Set(ScalarValue::Str("hi ".into())),
|
||||
obj: Cow::Owned(ObjectId::Id(OpId(1, actor.clone()))),
|
||||
key: Cow::Owned(Key::Seq(ElementId::Id(OpId(4, actor.clone())))),
|
||||
pred: Cow::Owned(vec![]),
|
||||
insert: true
|
||||
},
|
||||
ExpandedOp {
|
||||
action: InternalOpType::Set(ScalarValue::Str("world".to_owned())),
|
||||
action: InternalOpType::Set(ScalarValue::Str("world".into())),
|
||||
obj: Cow::Owned(ObjectId::Id(OpId(1, actor.clone()))),
|
||||
key: Cow::Owned(Key::Seq(ElementId::Id(OpId(5, actor)))),
|
||||
pred: Cow::Owned(vec![]),
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use automerge_protocol as amp;
|
||||
use nonzero_ext::nonzero;
|
||||
use smol_str::SmolStr;
|
||||
|
||||
#[derive(Eq, PartialEq, Hash, Debug, Clone, Copy)]
|
||||
pub(crate) struct ActorId(pub usize);
|
||||
|
@ -21,7 +22,7 @@ pub(crate) enum ElementId {
|
|||
|
||||
#[derive(PartialEq, Eq, Debug, Hash, Clone)]
|
||||
pub(crate) enum Key {
|
||||
Map(String),
|
||||
Map(SmolStr),
|
||||
Seq(ElementId),
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ use std::collections::{HashMap, HashSet};
|
|||
|
||||
use automerge_protocol as amp;
|
||||
use fxhash::FxBuildHasher;
|
||||
use smol_str::SmolStr;
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::{
|
||||
|
@ -335,7 +336,7 @@ impl<'a> PatchWorkshop for PatchWorkshopImpl<'a> {
|
|||
})
|
||||
}
|
||||
|
||||
fn key_to_string(&self, key: &crate::internal::Key) -> String {
|
||||
fn key_to_string(&self, key: &crate::internal::Key) -> SmolStr {
|
||||
self.actors.key_to_string(key)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use automerge_protocol as amp;
|
||||
use smol_str::SmolStr;
|
||||
|
||||
use crate::{
|
||||
internal::{Key, ObjectId, OpId},
|
||||
|
@ -14,7 +15,7 @@ use crate::{
|
|||
/// building of the patch. It's just where some tools to make the patch can be
|
||||
/// found
|
||||
pub(crate) trait PatchWorkshop {
|
||||
fn key_to_string(&self, key: &Key) -> String;
|
||||
fn key_to_string(&self, key: &Key) -> SmolStr;
|
||||
fn find_cursor(&self, opid: &::OpId) -> Option<amp::CursorDiff>;
|
||||
fn get_obj(&self, object_id: &ObjectId) -> Option<&ObjState>;
|
||||
fn make_external_objid(&self, object_id: &ObjectId) -> amp::ObjectId;
|
||||
|
|
|
@ -996,7 +996,7 @@ fn test_handle_changes_within_conflicted_lists() {
|
|||
value: Diff::Map(MapDiff{
|
||||
object_id: actor1.op_id_at(3).into(),
|
||||
props: hashmap!{
|
||||
"done".to_string() => hashmap!{
|
||||
"done".into() => hashmap!{
|
||||
actor1.op_id_at(6) => Diff::Value(true.into())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ thiserror = "1.0.16"
|
|||
im-rc = "15.0.0"
|
||||
unicode-segmentation = "1.7.1"
|
||||
arbitrary = { version = "1", features = ["derive"], optional = true }
|
||||
smol_str = "0.1.17"
|
||||
|
||||
[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies]
|
||||
getrandom = { version = "0.2.2", features=["js"] }
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use automerge_frontend::{Frontend, InvalidChangeRequest, LocalChange, Path, Value};
|
||||
use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion};
|
||||
use rand::{thread_rng, Rng};
|
||||
use smol_str::SmolStr;
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
pub fn insert_long_string(c: &mut Criterion) {
|
||||
|
@ -8,7 +9,7 @@ pub fn insert_long_string(c: &mut Criterion) {
|
|||
b.iter_batched(
|
||||
|| {
|
||||
let doc = Frontend::new();
|
||||
let random_string: String = thread_rng()
|
||||
let random_string: SmolStr = thread_rng()
|
||||
.sample_iter(&rand::distributions::Alphanumeric)
|
||||
.take(6000)
|
||||
.map(char::from)
|
||||
|
@ -21,7 +22,7 @@ pub fn insert_long_string(c: &mut Criterion) {
|
|||
doc.change::<_, _, InvalidChangeRequest>(None, |d| {
|
||||
d.add_change(LocalChange::set(
|
||||
Path::root().key("text"),
|
||||
Value::Text(string.graphemes(true).map(|s| s.to_owned()).collect()),
|
||||
Value::Text(string.graphemes(true).map(|s| s.into()).collect()),
|
||||
))
|
||||
})
|
||||
.unwrap()
|
||||
|
|
|
@ -16,7 +16,7 @@ pub fn sequential_inserts_in_multiple_patches(c: &mut Criterion) {
|
|||
pending_changes: 0,
|
||||
diffs: RootDiff {
|
||||
props: hashmap! {
|
||||
"text".to_string() => hashmap!{
|
||||
"text".into() => hashmap!{
|
||||
make_list_opid.clone() => amp::Diff::Text(amp::TextDiff{
|
||||
object_id: make_list_opid.clone().into(),
|
||||
edits: Vec::new(),
|
||||
|
@ -37,14 +37,14 @@ pub fn sequential_inserts_in_multiple_patches(c: &mut Criterion) {
|
|||
pending_changes: 0,
|
||||
diffs: RootDiff {
|
||||
props: hashmap! {
|
||||
"text".to_string() => hashmap!{
|
||||
"text".into() => hashmap!{
|
||||
make_list_opid.clone() => amp::Diff::Text(amp::TextDiff{
|
||||
object_id: make_list_opid.clone().into(),
|
||||
edits: vec![amp::DiffEdit::SingleElementInsert{
|
||||
index,
|
||||
elem_id: this_op_id.clone().into(),
|
||||
op_id: this_op_id.clone(),
|
||||
value: amp::Diff::Value(amp::ScalarValue::Str("c".to_string())),
|
||||
value: amp::Diff::Value(amp::ScalarValue::Str("c".into())),
|
||||
}],
|
||||
})
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ pub fn sequential_inserts_in_single_patch(c: &mut Criterion) {
|
|||
index,
|
||||
elem_id: this_op_id.clone().into(),
|
||||
op_id: this_op_id.clone(),
|
||||
value: amp::Diff::Value(amp::ScalarValue::Str("c".to_string())),
|
||||
value: amp::Diff::Value(amp::ScalarValue::Str("c".into())),
|
||||
});
|
||||
}
|
||||
let patch: amp::Patch = amp::Patch {
|
||||
|
@ -98,7 +98,7 @@ pub fn sequential_inserts_in_single_patch(c: &mut Criterion) {
|
|||
pending_changes: 0,
|
||||
diffs: RootDiff {
|
||||
props: hashmap! {
|
||||
"text".to_string() => hashmap!{
|
||||
"text".into() => hashmap!{
|
||||
make_list_opid.clone() => amp::Diff::Text(amp::TextDiff{
|
||||
object_id: make_list_opid.into(),
|
||||
edits,
|
||||
|
|
|
@ -410,7 +410,7 @@ impl Frontend {
|
|||
&front.actor_id,
|
||||
max_op,
|
||||
ObjectId::Root,
|
||||
&k.into(),
|
||||
&::Key::Map(k.clone()),
|
||||
v,
|
||||
false,
|
||||
);
|
||||
|
|
|
@ -131,7 +131,7 @@ impl<'a> MutationTracker<'a> {
|
|||
match value {
|
||||
Value::Map(kvs) => {
|
||||
for (k, v) in kvs.iter() {
|
||||
self.add_change(LocalChange::set(Path::root().key(k), v.clone()))?;
|
||||
self.add_change(LocalChange::set(Path::root().key(k.clone()), v.clone()))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -467,7 +467,7 @@ impl<'a> MutableDocument for MutationTracker<'a> {
|
|||
actor: &self.actor_id.clone(),
|
||||
value,
|
||||
};
|
||||
let (old, res) = root_target.set_key(k, payload);
|
||||
let (old, res) = root_target.set_key(k.clone(), payload);
|
||||
Ok((LocalOperationForRollback::Set { old }, res))
|
||||
}
|
||||
(PathElement::Key(ref k), ResolvedPathMut::Map(ref mut maptarget)) => {
|
||||
|
@ -476,7 +476,7 @@ impl<'a> MutableDocument for MutationTracker<'a> {
|
|||
actor: &self.actor_id.clone(),
|
||||
value,
|
||||
};
|
||||
let (old, res) = maptarget.set_key(k, payload);
|
||||
let (old, res) = maptarget.set_key(k.clone(), payload);
|
||||
Ok((LocalOperationForRollback::Set { old }, res))
|
||||
}
|
||||
(
|
||||
|
@ -488,7 +488,7 @@ impl<'a> MutableDocument for MutationTracker<'a> {
|
|||
actor: &self.actor_id.clone(),
|
||||
value,
|
||||
};
|
||||
let (old, res) = tabletarget.set_key(k, payload);
|
||||
let (old, res) = tabletarget.set_key(k.clone(), payload);
|
||||
Ok((LocalOperationForRollback::Set { old }, res))
|
||||
}
|
||||
// In this case we are trying to modify a key in something which is not
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
use std::fmt;
|
||||
|
||||
use smol_str::SmolStr;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub(crate) enum PathElement {
|
||||
Key(String),
|
||||
Key(SmolStr),
|
||||
Index(u32),
|
||||
}
|
||||
|
||||
|
@ -19,7 +21,7 @@ impl Path {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn key<S: Into<String>>(mut self, key: S) -> Path {
|
||||
pub fn key<S: Into<SmolStr>>(mut self, key: S) -> Path {
|
||||
self.0.push(PathElement::Key(key.into()));
|
||||
self
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ use automerge_protocol as amp;
|
|||
use automerge_protocol::RootDiff;
|
||||
use diffable_sequence::DiffableSequence;
|
||||
use multivalue::NewValueRequest;
|
||||
use smol_str::SmolStr;
|
||||
|
||||
use crate::{error, Cursor, Path, PathElement, Primitive, Value};
|
||||
|
||||
|
@ -28,7 +29,7 @@ pub(crate) struct LocalOperationResult {
|
|||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub(crate) struct StateTree {
|
||||
root_props: HashMap<String, MultiValue>,
|
||||
root_props: HashMap<SmolStr, MultiValue>,
|
||||
cursors: Cursors,
|
||||
}
|
||||
|
||||
|
@ -327,9 +328,12 @@ impl StateTreeComposite {
|
|||
Self::List(StateTreeList {
|
||||
elements: elems, ..
|
||||
}) => Value::Sequence(elems.iter().map(|e| e.default_value()).collect()),
|
||||
Self::Text(StateTreeText { graphemes, .. }) => {
|
||||
Value::Text(graphemes.iter().map(|c| c.default_grapheme()).collect())
|
||||
}
|
||||
Self::Text(StateTreeText { graphemes, .. }) => Value::Text(
|
||||
graphemes
|
||||
.iter()
|
||||
.map(|c| c.default_grapheme().clone())
|
||||
.collect(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -464,13 +468,13 @@ impl StateTreeValue {
|
|||
#[derive(Debug, Clone, PartialEq)]
|
||||
struct StateTreeMap {
|
||||
object_id: amp::ObjectId,
|
||||
props: HashMap<String, MultiValue>,
|
||||
props: HashMap<SmolStr, MultiValue>,
|
||||
}
|
||||
|
||||
impl StateTreeMap {
|
||||
fn check_diff(
|
||||
&self,
|
||||
prop_diffs: &HashMap<String, HashMap<amp::OpId, amp::Diff>>,
|
||||
prop_diffs: &HashMap<SmolStr, HashMap<amp::OpId, amp::Diff>>,
|
||||
) -> Result<(), error::InvalidPatch> {
|
||||
for (prop, prop_diff) in prop_diffs {
|
||||
let mut diff_iter = prop_diff.iter();
|
||||
|
@ -494,7 +498,7 @@ impl StateTreeMap {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn apply_diff(&mut self, prop_diffs: HashMap<String, HashMap<amp::OpId, amp::Diff>>) {
|
||||
fn apply_diff(&mut self, prop_diffs: HashMap<SmolStr, HashMap<amp::OpId, amp::Diff>>) {
|
||||
for (prop, prop_diff) in prop_diffs {
|
||||
let mut diff_iter = prop_diff.into_iter();
|
||||
match diff_iter.next() {
|
||||
|
@ -531,7 +535,7 @@ impl StateTreeMap {
|
|||
.get(key)
|
||||
.map(|mv| mv.update_default(StateTreeValue::Leaf(cursor)));
|
||||
if let Some(new_mv) = new_mv {
|
||||
self.props.insert(key.to_string(), new_mv);
|
||||
self.props.insert(key.into(), new_mv);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -564,13 +568,13 @@ impl StateTreeMap {
|
|||
#[derive(Debug, Clone, PartialEq)]
|
||||
struct StateTreeTable {
|
||||
object_id: amp::ObjectId,
|
||||
props: HashMap<String, MultiValue>,
|
||||
props: HashMap<SmolStr, MultiValue>,
|
||||
}
|
||||
|
||||
impl StateTreeTable {
|
||||
fn check_diff(
|
||||
&self,
|
||||
prop_diffs: &HashMap<String, HashMap<amp::OpId, amp::Diff>>,
|
||||
prop_diffs: &HashMap<SmolStr, HashMap<amp::OpId, amp::Diff>>,
|
||||
) -> Result<(), error::InvalidPatch> {
|
||||
for (prop, prop_diff) in prop_diffs {
|
||||
let mut diff_iter = prop_diff.iter();
|
||||
|
@ -594,7 +598,7 @@ impl StateTreeTable {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn apply_diff(&mut self, prop_diffs: HashMap<String, HashMap<amp::OpId, amp::Diff>>) {
|
||||
fn apply_diff(&mut self, prop_diffs: HashMap<SmolStr, HashMap<amp::OpId, amp::Diff>>) {
|
||||
for (prop, prop_diff) in prop_diffs {
|
||||
let mut diff_iter = prop_diff.into_iter();
|
||||
match diff_iter.next() {
|
||||
|
@ -625,13 +629,13 @@ impl StateTreeTable {
|
|||
.unwrap_or_else(Vec::new)
|
||||
}
|
||||
|
||||
pub fn mutably_update_cursor(&mut self, key: &str, cursor: Primitive) {
|
||||
pub fn mutably_update_cursor(&mut self, key: &SmolStr, cursor: Primitive) {
|
||||
let new_mv = self
|
||||
.props
|
||||
.get(key)
|
||||
.map(|mv| mv.update_default(StateTreeValue::Leaf(cursor)));
|
||||
if let Some(new_mv) = new_mv {
|
||||
self.props.insert(key.to_string(), new_mv);
|
||||
self.props.insert(key.clone(), new_mv);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -699,7 +703,7 @@ impl StateTreeText {
|
|||
pub(crate) fn elem_at(
|
||||
&self,
|
||||
index: usize,
|
||||
) -> Result<(&::OpId, String), error::MissingIndexError> {
|
||||
) -> Result<(&::OpId, &SmolStr), error::MissingIndexError> {
|
||||
self.graphemes
|
||||
.get(index)
|
||||
.map(|mc| (mc.0, mc.1.default_grapheme()))
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use std::{cmp::Ordering, collections::HashMap, iter::Iterator};
|
||||
|
||||
use automerge_protocol as amp;
|
||||
use smol_str::SmolStr;
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
use super::{
|
||||
|
@ -344,12 +345,12 @@ impl NewValue {
|
|||
/// sequences of grapheme clusters
|
||||
#[derive(Debug, Clone, PartialEq, Default)]
|
||||
pub struct MultiGrapheme {
|
||||
winning_value: (amp::OpId, String),
|
||||
conflicts: HashMap<amp::OpId, String>,
|
||||
winning_value: (amp::OpId, SmolStr),
|
||||
conflicts: HashMap<amp::OpId, SmolStr>,
|
||||
}
|
||||
|
||||
impl MultiGrapheme {
|
||||
pub(super) fn new_from_grapheme_cluster(opid: amp::OpId, s: String) -> MultiGrapheme {
|
||||
pub(super) fn new_from_grapheme_cluster(opid: amp::OpId, s: SmolStr) -> MultiGrapheme {
|
||||
debug_assert_eq!(s.graphemes(true).count(), 1);
|
||||
MultiGrapheme {
|
||||
winning_value: (opid, s),
|
||||
|
@ -450,7 +451,7 @@ impl MultiGrapheme {
|
|||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, key: &::OpId, value: String) {
|
||||
fn update(&mut self, key: &::OpId, value: SmolStr) {
|
||||
match key.cmp(&self.winning_value.0) {
|
||||
Ordering::Equal => {
|
||||
self.winning_value.1 = value;
|
||||
|
@ -467,15 +468,15 @@ impl MultiGrapheme {
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn default_grapheme(&self) -> String {
|
||||
self.winning_value.1.clone()
|
||||
pub(super) fn default_grapheme(&self) -> &SmolStr {
|
||||
&self.winning_value.1
|
||||
}
|
||||
|
||||
pub fn default_opid(&self) -> &::OpId {
|
||||
&self.winning_value.0
|
||||
}
|
||||
|
||||
fn iter(&self) -> impl std::iter::Iterator<Item = (&::OpId, &String)> {
|
||||
fn iter(&self) -> impl std::iter::Iterator<Item = (&::OpId, &SmolStr)> {
|
||||
std::iter::once((&(self.winning_value).0, &(self.winning_value.1)))
|
||||
.chain(self.conflicts.iter())
|
||||
}
|
||||
|
@ -568,7 +569,7 @@ where
|
|||
|
||||
fn new_map_or_table(
|
||||
self,
|
||||
props: std::collections::HashMap<String, Value>,
|
||||
props: std::collections::HashMap<SmolStr, Value>,
|
||||
map_type: amp::MapType,
|
||||
) -> NewValue {
|
||||
let make_op_id = amp::OpId(self.start_op, self.actor.clone());
|
||||
|
@ -584,7 +585,7 @@ where
|
|||
ops.push(make_op);
|
||||
let mut current_max_op = self.start_op;
|
||||
let mut cursors = Cursors::new();
|
||||
let mut result_props: HashMap<String, MultiValue> = HashMap::with_capacity(props.len());
|
||||
let mut result_props: HashMap<SmolStr, MultiValue> = HashMap::with_capacity(props.len());
|
||||
for (prop, value) in props {
|
||||
let context = NewValueContext {
|
||||
actor: self.actor,
|
||||
|
@ -669,7 +670,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn new_text(self, graphemes: Vec<String>) -> NewValue {
|
||||
fn new_text(self, graphemes: Vec<SmolStr>) -> NewValue {
|
||||
let make_text_opid = self.actor.op_id_at(self.start_op);
|
||||
let make_op = amp::Op {
|
||||
action: amp::OpType::Make(amp::ObjType::Text),
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use std::{convert::TryInto, num::NonZeroU32};
|
||||
|
||||
use automerge_protocol as amp;
|
||||
use smol_str::SmolStr;
|
||||
|
||||
use super::{
|
||||
random_op_id, LocalOperationResult, MultiGrapheme, MultiValue, NewValueRequest, StateTree,
|
||||
|
@ -108,9 +109,9 @@ impl<'a> ResolvedPath<'a> {
|
|||
ResolvedPath::Text(texttarget) => texttarget.multivalue.default_value(),
|
||||
ResolvedPath::Counter(countertarget) => countertarget.multivalue.default_value(),
|
||||
ResolvedPath::Primitive(p) => p.multivalue.default_value(),
|
||||
ResolvedPath::Character(ctarget) => {
|
||||
Value::Primitive(Primitive::Str(ctarget.multivalue.default_grapheme()))
|
||||
}
|
||||
ResolvedPath::Character(ctarget) => Value::Primitive(Primitive::Str(
|
||||
ctarget.multivalue.default_grapheme().clone(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -257,9 +258,9 @@ impl<'a> ResolvedPathMut<'a> {
|
|||
ResolvedPathMut::Text(texttarget) => texttarget.multivalue.default_value(),
|
||||
ResolvedPathMut::Counter(countertarget) => countertarget.multivalue.default_value(),
|
||||
ResolvedPathMut::Primitive(p) => p.multivalue.default_value(),
|
||||
ResolvedPathMut::Character(ctarget) => {
|
||||
Value::Primitive(Primitive::Str(ctarget.multivalue.default_grapheme()))
|
||||
}
|
||||
ResolvedPathMut::Character(ctarget) => Value::Primitive(Primitive::Str(
|
||||
ctarget.multivalue.default_grapheme().clone(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -281,24 +282,24 @@ pub struct ResolvedRootMut<'a> {
|
|||
impl<'a> ResolvedRootMut<'a> {
|
||||
pub(crate) fn set_key(
|
||||
&mut self,
|
||||
key: &str,
|
||||
key: SmolStr,
|
||||
payload: SetOrInsertPayload<Value>,
|
||||
) -> (Option<MultiValue>, LocalOperationResult) {
|
||||
let newvalue = MultiValue::new_from_value_2(NewValueRequest {
|
||||
actor: payload.actor,
|
||||
start_op: payload.start_op,
|
||||
key: key.into(),
|
||||
key: amp::Key::Map(key.clone()),
|
||||
parent_obj: &::ObjectId::Root,
|
||||
value: payload.value,
|
||||
insert: false,
|
||||
pred: self
|
||||
.root
|
||||
.get(key)
|
||||
.get(&key)
|
||||
.map(|mv| vec![mv.default_opid()])
|
||||
.unwrap_or_else(Vec::new),
|
||||
});
|
||||
let (multivalue, new_ops, _new_cursors) = newvalue.finish();
|
||||
let old = self.root.root_props.insert(key.to_string(), multivalue);
|
||||
let old = self.root.root_props.insert(key, multivalue);
|
||||
(old, LocalOperationResult { new_ops })
|
||||
}
|
||||
|
||||
|
@ -325,7 +326,7 @@ impl<'a> ResolvedRootMut<'a> {
|
|||
)
|
||||
}
|
||||
|
||||
pub(crate) fn rollback_set(&mut self, key: String, value: Option<MultiValue>) {
|
||||
pub(crate) fn rollback_set(&mut self, key: SmolStr, value: Option<MultiValue>) {
|
||||
match value {
|
||||
Some(old) => {
|
||||
self.root.root_props.insert(key, old);
|
||||
|
@ -336,7 +337,7 @@ impl<'a> ResolvedRootMut<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn rollback_delete(&mut self, key: String, value: MultiValue) {
|
||||
pub(crate) fn rollback_delete(&mut self, key: SmolStr, value: MultiValue) {
|
||||
self.root.root_props.insert(key, value);
|
||||
}
|
||||
}
|
||||
|
@ -393,7 +394,7 @@ pub struct ResolvedMapMut<'a> {
|
|||
impl<'a> ResolvedMapMut<'a> {
|
||||
pub(crate) fn set_key(
|
||||
&mut self,
|
||||
key: &str,
|
||||
key: SmolStr,
|
||||
payload: SetOrInsertPayload<Value>,
|
||||
) -> (Option<MultiValue>, LocalOperationResult) {
|
||||
let state_tree_map = match self.multivalue.default_statetree_value_mut() {
|
||||
|
@ -404,13 +405,13 @@ impl<'a> ResolvedMapMut<'a> {
|
|||
actor: payload.actor,
|
||||
start_op: payload.start_op,
|
||||
parent_obj: &state_tree_map.object_id,
|
||||
key: key.into(),
|
||||
key: amp::Key::Map(key.clone()),
|
||||
value: payload.value,
|
||||
insert: false,
|
||||
pred: state_tree_map.pred_for_key(key),
|
||||
pred: state_tree_map.pred_for_key(&key),
|
||||
});
|
||||
let (multivalue, new_ops, _new_cursors) = newvalue.finish();
|
||||
let old = state_tree_map.props.insert(key.to_string(), multivalue);
|
||||
let old = state_tree_map.props.insert(key, multivalue);
|
||||
(old, LocalOperationResult { new_ops })
|
||||
}
|
||||
|
||||
|
@ -437,7 +438,7 @@ impl<'a> ResolvedMapMut<'a> {
|
|||
)
|
||||
}
|
||||
|
||||
pub(crate) fn rollback_set(&mut self, key: String, value: Option<MultiValue>) {
|
||||
pub(crate) fn rollback_set(&mut self, key: SmolStr, value: Option<MultiValue>) {
|
||||
let state_tree_map = match self.multivalue.default_statetree_value_mut() {
|
||||
StateTreeValue::Composite(StateTreeComposite::Map(map)) => map,
|
||||
_ => unreachable!(),
|
||||
|
@ -452,7 +453,7 @@ impl<'a> ResolvedMapMut<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn rollback_delete(&mut self, key: String, value: MultiValue) {
|
||||
pub(crate) fn rollback_delete(&mut self, key: SmolStr, value: MultiValue) {
|
||||
let state_tree_map = match self.multivalue.default_statetree_value_mut() {
|
||||
StateTreeValue::Composite(StateTreeComposite::Map(map)) => map,
|
||||
_ => unreachable!(),
|
||||
|
@ -474,7 +475,7 @@ pub struct ResolvedTableMut<'a> {
|
|||
impl<'a> ResolvedTableMut<'a> {
|
||||
pub(crate) fn set_key(
|
||||
&mut self,
|
||||
key: &str,
|
||||
key: SmolStr,
|
||||
payload: SetOrInsertPayload<Value>,
|
||||
) -> (Option<MultiValue>, LocalOperationResult) {
|
||||
let state_tree_table = match self.multivalue.default_statetree_value_mut() {
|
||||
|
@ -485,13 +486,13 @@ impl<'a> ResolvedTableMut<'a> {
|
|||
actor: payload.actor,
|
||||
start_op: payload.start_op,
|
||||
parent_obj: &state_tree_table.object_id,
|
||||
key: key.into(),
|
||||
key: amp::Key::Map(key.clone()),
|
||||
value: payload.value,
|
||||
insert: false,
|
||||
pred: state_tree_table.pred_for_key(key),
|
||||
pred: state_tree_table.pred_for_key(&key),
|
||||
});
|
||||
let (multivalue, new_ops, _new_cursors) = newvalue.finish();
|
||||
let old = state_tree_table.props.insert(key.to_owned(), multivalue);
|
||||
let old = state_tree_table.props.insert(key, multivalue);
|
||||
(old, LocalOperationResult { new_ops })
|
||||
}
|
||||
|
||||
|
@ -518,7 +519,7 @@ impl<'a> ResolvedTableMut<'a> {
|
|||
)
|
||||
}
|
||||
|
||||
pub(crate) fn rollback_set(&mut self, key: String, value: Option<MultiValue>) {
|
||||
pub(crate) fn rollback_set(&mut self, key: SmolStr, value: Option<MultiValue>) {
|
||||
let state_tree_map = match self.multivalue.default_statetree_value_mut() {
|
||||
StateTreeValue::Composite(StateTreeComposite::Table(map)) => map,
|
||||
_ => unreachable!(),
|
||||
|
@ -533,7 +534,7 @@ impl<'a> ResolvedTableMut<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn rollback_delete(&mut self, key: String, value: MultiValue) {
|
||||
pub(crate) fn rollback_delete(&mut self, key: SmolStr, value: MultiValue) {
|
||||
let state_tree_map = match self.multivalue.default_statetree_value_mut() {
|
||||
StateTreeValue::Composite(StateTreeComposite::Table(map)) => map,
|
||||
_ => unreachable!(),
|
||||
|
@ -557,7 +558,7 @@ impl<'a> ResolvedTextMut<'a> {
|
|||
pub(crate) fn insert(
|
||||
&mut self,
|
||||
index: u32,
|
||||
payload: SetOrInsertPayload<String>,
|
||||
payload: SetOrInsertPayload<SmolStr>,
|
||||
) -> Result<LocalOperationResult, error::MissingIndexError> {
|
||||
let state_tree_text = match self.multivalue.default_statetree_value_mut() {
|
||||
StateTreeValue::Composite(StateTreeComposite::Text(text)) => text,
|
||||
|
@ -590,7 +591,7 @@ impl<'a> ResolvedTextMut<'a> {
|
|||
payload: SetOrInsertPayload<I>,
|
||||
) -> Result<LocalOperationResult, error::MissingIndexError>
|
||||
where
|
||||
I: ExactSizeIterator<Item = String>,
|
||||
I: ExactSizeIterator<Item = SmolStr>,
|
||||
{
|
||||
let state_tree_text = match self.multivalue.default_statetree_value_mut() {
|
||||
StateTreeValue::Composite(StateTreeComposite::Text(text)) => text,
|
||||
|
@ -607,7 +608,7 @@ impl<'a> ResolvedTextMut<'a> {
|
|||
let mut chars: Vec<amp::ScalarValue> = Vec::with_capacity(payload.value.len());
|
||||
for (i, c) in payload.value.enumerate() {
|
||||
let insert_op = amp::OpId::new(payload.start_op + i as u64, payload.actor);
|
||||
chars.push(amp::ScalarValue::Str(c.to_string()));
|
||||
chars.push(amp::ScalarValue::Str(c.clone()));
|
||||
let c = MultiGrapheme::new_from_grapheme_cluster(insert_op, c);
|
||||
values.push(c)
|
||||
}
|
||||
|
@ -630,7 +631,7 @@ impl<'a> ResolvedTextMut<'a> {
|
|||
pub(crate) fn set(
|
||||
&mut self,
|
||||
index: u32,
|
||||
payload: SetOrInsertPayload<String>,
|
||||
payload: SetOrInsertPayload<SmolStr>,
|
||||
) -> Result<(MultiGrapheme, LocalOperationResult), error::MissingIndexError> {
|
||||
let state_tree_text = match self.multivalue.default_statetree_value_mut() {
|
||||
StateTreeValue::Composite(StateTreeComposite::Text(text)) => text,
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
use std::{
|
||||
borrow::{Borrow, Cow},
|
||||
collections::HashMap,
|
||||
};
|
||||
use std::{borrow::Cow, collections::HashMap};
|
||||
|
||||
use automerge_protocol as amp;
|
||||
use serde::Serialize;
|
||||
use smol_str::SmolStr;
|
||||
|
||||
use crate::path::PathElement;
|
||||
|
||||
|
@ -21,11 +19,11 @@ impl From<HashMap<amp::OpId, Value>> for Conflicts {
|
|||
#[cfg_attr(feature = "derive-arbitrary", derive(arbitrary::Arbitrary))]
|
||||
#[serde(untagged)]
|
||||
pub enum Value {
|
||||
Map(HashMap<String, Value>),
|
||||
Table(HashMap<String, Value>),
|
||||
Map(HashMap<SmolStr, Value>),
|
||||
Table(HashMap<SmolStr, Value>),
|
||||
Sequence(Vec<Value>),
|
||||
/// Sequence of grapheme clusters
|
||||
Text(Vec<String>),
|
||||
Text(Vec<SmolStr>),
|
||||
Primitive(Primitive),
|
||||
}
|
||||
|
||||
|
@ -33,7 +31,7 @@ pub enum Value {
|
|||
#[cfg_attr(feature = "derive-arbitrary", derive(arbitrary::Arbitrary))]
|
||||
pub enum Primitive {
|
||||
Bytes(Vec<u8>),
|
||||
Str(String),
|
||||
Str(SmolStr),
|
||||
Int(i64),
|
||||
Uint(u64),
|
||||
F64(f64),
|
||||
|
@ -95,7 +93,7 @@ impl From<Primitive> for Value {
|
|||
|
||||
impl From<&str> for Value {
|
||||
fn from(s: &str) -> Self {
|
||||
Value::Primitive(Primitive::Str(s.to_string()))
|
||||
Value::Primitive(Primitive::Str(SmolStr::new(s)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,7 +109,7 @@ impl From<&::CursorDiff> for Primitive {
|
|||
|
||||
impl From<char> for Value {
|
||||
fn from(c: char) -> Value {
|
||||
Value::Primitive(Primitive::Str(c.to_string()))
|
||||
Value::Primitive(Primitive::Str(SmolStr::new(c.to_string())))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,12 +131,12 @@ impl From<i64> for Value {
|
|||
impl<T, K> From<HashMap<K, T>> for Value
|
||||
where
|
||||
T: Into<Value>,
|
||||
K: Borrow<str>,
|
||||
K: AsRef<str>,
|
||||
{
|
||||
fn from(h: HashMap<K, T>) -> Self {
|
||||
Value::Map(
|
||||
h.into_iter()
|
||||
.map(|(k, v)| (k.borrow().to_string(), v.into()))
|
||||
.map(|(k, v)| (SmolStr::new(k), v.into()))
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
@ -154,16 +152,16 @@ impl Value {
|
|||
pub fn from_json(json: &serde_json::Value) -> Value {
|
||||
match json {
|
||||
serde_json::Value::Object(kvs) => {
|
||||
let result: HashMap<String, Value> = kvs
|
||||
let result: HashMap<SmolStr, Value> = kvs
|
||||
.iter()
|
||||
.map(|(k, v)| (k.clone(), Value::from_json(v)))
|
||||
.map(|(k, v)| (SmolStr::new(k), Value::from_json(v)))
|
||||
.collect();
|
||||
Value::Map(result)
|
||||
}
|
||||
serde_json::Value::Array(vs) => {
|
||||
Value::Sequence(vs.iter().map(Value::from_json).collect())
|
||||
}
|
||||
serde_json::Value::String(s) => Value::Primitive(Primitive::Str(s.clone())),
|
||||
serde_json::Value::String(s) => Value::Primitive(Primitive::Str(SmolStr::new(s))),
|
||||
serde_json::Value::Number(n) => {
|
||||
Value::Primitive(Primitive::F64(n.as_f64().unwrap_or(0.0)))
|
||||
}
|
||||
|
@ -175,13 +173,17 @@ impl Value {
|
|||
pub fn to_json(&self) -> serde_json::Value {
|
||||
match self {
|
||||
Value::Map(map) => {
|
||||
let result: serde_json::map::Map<String, serde_json::Value> =
|
||||
map.iter().map(|(k, v)| (k.clone(), v.to_json())).collect();
|
||||
let result: serde_json::map::Map<String, serde_json::Value> = map
|
||||
.iter()
|
||||
.map(|(k, v)| (k.to_string(), v.to_json()))
|
||||
.collect();
|
||||
serde_json::Value::Object(result)
|
||||
}
|
||||
Value::Table(map) => {
|
||||
let result: serde_json::map::Map<String, serde_json::Value> =
|
||||
map.iter().map(|(k, v)| (k.clone(), v.to_json())).collect();
|
||||
let result: serde_json::map::Map<String, serde_json::Value> = map
|
||||
.iter()
|
||||
.map(|(k, v)| (k.to_string(), v.to_json()))
|
||||
.collect();
|
||||
serde_json::Value::Object(result)
|
||||
}
|
||||
Value::Sequence(elements) => {
|
||||
|
@ -313,7 +315,7 @@ pub(crate) fn value_to_op_requests(
|
|||
let mut op_num = start_op + 1;
|
||||
for c in chars.iter() {
|
||||
insert_ops.push(amp::Op {
|
||||
action: amp::OpType::Set(amp::ScalarValue::Str(c.to_string())),
|
||||
action: amp::OpType::Set(amp::ScalarValue::Str(c.clone())),
|
||||
obj: amp::ObjectId::from(make_text_op.clone()),
|
||||
key: last_elemid.clone().into(),
|
||||
insert: true,
|
||||
|
@ -400,15 +402,15 @@ mod tests {
|
|||
#[test]
|
||||
fn get_value() {
|
||||
let v = Value::Map(hashmap! {
|
||||
"hello".to_owned() => Value::Primitive(Primitive::Str("world".to_owned())),
|
||||
"again".to_owned() => Value::Sequence(vec![Value::Primitive(Primitive::Int(2))])
|
||||
"hello".into() => Value::Primitive(Primitive::Str("world".into())),
|
||||
"again".into() => Value::Sequence(vec![Value::Primitive(Primitive::Int(2))])
|
||||
});
|
||||
|
||||
assert_eq!(v.get_value(Path::root()), Some(Cow::Borrowed(&v)));
|
||||
assert_eq!(
|
||||
v.get_value(Path::root().key("hello")),
|
||||
Some(Cow::Borrowed(&Value::Primitive(Primitive::Str(
|
||||
"world".to_owned()
|
||||
"world".into()
|
||||
))))
|
||||
);
|
||||
assert_eq!(v.get_value(Path::root().index(0)), None);
|
||||
|
|
|
@ -600,7 +600,7 @@ fn apply_updates_inside_list_conflicts() {
|
|||
assert_eq!(
|
||||
frontend.state(),
|
||||
&Into::<Value>::into(
|
||||
hashmap! {"birds" => vec![hashmap!{"species" => Primitive::Str("lapwing".to_string()), "numSeen" => Primitive::Int(2)}]}
|
||||
hashmap! {"birds" => vec![hashmap!{"species" => Primitive::Str("lapwing".into()), "numSeen" => Primitive::Int(2)}]}
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -669,7 +669,7 @@ fn apply_updates_inside_list_conflicts() {
|
|||
assert_eq!(
|
||||
frontend.state(),
|
||||
&Into::<Value>::into(
|
||||
hashmap! {"birds" => vec![hashmap!{"species" => Primitive::Str("lapwing".to_string()), "numSeen" => Primitive::Int(2)}]}
|
||||
hashmap! {"birds" => vec![hashmap!{"species" => Primitive::Str("lapwing".into()), "numSeen" => Primitive::Int(2)}]}
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -726,7 +726,7 @@ fn apply_updates_inside_list_conflicts() {
|
|||
assert_eq!(
|
||||
frontend.state(),
|
||||
&Into::<Value>::into(
|
||||
hashmap! {"birds" => vec![hashmap!{"species" => Primitive::Str("lapwing".to_string()), "numSeen" => Primitive::Int(2)}]}
|
||||
hashmap! {"birds" => vec![hashmap!{"species" => Primitive::Str("lapwing".into()), "numSeen" => Primitive::Int(2)}]}
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -976,7 +976,7 @@ fn test_text_objects() {
|
|||
assert_eq!(
|
||||
frontend.state(),
|
||||
&Into::<Value>::into(
|
||||
hashmap! {"name" => Value::Text("ben".graphemes(true).map(|s|s.to_owned()).collect())}
|
||||
hashmap! {"name" => Value::Text("ben".graphemes(true).map(|s|s.into()).collect())}
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -999,7 +999,7 @@ fn test_text_objects() {
|
|||
amp::DiffEdit::Update{
|
||||
index: 1,
|
||||
op_id: actor.op_id_at(5),
|
||||
value: amp::Diff::Value(amp::ScalarValue::Str("i".to_string())),
|
||||
value: amp::Diff::Value(amp::ScalarValue::Str("i".into())),
|
||||
}
|
||||
],
|
||||
})
|
||||
|
@ -1013,7 +1013,7 @@ fn test_text_objects() {
|
|||
assert_eq!(
|
||||
frontend.state(),
|
||||
&Into::<Value>::into(
|
||||
hashmap! {"name" => Value::Text("bi".graphemes(true).map(|s|s.to_owned()).collect())}
|
||||
hashmap! {"name" => Value::Text("bi".graphemes(true).map(|s|s.into()).collect())}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -1030,7 +1030,7 @@ fn test_unchanged_diff_creates_empty_objects() {
|
|||
pending_changes: 0,
|
||||
diffs: RootDiff {
|
||||
props: hashmap! {
|
||||
"text".to_string() => hashmap!{
|
||||
"text".into() => hashmap!{
|
||||
"1@cfe5fefb771f4c15a716d488012cbf40".try_into().unwrap() => amp::Diff::Text(amp::TextDiff{
|
||||
object_id: "1@cfe5fefb771f4c15a716d488012cbf40".try_into().unwrap(),
|
||||
edits: Vec::new(),
|
||||
|
@ -1042,6 +1042,6 @@ fn test_unchanged_diff_creates_empty_objects() {
|
|||
doc.apply_patch(patch).unwrap();
|
||||
assert_eq!(
|
||||
doc.state(),
|
||||
&Value::Map(hashmap! {"text".to_string() => Value::Text(Vec::new())},),
|
||||
&Value::Map(hashmap! {"text".into() => Value::Text(Vec::new())},),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -289,7 +289,7 @@ fn dont_allow_out_of_order_request_patches() {
|
|||
deps: Vec::new(),
|
||||
diffs: RootDiff {
|
||||
props: hashmap! {
|
||||
"partridges".to_string() => hashmap!{
|
||||
"partridges".into() => hashmap!{
|
||||
random_op_id() => amp::Diff::Value(amp::ScalarValue::Int(1))
|
||||
}
|
||||
},
|
||||
|
@ -335,7 +335,7 @@ fn handle_concurrent_insertions_into_lists() {
|
|||
deps: Vec::new(),
|
||||
diffs: RootDiff {
|
||||
props: hashmap! {
|
||||
"birds".to_string() => hashmap!{
|
||||
"birds".into() => hashmap!{
|
||||
doc.actor_id.op_id_at(1) => amp::Diff::List(amp::ListDiff{
|
||||
object_id: birds_id.clone(),
|
||||
edits: vec![amp::DiffEdit::SingleElementInsert{
|
||||
|
@ -435,7 +435,7 @@ fn handle_concurrent_insertions_into_lists() {
|
|||
deps: Vec::new(),
|
||||
diffs: RootDiff {
|
||||
props: hashmap! {
|
||||
"birds".to_string() => hashmap!{
|
||||
"birds".into() => hashmap!{
|
||||
doc.actor_id.op_id_at(1) => amp::Diff::List(amp::ListDiff{
|
||||
object_id: birds_id,
|
||||
edits: vec![
|
||||
|
|
|
@ -51,7 +51,7 @@ fn test_allow_cursor_on_text_element() {
|
|||
.change::<_, _, InvalidChangeRequest>(None, |d| {
|
||||
d.add_change(LocalChange::set(
|
||||
Path::root().key("list"),
|
||||
Value::Text("123".graphemes(true).map(|s| s.to_owned()).collect()),
|
||||
Value::Text("123".graphemes(true).map(|s| s.into()).collect()),
|
||||
))?;
|
||||
let cursor = d
|
||||
.cursor_to_path(&Path::root().key("list").index(1))
|
||||
|
@ -90,7 +90,7 @@ fn test_do_not_allow_index_past_end_of_list() {
|
|||
.change::<_, _, InvalidChangeRequest>(None, |d| {
|
||||
d.add_change(LocalChange::set(
|
||||
Path::root().key("list"),
|
||||
Value::Text("123".graphemes(true).map(|s| s.to_owned()).collect()),
|
||||
Value::Text("123".graphemes(true).map(|s| s.into()).collect()),
|
||||
))?;
|
||||
let cursor = d.cursor_to_path(&Path::root().key("list").index(10));
|
||||
assert_eq!(cursor, None);
|
||||
|
@ -106,7 +106,7 @@ fn test_do_not_allow_index_past_end_of_list() {
|
|||
// .change::<_, _, InvalidChangeRequest>(None, |d| {
|
||||
// d.add_change(LocalChange::set(
|
||||
// Path::root().key("list"),
|
||||
// Value::Text("123".graphemes(true).map(|s| s.to_owned()).collect()),
|
||||
// Value::Text("123".graphemes(true).map(|s| s.into()).collect()),
|
||||
// ))?;
|
||||
// let cursor = d
|
||||
// .cursor_to_path(&Path::root().key("list").index(1))
|
||||
|
@ -121,7 +121,7 @@ fn test_do_not_allow_index_past_end_of_list() {
|
|||
|
||||
// d.add_change(LocalChange::insert(
|
||||
// Path::root().key("list").index(0),
|
||||
// Value::Primitive(Primitive::Str("0".to_string())),
|
||||
// Value::Primitive(Primitive::Str("0".into())),
|
||||
// ))?;
|
||||
// let cursor_the_third = d.value_at_path(&Path::root().key("cursor"));
|
||||
// if let Some(Value::Primitive(Primitive::Cursor(c))) = cursor_the_third {
|
||||
|
@ -147,7 +147,7 @@ fn test_set_cursor_to_new_element_in_diff() {
|
|||
pending_changes: 0,
|
||||
diffs: RootDiff {
|
||||
props: hashmap! {
|
||||
"list".to_string() => hashmap!{
|
||||
"list".into() => hashmap!{
|
||||
actor.op_id_at(1) => amp::Diff::List(amp::ListDiff{
|
||||
object_id: actor.op_id_at(1).into(),
|
||||
edits: vec![
|
||||
|
@ -166,7 +166,7 @@ fn test_set_cursor_to_new_element_in_diff() {
|
|||
],
|
||||
}),
|
||||
},
|
||||
"cursor".to_string() => hashmap!{
|
||||
"cursor".into() => hashmap!{
|
||||
actor.op_id_at(4) => amp::Diff::Cursor(amp::CursorDiff{
|
||||
elem_id: actor.op_id_at(3),
|
||||
index: 1,
|
||||
|
@ -187,7 +187,7 @@ fn test_set_cursor_to_new_element_in_diff() {
|
|||
pending_changes: 0,
|
||||
diffs: RootDiff {
|
||||
props: hashmap! {
|
||||
"cursor".to_string() => hashmap!{
|
||||
"cursor".into() => hashmap!{
|
||||
actor.op_id_at(4) => amp::Diff::Cursor(amp::CursorDiff{
|
||||
elem_id: actor.op_id_at(2),
|
||||
index: 0,
|
||||
|
@ -222,7 +222,7 @@ fn test_set_cursor_to_new_element_in_diff() {
|
|||
// .change::<_, _, InvalidChangeRequest>(None, |d| {
|
||||
// d.add_change(LocalChange::set(
|
||||
// Path::root().key("list"),
|
||||
// Value::Text("123".graphemes(true).map(|s| s.to_owned()).collect()),
|
||||
// Value::Text("123".graphemes(true).map(|s| s.into()).collect()),
|
||||
// ))?;
|
||||
// let cursor = d
|
||||
// .cursor_to_path(&Path::root().key("list").index(1))
|
||||
|
@ -237,11 +237,11 @@ fn test_set_cursor_to_new_element_in_diff() {
|
|||
|
||||
// d.add_change(LocalChange::insert(
|
||||
// Path::root().key("list").index(0),
|
||||
// Value::Primitive(Primitive::Str("0".to_string())),
|
||||
// Value::Primitive(Primitive::Str("0".into())),
|
||||
// ))?;
|
||||
// d.add_change(LocalChange::insert(
|
||||
// Path::root().key("list").index(0),
|
||||
// Value::Primitive(Primitive::Str("1".to_string())),
|
||||
// Value::Primitive(Primitive::Str("1".into())),
|
||||
// ))?;
|
||||
// let cursor = d
|
||||
// .cursor_to_path(&Path::root().key("list").index(2))
|
||||
|
@ -268,7 +268,7 @@ fn test_delete_cursor_and_adding_again() {
|
|||
.change::<_, _, InvalidChangeRequest>(None, |d| {
|
||||
d.add_change(LocalChange::set(
|
||||
Path::root().key("list"),
|
||||
Value::Text("123".graphemes(true).map(|s| s.to_owned()).collect()),
|
||||
Value::Text("123".graphemes(true).map(|s| s.into()).collect()),
|
||||
))?;
|
||||
let cursor = d
|
||||
.cursor_to_path(&Path::root().key("list").index(1))
|
||||
|
|
|
@ -50,7 +50,7 @@ fn test_set_root_object_properties() {
|
|||
.change::<_, _, InvalidChangeRequest>(Some("set root object".into()), |doc| {
|
||||
doc.add_change(LocalChange::set(
|
||||
Path::root().key("bird"),
|
||||
Value::Primitive(Primitive::Str("magpie".to_string())),
|
||||
Value::Primitive(Primitive::Str("magpie".into())),
|
||||
))?;
|
||||
Ok(())
|
||||
})
|
||||
|
@ -70,7 +70,7 @@ fn test_set_root_object_properties() {
|
|||
hash: None,
|
||||
deps: Vec::new(),
|
||||
operations: vec![amp::Op {
|
||||
action: amp::OpType::Set(amp::ScalarValue::Str("magpie".to_string())),
|
||||
action: amp::OpType::Set(amp::ScalarValue::Str("magpie".into())),
|
||||
obj: "_root".try_into().unwrap(),
|
||||
key: "bird".into(),
|
||||
insert: false,
|
||||
|
@ -687,7 +687,7 @@ fn test_sets_characters_in_text() {
|
|||
doc.change::<_, _, InvalidChangeRequest>(None, |doc| {
|
||||
doc.add_change(LocalChange::set(
|
||||
Path::root().key("text"),
|
||||
Value::Text("some".graphemes(true).map(|s| s.to_owned()).collect()),
|
||||
Value::Text("some".graphemes(true).map(|s| s.into()).collect()),
|
||||
))?;
|
||||
Ok(())
|
||||
})
|
||||
|
@ -727,7 +727,7 @@ fn test_sets_characters_in_text() {
|
|||
|
||||
let value = doc.get_value(&Path::root()).unwrap();
|
||||
let expected_value: Value = Value::Map(hashmap! {
|
||||
"text".into() => Value::Text(vec!["s".to_owned(), "a".to_owned(), "m".to_owned(), "e".to_owned()]),
|
||||
"text".into() => Value::Text(vec!["s".into(), "a".into(), "m".into(), "e".into()]),
|
||||
});
|
||||
assert_eq!(value, expected_value);
|
||||
}
|
||||
|
@ -738,7 +738,7 @@ fn test_inserts_characters_in_text() {
|
|||
doc.change::<_, _, InvalidChangeRequest>(None, |doc| {
|
||||
doc.add_change(LocalChange::set(
|
||||
Path::root().key("text"),
|
||||
Value::Text("same".graphemes(true).map(|s| s.to_owned()).collect()),
|
||||
Value::Text("same".graphemes(true).map(|s| s.into()).collect()),
|
||||
))?;
|
||||
Ok(())
|
||||
})
|
||||
|
@ -781,7 +781,7 @@ fn test_inserts_characters_in_text() {
|
|||
|
||||
let value = doc.get_value(&Path::root()).unwrap();
|
||||
let expected_value: Value = Value::Map(hashmap! {
|
||||
"text".into() => Value::Text(vec!["s".to_owned(), "h".to_owned(), "a".to_owned(), "m".to_owned(), "e".to_owned()]),
|
||||
"text".into() => Value::Text(vec!["s".into(), "h".into(), "a".into(), "m".into(), "e".into()]),
|
||||
});
|
||||
assert_eq!(value, expected_value);
|
||||
}
|
||||
|
@ -835,7 +835,7 @@ fn test_inserts_characters_at_start_of_text() {
|
|||
|
||||
let value = doc.get_value(&Path::root()).unwrap();
|
||||
let expected_value: Value = Value::Map(hashmap! {
|
||||
"text".into() => Value::Text(vec!["i".to_owned()]),
|
||||
"text".into() => Value::Text(vec!["i".into()]),
|
||||
});
|
||||
assert_eq!(value, expected_value);
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ uuid = { version = "^0.8.2", features=["v4"] }
|
|||
thiserror = "1.0.16"
|
||||
serde = { version = "^1.0", features=["derive"] }
|
||||
arbitrary = { version = "1", features = ["derive"], optional = true }
|
||||
smol_str = { version = "0.1.17", features = ["serde"] }
|
||||
|
||||
[dev-dependencies]
|
||||
maplit = "^1.0.2"
|
||||
|
|
|
@ -4,6 +4,7 @@ mod utility_impls;
|
|||
use std::{collections::HashMap, convert::TryFrom, fmt, num::NonZeroU32};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use smol_str::SmolStr;
|
||||
|
||||
#[derive(Eq, PartialEq, Hash, Clone, PartialOrd, Ord, Default)]
|
||||
#[cfg_attr(feature = "derive-arbitrary", derive(arbitrary::Arbitrary))]
|
||||
|
@ -169,7 +170,7 @@ impl ElementId {
|
|||
#[derive(Serialize, PartialEq, Eq, Debug, Hash, Clone)]
|
||||
#[serde(untagged)]
|
||||
pub enum Key {
|
||||
Map(String),
|
||||
Map(SmolStr),
|
||||
Seq(ElementId),
|
||||
}
|
||||
|
||||
|
@ -231,7 +232,7 @@ impl DataType {
|
|||
#[serde(untagged)]
|
||||
pub enum ScalarValue {
|
||||
Bytes(Vec<u8>),
|
||||
Str(String),
|
||||
Str(SmolStr),
|
||||
Int(i64),
|
||||
Uint(u64),
|
||||
F64(f64),
|
||||
|
@ -433,14 +434,14 @@ impl Diff {
|
|||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MapDiff {
|
||||
pub object_id: ObjectId,
|
||||
pub props: HashMap<String, HashMap<OpId, Diff>>,
|
||||
pub props: HashMap<SmolStr, HashMap<OpId, Diff>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, PartialEq, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TableDiff {
|
||||
pub object_id: ObjectId,
|
||||
pub props: HashMap<String, HashMap<OpId, Diff>>,
|
||||
pub props: HashMap<SmolStr, HashMap<OpId, Diff>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, PartialEq, Clone)]
|
||||
|
@ -537,7 +538,7 @@ pub struct Patch {
|
|||
/// A custom MapDiff that implicitly has the object_id Root and is a map object.
|
||||
#[derive(Debug, PartialEq, Clone, Default)]
|
||||
pub struct RootDiff {
|
||||
pub props: HashMap<String, HashMap<OpId, Diff>>,
|
||||
pub props: HashMap<SmolStr, HashMap<OpId, Diff>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
|
|
|
@ -6,6 +6,7 @@ use serde::{
|
|||
ser::SerializeStruct,
|
||||
Deserialize, Deserializer, Serialize, Serializer,
|
||||
};
|
||||
use smol_str::SmolStr;
|
||||
|
||||
use super::read_field;
|
||||
use crate::{
|
||||
|
@ -147,7 +148,7 @@ impl<'de> Deserialize<'de> for Diff {
|
|||
let mut object_id: Option<ObjectId> = None;
|
||||
let mut diff_type: Option<RawDiffType> = None;
|
||||
//let mut obj_type: Option<ObjType> = None;
|
||||
let mut props: Option<HashMap<String, HashMap<OpId, Diff>>> = None;
|
||||
let mut props: Option<HashMap<SmolStr, HashMap<OpId, Diff>>> = None;
|
||||
let mut value: Option<ScalarValue> = None;
|
||||
let mut datatype: Option<DataType> = None;
|
||||
let mut elem_id: Option<OpId> = None;
|
||||
|
@ -304,7 +305,7 @@ mod tests {
|
|||
let diff = Diff::Map(MapDiff {
|
||||
object_id: ObjectId::from_str("1@6121f8757d5d46609b665218b2b3a141").unwrap(),
|
||||
props: hashmap! {
|
||||
"key".to_string() => hashmap!{
|
||||
"key".into() => hashmap!{
|
||||
OpId::from_str("1@4a093244de2b4fd0a4203724e15dfc16").unwrap() => "value".into()
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use smol_str::SmolStr;
|
||||
|
||||
use crate::{ElementId, Key};
|
||||
|
||||
|
@ -9,7 +10,7 @@ impl<'de> Deserialize<'de> for Key {
|
|||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
let s = SmolStr::deserialize(deserializer)?;
|
||||
if let Ok(eid) = ElementId::from_str(&s) {
|
||||
Ok(Key::Seq(eid))
|
||||
} else {
|
||||
|
|
|
@ -311,7 +311,7 @@ mod tests {
|
|||
"pred": []
|
||||
}),
|
||||
expected: Ok(Op {
|
||||
action: OpType::Set(ScalarValue::Str("somestring".to_string())),
|
||||
action: OpType::Set(ScalarValue::Str("somestring".into())),
|
||||
obj: ObjectId::Root,
|
||||
key: "somekey".into(),
|
||||
insert: false,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use serde::{de, Deserialize, Deserializer};
|
||||
use smol_str::SmolStr;
|
||||
|
||||
use crate::ScalarValue;
|
||||
|
||||
|
@ -47,7 +48,7 @@ impl<'de> Deserialize<'de> for ScalarValue {
|
|||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Ok(ScalarValue::Str(value.to_string()))
|
||||
Ok(ScalarValue::Str(SmolStr::new(value)))
|
||||
}
|
||||
|
||||
fn visit_none<E>(self) -> Result<ScalarValue, E>
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use std::cmp::{Ordering, PartialOrd};
|
||||
|
||||
use smol_str::SmolStr;
|
||||
|
||||
use crate::{ElementId, Key, OpId};
|
||||
|
||||
impl PartialOrd for Key {
|
||||
|
@ -42,6 +44,6 @@ where
|
|||
S: AsRef<str>,
|
||||
{
|
||||
fn from(s: S) -> Self {
|
||||
Key::Map(s.as_ref().to_string())
|
||||
Key::Map(SmolStr::new(s))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use std::fmt;
|
||||
|
||||
use smol_str::SmolStr;
|
||||
|
||||
use crate::ScalarValue;
|
||||
|
||||
impl From<&str> for ScalarValue {
|
||||
|
@ -34,7 +36,7 @@ impl From<bool> for ScalarValue {
|
|||
|
||||
impl From<char> for ScalarValue {
|
||||
fn from(c: char) -> Self {
|
||||
ScalarValue::Str(c.to_string())
|
||||
ScalarValue::Str(SmolStr::new(c.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ fn arb_objtype() -> impl Strategy<Value = amp::ObjType> {
|
|||
|
||||
fn arb_scalar_value() -> impl Strategy<Value = amp::ScalarValue> {
|
||||
prop_oneof![
|
||||
any::<String>().prop_map(amp::ScalarValue::Str),
|
||||
any::<String>().prop_map(|s| amp::ScalarValue::Str(s.into())),
|
||||
any::<i64>().prop_map(amp::ScalarValue::Int),
|
||||
//This is necessary because we don't support integers larger than i64 in the JSON protocol
|
||||
//any::<i64>().prop_map(|i| amp::ScalarValue::Uint(i as u64)),
|
||||
|
@ -61,7 +61,7 @@ fn arb_elemid() -> impl Strategy<Value = amp::ElementId> {
|
|||
|
||||
fn arb_key() -> impl Strategy<Value = amp::Key> {
|
||||
prop_oneof![
|
||||
any::<String>().prop_map(amp::Key::Map),
|
||||
any::<String>().prop_map(|s| amp::Key::Map(s.into())),
|
||||
arb_elemid().prop_map(amp::Key::Seq),
|
||||
]
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ tracing = "0.1.25"
|
|||
tracing-subscriber = {version = "0.2", features = ["chrono", "env-filter", "fmt"]}
|
||||
unicode-segmentation = "1.7.1"
|
||||
maplit = "^1.0.2"
|
||||
smol_str = "0.1.17"
|
||||
|
||||
[[bench]]
|
||||
name = "crdt_benchmarks"
|
||||
|
|
|
@ -3,6 +3,7 @@ use std::{collections::HashMap, convert::TryInto, default::Default};
|
|||
use automerge::{Backend, Frontend, InvalidChangeRequest, LocalChange, Path, Primitive, Value};
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use rand::{thread_rng, Rng};
|
||||
use smol_str::SmolStr;
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
pub fn b1_1(c: &mut Criterion) {
|
||||
|
@ -100,15 +101,12 @@ pub fn b1_2(c: &mut Criterion) {
|
|||
.unwrap();
|
||||
doc2.apply_patch(patch2).unwrap();
|
||||
|
||||
let random_string: String = thread_rng()
|
||||
let random_string: SmolStr = thread_rng()
|
||||
.sample_iter(&rand::distributions::Alphanumeric)
|
||||
.take(6000)
|
||||
.map(char::from)
|
||||
.collect();
|
||||
let chars: Vec<_> = random_string
|
||||
.graphemes(true)
|
||||
.map(|s| s.to_owned())
|
||||
.collect();
|
||||
let chars: Vec<_> = random_string.graphemes(true).map(|s| s.into()).collect();
|
||||
let text = Value::Text(chars);
|
||||
(doc1, backend1, doc2, backend2, text)
|
||||
},
|
||||
|
|
|
@ -8,7 +8,7 @@ fn small_change_backend() -> Backend {
|
|||
.change::<_, _, InvalidChangeRequest>(None, |doc| {
|
||||
doc.add_change(LocalChange::set(
|
||||
Path::root().key("a"),
|
||||
Value::Primitive(Primitive::Str("hello world".to_owned())),
|
||||
Value::Primitive(Primitive::Str("hello world".into())),
|
||||
))?;
|
||||
Ok(())
|
||||
})
|
||||
|
@ -28,11 +28,11 @@ fn medium_change_backend() -> Backend {
|
|||
Value::Map(
|
||||
vec![
|
||||
(
|
||||
"\u{0}\u{0}".to_owned(),
|
||||
"\u{0}\u{0}".into(),
|
||||
Value::Sequence(vec![
|
||||
Value::Primitive(Primitive::Str("".to_owned())),
|
||||
Value::Primitive(Primitive::Str("".into())),
|
||||
Value::Primitive(Primitive::Counter(0)),
|
||||
Value::Primitive(Primitive::Str("".to_owned())),
|
||||
Value::Primitive(Primitive::Str("".into())),
|
||||
Value::Primitive(Primitive::Boolean(false)),
|
||||
Value::Primitive(Primitive::Timestamp(0)),
|
||||
Value::Primitive(Primitive::Int(0)),
|
||||
|
@ -50,20 +50,20 @@ fn medium_change_backend() -> Backend {
|
|||
]),
|
||||
),
|
||||
(
|
||||
"\u{2}".to_owned(),
|
||||
"\u{2}".into(),
|
||||
Value::Sequence(vec![
|
||||
Value::Primitive(Primitive::Null),
|
||||
Value::Primitive(Primitive::Uint(0)),
|
||||
Value::Primitive(Primitive::Str("".to_owned())),
|
||||
Value::Primitive(Primitive::Str("".into())),
|
||||
Value::Primitive(Primitive::Counter(0)),
|
||||
Value::Primitive(Primitive::Str("".to_owned())),
|
||||
Value::Primitive(Primitive::Str("".into())),
|
||||
]),
|
||||
),
|
||||
(
|
||||
"\u{0}".to_owned(),
|
||||
"\u{0}".into(),
|
||||
Value::Sequence(vec![
|
||||
Value::Primitive(Primitive::Counter(0)),
|
||||
Value::Primitive(Primitive::Str("".to_owned())),
|
||||
Value::Primitive(Primitive::Str("".into())),
|
||||
Value::Primitive(Primitive::Uint(0)),
|
||||
Value::Primitive(Primitive::Timestamp(0)),
|
||||
Value::Primitive(Primitive::Int(0)),
|
||||
|
@ -76,7 +76,7 @@ fn medium_change_backend() -> Backend {
|
|||
Value::Primitive(Primitive::Uint(0)),
|
||||
Value::Primitive(Primitive::Null),
|
||||
Value::Primitive(Primitive::Uint(0)),
|
||||
Value::Primitive(Primitive::Str("".to_owned())),
|
||||
Value::Primitive(Primitive::Str("".into())),
|
||||
Value::Primitive(Primitive::Null),
|
||||
Value::Primitive(Primitive::Timestamp(0)),
|
||||
Value::Primitive(Primitive::Timestamp(0)),
|
||||
|
@ -84,11 +84,11 @@ fn medium_change_backend() -> Backend {
|
|||
Value::Primitive(Primitive::Counter(0)),
|
||||
Value::Primitive(Primitive::Uint(0)),
|
||||
Value::Primitive(Primitive::F32(0.0)),
|
||||
Value::Primitive(Primitive::Str("".to_owned())),
|
||||
Value::Primitive(Primitive::Str("".into())),
|
||||
]),
|
||||
),
|
||||
(
|
||||
"".to_owned(),
|
||||
"".into(),
|
||||
Value::Sequence(vec![
|
||||
Value::Primitive(Primitive::Null),
|
||||
Value::Primitive(Primitive::Uint(0)),
|
||||
|
@ -99,7 +99,7 @@ fn medium_change_backend() -> Backend {
|
|||
Value::Primitive(Primitive::Uint(0)),
|
||||
Value::Primitive(Primitive::F64(0.0)),
|
||||
Value::Primitive(Primitive::Timestamp(0)),
|
||||
Value::Primitive(Primitive::Str("".to_owned())),
|
||||
Value::Primitive(Primitive::Str("".into())),
|
||||
Value::Primitive(Primitive::Boolean(false)),
|
||||
Value::Primitive(Primitive::Counter(0)),
|
||||
Value::Primitive(Primitive::Int(0)),
|
||||
|
@ -112,9 +112,9 @@ fn medium_change_backend() -> Backend {
|
|||
]),
|
||||
),
|
||||
(
|
||||
"\u{1}".to_owned(),
|
||||
"\u{1}".into(),
|
||||
Value::Table(
|
||||
vec![("".to_owned(), Value::Primitive(Primitive::F64(0.0)))]
|
||||
vec![("".into(), Value::Primitive(Primitive::F64(0.0)))]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
),
|
||||
|
|
|
@ -13,7 +13,7 @@ use test_env_log::test;
|
|||
fn test_frontend_uses_correct_elem_ids() {
|
||||
let mut hm = HashMap::new();
|
||||
hm.insert(
|
||||
"a".to_owned(),
|
||||
"a".into(),
|
||||
automerge::Value::Sequence(vec![automerge::Value::Primitive(Primitive::Null)]),
|
||||
);
|
||||
let mut backend = automerge::Backend::new();
|
||||
|
@ -44,7 +44,7 @@ fn test_frontend_uses_correct_elem_ids() {
|
|||
|
||||
let mut ehm = HashMap::new();
|
||||
ehm.insert(
|
||||
"a".to_owned(),
|
||||
"a".into(),
|
||||
automerge::Value::Sequence(vec![
|
||||
automerge::Value::Primitive(automerge::Primitive::Int(0)),
|
||||
automerge::Value::Primitive(automerge::Primitive::Boolean(false)),
|
||||
|
@ -75,7 +75,7 @@ fn test_multi_insert_expands_to_correct_indices() {
|
|||
Op {
|
||||
action: OpType::Make(ObjType::List),
|
||||
obj: ObjectId::Root,
|
||||
key: Key::Map("a".to_owned()),
|
||||
key: Key::Map("a".into()),
|
||||
pred: vec![],
|
||||
insert: false,
|
||||
},
|
||||
|
@ -112,7 +112,7 @@ fn test_multi_insert_expands_to_correct_indices() {
|
|||
};
|
||||
|
||||
let val = Value::Map(hashmap! {
|
||||
"a".to_owned() => Value::Sequence(
|
||||
"a".into() => Value::Sequence(
|
||||
vec![
|
||||
Value::Sequence(
|
||||
vec![],
|
||||
|
@ -162,12 +162,12 @@ fn test_frontend_doesnt_wait_for_empty_changes() {
|
|||
let vals = vec![
|
||||
Value::Map(hashmap! {}),
|
||||
Value::Map(hashmap! {
|
||||
"0".to_owned() => Value::Map(
|
||||
"0".into() => Value::Map(
|
||||
hashmap! {},
|
||||
),
|
||||
"a".to_owned() => Value::Map(
|
||||
"a".into() => Value::Map(
|
||||
hashmap!{
|
||||
"b".to_owned() => Value::Map(
|
||||
"b".into() => Value::Map(
|
||||
hashmap!{},
|
||||
),
|
||||
},
|
||||
|
@ -182,7 +182,7 @@ fn test_frontend_doesnt_wait_for_empty_changes() {
|
|||
LocalChange::set(Path::root().key("0"), Value::Map(HashMap::new())),
|
||||
LocalChange::set(
|
||||
Path::root().key("a"),
|
||||
Value::Map(hashmap! {"b".to_owned() => Value::Map(HashMap::new() )}),
|
||||
Value::Map(hashmap! {"b".into() => Value::Map(HashMap::new() )}),
|
||||
),
|
||||
],
|
||||
vec![
|
||||
|
|
|
@ -17,11 +17,11 @@ fn missing_object_error_flaky_null_rle_decoding() {
|
|||
Value::Map(
|
||||
vec![
|
||||
(
|
||||
"\u{0}\u{0}".to_owned(),
|
||||
"\u{0}\u{0}".into(),
|
||||
Value::Sequence(vec![
|
||||
Value::Primitive(Primitive::Str("".to_owned())),
|
||||
Value::Primitive(Primitive::Str("".into())),
|
||||
Value::Primitive(Primitive::Counter(0)),
|
||||
Value::Primitive(Primitive::Str("".to_owned())),
|
||||
Value::Primitive(Primitive::Str("".into())),
|
||||
Value::Primitive(Primitive::Boolean(false)),
|
||||
Value::Primitive(Primitive::Timestamp(0)),
|
||||
Value::Primitive(Primitive::Int(0)),
|
||||
|
@ -39,20 +39,20 @@ fn missing_object_error_flaky_null_rle_decoding() {
|
|||
]),
|
||||
),
|
||||
(
|
||||
"\u{2}".to_owned(),
|
||||
"\u{2}".into(),
|
||||
Value::Sequence(vec![
|
||||
Value::Primitive(Primitive::Null),
|
||||
Value::Primitive(Primitive::Uint(0)),
|
||||
Value::Primitive(Primitive::Str("".to_owned())),
|
||||
Value::Primitive(Primitive::Str("".into())),
|
||||
Value::Primitive(Primitive::Counter(0)),
|
||||
Value::Primitive(Primitive::Str("".to_owned())),
|
||||
Value::Primitive(Primitive::Str("".into())),
|
||||
]),
|
||||
),
|
||||
(
|
||||
"\u{0}".to_owned(),
|
||||
"\u{0}".into(),
|
||||
Value::Sequence(vec![
|
||||
Value::Primitive(Primitive::Counter(0)),
|
||||
Value::Primitive(Primitive::Str("".to_owned())),
|
||||
Value::Primitive(Primitive::Str("".into())),
|
||||
Value::Primitive(Primitive::Uint(0)),
|
||||
Value::Primitive(Primitive::Timestamp(0)),
|
||||
Value::Primitive(Primitive::Int(0)),
|
||||
|
@ -65,7 +65,7 @@ fn missing_object_error_flaky_null_rle_decoding() {
|
|||
Value::Primitive(Primitive::Uint(0)),
|
||||
Value::Primitive(Primitive::Null),
|
||||
Value::Primitive(Primitive::Uint(0)),
|
||||
Value::Primitive(Primitive::Str("".to_owned())),
|
||||
Value::Primitive(Primitive::Str("".into())),
|
||||
Value::Primitive(Primitive::Null),
|
||||
Value::Primitive(Primitive::Timestamp(0)),
|
||||
Value::Primitive(Primitive::Timestamp(0)),
|
||||
|
@ -73,11 +73,11 @@ fn missing_object_error_flaky_null_rle_decoding() {
|
|||
Value::Primitive(Primitive::Counter(0)),
|
||||
Value::Primitive(Primitive::Uint(0)),
|
||||
Value::Primitive(Primitive::F32(0.0)),
|
||||
Value::Primitive(Primitive::Str("".to_owned())),
|
||||
Value::Primitive(Primitive::Str("".into())),
|
||||
]),
|
||||
),
|
||||
(
|
||||
"".to_owned(),
|
||||
"".into(),
|
||||
Value::Sequence(vec![
|
||||
Value::Primitive(Primitive::Null),
|
||||
Value::Primitive(Primitive::Uint(0)),
|
||||
|
@ -88,7 +88,7 @@ fn missing_object_error_flaky_null_rle_decoding() {
|
|||
Value::Primitive(Primitive::Uint(0)),
|
||||
Value::Primitive(Primitive::F64(0.0)),
|
||||
Value::Primitive(Primitive::Timestamp(0)),
|
||||
Value::Primitive(Primitive::Str("".to_owned())),
|
||||
Value::Primitive(Primitive::Str("".into())),
|
||||
Value::Primitive(Primitive::Boolean(false)),
|
||||
Value::Primitive(Primitive::Counter(0)),
|
||||
Value::Primitive(Primitive::Int(0)),
|
||||
|
@ -101,9 +101,9 @@ fn missing_object_error_flaky_null_rle_decoding() {
|
|||
]),
|
||||
),
|
||||
(
|
||||
"\u{1}".to_owned(),
|
||||
"\u{1}".into(),
|
||||
Value::Table(
|
||||
vec![("".to_owned(), Value::Primitive(Primitive::F64(0.0)))]
|
||||
vec![("".into(), Value::Primitive(Primitive::F64(0.0)))]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
),
|
||||
|
@ -199,7 +199,7 @@ fn missing_object_error_null_rle_decoding() {
|
|||
Op {
|
||||
action: OpType::Make(ObjType::List),
|
||||
obj: ObjectId::Root,
|
||||
key: Key::Map("b".to_owned()),
|
||||
key: Key::Map("b".into()),
|
||||
pred: vec![],
|
||||
insert: false,
|
||||
},
|
||||
|
@ -311,7 +311,7 @@ fn missing_object_error_null_rle_decoding() {
|
|||
Op {
|
||||
action: OpType::Make(ObjType::List),
|
||||
obj: ObjectId::Root,
|
||||
key: Key::Map("\u{0}".to_owned()),
|
||||
key: Key::Map("\u{0}".into()),
|
||||
pred: vec![],
|
||||
insert: false,
|
||||
},
|
||||
|
@ -661,14 +661,14 @@ fn missing_object_error_null_rle_decoding() {
|
|||
Op {
|
||||
action: OpType::Make(ObjType::Map),
|
||||
obj: ObjectId::Root,
|
||||
key: Key::Map("\u{1}".to_owned()),
|
||||
key: Key::Map("\u{1}".into()),
|
||||
pred: vec![],
|
||||
insert: false,
|
||||
},
|
||||
Op {
|
||||
action: OpType::Set(ScalarValue::Null),
|
||||
obj: ObjectId::Id(OpId(67, actor_id.clone())),
|
||||
key: Key::Map("a".to_owned()),
|
||||
key: Key::Map("a".into()),
|
||||
pred: vec![],
|
||||
insert: false,
|
||||
},
|
||||
|
@ -678,7 +678,7 @@ fn missing_object_error_null_rle_decoding() {
|
|||
seq: 1,
|
||||
start_op: 1,
|
||||
time: 0,
|
||||
message: Some("".to_owned()),
|
||||
message: Some("".into()),
|
||||
deps: vec![],
|
||||
extra_bytes: vec![],
|
||||
};
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
use smol_str::SmolStr;
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
#[test]
|
||||
fn create_frontend_with_grapheme_clusters() {
|
||||
let mut hm = std::collections::HashMap::new();
|
||||
hm.insert(
|
||||
String::new(),
|
||||
automerge::Value::Text("\u{80}".graphemes(true).map(|s| s.to_owned()).collect()),
|
||||
SmolStr::default(),
|
||||
automerge::Value::Text("\u{80}".graphemes(true).map(|s| s.into()).collect()),
|
||||
);
|
||||
let (mut f, c) =
|
||||
automerge::Frontend::new_with_initial_state(automerge::Value::Map(hm)).unwrap();
|
||||
|
|
|
@ -14,3 +14,4 @@ maplit = "^1.0.2"
|
|||
unicode-segmentation = "1.7.1"
|
||||
serde = "1.0.126"
|
||||
serde_json = "1.0.64"
|
||||
smol_str = "0.1.17"
|
||||
|
|
|
@ -8,6 +8,7 @@ use automerge::{Backend, Frontend, InvalidChangeRequest, LocalChange, Path, Prim
|
|||
use automerge_frontend::Value;
|
||||
use maplit::hashmap;
|
||||
use rand::Rng;
|
||||
use smol_str::SmolStr;
|
||||
|
||||
fn f() {
|
||||
let mut doc = Frontend::new();
|
||||
|
@ -16,15 +17,15 @@ fn f() {
|
|||
let start = Instant::now();
|
||||
|
||||
let m = hashmap! {
|
||||
"a".to_owned() =>
|
||||
"a".into() =>
|
||||
Value::Map(hashmap!{
|
||||
"b".to_owned()=>
|
||||
"b".into()=>
|
||||
Value::Map(
|
||||
hashmap! {
|
||||
"abc".to_owned() => Value::Primitive(Primitive::Str("hello world".to_owned()))
|
||||
"abc".into() => Value::Primitive(Primitive::Str("hello world".into()))
|
||||
},
|
||||
),
|
||||
"d".to_owned() => Value::Primitive(Primitive::Uint(20)),
|
||||
"d".into() => Value::Primitive(Primitive::Uint(20)),
|
||||
},)
|
||||
};
|
||||
|
||||
|
@ -92,7 +93,7 @@ fn g() {
|
|||
|
||||
let iterations = 10_000;
|
||||
for i in 0..iterations {
|
||||
let random_string: String = rand::thread_rng()
|
||||
let random_string: SmolStr = rand::thread_rng()
|
||||
.sample_iter(&rand::distributions::Alphanumeric)
|
||||
.take(1)
|
||||
.map(char::from)
|
||||
|
@ -284,7 +285,7 @@ fn trace(edits: Vec<(u32, u32, Option<String>)>) {
|
|||
if let Some(c) = edit.2.clone() {
|
||||
d.add_change(LocalChange::insert(
|
||||
Path::root().key("text").index(edit.0),
|
||||
Value::Primitive(Primitive::Str(c)),
|
||||
Value::Primitive(Primitive::Str(SmolStr::new(c))),
|
||||
))?;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue