Stroika Library 3.0d18
 
Loading...
Searching...
No Matches
Foundation/Memory/Common.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Memory_Common_h_
5#define _Stroika_Foundation_Memory_Common_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <bit>
10#include <compare>
11#include <memory>
12#include <span>
13#include <type_traits>
14
15#include "Stroika/Foundation/Common/Common.h"
16#include "Stroika/Foundation/Common/Concepts.h"
17
18/**
19 */
20
21namespace Stroika::Foundation::Memory {
22
23#if qCompilerAndStdLib_illunderstood_ispan_Buggy
24 namespace Private_ {
25 template <class>
26 inline constexpr bool _Is_span_v = false;
27 template <class _Ty, size_t _Extent>
28 inline constexpr bool _Is_span_v<span<_Ty, _Extent>> = true;
29 }
30#endif
31
32 /**
33 * For when you want to assert an argument is a SPAN, but you haven't yet deduced the type its a span of yet.
34 *
35 * \note matches span<T>, span<T,EXTENT>, span<const T>, span<const T,EXTENT>, but not things that
36 * are CONVERTIBLE to span<T>
37 */
38#if qCompilerAndStdLib_illunderstood_ispan_Buggy
39 template <typename SPAN_T>
40 concept ISpan = Private_::_Is_span_v<SPAN_T>;
41#else
42 template <typename SPAN_T>
43 concept ISpan = requires (SPAN_T t) {
44 {
45 []<typename T1, size_t E1> (span<T1, E1>) {}(t)
46 };
47 };
48#endif
49#if qCompilerAndStdLib_illunderstood_ispan_Buggy
50 static_assert (ISpan<span<int>> and ISpan<span<int, 3>>);
51#else
53#endif
54 static_assert (not ISpan<std::string> and not ISpan<int> and not ISpan<vector<int>>); // we don't include <string>/<vector> in this module, but sometimes helpful to test/debug/document
55
56 /**
57 * \brief use ISpanOfT<T> as a concept declaration for parameters where you want a span, but accept either T or const T
58 *
59 * Sadly, if I declare a function
60 * f (span<int>) {}
61 * and try to call it with:
62 * f (span<const int>{}) - that fails, whereas I think, considering the logic/intent, it probably should work.
63 *
64 * Anyhow, replacing the f declaration with the (almost as clear);
65 * template <ISpanOfT<int> SPAN_OF_T>
66 * f (SPAN_OF_T) {}
67 *
68 * fixes the problem.
69 *
70 * @aliases
71 * SpanOfPossiblyConstT - but name just too long (though might be clearer).
72 *
73 * note - matches span<T>, span<T,EXTENT>, span<const T>, span<const T,EXTENT>, but not things that
74 *
75 * are CONVERTIBLE to span<T>
76 * \see https://stackoverflow.com/questions/62688814/stdspanconst-t-as-parameter-in-function-template
77 */
78 template <typename SPAN_T, typename T>
79 concept ISpanOfT = Common::IAnyOf<remove_cvref_t<SPAN_T>, span<T>, span<const T>, span<T, SPAN_T::extent>, span<const T, SPAN_T::extent>>;
80 static_assert (ISpanOfT<span<int>, int> and ISpanOfT<span<const int>, int> and ISpanOfT<span<const int, 3>, int> and not ISpanOfT<span<int>, char>);
81
82 /**
83 * \brief Can safely cast span<FROM_T,FROM_EXTENT> to a TO_SPAN (where the underlying types are POD - plain old data - types - roughly)
84 *
85 * \note - this requires the two spans to have the same number of bytes (cannot always be fully determined at compile time).
86 * But this returns true if its possible.
87 *
88 * \note its perfectly reasonable to span cast from span<uint32_t> to span<byte> - so long as the two spans have the same size_bytes()
89 *
90 * \note this also requires trivially_copyable on the types. Nothing REALLY requires that. But its more likely a bug than
91 * a feature if you are using types for which that is not true, so fail here. And force a more careful exam with explicit
92 * reinterpret_casts...
93 *
94 * \note This doesn't allow casting away constness of the underlying value_type (though it ignores the
95 * constness of the span itself).
96 */
97 template <typename TO_SPAN, typename FROM_SPAN>
100 (is_const_v<typename TO_SPAN::value_type> or not is_const_v<typename FROM_SPAN::value_type>) and
101 (sizeof (typename FROM_SPAN::value_type) % sizeof (typename TO_SPAN::value_type) == 0 or
102 sizeof (typename TO_SPAN::value_type) % sizeof (typename FROM_SPAN::value_type) == 0);
103
104 /**
105 * \brief convert a (possibly already const) span to the same span, but with const on the 'T' argument
106 *
107 * I hope this function isn't needed often. And wherever it is needed, I think is a std::span<> design
108 * defect (or something I don't yet understand about how to use span better).
109 *
110 * But it appears if you declare function argument span<const T,EXT> and pass in a non-const T span, you get
111 * error messages about failure to deduce (at least on visual studio).
112 *
113 * See https://stackoverflow.com/questions/62688814/stdspanconst-t-as-parameter-in-function-template
114 *
115 * \note this is sometimes useful to reduce deduction ambiguities, and cases where templates convert between
116 * multiple levels of deduction/inference
117 */
118 template <class T, size_t EXTENT>
119 constexpr span<const T, EXTENT> ConstSpan (span<T, EXTENT> s);
120
121 /**
122 * \brief NEltsOf(X) returns the number of elements in array argument (ie return sizeof (arg)/sizeof(arg[0]))
123 *
124 * @todo Found std::begin() could be used to replace old StartOfArray() macro -
125 * see if this too can be replaced with something in C++11?
126 */
127 template <typename ARRAY_TYPE, size_t SIZE_OF_ARRAY>
128 constexpr size_t NEltsOf ([[maybe_unused]] const ARRAY_TYPE (&arr)[SIZE_OF_ARRAY]);
129
130 /**
131 * \brief 'cast' the given POD data type argument to a span<const byte> - a bit like std::as_bytes, but any 'trivial' type T as argument (as_bytes takes span)
132 *
133 * \note only works on POD (trivially_v<T>) data
134 * \note returns address of argument, so use results before argument goes out of scope
135 * \note Similar to std::bit_cast<>, but always converts to span of bytes, and doesn't COPY anything - just 'casts' the elt to a span of bytes
136 */
137 template <typename T>
138 span<const byte> AsBytes (const T& elt)
139 requires (is_trivial_v<T>);
140
141 /**
142 * \brief - like std::memcmp() - except count is in units of T (not bytes) and this function is
143 * constexpr, and this function allows nullptr arguments (if count == 0), and it returns strong_ordering, and provides
144 * helpful overloads.
145 *
146 * @aliases memcmp, MemCmp
147 *
148 * Pointer Overload:
149 * \pre (count == 0 or lhs != nullptr);
150 * \pre (count == 0 or rhs != nullptr);
151 *
152 * Span Overload:
153 * \pre lhs.size () == rhs.size ()
154 *
155 * \note - like std::memcmp() it returns an int < 0 for less, == 0 for equal, and > 0 for greater, but that corresponds
156 * backward compatibly to the strong_ordering C++20 type, so we use that for clarity going forward.
157 *
158 * \note - This is LOGICALLY CompareBytes (span<const T> lhs, span<const T> rhs), but use use span<T> because unfortunately
159 * static_assert (not assignable_from<span<int>, span<const int>>); // which makes no sense to me
160 */
161 template <typename T>
162 constexpr strong_ordering CompareBytes (const T* lhs, const T* rhs, size_t count);
163 template <typename TL, size_t EL, typename TR, size_t ER>
164 constexpr strong_ordering CompareBytes (span<TL, EL> lhs, span<TR, ER> rhs)
165 requires (same_as<remove_cvref_t<TL>, remove_cvref_t<TR>> and is_trivial_v<TL>);
166
167 /**
168 * \brief return true iff intersection of the two spans is non-empty (contains any bytes)
169 *
170 * Note this is similar to Range::Intersects, except for the business about openness/closedness and details at the edge conditions
171 *
172 * \note - I remember from the days of segmented architectures, this may not be 100% safe, or done correctly. But occasionally important to check
173 * and not sure how todo better. Should work fine on any modern processor I'm aware of --LGP 2025-04-16
174 */
175 template <typename T1, typename T2, size_t E1, size_t E2>
176 constexpr bool Intersects (span<T1, E1> lhs, span<T2, E2> rhs);
177
178 /**
179 * \brief 'cast' a span of one thing to another, as if as_bytes, from_bytes; require span<T1...> and span<T2...> such that one T size is a multiple of the other
180 *
181 * \pre ((src.size_bytes () / sizeof (TO_T)) * sizeof (TO_T) == src.size_bytes ()); - so this doesn't change size in bytes of span
182 *
183 * This requirement on the same size in bytes of elements sizeof FROM_T must evenly divide sizeof TO_T (or the reverse).
184 * This is to allow the returned span{} to cover the same number of bytes.
185 *
186 * \note - TO_T == std::byte, this is the same as std::as_bytes or std::as_writable_bytes
187 *
188 * \post resulting span same size_bytes () as src.size_bytes().
189 *
190 * \note Though this CAN be used with fixed-extent spans, the caller must then specify the fixed extent,
191 * which must be correct. Probably works most simply if the EXTENT in the TO_SPAN is dynamic_extent (or omitted).
192 *
193 * \note until Stroika v3.0d12 this was called SpanReInterpretCast
194 *
195 * \note this allows compiling SpanBytesCast<span<uint32_t>> (span<byte>{}) - which may work fine, or may assert out if size of argument
196 * not divisible by 4 (EXAMPLE).
197 */
198 template <ISpan TO_SPAN, ISpanBytesCastable<TO_SPAN> FROM_SPAN>
199 constexpr TO_SPAN SpanBytesCast (FROM_SPAN src);
200
201 /**
202 * \brief Span-flavored memcpy/std::copy (copies from, to) - requires argument spans not overlap, requires src.size <= to.size()
203 *
204 * like std::copy, except copies the data the spans point to/reference. Target span maybe larger than src,
205 * but must (require) be no smaller than src span;
206 *
207 * \pre src.size () <= target.size () -- so that all of source can always be copied (else would need api/indicator of how much copied)
208 * \pre not Intersects (src, target) - so non-overlapping
209 *
210 * \note somewhat unlike memcpy, its fine if the spans{} are empty ()
211 *
212 * Returns the subset of the target span filled (so a subspan of target).
213 *
214 * @see also CopyOverlappingBytes for 'memmove' - same API but where the data can overlap
215 */
216 template <Common::trivially_copyable FROM_T, size_t FROM_E, Common::trivially_copyable TO_T, size_t TO_E>
217 constexpr span<TO_T, TO_E> CopyBytes (span<FROM_T, FROM_E> src, span<TO_T, TO_E> target) noexcept
218 requires (same_as<remove_cv_t<FROM_T>, TO_T>);
219
220 /**
221 * \brief Span-flavored memmove/ (copies from, to) - ALLOWING argument spans to overlap
222 *
223 * like std::copy_backward, except copies the data the spans point to/reference. Target span maybe larger than src,
224 * but must (require) be no smaller than src span;
225 *
226 * \pre src.size () <= target.size () -- so that all of source can always be copied (else would need api/indicator of how much copied)
227 *
228 * Returns the subset of the target span filled (so a subspan of target).
229 *
230 * @aliases could have been called CopyPotentiallyOverlappingBytes or CopyPossiblyOverlappingBytes
231 *
232 * @see also CopyBytes
233 */
234 template <Common::trivially_copyable FROM_T, size_t FROM_E, Common::trivially_copyable TO_T, size_t TO_E>
235 constexpr span<TO_T, TO_E> CopyOverlappingBytes (span<FROM_T, FROM_E> src, span<TO_T, TO_E> target) noexcept
236 requires (same_as<remove_cv_t<FROM_T>, TO_T>);
237
238 /*
239 * \brief Span-flavored std::copy (copies from, to), works with spans, not iterators, works with different sized from/to types
240 *
241 * Similar to CopyBytes, but works with non-trivially copyable data, as well as with differently sized
242 * data (strides), where the individuals are preserved (fully type-safe); No requirement in size_bytes on the two
243 * spans, just on the SIZE of the two spans.
244 *
245 * \pre from.size() <= to.size()
246 * \pre not Intersects (src, target) - so non-overlapping
247 *
248 * \returns the subspan of the target which was just filled in.
249 *
250 * \note @todo CONSIDER how similar/different this is from ranges::copy, or ranges_uninitialized_copy
251 *
252 * \note sizeof (FROM_T) may differ from sizeof (TO_T). So this can be used to downshift char16_t data
253 * to plain ASCII so long as the caller assures the source data is truly ascii first.
254 *
255 * \note WAS CALLED CopySpanData_StaticCast - briefly - until 3.0d12
256 */
257 template <typename FROM_T, size_t FROM_E, typename TO_T, size_t TO_E>
258 constexpr span<TO_T, TO_E> CopySpanData (span<FROM_T, FROM_E> src, span<TO_T, TO_E> target)
259 requires (not is_const_v<TO_T>);
260
261 /**
262 * like CopySpanData but src and target are POTENTIALLY overlapping
263 * BUT for this - dont need separate FROM_T and TO_T
264 *
265 * @aliases could have been called CopyPotentiallyOverlappingSpanData or CopyPossiblyOverlappingSpanData
266 */
267 template <typename T, size_t FROM_E, size_t TO_E>
268 constexpr span<T, TO_E> CopyOverlappingSpanData (span<T, FROM_E> src, span<T, TO_E> target)
269 requires (not is_const_v<T>);
270
271 /*
272 * \brief Logic to insert a span of elements into another span of elements (assuming caller externally assured enuf space)
273 *
274 * \req intoLiveSpan subspan of intoReservedSpan, starting at same offset
275 * \req enough space in intoReservedSpan to insert copyFrom (at position at) - ie at + copyFrom.size() <= intoReservedSpan.size()
276 *
277 * Leaves intoReservedSpan unchanged (doesn't allocate memory).
278 * But expands intoLiveSpan, and returns updated span.
279 *
280 * This assures the right constructors/destructors/move operators called on appropriate span elements.
281 *
282 * \note - this code assumes no exceptions copying elements. Generally safe, but not 100% guaranteed.
283 * @todo add appropriate 'T' is noexcept copyable requirement
284 *
285 * \par Example Usage
286 * \code
287 * // first ensure capacity large enuf, then...
288 * this->fSize_ = Memory::Insert (span{this->data (), size ()}, span{this->data (), capacity ()}, at, copyFrom).size ();
289 * \endcode
290 *
291 * @todo consider if we could allow 'copyFrom' to also be a 'moveFrom'. Generally wouldn't matter, but might
292 * if copy of objects was expensive, but move cheap (like std::vector = T for example).
293 */
294 template <ISpan INTO_SPAN, ISpan FROM_SPAN>
295 requires (same_as<remove_const_t<typename INTO_SPAN::value_type>, remove_const_t<typename FROM_SPAN::value_type>>)
296 remove_cvref_t<INTO_SPAN> Insert (const INTO_SPAN& intoLiveSpan, const INTO_SPAN& intoReservedSpan, size_t at, const FROM_SPAN& copyFrom) noexcept;
297
298 /*
299 * \brief Logic to remove span of elements from another span of elements (handling calling right move/ctor/dtors for elements)
300 *
301 * \req intoLiveSpan subspan of intoReservedSpan, starting at same offset
302 * \req enough space in intoReservedSpan to insert copyFrom (at position at)
303 *
304 * Leaves intoReservedSpan unchanged (doesn't allocate memory).
305 * But expands intoLiveSpan, and returns updated live span.
306 *
307 * This assures the right constructors/destructors/move operators called on appropriate span elements.
308 *
309 * \note - this code assumes no exceptions copying elements. Generally safe, but not 100% guaranteed.
310 * @todo add appropriate 'T' is noexcept copyable requirement
311 *
312 * \par Example Usage
313 * \code
314 * this->fSize_ = Memory::Remove (span{this->data (), size ()}, span{this->data (), capacity ()}, from, to).size ();
315 * \endcode
316 */
317 template <ISpan FROM_SPAN>
318 requires (not is_const_v<typename FROM_SPAN::value_type>)
319 remove_cvref_t<FROM_SPAN> Remove (FROM_SPAN&& spanToEdit, FROM_SPAN&& reservedSpan, size_t from, size_t to) noexcept;
320
321 /**
322 * \brief use Memory::OffsetOf(&CLASS::MEMBER) in place of offsetof(CLASS,MEMBER) to avoid compiler warnings, and cuz easier to
323 * map from other constructors (e.g. StructFieldMetaInfo) cuz ptr to member legit C++ object, whereas CLASS and MEMBER are not.
324 *
325 * REPLACE calls to:
326 * offsetof (CLASS, MEMBER)
327 * with:
328 * OffsetOf (&CLASS::MEMBER)
329 *
330 * \note The current implementation exploits UNDEFINED BEHAVIOR.
331 *
332 * expr.add-5.sentence-2
333 * "If the expressions P and Q point to, respectively, elements x[i] and x[j] of
334 * the same array object x, the expression P - Q has the value i - j; otherwise, the behavior is undefined.
335 *
336 * \par Example Usage
337 * \code
338 * struct Person {
339 * String firstName;
340 * String lastName;
341 * };
342 * constexpr size_t kOffset_ = OffsetOf(&Person::lastName);
343 * static_assert (OffsetOf (&Person::firstName) == 0); // NOTE - we WANT this to work, but for now cannot get constexpr stuff working
344 * \endcode
345 *
346 * \par Example Usage
347 * \code
348 * struct X1 {
349 * int a;
350 * int b;
351 * };
352 * void DoTest ()
353 * {
354 * assert (OffsetOf (&X1::a) == 0);
355 * assert (OffsetOf (&X1::b) >= sizeof (int));
356 * }
357 * \endcode
358 *
359 * @see https://gist.github.com/graphitemaster/494f21190bb2c63c5516
360 * @see https://en.cppreference.com/w/cpp/types/offsetof
361 * @see https://stackoverflow.com/questions/65940393/c-why-the-restriction-on-offsetof-for-non-standard-layout-objects-or-how-t
362 *
363 * \note Tricky to get this to work with constexpr. See implementation for details.
364 */
365 template <typename OUTER_OBJECT, typename DATA_MEMBER_TYPE>
366 constexpr size_t OffsetOf (DATA_MEMBER_TYPE (OUTER_OBJECT::* dataMember));
367
368 /**
369 * \brief UninitializedConstructorFlag::eUninitialized is a flag to some memory routines to allocate without initializing
370 *
371 * This is mainly used as a performance tweak, for objects that don't need to be initialized, and can just be copied into.
372 *
373 * \note the APIS that use this typically require static_assert (is_trivially_copyable_v<T>);
374 */
375 enum class UninitializedConstructorFlag {
376 eUninitialized,
377 eUninitializedIfTrivial
378 };
379
380 /**
381 * \brief eUninitialized is a flag to some memory routines to allocate without initializing (static_assert T is trivial)
382 *
383 * \see UninitializedConstructorFlag
384 */
385 using UninitializedConstructorFlag::eUninitialized;
386
387 /**
388 * \brief eUninitialized is a flag to some memory routines to allocate without initializing (if T is not trivial)
389 *
390 * \see UninitializedConstructorFlag
391 */
392 using UninitializedConstructorFlag::eUninitializedIfTrivial;
393
394 inline namespace Literals {
395 /**
396 * \brief A utility for declaring constant bytes (byte literals).
397 *
398 * \pre b <= 0xff
399 *
400 * \see https://stackoverflow.com/questions/75411756/how-do-i-declare-and-initialize-an-array-of-bytes-in-c
401 */
402 constexpr byte operator""_b (unsigned long long b);
403 }
404
405 /**
406 * API to return memory allocation statistics. Generally - these will be inaccurate,
407 * unless certain defines are set in Memory.cpp - but at least some stats can be
408 * returned in either case.
409 *
410 * Note - this currently only produces useful answers on windows, but can easily pull
411 * similar values out of /proc fs stuff with linux (nyi).
412 *
413 * @todo Does this belong in "Execution" module"
414 */
416 constexpr GlobalAllocationStatistics () = default;
417
418 size_t fTotalOutstandingAllocations{0};
419 size_t fTotalOutstandingBytesAllocated{0};
420 size_t fPageFaultCount{0};
421 size_t fWorkingSetSize{0};
422 size_t fPagefileUsage{0};
423 };
424 GlobalAllocationStatistics GetGlobalAllocationStatistics ();
425
426}
427
428/*
429 ********************************************************************************
430 ***************************** Implementation Details ***************************
431 ********************************************************************************
432 */
433#include "Common.inl"
434
435#endif /*_Stroika_Foundation_Memory_Common_h_*/
concept - trivial shorthand for variadic same_as A or same_as B, or ...
Definition Concepts.h:189
concept version of std::is_trivially_copyable_v
Definition Concepts.h:195
Can safely cast span<FROM_T,FROM_EXTENT> to a TO_SPAN (where the underlying types are POD - plain old...
use ISpanOfT<T> as a concept declaration for parameters where you want a span, but accept either T or...