diff --git a/Makefile b/Makefile index 29d54de..26a90d9 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,13 @@ CXX= clang++-8 -CXXFLAGS= --std=c++17 -g -fprofile-instr-generate -fcoverage-mapping +CXXFLAGS= --std=c++17 -g -fprofile-instr-generate -fcoverage-mapping -pthread all: tests tests: bin/tests LLVM_PROFILE_FILE="./bin/tests.profraw" ./bin/tests @llvm-profdata merge -sparse ./bin/tests.profraw -o ./bin/tests.profdata - @llvm-cov report ./bin/tests -instr-profile=./bin/tests.profdata include/*.hpp - @llvm-cov report ./bin/tests -instr-profile=./bin/tests.profdata include/*.hpp | tail -n 1 | tr -s " " | sed -e 's/ /,/g' -- | awk -F "," '{print $$9}' | sed -e 's/^/Untested lines: /g' + @llvm-cov report ./bin/tests -instr-profile=./bin/tests.profdata include/*.hpp include/gp/*.hpp include/gp/algorithm/*.hpp + @llvm-cov report ./bin/tests -instr-profile=./bin/tests.profdata include/*.hpp include/gp/*.hpp include/gp/algorithm/*.hpp | tail -n 1 | tr -s " " | sed -e 's/ /,/g' -- | awk -F "," '{print $$9}' | sed -e 's/^/Untested lines: /g' bin/tests: tests.cpp $(wildcard tests/*.cpp) $(wildcard include/*.hpp) ./tests/test_scaffold.h @mkdir -p $(@D) diff --git a/include/gp/algorithm/min_max.hpp b/include/gp/algorithm/min_max.hpp new file mode 100644 index 0000000..483ed05 --- /dev/null +++ b/include/gp/algorithm/min_max.hpp @@ -0,0 +1,29 @@ +#pragma once + +namespace gp{ + template + constexpr T max(T first, U second, rest... args) + { + if constexpr (sizeof...(args) == 0) + { + return first > second ? first : second; + } + else + { + return max(first > second ? first : second, args...); + } + } + + template + constexpr T min(T first, U second, rest... args) + { + if constexpr (sizeof...(args) == 0) + { + return first < second ? first : second; + } + else + { + return min(first < second ? first : second, args...); + } + } +} \ No newline at end of file diff --git a/include/gp/algorithm/move.hpp b/include/gp/algorithm/move.hpp new file mode 100644 index 0000000..bf84193 --- /dev/null +++ b/include/gp/algorithm/move.hpp @@ -0,0 +1,64 @@ +#pragma once +#include "gp/algorithm/tmp_manip.hpp" +#include "gp/range.hpp" + +namespace gp{ + template + typename gp::remove_reference::type&& move(T&& value) + { + return (typename gp::remove_reference::type&&)value; + } + + template + constexpr T&& forward(typename gp::remove_reference::type& t) noexcept + { + return static_cast(t); + } + template + constexpr T&& forward(typename gp::remove_reference::type&& t) noexcept + { + static_assert(!std::is_lvalue_reference_v,"bad forward of rvalue as lvalue"); + return static_cast(t); + } + + template + constexpr void swap( + T& lhs, + T& rhs + ) + { + auto tmp = lhs; + lhs = rhs; + rhs = tmp; + } + + template + nameless_range move(range_in src, range_out dest) + { + if(src.size()>dest.size()) + return nameless_range(dest.begin(), dest.end()); + auto in = src.begin(); + auto in_close = src.end(); + auto out = dest.begin(); + while(in != in_close) + { + *(out++) = gp::move(*(in++)); + } + return nameless_range{out, dest.end()}; + } + + template + nameless_range move_uninitialized(range_in src, range_out dest) + { + if(src.size()>dest.size()) + return nameless_range(dest.begin(), dest.end()); + auto in = src.begin(); + auto in_close = src.end(); + auto out = dest.begin(); + while(in != in_close) + { + new(&*(out++)) T{gp::move(*(in++))}; + } + return nameless_range{out, dest.end()}; + } +} \ No newline at end of file diff --git a/include/gp/algorithm/tmp_manip.hpp b/include/gp/algorithm/tmp_manip.hpp new file mode 100644 index 0000000..80af38f --- /dev/null +++ b/include/gp/algorithm/tmp_manip.hpp @@ -0,0 +1,234 @@ +#pragma once +#include +#include +#include +#include +#include "gp/algorithm/min_max.hpp" + + +namespace gp{ + template + struct either + { + }; + + template + struct either + { + typedef T type; + }; + + template + struct either + { + typedef U type; + }; + + template + struct constexpr_all_of + { + static constexpr bool value = first && constexpr_all_of::value; + }; + + template + struct constexpr_all_of + { + static constexpr bool value = first; + }; + + template + struct constexpr_any_of + { + static constexpr bool value = first || constexpr_any_of::value; + }; + + template + struct constexpr_any_of + { + static constexpr bool value = first; + }; + + template + constexpr bool is_fixed_size() + { + return std::is_final::value + || std::is_fundamental::value; + } + + template + struct all_of_fixed_size + { + static constexpr bool value = is_fixed_size() && all_of_fixed_size::value; + }; + + template + struct all_of_fixed_size + { + static constexpr bool value = is_fixed_size(); + }; + + template + struct list_contains_class + { + static constexpr bool value = ( + std::is_same::value + ) || list_contains_class::value; + }; + + template + struct list_contains_class + { + static constexpr bool value = std::is_same::value; + }; + + template + struct r_index_of + { + static constexpr std::size_t value = std::is_same::value ? sizeof...(rest) : r_index_of::value; + }; + + template + struct r_index_of + { + static constexpr std::size_t value = std::is_same::value ? 0 : std::numeric_limits::max(); + }; + + template + struct r_index_at + { + using type = typename either::type>::type; + }; + + template + constexpr std::size_t max_size() + { + if constexpr (sizeof...(rest) == 0) + { + return gp::max(sizeof(T),sizeof(U)); + } + else + { + return max_size< + either< + ( sizeof(T) > sizeof(U) ), + T, + U + >::type, + rest...>(); + } + } + + template + struct remove_reference + { + using type = T; + }; + + template + struct remove_reference + { + using type = T; + }; + + template + struct remove_reference + { + using type = T; + }; + + template + struct has_size_interface + { + private: + typedef std::true_type yes; + typedef std::false_type no; + + template struct SFINAE{}; + + + template static yes test(SFINAE*); + template static no test(...); + + public: + static constexpr bool value = std::is_same(nullptr))>::value; + }; + + template + struct has_begin_interface + { + private: + typedef std::true_type yes; + typedef std::false_type no; + + template struct SFINAE{}; + + + template static yes test(SFINAE*); + template static no test(...); + + public: + static constexpr bool value = std::is_same(nullptr))>::value; + }; + + template + struct has_end_interface + { + private: + typedef std::true_type yes; + typedef std::false_type no; + + template struct SFINAE{}; + + + template static yes test(SFINAE*); + template static no test(...); + + public: + static constexpr bool value = std::is_same(nullptr))>::value; + }; + + template + using has_range_interface = constexpr_all_of::value,has_end_interface::value>; + + template + using has_measurable_range_interface = constexpr_all_of::value,has_size_interface::value>; + + + + template + struct has_allocate_interface + { + private: + typedef std::true_type yes; + typedef std::false_type no; + + template struct SFINAE{}; + + + template static yes test(SFINAE*); + template static no test(...); + + public: + static constexpr bool value = std::is_same(nullptr))>::value; + }; + + template + struct has_deallocate_interface + { + private: + typedef std::true_type yes; + typedef std::false_type no; + + template struct SFINAE{}; + + + template static yes test(SFINAE*); + template static no test(...); + + public: + static constexpr bool value = std::is_same(nullptr))>::value; + }; + + template + using has_allocator_interface = constexpr_all_of::value,has_deallocate_interface::value>; +} \ No newline at end of file diff --git a/include/gp/arena.hpp b/include/gp/arena.hpp new file mode 100644 index 0000000..9133441 --- /dev/null +++ b/include/gp/arena.hpp @@ -0,0 +1,95 @@ +#pragma once +#include "gp_config.hpp" +#include "gp/buffer.hpp" +#include +#include + + + +namespace gp{ + template + class arena{ + page_allocator allocator; + fel::buffer data; + size_t last; + size_t count; + public: + arena() + :last(0) + ,count(0) + ,data(fel::buffer(nullptr,nullptr)) + {} + + template::value,int>::type> + arena(size_t sz) + :last(0) + ,count(0) + ,data(nullptr,nullptr) + { + if(sz!=0) + { + auto v=allocator.allocate(sz); + if(v!=nullptr) + { + data=fel::buffer(reinterpret_cast(v),reinterpret_cast(v)+sz); + } + } + } + + arena(char* pos,size_t sz) + :last(0) + ,count(0) + ,data(pos,pos+sz) + { + } + + void* allocate(size_t sz) + { + [[maybe_unused]] + size_t mod = 0; + + if constexpr (align != 1) + { + mod = align - ((intptr_t)data.begin())%align; + } + + auto ret=data.begin()+last+mod; + if(data.contains(ret)) + { + count++; + last+=sz+mod; + return &*(ret); + } + else + { + return nullptr; + } + } + + bool deallocate(void* ptr) + { + if(data.contains((char*)ptr)) + { + count--; + if(count==0) + { + for(auto& i : data) + { + i=0; + } + last=0; + } + return true; + } + return false; + } + + ~arena() + { + if constexpr(fel::has_allocator_interface::value) + { + allocator.deallocate(&data[0]); + } + } + }; +} \ No newline at end of file diff --git a/include/gp/array.hpp b/include/gp/array.hpp new file mode 100644 index 0000000..43c15f9 --- /dev/null +++ b/include/gp/array.hpp @@ -0,0 +1,48 @@ +#pragma once +#include + +namespace gp{ + template + class array{ + T ary[sz]; + public: + using associated_iterator = typename buffer::associated_iterator; + + constexpr T& operator[] (size_t off) + { + return ary[off]; + } + + constexpr size_t size() const + { + return sz; + } + + constexpr associated_iterator begin() const + { + return gp::buffer{(T*)ary, (T*)ary+sz}.begin(); + } + + constexpr associated_iterator end() const + { + return gp::buffer{(T*)ary, (T*)ary+sz}.end(); + } + + constexpr bool operator==(const array& oth) const + { + for(size_t idx = 0; idx as_buffer() + { + return gp::buffer{(T*)ary, (T*)ary+sz}; + } + }; +} \ No newline at end of file diff --git a/include/gp/bloomfilter.hpp b/include/gp/bloomfilter.hpp new file mode 100644 index 0000000..1dc3460 --- /dev/null +++ b/include/gp/bloomfilter.hpp @@ -0,0 +1,49 @@ +#pragma once +#include +#include +#include +#include + + +namespace gp { + template + class bloomfilter { + constexpr static size_t phys_size = (1 << magnitude) / 32; + gp::array< + typename gp::either< threading, + std::atomic_uint32_t, + uint32_t + >::type, + phys_size + > data; + + template + static void set_bit(T* value, const int v_pos) { + *value |= (1 << v_pos); + } + + template + static bool get_bit(T* value, const int v_pos) { + return (*value >> v_pos) & 1; + } + + public: + void set_hash(hash_type v) + { + const size_t modulo = v & ((1 << magnitude)-1); + const size_t p_pos = modulo / 32; + const size_t v_pos = modulo % 32; + + set_bit(&data[p_pos], v_pos); + } + + bool test_hash(hash_type v) + { + const size_t modulo = v & ((1 << magnitude)-1); + const size_t p_pos = modulo / 32; + const size_t v_pos = modulo % 32; + + return get_bit(&data[p_pos], v_pos); + } + }; +} \ No newline at end of file diff --git a/include/gp/buffer.hpp b/include/gp/buffer.hpp new file mode 100644 index 0000000..6c8345f --- /dev/null +++ b/include/gp/buffer.hpp @@ -0,0 +1,205 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +namespace gp{ + + template + class buffer final{ + public: + struct buffer_iterator final + { + T* data; + typedef T value_type; + typedef std::size_t difference_type; + static constexpr iterator_type_t iterator_type = iterator_type_t::contiguous_iterator; + + constexpr buffer_iterator(const buffer_iterator& oth) + : data{oth.data} + {} + + constexpr buffer_iterator(T* ptr) + : data{ptr} + {} + + constexpr operator T&() + { + return *data; + } + + constexpr T& operator*(){ + return *data; + } + + constexpr buffer_iterator operator++() + { + return buffer_iterator{++data}; + } + + constexpr buffer_iterator operator++(int) + { + return buffer_iterator{data++}; + } + + constexpr buffer_iterator operator--() + { + return buffer_iterator{--data}; + } + + constexpr buffer_iterator operator--(int) + { + return buffer_iterator{data--}; + } + + constexpr buffer_iterator operator+(const std::size_t offset) + { + return buffer_iterator{data+offset}; + } + + constexpr buffer_iterator operator+(const int offset) + { + return buffer_iterator{data+offset}; + } + + constexpr buffer_iterator operator-(const std::size_t offset) + { + return buffer_iterator{data-offset}; + } + + constexpr buffer_iterator operator-(const int offset) + { + return buffer_iterator{data-offset}; + } + + constexpr difference_type operator-(const buffer_iterator& oth) const + { + return (T*)data-(T*)oth.data; + } + + constexpr bool operator==(const buffer_iterator& oth) + { + return data==oth.data; + } + + constexpr bool operator!=(buffer_iterator& oth) + { + return data!=oth.data; + } + + constexpr bool before_or_equal(const buffer_iterator& oth) + { + return reinterpret_cast(data) <= reinterpret_cast(oth.data); + } + + constexpr bool operator<=(const buffer_iterator& oth) + { + return before_or_equal(oth); + } + }; + private: + buffer_iterator begin_elem; + buffer_iterator end_elem; + public: + using associated_iterator = buffer_iterator; + + constexpr buffer(T* beg_ptr, T* end_ptr) + : begin_elem{beg_ptr} + , end_elem{end_ptr} + {} + + constexpr buffer(T* beg_ptr, std::size_t sz) + : begin_elem{beg_ptr} + , end_elem{beg_ptr+sz} + {} + + constexpr typename buffer_iterator::difference_type size() const + { + return end_elem - begin_elem; + } + + constexpr associated_iterator begin() const + { + return begin_elem; + } + + constexpr associated_iterator end() const + { + return end_elem; + } + + constexpr T& operator[](std::size_t offset) + { + return *(begin_elem+offset); + } + + optional at(std::size_t offset) + { + auto elem = begin()+offset; + if(!contains(elem)) + { + return nullopt; + } + return elem; + } + + constexpr bool contains(buffer_iterator ptr) + { + return + ptr.data < end_elem.data + && ptr.data >= begin_elem.data; + } + + template + buffer cast() + { + if constexpr(sizeof(T)%sizeof(U) == 0) + { + return buffer(reinterpret_cast(&*begin_elem), size()*(sizeof(T)/sizeof(U))); + } + else + { + if(size()*sizeof(T)/sizeof(U)) + { + return buffer(reinterpret_cast(&*begin_elem), size()*sizeof(T)/sizeof(U)); + } + else if constexpr (gp_config::has_exceptions) + { + throw bad_buffer_cast{}; + } + else + { + return buffer(reinterpret_cast(nullptr), 0); + } + } + } + + buffer slice_start(size_t new_sz) + { + if(new_sz<=size()) + { + return buffer{&*begin(), &*(begin()+new_sz)}; + } + else + { + return buffer{(T*)nullptr,(size_t)0}; + } + } + + buffer slice_end(size_t new_sz) + { + if(new_sz<=size()) + { + return buffer{&*(end()-(1+new_sz)), &*end()}; + } + else + { + return buffer{(T*)nullptr,(size_t)0}; + } + } + }; +} \ No newline at end of file diff --git a/include/gp/exception.hpp b/include/gp/exception.hpp new file mode 100644 index 0000000..e571b52 --- /dev/null +++ b/include/gp/exception.hpp @@ -0,0 +1,117 @@ +#pragma once +#include "gp_config.hpp" +#include + +namespace gp{ + class runtime_error{ + protected: + const char* what_str; + public: + runtime_error(const char* what) + : what_str{what} + {} + + runtime_error() + { + what_str = "an unknown error has occured"; + } + + const char* what() + { + return what_str; + } + }; + + class out_of_range : public runtime_error{ + public: + out_of_range(const char* what) + : runtime_error{what} + {} + + out_of_range() + : runtime_error{} + { + what_str = "out_of_range error has occured"; + } + }; + + class bad_optional : public runtime_error{ + public: + bad_optional(const char* what) + : runtime_error{what} + {} + + bad_optional() + : runtime_error{} + { + what_str = "bad_optional error has occured"; + } + }; + + template + class bad_buffer_cast : public runtime_error{ + public: + bad_buffer_cast(const char* what) + : runtime_error{what} + {} + + bad_buffer_cast() + : runtime_error{} + { + what_str = "bad_buffer_cast error has occured"; + } + }; + + template + class bad_variant_access : public runtime_error{ + public: + bad_variant_access(const char* what) + : runtime_error{what} + {} + + bad_variant_access() + : runtime_error{} + { + what_str = "bad_variant_access error has occured"; + } + }; + + class bad_hashmap_access : public runtime_error{ + public: + bad_hashmap_access(const char* what) + : runtime_error{what} + {} + + bad_hashmap_access() + : runtime_error{} + { + what_str = "bad_hashmap_access error has occured"; + } + }; + + class bad_alloc : public runtime_error{ + public: + bad_alloc(const char* what) + : runtime_error{what} + {} + + bad_alloc() + : runtime_error{} + { + what_str = "bad_alloc error has occured"; + } + }; + + class bad_functor : public runtime_error{ + public: + bad_functor(const char* what) + : runtime_error{what} + {} + + bad_functor() + : runtime_error{} + { + what_str = "bad_functor error has occured"; + } + }; +} \ No newline at end of file diff --git a/include/gp/function.hpp b/include/gp/function.hpp new file mode 100644 index 0000000..63beaeb --- /dev/null +++ b/include/gp/function.hpp @@ -0,0 +1,140 @@ +#pragma once +#include "gp/memory.hpp" +#include "gp/exception.hpp" + +namespace gp{ + + template + class function; + + template + class function{ + struct virtual_callable + { + virtual ~virtual_callable() = default; + virtual ret operator() (args...) = 0; + }; + + template + class callable : virtual_callable{ + fn internal_representation; + public: + callable(const fn& func) + : internal_representation{func} + {} + + virtual ~callable() override = default; + + ret operator() (args... arg_list) + { + return internal_representation(arg_list...); + } + }; + + enum state_t : uint8_t{ + INACTIVE = 0, + ACTIVE = 1, + NO_SOO = 0, + SOO = 2 + }; + + state_t state = 0; + union{ + virtual_callable* functor = nullptr; + char inplace[12]; + } self; + + public: + template + function& operator=(T& t) + { + if(state & ACTIVE) + { + if(state & SOO) + { + ((virtual_callable*)&self)->~virtual_callable(); + } + else + { + delete self.functor; + } + } + if constexpr (sizeof(callable) <= sizeof(self)) + { + new((void*)&self) callable(t); + state = ACTIVE | SOO; + } + else + { + self = new callable(t); + state = ACTIVE | NO_SOO; + } + } + + template + function(T t) + { + if constexpr (sizeof(callable) <= sizeof(self)) + { + new((void*)&self) callable(t); + state = ACTIVE | SOO; + } + else + { + self = new callable(t); + state = ACTIVE | NO_SOO; + } + } + + template + function(T& t) + { + if constexpr (sizeof(callable) <= sizeof(self)) + { + new((void*)&self) callable(t); + state = ACTIVE | SOO; + } + else + { + self = new callable(t); + state = ACTIVE | NO_SOO; + } + } + + ret operator()(args... arg_list) const { + if constexpr (gp_config::has_exceptions) + { + if(!(state & ACTIVE)) + { + throw bad_functor{}; + } + } + if(state & SOO) + { + return (*(virtual_callable*)&self)(arg_list...); + } + else + { + return (*(self.functor))(arg_list...); + } + + } + + ~function() + { + if(state & ACTIVE) + { + if(state & SOO) + { + ((virtual_callable*)&self)->~virtual_callable(); + } + else + { + delete self.functor; + } + } + + } + }; + +} \ No newline at end of file diff --git a/include/gp/iterator.hpp b/include/gp/iterator.hpp new file mode 100644 index 0000000..e37fca7 --- /dev/null +++ b/include/gp/iterator.hpp @@ -0,0 +1,7 @@ +#pragma once + +enum class iterator_type_t{ + contiguous_iterator, + non_contiguous_iterator, + lazy_iterator +}; \ No newline at end of file diff --git a/include/gp/memory.hpp b/include/gp/memory.hpp new file mode 100644 index 0000000..90220bd --- /dev/null +++ b/include/gp/memory.hpp @@ -0,0 +1,56 @@ +#pragma once +#include "gp_config.hpp" +#include "gp/exception.hpp" +#include + +namespace gp{ + template> + class default_memory_allocator + { + public: + using pointer_type = T*; + using reference_type = T&; + using const_pointer_type = const T*; + using const_reference_type = const T&; + + pointer_type allocate(size_t sz) + { + return reinterpret_cast (gp_config::memory_module::memory_allocator(sizeof(T) * sz)); + } + + void deallocate(pointer_type ptr) + { + gp_config::memory_module::memory_deallocator(ptr); + } + + template + pointer_type construct(pointer_type ptr, params... args) + { + new(ptr) T(args...); + return ptr; + } + + void destroy(pointer_type v) + { + v->~T(); + } + }; +} + +void* operator new(size_t sz) +{ + auto ptr = gp_config::memory_module::memory_allocator(sz); + if constexpr (gp_config::has_exceptions) + { + if(!ptr) + { + throw gp::bad_alloc{}; + } + } + return ptr; +} + +void operator delete (void* ptr) noexcept +{ + gp_config::memory_module::memory_deallocator(ptr); +} \ No newline at end of file diff --git a/include/gp/optional.hpp b/include/gp/optional.hpp new file mode 100644 index 0000000..6080629 --- /dev/null +++ b/include/gp/optional.hpp @@ -0,0 +1,100 @@ +#pragma once +#include +#include "gp_config.hpp" +#include "gp/exception.hpp" +#include "gp/memory.hpp" + +namespace gp{ + struct nullopt_t{}; + + constexpr nullopt_t nullopt; + + template::value || std::is_fundamental::value> + class optional; + + template + class optional{ + bool ready = false; + char buffer[sizeof(T)]; + public: + constexpr optional() + : ready{false} + {} + + constexpr optional(nullopt_t) + : ready{false} + {} + + constexpr optional(T& value) + : ready{true} + { + new(buffer) T(value); + } + + constexpr optional(T&& value) + : ready{true} + { + new(buffer) T(std::move(value)); + } + + constexpr bool has_value() + { + return ready; + } + + constexpr T& value() + { + if constexpr (gp_config::has_exceptions) + { + if(!ready) + { + throw bad_optional{}; + } + } + return *reinterpret_cast(buffer); + } + }; + + template + class optional{ + bool ready = false; + void* ptr; + public: + constexpr optional() + : ready{false} + {} + + constexpr optional(nullopt_t) + : ready{false} + {} + + constexpr optional(T& value) + : ready{true} + { + ptr = (void*)new T(value); + } + + constexpr optional(T&& value) + : ready{true} + { + ptr = (void*)new T(std::move(value)); + } + + constexpr bool has_value() + { + return ready; + } + + constexpr T& value() + { + if constexpr (gp_config::has_exceptions) + { + if(!ready) + { + throw bad_optional{}; + } + } + return *reinterpret_cast(ptr); + } + }; +} \ No newline at end of file diff --git a/include/gp/range.hpp b/include/gp/range.hpp new file mode 100644 index 0000000..e35d25b --- /dev/null +++ b/include/gp/range.hpp @@ -0,0 +1,65 @@ +#pragma once +#include "gp/algorithm/tmp_manip.hpp" + +namespace gp{ + template::value> + class nameless_range; + + template + class nameless_range + { + it _begin; + it _end; + + friend nameless_range; + public: + using associated_iterator = it; + + nameless_range() = delete; + + template + nameless_range(const nameless_range& oth) + : _begin(oth._begin) + , _end(oth._end) + {} + + nameless_range(it sbegin, it send) + : _begin(sbegin) + , _end(send) + {} + + it begin() const + { + return _begin; + } + + it end() const + { + return _end; + } + }; + + template + class nameless_range : public nameless_range + { + public: + nameless_range() = delete; + + template + nameless_range(const nameless_range& oth) + : nameless_range(oth._begin,oth._end) + {} + + nameless_range(it sbegin, it send) + : nameless_range(sbegin, send) + {} + + std::size_t size() const + { + return this->_end-this->_begin; + } + }; +} + + + diff --git a/include/gp/variant.hpp b/include/gp/variant.hpp new file mode 100644 index 0000000..e8ab05e --- /dev/null +++ b/include/gp/variant.hpp @@ -0,0 +1,182 @@ +#pragma once +#include +#include +#include +#include "gp_config.hpp" +#include "gp/exception.hpp" +#include "gp/memory.hpp" +#include "gp/function.hpp" + + +namespace gp{ + + template< typename Enable, typename ...T> + class variant; + + template + class variant::value,int>::type, T...>{ + std::size_t index = std::numeric_limits::max(); + char buffer[max_size]; + gp::function dtor = [](void*){}; + public: + template::value,int>::type> + constexpr variant(U& value) + : index{r_index_of::value} + { + new(buffer) U(value); + dtor = gp::function([](void* thing){((U*)thing)->~U();}); + } + + template::value,int>::type> + constexpr variant(U&& value) + : index{r_index_of::value} + { + new(buffer) U(std::move(value)); + dtor = gp::function([](void* thing){((U*)thing)->~U();}); + } + + + + template::value,int>::type> + void operator=(U& value) + { + if(index != std::numeric_limits::max()) + { + dtor((void*)buffer); + } + index = r_index_of::value; + new(buffer) U(value); + dtor = gp::function([](void* thing){((U*)thing)->~U();}); + } + + template::value,int>::type> + void operator=(U&& value) + { + if(index != std::numeric_limits::max()) + { + dtor((void*)buffer); + } + index = r_index_of::value; + new(buffer) U(std::move(value)); + dtor = gp::function([](void* thing){((U*)thing)->~U();}); + } + + ~variant() + { + if(index != std::numeric_limits::max()) + { + dtor((void*)buffer); + } + } + + template + constexpr U& value() + { + if constexpr (gp_config::has_exceptions) + { + if(r_index_of::value != index) + { + throw bad_variant_access{}; + } + } + return *reinterpret_cast(buffer); + } + + template + constexpr U& is_a() + { + if(r_index_of::value == index) + { + return true; + } + else + { + return false; + } + } + }; + + template + class variant>::type, T...>{ + std::size_t index = std::numeric_limits::max(); + void* ptr; + gp::function dtor = [](void*){}; + public: + template::value,int>::type> + constexpr variant(U& value) + : index{r_index_of::value} + { + ptr = (void*)new(default_memory_allocator<>{}.allocate(sizeof(U))) U(value); + dtor = gp::function([](void* thing){((U*)thing)->~U();}); + } + + template::value,int>::type> + constexpr variant(U&& value) + : index{r_index_of::value} + { + ptr = (void*)new(default_memory_allocator<>{}.allocate(sizeof(U))) U(std::move(value)); + dtor = gp::function([](void* thing){((U*)thing)->~U();}); + } + + template::value,int>::type> + void operator=(U& value) + { + if(index != std::numeric_limits::max()) + { + dtor(ptr); + default_memory_allocator<>{}.deallocate(ptr); + } + index = r_index_of::value; + ptr = (void*)new(default_memory_allocator<>{}.allocate(sizeof(U))) U(value); + dtor = gp::function([](void* thing){((U*)thing)->~U();}); + } + + template::value,int>::type> + void operator=(U&& value) + { + if(index != std::numeric_limits::max()) + { + dtor(ptr); + default_memory_allocator<>{}.deallocate(ptr); + } + index = r_index_of::value; + ptr = (void*)new(default_memory_allocator<>{}.allocate(sizeof(U))) U(std::move(value)); + dtor = gp::function([](void* thing){((U*)thing)->~U();}); + } + + ~variant() + { + if(index != std::numeric_limits::max()) + { + dtor(ptr); + default_memory_allocator<>{}.deallocate(ptr); + } + } + + template + constexpr U& value() + { + if constexpr (gp_config::has_exceptions) + { + if(r_index_of::value != index) + { + throw bad_variant_access{}; + } + } + return *reinterpret_cast(ptr); + } + + template + constexpr U& is_a() + { + if(r_index_of::value == index) + { + return true; + } + else + { + return false; + } + } + }; +} \ No newline at end of file diff --git a/include/gp_config.hpp b/include/gp_config.hpp new file mode 100644 index 0000000..ccc3589 --- /dev/null +++ b/include/gp_config.hpp @@ -0,0 +1,64 @@ +#pragma once +#include +#include +#include +#include + +namespace gp_config{ + namespace memory_module{ + enum class memory_mode_t{ + other, + clib, + buffer, + arena_buffer + }; + + constexpr memory_mode_t memory_mode = memory_mode_t::clib; + + + template + constexpr void*(*memory_allocator)(std::size_t)=nullptr; + template + constexpr void(*memory_deallocator)(void*)=nullptr; + + // C Standard library memory usage + template<> + constexpr void*(*memory_allocator)(std::size_t) = malloc; + template<> + constexpr void(*memory_deallocator)(void*) = free; + + // Buffer memory usage only + template<> + constexpr void*(*memory_allocator)(std::size_t) = nullptr; + template<> + constexpr void(*memory_deallocator)(void*) = nullptr; + + // Buffer of arena memory usage + template<> + constexpr void*(*memory_allocator)(std::size_t) = nullptr; + template<> + constexpr void(*memory_deallocator)(void*) = nullptr; + + constexpr bool is_ok = + ( memory_allocator != nullptr ) + && ( memory_deallocator != nullptr ); + } + + constexpr bool has_exceptions = 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; + + 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);}; + */ +} \ No newline at end of file diff --git a/include/rc6_generic.hpp b/include/rc6_generic.hpp new file mode 100644 index 0000000..21b5dcf --- /dev/null +++ b/include/rc6_generic.hpp @@ -0,0 +1,132 @@ +#pragma once +#include +#include +#include +#include + +template +size_t lg(word_t v); + + +/** +Sean Eron Anderson +seander@cs.stanford.edu +**/ +template<> +size_t lg(uint32_t v) +{ + static const int MultiplyDeBruijnBitPosition[32] = + { + 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, + 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 + }; + + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + + return MultiplyDeBruijnBitPosition[(uint32_t)(v * 0x07C4ACDDU) >> 27]; +} +template<> +size_t lg(uint64_t v) +{ + static const int MultiplyDeBruijnBitPosition[64] = + { + 0, 58, 1, 59, 47, 53, 2, 60, 39, 48, 27, 54, 33, 42, 3, 61, + 51, 37, 40, 49, 18, 28, 20, 55, 30, 34, 11, 43, 14, 22, 4, 62, + 57, 46, 52, 38, 26, 32, 41, 50, 36, 17, 19, 29, 10, 13, 21, 56, + 45, 25, 31, 35, 16, 9, 12, 44, 24, 15, 8, 23, 7, 6, 5, 63 + }; + + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v |= v >> 32; + + return MultiplyDeBruijnBitPosition[(uint64_t)(v * 0x03f6eaf2cd271461) >> 58]; +} + + + +template +class RC6 { + static word_t r_l(const word_t& w, size_t v) { + return (w << v) | ( w >> (sizeof(w)-v)); + } + + static word_t r_r(const word_t& w, size_t v) { + return (w >> v) | ( w << (sizeof(w)-v)); + } + + class RC6_KeySched { + public: + static constexpr size_t c = b/sizeof(word_t)/8; + static constexpr size_t v_3 = std::max(c, 2*r+4); + static constexpr size_t v = v_3*3; + private: + std::array S; + public: + RC6_KeySched(std::array L) + { + S[0] = P; + for(size_t i = 1; i < 2*r+3; ++i) + { + S[i] = S[i - 1] + Q; + } + word_t A = 0; + word_t B = 0; + word_t i = 0; + word_t j = 0; + + for(size_t s = 1; s < v; ++s) + { + A = S[i] = r_l( S[i] + A + B, 3 ); + B = L[j] = r_l( L[j] + A + B, (A + B)%(8*sizeof(word_t))); + i = (i + 1) % (2*r+4); + j = (j + 1) % c; + } + } + + const word_t& operator[](const size_t pos) { + return S[pos]; + } + }; + + RC6_KeySched S; + +public: + + typedef std::array key_type; + typedef std::array block_type; + + RC6(const key_type& key) + : S(key) + {} + + void encrypt(block_type& plaintext) { + auto& A = plaintext[0]; + auto& B = plaintext[1]; + auto& C = plaintext[2]; + auto& D = plaintext[3]; + + B += S[0]; + D += S[1]; + + for(size_t i = 1; i < r; ++i) + { + auto t = r_l( B * ( 2 * B + 1 ), 5); + auto u = r_l( D * ( 2 * D + 1 ), 5); + A = ((A ^ t) << u%(8*sizeof(word_t))) + S[2*i]; + C = ((C ^ u) << t%(8*sizeof(word_t))) + S[2*i+1]; + std::rotate(plaintext.begin(), plaintext.begin()+1, plaintext.end()); + } + + A += S[2*r+3]; + C += S[2*r+2]; + } + +}; \ No newline at end of file diff --git a/include/shared_fd.hpp b/include/shared_fd.hpp index 213b453..e585161 100644 --- a/include/shared_fd.hpp +++ b/include/shared_fd.hpp @@ -123,7 +123,7 @@ namespace gp{ ret_addr.s_addr = *((uint32_t*)&address); ret.sin_family = AF_INET; ret.sin_addr = ret_addr; - ret.sin_port = port; + ret.sin_port = htons(port); return ret; } @@ -207,14 +207,32 @@ namespace gp{ return ret; } - static shared_fd socket(const socket_domain& dom, const socket_protocol& proto, const net_socket_opt_flags& flags) + static shared_fd tcp_socket() { shared_fd ret; - auto res = ::socket((int)dom, (int)proto, (int)flags); + auto res = ::socket(AF_INET, SOCK_STREAM, 0); if(res >= 0) + { ret = shared_fd{res}; + fcntl(res, F_SETFL, O_NONBLOCK); + } + else + ret.last_error = errno; + return ret; + } + + static shared_fd socket(const socket_domain& dom, const socket_protocol& proto)//, const net_socket_opt_flags& flags) + { + shared_fd ret; + auto res = ::socket((int)dom, (int)proto, (int)0); + if(res >= 0) + { + ret = shared_fd{res}; + } + else + ret.last_error = errno; return ret; } @@ -248,6 +266,80 @@ namespace gp{ return last_error; } + bool was_connection_refused() const { + return last_error==ECONNREFUSED; + } + + bool in_progress() const { + return last_error==EINPROGRESS; + } + + bool must_retry() const { + return last_error==EAGAIN; + } + + int error() const { + return last_error; + } + + void bind(const address& addr) + { + int ret; + std::visit([&](const auto& v){ + ret = ::bind(fd, (struct sockaddr*)&v, sizeof(v)); + }, addr); + if(ret==0) + { + last_error = 0; + return; + } + else + last_error = errno; + } + + void connect(const address& addr) + { + int ret; + std::visit([&](const auto& v){ + ret = ::connect(fd, (struct sockaddr*)&v, sizeof(v)); + }, addr); + if(ret!=0) + { + last_error = errno; + return; + } + last_error = 0; + } + + void listen(const int& backlog = 16) + { + int ret = 0; + std::visit([&](){ + ret = ::listen(fd, backlog); + }); + if(ret!=0) + { + last_error = errno; + return; + } + last_error = 0; + } + + shared_fd accept() + { + int ret = 0; + std::array buffer; + socklen_t len; + ret = ::accept(fd,(sockaddr*)buffer.begin(), &len); + if(ret!=0) + { + last_error = errno; + return shared_fd{}; + } + last_error = 0; + return shared_fd{ret}; + } + std::string_view read(const std::string_view buffer) { int sz = ::read(fd, (void*)(buffer.begin()), buffer.size()); diff --git a/tests.cpp b/tests.cpp index bcd1ca7..f40a712 100644 --- a/tests.cpp +++ b/tests.cpp @@ -1,6 +1,9 @@ #include "test_scaffold.h" #include "meta_test.cpp" #include "shared_fd.cpp" +#include "rc6_generic.cpp" +#include "gp_test.cpp" +#include "bloomfilter.cpp" #include int main() @@ -15,7 +18,7 @@ int main() value = test->run(); if(value) { - std::cout << test->name << " failed with "<< value << std::endl; + std::cout << std::dec << test->name << " failed with "<< value << std::endl; } } catch (...) { std::cout << test->name << " failed with an exception" << std::endl; @@ -23,6 +26,6 @@ int main() } failed += (value != 0); } - std::cout << "Runned "< +#include + +#include "gp/bloomfilter.hpp" + +typedef std::linear_congruential_engine cheap_rand; +typedef std::linear_congruential_engine cheap_rand_bis; + +struct bfilter_test : public test_scaffold { + uint32_t seed; + + bfilter_test() { + seed = std::random_device{}(); + name = __FILE__ ":1_seed"; + name += std::to_string(seed); + } + + virtual int run() { + + cheap_rand setter(seed); + cheap_rand getter(seed); + + gp::bloomfilter test_filter; + + for(int a = 0 ; a < 100; a++) + { + test_filter.set_hash(setter()); + } + + bool result = true; + for(int a = 0 ; a < 100; a++) + { + result *= test_filter.test_hash(getter()); + } + + return !result; + } +}; + +append_test dummy_r21fg6r43(new bfilter_test{}); + +struct bfilter2_test : public test_scaffold { + uint32_t seedA; + uint32_t seedB; + + bfilter2_test() { + seedA = std::random_device{}(); + seedB = std::random_device{}(); + name = __FILE__ ":2_seedA"; + name += std::to_string(seedA); + name += "&seedB"; + name += std::to_string(seedB); + } + + virtual int run() { + + cheap_rand setter(seedA); + cheap_rand_bis getter(seedB); + + gp::bloomfilter test_filter; + + 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()); + } + + return interference >= 550; + } +}; + +append_test dummy_r2gflu3(new bfilter2_test{}); + +struct bfilter3_test : public test_scaffold { + uint32_t seed; + + bfilter3_test() { + seed = std::random_device{}(); + name = __FILE__ ":3_seed"; + name += std::to_string(seed); + } + + virtual int run() { + + cheap_rand setter(seed); + cheap_rand getter(seed); + + gp::bloomfilter test_filter; + + for(int a = 0 ; a < 1000; a++) + { + test_filter.set_hash(setter()); + } + + bool result = true; + for(int a = 0 ; a < 1000; a++) + { + result *= test_filter.test_hash(getter()); + } + + return !result; + } +}; + +append_test dummy_56489flu3(new bfilter3_test{}); \ No newline at end of file diff --git a/tests/gp_test.cpp b/tests/gp_test.cpp new file mode 100644 index 0000000..c9e27d7 --- /dev/null +++ b/tests/gp_test.cpp @@ -0,0 +1,78 @@ +#include "test_scaffold.h" +#include "gp/array.hpp" +#include +#include +#include +#include +#include + + +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{}); \ No newline at end of file diff --git a/tests/rc6_generic.cpp b/tests/rc6_generic.cpp new file mode 100644 index 0000000..c44901e --- /dev/null +++ b/tests/rc6_generic.cpp @@ -0,0 +1,43 @@ +#include "rc6_generic.hpp" +#include "test_scaffold.h" +#include +#include + +struct RC6test : public test_scaffold { + RC6test() { + name = __FILE__ ":1"; + } + + virtual int run() { + using rc = RC6; + + rc::key_type key = {0,0,0,0}; + rc::block_type plaintext = {0,0,0,0}; + rc::block_type expected{0x8fc3a536,0x56b1f778,0xc129df4e,0x9848a41e}; + + + std::cout<<"plain:"; + for(auto a : plaintext) + std::cout << std::hex << a; + + auto cipher = rc(key); + cipher.encrypt(plaintext); + + std::cout<<"\nkey__:"; + for(auto a : key) + std::cout << std::hex << a; + + std::cout<<"\nciphe:"; + for(auto a : plaintext) + std::cout << std::hex << a; + + std::cout<<"\nexpec:"; + for(auto a : expected) + std::cout << std::hex << a; + std::cout << std::endl; + + return plaintext != expected; + } +}; + +append_test dummy_szfhu5463(new RC6test{}); \ No newline at end of file diff --git a/tests/shared_fd.cpp b/tests/shared_fd.cpp index 1ba3966..d4457ce 100644 --- a/tests/shared_fd.cpp +++ b/tests/shared_fd.cpp @@ -1,5 +1,9 @@ #include "shared_fd.hpp" #include "test_scaffold.h" +#include +#include +#include +#include struct create_test : public test_scaffold { @@ -134,11 +138,11 @@ struct make_address_test : public test_scaffold { virtual int run() { int error = 0; - gp::address ipv4 = gp::make_ipv4("127.0.0.1", 1234); + gp::address ipv4 = gp::make_ipv4("127.0.0.1", 0x1234); auto p = std::get(ipv4); error += (p.sin_family != AF_INET); error += (p.sin_addr.s_addr != htonl(0x7F000001)); - error += (p.sin_port != 1234); + error += (p.sin_port != htons(0x1234)); try{ gp::make_ipv4("not an IP", 1234); @@ -171,11 +175,11 @@ struct sockets_test : public test_scaffold { virtual int run() { int error = 0; - auto v = gp::shared_fd::socket(gp::socket_domain::ip4, gp::socket_protocol::tcp_like, (gp::net_socket_opt_flags)0); + auto v = gp::shared_fd::socket(gp::socket_domain::ip4, gp::socket_protocol::tcp_like); error += !(v.is_valid()); - v = gp::shared_fd::socket(gp::socket_domain::ip4, gp::socket_protocol::tcp_like, (gp::net_socket_opt_flags)-1); - error += (v.is_valid()); + v = gp::shared_fd::socket(gp::socket_domain::ip4, gp::socket_protocol::tcp_like); + error += !(v.is_valid()); auto pair_v = gp::shared_fd::unix_socket_pair(gp::socket_protocol::tcp_like, (gp::net_socket_opt_flags)0); error += !(pair_v.first.is_valid()); @@ -195,4 +199,89 @@ struct sockets_test : public test_scaffold { } }; -append_test dummy_r3321443(new sockets_test{}); \ No newline at end of file +append_test dummy_r3321443(new sockets_test{}); + +using namespace std::chrono_literals; +struct sockets_co_test : public test_scaffold { + + sockets_co_test() { + name = __FILE__ ":8"; + } + + virtual int run() { + std::atomic_int error = 0; + { + auto v1 = gp::shared_fd::tcp_socket(); + error += !(v1.is_valid()); + + auto v2 = gp::shared_fd::tcp_socket(); + error += !(v2.is_valid()); + + v1.bind(gp::make_ipv4("254.254.254.254",1234)); + error += !(v1.has_failed()); + + v1.bind(gp::make_ipv4("0.0.0.0",1235)); + error += v1.has_failed(); + + v1.listen(); + std::cout << "listens?:" << v1.is_valid()< buffer; + std::string_view retstr; + do + { + retstr = v2.read(std::string_view(buffer.begin(), buffer.size())); + std::this_thread::sleep_for(1ms); + } + while(v2.must_retry()); + puts("received"); + std::cout<< "out:" << std::quoted(retstr) << std::endl; + error += !(v2.has_failed()); + error += retstr != "potatoes"; + if(sside.joinable()) + sside.join(); + } + return error; + + } + return error; + } +}; + +//append_test dummy_polmdf43(new sockets_co_test{}); \ No newline at end of file