| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| #include <any> |
| #include <chrono> |
| #include <cstdlib> |
| #include <fstream> |
| #include <iostream> |
| #include <map> |
| #include <memory> |
| #include <string> |
| #include <vector> |
| |
| #include "addressbook.h" |
| #include "any_example.h" |
| #include "complex_fbs.h" |
| #include "fory/serialization/any_serializer.h" |
| #include "fory/serialization/fory.h" |
| #include "graph.h" |
| #include "monster.h" |
| #include "optional_types.h" |
| #include "tree.h" |
| |
| namespace { |
| |
| fory::Result<std::vector<uint8_t>, fory::Error> |
| ReadFile(const std::string &path) { |
| std::ifstream input(path, std::ios::binary); |
| if (FORY_PREDICT_FALSE(!input)) { |
| return fory::Unexpected( |
| fory::Error::invalid("failed to open data file for reading")); |
| } |
| std::vector<uint8_t> data((std::istreambuf_iterator<char>(input)), |
| std::istreambuf_iterator<char>()); |
| return data; |
| } |
| |
| fory::Result<void, fory::Error> WriteFile(const std::string &path, |
| const std::vector<uint8_t> &data) { |
| std::ofstream output(path, std::ios::binary | std::ios::trunc); |
| if (FORY_PREDICT_FALSE(!output)) { |
| return fory::Unexpected( |
| fory::Error::invalid("failed to open data file for writing")); |
| } |
| output.write(reinterpret_cast<const char *>(data.data()), |
| static_cast<std::streamsize>(data.size())); |
| if (FORY_PREDICT_FALSE(!output)) { |
| return fory::Unexpected(fory::Error::invalid("failed to write data file")); |
| } |
| return fory::Result<void, fory::Error>(); |
| } |
| |
| tree::TreeNode BuildTree() { |
| auto child_a = std::make_shared<tree::TreeNode>(); |
| child_a->set_id("child-a"); |
| child_a->set_name("child-a"); |
| |
| auto child_b = std::make_shared<tree::TreeNode>(); |
| child_b->set_id("child-b"); |
| child_b->set_name("child-b"); |
| |
| child_a->set_parent( |
| fory::serialization::SharedWeak<tree::TreeNode>::from(child_b)); |
| child_b->set_parent( |
| fory::serialization::SharedWeak<tree::TreeNode>::from(child_a)); |
| |
| tree::TreeNode root; |
| root.set_id("root"); |
| root.set_name("root"); |
| *root.mutable_children() = {child_a, child_a, child_b}; |
| return root; |
| } |
| |
| fory::Result<void, fory::Error> ValidateTree(const tree::TreeNode &root) { |
| const auto &children = root.children(); |
| if (children.size() != 3 || children[0] != children[1] || |
| children[0] == children[2]) { |
| return fory::Unexpected(fory::Error::invalid("tree children mismatch")); |
| } |
| auto parent_a = children[0]->parent().upgrade(); |
| auto parent_b = children[2]->parent().upgrade(); |
| if (!parent_a || !parent_b) { |
| return fory::Unexpected(fory::Error::invalid("tree parent upgrade failed")); |
| } |
| if (parent_a != children[2] || parent_b != children[0]) { |
| return fory::Unexpected(fory::Error::invalid("tree parent mismatch")); |
| } |
| return fory::Result<void, fory::Error>(); |
| } |
| |
| graph::Graph BuildGraph() { |
| auto node_a = std::make_shared<graph::Node>(); |
| node_a->set_id("node-a"); |
| auto node_b = std::make_shared<graph::Node>(); |
| node_b->set_id("node-b"); |
| |
| auto edge = std::make_shared<graph::Edge>(); |
| edge->set_id("edge-1"); |
| edge->set_weight(1.5F); |
| edge->set_from(fory::serialization::SharedWeak<graph::Node>::from(node_a)); |
| edge->set_to(fory::serialization::SharedWeak<graph::Node>::from(node_b)); |
| |
| *node_a->mutable_out_edges() = {edge}; |
| *node_a->mutable_in_edges() = {edge}; |
| *node_b->mutable_in_edges() = {edge}; |
| |
| graph::Graph graph_value; |
| *graph_value.mutable_nodes() = {node_a, node_b}; |
| *graph_value.mutable_edges() = {edge}; |
| return graph_value; |
| } |
| |
| fory::Result<void, fory::Error> ValidateGraph(const graph::Graph &graph_value) { |
| const auto &nodes = graph_value.nodes(); |
| const auto &edges = graph_value.edges(); |
| if (nodes.size() != 2 || edges.size() != 1) { |
| return fory::Unexpected(fory::Error::invalid("graph size mismatch")); |
| } |
| const auto &node_a = nodes[0]; |
| const auto &node_b = nodes[1]; |
| const auto &edge = edges[0]; |
| if (node_a->out_edges().empty() || node_a->in_edges().empty()) { |
| return fory::Unexpected(fory::Error::invalid("graph edge list empty")); |
| } |
| if (node_a->out_edges()[0] != node_a->in_edges()[0] || |
| node_a->out_edges()[0] != edge) { |
| return fory::Unexpected(fory::Error::invalid("graph shared edge mismatch")); |
| } |
| auto from = edge->from().upgrade(); |
| auto to = edge->to().upgrade(); |
| if (!from || !to) { |
| return fory::Unexpected(fory::Error::invalid("graph weak upgrade failed")); |
| } |
| if (from != node_a || to != node_b) { |
| return fory::Unexpected( |
| fory::Error::invalid("graph edge endpoints mismatch")); |
| } |
| return fory::Result<void, fory::Error>(); |
| } |
| |
| using StringMap = std::map<std::string, std::string>; |
| |
| fory::Result<void, fory::Error> RunRoundTrip() { |
| auto fory = fory::serialization::Fory::builder() |
| .xlang(true) |
| .check_struct_version(true) |
| .track_ref(false) |
| .build(); |
| |
| addressbook::RegisterTypes(fory); |
| monster::RegisterTypes(fory); |
| complex_fbs::RegisterTypes(fory); |
| optional_types::RegisterTypes(fory); |
| any_example::RegisterTypes(fory); |
| |
| FORY_RETURN_IF_ERROR( |
| fory::serialization::register_any_type<bool>(fory.type_resolver())); |
| FORY_RETURN_IF_ERROR(fory::serialization::register_any_type<std::string>( |
| fory.type_resolver())); |
| FORY_RETURN_IF_ERROR( |
| fory::serialization::register_any_type<fory::serialization::Date>( |
| fory.type_resolver())); |
| FORY_RETURN_IF_ERROR( |
| fory::serialization::register_any_type<fory::serialization::Timestamp>( |
| fory.type_resolver())); |
| FORY_RETURN_IF_ERROR( |
| fory::serialization::register_any_type<any_example::AnyInner>( |
| fory.type_resolver())); |
| FORY_RETURN_IF_ERROR( |
| fory::serialization::register_any_type<any_example::AnyUnion>( |
| fory.type_resolver())); |
| FORY_RETURN_IF_ERROR( |
| fory::serialization::register_any_type<std::vector<std::string>>( |
| fory.type_resolver())); |
| FORY_RETURN_IF_ERROR( |
| fory::serialization::register_any_type<StringMap>(fory.type_resolver())); |
| |
| addressbook::Person::PhoneNumber mobile; |
| mobile.set_number("555-0100"); |
| mobile.set_phone_type(addressbook::Person::PhoneType::MOBILE); |
| |
| addressbook::Person::PhoneNumber work; |
| work.set_number("555-0111"); |
| work.set_phone_type(addressbook::Person::PhoneType::WORK); |
| |
| addressbook::Person person; |
| person.set_name("Alice"); |
| person.set_id(123); |
| person.set_email("alice@example.com"); |
| *person.mutable_tags() = {"friend", "colleague"}; |
| *person.mutable_scores() = {{"math", 100}, {"science", 98}}; |
| person.set_salary(120000.5); |
| *person.mutable_phones() = {mobile, work}; |
| addressbook::Dog dog; |
| dog.set_name("Rex"); |
| dog.set_bark_volume(5); |
| *person.mutable_pet() = addressbook::Animal::dog(dog); |
| addressbook::Cat cat; |
| cat.set_name("Mimi"); |
| cat.set_lives(9); |
| *person.mutable_pet() = addressbook::Animal::cat(cat); |
| |
| addressbook::AddressBook book; |
| *book.mutable_people() = {person}; |
| *book.mutable_people_by_name() = {{person.name(), person}}; |
| |
| FORY_TRY(bytes, fory.serialize(book)); |
| FORY_TRY(roundtrip, fory.deserialize<addressbook::AddressBook>(bytes.data(), |
| bytes.size())); |
| |
| if (!(roundtrip == book)) { |
| return fory::Unexpected( |
| fory::Error::invalid("addressbook roundtrip mismatch")); |
| } |
| |
| addressbook::PrimitiveTypes types; |
| types.set_bool_value(true); |
| types.set_int8_value(12); |
| types.set_int16_value(1234); |
| types.set_int32_value(-123456); |
| types.set_varint32_value(-12345); |
| types.set_int64_value(-123456789); |
| types.set_varint64_value(-987654321); |
| types.set_tagged_int64_value(123456789); |
| types.set_uint8_value(200); |
| types.set_uint16_value(60000); |
| types.set_uint32_value(1234567890); |
| types.set_var_uint32_value(1234567890); |
| types.set_uint64_value(9876543210ULL); |
| types.set_var_uint64_value(12345678901ULL); |
| types.set_tagged_uint64_value(2222222222ULL); |
| types.set_float32_value(2.5F); |
| types.set_float64_value(3.5); |
| *types.mutable_contact() = |
| addressbook::PrimitiveTypes::Contact::email("alice@example.com"); |
| *types.mutable_contact() = addressbook::PrimitiveTypes::Contact::phone(12345); |
| |
| FORY_TRY(primitive_bytes, fory.serialize(types)); |
| FORY_TRY(primitive_roundtrip, |
| fory.deserialize<addressbook::PrimitiveTypes>( |
| primitive_bytes.data(), primitive_bytes.size())); |
| |
| if (!(primitive_roundtrip == types)) { |
| return fory::Unexpected( |
| fory::Error::invalid("primitive types roundtrip mismatch")); |
| } |
| |
| monster::Vec3 pos; |
| pos.set_x(1.0F); |
| pos.set_y(2.0F); |
| pos.set_z(3.0F); |
| |
| monster::Monster monster_value; |
| *monster_value.mutable_pos() = pos; |
| monster_value.set_mana(200); |
| monster_value.set_hp(80); |
| monster_value.set_name("Orc"); |
| monster_value.set_friendly(true); |
| *monster_value.mutable_inventory() = {static_cast<uint8_t>(1), |
| static_cast<uint8_t>(2), |
| static_cast<uint8_t>(3)}; |
| monster_value.set_color(monster::Color::Blue); |
| |
| FORY_TRY(monster_bytes, fory.serialize(monster_value)); |
| FORY_TRY(monster_roundtrip, fory.deserialize<monster::Monster>( |
| monster_bytes.data(), monster_bytes.size())); |
| |
| if (!(monster_roundtrip == monster_value)) { |
| return fory::Unexpected( |
| fory::Error::invalid("flatbuffers monster roundtrip mismatch")); |
| } |
| |
| complex_fbs::Container container; |
| container.set_id(9876543210ULL); |
| container.set_status(complex_fbs::Status::STARTED); |
| *container.mutable_bytes() = {static_cast<int8_t>(1), static_cast<int8_t>(2), |
| static_cast<int8_t>(3)}; |
| *container.mutable_numbers() = {10, 20, 30}; |
| auto *scalars = container.mutable_scalars(); |
| scalars->set_b(-8); |
| scalars->set_ub(200); |
| scalars->set_s(-1234); |
| scalars->set_us(40000); |
| scalars->set_i(-123456); |
| scalars->set_ui(123456); |
| scalars->set_l(-123456789); |
| scalars->set_ul(987654321); |
| scalars->set_f(1.5F); |
| scalars->set_d(2.5); |
| scalars->set_ok(true); |
| *container.mutable_names() = {"alpha", "beta"}; |
| *container.mutable_flags() = {true, false}; |
| complex_fbs::Note note; |
| note.set_text("alpha"); |
| *container.mutable_payload() = complex_fbs::Payload::note(note); |
| complex_fbs::Metric metric; |
| metric.set_value(42.0); |
| *container.mutable_payload() = complex_fbs::Payload::metric(metric); |
| |
| FORY_TRY(container_bytes, fory.serialize(container)); |
| FORY_TRY(container_roundtrip, |
| fory.deserialize<complex_fbs::Container>(container_bytes.data(), |
| container_bytes.size())); |
| |
| if (!(container_roundtrip == container)) { |
| return fory::Unexpected( |
| fory::Error::invalid("flatbuffers container roundtrip mismatch")); |
| } |
| |
| optional_types::AllOptionalTypes all_types; |
| all_types.set_bool_value(true); |
| all_types.set_int8_value(12); |
| all_types.set_int16_value(1234); |
| all_types.set_int32_value(-123456); |
| all_types.set_fixed_int32_value(-123456); |
| all_types.set_varint32_value(-12345); |
| all_types.set_int64_value(-123456789); |
| all_types.set_fixed_int64_value(-123456789); |
| all_types.set_varint64_value(-987654321); |
| all_types.set_tagged_int64_value(123456789); |
| all_types.set_uint8_value(200); |
| all_types.set_uint16_value(60000); |
| all_types.set_uint32_value(1234567890); |
| all_types.set_fixed_uint32_value(1234567890); |
| all_types.set_var_uint32_value(1234567890); |
| all_types.set_uint64_value(9876543210ULL); |
| all_types.set_fixed_uint64_value(9876543210ULL); |
| all_types.set_var_uint64_value(12345678901ULL); |
| all_types.set_tagged_uint64_value(2222222222ULL); |
| all_types.set_float32_value(2.5F); |
| all_types.set_float64_value(3.5); |
| all_types.set_string_value("optional"); |
| *all_types.mutable_bytes_value() = {static_cast<uint8_t>(1), |
| static_cast<uint8_t>(2), |
| static_cast<uint8_t>(3)}; |
| all_types.set_date_value(fory::serialization::Date(19724)); |
| all_types.set_timestamp_value( |
| fory::serialization::Timestamp(std::chrono::seconds(1704164645))); |
| *all_types.mutable_int32_list() = {1, 2, 3}; |
| *all_types.mutable_string_list() = {"alpha", "beta"}; |
| *all_types.mutable_int64_map() = {{"alpha", 10}, {"beta", 20}}; |
| |
| optional_types::OptionalHolder holder; |
| *holder.mutable_all_types() = all_types; |
| *holder.mutable_choice() = optional_types::OptionalUnion::note("optional"); |
| |
| FORY_TRY(optional_bytes, fory.serialize(holder)); |
| FORY_TRY(optional_roundtrip, |
| fory.deserialize<optional_types::OptionalHolder>( |
| optional_bytes.data(), optional_bytes.size())); |
| |
| if (!(optional_roundtrip == holder)) { |
| return fory::Unexpected( |
| fory::Error::invalid("optional types roundtrip mismatch")); |
| } |
| |
| any_example::AnyInner any_inner; |
| any_inner.set_name("inner"); |
| |
| any_example::AnyHolder any_holder; |
| any_holder.set_bool_value(std::any(true)); |
| any_holder.set_string_value(std::any(std::string("hello"))); |
| any_holder.set_date_value(std::any(fory::serialization::Date(19724))); |
| any_holder.set_timestamp_value(std::any( |
| fory::serialization::Timestamp(std::chrono::seconds(1704164645)))); |
| any_holder.set_message_value(std::any(any_inner)); |
| any_holder.set_union_value(std::any(any_example::AnyUnion::text("union"))); |
| any_holder.set_list_value( |
| std::any(std::vector<std::string>{"alpha", "beta"})); |
| any_holder.set_map_value(std::any(StringMap{{"k1", "v1"}, {"k2", "v2"}})); |
| |
| FORY_TRY(any_bytes, fory.serialize(any_holder)); |
| FORY_TRY(any_roundtrip, fory.deserialize<any_example::AnyHolder>( |
| any_bytes.data(), any_bytes.size())); |
| |
| if (!(any_roundtrip == any_holder)) { |
| return fory::Unexpected( |
| fory::Error::invalid("any holder roundtrip mismatch")); |
| } |
| |
| const char *data_file = std::getenv("DATA_FILE"); |
| if (data_file != nullptr && data_file[0] != '\0') { |
| FORY_TRY(payload, ReadFile(data_file)); |
| FORY_TRY(peer_book, fory.deserialize<addressbook::AddressBook>( |
| payload.data(), payload.size())); |
| if (!(peer_book == book)) { |
| return fory::Unexpected(fory::Error::invalid("peer payload mismatch")); |
| } |
| FORY_TRY(peer_bytes, fory.serialize(peer_book)); |
| FORY_RETURN_IF_ERROR(WriteFile(data_file, peer_bytes)); |
| } |
| |
| const char *primitive_file = std::getenv("DATA_FILE_PRIMITIVES"); |
| if (primitive_file != nullptr && primitive_file[0] != '\0') { |
| FORY_TRY(payload, ReadFile(primitive_file)); |
| FORY_TRY(peer_types, fory.deserialize<addressbook::PrimitiveTypes>( |
| payload.data(), payload.size())); |
| if (!(peer_types == types)) { |
| return fory::Unexpected( |
| fory::Error::invalid("peer primitive payload mismatch")); |
| } |
| FORY_TRY(peer_bytes, fory.serialize(peer_types)); |
| FORY_RETURN_IF_ERROR(WriteFile(primitive_file, peer_bytes)); |
| } |
| |
| const char *monster_file = std::getenv("DATA_FILE_FLATBUFFERS_MONSTER"); |
| if (monster_file != nullptr && monster_file[0] != '\0') { |
| FORY_TRY(payload, ReadFile(monster_file)); |
| FORY_TRY(peer_monster, fory.deserialize<monster::Monster>(payload.data(), |
| payload.size())); |
| if (!(peer_monster == monster_value)) { |
| return fory::Unexpected( |
| fory::Error::invalid("peer monster payload mismatch")); |
| } |
| FORY_TRY(peer_bytes, fory.serialize(peer_monster)); |
| FORY_RETURN_IF_ERROR(WriteFile(monster_file, peer_bytes)); |
| } |
| |
| const char *container_file = std::getenv("DATA_FILE_FLATBUFFERS_TEST2"); |
| if (container_file != nullptr && container_file[0] != '\0') { |
| FORY_TRY(payload, ReadFile(container_file)); |
| FORY_TRY(peer_container, fory.deserialize<complex_fbs::Container>( |
| payload.data(), payload.size())); |
| if (!(peer_container == container)) { |
| return fory::Unexpected( |
| fory::Error::invalid("peer container payload mismatch")); |
| } |
| FORY_TRY(peer_bytes, fory.serialize(peer_container)); |
| FORY_RETURN_IF_ERROR(WriteFile(container_file, peer_bytes)); |
| } |
| |
| const char *optional_file = std::getenv("DATA_FILE_OPTIONAL_TYPES"); |
| if (optional_file != nullptr && optional_file[0] != '\0') { |
| FORY_TRY(payload, ReadFile(optional_file)); |
| FORY_TRY(peer_holder, fory.deserialize<optional_types::OptionalHolder>( |
| payload.data(), payload.size())); |
| if (!(peer_holder == holder)) { |
| return fory::Unexpected( |
| fory::Error::invalid("peer optional payload mismatch")); |
| } |
| FORY_TRY(peer_bytes, fory.serialize(peer_holder)); |
| FORY_RETURN_IF_ERROR(WriteFile(optional_file, peer_bytes)); |
| } |
| |
| auto ref_fory = fory::serialization::Fory::builder() |
| .xlang(true) |
| .check_struct_version(true) |
| .track_ref(true) |
| .build(); |
| tree::RegisterTypes(ref_fory); |
| graph::RegisterTypes(ref_fory); |
| |
| tree::TreeNode tree_root = BuildTree(); |
| FORY_TRY(tree_bytes, ref_fory.serialize(tree_root)); |
| FORY_TRY(tree_roundtrip, ref_fory.deserialize<tree::TreeNode>( |
| tree_bytes.data(), tree_bytes.size())); |
| FORY_RETURN_IF_ERROR(ValidateTree(tree_roundtrip)); |
| |
| const char *tree_file = std::getenv("DATA_FILE_TREE"); |
| if (tree_file != nullptr && tree_file[0] != '\0') { |
| FORY_TRY(payload, ReadFile(tree_file)); |
| FORY_TRY(peer_tree, ref_fory.deserialize<tree::TreeNode>(payload.data(), |
| payload.size())); |
| FORY_RETURN_IF_ERROR(ValidateTree(peer_tree)); |
| FORY_TRY(peer_bytes, ref_fory.serialize(peer_tree)); |
| FORY_RETURN_IF_ERROR(WriteFile(tree_file, peer_bytes)); |
| } |
| |
| graph::Graph graph_value = BuildGraph(); |
| FORY_TRY(graph_bytes, ref_fory.serialize(graph_value)); |
| FORY_TRY(graph_roundtrip, ref_fory.deserialize<graph::Graph>( |
| graph_bytes.data(), graph_bytes.size())); |
| FORY_RETURN_IF_ERROR(ValidateGraph(graph_roundtrip)); |
| |
| const char *graph_file = std::getenv("DATA_FILE_GRAPH"); |
| if (graph_file != nullptr && graph_file[0] != '\0') { |
| FORY_TRY(payload, ReadFile(graph_file)); |
| FORY_TRY(peer_graph, ref_fory.deserialize<graph::Graph>(payload.data(), |
| payload.size())); |
| FORY_RETURN_IF_ERROR(ValidateGraph(peer_graph)); |
| FORY_TRY(peer_bytes, ref_fory.serialize(peer_graph)); |
| FORY_RETURN_IF_ERROR(WriteFile(graph_file, peer_bytes)); |
| } |
| |
| return fory::Result<void, fory::Error>(); |
| } |
| |
| } // namespace |
| |
| int main() { |
| auto result = RunRoundTrip(); |
| if (!result.ok()) { |
| std::cerr << "IDL roundtrip failed: " << result.error().message() |
| << std::endl; |
| return 1; |
| } |
| return 0; |
| } |