// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "PlainPropsTypes.h" #include "PlainPropsBind.h" #include "PlainPropsBuild.h" #include "PlainPropsDiff.h" #include "PlainPropsLoadMember.h" #include "PlainPropsSave.h" namespace PlainProps { FORCEINLINE bool Track(bool bDiff, const FBindContext&, FMemberBindType, FMemberId, FDiffMetadata, const void*, const void*) { return bDiff; } FORCEINLINE bool Track(bool bDiff, FDiffContext& Ctx, FMemberBindType Type, FMemberId Name, FDiffMetadata Meta, const void* A, const void* B) { if (bDiff) { Ctx.Out.Emplace(Type, ToOptional(Name), Meta, A, B); } return bDiff; } // TMemberSerializer helper template struct TStructSerializer : FBothStructId { static constexpr EMemberKind Kind = EMemberKind::Struct; template TStructSerializer(TCustomInit& Init, TConstArrayView Names, FBothStructId Both = GetStructBothId()) : FBothStructId(Both) { Init.RegisterInnerStruct(Both, Names); } FMemberSpec SpecMember() const { return FMemberSpec(DeclId); } FTypedValue SaveMember(const T& Value, const FSaveContext& Ctx) const { FMemberSchema Schema = { DefaultStructType, DefaultStructType, 0, FInnerId(BindId), nullptr}; return { Schema, {.Struct = SaveItem(Value, Ctx)} }; } void LoadMember(T& Dst, /* in-out */ FMemberLoader& Src) const { LoadStruct(&Dst, Src.GrabStruct()); } void ConstructAndLoadMember(void* Dst, /* in-out */ FMemberLoader& Src) const { if constexpr (std::is_default_constructible_v) { LoadMember(*new (Dst) T, Src); } else { static_assert(!std::is_void_v>, "Non-default constructible types must load via custom bindings"); ConstructAndLoadStruct(Dst, Src.GrabStruct()); } } template bool DiffMember(const T& A, const T& B, FMemberId Name, ContextType& Ctx) const { return Track(DiffStructs(&A, &B, BindId, Ctx), Ctx, DefaultStructBindType, Name, {.Struct = BindId}, &A, &B); } ////////// TRangeSaver API ////////// using BuiltItemType = FBuiltStruct*; FBuiltStruct* SaveItem(const T& Value, const FSaveContext& Ctx) const { return SaveStruct(&Value, BindId, Ctx); } FMemberSchema MakeMemberRangeSchema(ERangeSizeType MaxSize) const { return { FMemberType(MaxSize), DefaultStructType, 1, FInnerId(BindId), nullptr }; } }; ////////////////////////////////////////////////////////////////////////// template inline constexpr FMemberType ReflectInnermostType() { return DefaultStructType; } template inline constexpr FMemberType ReflectInnermostType() { return ReflectArithmetic.Pack(); } template inline constexpr FMemberType ReflectInnermostType() { return ReflectEnum.Pack(); } // TRangeSerializer constructor helper, FMemberBindType isn't default-constructible union FMemberBindTypeInitializer { FMemberBindTypeInitializer() : Unused() {} uint8_t Unused; FMemberBindType Value; }; // TMemberSerializer helper template struct TRangeSerializer { static constexpr EMemberKind Kind = EMemberKind::Range; static constexpr bool bLeafRange = std::is_base_of_v; static constexpr uint16 NumRanges = CountRangeBindings(); static constexpr uint16 NumTypes = NumRanges + 1; FRangeBinding const* const Bindings = nullptr; FOptionalInnerId InnermostId; FOptionalInnerId InnermostSpecId; FMemberBindTypeInitializer BindTypes[NumTypes]; FMemberType Types[NumTypes]; template TRangeSerializer(TCustomInit& Init, TConstArrayView Names) : Bindings(GetRangeBindings().GetData()) { for (uint16 Idx = 0; Idx < NumRanges; ++Idx) { ERangeSizeType MaxSize = Bindings[Idx].GetSizeType(); Types[Idx] = FMemberType(MaxSize); BindTypes[Idx].Value = FMemberBindType(MaxSize); } using InnermostType = typename TInnermostType::Type; Types[NumRanges] = ReflectInnermostType(); FMemberSpec Spec; BindTypes[NumRanges].Value = BindInnermostType(/* out */ InnermostId, /* out */ Spec); InnermostSpecId = InnermostId; if constexpr (!LeafType) { FBothStructId Both = {InnermostId.Get().AsStructBindId(), GetStructDeclId() }; InnermostSpecId = FInnerId(Both.DeclId); Init.RegisterInnerStruct(Both, Names); } } FMemberSpec SpecMember() const { return FMemberSpec(Types, InnermostSpecId); } void LoadMember(T& Dst, /* in-out */ FMemberLoader& Src) const { LoadRange(&Dst, Src.GrabRange(), MakeArrayView(Bindings, NumRanges)); } void ConstructAndLoadMember(void* Dst, /* in-out */ FMemberLoader& Src) const { static_assert(std::is_default_constructible_v, TEXT("Ranges must be default-constructible")); LoadMember(*new (Dst) T, Src); } FTypedValue SaveMember(const T& Value, const FSaveContext& Ctx) const { return { MakeMemberSchema(), {.Range = SaveItem(Value, Ctx)} }; } template bool DiffItems(const void* A, const void* B, const IItemRangeBinding& Binding, ContextType& Ctx) const { if constexpr (Kind == EMemberKind::Leaf) { return DiffRanges(A, B, Binding, ReflectLeaf); } else if constexpr (Kind == EMemberKind::Struct) { return DiffRanges(A, B, Binding, InnermostId.Get().AsStructBindId(), Ctx); } else { return DiffRanges(A, B, Binding, MakeInnerRangeBinding(), Ctx); } } inline FDiffMetadata MakeDiffMetadata() const { if constexpr (Kind == EMemberKind::Leaf) { return { .Leaf = ToOptionalEnum(InnermostId) }; } else if constexpr (Kind == EMemberKind::Struct) { return { .Struct = InnermostId.Get().AsStructBindId() }; } else { return { .Range = Bindings[1] }; } } template bool DiffMember(const T& A, const T& B, FMemberId Name, ContextType& Ctx) const { bool bDiff = bLeafRange ? Bindings[0].AsLeafBinding().DiffLeaves(&A, &B) : DiffItems(&A, &B, Bindings[0].AsItemBinding(), Ctx); return Track(bDiff, Ctx, BindTypes[0].Value, Name, MakeDiffMetadata(), &A, &B); } FMemberSchema MakeMemberSchema() const { return { Types[0], Types[1], NumRanges, InnermostId, NumRanges > 1 ? Types + 1 : nullptr }; } FRangeMemberBinding MakeInnerRangeBinding() const { return { &BindTypes[1].Value, Bindings, NumRanges, InnermostId, /* offset */ 0 }; } ////////// TRangeSaver API ////////// using BuiltItemType = FBuiltRange*; FMemberSchema MakeMemberRangeSchema(ERangeSizeType MaxSize) const { return { FMemberType(MaxSize), Types[0], NumTypes, InnermostId, Types }; } FBuiltRange* SaveItem(const T& Item, const FSaveContext& Ctx) const { if constexpr (bLeafRange) { return SaveLeafRange(&Item, Bindings[0].AsLeafBinding(), Types[1].AsLeaf(), Ctx); } else { FRangeMemberBinding MemberBinding = { &BindTypes[1].Value, Bindings, NumRanges, InnermostId, /* offset */ 0 }; return SaveRange(&Item, MakeInnerRangeBinding(), Ctx); } } }; ////////////////////////////////////////////////////////////////////////// // TMemberSerializer helper template struct TLeafSerializer { static void LoadMember(T& Dst, /* in-out */ FMemberLoader& Src) { Dst = Src.GrabLeaf().As(); } static void ConstructAndLoadMember(void* Dst, /* in-out */ FMemberLoader& Src) { LoadMember(*static_cast(Dst), Src); } static FTypedValue SaveLeaf(FMemberType Type, FOptionalInnerId Id, T Value) { return { {Type, Type, 0, Id, nullptr}, {.Leaf = ValueCast(Value)} }; } ////////// TRangeSaver API ////////// using BuiltItemType = T; static T SaveItem(T Value, const FSaveContext&) { return Value;} }; // TMemberSerializer helper template struct TArithmeticSerializer : TLeafSerializer { static constexpr FMemberType MemberType = ReflectArithmetic.Pack(); static constexpr FMemberBindType MemberBindType{ReflectArithmetic}; template TArithmeticSerializer(TCustomInit&, TConstArrayView) {} static FMemberSpec SpecMember() { return Specify(); } static FTypedValue SaveMember(T Value, const FSaveContext&) { return TLeafSerializer::SaveLeaf(MemberType, NoId, Value); } template inline static bool DiffMember(const T& A, const T& B, FMemberId Name, ContextType& Ctx) { return Track(A != B, Ctx, MemberBindType, Name, {.Leaf = NoId}, &A, &B); } ////////// TRangeSaver API ////////// FMemberSchema MakeMemberRangeSchema(ERangeSizeType MaxSize) const { return { FMemberType(MaxSize), MemberType, 1, NoId, nullptr }; } }; // TMemberSerializer helper template struct TEnumSerializer : TLeafSerializer { static constexpr FMemberType MemberType = ReflectEnum.Pack(); static constexpr FMemberBindType MemberBindType = ReflectEnum; const FEnumId Id; template TEnumSerializer(TCustomInit&, TConstArrayView) : Id(GetEnumId()) {} FMemberSpec SpecMember() const { return Specify(Id); } FTypedValue SaveMember(T Value, const FSaveContext& Ctx) const { return TLeafSerializer::SaveLeaf(MemberType, Id, Value); } template inline bool DiffMember(const T& A, const T& B, FMemberId Name, ContextType& Ctx) const { return Track(A != B, Ctx, MemberBindType, Name, {.Leaf = Id}, &A, &B); } ////////// TRangeSaver API ////////// FMemberSchema MakeMemberRangeSchema(ERangeSizeType MaxSize) const { return { FMemberType(MaxSize), MemberType, 1, FInnerId(Id), nullptr }; } }; ////////////////////////////////////////////////////////////////////////// template struct TSelectSerializer { using CustomBinding = CustomBind; using RangeBinding = RangeBind; static constexpr bool bRange = std::is_void_v && !std::is_void_v; using Type = std::conditional_t, TStructSerializer>; }; template struct TSelectSerializer { using Type = TArithmeticSerializer; }; template struct TSelectSerializer { using Type = TEnumSerializer; }; // Helps templated custom bindings save generic members template using TMemberSerializer = typename TSelectSerializer::Type; ////////////////////////////////////////////////////////////////////////// // Helps hide FBuiltRange internals class FRangeSaverBase { public: PLAINPROPS_API FRangeSaverBase(FScratchAllocator& Scratch, uint64 Num, SIZE_T ItemSize); protected: template inline void AddBuiltItem(BuiltItemType Item) { #if DO_CHECK check(It < End); #endif reinterpret_cast(*It) = Item; It += sizeof(Item); } [[nodiscard]] FTypedRange Finalize(FMemberSchema RangeSchema) { #if DO_CHECK check(It == End); #endif return { RangeSchema, Range }; } FBuiltRange* Range; uint8* It; #if DO_CHECK const uint8* End; #endif }; // Saves a range of T without a range binding template class TRangeSaver : public FRangeSaverBase { public: using FMemberSerializer = TMemberSerializer; using BuiltItemType = typename FMemberSerializer::BuiltItemType; static constexpr SIZE_T ItemSize = sizeof(BuiltItemType); TRangeSaver(const FSaveContext& InCtx, uint64 Num, const FMemberSerializer& InSchema) : FRangeSaverBase(InCtx.Scratch, Num, sizeof(BuiltItemType)) , Schema(InSchema) , Ctx(InCtx) {} void AddItem(const T& Item) { FRangeSaverBase::AddBuiltItem(Schema.SaveItem(Item, Ctx)); } [[nodiscard]] FTypedRange Finalize(ERangeSizeType MaxSize) { return FRangeSaverBase::Finalize(Schema.MakeMemberRangeSchema(MaxSize)); } private: const FMemberSerializer& Schema; const FSaveContext& Ctx; }; template class TLeafRangeSaver : public FRangeSaverBase { public: TLeafRangeSaver(FScratchAllocator& Scratch, uint64 Num) : FRangeSaverBase(Scratch, Num, sizeof(LeafType)) {} inline void AddItem(LeafType Item) {FRangeSaverBase::AddBuiltItem(Item); } using FRangeSaverBase::Finalize; }; class FNestedRangeSaver : public FRangeSaverBase { public: FNestedRangeSaver(FScratchAllocator& Scratch, uint64 Num) : FRangeSaverBase(Scratch, Num, sizeof(FBuiltRange*)) {} inline void AddItem(const FBuiltRange* Item) { FRangeSaverBase::AddBuiltItem(Item); } using FRangeSaverBase::Finalize; }; class FStructRangeSaver : public FRangeSaverBase { public: FStructRangeSaver(FScratchAllocator& Scratch, uint64 Num) : FRangeSaverBase(Scratch, Num, sizeof(FBuiltStruct*)) {} inline void AddItem(const FBuiltStruct* Item) { FRangeSaverBase::AddBuiltItem(Item); } using FRangeSaverBase::Finalize; }; } // namespace PlainProps