-
Notifications
You must be signed in to change notification settings - Fork 126
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
3f8c440
commit ea98b07
Showing
10 changed files
with
642 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
#pragma once | ||
|
||
#include <cinttypes> | ||
#include <cstring> | ||
#include <limits> | ||
|
||
#include "cista/bit_counting.h" | ||
#include "cista/containers/array.h" | ||
#include "cista/next_power_of_2.h" | ||
#include "cista/verify.h" | ||
|
||
namespace cista { | ||
|
||
template <typename SizeType> | ||
struct page { | ||
bool valid() const { return capacity_ != 0U; } | ||
SizeType size() const noexcept { return size_; } | ||
|
||
SizeType size_{0U}; | ||
SizeType capacity_{0U}; | ||
SizeType start_{0U}; | ||
}; | ||
|
||
template <typename DataVec, typename SizeType = typename DataVec::size_type, | ||
SizeType MinPageSize = next_power_of_two(3U * sizeof(SizeType)), | ||
SizeType MaxPageSize = 65536U> | ||
struct paged { | ||
using value_type = typename DataVec::value_type; | ||
using iterator = typename DataVec::iterator; | ||
using const_iterator = typename DataVec::const_iterator; | ||
using reference = typename DataVec::reference; | ||
using const_reference = typename DataVec::const_reference; | ||
using size_type = SizeType; | ||
using page_t = page<SizeType>; | ||
|
||
static_assert(sizeof(value_type) * MinPageSize >= sizeof(page_t)); | ||
static_assert(std::is_trivially_copyable_v<value_type>); | ||
|
||
static constexpr size_type free_list_index(size_type const capacity) { | ||
return static_cast<size_type>(constexpr_trailing_zeros(capacity) - | ||
constexpr_trailing_zeros(MinPageSize)); | ||
} | ||
|
||
static constexpr size_type free_list_size = free_list_index(MaxPageSize) + 1U; | ||
|
||
page_t resize_page(page_t const& p, size_type const size) { | ||
if (size <= p.capacity_) { | ||
return {size, p.capacity_, p.start_}; | ||
} else { | ||
auto const new_page = create_page(size); | ||
copy(new_page, p); | ||
free_page(p); | ||
return new_page; | ||
} | ||
} | ||
|
||
page_t create_page(size_type const size) { | ||
auto const capacity = next_power_of_two(std::max(MinPageSize, size)); | ||
auto const i = free_list_index(capacity); | ||
verify(i < free_list_.size(), "paged::create_page: size > max capacity"); | ||
if (!free_list_[i].empty()) { | ||
auto start = free_list_[i].pop(*this); | ||
return {size, capacity, start}; | ||
} else { | ||
auto const start = data_.size(); | ||
data_.resize(data_.size() + capacity); | ||
return {size, capacity, start}; | ||
} | ||
} | ||
|
||
void free_page(page_t const& p) { | ||
if (!p.valid()) { | ||
return; | ||
} | ||
auto const i = free_list_index(p.capacity_); | ||
verify(i < free_list_.size(), "paged::free_page: size > max capacity"); | ||
free_list_[i].push(*this, p.start_); | ||
} | ||
|
||
template <typename T> | ||
T read(size_type const offset) { | ||
static_assert(std::is_trivially_copyable_v<T>); | ||
auto x = T{}; | ||
std::memcpy(&x, &data_[offset], sizeof(x)); | ||
return x; | ||
} | ||
|
||
template <typename T> | ||
void write(size_type const offset, T const& x) { | ||
static_assert(std::is_trivially_copyable_v<T>); | ||
std::memcpy(&data_[offset], &x, sizeof(T)); | ||
} | ||
|
||
value_type* data(page_t const& p) { return &data_[p.start_]; } | ||
value_type const* data(page_t const& p) const { return &data_[p.start_]; } | ||
|
||
value_type* begin(page_t const& p) { return data(p); } | ||
value_type const* begin(page_t const& p) const { return data(p); } | ||
|
||
value_type* end(page_t& p) const { return begin(p) + p.size(); } | ||
value_type const* end(page_t const& p) const { return begin() + p.size; } | ||
|
||
void copy(page_t const& to, page_t const& from) { | ||
std::memcpy(data(to), data(from), from.size() * sizeof(value_type)); | ||
} | ||
|
||
template <typename ItA, typename ItB> | ||
void copy(page_t const& to, ItA begin, ItB end) { | ||
std::memcpy(data(to), &*begin, | ||
static_cast<std::size_t>(std::distance(begin, end))); | ||
} | ||
|
||
struct node { | ||
bool empty() const { | ||
return next_ == std::numeric_limits<size_type>::max(); | ||
} | ||
|
||
void push(paged& m, size_type const start) { | ||
m.write(start, next_); | ||
next_ = start; | ||
} | ||
|
||
size_type pop(paged& m) { | ||
verify(!empty(), "paged: invalid read access to empty free list entry"); | ||
auto const next_start = m.read<size_type>(next_); | ||
auto start = next_; | ||
next_ = next_start; | ||
return start; | ||
} | ||
|
||
size_type next_{std::numeric_limits<size_type>::max()}; | ||
}; | ||
|
||
DataVec data_; | ||
array<node, free_list_size> free_list_{}; | ||
}; | ||
|
||
} // namespace cista |
Oops, something went wrong.