diff --git a/Makefile b/Makefile index 8a5515d..6979335 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ CXX= clang++-8 -CXXFLAGS= --std=c++17 -g -O0 -fprofile-instr-generate -fcoverage-mapping -pthread - +CXXFLAGS= --std=c++17 -O0 -pthread -DFUZZ_STRENGTH=4000000 \ + -g -fprofile-instr-generate -fcoverage-mapping all: tests tests: bin/tests diff --git a/include/gp/algorithm/min_max.hpp b/include/gp/algorithm/min_max.hpp index 483ed05..6bfb560 100644 --- a/include/gp/algorithm/min_max.hpp +++ b/include/gp/algorithm/min_max.hpp @@ -26,4 +26,12 @@ namespace gp{ return min(first < second ? first : second, args...); } } + + template + constexpr T clamp(T first, T value, T last) + { + if(value < first) return first; + if(value > last) return last; + return value; + } } \ No newline at end of file diff --git a/include/gp/algorithm/modifiers.hpp b/include/gp/algorithm/modifiers.hpp new file mode 100644 index 0000000..667eeb8 --- /dev/null +++ b/include/gp/algorithm/modifiers.hpp @@ -0,0 +1,204 @@ +#pragma once +#include +#include "gp/algorithm/move.hpp" + +namespace gp { + + + + template + auto bind_front(F&& func, Args&&... arg_parent) + { + return [&](auto&&... argv){ + return func(arg_parent..., argv...); + }; + } + + + + + + + + + + + /* Code extrated from cppreference.com and the libc++ Library project, licensed under MIT license */ + + namespace detail { + template + constexpr bool is_reference_wrapper_v = false; + template + constexpr bool is_reference_wrapper_v> = true; + + template + constexpr decltype(auto) INVOKE(Type T::* f, T1&& t1, Args&&... args) + { + if constexpr (std::is_member_function_pointer_v) { + if constexpr (std::is_base_of_v>) + return (gp::forward(t1).*f)(gp::forward(args)...); + else if constexpr (is_reference_wrapper_v>) + return (t1.get().*f)(gp::forward(args)...); + else + return ((*gp::forward(t1)).*f)(gp::forward(args)...); + } else { + static_assert(std::is_member_object_pointer_v); + static_assert(sizeof...(args) == 0); + if constexpr (std::is_base_of_v>) + return gp::forward(t1).*f; + else if constexpr (is_reference_wrapper_v>) + return t1.get().*f; + else + return (*gp::forward(t1)).*f; + } + } + + template + constexpr decltype(auto) INVOKE(F&& f, Args&&... args) + { + return gp::forward(f)(gp::forward(args)...); + } + } // namespace detail + + template< class F, class... Args> + constexpr std::invoke_result_t invoke(F&& f, Args&&... args) + noexcept(std::is_nothrow_invocable_v) + { + return detail::INVOKE(gp::forward(f), gp::forward(args)...); + } + + + + template< class T > + struct remove_cvref { + typedef std::remove_cv_t> type; + }; + + template< class T > + using remove_cvref_t = typename remove_cvref::type; + + template< class T > + T* addressof(T& arg) + { + return reinterpret_cast( + &const_cast( + reinterpret_cast(arg))); + } + + namespace detail { + template constexpr T& FUN(T& t) noexcept { return t; } + template void FUN(T&&) = delete; + } + + template + class reference_wrapper { + public: + // types + typedef T type; + + // construct/copy/destroy + template (std::declval()), + std::enable_if_t>>() + )> + constexpr reference_wrapper(U&& u) noexcept(noexcept(detail::FUN(gp::forward(u)))) + : _ptr(gp::addressof(detail::FUN(gp::forward(u)))) {} + reference_wrapper(const reference_wrapper&) noexcept = default; + + // assignment + reference_wrapper& operator=(const reference_wrapper& x) noexcept = default; + + // access + constexpr operator T& () const noexcept { return *_ptr; } + constexpr T& get() const noexcept { return *_ptr; } + + template< class... ArgTypes > + constexpr std::invoke_result_t + operator() ( ArgTypes&&... args ) const { + return gp::invoke(get(), gp::forward(args)...); + } + + private: + T* _ptr; + }; + + // deduction guides + template + reference_wrapper(T&) -> reference_wrapper; + + template + auto ref(T& vref) { + return reference_wrapper(vref); + } + + template + class __not_fn_imp { + _DecayFunc __fd; + + public: + __not_fn_imp() = delete; + + template + auto operator()(_Args&& ...__args) & + noexcept(noexcept(!gp::invoke(__fd, gp::forward<_Args>(__args)...))) + -> decltype( !gp::invoke(__fd, gp::forward<_Args>(__args)...)) + { return !gp::invoke(__fd, gp::forward<_Args>(__args)...); } + + template + auto operator()(_Args&& ...__args) && + noexcept(noexcept(!gp::invoke(gp::move(__fd), gp::forward<_Args>(__args)...))) + -> decltype( !gp::invoke(gp::move(__fd), gp::forward<_Args>(__args)...)) + { return !gp::invoke(gp::move(__fd), gp::forward<_Args>(__args)...); } + + template + auto operator()(_Args&& ...__args) const& + noexcept(noexcept(!gp::invoke(__fd, gp::forward<_Args>(__args)...))) + -> decltype( !gp::invoke(__fd, gp::forward<_Args>(__args)...)) + { return !gp::invoke(__fd, gp::forward<_Args>(__args)...); } + + + template + auto operator()(_Args&& ...__args) const&& + noexcept(noexcept(!gp::invoke(gp::move(__fd), gp::forward<_Args>(__args)...))) + -> decltype( !gp::invoke(gp::move(__fd), gp::forward<_Args>(__args)...)) + { return !gp::invoke(gp::move(__fd), gp::forward<_Args>(__args)...); } + + private: + template , __not_fn_imp>::value>> + explicit __not_fn_imp(_RawFunc&& __rf) + : __fd(gp::forward<_RawFunc>(__rf)) {} + + template + friend inline __not_fn_imp> not_fn(_RawFunc&&); + }; + + template + inline __not_fn_imp> not_fn(_RawFunc&& __fn) { + return __not_fn_imp>(gp::forward<_RawFunc>(__fn)); + } + + + + + + + + + + + + + + + + + + + + + + + + +} \ No newline at end of file diff --git a/include/gp/algorithm/repeat.hpp b/include/gp/algorithm/repeat.hpp new file mode 100644 index 0000000..df810e4 --- /dev/null +++ b/include/gp/algorithm/repeat.hpp @@ -0,0 +1,9 @@ +#pragma once +#include + +namespace gp{ + template + void repeat(size_t n, F f) { + while (n--) f(); + } +} \ No newline at end of file diff --git a/include/gp/allocator/buddy.hpp b/include/gp/allocator/buddy.hpp index c9c819f..2bac2dd 100644 --- a/include/gp/allocator/buddy.hpp +++ b/include/gp/allocator/buddy.hpp @@ -5,11 +5,12 @@ #include "gp/integer_math.hpp" #include #include - +#include +#include namespace gp{ - template + template class buddy{ struct twig { bool used : 1; @@ -20,7 +21,7 @@ namespace gp{ } operator uint8_t() { - return used + 2 * used_children; + return 1 * used + 2 * used_children; } }; struct bundle { @@ -28,30 +29,35 @@ namespace gp{ uint8_t b : 2; uint8_t c : 2; uint8_t d : 2; + + bundle() { + a = 0; b = 0; c = 0; d = 0; + } }; page_allocator allocator; gp::buffer data; - static constexpr size_t max_depth = max_msb - gp::math::msb(align); - static constexpr size_t required_twigs = (1 << (max_depth + 1)) - 1; + const size_t max_depth; + const size_t twig_explore_length; + static constexpr size_t max_theoric_depth = max_msb - gp::math::msb(align); + static constexpr size_t required_twigs = (1 << (max_theoric_depth + 1)) - 1; /** * ((max allocatable size - min allocatable size) ** 2 - 1) / 4 twigs in a bundle **/ static constexpr size_t span_size = required_twigs / 4 + (required_twigs % 4 != 0); gp::array stack; - twig get_twig(size_t idx) { + twig get_twig(size_t idx) const { auto far = idx / 4; auto local = idx % 4; - auto& group = stack[far]; switch(local) { case 0: - return group.a; + return stack[far].a; case 1: - return group.b; + return stack[far].b; case 2: - return group.c; + return stack[far].c; case 3: - return group.d; + return stack[far].d; } } @@ -76,19 +82,23 @@ namespace gp{ } constexpr size_t size_to_depth(size_t sz) { - auto pow2 = gp::math::msb(sz); - return gp::max(max_depth ,max_msb - pow2 - (0 != sz % (1 << pow2))); + size_t pow2 = gp::math::msb(sz) - gp::math::msb(align); + return gp::clamp( + (size_t)0 , + max_depth - pow2, + max_depth + ); } constexpr size_t depth_to_size(size_t depth) { return 1 << (max_depth - depth + gp::math::msb(align)); } - size_t get_left(size_t index) { + constexpr size_t get_left(size_t index) const { return ((index + 1) << 1) - 1; } - size_t get_right(size_t index) { + constexpr size_t get_right(size_t index) const { return ((index + 1) << 1); } @@ -104,28 +114,32 @@ namespace gp{ template void all_over(size_t index, function func) { - size_t parent = ((index + 1) >> 1) - 1; - func(parent); - if(parent != 0) - all_over(parent, func); + if(index != 0) { + size_t parent = ((index + 1) >> 1) - 1; + func(parent); + if(parent != 0) + all_over(parent, func); + } } template - bool is_any_child(size_t index, function func) { + bool is_any_child(size_t index, function func) const { size_t left = get_left(index); size_t right = get_right(index); - if(func(left)) return true; - if(func(right)) return true; - if(any_child(left, func)) return true; - if(any_child(right, func)) return true; + if(left < twig_explore_length && right < twig_explore_length) { + if(func(left)) return true; + if(func(right)) return true; + if(is_any_child(left, func)) return true; + if(is_any_child(right, func)) return true; + } return false; } static constexpr size_t no_twig = -1; - size_t find_free_twig(size_t depth, size_t root = 0, size_t explored = 0) { + size_t find_free_twig(size_t depth, size_t root = 0, size_t explored = 0) const { + auto v = get_twig(root); if(depth == explored) { - auto v = get_twig(root); if(v.used || v.used_children) { return no_twig; @@ -133,6 +147,10 @@ namespace gp{ return root; } } else { + if(v.used) + { + return no_twig; + } ++explored; auto ret = find_free_twig(depth, get_right(root), explored); if(ret != no_twig) @@ -147,26 +165,65 @@ namespace gp{ } return no_twig; } + + size_t find_used_twig(size_t offset, size_t root = 0, size_t explored = 0) { + auto v = get_twig(root); + if(v.used && offset == 0) + { + return root; + } + ++explored; + if(explored > max_depth) return no_twig; + size_t cut = (1 << (max_depth + gp::math::log2(align))) >> explored; + if(offset >= cut) + { + return find_used_twig(offset-cut, get_right(root), explored); + } else { + return find_used_twig(offset, get_left(root), explored); + } + } + + + static bool empty_node(const buddy* me, size_t node) { + gp_config::assertion(node < me->twig_explore_length, "bad emptyness test"); + auto p = me->get_twig(node); + return !( + p.used | p.used_children + ); + } public: buddy() : data(gp::buffer(nullptr,nullptr)) + , max_depth(0) + , twig_explore_length(1 << max_depth) {} buddy(size_t sz) : data(nullptr,nullptr) + , max_depth(gp::math::msb(sz)-gp::math::msb(align)) + , twig_explore_length(1 << max_depth) { if(sz!=0 && (sz & (sz - 1)) == 0) { auto v=allocator.allocate(sz); - if(v!=nullptr && (static_cast(v) % align) ==0) + if(v!=nullptr) { - data=gp::buffer(reinterpret_cast(v),reinterpret_cast(v)+sz); + if((reinterpret_cast(v) % align) ==0) + { + data=gp::buffer(reinterpret_cast(v),reinterpret_cast(v)+sz); + } + else + { + allocator.deallocate(v); + } } } } buddy(char* pos,size_t sz) : data(pos,pos+sz) + , max_depth(gp::math::msb(sz)-gp::math::msb(align)) + , twig_explore_length(1 << max_depth) { } @@ -179,33 +236,68 @@ namespace gp{ return nullptr; } + auto pot = reinterpret_cast( + (index - (1 << depth) + 1)*depth_to_size(depth) + + reinterpret_cast(&*data.begin()) + ); + + if(!data.contains(pot)) { + return nullptr; + } + all_over(index, [&](size_t idx){ auto t = get_twig(idx); t.used_children = true; set_twig(idx, t); }); + auto t = get_twig(index); t.used = true; set_twig(index, t); - return reinterpret_cast(index*depth_to_size(depth) - + reinterpret_cast(&*data.begin())); + return pot; + } + + constexpr bool try_reallocate(void*, size_t) { + return false; } bool deallocate(void* ptr) { if(data.contains((char*)ptr)) { + size_t integral_offset = reinterpret_cast(ptr) - reinterpret_cast(&*data.begin()); + auto index = find_used_twig(integral_offset); + if(index == no_twig) + { + return false; + } + twig v = get_twig(index); + v.used = false; + v.used_children = false; + set_twig(index, v); + all_over(index, [&](size_t idx){ + auto l = get_twig(get_left(idx)); + auto r = get_twig(get_right(idx)); + + set_twig(idx, 2*(l.used | l.used_children | r.used | r.used_children)); + + }); + return true; } return false; } + bool empty() const { + buddy* addr = (buddy*)this; + auto prepred = not_fn(&buddy::empty_node); + auto pred = bind_front(prepred, addr); + return empty_node(addr, 0) && !is_any_child(0, pred); + } + ~buddy() { - if constexpr(gp::has_allocator_interface::value) - { - allocator.deallocate(&data[0]); - } + allocator.deallocate(data.begin().data); } }; } \ No newline at end of file diff --git a/include/gp/allocator/dummy.hpp b/include/gp/allocator/dummy.hpp index 75680e7..8d2ff32 100644 --- a/include/gp/allocator/dummy.hpp +++ b/include/gp/allocator/dummy.hpp @@ -3,7 +3,7 @@ namespace gp { struct dummy_allocator{ - void* allocator(size_t) + void* allocate(size_t) { return nullptr; } diff --git a/include/gp/array.hpp b/include/gp/array.hpp index 4cc3bce..f0b1e19 100644 --- a/include/gp/array.hpp +++ b/include/gp/array.hpp @@ -10,7 +10,9 @@ namespace gp{ using associated_iterator = pointer_iterator; using associated_const_iterator = pointer_iterator; - array() = default; + array() + : ary() + {} template array(U&& ...v) @@ -18,6 +20,18 @@ namespace gp{ {} constexpr T& operator[] (size_t off) + { + if constexpr (gp_config::has_buffer_bounds) + { + gp_config::assertion( + off < sz, + "Array bounds infringed" + ); + } + return ary[off]; + } + + constexpr const T& operator[] (size_t off) const { return ary[off]; } diff --git a/include/gp/buffer.hpp b/include/gp/buffer.hpp index d6d328c..f6a1577 100644 --- a/include/gp/buffer.hpp +++ b/include/gp/buffer.hpp @@ -45,6 +45,13 @@ namespace gp{ constexpr T& operator[](std::size_t offset) { + if constexpr (gp_config::has_buffer_bounds) + { + gp_config::assertion( + offset < size(), + "Buffer bounds infringed" + ); + } return *(begin_elem+offset); } diff --git a/include/gp/integer_math.hpp b/include/gp/integer_math.hpp index fd460de..a50e010 100644 --- a/include/gp/integer_math.hpp +++ b/include/gp/integer_math.hpp @@ -50,6 +50,8 @@ namespace gp { return MultiplyDeBruijnBitPosition[(uint64_t)(v * 0x03f6eaf2cd271461) >> 58]; } + static_assert(log2(7) == 2, "bad log2"); + static_assert(log2(8) == 3, "bad log2"); template constexpr size_t msb(word_t v); @@ -57,12 +59,17 @@ namespace gp { template<> constexpr size_t msb(uint32_t v) { - return log2(v); + auto l = log2(v); + return l + (((1 << l) ^ v) != 0); } template<> constexpr size_t msb(uint64_t v) { - return log2(v); + auto l = log2(v); + return l + (((1 << l) ^ v) != 0); } + + static_assert(msb(7) == 3, "bad log2"); + static_assert(msb(8) == 3, "bad log2"); } } diff --git a/include/gp_config.hpp b/include/gp_config.hpp index d832b4c..f5b7c0b 100644 --- a/include/gp_config.hpp +++ b/include/gp_config.hpp @@ -45,22 +45,26 @@ namespace gp_config{ } constexpr bool has_exceptions = true; + constexpr bool has_buffer_bounds = true; // Value of 8 is considered not cryptographically secure // Value of 12 offers a good compromise of performance and robustness // Value of 20 offers maximum robustness constexpr size_t arc4random_strength = 20; + struct assert_failure{ + assert_failure(const char* reason) + : what_str{reason} + {} + const char* what_str; + const char* what() {return what_str;} + }; + constexpr size_t assert_buffer_size = 0; - constexpr auto assert = [](bool, const char*) -> void{}; - /** Simple exit assert - constexpr auto assert = [](bool pred, const char*) -> void - { - if(pred) - return; - else - std::exit(-1);}; - */ + constexpr auto assertion = [](bool pred, const char* reason) -> void{ + if constexpr (has_exceptions) + if(!pred) throw assert_failure(reason); + }; } enum class gp_errorcodes : int { diff --git a/tests.cpp b/tests.cpp index 88a244f..e039d15 100644 --- a/tests.cpp +++ b/tests.cpp @@ -1,4 +1,5 @@ #include "test_scaffold.h" +#include "gp_config.hpp" #include "meta_test.cpp" #include "shared_fd.cpp" #include "rc6_generic.cpp" @@ -24,7 +25,10 @@ int main() /*} catch (gp::runtime_error err) { std::cout << test->name << " failed with an exception: " << err.what() << std::endl; value = -1; - //} catch (...) { + } catch (gp_config::assert_failure err) { + std::cout << test->name << " failed with an assertion failure: " << err.what() << std::endl; + value = -1; + } catch (...) { std::cout << test->name << " failed with an exception" << std::endl; value = -1; }*/ diff --git a/tests/bloomfilter.cpp b/tests/bloomfilter.cpp index 106ea25..0c8d289 100644 --- a/tests/bloomfilter.cpp +++ b/tests/bloomfilter.cpp @@ -3,9 +3,10 @@ #include #include "gp/bloomfilter.hpp" +#include "gp/algorithm/repeat.hpp" -typedef std::linear_congruential_engine cheap_rand; -typedef std::linear_congruential_engine cheap_rand_bis; +typedef std::mt19937_64 cheap_rand; +typedef std::mt19937_64 cheap_rand_bis; struct bfilter_test : public test_scaffold { uint32_t seed; @@ -57,21 +58,26 @@ struct bfilter2_test : public test_scaffold { cheap_rand setter(seedA); cheap_rand_bis getter(seedB); + int interference = 0; + auto cnt = 300; - gp::bloomfilter test_filter; + gp::repeat(cnt, [&](){ + gp::bloomfilter test_filter; - for(int a = 0 ; a < 10000; a++) - { - test_filter.set_hash(setter()); - } + for(int a = 0 ; a < 10000; a++) + { + test_filter.set_hash(setter()); + } - int interference = 0; - for(int a = 0 ; a < 10000; a++) - { - interference += test_filter.test_hash(getter()); - } + for(int a = 0 ; a < 10000; a++) + { + interference += test_filter.test_hash(getter()) == true; + } + }); + + float avg = interference / (float)cnt; - return interference >= 550; + return avg >= 210; } }; diff --git a/tests/gp_test.cpp b/tests/gp_test.cpp index 179a752..7d12655 100644 --- a/tests/gp_test.cpp +++ b/tests/gp_test.cpp @@ -2,13 +2,38 @@ #include "gp/array.hpp" #include "gp/allocator/buddy.hpp" #include "gp/allocator/dummy.hpp" +#include "gp/algorithm/repeat.hpp" #include #include #include +#include #include +#include +#include #include +#include #include +#include + +#define MACRO_STRGEN(X) #X +#define MACRO_STR(X) MACRO_STRGEN(X) +constexpr bool time_fuzzes = true; + +struct static_mapper { + static gp::array store; + static gp::buddy<> impl; + + void* allocate(size_t sz) { + return impl.allocate(sz); + } + + bool deallocate(void* ptr) { + return impl.deallocate(ptr); + } +}; +alignas(2048) gp::array static_mapper::store; +gp::buddy<> static_mapper::impl = gp::buddy<>{store.begin().data, store.size()}; struct arraysum_test : public test_scaffold { arraysum_test() { @@ -83,31 +108,227 @@ append_test dummy_mlyusisd3(new optional_test{}); struct buddy_test : public test_scaffold { buddy_test() { - name = __FILE__ ":3"; + 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{}); + + - gp::array store; + +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::buddy(4096)> bud{&*store.begin(), store.size()}; - std::set ptr_set; - for(int i = 0; i < 4096 / 8; i++) + 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) { - void* v = bud.allocate(8); - if(v == nullptr) throw gp::runtime_error("allocation failed"); - ptr_set.insert(v); + free(ptr); } - std::cout << ptr_set.size() << "//" << ptr_set.count(nullptr); - if(ptr_set.count(nullptr)!=0 || ptr_set.size()!=(4096/8)) throw gp::runtime_error("some allocations failed"); - res++; - res += nullptr == bud.allocate(8); - if(nullptr != bud.allocate(8)) throw gp::runtime_error("allocation succeeded, failure was expected"); - ++res; } + auto reference = std::chrono::steady_clock::now() - start; + + + 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_654sisd3(new buddy_test{}); \ No newline at end of file +append_test dummy_df987sd3(new buddy_fuzz_test{781017366}); +append_test dummy_df4sisd3(new buddy_fuzz_test{}); \ No newline at end of file diff --git a/tests/quotient_filter.cpp b/tests/quotient_filter.cpp index 9661de2..d816b13 100644 --- a/tests/quotient_filter.cpp +++ b/tests/quotient_filter.cpp @@ -4,8 +4,8 @@ #include "gp/quotient_filter.hpp" -typedef std::linear_congruential_engine cheap_rand; -typedef std::linear_congruential_engine cheap_rand_bis; +typedef std::mt19937_64 cheap_rand; +typedef std::mt19937_64 cheap_rand_bis; struct qfilter_test : public test_scaffold { uint32_t seed; diff --git a/tests/shared_fd.cpp b/tests/shared_fd.cpp index 7552edd..f7134c6 100644 --- a/tests/shared_fd.cpp +++ b/tests/shared_fd.cpp @@ -129,7 +129,7 @@ struct rw_err_test : public test_scaffold { append_test dummy_l6987erd3(new rw_err_test{}); - +/* struct make_address_test : public test_scaffold { make_address_test() { name = __FILE__ ":6"; @@ -282,4 +282,5 @@ struct sockets_co_test : public test_scaffold { } }; -// append_test dummy_polmdf43(new sockets_co_test{}); \ No newline at end of file +// append_test dummy_polmdf43(new sockets_co_test{}); +*/ \ No newline at end of file diff --git a/tests/test_scaffold.h b/tests/test_scaffold.h index db8a64e..bf855c5 100644 --- a/tests/test_scaffold.h +++ b/tests/test_scaffold.h @@ -3,6 +3,10 @@ #include #include +#ifndef FUZZ_STRENGTH +#define FUZZ_STRENGTH = 2048; +#endif + struct test_scaffold{ std::string name; virtual int run() = 0;