summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--source/core/slang-relative-ptr.h18
-rw-r--r--source/slang/slang-fossil.cpp36
-rw-r--r--source/slang/slang-fossil.h185
-rw-r--r--source/slang/slang-serialize-ast.cpp166
-rw-r--r--source/slang/slang-serialize-fossil.cpp532
-rw-r--r--source/slang/slang-serialize-fossil.h483
-rw-r--r--source/slang/slang-serialize-ir.cpp11
-rw-r--r--source/slang/slang-serialize-riff.cpp69
-rw-r--r--source/slang/slang-serialize-riff.h144
-rw-r--r--source/slang/slang-serialize.h422
10 files changed, 1203 insertions, 863 deletions
diff --git a/source/core/slang-relative-ptr.h b/source/core/slang-relative-ptr.h
index 6c4bc942c..1d47545d5 100644
--- a/source/core/slang-relative-ptr.h
+++ b/source/core/slang-relative-ptr.h
@@ -41,14 +41,14 @@ public:
using Offset = typename Traits::Offset;
using UOffset = typename Traits::UOffset;
- RelativePtr() = default;
- RelativePtr(RelativePtr const& ptr) { set(ptr); }
- RelativePtr(RelativePtr&& ptr) { set(ptr); }
- RelativePtr(T* ptr) { set(ptr); }
+ SLANG_FORCE_INLINE RelativePtr() = default;
+ SLANG_FORCE_INLINE RelativePtr(RelativePtr const& ptr) { set(ptr); }
+ SLANG_FORCE_INLINE RelativePtr(RelativePtr&& ptr) { set(ptr); }
+ SLANG_FORCE_INLINE RelativePtr(T* ptr) { set(ptr); }
- void operator=(RelativePtr const& ptr) { set(ptr); }
- void operator=(RelativePtr&& ptr) { set(ptr); }
- void operator=(T* ptr) { set(ptr); }
+ SLANG_FORCE_INLINE void operator=(RelativePtr const& ptr) { set(ptr); }
+ SLANG_FORCE_INLINE void operator=(RelativePtr&& ptr) { set(ptr); }
+ SLANG_FORCE_INLINE void operator=(T* ptr) { set(ptr); }
T* get() const
{
@@ -79,8 +79,8 @@ public:
SLANG_ASSERT(intptr_t(_offset) == offsetVal);
}
- operator T*() const { return get(); }
- T* operator->() const { return get(); }
+ SLANG_FORCE_INLINE operator T*() const { return get(); }
+ SLANG_FORCE_INLINE T* operator->() const { return get(); }
private:
Offset _offset = 0;
diff --git a/source/slang/slang-fossil.cpp b/source/slang/slang-fossil.cpp
index 204430901..b5074eac6 100644
--- a/source/slang/slang-fossil.cpp
+++ b/source/slang/slang-fossil.cpp
@@ -77,30 +77,6 @@ Fossil::AnyValPtr getRootValue(void const* data, Size size)
} // namespace Fossil
-Size FossilizedStringObj::getSize() const
-{
- auto sizePtr = (FossilUInt*)this - 1;
- return Size(*sizePtr);
-}
-
-UnownedTerminatedStringSlice FossilizedStringObj::get() const
-{
- auto size = getSize();
- return UnownedTerminatedStringSlice((char*)this, size);
-}
-
-Count FossilizedContainerObjBase::getElementCount() const
-{
- auto countPtr = (FossilUInt*)this - 1;
- return Size(*countPtr);
-}
-
-FossilizedValLayout* FossilizedVariantObj::getContentLayout() const
-{
- auto layoutPtrPtr = (FossilizedPtr<FossilizedValLayout>*)this - 1;
- return (*layoutPtrPtr).get();
-}
-
Fossil::AnyValRef Fossil::ValRef<FossilizedContainerObjBase>::getElement(Index index) const
{
SLANG_ASSERT(index >= 0);
@@ -137,16 +113,4 @@ Fossil::AnyValRef Fossil::ValRef<FossilizedRecordVal>::getField(Index index) con
return Fossil::AnyValRef(fieldPtr, fieldInfo->layout);
}
-#if 0
-FossilizedValRef getVariantContent(FossilizedVariantObjRef variantRef)
-{
- return getVariantContent(variantRef.getData());
-}
-#endif
-
-Fossil::AnyValPtr getVariantContentPtr(FossilizedVariantObj* variantPtr)
-{
- return Fossil::AnyValPtr(variantPtr->getContentDataPtr(), variantPtr->getContentLayout());
-}
-
} // namespace Slang
diff --git a/source/slang/slang-fossil.h b/source/slang/slang-fossil.h
index 8d2465ddb..b5fd9b7b5 100644
--- a/source/slang/slang-fossil.h
+++ b/source/slang/slang-fossil.h
@@ -69,7 +69,10 @@ public:
using Layout = FossilizedPtrLikeLayout;
- static bool isMatchingKind(FossilizedValKind kind) { return kind == FossilizedValKind::Ptr; }
+ SLANG_FORCE_INLINE static bool isMatchingKind(FossilizedValKind kind)
+ {
+ return kind == FossilizedValKind::Ptr;
+ }
};
static_assert(sizeof(FossilizedPtr<void>) == sizeof(uint32_t));
@@ -150,11 +153,11 @@ public:
using Layout = FossilizedValLayout;
static const FossilizedValKind kKind = Kind;
- T const& get() const { return _value; }
+ SLANG_FORCE_INLINE T const& get() const { return _value; }
- operator T const&() const { return _value; }
+ SLANG_FORCE_INLINE operator T const&() const { return _value; }
- static bool isMatchingKind(FossilizedValKind kind) { return kind == kKind; }
+ SLANG_FORCE_INLINE static bool isMatchingKind(FossilizedValKind kind) { return kind == kKind; }
private:
T _value;
@@ -207,11 +210,11 @@ public:
using Layout = FossilizedValLayout;
static const FossilizedValKind kKind = FossilizedValKind::Bool;
- bool get() const { return _value != 0; }
+ SLANG_FORCE_INLINE bool get() const { return _value != 0; }
- operator bool() const { return get(); }
+ SLANG_FORCE_INLINE operator bool() const { return get(); }
- static bool isMatchingKind(FossilizedValKind kind) { return kind == kKind; }
+ SLANG_FORCE_INLINE static bool isMatchingKind(FossilizedValKind kind) { return kind == kKind; }
private:
uint8_t _value;
@@ -231,9 +234,9 @@ template<typename LiveType, typename FossilizedAsType>
struct FossilizedViaCastVal
{
public:
- LiveType get() const { return LiveType(_value.get()); }
+ SLANG_FORCE_INLINE LiveType get() const { return LiveType(_value.get()); }
- operator LiveType() const { return get(); }
+ SLANG_FORCE_INLINE operator LiveType() const { return get(); }
private:
@@ -268,11 +271,11 @@ public:
Size getSize() const;
UnownedTerminatedStringSlice get() const;
- operator UnownedTerminatedStringSlice() const { return get(); }
+ SLANG_FORCE_INLINE operator UnownedTerminatedStringSlice() const { return get(); }
using Layout = FossilizedValLayout;
- static bool isMatchingKind(FossilizedValKind kind)
+ SLANG_FORCE_INLINE static bool isMatchingKind(FossilizedValKind kind)
{
return kind == FossilizedValKind::StringObj;
}
@@ -308,9 +311,9 @@ public:
Count getElementCount() const;
- void const* getBuffer() const { return this; }
+ SLANG_FORCE_INLINE void const* getBuffer() const { return this; }
- static bool isMatchingKind(FossilizedValKind kind)
+ SLANG_FORCE_INLINE static bool isMatchingKind(FossilizedValKind kind)
{
switch (kind)
{
@@ -342,7 +345,7 @@ public:
struct FossilizedArrayObjBase : FossilizedContainerObjBase
{
public:
- static bool isMatchingKind(FossilizedValKind kind)
+ SLANG_FORCE_INLINE static bool isMatchingKind(FossilizedValKind kind)
{
return kind == FossilizedValKind::ArrayObj;
}
@@ -419,13 +422,13 @@ struct FossilizedPtrLikeLayout
struct FossilizedOptionalObjBase
{
public:
- void* getValue() { return this; }
+ SLANG_FORCE_INLINE void* getValue() { return this; }
- void const* getValue() const { return this; }
+ SLANG_FORCE_INLINE void const* getValue() const { return this; }
using Layout = FossilizedPtrLikeLayout;
- static bool isMatchingKind(FossilizedValKind kind)
+ SLANG_FORCE_INLINE static bool isMatchingKind(FossilizedValKind kind)
{
return kind == FossilizedValKind::OptionalObj;
}
@@ -440,17 +443,17 @@ private:
template<typename T>
struct FossilizedOptionalObj : FossilizedOptionalObjBase
{
- T* getValue() { return this; }
+ SLANG_FORCE_INLINE T* getValue() { return this; }
- T const* getValue() const { return this; }
+ SLANG_FORCE_INLINE T const* getValue() const { return this; }
};
template<typename T>
struct FossilizedOptional
{
public:
- explicit operator bool() const { return _value.get() != nullptr; }
- T const& operator*() const { return *_value.get(); }
+ SLANG_FORCE_INLINE explicit operator bool() const { return _value.get() != nullptr; }
+ SLANG_FORCE_INLINE T const& operator*() const { return *_value.get(); }
private:
FossilizedPtr<T> _value;
@@ -475,40 +478,40 @@ static_assert(sizeof(Fossilized<std::optional<double>>) == sizeof(FossilUInt));
struct FossilizedString
{
public:
- Size getSize() const { return _obj ? _obj->getSize() : 0; }
+ SLANG_FORCE_INLINE Size getSize() const { return _obj ? _obj->getSize() : 0; }
- UnownedTerminatedStringSlice get() const
+ SLANG_FORCE_INLINE UnownedTerminatedStringSlice get() const
{
return _obj ? _obj->get() : UnownedTerminatedStringSlice();
}
- operator UnownedTerminatedStringSlice() const { return get(); }
+ SLANG_FORCE_INLINE operator UnownedTerminatedStringSlice() const { return get(); }
private:
FossilizedPtr<FossilizedStringObj> _obj;
};
-inline int compare(FossilizedString const& lhs, UnownedStringSlice const& rhs)
+SLANG_FORCE_INLINE int compare(FossilizedString const& lhs, UnownedStringSlice const& rhs)
{
return compare(lhs.get(), rhs);
}
-inline bool operator==(FossilizedString const& left, UnownedStringSlice const& right)
+SLANG_FORCE_INLINE bool operator==(FossilizedString const& left, UnownedStringSlice const& right)
{
return left.get() == right;
}
-inline bool operator!=(FossilizedString const& left, UnownedStringSlice const& right)
+SLANG_FORCE_INLINE bool operator!=(FossilizedString const& left, UnownedStringSlice const& right)
{
return left.get() != right;
}
-inline bool operator==(FossilizedStringObj const& left, UnownedStringSlice const& right)
+SLANG_FORCE_INLINE bool operator==(FossilizedStringObj const& left, UnownedStringSlice const& right)
{
return left.get() == right;
}
-inline bool operator!=(FossilizedStringObj const& left, UnownedStringSlice const& right)
+SLANG_FORCE_INLINE bool operator!=(FossilizedStringObj const& left, UnownedStringSlice const& right)
{
return left.get() != right;
}
@@ -531,21 +534,21 @@ template<typename T>
struct FossilizedContainer
{
public:
- Count getElementCount() const
+ SLANG_FORCE_INLINE Count getElementCount() const
{
if (!_obj)
return 0;
return _obj->getElementCount();
}
- T const* getBuffer() const
+ SLANG_FORCE_INLINE T const* getBuffer() const
{
if (!_obj)
return nullptr;
return (T const*)_obj.get()->getBuffer();
}
- T const* begin() const { return getBuffer(); }
- T const* end() const { return getBuffer() + getElementCount(); }
+ SLANG_FORCE_INLINE T const* begin() const { return getBuffer(); }
+ SLANG_FORCE_INLINE T const* end() const { return getBuffer() + getElementCount(); }
private:
FossilizedPtr<FossilizedContainerObj<T>> _obj;
@@ -555,7 +558,7 @@ template<typename T>
struct FossilizedArray : FossilizedContainer<T>
{
public:
- T const& operator[](Index index) const
+ SLANG_FORCE_INLINE T const& operator[](Index index) const
{
SLANG_ASSERT(index >= 0 && index < this->getElementCount());
return this->getBuffer()[index];
@@ -611,7 +614,7 @@ struct FossilizedTypeTraits<std::pair<K, V>>
struct FossilizedDictionaryObjBase : FossilizedContainerObjBase
{
public:
- static bool isMatchingKind(FossilizedValKind kind)
+ SLANG_FORCE_INLINE static bool isMatchingKind(FossilizedValKind kind)
{
return kind == FossilizedValKind::DictionaryObj;
}
@@ -677,7 +680,7 @@ struct FossilizedRecordVal
public:
using Layout = FossilizedRecordLayout;
- static bool isMatchingKind(FossilizedValKind kind)
+ SLANG_FORCE_INLINE static bool isMatchingKind(FossilizedValKind kind)
{
switch (kind)
{
@@ -705,10 +708,10 @@ public:
FossilizedValLayout* getContentLayout() const;
- void* getContentDataPtr() { return this; }
- void const* getContentDataPtr() const { return this; }
+ SLANG_FORCE_INLINE void* getContentDataPtr() { return this; }
+ SLANG_FORCE_INLINE void const* getContentDataPtr() const { return this; }
- static bool isMatchingKind(FossilizedValKind kind)
+ SLANG_FORCE_INLINE static bool isMatchingKind(FossilizedValKind kind)
{
return kind == FossilizedValKind::VariantObj;
}
@@ -802,11 +805,11 @@ public:
/// Construct a null reference.
///
- ValRefBase() {}
+ SLANG_FORCE_INLINE ValRefBase() {}
/// Construct a reference to the given `data`, assuming it has the given `layout`.
///
- ValRefBase(T* data, Layout const* layout)
+ SLANG_FORCE_INLINE ValRefBase(T* data, Layout const* layout)
: _data(data), _layout(layout)
{
}
@@ -816,34 +819,36 @@ public:
/// Only enabled if `U*` is convertible to `T*`.
///
template<typename U>
- ValRefBase(ValRefBase<U> ref, std::enable_if_t<std::is_convertible_v<U*, T*>, void>* = nullptr)
+ SLANG_FORCE_INLINE ValRefBase(
+ ValRefBase<U> ref,
+ std::enable_if_t<std::is_convertible_v<U*, T*>, void>* = nullptr)
: _data(ref.getDataPtr()), _layout((Layout const*)ref.getLayout())
{
}
/// Get a pointer to the value being referenced.
///
- T* getDataPtr() const { return _data; }
+ SLANG_FORCE_INLINE T* getDataPtr() const { return _data; }
/// Get a reference to the value being referenced.
///
/// This accessor is disabled in the case where `T` is `void`.
///
template<typename U = T>
- std::enable_if_t<!std::is_same_v<U, void>, T>& getDataRef() const
+ SLANG_FORCE_INLINE std::enable_if_t<!std::is_same_v<U, void>, T>& getDataRef() const
{
return *_data;
}
/// Get the layout of the value being referenced.
///
- Layout const* getLayout() const { return _layout; }
+ SLANG_FORCE_INLINE Layout const* getLayout() const { return _layout; }
/// Get the kind of value being referenced.
///
/// This reference must not be null.
///
- FossilizedValKind getKind() const
+ SLANG_FORCE_INLINE FossilizedValKind getKind() const
{
SLANG_ASSERT(getLayout());
return getLayout()->kind;
@@ -881,12 +886,12 @@ public:
/// Construct a null pointer.
///
- ValPtr() {}
- ValPtr(std::nullptr_t) {}
+ SLANG_FORCE_INLINE ValPtr() {}
+ SLANG_FORCE_INLINE ValPtr(std::nullptr_t) {}
/// Construct a pointer to the given `data`, assuming it has the given `layout`.
///
- ValPtr(T* data, TargetLayout const* layout)
+ SLANG_FORCE_INLINE ValPtr(T* data, TargetLayout const* layout)
: _ref(data, layout)
{
}
@@ -897,7 +902,7 @@ public:
/// We define it as a constructor as a slightly more preferable alternative
/// to overloading prefix `operator&` (which is almost always a Bad Idea)
///
- explicit ValPtr(ValRef<T> ref)
+ SLANG_FORCE_INLINE explicit ValPtr(ValRef<T> ref)
: _ref(ref)
{
}
@@ -907,25 +912,27 @@ public:
/// Only enabled if `U*` is convertible to `T*`.
///
template<typename U>
- ValPtr(ValPtr<U> ptr, std::enable_if_t<std::is_convertible_v<U*, T*>, void>* = nullptr)
+ SLANG_FORCE_INLINE ValPtr(
+ ValPtr<U> ptr,
+ std::enable_if_t<std::is_convertible_v<U*, T*>, void>* = nullptr)
: _ref(*ptr)
{
}
/// Get a pointer to the value being referenced.
///
- T* getDataPtr() const { return _ref.getDataPtr(); }
+ SLANG_FORCE_INLINE T* getDataPtr() const { return _ref.getDataPtr(); }
/// Get the layout of the value being referenced.
///
- TargetLayout* getLayout() const { return _ref.getLayout(); }
+ SLANG_FORCE_INLINE TargetLayout const* getLayout() const { return _ref.getLayout(); }
- T* get() const { return _ref.getDataPtr(); }
- operator T*() const { return get(); }
+ SLANG_FORCE_INLINE T* get() const { return _ref.getDataPtr(); }
+ SLANG_FORCE_INLINE operator T*() const { return get(); }
/// Deference this `ValPtr` to get a `ValRef`.
///
- ValRef<T> operator*() const { return _ref; }
+ SLANG_FORCE_INLINE ValRef<T> operator*() const { return _ref; }
/// Deference this `ValPtr` for member access.
///
@@ -939,7 +946,7 @@ public:
/// that behavior is for the `operator->` on `ValPtr`
/// to return a pointer to a `ValRef`.
///
- ValRef<T> const* operator->() const { return &_ref; }
+ SLANG_FORCE_INLINE ValRef<T> const* operator->() const { return &_ref; }
private:
ValRef<T> _ref;
@@ -948,7 +955,7 @@ private:
/// Get a `ValPtr` pointing to the same value as the given `ref`.
///
template<typename T>
-inline ValPtr<T> getAddress(ValRef<T> ref)
+SLANG_FORCE_INLINE ValPtr<T> getAddress(ValRef<T> ref)
{
return ValPtr<T>(ref);
}
@@ -973,10 +980,10 @@ struct ValRef<FossilizedStringObj> : ValRefBase<FossilizedStringObj>
public:
using ValRefBase<FossilizedStringObj>::ValRefBase;
- Size getSize() const { return getDataPtr()->getSize(); }
- UnownedTerminatedStringSlice get() const { return getDataPtr()->get(); }
+ SLANG_FORCE_INLINE Size getSize() const { return getDataPtr()->getSize(); }
+ SLANG_FORCE_INLINE UnownedTerminatedStringSlice get() const { return getDataPtr()->get(); }
- operator UnownedTerminatedStringSlice() const { return get(); }
+ SLANG_FORCE_INLINE operator UnownedTerminatedStringSlice() const { return get(); }
};
@@ -986,7 +993,7 @@ struct ValRef<FossilizedContainerObjBase> : ValRefBase<FossilizedContainerObjBas
public:
using ValRefBase<FossilizedContainerObjBase>::ValRefBase;
- Count getElementCount() const
+ SLANG_FORCE_INLINE Count getElementCount() const
{
auto data = this->getDataPtr();
if (!data)
@@ -1004,7 +1011,7 @@ struct ValRef<FossilizedArrayObjBase> : ValRefBase<FossilizedArrayObjBase>
public:
using ValRefBase<FossilizedArrayObjBase>::ValRefBase;
- Count getElementCount() const
+ SLANG_FORCE_INLINE Count getElementCount() const
{
auto data = this->getDataPtr();
if (!data)
@@ -1022,7 +1029,7 @@ struct ValRef<FossilizedDictionaryObjBase> : ValRefBase<FossilizedDictionaryObjB
public:
using ValRefBase<FossilizedDictionaryObjBase>::ValRefBase;
- Count getElementCount() const
+ SLANG_FORCE_INLINE Count getElementCount() const
{
auto data = this->getDataPtr();
if (!data)
@@ -1038,9 +1045,9 @@ struct ValRef<FossilizedOptionalObjBase> : ValRefBase<FossilizedOptionalObjBase>
public:
using ValRefBase<FossilizedOptionalObjBase>::ValRefBase;
- bool hasValue() const { return this->getDataPtr() != nullptr; }
+ SLANG_FORCE_INLINE bool hasValue() const { return this->getDataPtr() != nullptr; }
- AnyValRef getValue() const
+ SLANG_FORCE_INLINE AnyValRef getValue() const
{
SLANG_ASSERT(hasValue());
return AnyValRef(this->getDataPtr(), this->getLayout()->elementLayout.get());
@@ -1053,7 +1060,7 @@ struct ValRef<FossilizedRecordVal> : ValRefBase<FossilizedRecordVal>
public:
using ValRefBase<FossilizedRecordVal>::ValRefBase;
- Count getFieldCount() const { return getLayout()->fieldCount; }
+ SLANG_FORCE_INLINE Count getFieldCount() const { return getLayout()->fieldCount; }
AnyValRef getField(Index index) const;
};
@@ -1064,13 +1071,13 @@ struct ValRef<FossilizedPtr<T>> : ValRefBase<FossilizedPtr<T>>
public:
using ValRefBase<FossilizedPtr<T>>::ValRefBase;
- ValRef<T> getTargetValRef() const
+ SLANG_FORCE_INLINE ValRef<T> getTargetValRef() const
{
auto ptrPtr = this->getDataPtr();
return ValRef<T>(*ptrPtr, this->getLayout()->elementLayout.get());
}
- ValPtr<T> getTargetValPtr() const { return ValPtr<T>(getTargetValRef()); }
+ SLANG_FORCE_INLINE ValPtr<T> getTargetValPtr() const { return ValPtr<T>(getTargetValRef()); }
// ValRef<T> operator*() const;
};
@@ -1085,19 +1092,19 @@ public:
/// Statically cast a pointer to a fossilized value.
///
template<typename T>
-ValPtr<T> cast(AnyValPtr valPtr)
+SLANG_FORCE_INLINE ValPtr<T> cast(AnyValPtr valPtr)
{
- if (!valPtr)
- return ValPtr<T>();
+ // if (!valPtr)
+ // return ValPtr<T>();
return ValPtr<T>(
static_cast<T*>(valPtr.getDataPtr()),
- (typename T::Layout*)(valPtr->getLayout()));
+ (typename T::Layout*)(valPtr.getLayout()));
}
/// Dynamic cast of a pointer to a fossilized value.
///
template<typename T>
-ValPtr<T> as(AnyValPtr valPtr)
+SLANG_FORCE_INLINE ValPtr<T> as(AnyValPtr valPtr)
{
if (!valPtr || !T::isMatchingKind(valPtr->getKind()))
{
@@ -1174,6 +1181,36 @@ Fossil::AnyValPtr getRootValue(ISlangBlob* blob);
Fossil::AnyValPtr getRootValue(void const* data, Size size);
} // namespace Fossil
+SLANG_FORCE_INLINE Size FossilizedStringObj::getSize() const
+{
+ auto sizePtr = (FossilUInt*)this - 1;
+ return Size(*sizePtr);
+}
+
+SLANG_FORCE_INLINE UnownedTerminatedStringSlice FossilizedStringObj::get() const
+{
+ auto size = getSize();
+ return UnownedTerminatedStringSlice((char*)this, size);
+}
+
+SLANG_FORCE_INLINE Count FossilizedContainerObjBase::getElementCount() const
+{
+ auto countPtr = (FossilUInt*)this - 1;
+ return Size(*countPtr);
+}
+
+SLANG_FORCE_INLINE FossilizedValLayout* FossilizedVariantObj::getContentLayout() const
+{
+ auto layoutPtrPtr = (FossilizedPtr<FossilizedValLayout>*)this - 1;
+ return (*layoutPtrPtr).get();
+}
+
+SLANG_FORCE_INLINE Fossil::AnyValPtr getVariantContentPtr(FossilizedVariantObj* variantPtr)
+{
+ return Fossil::AnyValPtr(variantPtr->getContentDataPtr(), variantPtr->getContentLayout());
+}
+
+
} // namespace Slang
#endif
diff --git a/source/slang/slang-serialize-ast.cpp b/source/slang/slang-serialize-ast.cpp
index a288b3bd2..be959b8a1 100644
--- a/source/slang/slang-serialize-ast.cpp
+++ b/source/slang/slang-serialize-ast.cpp
@@ -1,6 +1,7 @@
// slang-serialize-ast.cpp
#include "slang-serialize-ast.h"
+#include "core/slang-performance-profiler.h"
#include "slang-ast-dispatch.h"
#include "slang-check.h"
#include "slang-compiler.h"
@@ -84,7 +85,8 @@ namespace Slang
// they are all just getting dumped here in the AST serialization logic, because
// it is currenly the only place that cares about this stuff.
//
-void serialize(Serializer const&, RefObject&)
+template<typename S>
+void serialize(S const&, RefObject&)
{
// There's actually no data stored in a `RefObject`, since it only exists
// to make reference-counting possible for other types. This function is
@@ -136,7 +138,8 @@ struct FossilizedTypeTraits<RefObject>
// While we could include this among the types we handle using fiddle,
// let's implement it by hand here, starting with the `serialize()` function:
//
-void serialize(Serializer const& serializer, MatrixCoord& value)
+template<typename S>
+void serialize(S const& serializer, MatrixCoord& value)
{
// We start with one of the `SLANG_SCOPED_SERIALIZER_*`
// macros, which basically just handles calling
@@ -210,7 +213,8 @@ struct FossilizedTypeTraits<MatrixCoord>
// as a single scalar value. We'll define our `serialize()` function
// so that it serializes that "raw" value instead:
//
-void serialize(Serializer const& serializer, SemanticVersion& value)
+template<typename S>
+void serialize(S const& serializer, SemanticVersion& value)
{
// This function is doing something a little "clever"
// handle the fact that it might be used to either
@@ -373,33 +377,17 @@ struct ContainerDeclDirectMemberDeclsInfo
};
//
-// Okay, that's enough examples for now. Let's move on to the next big
-// topic...
-//
// Many types in the AST need additional context information to be able to
-// read or write them properly, so instead of passing around the basic
-// `Serializer` type (which wraps an `ISerializerImpl`), for those types
-// that need extra context we will be passing around an `ASTSerializer`
-// (which wraps an `IASTSerializerImpl`, with the latter interface providing
-// the callbacks to handle the data types that need special-case behavior.
+// read or write them properly, so the concrete serializer type being passed
+// around will include an additional "context" type, that will be either
+// `ASTSerialReadContext` or `ASTSerialWriteContext`, depending on the mode
+// in which serialization is being performed.
+//
+// We could define a base class or interface with `virtual` functions for
+// accessing all of the relevant context, but because we are already
+// using template specialization, it is easier to just ensure that the
+// relevant context types both provide the required operations.
//
-
-struct ASTSerialContext;
-using ASTSerializer = Serializer_<ISerializerImpl, ASTSerialContext>;
-
-/// Context interface for AST serialization
-struct ASTSerialContext : SourceLocSerialContext
-{
-public:
- virtual void handleASTNode(ASTSerializer const& serializer, NodeBase*& value) = 0;
- virtual void handleASTNodeContents(ASTSerializer const& serializer, NodeBase* value) = 0;
- virtual void handleName(ASTSerializer const& serializer, Name*& value) = 0;
- virtual void handleToken(ASTSerializer const& serializer, Token& value) = 0;
- virtual void handleContainerDeclDirectMemberDecls(
- ASTSerializer const& serializer,
- ContainerDeclDirectMemberDecls& value) = 0;
-};
-
//
// Now that we've covered some of the big-picture structure, and shown
@@ -439,7 +427,8 @@ public:
%for _,T in ipairs(enumTypeNames) do
/// Serialize a `value` of type `$T`.
-void serialize(Serializer const& serializer, $T& value)
+template<typename S>
+void serialize(S const& serializer, $T& value)
{
serializeEnum(serializer, value);
}
@@ -518,7 +507,8 @@ struct Fossilized_$T;
SLANG_DECLARE_FOSSILIZED_TYPE($T, Fossilized_$T);
/// Serialize a `$T`
-void serialize(ASTSerializer const& serializer, $T& value);
+template<typename S>
+void serialize(S const& serializer, $T& value);
%end
#else // FIDDLE OUTPUT:
#define FIDDLE_GENERATED_OUTPUT_ID 1
@@ -545,7 +535,8 @@ struct Fossilized_$T;
SLANG_DECLARE_FOSSILIZED_TYPE($T, Fossilized_$T);
/// Serialize the content of a `$T`
-void _serializeASTNodeContents(ASTSerializer const& serializer, $T* value);
+template<typename S>
+void _serializeASTNodeContents(S const& serializer, $T* value);
%end
#else // FIDDLE OUTPUT:
#define FIDDLE_GENERATED_OUTPUT_ID 2
@@ -566,9 +557,11 @@ void _serializeASTNodeContents(ASTSerializer const& serializer, $T* value);
/// lower-level serialization operations to an underlying
/// `ISerializerImpl`.
///
-struct ASTSerialWriteContext : ASTSerialContext
+struct ASTSerialWriteContext : SourceLocSerialContext
{
public:
+ using ASTSerializer = Serializer<Fossil::SerialWriter, ASTSerialWriteContext>;
+
/// Construct a context for writing a serialized AST.
///
/// * `module` is the module that is being serialized, and will be
@@ -587,21 +580,23 @@ private:
ModuleDecl* _module = nullptr;
SerialSourceLocWriter* _sourceLocWriter = nullptr;
+public:
//
// For the most part, this type just implements the methods
// of the `IASTSerializerImpl` interface, and then has some
// support routines needed by those implementations.
//
- virtual void handleName(ASTSerializer const& serializer, Name*& value) override;
- virtual void handleToken(ASTSerializer const& serializer, Token& value) override;
- virtual void handleASTNode(ASTSerializer const& serializer, NodeBase*& node) override;
- virtual void handleASTNodeContents(ASTSerializer const& serializer, NodeBase* node) override;
- virtual void handleContainerDeclDirectMemberDecls(
+ void handleName(ASTSerializer const& serializer, Name*& value);
+ void handleToken(ASTSerializer const& serializer, Token& value);
+ void handleASTNode(ASTSerializer const& serializer, NodeBase*& node);
+ void handleASTNodeContents(ASTSerializer const& serializer, NodeBase* node);
+ void handleContainerDeclDirectMemberDecls(
ASTSerializer const& serializer,
- ContainerDeclDirectMemberDecls& value) override;
- virtual SerialSourceLocWriter* getSourceLocWriter() override { return _sourceLocWriter; }
+ ContainerDeclDirectMemberDecls& value);
+ SerialSourceLocWriter* getSourceLocWriter() { return _sourceLocWriter; }
+private:
void _writeImportedModule(ASTSerializer const& serializer, ModuleDecl* moduleDecl);
void _writeImportedDecl(
ASTSerializer const& serializer,
@@ -653,9 +648,11 @@ private:
/// contexts could result in the same declaration getting turned
/// into multiple distinct `Decl*`s.
///
-struct ASTSerialReadContext : public ASTSerialContext, public RefObject
+struct ASTSerialReadContext : public SourceLocSerialContext, public RefObject
{
public:
+ using ASTSerializer = Serializer<Fossil::SerialReader, ASTSerialReadContext>;
+
/// Construct an AST deserialization context.
///
/// The `linkage`, `astBuilder`, and `sink` arguments must
@@ -729,6 +726,7 @@ private:
Count _deserializedTopLevelDeclCount = 0;
#endif
+public:
//
// Much like the `ASTSerialWriter`, for the most part this
// type just implements the `IASTSerializer` interface,
@@ -736,15 +734,16 @@ private:
// implementations.
//
- virtual void handleName(ASTSerializer const& serializer, Name*& value) override;
- virtual void handleToken(ASTSerializer const& serializer, Token& value) override;
- virtual void handleASTNode(ASTSerializer const& serializer, NodeBase*& outNode) override;
- virtual void handleASTNodeContents(ASTSerializer const& serializer, NodeBase* node) override;
- virtual void handleContainerDeclDirectMemberDecls(
+ void handleName(ASTSerializer const& serializer, Name*& value);
+ void handleToken(ASTSerializer const& serializer, Token& value);
+ void handleASTNode(ASTSerializer const& serializer, NodeBase*& outNode);
+ void handleASTNodeContents(ASTSerializer const& serializer, NodeBase* node);
+ void handleContainerDeclDirectMemberDecls(
ASTSerializer const& serializer,
- ContainerDeclDirectMemberDecls& value) override;
- virtual SerialSourceLocReader* getSourceLocReader() override { return _sourceLocReader; }
+ ContainerDeclDirectMemberDecls& value);
+ SerialSourceLocReader* getSourceLocReader() { return _sourceLocReader; }
+private:
ModuleDecl* _readImportedModule(ASTSerializer const& serializer);
NodeBase* _readImportedDecl(ASTSerializer const& serializer);
@@ -770,7 +769,8 @@ private:
SLANG_DECLARE_FOSSILIZED_AS(Name, String);
-void serializeObject(ASTSerializer const& serializer, Name*& value, Name*)
+template<typename S>
+void serializeObject(S const& serializer, Name*& value, Name*)
{
serializer.getContext()->handleName(serializer, value);
}
@@ -807,7 +807,8 @@ struct FossilizedTypeTraits<Token>
};
};
-void serialize(ASTSerializer const& serializer, Token& value)
+template<typename S>
+void serialize(S const& serializer, Token& value)
{
serializer.getContext()->handleToken(serializer, value);
}
@@ -883,8 +884,8 @@ void ASTSerialReadContext::handleToken(ASTSerializer const& serializer, Token& v
// serialize any pointers to AST nodes.
//
-template<typename T>
-void serializeObject(ASTSerializer const& serializer, T*& value, NodeBase*)
+template<typename S, typename T>
+SLANG_FORCE_INLINE void serializeObject(S const& serializer, T*& value, NodeBase*)
{
// The general-purpose serialization layer defines
// a variant as akin to a struct, but where the
@@ -912,7 +913,8 @@ void serializeObject(ASTSerializer const& serializer, T*& value, NodeBase*)
// object in the reading direction.
//
-void serializeObjectContents(ASTSerializer const& serializer, NodeBase* value, NodeBase*)
+template<typename S>
+SLANG_FORCE_INLINE void serializeObjectContents(S const& serializer, NodeBase* value, NodeBase*)
{
serializer.getContext()->handleASTNodeContents(serializer, value);
}
@@ -930,7 +932,8 @@ void serializeObjectContents(ASTSerializer const& serializer, NodeBase* value, N
SLANG_DECLARE_FOSSILIZED_AS(ContainerDeclDirectMemberDecls, ContainerDeclDirectMemberDeclsInfo);
-void serialize(ASTSerializer const& serializer, ContainerDeclDirectMemberDecls& value)
+template<typename S>
+SLANG_FORCE_INLINE void serialize(S const& serializer, ContainerDeclDirectMemberDecls& value)
{
serializer.getContext()->handleContainerDeclDirectMemberDecls(serializer, value);
}
@@ -943,7 +946,8 @@ void serialize(ASTSerializer const& serializer, ContainerDeclDirectMemberDecls&
SLANG_DECLARE_FOSSILIZED_AS(DiagnosticInfo const*, Int32);
-void serializePtr(Serializer const& serializer, DiagnosticInfo const*& value, DiagnosticInfo const*)
+template<typename S>
+void serializePtr(S const& serializer, DiagnosticInfo const*& value, DiagnosticInfo const*)
{
Int32 id = 0;
if (isWriting(serializer))
@@ -964,8 +968,8 @@ void serializePtr(Serializer const& serializer, DiagnosticInfo const*& value, Di
// and we'll serialize it as such.
//
-template<typename T>
-void serialize(ASTSerializer const& serializer, DeclRef<T>& value)
+template<typename S, typename T>
+void serialize(S const& serializer, DeclRef<T>& value)
{
serialize(serializer, value.declRefBase);
}
@@ -987,7 +991,8 @@ struct FossilizedTypeTraits<DeclRef<T>>
SLANG_DECLARE_FOSSILIZED_AS(SyntaxClass<NodeBase>, ASTNodeType);
-void serialize(Serializer const& serializer, SyntaxClass<NodeBase>& value)
+template<typename S>
+void serialize(S const& serializer, SyntaxClass<NodeBase>& value)
{
ASTNodeType raw = ASTNodeType(0);
if (isWriting(serializer))
@@ -1011,7 +1016,8 @@ void serialize(Serializer const& serializer, SyntaxClass<NodeBase>& value)
SLANG_DECLARE_FOSSILIZED_AS(Modifiers, List<Modifier*>);
-void serialize(ASTSerializer const& serializer, Modifiers& value)
+template<typename S>
+void serialize(S const& serializer, Modifiers& value)
{
SLANG_SCOPED_SERIALIZER_ARRAY(serializer);
@@ -1059,7 +1065,8 @@ void serialize(ASTSerializer const& serializer, Modifiers& value)
//
SLANG_DECLARE_FOSSILIZED_AS_MEMBER(TypeExp, type);
-void serialize(ASTSerializer const& serializer, TypeExp& value)
+template<typename S>
+void serialize(S const& serializer, TypeExp& value)
{
serialize(serializer, value.type);
}
@@ -1071,7 +1078,8 @@ void serialize(ASTSerializer const& serializer, TypeExp& value)
SLANG_DECLARE_FOSSILIZED_AS_MEMBER(CandidateExtensionList, candidateExtensions);
-void serialize(ASTSerializer const& serializer, CandidateExtensionList& value)
+template<typename S>
+void serialize(S const& serializer, CandidateExtensionList& value)
{
serialize(serializer, value.candidateExtensions);
}
@@ -1079,7 +1087,8 @@ void serialize(ASTSerializer const& serializer, CandidateExtensionList& value)
SLANG_DECLARE_FOSSILIZED_AS_MEMBER(DeclAssociationList, associations);
-void serialize(ASTSerializer const& serializer, DeclAssociationList& value)
+template<typename S>
+void serialize(S const& serializer, DeclAssociationList& value)
{
serialize(serializer, value.associations);
}
@@ -1109,7 +1118,8 @@ SLANG_DECLARE_FOSSILIZED_AS(CapabilityTargetSet, CapabilityStageSets);
//
SLANG_DECLARE_FOSSILIZED_AS(CapabilitySet, CapabilityTargetSets);
-void serialize(Serializer const& serializer, CapabilityAtomSet& value)
+template<typename S>
+void serialize(S const& serializer, CapabilityAtomSet& value)
{
SLANG_SCOPED_SERIALIZER_ARRAY(serializer);
if (isWriting(serializer))
@@ -1131,12 +1141,14 @@ void serialize(Serializer const& serializer, CapabilityAtomSet& value)
}
}
-void serialize(Serializer const& serializer, CapabilityStageSet& value)
+template<typename S>
+void serialize(S const& serializer, CapabilityStageSet& value)
{
serialize(serializer, value.atomSet);
}
-void serialize(Serializer const& serializer, CapabilityTargetSet& value)
+template<typename S>
+void serialize(S const& serializer, CapabilityTargetSet& value)
{
serialize(serializer, value.shaderStageSets);
@@ -1154,7 +1166,8 @@ void serialize(Serializer const& serializer, CapabilityTargetSet& value)
}
}
-void serialize(Serializer const& serializer, CapabilitySet& value)
+template<typename S>
+void serialize(S const& serializer, CapabilitySet& value)
{
serialize(serializer, value.getCapabilityTargetSets());
@@ -1190,7 +1203,8 @@ struct FossilizedTypeTraits<RequirementWitness>
};
};
-void serialize(ASTSerializer const& serializer, RequirementWitness& value)
+template<typename S>
+void serialize(S const& serializer, RequirementWitness& value)
{
SLANG_SCOPED_SERIALIZER_VARIANT(serializer);
serialize(serializer, value.m_flavor);
@@ -1228,7 +1242,8 @@ struct FossilizedTypeTraits<ValNodeOperand>
};
};
-void serialize(ASTSerializer const& serializer, ValNodeOperand& value)
+template<typename S>
+void serialize(S const& serializer, ValNodeOperand& value)
{
SLANG_SCOPED_SERIALIZER_VARIANT(serializer);
serialize(serializer, value.kind);
@@ -1289,7 +1304,8 @@ struct Fossilized_$T
};
/// Serialize a `value` of type `$T`
-void serialize(ASTSerializer const& serializer, $T& value)
+template<typename S>
+void serialize(S const& serializer, $T& value)
{
SLANG_UNUSED(value);
SLANG_SCOPED_SERIALIZER_STRUCT(serializer);
@@ -1346,7 +1362,8 @@ struct Fossilized_$T
};
/// Serialize the contents of an AST node of type `$T`
-void _serializeASTNodeContents(ASTSerializer const& serializer, $T* value)
+template<typename S>
+void _serializeASTNodeContents(S const& serializer, $T* value)
{
SLANG_UNUSED(serializer);
SLANG_UNUSED(value);
@@ -1372,7 +1389,8 @@ void _serializeASTNodeContents(ASTSerializer const& serializer, $T* value)
// functions, and dispatches to the correct one based on the type of the given node.
//
-void serializeASTNodeContents(ASTSerializer const& serializer, NodeBase* node)
+template<typename S>
+void serializeASTNodeContents(S const& serializer, NodeBase* node)
{
ASTNodeDispatcher<NodeBase, void>::dispatch(
node,
@@ -1894,7 +1912,7 @@ void ASTSerialReadContext::handleContainerDeclDirectMemberDecls(
// `ISerializerImpl`, whereas we *know* it has a more
// specific type, which we want to make use of.
//
- ISerializerImpl* readerImpl = serializer.getImpl();
+ auto readerImpl = serializer.getImpl();
auto fossilReader = static_cast<Fossil::SerialReader*>(readerImpl);
//
auto fossilizedInfo =
@@ -1927,6 +1945,8 @@ void writeSerializedModuleAST(
ModuleDecl* moduleDecl,
SerialSourceLocWriter* sourceLocWriter)
{
+ SLANG_PROFILE;
+
// TODO: we might want to have a more careful pass here,
// where we only encode the public declarations.
@@ -1965,7 +1985,7 @@ void writeSerializedModuleAST(
//
Fossil::SerialWriter writer(blobBuilder);
ASTSerialWriteContext context(moduleDecl, sourceLocWriter);
- ASTSerializer serializer(&writer, &context);
+ ASTSerialWriteContext::ASTSerializer serializer(&writer, &context);
// Once we have our `serializer`, we can finally invoke
// `serialize()` on the `ASTModuleInfo` to cause everything
@@ -2030,6 +2050,8 @@ ModuleDecl* readSerializedModuleAST(
SerialSourceLocReader* sourceLocReader,
SourceLoc requestingSourceLoc)
{
+ SLANG_PROFILE;
+
// We expect the `chunk` that was passed in to be a RIFF
// data chunk (matching what was written in `writeSerializedModuleAST()`,
// and to be proper fossil-format data.
diff --git a/source/slang/slang-serialize-fossil.cpp b/source/slang/slang-serialize-fossil.cpp
index 7ce09d283..3a3d8c00f 100644
--- a/source/slang/slang-serialize-fossil.cpp
+++ b/source/slang/slang-serialize-fossil.cpp
@@ -2,6 +2,7 @@
#include "slang-serialize-fossil.h"
#include "../core/slang-blob.h"
+#include "core/slang-performance-profiler.h"
namespace Slang
{
@@ -142,16 +143,17 @@ void SerialWriter::handleString(String& value)
auto size = value.getLength();
if (_shouldEmitPotentiallyIndirectValueWithPointerIndirection())
{
- if (size == 0)
- {
- _writeNull();
- return;
- }
-
- if (auto found = _mapStringToChunk.tryGetValue(value))
+ ChunkBuilder* existingChunk = nullptr;
+ _mapStringToChunk.tryGetValue(value, existingChunk);
+
+ // If we found an existing chunk that holds the string
+ // value in question, we can re-use it. Also, if the
+ // string is empty, we can encode it as a null pointer
+ // in this case, so we don't care if we found a chunk
+ // or not.
+ //
+ if (existingChunk || size == 0)
{
- auto existingChunk = *found;
-
auto ptrLayout =
(ContainerLayoutObj*)_reserveDestinationForWrite(FossilizedValKind::Ptr);
_mergeLayout(ptrLayout->baseLayout, FossilizedValKind::StringObj);
@@ -174,22 +176,22 @@ void SerialWriter::handleString(String& value)
_mapStringToChunk.addIfNotExists(value, chunk);
}
-void SerialWriter::beginArray()
+void SerialWriter::beginArray(Scope&)
{
_pushContainerScope(FossilizedValKind::ArrayObj);
}
-void SerialWriter::endArray()
+void SerialWriter::endArray(Scope&)
{
_popContainerScope();
}
-void SerialWriter::beginDictionary()
+void SerialWriter::beginDictionary(Scope&)
{
_pushContainerScope(FossilizedValKind::DictionaryObj);
}
-void SerialWriter::endDictionary()
+void SerialWriter::endDictionary(Scope&)
{
_popContainerScope();
}
@@ -216,23 +218,23 @@ bool SerialWriter::hasElements()
return false;
}
-void SerialWriter::beginStruct()
+void SerialWriter::beginStruct(Scope&)
{
_pushInlineValueScope(FossilizedValKind::Struct);
}
-void SerialWriter::endStruct()
+void SerialWriter::endStruct(Scope&)
{
_popInlineValueScope();
}
-void SerialWriter::beginVariant()
+void SerialWriter::beginVariant(Scope&)
{
_pushVariantScope();
_pushInlineValueScope(FossilizedValKind::Struct);
}
-void SerialWriter::endVariant()
+void SerialWriter::endVariant(Scope&)
{
_popInlineValueScope();
_popVariantScope();
@@ -246,27 +248,27 @@ void SerialWriter::handleFieldKey(char const* name, Int index)
SLANG_UNUSED(index);
}
-void SerialWriter::beginTuple()
+void SerialWriter::beginTuple(Scope&)
{
_pushInlineValueScope(FossilizedValKind::Tuple);
}
-void SerialWriter::endTuple()
+void SerialWriter::endTuple(Scope&)
{
_popInlineValueScope();
}
-void SerialWriter::beginOptional()
+void SerialWriter::beginOptional(Scope&)
{
_pushIndirectValueScope(FossilizedValKind::OptionalObj);
}
-void SerialWriter::endOptional()
+void SerialWriter::endOptional(Scope&)
{
_popIndirectValueScope();
}
-void SerialWriter::handleSharedPtr(void*& value, Callback callback, void* context)
+void SerialWriter::handleSharedPtr(void*& value, SerializerCallback callback, void* context)
{
// Because we are writing, we only care about the
// pointer that is already present in `value`.
@@ -313,7 +315,7 @@ void SerialWriter::handleSharedPtr(void*& value, Callback callback, void* contex
_commitWrite(ValInfo::relativePtrTo(chunk));
}
-void SerialWriter::handleUniquePtr(void*& value, Callback callback, void* context)
+void SerialWriter::handleUniquePtr(void*& value, SerializerCallback callback, void* context)
{
// We treat all pointers as shared pointers, because there isn't really
// an optimized representation we would want to use for the unique case.
@@ -321,7 +323,10 @@ void SerialWriter::handleUniquePtr(void*& value, Callback callback, void* contex
handleSharedPtr(value, callback, context);
}
-void SerialWriter::handleDeferredObjectContents(void* valuePtr, Callback callback, void* context)
+void SerialWriter::handleDeferredObjectContents(
+ void* valuePtr,
+ SerializerCallback callback,
+ void* context)
{
// Because we are already deferring writing of the *entirety* of
// an object's members as part of how `handleSharedPtr()` works,
@@ -1152,7 +1157,7 @@ SerialReader::SerialReader(
switch (initialState)
{
case InitialStateType::Root:
- _state.type = State::Type::Root;
+ _state.type = State::Type::Object;
break;
case InitialStateType::PseudoPtr:
@@ -1160,9 +1165,9 @@ SerialReader::SerialReader(
break;
}
- _state.baseValPtr = valPtr;
- _state.elementIndex = 0;
- _state.elementCount = 1;
+ _state.dataCursor = valPtr.getDataPtr();
+ _state.layoutCursor = valPtr.getLayout();
+ _state.remainingValueCount = 1;
}
SerialReader::~SerialReader()
@@ -1207,149 +1212,12 @@ SerialReader::~SerialReader()
_context._readerCount--;
}
-Fossil::AnyValPtr SerialReader::readValPtr()
-{
- return _readValPtr();
-}
-
void SerialReader::flush()
{
_flush();
}
-SerializationMode SerialReader::getMode()
-{
- return SerializationMode::Read;
-}
-
-void SerialReader::handleBool(bool& value)
-{
- handleSimpleVal(value);
-}
-
-void SerialReader::handleInt8(int8_t& value)
-{
- handleSimpleVal(value);
-}
-
-void SerialReader::handleInt16(int16_t& value)
-{
- handleSimpleVal(value);
-}
-
-void SerialReader::handleInt32(Int32& value)
-{
- handleSimpleVal(value);
-}
-
-void SerialReader::handleInt64(Int64& value)
-{
- handleSimpleVal(value);
-}
-
-void SerialReader::handleUInt8(uint8_t& value)
-{
- handleSimpleVal(value);
-}
-
-void SerialReader::handleUInt16(uint16_t& value)
-{
- handleSimpleVal(value);
-}
-
-void SerialReader::handleUInt32(UInt32& value)
-{
- handleSimpleVal(value);
-}
-
-void SerialReader::handleUInt64(UInt64& value)
-{
- handleSimpleVal(value);
-}
-
-void SerialReader::handleFloat32(float& value)
-{
- handleSimpleVal(value);
-}
-
-void SerialReader::handleFloat64(double& value)
-{
- handleSimpleVal(value);
-}
-
-void SerialReader::handleString(String& value)
-{
- auto valPtr = _readPotentiallyIndirectValPtr();
- if (!valPtr)
- {
- value = String();
- }
- else
- {
- value = as<FossilizedStringObj>(valPtr)->get();
- }
-}
-
-void SerialReader::beginArray()
-{
- auto valPtr = _readPotentiallyIndirectValPtr();
- auto arrayPtr = as<FossilizedArrayObjBase>(valPtr);
-
- _pushState();
-
- _state.type = State::Type::Array;
- _state.baseValPtr = arrayPtr;
- _state.elementIndex = 0;
- _state.elementCount = arrayPtr->getElementCount();
-}
-
-void SerialReader::endArray()
-{
- _popState();
-}
-
-void SerialReader::beginDictionary()
-{
- auto valPtr = _readPotentiallyIndirectValPtr();
- auto dictionaryPtr = as<FossilizedDictionaryObjBase>(valPtr);
-
- _pushState();
-
- _state.type = State::Type::Dictionary;
- _state.baseValPtr = dictionaryPtr;
- _state.elementIndex = 0;
- _state.elementCount = dictionaryPtr->getElementCount();
-}
-
-void SerialReader::endDictionary()
-{
- _popState();
-}
-
-bool SerialReader::hasElements()
-{
- return _state.elementIndex < _state.elementCount;
-}
-
-void SerialReader::beginStruct()
-{
- auto valPtr = _readValPtr();
- auto recordPtr = as<FossilizedRecordVal>(valPtr);
-
- _pushState();
-
- _state.type = State::Type::Struct;
- _state.baseValPtr = valPtr;
- _state.elementIndex = 0;
- _state.elementCount = recordPtr->getFieldCount();
-}
-
-void SerialReader::endStruct()
-{
- _popState();
-}
-
-void SerialReader::beginVariant()
+void SerialReader::beginVariant(Scope& scope)
{
auto valPtr = _readPotentiallyIndirectValPtr();
if (auto variantPtr = as<FossilizedVariantObj>(valPtr))
@@ -1357,78 +1225,28 @@ void SerialReader::beginVariant()
auto contentValPtr = getVariantContentPtr(variantPtr);
valPtr = contentValPtr;
}
- auto recordPtr = as<FossilizedRecordVal>(valPtr);
-
- _pushState();
-
- _state.type = State::Type::Struct;
- _state.baseValPtr = recordPtr;
- _state.elementIndex = 0;
- _state.elementCount = recordPtr->getFieldCount();
-}
-
-void SerialReader::endVariant()
-{
- _popState();
-}
-
-void SerialReader::handleFieldKey(char const* name, Int index)
-{
- // For now we are ignoring field keys, and treating
- // structs as basically equivalent to tuples.
- SLANG_UNUSED(name);
- SLANG_UNUSED(index);
-}
-
-void SerialReader::beginTuple()
-{
- auto valPtr = _readValPtr();
- auto recordPtr = as<FossilizedRecordVal>(valPtr);
-
- _pushState();
-
- _state.type = State::Type::Tuple;
- _state.baseValPtr = recordPtr;
- _state.elementIndex = 0;
- _state.elementCount = recordPtr->getFieldCount();
-}
-
-void SerialReader::endTuple()
-{
- _popState();
-}
-
-void SerialReader::beginOptional()
-{
- auto valPtr = _readIndirectValPtr();
- auto optionalPtr = as<FossilizedOptionalObjBase>(valPtr);
-
- _pushState();
+ auto recordPtr = expectNonNullValOfType<FossilizedRecordVal>(valPtr);
- _state.type = State::Type::Optional;
- _state.baseValPtr = optionalPtr;
- _state.elementIndex = 0;
- _state.elementCount = Count(optionalPtr->hasValue());
+ _pushRecordState(scope, recordPtr);
}
-void SerialReader::endOptional()
+void SerialReader::handleSharedPtr(void*& value, SerializerCallback callback, void* context)
{
- _popState();
-}
+ Fossil::AnyValPtr valPtr = _readValPtr();
-void SerialReader::handleSharedPtr(void*& value, Callback callback, void* context)
-{
Fossil::AnyValPtr targetValPtr;
-
if (_state.type == State::Type::PseudoPtr)
{
- _state.type = State::Type::Root;
- targetValPtr = _readValPtr();
+ // TODO(tfoley): Having to include the `PseudoPtr` case here
+ // is frustrating, because it was only introduced to deal
+ // with a wrinkle related to on-demand AST deserialization,
+ // and ideally shouldn't be needed at all.
+
+ targetValPtr = valPtr;
}
else
{
- auto valPtr = _readValPtr();
- auto ptrPtr = as<FossilizedPtr<void>>(valPtr);
+ auto ptrPtr = expectNonNullValOfType<FossilizedPtr<void>>(valPtr);
targetValPtr = ptrPtr->getTargetValPtr();
}
@@ -1521,11 +1339,12 @@ void SerialReader::handleSharedPtr(void*& value, Callback callback, void* contex
// reading whatever comes after the pointer
// we were invoked to read.
//
- _pushState();
+ Scope callbackScope;
+ _pushState(callbackScope);
_state.type = State::Type::Object;
- _state.baseValPtr = objectInfo->fossilizedObjectPtr;
- _state.elementIndex = 0;
- _state.elementCount = 1;
+ _state.dataCursor = targetValPtr.getDataPtr();
+ _state.layoutCursor = targetValPtr.getLayout();
+ _state.remainingValueCount = 1;
// Note that we are passing the address of `objectInfo.ptr`,
// and `objectInfo` is a reference to an element of the
@@ -1543,22 +1362,17 @@ void SerialReader::handleSharedPtr(void*& value, Callback callback, void* contex
//
callback(&objectInfo->resurrectedObjectPtr, this, context);
- _popState();
+ _popState(callbackScope);
objectInfo->state = ObjectState::ReadingComplete;
value = objectInfo->resurrectedObjectPtr;
}
-void SerialReader::handleUniquePtr(void*& value, Callback callback, void* context)
-{
- // We treat all pointers as shared pointers, because there isn't really
- // an optimized representation we would want to use for the unique case.
- //
- handleSharedPtr(value, callback, context);
-}
-
-void SerialReader::handleDeferredObjectContents(void* valuePtr, Callback callback, void* context)
+void SerialReader::handleDeferredObjectContents(
+ void* valuePtr,
+ SerializerCallback callback,
+ void* context)
{
// Unlike the case in `SerialWriter::handleDeferredObjectContents()`,
// we very much *do* want to delay invoking the callback until later.
@@ -1604,57 +1418,100 @@ void SerialReader::_flush()
}
}
-Fossil::AnyValPtr SerialReader::_readValPtr()
+void SerialReader::_advanceCursor()
{
- switch (_state.type)
+ auto dataPtr = _state.dataCursor;
+
+ // The state also tracks the number of values that
+ // can still be read in the current scope. This value
+ // is used both to drive the loop when an application
+ // is reading a collection, and as a validation check
+ // here.
+ //
+ auto remainingValueCount = _state.remainingValueCount;
+ SLANG_SERIALIZE_FOSSIL_VALIDATE(remainingValueCount > 0);
+
+ // At minimum, we need to update the state to reflect
+ // the new number of values that remain.
+ //
+ remainingValueCount--;
+ _state.remainingValueCount = remainingValueCount;
+
+ // At this point, there is no need to update the
+ // state further, unless values remain. While
+ // we expect the "no values remain" case to be
+ // the less common one, we still guard all of
+ // this logic because we already need to branch
+ // to handle the case of record fields.
+ //
+ if (remainingValueCount != 0)
{
- case State::Type::Root:
- case State::Type::Object:
- case State::Type::PseudoPtr:
- SLANG_ASSERT(_state.elementCount == 1);
- SLANG_ASSERT(_state.elementIndex == 0);
- _state.elementIndex++;
- return _state.baseValPtr;
-
- case State::Type::Struct:
- case State::Type::Tuple:
+ // The primary task in this case is to update
+ // the data pointer to point to the next value
+ // in the stream.
+ //
+ auto nextDataPtr = (char*)dataPtr;
+
+ // If we are reading from a record (struct or tuple)
+ // then we will have an additional piece of state
+ // indicating the field that the data cursor was
+ // pointing at.
+ //
+ if (auto fieldInfoPtr = _state.fieldCursor)
{
- SLANG_ASSERT(_state.elementIndex < _state.elementCount);
- auto index = _state.elementIndex++;
+ // We know, because `remainingValueCount` is non-zero,
+ // that there is at least one more field after this
+ // one, so we can increment the `fieldInfoPtr` to
+ // get a pointer to the next field layout in the
+ // record layout.
+ //
+ auto nextFieldInfoPtr = fieldInfoPtr + 1;
- auto recordPtr = as<FossilizedRecordVal>(_state.baseValPtr);
- return getAddress(recordPtr->getField(index));
- }
+ // The layout information for a record type stores
+ // the offset of each field, so we can compute
+ // the relative offset between two fields as the
+ // difference between their offsets in the layout.
+ //
+ auto offsetToNextField = nextFieldInfoPtr->offset - fieldInfoPtr->offset;
- case State::Type::Optional:
- {
- SLANG_ASSERT(_state.elementCount == 1);
- SLANG_ASSERT(_state.elementIndex == 0);
+ // Adding that relative offset to the data pointer
+ // will navigate us to the next field.
+ //
+ nextDataPtr += offsetToNextField;
+ _state.fieldCursor = nextFieldInfoPtr;
- auto optionalPtr = as<FossilizedOptionalObjBase>(_state.baseValPtr);
- return getAddress(optionalPtr->getValue());
- }
+ // The info for the next field stores a (relative)
+ // pointer to its data layout, so we can write
+ // that into our state so that it is available
+ // for the next read operation.
- case State::Type::Array:
- case State::Type::Dictionary:
+ _state.layoutCursor = nextFieldInfoPtr->layout;
+ }
+ else
{
- SLANG_ASSERT(_state.elementIndex < _state.elementCount);
- auto index = _state.elementIndex++;
-
- auto containerPtr = as<FossilizedContainerObjBase>(_state.baseValPtr);
- return Fossil::ValPtr(containerPtr->getElement(index));
+ // The other case where more than one value can
+ // be read in the same state is when a collection
+ // is being read. In that case the setup logic
+ // will have already stored the stride between
+ // elements into the state, and we can use that
+ // to increment the data pointer.
+ //
+ // (In the container case the layout pointer doesn't
+ // need to change, since all of the elements are
+ // required to have the same layout).
+ //
+ auto dataStride = _state.dataStride;
+ nextDataPtr += dataStride;
}
- default:
- SLANG_UNEXPECTED("unhandled case");
- break;
+ _state.dataCursor = nextDataPtr;
}
}
-Fossil::AnyValPtr SerialReader::_readIndirectValPtr()
+SLANG_FORCE_INLINE Fossil::AnyValPtr SerialReader::_readIndirectValPtr()
{
auto baseValPtr = _readValPtr();
- auto basePtrPtr = as<FossilizedPtr<void>>(baseValPtr);
+ auto basePtrPtr = expectNonNullValOfType<FossilizedPtr<void>>(baseValPtr);
auto targetValPtr = basePtrPtr->getTargetValPtr();
return targetValPtr;
@@ -1663,25 +1520,124 @@ Fossil::AnyValPtr SerialReader::_readIndirectValPtr()
Fossil::AnyValPtr SerialReader::_readPotentiallyIndirectValPtr()
{
- auto baseValPtr = _readValPtr();
- if (auto basePtrPtr = as<FossilizedPtr<void>>(baseValPtr))
+ auto stateType = _state.type;
+ auto valPtr = _readValPtr();
+ if (stateType != State::Type::Object)
{
- auto targetValRef = basePtrPtr->getTargetValRef();
- return Fossil::ValPtr(targetValRef);
+ auto ptrPtr = expectNonNullValOfType<FossilizedPtr<void>>(valPtr);
+ valPtr = ptrPtr->getTargetValPtr();
}
- return baseValPtr;
+ return valPtr;
}
-void SerialReader::_pushState()
+void SerialReader::beginTuple(Scope& scope)
{
- _stack.add(_state);
+ auto valPtr = _readValPtr();
+ auto recordPtr = expectNonNullValOfType<FossilizedRecordVal>(valPtr);
+
+ _pushRecordState(scope, recordPtr);
}
-void SerialReader::_popState()
+void SerialReader::beginOptional(Scope& scope)
{
- SLANG_ASSERT(_stack.getCount() != 0);
- _state = _stack.getLast();
- _stack.removeLast();
+ auto valPtr = _readIndirectValPtr();
+ auto optionalPtr = expectPossiblyNullValOfType<FossilizedOptionalObjBase>(valPtr);
+ bool hasValue = optionalPtr->hasValue();
+
+ _pushState(scope);
+
+ _state.type = State::Type::Object;
+ if (hasValue)
+ {
+ auto heldValPtr = getAddress(optionalPtr->getValue());
+
+ _state.remainingValueCount = 1;
+ _state.dataCursor = heldValPtr.getDataPtr();
+ _state.layoutCursor = heldValPtr.getLayout();
+ }
+}
+
+void SerialReader::handleString(String& value)
+{
+ auto valPtr = _readPotentiallyIndirectValPtr();
+ auto stringPtr = expectPossiblyNullValOfType<FossilizedStringObj>(valPtr);
+ if (!stringPtr)
+ {
+ value = String();
+ }
+ else
+ {
+ value = stringPtr->get();
+ }
+}
+
+void SerialReader::_pushContainerState(
+ Scope& scope,
+ Fossil::ValPtr<FossilizedContainerObjBase> containerObjPtr)
+{
+ // The elements of a container object start immediately
+ // at its in-memory address, so we can use the pointer
+ // to the container object as the pointer to its data.
+ //
+ auto containerDataPtr = containerObjPtr.getDataPtr();
+ auto containerLayout = containerObjPtr.getLayout();
+
+ auto elementCount = (uint32_t)containerObjPtr->getElementCount();
+
+ FossilizedValLayout const* elementLayout = containerLayout->elementLayout;
+ auto elementStride = containerLayout->elementStride;
+
+ _pushState(scope);
+
+ _state.type = State::Type::Container;
+ _state.dataCursor = containerDataPtr;
+ _state.layoutCursor = elementLayout;
+ _state.dataStride = elementStride;
+ _state.remainingValueCount = elementCount;
+}
+
+void SerialReader::_pushRecordState(Scope& scope, Fossil::ValPtr<FossilizedRecordVal> recordPtr)
+{
+ auto recordDataPtr = recordPtr.getDataPtr();
+ auto recordLayout = recordPtr.getLayout();
+
+ auto fieldCount = recordLayout->fieldCount;
+
+ _pushState(scope);
+ _state.type = State::Type::Record;
+ _state.dataCursor = recordDataPtr;
+ _state.remainingValueCount = fieldCount;
+ if (fieldCount != 0)
+ {
+ auto fieldInfo = recordLayout->getField(0);
+ _state.layoutCursor = fieldInfo->layout;
+ _state.fieldCursor = fieldInfo;
+ }
+}
+
+
+void SerialReader::beginArray(Scope& scope)
+{
+ auto valPtr = _readPotentiallyIndirectValPtr();
+ auto arrayPtr = expectPossiblyNullValOfType<FossilizedArrayObjBase>(valPtr);
+
+ _pushContainerState(scope, arrayPtr);
+}
+
+void SerialReader::beginDictionary(Scope& scope)
+{
+ auto valPtr = _readPotentiallyIndirectValPtr();
+ auto dictionaryPtr = expectPossiblyNullValOfType<FossilizedDictionaryObjBase>(valPtr);
+
+ _pushContainerState(scope, dictionaryPtr);
+}
+
+void SerialReader::beginStruct(Scope& scope)
+{
+ auto valPtr = _readValPtr();
+ auto recordPtr = expectNonNullValOfType<FossilizedRecordVal>(valPtr);
+
+ _pushRecordState(scope, recordPtr);
}
} // namespace Fossil
diff --git a/source/slang/slang-serialize-fossil.h b/source/slang/slang-serialize-fossil.h
index 930719935..184831a39 100644
--- a/source/slang/slang-serialize-fossil.h
+++ b/source/slang/slang-serialize-fossil.h
@@ -21,8 +21,69 @@ namespace Slang
namespace Fossil
{
+// Deserializing data is an important place where security issues
+// can arise, so it is usually important to perform validation
+// checks throughout the process, and fail fast rather than
+// risk reading mal-formed data.
+//
+// However, validation typically comes at a performance cost,
+// and one of the key cases for serialization in Slang is loading
+// the core module from the `slang.dll` binary itself. In order
+// to measure how much performance is being lost to validation
+// checks, we provide a define that is intended to enable or
+// disable validation during deserialization.
+//
+#define SLANG_SERIALIZE_FOSSIL_ENABLE_VALIDATION_CHECKS 1
+
+#if SLANG_SERIALIZE_FOSSIL_ENABLE_VALIDATION_CHECKS
+#define SLANG_SERIALIZE_FOSSIL_VALIDATE(CONDITION) \
+ do \
+ { \
+ if (!(CONDITION)) \
+ SLANG_UNEXPECTED("invalid format encountered in serialized data"); \
+ } while (0)
+#else
+#define SLANG_SERIALIZE_FOSSIL_VALIDATE(CONDITION) SLANG_ASSERT(CONDITION)
+#endif
+
+// A commonly-occuring kind of validation check when reading
+// data in fossil format is asserting that some expected
+// piece of data is both *present* and has the expected
+// type/layout.
+//
+template<typename T>
+SLANG_FORCE_INLINE ValPtr<T> expectNonNullValOfType(AnyValPtr valPtr)
+{
+#if SLANG_SERIALIZE_FOSSIL_ENABLE_VALIDATION_CHECKS
+ if (auto resultPtr = as<T>(valPtr))
+ return resultPtr;
+ SLANG_UNEXPECTED("invalid format encountered in serialized data");
+#else
+ return cast<T>(valPtr);
+#endif
+}
+
+// Some types can be encoded via a null pointer when they
+// are in their "default" state. For example, an empty
+// dictionary or array may be encoded as a null pointer
+// to a `FossilizedContainerObj`. We still expect a match
+// on the *format* in such cases.
+//
+template<typename T>
+SLANG_FORCE_INLINE ValPtr<T> expectPossiblyNullValOfType(AnyValPtr valPtr)
+{
+#if SLANG_SERIALIZE_FOSSIL_ENABLE_VALIDATION_CHECKS
+ auto layout = valPtr.getLayout();
+ if (!layout || !T::isMatchingKind(layout->kind))
+ {
+ SLANG_UNEXPECTED("invalid format encountered in serialized data");
+ }
+#endif
+ return cast<T>(valPtr);
+}
+
/// Serializer implementation for writing objects to a fossil-format blob.
-struct SerialWriter : ISerializerImpl
+struct SerialWriter
{
public:
SerialWriter(ChunkBuilder* chunk);
@@ -75,12 +136,9 @@ private:
{
}
- virtual ~LayoutObj() {}
-
FossilizedValKind getKind() const { return kind; }
Size getSize() const { return size; }
- Size getAlignment() const { return alignment; }
FossilizedValKind kind;
Size size = 0;
@@ -133,6 +191,18 @@ private:
class SimpleLayoutObj : public LayoutObj
{
public:
+ SimpleLayoutObj(FossilizedValKind kind, Size size, Size alignment)
+ : LayoutObj(kind, size, alignment)
+ {
+ }
+
+ //
+ // Note that for `SimpleLayoutObj` the default
+ // alignment is the same as the `size`, while
+ // for the base `LayoutObj` the default is
+ // one-byte alignment.
+ //
+
SimpleLayoutObj(FossilizedValKind kind, Size size)
: LayoutObj(kind, size, size)
{
@@ -259,7 +329,7 @@ private:
LayoutObj* ptrLayout = nullptr;
/// Callback information used by the ISerializer interface.
- Callback callback = nullptr;
+ SerializerCallback callback = nullptr;
void* context = nullptr;
};
@@ -501,62 +571,73 @@ private:
void _pushState(LayoutObj* layout);
void _popState();
-private:
+public:
//
// The following declarations are the requirements
// of the `ISerializerImpl` interface:
//
- virtual SerializationMode getMode() override;
+ SerializationMode getMode();
+
+ void handleBool(bool& value);
- virtual void handleBool(bool& value) override;
+ void handleInt8(int8_t& value);
+ void handleInt16(int16_t& value);
+ void handleInt32(Int32& value);
+ void handleInt64(Int64& value);
- virtual void handleInt8(int8_t& value) override;
- virtual void handleInt16(int16_t& value) override;
- virtual void handleInt32(Int32& value) override;
- virtual void handleInt64(Int64& value) override;
+ void handleUInt8(uint8_t& value);
+ void handleUInt16(uint16_t& value);
+ void handleUInt32(UInt32& value);
+ void handleUInt64(UInt64& value);
- virtual void handleUInt8(uint8_t& value) override;
- virtual void handleUInt16(uint16_t& value) override;
- virtual void handleUInt32(UInt32& value) override;
- virtual void handleUInt64(UInt64& value) override;
+ void handleFloat32(float& value);
+ void handleFloat64(double& value);
- virtual void handleFloat32(float& value) override;
- virtual void handleFloat64(double& value) override;
+ void handleString(String& value);
- virtual void handleString(String& value) override;
+ struct Scope
+ {
+ // The `SerialWriter` implementation for fossil
+ // does not currently take advantage of the `Scope`
+ // facility provided by the serialization framework.
+ //
+ // If we *do* want to modify it to use that facility,
+ // then we can look to the `SerialReader` implementation
+ // for an example of how it can be used.
+ };
- virtual void beginArray() override;
- virtual void endArray() override;
- virtual void beginOptional() override;
- virtual void endOptional() override;
+ void beginArray(Scope& scope);
+ void endArray(Scope& scope);
- virtual void beginDictionary() override;
- virtual void endDictionary() override;
+ void beginOptional(Scope& scope);
+ void endOptional(Scope& scope);
- virtual bool hasElements() override;
+ void beginDictionary(Scope& scope);
+ void endDictionary(Scope& scope);
- virtual void beginTuple() override;
- virtual void endTuple() override;
+ bool hasElements();
- virtual void beginStruct() override;
- virtual void endStruct() override;
+ void beginTuple(Scope& scope);
+ void endTuple(Scope& scope);
- virtual void beginVariant() override;
- virtual void endVariant() override;
+ void beginStruct(Scope& scope);
+ void endStruct(Scope& scope);
- virtual void handleFieldKey(char const* name, Int index) override;
+ void beginVariant(Scope& scope);
+ void endVariant(Scope& scope);
- virtual void handleSharedPtr(void*& value, Callback callback, void* context) override;
- virtual void handleUniquePtr(void*& value, Callback callback, void* context) override;
+ void handleFieldKey(char const* name, Int index);
- virtual void handleDeferredObjectContents(void* valuePtr, Callback callback, void* context)
- override;
+ void handleSharedPtr(void*& value, SerializerCallback callback, void* context);
+ void handleUniquePtr(void*& value, SerializerCallback callback, void* context);
+
+ void handleDeferredObjectContents(void* valuePtr, SerializerCallback callback, void* context);
};
/// Serializer implementation for reading objects from a fossil-format blob.
-struct SerialReader : ISerializerImpl
+struct SerialReader
{
public:
struct ReadContext;
@@ -595,57 +676,91 @@ private:
/// A state that the reader can be in.
struct State
{
+ // Note: the exact order of declaration for the fields
+ // here can end up impacting the overall performance of
+ // deserialization, so be mindful when making changes.
+
/// Type of state; related to the kind of value being read from.
///
+ /// Most of these values are determined by the `FossilizedValKind`
+ /// of the parent/container that was pushed to create this state,
+ /// but the correspondance is not one-to-one. For example,
+ /// both `FossilizedValKind::ArrayObj` and `::DictinaryObj` map
+ /// to `State::Type::Container`.
+ ///
+ /// Reducing the number of distinct cases tracked here minimizes
+ /// the complexity of branches on the type of state.
+ ///
enum class Type
{
- Root,
- Array,
- Dictionary,
- Optional,
- Tuple,
- Struct,
Object,
-
+ Container,
+ Record,
PseudoPtr,
};
- /// The type of state.
- Type type = Type::Root;
+ /// The pointers to the data and layout of the fossilized
+ /// value that will be read next.
+ ///
+ void* dataCursor = nullptr;
+ FossilizedValLayout const* layoutCursor = nullptr;
- /// The fossilized value (data and layout) that is being read from.
+ /// The field layout information for the next value to be read.
///
- /// Depending on the `type` of state, this might either be the next value
- /// that will be read (e.g., for the `Root` case), or it might be
- /// a container that is a parent of the next value to be read.
+ /// This value is only used when `type == State::Type::Record`.
+ /// In that case, the `fieldCursor` can be used to locate
+ /// the layout for the next field (if their is one).
///
- Fossil::AnyValPtr baseValPtr;
+ FossilizedRecordElementLayout const* fieldCursor = nullptr;
- /// Index of next element to read.
+ /// The number of values that can still be read in this state.
///
- /// This is used in the case where `baseValue` is some kind of
- /// container or record.
+ /// If the `remainingValueCount` field is zero, then the contents
+ /// of the remaining fields are irrelevant (and may hold stale
+ /// values rather than being cleared correctly).
///
- Index elementIndex = 0;
+ uint32_t remainingValueCount = 0;
- /// Total number of values that can be read.
+ /// A stride (in bytes) between values.
///
- /// If `baseValue` is a container, this is the element count.
- /// If `baseValue` is a tuple/struct, this is the field count.
- /// If `baseValue` is an optional, this is either zero or one.
- /// If this state is a singleton case like `Root`, will be one.
+ /// This value is only used when `type == State::Type::Container`.
+ /// It should be set to zero for any other type of state.
///
- Count elementCount = 0;
+ uint32_t dataStride = 0;
+
+ /// The type of state.
+ Type type = Type::Object;
};
/// The current state.
State _state;
- /// Stack of saved states.
- List<State> _stack;
+ SLANG_FORCE_INLINE State& getState() { return _state; }
+
+public:
+ //
+ // The serialization protocol allows the back-end
+ // implementation to define a `Scope` type that will
+ // be passed into the paired `begin` and `end` operations.
+ //
+ // We define a scope type that is able to store a saved
+ // copy of the state of the `SerialReader`, which allows
+ // us to maintain a stack of states that gets stored on
+ // the run-time call stack, instead of requiring a
+ // heap-allocated container here.
+ //
+
+ struct Scope
+ {
+ private:
+ friend struct SerialReader;
+
+ State savedState;
+ };
+private:
//
- // Like other `ISerializerImpl`s for reading, we track objects
+ // Like other serializer implementations for reading, we track objects
// that are in the process of being read in, to avoid possible
// unbounded recursion (and detect circularities when they
// occur).
@@ -682,12 +797,15 @@ private:
State savedState;
- Callback callback;
+ SerializerCallback callback;
void* context;
};
- void _pushState();
- void _popState();
+ void _pushState(Scope& scope);
+ void _popState(Scope& scope);
+
+ void _pushContainerState(Scope& scope, Fossil::ValPtr<FossilizedContainerObjBase> containerPtr);
+ void _pushRecordState(Scope& scope, Fossil::ValPtr<FossilizedRecordVal> recordPtr);
/// Execute all deferred actions that are still pending.
@@ -699,6 +817,10 @@ private:
///
Fossil::AnyValPtr _readValPtr();
+ /// Advance the read cursor.
+ ///
+ void _advanceCursor();
+
/// Read an indirect value.
///
/// This is the case for things like optionals, that are
@@ -717,10 +839,16 @@ private:
template<typename T>
- void handleSimpleVal(T& value)
+ SLANG_FORCE_INLINE T _readSimpleVal()
{
auto valPtr = _readValPtr();
- value = as<Fossilized<T>>(valPtr)->getDataRef();
+ return expectNonNullValOfType<Fossilized<T>>(valPtr)->getDataRef();
+ }
+
+ template<typename T>
+ SLANG_FORCE_INLINE void _handleSimpleVal(T& value)
+ {
+ value = _readSimpleVal<T>();
}
public:
@@ -739,62 +867,213 @@ public:
};
-private:
+public:
//
// The following declarations are the requirements
// of the `ISerializerImpl` interface:
//
- virtual SerializationMode getMode() override;
+ SerializationMode getMode();
- virtual void handleBool(bool& value) override;
+ void handleBool(bool& value);
- virtual void handleInt8(int8_t& value) override;
- virtual void handleInt16(int16_t& value) override;
- virtual void handleInt32(Int32& value) override;
- virtual void handleInt64(Int64& value) override;
+ void handleInt8(int8_t& value);
+ void handleInt16(int16_t& value);
+ void handleInt32(Int32& value);
+ void handleInt64(Int64& value);
- virtual void handleUInt8(uint8_t& value) override;
- virtual void handleUInt16(uint16_t& value) override;
- virtual void handleUInt32(UInt32& value) override;
- virtual void handleUInt64(UInt64& value) override;
+ void handleUInt8(uint8_t& value);
+ void handleUInt16(uint16_t& value);
+ void handleUInt32(UInt32& value);
+ void handleUInt64(UInt64& value);
- virtual void handleFloat32(float& value) override;
- virtual void handleFloat64(double& value) override;
+ void handleFloat32(float& value);
+ void handleFloat64(double& value);
- virtual void handleString(String& value) override;
+ void handleString(String& value);
- virtual void beginArray() override;
- virtual void endArray() override;
+ void beginArray(Scope& scope);
+ void endArray(Scope& scope);
- virtual void beginDictionary() override;
- virtual void endDictionary() override;
+ void beginDictionary(Scope& scope);
+ void endDictionary(Scope& scope);
- virtual bool hasElements() override;
+ bool hasElements();
- virtual void beginStruct() override;
- virtual void endStruct() override;
+ void beginStruct(Scope& scope);
+ void endStruct(Scope& scope);
- virtual void beginVariant() override;
- virtual void endVariant() override;
+ void beginVariant(Scope& scope);
+ void endVariant(Scope& scope);
- virtual void handleFieldKey(char const* name, Int index) override;
+ void handleFieldKey(char const* name, Int index);
- virtual void beginTuple() override;
- virtual void endTuple() override;
+ void beginTuple(Scope& scope);
+ void endTuple(Scope& scope);
- virtual void beginOptional() override;
- virtual void endOptional() override;
+ void beginOptional(Scope& scope);
+ void endOptional(Scope& scope);
- virtual void handleSharedPtr(void*& value, Callback callback, void* context) override;
- virtual void handleUniquePtr(void*& value, Callback callback, void* context) override;
+ void handleSharedPtr(void*& value, SerializerCallback callback, void* context);
+ void handleUniquePtr(void*& value, SerializerCallback callback, void* context);
- virtual void handleDeferredObjectContents(void* valuePtr, Callback callback, void* context)
- override;
+ void handleDeferredObjectContents(void* valuePtr, SerializerCallback callback, void* context);
};
using ReadContext = SerialReader::ReadContext;
+SLANG_FORCE_INLINE Fossil::AnyValPtr SerialReader::readValPtr()
+{
+ return _readValPtr();
+}
+
+SLANG_FORCE_INLINE SerializationMode SerialReader::getMode()
+{
+ return SerializationMode::Read;
+}
+
+SLANG_FORCE_INLINE void SerialReader::handleBool(bool& value)
+{
+ _handleSimpleVal(value);
+}
+
+SLANG_FORCE_INLINE void SerialReader::handleInt8(int8_t& value)
+{
+ _handleSimpleVal(value);
+}
+
+SLANG_FORCE_INLINE void SerialReader::handleInt16(int16_t& value)
+{
+ _handleSimpleVal(value);
+}
+
+SLANG_FORCE_INLINE void SerialReader::handleInt32(Int32& value)
+{
+ _handleSimpleVal(value);
+}
+
+SLANG_FORCE_INLINE void SerialReader::handleInt64(Int64& value)
+{
+ _handleSimpleVal(value);
+}
+
+SLANG_FORCE_INLINE void SerialReader::handleUInt8(uint8_t& value)
+{
+ _handleSimpleVal(value);
+}
+
+SLANG_FORCE_INLINE void SerialReader::handleUInt16(uint16_t& value)
+{
+ _handleSimpleVal(value);
+}
+
+SLANG_FORCE_INLINE void SerialReader::handleUInt32(UInt32& value)
+{
+ _handleSimpleVal(value);
+}
+
+SLANG_FORCE_INLINE void SerialReader::handleUInt64(UInt64& value)
+{
+ _handleSimpleVal(value);
+}
+
+SLANG_FORCE_INLINE void SerialReader::handleFloat32(float& value)
+{
+ _handleSimpleVal(value);
+}
+
+SLANG_FORCE_INLINE void SerialReader::handleFloat64(double& value)
+{
+ _handleSimpleVal(value);
+}
+
+SLANG_FORCE_INLINE void SerialReader::endArray(Scope& scope)
+{
+ _popState(scope);
+}
+
+SLANG_FORCE_INLINE void SerialReader::endDictionary(Scope& scope)
+{
+ _popState(scope);
+}
+
+SLANG_FORCE_INLINE bool SerialReader::hasElements()
+{
+ return getState().remainingValueCount != 0;
+}
+
+SLANG_FORCE_INLINE void SerialReader::endStruct(Scope& scope)
+{
+ _popState(scope);
+}
+
+SLANG_FORCE_INLINE void SerialReader::endVariant(Scope& scope)
+{
+ _popState(scope);
+}
+
+SLANG_FORCE_INLINE void SerialReader::handleFieldKey(char const* name, Int index)
+{
+ // For now we are ignoring field keys, and treating
+ // structs as basically equivalent to tuples.
+ SLANG_UNUSED(name);
+ SLANG_UNUSED(index);
+}
+
+SLANG_FORCE_INLINE void SerialReader::endTuple(Scope& scope)
+{
+ _popState(scope);
+}
+
+SLANG_FORCE_INLINE void SerialReader::endOptional(Scope& scope)
+{
+ _popState(scope);
+}
+
+SLANG_FORCE_INLINE void SerialReader::handleUniquePtr(
+ void*& value,
+ SerializerCallback callback,
+ void* context)
+{
+ // We treat all pointers as shared pointers, because there isn't really
+ // an optimized representation we would want to use for the unique case.
+ //
+ handleSharedPtr(value, callback, context);
+}
+
+SLANG_FORCE_INLINE void SerialReader::_pushState(Scope& scope)
+{
+ scope.savedState = _state;
+ _state = State();
+}
+
+SLANG_FORCE_INLINE void SerialReader::_popState(Scope& scope)
+{
+ _state = scope.savedState;
+}
+
+SLANG_FORCE_INLINE Fossil::AnyValPtr SerialReader::_readValPtr()
+{
+ SLANG_ASSERT(_state.remainingValueCount > 0);
+
+ // The rest of the `SerialReader` implementation conspires
+ // to set things up so that the `dataCursor` and `layoutCursor`
+ // stored in `_state` will always represent the next value
+ // to be read, so the logic to determine the result of
+ // this function is trivial.
+ //
+ auto dataPtr = _state.dataCursor;
+ auto layoutPtr = _state.layoutCursor;
+
+ // Currently, the logic to advance the cursor(s) to the next
+ // value is more complicated, and thus it isn't included in
+ // the inlined part of the function.
+ //
+ _advanceCursor();
+
+ return AnyValPtr(dataPtr, layoutPtr);
+}
+
} // namespace Fossil
} // namespace Slang
diff --git a/source/slang/slang-serialize-ir.cpp b/source/slang/slang-serialize-ir.cpp
index 52ec0e3f2..594953d7e 100644
--- a/source/slang/slang-serialize-ir.cpp
+++ b/source/slang/slang-serialize-ir.cpp
@@ -4,6 +4,7 @@
#include "core/slang-blob-builder.h"
#include "core/slang-common.h"
#include "core/slang-dictionary.h"
+#include "core/slang-performance-profiler.h"
#include "slang-ir-insts-stable-names.h"
#include "slang-ir-insts.h"
#include "slang-ir-validate.h"
@@ -269,11 +270,11 @@ struct IRSerialWriteContext;
// Specialize to the reader/writer for the specific backend we're targeting
// instead of ISerializerImpl to avoid some virtual function calls
#if USE_RIFF
-using IRWriteSerializer = Serializer_<RIFFSerialWriter, IRSerialWriteContext>;
-using IRReadSerializer = Serializer_<RIFFSerialReader, IRSerialReadContext>;
+using IRWriteSerializer = Serializer<RIFFSerialWriter, IRSerialWriteContext>;
+using IRReadSerializer = Serializer<RIFFSerialReader, IRSerialReadContext>;
#else
-using IRWriteSerializer = Serializer_<Fossil::SerialWriter, IRSerialWriteContext>;
-using IRReadSerializer = Serializer_<Fossil::SerialReader, IRSerialReadContext>;
+using IRWriteSerializer = Serializer<Fossil::SerialWriter, IRSerialWriteContext>;
+using IRReadSerializer = Serializer<Fossil::SerialReader, IRSerialReadContext>;
#endif
struct IRSerialWriteContext : SourceLocSerialContext
@@ -848,6 +849,8 @@ Result readSerializedModuleIR(
SerialSourceLocReader* sourceLocReader,
RefPtr<IRModule>& outIRModule)
{
+ SLANG_PROFILE;
+
SLANG_RETURN_ON_FAIL(readSerializedModuleIR_(chunk, session, sourceLocReader, outIRModule));
//
diff --git a/source/slang/slang-serialize-riff.cpp b/source/slang/slang-serialize-riff.cpp
index fb8acd2bd..ff16dbf53 100644
--- a/source/slang/slang-serialize-riff.cpp
+++ b/source/slang/slang-serialize-riff.cpp
@@ -144,22 +144,22 @@ void RIFFSerialWriter::_writeFloat(double value)
}
}
-void RIFFSerialWriter::beginArray()
+void RIFFSerialWriter::beginArray(Scope&)
{
_cursor.beginListChunk(RIFFSerial::kArrayFourCC);
}
-void RIFFSerialWriter::endArray()
+void RIFFSerialWriter::endArray(Scope&)
{
_cursor.endChunk();
}
-void RIFFSerialWriter::beginDictionary()
+void RIFFSerialWriter::beginDictionary(Scope&)
{
_cursor.beginListChunk(RIFFSerial::kDictionaryFourCC);
}
-void RIFFSerialWriter::endDictionary()
+void RIFFSerialWriter::endDictionary(Scope&)
{
_cursor.endChunk();
}
@@ -169,24 +169,24 @@ bool RIFFSerialWriter::hasElements()
return false;
}
-void RIFFSerialWriter::beginStruct()
+void RIFFSerialWriter::beginStruct(Scope&)
{
_cursor.beginListChunk(RIFFSerial::kStructFourCC);
}
-void RIFFSerialWriter::endStruct()
+void RIFFSerialWriter::endStruct(Scope&)
{
_cursor.endChunk();
}
-void RIFFSerialWriter::beginVariant()
+void RIFFSerialWriter::beginVariant(Scope& scope)
{
- beginStruct();
+ beginStruct(scope);
}
-void RIFFSerialWriter::endVariant()
+void RIFFSerialWriter::endVariant(Scope& scope)
{
- endStruct();
+ endStruct(scope);
}
void RIFFSerialWriter::handleFieldKey(char const* name, Int index)
@@ -197,27 +197,27 @@ void RIFFSerialWriter::handleFieldKey(char const* name, Int index)
SLANG_UNUSED(index);
}
-void RIFFSerialWriter::beginTuple()
+void RIFFSerialWriter::beginTuple(Scope&)
{
_cursor.beginListChunk(RIFFSerial::kTupleFourCC);
}
-void RIFFSerialWriter::endTuple()
+void RIFFSerialWriter::endTuple(Scope&)
{
_cursor.endChunk();
}
-void RIFFSerialWriter::beginOptional()
+void RIFFSerialWriter::beginOptional(Scope&)
{
_cursor.beginListChunk(RIFFSerial::kOptionalFourCC);
}
-void RIFFSerialWriter::endOptional()
+void RIFFSerialWriter::endOptional(Scope&)
{
_cursor.endChunk();
}
-void RIFFSerialWriter::handleSharedPtr(void*& value, Callback callback, void* context)
+void RIFFSerialWriter::handleSharedPtr(void*& value, SerializerCallback callback, void* context)
{
// Because we are writing, we only care about the
// pointer that is already present in `value`.
@@ -283,7 +283,7 @@ void RIFFSerialWriter::handleSharedPtr(void*& value, Callback callback, void* co
_objects.add(objectInfo);
}
-void RIFFSerialWriter::handleUniquePtr(void*& value, Callback callback, void* context)
+void RIFFSerialWriter::handleUniquePtr(void*& value, SerializerCallback callback, void* context)
{
// We treat all pointers as shared pointers, because there isn't really
// an optimized representation we would want to use for the unique case.
@@ -293,7 +293,7 @@ void RIFFSerialWriter::handleUniquePtr(void*& value, Callback callback, void* co
void RIFFSerialWriter::handleDeferredObjectContents(
void* valuePtr,
- Callback callback,
+ SerializerCallback callback,
void* context)
{
// Because we are already deferring writing of the *entirety* of
@@ -485,23 +485,22 @@ void RIFFSerialReader::handleString(String& value)
_advanceCursor();
}
-void RIFFSerialReader::beginArray()
+void RIFFSerialReader::beginArray(Scope&)
{
_beginListChunk(RIFFSerial::kArrayFourCC);
}
-void RIFFSerialReader::endArray()
+void RIFFSerialReader::endArray(Scope&)
{
_endListChunk();
}
-
-void RIFFSerialReader::beginDictionary()
+void RIFFSerialReader::beginDictionary(Scope&)
{
_beginListChunk(RIFFSerial::kDictionaryFourCC);
}
-void RIFFSerialReader::endDictionary()
+void RIFFSerialReader::endDictionary(Scope&)
{
_endListChunk();
}
@@ -511,24 +510,24 @@ bool RIFFSerialReader::hasElements()
return _cursor.get() != nullptr;
}
-void RIFFSerialReader::beginStruct()
+void RIFFSerialReader::beginStruct(Scope&)
{
_beginListChunk(RIFFSerial::kStructFourCC);
}
-void RIFFSerialReader::endStruct()
+void RIFFSerialReader::endStruct(Scope&)
{
_endListChunk();
}
-void RIFFSerialReader::beginVariant()
+void RIFFSerialReader::beginVariant(Scope& scope)
{
- beginStruct();
+ beginStruct(scope);
}
-void RIFFSerialReader::endVariant()
+void RIFFSerialReader::endVariant(Scope& scope)
{
- endStruct();
+ endStruct(scope);
}
void RIFFSerialReader::handleFieldKey(char const* name, Int index)
@@ -539,22 +538,22 @@ void RIFFSerialReader::handleFieldKey(char const* name, Int index)
SLANG_UNUSED(index);
}
-void RIFFSerialReader::beginTuple()
+void RIFFSerialReader::beginTuple(Scope&)
{
_beginListChunk(RIFFSerial::kTupleFourCC);
}
-void RIFFSerialReader::endTuple()
+void RIFFSerialReader::endTuple(Scope&)
{
_endListChunk();
}
-void RIFFSerialReader::beginOptional()
+void RIFFSerialReader::beginOptional(Scope&)
{
_beginListChunk(RIFFSerial::kOptionalFourCC);
}
-void RIFFSerialReader::endOptional()
+void RIFFSerialReader::endOptional(Scope&)
{
_endListChunk();
}
@@ -572,7 +571,7 @@ RIFFSerialReader::ObjectIndex RIFFSerialReader::_readObjectReference()
return objectIndex;
}
-void RIFFSerialReader::handleSharedPtr(void*& value, Callback callback, void* context)
+void RIFFSerialReader::handleSharedPtr(void*& value, SerializerCallback callback, void* context)
{
// The logic here largely mirrors what appears in
// `RIFFSerialWriter::handleSharedPtr`.
@@ -695,7 +694,7 @@ void RIFFSerialReader::handleSharedPtr(void*& value, Callback callback, void* co
value = objectInfo.ptr;
}
-void RIFFSerialReader::handleUniquePtr(void*& value, Callback callback, void* userData)
+void RIFFSerialReader::handleUniquePtr(void*& value, SerializerCallback callback, void* userData)
{
// We treat all pointers as shared pointers, because there isn't really
// an optimized representation we would want to use for the unique case.
@@ -705,7 +704,7 @@ void RIFFSerialReader::handleUniquePtr(void*& value, Callback callback, void* us
void RIFFSerialReader::handleDeferredObjectContents(
void* valuePtr,
- Callback callback,
+ SerializerCallback callback,
void* context)
{
// Unlike the case in `RIFFSerialWriter::handleDeferredObjectContents()`,
diff --git a/source/slang/slang-serialize-riff.h b/source/slang/slang-serialize-riff.h
index 256c185d4..b305113fa 100644
--- a/source/slang/slang-serialize-riff.h
+++ b/source/slang/slang-serialize-riff.h
@@ -118,7 +118,7 @@ static const FourCC::RawValue kObjectDefinitionListFourCC = SLANG_FOUR_CC('o', '
} // namespace RIFFSerial
/// Serializer implementation for writing to a tree of RIFF chunks.
-struct RIFFSerialWriter : ISerializerImpl
+struct RIFFSerialWriter
{
public:
/// Construct a writer to append to the given RIFF `chunk`.
@@ -160,7 +160,7 @@ private:
void* ptr;
/// Callback that can be invoked to serialize the object's data.
- Callback callback;
+ SerializerCallback callback;
/// Context pointer for `callback`
void* context;
@@ -196,56 +196,62 @@ private:
// of the `ISerializerImpl` interface:
//
- virtual SerializationMode getMode() override;
+ SerializationMode getMode();
- virtual void handleBool(bool& value) override;
+ void handleBool(bool& value);
- virtual void handleInt8(int8_t& value) override;
- virtual void handleInt16(int16_t& value) override;
- virtual void handleInt32(Int32& value) override;
- virtual void handleInt64(Int64& value) override;
+ void handleInt8(int8_t& value);
+ void handleInt16(int16_t& value);
+ void handleInt32(Int32& value);
+ void handleInt64(Int64& value);
- virtual void handleUInt8(uint8_t& value) override;
- virtual void handleUInt16(uint16_t& value) override;
- virtual void handleUInt32(UInt32& value) override;
- virtual void handleUInt64(UInt64& value) override;
+ void handleUInt8(uint8_t& value);
+ void handleUInt16(uint16_t& value);
+ void handleUInt32(UInt32& value);
+ void handleUInt64(UInt64& value);
- virtual void handleFloat32(float& value) override;
- virtual void handleFloat64(double& value) override;
+ void handleFloat32(float& value);
+ void handleFloat64(double& value);
- virtual void handleString(String& value) override;
+ void handleString(String& value);
- virtual void beginArray() override;
- virtual void endArray() override;
+ struct Scope
+ {
+ // The RIFF serialization back-end is currently
+ // not taking advantage of the `Scope` facility
+ // in the serialization framework.
+ };
+
+ void beginArray(Scope&);
+ void endArray(Scope&);
- virtual void beginDictionary() override;
- virtual void endDictionary() override;
+ void beginDictionary(Scope&);
+ void endDictionary(Scope&);
- virtual bool hasElements() override;
+ bool hasElements();
- virtual void beginStruct() override;
- virtual void endStruct() override;
+ void beginStruct(Scope&);
+ void endStruct(Scope&);
- virtual void beginVariant() override;
- virtual void endVariant() override;
+ void beginVariant(Scope&);
+ void endVariant(Scope&);
- virtual void handleFieldKey(char const* name, Int index) override;
+ void handleFieldKey(char const* name, Int index);
- virtual void beginTuple() override;
- virtual void endTuple() override;
+ void beginTuple(Scope&);
+ void endTuple(Scope&);
- virtual void beginOptional() override;
- virtual void endOptional() override;
+ void beginOptional(Scope&);
+ void endOptional(Scope&);
- virtual void handleSharedPtr(void*& value, Callback callback, void* context) override;
- virtual void handleUniquePtr(void*& value, Callback callback, void* context) override;
+ void handleSharedPtr(void*& value, SerializerCallback callback, void* context);
+ void handleUniquePtr(void*& value, SerializerCallback callback, void* context);
- virtual void handleDeferredObjectContents(void* valuePtr, Callback callback, void* context)
- override;
+ void handleDeferredObjectContents(void* valuePtr, SerializerCallback callback, void* context);
};
/// Serializer implementation for reading from a tree of RIFF chunks.
-struct RIFFSerialReader : ISerializerImpl
+struct RIFFSerialReader
{
public:
/// Construct a reader to read data from the given `chunk`.
@@ -355,7 +361,7 @@ private:
Cursor savedCursor;
/// The callback to apply to read data into the `valuePtr`
- Callback callback;
+ SerializerCallback callback;
/// The context pointer for the `callback`.
void* context;
@@ -399,52 +405,58 @@ private:
// of the `ISerializerImpl` interface:
//
- virtual SerializationMode getMode() override;
+ SerializationMode getMode();
- virtual void handleBool(bool& value) override;
+ void handleBool(bool& value);
- virtual void handleInt8(int8_t& value) override;
- virtual void handleInt16(int16_t& value) override;
- virtual void handleInt32(Int32& value) override;
- virtual void handleInt64(Int64& value) override;
+ void handleInt8(int8_t& value);
+ void handleInt16(int16_t& value);
+ void handleInt32(Int32& value);
+ void handleInt64(Int64& value);
- virtual void handleUInt8(uint8_t& value) override;
- virtual void handleUInt16(uint16_t& value) override;
- virtual void handleUInt32(UInt32& value) override;
- virtual void handleUInt64(UInt64& value) override;
+ void handleUInt8(uint8_t& value);
+ void handleUInt16(uint16_t& value);
+ void handleUInt32(UInt32& value);
+ void handleUInt64(UInt64& value);
- virtual void handleFloat32(float& value) override;
- virtual void handleFloat64(double& value) override;
+ void handleFloat32(float& value);
+ void handleFloat64(double& value);
- virtual void handleString(String& value) override;
+ void handleString(String& value);
+
+ struct Scope
+ {
+ // The RIFF serialization back-end is currently
+ // not taking advantage of the `Scope` facility
+ // in the serialization framework.
+ };
- virtual void beginArray() override;
- virtual void endArray() override;
+ void beginArray(Scope&);
+ void endArray(Scope&);
- virtual void beginDictionary() override;
- virtual void endDictionary() override;
+ void beginDictionary(Scope&);
+ void endDictionary(Scope&);
- virtual bool hasElements() override;
+ bool hasElements();
- virtual void beginStruct() override;
- virtual void endStruct() override;
+ void beginStruct(Scope&);
+ void endStruct(Scope&);
- virtual void beginVariant() override;
- virtual void endVariant() override;
+ void beginVariant(Scope&);
+ void endVariant(Scope&);
- virtual void handleFieldKey(char const* name, Int index) override;
+ void handleFieldKey(char const* name, Int index);
- virtual void beginTuple() override;
- virtual void endTuple() override;
+ void beginTuple(Scope&);
+ void endTuple(Scope&);
- virtual void beginOptional() override;
- virtual void endOptional() override;
+ void beginOptional(Scope&);
+ void endOptional(Scope&);
- virtual void handleSharedPtr(void*& value, Callback callback, void* context) override;
- virtual void handleUniquePtr(void*& value, Callback callback, void* context) override;
+ void handleSharedPtr(void*& value, SerializerCallback callback, void* context);
+ void handleUniquePtr(void*& value, SerializerCallback callback, void* context);
- virtual void handleDeferredObjectContents(void* valuePtr, Callback callback, void* context)
- override;
+ void handleDeferredObjectContents(void* valuePtr, SerializerCallback callback, void* context);
};
} // namespace Slang
diff --git a/source/slang/slang-serialize.h b/source/slang/slang-serialize.h
index 261f3f2d6..4aad5a4f2 100644
--- a/source/slang/slang-serialize.h
+++ b/source/slang/slang-serialize.h
@@ -21,26 +21,11 @@
// SomeObject* object;
// };
//
-// then you can declare serialization support for your type
+// then you can implement serialization support for your type
// with something like:
//
-// // my-thing.h
-// ...
-// #include "slang-serialize.h"
-// ...
-//
-// struct MyThing { ... }
-//
-// void serialize(Serializer const& serializer, MyThing& value);
-//
-// and then implement that support with something like:
-//
-// // my-thing.cpp
-// #include "my-thing.h"
-//
-// ...
-//
-// void serialize(Serializer const& serializer, MyThing& value)
+// template<typename S>
+// void serialize(S const& serializer, MyThing& value)
// {
// SLANG_SCOPED_SERIALIZER_STRUCT(serializer);
// serialize(serializer, value.a);
@@ -81,17 +66,56 @@ enum class SerializationMode
};
//
-// In order to support different serialized formats, and to
-// abstract over the difference between reading and writing,
-// we define a base interface for serialization. This interface
-// is somewhat user-unfriendly, and is *not* intended for
-// ordinary code to interface with directly.
+// Complex pointer-based object graphs can be challenging for
+// a serialization system. Naively written recursive serialization
+// logic can lead to extremely deep call graphs at runtime
+// (potentially overflowing the stack), and when deserialization
+// needs to interact with systems for caching and/or deduplicating
+// objects, the ordering constraints imposed by those systems
+// must be respected.
+//
+// This serialization system relies on callback functions in
+// a few places to allow serialization actions to be queued up or
+// otherwise rescheduled to avoid these problems.
+//
+
+/// Callback type used by the serialization infrastructure.
+///
+/// Users typically will never define callbacks manually; the
+/// system creates and manages them behind the scenes, based
+/// on user-defined types.
+///
+typedef void (*SerializerCallback)(void* valuePtr, void* impl, void* context);
+
+//
+// In order to achieve the best possible performance,
+// serialization functions need to be statically specialized
+// to the particular data format being used. To that
+// end, concrete serialization formats are expected to
+// model the right concept (provide the right methods,
+// type declarations, etc.), but there is not a single
+// base class or interface that they all *must* inherit from.
+//
+// However, to help illustrate the requirements for a
+// complete serializer implementation, we will declare an
+// interface that helps illustrate the outline of what
+// a serializer implementation needs to provide.
+//
+// Note: this interface is somewhat user-unfriendly. It
+// is *not* intended to be something that user code would
+// usually interact with directly, and instead is intended
+// to define only the operations that a serialization
+// back-end must support.
//
/// Base interface for serialization.
///
/// Can be used for both reading and writing of serialized data.
///
+/// Note that implementations of serialization back-ends do
+/// *not* need to inherit from this type; it currently serves
+/// only to define the requirements.
+///
struct ISerializerImpl
{
/// Get the mode that this serializer is operating in (reading or writing).
@@ -217,6 +241,35 @@ struct ISerializerImpl
///
virtual void handleString(String& value) = 0;
+ //
+ // Every concrete serializer implementation should define
+ // its own `Scope` type, which will be passed as the
+ // operand to the various paired `begin`/`end` operations
+ // below. Code that is calling into a serializer takes
+ // responsibility for maintaining the `Scope` for the
+ // duration of the `begin`/`end` pair, and passing the
+ // same data in to `end` that was passed into `begin`,
+ // without making any modifications of its own.
+ //
+ // An implementation may be able to use the `Scope` to
+ // store information related to the hierarchy of `begin`
+ // and `end` operations, and thus avoid needing to
+ // maintain its own heap-allocated stack structure.
+ //
+ struct Scope
+ {
+ // The implementation of `Scope` in this placeholder
+ // interface is intended to define a kind of upper
+ // bound on the size of reasonable scope representations.
+ //
+ // The intention is that any concrete implementation
+ // could be wrapped up as an `ISerializerImpl`, so
+ // long as its `Scope` fits within the storage limit
+ // defined here.
+ //
+ void* storage[8];
+ };
+
/// Begin serializing an array value.
///
/// An array should be used to serialize an
@@ -235,10 +288,10 @@ struct ISerializerImpl
/// and serialize values in a loop until `hasElements()`
/// returns `false`.
///
- virtual void beginArray() = 0;
+ virtual void beginArray(Scope&) = 0;
/// End serializing an array value.
- virtual void endArray() = 0;
+ virtual void endArray(Scope&) = 0;
/// Begin serializing an optional value.
///
@@ -258,10 +311,10 @@ struct ISerializerImpl
/// test whether the serialized optional has a value and,
/// if it does, read the value before calling `endOptional()`.
///
- virtual void beginOptional() = 0;
+ virtual void beginOptional(Scope&) = 0;
/// End serializing an optional value.
- virtual void endOptional() = 0;
+ virtual void endOptional(Scope&) = 0;
/// Begin serializing a dictionary value.
///
@@ -285,10 +338,10 @@ struct ISerializerImpl
/// and serialize values in a loop until `hasElements()`
/// returns `false`.
///
- virtual void beginDictionary() = 0;
+ virtual void beginDictionary(Scope&) = 0;
/// End serializing a dictionary value.
- virtual void endDictionary() = 0;
+ virtual void endDictionary(Scope&) = 0;
/// Check whether there are elements remaining to be read
/// from a serialized container.
@@ -309,10 +362,10 @@ struct ISerializerImpl
/// to `hasElements()` are allowed between `beginTuple()`
/// and `endTuple()`.
///
- virtual void beginTuple() = 0;
+ virtual void beginTuple(Scope&) = 0;
/// End serializing a tuple value.
- virtual void endTuple() = 0;
+ virtual void endTuple(Scope&) = 0;
/// Begin serializing a struct value.
@@ -330,10 +383,10 @@ struct ISerializerImpl
/// they were written, and how to handle attempts
/// to read a field that was not written.
///
- virtual void beginStruct() = 0;
+ virtual void beginStruct(Scope&) = 0;
/// End serializing a struct value.
- virtual void endStruct() = 0;
+ virtual void endStruct(Scope&) = 0;
/// Begin serializing a variant value.
///
@@ -354,10 +407,10 @@ struct ISerializerImpl
/// struct always including the same members in the same
/// order.
///
- virtual void beginVariant() = 0;
+ virtual void beginVariant(Scope&) = 0;
/// End serializing a variant value.
- virtual void endVariant() = 0;
+ virtual void endVariant(Scope&) = 0;
/// Set the key for the next struct field to be serialized.
///
@@ -433,11 +486,12 @@ struct ISerializerImpl
};
//
-// Rather than interact with instances of `ISerializerImpl` directly,
+// Rather than interface with serialization back-ends directly,
// most client code will use a wrapper type that amounts to a kind
// of smart pointer.
//
-// While the `ISerializerImpl` interface can cover a wide range of
+// While serialization back-ends providing operations comparable to
+// `ISerializerImpl` above can cover a wide range of
// types that need to be serialized, it is common for types to require
// more specific *context* to be available in order to perform serialization.
// For example, code might need access to a factory object in order
@@ -451,35 +505,40 @@ struct ISerializerImpl
/// Base type for serialization contexts.
///
-/// The type parameter `Impl` should be a type that derives from
-/// `ISerializerImpl`, and the `Context` type parameter can be any
-/// type that passes along additional context information needed.
+/// The type parameter `I` should be the serialization back-end
+/// implementation (e.g., `ISerializerImpl`), while the `C` type
+/// parameter can be any type that carries additional context
+/// information needed.
///
-template<typename Impl, typename Context>
-struct SerializerBase
+template<typename I, typename C>
+struct Serializer
{
public:
- SerializerBase() = default;
- SerializerBase(Impl* impl, Context* context = nullptr)
+ using Impl = I;
+ using Context = C;
+
+ SLANG_FORCE_INLINE Serializer() = default;
+ SLANG_FORCE_INLINE Serializer(Impl* impl, Context* context = nullptr)
: _impl(impl), _context(context)
{
}
- template<typename I, typename C>
- SerializerBase(
- SerializerBase<I, C> const& serializer,
+ template<typename SourceImpl, typename SourceContext>
+ SLANG_FORCE_INLINE Serializer(
+ Serializer<SourceImpl, SourceContext> const& serializer,
std::enable_if_t<
- std::is_convertible_v<I*, Impl*> && std::is_convertible_v<C*, Context*>,
+ std::is_convertible_v<SourceImpl*, Impl*> &&
+ std::is_convertible_v<SourceContext*, Context*>,
void>* = nullptr)
: _impl(serializer.getImpl()), _context(serializer.getContext())
{
}
- Impl* getImpl() const { return _impl; }
- Context* getContext() const { return _context; }
+ SLANG_FORCE_INLINE Impl* getImpl() const { return _impl; }
+ SLANG_FORCE_INLINE Context* getContext() const { return _context; }
- Impl* get() const { return _impl; }
- Impl* operator->() const { return get(); }
+ SLANG_FORCE_INLINE Impl* get() const { return _impl; }
+ SLANG_FORCE_INLINE Impl* operator->() const { return get(); }
private:
@@ -487,113 +546,109 @@ private:
Context* _context = nullptr;
};
-/// A serialization context.
-///
-/// The type parameter `Impl` should be a type that derives from
-/// `ISerializerImpl`, and the `Context` type parameter can be any
-/// type that passes along additional context information needed.
-///
-template<typename Impl, typename Context>
-struct Serializer_ : SerializerBase<Impl, Context>
-{
- using SerializerBase<Impl, Context>::SerializerBase;
-};
-
-/// Default serialization context.
-using Serializer = Serializer_<ISerializerImpl, void>;
-
//
// We define namespace-scope functions that mirror some
-// of the operations of `ISerializerImpl`, so that they
-// can be invoked on any type that is contextually
-// convertible to a `Serializer`. This allows users
-// to define their own serialization context types while
-// still being able to take advantage of the utility
-// operations in this file for serializing basic types,
-// arrays, dictionaries, etc.
+// of the operations of `ISerializerImpl`, to insulate
+// user-defined `serialize()` function implementations
+// a bit from the details of the serializer concept/interface.
//
-
/// Get the mode of `serializer`.
-inline SerializationMode getMode(Serializer const& serializer)
+template<typename S>
+SLANG_FORCE_INLINE SerializationMode getMode(S const& serializer)
{
return serializer->getMode();
}
/// Check if `serializer` is reading serialized data.
-inline bool isReading(Serializer const& serializer)
+template<typename S>
+SLANG_FORCE_INLINE bool isReading(S const& serializer)
{
return getMode(serializer) == SerializationMode::Read;
}
/// Check if `serializer` is writing serialized data.
-inline bool isWriting(Serializer const& serializer)
+template<typename S>
+SLANG_FORCE_INLINE bool isWriting(S const& serializer)
{
return getMode(serializer) == SerializationMode::Write;
}
/// Check if `serializer` has more container elements.
-inline bool hasElements(Serializer const& serializer)
+template<typename S>
+SLANG_FORCE_INLINE bool hasElements(S const& serializer)
{
return serializer->hasElements();
}
-inline void serialize(Serializer const& serializer, bool& value)
+template<typename S>
+SLANG_FORCE_INLINE void serialize(S const& serializer, bool& value)
{
serializer->handleBool(value);
}
-inline void serialize(Serializer const& serializer, int8_t& value)
+template<typename S>
+SLANG_FORCE_INLINE void serialize(S const& serializer, int8_t& value)
{
serializer->handleInt8(value);
}
-inline void serialize(Serializer const& serializer, int16_t& value)
+template<typename S>
+SLANG_FORCE_INLINE void serialize(S const& serializer, int16_t& value)
{
serializer->handleInt16(value);
}
-inline void serialize(Serializer const& serializer, Int32& value)
+template<typename S>
+SLANG_FORCE_INLINE void serialize(S const& serializer, Int32& value)
{
serializer->handleInt32(value);
}
-inline void serialize(Serializer const& serializer, Int64& value)
+template<typename S>
+SLANG_FORCE_INLINE void serialize(S const& serializer, Int64& value)
{
serializer->handleInt64(value);
}
-inline void serialize(Serializer const& serializer, uint8_t& value)
+template<typename S>
+SLANG_FORCE_INLINE void serialize(S const& serializer, uint8_t& value)
{
serializer->handleUInt8(value);
}
-inline void serialize(Serializer const& serializer, uint16_t& value)
+template<typename S>
+SLANG_FORCE_INLINE void serialize(S const& serializer, uint16_t& value)
{
serializer->handleUInt16(value);
}
-inline void serialize(Serializer const& serializer, UInt32& value)
+template<typename S>
+SLANG_FORCE_INLINE void serialize(S const& serializer, UInt32& value)
{
serializer->handleUInt32(value);
}
-inline void serialize(Serializer const& serializer, UInt64& value)
+template<typename S>
+SLANG_FORCE_INLINE void serialize(S const& serializer, UInt64& value)
{
serializer->handleUInt64(value);
}
-inline void serialize(Serializer const& serializer, float& value)
+template<typename S>
+SLANG_FORCE_INLINE void serialize(S const& serializer, float& value)
{
serializer->handleFloat32(value);
}
-inline void serialize(Serializer const& serializer, double& value)
+template<typename S>
+SLANG_FORCE_INLINE void serialize(S const& serializer, double& value)
{
serializer->handleFloat64(value);
}
-inline void serialize(Serializer const& serializer, String& value)
+template<typename S>
+SLANG_FORCE_INLINE void serialize(S const& serializer, String& value)
{
serializer->handleString(value);
}
@@ -604,8 +659,8 @@ inline void serialize(Serializer const& serializer, String& value)
/// converting it to/from the given `RawType` for storage
/// in the serialized format.
///
-template<typename RawType = Int32, typename EnumType>
-void serializeEnum(Serializer const& serializer, EnumType& value)
+template<typename RawType = Int32, typename S, typename EnumType>
+SLANG_FORCE_INLINE void serializeEnum(S const& serializer, EnumType& value)
{
auto raw = RawType(value);
serialize(serializer, raw);
@@ -619,136 +674,152 @@ void serializeEnum(Serializer const& serializer, EnumType& value)
// each of those types we define a macro to simplify
// introducing a coresponding scope.
//
+// These types handle the details of properly allocating
+// and retaining a `Scope` value appropriate to the
+// serializer implementation, and passing it into both
+// the `begin` and `end` operations.
+//
+template<typename S>
struct ScopedSerializerArray
{
public:
- ScopedSerializerArray(Serializer const& serializer)
+ SLANG_FORCE_INLINE ScopedSerializerArray(S const& serializer)
: _serializer(serializer)
{
- serializer->beginArray();
+ serializer->beginArray(_scope);
}
- ~ScopedSerializerArray() { _serializer->endArray(); }
+ SLANG_FORCE_INLINE ~ScopedSerializerArray() { _serializer->endArray(_scope); }
private:
- Serializer _serializer;
+ S _serializer;
+ typename S::Impl::Scope _scope;
};
+template<typename S>
struct ScopedSerializerDictionary
{
public:
- ScopedSerializerDictionary(Serializer const& serializer)
+ SLANG_FORCE_INLINE ScopedSerializerDictionary(S const& serializer)
: _serializer(serializer)
{
- serializer->beginDictionary();
+ serializer->beginDictionary(_scope);
}
- ~ScopedSerializerDictionary() { _serializer->endDictionary(); }
+ SLANG_FORCE_INLINE ~ScopedSerializerDictionary() { _serializer->endDictionary(_scope); }
private:
- Serializer _serializer;
+ S _serializer;
+ typename S::Impl::Scope _scope;
};
+template<typename S>
struct ScopedSerializerStruct
{
public:
- ScopedSerializerStruct(Serializer const& serializer)
+ SLANG_FORCE_INLINE ScopedSerializerStruct(S const& serializer)
: _serializer(serializer)
{
- serializer->beginStruct();
+ serializer->beginStruct(_scope);
}
- ~ScopedSerializerStruct() { _serializer->endStruct(); }
+ SLANG_FORCE_INLINE ~ScopedSerializerStruct() { _serializer->endStruct(_scope); }
private:
- Serializer _serializer;
+ S _serializer;
+ typename S::Impl::Scope _scope;
};
+template<typename S>
struct ScopedSerializerVariant
{
public:
- ScopedSerializerVariant(Serializer const& serializer)
+ SLANG_FORCE_INLINE ScopedSerializerVariant(S const& serializer)
: _serializer(serializer)
{
- serializer->beginVariant();
+ serializer->beginVariant(_scope);
}
- ~ScopedSerializerVariant() { _serializer->endVariant(); }
+ SLANG_FORCE_INLINE ~ScopedSerializerVariant() { _serializer->endVariant(_scope); }
private:
- Serializer _serializer;
+ S _serializer;
+ typename S::Impl::Scope _scope;
};
+template<typename S>
struct ScopedSerializerTuple
{
public:
- ScopedSerializerTuple(Serializer const& serializer)
+ SLANG_FORCE_INLINE ScopedSerializerTuple(S const& serializer)
: _serializer(serializer)
{
- serializer->beginTuple();
+ serializer->beginTuple(_scope);
}
- ~ScopedSerializerTuple() { _serializer->endTuple(); }
+ SLANG_FORCE_INLINE ~ScopedSerializerTuple() { _serializer->endTuple(_scope); }
private:
- Serializer _serializer;
+ S _serializer;
+ typename S::Impl::Scope _scope;
};
+template<typename S>
struct ScopedSerializerOptional
{
public:
- ScopedSerializerOptional(Serializer const& serializer)
+ SLANG_FORCE_INLINE ScopedSerializerOptional(S const& serializer)
: _serializer(serializer)
{
- serializer->beginOptional();
+ serializer->beginOptional(_scope);
}
- ~ScopedSerializerOptional() { _serializer->endOptional(); }
+ SLANG_FORCE_INLINE ~ScopedSerializerOptional() { _serializer->endOptional(_scope); }
private:
- Serializer _serializer;
+ S _serializer;
+ typename S::Impl::Scope _scope;
};
-#define SLANG_SCOPED_SERIALIZER_ARRAY(SERIALIZER) \
- ::Slang::ScopedSerializerArray SLANG_CONCAT(_scopedSerializerArray, __LINE__)(SERIALIZER)
+#define SLANG_SCOPED_SERIALIZER_ARRAY(SERIALIZER) \
+ ::Slang::ScopedSerializerArray<std::remove_reference_t<decltype(SERIALIZER)>> SLANG_CONCAT( \
+ _scopedSerializerArray, \
+ __LINE__)(SERIALIZER)
-#define SLANG_SCOPED_SERIALIZER_DICTIONARY(SERIALIZER) \
- ::Slang::ScopedSerializerDictionary SLANG_CONCAT(_scopedSerializerDictionary, __LINE__)( \
- SERIALIZER)
+#define SLANG_SCOPED_SERIALIZER_DICTIONARY(SERIALIZER) \
+ ::Slang::ScopedSerializerDictionary<std::remove_reference_t<decltype(SERIALIZER)>> \
+ SLANG_CONCAT(_scopedSerializerDictionary, __LINE__)(SERIALIZER)
-#define SLANG_SCOPED_SERIALIZER_OPTIONAL(SERIALIZER) \
- ::Slang::ScopedSerializerOptional SLANG_CONCAT(_scopedSerializerOptional, __LINE__)(SERIALIZER)
+#define SLANG_SCOPED_SERIALIZER_OPTIONAL(SERIALIZER) \
+ ::Slang::ScopedSerializerOptional<std::remove_reference_t<decltype(SERIALIZER)>> SLANG_CONCAT( \
+ _scopedSerializerOptional, \
+ __LINE__)(SERIALIZER)
-#define SLANG_SCOPED_SERIALIZER_STRUCT(SERIALIZER) \
- ::Slang::ScopedSerializerStruct SLANG_CONCAT(_scopedSerializerStruct, __LINE__)(SERIALIZER)
+#define SLANG_SCOPED_SERIALIZER_STRUCT(SERIALIZER) \
+ ::Slang::ScopedSerializerStruct<std::remove_reference_t<decltype(SERIALIZER)>> SLANG_CONCAT( \
+ _scopedSerializerStruct, \
+ __LINE__)(SERIALIZER)
-#define SLANG_SCOPED_SERIALIZER_VARIANT(SERIALIZER) \
- ::Slang::ScopedSerializerVariant SLANG_CONCAT(_scopedSerializerVariant, __LINE__)(SERIALIZER)
+#define SLANG_SCOPED_SERIALIZER_VARIANT(SERIALIZER) \
+ ::Slang::ScopedSerializerVariant<std::remove_reference_t<decltype(SERIALIZER)>> SLANG_CONCAT( \
+ _scopedSerializerVariant, \
+ __LINE__)(SERIALIZER)
-#define SLANG_SCOPED_SERIALIZER_TUPLE(SERIALIZER) \
- ::Slang::ScopedSerializerTuple SLANG_CONCAT(_scopedSerializerTuple, __LINE__)(SERIALIZER)
+#define SLANG_SCOPED_SERIALIZER_TUPLE(SERIALIZER) \
+ ::Slang::ScopedSerializerTuple<std::remove_reference_t<decltype(SERIALIZER)>> SLANG_CONCAT( \
+ _scopedSerializerTuple, \
+ __LINE__)(SERIALIZER)
//
// Containers like arrays and dictionaries are more
// difficult to serialize than typical user-defined
-// types for a few reasons:
-//
-// * They typically need to have distinct code paths
-// for reading and writing, so they don't benefit
-// much from having a unified read/write abstraction.
-//
-// * They need to be written as templates, to abstract
-// over the element type, and thus need to be
-// defined in headers.
-//
-// * Because the element type might require a more
-// specialized type of serialization context, they
-// also need to be templated on the type of the
-// serializer itself.
-//
-// With all that said, the definitions themselves
+// types because they typically need to have distinct
+// code paths for reading and writing, so they don't
+// benefit much from having a unified read/write abstraction.
+///
+// That said, the definitions themselves
// are fairly straightforward. All we have to do is
// branch on whether we are reading or writing and
// either iterate over the serialized data to fill
@@ -856,7 +927,7 @@ void serialize(S const& serializer, KeyValuePair<K, V>& value)
}
template<typename S, typename K, typename V>
-void serialize(S const& serializer, std::pair<K, V>& value)
+SLANG_FORCE_INLINE void serialize(S const& serializer, std::pair<K, V>& value)
{
SLANG_SCOPED_SERIALIZER_TUPLE(serializer);
serialize(serializer, value.first);
@@ -864,7 +935,7 @@ void serialize(S const& serializer, std::pair<K, V>& value)
}
template<typename S, typename K, typename V>
-void serialize(S const& serializer, Dictionary<K, V>& value)
+SLANG_FORCE_INLINE void serialize(S const& serializer, Dictionary<K, V>& value)
{
SLANG_SCOPED_SERIALIZER_DICTIONARY(serializer);
if (isWriting(serializer))
@@ -957,27 +1028,26 @@ void serialize(S const& serializer, OrderedDictionary<K, V>& value)
//
template<typename S, typename T>
-void serializeObjectContents(S const& serializer, T* value, void*)
+SLANG_FORCE_INLINE void serializeObjectContents(S const& serializer, T* value, void*)
{
serialize(serializer, *value);
}
-template<typename I, typename C, typename T>
+template<typename S, typename T>
void _serializeObjectContentsCallback(void* valuePtr, void* impl, void* context)
{
- Serializer_<I, C> serializer((I*)impl, (C*)context);
+ S serializer((typename S::Impl*)impl, (typename S::Context*)context);
auto value = (T*)valuePtr;
serializeObjectContents(serializer, value, (T*)nullptr);
}
-template<typename I, typename C, typename T>
-void deferSerializeObjectContents(Serializer_<I, C> const& serializer, T* value)
+template<typename S, typename T>
+SLANG_FORCE_INLINE void deferSerializeObjectContents(S const& serializer, T* value)
{
- ((Serializer)serializer)
- ->handleDeferredObjectContents(
- value,
- _serializeObjectContentsCallback<I, C, T>,
- serializer.getContext());
+ serializer->handleDeferredObjectContents(
+ value,
+ _serializeObjectContentsCallback<S, T>,
+ serializer.getContext());
}
template<typename S, typename T>
@@ -990,48 +1060,46 @@ void serializeObject(S const& serializer, T*& value, void*)
deferSerializeObjectContents(serializer, value);
}
-template<typename I, typename C, typename T>
+template<typename S, typename T>
void _serializeObjectCallback(void* valuePtr, void* impl, void* context)
{
- Serializer_<I, C> serializer((I*)impl, (C*)context);
+ S serializer((typename S::Impl*)impl, (typename S::Context*)context);
auto& value = *(T**)valuePtr;
serializeObject(serializer, value, (T*)nullptr);
}
-template<typename I, typename C, typename T>
-void serializeSharedPtr(Serializer_<I, C> const& serializer, T*& value)
+template<typename S, typename T>
+SLANG_FORCE_INLINE void serializeSharedPtr(S const& serializer, T*& value)
{
- ((Serializer)serializer)
- ->handleSharedPtr(
- *(void**)&value,
- _serializeObjectCallback<I, C, T>,
- serializer.getContext());
+ serializer->handleSharedPtr(
+ *(void**)&value,
+ _serializeObjectCallback<S, T>,
+ serializer.getContext());
}
-template<typename I, typename C, typename T>
-void serializeUniquePtr(Serializer_<I, C> const& serializer, T*& value)
+template<typename S, typename T>
+SLANG_FORCE_INLINE void serializeUniquePtr(S const& serializer, T*& value)
{
- ((Serializer)serializer)
- ->handleUniquePtr(
- *(void**)&value,
- _serializeObjectCallback<I, C, T>,
- serializer.getContext());
+ serializer->handleUniquePtr(
+ *(void**)&value,
+ _serializeObjectCallback<S, T>,
+ serializer.getContext());
}
template<typename S, typename T>
-void serializePtr(S const& serializer, T*& value, void*)
+SLANG_FORCE_INLINE void serializePtr(S const& serializer, T*& value, void*)
{
serializeSharedPtr(serializer, value);
}
template<typename S, typename T>
-void serialize(S const& serializer, T*& value)
+SLANG_FORCE_INLINE void serialize(S const& serializer, T*& value)
{
serializePtr(serializer, value, (T*)nullptr);
}
template<typename S, typename T>
-void serialize(S const& serializer, RefPtr<T>& value)
+SLANG_FORCE_INLINE void serialize(S const& serializer, RefPtr<T>& value)
{
T* raw = value;
serialize(serializer, raw);