diff options
| author | Ellie Hermaszewska <ellieh@nvidia.com> | 2024-10-29 14:49:26 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-10-29 14:49:26 +0800 |
| commit | f65d756bff8d4c5cbc15bd0322a2ae8e6b896a21 (patch) | |
| tree | ea1d61342cd29368e19135000ec2948813096205 /source/core/slang-offset-container.h | |
| parent | a729c15e9dce9f5116a38afc66329ab2ca4cea54 (diff) | |
format
* format
* Minor test fixes
* enable checking cpp format in ci
Diffstat (limited to 'source/core/slang-offset-container.h')
| -rw-r--r-- | source/core/slang-offset-container.h | 336 |
1 files changed, 217 insertions, 119 deletions
diff --git a/source/core/slang-offset-container.h b/source/core/slang-offset-container.h index 95c3a6589..4e6920bed 100644 --- a/source/core/slang-offset-container.h +++ b/source/core/slang-offset-container.h @@ -4,54 +4,62 @@ #include "slang-basic.h" -namespace Slang { +namespace Slang +{ /* -The purpose of OffsetContainer and related types is to provide a mechanism to easily serialize offset structures. +The purpose of OffsetContainer and related types is to provide a mechanism to easily serialize +offset structures. -The root idea here is the "offset pointer". A typical pointer in a language like C/C++ holds the absolute address -in the current address space of the thing that is being pointed to. This introduces a problem, as when data is -serialized in the contents will very likely be be placed at different addresses - meaning any absolute pointer -will point to the wrong place. There is also a related issue around pointer sizes - on some targets they are 32 bits -and on others 64 bits. +The root idea here is the "offset pointer". A typical pointer in a language like C/C++ holds the +absolute address in the current address space of the thing that is being pointed to. This introduces +a problem, as when data is serialized in the contents will very likely be be placed at different +addresses - meaning any absolute pointer will point to the wrong place. There is also a related +issue around pointer sizes - on some targets they are 32 bits and on others 64 bits. -An offset pointer means a pointer that points to something 'offset' to some base address. The OffsetPtr uses a 32 bit -offset from the pointers location in memory. This means such a pointer can address a 4Gb address space. +An offset pointer means a pointer that points to something 'offset' to some base address. The +OffsetPtr uses a 32 bit offset from the pointers location in memory. This means such a pointer can +address a 4Gb address space. -Special care is needed when using offset pointers - both when constructing structures that contain them, reading -them and in general usage. +Special care is needed when using offset pointers - both when constructing structures that contain +them, reading them and in general usage. -For simplicity here we store all offset pointers within a single contiguous allocation. This allocation is -typically managed by the OffsetContainer for writing. When reading a MemoryOffsetBase can be used. +For simplicity here we store all offset pointers within a single contiguous allocation. This +allocation is typically managed by the OffsetContainer for writing. When reading a MemoryOffsetBase +can be used. -An issue around using offset pointers, is that we cannot directly access it's contents, because it's just an -offset to some base address. Thus to access the thing being pointed to we need to turn the offset pointer back into -a 'raw' pointer. This is achieved via using the asRaw methods on the OffsetBase. For a convenience operator[] can also -be used, and this is typically the preferred mechanism. +An issue around using offset pointers, is that we cannot directly access it's contents, because it's +just an offset to some base address. Thus to access the thing being pointed to we need to turn the +offset pointer back into a 'raw' pointer. This is achieved via using the asRaw methods on the +OffsetBase. For a convenience operator[] can also be used, and this is typically the preferred +mechanism. -NOTE! That the evaluation order of a function calls parameters is undefined in C++. That whilst it might appear doing +NOTE! That the evaluation order of a function calls parameters is undefined in C++. That whilst it +might appear doing ``` base[thing] = container.newObject<Thing>(); ``` -will evaluate the construction of newObject *before* the assignment, if you look at the assignment as being a function call -(as it is when it is overloaded), then base[thing] might be evaluated *before* newObject, and if it is then the result -could be wrong if the newObject needed to reallocate. Therefore when allocation is involved, a new (or any allocation backed -function call from the OffsetContainer) should always place a result in a local variable. Then assign as in +will evaluate the construction of newObject *before* the assignment, if you look at the assignment +as being a function call (as it is when it is overloaded), then base[thing] might be evaluated +*before* newObject, and if it is then the result could be wrong if the newObject needed to +reallocate. Therefore when allocation is involved, a new (or any allocation backed function call +from the OffsetContainer) should always place a result in a local variable. Then assign as in ``` auto anotherThing = container.newObject<Thing>(); -base[thing] = anotherThing; +base[thing] = anotherThing; ``` -When creating structures - unless you know the allocated space (in the OffsetContainer or some other piece of memory) -is larger than required, then special care is needed, because when a new larger piece of memory is allocated to hold -everything, raw pointers pointers will likely be invalidated. When reading there is typically no need to move -the base address, so raw pointers remain valid through out. When doing writing if a call is made to something that -allocates memory on the OffsetContainer - any raw pointer should be assumed invalid. +When creating structures - unless you know the allocated space (in the OffsetContainer or some other +piece of memory) is larger than required, then special care is needed, because when a new larger +piece of memory is allocated to hold everything, raw pointers pointers will likely be invalidated. +When reading there is typically no need to move the base address, so raw pointers remain valid +through out. When doing writing if a call is made to something that allocates memory on the +OffsetContainer - any raw pointer should be assumed invalid. -For example +For example ``` @@ -83,7 +91,7 @@ void func() // Or more perhaps slightly more conveniently [] auto rawThing = base[thing]; - + // We can write and read things via the Safe32Ptr rawThing->value = 10; const int value = rawThing->value; @@ -93,18 +101,18 @@ void func() // Now lets write to it { - // We can have raw pointer (or reference) to a thing but we need to be *careful* if we allocate - Thing* rawThing = base[thing]; - // We are okay here, nothing between getting the raw pointer and the write allocated/newed anything on the OffsetContainer - rawThing->value = 20; + // We can have raw pointer (or reference) to a thing but we need to be *careful* if we +allocate Thing* rawThing = base[thing]; + // We are okay here, nothing between getting the raw pointer and the write allocated/newed +anything on the OffsetContainer rawThing->value = 20; - // Lets set up name + // Lets set up name Offset32Ptr<OffsetString> text = offsetContainer.newString("Hello World!"); - // BAD! The rawThing point could now be invalid because the call to newString may have had to allocate more memory - rawThing->text = text; + // BAD! The rawThing point could now be invalid because the call to newString may have had +to allocate more memory rawThing->text = text; - // This is okay + // This is okay base[thing]->text = text; // Or we can update rawThing such that is up to date @@ -112,10 +120,12 @@ void func() // So now this is okay again rawThing->text = text; - // BAD! we don't know the evaluation order here, if the lhs is evaluate before the rhs, then it could write to the wrong area of memory. - base[thing]->text = offsetContainer.newString("Hello World again!"); + // BAD! we don't know the evaluation order here, if the lhs is evaluate before the rhs, then +it could write to the wrong area of memory. base[thing]->text = offsetContainer.newString("Hello +World again!"); - // So where there is allocation, and assignment to something that in held in offset ptr use a local for the allocation as in + // So where there is allocation, and assignment to something that in held in offset ptr use +a local for the allocation as in { auto text = offsetContainer.newString("Hello World again!"); base[thing]->text = text; @@ -128,23 +138,27 @@ void func() enum { - kNull32Offset = 0, - kStartOffset = uint32_t(sizeof(uint64_t)), ///< The offset to the first contained thing + kNull32Offset = 0, + kStartOffset = uint32_t(sizeof(uint64_t)), ///< The offset to the first contained thing }; -template <typename T> +template<typename T> class Offset32Ref; /* A pointer to items held in OffsetContainer (or OffsetBase relative) that remains correct even if the memory inside OffsetContainer moves. */ -template <typename T> +template<typename T> class Offset32Ptr { public: typedef Offset32Ptr ThisType; - const ThisType& operator=(const ThisType& rhs) { m_offset = rhs.m_offset; return *this; } + const ThisType& operator=(const ThisType& rhs) + { + m_offset = rhs.m_offset; + return *this; + } bool operator==(const ThisType& rhs) const { return m_offset == rhs.m_offset; } bool operator!=(const ThisType& rhs) const { return m_offset != rhs.m_offset; } @@ -157,55 +171,99 @@ public: Offset32Ref<T> operator*(); - ThisType& operator++() { m_offset += uint32_t(sizeof(T)); return *this; } - ThisType operator++(int) { const auto offset = m_offset; m_offset += uint32_t(sizeof(T)); return ThisType(offset); } + ThisType& operator++() + { + m_offset += uint32_t(sizeof(T)); + return *this; + } + ThisType operator++(int) + { + const auto offset = m_offset; + m_offset += uint32_t(sizeof(T)); + return ThisType(offset); + } - ThisType& operator--() { m_offset -= sizeof(T); return *this; } - ThisType operator--(int) { const auto offset = m_offset; m_offset -= uint32_t(sizeof(T)); return ThisType(offset); } + ThisType& operator--() + { + m_offset -= sizeof(T); + return *this; + } + ThisType operator--(int) + { + const auto offset = m_offset; + m_offset -= uint32_t(sizeof(T)); + return ThisType(offset); + } - friend ThisType operator+(const ThisType& a, Index b) { return ThisType(a.m_offset + uint32_t(sizeof(T) * b)); } - friend ThisType operator+(Index a, const ThisType& b) { return ThisType(b.m_offset + uint32_t(sizeof(T) * a)); } + friend ThisType operator+(const ThisType& a, Index b) + { + return ThisType(a.m_offset + uint32_t(sizeof(T) * b)); + } + friend ThisType operator+(Index a, const ThisType& b) + { + return ThisType(b.m_offset + uint32_t(sizeof(T) * a)); + } bool isNull() const { return m_offset == kNull32Offset; } void setNull() { m_offset = kNull32Offset; } - Offset32Ptr():m_offset(kNull32Offset) {} - Offset32Ptr(const ThisType& rhs): m_offset(rhs.m_offset) {} - explicit Offset32Ptr(uint32_t offset): m_offset(offset) {} + Offset32Ptr() + : m_offset(kNull32Offset) + { + } + Offset32Ptr(const ThisType& rhs) + : m_offset(rhs.m_offset) + { + } + explicit Offset32Ptr(uint32_t offset) + : m_offset(offset) + { + } uint32_t m_offset; }; -/* A reference to items held in OffsetContainer (or OffsetBase relative) that remains correct even if -the memory inside OffsetContainer moves. +/* A reference to items held in OffsetContainer (or OffsetBase relative) that remains correct even +if the memory inside OffsetContainer moves. */ -template <typename T> +template<typename T> class Offset32Ref { public: typedef Offset32Ref ThisType; - const ThisType& operator=(const ThisType& rhs) { m_offset = rhs.m_offset; return *this; } + const ThisType& operator=(const ThisType& rhs) + { + m_offset = rhs.m_offset; + return *this; + } Offset32Ptr<T> operator&() { return Offset32Ptr<T>(m_offset); } - Offset32Ref(const ThisType& rhs) : m_offset(rhs.m_offset) {} - explicit Offset32Ref(uint32_t offset) : m_offset(offset) { SLANG_ASSERT(offset != kNull32Offset); } + Offset32Ref(const ThisType& rhs) + : m_offset(rhs.m_offset) + { + } + explicit Offset32Ref(uint32_t offset) + : m_offset(offset) + { + SLANG_ASSERT(offset != kNull32Offset); + } uint32_t m_offset; }; // --------------------------------------------------------------------------- -template <typename T> +template<typename T> SLANG_FORCE_INLINE Offset32Ref<T> Offset32Ptr<T>::operator*() { return Offset32Ref<T>(m_offset); } -/* Much like Offset32Ptr this is an array but whose memory is stored inside the OffsetContainer. This means elements types -must be 'offset types'. */ -template <typename T> +/* Much like Offset32Ptr this is an array but whose memory is stored inside the OffsetContainer. +This means elements types must be 'offset types'. */ +template<typename T> class Offset32Array { public: @@ -217,20 +275,34 @@ public: Index getCount() const { return Index(m_count); } - Offset32Ref<const T> operator[](Index i) const { SLANG_ASSERT(i >= 0 && uint32_t(i) < m_count); return Offset32Ref<const T>((m_data + i).m_offset); } - Offset32Ref<T> operator[](Index i) { SLANG_ASSERT(i >= 0 && uint32_t(i) < m_count); return Offset32Ref<T>((m_data + i).m_offset); } + Offset32Ref<const T> operator[](Index i) const + { + SLANG_ASSERT(i >= 0 && uint32_t(i) < m_count); + return Offset32Ref<const T>((m_data + i).m_offset); + } + Offset32Ref<T> operator[](Index i) + { + SLANG_ASSERT(i >= 0 && uint32_t(i) < m_count); + return Offset32Ref<T>((m_data + i).m_offset); + } - Offset32Array(Offset32Ptr<T> data, uint32_t count) :m_data(data), m_count(count) {} + Offset32Array(Offset32Ptr<T> data, uint32_t count) + : m_data(data), m_count(count) + { + } - Offset32Array() :m_count(0) {} + Offset32Array() + : m_count(0) + { + } Offset32Ptr<T> m_data; uint32_t m_count; }; -/** OffsetString is used for storing strings within a OffsetContainer. Strings are stored with the initial byte indicating the size -of the string. Note that all offset strings are stored with a terminating zero, and that the terminating zero is *NOT* included in -the encoded size. */ +/** OffsetString is used for storing strings within a OffsetContainer. Strings are stored with the +initial byte indicating the size of the string. Note that all offset strings are stored with a +terminating zero, and that the terminating zero is *NOT* included in the encoded size. */ struct OffsetString { enum @@ -239,51 +311,75 @@ struct OffsetString kMaxSizeEncodeSize = 5, }; - /// Get contents as a slice + /// Get contents as a slice UnownedStringSlice getSlice() const; - /// Get null terminated string + /// Get null terminated string const char* getCstr() const; - /// Decode the size. Returns the start of the string text, and outSize holds the size (NOT including terminating 0) + /// Decode the size. Returns the start of the string text, and outSize holds the size (NOT + /// including terminating 0) static const char* decodeSize(const char* in, size_t& outSize); - /// Returns the amount of bytes used, end encoding in 'encode' + /// Returns the amount of bytes used, end encoding in 'encode' static size_t calcEncodedSize(size_t size, uint8_t encode[kMaxSizeEncodeSize]); - /// Calculate the total size needed to store the string *including* terminating 0 + /// Calculate the total size needed to store the string *including* terminating 0 static size_t calcAllocationSize(const UnownedStringSlice& slice); - /// Calculate the total size needed to store string. Size should be passed *without* terminating 0 + /// Calculate the total size needed to store string. Size should be passed *without* terminating + /// 0 static size_t calcAllocationSize(size_t size); char m_sizeThenContents[1]; }; -/* A type that is used to hold the base address of the contiguous memory that holds either Offset32Ptr and related types> -*/ +/* A type that is used to hold the base address of the contiguous memory that holds either + * Offset32Ptr and related types> + */ class OffsetBase { public: typedef OffsetBase ThisType; - /// Turn an offset into a raw regular pointer or reference - template <typename T> - T* asRaw(const Offset32Ptr<T>& ptr) { return (T*)_getRaw(ptr.m_offset); } - template <typename T> - T& asRaw(const Offset32Ref<T>& ref) { return *(T*)_getRaw(ref.m_offset); } - - /// A more terse way to get a raw pointer/reference. Using the [] operator can be seen as 'indexing' to access the - /// object the offset relates to. Unlike 'indices' that are typically used with [] offsets are generally not contiguous. - template <typename T> - T* operator[](const Offset32Ptr<T>& ptr) { return (T*)_getRaw(ptr.m_offset); } - template <typename T> - T& operator[](const Offset32Ref<T>& ref) { return *(T*)_getRaw(ref.m_offset); } - - template <typename T> - Offset32Ptr<T> asPtr(T* ptr) { return Offset32Ptr<T>(getOffset(ptr)); } - /// Note the use of ptr when setting up a reference here - it's needed because a ref does not have to be backed by a pointer. - /// And commonly is not when the const& and the thing referenced can be held in a word. - template <typename T> - Offset32Ref<T> asRef(T* ptr) { SLANG_ASSERT(ptr); return Offset32Ref<T>(getOffset(ptr)); } + /// Turn an offset into a raw regular pointer or reference + template<typename T> + T* asRaw(const Offset32Ptr<T>& ptr) + { + return (T*)_getRaw(ptr.m_offset); + } + template<typename T> + T& asRaw(const Offset32Ref<T>& ref) + { + return *(T*)_getRaw(ref.m_offset); + } + + /// A more terse way to get a raw pointer/reference. Using the [] operator can be seen as + /// 'indexing' to access the object the offset relates to. Unlike 'indices' that are typically + /// used with [] offsets are generally not contiguous. + template<typename T> + T* operator[](const Offset32Ptr<T>& ptr) + { + return (T*)_getRaw(ptr.m_offset); + } + template<typename T> + T& operator[](const Offset32Ref<T>& ref) + { + return *(T*)_getRaw(ref.m_offset); + } + + template<typename T> + Offset32Ptr<T> asPtr(T* ptr) + { + return Offset32Ptr<T>(getOffset(ptr)); + } + /// Note the use of ptr when setting up a reference here - it's needed because a ref does not + /// have to be backed by a pointer. And commonly is not when the const& and the thing referenced + /// can be held in a word. + template<typename T> + Offset32Ref<T> asRef(T* ptr) + { + SLANG_ASSERT(ptr); + return Offset32Ref<T>(getOffset(ptr)); + } uint32_t getOffset(const void* ptr) { @@ -296,30 +392,31 @@ public: return uint32_t(diff); } - /// Get the contained data + /// Get the contained data SLANG_FORCE_INLINE uint8_t* getData() { return m_data; } - /// Return the last used byte of the data + /// Return the last used byte of the data SLANG_FORCE_INLINE size_t getDataCount() const { return m_dataSize; } - /// Get the first allocated thing. Typically the root of the structure contained - void* getFirst() { return (m_dataSize < kStartOffset) ? nullptr : (m_data + kStartOffset); } - - /// Get a raw pointer from the offset - uint8_t* _getRaw(uint32_t offset) { return (offset == kNull32Offset) ? nullptr : (m_data + offset); } + /// Get the first allocated thing. Typically the root of the structure contained + void* getFirst() { return (m_dataSize < kStartOffset) ? nullptr : (m_data + kStartOffset); } - OffsetBase(): - m_data(nullptr), - m_dataSize(0) + /// Get a raw pointer from the offset + uint8_t* _getRaw(uint32_t offset) { + return (offset == kNull32Offset) ? nullptr : (m_data + offset); } + OffsetBase() + : m_data(nullptr), m_dataSize(0) + { + } uint8_t* m_data; size_t m_dataSize; protected: - /// We want protected, because we don't want copies to be made of OffsetBase by default! + /// We want protected, because we don't want copies to be made of OffsetBase by default! OffsetBase(const ThisType& rhs) = default; ThisType& operator=(const ThisType& rhs) = default; }; @@ -334,17 +431,18 @@ public: } }; -/* OffsetContainer is a type designed to manage the construction structures around 'offset types'. In particular it allows -for construction of offset structures where their total encoded size is not known at the outset. +/* OffsetContainer is a type designed to manage the construction structures around 'offset types'. +In particular it allows for construction of offset structures where their total encoded size is not +known at the outset. -The main mechanism to make this work is via the use of OffsetXXX types, which when constructed from the OffsetContainer will -maintain valid values, even if the underlying backing memories location is changed. +The main mechanism to make this work is via the use of OffsetXXX types, which when constructed from +the OffsetContainer will maintain valid values, even if the underlying backing memories location is +changed. */ class OffsetContainer : public OffsetBase { public: - - template <typename T> + template<typename T> Offset32Ptr<T> newObject() { void* data = allocate(sizeof(T), SLANG_ALIGN_OF(T)); @@ -352,7 +450,7 @@ public: return Offset32Ptr<T>(getOffset(data)); } - template <typename T> + template<typename T> Offset32Array<T> newArray(size_t size) { if (size == 0) @@ -367,7 +465,7 @@ public: return Offset32Array<T>(Offset32Ptr<T>(getOffset(data)), uint32_t(size)); } - /// Get the base - which is needed for turning offsets into things + /// Get the base - which is needed for turning offsets into things OffsetBase& asBase() { return *this; } /// Allocate without alignment (effectively 1) @@ -380,7 +478,7 @@ public: Offset32Ptr<OffsetString> newString(const UnownedStringSlice& slice); Offset32Ptr<OffsetString> newString(const char* contents); - /// Ctor + /// Ctor OffsetContainer(); ~OffsetContainer(); |
