mirror of
https://github.com/fraillt/bitsery.git
synced 2026-06-08 00:03:54 +00:00
Fix shared polymorphic poiner like type serialization/deserialization
This commit is contained in:
@@ -104,7 +104,7 @@ struct PtrObserverManager
|
||||
|
||||
static void destroyPolymorphic(T& obj,
|
||||
MemResourceBase*,
|
||||
PolymorphicHandlerBase&)
|
||||
const std::shared_ptr<PolymorphicHandlerBase>&)
|
||||
{
|
||||
obj = nullptr;
|
||||
}
|
||||
@@ -130,47 +130,22 @@ struct NonPtrManager
|
||||
|
||||
static void create(T&, MemResourceBase*, size_t) {}
|
||||
|
||||
static void createPolymorphic(T&, MemResourceBase*, PolymorphicHandlerBase&)
|
||||
static void createPolymorphic(T&,
|
||||
MemResourceBase*,
|
||||
const std::shared_ptr<PolymorphicHandlerBase>&)
|
||||
{
|
||||
}
|
||||
|
||||
static void destroy(T&, MemResourceBase*, size_t) {}
|
||||
|
||||
static void destroyPolymorphic(T&, MemResourceBase*, PolymorphicHandlerBase&)
|
||||
static void destroyPolymorphic(T&,
|
||||
MemResourceBase*,
|
||||
const std::shared_ptr<PolymorphicHandlerBase>&)
|
||||
{
|
||||
}
|
||||
// LCOV_EXCL_STOP
|
||||
};
|
||||
|
||||
// this class is used by NonPtrManager
|
||||
struct NoRTTI
|
||||
{
|
||||
template<typename TBase>
|
||||
static size_t get(TBase&)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename TBase>
|
||||
static constexpr size_t get()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename TBase, typename TDerived>
|
||||
static constexpr TDerived* cast(TBase* obj)
|
||||
{
|
||||
static_assert(!std::is_pointer<TDerived>::value, "");
|
||||
return dynamic_cast<TDerived*>(obj);
|
||||
}
|
||||
|
||||
template<typename TBase>
|
||||
static constexpr bool isPolymorphic()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template<typename RTTI>
|
||||
@@ -179,31 +154,34 @@ using PointerOwnerBase =
|
||||
PolymorphicContext,
|
||||
RTTI>;
|
||||
|
||||
using PointerOwner = PointerOwnerBase<StandardRTTI>;
|
||||
|
||||
using PointerObserver =
|
||||
template<typename RTTI>
|
||||
using PointerObserverBase =
|
||||
pointer_utils::PointerObjectExtensionBase<pointer_details::PtrObserverManager,
|
||||
PolymorphicContext,
|
||||
pointer_details::NoRTTI>;
|
||||
RTTI>;
|
||||
|
||||
// inherit from PointerObjectExtensionBase in order to specify
|
||||
// PointerType::NotNull
|
||||
class ReferencedByPointer
|
||||
template<typename RTTI>
|
||||
class ReferencedByPointerBase
|
||||
: public pointer_utils::PointerObjectExtensionBase<
|
||||
pointer_details::NonPtrManager,
|
||||
PolymorphicContext,
|
||||
pointer_details::NoRTTI>
|
||||
RTTI>
|
||||
{
|
||||
public:
|
||||
ReferencedByPointer()
|
||||
ReferencedByPointerBase()
|
||||
: pointer_utils::PointerObjectExtensionBase<pointer_details::NonPtrManager,
|
||||
PolymorphicContext,
|
||||
pointer_details::NoRTTI>(
|
||||
PointerType::NotNull)
|
||||
RTTI>(PointerType::NotNull)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using PointerOwner = PointerOwnerBase<StandardRTTI>;
|
||||
using PointerObserver = PointerObserverBase<StandardRTTI>;
|
||||
using ReferencedByPointer = ReferencedByPointerBase<StandardRTTI>;
|
||||
|
||||
}
|
||||
|
||||
namespace traits {
|
||||
@@ -219,8 +197,8 @@ struct ExtensionTraits<ext::PointerOwnerBase<RTTI>, T*>
|
||||
!RTTI::template isPolymorphic<TValue>();
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::PointerObserver, T*>
|
||||
template<typename T, typename RTTI>
|
||||
struct ExtensionTraits<ext::PointerObserverBase<RTTI>, T*>
|
||||
{
|
||||
// although pointer observer doesn't serialize anything, but we still add
|
||||
// value overload support to be consistent with pointer owners observer only
|
||||
@@ -231,8 +209,8 @@ struct ExtensionTraits<ext::PointerObserver, T*>
|
||||
static constexpr bool SupportLambdaOverload = false;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct ExtensionTraits<ext::ReferencedByPointer, T>
|
||||
template<typename T, typename RTTI>
|
||||
struct ExtensionTraits<ext::ReferencedByPointerBase<RTTI>, T>
|
||||
{
|
||||
// allow everything, because it is serialized as regular type, except it also
|
||||
// creates pointerId that is required by NonOwningPointer to work
|
||||
|
||||
@@ -198,10 +198,27 @@ struct SmartPtrOwnerManager
|
||||
state.obj = std::shared_ptr<TElement>(obj);
|
||||
}
|
||||
|
||||
static void saveToSharedStatePolymorphic(TSharedState& state, T& obj)
|
||||
{
|
||||
state.obj = std::shared_ptr<TElement>(obj);
|
||||
}
|
||||
|
||||
static void loadFromSharedState(TSharedState& state, T& obj)
|
||||
{
|
||||
// reinterpret_pointer_cast is only since c++17
|
||||
auto p = reinterpret_cast<TElement*>(state.obj.get());
|
||||
auto v = state.obj.get();
|
||||
auto p = reinterpret_cast<TElement*>(v);
|
||||
obj = std::shared_ptr<TElement>(state.obj, p);
|
||||
}
|
||||
|
||||
static void loadFromSharedStatePolymorphic(TSharedState& state, T& obj)
|
||||
{
|
||||
// TODO Fix pointer addresses in case objects are deserialized using
|
||||
// different bases
|
||||
|
||||
// reinterpret_pointer_cast is only since c++17
|
||||
auto v = state.obj.get();
|
||||
auto p = reinterpret_cast<TElement*>(v);
|
||||
obj = std::shared_ptr<TElement>(state.obj, p);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -120,10 +120,12 @@ struct PLCInfoSerializer : PLCInfo
|
||||
struct PLCInfoDeserializer : PLCInfo
|
||||
{
|
||||
PLCInfoDeserializer(void* ptr,
|
||||
size_t sharedTypeId_,
|
||||
PointerOwnershipType ownershipType_,
|
||||
MemResourceBase* memResource_)
|
||||
: PLCInfo(ownershipType_)
|
||||
, ownerPtr{ ptr }
|
||||
, sharedTypeId{ sharedTypeId_ }
|
||||
, memResource{ memResource_ }
|
||||
, observersList{ StdPolyAlloc<std::reference_wrapper<void*>>{
|
||||
memResource_ } } {};
|
||||
@@ -157,6 +159,9 @@ struct PLCInfoDeserializer : PLCInfo
|
||||
}
|
||||
|
||||
void* ownerPtr;
|
||||
// used for polymorphic types in order to identify
|
||||
// if shared objects can be assigned
|
||||
size_t sharedTypeId;
|
||||
MemResourceBase* memResource;
|
||||
std::vector<std::reference_wrapper<void*>,
|
||||
StdPolyAlloc<std::reference_wrapper<void*>>>
|
||||
@@ -254,8 +259,8 @@ public:
|
||||
|
||||
PLCInfoDeserializer& getInfoById(size_t id, PointerOwnershipType ptrType)
|
||||
{
|
||||
auto res =
|
||||
_idMap.emplace(id, PLCInfoDeserializer{ nullptr, ptrType, _memResource });
|
||||
auto res = _idMap.emplace(
|
||||
id, PLCInfoDeserializer{ nullptr, 0, ptrType, _memResource });
|
||||
auto& ptrInfo = res.first->second;
|
||||
if (!res.second)
|
||||
ptrInfo.update(ptrType);
|
||||
@@ -352,8 +357,8 @@ public:
|
||||
if (ptr) {
|
||||
auto& ctx = ser.template context<
|
||||
pointer_utils::PointerLinkingContextSerialization>();
|
||||
auto& ptrInfo =
|
||||
ctx.getInfoByPtr(getBasePtr(ptr), TPtrManager<T>::getOwnership());
|
||||
auto& ptrInfo = ctx.getInfoByPtr(getRootPtr(ser, ptr, IsPolymorphic<T>{}),
|
||||
TPtrManager<T>::getOwnership());
|
||||
details::writeSize(ser.adapter(), ptrInfo.id);
|
||||
if (TPtrManager<T>::getOwnership() != PointerOwnershipType::Observer) {
|
||||
if (!ptrInfo.isSharedProcessed)
|
||||
@@ -411,7 +416,7 @@ private:
|
||||
const auto& ctx = des.template context<TPolymorphicContext<RTTI>>();
|
||||
auto ptr = TPtrManager<TObj>::getPtr(obj);
|
||||
TPtrManager<TObj>::destroyPolymorphic(
|
||||
obj, memResource, ctx.getPolymorphicHandler(*ptr));
|
||||
obj, memResource, ctx.getPolymorphicHandler(ptr));
|
||||
}
|
||||
|
||||
template<typename Des, typename TObj>
|
||||
@@ -426,16 +431,23 @@ private:
|
||||
RTTI::template get<typename TPtrManager<TObj>::TElement>());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const void* getBasePtr(const T* ptr) const
|
||||
template<typename Ser, typename T>
|
||||
const void* getRootPtr(Ser&, const T* ptr, std::false_type) const
|
||||
{
|
||||
// todo implement handling of types with virtual inheritance
|
||||
// this is required to correctly track same object, when one object is
|
||||
// derived and other is base class e.g. shared_ptr<Base> and
|
||||
// weak_ptr<Derived> or pointer observer Base*
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// same pointer can be accessed through different types with the same base
|
||||
// e.g. if we have Base class and Derived(Base) we'll get different pointer
|
||||
// address depending if we access it through Base or Derived
|
||||
// this function always returns "root" (Base) pointer.
|
||||
template<typename Ser, typename T>
|
||||
const void* getRootPtr(Ser& ser, const T* ptr, std::true_type) const
|
||||
{
|
||||
const auto& ctx = ser.template context<TPolymorphicContext<RTTI>>();
|
||||
return ctx.getPolymorphicHandler(ptr)->getRootPtr(ptr);
|
||||
}
|
||||
|
||||
template<typename Ser, typename TPtr, typename Fnc>
|
||||
void serializeImpl(Ser& ser, TPtr& ptr, Fnc&&, std::true_type) const
|
||||
{
|
||||
@@ -506,8 +518,11 @@ private:
|
||||
std::true_type,
|
||||
OwnershipType<PointerOwnershipType::SharedOwner>) const
|
||||
{
|
||||
const auto& ctx = des.template context<TPolymorphicContext<RTTI>>();
|
||||
const size_t baseTypeId =
|
||||
RTTI::template get<typename TPtrManager<T>::TElement>();
|
||||
size_t deserializedTypeId = 0;
|
||||
if (!ptrInfo.sharedState) {
|
||||
const auto& ctx = des.template context<TPolymorphicContext<RTTI>>();
|
||||
ctx.deserialize(
|
||||
des,
|
||||
TPtrManager<T>::getPtr(obj),
|
||||
@@ -521,12 +536,46 @@ private:
|
||||
memResource](const std::shared_ptr<PolymorphicHandlerBase>& handler) {
|
||||
TPtrManager<T>::destroyPolymorphic(obj, memResource, handler);
|
||||
});
|
||||
if (!ptrInfo.sharedState)
|
||||
TPtrManager<T>::saveToSharedState(
|
||||
if (!ptrInfo.sharedState) {
|
||||
TPtrManager<T>::saveToSharedStatePolymorphic(
|
||||
createAndGetSharedStateObj<T>(ptrInfo), obj);
|
||||
}
|
||||
ptrInfo.sharedTypeId =
|
||||
ctx.getPolymorphicHandler(TPtrManager<T>::getPtr(obj))
|
||||
->getDerivedTypeId();
|
||||
// since we just deserialized an object, we can skip checking hierarchy
|
||||
// chain by assigning baseType id instead of derived type id
|
||||
deserializedTypeId = baseTypeId;
|
||||
} else {
|
||||
deserializedTypeId = ptrInfo.sharedTypeId;
|
||||
}
|
||||
TPtrManager<T>::loadFromSharedState(getSharedStateObj<T>(ptrInfo), obj);
|
||||
ptrInfo.processOwner(TPtrManager<T>::getPtr(obj));
|
||||
|
||||
if (canAssignToShared(baseTypeId, deserializedTypeId, ctx)) {
|
||||
TPtrManager<T>::loadFromSharedStatePolymorphic(
|
||||
getSharedStateObj<T>(ptrInfo), obj);
|
||||
ptrInfo.processOwner(TPtrManager<T>::getPtr(obj));
|
||||
} else {
|
||||
des.adapter().error(ReaderError::InvalidPointer);
|
||||
}
|
||||
}
|
||||
|
||||
// check if actual deserialized type can be assigned to the base type
|
||||
// (statically typed)
|
||||
bool canAssignToShared(size_t baseTypeId,
|
||||
size_t deserializedTypeId,
|
||||
const TPolymorphicContext<RTTI>& ctx) const
|
||||
{
|
||||
if (baseTypeId == deserializedTypeId)
|
||||
return true;
|
||||
auto bases = ctx.getDirectBases(deserializedTypeId);
|
||||
if (bases) {
|
||||
for (auto typeId : *bases) {
|
||||
if (canAssignToShared(baseTypeId, typeId, ctx)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename Des, typename T, typename Fnc>
|
||||
@@ -538,6 +587,8 @@ private:
|
||||
std::false_type,
|
||||
OwnershipType<PointerOwnershipType::SharedOwner>) const
|
||||
{
|
||||
const size_t baseTypeId =
|
||||
RTTI::template get<typename TPtrManager<T>::TElement>();
|
||||
if (!ptrInfo.sharedState) {
|
||||
auto ptr = TPtrManager<T>::getPtr(obj);
|
||||
if (ptr) {
|
||||
@@ -552,9 +603,15 @@ private:
|
||||
ptr = TPtrManager<T>::getPtr(obj);
|
||||
}
|
||||
fnc(des, *ptr);
|
||||
ptrInfo.sharedTypeId =
|
||||
RTTI::template get<typename TPtrManager<T>::TElement>();
|
||||
}
|
||||
if (baseTypeId == ptrInfo.sharedTypeId) {
|
||||
TPtrManager<T>::loadFromSharedState(getSharedStateObj<T>(ptrInfo), obj);
|
||||
ptrInfo.processOwner(TPtrManager<T>::getPtr(obj));
|
||||
} else {
|
||||
des.adapter().error(ReaderError::InvalidPointer);
|
||||
}
|
||||
TPtrManager<T>::loadFromSharedState(getSharedStateObj<T>(ptrInfo), obj);
|
||||
ptrInfo.processOwner(TPtrManager<T>::getPtr(obj));
|
||||
}
|
||||
|
||||
template<typename Des, typename T, typename Fnc, typename isPolymorph>
|
||||
|
||||
@@ -74,10 +74,18 @@ public:
|
||||
|
||||
virtual void process(void* ser, void* obj) const = 0;
|
||||
|
||||
virtual void* getRootPtr(const void* obj) const = 0;
|
||||
|
||||
virtual size_t getDerivedTypeId() const = 0;
|
||||
|
||||
virtual ~PolymorphicHandlerBase() = default;
|
||||
};
|
||||
|
||||
template<typename RTTI, typename TSerializer, typename TBase, typename TDerived>
|
||||
template<typename RTTI,
|
||||
typename TSerializer,
|
||||
typename TRoot,
|
||||
typename TBase,
|
||||
typename TDerived>
|
||||
class PolymorphicHandler : public PolymorphicHandlerBase
|
||||
{
|
||||
public:
|
||||
@@ -97,6 +105,17 @@ public:
|
||||
static_cast<TSerializer*>(ser)->object(*fromBase(obj));
|
||||
}
|
||||
|
||||
void* getRootPtr(const void* obj) const final
|
||||
{
|
||||
return RTTI::template cast<TBase, TRoot>(
|
||||
static_cast<TBase*>(const_cast<void*>(obj)));
|
||||
}
|
||||
|
||||
size_t getDerivedTypeId() const final
|
||||
{
|
||||
return RTTI::template get<TDerived>();
|
||||
}
|
||||
|
||||
private:
|
||||
TDerived* fromBase(void* obj) const
|
||||
{
|
||||
@@ -109,6 +128,35 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
// Even though we don't serialize/deserialize abstract classes
|
||||
// object might still be accessed through abstract class, hence we need this
|
||||
// for type information
|
||||
template<typename RTTI, typename TRoot, typename TBase, typename TDerived>
|
||||
class AbstractPolymorphicHandler : public PolymorphicHandlerBase
|
||||
{
|
||||
public:
|
||||
void* create(const pointer_utils::PolyAllocWithTypeId& alloc) const
|
||||
{
|
||||
assert(false);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void destroy(const pointer_utils::PolyAllocWithTypeId& alloc, void* ptr) const
|
||||
{
|
||||
assert(false);
|
||||
};
|
||||
|
||||
void process(void* ser, void* obj) const { assert(false); }
|
||||
|
||||
void* getRootPtr(const void* obj) const
|
||||
{
|
||||
assert(false);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t getDerivedTypeId() const { return RTTI::template get<TDerived>(); };
|
||||
};
|
||||
|
||||
template<typename RTTI>
|
||||
class PolymorphicContext
|
||||
{
|
||||
@@ -137,47 +185,54 @@ private:
|
||||
template<typename TSerializer,
|
||||
template<typename>
|
||||
class THierarchy,
|
||||
typename TRoot,
|
||||
typename TBase,
|
||||
typename TDerived>
|
||||
void add()
|
||||
void add(size_t depth)
|
||||
{
|
||||
addToMap<TSerializer, TBase, TDerived>(std::is_abstract<TDerived>{});
|
||||
addChilds<TSerializer, THierarchy, TBase, TDerived>(
|
||||
typename THierarchy<TDerived>::Childs{});
|
||||
addToMap<TSerializer, TRoot, TBase, TDerived>(depth == 1,
|
||||
std::is_abstract<TDerived>{});
|
||||
addChilds<TSerializer, THierarchy, TRoot, TBase, TDerived>(
|
||||
depth + 1, typename THierarchy<TDerived>::Childs{});
|
||||
}
|
||||
|
||||
template<typename TSerializer,
|
||||
template<typename>
|
||||
class THierarchy,
|
||||
typename TRoot,
|
||||
typename TBase,
|
||||
typename TDerived,
|
||||
typename T1,
|
||||
typename... Tn>
|
||||
void addChilds(PolymorphicClassesList<T1, Tn...>)
|
||||
void addChilds(size_t depth, PolymorphicClassesList<T1, Tn...>)
|
||||
{
|
||||
static_assert(std::is_base_of<TDerived, T1>::value,
|
||||
"PolymorphicBaseClass<TBase> must derive a list of derived "
|
||||
"classes from TBase.");
|
||||
add<TSerializer, THierarchy, TBase, T1>();
|
||||
addChilds<TSerializer, THierarchy, TBase, TDerived>(
|
||||
PolymorphicClassesList<Tn...>{});
|
||||
// iterate through derived class hierarchy as well
|
||||
add<TSerializer, THierarchy, T1, T1>();
|
||||
add<TSerializer, THierarchy, TRoot, TBase, T1>(depth);
|
||||
addChilds<TSerializer, THierarchy, TRoot, TBase, TDerived>(
|
||||
depth, PolymorphicClassesList<Tn...>{});
|
||||
add<TSerializer, THierarchy, TRoot, T1, T1>(0);
|
||||
}
|
||||
|
||||
template<typename TSerializer,
|
||||
template<typename>
|
||||
class THierarchy,
|
||||
typename TRoot,
|
||||
typename TBase,
|
||||
typename TDerived>
|
||||
void addChilds(PolymorphicClassesList<>)
|
||||
void addChilds(size_t, PolymorphicClassesList<>)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename TSerializer, typename TBase, typename TDerived>
|
||||
void addToMap(std::false_type)
|
||||
template<typename TSerializer,
|
||||
typename TRoot,
|
||||
typename TBase,
|
||||
typename TDerived>
|
||||
void addToMap(bool directBase, std::false_type)
|
||||
{
|
||||
using THandler = PolymorphicHandler<RTTI, TSerializer, TBase, TDerived>;
|
||||
using THandler =
|
||||
PolymorphicHandler<RTTI, TSerializer, TRoot, TBase, TDerived>;
|
||||
BaseToDerivedKey key{ RTTI::template get<TBase>(),
|
||||
RTTI::template get<TDerived>() };
|
||||
pointer_utils::StdPolyAlloc<THandler> alloc{ _memResource };
|
||||
@@ -201,12 +256,51 @@ private:
|
||||
}
|
||||
it->second.push_back(key.derivedHash);
|
||||
}
|
||||
if (directBase) {
|
||||
auto it = _derivedToBaseArray.find(key.derivedHash);
|
||||
if (it == _derivedToBaseArray.end()) {
|
||||
it = _derivedToBaseArray
|
||||
.emplace(std::piecewise_construct,
|
||||
std::forward_as_tuple(key.derivedHash),
|
||||
std::forward_as_tuple(
|
||||
pointer_utils::StdPolyAlloc<size_t>{ _memResource }))
|
||||
.first;
|
||||
}
|
||||
it->second.push_back(key.baseHash);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TSerializer, typename TBase, typename TDerived>
|
||||
void addToMap(std::true_type)
|
||||
template<typename TSerializer,
|
||||
typename TRoot,
|
||||
typename TBase,
|
||||
typename TDerived>
|
||||
void addToMap(bool directBase, std::true_type)
|
||||
{
|
||||
// cannot add abstract class
|
||||
using THandler = AbstractPolymorphicHandler<RTTI, TRoot, TBase, TDerived>;
|
||||
BaseToDerivedKey key{ RTTI::template get<TBase>(),
|
||||
RTTI::template get<TDerived>() };
|
||||
pointer_utils::StdPolyAlloc<THandler> alloc{ _memResource };
|
||||
auto ptr = alloc.allocate(1);
|
||||
std::shared_ptr<THandler> handler(
|
||||
new (ptr) THandler{},
|
||||
[alloc](THandler* data) mutable {
|
||||
data->~THandler();
|
||||
alloc.deallocate(data, 1);
|
||||
},
|
||||
alloc);
|
||||
_baseToDerivedMap.emplace(key, std::move(handler)).second;
|
||||
if (directBase) {
|
||||
auto it = _derivedToBaseArray.find(key.derivedHash);
|
||||
if (it == _derivedToBaseArray.end()) {
|
||||
it = _derivedToBaseArray
|
||||
.emplace(std::piecewise_construct,
|
||||
std::forward_as_tuple(key.derivedHash),
|
||||
std::forward_as_tuple(
|
||||
pointer_utils::StdPolyAlloc<size_t>{ _memResource }))
|
||||
.first;
|
||||
}
|
||||
it->second.push_back(key.baseHash);
|
||||
}
|
||||
}
|
||||
|
||||
MemResourceBase* _memResource;
|
||||
@@ -234,6 +328,17 @@ private:
|
||||
std::vector<size_t, pointer_utils::StdPolyAlloc<size_t>>>>>
|
||||
_baseToDerivedArray;
|
||||
|
||||
// Used to iterate through hierarchy chain from most derived to the base(s)
|
||||
std::unordered_map<
|
||||
size_t,
|
||||
std::vector<size_t, pointer_utils::StdPolyAlloc<size_t>>,
|
||||
std::hash<size_t>,
|
||||
std::equal_to<size_t>,
|
||||
pointer_utils::StdPolyAlloc<
|
||||
std::pair<const size_t,
|
||||
std::vector<size_t, pointer_utils::StdPolyAlloc<size_t>>>>>
|
||||
_derivedToBaseArray;
|
||||
|
||||
public:
|
||||
explicit PolymorphicContext(MemResourceBase* memResource = nullptr)
|
||||
: _memResource{ memResource }
|
||||
@@ -244,6 +349,11 @@ public:
|
||||
std::pair<const size_t,
|
||||
std::vector<size_t, pointer_utils::StdPolyAlloc<size_t>>>>{
|
||||
memResource } }
|
||||
, _derivedToBaseArray{ pointer_utils::StdPolyAlloc<
|
||||
std::pair<const size_t,
|
||||
std::vector<size_t, pointer_utils::StdPolyAlloc<size_t>>>>{
|
||||
memResource } }
|
||||
|
||||
{
|
||||
}
|
||||
|
||||
@@ -269,7 +379,7 @@ public:
|
||||
typename... Tn>
|
||||
void registerBasesList(PolymorphicClassesList<T1, Tn...>)
|
||||
{
|
||||
add<TSerializer, THierarchy, T1, T1>();
|
||||
add<TSerializer, THierarchy, T1, T1, T1>(0);
|
||||
registerBasesList<TSerializer, THierarchy>(PolymorphicClassesList<Tn...>{});
|
||||
}
|
||||
|
||||
@@ -278,18 +388,6 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
// optional method, in case you want to construct base class hierarchy your
|
||||
// self
|
||||
template<typename TSerializer, typename TBase, typename TDerived>
|
||||
void registerSingleBaseBranch()
|
||||
{
|
||||
static_assert(std::is_base_of<TBase, TDerived>::value,
|
||||
"TDerived must be derived from TBase");
|
||||
static_assert(!std::is_abstract<TDerived>::value,
|
||||
"TDerived cannot be abstract");
|
||||
addToMap<TSerializer, TBase, TDerived>(std::false_type{});
|
||||
}
|
||||
|
||||
template<typename Serializer, typename TBase>
|
||||
void serialize(Serializer& ser, TBase& obj) const
|
||||
{
|
||||
@@ -338,7 +436,7 @@ public:
|
||||
// if object is null or different type, create new and assign it
|
||||
if (obj == nullptr || RTTI::template get<TBase>(*obj) != derivedHash) {
|
||||
if (obj) {
|
||||
destroyFnc(getPolymorphicHandler(*obj));
|
||||
destroyFnc(getPolymorphicHandler(obj));
|
||||
}
|
||||
obj = createFnc(handler);
|
||||
}
|
||||
@@ -349,12 +447,32 @@ public:
|
||||
|
||||
template<typename TBase>
|
||||
const std::shared_ptr<PolymorphicHandlerBase>& getPolymorphicHandler(
|
||||
TBase& obj) const
|
||||
TBase* obj) const
|
||||
{
|
||||
auto deleteHandlerIt = _baseToDerivedMap.find(BaseToDerivedKey{
|
||||
RTTI::template get<TBase>(), RTTI::template get<TBase>(obj) });
|
||||
assert(deleteHandlerIt != _baseToDerivedMap.end());
|
||||
return deleteHandlerIt->second;
|
||||
auto it = _baseToDerivedMap.find(BaseToDerivedKey{
|
||||
RTTI::template get<TBase>(), RTTI::template get<TBase>(*obj) });
|
||||
assert(it != _baseToDerivedMap.end());
|
||||
return it->second;
|
||||
}
|
||||
|
||||
template<typename TBase>
|
||||
const std::shared_ptr<PolymorphicHandlerBase>& getPolymorphicHandler() const
|
||||
{
|
||||
auto it = _baseToDerivedMap.find(BaseToDerivedKey{
|
||||
RTTI::template get<TBase>(), RTTI::template get<TBase>() });
|
||||
assert(it != _baseToDerivedMap.end());
|
||||
return it->second;
|
||||
}
|
||||
|
||||
const std::vector<size_t, pointer_utils::StdPolyAlloc<size_t>>*
|
||||
getDirectBases(size_t derivedTypeId) const
|
||||
{
|
||||
auto it = _derivedToBaseArray.find(derivedTypeId);
|
||||
if (it != _derivedToBaseArray.end()) {
|
||||
return &it->second;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#include <bitsery/ext/inheritance.h>
|
||||
#include <bitsery/ext/pointer.h>
|
||||
#include <bitsery/ext/std_smart_ptr.h>
|
||||
#include <bitsery/traits/string.h>
|
||||
#include <bitsery/traits/vector.h>
|
||||
|
||||
#include "serialization_test_utils.h"
|
||||
#include <gmock/gmock.h>
|
||||
@@ -77,6 +79,27 @@ serialize(S& s, Derived& o)
|
||||
s.value1b(o.y);
|
||||
}
|
||||
|
||||
struct DerivedSibling : virtual Base
|
||||
{
|
||||
uint32_t y{};
|
||||
|
||||
DerivedSibling() = default;
|
||||
|
||||
DerivedSibling(uint8_t x_, uint32_t y_)
|
||||
{
|
||||
x = x_;
|
||||
y = y_;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename S>
|
||||
void
|
||||
serialize(S& s, DerivedSibling& o)
|
||||
{
|
||||
s.ext(o, VirtualBaseClass<Base>{});
|
||||
s.value4b(o.y);
|
||||
}
|
||||
|
||||
struct MoreDerived : Derived
|
||||
{
|
||||
uint8_t z{};
|
||||
@@ -741,6 +764,103 @@ TEST_F(SerializeExtensionStdSmartSharedPtr,
|
||||
EXPECT_THAT(baseRes1.use_count(), Eq(0));
|
||||
}
|
||||
|
||||
struct MightMatchVecLayout
|
||||
{
|
||||
size_t begin;
|
||||
size_t end;
|
||||
};
|
||||
|
||||
template<typename S>
|
||||
void
|
||||
serialize(S& s, MightMatchVecLayout& o)
|
||||
{
|
||||
s.template value<sizeof(size_t)>(o.begin);
|
||||
s.template value<sizeof(size_t)>(o.end);
|
||||
}
|
||||
|
||||
TEST_F(SerializeExtensionStdSmartSharedPtr,
|
||||
NonPolymorphicObservedPointerMustMatchActuallyDeserializedObjectType)
|
||||
{
|
||||
std::shared_ptr<std::vector<uint8_t>> baseData1{ new std::vector<uint8_t>{
|
||||
'a', 'b', 'c' } };
|
||||
std::shared_ptr<MightMatchVecLayout> baseData2{ new MightMatchVecLayout{
|
||||
1, 2 } };
|
||||
auto& ser = createSerializer();
|
||||
ser.ext(baseData1, StdSmartPtr{}, [](auto& ser, std::vector<uint8_t>& o) {
|
||||
ser.container1b(o, 100);
|
||||
});
|
||||
ser.ext(baseData2, StdSmartPtr{});
|
||||
// hack a buffer, so that during deserialization we would point to already
|
||||
// deserialized object
|
||||
sctx.buf[5] = 0x01;
|
||||
|
||||
std::shared_ptr<std::vector<uint8_t>> baseRes1{};
|
||||
std::shared_ptr<MightMatchVecLayout> baseRes2{};
|
||||
auto& des = createDeserializer();
|
||||
des.ext(baseRes1, StdSmartPtr{}, [](auto& ser, std::vector<uint8_t>& o) {
|
||||
ser.container1b(o, 100);
|
||||
});
|
||||
// if we would blindly trust the input (that the object is already
|
||||
// deserialized), we would end up
|
||||
// using memory of that object. In this case, we will get internal
|
||||
// representation of std::vector.
|
||||
// If this object is available to attacker, he'll be able to see leaked
|
||||
// address of that object,
|
||||
// allowing him to bypass ASLR.
|
||||
des.ext(baseRes2, StdSmartPtr{});
|
||||
|
||||
EXPECT_THAT(baseRes2.get(), ::testing::IsNull());
|
||||
auto err = des.adapter().error();
|
||||
EXPECT_THAT(err, Eq(bitsery::ReaderError::InvalidPointer));
|
||||
EXPECT_TRUE(isPointerContextValid());
|
||||
}
|
||||
|
||||
TEST_F(
|
||||
SerializeExtensionStdSmartSharedPtr,
|
||||
PolymorphicObservedPointerMustBeInInheritanceChainOfActuallyDeserializedObjectType1)
|
||||
{
|
||||
std::shared_ptr<Base> baseData1{ new MoreDerived{ 3, 7, 10 } };
|
||||
std::shared_ptr<Base> baseData2{};
|
||||
baseData2 = baseData1;
|
||||
auto& ser = createSerializer();
|
||||
ser.ext(baseData1, StdSmartPtr{});
|
||||
ser.ext(baseData2, StdSmartPtr{});
|
||||
|
||||
std::shared_ptr<Base> baseRes1{};
|
||||
std::shared_ptr<Derived> baseRes2{};
|
||||
auto& des = createDeserializer();
|
||||
des.ext(baseRes1, StdSmartPtr{});
|
||||
des.ext(baseRes2, StdSmartPtr{});
|
||||
|
||||
EXPECT_THAT(baseRes2.get(), ::testing::NotNull());
|
||||
auto err = des.adapter().error();
|
||||
EXPECT_THAT(err, Eq(bitsery::ReaderError::NoError));
|
||||
EXPECT_TRUE(isPointerContextValid());
|
||||
}
|
||||
|
||||
TEST_F(
|
||||
SerializeExtensionStdSmartSharedPtr,
|
||||
PolymorphicObservedPointerMustBeInInheritanceChainOfActuallyDeserializedObjectType2)
|
||||
{
|
||||
std::shared_ptr<Base> baseData1{ new MoreDerived{ 3, 7, 10 } };
|
||||
std::shared_ptr<Base> baseData2{};
|
||||
baseData2 = baseData1;
|
||||
auto& ser = createSerializer();
|
||||
ser.ext(baseData1, StdSmartPtr{});
|
||||
ser.ext(baseData2, StdSmartPtr{});
|
||||
|
||||
std::shared_ptr<Base> baseRes1{};
|
||||
std::shared_ptr<DerivedSibling> baseRes2{};
|
||||
auto& des = createDeserializer();
|
||||
des.ext(baseRes1, StdSmartPtr{});
|
||||
des.ext(baseRes2, StdSmartPtr{});
|
||||
|
||||
EXPECT_THAT(baseRes2.get(), ::testing::IsNull());
|
||||
auto err = des.adapter().error();
|
||||
EXPECT_THAT(err, Eq(bitsery::ReaderError::InvalidPointer));
|
||||
EXPECT_TRUE(isPointerContextValid());
|
||||
}
|
||||
|
||||
struct TestSharedFromThis
|
||||
: public std::enable_shared_from_this<TestSharedFromThis>
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user