Sane C++ Libraries
C++ Platform Abstraction Libraries
Span.h
1// Copyright (c) Stefano Cristiano
2// SPDX-License-Identifier: MIT
3#pragma once
4#include "../Foundation/InitializerList.h"
5#include "../Foundation/LibC.h" // memcmp
6#include "../Foundation/TypeTraits.h" // SameConstnessAs
7
8namespace SC
9{
10template <typename Type>
11struct Span;
12
13struct SpanStringView;
14struct SpanString;
15namespace detail
16{
17// clang-format off
18template<typename U> struct SpanSizeOfType { static constexpr auto size = sizeof(U); };
19template<> struct SpanSizeOfType<void> { static constexpr auto size = 1; };
20template<> struct SpanSizeOfType<const void> { static constexpr auto size = 1; };
21// clang-format on
22} // namespace detail
23} // namespace SC
24
27
30template <typename Type>
32{
33 private:
34 // clang-format off
35 using SizeType = size_t;
36 using VoidType = typename TypeTraits::SameConstnessAs<Type, void>::type;
37 template <typename U> using TypeIfNotVoid = typename TypeTraits::EnableIf<not TypeTraits::IsSame<U, VoidType>::value, Type>::type;
38 template <typename U> using SameType = TypeTraits::IsSame<typename TypeTraits::RemoveConst<U>::type, typename TypeTraits::RemoveConst<Type>::type>;
39 template <typename U> using EnableNotVoid = typename TypeTraits::EnableIf<SameType<U>::value and not TypeTraits::IsSame<U, VoidType>::value, bool>::type;
40 // clang-format on
41 Type* items;
42 SizeType sizeElements;
43
44 public:
45 template <size_t N, typename U = Type, EnableNotVoid<U> = true>
46 constexpr Span(U (&itemsArray)[N]) : items(itemsArray), sizeElements(N)
47 {}
48
50 constexpr Span() : items(nullptr), sizeElements(0) {}
51
55 constexpr Span(Type* items, SizeType sizeInElements) : items(items), sizeElements(sizeInElements) {}
56
59 template <typename U = Type>
60 constexpr Span(TypeIfNotVoid<U>& type) : items(&type), sizeElements(1)
61 {}
62
65 template <typename U = Type>
66 constexpr Span(std::initializer_list<TypeIfNotVoid<U>> list) : items(nullptr), sizeElements(0)
67 {
68 // We need this two step initialization to avoid warnings on all compilers
69 items = list.begin();
70 sizeElements = list.size();
71 }
72
73 // clang-format off
74 template <typename U = Type> operator Span<const TypeIfNotVoid<U>>() const { return {items, sizeElements}; }
75 template <typename U = Type> operator Span< TypeIfNotVoid<U>>() { return {items, sizeElements}; }
76 operator Span<const void>() const { return Span<const void>(items, sizeElements * detail::SpanSizeOfType<Type>::size); }
77 operator Span<void>() { return Span<void>(items, sizeElements * detail::SpanSizeOfType<Type>::size); }
78
83 template <typename T> [[nodiscard]] static Span<Type> reinterpret_object(T& value) { return {reinterpret_cast<Type*>(&value), sizeof(T) / detail::SpanSizeOfType<Type>::size}; }
84
89 [[nodiscard]] static Span<Type> reinterpret_bytes(VoidType* rawMemory, SizeType sizeInBytes) { return Span(reinterpret_cast<Type*>(rawMemory), sizeInBytes / detail::SpanSizeOfType<Type>::size); }
90
92 template <typename T> [[nodiscard]] Span<const T> reinterpret_as_span_of() const { return Span<const T>(reinterpret_cast<const T*>(items), sizeInBytes() / sizeof(T)); }
93
95 template <typename T> [[nodiscard]] Span<T> reinterpret_as_span_of() { return Span<T>(reinterpret_cast<T*>(items), sizeInBytes() / sizeof(T)); }
96
97 template <typename T> [[nodiscard]] T* start_lifetime_as_array() const noexcept
98 {
99 // TODO: Not a fully compliant implementation, replace it with a proper one under C++23
100 // https://stackoverflow.com/questions/79164176/emulate-stdstart-lifetime-as-array-in-c20
101 void* p = const_cast<void*>(static_cast<const void*>(data()));
102 return sizeElements == 0 ? static_cast<const T*>(p) : launder(static_cast<const T*>(::memmove(p, p, sizeof(T) * sizeElements)));
103 }
104
105 template <typename T> [[nodiscard]] T* start_lifetime_as_array() noexcept
106 {
107 void* p = data();
108 return sizeElements == 0 ? static_cast<T*>(p) : launder(static_cast<T*>(::memmove(p, p, sizeof(T) * sizeElements)));
109 }
110
111 template <typename T> [[nodiscard]] const T* start_lifetime_as() const noexcept
112 {
113 void* p = const_cast<void*>(static_cast<const void*>(data()));
114 return launder(static_cast<const T*>(::memmove(p, p, sizeof(T))));
115 }
116
117 template <typename T> [[nodiscard]] T* start_lifetime_as() noexcept
118 {
119 void* p = data();
120 return launder(static_cast<T*>(::memmove(p, p, sizeof(T))));
121 }
122 // clang-format on
123
126 [[nodiscard]] constexpr const Type* begin() const { return items; }
127
130 [[nodiscard]] constexpr const Type* end() const { return items + sizeElements; }
131
134 [[nodiscard]] constexpr const Type* data() const { return items; }
135
138 [[nodiscard]] constexpr Type* begin() { return items; }
139
142 [[nodiscard]] constexpr Type* end() { return items + sizeElements; }
143
146 [[nodiscard]] constexpr Type* data() { return items; }
147
150 [[nodiscard]] constexpr SizeType sizeInElements() const { return sizeElements; }
151
154 [[nodiscard]] constexpr SizeType sizeInBytes() const { return sizeElements * detail::SpanSizeOfType<Type>::size; }
155
162 [[nodiscard]] constexpr bool sliceStart(SizeType offsetInElements, Span& destination) const
163 {
164 if (offsetInElements <= sizeInElements())
165 {
166 destination = Span(items + offsetInElements, (sizeInElements() - offsetInElements));
167 return true;
168 }
169 return false;
170 }
171
179 [[nodiscard]] constexpr bool sliceStartLength(SizeType offsetInElements, SizeType lengthInElements,
180 Span& destination) const
181 {
182 if (offsetInElements + lengthInElements <= sizeInElements())
183 {
184 destination = Span(items + offsetInElements, lengthInElements);
185 return true;
186 }
187 return false;
188 }
189
193 [[nodiscard]] bool sliceFromStartUntil(Span other, Span& output) const
194 {
195 const auto diff = other.items - items;
196 if (diff < 0 or static_cast<SizeType>(diff) > sizeInBytes())
197 {
198 return false;
199 }
200 else
201 {
202 output = Span(items, static_cast<SizeType>(diff) / detail::SpanSizeOfType<Type>::size);
203 return true;
204 }
205 }
206
209 [[nodiscard]] constexpr bool empty() const { return sizeElements == 0; }
210
217 template <typename U>
218 [[nodiscard]] constexpr bool contains(const U& value, SizeType* index = nullptr) const
219 {
220 return find([&](auto& current) { return current == value; }, index);
221 }
222
228 template <typename Lambda>
229 [[nodiscard]] constexpr bool find(Lambda&& lambda, SizeType* index = nullptr) const
230 {
231 for (SizeType idx = 0; idx < sizeElements; ++idx)
232 {
233 if (lambda(items[idx]))
234 {
235 if (index)
236 {
237 *index = idx;
238 }
239 return true;
240 }
241 }
242 return false;
243 }
244
245 // clang-format off
246 template <typename U = Type> TypeIfNotVoid<U>& operator[](SizeType idx) { return items[idx]; }
247 template <typename U = Type> const TypeIfNotVoid<U>& operator[](SizeType idx) const { return items[idx]; }
248 // clang-format on
249
251 template <typename IntType>
252 Type* get(IntType idx)
253 {
254 if (idx >= 0 and idx < static_cast<IntType>(sizeElements))
255 return items + idx;
256 return nullptr;
257 }
258
260 template <typename IntType>
261 const Type* get(IntType idx) const
262 {
263 if (idx >= 0 and idx < static_cast<IntType>(sizeElements))
264 return items + idx;
265 return nullptr;
266 }
267
269 template <typename U>
270 [[nodiscard]] bool memcmpWith(const Span<U> other) const
271 {
272 if (sizeInBytes() != other.sizeInBytes())
273 return false;
274 if (sizeInBytes() == 0)
275 return true;
276 return ::memcmp(items, other.data(), sizeInBytes()) == 0;
277 }
278
280 template <typename U>
281 [[nodiscard]] bool memcpyTo(Span<U>& other) const
282 {
283 if (other.sizeInBytes() < sizeInBytes())
284 return false;
285 ::memcpy(other.data(), items, sizeInBytes());
286 other = {other.data(), sizeInBytes() / sizeof(U)};
287 return true;
288 }
289};
290
291#if SC_PLATFORM_WINDOWS
292#define SC_NATIVE_STR(str) L##str
293#else
294#define SC_NATIVE_STR(str) str
295#endif
296
299{
300 constexpr SpanStringView() = default;
301 constexpr SpanStringView(const char* string, size_t stringLength) : text(string, stringLength) {}
302 template <size_t N>
303 constexpr SpanStringView(const char (&charLiteral)[N]) : text(charLiteral)
304 {}
305
307 template <int N>
308 [[nodiscard]] bool writeNullTerminated(char (&buffer)[N]) const
309 {
310 if (N < text.sizeInElements() + 1)
311 return false;
312 ::memcpy(&buffer[0], text.data(), text.sizeInElements());
313 buffer[N - 1] = 0;
314 return true;
315 }
316
317 Span<const char> text;
318};
319
322{
323 template <size_t N>
324 constexpr SpanString(char (&buffer)[N]) : text(buffer)
325 {}
326
327 operator SpanStringView() const
328 {
330 sv.text = text;
331 return sv;
332 }
333
334 SC::Span<char> text;
335};
unsigned long size_t
Platform independent unsigned size type.
Definition: PrimitiveTypes.h:56
View over a contiguous sequence of items (pointer + size in elements).
Definition: Span.h:32
static Span< Type > reinterpret_object(T &value)
Constructs a Span reinterpreting memory pointed by object of type T as a type Type
Definition: Span.h:83
constexpr bool find(Lambda &&lambda, SizeType *index=nullptr) const
Finds the first item in span matching criteria given by the lambda.
Definition: Span.h:229
constexpr const Type * data() const
Returns pointer to first element of the span.
Definition: Span.h:134
static Span< Type > reinterpret_bytes(VoidType *rawMemory, SizeType sizeInBytes)
Construct a span reinterpreting raw memory (void* or const void*) to Type or const Type
Definition: Span.h:89
Span< T > reinterpret_as_span_of()
Reinterprets the current span as an array of the specified type.
Definition: Span.h:95
const Type * get(IntType idx) const
Gets the item at given index or nullptr if index is negative or bigger than size.
Definition: Span.h:261
Span< const T > reinterpret_as_span_of() const
Reinterprets the current span as an array of the specified type.
Definition: Span.h:92
constexpr SizeType sizeInBytes() const
Size of Span in bytes.
Definition: Span.h:154
bool memcmpWith(const Span< U > other) const
Compares this span with another one byte by byte.
Definition: Span.h:270
constexpr bool sliceStart(SizeType offsetInElements, Span &destination) const
Creates another Span, starting at an offset in elements from current Span, until end.
Definition: Span.h:162
constexpr bool contains(const U &value, SizeType *index=nullptr) const
Check if the current span contains a given value.
Definition: Span.h:218
constexpr Span()
Builds an empty Span.
Definition: Span.h:50
constexpr Type * begin()
Returns pointer to first element of the span.
Definition: Span.h:138
constexpr Type * end()
Returns pointer to one after the last element of the span.
Definition: Span.h:142
constexpr const Type * begin() const
Returns pointer to first element of the span.
Definition: Span.h:126
Type * get(IntType idx)
Gets the item at given index or nullptr if index is negative or bigger than size.
Definition: Span.h:252
constexpr bool sliceStartLength(SizeType offsetInElements, SizeType lengthInElements, Span &destination) const
Creates another Span, starting at an offset in elements from current Span of specified length.
Definition: Span.h:179
bool memcpyTo(Span< U > &other) const
Bitwise copies contents of this Span over another (non-overlapping)
Definition: Span.h:281
constexpr Span(Type *items, SizeType sizeInElements)
Builds a Span from an array.
Definition: Span.h:55
constexpr Type * data()
Returns pointer to first element of the span.
Definition: Span.h:146
bool sliceFromStartUntil(Span other, Span &output) const
Creates another Span shorter or equal than the current one such that its end equals other....
Definition: Span.h:193
constexpr bool empty() const
Check if Span is empty.
Definition: Span.h:209
constexpr SizeType sizeInElements() const
Size of Span in elements.
Definition: Span.h:150
constexpr Span(std::initializer_list< TypeIfNotVoid< U > > list)
Span specialized constructor (mainly used for converting const char* to StringView)
Definition: Span.h:66
constexpr const Type * end() const
Returns pointer to one after the last element of the span.
Definition: Span.h:130
constexpr Span(TypeIfNotVoid< U > &type)
Builds a Span from a single object.
Definition: Span.h:60
An writable view over an ASCII string (to avoid including Strings library)
Definition: Span.h:322
An read-only view over an ASCII string (to avoid including Strings library)
Definition: Span.h:299
bool writeNullTerminated(char(&buffer)[N]) const
Writes current string view over a sized char array buffer, adding a null terminator.
Definition: Span.h:308
EnableIf conditionally defines a type if a boolean template parameter is true.
Definition: TypeTraits.h:25
IsSame evaluates to true if the provided types T and U are the same, false otherwise.
Definition: TypeTraits.h:29