summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/core/slang-memory-arena.cpp331
-rw-r--r--source/core/slang-memory-arena.h176
-rw-r--r--tools/slang-test/main.cpp169
-rw-r--r--tools/slang-test/test-context.cpp75
-rw-r--r--tools/slang-test/test-context.h31
-rw-r--r--tools/slang-test/unit-test-memory-arena.cpp49
6 files changed, 445 insertions, 386 deletions
diff --git a/source/core/slang-memory-arena.cpp b/source/core/slang-memory-arena.cpp
index bdfd87602..4d8b2ed2a 100644
--- a/source/core/slang-memory-arena.cpp
+++ b/source/core/slang-memory-arena.cpp
@@ -7,32 +7,34 @@ MemoryArena::MemoryArena()
{
// Mark as invalid so any alloc call will fail
m_blockAlignment = 0;
- m_blockSize = 0;
+ m_blockAllocSize = 0;
// Set up as empty
- m_blocks = nullptr;
- _setCurrentBlock(nullptr);
+ m_usedOddBlocks = nullptr;
+ m_availableBlocks = nullptr;
+
+ _resetCurrentBlock();
+
m_blockFreeList.init(sizeof(Block), sizeof(void*), 16);
}
MemoryArena::~MemoryArena()
{
- _deallocateBlocks();
+ reset();
}
-MemoryArena::MemoryArena(size_t blockSize, size_t blockAlignment)
+MemoryArena::MemoryArena(size_t blockPayloadSize, size_t blockAlignment)
{
- _initialize(blockSize, blockAlignment);
+ _initialize(blockPayloadSize, blockAlignment);
}
-void MemoryArena::init(size_t blockSize, size_t blockAlignment)
+void MemoryArena::init(size_t blockPayloadSize, size_t blockAlignment)
{
- _deallocateBlocks();
- m_blockFreeList.reset();
- _initialize(blockSize, blockAlignment);
+ reset();
+ _initialize(blockPayloadSize, blockAlignment);
}
-void MemoryArena::_initialize(size_t blockSize, size_t alignment)
+void MemoryArena::_initialize(size_t blockPayloadSize, size_t alignment)
{
// Alignment must be a power of 2
assert(((alignment - 1) & alignment) == 0);
@@ -40,99 +42,156 @@ void MemoryArena::_initialize(size_t blockSize, size_t alignment)
// Must be at least sizeof(void*) in size, as that is the minimum the backing allocator will be
alignment = (alignment < kMinAlignment) ? kMinAlignment : alignment;
+ m_blockPayloadSize = blockPayloadSize;
+ size_t blockAllocSize = blockPayloadSize;
+
// 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;
+ blockAllocSize += alignment;
}
- m_blockSize = blockSize;
+ m_blockAllocSize = blockAllocSize;
m_blockAlignment = alignment;
- m_blocks = nullptr;
- _setCurrentBlock(nullptr);
+
+ m_availableBlocks = nullptr;
+ m_usedOddBlocks = nullptr;
+
m_blockFreeList.init(sizeof(Block), sizeof(void*), 16);
+
+ _resetCurrentBlock();
}
-void* MemoryArena::_allocateAligned(size_t size, size_t alignment)
+void MemoryArena::_resetCurrentBlock()
{
- 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;
+ m_start = nullptr;
+ m_end = nullptr;
+ m_current = nullptr;
+
+ m_usedBlocks = nullptr;
+}
+
+void MemoryArena::_addCurrentBlock(Block* block)
+{
+ // Set up for allocation from
+ m_end = block->m_end;
+ m_start = block->m_start;
+ m_current = m_start;
+
+ // Add to linked list of used block, making it the top used block
+ block->m_next = m_usedBlocks;
+ m_usedBlocks = block;
}
-void MemoryArena::_deallocateBlocks()
+void MemoryArena::_deallocateBlocksPayload(Block* start)
{
- Block* currentBlock = m_blocks;
- while (currentBlock)
+ Block* cur = start;
+ while (cur)
{
// Deallocate the block
- ::free(currentBlock->m_alloc);
- // next block
- currentBlock = currentBlock->m_next;
+ ::free(cur->m_alloc);
+ cur = cur->m_next;
}
- // Can deallocate all blocks to
- m_blockFreeList.deallocateAll();
}
-void MemoryArena::_setCurrentBlock(Block* block)
+void MemoryArena::_deallocateBlocks(Block* start)
{
- if (block)
+ Block* cur = start;
+ while (cur)
{
- m_end = block->m_end;
- m_start = block->m_start;
- m_current = m_start;
+ Block* next = cur->m_next;
+ // Deallocate the block
+ ::free(cur->m_alloc);
+
+ m_blockFreeList.deallocate(cur);
+ cur = next;
}
- else
+}
+
+/* static */MemoryArena::Block* MemoryArena::_joinBlocks(Block* pre, Block* post)
+{
+ if (pre && post)
{
- m_start = nullptr;
- m_end = nullptr;
- m_current = nullptr;
+ // If both are actual lists, concat post at end of pre
+ Block* cur = pre;
+ while (cur->m_next)
+ {
+ cur = cur->m_next;
+ }
+ // Attach post to end of pre
+ cur->m_next = post;
}
- m_currentBlock = block;
+ return pre ? pre : post;
}
-MemoryArena::Block* MemoryArena::_newCurrentBlock(size_t size, size_t alignment)
+void MemoryArena::deallocateAll()
{
- // 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);
+ // Free all oversized
+ _deallocateBlocks(m_usedOddBlocks);
+ m_usedOddBlocks = nullptr;
+ // We want to put used blocks onto the start of the available list
+ m_availableBlocks = _joinBlocks(m_usedBlocks, m_availableBlocks);
- // Alignment must at a minimum be block alignment (such if reused the constraints hold)
- alignment = (alignment < m_blockAlignment) ? m_blockAlignment : alignment;
+ // Reset current block
+ _resetCurrentBlock();
+}
- const size_t alignMask = alignment - 1;
+void MemoryArena::reset()
+{
+ _deallocateBlocksPayload(m_usedOddBlocks);
+ _deallocateBlocksPayload(m_usedBlocks);
+ _deallocateBlocksPayload(m_availableBlocks);
+
+ m_blockFreeList.reset();
+
+ m_usedOddBlocks = nullptr;
+ m_availableBlocks = nullptr;
- // First try the next block (if there is one)
+ _resetCurrentBlock();
+}
+
+const MemoryArena::Block* MemoryArena::_findNonCurrent(const void* data, size_t size) const
+{
+ // It must either be m_usedOversizedBlocks or after m_usedBlocks (because m_usedBlocks is m_current)
+ const Block* block = _findInBlocks(m_usedOddBlocks, data, size);
+ if (block)
{
- Block* next = m_currentBlock ? m_currentBlock->m_next : m_blocks;
- if (next)
+ return block;
+ }
+ return m_usedBlocks ? _findInBlocks(m_usedBlocks->m_next, data, size) : nullptr;
+}
+
+const MemoryArena::Block* MemoryArena::_findInBlocks(const Block* block, const void* data, size_t size) const
+{
+ const uint8_t* ptr = (const uint8_t*)data;
+ while (block)
+ {
+ if (ptr >= block->m_start && ptr + size <= block->m_end)
{
- // 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;
- }
+ return block;
}
+ block = block->m_next;
}
+ return nullptr;
+}
- // The size of the block must be at least large enough to take into account alignment
- size_t allocSize = (alignment <= kMinAlignment) ? size : (size + alignment);
+MemoryArena::Block* MemoryArena::_newNormalBlock()
+{
+ if (m_availableBlocks)
+ {
+ // We have an available block..
+ Block* block = m_availableBlocks;
+ m_availableBlocks = block->m_next;
+ return block;
+ }
- // The minimum block size should be at least m_blockSize
- allocSize = (allocSize < m_blockSize) ? m_blockSize : allocSize;
+ return _newBlock(m_blockAllocSize, m_blockAlignment);
+}
+
+MemoryArena::Block* MemoryArena::_newBlock(size_t allocSize, size_t alignment)
+{
+ assert(alignment >= m_blockAlignment);
// Allocate block
Block* block = (Block*)m_blockFreeList.allocate();
@@ -140,6 +199,7 @@ MemoryArena::Block* MemoryArena::_newCurrentBlock(size_t size, size_t alignment)
{
return nullptr;
}
+
// Allocate the memory
uint8_t* alloc = (uint8_t*)::malloc(allocSize);
if (!alloc)
@@ -147,6 +207,9 @@ MemoryArena::Block* MemoryArena::_newCurrentBlock(size_t size, size_t alignment)
m_blockFreeList.deallocate(block);
return nullptr;
}
+
+ const size_t alignMask = alignment - 1;
+
// Do the alignment on the allocation
uint8_t* const start = (uint8_t*)((size_t(alloc) + alignMask) & ~alignMask);
@@ -156,89 +219,103 @@ MemoryArena::Block* MemoryArena::_newCurrentBlock(size_t size, size_t alignment)
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
+void* MemoryArena::_allocateAlignedFromNewBlock(size_t size, size_t alignment)
{
- const uint8_t* ptr = (const uint8_t*)alloc;
+ // Make sure init has been called (or has been set up in parameterized constructor)
+ assert(m_blockAllocSize > 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;
- Block* block = m_blocks;
- while (block != endBlock)
+ // The size of the block must be at least large enough to take into account alignment
+ size_t allocSize = (alignment <= kMinAlignment) ? size : (size + alignment);
+
+ const size_t currentRemainSize = size_t(m_end - m_current);
+
+ // a) Allocate a new normal block and make current
+ // b) Allocate a new normal block (leave current)
+ // c) Allocate a new 'oversized' block (leave current)
+ //
+ // We only bother with a and c here, as b is only really usable if size is close to m_blockAllocSize
+
+ // If there is > 1/3 of block remaining, or the block required is too big to fit use an 'oversized' block
+ if ((currentRemainSize * 3 > m_blockPayloadSize) || (allocSize > m_blockAllocSize))
{
- if (ptr >= block->m_start && ptr < block->m_end)
+ // We don't change current because there is enough remaining
+ Block* block = _newBlock(allocSize, alignment);
+ if (!block)
{
- return block;
+ return nullptr;
}
- block = block->m_next;
+ // Add to odd used blocks list
+ block->m_next = m_usedOddBlocks;
+ m_usedOddBlocks = block;
+ // NOTE! We are keeping the previous m_current current, so any remaining space can still be used
+ // Return the address
+ return block->m_start;
}
- return nullptr;
+
+ // Must be allocatable within a normal block
+ assert(allocSize <= m_blockAllocSize);
+ Block* block = _newNormalBlock();
+ if (!block)
+ {
+ return nullptr;
+ }
+ // It's a new regular block...
+ _addCurrentBlock(block);
+
+ // Do the aligned allocation (which must fit) by aligning the pointer
+ uint8_t* memory = (uint8_t*)((size_t(m_current) + alignMask) & ~alignMask);
+ // It must fit if the previous code is correct...
+ assert(memory + size <= m_end);
+ // Move the current pointer
+ m_current = memory + size;
+ return memory;
}
-MemoryArena::Block* MemoryArena::_findPreviousBlock(Block* block)
+size_t MemoryArena::_calcBlocksUsedMemory(const Block* block) const
{
- Block* currentBlock = m_blocks;
- while (currentBlock)
+ size_t total = 0;
+ while (block)
{
- if (currentBlock->m_next == block)
- {
- return currentBlock;
- }
- currentBlock = currentBlock->m_next;
+ total += size_t(block->m_end - block->m_start);
+ block = block->m_next;
}
- return nullptr;
+ return total;
}
-void MemoryArena::deallocateAll()
+size_t MemoryArena::_calcBlocksAllocatedMemory(const Block* block) const
{
- Block** prev = &m_blocks;
- Block* block = m_blocks;
-
+ size_t total = 0;
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;
- }
+ total += size_t(block->m_end - block->m_alloc);
+ block = block->m_next;
}
+ return total;
+}
- // Make the first current (if any)
- _setCurrentBlock(m_blocks);
+size_t MemoryArena::calcTotalMemoryUsed() const
+{
+ return _calcBlocksUsedMemory(m_usedOddBlocks)
+ + (m_usedBlocks ? _calcBlocksUsedMemory(m_usedBlocks->m_next) : 0) +
+ size_t(m_current - m_start);
}
-void MemoryArena::reset()
+size_t MemoryArena::calcTotalMemoryAllocated() const
{
- _deallocateBlocks();
- m_blocks = nullptr;
- _setCurrentBlock(nullptr);
+ return _calcBlocksAllocatedMemory(m_usedOddBlocks) +
+ _calcBlocksAllocatedMemory(m_usedBlocks) +
+ _calcBlocksAllocatedMemory(m_availableBlocks);
}
+
} // namespace Slang
diff --git a/source/core/slang-memory-arena.h b/source/core/slang-memory-arena.h
index acbac51dd..1c59ee7d7 100644
--- a/source/core/slang-memory-arena.h
+++ b/source/core/slang-memory-arena.h
@@ -8,18 +8,16 @@
#include <stdlib.h>
#include <string.h>
+#include <type_traits>
+
#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.
+/** MemoryArena provides provides very fast allocation of small blocks, by aggregating many small allocations
+over smaller amount of larger blocks. A typical small unaligned allocation is a pointer bump.
-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
+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.
@@ -27,7 +25,7 @@ All memory allocated can be deallocated very quickly and without a client having
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
+from the heap. As such 'odd blocks' are seen as unusual and potentially wasteful so 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
@@ -35,18 +33,22 @@ user data. This is so that alignment permitting, block allocations sizes can be
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
+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.
+
+If many 'odd' allocations occur it probably means that the block size should be increased.
*/
class MemoryArena
{
public:
typedef MemoryArena ThisType;
- static const size_t kMinAlignment = sizeof(void*); ///< The minimum alignment of the backing memory allocator.
-
+ /** The minimum alignment of the backing memory allocator.
+ NOTE! That this should not be greater than the alignment of the underlying allocator.
+ */
+ static const size_t kMinAlignment = sizeof(void*);
/** 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
@@ -78,8 +80,7 @@ public:
/** 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. */
+ @return The allocation (or nullptr if unable to allocate). */
void* allocateUnaligned(size_t sizeInBytes);
/** Allocates a null terminated string.
@@ -109,12 +110,6 @@ public:
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. */
@@ -128,10 +123,15 @@ public:
/// Gets the block alignment that is passed at initialization otherwise 0 an invalid block alignment.
size_t getBlockAlignment() const { return m_blockAlignment; }
+ /// Estimate of total amount of memory used in bytes. The number can never be smaller than actual used memory but may be larger
+ size_t calcTotalMemoryUsed() const;
+ /// Total memory allocated in bytes
+ size_t calcTotalMemoryAllocated() const;
+
/// 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);
+ MemoryArena(size_t blockPayloadSize, size_t blockAlignment = kMinAlignment);
/// Dtor
~MemoryArena();
@@ -139,32 +139,51 @@ public:
protected:
struct Block
{
- Block* m_next;
- uint8_t* m_alloc;
- uint8_t* m_start;
- uint8_t* m_end;
+ Block* m_next; ///< Singly linked list of blocks
+ uint8_t* m_alloc; ///< Allocation start (ie what to free)
+ uint8_t* m_start; ///< Start of payload (takes into account alignment)
+ uint8_t* m_end; ///< End of payload (m_start to m_end defines payload)
};
- void _initialize(size_t blockSize, size_t blockAlignment);
+ void _initialize(size_t blockPayloadSize, size_t blockAlignment);
+
+ /// Delete the linked list of blocks specified by start
+ void _deallocateBlocks(Block* start);
+ /// Delete the linked list of blocks payloads specified by start
+ void _deallocateBlocksPayload(Block* start);
+
+ void _resetCurrentBlock();
+ void _addCurrentBlock(Block* block);
+
+ static Block* _joinBlocks(Block* pre, Block* post);
+
+ /// Create a new block with regular block alignment
+ Block* _newNormalBlock();
+ /// Allocates a new block with allocSize and alignment
+ Block* _newBlock(size_t allocSize, size_t alignment);
- void* _allocateAligned(size_t size, size_t alignment);
- void _deallocateBlocks();
+ void* _allocateAlignedFromNewBlock(size_t size, size_t alignment);
- void _setCurrentBlock(Block* block);
+ /// Find block that contains data/size that is _NOT_ current (ie not first block in m_usedBlocks)
+ const Block* _findNonCurrent(const void* data, size_t size) const;
+ const Block* _findInBlocks(const Block* block, const void* data, size_t size) const;
- Block* _newCurrentBlock(size_t size, size_t alignment);
- Block* _findBlock(const void* alloc, Block* endBlock = nullptr) const;
- Block* _findPreviousBlock(Block* block);
+ size_t _calcBlocksUsedMemory(const Block* block) const;
+ size_t _calcBlocksAllocatedMemory(const Block* block) const;
- 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;
+ uint8_t* m_start; ///< The start of the current block (pointed to by m_usedBlocks)
+ uint8_t* m_end; ///< The end of the current block
+ uint8_t* m_current; ///< The current position in current block
- FreeList m_blockFreeList;
+ size_t m_blockPayloadSize; ///< The size of the payload of a block
+ size_t m_blockAllocSize; ///< The size of a block allocation (must be the same size or bigger than m_blockPayloadSize)
+ size_t m_blockAlignment; ///< The alignment applied to used blocks
+
+ Block* m_availableBlocks; ///< Standard sized blocks that are available
+ Block* m_usedBlocks; ///< List of all normal sized used blocks. The first one is the 'current block'
+ Block* m_usedOddBlocks; ///< Used 'odd' blocks - blocks can actually be smaller than normal blocks, but are typically larger.
+
+ FreeList m_blockFreeList; ///< Holds all of the blocks for fast allocation/free
private:
// Disable
@@ -176,21 +195,14 @@ protected:
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);
+ return (ptr >= m_start && ptr + size <= m_current) || _findNonCurrent(data, size) != nullptr;
}
// --------------------------------------------------------------------------
SLANG_FORCE_INLINE void* MemoryArena::allocateUnaligned(size_t size)
{
+ assert(size > 0);
// Align with the minimum alignment
uint8_t* mem = m_current;
uint8_t* end = mem + size;
@@ -201,13 +213,14 @@ SLANG_FORCE_INLINE void* MemoryArena::allocateUnaligned(size_t size)
}
else
{
- return _allocateAligned(size, m_blockAlignment);
+ return _allocateAlignedFromNewBlock(size, kMinAlignment);
}
}
// --------------------------------------------------------------------------
SLANG_FORCE_INLINE void* MemoryArena::allocate(size_t size)
{
+ assert(size > 0);
// Align with the minimum alignment
const size_t alignMask = kMinAlignment - 1;
uint8_t* mem = (uint8_t*)((size_t(m_current) + alignMask) & ~alignMask);
@@ -219,13 +232,14 @@ SLANG_FORCE_INLINE void* MemoryArena::allocate(size_t size)
}
else
{
- return _allocateAligned(size, kMinAlignment);
+ return _allocateAlignedFromNewBlock(size, kMinAlignment);
}
}
// --------------------------------------------------------------------------
inline void* MemoryArena::allocateAligned(size_t size, size_t alignment)
{
+ assert(size > 0);
// Alignment must be a power of 2
assert(((alignment - 1) & alignment) == 0);
@@ -240,7 +254,7 @@ inline void* MemoryArena::allocateAligned(size_t size, size_t alignment)
}
else
{
- return _allocateAligned(size, alignment);
+ return _allocateAlignedFromNewBlock(size, alignment);
}
}
@@ -290,6 +304,8 @@ inline T* MemoryArena::allocateArray(size_t count)
template <typename T>
inline T* MemoryArena::allocateAndCopyArray(const T* arr, size_t size)
{
+ SLANG_COMPILE_TIME_ASSERT(std::is_pod<T>::value);
+
if (size > 0)
{
const size_t totalSize = sizeof(T) * size;
@@ -315,59 +331,6 @@ inline T* MemoryArena::allocateAndZeroArray(size_t size)
}
// --------------------------------------------------------------------------
-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;
@@ -377,8 +340,9 @@ inline void MemoryArena::adjustToBlockAlignment()
// 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);
+ // We'll need a new block to make this alignment. Allocate a byte, and then rewind it.
+ _allocateAlignedFromNewBlock(1, 1);
+ m_current = m_usedBlocks->m_start;
}
else
{
diff --git a/tools/slang-test/main.cpp b/tools/slang-test/main.cpp
index b57dc6d9c..a4b903e25 100644
--- a/tools/slang-test/main.cpp
+++ b/tools/slang-test/main.cpp
@@ -99,7 +99,7 @@ struct Options
bool dumpOutputOnFailure = false;
// kind of output to generate
- TestOutputMode outputMode = TestOutputMode::eDefault;
+ TestOutputMode outputMode = TestOutputMode::Default;
// Only run tests that match one of the given categories
Dictionary<TestCategory*, TestCategory*> includeCategories;
@@ -212,21 +212,21 @@ Result parseOptions(int* argc, char** argv)
}
else if( strcmp(arg, "-appveyor") == 0 )
{
- g_options.outputMode = TestOutputMode::eAppVeyor;
+ g_options.outputMode = TestOutputMode::AppVeyor;
g_options.dumpOutputOnFailure = true;
}
else if( strcmp(arg, "-travis") == 0 )
{
- g_options.outputMode = TestOutputMode::eTravis;
+ g_options.outputMode = TestOutputMode::Travis;
g_options.dumpOutputOnFailure = true;
}
else if (strcmp(arg, "-xunit") == 0)
{
- g_options.outputMode = TestOutputMode::eXUnit;
+ g_options.outputMode = TestOutputMode::XUnit;
}
else if (strcmp(arg, "-xunit2") == 0)
{
- g_options.outputMode = TestOutputMode::eXUnit2;
+ g_options.outputMode = TestOutputMode::XUnit2;
}
else if( strcmp(arg, "-category") == 0 )
{
@@ -484,7 +484,7 @@ TestResult gatherTestOptions(
if(!category)
{
- return TestResult::eFail;
+ return TestResult::Fail;
}
testOptions.categories.Add(category);
@@ -499,7 +499,7 @@ TestResult gatherTestOptions(
break;
case 0: case '\r': case '\n':
- return TestResult::eFail;
+ return TestResult::Fail;
}
break;
@@ -516,7 +516,7 @@ TestResult gatherTestOptions(
cursor++;
else
{
- return TestResult::eFail;
+ return TestResult::Fail;
}
// Next scan for a sub-command name
@@ -533,7 +533,7 @@ TestResult gatherTestOptions(
break;
case 0: case '\r': case '\n':
- return TestResult::eFail;
+ return TestResult::Fail;
}
break;
@@ -546,7 +546,7 @@ TestResult gatherTestOptions(
cursor++;
else
{
- return TestResult::eFail;
+ return TestResult::Fail;
}
// Now scan for arguments. For now we just assume that
@@ -562,7 +562,7 @@ TestResult gatherTestOptions(
case 0: case '\r': case '\n':
skipToEndOfLine(&cursor);
testList->tests.Add(testOptions);
- return TestResult::ePass;
+ return TestResult::Pass;
default:
break;
@@ -603,7 +603,7 @@ TestResult gatherTestsForFile(
}
catch (Slang::IOException)
{
- return TestResult::eFail;
+ return TestResult::Fail;
}
@@ -619,12 +619,12 @@ TestResult gatherTestsForFile(
// Look for a pattern that matches what we want
if(match(&cursor, "//TEST_IGNORE_FILE"))
{
- return TestResult::eIgnored;
+ return TestResult::Ignored;
}
else if(match(&cursor, "//TEST"))
{
- if(gatherTestOptions(&cursor, testList) != TestResult::ePass)
- return TestResult::eFail;
+ if(gatherTestOptions(&cursor, testList) != TestResult::Pass)
+ return TestResult::Fail;
}
else
{
@@ -632,7 +632,7 @@ TestResult gatherTestsForFile(
}
}
- return TestResult::ePass;
+ return TestResult::Pass;
}
OSError spawnAndWait(TestContext* context, const String& testPath, OSProcessSpawner& spawner)
@@ -642,14 +642,14 @@ OSError spawnAndWait(TestContext* context, const String& testPath, OSProcessSpaw
if(context->m_isVerbose)
{
String commandLine = spawner.getCommandLine();
- context->messageFormat(TestMessageType::eInfo, "%s\n", commandLine.begin());
+ context->messageFormat(TestMessageType::Info, "%s\n", commandLine.begin());
}
OSError err = spawner.spawnAndWaitForCompletion();
if (err != kOSError_None)
{
// fprintf(stderr, "failed to run test '%S'\n", testPath.ToWString());
- context->messageFormat(TestMessageType::eRunError, "failed to run test '%S'", testPath.ToWString().begin());
+ context->messageFormat(TestMessageType::RunError, "failed to run test '%S'", testPath.ToWString().begin());
}
return err;
}
@@ -731,7 +731,7 @@ TestResult runSimpleTest(TestContext* context, TestInput& input)
if (spawnAndWait(context, outputStem, spawner) != kOSError_None)
{
- return TestResult::eFail;
+ return TestResult::Fail;
}
String actualOutput = getOutput(spawner);
@@ -753,19 +753,19 @@ TestResult runSimpleTest(TestContext* context, TestInput& input)
expectedOutput = "result code = 0\nstandard error = {\n}\nstandard output = {\n}\n";
}
- TestResult result = TestResult::ePass;
+ TestResult result = TestResult::Pass;
// Otherwise we compare to the expected output
if (actualOutput != expectedOutput)
{
context->dumpOutputDifference(expectedOutput, actualOutput);
- result = TestResult::eFail;
+ result = TestResult::Fail;
}
// If the test failed, then we write the actual output to a file
// so that we can easily diff it from the command line and
// diagnose the problem.
- if (result == TestResult::eFail)
+ if (result == TestResult::Fail)
{
String actualOutputPath = outputStem + ".actual";
Slang::File::WriteAllText(actualOutputPath, actualOutput);
@@ -793,7 +793,7 @@ TestResult runReflectionTest(TestContext* context, TestInput& input)
if (spawnAndWait(context, outputStem, spawner) != kOSError_None)
{
- return TestResult::eFail;
+ return TestResult::Fail;
}
String actualOutput = getOutput(spawner);
@@ -815,18 +815,18 @@ TestResult runReflectionTest(TestContext* context, TestInput& input)
expectedOutput = "result code = 0\nstandard error = {\n}\nstandard output = {\n}\n";
}
- TestResult result = TestResult::ePass;
+ TestResult result = TestResult::Pass;
// Otherwise we compare to the expected output
if (actualOutput != expectedOutput)
{
- result = TestResult::eFail;
+ result = TestResult::Fail;
}
// If the test failed, then we write the actual output to a file
// so that we can easily diff it from the command line and
// diagnose the problem.
- if (result == TestResult::eFail)
+ if (result == TestResult::Fail)
{
String actualOutputPath = outputStem + ".actual";
Slang::File::WriteAllText(actualOutputPath, actualOutput);
@@ -878,24 +878,24 @@ TestResult runEvalTest(TestContext* context, TestInput& input)
if (spawnAndWait(context, outputStem, spawner) != kOSError_None)
{
- return TestResult::eFail;
+ return TestResult::Fail;
}
String actualOutput = getOutput(spawner);
String expectedOutput = getExpectedOutput(outputStem);
- TestResult result = TestResult::ePass;
+ TestResult result = TestResult::Pass;
// Otherwise we compare to the expected output
if (actualOutput != expectedOutput)
{
- result = TestResult::eFail;
+ result = TestResult::Fail;
}
// If the test failed, then we write the actual output to a file
// so that we can easily diff it from the command line and
// diagnose the problem.
- if (result == TestResult::eFail)
+ if (result == TestResult::Fail)
{
String actualOutputPath = outputStem + ".actual";
Slang::File::WriteAllText(actualOutputPath, actualOutput);
@@ -934,7 +934,7 @@ TestResult runCrossCompilerTest(TestContext* context, TestInput& input)
if (spawnAndWait(context, outputStem, expectedSpawner) != kOSError_None)
{
- return TestResult::eFail;
+ return TestResult::Fail;
}
String expectedOutput = getOutput(expectedSpawner);
@@ -945,27 +945,27 @@ TestResult runCrossCompilerTest(TestContext* context, TestInput& input)
}
catch (Slang::IOException)
{
- return TestResult::eFail;
+ return TestResult::Fail;
}
if (spawnAndWait(context, outputStem, actualSpawner) != kOSError_None)
{
- return TestResult::eFail;
+ return TestResult::Fail;
}
String actualOutput = getOutput(actualSpawner);
- TestResult result = TestResult::ePass;
+ TestResult result = TestResult::Pass;
// Otherwise we compare to the expected output
if (actualOutput != expectedOutput)
{
- result = TestResult::eFail;
+ result = TestResult::Fail;
}
// If the test failed, then we write the actual output to a file
// so that we can easily diff it from the command line and
// diagnose the problem.
- if (result == TestResult::eFail)
+ if (result == TestResult::Fail)
{
String actualOutputPath = outputStem + ".actual";
Slang::File::WriteAllText(actualOutputPath, actualOutput);
@@ -999,7 +999,7 @@ TestResult generateHLSLBaseline(TestContext* context, TestInput& input)
if (spawnAndWait(context, outputStem, spawner) != kOSError_None)
{
- return TestResult::eFail;
+ return TestResult::Fail;
}
String expectedOutput = getOutput(spawner);
@@ -1010,9 +1010,9 @@ TestResult generateHLSLBaseline(TestContext* context, TestInput& input)
}
catch (Slang::IOException)
{
- return TestResult::eFail;
+ return TestResult::Fail;
}
- return TestResult::ePass;
+ return TestResult::Pass;
}
TestResult runHLSLComparisonTest(TestContext* context, TestInput& input)
@@ -1047,7 +1047,7 @@ TestResult runHLSLComparisonTest(TestContext* context, TestInput& input)
if (spawnAndWait(context, outputStem, spawner) != kOSError_None)
{
- return TestResult::eFail;
+ return TestResult::Fail;
}
// We ignore output to stdout, and only worry about what the compiler
@@ -1079,26 +1079,26 @@ TestResult runHLSLComparisonTest(TestContext* context, TestInput& input)
{
}
- TestResult result = TestResult::ePass;
+ TestResult result = TestResult::Pass;
// If no expected output file was found, then we
// expect everything to be empty
if (expectedOutput.Length() == 0)
{
- if (resultCode != 0) result = TestResult::eFail;
- if (standardError.Length() != 0) result = TestResult::eFail;
- if (standardOutput.Length() != 0) result = TestResult::eFail;
+ if (resultCode != 0) result = TestResult::Fail;
+ if (standardError.Length() != 0) result = TestResult::Fail;
+ if (standardOutput.Length() != 0) result = TestResult::Fail;
}
// Otherwise we compare to the expected output
else if (actualOutput != expectedOutput)
{
- result = TestResult::eFail;
+ result = TestResult::Fail;
}
// If the test failed, then we write the actual output to a file
// so that we can easily diff it from the command line and
// diagnose the problem.
- if (result == TestResult::eFail)
+ if (result == TestResult::Fail)
{
String actualOutputPath = outputStem + ".actual";
Slang::File::WriteAllText(actualOutputPath, actualOutput);
@@ -1147,7 +1147,7 @@ TestResult doGLSLComparisonTestRun(TestContext* context,
if (spawnAndWait(context, outputStem, spawner) != kOSError_None)
{
- return TestResult::eFail;
+ return TestResult::Fail;
}
OSProcessSpawner::ResultCode resultCode = spawner.getResultCode();
@@ -1170,7 +1170,7 @@ TestResult doGLSLComparisonTestRun(TestContext* context,
*outOutput = output;
- return TestResult::ePass;
+ return TestResult::Pass;
}
TestResult runGLSLComparisonTest(TestContext* context, TestInput& input)
@@ -1187,17 +1187,17 @@ TestResult runGLSLComparisonTest(TestContext* context, TestInput& input)
Slang::File::WriteAllText(outputStem + ".expected", expectedOutput);
Slang::File::WriteAllText(outputStem + ".actual", actualOutput);
- if( hlslResult == TestResult::eFail ) return TestResult::eFail;
- if( slangResult == TestResult::eFail ) return TestResult::eFail;
+ if( hlslResult == TestResult::Fail ) return TestResult::Fail;
+ if( slangResult == TestResult::Fail ) return TestResult::Fail;
if (actualOutput != expectedOutput)
{
context->dumpOutputDifference(expectedOutput, actualOutput);
- return TestResult::eFail;
+ return TestResult::Fail;
}
- return TestResult::ePass;
+ return TestResult::Pass;
}
@@ -1210,7 +1210,7 @@ TestResult runComputeComparisonImpl(TestContext* context, TestInput& input, cons
const String referenceOutput = findExpectedPath(input, ".expected.txt");
if (referenceOutput.Length() <= 0)
{
- return TestResult::eFail;
+ return TestResult::Fail;
}
OSProcessSpawner spawner;
@@ -1234,7 +1234,7 @@ TestResult runComputeComparisonImpl(TestContext* context, TestInput& input, cons
if (spawnAndWait(context, outputStem, spawner) != kOSError_None)
{
printf("error spawning render-test\n");
- return TestResult::eFail;
+ return TestResult::Fail;
}
auto actualOutput = getOutput(spawner);
@@ -1246,7 +1246,7 @@ TestResult runComputeComparisonImpl(TestContext* context, TestInput& input, cons
String actualOutputPath = outputStem + ".actual";
Slang::File::WriteAllText(actualOutputPath, actualOutput);
- return TestResult::eFail;
+ return TestResult::Fail;
}
// check against reference output
@@ -1254,24 +1254,24 @@ TestResult runComputeComparisonImpl(TestContext* context, TestInput& input, cons
{
printf("render-test not producing expected outputs.\n");
printf("render-test output:\n%s\n", actualOutput.Buffer());
- return TestResult::eFail;
+ return TestResult::Fail;
}
if (!File::Exists(referenceOutput))
{
printf("referenceOutput %s not found.\n", referenceOutput.Buffer());
- return TestResult::eFail;
+ return TestResult::Fail;
}
auto actualOutputContent = File::ReadAllText(actualOutputFile);
auto actualProgramOutput = Split(actualOutputContent, '\n');
auto referenceProgramOutput = Split(File::ReadAllText(referenceOutput), '\n');
auto printOutput = [&]()
{
- context->messageFormat(TestMessageType::eTestFailure, "output mismatch! actual output: {\n%s\n}, \n%s\n", actualOutputContent.Buffer(), actualOutput.Buffer());
+ context->messageFormat(TestMessageType::TestFailure, "output mismatch! actual output: {\n%s\n}, \n%s\n", actualOutputContent.Buffer(), actualOutput.Buffer());
};
if (actualProgramOutput.Count() < referenceProgramOutput.Count())
{
printOutput();
- return TestResult::eFail;
+ return TestResult::Fail;
}
for (int i = 0; i < (int)referenceProgramOutput.Count(); i++)
{
@@ -1285,13 +1285,13 @@ TestResult runComputeComparisonImpl(TestContext* context, TestInput& input, cons
if (actual != uval)
{
printOutput();
- return TestResult::eFail;
+ return TestResult::Fail;
}
else
- return TestResult::ePass;
+ return TestResult::Pass;
}
}
- return TestResult::ePass;
+ return TestResult::Pass;
}
TestResult runSlangComputeComparisonTest(TestContext* context, TestInput& input)
@@ -1337,7 +1337,7 @@ TestResult doRenderComparisonTestRun(TestContext* context, TestInput& input, cha
if (spawnAndWait(context, outputStem, spawner) != kOSError_None)
{
- return TestResult::eFail;
+ return TestResult::Fail;
}
OSProcessSpawner::ResultCode resultCode = spawner.getResultCode();
@@ -1360,7 +1360,7 @@ TestResult doRenderComparisonTestRun(TestContext* context, TestInput& input, cha
*outOutput = output;
- return TestResult::ePass;
+ return TestResult::Pass;
}
TestResult doImageComparison(TestContext* context, String const& filePath)
@@ -1382,19 +1382,19 @@ TestResult doImageComparison(TestContext* context, String const& filePath)
if(!expectedData)
{
- context->messageFormat(TestMessageType::eRunError, "Unable to load image ;%s'", expectedPath.Buffer());
- return TestResult::eFail;
+ context->messageFormat(TestMessageType::RunError, "Unable to load image ;%s'", expectedPath.Buffer());
+ return TestResult::Fail;
}
if(!actualData)
{
- context->messageFormat(TestMessageType::eRunError, "Unable to load image '%s'", actualPath.Buffer());
- return TestResult::eFail;
+ context->messageFormat(TestMessageType::RunError, "Unable to load image '%s'", actualPath.Buffer());
+ return TestResult::Fail;
}
if(expectedX != actualX || expectedY != actualY || expectedN != actualN)
{
- context->messageFormat(TestMessageType::eTestFailure, "Images are different sizes '%s' '%s'", actualPath.Buffer(), expectedPath.Buffer());
- return TestResult::eFail;
+ context->messageFormat(TestMessageType::TestFailure, "Images are different sizes '%s' '%s'", actualPath.Buffer(), expectedPath.Buffer());
+ return TestResult::Fail;
}
unsigned char* expectedCursor = expectedData;
@@ -1434,7 +1434,7 @@ TestResult doImageComparison(TestContext* context, String const& filePath)
// cases where vertex shader results lead to rendering that is off
// by one pixel...
- context->messageFormat(TestMessageType::eTestFailure, "image compare failure at (%d,%d) channel %d. expected %d got %d (absolute error: %d, relative error: %f)\n",
+ context->messageFormat(TestMessageType::TestFailure, "image compare failure at (%d,%d) channel %d. expected %d got %d (absolute error: %d, relative error: %f)\n",
x, y, n,
expectedVal,
actualVal,
@@ -1442,12 +1442,12 @@ TestResult doImageComparison(TestContext* context, String const& filePath)
relativeDiff);
// There was a difference we couldn't excuse!
- return TestResult::eFail;
+ return TestResult::Fail;
}
}
}
- return TestResult::ePass;
+ return TestResult::Pass;
}
TestResult runHLSLRenderComparisonTestImpl(
@@ -1468,23 +1468,23 @@ TestResult runHLSLRenderComparisonTestImpl(
Slang::File::WriteAllText(outputStem + ".expected", expectedOutput);
Slang::File::WriteAllText(outputStem + ".actual", actualOutput);
- if( hlslResult == TestResult::eFail ) return TestResult::eFail;
- if( slangResult == TestResult::eFail ) return TestResult::eFail;
+ if( hlslResult == TestResult::Fail ) return TestResult::Fail;
+ if( slangResult == TestResult::Fail ) return TestResult::Fail;
if (actualOutput != expectedOutput)
{
context->dumpOutputDifference(expectedOutput, actualOutput);
- return TestResult::eFail;
+ return TestResult::Fail;
}
// Next do an image comparison on the expected output images!
TestResult imageCompareResult = doImageComparison(context, outputStem);
- if(imageCompareResult != TestResult::ePass)
+ if(imageCompareResult != TestResult::Pass)
return imageCompareResult;
- return TestResult::ePass;
+ return TestResult::Pass;
}
TestResult runHLSLRenderComparisonTest(TestContext* context, TestInput& input)
@@ -1504,7 +1504,7 @@ TestResult runHLSLAndGLSLRenderComparisonTest(TestContext* context, TestInput& i
TestResult skipTest(TestContext* /* context */, TestInput& /*input*/)
{
- return TestResult::eIgnored;
+ return TestResult::Ignored;
}
@@ -1610,7 +1610,7 @@ TestResult runTest(
// If this test can be ignored
if (canIgnoreTestWithDisabledRenderer(testOptions))
{
- return TestResult::eIgnored;
+ return TestResult::Ignored;
}
// based on command name, dispatch to an appropriate callback
@@ -1673,7 +1673,7 @@ TestResult runTest(
// No actual test runner found!
- return TestResult::eFail;
+ return TestResult::Fail;
}
bool testCategoryMatches(
@@ -1732,7 +1732,7 @@ void runTestsOnFile(
// Gather a list of tests to run
FileTestList testList;
- if( gatherTestsForFile(filePath, &testList) == TestResult::eIgnored )
+ if( gatherTestsForFile(filePath, &testList) == TestResult::Ignored )
{
// Test was explicitly ignored
return;
@@ -1741,7 +1741,7 @@ void runTestsOnFile(
// Note cases where a test file exists, but we found nothing to run
if( testList.tests.Count() == 0 )
{
- context->addTest(filePath, TestResult::eIgnored);
+ context->addTest(filePath, TestResult::Ignored);
return;
}
@@ -1933,7 +1933,7 @@ int main(
// Exclude rendering tests when building under AppVeyor.
//
// TODO: this is very ad hoc, and we should do something cleaner.
- if( g_options.outputMode == TestOutputMode::eAppVeyor )
+ if( g_options.outputMode == TestOutputMode::AppVeyor )
{
g_options.excludeCategories.Add(renderTestCategory, renderTestCategory);
g_options.excludeCategories.Add(vulkanTestCategory, vulkanTestCategory);
@@ -1972,7 +1972,6 @@ int main(
// TODO: add more directories to this list
// TODO: allow for a command-line argument to select a particular directory
runTestsInDirectory(&context, "tests/");
-
}
context.outputSummary();
diff --git a/tools/slang-test/test-context.cpp b/tools/slang-test/test-context.cpp
index f77262001..822a5953c 100644
--- a/tools/slang-test/test-context.cpp
+++ b/tools/slang-test/test-context.cpp
@@ -77,7 +77,7 @@ TestContext::TestContext(TestOutputMode outputMode) :
m_failedTestCount = 0;
m_ignoredTestCount = 0;
- m_maxTestResults = 10;
+ m_maxFailTestResults = 10;
m_inTest = false;
m_dumpOutputOnFailure = false;
@@ -88,8 +88,8 @@ bool TestContext::canWriteStdError() const
{
switch (m_outputMode)
{
- case TestOutputMode::eXUnit:
- case TestOutputMode::eXUnit2:
+ case TestOutputMode::XUnit:
+ case TestOutputMode::XUnit2:
{
return false;
}
@@ -103,6 +103,8 @@ void TestContext::startTest(const String& testName)
m_inTest = true;
m_numCurrentResults = 0;
+ m_numFailResults = 0;
+
m_currentInfo = TestInfo();
m_currentInfo.name = testName;
m_currentMessage.Clear();
@@ -133,19 +135,22 @@ void TestContext::addResultWithLocation(TestResult result, const char* testText,
m_numCurrentResults++;
m_currentInfo.testResult = combine(m_currentInfo.testResult, result);
- if (result != TestResult::eFail)
+ if (result != TestResult::Fail)
{
// We don't need to output the result if it
return;
}
- if (m_maxTestResults > 0)
+ m_numFailResults++;
+
+ if (m_maxFailTestResults > 0)
{
- if (m_numCurrentResults > m_maxTestResults)
+ if (m_numFailResults > m_maxFailTestResults)
{
- if (m_numCurrentResults == m_maxTestResults + 1)
+ if (m_numFailResults == m_maxFailTestResults + 1)
{
- message(TestMessageType::eInfo, "...");
+ // It's a failure, but to show that there are more than are going to be shown, just show '...'
+ message(TestMessageType::TestFailure, "...");
}
return;
}
@@ -154,17 +159,17 @@ void TestContext::addResultWithLocation(TestResult result, const char* testText,
StringBuilder buf;
buf << testText << " - " << file << " (" << line << ")";
- message(TestMessageType::eTestFailure, buf);
+ message(TestMessageType::TestFailure, buf);
}
void TestContext::addResultWithLocation(bool testSucceeded, const char* testText, const char* file, int line)
{
- addResultWithLocation(testSucceeded ? TestResult::ePass : TestResult::eFail, testText, file, line);
+ addResultWithLocation(testSucceeded ? TestResult::Pass : TestResult::Fail, testText, file, line);
}
TestResult TestContext::addTest(const String& testName, bool isPass)
{
- const TestResult res = isPass ? TestResult::ePass : TestResult::eFail;
+ const TestResult res = isPass ? TestResult::Pass : TestResult::Fail;
addTest(testName, res);
return res;
}
@@ -188,7 +193,7 @@ void TestContext::dumpOutputDifference(const String& expectedOutput, const Strin
}
// Add to the m_currentInfo
- message(TestMessageType::eTestFailure, builder);
+ message(TestMessageType::TestFailure, builder);
}
void TestContext::_addResult(const TestInfo& info)
@@ -197,15 +202,15 @@ void TestContext::_addResult(const TestInfo& info)
switch (info.testResult)
{
- case TestResult::eFail:
+ case TestResult::Fail:
m_failedTestCount++;
break;
- case TestResult::ePass:
+ case TestResult::Pass:
m_passedTestCount++;
break;
- case TestResult::eIgnored:
+ case TestResult::Ignored:
m_ignoredTestCount++;
break;
@@ -224,9 +229,9 @@ void TestContext::_addResult(const TestInfo& info)
char const* resultString = "UNEXPECTED";
switch (info.testResult)
{
- case TestResult::eFail: resultString = "FAILED"; break;
- case TestResult::ePass: resultString = "passed"; break;
- case TestResult::eIgnored: resultString = "ignored"; break;
+ case TestResult::Fail: resultString = "FAILED"; break;
+ case TestResult::Pass: resultString = "passed"; break;
+ case TestResult::Ignored: resultString = "ignored"; break;
default:
assert(!"unexpected");
break;
@@ -234,20 +239,20 @@ void TestContext::_addResult(const TestInfo& info)
printf("%s test: '%S'\n", resultString, info.name.ToWString().begin());
break;
}
- case TestOutputMode::eXUnit2:
- case TestOutputMode::eXUnit:
+ case TestOutputMode::XUnit2:
+ case TestOutputMode::XUnit:
{
// Don't output anything -> we'll output all in one go at the end
break;
}
- case TestOutputMode::eAppVeyor:
+ case TestOutputMode::AppVeyor:
{
char const* resultString = "None";
switch (info.testResult)
{
- case TestResult::eFail: resultString = "Failed"; break;
- case TestResult::ePass: resultString = "Passed"; break;
- case TestResult::eIgnored: resultString = "Ignored"; break;
+ case TestResult::Fail: resultString = "Failed"; break;
+ case TestResult::Pass: resultString = "Passed"; break;
+ case TestResult::Ignored: resultString = "Ignored"; break;
default:
assert(!"unexpected");
break;
@@ -269,7 +274,7 @@ void TestContext::_addResult(const TestInfo& info)
if (err != kOSError_None)
{
- messageFormat(TestMessageType::eInfo, "failed to add appveyor test results for '%S'\n", info.name.ToWString().begin());
+ messageFormat(TestMessageType::Info, "failed to add appveyor test results for '%S'\n", info.name.ToWString().begin());
#if 0
fprintf(stderr, "[%d] TEST RESULT: %s {%d} {%s} {%s}\n", err, spawner.commandLine_.Buffer(),
@@ -297,7 +302,7 @@ void TestContext::addTest(const String& testName, TestResult testResult)
void TestContext::message(TestMessageType type, const String& message)
{
- if (type == TestMessageType::eInfo)
+ if (type == TestMessageType::Info)
{
if (m_isVerbose && canWriteStdError())
{
@@ -310,7 +315,7 @@ void TestContext::message(TestMessageType type, const String& message)
if (canWriteStdError())
{
- if (type == TestMessageType::eRunError || type == TestMessageType::eTestFailure)
+ if (type == TestMessageType::RunError || type == TestMessageType::TestFailure)
{
fprintf(stderr, "error: ");
fputs(message.Buffer(), stderr);
@@ -383,7 +388,7 @@ void TestContext::outputSummary()
printf("---\n");
for (const auto& testInfo : m_testInfos)
{
- if (testInfo.testResult == TestResult::eFail)
+ if (testInfo.testResult == TestResult::Fail)
{
printf("%s\n", testInfo.name.Buffer());
}
@@ -392,7 +397,7 @@ void TestContext::outputSummary()
}
break;
}
- case TestOutputMode::eXUnit:
+ case TestOutputMode::XUnit:
{
// xUnit 1.0 format
@@ -402,11 +407,11 @@ void TestContext::outputSummary()
for (const auto& testInfo : m_testInfos)
{
- const int numFailed = (testInfo.testResult == TestResult::eFail);
- const int numIgnored = (testInfo.testResult == TestResult::eIgnored);
+ const int numFailed = (testInfo.testResult == TestResult::Fail);
+ const int numIgnored = (testInfo.testResult == TestResult::Ignored);
//int numPassed = (testInfo.testResult == TestResult::ePass);
- if (testInfo.testResult == TestResult::ePass)
+ if (testInfo.testResult == TestResult::Pass)
{
printf(" <testcase name=\"%s\" status=\"run\"/>\n", testInfo.name.Buffer());
}
@@ -415,7 +420,7 @@ void TestContext::outputSummary()
printf(" <testcase name=\"%s\" status=\"run\">\n", testInfo.name.Buffer());
switch (testInfo.testResult)
{
- case TestResult::eFail:
+ case TestResult::Fail:
{
StringBuilder buf;
appendXmlEncode(testInfo.message, buf);
@@ -425,7 +430,7 @@ void TestContext::outputSummary()
printf(" </error>\n");
break;
}
- case TestResult::eIgnored:
+ case TestResult::Ignored:
{
printf(" <skip>Ignored</skip>\n");
break;
@@ -440,7 +445,7 @@ void TestContext::outputSummary()
printf("</testSuites>\n");
break;
}
- case TestOutputMode::eXUnit2:
+ case TestOutputMode::XUnit2:
{
// https://xunit.github.io/docs/format-xml-v2
assert("Not currently supported");
diff --git a/tools/slang-test/test-context.h b/tools/slang-test/test-context.h
index 22042a4e6..853bd3015 100644
--- a/tools/slang-test/test-context.h
+++ b/tools/slang-test/test-context.h
@@ -27,25 +27,27 @@ struct TestRegister
enum class TestOutputMode
{
- eDefault = 0, ///< Default mode is to write test results to the console
- eAppVeyor, ///< For AppVeyor continuous integration
- eTravis, ///< We currently don't specialize for Travis, but maybe we should.
- eXUnit, ///< xUnit original format https://nose.readthedocs.io/en/latest/plugins/xunit.html
- eXUnit2, ///< https://xunit.github.io/docs/format-xml-v2
+ Default = 0, ///< Default mode is to write test results to the console
+ AppVeyor, ///< For AppVeyor continuous integration
+ Travis, ///< We currently don't specialize for Travis, but maybe we should.
+ XUnit, ///< xUnit original format https://nose.readthedocs.io/en/latest/plugins/xunit.html
+ XUnit2, ///< https://xunit.github.io/docs/format-xml-v2
};
enum class TestResult
{
- eIgnored,
- ePass,
- eFail,
+ // NOTE! Must keep in order such that combine is meaningful. That is larger values are higher precident - and a series of tests that has lots of passes
+ // and a fail, is still a fail overall.
+ Ignored,
+ Pass,
+ Fail,
};
enum class TestMessageType
{
- eInfo, ///< General info (may not be shown depending on verbosity setting)
- eTestFailure, ///< Describes how a test failure took place
- eRunError, ///< Describes an error that caused a test not to actually correctly run
+ Info, ///< General info (may not be shown depending on verbosity setting)
+ TestFailure, ///< Describes how a test failure took place
+ RunError, ///< Describes an error that caused a test not to actually correctly run
};
class TestContext
@@ -54,7 +56,7 @@ class TestContext
struct TestInfo
{
- TestResult testResult = TestResult::eIgnored;
+ TestResult testResult = TestResult::Ignored;
Slang::String name;
Slang::String message; ///< Message that is specific for the testResult
};
@@ -118,9 +120,9 @@ class TestContext
int m_failedTestCount;
int m_ignoredTestCount;
- int m_maxTestResults; ///< Maximum amount of results per test. If 0 it's infinite.
+ int m_maxFailTestResults; ///< Maximum amount of results per test. If 0 it's infinite.
- TestOutputMode m_outputMode = TestOutputMode::eDefault;
+ TestOutputMode m_outputMode = TestOutputMode::Default;
bool m_dumpOutputOnFailure;
bool m_isVerbose;
@@ -130,6 +132,7 @@ protected:
Slang::StringBuilder m_currentMessage;
TestInfo m_currentInfo;
int m_numCurrentResults;
+ int m_numFailResults;
bool m_inTest;
diff --git a/tools/slang-test/unit-test-memory-arena.cpp b/tools/slang-test/unit-test-memory-arena.cpp
index 69eed520a..20a7e047d 100644
--- a/tools/slang-test/unit-test-memory-arena.cpp
+++ b/tools/slang-test/unit-test-memory-arena.cpp
@@ -123,15 +123,23 @@ static void memoryArenaUnitTest()
blocks.Add(arena.allocate(blockSize * 2));
blocks.Add(arena.allocate(100));
- while (blocks.Count())
+ arena.deallocateAll();
+ blocks.Add(arena.allocate(100));
+ blocks.Add(arena.allocate(blockSize * 2));
+
+ arena.reset();
+
{
- arena.deallocateLast(blocks.Last());
- blocks.RemoveLast();
+ uint32_t data[] = { 1, 2, 3 };
+
+ const uint32_t* copy = arena.allocateAndCopyArray(data, SLANG_COUNT_OF(data));
+
+ SLANG_CHECK(::memcmp(copy, data, sizeof(data)) == 0);
}
}
{
-
+ int count = 0;
const size_t blockSize = 1024;
for (TestMode mode = TestMode(0); int(mode) < int(TestMode::eCount); mode = TestMode(int(mode) + 1))
@@ -143,32 +151,30 @@ static void memoryArenaUnitTest()
List<Block> blocks;
- for (int i = 0; i < 1000; i++)
+ for (int i = 0; i < 10000; i++)
{
- int var = randGen.nextInt32() & 0x3ff;
+ count++;
+
+ const int var = randGen.nextInt32() & 0x3ff;
if (var < 3 && blocks.Count() > 0)
{
- if (var == 0)
- {
- // Do a single dealloc
- arena.deallocateLast(blocks.Last().m_data);
- blocks.RemoveLast();
- }
- else if (var == 1)
+ if (var == 1)
{
// Deallocate everything
arena.deallocateAll();
blocks.Clear();
+ }
+ else if (var == 2)
+ {
+ arena.reset();
+ blocks.Clear();
}
else
{
- // Do a multiple dealloc
- int index = randGen.nextInt32UpTo(int(blocks.Count()));
+ size_t usedMemory = arena.calcTotalMemoryUsed();
+ size_t allocatedMemory = arena.calcTotalMemoryAllocated();
- // Deallocate all afterwards
- arena.deallocateAllFrom(blocks[index].m_data);
-
- blocks.SetSize(index);
+ SLANG_CHECK(allocatedMemory >= usedMemory);
}
}
else
@@ -180,6 +186,11 @@ static void memoryArenaUnitTest()
{
sizeInBytes += blockSize;
}
+ else if ((randGen.nextInt32() & 0xff) < 2)
+ {
+ // Let's try for a block that's awkwardly sized
+ sizeInBytes = blockSize / 3 + 10;
+ }
const uint8_t value = uint8_t(randGen.nextInt32());