#include "allocator.hpp" #include "gp/algorithm/repeat.hpp" #include "gp/algorithm/rotate.hpp" #include "gp/algorithm/move.hpp" #include "gp/allocator/aggregator.hpp" #include "gp/allocator/arena.hpp" #include "gp/allocator/buddy.hpp" #include "gp/allocator/dummy.hpp" #include "gp/array.hpp" #include "gp/bitops.hpp" #include "gp/indexed_array.hpp" #include "gp/ring_list.hpp" #include "test_scaffold.h" #include #include #include #include #include #include #include #include #include #include #ifndef FUZZ_STRENGTH #define FUZZ_STRENGTH 2048 #endif #define MACRO_STRGEN(X) #X #define MACRO_STR(X) MACRO_STRGEN(X) constexpr bool time_fuzzes = true; struct arraysum_test : public test_scaffold { arraysum_test() { name = __FILE__ ":1"; } virtual int run() { gp::array test; for(auto& elem : test) { elem = 12; } return std::accumulate(test.begin(), test.end(), 0) != 12*test.size(); } }; append_test dummy_sd45uisd3(new arraysum_test{}); struct optional_test : public test_scaffold { optional_test() { name = __FILE__ ":1"; } virtual int run() { int res = 0; { gp::optional test; if(test.has_value()) { res++; } test = 12; if(test.has_value()) { if(test.value()!=12) { res++; } } else { res++; } } { gp::optional test; if(test.has_value()) { res++; } test = std::ifstream("/proc/cpuinfo"); if(!test.has_value()) { res++; } } return res; } }; append_test dummy_mlyusisd3(new optional_test{}); struct buddy_test : public test_scaffold { buddy_test() { name = std::string(__FILE__ "seed_") + std::to_string(seed) + ":3"; rng.seed(seed); } std::mt19937 rng{}; int seed = std::random_device{}(); virtual int run() { int res = 0; gp::repeat(10, [&](){ gp::array store; { gp::buddy(4096)> bud{&*store.begin(), store.size()}; gp::buddy(4096)> dum_bud{store.size()}; gp::buddy inner_bud{2048}; gp::dummy_allocator dummyall; { gp_config::assertion(!dummyall.try_reallocate(nullptr, 0), "reallocation works wut?"); gp_config::assertion(!bud.try_reallocate(nullptr, 0), "reallocation works wut?"); gp_config::assertion(!inner_bud.try_reallocate(nullptr, 0), "reallocation works wut?"); std::set ptr_set; for(int i = 0; i < 2048 / 16; i++) { void* v = inner_bud.allocate(16); gp_config::assertion(!inner_bud.empty(), "allocator should have elements"); if(v == nullptr) throw gp::runtime_error("allocation failed"); ptr_set.insert(v); } bool wut = ptr_set.count(nullptr)!=0 || ptr_set.size()!=(2048/16); if(wut) throw gp::runtime_error("some allocations failed line: " MACRO_STR(__LINE__)); if(nullptr != inner_bud.allocate(8)) throw gp::runtime_error("allocation succeeded, failure was expected"); for(auto elem : ptr_set) { gp_config::assertion(!inner_bud.empty(), "allocator should have elements"); if(inner_bud.deallocate(elem) == false) { res += 1; } } gp_config::assertion(inner_bud.empty(), "allocator should be empty"); } { gp_config::assertion(!dummyall.try_reallocate(nullptr, 0), "reallocation works wut?"); gp_config::assertion(!bud.try_reallocate(nullptr, 0), "reallocation works wut?"); std::set ptr_set; for(int i = 0; i < 4096 / 16; i++) { void* v = bud.allocate(16); gp_config::assertion(!bud.empty(), "allocator should have elements"); if(v == nullptr) throw gp::runtime_error("allocation failed"); ptr_set.insert(v); } if(ptr_set.count(nullptr)!=0 || ptr_set.size()!=(4096/16)) throw gp::runtime_error("some allocations failed line: " MACRO_STR(__LINE__)); if(nullptr != bud.allocate(8)) throw gp::runtime_error("allocation succeeded, failure was expected"); for(auto elem : ptr_set) { gp_config::assertion(!bud.empty(), "allocator should have elements"); if(bud.deallocate(elem) == false) { res += 1; } } gp_config::assertion(bud.empty(), "allocator should be empty"); } { std::set ptr_set; for(int i = 0; i < 4096 / 8; i++) { void* v = bud.allocate(8); gp_config::assertion(!bud.empty(), "allocator should have elements"); if(v == nullptr) throw gp::runtime_error("allocation failed"); ptr_set.insert(v); } if(ptr_set.count(nullptr)!=0 || ptr_set.size()!=(4096/8)) throw gp::runtime_error("some allocations failed line: " MACRO_STR(__LINE__)); if(nullptr != bud.allocate(8)) throw gp::runtime_error("allocation succeeded, failure was expected"); for(auto elem : ptr_set) { gp_config::assertion(!bud.empty(), "allocator should have elements"); if(bud.deallocate(elem) == false) { res += 1; } } gp_config::assertion(bud.empty(), "allocator should be empty"); } { std::set ptr_set; std::vector infill; std::insert_iterator< std::vector > inserter{infill, std::end(infill)}; std::fill_n(inserter, 4096 / 16 / 4, 16); inserter = std::insert_iterator< std::vector >{infill, std::end(infill)}; std::fill_n(inserter, 4096 / 8 / 4, 8); std::shuffle(infill.begin(), infill.end(), rng); for(auto sz : infill) { void* v = bud.allocate(sz); gp_config::assertion(!bud.empty(), "allocator should have elements"); if(v == nullptr) throw gp::runtime_error("allocation failed"); ptr_set.insert(v); } if(ptr_set.count(nullptr)!=0) throw gp::runtime_error("some allocations failed line: " MACRO_STR(__LINE__)); gp_config::assertion(!bud.deallocate((char*)store.begin().data + 1), "misaligned deallocation fails"); for(auto elem : ptr_set) { gp_config::assertion(!bud.empty(), "allocator should have elements"); if(bud.deallocate(elem) == false) { res += 1; } } gp_config::assertion(!bud.deallocate(nullptr), "deallocating out of scope returns false"); gp_config::assertion(bud.empty(), "allocator should be empty"); } } }); return res; } }; append_test dummy_654sisd3(new buddy_test{}); // TODO: should implement a test that tries to wrongly remove pointers as well as to allocate them correctly struct buddy_fuzz_test : public test_scaffold { buddy_fuzz_test() { name = std::string(__FILE__ "seed_") + std::to_string(seed) + ":4"; rng.seed(seed); } buddy_fuzz_test(size_t _seed) { seed = _seed; name = std::string(__FILE__ "seed_") + std::to_string(seed) + ":4"; rng.seed(seed); } std::mt19937 rng{}; int seed = std::random_device{}(); virtual int run() { int res = 0; alignas(8) gp::array store; gp::buddy(4096)> bud{&*store.begin(), store.size()}; std::vector ptr_set; auto get_random_mem_qt = [&]() -> size_t { return 1+rng()%(store.size()-1); }; auto start = std::chrono::steady_clock::now(); { gp::repeat(FUZZ_STRENGTH, [&](){ void* ptr; auto sz = get_random_mem_qt(); size_t tries = 0; std::shuffle( ptr_set.begin(), ptr_set.end(), rng ); while(!(ptr = bud.allocate(sz))) { void* free_ptr = ptr_set.back(); ptr_set.pop_back(); gp_config::assertion(bud.deallocate(free_ptr), "could not free sample"); gp_config::assertion(++tries <= store.size(), "infinite fuzzing"); } ptr_set.emplace_back(ptr); }); for(auto ptr : ptr_set) { bud.deallocate(ptr); } ptr_set.resize(0); } auto duration = std::chrono::steady_clock::now() - start; start = std::chrono::steady_clock::now(); { size_t acc = 0; gp::repeat(FUZZ_STRENGTH, [&](){ void* ptr; auto sz = get_random_mem_qt(); size_t tries = 0; std::shuffle( ptr_set.begin(), ptr_set.end(), rng ); ptr = malloc(sz); acc+=1; while(acc > 20) { void* free_ptr = ptr_set.back(); free(free_ptr); acc -= 1; ptr_set.pop_back(); gp_config::assertion(++tries <= store.size(), "infinite fuzzing"); } ptr_set.emplace_back(ptr); }); for(auto ptr : ptr_set) { free(ptr); } } auto reference = std::chrono::steady_clock::now() - start; if(do_bench){ std::cout << "Fuzzing timed at " << std::chrono::duration_cast(duration).count() << "µs for " << FUZZ_STRENGTH << " (reference: " << std::chrono::duration_cast(reference).count() << "µs)" << std::endl; } return res; } }; append_test dummy_df987sd3(new buddy_fuzz_test{781017366}); append_test dummy_df4sisd3(new buddy_fuzz_test{}); struct ring_list_test : public test_scaffold { ring_list_test() { name = __FILE__ ":5"; } virtual int run() { int res = 0; alignas(8) gp::array store; using local_allocator = gp::buddy(4096)>; local_allocator bud{&*store.begin(), store.size()}; { using string_ring = gp::ring_list; auto p = new(bud.allocate(sizeof(std::string))) std::string("Hello"); auto orig = new(bud.allocate(sizeof(string_ring::node))) string_ring::node(p); string_ring ring{orig, bud}; ring.insert("World"); auto it = ring.explore(); std::string test = ""; do{ test += *it; ++it; }while(it != ring.explore()); res += (test != "HelloWorld"); } return res; } }; append_test dummy_867fdrgsd3(new ring_list_test{}); struct aggregator_test : public test_scaffold { aggregator_test() { name = std::string(__FILE__ "seed_") + std::to_string(seed) + ":6"; rng.seed(seed); } std::mt19937 rng{}; int seed = std::random_device{}(); virtual int run() { int res = 0; alignas(8) gp::array store; using local_allocator = gp::buddy(4096)>; local_allocator bud{&*store.begin(), store.size()}; alignas(8) gp::array store2; local_allocator bud2{&*store2.begin(), store2.size()}; gp::aggregator allocator{bud}; allocator.insert(bud2); { std::vector ptr_set; auto get_random_mem_qt = [&]() -> size_t { return 1+rng()%(store.size()-1); }; auto start = std::chrono::steady_clock::now(); { gp::repeat(FUZZ_STRENGTH, [&](){ void* ptr; auto sz = get_random_mem_qt(); size_t tries = 0; std::shuffle( ptr_set.begin(), ptr_set.end(), rng ); while(!(ptr = allocator.allocate(sz))) { void* free_ptr = ptr_set.back(); ptr_set.pop_back(); gp_config::assertion(allocator.deallocate(free_ptr), "could not free sample"); gp_config::assertion(++tries <= store.size(), "infinite fuzzing"); } ptr_set.emplace_back(ptr); }); for(auto ptr : ptr_set) { bud.deallocate(ptr); } ptr_set.resize(0); } auto duration = std::chrono::steady_clock::now() - start; } void* a = allocator.allocate(8); gp_config::assertion(allocator.try_reallocate(a, 16) == false, "could reallocate? was it implemented?"); gp_config::assertion(allocator.deallocate(nullptr) == false, "error, could free an invalid pointer"); allocator.deallocate(a); { gp::ring_list list{allocator}; list.insert(8); list.insert(16); list.insert(32); } { gp::array work_array; gp::arena<> alloc_work(work_array.begin().data, work_array.size()); gp::ring_list, false> list{alloc_work}; gp_config::assertion(list.insert(8) == true, "could allocate in list with good enough allocator"); gp_config::assertion(list.insert(8) == true, "could allocate in list with good enough allocator"); gp_config::assertion(list.insert(8) == true, "could allocate in list with good enough allocator"); } { gp::array once_array; gp::arena<> alloc_once(once_array.begin().data, once_array.size()); gp::ring_list, false> list{alloc_once}; gp_config::assertion(list.insert(8) == false, "could allocate in list with insufficient allocator"); } { gp::arena<> alloc_none(nullptr, 0); gp::ring_list, false> list{alloc_none}; gp_config::assertion(list.insert(8) == false, "could allocate in list with fake allocator"); } return res; } }; append_test dummy_8ijfsd658(new aggregator_test{}); struct array_test : public test_scaffold { array_test() { name = __FILE__ ":7"; } virtual int run() { int res = 0; gp::array store; { int i = 0; for(auto& p : store) { p = i++; } for(auto it = store.rbegin(); it != store.rend(); ++it) { gp_config::assertion(*it == --i, "array error"); } for(const auto& p : store) { gp_config::assertion(p == i++, "array error"); } for(auto it = store.crbegin(); it != store.crend(); ++it) { gp_config::assertion(*it == --i, "array error"); } size_t cnt = 0; for(const auto& p : store) { cnt++; } gp_config::assertion(cnt == store.size(), "array error"); cnt = 0; for(auto& p : store) { cnt++; } gp_config::assertion(cnt == store.size(), "array error"); cnt = 0; for(auto it = store.crbegin(); it != store.crend(); ++it) { cnt++; } gp_config::assertion(cnt == store.size(), "array error"); cnt = 0; for(auto it = store.rbegin(); it != store.rend(); ++it) { cnt++; } gp_config::assertion(cnt == store.size(), "array error"); gp::rotate(store.begin(), store.begin()+1, store.end()); gp::array rotated({1,2,3,4,5,6,7,0}); gp_config::assertion(store == rotated, "rotate error"); gp::rotate(store.begin(), store.end()-1, store.end()); gp_config::assertion(store[0] == 0, "rotate error"); } return res; } }; append_test dummy_ajcurgsd3(new array_test{}); struct indexed_array_test : public test_scaffold { indexed_array_test() { name = __FILE__ ":7"; } virtual int run() { int res = 0; { gp::indexed_array store; size_t idx = store.push (112); store.push (113); store.push (114); gp_config::assertion(store[idx] == 112, "Bad value in indexed array"); store.mark_for_removal(idx); store.sweep_removed(); for(auto& p : store) { if(p == 112) res++; } gp_config::assertion(store.size() == 2, "Bad size of indexed array"); } { gp::indexed_array store; size_t idx = store.push ("112"); store.push ("113"); store.push ("114"); gp_config::assertion(store[idx] == "112", "Bad value in indexed array"); store.mark_for_removal(idx); store.sweep_removed(); for(auto& p : store) { if(p == "112") res++; } gp_config::assertion(store.size() == 2, "Bad size of indexed array"); { // TODO: write a concrete implementation and test it // gp::vfs fs; } } return res; } }; append_test dummy_khxurgsd3(new indexed_array_test{}); struct move_uninitialized_test : public test_scaffold { move_uninitialized_test() { name = __FILE__ ":8"; } struct tester { size_t* const incremented; tester(size_t* ptr) : incremented(ptr) {} tester(tester&& oth) : incremented(oth.incremented) { ++*incremented; } }; virtual int run() { int res = 0; { size_t counter; using src_t = gp::array; src_t *source = reinterpret_cast(malloc(sizeof(src_t))); gp::array buffer; for(auto& a : *source) { new(&a) tester(&counter); } gp::move_uninitialized(*source, buffer.as_buffer().cast()); free(source); } return res; } }; append_test dummy_hkfyr5f5(new move_uninitialized_test{}); struct clamp_test : public test_scaffold { clamp_test() { name = __FILE__ ":9"; } virtual int run() { int res = 0; { res += gp::clamp(0.0, -1.0, 1.0); res += gp::clamp(-1.0, 1.0, 0.0); res += gp::max(-1, -2, 0, -3); } return res; } }; append_test dummy_gsdh25f5(new clamp_test{}); struct buffer_test : public test_scaffold { buffer_test() { name = __FILE__ ":10"; } virtual int run() { int res = 0; { gp::array data; gp::array data_e; gp::buffer handle = data.as_buffer(); handle[12] = '&'; gp_config::assertion(*(handle.begin()+12) == '&', "Could not assign to the buffer"); res += 1; try { handle[24] = 16; res += 1; handle[-1] = 16; res += 1; handle[1024] = 16; } catch (...) { res -= 1; } res += 1; try { auto cast = handle.cast>(); } catch (...) { res -= 1; } auto cast = handle.template cast>().template cast>(); gp_config::assertion(false == (data == data_e), "Different arrays should return false here"); } return res; } }; append_test dummy_gs87ytf5f5(new buffer_test{}); struct endian_test : public test_scaffold { endian_test() { name = __FILE__ ":11"; } virtual int run() { int res = 0; { gp::endian_wrapper a = 0x41424344UL; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wfour-char-constants" #pragma gcc diagnostic push #pragma gcc diagnostic ignored "-Wfour-char-constants" gp_config::assertion(a.value != 'ABCD', "Not a big endian in a big endian wrapper"); #pragma gcc diagnostic pop #pragma clang diagnostic pop gp_config::assertion(gp::swap_endian(0x41424344UL)==0x44434241UL, "swap_endian doesn't swap endian"); } return res; } }; append_test dummy_45zelotf5f5(new endian_test{}); struct alloc_bench_test : public test_scaffold { alloc_bench_test() { name = __FILE__ ":12"; } virtual int run() { int res = 0; if(do_bench) { auto store = std::make_unique>(); using buddy_loc = gp::buddy<>; using arena_loc = gp::arena<>; buddy_loc bud{&*store->begin(), store->size()}; arena_loc are{&*store->begin(), store->size()}; std::cout << "Allocator | Operation | Divider | Time (µs)" << std::endl; for(size_t divider = 2; divider < 32; ++divider) { gp::ring_list a{bud}; gp::ring_list b{are}; std::cout << "ARE | " << "INS | " << divider << " | " << time_operation([&](){ for(size_t i = 0; i < store->size()/sizeof(gp::ring_list::node)/divider; i++) { b.insert(i); } }).count() << std::endl; std::cout << "ARE | " << "DEL | " << divider << " | " << time_operation([&](){ for(size_t i = 0; i < store->size()/sizeof(gp::ring_list::node)/divider; i++) { gp::ring_list::explorer e = b.explore(); b.remove(e); } }).count() << std::endl; std::cout << "BUD | " << "INS | " << divider << " | " << time_operation([&](){ for(size_t i = 0; i < store->size()/sizeof(gp::ring_list::node)/divider; i++) { a.insert(i); } }).count() << std::endl; std::cout << "BUD | " << "DEL | " << divider << " | " << time_operation([&](){ for(size_t i = 0; i < store->size()/sizeof(gp::ring_list::node)/divider; i++) { gp::ring_list::explorer e = a.explore(); a.remove(e); } }).count() << std::endl; } { gp::ring_list a{bud}; gp::ring_list b{are}; for(size_t i = 0; i < store->size()/sizeof(gp::ring_list::node)/2; i++) { a.insert(i); } for(size_t i = 0; i < store->size()/sizeof(gp::ring_list::node)/2; i++) { gp::ring_list::explorer e = a.explore(); a.remove(++e); } } } return res; } }; append_test dummy_jhgspo5d5(new alloc_bench_test{}); struct indexed_array_stair_test : public test_scaffold { indexed_array_stair_test() { name = __FILE__ ":13"; } virtual int run() { int res = 0; { int climb = 0; int curr_climb = 0; gp::indexed_array arr; while(climb < 1000) { for(; curr_climb < 10; climb++, curr_climb++ ) { arr.push(climb); } for(; curr_climb != 0; curr_climb -= 2) { arr.pop(arr.size() - curr_climb); } arr.pop(arr.size() - 1); } } return res; } }; append_test dummy_amf763ld5(new indexed_array_stair_test{});