@ -1,3 +1,69 @@ | |||
# gplib | |||
General Purpose library for POSIX systems | |||
General Purpose library for Freestanding C++ environment and POSIX systems. | |||
> Expects C++17 | |||
## Datastructures | |||
### `buffer` | |||
Also named "slices" in other languages, they are a combination of a pointer and a size. | |||
### `array` | |||
A fixed size, inplace array. | |||
### `indexed_array` | |||
A resizable, fragmentation resistant array, it associates elements with fixed indices. It will reuse deleted indices. | |||
### `optional` | |||
A Maybe monad, will not allocate for final classes or primitives. | |||
### `variant` and `fixed_variant` | |||
### `ring_list` | |||
### `bloomfilter` | |||
### `quotient_filter` | |||
## Algorithms | |||
## Tools | |||
### Allocators | |||
### Renderer | |||
### Internal file-system | |||
## GP Configuration | |||
### `enum class gp_errorcodes` | |||
This `enum` should not contain any non-zero value. | |||
- `infinite_skipstone`: used when a linear probing reaches infinity lookup | |||
### Generic elements | |||
- `constexpr bool gp_config::has_exceptions`: enables or disables exceptions throwing | |||
- `constexpr bool gp_config::has_buffer_bounds`: enables or disables bound checking | |||
- `constexpr size_t gp_config::arc4random_strength`: determines the amount of shuffling of the arc4random PRNG | |||
- `constexpr /* T */ gp_config::assertion`: `T` is a callable type taking a boolean predicate and a `const char*` | |||
- `typedef /* T */ file_descriptor_t`: `T` is an integer type. No negative value should be expected | |||
### Rendering and mathematics | |||
- `using gp_config::rendering::default_type`: provide a numeric type for rendering and for the mathematical framework | |||
- `constexpr default_type gp_config::rendering::epsilon`: a small value of the default type (example for float: `0.001f`) | |||
- `#define GP_CONFIG__RENDERING__COLOR_T`: a configuration define for storing a color for rendering purposes | |||
### Memory | |||
- `using gp_config::memory_module::default_allocator`: a default constructible allocator type | |||
- `constexpr bool gp_config::memory_module::is_ok`: true if the default allocator is able to allocate, false if not |
@ -0,0 +1,26 @@ | |||
#pragma once | |||
#include "gp/algorithm/move.hpp" | |||
namespace gp { | |||
template<typename iterator> | |||
iterator rotate(iterator first, iterator new_first, iterator last) | |||
{ | |||
if(first == new_first) return last; | |||
if(new_first == last) return first; | |||
iterator in = new_first; | |||
iterator out = first; | |||
iterator mv = first; | |||
while(in != last) { | |||
if(out == mv) mv = in; | |||
gp::swap((*out++), (*in++)); | |||
} | |||
// rotate the remaining sequence into place | |||
(rotate)(out, mv, last); | |||
return out; | |||
} | |||
} |
@ -0,0 +1,130 @@ | |||
#pragma once | |||
#include <stddef.h> | |||
#include "gp_config.hpp" | |||
#include "gp/algorithm/move.hpp" | |||
#include "gp/iterator.hpp" | |||
namespace gp{ | |||
template<typename T, size_t _capacity> | |||
class indexed_array{ | |||
size_t data_top = 0; | |||
size_t available_indexes_top = 0; | |||
size_t remove_top = 0; | |||
T data_table[_capacity]; | |||
size_t available_indexes[_capacity]; | |||
size_t translation_table[_capacity]; | |||
size_t reverse_translation_table[_capacity]; | |||
size_t remove_table[_capacity]; | |||
public: | |||
indexed_array() {} | |||
size_t push(T&& value) { | |||
size_t index; | |||
gp_config::assertion(data_top+1 != _capacity, "Indexed array capacity exceeded"); | |||
if(available_indexes_top) { | |||
available_indexes_top--; | |||
index = available_indexes[available_indexes_top]; | |||
} else { | |||
index = data_top; | |||
} | |||
new(&data_table[data_top]) T(gp::move(value)); | |||
translation_table[index] = data_top; | |||
reverse_translation_table[data_top] = index; | |||
++data_top; | |||
return index; | |||
} | |||
void pop(size_t idx) { | |||
size_t v_idx = translation_table[idx]; | |||
available_indexes[available_indexes_top] = idx; | |||
++available_indexes_top; | |||
translation_table[idx] = -1; | |||
--data_top; | |||
if(v_idx < data_top) { | |||
size_t u_idx = reverse_translation_table[data_top]; | |||
data_table[v_idx] = gp::move(data_table[data_top]); | |||
::operator delete(&data_table[data_top], &data_table[data_top]); | |||
data_table[data_top].~T(); | |||
translation_table[u_idx] = v_idx; | |||
reverse_translation_table[v_idx] = u_idx; | |||
} | |||
} | |||
void reset() { | |||
auto it = data_table; | |||
auto end = data_table+data_top; | |||
while(it != end) { | |||
::operator delete(it, it); | |||
++it; | |||
} | |||
data_top = 0; | |||
available_indexes_top = 0; | |||
remove_top = 0; | |||
} | |||
void mark_internal_for_removal(size_t i_idx) { | |||
remove_table[remove_top] = reverse_translation_table[i_idx]; | |||
++remove_top; | |||
} | |||
void mark_for_removal(size_t idx) { | |||
remove_table[remove_top] = idx; | |||
++remove_top; | |||
} | |||
void sweep_removed() { | |||
auto it = remove_table; | |||
auto end = remove_table+remove_top; | |||
while(it != end) { | |||
pop(*it); | |||
++it; | |||
} | |||
} | |||
bool has(size_t idx) { | |||
if(idx > data_top) return false; | |||
if(translation_table[idx] == -1) return false; | |||
return true; | |||
} | |||
pointer_iterator<T> begin() | |||
{ | |||
return data_table; | |||
} | |||
pointer_iterator<T> end() | |||
{ | |||
return data_table+data_top; | |||
} | |||
const_pointer_iterator<T> cbegin() | |||
{ | |||
return data_table; | |||
} | |||
const_pointer_iterator<T> cend() | |||
{ | |||
return data_table+data_top; | |||
} | |||
size_t size() { | |||
return data_top; | |||
} | |||
size_t capacity() { | |||
return _capacity; | |||
} | |||
T& operator[](size_t idx) { | |||
gp_config::assertion(idx < data_top, "Bad indexed array access"); | |||
return data_table[translation_table[idx]]; | |||
} | |||
}; | |||
} |
@ -0,0 +1,351 @@ | |||
#pragma once | |||
#include "gp_config.hpp" | |||
#include "gp/math/integer_math.hpp" | |||
#include "gp/math/q_math.hpp" | |||
#if !defined(NO_FP_MATH) | |||
# include "gp/math/fp_math.hpp" | |||
#endif | |||
#include "gp/algorithm/repeat.hpp" | |||
#include "gp/algorithm/min_max.hpp" | |||
namespace gp { | |||
template<typename T> | |||
T lerp(T input, T low, T high) { | |||
return low + (high - low) * input; | |||
} | |||
template<typename T> | |||
T lextrap(T input, T low, T high) { | |||
return (input - low) / (high - low); | |||
} | |||
template<typename T, size_t fixed_passes = 16> | |||
T fixed_sqrt(T value) { | |||
gp_config::assertion(value >= 0, "trying to compute square root of negative number"); | |||
if(value == 0) return 0; | |||
T ret = value / 2; | |||
T tmp; | |||
gp::repeat(fixed_passes, [&](){ | |||
tmp = ret; | |||
ret = (value / tmp + tmp) / 2; | |||
}); | |||
return ret; | |||
} | |||
template<typename T, size_t cap_passes = 16> | |||
T epsilon_sqrt(T value) { | |||
gp_config::assertion(value >= 0, "trying to compute square root of negative number"); | |||
if(value == 0) return 0; | |||
T ret = value / 2; | |||
T tmp; | |||
constexpr T epsilon = gp_config::rendering::epsilon; | |||
size_t cnt = 0; | |||
while( | |||
!( | |||
(ret+epsilon)*ret > value | |||
&& (ret-epsilon)*ret < value | |||
) | |||
&& cnt < cap_passes | |||
){ | |||
tmp = ret; | |||
ret = (value / tmp + tmp) / 2; | |||
++cnt; | |||
}; | |||
return ret; | |||
} | |||
template<typename T, size_t cap_passes = 16> | |||
T stable_sqrt(T value) { | |||
gp_config::assertion(value >= 0, "trying to compute square root of negative number"); | |||
if(value == 0) return 0; | |||
T ret = value / 2; | |||
T tmp; | |||
while(ret != tmp){ | |||
tmp = ret; | |||
ret = (value / tmp + tmp) / 2; | |||
}; | |||
return ret; | |||
} | |||
template<typename T = gp_config::rendering::default_type> | |||
struct vec2_g { | |||
T x; | |||
T y; | |||
vec2_g() | |||
: x{} | |||
, y{} | |||
{} | |||
vec2_g( | |||
T _x, | |||
T _y | |||
) | |||
: x{_x} | |||
, y{_y} | |||
{} | |||
vec2_g operator/(vec2_g rhs) { | |||
return { | |||
x / rhs.x, | |||
y / rhs.y | |||
}; | |||
} | |||
vec2_g operator*(vec2_g rhs) { | |||
return { | |||
x * rhs.x, | |||
y * rhs.y | |||
}; | |||
} | |||
vec2_g operator+(vec2_g oth) { | |||
return {x+oth.x, y+oth.y}; | |||
} | |||
vec2_g operator-(vec2_g oth) { | |||
return {x-oth.x, y-oth.y}; | |||
} | |||
vec2_g normalize() { | |||
T ilen = fast_isqrt(x*x+y*y); | |||
return {x*ilen, y*ilen}; | |||
} | |||
T length() { | |||
return fixed_sqrt(x*x+y*y); | |||
} | |||
}; | |||
template<typename T = gp_config::rendering::default_type> | |||
struct vec3_g { | |||
T x; | |||
T y; | |||
T z; | |||
T& r(){ | |||
return x; | |||
} | |||
T& g(){ | |||
return y; | |||
} | |||
T& b(){ | |||
return z; | |||
} | |||
vec3_g() | |||
: x{} | |||
, y{} | |||
, z{} | |||
{} | |||
vec3_g( | |||
T _x, | |||
T _y, | |||
T _z | |||
) | |||
: x{_x} | |||
, y{_y} | |||
, z{_z} | |||
{} | |||
vec3_g(vec2_g<T> left, T right) | |||
: x{left.x} | |||
, y{left.y} | |||
, z{right} | |||
{} | |||
vec3_g(T left, vec2_g<T> right) | |||
: x{left} | |||
, y{right.x} | |||
, z{right.y} | |||
{} | |||
vec3_g operator/(vec3_g rhs) { | |||
return { | |||
x / rhs.x, | |||
y / rhs.y, | |||
z / rhs.z | |||
}; | |||
} | |||
vec3_g operator*(vec3_g rhs) { | |||
return { | |||
x * rhs.x, | |||
y * rhs.y, | |||
z * rhs.z | |||
}; | |||
} | |||
vec3_g operator+(vec3_g oth) { | |||
return {x+oth.x, y+oth.y, z+oth.z}; | |||
} | |||
vec3_g operator-(vec3_g oth) { | |||
return {x-oth.x, y-oth.y, z-oth.z}; | |||
} | |||
vec3_g normalize() { | |||
T ilen = fast_isqrt(x*x+y*y+z*z); | |||
return {x*ilen, y*ilen, z*ilen}; | |||
} | |||
T length() { | |||
return fixed_sqrt(x*x+y*y+z*z); | |||
} | |||
}; | |||
template<typename T = gp_config::rendering::default_type> | |||
struct vec4_g { | |||
T x; | |||
T y; | |||
T z; | |||
T w; | |||
T& r(){ | |||
return x; | |||
} | |||
T& g(){ | |||
return y; | |||
} | |||
T& b(){ | |||
return z; | |||
} | |||
T& a(){ | |||
return w; | |||
} | |||
vec4_g() | |||
: x{} | |||
, y{} | |||
, z{} | |||
, w{} | |||
{} | |||
vec4_g( | |||
T _x, | |||
T _y, | |||
T _z, | |||
T _w | |||
) | |||
: x{_x} | |||
, y{_y} | |||
, z{_z} | |||
, w{_w} | |||
{} | |||
vec4_g(T left, vec3_g<> right) | |||
: x{left} | |||
, y{right.x} | |||
, z{right.y} | |||
, w{right.z} | |||
{} | |||
vec4_g(vec3_g<> left, T right) | |||
: x{left.x} | |||
, y{left.y} | |||
, z{left.z} | |||
, w{right} | |||
{} | |||
vec4_g operator/(vec4_g rhs) { | |||
return { | |||
x / rhs.x, | |||
y / rhs.y, | |||
z / rhs.z, | |||
w / rhs.w | |||
}; | |||
} | |||
vec4_g operator*(vec4_g rhs) { | |||
return { | |||
x * rhs.x, | |||
y * rhs.y, | |||
z * rhs.z, | |||
w * rhs.w | |||
}; | |||
} | |||
vec4_g operator+(vec4_g oth) { | |||
return {x+oth.x, y+oth.y, z+oth.z, w+oth.w}; | |||
} | |||
vec4_g operator-(vec4_g oth) { | |||
return {x-oth.x, y-oth.y, z-oth.w, z-oth.w}; | |||
} | |||
vec4_g normalize() { | |||
T ilen = fast_isqrt(x*x+y*y+z*z+w*w); | |||
return {x*ilen, y*ilen, z*ilen, w*ilen}; | |||
} | |||
T length() { | |||
return fixed_sqrt(x*x+y*y+z*z+w*w); | |||
} | |||
}; | |||
template<typename T> | |||
auto sphere_sdf(vec3_g<T> center, T radius) { | |||
return [=](vec3_g<T> position) -> T const { | |||
auto p = position - center; | |||
return p.length() - radius; | |||
}; | |||
} | |||
template<typename T, typename lhs, typename rhs> | |||
auto union_sdf(lhs _l, rhs _r) { | |||
return [=](vec3_g<T> position) -> T const { | |||
return gp::min(_l(position), _r(position)); | |||
}; | |||
} | |||
template<typename T, typename lhs, typename rhs> | |||
auto intersect_sdf(lhs _l, rhs _r) { | |||
return [=](vec3_g<T> position) -> T const { | |||
return gp::max(_l(position), _r(position)); | |||
}; | |||
} | |||
template<typename T, typename lhs, typename rhs> | |||
auto difference_sdf(lhs _l, rhs _r) { | |||
return [=](vec3_g<T> position) -> T const { | |||
return gp::max(_l(position), -_r(position)); | |||
}; | |||
} | |||
template<typename T> | |||
vec2_g<T> operator*(vec2_g<T> p, T v) { | |||
return {p.x*v, p.y*v}; | |||
} | |||
template<typename T> | |||
vec3_g<T> operator*(vec3_g<T> p, T v) { | |||
return {p.x*v, p.y*v, p.z*v}; | |||
} | |||
template<typename T> | |||
vec4_g<T> operator*(vec4_g<T> p, T v) { | |||
return {p.x*v, p.y*v, p.z*v, p.w*v}; | |||
} | |||
template<typename T> | |||
vec2_g<T> operator*(T v, vec2_g<T> p) { | |||
return p*v; | |||
} | |||
template<typename T> | |||
vec3_g<T> operator*(T v, vec3_g<T> p) { | |||
return p*v; | |||
} | |||
template<typename T> | |||
vec4_g<T> operator*(T v, vec4_g<T> p) { | |||
return p*v; | |||
} | |||
} | |||
static_assert(sizeof(gp::vec3_g<int>) == sizeof(int)*3, "vec3_g has strange alignment"); | |||
static_assert(sizeof(gp::vec4_g<int>) == sizeof(int)*4, "vec4_g has strange alignment"); |
@ -0,0 +1,196 @@ | |||
#pragma once | |||
#include <stddef.h> | |||
#include <stdint.h> | |||
#include <limits> | |||
#include "gp/algorithm/repeat.hpp" | |||
namespace gp{ | |||
template<typename T> | |||
constexpr T pi; | |||
template<> | |||
constexpr float pi<float> = 3.1415926535897932384626433832795028841971693993751058209749445923078164062; | |||
template<> | |||
constexpr double pi<double> = 3.1415926535897932384626433832795028841971693993751058209749445923078164062; | |||
template<typename T> | |||
T abs(T); | |||
template<> | |||
float abs<float>(float value) { | |||
static_assert(sizeof(float) == 4, "bad float size"); | |||
union { | |||
float fp; | |||
uint32_t ab; | |||
} p; | |||
p.fp = value; | |||
p.ab &= 0x7fFFffFF; | |||
return p.fp; | |||
} | |||
template<> | |||
double abs<double>(double value) { | |||
static_assert(sizeof(double) == 8, "bad double size"); | |||
union { | |||
double fp; | |||
uint64_t ab; | |||
} p; | |||
p.fp = value; | |||
p.ab &= 0x7fFFffFFffFFffFF; | |||
return p.fp; | |||
} | |||
template<typename T> | |||
T floor(T); | |||
template<> | |||
float floor<float>(float value) { | |||
static_assert(sizeof(float) == 4, "bad float size"); | |||
if( | |||
value >= std::numeric_limits<int32_t>::max() | |||
|| value <= std::numeric_limits<int32_t>::min() | |||
|| value != value | |||
) { | |||
return value; | |||
} | |||
int32_t ret = value; | |||
float ret_d = ret; | |||
if(value == ret_d || value >= 0) { | |||
return ret; | |||
} else { | |||
return ret-1; | |||
} | |||
} | |||
template<> | |||
double floor<double>(double value) { | |||
static_assert(sizeof(double) == 8, "bad double size"); | |||
if( | |||
value >= std::numeric_limits<int64_t>::max() | |||
|| value <= std::numeric_limits<int64_t>::min() | |||
|| value != value | |||
) { | |||
return value; | |||
} | |||
int64_t ret = value; | |||
double ret_d = ret; | |||
if(value == ret_d || value >= 0) { | |||
return ret; | |||
} else { | |||
return ret-1; | |||
} | |||
} | |||
template<typename T> | |||
T sign(T); | |||
template<> | |||
float sign<float>(float value) { | |||
static_assert(sizeof(float) == 4, "bad float size"); | |||
if(!value) return 0; | |||
union { | |||
float fp; | |||
uint32_t ab; | |||
} p; | |||
p.fp = value; | |||
p.ab &= 0x7fFFffFF; | |||
return value/p.fp; | |||
} | |||
template<> | |||
double sign<double>(double value) { | |||
static_assert(sizeof(double) == 8, "bad double size"); | |||
if(!value) return 0; | |||
union { | |||
double fp; | |||
uint64_t ab; | |||
} p; | |||
p.fp = value; | |||
p.ab &= 0x7fFFffFFffFFffFF; | |||
return value/p.fp; | |||
} | |||
template<size_t steps, typename T, size_t accuracy = 1000000> | |||
T sin_taylor(T value) { | |||
const T acc = T{1}/T{accuracy}; | |||
T B = value; | |||
T C = 1; | |||
T ret = B/C; | |||
for(size_t i = 1; (i < steps) && (abs<>(B/C) > acc); ++i) { | |||
B *= -1*value*value; | |||
C *= 2*i*(2*i+1); | |||
ret += B/C; | |||
} | |||
return ret; | |||
} | |||
float sin(float v) { | |||
v += pi<float>; | |||
v = v - 2*pi<float>*floor(v/(2*pi<float>)); | |||
v -= pi<float>; | |||
float s = sign(v); | |||
v *= s; | |||
return sin_taylor<10>(v)*s; | |||
} | |||
double sin(double v) { | |||
v += pi<double>; | |||
v = v - 2*pi<double>*floor(v/(2*pi<double>)); | |||
v -= pi<double>; | |||
float s = sign(v); | |||
v *= s; | |||
return sin_taylor<10>(v)*s; | |||
} | |||
// TODO: replace with an actual implementation | |||
float cos(float v) { | |||
return sin(v+pi<float>/2); | |||
} | |||
// TODO: replace with an actual implementation | |||
double cos(double v) { | |||
return sin(v+pi<float>/2); | |||
} | |||
template<size_t cycles = 5> | |||
float isqrt(float v) { | |||
int32_t i; | |||
float x2, y; | |||
constexpr float threehalfs = 1.5F; | |||
x2 = v * 0.5F; | |||
y = v; | |||
i = * ( int32_t * ) &y; | |||
i = 0x5F375A86 - ( i >> 1 ); | |||
y = * ( float * ) &i; | |||
gp::repeat(cycles, [&](){ | |||
y = y * ( threehalfs - ( x2 * y * y ) ); | |||
}); | |||
return y; | |||
} | |||
template<size_t cycles = 5> | |||
double isqrt(double v) { | |||
int64_t i; | |||
double x2, y; | |||
constexpr double threehalfs = 1.5F; | |||
x2 = v * 0.5F; | |||
y = v; | |||
i = * ( int64_t * ) &y; | |||
i = 0x5FE6EB50C7B537A9 - ( i >> 1 ); | |||
y = * ( double * ) &i; | |||
gp::repeat(cycles, [&](){ | |||
y = y * ( threehalfs - ( x2 * y * y ) ); | |||
}); | |||
return y; | |||
} | |||
float fast_isqrt(float v) {return isqrt<1>(v);} | |||
double fast_isqrt(double v) {return isqrt<1>(v);} | |||
} |
@ -0,0 +1,76 @@ | |||
#pragma once | |||
#include <stdint.h> | |||
#include <stddef.h> | |||
namespace gp { | |||
namespace math { | |||
template<typename word_t> | |||
size_t log2(word_t v); | |||
/** | |||
Sean Eron Anderson | |||
seander@cs.stanford.edu | |||
**/ | |||
template<> | |||
constexpr size_t log2<uint32_t>(uint32_t v) | |||
{ | |||
constexpr 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<> | |||
constexpr size_t log2<uint64_t>(uint64_t v) | |||
{ | |||
constexpr 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]; | |||
} | |||
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); | |||
template<> | |||
constexpr size_t msb<uint32_t>(uint32_t v) | |||
{ | |||
auto l = log2(v); | |||
return l + (((1 << l) ^ v) != 0); | |||
} | |||
template<> | |||
constexpr size_t msb<uint64_t>(uint64_t 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"); | |||
} | |||
} |
@ -0,0 +1,16 @@ | |||
#pragma once | |||
#include "gp/allocator/buddy.hpp" | |||
#include "gp/allocator/dummy.hpp" | |||
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); | |||
} | |||
}; |