/* * Copyright (C) 2023 Brett Terpstra * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ #include <memory_test.h> #include <blt/std/logging.h> #include <blt/std/memory.h> #include <blt/std/assert.h> #include <blt/std/random.h> #include <type_traits> #include "blt/std/utility.h" #include <unordered_set> #include <blt/compatibility.h> template<typename T> blt::scoped_buffer<T> create_scoped_buffer(size_t size) { static std::random_device dev; static std::mt19937_64 engine(dev()); blt::scoped_buffer<T> data(size); if constexpr (std::is_floating_point_v<T>) { static std::uniform_real_distribution<T> dist(std::numeric_limits<T>::min(), std::numeric_limits<T>::max()); for (auto& v : data) v = dist(engine); } else if (std::is_integral_v<T>) { static std::uniform_int_distribution<T> dist(std::numeric_limits<T>::min(), std::numeric_limits<T>::max()); for (auto& v : data) v = dist(engine); } return data; } template<typename T> blt::scoped_buffer<T> modify_copy(blt::scoped_buffer<T> fill) { for (size_t i = 0; i < size_t(fill.size() / 2); i++) { std::swap(fill[i], fill[fill.size() - i - 1]); } return fill; } template<typename T> T collect(blt::scoped_buffer<T> buff) { T val = 0; for (auto v : buff) val = std::max(v, val); return val; } void blt::test::memory::copy() { BLT_INFO("Running memory copy tests"); auto int_buffer_small = create_scoped_buffer<int32_t>(16); auto int_buffer_medium = create_scoped_buffer<int32_t>(512); auto int_buffer_large = create_scoped_buffer<int32_t>(8192); auto float_buffer_small = create_scoped_buffer<float>(16); auto float_buffer_medium = create_scoped_buffer<float>(512); auto float_buffer_large = create_scoped_buffer<float>(8192); auto int_small = collect(modify_copy(int_buffer_small)); auto int_medium = collect(modify_copy(int_buffer_medium)); auto int_large = collect(modify_copy(int_buffer_large)); auto float_small = collect(modify_copy(float_buffer_small)); auto float_medium = collect(modify_copy(float_buffer_medium)); auto float_large = collect(modify_copy(float_buffer_large)); BLT_TRACE("We collected values [%d, %d, %d]; [%f.0, %f.0, %f.0]", int_small, int_medium, int_large, float_small, float_medium, float_large); } void blt::test::memory::move() { BLT_INFO("Running memory move tests"); } void blt::test::memory::access() { BLT_INFO("Running memory construction tests"); } void blt::test::memory::static_vector_test() { blt::static_vector<int, 16> vec; for (size_t i = 0; i < 16; i++) vec[i] = static_cast<int>(i * 2); for (size_t i = 0; i < 16; i++) BLT_DEBUG_STREAM << vec[i] << ' '; BLT_DEBUG_STREAM << '\n'; vec[3] = 120; vec[7] = 230; vec.reserve(vec.capacity()); for (auto v : vec) BLT_DEBUG_STREAM << v << ' '; BLT_DEBUG_STREAM << '\n'; vec.reserve(0); for (size_t i = 0; i < vec.capacity(); i++) { if (!vec.push_back(static_cast<int>(i))) BLT_INFO("Failed to insert on %d", i); } if (!vec.push_back(10)) BLT_INFO("Vector unable to push, current size vs capacity: %d vs %d", vec.size(), vec.capacity()); for (auto v : vec) BLT_DEBUG_STREAM << v << ' '; BLT_DEBUG_STREAM << '\n'; } struct fucked_type2 { public: static constexpr size_t initial_value = 50; int T = 0; public: fucked_type2() { T = initial_value; //BLT_DEBUG("I HAVE BEEN CONSTRUCTED"); } void set(int t) { T = t; } ~fucked_type2() { //BLT_DEBUG("I HAVE BEEN DESTRUCTED!"); } }; #define ALLOC(alloc, amount) alloc.allocate(amount), amount /** * run tests to make sure that we can actually allocate blocks of memory. * we are using a custom type to ensure that the state is known and the example is complex enough * if this work then it should work for any generic type */ template<size_t allocator_size = 20> void test_allocations_1() { std::vector<std::pair<fucked_type2*, size_t>> types; blt::area_allocator<fucked_type2, allocator_size> int_test{}; types.emplace_back(ALLOC(int_test, static_cast<size_t>(allocator_size * 0.75))); for (size_t i = 0; i < static_cast<size_t>(allocator_size * 0.30); i++) { types.emplace_back(ALLOC(int_test, 1)); auto v = std::pair{ALLOC(int_test, 1)}; v.first->set(120); int_test.deallocate(v.first, 1); types.emplace_back(ALLOC(int_test, 1)); types.emplace_back(ALLOC(int_test, 1)); types.emplace_back(ALLOC(int_test, 1)); } types.emplace_back(ALLOC(int_test, 1)); types.emplace_back(ALLOC(int_test, 1)); types.emplace_back(ALLOC(int_test, 1)); types.emplace_back(ALLOC(int_test, 1)); types.emplace_back(ALLOC(int_test, 1)); bool passed = true; std::unordered_set<fucked_type2*> used_pointers; for (const auto& pair : types) { for (size_t i = 0; i < pair.second; i++) { // every value should be the initial value assigned in the constructor // if this isn't the case there was an error. if (pair.first[i].T != fucked_type2::initial_value) { BLT_WARN("We have an allocated value that isn't initial at index %d (allocated in a block of size %d at pointer %p)", i, pair.second, pair.first); passed = false; break; } // every allocation here should be unique. // if we have a pointer in our list which is not unique, // we know we have an error if (BLT_CONTAINS(used_pointers, &pair.first[i])) { BLT_WARN( "We have found another pointer which was allocated as a unique block but isn't (in block %d with size %d; pointer in question: %p)", i, pair.second, pair.first); passed = false; break; } used_pointers.insert(&pair.first[i]); } int_test.deallocate(pair.first, pair.second); } if (passed) BLT_INFO("Test (1) with size %d passed!", allocator_size); else BLT_ERROR("Test (1) with size %d failed!", allocator_size); } void blt::test::memory::test() { test_allocations_1(); test_allocations_1<50>(); test_allocations_1<4096>(); std::vector<std::pair<fucked_type2*, size_t>> types; area_allocator<fucked_type2, 20> int_test{}; //auto arr = int_test.allocate(10); types.emplace_back(ALLOC(int_test, 15)); types.emplace_back(ALLOC(int_test, 1)); auto v = std::pair{ALLOC(int_test, 1)}; v.first->set(120); int_test.deallocate(v.first, 1); types.emplace_back(ALLOC(int_test, 1)); types.emplace_back(ALLOC(int_test, 1)); types.emplace_back(ALLOC(int_test, 1)); types.emplace_back(ALLOC(int_test, 1)); types.emplace_back(ALLOC(int_test, 1)); types.emplace_back(ALLOC(int_test, 1)); types.emplace_back(ALLOC(int_test, 1)); types.emplace_back(ALLOC(int_test, 1)); //blt::black_box(arr4); BLT_INFO("CUM"); for (const auto& pair : types) { BLT_TRACE("Pointer: %p", pair.first); for (size_t i = 0; i < pair.second; i++) { BLT_TRACE_STREAM << pair.first[i].T << ' '; } BLT_TRACE_STREAM << '\n'; int_test.deallocate(pair.first, pair.second); BLT_INFO("-----------------"); } }