Stroika Library 3.0d20
 
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 * @aliases CopyNonOverlappingBytes
211 *
212 * \note somewhat unlike memcpy, its fine if the spans{} are empty ()
213 *
214 * Returns the subset of the target span filled (so a subspan of target).
215 *
216 * @see also CopyOverlappingBytes for 'memmove' - same API but where the data can overlap
217 */
218 template <Common::trivially_copyable FROM_T, size_t FROM_E, Common::trivially_copyable TO_T, size_t TO_E>
219 constexpr span<TO_T, TO_E> CopyBytes (span<FROM_T, FROM_E> src, span<TO_T, TO_E> target) noexcept
220 requires (same_as<remove_cv_t<FROM_T>, TO_T>);
221
222 /**
223 * \brief Span-flavored memmove/ (copies from, to) - ALLOWING argument spans to overlap
224 *
225 * like std::copy_backward, except copies the data the spans point to/reference. Target span maybe larger than src,
226 * but must (require) be no smaller than src span;
227 *
228 * \pre src.size () <= target.size () -- so that all of source can always be copied (else would need api/indicator of how much copied)
229 *
230 * Returns the subset of the target span filled (so a subspan of target).
231 *
232 * @aliases could have been called CopyPotentiallyOverlappingBytes or CopyPossiblyOverlappingBytes
233 *
234 * @see also CopyBytes
235 */
236 template <Common::trivially_copyable FROM_T, size_t FROM_E, Common::trivially_copyable TO_T, size_t TO_E>
237 constexpr span<TO_T, TO_E> CopyOverlappingBytes (span<FROM_T, FROM_E> src, span<TO_T, TO_E> target) noexcept
238 requires (same_as<remove_cv_t<FROM_T>, TO_T>);
239
240 /*
241 * \brief Span-flavored std::copy (copies from, to), works with spans, not iterators, works with different sized from/to types
242 *
243 * Similar to CopyBytes, but works with non-trivially copyable data, as well as with differently sized
244 * data (strides), where the individuals are preserved (fully type-safe); No requirement in size_bytes on the two
245 * spans, just on the SIZE of the two spans.
246 *
247 * \pre from.size() <= to.size()
248 * \pre not Intersects (src, target) - so non-overlapping
249 *
250 * @aliases Could be called CopyNonOverlappingSpanData
251 *
252 * \returns the subspan of the target which was just filled in.
253 *
254 * \note @todo CONSIDER how similar/different this is from ranges::copy, or ranges_uninitialized_copy
255 *
256 * \note sizeof (FROM_T) may differ from sizeof (TO_T). So this can be used to downshift char16_t data
257 * to plain ASCII so long as the caller assures the source data is truly ascii first.
258 *
259 * \note WAS CALLED CopySpanData_StaticCast - briefly - until 3.0d12
260 */
261 template <typename FROM_T, size_t FROM_E, typename TO_T, size_t TO_E>
262 constexpr span<TO_T, TO_E> CopySpanData (span<FROM_T, FROM_E> src, span<TO_T, TO_E> target)
263 requires (not is_const_v<TO_T>);
264
265 /**
266 * like CopySpanData but src and target are POTENTIALLY overlapping
267 * BUT for this - dont need separate FROM_T and TO_T
268 *
269 * @aliases could have been called CopyPotentiallyOverlappingSpanData or CopyPossiblyOverlappingSpanData
270 */
271 template <typename T, size_t FROM_E, size_t TO_E>
272 constexpr span<T, TO_E> CopyOverlappingSpanData (span<T, FROM_E> src, span<T, TO_E> target)
273 requires (not is_const_v<T>);
274
275 /*
276 * \brief Logic to insert a span of elements into another span of elements (assuming caller externally assured enuf space)
277 *
278 * \req intoLiveSpan subspan of intoReservedSpan, starting at same offset
279 * \req enough space in intoReservedSpan to insert copyFrom (at position at) - ie at + copyFrom.size() <= intoReservedSpan.size()
280 *
281 * Leaves intoReservedSpan unchanged (doesn't allocate memory).
282 * But expands intoLiveSpan, and returns updated span.
283 *
284 * This assures the right constructors/destructors/move operators called on appropriate span elements.
285 *
286 * \note - this code assumes no exceptions copying elements. Generally safe, but not 100% guaranteed.
287 * @todo add appropriate 'T' is noexcept copyable requirement
288 *
289 * \par Example Usage
290 * \code
291 * // first ensure capacity large enuf, then...
292 * this->fSize_ = Memory::Insert (span{this->data (), size ()}, span{this->data (), capacity ()}, at, copyFrom).size ();
293 * \endcode
294 *
295 * @todo consider if we could allow 'copyFrom' to also be a 'moveFrom'. Generally wouldn't matter, but might
296 * if copy of objects was expensive, but move cheap (like std::vector = T for example).
297 */
298 template <ISpan INTO_SPAN, ISpan FROM_SPAN>
299 requires (same_as<remove_const_t<typename INTO_SPAN::value_type>, remove_const_t<typename FROM_SPAN::value_type>>)
300 remove_cvref_t<INTO_SPAN> Insert (const INTO_SPAN& intoLiveSpan, const INTO_SPAN& intoReservedSpan, size_t at, const FROM_SPAN& copyFrom) noexcept;
301
302 /*
303 * \brief Logic to remove span of elements from another span of elements (handling calling right move/ctor/dtors for elements)
304 *
305 * \req intoLiveSpan subspan of intoReservedSpan, starting at same offset
306 * \req enough space in intoReservedSpan to insert copyFrom (at position at)
307 *
308 * Leaves intoReservedSpan unchanged (doesn't allocate memory).
309 * But expands intoLiveSpan, and returns updated live span.
310 *
311 * This assures the right constructors/destructors/move operators called on appropriate span elements.
312 *
313 * \note - this code assumes no exceptions copying elements. Generally safe, but not 100% guaranteed.
314 * @todo add appropriate 'T' is noexcept copyable requirement
315 *
316 * \par Example Usage
317 * \code
318 * this->fSize_ = Memory::Remove (span{this->data (), size ()}, span{this->data (), capacity ()}, from, to).size ();
319 * \endcode
320 */
321 template <ISpan FROM_SPAN>
322 requires (not is_const_v<typename FROM_SPAN::value_type>)
323 remove_cvref_t<FROM_SPAN> Remove (FROM_SPAN&& spanToEdit, FROM_SPAN&& reservedSpan, size_t from, size_t to) noexcept;
324
325 /**
326 * \brief use Memory::OffsetOf(&CLASS::MEMBER) in place of offsetof(CLASS,MEMBER) to avoid compiler warnings, and cuz easier to
327 * map from other constructors (e.g. StructFieldMetaInfo) cuz ptr to member legit C++ object, whereas CLASS and MEMBER are not.
328 *
329 * REPLACE calls to:
330 * offsetof (CLASS, MEMBER)
331 * with:
332 * OffsetOf (&CLASS::MEMBER)
333 *
334 * \note The current implementation exploits UNDEFINED BEHAVIOR.
335 *
336 * expr.add-5.sentence-2
337 * "If the expressions P and Q point to, respectively, elements x[i] and x[j] of
338 * the same array object x, the expression P - Q has the value i - j; otherwise, the behavior is undefined.
339 *
340 * \par Example Usage
341 * \code
342 * struct Person {
343 * String firstName;
344 * String lastName;
345 * };
346 * constexpr size_t kOffset_ = OffsetOf(&Person::lastName);
347 * static_assert (OffsetOf (&Person::firstName) == 0); // NOTE - we WANT this to work, but for now cannot get constexpr stuff working
348 * \endcode
349 *
350 * \par Example Usage
351 * \code
352 * struct X1 {
353 * int a;
354 * int b;
355 * };
356 * void DoTest ()
357 * {
358 * assert (OffsetOf (&X1::a) == 0);
359 * assert (OffsetOf (&X1::b) >= sizeof (int));
360 * }
361 * \endcode
362 *
363 * @see https://gist.github.com/graphitemaster/494f21190bb2c63c5516
364 * @see https://en.cppreference.com/w/cpp/types/offsetof
365 * @see https://stackoverflow.com/questions/65940393/c-why-the-restriction-on-offsetof-for-non-standard-layout-objects-or-how-t
366 *
367 * \note Tricky to get this to work with constexpr. See implementation for details.
368 */
369 template <typename OUTER_OBJECT, typename DATA_MEMBER_TYPE>
370 constexpr size_t OffsetOf (DATA_MEMBER_TYPE (OUTER_OBJECT::* dataMember));
371
372 /**
373 * \brief UninitializedConstructorFlag::eUninitialized is a flag to some memory routines to allocate without initializing
374 *
375 * This is mainly used as a performance tweak, for objects that don't need to be initialized, and can just be copied into.
376 *
377 * \note the APIS that use this typically require static_assert (is_trivially_copyable_v<T>);
378 */
379 enum class UninitializedConstructorFlag {
380 eUninitialized,
381 eUninitializedIfTrivial
382 };
383
384 /**
385 * \brief eUninitialized is a flag to some memory routines to allocate without initializing (static_assert T is trivial)
386 *
387 * \see UninitializedConstructorFlag
388 */
389 using UninitializedConstructorFlag::eUninitialized;
390
391 /**
392 * \brief eUninitialized is a flag to some memory routines to allocate without initializing (if T is not trivial)
393 *
394 * \see UninitializedConstructorFlag
395 */
396 using UninitializedConstructorFlag::eUninitializedIfTrivial;
397
398 inline namespace Literals {
399 /**
400 * \brief A utility for declaring constant bytes (byte literals).
401 *
402 * \pre b <= 0xff
403 *
404 * \see https://stackoverflow.com/questions/75411756/how-do-i-declare-and-initialize-an-array-of-bytes-in-c
405 */
406 constexpr byte operator""_b (unsigned long long b);
407 }
408
409 /**
410 * API to return memory allocation statistics. Generally - these will be inaccurate,
411 * unless certain defines are set in Memory.cpp - but at least some stats can be
412 * returned in either case.
413 *
414 * Note - this currently only produces useful answers on windows, but can easily pull
415 * similar values out of /proc fs stuff with linux (nyi).
416 *
417 * @todo Does this belong in "Execution" module"
418 */
420 constexpr GlobalAllocationStatistics () = default;
421
422 size_t fTotalOutstandingAllocations{0};
423 size_t fTotalOutstandingBytesAllocated{0};
424 size_t fPageFaultCount{0};
425 size_t fWorkingSetSize{0};
426 size_t fPagefileUsage{0};
427 };
428 GlobalAllocationStatistics GetGlobalAllocationStatistics ();
429
430}
431
432/*
433 ********************************************************************************
434 ***************************** Implementation Details ***************************
435 ********************************************************************************
436 */
437#include "Common.inl"
438
439#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...