Stroika Library 3.0d21
 
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 [[deprecated ("Since C++-17 can use std::size(), so deprecated since Stroika v3.0d21")]] constexpr size_t
129 NEltsOf ([[maybe_unused]] const ARRAY_TYPE (&arr)[SIZE_OF_ARRAY]);
130
131 /**
132 * \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)
133 *
134 * \note only works on POD (trivially_v<T>) data
135 * \note returns address of argument, so use results before argument goes out of scope
136 * \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
137 */
138 template <typename T>
139 span<const byte> AsBytes (const T& elt)
140 requires (is_trivial_v<T>);
141
142 /**
143 * \brief - like std::memcmp() - except count is in units of T (not bytes) and this function is
144 * constexpr, and this function allows nullptr arguments (if count == 0), and it returns strong_ordering, and provides
145 * helpful overloads.
146 *
147 * @aliases memcmp, MemCmp
148 *
149 * Pointer Overload:
150 * \pre (count == 0 or lhs != nullptr);
151 * \pre (count == 0 or rhs != nullptr);
152 *
153 * Span Overload:
154 * \pre lhs.size () == rhs.size ()
155 *
156 * \note - like std::memcmp() it returns an int < 0 for less, == 0 for equal, and > 0 for greater, but that corresponds
157 * backward compatibly to the strong_ordering C++20 type, so we use that for clarity going forward.
158 *
159 * \note - This is LOGICALLY CompareBytes (span<const T> lhs, span<const T> rhs), but use use span<T> because unfortunately
160 * static_assert (not assignable_from<span<int>, span<const int>>); // which makes no sense to me
161 */
162 template <typename T>
163 constexpr strong_ordering CompareBytes (const T* lhs, const T* rhs, size_t count);
164 template <typename TL, size_t EL, typename TR, size_t ER>
165 constexpr strong_ordering CompareBytes (span<TL, EL> lhs, span<TR, ER> rhs)
166 requires (same_as<remove_cvref_t<TL>, remove_cvref_t<TR>> and is_trivial_v<TL>);
167
168 /**
169 * \brief return true iff intersection of the two spans is non-empty (contains any bytes)
170 *
171 * Note this is similar to Range::Intersects, except for the business about openness/closedness and details at the edge conditions
172 *
173 * \note - I remember from the days of segmented architectures, this may not be 100% safe, or done correctly. But occasionally important to check
174 * and not sure how todo better. Should work fine on any modern processor I'm aware of --LGP 2025-04-16
175 */
176 template <typename T1, typename T2, size_t E1, size_t E2>
177 constexpr bool Intersects (span<T1, E1> lhs, span<T2, E2> rhs);
178
179 /**
180 * \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
181 *
182 * \pre ((src.size_bytes () / sizeof (TO_T)) * sizeof (TO_T) == src.size_bytes ()); - so this doesn't change size in bytes of span
183 *
184 * This requirement on the same size in bytes of elements sizeof FROM_T must evenly divide sizeof TO_T (or the reverse).
185 * This is to allow the returned span{} to cover the same number of bytes.
186 *
187 * \note - TO_T == std::byte, this is the same as std::as_bytes or std::as_writable_bytes
188 *
189 * \post resulting span same size_bytes () as src.size_bytes().
190 *
191 * \note Though this CAN be used with fixed-extent spans, the caller must then specify the fixed extent,
192 * which must be correct. Probably works most simply if the EXTENT in the TO_SPAN is dynamic_extent (or omitted).
193 *
194 * \note until Stroika v3.0d12 this was called SpanReInterpretCast
195 *
196 * \note this allows compiling SpanBytesCast<span<uint32_t>> (span<byte>{}) - which may work fine, or may assert out if size of argument
197 * not divisible by 4 (EXAMPLE).
198 */
199 template <ISpan TO_SPAN, ISpanBytesCastable<TO_SPAN> FROM_SPAN>
200 constexpr TO_SPAN SpanBytesCast (FROM_SPAN src);
201
202 /**
203 * \brief Span-flavored memcpy/std::copy (copies from, to) - requires argument spans not overlap, requires src.size <= to.size()
204 *
205 * like std::copy, except copies the data the spans point to/reference. Target span maybe larger than src,
206 * but must (require) be no smaller than src span;
207 *
208 * \pre src.size () <= target.size () -- so that all of source can always be copied (else would need api/indicator of how much copied)
209 * \pre not Intersects (src, target) - so non-overlapping
210 *
211 * @aliases CopyNonOverlappingBytes
212 *
213 * \note somewhat unlike memcpy, its fine if the spans{} are empty ()
214 *
215 * Returns the subset of the target span filled (so a subspan of target).
216 *
217 * @see also CopyOverlappingBytes for 'memmove' - same API but where the data can overlap
218 */
219 template <Common::trivially_copyable FROM_T, size_t FROM_E, Common::trivially_copyable TO_T, size_t TO_E>
220 constexpr span<TO_T, TO_E> CopyBytes (span<FROM_T, FROM_E> src, span<TO_T, TO_E> target) noexcept
221 requires (same_as<remove_cv_t<FROM_T>, TO_T>);
222
223 /**
224 * \brief Span-flavored memmove/ (copies from, to) - ALLOWING argument spans to overlap
225 *
226 * like std::copy_backward, except copies the data the spans point to/reference. Target span maybe larger than src,
227 * but must (require) be no smaller than src span;
228 *
229 * \pre src.size () <= target.size () -- so that all of source can always be copied (else would need api/indicator of how much copied)
230 *
231 * Returns the subset of the target span filled (so a subspan of target).
232 *
233 * @aliases could have been called CopyPotentiallyOverlappingBytes or CopyPossiblyOverlappingBytes
234 *
235 * @see also CopyBytes
236 */
237 template <Common::trivially_copyable FROM_T, size_t FROM_E, Common::trivially_copyable TO_T, size_t TO_E>
238 constexpr span<TO_T, TO_E> CopyOverlappingBytes (span<FROM_T, FROM_E> src, span<TO_T, TO_E> target) noexcept
239 requires (same_as<remove_cv_t<FROM_T>, TO_T>);
240
241 /*
242 * \brief Span-flavored std::copy (copies from, to), works with spans, not iterators, works with different sized from/to types
243 *
244 * Similar to CopyBytes, but works with non-trivially copyable data, as well as with differently sized
245 * data (strides), where the individuals are preserved (fully type-safe); No requirement in size_bytes on the two
246 * spans, just on the SIZE of the two spans.
247 *
248 * \pre from.size() <= to.size()
249 * \pre not Intersects (src, target) - so non-overlapping
250 *
251 * @aliases Could be called CopyNonOverlappingSpanData
252 *
253 * \returns the subspan of the target which was just filled in.
254 *
255 * \note @todo CONSIDER how similar/different this is from ranges::copy, or ranges_uninitialized_copy
256 *
257 * \note sizeof (FROM_T) may differ from sizeof (TO_T). So this can be used to downshift char16_t data
258 * to plain ASCII so long as the caller assures the source data is truly ascii first.
259 *
260 * \note WAS CALLED CopySpanData_StaticCast - briefly - until 3.0d12
261 */
262 template <typename FROM_T, size_t FROM_E, typename TO_T, size_t TO_E>
263 constexpr span<TO_T, TO_E> CopySpanData (span<FROM_T, FROM_E> src, span<TO_T, TO_E> target)
264 requires (not is_const_v<TO_T>);
265
266 /**
267 * like CopySpanData but src and target are POTENTIALLY overlapping
268 * BUT for this - dont need separate FROM_T and TO_T
269 *
270 * @aliases could have been called CopyPotentiallyOverlappingSpanData or CopyPossiblyOverlappingSpanData
271 */
272 template <typename T, size_t FROM_E, size_t TO_E>
273 constexpr span<T, TO_E> CopyOverlappingSpanData (span<T, FROM_E> src, span<T, TO_E> target)
274 requires (not is_const_v<T>);
275
276 /*
277 * \brief Logic to insert a span of elements into another span of elements (assuming caller externally assured enuf space)
278 *
279 * \req intoLiveSpan subspan of intoReservedSpan, starting at same offset
280 * \req enough space in intoReservedSpan to insert copyFrom (at position at) - ie at + copyFrom.size() <= intoReservedSpan.size()
281 *
282 * Leaves intoReservedSpan unchanged (doesn't allocate memory).
283 * But expands intoLiveSpan, and returns updated span.
284 *
285 * This assures the right constructors/destructors/move operators called on appropriate span elements.
286 *
287 * \note - this code assumes no exceptions copying elements. Generally safe, but not 100% guaranteed.
288 * @todo add appropriate 'T' is noexcept copyable requirement
289 *
290 * \par Example Usage
291 * \code
292 * // first ensure capacity large enuf, then...
293 * this->fSize_ = Memory::Insert (span{this->data (), size ()}, span{this->data (), capacity ()}, at, copyFrom).size ();
294 * \endcode
295 *
296 * @todo consider if we could allow 'copyFrom' to also be a 'moveFrom'. Generally wouldn't matter, but might
297 * if copy of objects was expensive, but move cheap (like std::vector = T for example).
298 */
299 template <ISpan INTO_SPAN, ISpan FROM_SPAN>
300 requires (same_as<remove_const_t<typename INTO_SPAN::value_type>, remove_const_t<typename FROM_SPAN::value_type>>)
301 remove_cvref_t<INTO_SPAN> Insert (const INTO_SPAN& intoLiveSpan, const INTO_SPAN& intoReservedSpan, size_t at, const FROM_SPAN& copyFrom) noexcept;
302
303 /*
304 * \brief Logic to remove span of elements from another span of elements (handling calling right move/ctor/dtors for elements)
305 *
306 * \req intoLiveSpan subspan of intoReservedSpan, starting at same offset
307 * \req enough space in intoReservedSpan to insert copyFrom (at position at)
308 *
309 * Leaves intoReservedSpan unchanged (doesn't allocate memory).
310 * But expands intoLiveSpan, and returns updated live span.
311 *
312 * This assures the right constructors/destructors/move operators called on appropriate span elements.
313 *
314 * \note - this code assumes no exceptions copying elements. Generally safe, but not 100% guaranteed.
315 * @todo add appropriate 'T' is noexcept copyable requirement
316 *
317 * \par Example Usage
318 * \code
319 * this->fSize_ = Memory::Remove (span{this->data (), size ()}, span{this->data (), capacity ()}, from, to).size ();
320 * \endcode
321 */
322 template <ISpan FROM_SPAN>
323 requires (not is_const_v<typename FROM_SPAN::value_type>)
324 remove_cvref_t<FROM_SPAN> Remove (FROM_SPAN&& spanToEdit, FROM_SPAN&& reservedSpan, size_t from, size_t to) noexcept;
325
326 /**
327 * \brief use Memory::OffsetOf(&CLASS::MEMBER) in place of offsetof(CLASS,MEMBER) to avoid compiler warnings, and cuz easier to
328 * map from other constructors (e.g. StructFieldMetaInfo) cuz ptr to member legit C++ object, whereas CLASS and MEMBER are not.
329 *
330 * REPLACE calls to:
331 * offsetof (CLASS, MEMBER)
332 * with:
333 * OffsetOf (&CLASS::MEMBER)
334 *
335 * \note The current implementation exploits UNDEFINED BEHAVIOR.
336 *
337 * expr.add-5.sentence-2
338 * "If the expressions P and Q point to, respectively, elements x[i] and x[j] of
339 * the same array object x, the expression P - Q has the value i - j; otherwise, the behavior is undefined.
340 *
341 * \par Example Usage
342 * \code
343 * struct Person {
344 * String firstName;
345 * String lastName;
346 * };
347 * constexpr size_t kOffset_ = OffsetOf(&Person::lastName);
348 * static_assert (OffsetOf (&Person::firstName) == 0); // NOTE - we WANT this to work, but for now cannot get constexpr stuff working
349 * \endcode
350 *
351 * \par Example Usage
352 * \code
353 * struct X1 {
354 * int a;
355 * int b;
356 * };
357 * void DoTest ()
358 * {
359 * assert (OffsetOf (&X1::a) == 0);
360 * assert (OffsetOf (&X1::b) >= sizeof (int));
361 * }
362 * \endcode
363 *
364 * @see https://gist.github.com/graphitemaster/494f21190bb2c63c5516
365 * @see https://en.cppreference.com/w/cpp/types/offsetof
366 * @see https://stackoverflow.com/questions/65940393/c-why-the-restriction-on-offsetof-for-non-standard-layout-objects-or-how-t
367 *
368 * \note Tricky to get this to work with constexpr. See implementation for details.
369 */
370 template <typename OUTER_OBJECT, typename DATA_MEMBER_TYPE>
371 constexpr size_t OffsetOf (DATA_MEMBER_TYPE (OUTER_OBJECT::* dataMember));
372
373 /**
374 * \brief UninitializedConstructorFlag::eUninitialized is a flag to some memory routines to allocate without initializing
375 *
376 * This is mainly used as a performance tweak, for objects that don't need to be initialized, and can just be copied into.
377 *
378 * \note the APIS that use this typically require static_assert (is_trivially_copyable_v<T>);
379 */
380 enum class UninitializedConstructorFlag {
381 eUninitialized,
382 eUninitializedIfTrivial
383 };
384
385 /**
386 * \brief eUninitialized is a flag to some memory routines to allocate without initializing (static_assert T is trivial)
387 *
388 * \see UninitializedConstructorFlag
389 */
390 using UninitializedConstructorFlag::eUninitialized;
391
392 /**
393 * \brief eUninitialized is a flag to some memory routines to allocate without initializing (if T is not trivial)
394 *
395 * \see UninitializedConstructorFlag
396 */
397 using UninitializedConstructorFlag::eUninitializedIfTrivial;
398
399 inline namespace Literals {
400 /**
401 * \brief A utility for declaring constant bytes (byte literals).
402 *
403 * \pre b <= 0xff
404 *
405 * \see https://stackoverflow.com/questions/75411756/how-do-i-declare-and-initialize-an-array-of-bytes-in-c
406 */
407 constexpr byte operator""_b (unsigned long long b);
408 }
409
410 /**
411 * API to return memory allocation statistics. Generally - these will be inaccurate,
412 * unless certain defines are set in Memory.cpp - but at least some stats can be
413 * returned in either case.
414 *
415 * Note - this currently only produces useful answers on windows, but can easily pull
416 * similar values out of /proc fs stuff with linux (nyi).
417 *
418 * @todo Does this belong in "Execution" module"
419 */
421 constexpr GlobalAllocationStatistics () = default;
422
423 size_t fTotalOutstandingAllocations{0};
424 size_t fTotalOutstandingBytesAllocated{0};
425 size_t fPageFaultCount{0};
426 size_t fWorkingSetSize{0};
427 size_t fPagefileUsage{0};
428 };
429 GlobalAllocationStatistics GetGlobalAllocationStatistics ();
430
431}
432
433/*
434 ********************************************************************************
435 ***************************** Implementation Details ***************************
436 ********************************************************************************
437 */
438#include "Common.inl"
439
440#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...