1#ifndef DONUT_LINEAR_BUFFER_HPP
2#define DONUT_LINEAR_BUFFER_HPP
21template <
typename... Ts>
26template <
typename T, std::size_t Index,
typename... Ts>
27struct LinearBufferIndexImpl;
29template <
typename T, std::size_t Index,
typename First,
typename... Rest>
30struct LinearBufferIndexImpl<T, Index, First, Rest...> : LinearBufferIndexImpl<T, Index + 1, Rest...> {};
32template <
typename T, std::size_t Index,
typename... Rest>
33struct LinearBufferIndexImpl<T, Index, T, Rest...> : std::integral_constant<std::size_t, Index> {};
36struct LinearBufferMinElementSize : std::integral_constant<std::size_t, sizeof(T)> {};
39struct LinearBufferMinElementSize<T[]> : std::integral_constant<std::size_t, sizeof(std::size_t)> {};
42struct LinearBufferVisitorParameterType {
47struct LinearBufferVisitorParameterType<T[]> {
48 using type = std::span<const T>;
54template <
typename T,
typename B>
55struct linear_buffer_has_alternative;
57template <
typename T,
typename First,
typename... Rest>
58struct linear_buffer_has_alternative<T, LinearBuffer<First, Rest...>> : linear_buffer_has_alternative<T, LinearBuffer<Rest...>> {};
61struct linear_buffer_has_alternative<T, LinearBuffer<>> : std::false_type {};
63template <
typename T,
typename... Rest>
64struct linear_buffer_has_alternative<T, LinearBuffer<T, Rest...>> : std::true_type {};
67template <
typename T,
typename B>
71template <
typename T,
typename B>
72struct linear_buffer_index;
74template <
typename T,
typename... Ts>
75struct linear_buffer_index<T,
LinearBuffer<Ts...>> : detail::LinearBufferIndexImpl<T, 0, Ts...> {};
78template <
typename T,
typename B>
82template <std::
size_t Index,
typename B>
83struct linear_buffer_alternative;
85template <std::size_t Index,
typename First,
typename... Rest>
86struct linear_buffer_alternative<Index,
LinearBuffer<First, Rest...>> : linear_buffer_alternative<Index - 1, LinearBuffer<Rest...>> {};
88template <
typename T,
typename... Rest>
89struct linear_buffer_alternative<0, LinearBuffer<T, Rest...>> {
94template <std::
size_t Index,
typename B>
99struct linear_buffer_size;
101template <
typename... Ts>
102struct linear_buffer_size<
LinearBuffer<Ts...>> : std::integral_constant<std::size_t, sizeof...(Ts)> {};
108template <
typename... Ts>
111 static_assert((std::is_trivially_copyable_v<std::remove_extent_t<Ts>> && ...),
"LinearBuffer requires all element types to be trivially copyable.");
115 std::conditional_t<
sizeof...(Ts) < 255ull, std::uint8_t,
116 std::conditional_t<
sizeof...(Ts) < 65535ull, std::uint16_t,
117 std::conditional_t<
sizeof...(Ts) < 4294967295ull, std::uint32_t,
124 : memoryResource(memoryResource)
125 , nextChunkSize(std::max(nextChunkSize, MIN_CHUNK_SIZE)) {
126 assert(memoryResource);
129 template <
typename T>
130 void push_back(
const T& value)
requires(!std::is_unbounded_array_v<T> && linear_buffer_has_alternative_v<T, LinearBuffer>) {
131 constexpr std::size_t HEADER_SIZE =
sizeof(
index_type);
132 constexpr std::size_t requiredSize = HEADER_SIZE +
sizeof(T) +
sizeof(
index_type) +
sizeof(std::byte*);
133 const std::size_t remainingMemorySize =
static_cast<std::size_t
>(remainingMemoryEnd - remainingMemoryBegin);
134 if (remainingMemorySize < requiredSize) {
136 const std::size_t newChunkSize = std::max(requiredSize, nextChunkSize);
138 assert(remainingMemorySize >=
sizeof(
index_type) +
sizeof(std::byte*));
139 std::byte*
const oldChunkTail = remainingMemoryBegin;
140 std::byte*
const newChunk = allocateChunk(newChunkSize);
143 std::memcpy(oldChunkTail +
sizeof(
index_type), &newChunk,
sizeof(std::byte*));
145 head = allocateChunk(newChunkSize);
148 constexpr index_type index = linear_buffer_index_v<T, LinearBuffer>;
149 std::memcpy(remainingMemoryBegin, &index,
sizeof(
index_type));
150 std::memcpy(remainingMemoryBegin + HEADER_SIZE, &value,
sizeof(T));
151 remainingMemoryBegin += HEADER_SIZE +
sizeof(T);
154 template <
typename T,
typename... Args>
155 void emplace_back(Args&&... args)
requires(!std::is_unbounded_array_v<T> && linear_buffer_has_alternative_v<T, LinearBuffer> && std::is_constructible_v<T, Args...>) {
156 return push_back<T>(T{std::forward<Args>(args)...});
159 template <
typename T>
160 std::span<const T>
append(std::span<const T> values)
requires(linear_buffer_has_alternative_v<T[], LinearBuffer>) {
161 constexpr std::size_t HEADER_SIZE =
sizeof(
index_type) +
sizeof(std::size_t);
162 const std::size_t remainingMemorySize =
static_cast<std::size_t
>(remainingMemoryEnd - remainingMemoryBegin);
163 void* alignedPointer = remainingMemoryBegin + HEADER_SIZE;
164 const std::size_t minRequiredSize = HEADER_SIZE +
sizeof(
index_type) +
sizeof(std::byte*);
165 std::size_t space = remainingMemorySize - minRequiredSize;
166 if (remainingMemorySize < minRequiredSize || (!values.empty() && !std::align(
alignof(T), values.size_bytes(), alignedPointer, space))) {
168 const std::size_t requiredSize = HEADER_SIZE +
alignof(T) - 1 + values.size_bytes() +
sizeof(
index_type) +
sizeof(std::byte*);
169 const std::size_t newChunkSize = std::max(requiredSize, nextChunkSize);
171 assert(remainingMemorySize >=
sizeof(
index_type) +
sizeof(std::byte*));
172 std::byte*
const oldChunkTail = remainingMemoryBegin;
173 std::byte*
const newChunk = allocateChunk(newChunkSize);
176 std::memcpy(oldChunkTail +
sizeof(
index_type), &newChunk,
sizeof(std::byte*));
178 head = allocateChunk(newChunkSize);
180 alignedPointer = remainingMemoryBegin + HEADER_SIZE;
181 space =
static_cast<std::size_t
>(remainingMemoryEnd - remainingMemoryBegin) - minRequiredSize;
182 [[maybe_unused]]
void*
const aligned = std::align(
alignof(T), values.size_bytes(), alignedPointer, space);
185 constexpr index_type index = linear_buffer_index_v<T[], LinearBuffer>;
186 const std::size_t count = values.size();
187 std::memcpy(remainingMemoryBegin, &index,
sizeof(
index_type));
188 std::memcpy(remainingMemoryBegin +
sizeof(
index_type), &count,
sizeof(std::size_t));
189 std::memcpy(alignedPointer, values.data(), values.size_bytes());
190 remainingMemoryBegin =
static_cast<std::byte*
>(alignedPointer) + values.size_bytes();
191 return std::span<const T>{
reinterpret_cast<const T*
>(alignedPointer), count};
194 template <
typename Visitor>
195 auto visit(Visitor&& visitor)
const {
196 using R = std::common_type_t<decltype(std::invoke(std::forward<Visitor>(visitor), std::declval<typename detail::LinearBufferVisitorParameterType<Ts>::type>()))...>;
197 const std::byte*
const end = remainingMemoryBegin;
198 for (
const std::byte* pointer = head; pointer != end;) {
200 std::memcpy(&index, pointer,
sizeof(
index_type));
202 const auto apply = [&]<std::size_t Index>(std::in_place_index_t<Index>) ->
void {
204 if constexpr (std::is_unbounded_array_v<RawType>) {
205 using T = std::remove_extent_t<RawType>;
207 std::memcpy(&count, pointer,
sizeof(std::size_t));
208 pointer +=
sizeof(std::size_t);
209 if constexpr (
alignof(T) > 1) {
211 void* alignedPointer =
const_cast<void*
>(
static_cast<const void*
>(pointer));
212 std::size_t remainingMemorySize =
static_cast<std::size_t
>(end - pointer);
213 [[maybe_unused]]
void*
const aligned = std::align(
alignof(T), count *
sizeof(T), alignedPointer, remainingMemorySize);
215 pointer =
static_cast<const std::byte*
>(alignedPointer);
218 const std::span<const T> values{
reinterpret_cast<const T*
>(pointer), count};
219 if constexpr (std::is_void_v<R>) {
220 std::invoke(std::forward<Visitor>(visitor), values);
221 pointer += count *
sizeof(T);
223 if (std::invoke(std::forward<Visitor>(visitor), values)) {
224 pointer += count *
sizeof(T);
231 alignas(T) std::array<std::byte,
sizeof(T)> storage;
232 std::memcpy(storage.data(), pointer,
sizeof(T));
233 const T& value = *std::launder(
reinterpret_cast<const T*
>(storage.data()));
234 if constexpr (std::is_void_v<R>) {
235 std::invoke(std::forward<Visitor>(visitor), value);
236 pointer +=
sizeof(T);
238 if (std::invoke(std::forward<Visitor>(visitor), value)) {
239 pointer +=
sizeof(T);
246 [&]<std::size_t... Indices>(std::index_sequence<Indices...>) ->
void {
247 if (!(((index == Indices) ? (apply(std::in_place_index<Indices>),
true) :
false) || ...)) {
249 std::memcpy(&pointer, pointer,
sizeof(std::byte*));
251 }(std::make_index_sequence<
sizeof...(Ts)>{});
253 if constexpr (!std::is_void_v<R>) {
259 static constexpr std::size_t MIN_CHUNK_SIZE = std::max({(
sizeof(
index_type) + detail::LinearBufferMinElementSize<Ts>::value +
sizeof(
index_type) +
sizeof(std::byte*))...});
261 [[nodiscard]] std::byte* allocateChunk(std::size_t newChunkSize) {
262 assert(memoryResource);
263 std::byte*
const newChunk =
static_cast<std::byte*
>(memoryResource->
allocate(newChunkSize, 1));
264 remainingMemoryBegin = newChunk;
265 remainingMemoryEnd = newChunk + newChunkSize;
266 nextChunkSize += nextChunkSize / 2;
270 LinearMemoryResource* memoryResource;
271 std::byte* head =
nullptr;
272 std::byte* remainingMemoryBegin =
nullptr;
273 std::byte* remainingMemoryEnd =
nullptr;
274 std::size_t nextChunkSize;
Definition LinearBuffer.hpp:109
auto visit(Visitor &&visitor) const
Definition LinearBuffer.hpp:195
LinearBuffer(LinearMemoryResource *memoryResource, std::size_t nextChunkSize=64) noexcept
Definition LinearBuffer.hpp:123
void emplace_back(Args &&... args)
Definition LinearBuffer.hpp:155
std::span< const T > append(std::span< const T > values)
Definition LinearBuffer.hpp:160
std::conditional_t< sizeof...(Ts)< 255ull, std::uint8_t, std::conditional_t< sizeof...(Ts)< 65535ull, std::uint16_t, std::conditional_t< sizeof...(Ts)< 4294967295ull, std::uint32_t, std::uint64_t > > > index_type
Definition LinearBuffer.hpp:118
static constexpr index_type npos
Definition LinearBuffer.hpp:121
void push_back(const T &value)
Definition LinearBuffer.hpp:130
Definition LinearAllocator.hpp:15
void * allocate(std::size_t size, std::size_t alignment)
Definition LinearAllocator.hpp:25
Definition Application.hpp:9
constexpr bool linear_buffer_has_alternative_v
Definition LinearBuffer.hpp:68
constexpr std::size_t linear_buffer_size_v
Definition LinearBuffer.hpp:106
typename linear_buffer_alternative< Index, B >::type linear_buffer_alternative_t
Definition LinearBuffer.hpp:95
constexpr std::size_t linear_buffer_index_v
Definition LinearBuffer.hpp:79