Browse Source

Buddy allocator works

devel
Ludovic 'Archivist' Lagouardette 4 years ago
parent
commit
968dcfb0ad
16 changed files with 663 additions and 82 deletions
  1. +2
    -2
      Makefile
  2. +8
    -0
      include/gp/algorithm/min_max.hpp
  3. +204
    -0
      include/gp/algorithm/modifiers.hpp
  4. +9
    -0
      include/gp/algorithm/repeat.hpp
  5. +126
    -34
      include/gp/allocator/buddy.hpp
  6. +1
    -1
      include/gp/allocator/dummy.hpp
  7. +15
    -1
      include/gp/array.hpp
  8. +7
    -0
      include/gp/buffer.hpp
  9. +9
    -2
      include/gp/integer_math.hpp
  10. +13
    -9
      include/gp_config.hpp
  11. +5
    -1
      tests.cpp
  12. +19
    -13
      tests/bloomfilter.cpp
  13. +236
    -15
      tests/gp_test.cpp
  14. +2
    -2
      tests/quotient_filter.cpp
  15. +3
    -2
      tests/shared_fd.cpp
  16. +4
    -0
      tests/test_scaffold.h

+ 2
- 2
Makefile View File

@ -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

+ 8
- 0
include/gp/algorithm/min_max.hpp View File

@ -26,4 +26,12 @@ namespace gp{
return min(first < second ? first : second, args...);
}
}
template<typename T>
constexpr T clamp(T first, T value, T last)
{
if(value < first) return first;
if(value > last) return last;
return value;
}
}

+ 204
- 0
include/gp/algorithm/modifiers.hpp View File

