Sane C++ Libraries
C++ Platform Abstraction Libraries
Loading...
Searching...
No Matches
Segment.h
1// Copyright (c) Stefano Cristiano
2// SPDX-License-Identifier: MIT
3#pragma once
4#include "../Common/CompilerMacrosLifetimeBound.h"
5#include "../Common/CompilerMinMax.h"
6#include "../Common/Span.h"
7#include "../Memory/Memory.h"
8#if SC_COMPILER_FILC
9#include <stdfil.h>
10#endif
11
12namespace SC
13{
16enum class SegmentAllocator
17{
18 Global = 0,
19 ThreadLocal = 1,
20};
21
22namespace detail
23{
24struct SC_MEMORY_EXPORT SegmentHeader;
25struct alignas(uint64_t) SegmentHeader
26{
27 static constexpr uint32_t MaxCapacity = (~static_cast<uint32_t>(0)) >> 1;
28
29 SegmentHeader(uint32_t capacity = 0, SegmentAllocator allocator = SegmentAllocator::Global)
30 {
31 sizeBytes = 0;
32 allocatorType = static_cast<uint32_t>(allocator);
33 capacityBytes = capacity;
34 hasInlineData = capacity > 0;
35 }
36 uint32_t sizeBytes : sizeof(uint32_t) * 8 - 1;
37 uint32_t allocatorType : 1;
38 uint32_t capacityBytes : sizeof(uint32_t) * 8 - 1;
39 uint32_t hasInlineData : 1;
40};
41
42struct SC_MEMORY_EXPORT SegmentHeaderOffset
43{
44 using PtrOffset = size_t;
45 SegmentHeader header;
46 PtrOffset offset = 0; // memory offset representing relative pointer to data from "this"
47};
48
49#if SC_COMPILER_FILC
50inline zexact_ptrtable* filcExactPtrTable() noexcept
51{
52 static zexact_ptrtable* table = zexact_ptrtable_new();
53 return table;
54}
55
56template <typename T>
57inline T* filcResolveRelativePointer(const void* self, size_t address) noexcept
58{
59 const size_t lower = reinterpret_cast<size_t>(zgetlower(const_cast<void*>(self)));
60 const size_t upper = reinterpret_cast<size_t>(zgetupper(const_cast<void*>(self)));
61 if (address >= lower and address < upper)
62 return static_cast<T*>(zmkptr(const_cast<void*>(self), static_cast<unsigned long>(address)));
63 return static_cast<T*>(zexact_ptrtable_decode(filcExactPtrTable(), address));
64}
65#endif
66
67template <typename T>
68struct SC_MEMORY_EXPORT SegmentSelfRelativePointer : protected SegmentHeaderOffset
69{
70 // clang-format off
71 SC_COMPILER_FORCE_INLINE T* data() noexcept
72 {
73#if SC_COMPILER_FILC
74 if (offset == 0)
75 return nullptr;
76 const auto address = toOffset(this) + offset;
77 if (header.hasInlineData and offset == inlineDataOffset())
78 return static_cast<T*>(zmkptr(this, static_cast<unsigned long>(address)));
79 return filcResolveRelativePointer<T>(this, address);
80#else
81 return offset == 0 ? nullptr : toPtr(toOffset(this) + offset);
82#endif
83 }
84 SC_COMPILER_FORCE_INLINE const T* data() const noexcept
85 {
86#if SC_COMPILER_FILC
87 if (offset == 0)
88 return nullptr;
89 const auto address = toOffset(this) + offset;
90 if (header.hasInlineData and offset == inlineDataOffset())
91 return static_cast<const T*>(zmkptr(const_cast<SegmentSelfRelativePointer*>(this), static_cast<unsigned long>(address)));
92 return filcResolveRelativePointer<const T>(this, address);
93#else
94 return offset == 0 ? nullptr : toPtr(toOffset(this) + offset);
95#endif
96 }
97 SC_COMPILER_FORCE_INLINE bool isInline() const noexcept { return (offset == inlineDataOffset()) and header.hasInlineData; }
98
99 protected:
100 struct InlineData : public SegmentHeaderOffset // Data layout corresponds to SmallBuffer, SmallVector etc.
101 {
102 uint64_t capacity; // Could use uint32_t but we need to align data to 64 bit anyway
103 ~InlineData() {}
104 union
105 {
106 T data[1]; // Accessing the whole class through volatile cast anyway so array size can be whatever
107 };
108 };
109 SC_COMPILER_FORCE_INLINE static constexpr PtrOffset inlineDataOffset() noexcept { return sizeof(SegmentHeaderOffset) + sizeof(uint64_t); }
110 SC_COMPILER_FORCE_INLINE static auto toOffset(const volatile void* src) noexcept { return reinterpret_cast<PtrOffset>(src); }
111 SC_COMPILER_FORCE_INLINE static T* toPtr(PtrOffset src) noexcept { return reinterpret_cast<T*>(src); }
112 SC_COMPILER_FORCE_INLINE void setData(T* mem) noexcept
113 {
114 offset = mem == nullptr ? 0 : toOffset(mem) - toOffset(this);
115#if SC_COMPILER_FILC
116 if (mem != nullptr and not (header.hasInlineData and mem == getInlineData()))
117 (void)zexact_ptrtable_encode(filcExactPtrTable(), mem);
118#endif
119 }
120 SC_COMPILER_FORCE_INLINE T* getInlineData() noexcept { return (T*)reinterpret_cast<volatile InlineData*>(this)->data; }
121 SC_COMPILER_FORCE_INLINE auto getInlineCapacity() noexcept { return reinterpret_cast<volatile InlineData*>(this)->capacity; }
122 // clang-format on
123};
124
126template <typename T>
127struct SC_MEMORY_EXPORT SegmentTrivial
128{
129 using Type = T;
130 inline static void destruct(Span<T> data) noexcept;
131 // clang-format off
132 template <typename U> inline static void copyConstructAs(Span<T> data, Span<const U> value) noexcept;
133 template <typename U> inline static void copyConstruct(Span<T> data, const U* src) noexcept;
134 template <typename U> inline static void copyAssign(Span<T> data, const U* src) noexcept;
135 template <typename U> inline static void copyInsert(Span<T> data, Span<const U> values) noexcept;
136 template <typename U> inline static void moveConstruct(Span<T> data, U* src) noexcept;
137 template <typename U> inline static void moveAssign(Span<T> data, U* src) noexcept;
138 // clang-format on
139 inline static void remove(Span<T> data, size_t numElements) noexcept;
140};
141
143template <typename ParentSegment, typename CommonParent, int N = 0,
144 SegmentAllocator Allocator = SegmentAllocator::ThreadLocal>
145struct SC_MEMORY_EXPORT SegmentCustom : public ParentSegment
146{
147 SegmentCustom() : ParentSegment(N, Allocator) {}
148 SegmentCustom(const CommonParent& other) : SegmentCustom() { CommonParent::operator=(other); }
149 SegmentCustom(CommonParent&& other) : SegmentCustom() { CommonParent::operator=(move(other)); }
150
151 SegmentCustom(const SegmentCustom& other) : SegmentCustom() { ParentSegment::operator=(other); }
152 SegmentCustom(SegmentCustom&& other) : SegmentCustom() { ParentSegment::operator=(move(other)); }
153 // clang-format off
154 SegmentCustom& operator=(const SegmentCustom& other) { ParentSegment::operator=(other); return *this; }
155 SegmentCustom& operator=(SegmentCustom&& other) { ParentSegment::operator=(move(other)); return *this; }
156 // clang-format on
157};
158} // namespace detail
159
166template <typename VTable>
167struct SC_MEMORY_EXPORT Segment : public VTable
168{
169 using VTable::data;
170 using T = typename VTable::Type;
171 Segment(uint32_t capacityInBytes, SegmentAllocator allocator = SegmentAllocator::Global) noexcept;
172
173 Segment() noexcept;
174 ~Segment() noexcept;
175 Segment(Segment&& other) noexcept;
176 Segment(const Segment& other) noexcept;
177 Segment& operator=(Segment&& other) noexcept;
178 Segment& operator=(const Segment& other) noexcept;
179
180 // clang-format off
181 template <typename U = T> Segment(Span<const U> span) noexcept : Segment() { SC_MEMORY_ASSERT_RELEASE(assign(span)); }
182 // clang-format on
183 Segment(std::initializer_list<T> list) noexcept;
184
187 [[nodiscard]] bool resizeWithoutInitializing(size_t newSize) noexcept;
188
190 [[nodiscard]] bool resize(size_t newSize, const T& value = T()) noexcept;
191
193 [[nodiscard]] bool reserve(size_t capacity) noexcept;
194
196 template <typename U = T>
197 [[nodiscard]] bool append(Span<const U> span) noexcept;
198
200 template <typename VTable2>
201 [[nodiscard]] bool appendMove(Segment<VTable2>&& other) noexcept;
202
205 [[nodiscard]] bool shrink_to_fit() noexcept;
206
208 void clear() noexcept;
209
212 template <typename U = T>
213 [[nodiscard]] bool assign(Span<const U> span) noexcept;
214
217 template <typename VTable2>
218 [[nodiscard]] bool assignMove(Segment<VTable2>&& other) noexcept;
219
221 [[nodiscard]] bool push_back(const T& value) noexcept { return resize(size() + 1, value); }
222
224 [[nodiscard]] bool push_back(T&& value) noexcept;
225
227 [[nodiscard]] bool push_front(const T& value) noexcept { return insert(0, value); }
228
232 [[nodiscard]] bool pop_back(T* removedValue = nullptr) noexcept;
233
237 [[nodiscard]] bool pop_front(T* removedValue = nullptr) noexcept;
238
239 // clang-format off
240 [[nodiscard]] T* begin() noexcept SC_LANGUAGE_LIFETIME_BOUND { return data(); }
241 [[nodiscard]] const T* begin() const noexcept SC_LANGUAGE_LIFETIME_BOUND { return data(); }
242 [[nodiscard]] T* end() noexcept SC_LANGUAGE_LIFETIME_BOUND { return data() + size(); }
243 [[nodiscard]] const T* end() const noexcept SC_LANGUAGE_LIFETIME_BOUND { return data() + size(); }
244
245 [[nodiscard]] T& back() noexcept SC_LANGUAGE_LIFETIME_BOUND { SC_MEMORY_ASSERT_RELEASE(not isEmpty()); return *(data() + size() - 1);}
246 [[nodiscard]] T& front() noexcept SC_LANGUAGE_LIFETIME_BOUND { SC_MEMORY_ASSERT_RELEASE(not isEmpty()); return *data();}
247 [[nodiscard]] const T& back() const noexcept SC_LANGUAGE_LIFETIME_BOUND { SC_MEMORY_ASSERT_RELEASE(not isEmpty()); return *(data() + size() - 1);}
248 [[nodiscard]] const T& front() const noexcept SC_LANGUAGE_LIFETIME_BOUND { SC_MEMORY_ASSERT_RELEASE(not isEmpty()); return *data();}
249
250 [[nodiscard]] T& operator[](size_t idx) noexcept SC_LANGUAGE_LIFETIME_BOUND { SC_MEMORY_ASSERT_DEBUG(idx < size()); return *(data() + idx);}
251 [[nodiscard]] const T& operator[](size_t idx) const noexcept SC_LANGUAGE_LIFETIME_BOUND { SC_MEMORY_ASSERT_DEBUG(idx < size()); return *(data() + idx);}
252 // clang-format on
253
255 [[nodiscard]] bool isEmpty() const noexcept { return VTable::header.sizeBytes == 0; }
256
258 [[nodiscard]] Span<T> toSpan() noexcept SC_LANGUAGE_LIFETIME_BOUND { return {data(), size()}; }
259
261 [[nodiscard]] Span<const T> toSpanConst() const noexcept SC_LANGUAGE_LIFETIME_BOUND { return {data(), size()}; }
262
264 [[nodiscard]] size_t size() const noexcept { return VTable::header.sizeBytes / sizeof(T); }
265
267 [[nodiscard]] size_t capacity() const noexcept { return VTable::header.capacityBytes / sizeof(T); }
268
273 [[nodiscard]] bool removeRange(size_t start, size_t length) noexcept;
274
278 [[nodiscard]] bool removeAt(size_t index) noexcept { return removeRange(index, 1); }
279
284 [[nodiscard]] bool insert(size_t index, Span<const T> data) noexcept;
285
286 protected:
287 template <typename VTable2>
288 friend struct Segment;
289 struct Internal;
290};
291
293} // namespace SC
A slice of contiguous memory, prefixed by and header containing size and capacity.
Definition Segment.h:168
size_t size() const noexcept
Returns current size.
Definition Segment.h:264
bool resize(size_t newSize, const T &value=T()) noexcept
Re-allocates to the requested new size, preserving its contents and setting new items to value.
size_t capacity() const noexcept
Returns current capacity (always >= of size())
Definition Segment.h:267
Span< T > toSpan() noexcept SC_LANGUAGE_LIFETIME_BOUND
Obtains a Span of internal contents.
Definition Segment.h:258
bool isEmpty() const noexcept
Check if is empty (size() == 0)
Definition Segment.h:255
bool removeRange(size_t start, size_t length) noexcept
Removes the range [start, start + length] from the segment.
bool push_front(const T &value) noexcept
Appends a single element to the start of the segment.
Definition Segment.h:227
bool removeAt(size_t index) noexcept
Removes the element at index.
Definition Segment.h:278
bool pop_back(T *removedValue=nullptr) noexcept
Removes the last element of the segment.
bool push_back(T &&value) noexcept
Moves a single element to the end of the segment.
bool insert(size_t index, Span< const T > data) noexcept
Insert a span at the given index.
bool resizeWithoutInitializing(size_t newSize) noexcept
Re-allocates to the requested new size, preserving its contents.
Span< const T > toSpanConst() const noexcept SC_LANGUAGE_LIFETIME_BOUND
Obtains a Span of internal contents.
Definition Segment.h:261