summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2018-09-12 16:27:42 -0400
committerGitHub <noreply@github.com>2018-09-12 16:27:42 -0400
commitf60135cec62c91a9d7923397fe8796d2b3eaa5cb (patch)
tree777646cb3611bf5809dc18e120e506117e143e11 /source
parent9a9733091cc7c9628e445313785d561deb229072 (diff)
Feature/memory arena (#631)
* First pass at MemoryArena. * First pass at RandomGenerator. * Extract TestContext into external source file. * Fix warning on printf. * Use enum classes for Test enums. OutputMode -> TestOutputMode. * First pass at FreeList unit test. * Auto registering tests. Improvements to RandomGenerator. * Remove the need for unitTest headers - cos can use registering. * Added unitTest for MemoryArena. * Do unit tests. * Fix typo.
Diffstat (limited to 'source')
-rw-r--r--source/core/core.vcxproj4
-rw-r--r--source/core/core.vcxproj.filters14
-rw-r--r--source/core/list.h9
-rw-r--r--source/core/slang-memory-arena.cpp244
-rw-r--r--source/core/slang-memory-arena.h393
-rw-r--r--source/core/slang-random-generator.cpp143
-rw-r--r--source/core/slang-random-generator.h97
-rw-r--r--source/core/slang-string-util.cpp48
-rw-r--r--source/core/slang-string-util.h14
9 files changed, 964 insertions, 2 deletions
diff --git a/source/core/core.vcxproj b/source/core/core.vcxproj
index 3dbfaac3f..2944ee9dd 100644
--- a/source/core/core.vcxproj
+++ b/source/core/core.vcxproj
@@ -185,6 +185,8 @@
<ClInclude Include="slang-free-list.h" />
<ClInclude Include="slang-io.h" />
<ClInclude Include="slang-math.h" />
+ <ClInclude Include="slang-memory-arena.h" />
+ <ClInclude Include="slang-random-generator.h" />
<ClInclude Include="slang-string-util.h" />
<ClInclude Include="slang-string.h" />
<ClInclude Include="smart-pointer.h" />
@@ -197,6 +199,8 @@
<ClCompile Include="platform.cpp" />
<ClCompile Include="slang-free-list.cpp" />
<ClCompile Include="slang-io.cpp" />
+ <ClCompile Include="slang-memory-arena.cpp" />
+ <ClCompile Include="slang-random-generator.cpp" />
<ClCompile Include="slang-string-util.cpp" />
<ClCompile Include="slang-string.cpp" />
<ClCompile Include="stream.cpp" />
diff --git a/source/core/core.vcxproj.filters b/source/core/core.vcxproj.filters
index 27b0fe82f..9e90aaa77 100644
--- a/source/core/core.vcxproj.filters
+++ b/source/core/core.vcxproj.filters
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Header Files">
@@ -75,6 +75,12 @@
<ClInclude Include="type-traits.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="slang-memory-arena.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="slang-random-generator.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="platform.cpp">
@@ -101,6 +107,12 @@
<ClCompile Include="token-reader.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="slang-memory-arena.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="slang-random-generator.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="core.natvis">
diff --git a/source/core/list.h b/source/core/list.h
index 6df60b74b..cddcbb6c0 100644
--- a/source/core/list.h
+++ b/source/core/list.h
@@ -199,6 +199,15 @@ namespace Slang
return buffer[_count-1];
}
+ void RemoveLast()
+ {
+#ifdef _DEBUG
+ if (_count == 0)
+ throw "Index out of range.";
+#endif
+ _count--;
+ }
+
inline void SwapWith(List<T, TAllocator> & other)
{
T* tmpBuffer = this->buffer;
diff --git a/source/core/slang-memory-arena.cpp b/source/core/slang-memory-arena.cpp
new file mode 100644
index 000000000..bdfd87602
--- /dev/null
+++ b/source/core/slang-memory-arena.cpp
@@ -0,0 +1,244 @@
+
+#include "slang-memory-arena.h"
+
+namespace Slang {
+
+MemoryArena::MemoryArena()
+{
+ // Mark as invalid so any alloc call will fail
+ m_blockAlignment = 0;
+ m_blockSize = 0;
+
+ // Set up as empty
+ m_blocks = nullptr;
+ _setCurrentBlock(nullptr);
+ m_blockFreeList.init(sizeof(Block), sizeof(void*), 16);
+}
+
+MemoryArena::~MemoryArena()
+{
+ _deallocateBlocks();
+}
+
+MemoryArena::MemoryArena(size_t blockSize, size_t blockAlignment)
+{
+ _initialize(blockSize, blockAlignment);
+}
+
+void MemoryArena::init(size_t blockSize, size_t blockAlignment)
+{
+ _deallocateBlocks();
+ m_blockFreeList.reset();
+ _initialize(blockSize, blockAlignment);
+}
+
+void MemoryArena::_initialize(size_t blockSize, size_t alignment)
+{
+ // Alignment must be a power of 2
+ assert(((alignment - 1) & alignment) == 0);
+
+ // Must be at least sizeof(void*) in size, as that is the minimum the backing allocator will be
+ alignment = (alignment < kMinAlignment) ? kMinAlignment : alignment;
+
+ // If alignment required is larger then the backing allocators then
+ // make larger to ensure when alignment correction takes place it will be aligned
+ if (alignment > kMinAlignment)
+ {
+ blockSize += alignment;
+ }
+
+ m_blockSize = blockSize;
+ m_blockAlignment = alignment;
+ m_blocks = nullptr;
+ _setCurrentBlock(nullptr);
+ m_blockFreeList.init(sizeof(Block), sizeof(void*), 16);
+}
+
+void* MemoryArena::_allocateAligned(size_t size, size_t alignment)
+{
+ assert(size);
+ // Can't be space in the current block -> so we can either place in next, or in a new block
+ _newCurrentBlock(size, alignment);
+ uint8_t* const current = m_current;
+ // If everything has gone to plan, must be space here...
+ assert(current + size <= m_end);
+ m_current = current + size;
+ return current;
+}
+
+void MemoryArena::_deallocateBlocks()
+{
+ Block* currentBlock = m_blocks;
+ while (currentBlock)
+ {
+ // Deallocate the block
+ ::free(currentBlock->m_alloc);
+ // next block
+ currentBlock = currentBlock->m_next;
+ }
+ // Can deallocate all blocks to
+ m_blockFreeList.deallocateAll();
+}
+
+void MemoryArena::_setCurrentBlock(Block* block)
+{
+ if (block)
+ {
+ m_end = block->m_end;
+ m_start = block->m_start;
+ m_current = m_start;
+ }
+ else
+ {
+ m_start = nullptr;
+ m_end = nullptr;
+ m_current = nullptr;
+ }
+ m_currentBlock = block;
+}
+
+MemoryArena::Block* MemoryArena::_newCurrentBlock(size_t size, size_t alignment)
+{
+ // Make sure init has been called (or has been set up in parameterized constructor)
+ assert(m_blockSize > 0);
+ // Alignment must be a power of 2
+ assert(((alignment - 1) & alignment) == 0);
+
+ // Alignment must at a minimum be block alignment (such if reused the constraints hold)
+ alignment = (alignment < m_blockAlignment) ? m_blockAlignment : alignment;
+
+ const size_t alignMask = alignment - 1;
+
+ // First try the next block (if there is one)
+ {
+ Block* next = m_currentBlock ? m_currentBlock->m_next : m_blocks;
+ if (next)
+ {
+ // Align could be done from the actual allocation start, but doing so would mean a pointer which
+ // didn't hit the constraint of being between start/end
+ // So have to align conservatively using start
+ uint8_t* memory = (uint8_t*)((size_t(next->m_start) + alignMask) & ~alignMask);
+
+ // Check if can fit block in
+ if (memory + size <= next->m_end)
+ {
+ _setCurrentBlock(next);
+ return next;
+ }
+ }
+ }
+
+ // The size of the block must be at least large enough to take into account alignment
+ size_t allocSize = (alignment <= kMinAlignment) ? size : (size + alignment);
+
+ // The minimum block size should be at least m_blockSize
+ allocSize = (allocSize < m_blockSize) ? m_blockSize : allocSize;
+
+ // Allocate block
+ Block* block = (Block*)m_blockFreeList.allocate();
+ if (!block)
+ {
+ return nullptr;
+ }
+ // Allocate the memory
+ uint8_t* alloc = (uint8_t*)::malloc(allocSize);
+ if (!alloc)
+ {
+ m_blockFreeList.deallocate(block);
+ return nullptr;
+ }
+ // Do the alignment on the allocation
+ uint8_t* const start = (uint8_t*)((size_t(alloc) + alignMask) & ~alignMask);
+
+ // Setup the block
+ block->m_alloc = alloc;
+ block->m_start = start;
+ block->m_end = alloc + allocSize;
+ block->m_next = nullptr;
+
+ // Insert block into list
+ if (m_currentBlock)
+ {
+ // Insert after current block
+ block->m_next = m_currentBlock->m_next;
+ m_currentBlock->m_next = block;
+ }
+ else
+ {
+ // Add to start of the list of the blocks
+ block->m_next = m_blocks;
+ m_blocks = block;
+ }
+ _setCurrentBlock(block);
+ return block;
+}
+
+MemoryArena::Block* MemoryArena::_findBlock(const void* alloc, Block* endBlock) const
+{
+ const uint8_t* ptr = (const uint8_t*)alloc;
+
+ Block* block = m_blocks;
+ while (block != endBlock)
+ {
+ if (ptr >= block->m_start && ptr < block->m_end)
+ {
+ return block;
+ }
+ block = block->m_next;
+ }
+ return nullptr;
+}
+
+MemoryArena::Block* MemoryArena::_findPreviousBlock(Block* block)
+{
+ Block* currentBlock = m_blocks;
+ while (currentBlock)
+ {
+ if (currentBlock->m_next == block)
+ {
+ return currentBlock;
+ }
+ currentBlock = currentBlock->m_next;
+ }
+ return nullptr;
+}
+
+void MemoryArena::deallocateAll()
+{
+ Block** prev = &m_blocks;
+ Block* block = m_blocks;
+
+ while (block)
+ {
+ if (size_t(block->m_end - block->m_alloc) > m_blockSize)
+ {
+ // Oversized block so we need to free it and remove from the list
+ Block* nextBlock = block->m_next;
+ *prev = nextBlock;
+ // Free the backing memory
+ ::free(block->m_alloc);
+ // Free the block
+ m_blockFreeList.deallocate(block);
+ // prev stays the same, now working on next tho
+ block = nextBlock;
+ }
+ else
+ {
+ // Onto next
+ prev = &block->m_next;
+ block = block->m_next;
+ }
+ }
+
+ // Make the first current (if any)
+ _setCurrentBlock(m_blocks);
+}
+
+void MemoryArena::reset()
+{
+ _deallocateBlocks();
+ m_blocks = nullptr;
+ _setCurrentBlock(nullptr);
+}
+
+} // namespace Slang
diff --git a/source/core/slang-memory-arena.h b/source/core/slang-memory-arena.h
new file mode 100644
index 000000000..acbac51dd
--- /dev/null
+++ b/source/core/slang-memory-arena.h
@@ -0,0 +1,393 @@
+#ifndef SLANG_MEMORY_ARENA_H
+#define SLANG_MEMORY_ARENA_H
+
+#include "../../slang.h"
+
+#include <assert.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "slang-free-list.h"
+
+namespace Slang {
+
+/** Defines arena allocator where allocations are made very quickly, but that deallocations
+can only be performed in reverse order, or with the client code knowing a previous deallocation (say with
+ deallocateAllFrom), automatically deallocates everything after it.
+
+It works by allocating large blocks and then cutting out smaller pieces as requested. If a piece of memory is
+deallocated, it either MUST be in reverse allocation order OR the subsequent allocations are implicitly
+deallocated too, and therefore accessing their memory is now undefined behavior. Allocations are made
+contiguously from the current block. If there is no space in the current block, the
+next block (which is unused) if available is checked. If that works, an allocation is made from the next block.
+If not a new block is allocated that can hold at least the allocation with required alignment.
+
+All memory allocated can be deallocated very quickly and without a client having to track any memory.
+All memory allocated will be freed on destruction - or with reset.
+
+A memory arena can have requests larger than the block size. When that happens they will just be allocated
+from the heap. As such 'oversized blocks' are seen as unusual and potentially wasteful they are deallocated
+when deallocateAll is called, whereas regular size blocks will remain allocated for fast subsequent allocation.
+
+It is intentional that blocks information is stored separately from the allocations that store the
+user data. This is so that alignment permitting, block allocations sizes can be passed directly to underlying allocator.
+For large power of 2 backing allocations this might mean a page/pages directly allocated by the OS for example.
+Also means better cache coherency when traversing blocks -> as generally they will be contiguous in memory.
+
+Also note that allocateUnaligned can be used for slightly faster aligned allocations. All blocks allocated internally
+are aligned to the blockAlignment passed to the constructor. If subsequent allocations (of any type) sizes are of that
+alignment or larger then no alignment fixing is required (because allocations are contiguous) and so 'allocateUnaligned'
+will return allocations of blockAlignment alignment.
+*/
+class MemoryArena
+{
+public:
+ typedef MemoryArena ThisType;
+
+ static const size_t kMinAlignment = sizeof(void*); ///< The minimum alignment of the backing memory allocator.
+
+ /** Determines if an allocation is consistent with an allocation from this arena.
+
+ The test cannot say definitively if this was such an allocation, because the exact details
+ of each allocation is not kept.
+ @param alloc The start of the allocation
+ @param sizeInBytes The size of the allocation
+ @return true if allocation could have been from this Arena */
+ bool isValid(const void* alloc, size_t sizeInBytes) const;
+
+ /** Initialize the arena with specified block size and alignment
+ If the arena has been previously initialized will free and deallocate all memory */
+ void init(size_t blockSize, size_t blockAlignment = kMinAlignment);
+
+ /** Allocate some memory of at least size bytes without having any specific alignment.
+
+ Can be used for slightly faster *aligned* allocations if caveats in class description are met. The
+ Unaligned, means the method will not enforce alignment - but a client call to allocateUnaligned can control
+ subsequent allocations alignments via it's size.
+
+ @param size The size of the allocation requested (in bytes and must be > 0).
+ @return The allocation. Can be nullptr if backing allocator was not able to request required memory */
+ void* allocate(size_t sizeInBytes);
+
+ /** Allocate some aligned memory of at least size bytes
+ @param size Size of allocation wanted (must be > 0).
+ @param alignment Alignment of allocation - must be a power of 2.
+ @return The allocation (or nullptr if unable to allocate). Will be at least 'alignment' alignment or better. */
+ void* allocateAligned(size_t sizeInBytes, size_t alignment);
+
+ /** Allocate some aligned memory of at least size bytes
+ @param size Size of allocation wanted (must be > 0).
+ @param alignment Alignment of allocation - must be a power of 2.
+ @return The allocation (or nullptr if unable to allocate). Will be at least 'alignment' alignment or better. */
+ void* allocateUnaligned(size_t sizeInBytes);
+
+ /** Allocates a null terminated string.
+ @param str A null-terminated string
+ @return A copy of the string held on the arena */
+ const char* allocateString(const char* str);
+
+ /** Allocates a null terminated string.
+ @param chars Pointer to first character
+ @param charCount The amount of characters NOT including terminating 0.
+ @return A copy of the string held on the arena. */
+ const char* allocateString(const char* chars, size_t numChars);
+
+ /// Allocate an element of the specified type. Note: Constructor for type is not executed.
+ template <typename T>
+ T* allocate();
+
+ /// Allocate an array of a specified type. NOTE Constructor of T is NOT executed.
+ template <typename T>
+ T* allocateArray(size_t size);
+
+ /// Allocate an array of a specified type, and copy array passed into it.
+ template <typename T>
+ T* allocateAndCopyArray(const T* src, size_t size);
+
+ /// Allocate an array of a specified type, and zero it.
+ template <typename T>
+ T* allocateAndZeroArray(size_t size);
+
+ /// Deallocate the last allocation. If data is not from the last allocation then the behavior is undefined.
+ void deallocateLast(void* data);
+
+ /// Deallocate this allocation and all remaining after it.
+ void deallocateAllFrom(void* dataStart);
+
+ /** Deallocates all allocated memory. That backing memory will generally not be released so
+ subsequent allocation will be fast, and from the same memory. Note though that 'oversize' blocks
+ will be deallocated. */
+ void deallocateAll();
+
+ /// Resets to the initial state when constructed (and all backing memory will be deallocated)
+ void reset();
+ /// Adjusts such that the next allocate will be at least to the block alignment.
+ void adjustToBlockAlignment();
+
+ /// Gets the block alignment that is passed at initialization otherwise 0 an invalid block alignment.
+ size_t getBlockAlignment() const { return m_blockAlignment; }
+
+ /// Default Ctor
+ MemoryArena();
+ /// Construct with block size and alignment. Block alignment must be a power of 2.
+ MemoryArena(size_t blockSize, size_t blockAlignment = kMinAlignment);
+
+ /// Dtor
+ ~MemoryArena();
+
+protected:
+ struct Block
+ {
+ Block* m_next;
+ uint8_t* m_alloc;
+ uint8_t* m_start;
+ uint8_t* m_end;
+ };
+
+ void _initialize(size_t blockSize, size_t blockAlignment);
+
+ void* _allocateAligned(size_t size, size_t alignment);
+ void _deallocateBlocks();
+
+ void _setCurrentBlock(Block* block);
+
+ Block* _newCurrentBlock(size_t size, size_t alignment);
+ Block* _findBlock(const void* alloc, Block* endBlock = nullptr) const;
+ Block* _findPreviousBlock(Block* block);
+
+ uint8_t* m_start;
+ uint8_t* m_end;
+ uint8_t* m_current;
+ size_t m_blockSize;
+ size_t m_blockAlignment;
+ Block* m_blocks;
+ Block* m_currentBlock;
+
+ FreeList m_blockFreeList;
+
+ private:
+ // Disable
+ MemoryArena(const ThisType& rhs) = delete;
+ void operator=(const ThisType& rhs) = delete;
+};
+
+// --------------------------------------------------------------------------
+inline bool MemoryArena::isValid(const void* data, size_t size) const
+{
+ assert(size);
+
+ uint8_t* ptr = (uint8_t*)data;
+ // Is it in current
+ if (ptr >= m_start && ptr + size <= m_current)
+ {
+ return true;
+ }
+ // Is it in a previous block?
+ Block* block = _findBlock(data, m_currentBlock);
+ return block && (ptr >= block->m_start && (ptr + size) <= block->m_end);
+}
+
+// --------------------------------------------------------------------------
+SLANG_FORCE_INLINE void* MemoryArena::allocateUnaligned(size_t size)
+{
+ // Align with the minimum alignment
+ uint8_t* mem = m_current;
+ uint8_t* end = mem + size;
+ if (end <= m_end)
+ {
+ m_current = end;
+ return mem;
+ }
+ else
+ {
+ return _allocateAligned(size, m_blockAlignment);
+ }
+}
+
+// --------------------------------------------------------------------------
+SLANG_FORCE_INLINE void* MemoryArena::allocate(size_t size)
+{
+ // Align with the minimum alignment
+ const size_t alignMask = kMinAlignment - 1;
+ uint8_t* mem = (uint8_t*)((size_t(m_current) + alignMask) & ~alignMask);
+
+ if (mem + size <= m_end)
+ {
+ m_current = mem + size;
+ return mem;
+ }
+ else
+ {
+ return _allocateAligned(size, kMinAlignment);
+ }
+}
+
+// --------------------------------------------------------------------------
+inline void* MemoryArena::allocateAligned(size_t size, size_t alignment)
+{
+ // Alignment must be a power of 2
+ assert(((alignment - 1) & alignment) == 0);
+
+ // Align the pointer
+ const size_t alignMask = alignment - 1;
+ uint8_t* memory = (uint8_t*)((size_t(m_current) + alignMask) & ~alignMask);
+
+ if (memory + size <= m_end)
+ {
+ m_current = memory + size;
+ return memory;
+ }
+ else
+ {
+ return _allocateAligned(size, alignment);
+ }
+}
+
+// --------------------------------------------------------------------------
+inline const char* MemoryArena::allocateString(const char* str)
+{
+ size_t size = ::strlen(str);
+ if (size == 0)
+ {
+ return "";
+ }
+ char* dst = (char*)allocateUnaligned(size + 1);
+ ::memcpy(dst, str, size + 1);
+ return dst;
+}
+
+// --------------------------------------------------------------------------
+inline const char* MemoryArena::allocateString(const char* chars, size_t charsCount)
+{
+ if (charsCount == 0)
+ {
+ return "";
+ }
+ char* dst = (char*)allocateUnaligned(charsCount + 1);
+ ::memcpy(dst, chars, charsCount);
+
+ // Add null-terminating zero
+ dst[charsCount] = 0;
+ return dst;
+}
+
+// --------------------------------------------------------------------------
+template <typename T>
+inline T* MemoryArena::allocate()
+{
+ return reinterpret_cast<T*>(allocateAligned(sizeof(T), SLANG_ALIGN_OF(T)));
+}
+
+// --------------------------------------------------------------------------
+template <typename T>
+inline T* MemoryArena::allocateArray(size_t count)
+{
+ return (count > 0) ? reinterpret_cast<T*>(allocateAligned(sizeof(T) * count, SLANG_ALIGN_OF(T))) : nullptr;
+}
+
+// --------------------------------------------------------------------------
+template <typename T>
+inline T* MemoryArena::allocateAndCopyArray(const T* arr, size_t size)
+{
+ if (size > 0)
+ {
+ const size_t totalSize = sizeof(T) * size;
+ void* ptr = allocateAligned(totalSize, SLANG_ALIGN_OF(T));
+ ::memcpy(ptr, arr, totalSize);
+ return reinterpret_cast<T*>(ptr);
+ }
+ return nullptr;
+}
+
+// ---------------------------------------------------------------------------
+template <typename T>
+inline T* MemoryArena::allocateAndZeroArray(size_t size)
+{
+ if (size > 0)
+ {
+ const size_t totalSize = sizeof(T) * size;
+ void* ptr = allocateAligned(totalSize, SLANG_ALIGN_OF(T));
+ ::memset(ptr, 0, totalSize);
+ return reinterpret_cast<T*>(ptr);
+ }
+ return nullptr;
+}
+
+// --------------------------------------------------------------------------
+inline void MemoryArena::deallocateLast(void* data)
+{
+ // See if it's in current block
+ uint8_t* ptr = (uint8_t*)data;
+ if (ptr >= m_start && ptr < m_current)
+ {
+ // Then just go back
+ m_current = ptr;
+ }
+ else
+ {
+ // Only called if not in the current block. Therefore can only be in previous
+ Block* prevBlock = _findPreviousBlock(m_currentBlock);
+ if (prevBlock == nullptr || (!(ptr >= prevBlock->m_start && ptr < prevBlock->m_end)))
+ {
+ assert(!"Allocation not found");
+ return;
+ }
+
+ // Make the previous block the current
+ _setCurrentBlock(prevBlock);
+ // Make the current the alloc freed
+ m_current = ptr;
+ }
+}
+
+// --------------------------------------------------------------------------
+inline void MemoryArena::deallocateAllFrom(void* data)
+{
+ // See if it's in current block, and is allocated (ie < m_current)
+ uint8_t* ptr = (uint8_t*)data;
+ if (ptr >= m_start && ptr < m_current)
+ {
+ // If it's in current block, then just go back
+ m_current = ptr;
+ return;
+ }
+
+ // Search all blocks prior to current block
+ Block* block = _findBlock(data, m_currentBlock);
+ assert(block);
+ if (!block)
+ {
+ return;
+ }
+ // Make this current block
+ _setCurrentBlock(block);
+
+ // Move the pointer to the allocations position
+ m_current = ptr;
+}
+
+// --------------------------------------------------------------------------
+inline void MemoryArena::adjustToBlockAlignment()
+{
+ const size_t alignMask = m_blockAlignment - 1;
+ uint8_t* ptr = (uint8_t*)((size_t(m_current) + alignMask) & ~alignMask);
+
+ // Alignment might push beyond end of block... if so allocate a new block
+ // This test could be avoided if we aligned m_end, but depending on block alignment that might waste some space
+ if (ptr > m_end)
+ {
+ // We'll need a new block to make this alignment
+ _newCurrentBlock(0, m_blockAlignment);
+ }
+ else
+ {
+ // Set the position
+ m_current = ptr;
+ }
+ assert(size_t(m_current) & alignMask);
+}
+
+} // namespace Slang
+
+#endif // SLANG_MEMORY_ARENA_H \ No newline at end of file
diff --git a/source/core/slang-random-generator.cpp b/source/core/slang-random-generator.cpp
new file mode 100644
index 000000000..7e8476c30
--- /dev/null
+++ b/source/core/slang-random-generator.cpp
@@ -0,0 +1,143 @@
+
+#include "slang-random-generator.h"
+
+namespace Slang {
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!! RandomGenerator !!!!!!!!!!!!!!!!!!!!!!!! */
+
+float RandomGenerator::nextUnitFloat32()
+{
+ int32_t intValue = nextInt32();
+ return (intValue & 0x7fffffff) * (1.0f / 0x7fffffff);
+}
+
+bool RandomGenerator::nextBool()
+{
+ uint32_t bits = uint32_t(nextInt32());
+
+ // Xor together all bits in each byte
+ bits = ((bits & 0xaaaaaaaa) >> 1) ^ (bits & 0x55555555);
+ bits = ((bits & 0x44444444) >> 2) ^ (bits & 0x11111111);
+ bits = ((bits & 0x10101010) >> 4) ^ (bits & 0x01010101);
+
+ // In effect is the xor of all the bits of the original last byte
+ return ( bits & 1) != 0;
+}
+
+int64_t RandomGenerator::nextInt64()
+{
+ const int32_t high = nextInt32();
+ const int32_t low = nextInt32();
+
+ return (int64_t(high) << 32) | low;
+}
+
+int32_t RandomGenerator::nextInt32InRange(int32_t min, int32_t max)
+{
+ int32_t diff = max - min;
+ if (diff <= 1)
+ {
+ return min;
+ }
+
+ return (nextPositiveInt32() % diff) + min;
+}
+
+int64_t RandomGenerator::nextInt64InRange(int64_t min, int64_t max)
+{
+ int64_t diff = max - min;
+ if (diff <= 1)
+ {
+ return min;
+ }
+ return (nextPositiveInt64() % diff) + min;
+}
+
+/* static */RandomGenerator* RandomGenerator::create(int32_t seed)
+{
+ return new DefaultRandomGenerator(seed);
+}
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!! Mt19937RandomGenerator !!!!!!!!!!!!!!!!!!!!!!!! */
+
+Mt19937RandomGenerator::Mt19937RandomGenerator()
+{
+ reset(21452);
+}
+
+Mt19937RandomGenerator::Mt19937RandomGenerator(const ThisType& rhs)
+{
+ *this = rhs;
+}
+
+Mt19937RandomGenerator::Mt19937RandomGenerator(int32_t seed)
+{
+ reset(seed);
+}
+
+void Mt19937RandomGenerator::_generate()
+{
+ const uint32_t xorValue = 2567483615u;
+ for (int i = 0; i < kNumEntries - 1; ++i)
+ {
+ const uint32_t y = (m_mt[i] & 0x80000000) + (m_mt[i + 1] & 0x7fffffff);
+
+ // o = (i + 397) % kNumEntries
+ int32_t o = i + 397;
+ o = (o >= kNumEntries) ? (o - kNumEntries) : o;
+
+ m_mt[i] = m_mt[o] ^ (y >> 1);
+ // If y is odd
+ if (y & 1)
+ {
+ m_mt[i] = m_mt[i] ^ xorValue;
+ }
+ }
+
+ // Last
+ {
+ const int i = kNumEntries - 1;
+ const uint32_t y = (m_mt[i] & 0x80000000) + (m_mt[0] & 0x7fffffff);
+ const int32_t o = ((i + 397) - kNumEntries);
+
+ m_mt[i] = m_mt[o] ^ (y >> 1);
+ // If y is odd
+ if (y & 1)
+ {
+ m_mt[i] = m_mt[i] ^ xorValue;
+ }
+ }
+
+ m_index = 0;
+}
+
+void Mt19937RandomGenerator::reset(int32_t seedIn)
+{
+ m_index = 0;
+ m_mt[0] = uint32_t(seedIn);
+ for (int i = 1; i < kNumEntries; ++i)
+ {
+ m_mt[i] = (1812433253 * (m_mt[i - 1] ^ (m_mt[i - 1] >> 30)) + i);
+ }
+}
+
+int32_t Mt19937RandomGenerator::nextInt32()
+{
+ if (m_index >= kNumEntries)
+ {
+ _generate();
+ }
+
+ uint32_t y = m_mt[m_index++];
+ y = y ^ (y >> 11);
+ y = y ^ ((y << 7) & uint32_t(0x9d2c5680u));
+ y = y ^ ((y << 15) & uint32_t(0xefc6000u));
+ y = y ^ (y >> 18);
+
+ return int32_t(y);
+}
+
+
+
+
+} // namespace Slang
diff --git a/source/core/slang-random-generator.h b/source/core/slang-random-generator.h
new file mode 100644
index 000000000..7b3e42288
--- /dev/null
+++ b/source/core/slang-random-generator.h
@@ -0,0 +1,97 @@
+#ifndef SLANG_RANDOM_GENERATOR_H
+#define SLANG_RANDOM_GENERATOR_H
+
+#include "../../slang.h"
+
+#include <assert.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "smart-pointer.h"
+
+namespace Slang {
+
+class RandomGenerator: public RefObject
+{
+ public:
+
+ /// Make a copy of the generator in the same state
+ virtual RandomGenerator* clone() = 0;
+
+ /// Reset with a seed
+ virtual void reset(int32_t seed) = 0;
+ /// Next int32_t random number
+ virtual int32_t nextInt32() = 0;
+ /// Next int64_t random number
+ virtual int64_t nextInt64();
+
+ /// Get a 0-1 range floating point
+ virtual float nextUnitFloat32();
+
+ /// Get the next bool
+ virtual bool nextBool();
+
+ /// Next Int32 which can only be positive
+ int32_t nextPositiveInt32() { return nextInt32() & 0x7fffffff; }
+ /// Next Int64 which can only be positive
+ int64_t nextPositiveInt64() { return nextInt64() & SLANG_INT64(0x7fffffffffffffff); }
+
+ /// Returns value up to BUT NOT INCLUDING maxValue.
+ int32_t nextInt32UpTo(int32_t maxValue) { assert(maxValue > 0); return (maxValue <= 1) ? 0 : (nextPositiveInt32() % maxValue); }
+
+ /// Returns value from min up to BUT NOT INCLUDING max
+ int32_t nextInt32InRange(int32_t min, int32_t max);
+
+ /// Returns value up to BUT NOT INCLUDING maxValue
+ int64_t nextInt64UpTo(int64_t maxValue) { assert(maxValue > 0); return (maxValue <= 1) ? 0 : (nextPositiveInt64() % maxValue); }
+
+ /// Returns value from min up to BUT NOT INCLUDING max
+ int64_t nextInt64InRange(int64_t min, int64_t max);
+
+ /// Create a RandomGenerator with specified seed using default generator type
+ static RandomGenerator* create(int32_t seed);
+};
+
+/* Mersenne Twister random number generator
+https://en.wikipedia.org/wiki/Mersenne_Twister
+*/
+class Mt19937RandomGenerator: public RandomGenerator
+{
+ public:
+ typedef Mt19937RandomGenerator ThisType;
+
+ enum
+ {
+ kNumEntries = 624
+ };
+
+ Mt19937RandomGenerator* clone() SLANG_OVERRIDE { return new ThisType(*this); }
+ void reset(int32_t seed) SLANG_OVERRIDE;
+ int32_t nextInt32() SLANG_OVERRIDE;
+
+ /// Ctor
+ Mt19937RandomGenerator();
+ Mt19937RandomGenerator(const ThisType& rhs);
+ explicit Mt19937RandomGenerator(int32_t seed);
+
+ /// Assignment
+ void operator=(const ThisType& rhs)
+ {
+ m_index = rhs.m_index;
+ ::memcpy(m_mt, rhs.m_mt, sizeof(m_mt));
+ }
+
+ protected:
+ void _generate();
+
+ uint32_t m_mt[kNumEntries]; ///< The random state vector
+ int m_index; ///< If set to >= kMaxEntries it means reset
+
+};
+
+typedef Mt19937RandomGenerator DefaultRandomGenerator;
+
+} // namespace Slang
+
+#endif // SLANG_RANDOM_GENERATOR_H \ No newline at end of file
diff --git a/source/core/slang-string-util.cpp b/source/core/slang-string-util.cpp
index c60ad9683..29f8dc0ca 100644
--- a/source/core/slang-string-util.cpp
+++ b/source/core/slang-string-util.cpp
@@ -26,4 +26,52 @@ namespace Slang {
}
}
+
+/* static */void StringUtil::append(const char* format, va_list args, StringBuilder& buf)
+{
+ int numChars = 0;
+
+#if SLANG_WINDOWS_FAMILY
+ numChars = _vscprintf(format, args);
+#else
+ {
+ va_list argsCopy;
+ va_copy(argsCopy, args);
+ numChars = vsnprintf(nullptr, 0, format, argsCopy);
+ va_end(argsCopy);
+ }
+#endif
+
+ List<char> chars;
+ chars.SetSize(numChars + 1);
+
+#if SLANG_WINDOWS_FAMILY
+ vsnprintf_s(chars.Buffer(), numChars + 1, _TRUNCATE, format, args);
+#else
+ vsnprintf(chars.Buffer(), numChars + 1, format, args);
+#endif
+
+ buf.Append(chars.Buffer(), numChars);
+}
+
+/* static */void StringUtil::appendFormat(StringBuilder& buf, const char* format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ append(format, args, buf);
+ va_end(args);
+}
+
+/* static */String StringUtil::makeStringWithFormat(const char* format, ...)
+{
+ StringBuilder builder;
+
+ va_list args;
+ va_start(args, format);
+ append(format, args, builder);
+ va_end(args);
+
+ return builder;
+}
+
} // namespace Slang
diff --git a/source/core/slang-string-util.h b/source/core/slang-string-util.h
index bae576370..fc3258490 100644
--- a/source/core/slang-string-util.h
+++ b/source/core/slang-string-util.h
@@ -4,14 +4,26 @@
#include "slang-string.h"
#include "list.h"
+#include <stdarg.h>
+
namespace Slang {
struct StringUtil
{
+ /// Split in, by specified splitChar into slices out
+ /// Slices contents will directly address into in, so contents will only stay valid as long as in does.
static void split(const UnownedStringSlice& in, char splitChar, List<UnownedStringSlice>& slicesOut);
+
+ /// Appends formatted string with args into buf
+ static void append(const char* format, va_list args, StringBuilder& buf);
+
+ /// Appends the formatted string with specified trailing args
+ static void appendFormat(StringBuilder& buf, const char* format, ...);
+
+ /// Create a string from the format string applying args (like sprintf)
+ static String makeStringWithFormat(const char* format, ...);
};
} // namespace Slang
-
#endif // SLANG_STRING_UTIL_H