@ -0,0 +1,204 @@
#pragma once
#include <type_traits>
#include "gp/algorithm/move.hpp"
namespace gp {
template<typename F, typename ... Args>
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 <class>
constexpr bool is_reference_wrapper_v = false;
template <class U>
constexpr bool is_reference_wrapper_v<std::reference_wrapper<U>> = true;
template <class T, class Type, class T1, class... Args>
constexpr decltype(auto) INVOKE(Type T::* f, T1&& t1, Args&&... args)
{
if constexpr (std::is_member_function_pointer_v<decltype(f)>) {
if constexpr (std::is_base_of_v<T, std::decay_t<T1>>)
return (gp::forward<T1>(t1).*f)(gp::forward<Args>(args)...);
else if constexpr (is_reference_wrapper_v<std::decay_t<T1>>)
return (t1.get().*f)(gp::forward<Args>(args)...);
else
return ((*gp::forward<T1>(t1)).*f)(gp::forward<Args>(args)...);
} else {
static_assert(std::is_member_object_pointer_v<decltype(f)>);
static_assert(sizeof...(args) == 0);
if constexpr (std::is_base_of_v<T, std::decay_t<T1>>)
return gp::forward<T1>(t1).*f;
else if constexpr (is_reference_wrapper_v<std::decay_t<T1>>)
return t1.get().*f;
else
return (*gp::forward<T1>(t1)).*f;
}
}
template <class F, class... Args>
constexpr decltype(auto) INVOKE(F&& f, Args&&... args)
{
return gp::forward<F>(f)(gp::forward<Args>(args)...);
}
} // namespace detail
template< class F, class... Args>
constexpr std::invoke_result_t<F, Args...> invoke(F&& f, Args&&... args)
noexcept(std::is_nothrow_invocable_v<F, Args...>)
{
return detail::INVOKE(gp::forward<F>(f), gp::forward<Args>(args)...);
}
template< class T >
struct remove_cvref {
typedef std::remove_cv_t<std::remove_reference_t<T>> type;
};
template< class T >
using remove_cvref_t = typename remove_cvref<T>::type;
template< class T >
T* addressof(T& arg)
{
return reinterpret_cast<T*>(
&const_cast<char&>(
reinterpret_cast<const volatile char&>(arg)));
}
namespace detail {
template <class T> constexpr T& FUN(T& t) noexcept { return t; }
template <class T> void FUN(T&&) = delete;
}
template <class T>
class reference_wrapper {
public:
// types
typedef T type;
// construct/copy/destroy
template <class U, class = decltype(
detail::FUN<T>(std::declval<U>()),
std::enable_if_t<!std::is_same_v<reference_wrapper, gp::remove_cvref_t<U>>>()
)>
constexpr reference_wrapper(U&& u) noexcept(noexcept(detail::FUN<T>(gp::forward<U>(u))))
: _ptr(gp::addressof(detail::FUN<T>(gp::forward<U>(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<T&, ArgTypes...>
operator() ( ArgTypes&&... args ) const {
return gp::invoke(get(), gp::forward<ArgTypes>(args)...);
}
private:
T* _ptr;
};
// deduction guides
template<class T>
reference_wrapper(T&) -> reference_wrapper<T>;
template<typename T>
auto ref(T& vref) {
return reference_wrapper<T>(vref);
}
template <class _DecayFunc>
class __not_fn_imp {
_DecayFunc __fd;
public:
__not_fn_imp() = delete;
template <class ..._Args>
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 <class ..._Args>
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 <class ..._Args>
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 <class ..._Args>
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 <class _RawFunc,
class = std::enable_if_t<!std::is_same<std::decay_t<_RawFunc>, __not_fn_imp>::value>>
explicit __not_fn_imp(_RawFunc&& __rf)
: __fd(gp::forward<_RawFunc>(__rf)) {}
template <class _RawFunc>
friend inline __not_fn_imp<std::decay_t<_RawFunc>> not_fn(_RawFunc&&);
};
template <class _RawFunc>
inline __not_fn_imp<std::decay_t<_RawFunc>> not_fn(_RawFunc&& __fn) {
return __not_fn_imp<std::decay_t<_RawFunc>>(gp::forward<_RawFunc>(__fn));
}
}

+ 9
- 0
include/gp/algorithm/repeat.hpp View File

@ -0,0 +1,9 @@
#pragma once
#include <stddef.h>
namespace gp{
template <typename F>
void repeat(size_t n, F f) {
while (n--) f();
}
}

+ 126
- 34
include/gp/allocator/buddy.hpp View File

@ -5,11 +5,12 @@
#include "gp/integer_math.hpp"
#include <type_traits>
#include <gp/algorithm/tmp_manip.hpp>
#include <gp/algorithm/modifiers.hpp>
#include <gp/allocator/dummy.hpp>
namespace gp{
template<typename page_allocator, size_t max_msb = 24, size_t align = 8>
template<typename page_allocator = gp::dummy_allocator, size_t max_msb = 24, size_t align = 8>
class buddy{
struct twig {
bool used : 1;
@ -20,7 +21,7 @@ namespace gp{
}
operator uint8_t() {
return used + 2 * used_children;
return mi">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<char> 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<bundle, span_size> stack;
twig get_twig(size_t idx) {
twig get_twig(size_t idx) k">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) {
k">constexpr size_t get_left(size_t index) const {
return ((index + 1) << 1) - 1;
}
size_t get_right(size_t index) {
k">constexpr size_t get_right(size_t index) const {
return ((index + 1) << 1);
}
@ -104,28 +114,32 @@ namespace gp{
template<typename function>
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<typename function>
bool is_any_child(size_t index, function func) {
bool is_any_child(size_t index, function func) k">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<char>(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<intptr_t>(v) % align) ==0)
if(v!=nullptr)
{
data=gp::buffer<char>(reinterpret_cast<char*>(v),reinterpret_cast<char*>(v)+sz);
if((reinterpret_cast<intptr_t>(v) % align) ==0)
{
data=gp::buffer<char>(reinterpret_cast<char*>(v),reinterpret_cast<char*>(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<char*>(
(index - (1 << depth) + 1)*depth_to_size(depth)
+ reinterpret_cast<intptr_t>(&*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<void*>(index*depth_to_size(depth)
+ reinterpret_cast<intptr_t>(&*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<intptr_t>(ptr) - reinterpret_cast<intptr_t>(&*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<page_allocator>::value)
{
allocator.deallocate(&data[0]);
}
allocator.deallocate(data.begin().data);
}
};
}

+ 1
- 1
include/gp/allocator/dummy.hpp View File

@ -3,7 +3,7 @@
namespace gp {
struct dummy_allocator{
void* allocator(size_t)
void* allocate(size_t)
{
return nullptr;
}

+ 15
- 1
include/gp/array.hpp View File

@ -10,7 +10,9 @@ namespace gp{
using associated_iterator = pointer_iterator<T>;
using associated_const_iterator = pointer_iterator<T>;
array() = default;
array()
: ary()
{}
template<typename ...U>
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];
}

+ 7
- 0
include/gp/buffer.hpp View File

@ -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);
}

+ 9
- 2
include/gp/integer_math.hpp View File

@ -50,6 +50,8 @@ namespace gp {
return MultiplyDeBruijnBitPosition[(uint64_t)(v * 0x03f6eaf2cd271461) >> 58];
}
static_assert(log2<uint32_t>(7) == 2, "bad log2");
static_assert(log2<uint32_t>(8) == 3, "bad log2");
template<typename word_t>
constexpr size_t msb(word_t v);
@ -57,12 +59,17 @@ namespace gp {
template<>
constexpr size_t msb<uint32_t>(uint32_t v)
{
return log2(v);
auto l = log2(v);
return l + (((1 << l) ^ v) != 0);
}
template<>
constexpr size_t msb<uint64_t>(uint64_t v)
{
return log2(v);
auto l = log2(v);
return l + (((1 << l) ^ v) != 0);
}
static_assert(msb<uint32_t>(7) == 3, "bad log2");
static_assert(msb<uint32_t>(8) == 3, "bad log2");
}
}

+ 13
- 9
include/gp_config.hpp View File

@ -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 {

+ 5
- 1
tests.cpp View File

@ -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;
}*/

+ 19
- 13
tests/bloomfilter.cpp View File

@ -3,9 +3,10 @@
#include <string>
#include "gp/bloomfilter.hpp"
#include "gp/algorithm/repeat.hpp"
typedef std::linear_congruential_engine<uint_fast64_t, 128273, 13, 2147483647> cheap_rand;
typedef std::linear_congruential_engine<uint_fast64_t, 69857, 87541, 2147483647> 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;
}
};

+ 236
- 15
tests/gp_test.cpp View File

@ -2,13 +2,38 @@
#include "gp/array.hpp"
#include "gp/allocator/buddy.hpp"
#include "gp/allocator/dummy.hpp"
#include "gp/algorithm/repeat.hpp"
#include <thread>
#include <chrono>
#include <set>
#include <stack>
#include <numeric>
#include <chrono>
#include <random>
#include <iomanip>
#include <iostream>
#include <fstream>
#include <algorithm>
#define MACRO_STRGEN(X) #X
#define MACRO_STR(X) MACRO_STRGEN(X)
constexpr bool time_fuzzes = true;
struct static_mapper {
static gp::array<char, 4096> 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<char, 4096> 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<char, 4096> store;
{
gp::buddy<gp::dummy_allocator, gp::math::msb<uint64_t>(4096)> bud{&*store.begin(), store.size()};
gp::buddy<gp::dummy_allocator, gp::math::msb<uint64_t>(4096)> dum_bud{store.size()};
gp::buddy<static_mapper> 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<void*> 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<void*> 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<void*> 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<void*> ptr_set;
std::vector<size_t> infill;
std::insert_iterator< std::vector<size_t> > inserter{infill, std::end(infill)};
std::fill_n(inserter, 4096 / 16 / 4, 16);
inserter = std::insert_iterator< std::vector<size_t> >{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<char, 4096> 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<char, 4096> store;
gp::buddy<gp::dummy_allocator, gp::math::msb<uint64_t>(4096)> bud{&*store.begin(), store.size()};
std::vector<void*> ptr_set;
auto get_random_mem_qt = [&]() -> size_t {
return 1+rng()%(store.size()-1);
};
auto start = std::chrono::steady_clock::now();
{
gp::buddy<gp::dummy_allocator, gp::math::msb<uint64_t>(4096)> bud{&*store.begin(), store.size()};
std::set<void*> 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<std::chrono::microseconds>(duration).count()
<< "µs for "
<< FUZZ_STRENGTH
<< " (reference: "
<< std::chrono::duration_cast<std::chrono::microseconds>(reference).count()
<< "µs)"
<< std::endl;
return res;
}
};
append_test dummy_654sisd3(new buddy_test{});
append_test dummy_df987sd3(new buddy_fuzz_test{781017366});
append_test dummy_df4sisd3(new buddy_fuzz_test{});

+ 2
- 2
tests/quotient_filter.cpp View File

@ -4,8 +4,8 @@
#include "gp/quotient_filter.hpp"
typedef std::linear_congruential_engine<uint_fast64_t, 128273, 13, 2147483647> cheap_rand;
typedef std::linear_congruential_engine<uint_fast64_t, 69857, 87541, 2147483647> 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;

+ 3
- 2
tests/shared_fd.cpp View File

@ -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{});
// append_test dummy_polmdf43(new sockets_co_test{});
*/

+ 4
- 0
tests/test_scaffold.h View File

@ -3,6 +3,10 @@
#include <vector>
#include <memory>
#ifndef FUZZ_STRENGTH
#define FUZZ_STRENGTH = 2048;
#endif
struct test_scaffold{
std::string name;
virtual int run() = 0;

Loading…
Cancel
Save