#pragma once
|
|
|
|
#include <gp/algorithms/move.hpp>
|
|
#include <gp/functional/function.hpp>
|
|
#include <gp/exception.hpp>
|
|
#include <gp/utils/iterator.hpp>
|
|
#include <gp/functional/optional.hpp>
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
|
|
namespace gp{
|
|
|
|
template<typename T>
|
|
class buffer;
|
|
|
|
template<typename T>
|
|
bool hold_same_data(const gp::buffer<T>& lhs, const gp::buffer<T>& rhs);
|
|
|
|
template<typename T>
|
|
class buffer final{
|
|
public:
|
|
private:
|
|
pointer_iterator<T> begin_elem;
|
|
pointer_iterator<T> end_elem;
|
|
|
|
|
|
static constexpr size_t local_strlen(const char* str) {
|
|
auto ptr = str;
|
|
do{}while(*(ptr++));
|
|
return ptr-str-1;
|
|
}
|
|
|
|
public:
|
|
using associated_iterator = pointer_iterator<T>;
|
|
|
|
constexpr buffer(const char* val) requires std::is_same_v<T, char>
|
|
: begin_elem{const_cast<char*>(val)}
|
|
, end_elem{const_cast<char*>(val)+local_strlen(val)}
|
|
{}
|
|
|
|
constexpr buffer(T* beg_ptr, T* end_ptr)
|
|
: begin_elem{beg_ptr}
|
|
, end_elem{end_ptr}
|
|
{}
|
|
|
|
constexpr buffer(associated_iterator beg_ptr, associated_iterator 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 size_t 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)
|
|
{
|
|
if constexpr (gp_config::has_buffer_bounds)
|
|
{
|
|
gp_config::assertion(
|
|
offset < size(),
|
|
"Buffer bounds infringed"
|
|
);
|
|
}
|
|
return *(begin_elem+offset);
|
|
}
|
|
|
|
constexpr const T& operator[](std::size_t offset) const
|
|
{
|
|
if constexpr (gp_config::has_buffer_bounds)
|
|
{
|
|
gp_config::assertion(
|
|
offset < size(),
|
|
"Buffer bounds infringed"
|
|
);
|
|
}
|
|
return *(begin_elem+offset);
|
|
}
|
|
|
|
optional<pointer_iterator<T>> at(std::size_t offset)
|
|
{
|
|
auto elem = begin()+offset;
|
|
if(!contains(elem))
|
|
{
|
|
return nullopt;
|
|
}
|
|
return elem;
|
|
}
|
|
|
|
constexpr bool contains(pointer_iterator<T> ptr)
|
|
{
|
|
return
|
|
ptr.data < end_elem.data
|
|
&& ptr.data >= begin_elem.data;
|
|
}
|
|
|
|
template<typename U>
|
|
buffer<U> cast()
|
|
{
|
|
if constexpr(sizeof(T)%sizeof(U) == 0)
|
|
{
|
|
return buffer<U>(reinterpret_cast<U*>(&*begin_elem), size()*(sizeof(T)/sizeof(U)));
|
|
}
|
|
else
|
|
{
|
|
if((size()*sizeof(T))%sizeof(U) == 0)
|
|
{
|
|
return buffer<U>(reinterpret_cast<U*>(&*begin_elem), size()*sizeof(T)/sizeof(U));
|
|
}
|
|
else if constexpr (gp_config::has_exceptions)
|
|
{
|
|
throw bad_buffer_cast<T, U>{};
|
|
}
|
|
else
|
|
{
|
|
gp_config::assertion(
|
|
false,
|
|
"Buffer bounds infringed during cast"
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a new subslice of the buffer containing `new_sz` elements exactly, or no elements
|
|
* @param new_sz the size of the new slice
|
|
* @return a slice of size new_sz or an empty slice
|
|
*/
|
|
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};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a new subslice from the end of the buffer containing `new_sz` elements exactly, or no elements
|
|
* @param new_sz the size of the new slice
|
|
* @return a slice of size new_sz or an empty slice
|
|
*/
|
|
buffer slice_end(size_t new_sz)
|
|
{
|
|
if(new_sz<=size())
|
|
{
|
|
return buffer{&*(end()-(new_sz)), &*end()};
|
|
}
|
|
else
|
|
{
|
|
return buffer{(T*)nullptr,(size_t)0};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a new subslice from the end of the buffer containing `rm_sz` elements less elements than the original
|
|
* buffer, or no elements
|
|
* @param rm_sz the number of elements to remove from the start of the slice
|
|
* @return a new slice
|
|
*/
|
|
buffer trim_start(size_t rm_sz)
|
|
{
|
|
if(rm_sz<=size())
|
|
{
|
|
return buffer{begin().data + rm_sz, end().data};
|
|
}
|
|
else
|
|
{
|
|
return buffer{(T*)nullptr,(size_t)0};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a new subslice from the start of the buffer containing `rm_sz` elements less elements than the
|
|
* original buffer, or no elements
|
|
* @param rm_sz the number of elements to remove from the start of the slice
|
|
* @return a new slice
|
|
*/
|
|
buffer trim_end(size_t rm_sz)
|
|
{
|
|
if(rm_sz<=size())
|
|
{
|
|
return buffer{begin().data, end().data - rm_sz};
|
|
}
|
|
else
|
|
{
|
|
return buffer{(T*)nullptr,(size_t)0};
|
|
}
|
|
}
|
|
|
|
friend bool hold_same_data(const gp::buffer<T>& lhs, const gp::buffer<T>& rhs);
|
|
|
|
bool starts_with(gp::buffer<T> oth) {
|
|
return hold_same_data<T>(this->slice_start(oth.size()), oth);
|
|
}
|
|
|
|
optional<T> front() {
|
|
if(size() == 0) return nullopt;
|
|
return *begin();
|
|
}
|
|
|
|
optional<T> back() {
|
|
if(size() == 0) return nullopt;
|
|
return *slice_end(1).begin();
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
bool hold_same_data(const gp::buffer<T>& lhs, const gp::buffer<T>& rhs) {
|
|
if(lhs.size() != rhs.size()) return false;
|
|
size_t idx = 0;
|
|
while(idx != lhs.size()) {
|
|
if(lhs[idx] != rhs[idx]) return false;
|
|
++idx;
|
|
}
|
|
return true;
|
|
}
|
|
}
|