record.hpp algorithm array bit concepts cstdint exception expected functional limits map optional string string_view tuple type_traits utility variant vector src/include/serif/backend/mfem/serialize/resources.hpp src/include/serif/utils/serialize/archive.hpp src/include/serif/utils/serialize/components.hpp src/include/serif/utils/serialize/top.hpp serif::utils::serialize::Error serif::utils::serialize::Node serif::utils::serialize::Field serif::utils::serialize::Member serif::utils::serialize::Serializer< Node > serif::utils::serialize::Serializer< T > serif::utils::serialize::Serializer< std::string > serif::utils::serialize::Serializer< std::string_view > serif::utils::serialize::Serializer< char[Size]> serif::utils::serialize::Serializer< std::vector< T > > serif::utils::serialize::Serializer< std::array< T, Size > > serif::utils::serialize::Serializer< std::optional< T > > serif::utils::serialize::Serializer< std::tuple< T... > > serif serif::utils serif::utils::serialize #pragmaonce #include<algorithm> #include<array> #include<bit> #include<concepts> #include<cstdint> #include<exception> #include<expected> #include<functional> #include<limits> #include<map> #include<optional> #include<string> #include<string_view> #include<tuple> #include<type_traits> #include<utility> #include<variant> #include<vector> namespaceserif::utils::serialize{ structError{ std::stringpath; std::stringmessage; }; template<typenameT> usingResult=std::expected<T,Error>; usingValue=std::variant<std::monostate,bool,std::int64_t,std::uint64_t,double,std::string, std::vector<double>,std::vector<std::int64_t>,std::vector<std::uint64_t>>; structNode{ std::stringtype; std::uint32_tversion{1}; Valuevalue; std::map<std::string,Node,std::less<>>children; booloperator==(constNode&other)const{ if(type!=other.type||version!=other.version||value.index()!=other.value.index()||children!=other.children){ returnfalse; } if(constauto*real=std::get_if<double>(&value)){ returnstd::bit_cast<std::uint64_t>(*real)==std::bit_cast<std::uint64_t>(std::get<double>(other.value)); } if(constauto*reals=std::get_if<std::vector<double>>(&value)){ constauto&theirs=std::get<std::vector<double>>(other.value); returnstd::ranges::equal(*reals,theirs,[](doublefirst,doublesecond){ returnstd::bit_cast<std::uint64_t>(first)==std::bit_cast<std::uint64_t>(second); }); } returnvalue==other.value; } }; template<typenameT> structSerializer; template<typenameT> [[nodiscard]]Nodeencode(constT&value){ ifconstexpr(requires{Serializer<std::remove_cvref_t<T>>::save(value);}){ returnSerializer<std::remove_cvref_t<T>>::save(value); }else{ return{.type="unsupported",.value=std::string{"NoSerializer<T>isregisteredforthiscomponent."}}; } } template<typenameT> [[nodiscard]]Result<T>decode(constNode&node){ constexprstd::uint32_tversion=[]{ ifconstexpr(requires{Serializer<T>::version;}){ returnstatic_cast<std::uint32_t>(Serializer<T>::version); }else{ returnstd::uint32_t{1}; } }(); if(node.version!=version){ returnstd::unexpected(Error{"","Unsupportedcomponentschemaversion:"+std::to_string(node.version)}); } ifconstexpr(requires{Serializer<T>::load(node);}){ try{ returnSerializer<T>::load(node); }catch(conststd::exception&error){ returnstd::unexpected(Error{"","Invalidsavedcomponent:"+std::string{error.what()}}); } }else{ returnstd::unexpected(Error{"","Noloaderisregisteredforthiscomponenttype."}); } } structField{ std::stringname; Nodenode; }; template<typenameT> [[nodiscard]]Fieldfield(std::stringname,constT&value){ return{std::move(name),encode(value)}; } template<typename...Fields> [[nodiscard]]Nodeobject(std::string_viewtype,Fields...fields){ Noderesult{.type=std::string{type}}; (result.children.emplace(std::move(fields.name),std::move(fields.node)),...); returnresult; } template<typenameT> structMember{ usingType=T; std::string_viewname; }; template<typenameT> [[nodiscard]]Member<T>member(std::string_viewname){ return{name}; } template<typenameT> [[nodiscard]]Result<T>read_field(constNode&node,std::string_viewname){ constautofound=node.children.find(name); if(found==node.children.end()){ returnstd::unexpected(Error{std::string{name},"Requiredfieldismissing."}); } Result<T>result=decode<T>(found->second); if(!result){ result.error().path=std::string{name}+(result.error().path.empty()?"":"/"+result.error().path); } returnresult; } template<typenameConstruct,typename...Members> [[nodiscard]]autoread_object(constNode&node,std::string_viewtype,Constructconstruct,Members...members) ->Result<std::invoke_result_t<Construct,typenameMembers::Type...>>{ if(node.type!=type||!std::holds_alternative<std::monostate>(node.value)){ returnstd::unexpected(Error{"","Expectedcomponentschema'"+std::string{type}+"',received'"+node.type+"'."}); } autovalues=std::tuple{read_field<typename Members::Type>(node,members.name)...}; std::optional<Error>error; std::apply([&](constauto&...value){ ([&]{ if(!value&&!error){ error=value.error(); } }(),...); },values); if(error){ returnstd::unexpected(std::move(*error)); } returnstd::apply([&](auto&...value){returnstd::invoke(construct,std::move(*value)...);},values); } template<> structSerializer<Node>{ staticNodesave(constNode&node){returnnode;} staticResult<Node>load(constNode&node){returnnode;} }; template<typenameT>requiresstd::is_arithmetic_v<T> structSerializer<T>{ staticNodesave(Tvalue){ ifconstexpr(std::same_as<T,bool>){ return{.type="bool",.value=value}; }elseifconstexpr(std::floating_point<T>){ static_assert(sizeof(T)<=sizeof(double),"Provideanexplicitserializerforextendedprecision."); return{.type="real",.value=static_cast<double>(value)}; }elseifconstexpr(std::signed_integral<T>){ return{.type="integer",.value=static_cast<std::int64_t>(value)}; }else{ return{.type="unsigned",.value=static_cast<std::uint64_t>(value)}; } } staticResult<T>load(constNode&node){ usingStored=std::conditional_t<std::same_as<T,bool>,bool, std::conditional_t<std::floating_point<T>,double, std::conditional_t<std::signed_integral<T>,std::int64_t,std::uint64_t>>>; constauto*value=std::get_if<Stored>(&node.value); if(value==nullptr||!node.children.empty()||node.type!=save(T{}).type){ returnstd::unexpected(Error{"","Savedscalarhasthewrongrepresentation."}); } ifconstexpr(std::integral<T>&&!std::same_as<T,bool>){ if(*value<std::numeric_limits<T>::lowest()||*value>std::numeric_limits<T>::max()){ returnstd::unexpected(Error{"","Savedintegerisoutsidetherequestedtype'srange."}); } } returnstatic_cast<T>(*value); } }; template<> structSerializer<std::string>{ staticNodesave(conststd::string&value){return{.type="string",.value=value};} staticResult<std::string>load(constNode&node){ if(constauto*value=std::get_if<std::string>(&node.value);value!=nullptr&&node.type=="string"&&node.children.empty()){ return*value; } returnstd::unexpected(Error{"","Expectedastring."}); } }; template<> structSerializer<std::string_view>{ staticNodesave(std::string_viewvalue){returnSerializer<std::string>::save(std::string{value});} }; template<std::size_tSize> structSerializer<char[Size]>{ staticNodesave(constchar(&value)[Size]){returnencode(std::string{value,Size-(value[Size-1]=='\0'?1:0)});} }; template<typenameT> structSerializer<std::vector<T>>{ staticNodesave(conststd::vector<T>&values){ Nodenode{.type="array"}; ifconstexpr(std::same_as<T,double>||std::same_as<T,std::int64_t>||std::same_as<T,std::uint64_t>){ node.value=values; }elseifconstexpr(std::integral<T>&&!std::same_as<T,bool>){ usingStored=std::conditional_t<std::signed_integral<T>,std::int64_t,std::uint64_t>; node.value=std::vector<Stored>(values.begin(),values.end()); }else{ for(std::size_tindex=0;index<values.size();++index){ node.children.emplace(std::to_string(index),encode(static_cast<constT&>(values[index]))); } } returnnode; } staticResult<std::vector<T>>load(constNode&node){ if(node.type!="array"){ returnstd::unexpected(Error{"","Expectedanarray."}); } ifconstexpr(std::same_as<T,double>||(std::integral<T>&&!std::same_as<T,bool>)){ usingStored=std::conditional_t<std::same_as<T,double>,double, std::conditional_t<std::signed_integral<T>,std::int64_t,std::uint64_t>>; constauto*values=std::get_if<std::vector<Stored>>(&node.value); if(values==nullptr||!node.children.empty()){ returnstd::unexpected(Error{"","Savedarrayhasthewrongnumericrepresentation."}); } ifconstexpr(std::same_as<T,Stored>){ return*values; } std::vector<T>result; result.reserve(values->size()); for(Storedvalue:*values){ autoconverted=decode<T>(encode(value)); if(!converted){ returnstd::unexpected(converted.error()); } result.push_back(*converted); } returnresult; }else{ if(!std::holds_alternative<std::monostate>(node.value)){ returnstd::unexpected(Error{"","Expectedacompoundarray."}); } std::vector<T>result; result.reserve(node.children.size()); for(std::size_tindex=0;index<node.children.size();++index){ autovalue=read_field<T>(node,std::to_string(index)); if(!value){ returnstd::unexpected(value.error()); } result.push_back(std::move(*value)); } returnresult; } } }; template<typenameT,std::size_tSize> structSerializer<std::array<T,Size>>{ staticNodesave(conststd::array<T,Size>&values){returnencode(std::vector<T>{values.begin(),values.end()});} staticResult<std::array<T, Size>>load(constNode&node){ autovalues=decode<std::vector<T>>(node); if(!values){ returnstd::unexpected(values.error()); } if(values->size()!=Size){ returnstd::unexpected(Error{"","Savedarraylengthdoesnotmatchtherequestedfixedextent."}); } std::array<T,Size>result{}; std::copy(values->begin(),values->end(),result.begin()); returnresult; } }; template<typenameT> structSerializer<std::optional<T>>{ staticNodesave(conststd::optional<T>&value){ Nodenode{.type="optional"}; if(value){ node.children.emplace("value",encode(*value)); } returnnode; } staticResult<std::optional<T>>load(constNode&node){ if(node.type!="optional"||!std::holds_alternative<std::monostate>(node.value)){ returnstd::unexpected(Error{"","Expectedanoptionalvalue."}); } if(node.children.empty()){ returnstd::optional<T>{}; } autovalue=read_field<T>(node,"value"); if(!value){ returnstd::unexpected(value.error()); } returnstd::optional<T>{std::move(*value)}; } }; template<typename...T> structSerializer<std::tuple<T...>>{ staticNodesave(conststd::tuple<T...>&values){ return[&]<std::size_t...I>(std::index_sequence<I...>){ returnobject("tuple",field(std::to_string(I),std::get<I>(values))...); }(std::index_sequence_for<T...>{}); } staticResult<std::tuple<T...>>load(constNode&node){ return[&]<std::size_t...I>(std::index_sequence<I...>){ returnread_object(node,"tuple",[](T...values){returnstd::tuple<T...>{std::move(values)...};},member<T>(std::to_string(I))...); }(std::index_sequence_for<T...>{}); } }; [[nodiscard]]std::stringfingerprint(std::string_viewbytes); }