You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fory.apache.org by ch...@apache.org on 2025/09/24 05:00:39 UTC
(fory) branch main updated: feat(rust): support Map and register_by_name (#2649)
This is an automated email from the ASF dual-hosted git repository.
chaokunyang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fory.git
The following commit(s) were added to refs/heads/main by this push:
new bd7b369d6 feat(rust): support Map and register_by_name (#2649)
bd7b369d6 is described below
commit bd7b369d60f5f321406cc773f580fe9efa343b81
Author: urlyy <li...@gmail.com>
AuthorDate: Wed Sep 24 13:00:36 2025 +0800
feat(rust): support Map and register_by_name (#2649)
## What does this PR do?
1. Refactored the code to reduce function reuse, making it easier to
maintain and extend later.
2. Added support for handling `Map`.
3. Added support for `register_by_name`.
4. Add new alignment tests with Java.
5. Support handle string when not string_compressed.
6. Support int <-> Integer conversion.
7. Add skip meta bytes after deserialize.
## Does this PR introduce any user-facing change?
- [ ] Does this PR introduce any public API change?
- [ ] Does this PR introduce any binary protocol compatibility change?
---------
Co-authored-by: Shawn Yang <ch...@apache.org>
Co-authored-by: Shawn Yang <sh...@gmail.com>
---
.../test/java/org/apache/fory/RustXlangTest.java | 254 ++++++++++++++++---
rust/fory-core/src/buffer.rs | 4 +
rust/fory-core/src/fory.rs | 57 +++--
rust/fory-core/src/meta/meta_string.rs | 174 ++++++++++---
rust/fory-core/src/meta/mod.rs | 7 +-
rust/fory-core/src/meta/type_meta.rs | 104 +++++---
rust/fory-core/src/resolver/context.rs | 4 +-
rust/fory-core/src/resolver/meta_resolver.rs | 3 +-
rust/fory-core/src/resolver/type_resolver.rs | 115 +++++----
rust/fory-core/src/serializer/bool.rs | 14 +-
rust/fory-core/src/serializer/collection.rs | 111 ++++----
rust/fory-core/src/serializer/datetime.rs | 42 ++--
rust/fory-core/src/serializer/list.rs | 18 +-
rust/fory-core/src/serializer/map.rs | 214 ++++++++++++----
rust/fory-core/src/serializer/mod.rs | 70 ++++--
rust/fory-core/src/serializer/number.rs | 14 +-
rust/fory-core/src/serializer/option.rs | 21 +-
rust/fory-core/src/serializer/primitive_list.rs | 25 +-
rust/fory-core/src/serializer/set.rs | 18 +-
rust/fory-core/src/serializer/skip.rs | 115 ++++++---
rust/fory-core/src/serializer/string.rs | 37 ++-
rust/fory-core/src/types.rs | 12 +-
rust/fory-derive/src/object/derive_enum.rs | 74 ++++--
rust/fory-derive/src/object/misc.rs | 23 +-
rust/fory-derive/src/object/read.rs | 52 ++--
rust/fory-derive/src/object/serializer.rs | 4 +-
rust/fory-derive/src/object/util.rs | 214 +++++++++++++---
rust/fory-derive/src/object/write.rs | 29 ++-
rust/tests/tests/test_compatible.rs | 195 ++++++++++++--
rust/tests/tests/test_cross_language.rs | 279 ++++++++++++++++++---
rust/tests/tests/test_meta_string.rs | 85 ++++---
31 files changed, 1810 insertions(+), 578 deletions(-)
diff --git a/java/fory-core/src/test/java/org/apache/fory/RustXlangTest.java b/java/fory-core/src/test/java/org/apache/fory/RustXlangTest.java
index 6e02e4c82..78397794a 100644
--- a/java/fory-core/src/test/java/org/apache/fory/RustXlangTest.java
+++ b/java/fory-core/src/test/java/org/apache/fory/RustXlangTest.java
@@ -81,7 +81,6 @@ public class RustXlangTest extends ForyTestBase {
@BeforeClass
public void isRustJavaCIEnabled() {
String enabled = System.getenv("FORY_RUST_JAVA_CI");
- enabled = "0"; // skip rust ci
if (enabled == null || !enabled.equals("1")) {
throw new SkipException("Skipping RustXlangTest: FORY_RUST_JAVA_CI not set to 1");
}
@@ -115,6 +114,14 @@ public class RustXlangTest extends ForyTestBase {
testCrossLanguageSerializer(Language.RUST, command);
command.set(RUST_TESTCASE_INDEX, "test_simple_struct");
testSimpleStruct(Language.RUST, command);
+ command.set(RUST_TESTCASE_INDEX, "test_simple_named_struct");
+ testSimpleNamedStruct(Language.RUST, command);
+ command.set(RUST_TESTCASE_INDEX, "test_list");
+ testList(Language.RUST, command);
+ command.set(RUST_TESTCASE_INDEX, "test_map");
+ testMap(Language.RUST, command);
+ command.set(RUST_TESTCASE_INDEX, "test_integer");
+ testInteger(Language.RUST, command);
}
@Test
@@ -291,15 +298,9 @@ public class RustXlangTest extends ForyTestBase {
Assert.assertTrue(executeCommand(command, 30, env_workdir.getLeft(), env_workdir.getRight()));
}
- private void testStringSerializer(Language language, List<String> command) throws Exception {
- Path dataFile = Files.createTempFile("test_string_serializer", "data");
+ private void _testStringSerializer(
+ Fory fory, Path dataFile, Language language, List<String> command) throws IOException {
MemoryBuffer buffer = MemoryUtils.buffer(100);
- Fory fory =
- Fory.builder()
- .withStringCompressed(true)
- .withWriteNumUtf16BytesForUtf8Encoding(false)
- .requireClassRegistration(false)
- .build();
StringSerializer serializer = new StringSerializer(fory);
String[] testStrings =
new String[] {
@@ -320,15 +321,31 @@ public class RustXlangTest extends ForyTestBase {
Pair<Map<String, String>, File> env_workdir =
setFilePath(language, command, dataFile, buffer.getBytes(0, buffer.writerIndex()));
Assert.assertTrue(executeCommand(command, 30, env_workdir.getLeft(), env_workdir.getRight()));
-
buffer = MemoryUtils.wrap(Files.readAllBytes(dataFile));
-
for (String expected : testStrings) {
String actual = serializer.readJavaString(buffer);
Assert.assertEquals(actual, expected);
}
}
+ private void testStringSerializer(Language language, List<String> command) throws Exception {
+ Path dataFile = Files.createTempFile("test_string_serializer", "data");
+ Fory fory =
+ Fory.builder()
+ .withLanguage(Language.XLANG)
+ .withCompatibleMode(CompatibleMode.COMPATIBLE)
+ .build();
+ _testStringSerializer(fory, dataFile, language, command);
+ Fory foryCompress =
+ Fory.builder()
+ .withLanguage(Language.XLANG)
+ .withCompatibleMode(CompatibleMode.COMPATIBLE)
+ .withStringCompressed(true)
+ .withWriteNumUtf16BytesForUtf8Encoding(false)
+ .build();
+ _testStringSerializer(foryCompress, dataFile, language, command);
+ }
+
enum Color {
Green,
Red,
@@ -348,10 +365,7 @@ public class RustXlangTest extends ForyTestBase {
Fory fory =
Fory.builder()
.withLanguage(Language.XLANG)
- .requireClassRegistration(false)
- .withStringCompressed(true)
.withCompatibleMode(CompatibleMode.COMPATIBLE)
- .withWriteNumUtf16BytesForUtf8Encoding(false)
.build();
fory.register(Color.class, 101);
MemoryBuffer buffer = MemoryUtils.buffer(32);
@@ -373,7 +387,6 @@ public class RustXlangTest extends ForyTestBase {
fory.serialize(buffer, day);
Instant instant = Instant.ofEpochSecond(100);
fory.serialize(buffer, instant);
- // test primitive arrays
fory.serialize(buffer, new boolean[] {true, false});
fory.serialize(buffer, new short[] {1, Short.MAX_VALUE});
fory.serialize(buffer, new int[] {1, Integer.MAX_VALUE});
@@ -390,8 +403,7 @@ public class RustXlangTest extends ForyTestBase {
// map.put(list.get(i), list.get(i));
// }
// fory.serialize(buffer, map);
- // System.out.println("bytes: " + Arrays.toString(buffer.getBytes(0,
- // buffer.writerIndex())));
+
// Set<Object> set = new HashSet<>(list);
// fory.serialize(buffer, set);
@@ -443,11 +455,15 @@ public class RustXlangTest extends ForyTestBase {
@Data
static class SimpleStruct {
- // int f2;
+ HashMap<Integer, Double> f1;
+ int f2;
Item f3;
String f4;
- Color f5;
- // int last;
+ // Color f5;
+ List<String> f6;
+ int f7;
+ // Integer f8;
+ // Integer last;
}
private void testSimpleStruct(Language language, List<String> command)
@@ -455,31 +471,207 @@ public class RustXlangTest extends ForyTestBase {
Fory fory =
Fory.builder()
.withLanguage(Language.XLANG)
- .withRefTracking(false)
.withCompatibleMode(CompatibleMode.COMPATIBLE)
.withCodegen(false)
- .withStringCompressed(true)
- .withWriteNumUtf16BytesForUtf8Encoding(false)
.build();
fory.register(Color.class, 101);
fory.register(Item.class, 102);
fory.register(SimpleStruct.class, 103);
+
Item item = new Item();
item.name = "item";
+ HashMap<Integer, Double> f1 = new HashMap<>();
+ f1.put(1, 1.0);
+ f1.put(2, 2.0);
SimpleStruct obj = new SimpleStruct();
- // obj.f2 = 10;
+ obj.f1 = f1;
+ obj.f2 = 39;
obj.f3 = item;
- obj.f4 = "f3";
- obj.f5 = Color.White;
+ obj.f4 = "f4";
+ // obj.f5 = Color.White;
+ obj.f6 = Collections.singletonList("f6");
+ obj.f7 = 40;
+ // obj.f8 = 41;
// obj.last = 42;
- byte[] serialized = fory.serialize(obj);
- Assert.assertEquals(fory.deserialize(serialized), obj);
- System.out.println(Arrays.toString(serialized));
+
+ MemoryBuffer buffer = MemoryUtils.buffer(32);
+ fory.serialize(buffer, obj);
+
Path dataFile = Files.createTempFile("test_simple_struct", "data");
Pair<Map<String, String>, File> env_workdir =
- setFilePath(language, command, dataFile, serialized);
+ setFilePath(language, command, dataFile, buffer.getBytes(0, buffer.writerIndex()));
+ Assert.assertTrue(executeCommand(command, 30, env_workdir.getLeft(), env_workdir.getRight()));
+ MemoryBuffer buffer2 = MemoryUtils.wrap(Files.readAllBytes(dataFile));
+ Assert.assertEquals(fory.deserialize(buffer2), obj);
+ }
+
+ private void testSimpleNamedStruct(Language language, List<String> command)
+ throws java.io.IOException {
+ Fory fory =
+ Fory.builder()
+ .withLanguage(Language.XLANG)
+ .withCompatibleMode(CompatibleMode.COMPATIBLE)
+ .withCodegen(false)
+ .build();
+ fory.register(Color.class, "demo", "color");
+ fory.register(Item.class, "demo", "item");
+ fory.register(SimpleStruct.class, "demo", "simple_struct");
+
+ Item item = new Item();
+ item.name = "item";
+ HashMap<Integer, Double> f1 = new HashMap<>();
+ f1.put(1, 1.0);
+ f1.put(2, 2.0);
+ SimpleStruct obj = new SimpleStruct();
+ obj.f1 = f1;
+ obj.f2 = 39;
+ obj.f3 = item;
+ obj.f4 = "f4";
+ // obj.f5 = Color.White;
+ obj.f6 = Collections.singletonList("f6");
+ obj.f7 = 40;
+ // obj.f8 = 41;
+ // obj.last = 42;
+
+ MemoryBuffer buffer = MemoryUtils.buffer(32);
+ fory.serialize(buffer, obj);
+
+ Path dataFile = Files.createTempFile("test_named_simple_struct", "data");
+ Pair<Map<String, String>, File> env_workdir =
+ setFilePath(language, command, dataFile, buffer.getBytes(0, buffer.writerIndex()));
+ Assert.assertTrue(executeCommand(command, 30, env_workdir.getLeft(), env_workdir.getRight()));
+ MemoryBuffer buffer2 = MemoryUtils.wrap(Files.readAllBytes(dataFile));
+ Assert.assertEquals(fory.deserialize(buffer2), obj);
+ }
+
+ private void testList(Language language, List<String> command) throws java.io.IOException {
+ Fory fory =
+ Fory.builder()
+ .withLanguage(Language.XLANG)
+ .withCompatibleMode(CompatibleMode.COMPATIBLE)
+ .withCodegen(false)
+ .build();
+ fory.register(Item.class, 102);
+ MemoryBuffer buffer = MemoryUtils.buffer(32);
+ List<String> strList = Arrays.asList("a", "b");
+ List<String> strList2 = Arrays.asList(null, "b");
+ Item item = new Item();
+ item.name = "a";
+ Item item2 = new Item();
+ item2.name = "b";
+ Item item3 = new Item();
+ item3.name = "c";
+ List<Item> itemList = Arrays.asList(item, item2);
+ List<Item> itemList2 = Arrays.asList(null, item3);
+ fory.serialize(buffer, strList);
+ fory.serialize(buffer, strList2);
+ fory.serialize(buffer, itemList);
+ fory.serialize(buffer, itemList2);
+
+ Path dataFile = Files.createTempFile("test_list", "data");
+ Pair<Map<String, String>, File> env_workdir =
+ setFilePath(language, command, dataFile, buffer.getBytes(0, buffer.writerIndex()));
+ Assert.assertTrue(executeCommand(command, 30, env_workdir.getLeft(), env_workdir.getRight()));
+ MemoryBuffer buffer2 = MemoryUtils.wrap(Files.readAllBytes(dataFile));
+ Assert.assertEquals(fory.deserialize(buffer2), strList);
+ Assert.assertEquals(fory.deserialize(buffer2), strList2);
+ Assert.assertEquals(fory.deserialize(buffer2), itemList);
+ Assert.assertEquals(fory.deserialize(buffer2), itemList2);
+ }
+
+ private void testMap(Language language, List<String> command) throws java.io.IOException {
+ Fory fory =
+ Fory.builder()
+ .withLanguage(Language.XLANG)
+ .withCompatibleMode(CompatibleMode.COMPATIBLE)
+ .withCodegen(false)
+ .build();
+ fory.register(Item.class, 102);
+ MemoryBuffer buffer = MemoryUtils.buffer(32);
+ Map<String, String> strMap = new HashMap<>();
+ strMap.put("k1", "v1");
+ strMap.put(null, "v2");
+ strMap.put("k3", null);
+ strMap.put("k4", "v4");
+ Item item = new Item();
+ item.name = "item1";
+ Item item2 = new Item();
+ item2.name = "item2";
+ Item item3 = new Item();
+ item3.name = "item3";
+ Map<String, Item> itemMap = new HashMap<>();
+ itemMap.put("k1", item);
+ itemMap.put(null, item2);
+ itemMap.put("k3", null);
+ itemMap.put("k4", item3);
+ fory.serialize(buffer, strMap);
+ fory.serialize(buffer, itemMap);
+ Path dataFile = Files.createTempFile("test_map", "data");
+ Pair<Map<String, String>, File> env_workdir =
+ setFilePath(language, command, dataFile, buffer.getBytes(0, buffer.writerIndex()));
+ Assert.assertTrue(executeCommand(command, 30, env_workdir.getLeft(), env_workdir.getRight()));
+ MemoryBuffer buffer2 = MemoryUtils.wrap(Files.readAllBytes(dataFile));
+ Assert.assertEquals(fory.deserialize(buffer2), strMap);
+ Assert.assertEquals(fory.deserialize(buffer2), itemMap);
+ }
+
+ static class Item1 {
+ int f1;
+ int f2;
+ Integer f3;
+ Integer f4;
+ Integer f5;
+ Integer f6;
+ }
+
+ private void testInteger(Language language, List<String> command) throws java.io.IOException {
+ Fory fory =
+ Fory.builder()
+ .withLanguage(Language.XLANG)
+ .withCompatibleMode(CompatibleMode.COMPATIBLE)
+ .build();
+ fory.register(Item1.class, 101);
+ Item1 item1 = new Item1();
+ int f1 = 1;
+ int f2 = 2;
+ Integer f3 = 3;
+ Integer f4 = 4;
+ Integer f5 = null;
+ Integer f6 = null;
+ item1.f1 = f1;
+ item1.f2 = f2;
+ item1.f3 = f3;
+ item1.f4 = f4;
+ item1.f5 = f5;
+ item1.f6 = f6;
+ MemoryBuffer buffer = MemoryBuffer.newHeapBuffer(32);
+ fory.serialize(buffer, item1);
+ fory.serialize(buffer, f1);
+ fory.serialize(buffer, f2);
+ fory.serialize(buffer, f3);
+ fory.serialize(buffer, f4);
+ fory.serialize(buffer, f5);
+ fory.serialize(buffer, f6);
+
+ Path dataFile = Files.createTempFile("test_integer", "data");
+ Pair<Map<String, String>, File> env_workdir =
+ setFilePath(language, command, dataFile, buffer.getBytes(0, buffer.writerIndex()));
Assert.assertTrue(executeCommand(command, 30, env_workdir.getLeft(), env_workdir.getRight()));
- Assert.assertEquals(fory.deserialize(Files.readAllBytes(dataFile)), obj);
+
+ MemoryBuffer buffer2 = MemoryUtils.wrap(Files.readAllBytes(dataFile));
+ // Item1 newItem1 = (Item1) fory.deserialize(buffer2);
+ // Assert.assertEquals(newItem1.f1, 1);
+ // Assert.assertEquals(newItem1.f2, 2);
+ // Assert.assertEquals(newItem1.f3, 3);
+ // Assert.assertEquals(newItem1.f4, 4);
+ // Assert.assertEquals(newItem1.f5, 0);
+ // Assert.assertNull(newItem1.f6);
+ Assert.assertEquals(fory.deserialize(buffer2), 1);
+ Assert.assertEquals(fory.deserialize(buffer2), 2);
+ Assert.assertEquals(fory.deserialize(buffer2), 3);
+ Assert.assertEquals(fory.deserialize(buffer2), 4);
+ Assert.assertEquals(fory.deserialize(buffer2), 0);
+ Assert.assertNull(fory.deserialize(buffer2));
}
/**
diff --git a/rust/fory-core/src/buffer.rs b/rust/fory-core/src/buffer.rs
index 78df85a87..87ddeb163 100644
--- a/rust/fory-core/src/buffer.rs
+++ b/rust/fory-core/src/buffer.rs
@@ -311,6 +311,10 @@ impl<'bf> Reader<'bf> {
&self.bf[self.cursor..self.bf.len()]
}
+ pub fn get_cursor(&self) -> usize {
+ self.cursor
+ }
+
pub fn u8(&mut self) -> u8 {
let result = self.bf[self.cursor];
self.move_next(1);
diff --git a/rust/fory-core/src/fory.rs b/rust/fory-core/src/fory.rs
index baa0808a3..4a7412176 100644
--- a/rust/fory-core/src/fory.rs
+++ b/rust/fory-core/src/fory.rs
@@ -18,7 +18,6 @@
use crate::buffer::{Reader, Writer};
use crate::ensure;
use crate::error::Error;
-use crate::meta::MetaStringEncoder;
use crate::resolver::context::ReadContext;
use crate::resolver::context::WriteContext;
use crate::resolver::type_resolver::{TypeInfo, TypeResolver};
@@ -34,6 +33,7 @@ pub struct Fory {
mode: Mode,
xlang: bool,
type_resolver: TypeResolver,
+ compress_string: bool,
}
impl Default for Fory {
@@ -42,6 +42,7 @@ impl Default for Fory {
mode: Mode::SchemaConsistent,
xlang: true,
type_resolver: TypeResolver::default(),
+ compress_string: false,
}
}
}
@@ -57,10 +58,19 @@ impl Fory {
self
}
+ pub fn compress_string(mut self, compress_string: bool) -> Self {
+ self.compress_string = compress_string;
+ self
+ }
+
pub fn get_mode(&self) -> &Mode {
&self.mode
}
+ pub fn is_compress_string(&self) -> bool {
+ self.compress_string
+ }
+
pub fn write_head<T: Serializer>(&self, is_none: bool, writer: &mut Writer) {
const HEAD_SIZE: usize = 10;
writer.reserve(<T as Serializer>::reserved_space() + SIZE_OF_REF_AND_TYPE + HEAD_SIZE);
@@ -136,13 +146,18 @@ impl Fory {
if is_none {
return Ok(T::default());
}
+ let mut bytes_to_skip = 0;
if self.mode == Mode::Compatible {
let meta_offset = context.reader.i32();
if meta_offset != -1 {
- context.load_meta(meta_offset as usize);
+ bytes_to_skip = context.load_meta(meta_offset as usize);
}
}
- <T as Serializer>::deserialize(context, false)
+ let result = <T as Serializer>::deserialize(context, false);
+ if bytes_to_skip > 0 {
+ context.reader.skip(bytes_to_skip as u32);
+ }
+ result
}
pub fn serialize<T: Serializer>(&self, record: &T) -> Vec<u8> {
@@ -176,34 +191,24 @@ impl Fory {
}
pub fn register<T: 'static + StructSerializer>(&mut self, id: u32) {
- let actual_type_id = T::actual_type_id(id);
- let empty_bytes = "".as_bytes().to_vec();
- let type_info = TypeInfo::new::<T>(
- self,
- actual_type_id,
- empty_bytes.clone(),
- empty_bytes,
- false,
- );
- self.type_resolver.register::<T>(type_info);
+ let actual_type_id = T::actual_type_id(id, false, &self.mode);
+ let empty_string = String::new();
+ let type_info =
+ TypeInfo::new::<T>(self, actual_type_id, &empty_string, &empty_string, false);
+ self.type_resolver.register::<T>(&type_info);
}
- pub fn register_by_name<T: 'static + StructSerializer>(
+ pub fn register_by_namespace<T: 'static + StructSerializer>(
&mut self,
namespace: &str,
type_name: &str,
) {
- let type_id = T::actual_type_id(0);
- let encoder = MetaStringEncoder::new();
- let namespace_metastring = encoder.encode(namespace).unwrap();
- let type_name_metastring = encoder.encode(type_name).unwrap();
- let type_info = TypeInfo::new::<T>(
- self,
- type_id,
- namespace_metastring.bytes,
- type_name_metastring.bytes,
- true,
- );
- self.type_resolver.register::<T>(type_info);
+ let actual_type_id = T::actual_type_id(0, true, &self.mode);
+ let type_info = TypeInfo::new::<T>(self, actual_type_id, namespace, type_name, true);
+ self.type_resolver.register::<T>(&type_info);
+ }
+
+ pub fn register_by_name<T: 'static + StructSerializer>(&mut self, type_name: &str) {
+ self.register_by_namespace::<T>("", type_name);
}
}
diff --git a/rust/fory-core/src/meta/meta_string.rs b/rust/fory-core/src/meta/meta_string.rs
index 05205ea81..4e8916f36 100644
--- a/rust/fory-core/src/meta/meta_string.rs
+++ b/rust/fory-core/src/meta/meta_string.rs
@@ -25,9 +25,18 @@ use crate::meta::string_util;
const SHORT_MAX_VALUE: usize = 32767;
// const HEADER_MASK:i64 = 0xff;
-#[derive(Debug, PartialEq, Hash, Eq, Clone)]
+pub static NAMESPACE_ENCODER: MetaStringEncoder = MetaStringEncoder::new('.', '_');
+pub static TYPE_NAME_ENCODER: MetaStringEncoder = MetaStringEncoder::new('$', '_');
+pub static FIELD_NAME_ENCODER: MetaStringEncoder = MetaStringEncoder::new('$', '_');
+
+pub static NAMESPACE_DECODER: MetaStringDecoder = MetaStringDecoder::new('.', '_');
+pub static FIELD_NAME_DECODER: MetaStringDecoder = MetaStringDecoder::new('$', '_');
+pub static TYPE_NAME_DECODER: MetaStringDecoder = MetaStringDecoder::new('$', '_');
+
+#[derive(Debug, PartialEq, Hash, Eq, Clone, Copy, Default)]
#[repr(i16)]
pub enum Encoding {
+ #[default]
Utf8 = 0x00,
LowerSpecial = 0x01,
LowerUpperDigitSpecial = 0x02,
@@ -59,16 +68,24 @@ impl MetaStringBytes {
}
}
-#[derive(Debug, PartialEq, Hash, Eq, Clone)]
+#[derive(Debug, PartialEq, Hash, Eq, Clone, Default)]
pub struct MetaString {
pub original: String,
pub encoding: Encoding,
pub bytes: Vec<u8>,
pub strip_last_char: bool,
+ pub special_char1: char,
+ pub special_char2: char,
}
impl MetaString {
- pub fn new(original: String, encoding: Encoding, bytes: Vec<u8>) -> Result<Self, Error> {
+ pub fn new(
+ original: String,
+ encoding: Encoding,
+ bytes: Vec<u8>,
+ special_char1: char,
+ special_char2: char,
+ ) -> Result<Self, Error> {
let mut strip_last_char = false;
if encoding != Encoding::Utf8 {
ensure!(!bytes.is_empty(), anyhow!("Encoded data cannot be empty"));
@@ -80,20 +97,22 @@ impl MetaString {
encoding,
bytes,
strip_last_char,
+ special_char1,
+ special_char2,
})
}
}
-pub struct MetaStringDecoder {}
-impl Default for MetaStringDecoder {
- fn default() -> Self {
- Self::new()
- }
+#[derive(Clone)]
+pub struct MetaStringDecoder {
+ pub special_char1: char,
+ pub special_char2: char,
}
-#[derive(Default)]
-pub struct MetaStringEncoder<'a> {
- encoding_options: Option<&'a [Encoding]>,
+#[derive(Clone)]
+pub struct MetaStringEncoder {
+ pub special_char1: char,
+ pub special_char2: char,
}
#[derive(Debug)]
@@ -104,23 +123,29 @@ struct StringStatistics {
can_lower_special_encoded: bool,
}
-impl<'a> MetaStringEncoder<'a> {
- pub fn new() -> Self {
- Self::default()
- }
- pub fn set_options(mut self, encoding_options: Option<&'a [Encoding]>) -> Self {
- self.encoding_options = encoding_options;
- self
+impl MetaStringEncoder {
+ pub const fn new(special_char1: char, special_char2: char) -> Self {
+ Self {
+ special_char1,
+ special_char2,
+ }
}
fn is_latin(&self, s: &str) -> bool {
string_util::is_latin(s)
}
- pub fn encode(&self, input: &str) -> Result<MetaString, Error> {
+ fn _encode(&self, input: &str) -> Result<Option<MetaString>, Error> {
if input.is_empty() {
- return MetaString::new(input.to_string(), Encoding::Utf8, vec![]);
+ return Ok(Some(MetaString::new(
+ input.to_string(),
+ Encoding::Utf8,
+ vec![],
+ self.special_char1,
+ self.special_char2,
+ )?));
}
+
ensure!(
input.len() < SHORT_MAX_VALUE,
anyhow!(
@@ -128,15 +153,42 @@ impl<'a> MetaStringEncoder<'a> {
input.len()
)
);
+
if !self.is_latin(input) {
- return MetaString::new(input.to_string(), Encoding::Utf8, input.as_bytes().to_vec());
+ return Ok(Some(MetaString::new(
+ input.to_string(),
+ Encoding::Utf8,
+ input.as_bytes().to_vec(),
+ self.special_char1,
+ self.special_char2,
+ )?));
+ }
+
+ Ok(None)
+ }
+
+ pub fn encode(&self, input: &str) -> Result<MetaString, Error> {
+ if let Some(ms) = self._encode(input)? {
+ return Ok(ms);
}
- let encoding = self.compute_encoding(input);
+ let encoding = self.compute_encoding(input, None);
self.encode_with_encoding(input, encoding)
}
- fn compute_encoding(&self, input: &str) -> Encoding {
- let allow = |e: Encoding| self.encoding_options.map_or(true, |opts| opts.contains(&e));
+ pub fn encode_with_encodings(
+ &self,
+ input: &str,
+ encodings: &[Encoding],
+ ) -> Result<MetaString, Error> {
+ if let Some(ms) = self._encode(input)? {
+ return Ok(ms);
+ }
+ let encoding = self.compute_encoding(input, Some(encodings));
+ self.encode_with_encoding(input, encoding)
+ }
+
+ fn compute_encoding(&self, input: &str, encodings: Option<&[Encoding]>) -> Encoding {
+ let allow = |e: Encoding| encodings.map_or(true, |opts| opts.contains(&e));
let statistics = self.compute_statistics(input);
if statistics.can_lower_special_encoded && allow(Encoding::LowerSpecial) {
return Encoding::LowerSpecial;
@@ -174,8 +226,7 @@ impl<'a> MetaStringEncoder<'a> {
&& !(c.is_lowercase()
|| c.is_uppercase()
|| c.is_ascii_digit()
- || c == '.'
- || c == '_')
+ || (c == self.special_char1 || c == self.special_char2))
{
can_lower_upper_digit_special_encoded = false;
}
@@ -205,7 +256,13 @@ impl<'a> MetaStringEncoder<'a> {
encoding: Encoding,
) -> Result<MetaString, Error> {
if input.is_empty() {
- return MetaString::new(input.to_string(), Encoding::Utf8, vec![]);
+ return MetaString::new(
+ input.to_string(),
+ Encoding::Utf8,
+ vec![],
+ self.special_char1,
+ self.special_char2,
+ );
}
ensure!(
input.len() < SHORT_MAX_VALUE,
@@ -220,30 +277,66 @@ impl<'a> MetaStringEncoder<'a> {
);
if input.is_empty() {
- return MetaString::new(input.to_string(), Encoding::Utf8, vec![]);
+ return MetaString::new(
+ input.to_string(),
+ Encoding::Utf8,
+ vec![],
+ self.special_char1,
+ self.special_char2,
+ );
};
match encoding {
Encoding::LowerSpecial => {
let encoded_data = self.encode_lower_special(input)?;
- MetaString::new(input.to_string(), encoding, encoded_data)
+ MetaString::new(
+ input.to_string(),
+ encoding,
+ encoded_data,
+ self.special_char1,
+ self.special_char2,
+ )
}
Encoding::LowerUpperDigitSpecial => {
let encoded_data = self.encode_lower_upper_digit_special(input)?;
- MetaString::new(input.to_string(), encoding, encoded_data)
+ MetaString::new(
+ input.to_string(),
+ encoding,
+ encoded_data,
+ self.special_char1,
+ self.special_char2,
+ )
}
Encoding::FirstToLowerSpecial => {
let encoded_data = self.encode_first_to_lower_special(input)?;
- MetaString::new(input.to_string(), encoding, encoded_data)
+ MetaString::new(
+ input.to_string(),
+ encoding,
+ encoded_data,
+ self.special_char1,
+ self.special_char2,
+ )
}
Encoding::AllToLowerSpecial => {
let upper_count = input.chars().filter(|c| c.is_uppercase()).count();
let encoded_data = self.encode_all_to_lower_special(input, upper_count)?;
- MetaString::new(input.to_string(), encoding, encoded_data)
+ MetaString::new(
+ input.to_string(),
+ encoding,
+ encoded_data,
+ self.special_char1,
+ self.special_char2,
+ )
}
Encoding::Utf8 => {
let encoded_data = input.as_bytes().to_vec();
- MetaString::new(input.to_string(), Encoding::Utf8, encoded_data)
+ MetaString::new(
+ input.to_string(),
+ Encoding::Utf8,
+ encoded_data,
+ self.special_char1,
+ self.special_char2,
+ )
}
}
}
@@ -317,9 +410,9 @@ impl<'a> MetaStringEncoder<'a> {
'A'..='Z' => Ok(c as u8 - b'A' + 26),
'0'..='9' => Ok(c as u8 - b'0' + 52),
_ => {
- if c == '.' {
+ if c == self.special_char1 {
Ok(62)
- } else if c == '_' {
+ } else if c == self.special_char2 {
Ok(63)
} else {
Err(anyhow!(
@@ -334,8 +427,11 @@ impl<'a> MetaStringEncoder<'a> {
}
impl MetaStringDecoder {
- pub fn new() -> Self {
- MetaStringDecoder {}
+ pub const fn new(special_char1: char, special_char2: char) -> Self {
+ MetaStringDecoder {
+ special_char1,
+ special_char2,
+ }
}
pub fn decode(&self, encoded_data: &[u8], encoding: Encoding) -> Result<String, Error> {
@@ -424,8 +520,8 @@ impl MetaStringDecoder {
0..=25 => Ok((b'a' + char_value) as char),
26..=51 => Ok((b'A' + char_value - 26) as char),
52..=61 => Ok((b'0' + char_value - 52) as char),
- 62 => Ok('.'),
- 63 => Ok('_'),
+ 62 => Ok(self.special_char1),
+ 63 => Ok(self.special_char2),
_ => Err(anyhow!(
"Invalid character value for LOWER_UPPER_DIGIT_SPECIAL decoding: {char_value}"
))?,
diff --git a/rust/fory-core/src/meta/mod.rs b/rust/fory-core/src/meta/mod.rs
index ffc3931f0..4eea1ae6b 100644
--- a/rust/fory-core/src/meta/mod.rs
+++ b/rust/fory-core/src/meta/mod.rs
@@ -21,6 +21,11 @@ mod type_meta;
pub use meta_string::{
Encoding, MetaString, MetaStringBytes, MetaStringDecoder, MetaStringEncoder,
+ FIELD_NAME_DECODER, FIELD_NAME_ENCODER, NAMESPACE_DECODER, NAMESPACE_ENCODER,
+ TYPE_NAME_DECODER, TYPE_NAME_ENCODER,
};
pub use string_util::{get_latin1_length, is_latin, murmurhash3_x64_128};
-pub use type_meta::{FieldInfo, FieldType, NullableFieldType, TypeMeta, TypeMetaLayer};
+pub use type_meta::{
+ FieldInfo, FieldType, NullableFieldType, TypeMeta, TypeMetaLayer, NAMESPACE_ENCODINGS,
+ TYPE_NAME_ENCODINGS,
+};
diff --git a/rust/fory-core/src/meta/type_meta.rs b/rust/fory-core/src/meta/type_meta.rs
index eef783f20..13c3b4042 100644
--- a/rust/fory-core/src/meta/type_meta.rs
+++ b/rust/fory-core/src/meta/type_meta.rs
@@ -17,10 +17,13 @@
use crate::buffer::{Reader, Writer};
use crate::error::Error;
-use crate::meta::murmurhash3_x64_128;
-use crate::meta::{Encoding, MetaStringDecoder, MetaStringEncoder};
+use crate::meta::{
+ murmurhash3_x64_128, Encoding, MetaString, MetaStringDecoder, FIELD_NAME_DECODER,
+ FIELD_NAME_ENCODER, NAMESPACE_DECODER,
+};
use crate::types::TypeId;
use anyhow::anyhow;
+use std::clone::Clone;
use std::cmp::min;
const SMALL_NUM_FIELDS_THRESHOLD: usize = 0b11111;
@@ -34,7 +37,20 @@ const COMPRESS_META_FLAG: i64 = 0b1 << 13;
const HAS_FIELDS_META_FLAG: i64 = 0b1 << 12;
const NUM_HASH_BITS: i8 = 50;
-static ENCODING_OPTIONS: &[Encoding] = &[
+pub static NAMESPACE_ENCODINGS: &[Encoding] = &[
+ Encoding::Utf8,
+ Encoding::AllToLowerSpecial,
+ Encoding::LowerUpperDigitSpecial,
+];
+
+pub static TYPE_NAME_ENCODINGS: &[Encoding] = &[
+ Encoding::Utf8,
+ Encoding::AllToLowerSpecial,
+ Encoding::LowerUpperDigitSpecial,
+ Encoding::FirstToLowerSpecial,
+];
+
+static FIELD_NAME_ENCODINGS: &[Encoding] = &[
Encoding::Utf8,
Encoding::AllToLowerSpecial,
Encoding::LowerUpperDigitSpecial,
@@ -207,7 +223,7 @@ impl FieldInfo {
let field_name_bytes = reader.bytes(name_size);
- let field_name = MetaStringDecoder::new()
+ let field_name = FIELD_NAME_DECODER
.decode(field_name_bytes, encoding)
.unwrap();
FieldInfo {
@@ -220,9 +236,8 @@ impl FieldInfo {
// field_bytes: | header | type_info | field_name |
let mut writer = Writer::default();
// header: | field_name_encoding:2bits | size:4bits | nullability:1bit | ref_tracking:1bit |
- let meta_string = MetaStringEncoder::new()
- .set_options(Some(ENCODING_OPTIONS))
- .encode(&self.field_name)?;
+ let meta_string =
+ FIELD_NAME_ENCODER.encode_with_encodings(&self.field_name, FIELD_NAME_ENCODINGS)?;
let name_encoded = meta_string.bytes.as_slice();
let name_size = name_encoded.len() - 1;
let mut header: u8 = (min(FIELD_NAME_SIZE_THRESHOLD, name_size) as u8) << 2;
@@ -234,7 +249,7 @@ impl FieldInfo {
if nullable {
header |= 2;
}
- let encoding_idx = ENCODING_OPTIONS
+ let encoding_idx = FIELD_NAME_ENCODINGS
.iter()
.position(|x| *x == meta_string.encoding)
.unwrap() as u8;
@@ -259,8 +274,8 @@ impl PartialEq for FieldType {
#[derive(Debug)]
pub struct TypeMetaLayer {
type_id: u32,
- namespace: Vec<u8>,
- type_name: Vec<u8>,
+ namespace: MetaString,
+ type_name: MetaString,
register_by_name: bool,
field_infos: Vec<FieldInfo>,
}
@@ -268,8 +283,8 @@ pub struct TypeMetaLayer {
impl TypeMetaLayer {
pub fn new(
type_id: u32,
- namespace: Vec<u8>,
- type_name: Vec<u8>,
+ namespace: MetaString,
+ type_name: MetaString,
register_by_name: bool,
field_infos: Vec<FieldInfo>,
) -> TypeMetaLayer {
@@ -286,47 +301,70 @@ impl TypeMetaLayer {
self.type_id
}
+ pub fn get_type_name(&self) -> MetaString {
+ self.type_name.clone()
+ }
+
+ pub fn get_namespace(&self) -> MetaString {
+ self.namespace.clone()
+ }
+
pub fn get_field_infos(&self) -> &Vec<FieldInfo> {
&self.field_infos
}
- fn write_name(writer: &mut Writer, bytes: &[u8], encoding: Encoding) {
+ fn write_name(writer: &mut Writer, name: &MetaString, encodings: &[Encoding]) {
+ let encoding_idx = encodings.iter().position(|x| *x == name.encoding).unwrap() as u8;
+ let bytes = name.bytes.as_slice();
if bytes.len() >= BIG_NAME_THRESHOLD {
- writer.u8((BIG_NAME_THRESHOLD << 2) as u8 | encoding as u8);
+ writer.u8((BIG_NAME_THRESHOLD << 2) as u8 | encoding_idx);
writer.var_uint32((bytes.len() - BIG_NAME_THRESHOLD) as u32);
} else {
- writer.u8((bytes.len() << 2) as u8 | encoding as u8);
+ writer.u8((bytes.len() << 2) as u8 | encoding_idx);
}
writer.bytes(bytes);
}
pub fn write_namespace(&self, writer: &mut Writer) {
- Self::write_name(writer, &self.namespace, Encoding::Utf8);
+ Self::write_name(writer, &self.namespace, NAMESPACE_ENCODINGS);
}
pub fn write_type_name(&self, writer: &mut Writer) {
- Self::write_name(writer, &self.type_name, Encoding::Utf8);
+ Self::write_name(writer, &self.type_name, TYPE_NAME_ENCODINGS);
}
- fn read_name(reader: &mut Reader) -> Vec<u8> {
+ fn read_name(
+ reader: &mut Reader,
+ decoder: &MetaStringDecoder,
+ encodings: &[Encoding],
+ ) -> MetaString {
let header = reader.u8();
- let _encoding = header & 0b11;
+ let encoding_idx = header & 0b11;
let length = header >> 2;
-
let length = if length >= BIG_NAME_THRESHOLD as u8 {
BIG_NAME_THRESHOLD + reader.var_uint32() as usize
} else {
length as usize
};
- reader.bytes(length).to_vec()
+ let bytes = reader.bytes(length);
+ let encoding = encodings[encoding_idx as usize];
+ let name_str = decoder.decode(bytes, encoding).unwrap();
+ MetaString::new(
+ name_str,
+ encoding,
+ bytes.to_vec(),
+ decoder.special_char1,
+ decoder.special_char2,
+ )
+ .unwrap()
}
- pub fn read_namespace(reader: &mut Reader) -> Vec<u8> {
- Self::read_name(reader)
+ pub fn read_namespace(reader: &mut Reader) -> MetaString {
+ Self::read_name(reader, &NAMESPACE_DECODER, NAMESPACE_ENCODINGS)
}
- pub fn read_type_name(reader: &mut Reader) -> Vec<u8> {
- Self::read_name(reader)
+ pub fn read_type_name(reader: &mut Reader) -> MetaString {
+ Self::read_name(reader, &NAMESPACE_DECODER, TYPE_NAME_ENCODINGS)
}
fn to_bytes(&self) -> Result<Vec<u8>, Error> {
@@ -371,7 +409,7 @@ impl TypeMetaLayer {
type_id = TypeId::UNKNOWN as u32;
} else {
type_id = reader.var_uint32();
- let empty_name = "".as_bytes().to_vec();
+ let empty_name = MetaString::default();
namespace = empty_name.clone();
type_name = empty_name;
}
@@ -399,10 +437,18 @@ impl TypeMeta {
self.layers.first().unwrap().get_type_id()
}
+ pub fn get_type_name(&self) -> MetaString {
+ self.layers.first().unwrap().get_type_name()
+ }
+
+ pub fn get_namespace(&self) -> MetaString {
+ self.layers.first().unwrap().get_namespace()
+ }
+
pub fn from_fields(
type_id: u32,
- namespace: Vec<u8>,
- type_name: Vec<u8>,
+ namespace: MetaString,
+ type_name: MetaString,
register_by_name: bool,
field_infos: Vec<FieldInfo>,
) -> TypeMeta {
@@ -448,7 +494,7 @@ impl TypeMeta {
// global_binary_header:| hash:50bits | is_compressed:1bit | write_fields_meta:1bit | meta_size:12bits |
let meta_size = layers_writer.len() as i64;
let mut header: i64 = min(META_SIZE_MASK, meta_size);
- let write_meta_fields_flag = true;
+ let write_meta_fields_flag = self.get_field_infos().is_empty();
if write_meta_fields_flag {
header |= HAS_FIELDS_META_FLAG;
}
diff --git a/rust/fory-core/src/resolver/context.rs b/rust/fory-core/src/resolver/context.rs
index 0cfb35f78..3d4f27294 100644
--- a/rust/fory-core/src/resolver/context.rs
+++ b/rust/fory-core/src/resolver/context.rs
@@ -84,7 +84,7 @@ impl<'se> WriteContext<'se> {
pub struct ReadContext<'de, 'bf: 'de> {
pub reader: Reader<'bf>,
pub tags: Vec<&'de str>,
- pub fory: &'de Fory,
+ fory: &'de Fory,
pub meta_resolver: MetaReaderResolver,
}
@@ -106,7 +106,7 @@ impl<'de, 'bf: 'de> ReadContext<'de, 'bf> {
self.meta_resolver.get(type_index)
}
- pub fn load_meta(&mut self, offset: usize) {
+ pub fn load_meta(&mut self, offset: usize) -> usize {
self.meta_resolver.load(&mut Reader::new(
&self.reader.slice_after_cursor()[offset..],
))
diff --git a/rust/fory-core/src/resolver/meta_resolver.rs b/rust/fory-core/src/resolver/meta_resolver.rs
index cdd23023e..fedde3930 100644
--- a/rust/fory-core/src/resolver/meta_resolver.rs
+++ b/rust/fory-core/src/resolver/meta_resolver.rs
@@ -32,13 +32,14 @@ impl MetaReaderResolver {
unsafe { self.reading_type_defs.get_unchecked(index) }
}
- pub fn load(&mut self, reader: &mut Reader) {
+ pub fn load(&mut self, reader: &mut Reader) -> usize {
let meta_size = reader.var_uint32();
// self.reading_type_defs.reserve(meta_size as usize);
for _ in 0..meta_size {
self.reading_type_defs
.push(Rc::new(TypeMeta::from_bytes(reader)));
}
+ reader.get_cursor()
}
}
diff --git a/rust/fory-core/src/resolver/type_resolver.rs b/rust/fory-core/src/resolver/type_resolver.rs
index 03bfafea7..d7af1d809 100644
--- a/rust/fory-core/src/resolver/type_resolver.rs
+++ b/rust/fory-core/src/resolver/type_resolver.rs
@@ -22,58 +22,55 @@ use crate::serializer::StructSerializer;
use std::cell::RefCell;
use std::{any::Any, collections::HashMap};
+type SerializerFn = fn(&dyn Any, &mut WriteContext, is_field: bool);
+type DeserializerFn =
+ fn(&mut ReadContext, is_field: bool, skip_ref_flag: bool) -> Result<Box<dyn Any>, Error>;
+
pub struct Harness {
- serializer: fn(&dyn Any, &mut WriteContext),
- deserializer: fn(&mut ReadContext) -> Result<Box<dyn Any>, Error>,
+ serializer: SerializerFn,
+ deserializer: DeserializerFn,
}
impl Harness {
- pub fn new(
- serializer: fn(&dyn Any, &mut WriteContext),
- deserializer: fn(&mut ReadContext) -> Result<Box<dyn Any>, Error>,
- ) -> Harness {
+ pub fn new(serializer: SerializerFn, deserializer: DeserializerFn) -> Harness {
Harness {
serializer,
deserializer,
}
}
- pub fn get_serializer(&self) -> fn(&dyn Any, &mut WriteContext) {
+ pub fn get_serializer(&self) -> fn(&dyn Any, &mut WriteContext, is_field: bool) {
self.serializer
}
- pub fn get_deserializer(&self) -> fn(&mut ReadContext) -> Result<Box<dyn Any>, Error> {
+ pub fn get_deserializer(&self) -> DeserializerFn {
self.deserializer
}
}
-#[allow(dead_code)]
+#[derive(Clone, Debug)]
pub struct TypeInfo {
type_def: Vec<u8>,
type_id: u32,
- namespace: Vec<u8>,
- type_name: Vec<u8>,
+ namespace: String,
+ type_name: String,
+ register_by_name: bool,
}
impl TypeInfo {
pub fn new<T: StructSerializer>(
fory: &Fory,
type_id: u32,
- namespace: Vec<u8>,
- type_name: Vec<u8>,
+ namespace: &str,
+ type_name: &str,
register_by_name: bool,
) -> TypeInfo {
TypeInfo {
- type_def: T::type_def(
- fory,
- type_id,
- namespace.clone(),
- type_name.clone(),
- register_by_name,
- ),
+ type_def: T::type_def(fory, type_id, namespace, type_name, register_by_name),
type_id,
- namespace,
- type_name,
+ namespace: namespace.to_owned(),
+ type_name: type_name.to_owned(),
+ register_by_name,
}
}
@@ -89,11 +86,12 @@ impl TypeInfo {
#[derive(Default)]
pub struct TypeResolver {
serialize_map: HashMap<u32, Harness>,
+ name_serialize_map: HashMap<(String, String), Harness>,
type_id_map: HashMap<std::any::TypeId, u32>,
+ type_name_map: HashMap<std::any::TypeId, (String, String)>,
type_info_map: HashMap<std::any::TypeId, TypeInfo>,
// Fast lookup by numeric ID for common types
type_id_index: Vec<u32>,
- type_id_counter: u32,
sorted_field_names_map: RefCell<HashMap<std::any::TypeId, Vec<String>>>,
}
@@ -124,12 +122,18 @@ impl TypeResolver {
)
}
- pub fn register<T: StructSerializer>(&mut self, type_info: TypeInfo) {
- fn serializer<T2: 'static + StructSerializer>(this: &dyn Any, context: &mut WriteContext) {
+ pub fn register<T: StructSerializer>(&mut self, type_info: &TypeInfo) {
+ fn serializer<T2: 'static + StructSerializer>(
+ this: &dyn Any,
+ context: &mut WriteContext,
+ is_field: bool,
+ ) {
let this = this.downcast_ref::<T2>();
match this {
Some(v) => {
- T2::write(v, context, true);
+ let skip_ref_flag =
+ crate::serializer::get_skip_ref_flag::<T2>(context.get_fory());
+ crate::serializer::write_data(v, context, is_field, skip_ref_flag, true);
}
None => todo!(),
}
@@ -137,39 +141,53 @@ impl TypeResolver {
fn deserializer<T2: 'static + StructSerializer>(
context: &mut ReadContext,
+ is_field: bool,
+ skip_ref_flag: bool,
) -> Result<Box<dyn Any>, Error> {
- match T2::read_compatible(context) {
+ match crate::serializer::read_data::<T2>(context, is_field, skip_ref_flag, true) {
Ok(v) => Ok(Box::new(v)),
Err(e) => Err(e),
}
}
+ let rs_type_id = std::any::TypeId::of::<T>();
+ if self.type_info_map.contains_key(&rs_type_id) {
+ panic!("rs_struct:{:?} already registered", type_info.type_id);
+ }
+ self.type_info_map.insert(rs_type_id, (*type_info).clone());
+
let index = T::type_index() as usize;
if index >= self.type_id_index.len() {
self.type_id_index.resize(index + 1, NO_TYPE_ID);
}
self.type_id_index[index] = type_info.type_id;
- if self.serialize_map.contains_key(&type_info.type_id) {
- panic!("TypeId {:?} already registered", type_info.type_id);
- }
- self.type_id_map
- .insert(std::any::TypeId::of::<T>(), type_info.type_id);
- self.serialize_map.insert(
- type_info.type_id,
- Harness::new(serializer::<T>, deserializer::<T>),
- );
- self.type_info_map
- .insert(std::any::TypeId::of::<T>(), type_info);
- }
- pub fn next_type_id(&mut self) -> u32 {
- let old_type_id = self.type_id_counter;
- loop {
- self.type_id_counter += 1;
- if !self.serialize_map.contains_key(&self.type_id_counter) {
- break;
+ if type_info.register_by_name {
+ if self
+ .name_serialize_map
+ .contains_key(&(type_info.namespace.clone(), type_info.type_name.clone()))
+ {
+ panic!("TypeId {:?} already registered_by_name", type_info.type_id);
+ }
+ let namespace_bytes = type_info.namespace.clone();
+ let type_name_bytes = type_info.type_name.clone();
+ self.type_name_map.insert(
+ rs_type_id,
+ (namespace_bytes.clone(), type_name_bytes.clone()),
+ );
+ self.name_serialize_map.insert(
+ (namespace_bytes, type_name_bytes),
+ Harness::new(serializer::<T>, deserializer::<T>),
+ );
+ } else {
+ if self.serialize_map.contains_key(&type_info.type_id) {
+ panic!("TypeId {:?} already registered_by_id", type_info.type_id);
}
+ self.type_id_map.insert(rs_type_id, type_info.type_id);
+ self.serialize_map.insert(
+ type_info.type_id,
+ Harness::new(serializer::<T>, deserializer::<T>),
+ );
}
- old_type_id
}
pub fn get_harness_by_type(&self, type_id: std::any::TypeId) -> Option<&Harness> {
@@ -180,6 +198,11 @@ impl TypeResolver {
self.serialize_map.get(&id)
}
+ pub fn get_name_harness(&self, namespace: &str, type_name: &str) -> Option<&Harness> {
+ self.name_serialize_map
+ .get(&(namespace.to_owned(), type_name.to_owned()))
+ }
+
pub fn get_sorted_field_names<T: StructSerializer>(
&self,
type_id: std::any::TypeId,
diff --git a/rust/fory-core/src/serializer/bool.rs b/rust/fory-core/src/serializer/bool.rs
index d6f61ae2d..5e1e8828e 100644
--- a/rust/fory-core/src/serializer/bool.rs
+++ b/rust/fory-core/src/serializer/bool.rs
@@ -28,19 +28,25 @@ impl Serializer for bool {
mem::size_of::<i32>()
}
- fn write(&self, context: &mut WriteContext, is_field: bool) {
+ fn write(&self, context: &mut WriteContext, _is_field: bool) {
+ context.writer.u8(if *self { 1 } else { 0 });
+ }
+
+ fn write_type_info(context: &mut WriteContext, is_field: bool) {
if *context.get_fory().get_mode() == Mode::Compatible && !is_field {
context.writer.var_uint32(TypeId::BOOL as u32);
}
- context.writer.u8(if *self { 1 } else { 0 });
}
- fn read(context: &mut ReadContext, is_field: bool) -> Result<Self, Error> {
+ fn read(context: &mut ReadContext) -> Result<Self, Error> {
+ Ok(context.reader.u8() == 1)
+ }
+
+ fn read_type_info(context: &mut ReadContext, is_field: bool) {
if *context.get_fory().get_mode() == Mode::Compatible && !is_field {
let remote_type_id = context.reader.var_uint32();
assert_eq!(remote_type_id, TypeId::BOOL as u32);
}
- Ok(context.reader.u8() == 1)
}
fn get_type_id(_fory: &Fory) -> u32 {
diff --git a/rust/fory-core/src/serializer/collection.rs b/rust/fory-core/src/serializer/collection.rs
index 3e20f4fb5..de3e6f333 100644
--- a/rust/fory-core/src/serializer/collection.rs
+++ b/rust/fory-core/src/serializer/collection.rs
@@ -15,26 +15,23 @@
// specific language governing permissions and limitations
// under the License.
-use crate::ensure;
use crate::error::Error;
use crate::resolver::context::ReadContext;
use crate::resolver::context::WriteContext;
-use crate::serializer::{deserialize, serialize, Serializer};
-use crate::types::{Mode, SIZE_OF_REF_AND_TYPE};
-use anyhow::anyhow;
+use crate::serializer::Serializer;
+use crate::types::Mode;
// const TRACKING_REF: u8 = 0b1;
pub const HAS_NULL: u8 = 0b10;
-// Whether collection elements type is not declare type.
-const NOT_DECL_ELEMENT_TYPE: u8 = 0b100;
+// Whether collection elements type is declare type.
+const DECL_ELEMENT_TYPE: u8 = 0b100;
-// Whether collection elements type different.
-// const NOT_SAME_TYPE: u8 = 0b1000;
+// Whether collection elements type same.
+pub const IS_SAME_TYPE: u8 = 0b1000;
-pub fn write_collection<'a, T: Serializer + 'a, I: IntoIterator<Item = &'a T>>(
- iter: I,
+pub fn write_collection_type_info(
context: &mut WriteContext,
is_field: bool,
collection_type_id: u32,
@@ -42,71 +39,77 @@ pub fn write_collection<'a, T: Serializer + 'a, I: IntoIterator<Item = &'a T>>(
if *context.get_fory().get_mode() == Mode::Compatible && !is_field {
context.writer.var_uint32(collection_type_id);
}
- let iter = iter.into_iter();
- let len = iter.size_hint().0;
+}
+
+pub fn write_collection<'a, T: Serializer + 'a, I: IntoIterator<Item = &'a T>>(
+ iter: I,
+ context: &mut WriteContext,
+ is_field: bool,
+) {
+ let items: Vec<&T> = iter.into_iter().collect();
+ let len = items.len();
context.writer.var_uint32(len as u32);
if len == 0 {
return;
}
- let has_null = T::is_option();
let mut header = 0;
+ let mut has_null = false;
+ if T::is_option() {
+ for item in &items {
+ if item.is_none() {
+ has_null = true;
+ break;
+ }
+ }
+ }
+ let is_same_type = true;
if has_null {
header |= HAS_NULL;
}
- header |= NOT_DECL_ELEMENT_TYPE;
+ if is_field {
+ header |= DECL_ELEMENT_TYPE;
+ }
+ if is_same_type {
+ header |= IS_SAME_TYPE;
+ }
context.writer.u8(header);
- context
- .writer
- .var_uint32(T::get_type_id(context.get_fory()));
- if !has_null {
- context
- .writer
- .reserve((<T as Serializer>::reserved_space()) * len);
- for item in iter {
- item.write(context, true);
- }
- } else {
- context
- .writer
- .reserve((<T as Serializer>::reserved_space() + SIZE_OF_REF_AND_TYPE) * len);
- for item in iter {
- serialize(item, context, true);
- }
+ T::write_type_info(context, is_field);
+ // context.writer.reserve((T::reserved_space() + SIZE_OF_REF_AND_TYPE) * len);
+ for item in &items {
+ // let skip_ref_flag = crate::serializer::get_skip_ref_flag::<T>(context.get_fory());
+ let skip_ref_flag = is_same_type && !has_null;
+ crate::serializer::write_data(*item, context, is_field, skip_ref_flag, true);
}
- println!("bytes after write collection {:?}", context.writer.dump());
}
-pub fn read_collection<C, T>(
+pub fn read_collection_type_info(
context: &mut ReadContext,
is_field: bool,
- expected_collection_type_id: u32,
-) -> Result<C, Error>
+ collection_type_id: u32,
+) {
+ if *context.get_fory().get_mode() == Mode::Compatible && !is_field {
+ let remote_collection_type_id = context.reader.var_uint32();
+ assert_eq!(remote_collection_type_id, collection_type_id);
+ }
+}
+
+pub fn read_collection<C, T>(context: &mut ReadContext) -> Result<C, Error>
where
T: Serializer,
C: FromIterator<T>,
{
- if *context.get_fory().get_mode() == Mode::Compatible && !is_field {
- let remote_collection_type_id = context.reader.var_uint32();
- assert_eq!(remote_collection_type_id, expected_collection_type_id);
- }
let len = context.reader.var_uint32();
if len == 0 {
return Ok(C::from_iter(std::iter::empty()));
}
let header = context.reader.u8();
- let actual_elem_type_id = context.reader.var_uint32();
- let expected_elem_id = T::get_type_id(context.fory);
- ensure!(
- expected_elem_id == actual_elem_type_id,
- anyhow!("Invalid field type, expected:{expected_elem_id}, actual:{actual_elem_type_id}")
- );
- if header & HAS_NULL == 0 {
- (0..len)
- .map(|_| T::read(context, true))
- .collect::<Result<C, Error>>()
- } else {
- (0..len)
- .map(|_| deserialize(context, true))
- .collect::<Result<C, Error>>()
- }
+ let declared = (header & DECL_ELEMENT_TYPE) != 0;
+ T::read_type_info(context, declared);
+ let has_null = (header & HAS_NULL) != 0;
+ let is_same_type = (header & IS_SAME_TYPE) != 0;
+ let skip_ref_flag = is_same_type && !has_null;
+ // let skip_ref_flag = crate::serializer::get_skip_ref_flag::<T>(context.get_fory());
+ (0..len)
+ .map(|_| crate::serializer::read_data(context, declared, skip_ref_flag, true))
+ .collect::<Result<C, Error>>()
}
diff --git a/rust/fory-core/src/serializer/datetime.rs b/rust/fory-core/src/serializer/datetime.rs
index 48c0b57b9..ffe89fe8a 100644
--- a/rust/fory-core/src/serializer/datetime.rs
+++ b/rust/fory-core/src/serializer/datetime.rs
@@ -27,11 +27,7 @@ use chrono::{DateTime, Days, NaiveDate, NaiveDateTime};
use std::mem;
impl Serializer for NaiveDateTime {
- fn read(context: &mut ReadContext, is_field: bool) -> Result<Self, Error> {
- if *context.get_fory().get_mode() == Mode::Compatible && !is_field {
- let remote_type_id = context.reader.var_uint32();
- assert_eq!(remote_type_id, TypeId::TIMESTAMP as u32);
- }
+ fn read(context: &mut ReadContext) -> Result<Self, Error> {
let micros = context.reader.i64();
let seconds = micros / 1_000_000;
let subsec_micros = (micros % 1_000_000) as u32;
@@ -43,15 +39,25 @@ impl Serializer for NaiveDateTime {
)))
}
- fn write(&self, context: &mut WriteContext, is_field: bool) {
+ fn read_type_info(context: &mut ReadContext, is_field: bool) {
if *context.get_fory().get_mode() == Mode::Compatible && !is_field {
- context.writer.var_uint32(TypeId::TIMESTAMP as u32);
+ let remote_type_id = context.reader.var_uint32();
+ assert_eq!(remote_type_id, TypeId::TIMESTAMP as u32);
}
+ }
+
+ fn write(&self, context: &mut WriteContext, _is_field: bool) {
let dt = self.and_utc();
let micros = dt.timestamp() * 1_000_000 + dt.timestamp_subsec_micros() as i64;
context.writer.i64(micros);
}
+ fn write_type_info(context: &mut WriteContext, is_field: bool) {
+ if *context.get_fory().get_mode() == Mode::Compatible && !is_field {
+ context.writer.var_uint32(TypeId::TIMESTAMP as u32);
+ }
+ }
+
fn reserved_space() -> usize {
mem::size_of::<u64>()
}
@@ -64,23 +70,22 @@ impl Serializer for NaiveDateTime {
impl ForyGeneralList for NaiveDateTime {}
impl Serializer for NaiveDate {
- fn write(&self, context: &mut WriteContext, is_field: bool) {
+ fn write(&self, context: &mut WriteContext, _is_field: bool) {
+ let days_since_epoch = self.signed_duration_since(EPOCH).num_days();
+ context.writer.i32(days_since_epoch as i32);
+ }
+
+ fn write_type_info(context: &mut WriteContext, is_field: bool) {
if *context.get_fory().get_mode() == Mode::Compatible && !is_field {
context.writer.var_uint32(TypeId::LOCAL_DATE as u32);
}
- let days_since_epoch = self.signed_duration_since(EPOCH).num_days();
- context.writer.i32(days_since_epoch as i32);
}
fn reserved_space() -> usize {
mem::size_of::<i32>()
}
- fn read(context: &mut ReadContext, is_field: bool) -> Result<Self, Error> {
- if *context.get_fory().get_mode() == Mode::Compatible && !is_field {
- let remote_type_id = context.reader.var_uint32();
- assert_eq!(remote_type_id, TypeId::LOCAL_DATE as u32);
- }
+ fn read(context: &mut ReadContext) -> Result<Self, Error> {
let days = context.reader.i32();
EPOCH
.checked_add_days(Days::new(days as u64))
@@ -89,6 +94,13 @@ impl Serializer for NaiveDate {
)))
}
+ fn read_type_info(context: &mut ReadContext, is_field: bool) {
+ if *context.get_fory().get_mode() == Mode::Compatible && !is_field {
+ let remote_type_id = context.reader.var_uint32();
+ assert_eq!(remote_type_id, TypeId::LOCAL_DATE as u32);
+ }
+ }
+
fn get_type_id(_fory: &Fory) -> u32 {
TypeId::LOCAL_DATE as u32
}
diff --git a/rust/fory-core/src/serializer/list.rs b/rust/fory-core/src/serializer/list.rs
index cf95253b6..a7488e89a 100644
--- a/rust/fory-core/src/serializer/list.rs
+++ b/rust/fory-core/src/serializer/list.rs
@@ -23,18 +23,28 @@ use crate::serializer::Serializer;
use crate::types::{ForyGeneralList, TypeId};
use std::mem;
-use super::collection::{read_collection, write_collection};
+use super::collection::{
+ read_collection, read_collection_type_info, write_collection, write_collection_type_info,
+};
impl<T> Serializer for Vec<T>
where
T: Serializer + ForyGeneralList,
{
fn write(&self, context: &mut WriteContext, is_field: bool) {
- write_collection(self.iter(), context, is_field, TypeId::LIST as u32);
+ write_collection(self, context, is_field);
}
- fn read(context: &mut ReadContext, is_field: bool) -> Result<Self, Error> {
- read_collection(context, is_field, TypeId::LIST as u32)
+ fn write_type_info(context: &mut WriteContext, is_field: bool) {
+ write_collection_type_info(context, is_field, TypeId::LIST as u32);
+ }
+
+ fn read(context: &mut ReadContext) -> Result<Self, Error> {
+ read_collection(context)
+ }
+
+ fn read_type_info(context: &mut ReadContext, is_field: bool) {
+ read_collection_type_info(context, is_field, TypeId::LIST as u32)
}
fn reserved_space() -> usize {
diff --git a/rust/fory-core/src/serializer/map.rs b/rust/fory-core/src/serializer/map.rs
index f374a1cdf..c349a8836 100644
--- a/rust/fory-core/src/serializer/map.rs
+++ b/rust/fory-core/src/serializer/map.rs
@@ -15,102 +15,214 @@
// specific language governing permissions and limitations
// under the License.
-use crate::ensure;
use crate::error::Error;
use crate::fory::Fory;
use crate::resolver::context::ReadContext;
use crate::resolver::context::WriteContext;
use crate::serializer::Serializer;
use crate::types::{ForyGeneralList, Mode, TypeId, SIZE_OF_REF_AND_TYPE};
-use anyhow::anyhow;
use std::collections::HashMap;
use std::mem;
const MAX_CHUNK_SIZE: u8 = 255;
-impl<T1: Serializer + Eq + std::hash::Hash, T2: Serializer> Serializer for HashMap<T1, T2> {
+const TRACKING_KEY_REF: u8 = 0b1;
+pub const KEY_NULL: u8 = 0b10;
+const DECL_KEY_TYPE: u8 = 0b100;
+const TRACKING_VALUE_REF: u8 = 0b1000;
+pub const VALUE_NULL: u8 = 0b10000;
+const DECL_VALUE_TYPE: u8 = 0b100000;
+
+fn check_and_write_null<K: Serializer + Eq + std::hash::Hash, V: Serializer>(
+ context: &mut WriteContext,
+ is_field: bool,
+ key: &K,
+ value: &V,
+) -> bool {
+ if key.is_none() && value.is_none() {
+ context.writer.u8(KEY_NULL | VALUE_NULL);
+ return true;
+ }
+ if key.is_none() {
+ let mut chunk_header = KEY_NULL;
+ let skip_ref_flag;
+ if is_field {
+ skip_ref_flag = crate::serializer::get_skip_ref_flag::<V>(context.get_fory());
+ chunk_header |= DECL_VALUE_TYPE;
+ } else {
+ skip_ref_flag = false;
+ chunk_header |= TRACKING_VALUE_REF;
+ }
+ context.writer.u8(chunk_header);
+
+ crate::serializer::write_data(value, context, is_field, skip_ref_flag, false);
+ return true;
+ }
+ if value.is_none() {
+ let mut chunk_header = VALUE_NULL;
+ let skip_ref_flag;
+ if is_field {
+ // skip_ref_flag = crate::serializer::get_skip_ref_flag::<V>(context.get_fory());
+ skip_ref_flag = true;
+ chunk_header |= DECL_KEY_TYPE;
+ } else {
+ skip_ref_flag = false;
+ chunk_header |= TRACKING_KEY_REF;
+ }
+ context.writer.u8(chunk_header);
+ crate::serializer::write_data(key, context, is_field, skip_ref_flag, false);
+ return true;
+ }
+ false
+}
+
+fn write_chunk_size(context: &mut WriteContext, header_offset: usize, size: u8) {
+ context.writer.set_bytes(header_offset + 1, &[size]);
+}
+
+impl<K: Serializer + Eq + std::hash::Hash, V: Serializer> Serializer for HashMap<K, V> {
fn write(&self, context: &mut WriteContext, is_field: bool) {
- if *context.get_fory().get_mode() == Mode::Compatible && !is_field {
- context.writer.var_uint32(TypeId::MAP as u32);
+ let length = self.len();
+ context.writer.var_uint32(length as u32);
+ if length == 0 {
+ return;
}
- context.writer.var_uint32(self.len() as u32);
- let reserved_space = (<T1 as Serializer>::reserved_space() + SIZE_OF_REF_AND_TYPE)
+ let reserved_space = (<K as Serializer>::reserved_space() + SIZE_OF_REF_AND_TYPE)
* self.len()
- + (<T2 as Serializer>::reserved_space() + SIZE_OF_REF_AND_TYPE) * self.len();
+ + (<V as Serializer>::reserved_space() + SIZE_OF_REF_AND_TYPE) * self.len();
context.writer.reserve(reserved_space);
let mut header_offset = 0;
- let mut pair_counter = 0;
- let mut header_gen = false;
+ let mut pair_counter: u8 = 0;
+ let mut need_write_header = true;
+ let mut skip_key_ref_flag = false;
+ let mut skip_val_ref_flag = false;
for entry in self.iter() {
- if !header_gen {
+ let key = entry.0;
+ let value = entry.1;
+ if need_write_header {
+ if check_and_write_null(context, is_field, key, value) {
+ continue;
+ }
header_offset = context.writer.len();
- let _is_key_null = false;
- let _is_val_null = false;
- // todo
- // if T1::is_option() {}
- // if T2::is_option() {}
context.writer.i16(-1);
- context
- .writer
- .var_uint32(T1::get_type_id(context.get_fory()));
- context
- .writer
- .var_uint32(T2::get_type_id(context.get_fory()));
- let header = 0;
- context.writer.set_bytes(header_offset, &[header]);
- header_gen = true;
+ let mut chunk_header = 0;
+ if is_field {
+ chunk_header |= DECL_KEY_TYPE;
+ chunk_header |= DECL_VALUE_TYPE;
+ }
+ // skip_key_ref_flag = crate::serializer::get_skip_ref_flag::<K>(context.get_fory());
+ skip_key_ref_flag = true;
+ // skip_val_ref_flag = crate::serializer::get_skip_ref_flag::<V>(context.get_fory());
+ skip_val_ref_flag = true;
+ if !skip_key_ref_flag {
+ chunk_header |= TRACKING_KEY_REF;
+ }
+ if !skip_val_ref_flag {
+ chunk_header |= TRACKING_VALUE_REF;
+ }
+ K::write_type_info(context, is_field);
+ V::write_type_info(context, is_field);
+ context.writer.set_bytes(header_offset, &[chunk_header]);
+ need_write_header = false;
}
- entry.0.write(context, true);
- entry.1.write(context, true);
+ if key.is_none() || value.is_none() {
+ write_chunk_size(context, header_offset, pair_counter);
+ pair_counter = 0;
+ need_write_header = true;
+ check_and_write_null(context, is_field, key, value);
+ continue;
+ }
+ crate::serializer::write_data(key, context, is_field, skip_key_ref_flag, true);
+ crate::serializer::write_data(value, context, is_field, skip_val_ref_flag, true);
pair_counter += 1;
if pair_counter == MAX_CHUNK_SIZE {
- context.writer.set_bytes(header_offset + 1, &[pair_counter]);
+ write_chunk_size(context, header_offset, pair_counter);
pair_counter = 0;
- header_gen = false;
+ need_write_header = true;
}
}
if pair_counter > 0 {
- assert!(header_offset > 0);
- context.writer.set_bytes(header_offset + 1, &[pair_counter]);
+ write_chunk_size(context, header_offset, pair_counter);
}
}
- fn read(context: &mut ReadContext, is_field: bool) -> Result<Self, Error> {
- if *context.get_fory().get_mode() == Mode::Compatible && !is_field {
- let remote_collection_type_id = context.reader.var_uint32();
- assert_eq!(remote_collection_type_id, TypeId::MAP as u32);
- }
- let mut map = HashMap::<T1, T2>::new();
+ fn read(context: &mut ReadContext) -> Result<Self, Error> {
let len = context.reader.var_uint32();
+ let mut map = HashMap::<K, V>::with_capacity(len as usize);
+ if len == 0 {
+ return Ok(map);
+ }
let mut len_counter = 0;
loop {
if len_counter == len {
break;
}
- let _header = context.reader.u8();
+ let header = context.reader.u8();
+ if header & KEY_NULL != 0 && header & VALUE_NULL != 0 {
+ map.insert(K::default(), V::default());
+ len_counter += 1;
+ continue;
+ }
+ let key_declared = (header & DECL_KEY_TYPE) != 0;
+ let _key_tracking_ref = (header & TRACKING_KEY_REF) != 0;
+ let value_declared = (header & DECL_VALUE_TYPE) != 0;
+ let _value_tracking_ref = (header & TRACKING_VALUE_REF) != 0;
+ if header & KEY_NULL != 0 {
+ // let skip_ref_flag = crate::serializer::get_skip_ref_flag::<V>(context.get_fory());
+ let skip_ref_flag = if value_declared {
+ crate::serializer::get_skip_ref_flag::<V>(context.get_fory())
+ } else {
+ false
+ };
+ let value =
+ crate::serializer::read_data(context, value_declared, skip_ref_flag, false)?;
+ map.insert(K::default(), value);
+ len_counter += 1;
+ continue;
+ }
+ if header & VALUE_NULL != 0 {
+ // let skip_ref_flag = crate::serializer::get_skip_ref_flag::<K>(context.get_fory());
+ let skip_ref_flag = if key_declared {
+ crate::serializer::get_skip_ref_flag::<K>(context.get_fory())
+ } else {
+ false
+ };
+ let key =
+ crate::serializer::read_data(context, key_declared, skip_ref_flag, false)?;
+ map.insert(key, V::default());
+ len_counter += 1;
+ continue;
+ }
let chunk_size = context.reader.u8();
- let actual_key_id = context.reader.var_uint32();
- let expected_key_id = T1::get_type_id(context.fory);
- ensure!(
- expected_key_id == actual_key_id,
- anyhow!("Invalid field type, expected:{expected_key_id}, actual:{actual_key_id}")
- );
- let actual_val_id = context.reader.var_uint32();
- let expected_val_id = T2::get_type_id(context.fory);
- ensure!(
- expected_val_id == actual_val_id,
- anyhow!("Invalid field type, expected:{expected_val_id}, actual:{actual_val_id}")
- );
+ K::read_type_info(context, key_declared);
+ V::read_type_info(context, value_declared);
assert!(len_counter + chunk_size as u32 <= len);
for _ in (0..chunk_size).enumerate() {
- map.insert(T1::read(context, true)?, T2::read(context, true)?);
+ // let skip_ref_flag = crate::serializer::get_skip_ref_flag::<K>(context.get_fory());
+ let key = crate::serializer::read_data(context, key_declared, true, true)?;
+ // let skip_ref_flag = crate::serializer::get_skip_ref_flag::<V>(context.get_fory());
+ let value = crate::serializer::read_data(context, value_declared, true, true)?;
+ map.insert(key, value);
}
len_counter += chunk_size as u32;
}
Ok(map)
}
+ fn write_type_info(context: &mut WriteContext, is_field: bool) {
+ if *context.get_fory().get_mode() == Mode::Compatible && !is_field {
+ context.writer.var_uint32(TypeId::MAP as u32);
+ }
+ }
+
+ fn read_type_info(context: &mut ReadContext, is_field: bool) {
+ if *context.get_fory().get_mode() == Mode::Compatible && !is_field {
+ let remote_collection_type_id = context.reader.var_uint32();
+ assert_eq!(remote_collection_type_id, TypeId::MAP as u32);
+ }
+ }
+
fn reserved_space() -> usize {
mem::size_of::<i32>()
}
diff --git a/rust/fory-core/src/serializer/mod.rs b/rust/fory-core/src/serializer/mod.rs
index b918db5f5..bcf207787 100644
--- a/rust/fory-core/src/serializer/mod.rs
+++ b/rust/fory-core/src/serializer/mod.rs
@@ -18,15 +18,14 @@
use crate::error::Error;
use crate::fory::Fory;
use crate::resolver::context::{ReadContext, WriteContext};
-use crate::types::RefFlag;
-use anyhow::anyhow;
+use crate::types::{Mode, RefFlag, PRIMITIVE_TYPES};
mod any;
mod bool;
pub mod collection;
mod datetime;
mod list;
-mod map;
+pub mod map;
mod number;
mod option;
mod primitive_list;
@@ -34,32 +33,57 @@ mod set;
pub mod skip;
mod string;
-pub fn serialize<T: Serializer + 'static>(record: &T, context: &mut WriteContext, is_field: bool) {
+pub fn write_data<T: Serializer + 'static>(
+ record: &T,
+ context: &mut WriteContext,
+ is_field: bool,
+ skip_ref_flag: bool,
+ skip_type_info: bool,
+) {
if record.is_none() {
context.writer.i8(RefFlag::Null as i8);
} else {
- context.writer.i8(RefFlag::NotNullValue as i8);
+ if !skip_ref_flag {
+ context.writer.i8(RefFlag::NotNullValue as i8);
+ }
+ if !skip_type_info {
+ T::write_type_info(context, is_field);
+ }
record.write(context, is_field);
}
}
-pub fn deserialize<T: Serializer + Default>(
+pub fn read_data<T: Serializer + Default>(
context: &mut ReadContext,
is_field: bool,
+ skip_ref_flag: bool,
+ skip_type_info: bool,
) -> Result<T, Error> {
- let ref_flag = context.reader.i8();
- if ref_flag == (RefFlag::NotNullValue as i8) || ref_flag == (RefFlag::RefValue as i8) {
- T::read(context, is_field)
- } else if ref_flag == (RefFlag::Null as i8) {
- Ok(T::default())
- // Err(anyhow!("Try to deserialize non-option type to null"))?
- } else if ref_flag == (RefFlag::Ref as i8) {
- Err(Error::Ref)
+ if !skip_ref_flag {
+ let ref_flag = context.reader.i8();
+ if ref_flag == RefFlag::Null as i8 {
+ Ok(T::default())
+ } else if ref_flag == (RefFlag::NotNullValue as i8) {
+ if !skip_type_info {
+ T::read_type_info(context, is_field);
+ }
+ T::read(context)
+ } else {
+ unimplemented!()
+ }
} else {
- Err(anyhow!("Unknown ref flag, value:{ref_flag}"))?
+ if !skip_type_info {
+ T::read_type_info(context, is_field);
+ }
+ T::read(context)
}
}
+pub fn get_skip_ref_flag<T: Serializer>(fory: &Fory) -> bool {
+ let elem_type_id = T::get_type_id(fory);
+ !T::is_option() && PRIMITIVE_TYPES.contains(&elem_type_id)
+}
+
pub trait Serializer
where
Self: Sized + Default + 'static,
@@ -71,18 +95,22 @@ where
/// Write the data into the buffer.
fn write(&self, context: &mut WriteContext, is_field: bool);
+ fn write_type_info(context: &mut WriteContext, is_field: bool);
+
/// Entry point of the serialization.
///
/// Step 1: write the type flag and type flag into the buffer.
/// Step 2: invoke the write function to write the Rust object.
fn serialize(&self, context: &mut WriteContext, is_field: bool) {
- serialize(self, context, is_field);
+ write_data(self, context, is_field, false, false);
}
- fn read(context: &mut ReadContext, is_field: bool) -> Result<Self, Error>;
+ fn read(context: &mut ReadContext) -> Result<Self, Error>;
+
+ fn read_type_info(context: &mut ReadContext, is_field: bool);
fn deserialize(context: &mut ReadContext, is_field: bool) -> Result<Self, Error> {
- deserialize(context, is_field)
+ read_data(context, is_field, false, false)
}
fn get_type_id(_fory: &Fory) -> u32;
@@ -100,13 +128,13 @@ pub trait StructSerializer: Serializer + 'static {
fn type_def(
fory: &Fory,
type_id: u32,
- namespace: Vec<u8>,
- type_name: Vec<u8>,
+ namespace: &str,
+ type_name: &str,
register_by_name: bool,
) -> Vec<u8>;
fn type_index() -> u32;
- fn actual_type_id(type_id: u32) -> u32;
+ fn actual_type_id(type_id: u32, register_by_name: bool, mode: &Mode) -> u32;
fn read_compatible(context: &mut ReadContext) -> Result<Self, Error>;
diff --git a/rust/fory-core/src/serializer/number.rs b/rust/fory-core/src/serializer/number.rs
index aa5688dc6..fc1aecb1a 100644
--- a/rust/fory-core/src/serializer/number.rs
+++ b/rust/fory-core/src/serializer/number.rs
@@ -25,19 +25,25 @@ use crate::types::{ForyGeneralList, TypeId};
macro_rules! impl_num_serializer {
($name: ident, $ty:tt, $field_type: expr) => {
impl Serializer for $ty {
- fn write(&self, context: &mut WriteContext, is_field: bool) {
+ fn write(&self, context: &mut WriteContext, _is_field: bool) {
+ context.writer.$name(*self);
+ }
+
+ fn write_type_info(context: &mut WriteContext, is_field: bool) {
if *context.get_fory().get_mode() == crate::types::Mode::Compatible && !is_field {
context.writer.var_uint32($field_type as u32);
}
- context.writer.$name(*self);
}
- fn read(context: &mut ReadContext, is_field: bool) -> Result<Self, Error> {
+ fn read(context: &mut ReadContext) -> Result<Self, Error> {
+ Ok(context.reader.$name())
+ }
+
+ fn read_type_info(context: &mut ReadContext, is_field: bool) {
if *context.get_fory().get_mode() == crate::types::Mode::Compatible && !is_field {
let remote_type_id = context.reader.var_uint32();
assert_eq!(remote_type_id, $field_type as u32);
}
- Ok(context.reader.$name())
}
fn reserved_space() -> usize {
diff --git a/rust/fory-core/src/serializer/option.rs b/rust/fory-core/src/serializer/option.rs
index 8a1129d0c..488682dfd 100644
--- a/rust/fory-core/src/serializer/option.rs
+++ b/rust/fory-core/src/serializer/option.rs
@@ -20,21 +20,18 @@ use crate::fory::Fory;
use crate::resolver::context::ReadContext;
use crate::resolver::context::WriteContext;
use crate::serializer::Serializer;
-use crate::types::{ForyGeneralList, Mode, TypeId};
+use crate::types::ForyGeneralList;
impl<T: Serializer> Serializer for Option<T> {
- fn read(context: &mut ReadContext, is_field: bool) -> Result<Self, Error> {
- if *context.get_fory().get_mode() == Mode::Compatible && !is_field {
- let remote_type_id = context.reader.var_uint32();
- assert_eq!(remote_type_id, T::get_type_id(context.fory));
- }
- Ok(Some(T::read(context, is_field)?))
+ fn read(context: &mut ReadContext) -> Result<Self, Error> {
+ Ok(Some(T::read(context)?))
+ }
+
+ fn read_type_info(context: &mut ReadContext, is_field: bool) {
+ T::read_type_info(context, is_field);
}
fn write(&self, context: &mut WriteContext, is_field: bool) {
- if *context.get_fory().get_mode() == Mode::Compatible && !is_field {
- context.writer.var_uint32(TypeId::BOOL as u32);
- }
if let Some(v) = self {
T::write(v, context, is_field)
} else {
@@ -42,6 +39,10 @@ impl<T: Serializer> Serializer for Option<T> {
}
}
+ fn write_type_info(context: &mut WriteContext, is_field: bool) {
+ T::write_type_info(context, is_field);
+ }
+
fn reserved_space() -> usize {
std::mem::size_of::<T>()
}
diff --git a/rust/fory-core/src/serializer/primitive_list.rs b/rust/fory-core/src/serializer/primitive_list.rs
index 4abea1a53..bfabd7e9a 100644
--- a/rust/fory-core/src/serializer/primitive_list.rs
+++ b/rust/fory-core/src/serializer/primitive_list.rs
@@ -25,10 +25,7 @@ use crate::types::TypeId;
macro_rules! impl_primitive_vec {
($name:ident, $ty:ty, $field_type:expr) => {
impl Serializer for Vec<$ty> {
- fn write(&self, context: &mut WriteContext, is_field: bool) {
- if *context.get_fory().get_mode() == crate::types::Mode::Compatible && !is_field {
- context.writer.var_uint32($field_type as u32);
- }
+ fn write(&self, context: &mut WriteContext, _is_field: bool) {
let len_bytes = self.len() * std::mem::size_of::<$ty>();
context.writer.var_uint32(len_bytes as u32);
context.writer.reserve(len_bytes);
@@ -42,31 +39,35 @@ macro_rules! impl_primitive_vec {
}
}
- fn read(context: &mut ReadContext, is_field: bool) -> Result<Self, Error> {
+ fn write_type_info(context: &mut WriteContext, is_field: bool) {
if *context.get_fory().get_mode() == crate::types::Mode::Compatible && !is_field {
- let remote_type_id = context.reader.var_uint32();
- assert_eq!(remote_type_id, $field_type as u32);
+ context.writer.var_uint32($field_type as u32);
}
- let size_bytes = context.reader.var_uint32() as usize;
+ }
+ fn read(context: &mut ReadContext) -> Result<Self, Error> {
+ let size_bytes = context.reader.var_uint32() as usize;
if size_bytes % std::mem::size_of::<$ty>() != 0 {
panic!("Invalid data length");
}
-
let len = size_bytes / std::mem::size_of::<$ty>();
-
let mut vec: Vec<$ty> = Vec::with_capacity(len);
-
unsafe {
let dst_ptr = vec.as_mut_ptr() as *mut u8;
let src = context.reader.bytes(size_bytes);
std::ptr::copy_nonoverlapping(src.as_ptr(), dst_ptr, size_bytes);
vec.set_len(len);
}
-
Ok(vec)
}
+ fn read_type_info(context: &mut ReadContext, is_field: bool) {
+ if *context.get_fory().get_mode() == crate::types::Mode::Compatible && !is_field {
+ let remote_type_id = context.reader.var_uint32();
+ assert_eq!(remote_type_id, $field_type as u32);
+ }
+ }
+
fn reserved_space() -> usize {
std::mem::size_of::<$ty>()
}
diff --git a/rust/fory-core/src/serializer/set.rs b/rust/fory-core/src/serializer/set.rs
index 8b2949fd5..a9ba775bd 100644
--- a/rust/fory-core/src/serializer/set.rs
+++ b/rust/fory-core/src/serializer/set.rs
@@ -19,7 +19,9 @@ use crate::error::Error;
use crate::fory::Fory;
use crate::resolver::context::ReadContext;
use crate::resolver::context::WriteContext;
-use crate::serializer::collection::{read_collection, write_collection};
+use crate::serializer::collection::{
+ read_collection, read_collection_type_info, write_collection, write_collection_type_info,
+};
use crate::serializer::Serializer;
use crate::types::{ForyGeneralList, TypeId};
use std::collections::HashSet;
@@ -27,11 +29,19 @@ use std::mem;
impl<T: Serializer + Eq + std::hash::Hash> Serializer for HashSet<T> {
fn write(&self, context: &mut WriteContext, is_field: bool) {
- write_collection(self.iter(), context, is_field, TypeId::SET as u32);
+ write_collection(self, context, is_field);
}
- fn read(context: &mut ReadContext, is_field: bool) -> Result<Self, Error> {
- read_collection(context, is_field, TypeId::SET as u32)
+ fn write_type_info(context: &mut WriteContext, is_field: bool) {
+ write_collection_type_info(context, is_field, TypeId::SET as u32);
+ }
+
+ fn read(context: &mut ReadContext) -> Result<Self, Error> {
+ read_collection(context)
+ }
+
+ fn read_type_info(context: &mut ReadContext, is_field: bool) {
+ read_collection_type_info(context, is_field, TypeId::SET as u32)
}
fn reserved_space() -> usize {
diff --git a/rust/fory-core/src/serializer/skip.rs b/rust/fory-core/src/serializer/skip.rs
index 0972afcb8..ca09fee26 100644
--- a/rust/fory-core/src/serializer/skip.rs
+++ b/rust/fory-core/src/serializer/skip.rs
@@ -18,16 +18,22 @@
use crate::error::Error;
use crate::meta::NullableFieldType;
use crate::resolver::context::ReadContext;
-use crate::serializer::collection::HAS_NULL;
+use crate::serializer::collection::{HAS_NULL, IS_SAME_TYPE};
use crate::serializer::Serializer;
-use crate::types::{RefFlag, TypeId, BASIC_TYPES, CONTAINER_TYPES};
+use crate::types::{RefFlag, TypeId, BASIC_TYPES, CONTAINER_TYPES, PRIMITIVE_TYPES};
use chrono::{NaiveDate, NaiveDateTime};
+pub fn get_read_ref_flag(field_type: &NullableFieldType) -> bool {
+ let nullable = field_type.nullable;
+ nullable || !PRIMITIVE_TYPES.contains(&field_type.type_id)
+}
+
macro_rules! basic_type_deserialize {
($tid:expr, $context:expr; $(($ty:ty, $id:ident)),+ $(,)?) => {
$(
if $tid == TypeId::$id {
- <$ty>::read($context, true)?;
+ <$ty>::read_type_info($context, true);
+ <$ty>::read($context)?;
return Ok(());
}
)+else {
@@ -45,10 +51,7 @@ pub fn skip_field_value(
) -> Result<(), Error> {
if read_ref_flag {
let ref_flag = context.reader.i8();
- if field_type.nullable
- && ref_flag != (RefFlag::NotNullValue as i8)
- && ref_flag != (RefFlag::RefValue as i8)
- {
+ if field_type.nullable && ref_flag == (RefFlag::Null as i8) {
return Ok(());
}
}
@@ -79,50 +82,102 @@ pub fn skip_field_value(
} else if CONTAINER_TYPES.contains(&type_id) {
if type_id == TypeId::LIST || type_id == TypeId::SET {
let length = context.reader.var_uint32() as usize;
- // todo
+ if length == 0 {
+ return Ok(());
+ }
let header = context.reader.u8();
- let read_ref_flag = (header & HAS_NULL) != 0;
- let _elem_type = context.reader.var_uint32();
+ let has_null = (header & HAS_NULL) != 0;
+ let is_same_type = (header & IS_SAME_TYPE) != 0;
+ let skip_ref_flag = is_same_type && !has_null;
+ let elem_type = field_type.generics.first().unwrap();
for _ in 0..length {
- skip_field_value(
- context,
- field_type.generics.first().unwrap(),
- read_ref_flag,
- )?;
+ skip_field_value(context, elem_type, !skip_ref_flag)?;
}
} else if type_id == TypeId::MAP {
- todo!();
- let length = context.reader.var_uint32() as usize;
- for _ in 0..length {
- skip_field_value(context, field_type.generics.first().unwrap(), true)?;
- skip_field_value(context, field_type.generics.get(1).unwrap(), true)?;
+ let length = context.reader.var_uint32();
+ if length == 0 {
+ return Ok(());
+ }
+ let mut len_counter = 0;
+ let key_type = field_type.generics.first().unwrap();
+ let value_type = field_type.generics.get(1).unwrap();
+ loop {
+ if len_counter == length {
+ break;
+ }
+ let header = context.reader.u8();
+ if header & crate::serializer::map::KEY_NULL != 0
+ && header & crate::serializer::map::VALUE_NULL != 0
+ {
+ len_counter += 1;
+ continue;
+ }
+ if header & crate::serializer::map::KEY_NULL != 0 {
+ // let read_ref_flag = get_read_ref_flag(value_type);
+ skip_field_value(context, value_type, false).unwrap();
+ len_counter += 1;
+ continue;
+ }
+ if header & crate::serializer::map::VALUE_NULL != 0 {
+ // let read_ref_flag = get_read_ref_flag(key_type);
+ skip_field_value(context, key_type, false).unwrap();
+ len_counter += 1;
+ continue;
+ }
+ let chunk_size = context.reader.u8();
+ for _ in (0..chunk_size).enumerate() {
+ // let read_ref_flag = get_read_ref_flag(key_type);
+ skip_field_value(context, key_type, false).unwrap();
+ // let read_ref_flag = get_read_ref_flag(value_type);
+ skip_field_value(context, value_type, false).unwrap();
+ }
+ len_counter += chunk_size as u32;
}
}
Ok(())
+ } else if type_id == TypeId::NAMED_ENUM {
+ let remote_type_id = context.reader.var_uint32();
+ assert_eq!(type_id_num, remote_type_id);
+ let meta_index = context.reader.var_uint32();
+ let type_def = context.get_meta(meta_index as usize);
+ let type_resolver = context.get_fory().get_type_resolver();
+ type_resolver
+ .get_name_harness(
+ &type_def.get_namespace().original,
+ &type_def.get_type_name().original,
+ )
+ .unwrap()
+ .get_deserializer()(context, true, true)?;
+ Ok(())
} else {
- unreachable!()
+ unreachable!();
}
}
Err(_) => {
- let tag = type_id_num & 0xff;
- if tag == TypeId::COMPATIBLE_STRUCT as u32 {
+ let internal_id = type_id_num & 0xff;
+ const COMPATIBLE_STRUCT_ID: u32 = TypeId::COMPATIBLE_STRUCT as u32;
+ const NAMED_COMPATIBLE_STRUCT_ID: u32 = TypeId::NAMED_COMPATIBLE_STRUCT as u32;
+ const ENUM_ID: u32 = TypeId::ENUM as u32;
+ if internal_id == COMPATIBLE_STRUCT_ID || internal_id == NAMED_COMPATIBLE_STRUCT_ID {
let remote_type_id = context.reader.var_uint32();
let meta_index = context.reader.var_uint32();
let type_def = context.get_meta(meta_index as usize);
assert_eq!(remote_type_id, type_def.get_type_id());
- let field_infos: Vec<_> = type_def.get_field_infos().to_vec();
+ let field_infos = type_def.get_field_infos().to_vec();
for field_info in field_infos.iter() {
let nullable_field_type =
NullableFieldType::from(field_info.field_type.clone());
- skip_field_value(context, &nullable_field_type, true)?;
+ let read_ref_flag = get_read_ref_flag(&nullable_field_type);
+ skip_field_value(context, &nullable_field_type, read_ref_flag)?;
}
- } else if tag == TypeId::ENUM as u32 {
- context
- .fory
- .get_type_resolver()
+ } else if internal_id == ENUM_ID {
+ let remote_type_id = context.reader.var_uint32();
+ assert_eq!(type_id_num, remote_type_id);
+ let type_resolver = context.get_fory().get_type_resolver();
+ type_resolver
.get_harness(type_id_num)
.unwrap()
- .get_deserializer()(context)?;
+ .get_deserializer()(context, true, true)?;
} else {
unimplemented!()
}
diff --git a/rust/fory-core/src/serializer/string.rs b/rust/fory-core/src/serializer/string.rs
index bc1bf1592..d92096a46 100644
--- a/rust/fory-core/src/serializer/string.rs
+++ b/rust/fory-core/src/serializer/string.rs
@@ -35,28 +35,42 @@ impl Serializer for String {
mem::size_of::<i32>()
}
- fn write(&self, context: &mut WriteContext, is_field: bool) {
- if *context.get_fory().get_mode() == Mode::Compatible && !is_field {
- context.writer.var_uint32(TypeId::STRING as u32);
- }
+ fn write(&self, context: &mut WriteContext, _is_field: bool) {
let mut len = get_latin1_length(self);
if len >= 0 {
let bitor = (len as u64) << 2 | StrEncoding::Latin1 as u64;
context.writer.var_uint36_small(bitor);
context.writer.latin1_string(self);
- } else {
+ } else if context.get_fory().is_compress_string() {
+ // todo: support `writeNumUtf16BytesForUtf8Encoding` like in java
len = self.len() as i32;
let bitor = (len as u64) << 2 | StrEncoding::Utf8 as u64;
context.writer.var_uint36_small(bitor);
context.writer.utf8_string(self);
+ } else {
+ let utf16: Vec<u16> = self.encode_utf16().collect();
+ let bitor = (utf16.len() as u64 * 2) << 2 | StrEncoding::Utf16 as u64;
+ context.writer.var_uint36_small(bitor);
+ for unit in utf16 {
+ #[cfg(target_endian = "little")]
+ {
+ context.writer.u16(unit);
+ }
+ #[cfg(target_endian = "big")]
+ {
+ unimplemented!()
+ }
+ }
}
}
- fn read(context: &mut ReadContext, is_field: bool) -> Result<Self, Error> {
+ fn write_type_info(context: &mut WriteContext, is_field: bool) {
if *context.get_fory().get_mode() == Mode::Compatible && !is_field {
- let remote_type_id = context.reader.var_uint32();
- assert_eq!(remote_type_id, TypeId::STRING as u32);
+ context.writer.var_uint32(TypeId::STRING as u32);
}
+ }
+
+ fn read(context: &mut ReadContext) -> Result<Self, Error> {
let bitor = context.reader.var_uint36_small();
let len = bitor >> 2;
let encoding = bitor & 0b11;
@@ -76,6 +90,13 @@ impl Serializer for String {
Ok(s)
}
+ fn read_type_info(context: &mut ReadContext, is_field: bool) {
+ if *context.get_fory().get_mode() == Mode::Compatible && !is_field {
+ let remote_type_id = context.reader.var_uint32();
+ assert_eq!(remote_type_id, TypeId::STRING as u32);
+ }
+ }
+
fn get_type_id(_fory: &Fory) -> u32 {
TypeId::STRING as u32
}
diff --git a/rust/fory-core/src/types.rs b/rust/fory-core/src/types.rs
index 39a395296..389395327 100644
--- a/rust/fory-core/src/types.rs
+++ b/rust/fory-core/src/types.rs
@@ -59,7 +59,7 @@ pub enum TypeId {
NAMED_ENUM = 14,
STRUCT = 15,
COMPATIBLE_STRUCT = 16,
- NamedStruct = 17,
+ NAMED_STRUCT = 17,
NAMED_COMPATIBLE_STRUCT = 18,
EXT = 19,
NAMED_EXT = 20,
@@ -126,6 +126,16 @@ pub static BASIC_TYPES: [TypeId; 18] = [
TypeId::FLOAT64_ARRAY,
];
+pub static PRIMITIVE_TYPES: [u32; 7] = [
+ TypeId::BOOL as u32,
+ TypeId::INT8 as u32,
+ TypeId::INT16 as u32,
+ TypeId::INT32 as u32,
+ TypeId::INT64 as u32,
+ TypeId::FLOAT32 as u32,
+ TypeId::FLOAT64 as u32,
+];
+
pub static FINAL_TYPES: [TypeId; 3] = [TypeId::STRING, TypeId::LOCAL_DATE, TypeId::TIMESTAMP];
pub static PRIMITIVE_ARRAY_TYPES: [TypeId; 8] = [
diff --git a/rust/fory-derive/src/object/derive_enum.rs b/rust/fory-derive/src/object/derive_enum.rs
index 6c9efa703..7adf1c661 100644
--- a/rust/fory-derive/src/object/derive_enum.rs
+++ b/rust/fory-derive/src/object/derive_enum.rs
@@ -21,7 +21,16 @@ use syn::DataEnum;
pub fn gen_type_def(_data_enum: &DataEnum) -> TokenStream {
quote! {
- Vec::new()
+ let namespace_metastring = fory_core::meta::NAMESPACE_ENCODER.encode_with_encodings(namespace, fory_core::meta::NAMESPACE_ENCODINGS).unwrap();
+ let type_name_metastring = fory_core::meta::TYPE_NAME_ENCODER.encode_with_encodings(type_name, fory_core::meta::TYPE_NAME_ENCODINGS).unwrap();
+ let meta = fory_core::meta::TypeMeta::from_fields(
+ type_id,
+ namespace_metastring,
+ type_name_metastring,
+ register_by_name,
+ vec![],
+ );
+ meta.to_bytes().unwrap()
}
}
@@ -30,11 +39,13 @@ pub fn gen_write(data_enum: &DataEnum) -> TokenStream {
let variant_values: Vec<_> = (0..variant_idents.len()).map(|v| v as u32).collect();
quote! {
- fn write(&self, context: &mut fory_core::resolver::context::WriteContext, is_field: bool) {
- if !is_field {
- let type_id = Self::get_type_id(context.get_fory());
- context.writer.var_uint32(type_id);
- }
+ fn serialize(&self, context: &mut fory_core::resolver::context::WriteContext, is_field: bool) {
+ context.writer.i8(fory_core::types::RefFlag::NotNullValue as i8);
+ Self::write_type_info(context, is_field);
+ self.write(context, is_field);
+ }
+
+ fn write(&self, context: &mut fory_core::resolver::context::WriteContext, _is_field: bool) {
match self {
#(
Self::#variant_idents => {
@@ -44,6 +55,19 @@ pub fn gen_write(data_enum: &DataEnum) -> TokenStream {
}
}
+ fn write_type_info(context: &mut fory_core::resolver::context::WriteContext, is_field: bool){
+ if *context.get_fory().get_mode() == fory_core::types::Mode::Compatible {
+ let type_id = Self::get_type_id(context.get_fory());
+ context.writer.var_uint32(type_id);
+ if type_id & 0xff == fory_core::types::TypeId::NAMED_ENUM as u32 {
+ let meta_index = context.push_meta(
+ std::any::TypeId::of::<Self>()
+ ) as u32;
+ context.writer.var_uint32(meta_index);
+ }
+ }
+ }
+
fn reserved_space() -> usize {
4
}
@@ -55,15 +79,21 @@ pub fn gen_read(data_enum: &DataEnum) -> TokenStream {
let variant_values: Vec<_> = (0..variant_idents.len()).map(|v| v as u32).collect();
quote! {
+ fn deserialize(context: &mut fory_core::resolver::context::ReadContext, _is_field: bool) -> Result<Self, fory_core::error::Error> {
+ let ref_flag = context.reader.i8();
+ if ref_flag == fory_core::types::RefFlag::Null as i8 {
+ Ok(Self::default())
+ } else if ref_flag == (fory_core::types::RefFlag::NotNullValue as i8) {
+ Self::read_type_info(context, false);
+ Self::read(context)
+ } else {
+ unimplemented!()
+ }
+ }
+
fn read(
context: &mut fory_core::resolver::context::ReadContext,
- is_field: bool
) -> Result<Self, fory_core::error::Error> {
- if !is_field {
- let remote_type_id = context.reader.var_uint32();
- let local_type_id = Self::get_type_id(context.get_fory());
- assert_eq!(remote_type_id, local_type_id);
- }
let ordinal = context.reader.var_uint32();
match ordinal {
#(
@@ -72,19 +102,35 @@ pub fn gen_read(data_enum: &DataEnum) -> TokenStream {
_ => panic!("unknown value"),
}
}
+
+ fn read_type_info(context: &mut fory_core::resolver::context::ReadContext, is_field: bool) {
+ if *context.get_fory().get_mode() == fory_core::types::Mode::Compatible {
+ let local_type_id = Self::get_type_id(context.get_fory());
+ let remote_type_id = context.reader.var_uint32();
+ assert_eq!(local_type_id, remote_type_id);
+ if local_type_id & 0xff == fory_core::types::TypeId::NAMED_ENUM as u32 {
+ let _meta_index = context.reader.var_uint32();
+ }
+ }
+ }
}
}
pub fn gen_actual_type_id() -> TokenStream {
quote! {
- (type_id << 8) + fory_core::types::TypeId::ENUM as u32
+ if register_by_name {
+ fory_core::types::TypeId::NAMED_ENUM as u32
+ } else {
+ (type_id << 8) + fory_core::types::TypeId::ENUM as u32
+ }
}
}
pub fn gen_read_compatible(_data_enum: &DataEnum) -> TokenStream {
quote! {
fn read_compatible(context: &mut fory_core::resolver::context::ReadContext) -> Result<Self, fory_core::error::Error> {
- <Self as fory_core::serializer::Serializer>::read(context, true)
+ <Self as fory_core::serializer::Serializer>::read_type_info(context, true);
+ <Self as fory_core::serializer::Serializer>::read(context)
}
}
}
diff --git a/rust/fory-derive/src/object/misc.rs b/rust/fory-derive/src/object/misc.rs
index ec7eac6bf..a23afce4a 100644
--- a/rust/fory-derive/src/object/misc.rs
+++ b/rust/fory-derive/src/object/misc.rs
@@ -66,7 +66,7 @@ fn type_def(fields: &[&Field]) -> TokenStream {
});
quote! {
let sorted_field_names = <Self as fory_core::serializer::StructSerializer>::get_sorted_field_names(fory);
- let field_infos = vec![#(#field_infos),*];
+ let field_infos: Vec<fory_core::meta::FieldInfo> = vec![#(#field_infos),*];
let mut sorted_field_infos = Vec::with_capacity(field_infos.len());
for name in &sorted_field_names {
if let Some(info) = field_infos.iter().find(|f| &f.field_name == name) {
@@ -75,10 +75,12 @@ fn type_def(fields: &[&Field]) -> TokenStream {
panic!("Field {} not found in field_infos", name);
}
}
+ let namespace_metastring = fory_core::meta::NAMESPACE_ENCODER.encode_with_encodings(namespace, fory_core::meta::NAMESPACE_ENCODINGS).unwrap();
+ let type_name_metastring = fory_core::meta::TYPE_NAME_ENCODER.encode_with_encodings(type_name, fory_core::meta::TYPE_NAME_ENCODINGS).unwrap();
let meta = fory_core::meta::TypeMeta::from_fields(
type_id,
- namespace,
- type_name,
+ namespace_metastring,
+ type_name_metastring,
register_by_name,
sorted_field_infos,
);
@@ -97,7 +99,20 @@ pub fn gen_in_struct_impl(fields: &[&Field]) -> TokenStream {
pub fn gen_actual_type_id() -> TokenStream {
quote! {
- (type_id << 8) + fory_core::types::TypeId::COMPATIBLE_STRUCT as u32
+ if mode == &fory_core::types::Mode::Compatible {
+ if register_by_name {
+ fory_core::types::TypeId::NAMED_COMPATIBLE_STRUCT as u32
+ } else {
+ (type_id << 8) + fory_core::types::TypeId::COMPATIBLE_STRUCT as u32
+ }
+ } else {
+ if register_by_name {
+ fory_core::types::TypeId::NAMED_STRUCT as u32
+ } else {
+ (type_id << 8) + fory_core::types::TypeId::STRUCT as u32
+ }
+ }
+
}
}
diff --git a/rust/fory-derive/src/object/read.rs b/rust/fory-derive/src/object/read.rs
index 0a1833aba..fae9ad747 100644
--- a/rust/fory-derive/src/object/read.rs
+++ b/rust/fory-derive/src/object/read.rs
@@ -67,7 +67,9 @@ fn read(fields: &[&Field]) -> TokenStream {
.iter()
.map(|f| create_private_field_name(f))
.collect();
- let sorted_deserialize = {
+ let sorted_deserialize = if fields.is_empty() {
+ quote! {}
+ } else {
let let_decls = fields
.iter()
.zip(private_idents.iter())
@@ -77,20 +79,18 @@ fn read(fields: &[&Field]) -> TokenStream {
let mut #private_ident: #ty = Default::default();
}
});
-
let match_ts = fields.iter().zip(private_idents.iter()).map(|(field, private_ident)| {
let ty = &field.ty;
let name_str = field.ident.as_ref().unwrap().to_string();
quote! {
#name_str => {
- #private_ident = <#ty as fory_core::serializer::Serializer>::deserialize(context, true)?;
+ let skip_ref_flag = fory_core::serializer::get_skip_ref_flag::<#ty>(context.get_fory());
+ #private_ident = fory_core::serializer::read_data::<#ty>(context, true, skip_ref_flag, false)?;
}
}
});
-
quote! {
#(#let_decls)*
-
let sorted_field_names = <Self as fory_core::serializer::StructSerializer>::get_sorted_field_names(context.get_fory());
for field_name in sorted_field_names {
match field_name.as_str() {
@@ -110,19 +110,22 @@ fn read(fields: &[&Field]) -> TokenStream {
}
});
quote! {
- fn read(context: &mut fory_core::resolver::context::ReadContext, _is_field: bool) -> Result<Self, fory_core::error::Error> {
+ fn read(context: &mut fory_core::resolver::context::ReadContext) -> Result<Self, fory_core::error::Error> {
+ #sorted_deserialize
+ Ok(Self {
+ #(#field_idents),*
+ })
+ // Ok(Self {
+ // #(#assign_stmt),*
+ // })
+ }
+
+ fn read_type_info(context: &mut fory_core::resolver::context::ReadContext, _is_field: bool) {
let remote_type_id = context.reader.var_uint32();
assert_eq!(remote_type_id, Self::get_type_id(context.get_fory()));
if *context.get_fory().get_mode() == fory_core::types::Mode::Compatible {
let _meta_index = context.reader.var_uint32();
}
- // Ok(Self {
- // #(#assign_stmt),*
- // })
- #sorted_deserialize
- Ok(Self {
- #(#field_idents),*
- })
}
}
}
@@ -151,7 +154,7 @@ pub fn gen_deserialize_nullable(fields: &[&Field]) -> TokenStream {
let ty = &field.ty;
let generic_tree = parse_generic_tree(ty);
let nullable_generic_tree = NullableTypeNode::from(generic_tree);
- let deserialize_tokens = nullable_generic_tree.to_deserialize_tokens(&vec![]);
+ let deserialize_tokens = nullable_generic_tree.to_deserialize_tokens(&vec![], true);
quote! {
fn #fn_name(
context: &mut fory_core::resolver::context::ReadContext,
@@ -191,7 +194,8 @@ pub fn gen_read_compatible(fields: &[&Field], struct_ident: &Ident) -> TokenStre
if _field.field_name.as_str() == #field_name_str {
let local_field_type = #generic_token;
if &_field.field_type == &local_field_type {
- #var_name = Some(<#ty as fory_core::serializer::Serializer>::deserialize(context, true).unwrap_or_else(|_err| {
+ let skip_ref_flag = fory_core::serializer::get_skip_ref_flag::<#ty>(context.get_fory());
+ #var_name = Some(fory_core::serializer::read_data::<#ty>(context, true, skip_ref_flag, false).unwrap_or_else(|_err| {
// same type, err means something wrong
panic!("Err at deserializing {:?}: {:?}", #field_name_str, _err);
}));
@@ -201,7 +205,8 @@ pub fn gen_read_compatible(fields: &[&Field], struct_ident: &Ident) -> TokenStre
if local_nullable_type != remote_nullable_type {
// set default and skip bytes
println!("Type not match, just skip: {}", #field_name_str);
- fory_core::serializer::skip::skip_field_value(context, &remote_nullable_type, true).unwrap();
+ let read_ref_flag = fory_core::serializer::skip::get_read_ref_flag(&remote_nullable_type);
+ fory_core::serializer::skip::skip_field_value(context, &remote_nullable_type, read_ref_flag).unwrap();
#var_name = Some(#base_ty::default());
} else {
println!("Try to deserialize: {}", #field_name_str);
@@ -236,7 +241,8 @@ pub fn gen_read_compatible(fields: &[&Field], struct_ident: &Ident) -> TokenStre
#(#pattern_items else)* {
println!("skip {:?}:{:?}", _field.field_name.as_str(), _field.field_type);
let nullable_field_type = fory_core::meta::NullableFieldType::from(_field.field_type.clone());
- fory_core::serializer::skip::skip_field_value(context, &nullable_field_type, true).unwrap();
+ let read_ref_flag = fory_core::serializer::skip::get_read_ref_flag(&nullable_field_type);
+ fory_core::serializer::skip::skip_field_value(context, &nullable_field_type, read_ref_flag).unwrap();
}
}
Ok(Self {
@@ -251,10 +257,18 @@ pub fn gen(fields: &[&Field], struct_ident: &Ident) -> TokenStream {
let compatible_token_stream = deserialize_compatible(struct_ident);
quote! {
- fn deserialize(context: &mut fory_core::resolver::context::ReadContext, is_field: bool) -> Result<Self, fory_core::error::Error> {
+ fn deserialize(context: &mut fory_core::resolver::context::ReadContext, _is_field: bool) -> Result<Self, fory_core::error::Error> {
match context.get_fory().get_mode() {
fory_core::types::Mode::SchemaConsistent => {
- fory_core::serializer::deserialize::<Self>(context, is_field)
+ let ref_flag = context.reader.i8();
+ if ref_flag == fory_core::types::RefFlag::Null as i8 {
+ Ok(Self::default())
+ } else if ref_flag == (fory_core::types::RefFlag::NotNullValue as i8) {
+ Self::read_type_info(context, false);
+ Self::read(context)
+ } else {
+ unimplemented!()
+ }
},
fory_core::types::Mode::Compatible => {
#compatible_token_stream
diff --git a/rust/fory-derive/src/object/serializer.rs b/rust/fory-derive/src/object/serializer.rs
index fc5e7497d..4a8c42713 100644
--- a/rust/fory-derive/src/object/serializer.rs
+++ b/rust/fory-derive/src/object/serializer.rs
@@ -66,10 +66,10 @@ pub fn derive_serializer(ast: &syn::DeriveInput) -> TokenStream {
let gen = quote! {
impl fory_core::serializer::StructSerializer for #name {
- fn type_def(fory: &fory_core::fory::Fory, type_id: u32, namespace: Vec<u8>, type_name: Vec<u8>, register_by_name: bool) -> Vec<u8> {
+ fn type_def(fory: &fory_core::fory::Fory, type_id: u32, namespace: &str, type_name: &str, register_by_name: bool) -> Vec<u8> {
#type_def_token_stream
}
- fn actual_type_id(type_id: u32) -> u32 {
+ fn actual_type_id(type_id: u32, register_by_name: bool, mode: &fory_core::types::Mode) -> u32 {
#actual_type_id_token_stream
}
#type_index_token_stream
diff --git a/rust/fory-derive/src/object/util.rs b/rust/fory-derive/src/object/util.rs
index 2491600ee..b511b6728 100644
--- a/rust/fory-derive/src/object/util.rs
+++ b/rust/fory-derive/src/object/util.rs
@@ -41,22 +41,16 @@ macro_rules! basic_type_deserialize {
$ty_str => {
if $nullable {
quote! {
- let res1 = if cur_remote_nullable_type.nullable && ref_flag == (fory_core::types::RefFlag::Null as i8) {
- None
- } else {
- Some(<$ty as fory_core::serializer::Serializer>::read(context, true)
- .map_err(fory_core::error::Error::from)?)
- };
+ <$ty as fory_core::serializer::Serializer>::read_type_info(context, true);
+ let res1 = Some(<$ty as fory_core::serializer::Serializer>::read(context)
+ .map_err(fory_core::error::Error::from)?);
Ok::<Option<$ty>, fory_core::error::Error>(res1)
}
} else {
quote! {
- let res2 = if cur_remote_nullable_type.nullable && ref_flag == (fory_core::types::RefFlag::Null as i8) {
- $ty::default()
- } else {
- <$ty as fory_core::serializer::Serializer>::read(context, true)
- .map_err(fory_core::error::Error::from)?
- };
+ <$ty as fory_core::serializer::Serializer>::read_type_info(context, true);
+ let res2 = <$ty as fory_core::serializer::Serializer>::read(context)
+ .map_err(fory_core::error::Error::from)?;
Ok::<$ty, fory_core::error::Error>(res2)
}
}
@@ -114,7 +108,11 @@ pub fn try_primitive_vec_type_name(node: &NullableTypeNode) -> Option<String> {
}
impl NullableTypeNode {
- pub(super) fn to_deserialize_tokens(&self, generic_path: &Vec<i8>) -> TokenStream {
+ pub(super) fn to_deserialize_tokens(
+ &self,
+ generic_path: &Vec<i8>,
+ read_ref_flag: bool,
+ ) -> TokenStream {
let tokens = if let Some(primitive_ty_name) = try_primitive_vec_type_name(self) {
let ty_type: Type = parse_str(&primitive_ty_name).expect("Invalid primitive type name");
let nullable = self.nullable;
@@ -123,7 +121,8 @@ impl NullableTypeNode {
let res1 = if cur_remote_nullable_type.nullable && ref_flag == (fory_core::types::RefFlag::Null as i8) {
None
} else {
- Some(<#ty_type as fory_core::serializer::Serializer>::read(context, true)
+ <#ty_type as fory_core::serializer::Serializer>::read_type_info(context, true);
+ Some(<#ty_type as fory_core::serializer::Serializer>::read(context)
.map_err(fory_core::error::Error::from)?)
};
Ok::<Option<#ty_type>, fory_core::error::Error>(res1)
@@ -133,7 +132,8 @@ impl NullableTypeNode {
let res2 = if cur_remote_nullable_type.nullable && ref_flag == (fory_core::types::RefFlag::Null as i8) {
Vec::default()
} else {
- <#ty_type as fory_core::serializer::Serializer>::read(context, true)
+ <#ty_type as fory_core::serializer::Serializer>::read_type_info(context, true);
+ <#ty_type as fory_core::serializer::Serializer>::read(context)
.map_err(fory_core::error::Error::from)?
};
Ok::<#ty_type, fory_core::error::Error>(res2)
@@ -158,12 +158,41 @@ impl NullableTypeNode {
match self.name.as_str() {
"Vec" => {
new_path.push(0);
+ let generic_node = self.generics.first().unwrap();
+ let element_tokens = generic_node.to_deserialize_tokens(&new_path, false);
+ let element_ty: Type = parse_str(&generic_node.to_string()).unwrap();
+ let vec_ts = quote! {
+ let length = context.reader.var_uint32() as usize;
+ if length == 0 {
+ Vec::default()
+ } else {
+ let mut v = Vec::with_capacity(length);
+ let header = context.reader.u8();
+ let has_null = (header & fory_core::serializer::collection::HAS_NULL) != 0;
+ let is_same_type = (header & fory_core::serializer::collection::IS_SAME_TYPE) != 0;
+ let read_ref_flag = !(is_same_type && !has_null);
+ for _ in 0..length {
+ let ref_flag = if read_ref_flag {
+ context.reader.i8()
+ } else {
+ fory_core::types::RefFlag::NotNullValue as i8
+ };
+ let element = if ref_flag == fory_core::types::RefFlag::Null as i8 {
+ <#element_ty>::default()
+ } else {
+ #element_tokens?
+ };
+ v.push(element);
+ }
+ v
+ }
+ };
if self.nullable {
quote! {
let v = if cur_remote_nullable_type.nullable && ref_flag == (fory_core::types::RefFlag::Null as i8) {
None
} else {
- Some(fory_core::serializer::collection::read_collection(context, true, fory_core::types::TypeId::LIST as u32)?)
+ Some({#vec_ts})
};
Ok::<#ty, fory_core::error::Error>(v)
}
@@ -172,7 +201,7 @@ impl NullableTypeNode {
let v = if cur_remote_nullable_type.nullable && ref_flag == (fory_core::types::RefFlag::Null as i8) {
Vec::default()
} else {
- fory_core::serializer::collection::read_collection(context, true, fory_core::types::TypeId::LIST as u32)?
+ #vec_ts
};
Ok::<#ty, fory_core::error::Error>(v)
}
@@ -180,12 +209,41 @@ impl NullableTypeNode {
}
"HashSet" => {
new_path.push(0);
+ let generic_node = self.generics.first().unwrap();
+ let element_tokens = generic_node.to_deserialize_tokens(&new_path, false);
+ let element_ty: Type = parse_str(&generic_node.to_string()).unwrap();
+ let set_ts = quote! {
+ let length = context.reader.var_uint32() as usize;
+ if length == 0 {
+ HashSet::default()
+ } else {
+ let mut s = HashSet::with_capacity(length);
+ let header = context.reader.u8();
+ let has_null = (header & fory_core::serializer::collection::HAS_NULL) != 0;
+ let is_same_type = (header & fory_core::serializer::collection::IS_SAME_TYPE) != 0;
+ let read_ref_flag = !(is_same_type && !has_null);
+ for _ in 0..length {
+ let ref_flag = if read_ref_flag {
+ context.reader.i8()
+ } else {
+ fory_core::types::RefFlag::NotNullValue as i8
+ };
+ let element = if ref_flag == fory_core::types::RefFlag::Null as i8 {
+ <#element_ty>::default()
+ } else {
+ #element_tokens?
+ };
+ s.insert(element);
+ }
+ s
+ }
+ };
if self.nullable {
quote! {
let s = if cur_remote_nullable_type.nullable && ref_flag == (fory_core::types::RefFlag::Null as i8) {
None
} else {
- Some(fory_core::serializer::collection::read_collection(context, true, fory_core::types::TypeId::SET as u32)?)
+ Some({#set_ts})
};
Ok::<#ty, fory_core::error::Error>(s)
}
@@ -194,7 +252,7 @@ impl NullableTypeNode {
let s = if cur_remote_nullable_type.nullable && ref_flag == (fory_core::types::RefFlag::Null as i8) {
HashSet::default()
} else {
- fory_core::serializer::collection::read_collection(context, true, fory_core::types::TypeId::SET as u32)?
+ #set_ts
};
Ok::<#ty, fory_core::error::Error>(s)
}
@@ -203,17 +261,64 @@ impl NullableTypeNode {
"HashMap" => {
let key_generic_node = self.generics.first().unwrap();
let val_generic_node = self.generics.get(1).unwrap();
+
new_path.push(0);
+ let key_tokens = key_generic_node.to_deserialize_tokens(&new_path, false);
new_path.pop();
new_path.push(1);
+ let val_tokens = val_generic_node.to_deserialize_tokens(&new_path, false);
+
let key_ty: Type = parse_str(&key_generic_node.to_string()).unwrap();
let val_ty: Type = parse_str(&val_generic_node.to_string()).unwrap();
+
+ let map_ts = quote! {
+ let length = context.reader.var_uint32();
+ let mut map = HashMap::with_capacity(length as usize);
+ if length == 0 {
+ map
+ } else {
+ let mut len_counter = 0;
+ loop {
+ if len_counter == length {
+ break;
+ }
+ let header = context.reader.u8();
+ if header & fory_core::serializer::map::KEY_NULL != 0 && header & fory_core::serializer::map::VALUE_NULL != 0 {
+ map.insert(<#key_ty>::default(), <#val_ty>::default());
+ len_counter += 1;
+ continue;
+ }
+ if header & fory_core::serializer::map::KEY_NULL != 0 {
+ let value: #val_ty = {#val_tokens}?;
+ map.insert(<#key_ty>::default(), value);
+ len_counter += 1;
+ continue;
+ }
+ if header & fory_core::serializer::map::VALUE_NULL != 0 {
+ let key: #key_ty = {#key_tokens}?;
+ map.insert(key, <#val_ty>::default());
+ len_counter += 1;
+ continue;
+ }
+ let chunk_size = context.reader.u8();
+ <#key_ty as fory_core::serializer::Serializer>::read_type_info(context, true);
+ <#val_ty as fory_core::serializer::Serializer>::read_type_info(context, true);
+ for _ in (0..chunk_size).enumerate() {
+ let key: #key_ty = {#key_tokens}?;
+ let value: #val_ty = {#val_tokens}?;
+ map.insert(key, value);
+ }
+ len_counter += chunk_size as u32;
+ }
+ map
+ }
+ };
if self.nullable {
quote! {
let m = if cur_remote_nullable_type.nullable && ref_flag == (fory_core::types::RefFlag::Null as i8) {
None
} else {
- Some(<HashMap<#key_ty, #val_ty> as fory_core::serializer::Serializer>::read(context, true)?)
+ Some({#map_ts})
};
Ok::<#ty, fory_core::error::Error>(m)
}
@@ -222,7 +327,7 @@ impl NullableTypeNode {
let m = if cur_remote_nullable_type.nullable && ref_flag == (fory_core::types::RefFlag::Null as i8) {
HashMap::default()
} else {
- <HashMap<#key_ty, #val_ty> as fory_core::serializer::Serializer>::read(context, true)?
+ #map_ts
};
Ok::<#ty, fory_core::error::Error>(m)
}
@@ -234,40 +339,51 @@ impl NullableTypeNode {
// struct or enum
let nullable_ty = parse_str::<Type>(&self.nullable_ty_string()).unwrap();
let ty = parse_str::<Type>(&self.to_string()).unwrap();
- if self.nullable {
+ let ts = if self.nullable {
quote! {
- const COMPATIBLE_STRUCT_ID: u32 = fory_core::types::TypeId::COMPATIBLE_STRUCT as u32;
- const ENUM_ID: u32 = fory_core::types::TypeId::ENUM as u32;
let res1 = if cur_remote_nullable_type.nullable && ref_flag == (fory_core::types::RefFlag::Null as i8) {
None
} else {
- Some(<#nullable_ty as fory_core::serializer::StructSerializer>::read_compatible(context).map_err(fory_core::error::Error::from)?)
+ let type_id = cur_remote_nullable_type.type_id;
+ let internal_id = type_id & 0xff;
+ Some(if internal_id == COMPATIBLE_STRUCT_ID || internal_id == NAMED_COMPATIBLE_STRUCT_ID {
+ <#nullable_ty as fory_core::serializer::StructSerializer>::read_compatible(context)
+ .map_err(fory_core::error::Error::from)?
+ } else if internal_id == ENUM_ID || internal_id == NAMED_ENUM_ID {
+ <#nullable_ty as fory_core::serializer::StructSerializer>::read_compatible(context)
+ .map_err(fory_core::error::Error::from)?
+ } else {
+ unimplemented!()
+ })
};
Ok::<#ty, fory_core::error::Error>(res1)
}
} else {
quote! {
- const COMPATIBLE_STRUCT_ID: u32 = fory_core::types::TypeId::COMPATIBLE_STRUCT as u32;
- const ENUM_ID: u32 = fory_core::types::TypeId::ENUM as u32;
let res2 = if cur_remote_nullable_type.nullable && ref_flag == (fory_core::types::RefFlag::Null as i8) {
#ty::default()
} else {
let type_id = cur_remote_nullable_type.type_id;
let internal_id = type_id & 0xff;
- match internal_id {
- COMPATIBLE_STRUCT_ID => {
- <#nullable_ty as fory_core::serializer::StructSerializer>::read_compatible(context)
+ if internal_id == COMPATIBLE_STRUCT_ID || internal_id == NAMED_COMPATIBLE_STRUCT_ID {
+ <#nullable_ty as fory_core::serializer::StructSerializer>::read_compatible(context)
.map_err(fory_core::error::Error::from)?
- }
- ENUM_ID => {
- <#nullable_ty as fory_core::serializer::Serializer>::read(context, true)
+ } else if internal_id == ENUM_ID || internal_id == NAMED_ENUM_ID {
+ <#nullable_ty as fory_core::serializer::StructSerializer>::read_compatible(context)
.map_err(fory_core::error::Error::from)?
- }
- _ => unimplemented!(),
+ } else {
+ unimplemented!("")
}
};
Ok::<#ty, fory_core::error::Error>(res2)
}
+ };
+ quote! {
+ const COMPATIBLE_STRUCT_ID: u32 = fory_core::types::TypeId::COMPATIBLE_STRUCT as u32;
+ const ENUM_ID: u32 = fory_core::types::TypeId::ENUM as u32;
+ const NAMED_COMPATIBLE_STRUCT_ID: u32 = fory_core::types::TypeId::NAMED_COMPATIBLE_STRUCT as u32;
+ const NAMED_ENUM_ID: u32 = fory_core::types::TypeId::NAMED_ENUM as u32;
+ #ts
}
};
let mut cur_remote_nullable_type = quote! { remote_nullable_type };
@@ -276,10 +392,26 @@ impl NullableTypeNode {
#cur_remote_nullable_type.generics.get(#idx as usize).unwrap()
};
}
+ let read_ref_flag_ts = if read_ref_flag {
+ quote! {
+ let read_ref_flag = fory_core::serializer::skip::get_read_ref_flag(cur_remote_nullable_type);
+ let ref_flag = if read_ref_flag {
+ context.reader.i8()
+ } else {
+ fory_core::types::RefFlag::NotNullValue as i8
+ };
+ if ref_flag == fory_core::types::RefFlag::Null as i8 {
+ Ok(Default::default())
+ } else {
+ #tokens
+ }
+ }
+ } else {
+ quote! { #tokens }
+ };
quote! {
let cur_remote_nullable_type = &#cur_remote_nullable_type;
- let ref_flag = context.reader.i8();
- #tokens
+ #read_ref_flag_ts
}
}
@@ -422,7 +554,7 @@ pub(super) fn generic_tree_to_tokens(node: &TypeNode, have_context: bool) -> Tok
let ty: syn::Type = syn::parse_str(&node.to_string()).unwrap();
let param = if have_context {
quote! {
- context.fory
+ context.get_fory()
}
} else {
quote! {
@@ -715,12 +847,12 @@ pub(super) fn get_sort_fields_ts(fields: &[&Field]) -> TokenStream {
quote! {
let field_type_id = <#ty_type as fory_core::serializer::Serializer>::get_type_id(fory);
let internal_id = field_type_id & 0xff;
- if internal_id == fory_core::types::TypeId::COMPATIBLE_STRUCT as u32 {
+ if internal_id == fory_core::types::TypeId::COMPATIBLE_STRUCT as u32 || internal_id == fory_core::types::TypeId::NAMED_COMPATIBLE_STRUCT as u32 || internal_id == fory_core::types::TypeId::STRUCT as u32 || internal_id == fory_core::types::TypeId::NAMED_STRUCT as u32 {
other_fields.push((field_type_id, #name.to_string()));
- } else if internal_id == fory_core::types::TypeId::ENUM as u32 {
+ } else if internal_id == fory_core::types::TypeId::ENUM as u32 || internal_id == fory_core::types::TypeId::NAMED_ENUM as u32 {
final_fields.push((field_type_id, #name.to_string()));
} else {
- unimplemented!();
+ unimplemented!("unknown internal_id when group_sort_enum_other_fields");
}
}
})
diff --git a/rust/fory-derive/src/object/write.rs b/rust/fory-derive/src/object/write.rs
index 4851f420a..59e7f4342 100644
--- a/rust/fory-derive/src/object/write.rs
+++ b/rust/fory-derive/src/object/write.rs
@@ -20,14 +20,17 @@ use quote::quote;
use syn::Field;
pub fn gen(fields: &[&Field]) -> TokenStream {
- let sorted_serialize = {
+ let sorted_serialize = if fields.is_empty() {
+ quote! {}
+ } else {
let match_ts = fields.iter().map(|field| {
let ty = &field.ty;
let ident = &field.ident;
let name_str = ident.as_ref().unwrap().to_string();
quote! {
#name_str => {
- <#ty as fory_core::serializer::Serializer>::serialize(&self.#ident, context, true);
+ let skip_ref_flag = fory_core::serializer::get_skip_ref_flag::<#ty>(context.get_fory());
+ fory_core::serializer::write_data::<#ty>(&self.#ident, context, true, skip_ref_flag, false);
}
}
});
@@ -66,18 +69,29 @@ pub fn gen(fields: &[&Field]) -> TokenStream {
};
quote! {
- fn serialize(&self, context: &mut fory_core::resolver::context::WriteContext, is_field: bool) {
+ fn serialize(&self, context: &mut fory_core::resolver::context::WriteContext, _is_field: bool) {
match context.get_fory().get_mode() {
fory_core::types::Mode::SchemaConsistent => {
- fory_core::serializer::serialize(self, context, is_field);
+ context.writer.i8(fory_core::types::RefFlag::NotNullValue as i8);
+ Self::write_type_info(context, false);
+ self.write(context, true);
},
fory_core::types::Mode::Compatible => {
- fory_core::serializer::serialize(self, context, is_field);
+ context.writer.i8(fory_core::types::RefFlag::NotNullValue as i8);
+ Self::write_type_info(context, false);
+ self.write(context, true);
}
}
}
fn write(&self, context: &mut fory_core::resolver::context::WriteContext, _is_field: bool) {
+ // write way before
+ // #(#accessor_expr)*
+ // sort and write
+ #sorted_serialize
+ }
+
+ fn write_type_info(context: &mut fory_core::resolver::context::WriteContext, _is_field: bool){
let type_id = Self::get_type_id(context.get_fory());
context.writer.var_uint32(type_id);
if *context.get_fory().get_mode() == fory_core::types::Mode::Compatible {
@@ -86,11 +100,6 @@ pub fn gen(fields: &[&Field]) -> TokenStream {
) as u32;
context.writer.var_uint32(meta_index);
}
- // write fields
- // write way before
- // #(#accessor_expr)*
- // sort and write
- #sorted_serialize
}
fn reserved_space() -> usize {
diff --git a/rust/tests/tests/test_compatible.rs b/rust/tests/tests/test_compatible.rs
index 8178d861d..d0e20fa5a 100644
--- a/rust/tests/tests/test_compatible.rs
+++ b/rust/tests/tests/test_compatible.rs
@@ -18,17 +18,16 @@
use fory_core::fory::Fory;
use fory_core::types::Mode::Compatible;
use fory_derive::Fory;
-use std::collections::HashSet;
+use std::collections::{HashMap, HashSet};
// RUSTFLAGS="-Awarnings" cargo expand -p fory-tests --test test_compatible
#[test]
fn simple() {
#[derive(Fory, Debug, Default)]
struct Animal1 {
- // f1: HashMap<i8, Vec<i8>>,
+ f1: HashMap<i8, Vec<i8>>,
f2: String,
f3: Vec<i8>,
- // f4: String,
f5: String,
f6: Vec<i8>,
f7: i8,
@@ -37,8 +36,7 @@ fn simple() {
#[derive(Fory, Debug, Default)]
struct Animal2 {
- // f1: HashMap<i8, Vec<i8>>,
- // f2: String,
+ f1: HashMap<i8, Vec<i8>>,
f3: Vec<i8>,
f4: String,
f5: i8,
@@ -51,7 +49,7 @@ fn simple() {
fory1.register::<Animal1>(999);
fory2.register::<Animal2>(999);
let animal: Animal1 = Animal1 {
- // f1: HashMap::from([(1, vec![2])]),
+ f1: HashMap::from([(1, vec![2])]),
f2: String::from("hello"),
f3: vec![1, 2, 3],
f5: String::from("f5"),
@@ -61,7 +59,7 @@ fn simple() {
};
let bin = fory1.serialize(&animal);
let obj: Animal2 = fory2.deserialize(&bin).unwrap();
- // assert_eq!(animal.f1, obj.f1);
+ assert_eq!(animal.f1, obj.f1);
assert_eq!(animal.f3, obj.f3);
assert_eq!(obj.f4, String::default());
assert_eq!(obj.f5, i8::default());
@@ -230,11 +228,11 @@ fn nullable_container() {
f2: Option<Vec<i8>>,
f3: HashSet<i8>,
f4: Option<HashSet<i8>>,
- // f5: HashMap<i8, Vec<i8>>,
- // f6: Option<HashMap<i8, Vec<i8>>>,
+ f5: HashMap<i8, Vec<i8>>,
+ f6: Option<HashMap<i8, Vec<i8>>>,
f7: Option<Vec<i8>>,
f8: Option<HashSet<i8>>,
- // f9: Option<HashMap<i8, i8>>,
+ f9: Option<HashMap<i8, i8>>,
last: i64,
}
@@ -244,11 +242,11 @@ fn nullable_container() {
f2: Vec<i8>,
f3: Option<HashSet<i8>>,
f4: HashSet<i8>,
- // f5: Option<HashMap<i8, Vec<i8>>>,
- // f6: HashMap<i8, Vec<i8>>,
+ f5: Option<HashMap<i8, Vec<i8>>>,
+ f6: HashMap<i8, Vec<i8>>,
f7: Vec<i8>,
f8: HashSet<i8>,
- // f9: HashMap<i8, i8>,
+ f9: HashMap<i8, i8>,
last: i64,
}
@@ -262,11 +260,11 @@ fn nullable_container() {
f2: Some(vec![43]),
f3: HashSet::from([44, 45]),
f4: Some(HashSet::from([46, 47])),
- // f5: HashMap::from([(48, vec![49])]),
- // f6: Some(HashMap::from([(48, vec![49])])),
+ f5: HashMap::from([(48, vec![49])]),
+ f6: Some(HashMap::from([(48, vec![49])])),
f7: None,
f8: None,
- // f9: None,
+ f9: None,
last: 666,
};
@@ -277,11 +275,11 @@ fn nullable_container() {
assert_eq!(item2.f2, item1.f2.unwrap());
assert_eq!(item2.f3.unwrap(), item1.f3);
assert_eq!(item2.f4, item1.f4.unwrap());
- // assert_eq!(item2.f5.unwrap(), item1.f5);
- // assert_eq!(item2.f6, item1.f6.unwrap());
+ assert_eq!(item2.f5.unwrap(), item1.f5);
+ assert_eq!(item2.f6, item1.f6.unwrap());
assert_eq!(item2.f7, Vec::default());
assert_eq!(item2.f8, HashSet::default());
- // assert_eq!(item2.f9, HashMap::default());
+ assert_eq!(item2.f9, HashMap::default());
assert_eq!(item2.last, item1.last);
}
@@ -291,14 +289,16 @@ fn inner_nullable() {
pub struct Item1 {
f1: Vec<Option<String>>,
f2: HashSet<Option<i8>>,
- // f3: HashMap<i8, Option<i8>>,
+ f3: HashMap<i8, Option<i8>>,
+ last: i64,
}
#[derive(Fory, Debug, Default)]
pub struct Item2 {
f1: Vec<String>,
f2: HashSet<i8>,
- // f3: HashMap<i8, i8>,
+ f3: HashMap<i8, i8>,
+ last: i64,
}
let mut fory1 = Fory::default().mode(Compatible);
let mut fory2 = Fory::default().mode(Compatible);
@@ -308,14 +308,16 @@ fn inner_nullable() {
let item1 = Item1 {
f1: vec![None, Some("hello".to_string())],
f2: HashSet::from([None, Some(43)]),
- // f3: HashMap::from([(44, None), (45, Some(46))]),
+ f3: HashMap::from([(44, None), (45, Some(46))]),
+ last: 666,
};
let bin = fory1.serialize(&item1);
let item2: Item2 = fory2.deserialize(&bin).unwrap();
assert_eq!(item2.f1, vec![String::default(), "hello".to_string()]);
assert_eq!(item2.f2, HashSet::from([0, 43]));
- // assert_eq!(item2.f3, HashMap::from([(44, 0), (45, 46)]));
+ assert_eq!(item2.f3, HashMap::from([(44, 0), (45, 46)]));
+ assert_eq!(item2.last, item1.last);
}
#[test]
@@ -357,8 +359,8 @@ fn nullable_struct() {
},
f2: None,
f3: Some(Item {
- name: "f3".to_string(),
- data: vec![None, Some("hello".to_string())],
+ name: "b".to_string(),
+ data: vec![None, Some("a".to_string())],
last: 45,
}),
last: 46,
@@ -436,7 +438,6 @@ fn enum_without_payload() {
last: 10,
};
let bin = fory1.serialize(&person1);
- println!("bin: {:?}", bin);
let person2: Person2 = fory2.deserialize(&bin).expect("");
assert_eq!(person2.f1, person1.f1);
assert_eq!(person2.f2, Color2::default());
@@ -446,3 +447,145 @@ fn enum_without_payload() {
assert_eq!(person2.f8.unwrap(), person1.f8);
assert_eq!(person2.last, person1.last);
}
+
+#[test]
+fn named_enum() {
+ #[derive(Fory, Debug, PartialEq, Default)]
+ enum Color {
+ #[default]
+ Green,
+ Red,
+ Blue,
+ White,
+ }
+ #[derive(Fory, Debug, PartialEq, Default)]
+ struct Item1 {
+ f1: Color,
+ f2: Color,
+ f3: Option<Color>,
+ f4: Option<Color>,
+ f5: Option<Color>,
+ f6: Option<Color>,
+ // skip
+ f7: Color,
+ f8: Option<Color>,
+ f9: Option<Color>,
+ last: i8,
+ }
+ #[derive(Fory, Debug, PartialEq, Default)]
+ struct Item2 {
+ f1: Color,
+ f2: Option<Color>,
+ f3: Color,
+ f4: Option<Color>,
+ f5: Color,
+ f6: Option<Color>,
+ last: i8,
+ }
+ let mut fory1 = Fory::default().mode(Compatible).xlang(true);
+ fory1.register_by_name::<Color>("a");
+ fory1.register::<Item1>(101);
+ let mut fory2 = Fory::default().mode(Compatible).xlang(true);
+ fory2.register_by_name::<Color>("a");
+ fory2.register::<Item2>(101);
+ let item1 = Item1 {
+ f1: Color::Red,
+ f2: Color::Blue,
+ f3: Some(Color::White),
+ f4: Some(Color::White),
+ f5: None,
+ f6: None,
+ f7: Color::White,
+ f8: Some(Color::White),
+ f9: Some(Color::White),
+ last: 42,
+ };
+ let expected_item2 = Item2 {
+ f1: Color::Red,
+ f2: Some(Color::Blue),
+ f3: Color::White,
+ f4: Some(Color::White),
+ f5: Color::default(),
+ f6: None,
+ last: 42,
+ };
+ let bin = fory1.serialize(&item1);
+ let actual_item2: Item2 = fory2.deserialize(&bin).unwrap();
+ assert_eq!(expected_item2, actual_item2);
+}
+
+#[test]
+#[allow(clippy::unnecessary_literal_unwrap)]
+fn boxed() {
+ #[derive(Fory, Debug, PartialEq, Default)]
+ struct Item1 {
+ f1: i32,
+ f2: i32,
+ f3: Option<i32>,
+ f4: Option<i32>,
+ f5: Option<i32>,
+ f6: Option<i32>,
+ }
+
+ #[derive(Fory, Debug, PartialEq, Default)]
+ struct Item2 {
+ f1: i32,
+ f2: Option<i32>,
+ f3: Option<i32>,
+ f4: i32,
+ f5: i32,
+ f6: Option<i32>,
+ }
+
+ let mut fory1 = Fory::default().mode(Compatible).xlang(true);
+ fory1.register::<Item1>(101);
+ let mut fory2 = Fory::default().mode(Compatible).xlang(true);
+ fory2.register::<Item2>(101);
+
+ let f1 = 1;
+ let f2 = 2;
+ let f3 = Some(3);
+ let f4 = Some(4);
+ let f5: Option<i32> = None;
+ let f6: Option<i32> = None;
+ let item1 = Item1 {
+ f1,
+ f2,
+ f3,
+ f4,
+ f5,
+ f6,
+ };
+ let bytes = fory1.serialize(&item1);
+ let item2: Item2 = fory2.deserialize(&bytes).unwrap();
+ assert_eq!(item2.f1, f1);
+ assert_eq!(item2.f2.unwrap(), f2);
+ assert_eq!(item2.f3, f3);
+ assert_eq!(item2.f4, f4.unwrap());
+ assert_eq!(item2.f5, i32::default());
+ assert_eq!(item2.f6, f6);
+
+ let bytes = fory1.serialize(&f1);
+ let item2_f1: i32 = fory2.deserialize(&bytes).unwrap();
+ assert_eq!(item2.f1, item2_f1);
+
+ let bytes = fory1.serialize(&f2);
+ let item2_f2: Option<i32> = fory2.deserialize(&bytes).unwrap();
+ assert_eq!(item2.f2, item2_f2);
+
+ let bytes = fory1.serialize(&f3);
+ let item2_f3: Option<i32> = fory2.deserialize(&bytes).unwrap();
+ assert_eq!(item2.f3, item2_f3);
+
+ let bytes = fory1.serialize(&f4);
+ let item2_f4: i32 = fory2.deserialize(&bytes).unwrap();
+ assert_eq!(item2.f4, item2_f4);
+
+ let bytes = fory1.serialize(&f5);
+ let item2_f5: i32 = fory2.deserialize(&bytes).unwrap();
+ assert_eq!(item2.f5, item2_f5);
+
+ let bytes = fory1.serialize(&f6);
+ let item2_f6: Option<i32> = fory2.deserialize(&bytes).unwrap();
+ assert_eq!(item2.f6, item2_f6);
+}
diff --git a/rust/tests/tests/test_cross_language.rs b/rust/tests/tests/test_cross_language.rs
index c348f7027..05b7fbd97 100644
--- a/rust/tests/tests/test_cross_language.rs
+++ b/rust/tests/tests/test_cross_language.rs
@@ -48,13 +48,15 @@ struct Item {
#[derive(Fory, Debug, PartialEq, Default)]
struct SimpleStruct {
// field_order != sorted_order
- // f1: HashMap<i32, f64>,
- // f2: i32,
+ f1: HashMap<i32, f64>,
+ f2: i32,
f3: Item,
f4: Option<String>,
- f5: Color,
- // f6: Vec<Option<String>>,
- // last: i32
+ // f5: Color,
+ f6: Vec<Option<String>>,
+ f7: i32,
+ // f8: i32,
+ // last: i32,
}
#[test]
@@ -219,8 +221,17 @@ fn test_string_serializer() {
let data_file_path = get_data_file();
let bytes = fs::read(&data_file_path).unwrap();
let reader = Reader::new(bytes.as_slice());
- let fory = Fory::default().mode(Compatible).xlang(true);
+ let fory = Fory::default()
+ .mode(Compatible)
+ .xlang(true)
+ .compress_string(false);
let mut context = ReadContext::new(&fory, reader);
+ let reader_compress = Reader::new(bytes.as_slice());
+ let fory_compress = Fory::default()
+ .mode(Compatible)
+ .xlang(true)
+ .compress_string(true);
+ let mut context_compress = ReadContext::new(&fory_compress, reader_compress);
let test_strings: Vec<String> = vec![
// Latin1
"ab".to_string(),
@@ -235,7 +246,8 @@ fn test_string_serializer() {
];
for s in &test_strings {
// make is_field=true to skip read/write type_id
- assert_eq!(*s, String::read(&mut context, true).unwrap());
+ assert_eq!(*s, String::read(&mut context).unwrap());
+ assert_eq!(*s, String::read(&mut context_compress).unwrap());
}
let mut writer = Writer::default();
let fory = Fory::default().mode(Compatible).xlang(true);
@@ -257,6 +269,8 @@ macro_rules! assert_de {
#[ignore]
#[allow(deprecated)]
fn test_cross_language_serializer() {
+ let day = NaiveDate::from_ymd_opt(2021, 11, 23).unwrap();
+ let instant = NaiveDateTime::from_timestamp(100, 0);
let str_list = vec!["hello".to_string(), "world".to_string()];
let str_set = HashSet::from(["hello".to_string(), "world".to_string()]);
let str_map = HashMap::<String, String>::from([
@@ -285,18 +299,8 @@ fn test_cross_language_serializer() {
assert_de!(fory, context, f32, -1f32);
assert_de!(fory, context, f64, -1f64);
assert_de!(fory, context, String, "str".to_string());
- assert_de!(
- fory,
- context,
- NaiveDate,
- NaiveDate::from_ymd_opt(2021, 11, 23).unwrap()
- );
- assert_de!(
- fory,
- context,
- NaiveDateTime,
- NaiveDateTime::from_timestamp(100, 0)
- );
+ assert_de!(fory, context, NaiveDate, day);
+ assert_de!(fory, context, NaiveDateTime, instant);
assert_de!(fory, context, Vec<bool>, [true, false]);
assert_de!(fory, context, Vec<i16>, [1, i16::MAX]);
assert_de!(fory, context, Vec<i32>, [1, i32::MAX]);
@@ -324,11 +328,8 @@ fn test_cross_language_serializer() {
fory.serialize_with_context(&-1f32, &mut context);
fory.serialize_with_context(&-1f64, &mut context);
fory.serialize_with_context(&"str".to_string(), &mut context);
- fory.serialize_with_context(
- &NaiveDate::from_ymd_opt(2021, 11, 23).unwrap(),
- &mut context,
- );
- fory.serialize_with_context(&NaiveDateTime::from_timestamp(100, 0), &mut context);
+ fory.serialize_with_context(&day, &mut context);
+ fory.serialize_with_context(&instant, &mut context);
fory.serialize_with_context(&vec![true, false], &mut context);
fory.serialize_with_context(&vec![1, i16::MAX], &mut context);
fory.serialize_with_context(&vec![1, i32::MAX], &mut context);
@@ -351,21 +352,239 @@ fn test_simple_struct() {
fory.register::<Color>(101);
fory.register::<Item>(102);
fory.register::<SimpleStruct>(103);
+
+ let local_obj = SimpleStruct {
+ f1: HashMap::from([(1, 1.0f64), (2, 2.0f64)]),
+ f2: 39,
+ f3: Item {
+ name: Some("item".to_string()),
+ },
+ f4: Some("f4".to_string()),
+ // f5: Color::White,
+ f6: vec![Some("f6".to_string())],
+ f7: 40,
+ // f8: 41,
+ // last: 42,
+ };
let remote_obj: SimpleStruct = fory.deserialize(&bytes).unwrap();
+ assert_eq!(remote_obj, local_obj);
+ let new_bytes = fory.serialize(&remote_obj);
+ let new_local_obj: SimpleStruct = fory.deserialize(&new_bytes).unwrap();
+ assert_eq!(new_local_obj, local_obj);
+ fs::write(&data_file_path, new_bytes).unwrap();
+}
+
+#[test]
+#[ignore]
+fn test_simple_named_struct() {
+ let data_file_path = get_data_file();
+ let bytes = fs::read(&data_file_path).unwrap();
+ let mut fory = Fory::default().mode(Compatible).xlang(true);
+ fory.register_by_namespace::<Color>("demo", "color");
+ fory.register_by_namespace::<Item>("demo", "item");
+ fory.register_by_namespace::<SimpleStruct>("demo", "simple_struct");
+
let local_obj = SimpleStruct {
- // f1: HashMap::from([(1, 1.0f64), (2, 2.0f64)]),
- // f2: 10,
+ f1: HashMap::from([(1, 1.0f64), (2, 2.0f64)]),
+ f2: 39,
f3: Item {
name: Some("item".to_string()),
},
- f4: Some("f3".to_string()),
- f5: Color::White,
- // f6: vec![Some("f6".to_string())],
+ f4: Some("f4".to_string()),
+ // f5: Color::White,
+ f6: vec![Some("f6".to_string())],
+ f7: 40,
+ // f8: 41,
// last: 42,
};
+ let remote_obj: SimpleStruct = fory.deserialize(&bytes).unwrap();
assert_eq!(remote_obj, local_obj);
let new_bytes = fory.serialize(&remote_obj);
- let new_local_obj: SimpleStruct = fory.deserialize(new_bytes.as_slice()).unwrap();
+ let new_local_obj: SimpleStruct = fory.deserialize(&new_bytes).unwrap();
assert_eq!(new_local_obj, local_obj);
fs::write(&data_file_path, new_bytes).unwrap();
}
+
+#[test]
+#[ignore]
+fn test_list() {
+ let data_file_path = get_data_file();
+ let bytes = fs::read(&data_file_path).unwrap();
+
+ let mut fory = Fory::default().mode(Compatible);
+ fory.register::<Item>(102);
+ let reader = Reader::new(bytes.as_slice());
+
+ let str_list = vec![Some("a".to_string()), Some("b".to_string())];
+ let str_list2 = vec![None, Some("b".to_string())];
+ let item = Item {
+ name: Some("a".to_string()),
+ };
+ let item2 = Item {
+ name: Some("b".to_string()),
+ };
+ let item3 = Item {
+ name: Some("c".to_string()),
+ };
+ let item_list = vec![Some(item), Some(item2)];
+ let item_list2 = vec![None, Some(item3)];
+
+ let bytes = reader.slice();
+ let remote_str_list: Vec<Option<String>> = fory.deserialize(bytes).unwrap();
+ assert_eq!(remote_str_list, str_list);
+ let data_bytes1 = fory.serialize(&remote_str_list);
+ let new_local_str_list: Vec<Option<String>> = fory.deserialize(&data_bytes1).unwrap();
+ assert_eq!(new_local_str_list, str_list);
+
+ let bytes = &bytes[data_bytes1.len()..];
+ let remote_str_list2: Vec<Option<String>> = fory.deserialize(bytes).unwrap();
+ assert_eq!(remote_str_list2, str_list2);
+ let data_bytes2 = fory.serialize(&remote_str_list2);
+ let new_local_str_list2: Vec<Option<String>> = fory.deserialize(&data_bytes2).unwrap();
+ assert_eq!(new_local_str_list2, str_list2);
+
+ let bytes = &bytes[data_bytes2.len()..];
+ let remote_item_list: Vec<Option<Item>> = fory.deserialize(bytes).unwrap();
+ assert_eq!(remote_item_list, item_list);
+ let data_bytes3 = fory.serialize(&remote_item_list);
+ let new_local_item_list: Vec<Option<Item>> = fory.deserialize(&data_bytes3).unwrap();
+ assert_eq!(new_local_item_list, item_list);
+
+ let bytes = &bytes[data_bytes3.len()..];
+ let remote_item_list2: Vec<Option<Item>> = fory.deserialize(bytes).unwrap();
+ assert_eq!(remote_item_list2, item_list2);
+ let data_bytes4 = fory.serialize(&remote_item_list2);
+ let new_local_item_list2: Vec<Option<Item>> = fory.deserialize(&data_bytes4).unwrap();
+ assert_eq!(new_local_item_list2, item_list2);
+
+ let all_bytes = [
+ data_bytes1.as_slice(),
+ data_bytes2.as_slice(),
+ data_bytes3.as_slice(),
+ data_bytes4.as_slice(),
+ ]
+ .concat();
+ fs::write(&data_file_path, all_bytes).unwrap();
+}
+
+#[test]
+#[ignore]
+fn test_map() {
+ let data_file_path = get_data_file();
+ let bytes = fs::read(&data_file_path).unwrap();
+
+ let mut fory = Fory::default().mode(Compatible);
+ fory.register::<Item>(102);
+ let reader = Reader::new(bytes.as_slice());
+ let mut context = ReadContext::new(&fory, reader);
+
+ let str_map = HashMap::from([
+ (Some("k1".to_string()), Some("v1".to_string())),
+ (None, Some("v2".to_string())),
+ (Some("k3".to_string()), None),
+ (Some("k4".to_string()), Some("v4".to_string())),
+ ]);
+ let item_map = HashMap::from([
+ (
+ Some("k1".to_string()),
+ Some(Item {
+ name: Some("item1".to_string()),
+ }),
+ ),
+ (
+ None,
+ Some(Item {
+ name: Some("item2".to_string()),
+ }),
+ ),
+ (Some("k3".to_string()), None),
+ (
+ Some("k4".to_string()),
+ Some(Item {
+ name: Some("item3".to_string()),
+ }),
+ ),
+ ]);
+
+ let remote_str_map: HashMap<Option<String>, Option<String>> =
+ fory.deserialize_with_context(&mut context).unwrap();
+ assert_eq!(remote_str_map, str_map);
+ let data_bytes1 = fory.serialize(&remote_str_map);
+ let new_local_str_map: HashMap<Option<String>, Option<String>> =
+ fory.deserialize(&data_bytes1).unwrap();
+ assert_eq!(new_local_str_map, str_map);
+
+ let remote_item_map: HashMap<Option<String>, Option<Item>> =
+ fory.deserialize_with_context(&mut context).unwrap();
+ assert_eq!(remote_item_map, item_map);
+ let data_bytes2 = fory.serialize(&remote_item_map);
+ let new_local_item_map: HashMap<Option<String>, Option<Item>> =
+ fory.deserialize(&data_bytes2).unwrap();
+ assert_eq!(new_local_item_map, item_map);
+
+ let all_bytes = [data_bytes1.as_slice(), data_bytes2.as_slice()].concat();
+ fs::write(&data_file_path, all_bytes).unwrap();
+}
+
+#[test]
+#[ignore]
+fn test_integer() {
+ #[derive(Fory, Debug, PartialEq, Default)]
+ struct Item2 {
+ f1: i32,
+ f2: Option<i32>,
+ f3: Option<i32>,
+ f4: i32,
+ f5: i32,
+ f6: Option<i32>,
+ }
+
+ let data_file_path = get_data_file();
+ let bytes = fs::read(&data_file_path).unwrap();
+
+ let mut fory = Fory::default().mode(Compatible);
+ fory.register::<Item2>(101);
+ let reader = Reader::new(bytes.as_slice());
+
+ let mut context = ReadContext::new(&fory, reader);
+ let f1 = 1;
+ let f2 = Some(2);
+ let f3 = Some(3);
+ let f4 = 4;
+ let f5 = i32::default();
+ let f6 = None;
+
+ let local_item2 = Item2 {
+ f1,
+ f2,
+ f3,
+ f4,
+ f5,
+ f6,
+ };
+ let remote_item2: Item2 = fory.deserialize_with_context(&mut context).unwrap();
+ assert_eq!(remote_item2, local_item2);
+ let remote_f1: i32 = fory.deserialize_with_context(&mut context).unwrap();
+ assert_eq!(remote_f1, f1);
+ let remote_f2: Option<i32> = fory.deserialize_with_context(&mut context).unwrap();
+ assert_eq!(remote_f2, f2);
+ let remote_f3: Option<i32> = fory.deserialize_with_context(&mut context).unwrap();
+ assert_eq!(remote_f3, f3);
+ let remote_f4: i32 = fory.deserialize_with_context(&mut context).unwrap();
+ assert_eq!(remote_f4, f4);
+ let remote_f5: i32 = fory.deserialize_with_context(&mut context).unwrap();
+ assert_eq!(remote_f5, f5);
+ let remote_f6: Option<i32> = fory.deserialize_with_context(&mut context).unwrap();
+ assert_eq!(remote_f6, f6);
+
+ let mut writer = Writer::default();
+ let mut context = WriteContext::new(&fory, &mut writer);
+ // fory.serialize_with_context(&remote_item2, &mut context);
+ fory.serialize_with_context(&remote_f1, &mut context);
+ fory.serialize_with_context(&remote_f2, &mut context);
+ fory.serialize_with_context(&remote_f3, &mut context);
+ fory.serialize_with_context(&remote_f4, &mut context);
+ fory.serialize_with_context(&remote_f5, &mut context);
+ fory.serialize_with_context(&remote_f6, &mut context);
+ fs::write(&data_file_path, context.writer.dump()).unwrap();
+}
diff --git a/rust/tests/tests/test_meta_string.rs b/rust/tests/tests/test_meta_string.rs
index 701505211..1eaa4678a 100644
--- a/rust/tests/tests/test_meta_string.rs
+++ b/rust/tests/tests/test_meta_string.rs
@@ -17,11 +17,14 @@
use std::iter;
-use fory_core::meta::{Encoding, MetaStringDecoder, MetaStringEncoder};
+use fory_core::meta::{
+ Encoding, MetaStringDecoder, MetaStringEncoder, NAMESPACE_DECODER, NAMESPACE_ENCODER,
+ TYPE_NAME_DECODER, TYPE_NAME_ENCODER,
+};
#[test]
fn test_encode_meta_string_lower_special() {
- let encoder = MetaStringEncoder::new();
+ let encoder = &TYPE_NAME_ENCODER;
let bytes1 = encoder.encode_lower_special("abc_def").unwrap();
assert_eq!(bytes1.len(), 5);
let bytes2 = encoder
@@ -31,7 +34,7 @@ fn test_encode_meta_string_lower_special() {
assert_eq!(bytes2.len(), 19);
let bytes3 = encoder.encode("MediaContent").unwrap().bytes;
assert_eq!(bytes3.len(), 9);
- let decoder = MetaStringDecoder::new();
+ let decoder = &TYPE_NAME_DECODER;
assert_eq!(
decoder.decode(&bytes1, Encoding::LowerSpecial).unwrap(),
"abc_def"
@@ -49,7 +52,7 @@ fn test_encode_meta_string_lower_special() {
}
}
-fn create_string(length: usize) -> String {
+fn create_string(length: usize, special_char1: char, special_char2: char) -> String {
(0..length)
.map(|j| {
let n = j % 64;
@@ -57,8 +60,8 @@ fn create_string(length: usize) -> String {
0..=25 => (b'a' + n as u8) as char,
26..=51 => (b'A' + (n - 26) as u8) as char,
52..=61 => (b'0' + (n - 52) as u8) as char,
- 62 => '.',
- 63 => '_',
+ 62 => special_char1,
+ 63 => special_char2,
_ => unreachable!(),
}
})
@@ -67,20 +70,24 @@ fn create_string(length: usize) -> String {
#[test]
fn test_encode_meta_string_lower_upper_digit_special() {
- let encoder = MetaStringEncoder::new();
+ let encoder = &NAMESPACE_ENCODER;
let encoded = encoder
.encode_lower_upper_digit_special("ExampleInput123")
.unwrap();
assert_eq!(encoded.len(), 12);
- let decoder = MetaStringDecoder::new();
+ let decoder = &NAMESPACE_DECODER;
let decoded = decoder
.decode(&encoded, Encoding::LowerUpperDigitSpecial)
.unwrap();
assert_eq!(decoded, "ExampleInput123");
for i in 1..128 {
- let origin_string = create_string(i);
+ let origin_string = create_string(
+ i,
+ NAMESPACE_ENCODER.special_char1,
+ NAMESPACE_ENCODER.special_char2,
+ );
let encoded = encoder
.encode_lower_upper_digit_special(&origin_string)
.unwrap();
@@ -93,27 +100,27 @@ fn test_encode_meta_string_lower_upper_digit_special() {
#[test]
fn test_meta_string() {
- let encoder = MetaStringEncoder::new();
-
- for i in 1..=127 {
- let origin_string = create_string(i);
-
- let meta_string = encoder.encode(&origin_string).unwrap();
- assert_ne!(meta_string.encoding, Encoding::Utf8);
- assert_eq!(meta_string.original, origin_string);
-
- let decoder = MetaStringDecoder::new();
- let new_string = decoder
- .decode(&meta_string.bytes, meta_string.encoding)
- .unwrap();
- assert_eq!(new_string, origin_string);
+ let special_chars = vec![('.', '_'), ('.', '$'), ('_', '$')];
+ for (special_char1, special_char2) in special_chars {
+ let encoder = MetaStringEncoder::new(special_char1, special_char2);
+ for i in 1..=127 {
+ let origin_string = create_string(i, special_char1, special_char2);
+ let meta_string = encoder.encode(&origin_string).unwrap();
+ assert_ne!(meta_string.encoding, Encoding::Utf8);
+ assert_eq!(meta_string.original, origin_string);
+ let decoder = MetaStringDecoder::new(special_char1, special_char2);
+ let new_string = decoder
+ .decode(&meta_string.bytes, meta_string.encoding)
+ .unwrap();
+ assert_eq!(new_string, origin_string);
+ }
}
}
#[test]
fn test_encode_empty_string() {
- let encoder = MetaStringEncoder::new();
- let decoder = MetaStringDecoder::new();
+ let encoder = &TYPE_NAME_ENCODER;
+ let decoder = &TYPE_NAME_DECODER;
for encoding in [
Encoding::LowerSpecial,
Encoding::LowerUpperDigitSpecial,
@@ -132,7 +139,7 @@ fn test_encode_empty_string() {
#[test]
fn test_encode_characters_outside_of_lower_special() {
- let encoder = MetaStringEncoder::new();
+ let encoder = &TYPE_NAME_ENCODER;
let test_string = "abcdefABCDEF1234!@#";
let meta_string = encoder.encode(test_string).unwrap();
assert_eq!(meta_string.encoding, Encoding::Utf8);
@@ -140,8 +147,8 @@ fn test_encode_characters_outside_of_lower_special() {
#[test]
fn test_all_to_upper_special_encoding() {
- let encoder = MetaStringEncoder::new();
- let decoder = MetaStringDecoder::new();
+ let encoder = &TYPE_NAME_ENCODER;
+ let decoder = &TYPE_NAME_DECODER;
let test_string = "ABC_DEF";
let meta_string = encoder.encode(test_string).unwrap();
assert_eq!(meta_string.encoding, Encoding::LowerUpperDigitSpecial);
@@ -153,8 +160,8 @@ fn test_all_to_upper_special_encoding() {
#[test]
fn test_first_to_lower_special_encoding() {
- let encoder = MetaStringEncoder::new();
- let decoder = MetaStringDecoder::new();
+ let encoder = &TYPE_NAME_ENCODER;
+ let decoder = &TYPE_NAME_DECODER;
let test_string = "Aabcdef";
let meta_string = encoder.encode(test_string).unwrap();
assert_eq!(meta_string.encoding, Encoding::FirstToLowerSpecial);
@@ -166,11 +173,11 @@ fn test_first_to_lower_special_encoding() {
#[test]
fn test_utf8_encoding() {
- let encoder = MetaStringEncoder::new();
+ let encoder = &NAMESPACE_ENCODER;
let test_string = "你好,世界";
let meta_string = encoder.encode(test_string).unwrap();
assert_eq!(meta_string.encoding, Encoding::Utf8);
- let decoder = MetaStringDecoder::new();
+ let decoder = &TYPE_NAME_DECODER;
let decoded_string = decoder
.decode(&meta_string.bytes, meta_string.encoding)
.unwrap();
@@ -178,7 +185,7 @@ fn test_utf8_encoding() {
let test_string = "aA$";
let meta_string = encoder.encode(test_string).unwrap();
assert_eq!(meta_string.encoding, Encoding::Utf8);
- let decoder = MetaStringDecoder::new();
+ let decoder = &TYPE_NAME_DECODER;
let decoded_string = decoder
.decode(&meta_string.bytes, meta_string.encoding)
.unwrap();
@@ -187,7 +194,7 @@ fn test_utf8_encoding() {
#[test]
fn test_strip_last_char() {
- let encoder = MetaStringEncoder::new();
+ let encoder = &TYPE_NAME_ENCODER;
let test_string = "abc";
let encoded_meta_string = encoder.encode(test_string).unwrap();
assert!(!encoded_meta_string.strip_last_char);
@@ -199,8 +206,8 @@ fn test_strip_last_char() {
#[test]
fn test_empty_string() {
- let encoder = MetaStringEncoder::new();
- let decoder = MetaStringDecoder::new();
+ let encoder = &TYPE_NAME_ENCODER;
+ let decoder = &TYPE_NAME_DECODER;
let meta_string = encoder.encode("").unwrap();
assert!(meta_string.bytes.is_empty());
let decoded = decoder
@@ -211,7 +218,7 @@ fn test_empty_string() {
#[test]
fn test_ascii_encoding() {
- let encoder = MetaStringEncoder::new();
+ let encoder = &TYPE_NAME_ENCODER;
let test_string = "asciiOnly";
let encoded_meta_string = encoder.encode(test_string).unwrap();
assert_ne!(encoded_meta_string.encoding, Encoding::Utf8);
@@ -220,7 +227,7 @@ fn test_ascii_encoding() {
#[test]
fn test_non_ascii_encoding() {
- let encoder = MetaStringEncoder::new();
+ let encoder = &TYPE_NAME_ENCODER;
let test_string = "こんにちは";
let encoded_meta_string = encoder.encode(test_string).unwrap();
assert_eq!(encoded_meta_string.encoding, Encoding::Utf8);
@@ -228,7 +235,7 @@ fn test_non_ascii_encoding() {
#[test]
fn test_non_ascii_encoding_and_non_utf8() {
- let encoder = MetaStringEncoder::new();
+ let encoder = &TYPE_NAME_ENCODER;
let non_ascii_string = "こんにちは";
match encoder.encode_with_encoding(non_ascii_string, Encoding::LowerSpecial) {
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@fory.apache.org
For additional commands, e-mail: commits-help@fory.apache.org