Sane C++ Libraries
C++ Platform Abstraction Libraries
Segment.h
1// Copyright (c) Stefano Cristiano
2// SPDX-License-Identifier: MIT
3#pragma once
4#include "../Foundation/Assert.h"
5#include "../Foundation/Span.h"
6
7namespace SC
8{
9struct SC_COMPILER_EXPORT SegmentHeader;
10struct alignas(uint64_t) SegmentHeader
11{
12 static constexpr uint32_t MaxCapacity = (~static_cast<uint32_t>(0)) >> 1;
13
14 uint32_t sizeBytes : sizeof(uint32_t) * 8 - 1;
15 uint32_t isInlineBuffer : 1;
16 uint32_t capacityBytes : sizeof(uint32_t) * 8 - 1;
17 uint32_t isFollowedByInlineBuffer : 1;
18 // clang-format off
19 template <typename T> T* getData() { return reinterpret_cast<T*>(reinterpret_cast<char*>(this) + sizeof(*this)); }
20 template <typename T> const T* getData() const { return reinterpret_cast<const T*>(reinterpret_cast<const char*>(this) + sizeof(*this)); }
21 // clang-format on
22};
23
26
33template <typename VTable>
34struct Segment
35{
36 using T = typename VTable::Type;
37
38 Segment();
39 ~Segment();
40 Segment(Segment&& other);
41 Segment(const Segment& other);
42 Segment& operator=(Segment&& other);
43 Segment& operator=(const Segment& other);
44
45 template <typename U>
47 {
48 SC_ASSERT_RELEASE(assign(span));
49 }
50
51 Segment(Span<const T> span) : Segment() { SC_ASSERT_RELEASE(assign(span)); }
52
53 Segment(std::initializer_list<T> list) : Segment() { SC_ASSERT_RELEASE(assign({list.begin(), list.size()})); }
54
57 [[nodiscard]] bool resizeWithoutInitializing(size_t newSize);
58
60 [[nodiscard]] bool resize(size_t newSize, const T& value = T());
61
63 [[nodiscard]] bool reserve(size_t newCapacity);
64
66 [[nodiscard]] bool append(Span<const T> span);
67
69 template <typename U>
70 [[nodiscard]] bool append(Span<const U> span);
71
73 [[nodiscard]] bool appendMove(Segment&& other);
74
77 [[nodiscard]] bool shrink_to_fit();
78
80 void clear();
81
84 [[nodiscard]] bool assign(Span<const T> span);
85
88 [[nodiscard]] bool assignMove(Segment&& other);
89
91 [[nodiscard]] bool push_back(const T& value) { return resize(size() + 1, value); }
92
94 [[nodiscard]] bool push_back(T&& value);
95
97 [[nodiscard]] bool push_front(const T& value) { return insert(0, value); }
98
102 [[nodiscard]] bool pop_back(T* removedValue = nullptr);
103
107 [[nodiscard]] bool pop_front(T* removedValue = nullptr);
108
109 // clang-format off
110
112 [[nodiscard]] const T* data() const SC_LANGUAGE_LIFETIME_BOUND
113 {
114 return header == nullptr ? nullptr : reinterpret_cast<const T*>(reinterpret_cast<const char*>(header) + sizeof(SegmentHeader));
115 }
116
118 [[nodiscard]] T* data() SC_LANGUAGE_LIFETIME_BOUND
119 {
120 return header == nullptr ? nullptr : reinterpret_cast<T*>(reinterpret_cast<char*>(header) + sizeof(SegmentHeader));
121 }
122
123 [[nodiscard]] T* begin() SC_LANGUAGE_LIFETIME_BOUND { return data(); }
124 [[nodiscard]] const T* begin() const SC_LANGUAGE_LIFETIME_BOUND { return data(); }
125 [[nodiscard]] T* end() SC_LANGUAGE_LIFETIME_BOUND { return data() + size(); }
126 [[nodiscard]] const T* end() const SC_LANGUAGE_LIFETIME_BOUND { return data() + size(); }
127
128 [[nodiscard]] T& back() SC_LANGUAGE_LIFETIME_BOUND { SC_ASSERT_RELEASE(not isEmpty()); return *(data() + size() - 1);}
129 [[nodiscard]] T& front() SC_LANGUAGE_LIFETIME_BOUND { SC_ASSERT_RELEASE(not isEmpty()); return *data();}
130 [[nodiscard]] T& operator[](size_t idx) SC_LANGUAGE_LIFETIME_BOUND { SC_ASSERT_DEBUG(idx < size()); return *(data() + idx);}
131 [[nodiscard]] const T& back() const SC_LANGUAGE_LIFETIME_BOUND { SC_ASSERT_RELEASE(not isEmpty()); return *(data() + size() - 1);}
132 [[nodiscard]] const T& front() const SC_LANGUAGE_LIFETIME_BOUND { SC_ASSERT_RELEASE(not isEmpty()); return *data();}
133 [[nodiscard]] const T& operator[](size_t idx) const SC_LANGUAGE_LIFETIME_BOUND { SC_ASSERT_DEBUG(idx < size()); return *(data() + idx);}
134
135 // clang-format on
136
138 [[nodiscard]] bool isInlineBuffer() const { return header != nullptr and header->isInlineBuffer; }
139
141 [[nodiscard]] bool isEmpty() const { return header == nullptr or header->sizeBytes == 0; }
142
144 [[nodiscard]] Span<T> toSpan() SC_LANGUAGE_LIFETIME_BOUND { return {data(), size()}; }
145
147 [[nodiscard]] Span<const T> toSpanConst() const SC_LANGUAGE_LIFETIME_BOUND { return {data(), size()}; }
148
150 [[nodiscard]] size_t size() const { return header == nullptr ? 0 : header->sizeBytes / sizeof(T); }
151
153 [[nodiscard]] size_t capacity() { return header == nullptr ? 0 : header->capacityBytes / sizeof(T); }
154
159 [[nodiscard]] bool removeRange(size_t start, size_t length);
160
164 [[nodiscard]] bool removeAt(size_t index) { return removeRange(index, 1); }
165
170 [[nodiscard]] bool insert(size_t index, Span<const T> data);
171
173 void unsafeSetHeader(SegmentHeader* newHeader) { header = newHeader; }
174
176 [[nodiscard]] SegmentHeader* unsafeGetHeader() const { return header; }
177
179 Segment(SegmentHeader& inlineHeader, uint32_t capacityInBytes);
180
181 protected:
182 struct Internal;
183 // Alignment is relevant for 32 bit platforms
184 alignas(alignof(SegmentHeader)) SegmentHeader* header;
185};
186
189{
190 static void destruct(SegmentHeader& header, size_t bytesOffset, size_t numBytes);
191 static void copyConstructSingle(SegmentHeader& dest, size_t bytesOffset, const void* value, size_t numBytes,
192 size_t valueSize);
193 static void copyConstruct(SegmentHeader& dest, size_t bytesOffset, const void* src, size_t numBytes);
194 static void copyAssign(SegmentHeader& dest, size_t bytesOffset, const void* src, size_t numBytes);
195 static void copyInsert(SegmentHeader& dest, size_t bytesOffset, const void* src, size_t numBytes);
196 static void moveConstruct(SegmentHeader& dest, size_t bytesOffset, void* src, size_t numBytes);
197 static void moveAssign(SegmentHeader& dest, size_t bytesOffset, void* src, size_t numBytes);
198 static void remove(SegmentHeader& dest, size_t fromBytesOffset, size_t toBytesOffset);
199};
200
203{
204 static SegmentHeader* allocateNewHeader(size_t newCapacityInBytes);
205 static SegmentHeader* reallocateExistingHeader(SegmentHeader& src, size_t newCapacityInBytes);
206
207 static void destroyHeader(SegmentHeader& header);
208};
210} // namespace SC
#define SC_ASSERT_DEBUG(e)
Assert expression e to be true.
Definition: Assert.h:82
#define SC_COMPILER_EXPORT
Macro for symbol visibility in non-MSVC compilers.
Definition: Compiler.h:78
#define SC_ASSERT_RELEASE(e)
Assert expression e to be true.
Definition: Assert.h:66
unsigned int uint32_t
Platform independent (4) bytes unsigned int.
Definition: PrimitiveTypes.h:38
Basic allocator for SC::Segment using Memory functions.
Definition: Segment.h:203
Definition: Segment.h:11
A slice of contiguous memory, prefixed by and header containing size and capacity.
Definition: Segment.h:35
Segment(SegmentHeader &inlineHeader, uint32_t capacityInBytes)
Builds a Segment with an inlineHeader of given capacity in bytes.
bool pop_back(T *removedValue=nullptr)
Removes the last element of the segment.
bool appendMove(Segment &&other)
Moves contents of another segment to the end of this segment.
size_t size() const
Returns current size.
Definition: Segment.h:150
bool append(Span< const T > span)
Appends a Span to the end of the segment.
void clear()
Sets size to zero without freeing any memory (use shrink_to_fit() to free memory)
bool shrink_to_fit()
Ensures capacity == size re-allocating (if capacity>size) or freeing ( if size==0) memory.
bool removeRange(size_t start, size_t length)
Removes the range [start, start + length] from the segment.
bool resize(size_t newSize, const T &value=T())
Re-allocates to the requested new size, preserving its contents and setting new items to value.
bool resizeWithoutInitializing(size_t newSize)
Re-allocates to the requested new size, preserving its contents.
bool isEmpty() const
Check if is empty (size() == 0)
Definition: Segment.h:141
size_t capacity()
Returns current capacity (always >= of size())
Definition: Segment.h:153
bool isInlineBuffer() const
Returns true if an inline buffer is in use (false if segment is heap allocated).
Definition: Segment.h:138
bool push_front(const T &value)
Appends a single element to the start of the segment.
Definition: Segment.h:97
T * data() SC_LANGUAGE_LIFETIME_BOUND
Access data owned by the segment or nullptr if segment is empty.
Definition: Segment.h:118
bool assign(Span< const T > span)
Replaces contents with contents of the span.
bool insert(size_t index, Span< const T > data)
Insert a span at the given index.
const T * data() const SC_LANGUAGE_LIFETIME_BOUND
Access data owned by the segment or nullptr if segment is empty.
Definition: Segment.h:112
Span< const T > toSpanConst() const SC_LANGUAGE_LIFETIME_BOUND
Obtains a Span of internal contents.
Definition: Segment.h:147
bool removeAt(size_t index)
Removes the element at index.
Definition: Segment.h:164
bool push_back(T &&value)
Moves a single element to the end of the segment.
bool append(Span< const U > span)
Appends a Span of items convertible to T to the end of the segment.
bool push_back(const T &value)
Appends a single element to the end of the segment.
Definition: Segment.h:91
bool reserve(size_t newCapacity)
Reserves capacity to avoid heap-allocation during a future append, assign or resize.
void unsafeSetHeader(SegmentHeader *newHeader)
Sets the internal header handled by this class.
Definition: Segment.h:173
bool pop_front(T *removedValue=nullptr)
Removes the first element of the segment.
bool assignMove(Segment &&other)
Replaces content moving (possibly "stealing") content of another segment.
Span< T > toSpan() SC_LANGUAGE_LIFETIME_BOUND
Obtains a Span of internal contents.
Definition: Segment.h:144
SegmentHeader * unsafeGetHeader() const
Get the internal header handled by this class.
Definition: Segment.h:176
Allows SC::Segment handle trivial types.
Definition: Segment.h:189
View over a contiguous sequence of items (pointer + size in elements).
Definition: Span.h:24