From 1d5f815b3964edee8a2d701e1a6cc078c89d677f Mon Sep 17 00:00:00 2001 From: jsmall-nvidia Date: Thu, 18 Nov 2021 15:58:12 -0500 Subject: RTTI/JSON (#2021) * #include an absolute path didn't work - because paths were taken to always be relative. * Use 'Process' to communicate with an command line tool. * Remove slang-win-stream * Tidy up windows ProcessUtil. * First version of BufferedReadStream. * Windows working IPC for steams. * Test proxy count option. * Split Process/ProcessUtil. Process is platform dependant. ProcessUtil are functions that are platform independent. * First implementation of Unix Process interface. * Unix process compiles on cygwin. * Fix typo in unix process. * Separate unix pipe stream error of invalid access, from pipe availability. * Fix in standard line extraction. * Make fd non blocking. * Fix issues with Windows Process streams. * Added UnixPipe. * Some fixes around UnixPipeStream. * Make a unix stream closed explicit. * Hack to debug linux process/stream. * Revert to old linux pipe handling. * Pass executable path for unit tests. Split out CommandLine into own source. * Small improvements in process/command line. * Check process behavior with crash. * Make stderr and stdout unbuffered for crash testing. * Only turn disable buffering in crash test. * Disable crash test on CI. * Fix crash on clang/linux. * Enable crash test. Remove _appendBuffer as can use StreamUtil functionality. * Added inital processing for http headers. * Small improvements to HttpHeader. * First pass HTTPPacketConnection working on windows. * Enable other Process communication tests. * Update comments. * WIP JSON RPC. * Add terminate to Process. Made JSONRPC a Util. * Small tidy up around HTTPPacketConnection. * Improve process termination options. * WIP for test-server. * Add diagnostics error handling to test-server. * Improved JSON support. Parsing/creating JSON-RPC messages. * WIP JSONRPC parsing. * First pass RttiInfo support. * WIP converting between JSON/native types. * Project files. * Split out RttiUtil. Made RttiInfo constuction thread safe. * WIP RTTI<->JSON. * Add diagnostics to JSON<->native conversions. * Make RttiInfo for structs globals. Avoids problem around derived types (like pointers), being able to cause an abort. * Add pointer support to RTTI. Fixed some compilation issues on linux. * Add fixed array support. * Added Rtti unit test. * Add rtti unit test. * Split out quoted/unquoted key handling. Fix bugs in JSON value/container. Added JSON native test. * Make default array allocator use malloc/free. Remove the new[] handler (doesn't work on visuals studio). * Fix for linux warning. * Remove some test code. * Fix issues on x86 win. * Fix warning on aarch64. --- source/core/slang-allocator.h | 21 +-- source/core/slang-common.h | 3 +- source/core/slang-list.h | 13 +- source/core/slang-rtti-info.cpp | 184 +++++++++++++++++++ source/core/slang-rtti-info.h | 341 ++++++++++++++++++++++++++++++++++ source/core/slang-rtti-util.cpp | 392 ++++++++++++++++++++++++++++++++++++++++ source/core/slang-rtti-util.h | 30 +++ 7 files changed, 971 insertions(+), 13 deletions(-) create mode 100644 source/core/slang-rtti-info.cpp create mode 100644 source/core/slang-rtti-info.h create mode 100644 source/core/slang-rtti-util.cpp create mode 100644 source/core/slang-rtti-util.h (limited to 'source/core') diff --git a/source/core/slang-allocator.h b/source/core/slang-allocator.h index f25fd92c9..bc1b880f8 100644 --- a/source/core/slang-allocator.h +++ b/source/core/slang-allocator.h @@ -19,11 +19,9 @@ namespace Slang #elif defined(__CYGWIN__) return aligned_alloc(alignment, size); #else - void * rs = 0; + void* rs = nullptr; int succ = posix_memalign(&rs, alignment, size); - if (succ!=0) - rs = 0; - return rs; + return (succ == 0) ? rs : nullptr; #endif } @@ -66,18 +64,15 @@ namespace Slang // Helper utilties for calling allocators. template - class Initializer - { - - }; + class Initializer; template class Initializer { public: - static void initialize(T* buffer, int size) + static void initialize(T* buffer, Index size) { - for (int i = 0; i < size; i++) + for (Index i = 0; i < size; i++) new (buffer + i) T(); } }; @@ -85,8 +80,10 @@ namespace Slang class Initializer { public: - static void initialize(T* buffer, int size) + static void initialize(T* buffer, Index size) { + SLANG_UNUSED(buffer); + SLANG_UNUSED(size); // It's pod so no initialization required //for (int i = 0; i < size; i++) // new (buffer + i) T; @@ -116,6 +113,7 @@ namespace Slang } }; +#if 0 template class AllocateMethod { @@ -129,6 +127,7 @@ namespace Slang delete[] ptr; } }; +#endif } #endif diff --git a/source/core/slang-common.h b/source/core/slang-common.h index eb6502b41..6f91da873 100644 --- a/source/core/slang-common.h +++ b/source/core/slang-common.h @@ -33,8 +33,9 @@ namespace Slang // TODO(JS): It looks like Index is actually 64 bit on 64 bit targets(!) // Previous discussions landed on Index being int32_t. - // Type used for indexing, in arrays/views etc + // Type used for indexing, in arrays/views etc. Signed. typedef Int Index; + typedef UInt UIndex; static const Index kMaxIndex = kMaxInt; diff --git a/source/core/slang-list.h b/source/core/slang-list.h index 4420cc084..25687d129 100644 --- a/source/core/slang-list.h +++ b/source/core/slang-list.h @@ -145,6 +145,15 @@ namespace Slang m_capacity = 0; return rs; } + void attachBuffer(T* buffer, Index count, Index capacity) + { + // Can only attach a buffer if there isn't a buffer already associated + SLANG_ASSERT(m_buffer == nullptr); + SLANG_ASSERT(count <= capacity); + m_buffer = buffer; + m_count = count; + m_capacity = capacity; + } inline ArrayView getArrayView() const { @@ -324,7 +333,9 @@ namespace Slang void reserve(Index size) { - if(size > m_capacity) + // The cast for this comparison is needed, otherwise some compilers erroneously detect + // the possiblity of a zero sized allocation (possible if m_capacity is assumed to be negative). + if(UIndex(size) > UIndex(m_capacity)) { T* newBuffer = _allocate(size); if (m_capacity) diff --git a/source/core/slang-rtti-info.cpp b/source/core/slang-rtti-info.cpp new file mode 100644 index 000000000..f53cf742f --- /dev/null +++ b/source/core/slang-rtti-info.cpp @@ -0,0 +1,184 @@ +#include "slang-rtti-info.h" + +#include "../../slang-com-helper.h" + +#include + +namespace Slang { + +#define SLANG_RTTI_INFO_INVALID(name) RttiInfo{RttiInfo::Kind::Invalid, 0, 0} +#define SLANG_RTTI_INFO_BASIC(name, type) \ + RttiInfo{RttiInfo::Kind::name, RttiInfo::AlignmentType(SLANG_ALIGN_OF(type)), RttiInfo::SizeType(sizeof(type))} + +/* static */const RttiInfo RttiInfo::g_basicTypes[Index(Kind::CountOf)] = +{ + SLANG_RTTI_INFO_INVALID(Invalid), + SLANG_RTTI_INFO_BASIC(I32, int32_t), + SLANG_RTTI_INFO_BASIC(U32, uint32_t), + SLANG_RTTI_INFO_BASIC(I64, int64_t), + SLANG_RTTI_INFO_BASIC(U64, uint64_t), + SLANG_RTTI_INFO_BASIC(F32, float), + SLANG_RTTI_INFO_BASIC(F64, double), + SLANG_RTTI_INFO_BASIC(Bool, bool), + SLANG_RTTI_INFO_BASIC(String, String), + SLANG_RTTI_INFO_BASIC(UnownedStringSlice, UnownedStringSlice), + SLANG_RTTI_INFO_BASIC(Ptr, void*), + SLANG_RTTI_INFO_BASIC(RefPtr, RefPtr), + SLANG_RTTI_INFO_INVALID(FixedArray), + SLANG_RTTI_INFO_INVALID(Struct), + SLANG_RTTI_INFO_INVALID(Other), + SLANG_RTTI_INFO_INVALID(Enum), + SLANG_RTTI_INFO_INVALID(List), + SLANG_RTTI_INFO_INVALID(Dictionary), +}; + +struct RttiInfoManager +{ + void* allocate(size_t size) + { + std::lock_guard guard(m_mutex); + return m_arena.allocate(size); + } + + static RttiInfoManager& getSingleton() + { + static RttiInfoManager g_manager; + return g_manager; + } + +protected: + RttiInfoManager() : + m_arena(1024) + { + } + + std::recursive_mutex m_mutex; ///< We need a mutex to guard access to m_arena + MemoryArena m_arena; +}; + +/* static */void* RttiInfo::allocate(size_t size) +{ + return RttiInfoManager::getSingleton().allocate(size); +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! StructRttiBuilder !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +static void _appendFixedArray(const FixedArrayRttiInfo* inFixedArray, StringBuilder& out) +{ + List fixedArrays; + fixedArrays.add(inFixedArray); + + const RttiInfo* cur = inFixedArray->m_elementType; + while (cur->m_kind == RttiInfo::Kind::FixedArray) + { + const FixedArrayRttiInfo* curArray = static_cast(cur); + fixedArrays.add(curArray); + cur = curArray->m_elementType; + } + + // Append the 'target' which is in cur + RttiInfo::append(cur, out); + // Now all the fixed array values, in order + for (auto fixedArray : fixedArrays) + { + out << "[" << int32_t(fixedArray->m_elementCount) << "]"; + } +} + +/* static */void RttiInfo::append(const RttiInfo* info, StringBuilder& out) +{ + switch (info->m_kind) + { + case RttiInfo::Kind::I32: out << "int32_t"; break; + case RttiInfo::Kind::U32: out << "uint32_t"; break; + case RttiInfo::Kind::I64: out << "int64_t"; break; + case RttiInfo::Kind::U64: out << "uint64_t"; break; + case RttiInfo::Kind::F32: out << "float"; break; + case RttiInfo::Kind::F64: out << "double"; break; + case RttiInfo::Kind::Bool: out << "bool"; break; + case RttiInfo::Kind::String: out << "String"; break; + case RttiInfo::Kind::UnownedStringSlice: out << "UnownedStringSlice"; break; + case RttiInfo::Kind::Ptr: + { + const PtrRttiInfo* ptrRttiInfo = static_cast(info); + append(ptrRttiInfo->m_targetType, out); + out << "*"; + break; + } + case RttiInfo::Kind::RefPtr: + { + const RefPtrRttiInfo* ptrRttiInfo = static_cast(info); + out << "RefPtr<"; + append(ptrRttiInfo->m_targetType, out); + out << ">"; + break; + } + case RttiInfo::Kind::FixedArray: + { + const FixedArrayRttiInfo* arrayRttiInfo = static_cast(info); + _appendFixedArray(arrayRttiInfo, out); + break; + } + case RttiInfo::Kind::List: + { + const ListRttiInfo* listRttiInfo = static_cast(info); + out << "List<"; + append(listRttiInfo->m_elementType, out); + out << ">"; + break; + } + case RttiInfo::Kind::Dictionary: + { + const DictionaryRttiInfo* dictionaryRttiInfo = static_cast(info); + + out << "Dictionary<"; + append(dictionaryRttiInfo->m_keyType, out); + out << ","; + append(dictionaryRttiInfo->m_valueType, out); + out << ">"; + break; + } + default: + { + if (info->isNamed()) + { + const NamedRttiInfo* namedRttiInfo = static_cast(info); + out << namedRttiInfo->m_name; + break; + } + + out << "%Unknown%"; + break; + } + } +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! StructRttiBuilder !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +void StructRttiBuilder::_init(const char* name, const StructRttiInfo* super, const Byte* base) +{ + m_rttiInfo.m_name = name; + m_rttiInfo.m_super = super; + m_base = base; + + m_rttiInfo.m_fieldCount = 0; + m_rttiInfo.m_fields = nullptr; +} + +StructRttiInfo StructRttiBuilder::make() +{ + const Index fieldCount = m_fields.getCount(); + + if (fieldCount) + { + StructRttiInfo::Field* dstFields = (StructRttiInfo::Field*)RttiInfo::allocate(sizeof(StructRttiInfo::Field) * fieldCount); + ::memcpy(dstFields, m_fields.getBuffer(), sizeof(StructRttiInfo::Field) * fieldCount); + + m_rttiInfo.m_fields = dstFields; + m_rttiInfo.m_fieldCount = fieldCount; + } + + return m_rttiInfo; +} + +} // namespace Slang diff --git a/source/core/slang-rtti-info.h b/source/core/slang-rtti-info.h new file mode 100644 index 000000000..c1fe1810f --- /dev/null +++ b/source/core/slang-rtti-info.h @@ -0,0 +1,341 @@ +#ifndef SLANG_CORE_RTTI_INFO_H +#define SLANG_CORE_RTTI_INFO_H + +#include "slang-basic.h" +#include "slang-memory-arena.h" + +#include "slang-list.h" +#include "slang-dictionary.h" + +namespace Slang { + +struct RttiInfo; + +struct RttiTypeFuncs +{ + typedef void (*CtorArray)(const RttiInfo* rttiInfo, void* dst, Index count); + typedef void (*DtorArray)(const RttiInfo* rttiInfo, void* dst, Index count); + typedef void (*CopyArray)(const RttiInfo* rttiInfo, void* dst, const void* src, Index count); + + bool isValid() const { return ctorArray && dtorArray && copyArray; } + + static RttiTypeFuncs makeEmpty() { return RttiTypeFuncs{ nullptr, nullptr, nullptr }; } + + CtorArray ctorArray; + DtorArray dtorArray; + CopyArray copyArray; +}; + +template +struct GetRttiTypeFuncs +{ + static void ctorArray(const RttiInfo* rttiInfo, void* in, Index count) + { + SLANG_UNUSED(rttiInfo); + T* dst = (T*)in; + for (Index i = 0; i < count; ++i) + { + new (dst + i) T; + } + } + static void dtorArray(const RttiInfo* rttiInfo, void* in, Index count) + { + SLANG_UNUSED(rttiInfo); + T* dst = (T*)in; + for (Index i = 0; i < count; ++i) + { + (dst + i)->~T(); + } + } + static void copyArray(const RttiInfo* rttiInfo, void* inDst, const void* inSrc, Index count) + { + SLANG_UNUSED(rttiInfo); + T* dst = (T*)inDst; + const T* src = (T*)inSrc; + for (Index i = 0; i < count; ++i) + { + dst[i] = src[i]; + } + } + static RttiTypeFuncs getFuncs() + { + RttiTypeFuncs funcs; + funcs.copyArray = ©Array; + funcs.dtorArray = &dtorArray; + funcs.ctorArray = &ctorArray; + return funcs; + } +}; + +struct RttiInfo +{ + typedef uint8_t AlignmentType; + typedef uint16_t SizeType; + + enum class Kind : uint8_t + { + Invalid, + I32, + U32, + I64, + U64, + F32, + F64, + Bool, + String, + UnownedStringSlice, + Ptr, + RefPtr, + FixedArray, + Struct, + Other, + Enum, + List, + Dictionary, + + CountOf, + }; + + Kind m_kind; + AlignmentType m_alignment; + SizeType m_size; + + void init(Kind kind, size_t alignment, size_t size) { m_kind = kind; m_alignment = AlignmentType(alignment); m_size = SizeType(size); } + + template + void init(Kind kind) { init(kind, SLANG_ALIGN_OF(T), sizeof(T)); } + + /// Allocate memory for RttiInfo types. + /// Is thread safe, and doesn't require the memory to be freed explicitly + /// Will be freed at shutdown (via global dtor) + static void* allocate(size_t size); + + static bool isIntegral(RttiInfo::Kind kind) { return Index(kind) >= Index(RttiInfo::Kind::I32) && Index(kind) <= Index(RttiInfo::Kind::U64); } + static bool isFloat(RttiInfo::Kind kind) { return kind == RttiInfo::Kind::F32 || kind == RttiInfo::Kind::F64; } + static bool isBuiltIn(RttiInfo::Kind kind) { return kind == RttiInfo::Kind::I32 || kind == RttiInfo::Kind::Bool; } + static bool isNamed(RttiInfo::Kind kind) { return Index(kind) >= Index(RttiInfo::Kind::Struct) && Index(kind) <= Index(RttiInfo::Kind::Enum); } + + bool isIntegral() const { return isIntegral(m_kind); } + bool isFloat() const { return isFloat(m_kind); } + bool isBuiltIn() const { return isBuiltIn(m_kind); } + bool isNamed() const { return isNamed(m_kind); } + + static void append(const RttiInfo* info, StringBuilder& out); + + static const RttiInfo g_basicTypes[Index(Kind::CountOf)]; +}; + +// Can combine into flags on a field. Could store default value with a field, +// but this works fine for most purposes +enum class RttiDefaultValue : uint8_t +{ + Normal, ///< Zero for integral/float types/false for bool + One, + MinusOne, + + Mask = 0x7, +}; + +struct NamedRttiInfo : public RttiInfo +{ + const char* m_name; ///< Name +}; + +struct StructRttiInfo : public NamedRttiInfo +{ + typedef uint8_t Flags; + struct Flag + { + enum Enum : Flags + { + // We use low bits for 'RttiDefaultValue' value + Optional = 0x8, + }; + }; + + struct Field + { + const char* m_name; ///< Name of this field + const RttiInfo* m_type; ///< The type of this field + uint32_t m_offset; ///< Offset from object type in bytes + Flags m_flags; ///< Field flags + }; + + const StructRttiInfo* m_super; ///< Super class or nullptr if not defined + + Index m_fieldCount; ///< Amount of fields + const Field* m_fields; ///< Fields +}; + +struct EnumRttiInfo : public NamedRttiInfo +{ + // TODO(JS): +}; + +SLANG_FORCE_INLINE StructRttiInfo::Flags combine(StructRttiInfo::Flags flags, RttiDefaultValue defaultValue) +{ + return StructRttiInfo::Flags(defaultValue) | flags; +} + +struct ListRttiInfo : public RttiInfo +{ + const RttiInfo* m_elementType; +}; + +struct DictionaryRttiInfo : public RttiInfo +{ + const RttiInfo* m_keyType; + const RttiInfo* m_valueType; +}; + +struct PtrRttiInfo : public RttiInfo +{ + const RttiInfo* m_targetType; +}; + +struct RefPtrRttiInfo : public RttiInfo +{ + const RttiInfo* m_targetType; +}; + +struct FixedArrayRttiInfo : public RttiInfo +{ + const RttiInfo* m_elementType; + size_t m_elementCount; +}; + +struct OtherRttiInfo : public NamedRttiInfo +{ + typedef bool (*IsDefaultFunc)(const RttiInfo* rttiInfo, const void* in); + IsDefaultFunc m_isDefaultFunc; + RttiTypeFuncs m_typeFuncs; +}; + +// The default is to just get the info from a global held inside the type. +template +struct GetRttiInfo +{ + SLANG_FORCE_INLINE static const RttiInfo* get() { return &T::g_rttiInfo; } +}; + +template <> struct GetRttiInfo { static const RttiInfo* get() { return &RttiInfo::g_basicTypes[Index(RttiInfo::Kind::Bool)];} }; +template <> struct GetRttiInfo { static const RttiInfo* get() { return &RttiInfo::g_basicTypes[Index(RttiInfo::Kind::I32)]; } }; +template <> struct GetRttiInfo { static const RttiInfo* get() { return &RttiInfo::g_basicTypes[Index(RttiInfo::Kind::I64)]; } }; +template <> struct GetRttiInfo { static const RttiInfo* get() { return &RttiInfo::g_basicTypes[Index(RttiInfo::Kind::U32)]; } }; +template <> struct GetRttiInfo { static const RttiInfo* get() { return &RttiInfo::g_basicTypes[Index(RttiInfo::Kind::U64)]; } }; +template <> struct GetRttiInfo { static const RttiInfo* get() { return &RttiInfo::g_basicTypes[Index(RttiInfo::Kind::F32)]; } }; +template <> struct GetRttiInfo { static const RttiInfo* get() { return &RttiInfo::g_basicTypes[Index(RttiInfo::Kind::F64)]; } }; +template <> struct GetRttiInfo { static const RttiInfo* get() { return &RttiInfo::g_basicTypes[Index(RttiInfo::Kind::String)]; } }; +template <> struct GetRttiInfo { static const RttiInfo* get() { return &RttiInfo::g_basicTypes[Index(RttiInfo::Kind::UnownedStringSlice)]; } }; + +template +struct GetRttiInfo> +{ + static const ListRttiInfo _make() + { + ListRttiInfo info; + info.init>(RttiInfo::Kind::List); + info.m_elementType = GetRttiInfo::get(); + return info; + } + static const RttiInfo* get() { static const ListRttiInfo g_info = _make(); return &g_info; } +}; + +// Strip const +template +struct GetRttiInfo +{ + static const RttiInfo* get() { return GetRttiInfo::get(); } +}; + +template +struct GetRttiInfo> +{ + static const DictionaryRttiInfo _make() + { + DictionaryRttiInfo info; + info.init>(RttiInfo::Kind::Dictionary); + info.m_keyType = GetRttiInfo::get(); + info.m_valueType = GetRttiInfo::get(); + return info; + } + static const RttiInfo* get() { static const DictionaryRttiInfo g_info = _make(); return &g_info; } +}; + +template +struct GetRttiInfo +{ + static const PtrRttiInfo _make() + { + PtrRttiInfo info; + info.init(RttiInfo::Kind::Ptr); + info.m_targetType = GetRttiInfo::get(); + return info; + } + static const RttiInfo* get() { static const PtrRttiInfo g_info = _make(); return &g_info; } +}; + +template +struct GetRttiInfo> +{ + static const RefPtrRttiInfo _make() + { + RefPtrRttiInfo info; + info.init>(RttiInfo::Kind::RefPtr); + info.m_targetType = GetRttiInfo::get(); + return info; + } + static const RttiInfo* get() { static const RefPtrRttiInfo g_info = _make(); return &g_info; } +}; + +template +struct GetRttiInfo +{ + static const FixedArrayRttiInfo _make() + { + FixedArrayRttiInfo info; + info.m_kind = RttiInfo::Kind::FixedArray; + info.m_alignment = RttiInfo::AlignmentType(SLANG_ALIGN_OF(T)); + info.m_size = RttiInfo::SizeType(sizeof(T) * COUNT); + info.m_elementType = GetRttiInfo::get(); + info.m_elementCount = COUNT; + return info; + } + static const RttiInfo* get() { static const FixedArrayRttiInfo g_info = _make(); return &g_info; } +}; + +struct StructRttiBuilder +{ + template + StructRttiBuilder(T* obj, const char* name, const StructRttiInfo* super) + { + m_rttiInfo.init(RttiInfo::Kind::Struct); + _init(name, super, (const Byte*)obj); + } + + template + void addField(const char* name, const T* fieldPtr, StructRttiInfo::Flags flags = 0) + { + StructRttiInfo::Field field; + + field.m_name = name; + field.m_type = GetRttiInfo::get(); + field.m_offset = uint32_t(ptrdiff_t((const Byte*)fieldPtr - m_base)); + field.m_flags = flags; + m_fields.add(field); + } + + StructRttiInfo make(); + + void _init(const char* name, const StructRttiInfo* super, const Byte* base); + + StructRttiInfo m_rttiInfo; + + List m_fields; + const Byte* m_base; +}; + + +} // namespace Slang + +#endif // SLANG_CORE_RTTI_INFO_H diff --git a/source/core/slang-rtti-util.cpp b/source/core/slang-rtti-util.cpp new file mode 100644 index 000000000..ed4c32e58 --- /dev/null +++ b/source/core/slang-rtti-util.cpp @@ -0,0 +1,392 @@ +#include "slang-rtti-util.h" + +namespace Slang { + +/* static */SlangResult RttiUtil::setInt(int64_t value, const RttiInfo* rttiInfo, void* dst) +{ + SLANG_ASSERT(rttiInfo->isIntegral()); + + // We could check ranges are appropriate, but for now we just write. + // Passing in rttiInfo allows for other more complex types to be econverted + switch (rttiInfo->m_kind) + { + case RttiInfo::Kind::I32: *(int32_t*)dst = int32_t(value); break; + case RttiInfo::Kind::U32: *(uint32_t*)dst = uint32_t(value); break; + case RttiInfo::Kind::I64: *(int64_t*)dst = int64_t(value); break; + case RttiInfo::Kind::U64: *(uint64_t*)dst = uint64_t(value); break; + default: return SLANG_FAIL; + } + return SLANG_OK; +} + +/* static */int64_t RttiUtil::getInt64(const RttiInfo* rttiInfo, const void* src) +{ + SLANG_ASSERT(rttiInfo->isIntegral()); + + switch (rttiInfo->m_kind) + { + case RttiInfo::Kind::I32: return *(const int32_t*)src; + case RttiInfo::Kind::U32: return *(const uint32_t*)src; + case RttiInfo::Kind::I64: return *(const int64_t*)src; + case RttiInfo::Kind::U64: return *(const uint64_t*)src; + default: break; + } + + SLANG_ASSERT(!"Not integral!"); + return -1; +} + +/* static */double RttiUtil::asDouble(const RttiInfo* rttiInfo, const void* src) +{ + if (rttiInfo->isIntegral()) + { + return (double)getInt64(rttiInfo, src); + } + else if (rttiInfo->isFloat()) + { + switch (rttiInfo->m_kind) + { + case RttiInfo::Kind::F32: return *(const float*)src; + case RttiInfo::Kind::F64: return *(const double*)src; + default: break; + } + } + + SLANG_ASSERT(!"Cannot convert to float"); + return 0.0; +} + +/* static */SlangResult RttiUtil::setFromDouble(double v, const RttiInfo* rttiInfo, void* dst) +{ + if (rttiInfo->isIntegral()) + { + return setInt(int64_t(v), rttiInfo, dst); + } + else if (rttiInfo->isFloat()) + { + switch (rttiInfo->m_kind) + { + case RttiInfo::Kind::F32: *(float*)dst = float(v); return SLANG_OK; + case RttiInfo::Kind::F64: *(double*)dst = v; return SLANG_OK; + default: break; + } + } + + return SLANG_FAIL; +} + +/* static */bool RttiUtil::asBool(const RttiInfo* rttiInfo, const void* src) +{ + if (rttiInfo->m_kind == RttiInfo::Kind::Bool) + { + return *(const bool*)src; + } + + if (rttiInfo->isIntegral()) + { + return getInt64(rttiInfo, src) != 0; + } + else if (rttiInfo->isFloat()) + { + return asDouble(rttiInfo, src) != 0.0; + } + + SLANG_ASSERT(!"Cannot convert to bool"); + return false; +} + +static int64_t _getIntDefaultValue(RttiDefaultValue value) +{ + switch (value) + { + default: + case RttiDefaultValue::Normal: return 0; + case RttiDefaultValue::One: return 1; + case RttiDefaultValue::MinusOne: return -1; + } +} + +static bool _isStructDefault(const StructRttiInfo* type, const void* src) +{ + if (type->m_super) + { + if (!_isStructDefault(type->m_super, src)) + { + return false; + } + } + + const Byte* base = (const Byte*)src; + + const Index count = type->m_fieldCount; + for (Index i = 0; i < count; ++i) + { + const auto& field = type->m_fields[i]; + + const RttiDefaultValue defaultValue = RttiDefaultValue(field.m_flags & uint8_t(RttiDefaultValue::Mask)); + + if (!RttiUtil::isDefault(defaultValue, field.m_type, base + field.m_offset)) + { + return false; + } + } + + return true; +} + +/* static */bool RttiUtil::isDefault(RttiDefaultValue defaultValue, const RttiInfo* rttiInfo, const void* src) +{ + if (rttiInfo->isIntegral()) + { + const auto value = getInt64(rttiInfo, src); + return _getIntDefaultValue(defaultValue) == value; + } + else if (rttiInfo->isFloat()) + { + const auto value = asDouble(rttiInfo, src); + return _getIntDefaultValue(defaultValue) == value; + } + + switch (rttiInfo->m_kind) + { + case RttiInfo::Kind::Invalid: return true; + case RttiInfo::Kind::Bool: return *(const bool*)src == (_getIntDefaultValue(defaultValue) != 0); + case RttiInfo::Kind::String: + { + return ((const String*)src)->getLength() == 0; + } + case RttiInfo::Kind::UnownedStringSlice: + { + return ((const UnownedStringSlice*)src)->getLength() == 0; + } + case RttiInfo::Kind::Struct: + { + return _isStructDefault(static_cast(rttiInfo), src); + } + case RttiInfo::Kind::Enum: + { + SLANG_ASSERT(!"Not implemented yet"); + return false; + } + case RttiInfo::Kind::List: + { + const auto& v = *(const List*)src; + return v.getCount() == 0; + } + case RttiInfo::Kind::Dictionary: + { + const auto& v = *(const Dictionary*)src; + return v.Count() == 0; + } + case RttiInfo::Kind::Other: + { + const OtherRttiInfo* otherRttiInfo = static_cast(rttiInfo); + return otherRttiInfo->m_isDefaultFunc && otherRttiInfo->m_isDefaultFunc(rttiInfo, src); + } + default: + { + return false; + } + } +} + +template +struct GetRttiTypeFuncsForBuiltIn +{ + static void ctorArray(const RttiInfo* rttiInfo, void* dst, Index count) { SLANG_UNUSED(rttiInfo); ::memset(dst, 0, sizeof(T) * count); } + static void dtorArray(const RttiInfo* rttiInfo, void* dst, Index count) { SLANG_UNUSED(rttiInfo); SLANG_UNUSED(dst); SLANG_UNUSED(count); } + static void copyArray(const RttiInfo* rttiInfo, void* dst, const void* src, Index count) { SLANG_UNUSED(rttiInfo); ::memcpy(dst, src, sizeof(T) * count); } + + static RttiTypeFuncs getFuncs() + { + RttiTypeFuncs funcs; + funcs.copyArray = ©Array; + funcs.dtorArray = &dtorArray; + funcs.ctorArray = &ctorArray; + return funcs; + } +}; + +struct ListFuncs +{ + static void ctorArray(const RttiInfo* rttiInfo, void* inDst, Index count) + { + SLANG_UNUSED(rttiInfo); + SLANG_ASSERT(rttiInfo->m_kind == RttiInfo::Kind::List); + + // We don't care about the element type, as we can just initialize them all as List + //const ListRttiInfo* listRttiInfo = static_cast(rttiInfo); + typedef List Type; + + Type* dst = (Type*)inDst; + + for (Index i = 0; i < count; ++i) + { + new (dst + i) Type; + } + } + static void copyArray(const RttiInfo* rttiInfo, void* inDst, const void* inSrc, Index count) + { + SLANG_ASSERT(rttiInfo->m_kind == RttiInfo::Kind::List); + const ListRttiInfo* listRttiInfo = static_cast(rttiInfo); + const auto elementType = listRttiInfo->m_elementType; + + // We need to get the type funcs + auto typeFuncs = RttiUtil::getTypeFuncs(elementType); + SLANG_ASSERT(typeFuncs.isValid()); + + // We need a type that we can get information from the list from - List gives us the functions we need. + typedef List Type; + + Type* dst = (Type*)inDst; + const Type* src = (const Type*)inSrc; + + for (Index i = 0; i < count; ++i) + { + auto& dstList = dst[i]; + auto& srcList = src[i]; + + const Index srcCount = srcList.getCount(); + + if (srcCount > dstList.getCount()) + { + // Allocate new memory + const Index dstCapacity = dstList.getCapacity(); + void* oldBuffer = dstList.detachBuffer(); + + void* newBuffer = ::malloc(count * elementType->m_size); + // Initialize it all first + typeFuncs.ctorArray(elementType, newBuffer, count); + typeFuncs.copyArray(elementType, newBuffer, oldBuffer, count); + + // Attach the new buffer + dstList.attachBuffer((Byte*)newBuffer, count, count); + + // Free the old buffer + if (oldBuffer) + { + typeFuncs.dtorArray(elementType, oldBuffer, dstCapacity); + + ::free(oldBuffer); + } + } + else + { + typeFuncs.copyArray(elementType, dstList.getBuffer(), srcList.getBuffer(), srcCount); + dstList.unsafeShrinkToCount(srcCount); + } + } + } + + static void dtorArray(const RttiInfo* rttiInfo, void* inDst, Index count) + { + SLANG_ASSERT(rttiInfo->m_kind == RttiInfo::Kind::List); + const ListRttiInfo* listRttiInfo = static_cast(rttiInfo); + + const auto elementType = listRttiInfo->m_elementType; + + // We need to get the type funcs + auto typeFuncs = RttiUtil::getTypeFuncs(elementType); + SLANG_ASSERT(typeFuncs.isValid()); + + typedef List Type; + Type* dst = (Type*)inDst; + + for (Index i = 0; i < count; ++i) + { + auto& dstList = dst[i]; + + const Index capacity = dstList.getCapacity(); + Byte* buffer = dstList.detachBuffer(); + + if (buffer) + { + typeFuncs.dtorArray(elementType, buffer, capacity); + ::free(buffer); + } + } + } + + static RttiTypeFuncs getFuncs() + { + RttiTypeFuncs funcs; + funcs.copyArray = ©Array; + funcs.dtorArray = &dtorArray; + funcs.ctorArray = &ctorArray; + return funcs; + } +}; + +RttiTypeFuncs RttiUtil::getTypeFuncs(const RttiInfo* rttiInfo) +{ + if (rttiInfo->isBuiltIn()) + { + switch (rttiInfo->m_size) + { + case 1: return GetRttiTypeFuncsForBuiltIn::getFuncs(); + case 2: return GetRttiTypeFuncsForBuiltIn::getFuncs(); + case 4: return GetRttiTypeFuncsForBuiltIn::getFuncs(); + case 8: return GetRttiTypeFuncsForBuiltIn::getFuncs(); + } + return RttiTypeFuncs::makeEmpty(); + } + + switch (rttiInfo->m_kind) + { + case RttiInfo::Kind::String: return GetRttiTypeFuncs::getFuncs(); + case RttiInfo::Kind::UnownedStringSlice: return GetRttiTypeFuncs::getFuncs(); + case RttiInfo::Kind::List: return ListFuncs::getFuncs(); + default: break; + } + + return RttiTypeFuncs::makeEmpty(); +} + +/* static */SlangResult RttiUtil::setListCount(const RttiInfo* elementType, void* dst, Index count) +{ + // NOTE! The following only works because List has capacity initialized members, and + // setting the count if it is <= capacity just sets the count (ie things aren't released(!)). + + List& dstList = *(List*)dst; + const Index oldCount = dstList.getCount(); + if (oldCount == count) + { + return SLANG_OK; + } + if (count < oldCount) + { + dstList.unsafeShrinkToCount(count); + return SLANG_OK; + } + + // Get funcs needed + const auto typeFuncs = RttiUtil::getTypeFuncs(elementType); + + if (!typeFuncs.isValid()) + { + return SLANG_FAIL; + } + + const Index dstCapacity = dstList.getCapacity(); + void* oldBuffer = dstList.detachBuffer(); + + void* newBuffer = ::malloc(count * elementType->m_size); + // Initialize it all first + typeFuncs.ctorArray(elementType, newBuffer, count); + + typeFuncs.copyArray(elementType, newBuffer, oldBuffer, oldCount); + + // Attach the new buffer + dstList.attachBuffer((Byte*)newBuffer, count, count); + + // Free the old buffer + if (oldBuffer) + { + typeFuncs.dtorArray(elementType, oldBuffer, dstCapacity); + ::free(oldBuffer); + } + + return SLANG_OK; +} + +} // namespace Slang diff --git a/source/core/slang-rtti-util.h b/source/core/slang-rtti-util.h new file mode 100644 index 000000000..d514d1980 --- /dev/null +++ b/source/core/slang-rtti-util.h @@ -0,0 +1,30 @@ +#ifndef SLANG_CORE_RTTI_UTIL_H +#define SLANG_CORE_RTTI_UTIL_H + +#include "slang-rtti-info.h" + +namespace Slang { +struct RttiUtil +{ + + static SlangResult setInt(int64_t value, const RttiInfo* rttiInfo, void* dst); + static int64_t getInt64(const RttiInfo* rttiInfo, const void* src); + + static double asDouble(const RttiInfo* rttiInfo, const void* src); + + static SlangResult setFromDouble(double v, const RttiInfo* rttiInfo, void* dst); + + static bool asBool(const RttiInfo* rttiInfo, const void* src); + + static bool isDefault(RttiDefaultValue defaultValue, const RttiInfo* rttiInfo, const void* src); + + static RttiTypeFuncs getTypeFuncs(const RttiInfo* rttiInfo); + + /// Set a list count + static SlangResult setListCount(const RttiInfo* elementType, void* dst, Index count); + +}; + +} // namespace Slang + +#endif // SLANG_CORE_RTTI_UTIL_H -- cgit v1.2.3