Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Foundation/Memory/Common.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include <cstring>
5
9
10namespace Stroika::Foundation::Memory {
11
12 /*
13 ********************************************************************************
14 ********************************* Memory::NEltsOf ******************************
15 ********************************************************************************
16 */
17 template <typename ARRAY_TYPE, size_t SIZE_OF_ARRAY>
18 inline constexpr size_t NEltsOf ([[maybe_unused]] const ARRAY_TYPE (&arr)[SIZE_OF_ARRAY])
19 {
20 return SIZE_OF_ARRAY;
21 }
22
23 /*
24 ********************************************************************************
25 *************************** Memory::CompareBytes *******************************
26 ********************************************************************************
27 */
28 template <>
29 constexpr strong_ordering CompareBytes (const uint8_t* lhs, const uint8_t* rhs, std::size_t count)
30 {
31 DISABLE_COMPILER_MSC_WARNING_START (5063)
32 DISABLE_COMPILER_CLANG_WARNING_START ("clang diagnostic ignored \"-Wconstant-evaluated\"");
33 DISABLE_COMPILER_GCC_WARNING_START ("GCC diagnostic ignored \"-Wtautological-compare\"");
34 if constexpr (is_constant_evaluated ()) {
35 //Require (count == 0 or lhs != nullptr);
36 //Require (count == 0 or rhs != nullptr);
37 const uint8_t* li = lhs;
38 const uint8_t* ri = rhs;
39 for (; count--; ++li, ++ri) {
40 if (int cmp = static_cast<int> (*li) - static_cast<int> (*ri)) {
42 }
43 }
44 return strong_ordering::equal;
45 }
46 else {
47 if (count == 0) {
48 return strong_ordering::equal;
49 }
50 return Common::CompareResultNormalizer (std::memcmp (lhs, rhs, count));
51 }
52 DISABLE_COMPILER_GCC_WARNING_END ("GCC diagnostic ignored \"-Wtautological-compare\"");
53 DISABLE_COMPILER_MSC_WARNING_END (5063)
54 DISABLE_COMPILER_CLANG_WARNING_END ("clang diagnostic ignored \"-Wconstant-evaluated\"");
55 }
56 template <typename T>
57 constexpr strong_ordering CompareBytes (const T* lhs, const T* rhs, size_t count)
58 {
59 return CompareBytes (reinterpret_cast<const uint8_t*> (lhs), reinterpret_cast<const uint8_t*> (rhs), count * sizeof (T));
60 }
61 template <typename T>
62 constexpr strong_ordering CompareBytes (span<const T> lhs, span<const T> rhs)
63 {
64 Require (lhs.size () == rhs.size ());
65 return CompareBytes (lhs.data (), rhs.data (), lhs.size ());
66 }
67 template <typename T>
68 constexpr strong_ordering CompareBytes (span<T> lhs, span<T> rhs)
69 {
70 return CompareBytes (ConstSpan (lhs), ConstSpan (rhs));
71 }
72
73 /*
74 ********************************************************************************
75 ******************************** Memory::AsBytes *******************************
76 ********************************************************************************
77 */
78 template <typename T>
79 inline span<const byte> AsBytes (const T& elt)
80 requires (is_trivial_v<T>)
81 {
82 return as_bytes (span{&elt, 1}); //return span<const byte>{reinterpret_cast<const byte*> (&elt), sizeof (elt)};
83 }
84
85 /*
86 ********************************************************************************
87 ***************************** Memory::ConstSpan ********************************
88 ********************************************************************************
89 */
90 template <class T, size_t EXTENT>
91 constexpr span<const T, EXTENT> ConstSpan (span<T, EXTENT> s)
92 {
93 return s;
94 }
95
96 /*
97 ********************************************************************************
98 ***************************** Memory::Intersects *******************************
99 ********************************************************************************
100 */
101 template <typename T1, typename T2, size_t E1, size_t E2>
102 constexpr bool Intersects (span<T1, E1> lhs, span<T2, E2> rhs)
103 {
104 // See Range<T, TRAITS>::Intersects for explanation - avoid direct call here to avoid include file reference
105 auto lhsStart = as_bytes (lhs).data ();
106 auto rhsStart = as_bytes (rhs).data ();
107 auto lhsEnd = lhsStart + lhs.size_bytes ();
108 auto rhsEnd = rhsStart + rhs.size_bytes ();
109 if (rhsEnd <= lhsStart) {
110 return false;
111 }
112 if (rhsStart >= lhsEnd) {
113 return false;
114 }
115 if (lhs.empty () or rhs.empty ()) {
116 return false;
117 }
118 return true;
119 }
120
121 /*
122 ********************************************************************************
123 ***************************** Memory::SpanBytesCast ****************************
124 ********************************************************************************
125 */
126 template <ISpan TO_SPAN, ISpanBytesCastable<TO_SPAN> FROM_SPAN>
127 constexpr TO_SPAN SpanBytesCast (FROM_SPAN src)
128 {
129 using TO_T = typename TO_SPAN::value_type;
130 Require ((src.size_bytes () / sizeof (TO_T)) * sizeof (TO_T) == src.size_bytes ()); // else cannot map evenly
131 // allow EITHER size or constness conversions - so NOT re-interpret cast
132 TO_SPAN result{(TO_T*)(src.data ()), src.size_bytes () / sizeof (TO_T)}; // no need to worry about overflow cuz then addressses would overflow
133 Ensure (src.size_bytes () == result.size_bytes ()); // size in elements can be <, ==, or >
134 return result;
135 }
136
137 /*
138 ********************************************************************************
139 ***************************** Memory::CopyBytes ********************************
140 ********************************************************************************
141 */
142 template <Common::trivially_copyable FROM_T, size_t FROM_E, Common::trivially_copyable TO_T, size_t TO_E>
143 constexpr span<TO_T, TO_E> CopyBytes (span<FROM_T, FROM_E> src, span<TO_T, TO_E> target) noexcept
144 requires (same_as<remove_cvref_t<FROM_T>, remove_cvref_t<TO_T>>)
145 {
146 Require (src.size () <= target.size ());
147 Require (not Intersects (src, target));
148 DISABLE_COMPILER_GCC_WARNING_START ("GCC diagnostic ignored \"-Wstringop-overflow\""); // this suppress doesn't work for g++-11, so must use configure to add suppress to cmdline
149 std::copy (src.begin (), src.end (), target.data ());
150 DISABLE_COMPILER_GCC_WARNING_END ("GCC diagnostic ignored \"-Wstringop-overflow\"");
151 return target.subspan (0, src.size ());
152 }
153
154 /*
155 ********************************************************************************
156 ****************************** Memory::CopySpanData ****************************
157 ********************************************************************************
158 */
159 template <typename FROM_T, size_t FROM_E, typename TO_T, size_t TO_E>
160 constexpr span<TO_T, TO_E> CopySpanData (span<FROM_T, FROM_E> src, span<TO_T, TO_E> target)
161 requires (not is_const_v<TO_T>)
162 {
163 Require (not Intersects (src, target));
164 Require (src.size () <= target.size ()); // BUT size in BYTES need not match
165 if constexpr (Common::trivially_copyable<TO_T> and Common::trivially_copyable<FROM_T> and sizeof (FROM_T) == sizeof (TO_T)) {
166 // if elements trivially copyable, and STRIDE of elements same on both sides, can copy as bytes
167 return CopyBytes (SpanBytesCast<span<const TO_T, TO_E>> (src), target);
168 }
169 else {
170 // Do a for loop copying each element; this can be used to copy any data,
171 // like std::copy, except using spans not iterators, and no warning/error about target assignment (static_cast)
172 TO_T* tb = target.data ();
173 for (const FROM_T& i : src) {
174 *tb++ = static_cast<TO_T> (i);
175 }
176 return target.subspan (0, src.size ());
177 }
178 }
179
180 /*
181 ********************************************************************************
182 ************************ Memory::CopyOverlappingBytes **************************
183 ********************************************************************************
184 */
185 template <Common::trivially_copyable FROM_T, size_t FROM_E, Common::trivially_copyable TO_T, size_t TO_E>
186 constexpr span<TO_T, TO_E> CopyOverlappingBytes (span<FROM_T, FROM_E> src, span<TO_T, TO_E> target) noexcept
187 requires (same_as<remove_cvref_t<FROM_T>, remove_cvref_t<TO_T>>)
188 {
189 Require (src.size () <= target.size ());
190 DISABLE_COMPILER_GCC_WARNING_START ("GCC diagnostic ignored \"-Wstringop-overflow\""); // this suppress doesn't work for g++-11, so must use configure to add suppress to cmdline
191 std::copy_backward (src.begin (), src.end (), target.data ());
192 DISABLE_COMPILER_GCC_WARNING_END ("GCC diagnostic ignored \"-Wstringop-overflow\"");
193 return target.subspan (0, src.size ());
194 }
195
196 /*
197 ********************************************************************************
198 ******************************** Memory::OffsetOf ******************************
199 ********************************************************************************
200 */
201 namespace Private_ {
202 namespace OffsetOfImpl_ {
203 namespace UsingRecursiveSideStruct_ {
204 // OffsetOf_ BASED ON CODE FROM - https://gist.github.com/graphitemaster/494f21190bb2c63c5516
205#pragma pack(push, 1)
206 template <typename MEMBER, size_t N_PAD_BYTES>
207 struct Pad {
208 char pad[N_PAD_BYTES];
209 MEMBER m;
210 };
211#pragma pack(pop)
212 template <typename MEMBER>
213 struct Pad<MEMBER, 0> {
214 MEMBER m;
215 };
216
217 DISABLE_COMPILER_MSC_WARNING_START (4324);
218 template <typename BASE, typename MEMBER, size_t O>
219 struct MakeUnion {
220 union U {
221 char c;
222 BASE base;
223 Pad<MEMBER, O> pad;
224 constexpr U () noexcept
225 : c{} {};
226 ~U () = delete;
227 };
228 //constexpr static U* u{}; // old code did this, but doesn't work if MEMBER field has type which is not allowed to be constexpr --LGP 2023-08-20
229 constexpr static U* u{nullptr}; // don't actually allocate an object (maybe use declval instead?) - cuz else U not literal type sometimes if MEMBER obj type not literal
230 };
231 DISABLE_COMPILER_MSC_WARNING_END (4324);
232
233 template <typename MEMBER, typename BASE_CLASS, typename ORIG_CLASS>
234 struct offset_of_impl {
235 template <size_t off, auto union_part = MakeUnion<BASE_CLASS, MEMBER, off>::u>
236 // This gets called with nullptr as 'object' for computing diff below to avoid ever building the object (cuz just computing offsets)
237 Stroika_Foundation_Debug_ATTRIBUTE_NO_SANITIZE_UNDEFINED static constexpr ptrdiff_t offset2 (MEMBER ORIG_CLASS::* member)
238 {
239 if constexpr (off > sizeof (BASE_CLASS)) {
240 throw 1;
241 }
242 else {
243 const auto diff1 = &((static_cast<const ORIG_CLASS*> (&union_part->base))->*member);
244 const auto diff2 = &union_part->pad.m;
245 if (diff1 > diff2) {
246 constexpr auto MIN = (sizeof (MEMBER) < alignof (ORIG_CLASS)) ? sizeof (MEMBER) : alignof (ORIG_CLASS);
247 return offset2<off + MIN> (member);
248 }
249 else {
250 return off;
251 }
252 }
253 }
254 };
255
256 template <class MEMBER, class BASE_CLASS>
257 tuple<MEMBER, BASE_CLASS> get_types (MEMBER BASE_CLASS::*); // never defined, never really called, just used to extract types with decltype()
258
259 template <class TheBase = void, class TT>
260 inline constexpr size_t offset_of (TT member)
261 {
262 using T = decltype (get_types (declval<TT> ()));
263 using Member = tuple_element_t<0, T>;
264 using Orig = tuple_element_t<1, T>;
265 using Base = conditional_t<is_void_v<TheBase>, Orig, TheBase>;
266 return static_cast<size_t> (offset_of_impl<Member, Base, Orig>::template offset2<0> (member));
267 }
268 }
269
270 namespace RequiringDefaultConstructibleObjectType_ {
271 // @see https://gist.github.com/graphitemaster/494f21190bb2c63c5516 for more info on maybe how to
272 template <typename T1, typename T2>
273 struct offset_of {
274 static constexpr size_t offset (T1 T2::* member)
275 {
276 union X {
277 array<char, sizeof (T2)> bytes;
278 T2 obj;
279 X () {};
280 ~X () {};
281 } objAsUnion;
282 /*
283 * &&& maybe not undefined anymore
284 * UNDEFINED BEHAVIOR: it is undefined, but for the following reason: expr.add-5.sentence-2
285 * "If the expressions P and Q point to, respectively, elements x[i] and x[j] of
286 * the same array object x, the expression P - Q has the value i - j; otherwise, the behavior is undefined."]
287 */
288 return size_t (&(objAsUnion.obj.*member)) - size_t (&objAsUnion.obj);
289 }
290 };
291 }
292
293 namespace UsingAlignedByteArrayBuf_ {
294 template <typename FIELD_VALUE_TYPE, typename OWNING_OBJECT>
295 inline size_t offset_of (FIELD_VALUE_TYPE OWNING_OBJECT::* member)
296 {
297 // Still not totally legal for non-std-layout classes, but seems to work, and I haven't found a better way
298 // --LGP 2021-05-27
299 alignas (OWNING_OBJECT) byte buf[sizeof (OWNING_OBJECT)]{};
300 const OWNING_OBJECT& o = *reinterpret_cast<const OWNING_OBJECT*> (&buf);
301 auto result = size_t (reinterpret_cast<const char*> (&(o.*member)) - reinterpret_cast<const char*> (&o));
302 // Avoid #include - Ensure (result <= sizeof (OWNING_OBJECT));
303 return result;
304 }
305 }
306
307 namespace UseExplicitDefaultConstructibleStaticInstance_ {
308 // @see https://gist.github.com/graphitemaster/494f21190bb2c63c5516 for more info on maybe how to
309 // get this working with constexpr and without static object
310 template <typename T1, typename T2>
311 struct offset_of_ {
312 static inline constexpr T2 sObj_{};
313 static constexpr size_t offset (T1 T2::* member)
314 {
315 /*
316 * UNDEFINED BEHAVIOR: it is undefined, but for the following reason: expr.add-5.sentence-2
317 * "If the expressions P and Q point to, respectively, elements x[i] and x[j] of
318 * the same array object x, the expression P - Q has the value i - j; otherwise, the behavior is undefined."]
319 */
320 return size_t (&(offset_of_<T1, T2>::sObj_.*member)) - size_t (&offset_of_<T1, T2>::sObj_);
321 }
322 };
323 }
324
325 namespace UsingSimpleUnionToConstructActualObj_ {
326
327 template <typename OUTER_OBJECT, typename DATA_MEMBER_TYPE>
328 inline constexpr size_t offset_of (DATA_MEMBER_TYPE (OUTER_OBJECT::* dataMember))
329 {
330 // NOT real assert - just tmphack to test
331 // auto a1 = Private_::OffsetOfImpl_::UsingRecursiveSideStruct_::offset_of<OUTER_OBJECT> (dataMember);
332 // auto a2 = PRIVATE_::OffsetOfImpl_::UsingAlignedByteArrayBuf_ (dataMember);
333 // auto a3 = PRIVATE_::OffsetOfImpl_::RequiringDefaultConstructibleObjectType_::offset_of<DATA_MEMBER_TYPE, OUTER_OBJECT>::offset (dataMember);
334 //WeakAssert (a1 == a2);
335
336 union X {
337 array<char, sizeof (OUTER_OBJECT)> bytes;
338 OUTER_OBJECT obj;
339 X () {};
340 ~X () {};
341 } objAsUnion;
342
343 // auto a4 = size_t (&(objAsUnion.obj.*dataMember)) - size_t (&objAsUnion.bytes);
344
345 /*
346 * &&& maybe not undefined anymore
347 * UNDEFINED BEHAVIOR: it is undefined, but for the following reason: expr.add-5.sentence-2
348 * "If the expressions P and Q point to, respectively, elements x[i] and x[j] of
349 * the same array object x, the expression P - Q has the value i - j; otherwise, the behavior is undefined."]
350 */
351 return size_t (&(objAsUnion.obj.*dataMember)) - size_t (&objAsUnion.obj);
352
353 //https://stackoverflow.com/questions/12141446/offset-from-member-pointer-without-temporary-instance
354 // return Private_::OffsetOf2_::offset_of<OUTER_OBJECT> (dataMember);
355 }
356
357 }
358 }
359 }
360
361 template <typename OUTER_OBJECT, typename DATA_MEMBER_TYPE>
362 inline constexpr size_t OffsetOf (DATA_MEMBER_TYPE (OUTER_OBJECT::* dataMember))
363 {
364 //constexpr bool kTestAllWays_ = false;
365 constexpr bool kTestAllWays_ = true;
366 [[maybe_unused]] size_t r1;
367 [[maybe_unused]] size_t r2;
368 [[maybe_unused]] size_t r3;
369 [[maybe_unused]] size_t r4;
370 if constexpr (kTestAllWays_) {
371 /*
372 * Setup to test/try each implementation. One with apparently best shot of working constexpr is UsingRecursiveSideStruct_
373 * HOWEVER, it requires (on unix) setting -ftemplate-depth=5000 flags, and even then, doesn't really work constexpr (complains about dereferencing nullptr).
374 *
375 * Don't give up, but no success so far. MAYBE works OK/portably without the constexpr stuff. Dunno. Probably NOT for the same deref-nullptr reason.
376 */
377#if defined(_MSC_VER)
378 // Don't bother compiling for gcc/clang cuz fails on some compilers without -fdepth= flag, and no point since wont really work right even
379 // if you provide that flag. REVISIT in the future maybe, but for now don't even bother calling
380 r1 = Private_::OffsetOfImpl_::UsingRecursiveSideStruct_::offset_of<OUTER_OBJECT> (dataMember);
381#endif
382 r2 = Private_::OffsetOfImpl_::RequiringDefaultConstructibleObjectType_::offset_of<DATA_MEMBER_TYPE, OUTER_OBJECT>::offset (dataMember);
383 r3 = Private_::OffsetOfImpl_::UsingAlignedByteArrayBuf_::offset_of<DATA_MEMBER_TYPE, OUTER_OBJECT> (dataMember);
384 if constexpr (is_trivially_default_constructible_v<OUTER_OBJECT>) {
385 r4 = Private_::OffsetOfImpl_::UseExplicitDefaultConstructibleStaticInstance_::offset_of_<DATA_MEMBER_TYPE, OUTER_OBJECT>::offset (dataMember);
386 }
387 }
388 size_t r5 = Private_::OffsetOfImpl_::UsingSimpleUnionToConstructActualObj_::offset_of<OUTER_OBJECT, DATA_MEMBER_TYPE> (dataMember);
389 size_t rr = r5;
390 if (not is_constant_evaluated () and kTestAllWays_) {
391#if defined(_MSC_VER)
392 Assert (r1 == rr);
393#endif
394 Assert (r2 == rr);
395 Assert (r3 == rr);
396 if constexpr (is_trivially_default_constructible_v<OUTER_OBJECT>) {
397 Assert (r4 == rr);
398 }
399 }
400 return rr;
401 }
402
403 /*
404 ********************************************************************************
405 ********************* Memory::Literals::operator""_b ***************************
406 ********************************************************************************
407 */
408 inline namespace Literals {
409 constexpr byte operator""_b (unsigned long long b)
410 {
411 Require (b <= 0xff);
412 return static_cast<byte> (b);
413 }
414 }
415
416 ////////////////////// DEPRECATED .//////////////////////////
417 template <typename FIELD_VALUE_TYPE, typename OWNING_OBJECT>
418 [[deprecated ("Since Stroika v3.0d2 - just use OffsetOf")]] inline size_t constexpr OffsetOf_Constexpr (FIELD_VALUE_TYPE OWNING_OBJECT::* member)
419 {
420 return OffsetOf (member);
421 }
422 template <typename T>
423 [[deprecated ("Since Stroika v3.0d12 - use CompareBytes")]] constexpr strong_ordering MemCmp (const T* lhs, const T* rhs, size_t count)
424 {
425 return CompareBytes (lhs, rhs, count);
426 }
427 template <typename T>
428 [[deprecated ("Since Stroika v3.0d12 - use CompareBytes")]] constexpr strong_ordering MemCmp (span<const T> lhs, span<const T> rhs)
429 {
430 return CompareBytes (lhs, rhs);
431 }
432 template <typename T>
433 [[deprecated ("Since Stroika v3.0d12 - use CompareBytes")]] constexpr strong_ordering MemCmp (span<T> lhs, span<T> rhs)
434 {
435 return CompareBytes (lhs, rhs);
436 }
437 template <typename FROM_T, size_t FROM_E, typename TO_T, size_t TO_E>
438 [[deprecated ("Since Stroika v3.0d12 use CopyBytes")]] constexpr span<TO_T, TO_E> CopySpanData_StaticCast (span<const FROM_T, FROM_E> src,
439 span<TO_T, TO_E> target)
440 {
441 return CopySpanData (src, target);
442 }
443 template <typename FROM_T, size_t FROM_E, typename TO_T, size_t TO_E>
444 [[deprecated ("Since Stroika v3.0d12 use CopyBytes")]] constexpr std::span<TO_T, TO_E> CopySpanData_StaticCast (span<FROM_T, FROM_E> src,
445 span<TO_T, TO_E> target)
446 {
447 return CopySpanData (ConstSpan (src), target);
448 }
449
450}
bool Intersects(const set< T > &s1, const set< T > &s2)
constexpr strong_ordering CompareResultNormalizer(FROM_INT_TYPE f)
Definition Compare.inl:226