diff --git a/duper_uniffi/dotnet/DuperSerializer.cs b/duper_uniffi/dotnet/DuperSerializer.cs index 45fb0d8..a08f85f 100644 --- a/duper_uniffi/dotnet/DuperSerializer.cs +++ b/duper_uniffi/dotnet/DuperSerializer.cs @@ -72,7 +72,7 @@ public class DuperSerializer var addMethod = concreteType.GetMethod("Add") ?? throw new ApplicationException("No Add method found for Dictionary"); foreach (var item in obj.value) { - addMethod.Invoke(dict, [item.Key, DeserializeInner(item.Value, valueType)]); + addMethod.Invoke(dict, [item.key, DeserializeInner(item.value, valueType)]); } return dict; } @@ -93,13 +93,18 @@ public class DuperSerializer var addMethod = interfaceType.GetMethod("Add") ?? throw new ApplicationException("No Add method found for IDictionary"); foreach (var item in obj.value) { - addMethod.Invoke(dict, [item.Key, DeserializeInner(item.Value, valueType)]); + addMethod.Invoke(dict, [item.key, DeserializeInner(item.value, valueType)]); } return dict; } } // Create class instance object instance = Activator.CreateInstance(t) ?? throw new ApplicationException($"No constructor found for {t}"); + Dictionary classFields = new(obj.value.Length); + foreach (var entry in obj.value) + { + classFields.Add(entry.key, entry.value); + } foreach (var field in t.GetFields()) { string key = field.Name; @@ -115,7 +120,7 @@ public class DuperSerializer break; } } - var item = obj.value[key] ?? throw new ApplicationException($"No key {key} found in Duper object"); + var item = classFields[key] ?? throw new ApplicationException($"No key {key} found in Duper object"); field.SetValue(instance, DeserializeInner(item, field.FieldType)); } foreach (var prop in t.GetProperties()) @@ -133,7 +138,7 @@ public class DuperSerializer break; } } - var item = obj.value[key] ?? throw new ApplicationException($"No key {key} found in Duper object"); + var item = classFields[key] ?? throw new ApplicationException($"No key {key} found in Duper object"); prop.SetValue(instance, DeserializeInner(item, prop.PropertyType)); } return instance; @@ -571,13 +576,13 @@ public class DuperSerializer throw new ApplicationException($"Cannot serialize dictionary with non-string keys to Duper"); } Type valueType = generics[1]; - Dictionary objValue = []; IDictionary valueDict = (value as IDictionary) ?? throw new ApplicationException("IDictionary cast shouldn't fail"); + List objValue = new(valueDict.Count); foreach (var key in valueDict.Keys) { - objValue[(string)key] = SerializeInner(valueDict[key], valueType, null); + objValue.Add(new DuperObjectEntry((string)key, SerializeInner(valueDict[key], valueType, null))); } - return new DuperValue.Object(identifier, objValue); + return new DuperValue.Object(identifier, [.. objValue]); } Type? iformattable = null; @@ -607,13 +612,13 @@ public class DuperSerializer throw new ApplicationException($"Cannot serialize dictionary with non-string keys to Duper"); } Type valueType = generics[1]; - Dictionary objValue = []; - System.Collections.IDictionary valueDict = (value as System.Collections.IDictionary) ?? throw new ApplicationException("IDictionary cast shouldn't fail"); + IDictionary valueDict = (value as IDictionary) ?? throw new ApplicationException("IDictionary cast shouldn't fail"); + List objValue = new(valueDict.Count); foreach (var key in valueDict.Keys) { - objValue[(string)key] = SerializeInner(valueDict[key], valueType, null); + objValue.Add(new DuperObjectEntry((string)key, SerializeInner(valueDict[key], valueType, null))); } - return new DuperValue.Object(identifier, objValue); + return new DuperValue.Object(identifier, [.. objValue]); } else if (interfaceType == typeof(IFormattable)) { @@ -632,7 +637,7 @@ public class DuperSerializer } } - Dictionary classDict = []; + List classDict = []; foreach (var field in t.GetFields()) { @@ -651,7 +656,7 @@ public class DuperSerializer break; } } - classDict[key] = SerializeInner(field.GetValue(value), field.FieldType, fieldIdentifier); + classDict.Add(new DuperObjectEntry(key, SerializeInner(field.GetValue(value), field.FieldType, fieldIdentifier))); } foreach (var prop in t.GetProperties()) @@ -671,10 +676,10 @@ public class DuperSerializer break; } } - classDict[key] = SerializeInner(prop.GetValue(value), prop.PropertyType, propIdentifier); + classDict.Add(new DuperObjectEntry(key, SerializeInner(prop.GetValue(value), prop.PropertyType, propIdentifier))); } - return new DuperValue.Object(identifier, classDict); + return new DuperValue.Object(identifier, [.. classDict]); } private static string FormatViaGeneric(T value) where T : IFormattable diff --git a/duper_uniffi/dotnet/duper.cs b/duper_uniffi/dotnet/duper.cs index 179d94b..11bc6c0 100644 --- a/duper_uniffi/dotnet/duper.cs +++ b/duper_uniffi/dotnet/duper.cs @@ -1345,6 +1345,35 @@ class FfiConverterByteArray : FfiConverterRustBuffer } } +internal record DuperObjectEntry(string @key, DuperValue @value) { } + +class FfiConverterTypeDuperObjectEntry : FfiConverterRustBuffer +{ + public static FfiConverterTypeDuperObjectEntry INSTANCE = + new FfiConverterTypeDuperObjectEntry(); + + public override DuperObjectEntry Read(BigEndianStream stream) + { + return new DuperObjectEntry( + @key: FfiConverterString.INSTANCE.Read(stream), + @value: FfiConverterTypeDuperValue.INSTANCE.Read(stream) + ); + } + + public override int AllocationSize(DuperObjectEntry value) + { + return 0 + + FfiConverterString.INSTANCE.AllocationSize(value.@key) + + FfiConverterTypeDuperValue.INSTANCE.AllocationSize(value.@value); + } + + public override void Write(DuperObjectEntry value, BigEndianStream stream) + { + FfiConverterString.INSTANCE.Write(value.@key, stream); + FfiConverterTypeDuperValue.INSTANCE.Write(value.@value, stream); + } +} + internal record SerializeOptions(string? @indent, bool @stripIdentifiers, bool @minify) { } class FfiConverterTypeSerializeOptions : FfiConverterRustBuffer @@ -1488,8 +1517,7 @@ class FfiConverterTypeDuperError internal record DuperValue { - public record Object(string? @identifier, Dictionary @value) - : DuperValue { } + public record Object(string? @identifier, DuperObjectEntry[] @value) : DuperValue { } public record Array(string? @identifier, DuperValue[] @value) : DuperValue { } @@ -1522,7 +1550,7 @@ class FfiConverterTypeDuperValue : FfiConverterRustBuffer case 1: return new DuperValue.Object( FfiConverterOptionalString.INSTANCE.Read(stream), - FfiConverterDictionaryStringTypeDuperValue.INSTANCE.Read(stream) + FfiConverterSequenceTypeDuperObjectEntry.INSTANCE.Read(stream) ); case 2: return new DuperValue.Array( @@ -1583,7 +1611,7 @@ class FfiConverterTypeDuperValue : FfiConverterRustBuffer case DuperValue.Object variant_value: return 4 + FfiConverterOptionalString.INSTANCE.AllocationSize(variant_value.@identifier) - + FfiConverterDictionaryStringTypeDuperValue.INSTANCE.AllocationSize( + + FfiConverterSequenceTypeDuperObjectEntry.INSTANCE.AllocationSize( variant_value.@value ); case DuperValue.Array variant_value: @@ -1642,7 +1670,7 @@ class FfiConverterTypeDuperValue : FfiConverterRustBuffer case DuperValue.Object variant_value: stream.WriteInt(1); FfiConverterOptionalString.INSTANCE.Write(variant_value.@identifier, stream); - FfiConverterDictionaryStringTypeDuperValue.INSTANCE.Write( + FfiConverterSequenceTypeDuperObjectEntry.INSTANCE.Write( variant_value.@value, stream ); @@ -1782,12 +1810,12 @@ class FfiConverterOptionalTypeSerializeOptions : FfiConverterRustBuffer +class FfiConverterSequenceTypeDuperObjectEntry : FfiConverterRustBuffer { - public static FfiConverterSequenceTypeDuperValue INSTANCE = - new FfiConverterSequenceTypeDuperValue(); + public static FfiConverterSequenceTypeDuperObjectEntry INSTANCE = + new FfiConverterSequenceTypeDuperObjectEntry(); - public override DuperValue[] Read(BigEndianStream stream) + public override DuperObjectEntry[] Read(BigEndianStream stream) { var length = stream.ReadInt(); if (length == 0) @@ -1795,8 +1823,8 @@ class FfiConverterSequenceTypeDuperValue : FfiConverterRustBuffer return []; } - var result = new DuperValue[(length)]; - var readFn = FfiConverterTypeDuperValue.INSTANCE.Read; + var result = new DuperObjectEntry[(length)]; + var readFn = FfiConverterTypeDuperObjectEntry.INSTANCE.Read; for (int i = 0; i < length; i++) { result[i] = readFn(stream); @@ -1804,7 +1832,7 @@ class FfiConverterSequenceTypeDuperValue : FfiConverterRustBuffer return result; } - public override int AllocationSize(DuperValue[] value) + public override int AllocationSize(DuperObjectEntry[] value) { var sizeForLength = 4; @@ -1814,12 +1842,12 @@ class FfiConverterSequenceTypeDuperValue : FfiConverterRustBuffer return sizeForLength; } - var allocationSizeFn = FfiConverterTypeDuperValue.INSTANCE.AllocationSize; + var allocationSizeFn = FfiConverterTypeDuperObjectEntry.INSTANCE.AllocationSize; var sizeForItems = value.Sum(item => allocationSizeFn(item)); return sizeForLength + sizeForItems; } - public override void Write(DuperValue[] value, BigEndianStream stream) + public override void Write(DuperObjectEntry[] value, BigEndianStream stream) { // details/1-empty-list-as-default-method-parameter.md if (value == null) @@ -1829,34 +1857,34 @@ class FfiConverterSequenceTypeDuperValue : FfiConverterRustBuffer } stream.WriteInt(value.Length); - var writerFn = FfiConverterTypeDuperValue.INSTANCE.Write; + var writerFn = FfiConverterTypeDuperObjectEntry.INSTANCE.Write; value.ForEach(item => writerFn(item, stream)); } } -class FfiConverterDictionaryStringTypeDuperValue - : FfiConverterRustBuffer> +class FfiConverterSequenceTypeDuperValue : FfiConverterRustBuffer { - public static FfiConverterDictionaryStringTypeDuperValue INSTANCE = - new FfiConverterDictionaryStringTypeDuperValue(); + public static FfiConverterSequenceTypeDuperValue INSTANCE = + new FfiConverterSequenceTypeDuperValue(); - public override Dictionary Read(BigEndianStream stream) + public override DuperValue[] Read(BigEndianStream stream) { - var len = stream.ReadInt(); - var result = new Dictionary(len); - var readerKey = FfiConverterString.INSTANCE.Read; - var readerValue = FfiConverterTypeDuperValue.INSTANCE.Read; - for (int i = 0; i < len; i++) + var length = stream.ReadInt(); + if (length == 0) { - var key = readerKey(stream); - var value = readerValue(stream); - result[key] = value; + return []; } + var result = new DuperValue[(length)]; + var readFn = FfiConverterTypeDuperValue.INSTANCE.Read; + for (int i = 0; i < length; i++) + { + result[i] = readFn(stream); + } return result; } - public override int AllocationSize(Dictionary value) + public override int AllocationSize(DuperValue[] value) { var sizeForLength = 4; @@ -1866,15 +1894,12 @@ class FfiConverterDictionaryStringTypeDuperValue return sizeForLength; } - var allocationKeySizeFn = FfiConverterString.INSTANCE.AllocationSize; - var allocationKValueSizeFn = FfiConverterTypeDuperValue.INSTANCE.AllocationSize; - var sizeForItems = value.Sum(item => - allocationKeySizeFn(item.Key) + allocationKValueSizeFn(item.Value) - ); + var allocationSizeFn = FfiConverterTypeDuperValue.INSTANCE.AllocationSize; + var sizeForItems = value.Sum(item => allocationSizeFn(item)); return sizeForLength + sizeForItems; } - public override void Write(Dictionary value, BigEndianStream stream) + public override void Write(DuperValue[] value, BigEndianStream stream) { // details/1-empty-list-as-default-method-parameter.md if (value == null) @@ -1883,14 +1908,9 @@ class FfiConverterDictionaryStringTypeDuperValue return; } - stream.WriteInt(value.Count); - var writerKey = FfiConverterString.INSTANCE.Write; - var writerValue = FfiConverterTypeDuperValue.INSTANCE.Write; - foreach (var item in value) - { - writerKey(item.Key, stream); - writerValue(item.Value, stream); - } + stream.WriteInt(value.Length); + var writerFn = FfiConverterTypeDuperValue.INSTANCE.Write; + value.ForEach(item => writerFn(item, stream)); } } #pragma warning restore 8625 diff --git a/duper_uniffi/src/duper.udl b/duper_uniffi/src/duper.udl index acb4144..18e5790 100644 --- a/duper_uniffi/src/duper.udl +++ b/duper_uniffi/src/duper.udl @@ -1,6 +1,6 @@ [Enum] interface DuperValue { - Object(string? identifier, record value); + Object(string? identifier, sequence value); Array(string? identifier, sequence value); Tuple(string? identifier, sequence value); String(string? identifier, string value); @@ -12,6 +12,11 @@ interface DuperValue { Null(string? identifier); }; +dictionary DuperObjectEntry { + string key; + DuperValue value; +}; + [Error] enum DuperError { "Parse", diff --git a/duper_uniffi/src/lib.rs b/duper_uniffi/src/lib.rs index 821a5f2..1cee2e2 100644 --- a/duper_uniffi/src/lib.rs +++ b/duper_uniffi/src/lib.rs @@ -1,5 +1,3 @@ -use std::collections::HashMap; - use duper::{ DuperIdentifierTryFromError, DuperObjectTryFromError, DuperParser, DuperTemporalTryFromError, PrettyPrinter, Serializer, @@ -10,10 +8,15 @@ use crate::parse::UniffiVisitor; mod parse; mod serialize; +pub struct DuperObjectEntry { + pub key: String, + pub value: DuperValue, +} + pub enum DuperValue { Object { identifier: Option, - value: HashMap, + value: Vec, }, Array { identifier: Option, @@ -89,7 +92,6 @@ pub fn serialize( value: DuperValue, options: Option, ) -> Result { - let value = value.serialize()?; let SerializeOptions { indent, strip_identifiers, @@ -107,10 +109,10 @@ pub fn serialize( } else { Ok(PrettyPrinter::new(strip_identifiers, indent.as_ref()) .map_err(DuperError::SerializeOptions)? - .pretty_print(value)) + .pretty_print(value.serialize()?)) } } else { - Ok(Serializer::new(strip_identifiers, minify).serialize(value)) + Ok(Serializer::new(strip_identifiers, minify).serialize(value.serialize()?)) } } diff --git a/duper_uniffi/src/parse.rs b/duper_uniffi/src/parse.rs index 65b16de..c91626f 100644 --- a/duper_uniffi/src/parse.rs +++ b/duper_uniffi/src/parse.rs @@ -1,11 +1,9 @@ -use std::collections::HashMap; - use duper::{ DuperArray, DuperBytes, DuperIdentifier, DuperObject, DuperString, DuperTemporal, DuperTuple, visitor::DuperVisitor, }; -use crate::DuperValue; +use crate::{DuperObjectEntry, DuperValue}; pub(crate) struct UniffiVisitor; @@ -17,9 +15,12 @@ impl DuperVisitor for UniffiVisitor { identifier: Option<&DuperIdentifier<'a>>, object: &DuperObject<'a>, ) -> Self::Value { - let mut value = HashMap::with_capacity(object.len()); + let mut value = Vec::with_capacity(object.len()); for (key, val) in object.iter() { - value.insert(key.as_ref().to_string(), val.accept(self).into()); + value.push(DuperObjectEntry { + key: key.as_ref().to_string(), + value: val.accept(self).into(), + }); } DuperValue::Object { identifier: identifier.map(|identifier| identifier.as_ref().to_string()), diff --git a/duper_uniffi/src/serialize.rs b/duper_uniffi/src/serialize.rs index ed59082..222c854 100644 --- a/duper_uniffi/src/serialize.rs +++ b/duper_uniffi/src/serialize.rs @@ -5,7 +5,7 @@ use duper::{ DuperTemporal, DuperTuple, DuperValue, }; -use crate::{DuperError, DuperValue as Value}; +use crate::{DuperError, DuperObjectEntry, DuperValue as Value}; impl Value { pub(crate) fn serialize(self) -> Result, DuperError> { @@ -17,7 +17,9 @@ impl Value { inner: DuperInner::Object(DuperObject::try_from( value .into_iter() - .map(|(key, val)| val.serialize().map(|val| (DuperKey::from(key), val))) + .map(|DuperObjectEntry { key, value }| { + value.serialize().map(|val| (DuperKey::from(key), val)) + }) .collect::, _>>()?, )?), }),