Implement edge cases in insert/end and add a simple test case.
This commit is contained in:
parent
6f8982bf3f
commit
3a57286595
@ -415,11 +415,18 @@ if (SPIRV_CROSS_CLI)
|
||||
add_executable(spirv-cross-c-api-test tests-other/c_api_test.c)
|
||||
target_link_libraries(spirv-cross-c-api-test spirv-cross-c)
|
||||
set_target_properties(spirv-cross-c-api-test PROPERTIES LINK_FLAGS "${spirv-cross-link-flags}")
|
||||
|
||||
add_executable(spirv-cross-small-vector-test tests-other/small_vector.cpp)
|
||||
target_link_libraries(spirv-cross-small-vector-test spirv-cross-core)
|
||||
set_target_properties(spirv-cross-small-vector-test PROPERTIES LINK_FLAGS "${spirv-cross-link-flags}")
|
||||
|
||||
if (CMAKE_COMPILER_IS_GNUCXX OR (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang"))
|
||||
target_compile_options(spirv-cross-c-api-test PRIVATE -std=c89 -Wall -Wextra)
|
||||
endif()
|
||||
add_test(NAME spirv-cross-c-api-test
|
||||
COMMAND $<TARGET_FILE:spirv-cross-c-api-test> ${CMAKE_CURRENT_SOURCE_DIR}/tests-other/c_api_test.spv)
|
||||
add_test(NAME spirv-cross-small-vector-test
|
||||
COMMAND $<TARGET_FILE:spirv-cross-small-vector-test>)
|
||||
add_test(NAME spirv-cross-test
|
||||
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --parallel
|
||||
${spirv-cross-externals}
|
||||
|
@ -222,7 +222,7 @@ protected:
|
||||
// Simple vector which supports up to N elements inline, without malloc/free.
|
||||
// We use a lot of throwaway vectors all over the place which triggers allocations.
|
||||
// This class only implements the subset of std::vector we need in SPIRV-Cross.
|
||||
// It is *NOT* a drop-in replacement.
|
||||
// It is *NOT* a drop-in replacement in general projects.
|
||||
template <typename T, size_t N = 8>
|
||||
class SmallVector : public VectorView<T>
|
||||
{
|
||||
@ -380,16 +380,89 @@ public:
|
||||
|
||||
void insert(T *itr, const T *insert_begin, const T *insert_end)
|
||||
{
|
||||
auto count = size_t(insert_end - insert_begin);
|
||||
if (itr == this->end())
|
||||
{
|
||||
auto count = size_t(insert_end - insert_begin);
|
||||
reserve(this->buffer_size + count);
|
||||
for (size_t i = 0; i < count; i++, insert_begin++)
|
||||
new (&this->ptr[this->buffer_size + i]) T(*insert_begin);
|
||||
this->buffer_size += count;
|
||||
}
|
||||
else
|
||||
SPIRV_CROSS_THROW("Mid-insert not implemented.");
|
||||
{
|
||||
if (this->buffer_size + count > buffer_capacity)
|
||||
{
|
||||
auto target_capacity = this->buffer_size + count;
|
||||
if (target_capacity == 0)
|
||||
target_capacity = 1;
|
||||
if (target_capacity < N)
|
||||
target_capacity = N;
|
||||
|
||||
while (target_capacity < count)
|
||||
target_capacity <<= 1u;
|
||||
|
||||
// Need to allocate new buffer. Move everything to a new buffer.
|
||||
T *new_buffer =
|
||||
target_capacity > N ? static_cast<T *>(malloc(target_capacity * sizeof(T))) : stack_storage.data();
|
||||
if (!new_buffer)
|
||||
SPIRV_CROSS_THROW("Out of memory.");
|
||||
|
||||
// First, move elements from source buffer to new buffer.
|
||||
// We don't deal with types which can throw in move constructor.
|
||||
auto *target_itr = new_buffer;
|
||||
auto *original_source_itr = this->begin();
|
||||
|
||||
if (new_buffer != this->ptr)
|
||||
{
|
||||
while (original_source_itr != itr)
|
||||
{
|
||||
new(target_itr) T(std::move(*original_source_itr));
|
||||
original_source_itr->~T();
|
||||
++original_source_itr;
|
||||
++target_itr;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy-construct new elements.
|
||||
for (auto *source_itr = insert_begin; source_itr != insert_end; ++source_itr, ++target_itr)
|
||||
new (target_itr) T(*source_itr);
|
||||
|
||||
// Move over the other half.
|
||||
if (new_buffer != this->ptr || insert_begin != insert_end)
|
||||
{
|
||||
while (original_source_itr != this->end())
|
||||
{
|
||||
new(target_itr) T(std::move(*original_source_itr));
|
||||
original_source_itr->~T();
|
||||
++original_source_itr;
|
||||
++target_itr;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->ptr != stack_storage.data())
|
||||
free(this->ptr);
|
||||
this->ptr = new_buffer;
|
||||
buffer_capacity = target_capacity;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Move in place, need to be a bit careful about which elements are constructed and which are not.
|
||||
// Move the end and construct the new elements.
|
||||
auto *target_itr = this->end() + count;
|
||||
auto *source_itr = this->end();
|
||||
while (target_itr != this->end())
|
||||
{
|
||||
--target_itr;
|
||||
--source_itr;
|
||||
new (target_itr) T(std::move(*source_itr));
|
||||
}
|
||||
// For already constructed elements we can move-assign.
|
||||
std::move_backward(itr, source_itr, target_itr);
|
||||
std::copy(insert_begin, insert_end, itr);
|
||||
}
|
||||
|
||||
this->buffer_size += count;
|
||||
}
|
||||
}
|
||||
|
||||
T *erase(T *itr)
|
||||
@ -401,9 +474,16 @@ public:
|
||||
|
||||
void erase(T *start_erase, T *end_erase)
|
||||
{
|
||||
if (end_erase != this->end())
|
||||
SPIRV_CROSS_THROW("Mid-erase not implemented.");
|
||||
resize(size_t(start_erase - this->begin()));
|
||||
if (end_erase == this->end())
|
||||
{
|
||||
resize(size_t(start_erase - this->begin()));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto new_size = this->buffer_size - (end_erase - start_erase);
|
||||
std::move(end_erase, this->end(), start_erase);
|
||||
resize(new_size);
|
||||
}
|
||||
}
|
||||
|
||||
void resize(size_t new_size)
|
||||
|
201
tests-other/small_vector.cpp
Normal file
201
tests-other/small_vector.cpp
Normal file
@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Copyright 2019 Hans-Kristian Arntzen
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "spirv_cross.hpp"
|
||||
|
||||
using namespace spirv_cross;
|
||||
|
||||
// Test the tricky bits of the implementation.
|
||||
// Running the entire test suite on this implementation should find all other potential issues.
|
||||
|
||||
static int allocations = 0;
|
||||
static int deallocations = 0;
|
||||
|
||||
#define SPVC_ASSERT(x) do { \
|
||||
if (!(x)) SPIRV_CROSS_THROW("Assert: " #x " failed!"); \
|
||||
} while(0)
|
||||
|
||||
struct RAIIInt
|
||||
{
|
||||
RAIIInt(int v_) : v(v_) { allocations++; }
|
||||
~RAIIInt() { deallocations++; }
|
||||
RAIIInt() { allocations++; }
|
||||
RAIIInt(const RAIIInt &other) { v = other.v; allocations++; }
|
||||
RAIIInt(RAIIInt &&other) SPIRV_CROSS_NOEXCEPT { v = other.v; allocations++; }
|
||||
RAIIInt &operator=(RAIIInt &&) = default;
|
||||
RAIIInt &operator=(const RAIIInt &) = default;
|
||||
|
||||
int v = 0;
|
||||
};
|
||||
|
||||
static void propagate_stack_to_heap()
|
||||
{
|
||||
SmallVector<RAIIInt, 2> ints;
|
||||
ints.emplace_back(1);
|
||||
ints.emplace_back(2);
|
||||
auto *old_data = ints.data();
|
||||
SPVC_ASSERT(ints[0].v == 1);
|
||||
SPVC_ASSERT(ints[1].v == 2);
|
||||
ints.emplace_back(3);
|
||||
SPVC_ASSERT(old_data != ints.data());
|
||||
SPVC_ASSERT(ints[0].v == 1);
|
||||
SPVC_ASSERT(ints[1].v == 2);
|
||||
SPVC_ASSERT(ints[2].v == 3);
|
||||
SPVC_ASSERT(ints.size() == 3);
|
||||
}
|
||||
|
||||
static void insert_end()
|
||||
{
|
||||
SmallVector<RAIIInt, 2> ints;
|
||||
ints.emplace_back(1);
|
||||
ints.emplace_back(2);
|
||||
|
||||
const RAIIInt new_ints[3] = { 10, 20, 30 };
|
||||
ints.insert(ints.end(), new_ints, new_ints + 3);
|
||||
SPVC_ASSERT(ints.size() == 5);
|
||||
|
||||
SPVC_ASSERT(ints[0].v == 1);
|
||||
SPVC_ASSERT(ints[1].v == 2);
|
||||
SPVC_ASSERT(ints[2].v == 10);
|
||||
SPVC_ASSERT(ints[3].v == 20);
|
||||
SPVC_ASSERT(ints[4].v == 30);
|
||||
}
|
||||
|
||||
static void insert_begin_realloc()
|
||||
{
|
||||
SmallVector<RAIIInt, 2> ints;
|
||||
ints.emplace_back(1);
|
||||
ints.emplace_back(2);
|
||||
|
||||
const RAIIInt new_ints[3] = { 10, 20, 30 };
|
||||
ints.insert(ints.begin(), new_ints, new_ints + 3);
|
||||
SPVC_ASSERT(ints.size() == 5);
|
||||
|
||||
SPVC_ASSERT(ints[0].v == 10);
|
||||
SPVC_ASSERT(ints[1].v == 20);
|
||||
SPVC_ASSERT(ints[2].v == 30);
|
||||
SPVC_ASSERT(ints[3].v == 1);
|
||||
SPVC_ASSERT(ints[4].v == 2);
|
||||
}
|
||||
|
||||
static void insert_middle_realloc()
|
||||
{
|
||||
SmallVector<RAIIInt, 2> ints;
|
||||
ints.emplace_back(1);
|
||||
ints.emplace_back(2);
|
||||
|
||||
const RAIIInt new_ints[3] = { 10, 20, 30 };
|
||||
ints.insert(ints.begin() + 1, new_ints, new_ints + 3);
|
||||
SPVC_ASSERT(ints.size() == 5);
|
||||
|
||||
SPVC_ASSERT(ints[0].v == 1);
|
||||
SPVC_ASSERT(ints[1].v == 10);
|
||||
SPVC_ASSERT(ints[2].v == 20);
|
||||
SPVC_ASSERT(ints[3].v == 30);
|
||||
SPVC_ASSERT(ints[4].v == 2);
|
||||
}
|
||||
|
||||
static void insert_begin_no_realloc()
|
||||
{
|
||||
SmallVector<RAIIInt, 2> ints;
|
||||
ints.reserve(10);
|
||||
ints.emplace_back(1);
|
||||
ints.emplace_back(2);
|
||||
|
||||
const RAIIInt new_ints[3] = { 10, 20, 30 };
|
||||
ints.insert(ints.begin(), new_ints, new_ints + 3);
|
||||
SPVC_ASSERT(ints.size() == 5);
|
||||
|
||||
SPVC_ASSERT(ints[0].v == 10);
|
||||
SPVC_ASSERT(ints[1].v == 20);
|
||||
SPVC_ASSERT(ints[2].v == 30);
|
||||
SPVC_ASSERT(ints[3].v == 1);
|
||||
SPVC_ASSERT(ints[4].v == 2);
|
||||
}
|
||||
|
||||
static void insert_middle_no_realloc()
|
||||
{
|
||||
SmallVector<RAIIInt, 2> ints;
|
||||
ints.reserve(10);
|
||||
ints.emplace_back(1);
|
||||
ints.emplace_back(2);
|
||||
|
||||
const RAIIInt new_ints[3] = { 10, 20, 30 };
|
||||
ints.insert(ints.begin() + 1, new_ints, new_ints + 3);
|
||||
SPVC_ASSERT(ints.size() == 5);
|
||||
|
||||
SPVC_ASSERT(ints[0].v == 1);
|
||||
SPVC_ASSERT(ints[1].v == 10);
|
||||
SPVC_ASSERT(ints[2].v == 20);
|
||||
SPVC_ASSERT(ints[3].v == 30);
|
||||
SPVC_ASSERT(ints[4].v == 2);
|
||||
}
|
||||
|
||||
static void erase_end()
|
||||
{
|
||||
SmallVector<RAIIInt, 2> ints;
|
||||
ints.emplace_back(1);
|
||||
ints.emplace_back(2);
|
||||
ints.emplace_back(3);
|
||||
ints.emplace_back(4);
|
||||
ints.erase(ints.begin() + 1, ints.end());
|
||||
|
||||
SPVC_ASSERT(ints.size() == 1);
|
||||
SPVC_ASSERT(ints[0].v == 1);
|
||||
}
|
||||
|
||||
static void erase_middle()
|
||||
{
|
||||
SmallVector<RAIIInt, 2> ints;
|
||||
ints.emplace_back(1);
|
||||
ints.emplace_back(2);
|
||||
ints.emplace_back(3);
|
||||
ints.emplace_back(4);
|
||||
ints.erase(ints.begin() + 1, ints.end() - 1);
|
||||
|
||||
SPVC_ASSERT(ints.size() == 2);
|
||||
SPVC_ASSERT(ints[0].v == 1);
|
||||
SPVC_ASSERT(ints[1].v == 4);
|
||||
}
|
||||
|
||||
static void erase_start()
|
||||
{
|
||||
SmallVector<RAIIInt, 2> ints;
|
||||
ints.emplace_back(1);
|
||||
ints.emplace_back(2);
|
||||
ints.emplace_back(3);
|
||||
ints.emplace_back(4);
|
||||
ints.erase(ints.begin(), ints.end() - 2);
|
||||
|
||||
SPVC_ASSERT(ints.size() == 2);
|
||||
SPVC_ASSERT(ints[0].v == 3);
|
||||
SPVC_ASSERT(ints[1].v == 4);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
propagate_stack_to_heap();
|
||||
insert_end();
|
||||
insert_begin_realloc();
|
||||
insert_begin_no_realloc();
|
||||
insert_middle_realloc();
|
||||
insert_middle_no_realloc();
|
||||
erase_end();
|
||||
erase_middle();
|
||||
erase_start();
|
||||
|
||||
SPVC_ASSERT(allocations > 0 && deallocations > 0 && deallocations == allocations);
|
||||
}
|
Loading…
Reference in New Issue
Block a user