ALib C++ Framework
by
Library Version: 2605 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
allocation.hpp
Go to the documentation of this file.
1//==================================================================================================
2/// \file
3/// This header-file is part of module \alib_lang of the \aliblong.
4///
5/// Copyright 2013-2026 A-Worx GmbH, Germany.
6/// Published under #"mainpage_license".
7//==================================================================================================
8ALIB_EXPORT namespace alib { namespace lang {
9
10/// Provides debugging utility methods used by allocator types #"HeapAllocator",
11/// #"TMonoAllocator", and #"TPoolAllocator".
12/// Each method is empty and thus optimized out in case the configuration macro
13/// #"ALIB_DEBUG_ALLOCATIONS" is not set.
14/// @see Chapter #"alib_contmono_further_debug" of the Programmer's Manual.
15struct DbgAlloc {
16 /// Adds needed space for \b 4 magic bytes plus the <c>size_t</c> value that is stored.
17 /// @tparam TIntegral The type of given \p{size}.
18 /// (Deduced by the compiler, templated for convenience.)
19 /// @param size The given size or the extended size.
20 /// @return The truly allocated size.
21 template<typename TIntegral>
22 static constexpr size_t extSize (TIntegral size) {
23 #if !ALIB_DEBUG_ALLOCATIONS
24 return size_t(size);
25 #else
26 return size_t(size) + 4 + sizeof(size_t);
27 #endif
28 }
29
30 /// Writes magic bytes and size-information behind the given memory.
31 /// @tparam T The type of the given pointer \p{mem}.
32 /// (Deduced by the compiler, templated for convenience.)
33 /// @param mem The pointer to the allocated object.
34 /// @param size The originally requested size.
35 /// @param magic The magic byte to write after the element.
36 template<typename T>
37 static constexpr void annotate(T* mem, size_t size, unsigned char magic) {
38 #if ALIB_DEBUG_ALLOCATIONS
39 auto* cMem= reinterpret_cast<unsigned char*>(mem);
40 size_t idx= size;
41 for( ; idx< size + 4; ++idx)
42 cMem[idx] = magic;
43 for( size_t i= 0; i< sizeof(size_t); ++i) {
44 cMem[idx++]= static_cast<unsigned char>( size & 0xFF );
45 size>>=8;
46 }
47 #else
48 (void) mem; (void) size; (void) magic;
49 #endif
50 }
51
52 /// The given character \p{c} is written into the allocated memory (including its extended
53 /// size).
54 /// @tparam T The type of the given pointer \p{mem}. (Deduced by the compiler.)
55 /// @param mem The pointer to the allocated object.
56 /// @param size The allocated size from the user's perspective (not the real size)
57 /// @param magic The magic byte to overwrite the freed memory with.
58 template<typename T>
59 static constexpr void clearMem(T* mem, size_t size, unsigned char magic) {
60 #if ALIB_DEBUG_ALLOCATIONS
61 memset( reinterpret_cast<char*>(mem), magic, extSize(size) );
62 #else
63 (void) mem; (void) size; (void) magic;
64 #endif
65 }
66
68 /// #"alib_mod_assert;Raises an ALib error" if the piece of allocated memory is
69 /// corrupted or its allocation size is not rightfully given by the using code.
70 ///
71 /// @tparam TSize The type of parameter \p{size}. (Deduced by the compiler.)
72 /// @param mem The address of the allocated object.
73 /// @param size The requested allocation size of the object.
74 /// @param magic The value of the magic bytes that had been written with #"annotate".
75 /// @param name The type of the allocator.
76 /// In case of type #"%MonoAllocator", the debug-name of the instance.
77 template<typename TSize>
78 static
79 void checkMem( void* mem, const TSize size, unsigned char magic, const char* name ) {
80 #if ALIB_DEBUG_ALLOCATIONS
81 auto* cMem= static_cast<unsigned char*>(mem);
82 size_t idx= size;
83 while ( idx< size + 4)
84 if( cMem[idx++] != magic )
85 ALIB_ERROR( "MONOMEM", "Corrupt memory with allocator ", name )
86
87 size_t storedSize= 0;
88 idx+= sizeof(size_t);
89 do
90 {
91 storedSize<<= 8;
92 storedSize|= cMem[--idx];
93 }
94 while( idx > size + 4);
95
96 if( storedSize != size )
97 ALIB_ERROR( "MONOMEM", "Given size does not match the allocated size "
98 "(or corrupt memory). Allocator: ", name )
99 #else
100 (void) mem; (void) size; (void) magic; (void) name;
101 #endif
102 }
103 #include "ALib.Lang.CIMethods.H"
104
105}; // struct DbgAlloc
106
107#if DOXYGEN
108//==================================================================================================
109/// This is class does not exist but is just exposed in the documentation of \alib.
110/// Its purpose is to precisely define what an \alib "Allocator" needs to offer as an interface
111/// to be usable with \alib template types that expect an allocator.
112///
113/// Implementations of this interface exist with \alib.:
114/// - #"HeapAllocator",
115/// - #"TMonoAllocator", its derivate
116/// - #"TLocalAllocator", and
117/// - #"TPoolAllocator".
118///
119/// The first is just using \c std::malloc and \c std::free, hence it does just the same
120/// allocation that C++ keywords <c>new</c> and <c>delete</c> do. The others are introduced and
121/// discussed by module \alib_monomem.
122///
123/// As an exception to the #"alib_manual_appendix_naming;naming conventions used by ALib",
124/// the members of this type start with a non-capital letter, although they are declared \c public.
125/// This was done to emphasize that an indirect use of this type through type
126/// #"AllocatorInterface" is available with #".operator()()", and such usage is preferred.
127///
128/// Along with this pure documentation class, a C++20 concept exists with
129/// #"IsAllocator", which duly checks if all entities defined with this type are present
130/// in a custom allocator implementation.
131/// Whenever an allocator is expected as a template type, this concept is required to be satisfied.
132//==================================================================================================
134{
135 /// The type of the allocator that this allocator uses underneath for its allocations.
136 /// In case of #"HeapAllocator", this type is defined as <c>void</c>.<br>
137 /// Allocators provided by \alib allow to access the instance with publicly inherited methods
138 /// #"AllocatorMember::GetAllocator" and #"AllocatorMember::AIF".
139 using ChainedAllocator = TAllocator;
140
141 /// The minimum alignment an allocator supports, respectively uses even if lower values
142 /// are requested.
143 /// @see Chapter #"alib_contmono_further_alignment" of the Programmer's Manual of module
144 /// \alib_monomem.
145 static constexpr size_t MIN_ALIGNMENT;
146
147 /// The maximum alignment an allocator supports.
148 /// @see Chapter #"alib_contmono_further_alignment" of the Programmer's Manual of module
149 /// \alib_monomem.
150 static constexpr size_t MAX_ALIGNMENT;
151
152 /// Allocate memory using the allocator.<br>
153 /// Note, that parameter \p{size} is provided as a reference. If an allocator implementation
154 /// allocates bigger pieces than requested (which is true for
155 /// #"TPoolAllocator;PoolAllocator"), then this parameter is modified to this higher
156 /// value.
157 /// @param[in,out] size The size of memory the block to allocate in bytes. This is an input and
158 /// output parameter. Might be increased to the truly allocated size.
159 /// In case it was increased, the new value does not need to be passed
160 /// back to #".reallocate" or #".free". Allocators are agnostic which of the
161 /// sizes are passed back, the requested, or the true size of the block.
162 /// @param alignment The (minimum) alignment of the memory block to allocate in bytes.
163 /// @return Pointer to the allocated memory.
164 void* allocate( size_t& size, size_t alignment );
165
166 /// Shrinks or grows a piece of memory. If a new allocation was performed the existing
167 /// data is copied.
168 /// Note, that parameter \p{newSize} is provided as a reference. If an allocator implementation
169 /// allocates bigger pieces than requested (which is true for
170 /// #"TPoolAllocator;PoolAllocator"), then this parameter is modified to this higher
171 /// value.
172 /// @param mem The memory to reallocate.
173 /// @param oldSize The current size of \p{mem}.
174 /// @param[in,out] newSize The new size of memory requested to allocate in bytes. This is an
175 /// input and output parameter. It might be increased to the truly
176 /// allocated size.
177 /// @param alignment The (minimum) alignment of the memory block to allocate in bytes.
178 /// @return Pointer to the re-allocated memory.
179 void* reallocate( void* mem, size_t oldSize, size_t& newSize, size_t alignment );
180
181 /// Frees memory that was previously allocated with the same allocator.
182 ///
183 /// @param mem The memory to dispose.
184 /// @param size The size of the given \p{mem}.
185 void free(void* mem, size_t size);
186
187 /// Returns a temporary object (which is usually optimized out together with a call to this
188 /// operator) providing high-level convenience methods for allocation.
189 /// @see Class #"AllocatorInterface"
190 /// @return A temporary high-level interface into the allocator.
192
193 /// This is a constexpr static method that determines if a type allows splitting memory
194 /// and later passing the pieces back to free.<br>
195 /// Of the allocators exposed by module \alib_monomem, only #"TMonoAllocator"
196 /// returns \c true. This is because this allocator has an empty method #"%.free" and thus
197 /// is agnostic about the memory freed.
198 /// If the configuration macro #"ALIB_DEBUG_ALLOCATIONS" is set, then also this allocator returns
199 /// \c false.
200 ///
201 /// @return \c true if this allocator is agnostic against freeing of objects which have not
202 /// been allocated as given.
203 static constexpr bool allowsMemSplit() noexcept;
204
205 /// Allocator name. This is a pure debug-field that helps to identify leaks and other issues
206 /// with allocators. The field is public an may be changed as wanted by a user of the library.
207 ///
208 /// Class #"TMonoAllocator;MonoAllocator" requires a name as the first parameter
209 /// with every overloaded constructor. Consequently, this argument has to be passed enclosed
210 /// in macro #"ALIB_DBG" (with a trailing comma) to have it pruned with release-builds.
211 ///
212 /// Class #"TPoolAllocator;PoolAllocator" grabs this name from its chained
213 /// allocator, which most often is an already named #"%MonoAllocator". Thus, no constructor
214 /// argument is available. Its name may be changed after construction as appropriate.
215 const char* DbgName;
216
217 /// This method has to be called to set the correct object size in the case that:
218 /// a) An allocation method returned a higher allocation size (as of the current \alib release,
219 /// this only happens with the use of class #"TPoolAllocator;PoolAllocator", and
220 /// b) this higher size is not ignored by the using code and,
221 /// c) this higher size is reported to one of the <em>free</em>-methods on object destruction.
222 ///
223 /// Within \alib a sample of such a type is class #"^AString": this type requests a string
224 /// buffer as needed but acknowledges if the returned buffer is lager.
225 /// With destruction, this higher capacity is passed when the buffer is deleted.
226 /// Thus, this type invokes this method after performing allocations.
227 ///
228 /// Note that this method is empty and optimized out, when the configuration macro
229 /// #"ALIB_DEBUG_ALLOCATIONS" is not set. If set, an \alib_assertion is raised if either the
230 /// magic bytes around the memory are not found, or the given \p{allocSize} does not
231 ///
232 /// @see Chapter #"alib_contmono_further_debug" of the Programmer's Manual.
233 ///
234 /// @tparam TSize The type of parameter \p{allocSize}. (Deduced by the compiler.)
235 /// @param mem The address of the allocated object.
236 /// @param allocSize The true allocation size returned by the method #".allocate".
237 template<typename TSize>
238 void dbgAcknowledgeIncreasedAllocSize( void* mem, TSize allocSize ) const;
239
240 /// If the configuration macro #"ALIB_DEBUG_ALLOCATIONS" is not set, this method is empty and will
241 /// be optimized out. Otherwise, this will raise an \alib_assertion if the piece of allocated
242 /// memory is corrupted or its allocation size is not rightfully given by the using code.
243 /// @see Chapter #"alib_contmono_further_debug" of the Programmer's Manual.
244 ///
245 /// @tparam TSize The type of parameter \p{size}. (Deduced by the compiler.)
246 /// @param mem The address of the allocated object.
247 /// @param size The requested allocation size of the object.
248 template<typename TSize>
249 void dbgCheckMemory( void* mem, TSize size ) const;
250
251}; // struct Allocator
252#endif // Doxygen
253
254//==================================================================================================
255/// This type offers high-level convenience methods for \alib allocators.<br>
256/// An \alib allocator (a type that offers an interface as specified with prototype
257/// #"lang::Allocator") has to provide only three <b>low-level (!)</b> allocation methods.
258/// Those are:
259/// - #"Allocator::allocate(size_t& size, size_t alignment)",
260/// - #"Allocator::reallocate(void* mem, size_t oldSize, size_t& newSize, size_t alignment)",
261/// - #"Allocator::free(void* disposable, size_t size)", and
262///
263/// Besides these, the C++ function call operator #"Allocator::operator()()" has to be
264/// implemented to return a temporary instance of this type.
265/// Note that the compiler will optimize the function call and the temporary instance out.
266/// With this setup, each allocator offers the <b>high-level</b> allocation methods provided
267/// with this class.
268///
269/// The naming scheme of the convenience methods found with this type, is along the standards:
270/// - Methods containing the words <c>"Alloc"</c> or <c>"Free"</c> allocate, respectively free
271/// memory without construction, respectively destruction of type instances.
272/// - Methods containing the word <c>"New"</c> and <c>"Delete"</c> allocate, respectively free
273/// memory and in addition invoke constructors, respectively destructors of type instances.
274///
275/// In contrast to the low-level interface methods #"Allocator::allocate;*" and
276/// #"Allocator::reallocate;*", information about a piece of memory returned that is larger
277/// than requested is not retrievable with the methods provided here. In case this can be of
278/// value, the low-level interface of an allocator has to be used.
279///
280/// The following quick sample illustrates the use of this class in combination with a
281/// #"TMonoAllocator":
282///
283/// \snippet "ut_monomem.cpp" DOX_MONOMEM_ALLOCATOR_INTERFACE_TYPE
284/// \snippet "ut_monomem.cpp" DOX_MONOMEM_ALLOCATOR_INTERFACE
285///
286/// \note
287/// In this sample, the destructor is of class \c MyType is emtpy, and furthermore, the method
288/// #"%TMonoAllocator::free" is empty. Thus, the whole call to #".Delete" will be
289/// optimized out. With other allocators this is different. It is recommended to still include
290/// such calls to avoid memory leaks at the moment either the allocator is changed, an allocated
291/// type gets equipped with a destructor, etc.
292///
293///@see
294/// - The #"alib_mods_contmono;Programmer's Manual" of module \alib_monomem_nl.
295/// - Allocator types #"TMonoAllocator" and #"TPoolAllocator"
296/// found in module \alib_monomem_nl.
297/// - Class #"HeapAllocator", which is useful for templated types which are agnostic
298/// to underlying allocation strategies.
299/// - To allocate and create a copy of a character string, the method #"TString::Allocate;*"
300/// or constructor #"TString;TString(TAllocator& allocator, const TString<TChar>&)"
301/// may be used. Freeing is then to be performed by using #"TString::Free;*".
302//==================================================================================================
303template<typename TAllocator>
305 /// The allocator type to use.
306 TAllocator& allocator;
307
308 /// Constructor. Usually construction is performed by the function operator of
309 /// an #"%lang::Allocator", which then passes itself as parameter \p{pAllocator}.
310 /// @param pAllocator The allocator.
311 AllocatorInterface(TAllocator& pAllocator) : allocator(pAllocator) {}
312
313 /// Allocates memory of the requested \p{size} and \p{alignment}.
314 /// @tparam TSize The type of parameter \p{size}. Deduced by the compiler.
315 /// (Must be integral.)
316 /// @tparam TAlignment The type of parameter \p{alignment}. Deduced by the compiler.
317 /// (Must be integral.)
318 /// @param size The allocation size requested.
319 /// @param alignment The required alignment.
320 /// @return A pointer to the allocated memory.
321 template<typename TSize, typename TAlignment>
322 void* Alloc( TSize size, TAlignment alignment)
323 {
324 size_t s= size_t(size);
325 return allocator.allocate( s, size_t(alignment) );
326 }
327
328 /// Allocates memory of <c>sizeof(T)</c> and <c>alignof(T)</c>.
329 /// @tparam T The type of object to allocate memory for.
330 /// @return A pointer to the allocated memory.
331 template<typename T>
332 T* Alloc()
333 {
334 auto size= sizeof(T);
335 return static_cast<T*>( allocator.allocate(size, alignof(T) ) );
336 }
337
338 /// Allocates aligned memory for an array of objects of type \p{T} of size \p{length}
339 /// leaving the memory uninitialized.
340 /// @tparam T The array element type.
341 /// @tparam TLength The type of parameter \p{length}. Has to be integral.
342 /// Deduced by the compiler.
343 /// @param length The capacity of the requested array.
344 /// @return A pointer to the first element of the allocated array.
345 template<typename T, typename TLength>
346 T* AllocArray( TLength length )
347 {
348 auto size= sizeof(T[1]) * size_t(length);
349 return static_cast<T*>( allocator.allocate( size, alignof(T[]) ) );
350 }
351
352 /// Allocates memory of size- and alignment-suitable for type \p{T} and performs a
353 /// C++ "placement-new", passing the given variadic arguments to the type's constructor.
354 /// @tparam T Type of the object to allocate and construct.
355 /// @tparam TArgs Types of variadic parameters given with parameter \p{args}.
356 /// @param args Variadic parameters to be forwarded to the constructor of type \p{T}.
357 /// @return A pointer to the initialized array.
358 template<typename T, typename... TArgs>
359 T* New( TArgs&& ... args )
360 { return new (Alloc<T>()) T( std::forward<TArgs>( args )... ); }
361
362 /// Allocates aligned memory for an array of objects of type \p{T} of size \p{length}.
363 /// All array elements are initialized using a "placement-new", passing the given \p{args} to
364 /// each element's constructor.
365 /// @tparam T Element type of the array to allocate and construct.
366 /// @tparam TArgs Types of variadic parameters given with parameter \p{args}.
367 /// @tparam TSize The type of parameter \p{length}. Deduced by the compiler.
368 /// (Must be integral.)
369 /// @param length The capacity of the requested array.
370 /// @param args Variadic parameters to be forwarded to the constructor of each array element
371 /// of type \p{T}.
372 /// @return A pointer to the first element of the allocated array.
373 template<typename T,typename TSize, typename... TArgs>
374 T* NewArray(TSize length, TArgs&& ... args ) {
375 T* mem= AllocArray<T, TSize>( length );
376 for( TSize i= 0 ; i < length ; ++i )
377 new (mem + i) T(std::forward<TArgs>( args )...);
378 return mem;
379 }
380
381 /// Calls the destructor of the given \p{object} of type \p{T} and then frees the memory
382 /// with the associated allocator.
383 /// \attention
384 /// In the context of single inheritance, this convenience function supports deleting pointers
385 /// to objects of base types, also when they were previously allocated using a derived type,
386 /// provided that virtual destructors are in place. Virtual destructors ensure that the
387 /// appropriate destructor is called, preventing resource leaks.
388 ///
389 /// \attention
390 /// However, in cases of multiple inheritance, passing pointers to base types for deletion
391 /// might lead to undefined behavior. The underlying challenge here is that safely converting
392 /// a base type pointer back to the originally allocated derived type pointer (as required
393 /// for correct memory deallocation) is not feasible with standard C++ mechanisms.
394 /// While the C++ `delete` operator is equipped to handle such scenarios - thanks to internal
395 /// compiler implementations that track the original pointer - standard C++ does not expose
396 /// a portable way to replicate this functionality.
397 /// Consequently, objects utilizing multiple inheritance have to be deleted using the same
398 /// derived type pointer that was used for allocation.
399 ///
400 /// @tparam T The type that parameter \p{object} points to.
401 /// @param object The object to delete.
402 template<typename T>
403 inline void Delete(T* object) { object->~T(); allocator.free(object, sizeof(*object)); }
404
405 /// Destructs all array elements and frees the array's memory.
406 /// @tparam T The pointer type of parameter \p{array}.
407 /// @tparam TIntegral Integral type of parameter \p{size}. Deduced by the compiler.
408 /// @param array Pointer to the first array element.
409 /// @param length The size of the given \p{mem}.
410 template<typename T, typename TIntegral>
411 inline
412 void DeleteArray(T* array, TIntegral length) {
413 for( TIntegral i= 0 ; i < length ; ++i )
414 (array + i)->~T();
415 allocator.free(array, sizeof(T[1]) * size_t(length) );
416 }
417
418
419 /// Frees memory of size <c>sizeof(T)</c> pointed to by \p{mem}.
420 /// This method does \b not call a destructor. To dispose custom types with due destruction,
421 /// use method #".Delete(T*)".
422 ///
423 /// @tparam T The pointer type of the given \p{mem} to free.
424 /// @param mem The memory to free.
425 template<typename T>
426 void Free(T* mem)
427 { allocator.free(static_cast<void*>(const_cast<std::remove_const_t<T>*>(mem)), sizeof(T) ); }
428
429 /// Frees memory of given \p{size} pointed to by \p{mem}.
430 /// This method does \b not call a destructor. To dispose custom types with due destruction,
431 /// use method #".Delete(T*)".
432 ///
433 /// @tparam T The pointer type of the given \p{mem} to free.
434 /// @tparam TIntegral The type that parameter \p{size} is provided with.
435 /// Deduced by the compiler. (Has to be integral.)
436 /// @param mem The memory to free.
437 /// @param size The size of the given \p{mem}.
438 template<typename T, typename TIntegral>
439 void Free(T* mem, TIntegral size)
440 { allocator.free(static_cast<void*>(const_cast<std::remove_const_t<T>*>(mem)), size_t(size) ); }
441
442 /// Frees memory of an array of objects.
443 /// This method does \b not call a destructor.
444 /// To also destruct the elements of an array, use method #".DeleteArray".
445 /// @tparam T The pointer type of parameter \p{array}.
446 /// @tparam TIntegral Integral type of parameter \p{size}. Deduced by the compiler.
447 /// @param array Pointer to the first array element.
448 /// @param length The size of the given \p{mem}.
449 template<typename T, typename TIntegral>
450 inline
451 void FreeArray(T* array, TIntegral length)
452 {
453 allocator.free( static_cast<void*>(const_cast<std::remove_const_t<T>*>(array)),
454 sizeof(T[1]) * size_t(length) );
455 }
456}; // struct AllocatorInterface
457
459/// A concept to identify types that satisfy the interface documented with prototype class
460/// #"lang::Allocator".<br>
461/// In fact, all methods that are defined in the non-existing class #"%lang::Allocator" (which is
462/// purely living in the documentation to define what an allocator needs to provide) are
463/// tested with this concept.
464/// This way, its use verifies likewise the due implementation of (custom) allocators.
465/// @tparam T The type to be tested.
466template <typename T>
468 requires(T obj, size_t& size, size_t alignment, size_t oldSize, void* mem) {
469 typename T::ChainedAllocator;
470 { T::MIN_ALIGNMENT } -> std::convertible_to<size_t>;
471 { T::MAX_ALIGNMENT } -> std::convertible_to<size_t>;
472 { obj.allocate (size, alignment) } -> std::same_as<void*>;
473 { obj.reallocate( mem, oldSize, size, alignment ) } -> std::same_as<void*>;
474 { obj.free (mem, size) } -> std::same_as<void>;
475 { obj.operator()() } -> std::same_as<AllocatorInterface<T>>;
476 { obj.allowsMemSplit() } -> std::convertible_to<bool>;
477
478 #if ALIB_DEBUG
479 { obj.DbgName } -> std::convertible_to<const char*>;
480 { obj.dbgAcknowledgeIncreasedAllocSize(mem, size) } -> std::same_as<void>;
481 { obj.dbgCheckMemory(mem, size) } -> std::same_as<void>;
482 #endif
483};
485
486
487//==================================================================================================
488/// Implementation of (prototype-) class #"lang::Allocator" which performs standard
489/// heap-allocation by using <c>std::malloc</c>, <c>std::realloc</c>, and <c>std::free</c>
490///
491/// This class may be passed as the template parameter \p{TAllocator} with \alib- or custom types
492/// that expect an allocator.
493/// For example, the type definition #"AString;2" uses this type and thus implements a common
494/// heap-allocated string buffer.
495///
496/// In contrast to other allocator types, this type is stateless and default-constructible, and
497/// temporary instances can be used and disposed of right away with no effort.
498/// Therefore, all types across \alib that are templated over an allocator type, provide two
499/// versions of each constructor, one that expects an allocator and one that does not expect an
500/// external instance. If this type #"%HeapAllocator" is used, the constructors that do not
501/// expect an allocator become effective.
502///
503/// @see
504/// - Struct #"AllocatorMember", which should be used as a base class to inherit from
505/// if an arbitrary allocator is to be stored. A specialization for this #"%HeapAllocator"
506/// is given, which does not contain a member and hence avoids increasing the footprint
507/// of derived types.
508/// - Types #"TMonoAllocator", #"TLocalAllocator", and
509/// #"TPoolAllocator" introduced with module \alib_monomem.
510//==================================================================================================
512 /// A magic byte, used with the configuration macro #"ALIB_DEBUG_ALLOCATIONS" to mark
513 /// memory and detect out-of-bounds writes.
514 /// @see Method #"Allocator::dbgCheckMemory;*".
515 static constexpr unsigned char MAGIC= 0xA1;
516
517 /// A magic byte written when memory is freed.
518 /// @see Field #".CLEAR".
519 static constexpr unsigned char CLEAR= 0xF1;
520
521 /// Evaluates to <c>alignof(std::max_align_t)</c>.
522 /// @see Field #"Allocator::MIN_ALIGNMENT;*".
523 static constexpr size_t MIN_ALIGNMENT = alignof(std::max_align_t);
524
525 /// Evaluates to <c>alignof(std::max_align_t)</c>.
526 /// @see Field #"Allocator::MAX_ALIGNMENT;*".
527 static constexpr size_t MAX_ALIGNMENT = alignof(std::max_align_t);
528
529 /// The type of the allocator that this allocator uses underneath.
530 /// In this case, no chaining is available, hence this evaluates to <c>void</c>.
531 using ChainedAllocator = void;
532
533 /// Allocate memory using <c>std::malloc</c>.
534 /// @param size The size of memory the block to allocate in bytes. With this allocator
535 /// this is not an input/output parameter.
536 /// @param alignment This type ignores the alignment parameter because <c>std::malloc</c>
537 /// always uses <c>alignof(std::max_align_t)</c>.
538 /// @return Pointer to the allocated memory block.
539 inline void* allocate( size_t size, size_t alignment ) const {
540 ALIB_ASSERT_ERROR( alignment <= alignof(std::max_align_t), "ALIB",
541 "The HeapAllocator is not designed to provide alignments greater "
542 "than alignof(std::max_align_t): {} > {}. ", alignment, alignof(std::max_align_t) )
543 (void) alignment;
544 void* mem= std::malloc(DbgAlloc::extSize(size));
545 DbgAlloc::annotate(mem, size, MAGIC);
546 return mem;
547 }
548
549 /// Shrinks or grows a piece of memory.
550 /// If a new allocation was performed, the existing data is copied.
551 /// Note that unlike specified by the prototype, parameter \p{newSize} is not provided as a
552 /// reference. This allocator will not allocate bigger objects.
553 /// @param mem The memory to reallocate.
554 /// @param oldSize The current size of \p{mem}.
555 /// @param newSize The now required size of \p{mem} in bytes.
556 /// With this allocator this is not an input/output parameter.
557 /// @return Pointer to the re-allocated memory.
558 inline void* reallocate(void* mem, size_t oldSize, size_t newSize, size_t ) {
559 DbgAlloc::checkMem( mem, oldSize, MAGIC, "HeapAllocator" );
560 void* newMem= std::realloc(mem, lang::DbgAlloc::extSize(newSize));
561 DbgAlloc::annotate(newMem, newSize, MAGIC);
562 return newMem;
563 }
564
565 /// Frees memory by invoking <c>std::free</c>.
566 /// @param mem The memory to dispose.
567 /// @param size The size of the given \p{mem}.
568 inline void free(void* mem, size_t size) const {
569 DbgAlloc::checkMem( mem, size, MAGIC, "HeapAllocator" );
570 DbgAlloc::clearMem(mem, size, CLEAR);
571 std::free(mem);
572 }
573
574 /// This is an empty implementation of the prototyped method.
575 /// It is empty because this allocator never returns a higher allocation size than requested.
576 /// #"Allocator::dbgAcknowledgeIncreasedAllocSize;*".
577 /// @tparam TSize The type of parameter \p{allocSize}. (Deduced by the compiler.)
578 template<typename TSize>
579 void dbgAcknowledgeIncreasedAllocSize( void*, TSize ) const {}
580
581 /// Returns a temporary object (which is usually optimized out together with a call to this
582 /// operator) providing high-level convenience methods for allocation.
583 /// @see Class #"AllocatorInterface"
584 /// @return A temporary high-level interface into the allocator.
587
588 /// This is a constexpr static method that determines if a type allows splitting memory
589 /// and later passing the pieces back to free.<br>
590 /// @return \c false.
591 static constexpr bool allowsMemSplit() { return false; }
592
593 #if ALIB_DEBUG
594 /// With this allocator, the debug-name is \c constexpr "HeapAllocator".
595 static constexpr const char* DbgName = "HeapAllocator";
596 #endif
597
598 /// If the configuration macro #"ALIB_DEBUG_ALLOCATIONS" is not set, this method is empty and will
599 /// be optimized out. Otherwise, this will raise an \alib_assertion if the piece of allocated
600 /// memory is corrupted or its allocation size is not rightfully given by the using code.
601 /// @see Chapter #"alib_contmono_further_debug" of the Programmer's Manual.
602 ///
603 /// @tparam TSize The type of parameter \p{size}. (Deduced by the compiler.)
604 /// @param mem The address of the allocated object.
605 /// @param size The requested allocation size of the object.
606 template<typename TSize>
607 void dbgCheckMemory( void* mem, TSize size )
608 { DbgAlloc::checkMem( mem, size, MAGIC, "HeapAllocator" ); }
609
610}; // struct HeapAllocator
611
612
613//==================================================================================================
614/// This templated class is used to inherit an allocation member. The rationale for choosing
615/// inheritance instead of just having types defining a member for an allocator, lies in the
616/// C++ standard's concept of \https{Empty Base Optimization,en.cppreference.com/w/cpp/language/ebo}.
617///
618/// The class has a specialization with #"lang::AllocatorMember<HeapAllocator>", which does
619/// not define a member and thus does not increase the footprint of a type that inherits it.
620/// With the standard implementation, a reference to the allocator specified with template
621/// parameter \p{TAllocator} is defined.
622///
623/// Consequently, this type is used to implement templated types that are allocation agnostic.
624/// Across \alib, a template parameter specifying an allocator is always named \p{TAllocator}.
625/// Types with this template parameter, in parallel inherit this type.
626/// A prominent example is the string buffer type of module \alib_strings, which usually is known
627/// through it's alias name #"AString;2", which performs classic heap allocation.
628/// Its alias type definition, is given as:
629/// \snippet "strings/detail/tastring.hpp" DOX_MONOMEM_ALLOCMEMBER
630/// Obviously the original type #"%TString" has two template parameters, one specifying the
631/// character type, the other specifying the allocator. Now looking at the
632/// #"TAString;reference documentation of type TAString", it can be seen that this type
633/// #"%AllocatorMember" is a protected base class and template parameter \p{TAllocator} is forwarded
634/// to this base.
635///
636/// \note As explained, this type is useful if allocation agnostic types are needed. This is true
637/// for types in this library. However, in many end-user cases, the allocation strategy will
638/// be fixed to a certain allocator, and therefore in this case, neither a templated approach,
639/// nor the helper-struct is needed.
640///
641/// @tparam TAllocator The allocator type of the inherited member, as prototyped with class
642/// #"lang::Allocator".
643//==================================================================================================
644template<typename TAllocator>
646 protected:
647 TAllocator* allocator; ///< The allocator stored.
648
649 public:
650 /// Exposes the allocator type.
651 using AllocatorType = TAllocator;
652
653 /// Default constructor. Only applicable if the allocator type is default-constructible,
654 /// i.e., #"HeapAllocator".
655 /// @tparam TIf Defaulted, must not be given.
656 template<typename TIf= TAllocator>
657 requires std::is_default_constructible_v<TIf>
659
660 /// Constructor.
661 /// @param pAllocator A reference to the allocator to store in member #".allocator".
662 AllocatorMember( TAllocator& pAllocator ) noexcept
663 : allocator(&pAllocator) {}
664
665 /// Returns the reference to the allocator.
666 /// @return The allocator given with construction.
667 TAllocator& GetAllocator() const noexcept { return *allocator; }
668
669 /// This is a convenience operator that returns the #"AllocatorInterface" for the
670 /// stored allocator (by calling #"Allocator::operator()();*").
671 /// @return The allocator interface of the stored allocator.
672 AllocatorInterface<TAllocator> AIF() const noexcept { return (*allocator)(); }
673};
674
675/// Specialization of this helper-struct for type #"HeapAllocator". Does not define a
676/// member and thus does not increase the footprint of a type that inherits this specialization.
677template<>
679{
680 /// This is an empty instance of the empty heap allocation type.
681 /// It is used to receive a non-const reference with the method #".GetAllocator".
683
684 /// Default Constructor.
685 AllocatorMember() =default;
686
687 /// Constructor.
688 /// @param heapAllocator This parameter is ignored, as always #".HEAP_ALLOCATOR_INSTANCE" is used.
689 AllocatorMember(const HeapAllocator& heapAllocator) noexcept { (void) heapAllocator; }
690
691 /// Static implementation of interface function. (Optimized out.)
692 /// @return A reference to the 'fake' object defined with# HEAP_ALLOCATOR_INSTANCE.
693 HeapAllocator& GetAllocator() const noexcept
694 { return const_cast<HeapAllocator&>(HEAP_ALLOCATOR_INSTANCE); }
695
696 /// This is a convenience operator which returns the #"AllocatorInterface" for the
697 /// stored allocator (by calling #"Allocator::operator()();*").
698 /// @return The allocator interface of the stored allocator.
700};
701
702
703//==================================================================================================
704// C++ standard allocators based on ALib allocators
705//==================================================================================================
706
707//==================================================================================================
708/// This struct is an implementation of C++ standard library type <c>std::allocator</c>.
709/// It is to be used with container types provided by the C++ standard library in combination
710/// with #"lang::Allocator;ALib Allocators".
711///
712/// With the inclusion of module \alib_monomem in the \alibbuild, this allocator is suitable to use
713/// cases of #"alib_contmono_intro_strictweak;strict or weak monotonic allocation".
714///
715/// ### Shortcuts for Specific ALib Allocators ###
716/// The following alias type definitions exist for this type, each addressing a specific allocator:
717/// - #"StdMA", and
718/// - #"StdPA".
719///
720/// Along with this, shortcuts to the container types are likewise provided with:
721/// - #"StdVectorMA", #"StdVectorPA",
722/// - #"StdListMA", #"StdListPA", and
723/// - #"StdDequeMA", #"StdDequePA".
724///
725/// \anchor alib_ns_monomem_stdma_reset
726/// ### Resetting A Container ###
727/// While the dedicated container types provided with module \alib_containers offer a method
728/// named \b Reset (see, for example, #"HashTable::Reset;2"), the C++ standard
729/// containers do not. This is a challenge because their internal memory will be invalid
730/// with a reset. For example, if allocator type #"MonoAllocator" was used with class
731/// <c>std::vector</c>, there is no interface method that makes the vector "forget" its internal
732/// data array. Its method <c>shrink_to_fit</c> by its specification is not forced to shrink
733/// anything or even dispose the data if the size was \c 0 when called.
734/// This is implementation-dependent.
735///
736/// The way out is as simple as radical: The container is just to be destructed and reconstructed
737/// "in place". This can be done using a <em>C++ placement-new</em>. The following
738/// code snippet demonstrates this:
739/// \snippet "ut_stdcontainers.cpp" DOX_MONOMEM_STDMA_PLACEMENT_NEW
740///
741/// \see
742/// - An alternative version that #"alib_contmono_intro_recycling;recycles nodes" (and also
743/// bucket arrays in case of <c>std::unordered_set</c> and <c>std::unordered_map</c>), is
744/// provided with sibling type #"StdRecyclingAllocator".
745/// - Type definitions #"StdMA" and #"StdPA" given with module \alib_monomem.
746/// - Further shortcuts for standard container types, given with #"StdVectorMA",
747/// #"StdVectorPA", #"StdListMA", #"StdListPA", #"StdDequeMA", and
748/// #"StdDequePA".
749///
750/// @tparam T The type of objects to be allocated.
751/// @tparam TAllocator The allocator type, as prototyped with class #"lang::Allocator".
752//==================================================================================================
753template<typename T, typename TAllocator>
754struct StdAllocator : AllocatorMember<TAllocator> {
755 using value_type = T ; ///< Type definition as required by C++ library standards.
756 using size_type = std::size_t ; ///< Type definition as required by C++ library standards.
757 using difference_type = std::ptrdiff_t ; ///< Type definition as required by C++ library standards.
758 using propagate_on_container_move_assignment= std::false_type; ///< Type definition as required by C++ library standards.
759
760 /// Exposes template parameter \p{TAllocator}.
761 using AllocatorType = TAllocator;
762
763 /// The type of the base class that stores the allocator.
765
766 /// Parameterless constructor used with heap allocation.
767 constexpr StdAllocator() noexcept =default;
768
769 /// Constructor for the initial allocator instantiation.
770 /// @param pAllocator The allocator.
771 constexpr
772 StdAllocator( TAllocator& pAllocator ) noexcept
773 : allocBase(pAllocator) {}
774
775 /// Defaulted copy constructor
776 constexpr StdAllocator( const StdAllocator& ) noexcept =default;
777
778 /// Defaulted move constructor
779 constexpr StdAllocator( StdAllocator&& ) noexcept =default;
780
781 /// Copy constructor using an instance of a different template type.
782 /// @tparam TSibling The originating allocator's type (<b>StdAllocator<X></b>).
783 /// @param origin The originating allocator of type \p{TSibling} .
784 template<typename TSibling>
785 requires alib::lang::IsAllocator<typename TSibling::allocator>
786 constexpr
787 StdAllocator(const TSibling& origin) noexcept
788 : allocBase(origin) {}
789
790 /// Copy constructor using an instance of a different template type.
791 /// @tparam TSibling The originating allocator's type (<b>StdAllocator<X></b>).
792 /// @param origin The originating allocator of type \p{TSibling} .
793 template<typename TSibling>
796
797 /// Parameterless destructor used with heap allocation.
798 constexpr ~StdAllocator() noexcept =default;
799
800 //################################################################################################
801 // ### Allocate/de-allocate
802 //################################################################################################
803 /// Passes the allocation request to the #"%AllocatorMember".
804 ///
805 /// @param n The number of requested objects to allocate storage for.
806 /// @return Pointer to the start of the array of \p{n} objects of type \p{T}.
807 [[nodiscard]] constexpr
808 T* allocate( size_t n ) { return allocBase::AIF().template AllocArray<T>( n ); }
809
810 #if defined(__cpp_lib_allocate_at_least) || DOXYGEN
811 /// The C++23 extension of std::allocator finally supports \alib mechanics of using
812 /// oversized allocation objects.
813 /// @param n The number of requested objects to allocate storage for.
814 /// @return The Pointer to the start of the allocated objects of type \p{T} as well as
815 /// the number of objects allocated, which will be equal or higher than \p{n}.
816 [[nodiscard]] constexpr
817 std::allocation_result<T*, std::size_t>
818 allocate_at_least( std::size_t n ) {
819 std::allocation_result<T*, std::size_t> result;
820 result.count= n * sizeof(T);
821 result.ptr= static_cast<T*>( allocBase::GetAllocator().allocate(result.count, alignof(T)));
822 result.count/= sizeof(T);
823 return result;
824 }
825
826 #endif
827
828 /// Frees the given array of objects.
829 /// @param p Pointer to the previously allocated memory.
830 /// @param n The number of objects allocated.
831 constexpr
832 void deallocate( T* p, std::size_t n ) { allocBase::AIF().FreeArray( p, n ); }
833
834}; // struct StdAllocator
835
836
837/// Comparison operator for type #"StdAllocator".
838/// @param lhs The left-hand side allocator.
839/// @param rhs The right-hand side allocator.
840/// @tparam TLhs The allocation type of the left-hand side allocator.
841/// @tparam TRhs The allocation type of the right-hand side allocator.
842/// @return \c true if both allocators use the same allocator internally, \c false otherwise.
843template< typename TLhs, typename TRhs, typename TAllocator >
845 const StdAllocator<TRhs, TAllocator>& rhs ) noexcept {
846 return &lhs.GetAllocator() == &rhs.GetAllocator();
847}
848
849//==================================================================================================
850/// This structs' name stands for "runtime type recycling allocator".
851/// The struct encapsulates an allocator and recycles (caches) objects whose size and alignment are
852/// only determined and detected at runtime.
853/// This type should \e only be used in combination with struct
854/// #"StdRecyclingAllocator" or in similar (unlikely) situations when the type of
855/// recyclable objects is unknown at compile time.
856/// \note The reason why the size of node types of containers of the C++ standard library is known
857/// only at runtime, is that the implementation of the containers is not standardized.
858/// Hence, in fact the node size is of course known at compile time, it is just not accessible
859/// in a platform-independent fashion.
860///
861/// This type is provided by \alib to support recycling of monotonically allocated memory with
862/// container types provided by the C++ standard library. With those, the internal node types are
863/// unspecified.
864/// A typical implementation and use of the containers will always allocate the same object size.
865/// This is detected with the first allocation, and from this point in time, future deallocations
866/// will recycle pieces of memory of exactly this type.
867///
868/// If the type #"HeapAllocator" is given with the template parameter \p{TAllocator}, this
869/// recycler uses <c>std::malloc</c> and <c>std::free</c> for memory allocation and deallocation.
870/// While such an operation mode does not imply all performance benefits of monotonic allocation
871/// scenarios, still, the recycling of node objects may avoid many malloc/free operations and
872/// therefore reduce memory fragmentation significantly.<br>
873/// Method #".RecycleChunk" will not create recyclable objects in that mode but will duly
874/// use <c>std::free</c> to directly free a chunk of non-object size.
875///
876/// @tparam TAllocator The allocator type, as prototyped with class #"lang::Allocator".
877//==================================================================================================
878template<typename TAllocator>
879struct RTTRAllocator : AllocatorMember<TAllocator> {
880 /// The type of the base class that stores the allocator.
882
883 /// The node type of the internal node type used for stacking recyclables. Besides inheriting
884 /// the single-list pointer, this type is empty.
885 struct Node : SidiNodeBase<Node>
886 {};
887
888 /// List of destructed objects available for recycling.
890
891 /// The object size of recyclables. Will be detected with the first invocation of #".Get".
893
894 /// The required object alignment. Will be detected with the first invocation of #".Get".
896
897 #if ALIB_DEBUG
898 /// The detected object's runtime type information struct.<br>
899 /// Available only in debug-builds.
900 const std::type_info* dbgDetectedObjectTypeInfo =nullptr;
901
902 /// Flag on raising an \alib_warning. Defaults to \c true and set to \c false when a
903 /// corresponding warning was given.<br>
904 /// Available only in debug-builds.
906
907 /// Flag on raising an \alib_warning. Defaults to \c true and set to \c false when a
908 /// corresponding warning was given.<br>
909 /// Available only in debug-builds.
911
912 /// Flag on raising an \alib_warning. Defaults to \c true and set to \c false when a
913 /// corresponding warning was given.<br>
914 /// Available only in debug-builds.
916
917 /// Flag on raising an \alib_warning. Defaults to \c true and set to \c false when a
918 /// corresponding warning was given.<br>
919 /// Available only in debug-builds.
921 #endif
922
923 /// Constructor taking the monotonic allocator.
924 ///
925 /// If \c nullptr is given, this recycler uses <c>std::malloc</c> and <c>std::free</c> for memory
926 /// allocation and deallocation.
927 ///
928 /// @param pAllocator The monotonic allocator. If \c nullptr is given, still recycling of node
929 /// objects is performed.
930 RTTRAllocator( TAllocator& pAllocator ) noexcept : allocBase(pAllocator) {}
931
932 //################################################################################################
933 // ### Allocation
934 //################################################################################################
935#if DOXYGEN
936 //==============================================================================================
937 /// Allocates or recycles memory for the dedicated object type. With the first invocation
938 /// of this method, this type is determined object type.
939 /// In debug-builds, this method will raise an \alib_warning in case a different
940 /// object type is requested.
941 ///
942 /// @param size The requested size.
943 /// @param alignment The requested alignment
944 /// @param dbgTypeInfo The type information of the object to allocate.
945 /// Available only in debug-builds.
946 /// @return The requested memory.
947 //==============================================================================================
948 inline
949 void* Get( size_t size, size_t alignment, const type_info& dbgTypeInfo);
950#else
951 void* Get( size_t size, size_t alignment ALIB_DBG(, const std::type_info& dbgTypeInfo) ) {
952 ALIB_DBG( (void) dbgTypeInfo; )
953
954 // detect object size
955 if(detectedObjectSize == 0) {
956 detectedObjectSize = size;
957 detectedObjectAlignment = alignment;
958 ALIB_DBG( dbgDetectedObjectTypeInfo = &dbgTypeInfo; )
959
960 ALIB_MESSAGE( "RTTRA", "Object type detected : ", &dbgTypeInfo )
961 ALIB_ASSERT_ERROR( alignment >= alignof(Node), "RTTRA",
962 "Struct RTTRAllocator cannot be used to recycle types with an alignment "
963 "smaller than {}. Requested: {}", alignof(Node), alignment )
964 }
965
966 if( (size == detectedObjectSize) && (detectedObjectAlignment== alignment) ) {
967 if( !stack.isEmpty() ) {
968 #if ALIB_DEBUG_MEMORY
969 ALIB_MESSAGE( "RTTRA", "Recycling object. Type: ", &dbgTypeInfo )
970 #endif
971 return reinterpret_cast<char*>( stack.popFront() );
972 } }
973 #if ALIB_DEBUG
974 else {
975 if(dbgWarnDifferentObjectTypeAlloc) {
976 ALIB_MESSAGE( "RTTRA",
977 "A different object was requested for allocation!\n"
978 " Previous type : <{}>\n"
979 " Requested type: <{}>\n"
980 "Note: This allocator may not be efficient when used.\n"
981 " If this is a use case using a 'std' library container, this message indicates\n"
982 " that a RTTRAllocator was shared between different container instantiations.\n"
983 " If this is not the case, than an 'unusual' implementation of such C++ library may\n"
984 " prevent this concept from working. See ALib manual for further information.",
985 dbgDetectedObjectTypeInfo, &dbgTypeInfo )
986
987
988 dbgWarnDifferentObjectTypeAlloc= false;
989 }
990 ALIB_MESSAGE( "RTTRA", "Allocating a different object type \"{}\"\n"
991 " Note: This object cannot be recycled.", &dbgTypeInfo )
992 return allocBase::AIF().Alloc( size, alignment );
993 }
994 #endif
995
996 #if ALIB_DEBUG_MEMORY
997 ALIB_MESSAGE( "RTTRA", "Allocating object. Type: \"{}\"", &dbgTypeInfo )
998 #endif
999
1000 return allocBase::AIF().Alloc( size, alignment );
1001 }
1002#endif // DOXYGEN
1003
1004#if DOXYGEN
1005 //==============================================================================================
1006 /// Allocates memory for a type different to the dedicated, detected object type.
1007 /// @param size The requested size.
1008 /// @param alignment The requested alignment
1009 /// @param dbgTypeInfo The type information of the object to allocate.
1010 /// Available only in debug-builds.
1011 /// @return The requested memory.
1012 //==============================================================================================
1013 inline
1014 void* AllocUnrelated( size_t size, size_t alignment, const type_info& dbgTypeInfo );
1015#else
1016 void* AllocUnrelated(size_t size,size_t alignment ALIB_DBG(,const std::type_info& dbgTypeInfo)){
1017 ALIB_DBG( (void) dbgTypeInfo; )
1019 ALIB_MESSAGE( "RTTRA", "Allocating other. Type: <{}>", &dbgTypeInfo )
1020 #endif
1021 return allocBase::AIF().Alloc( size, alignment );
1022 }
1023#endif // DOXYGEN
1024
1025
1026 //################################################################################################
1027 // ### Deallocation
1028 //################################################################################################
1029#if DOXYGEN
1030 //==============================================================================================
1031 /// Deallocates memory for the dedicated, detected object type.
1032 ///
1033 /// In debug-builds, this method will raise an \alib_warning in case a different
1034 /// object type is deallocated as had been detected.
1035 /// Furthermore, a #"alib_mod_assert;warning is raised" in case no previous call to #".Get"
1036 /// has been performed.
1037 ///
1038 /// @param mem The object to deallocate.
1039 /// @param size The size of the object to deallocate.
1040 /// @param alignment The alignment of the object to deallocate.
1041 /// @param dbgTypeInfo The type information of the object to de-allocate.
1042 /// Available only in debug-builds.
1043 //==============================================================================================
1044 inline
1045 void Recycle( void* mem, size_t size, size_t alignment, const type_info& dbgTypeInfo );
1046#else
1047 void Recycle( void* mem, size_t size, size_t alignment
1048 ALIB_DBG(, const std::type_info& dbgTypeInfo) ) {
1049 if( size == detectedObjectSize
1050 && alignment == detectedObjectAlignment )
1051 {
1052 stack.pushFront( static_cast<Node*>( mem ) );
1053 #if ALIB_DEBUG_MEMORY
1054 ALIB_MESSAGE( "RTTRA", "Stacking object. Type: ", &dbgTypeInfo )
1055 #endif
1056 } else {
1057 allocBase::GetAllocator().free(mem, size);
1058 #if ALIB_DEBUG
1059 if( detectedObjectSize == 0 ) {
1060 if( dbgWarnDeallocationPriorToAllocation) {
1061 ALIB_WARNING( "RTTRA",
1062 "Deallocation before a first object allocation needed to detect recyclable type!\n"
1063 " De-allocated object type: <{}>\n"
1064 "Note: This allocator may not be efficient when used.\n"
1065 " If this is a use case using a 'std' library container, this message indicates\n"
1066 " an 'unusual' implementation of such C++ standard library."
1067 , &dbgTypeInfo )
1068 }
1069 dbgWarnDeallocationPriorToAllocation= false;
1070 } else {
1071 if( dbgWarnDifferentObjectTypeDealloc ) {
1072 ALIB_WARNING( "RTTRA",
1073 "A different object for was requested for de-allocoation!\n"
1074 " Previous type : <{}>\n"
1075 " Requested type: <{}>\n"
1076 "Note: This allocator may not be efficient when used.\n"
1077 " If this is a use case using a 'std' library container, this message indicates\n"
1078 " that a RTTRAllocator was shared between different container instantiations.\n"
1079 " If this is not the case, than an 'unusual' implementation of such C++ library may\n"
1080 " prevent this concept from working. See ALib manual for further information"
1081 , dbgDetectedObjectTypeInfo, &dbgTypeInfo )
1082 dbgWarnDifferentObjectTypeDealloc= false;
1083 } }
1084 #endif
1085 } }
1086#endif
1087
1088
1089#if DOXYGEN
1090 //==============================================================================================
1091 /// Deallocates memory for a type different to the dedicated, detected object type.
1092 ///
1093 /// In debug-builds, this method will raise an \alib_warning no previous call to #".Get"
1094 /// has been performed.<br>
1095 /// Furthermore, a #"alib_mod_assert;warning is raised" in case that the provided memory
1096 /// chunk is too small to be sliced into at least one recyclable object.
1097 ///
1098 /// @param mem The object to deallocate.
1099 /// @param size The size of the object to deallocate.
1100 /// @param dbgTypeInfo The type information of the object to de-allocate.
1101 /// Available only in debug-builds.
1102 //==============================================================================================
1103 inline
1104 void RecycleChunk( void* mem, size_t size, const type_info& dbgTypeInfo );
1105#else
1106 void RecycleChunk( void* memUnaligned, size_t size
1107 ALIB_DBG(, const std::type_info& dbgTypeInfo) ) {
1108 // This whole exercise must only be done with MonoAllocator and if ALIB_DEBUG_ALLOCATIONS is
1109 // not set. (This is ensured by 'TAllocator::allowsMemSplit()')
1110 if( !TAllocator::allowsMemSplit() ) {
1111 allocBase::GetAllocator().free(memUnaligned, size);
1112 return;
1113 }
1114
1115 // if object size not detected, yet, we cannot create recyclables.
1116 ALIB_DBG( size_t origSize= size; )
1117 if( detectedObjectSize == 0) {
1118 #if ALIB_DEBUG
1119 if( dbgWarnRecycleChunkPriorToAllocation ) {
1120 ALIB_WARNING( "RTTRA",
1121 "Deallocation before a first object allocation needed to detect recyclable type!\n"
1122 " De-allocated object type: <{}>.\n"
1123 "Note: If this recycler is used with a 'std' library container, this either\n"
1124 " indicates an 'unusual' implementation of such C++ standard library,\n"
1125 " or a manual shrink of the capacity without any prior object insertion.\n",
1126 &dbgTypeInfo )
1127 dbgWarnRecycleChunkPriorToAllocation = false ;
1128 }
1129 #endif
1130
1131 return;
1132 }
1133
1134 // align beginning of buffer
1135 char* mem = reinterpret_cast<char*>( (size_t(memUnaligned) + detectedObjectAlignment - 1)
1136 & ~(detectedObjectAlignment -1) );
1137 size-= size_t(mem - reinterpret_cast<char*>(memUnaligned));
1138
1139 // create recyclables
1140 ALIB_DBG( size_t cntStackedObjects= 0; )
1141 while(size > detectedObjectSize) {
1142 stack.pushFront( reinterpret_cast<Node*>( mem ) );
1143 mem = static_cast<char*>(mem) + detectedObjectSize;
1144 size -= detectedObjectSize;
1145 ALIB_DBG( ++cntStackedObjects; )
1146 }
1147
1148 #if ALIB_DEBUG
1149 if( cntStackedObjects > 0 ) {
1150 ALIB_WARNING( "RTTRA",
1151 "De-allocated chunk's size is smaller than detected object size.\n"
1152 " Deallocated object: Type: <{}>\n"
1153 " Size: {} bytes\n"
1154 " Detected object: Type: <{}>\n"
1155 " Size: {} bytes, alignment: {}\n"
1156 "Note: If this recycler is used with a <std::unordered_map> or <std::unordered_set>,\n"
1157 " this message may be eliminated by reserving a reasonable initial bucket size.",
1158 &dbgTypeInfo, origSize, dbgDetectedObjectTypeInfo,
1159 detectedObjectSize, detectedObjectAlignment )
1160 }
1161
1162 #endif
1163 #if ALIB_DEBUG_MEMORY
1164 ALIB_MESSAGE( "RTTRA",
1165 "Stacking {} objects from de-allocated memory of size {} (lost {} bytes).\n"
1166 "Deallocated type: {}", cntStackedObjects, origSize,
1167 origSize - cntStackedObjects * detectedObjectSize, &dbgTypeInfo )
1168 #endif
1169 }
1170#endif
1171
1172}; // RTTRAllocator
1173
1174//==================================================================================================
1175/// Implementation of <c>std::allocator</c> to be used with container types provided by the
1176/// C++ standard library.
1177///
1178/// As the C+++ library's specification does not include details of the container class's
1179/// implementation, this allocator uses type #"RTTRAllocator" which detects
1180/// node types automatically.
1181///
1182/// \attention
1183/// It cannot be guaranteed that a certain, uncommon implementation of the C++ library
1184/// allocates memory in a way that this allocator truly recycles objects, and - even if the
1185/// authors of \alib are optimistic hat this type works with any implementation - it is
1186/// recommended to use the alternative container types found in this \alibmod_nl.
1187///
1188/// \see
1189/// For background information about monotonic memory allocation, recycling, and the issues
1190/// of C++ standard container types, see the #"alib_mods_contmono;Programmer's Manual" of
1191/// \alibmods_nl \b Containers" and \b Monomem.
1192///
1193/// @tparam T The type of objects to be allocated.
1194/// @tparam TAllocator The allocator type, as prototyped with class #"lang::Allocator".
1195//==================================================================================================
1196template<typename T, typename TAllocator>
1198 //################################################################################################
1199 // ### Type definitions
1200 //################################################################################################
1201 using size_type = size_t ; ///< Type definition as required by C++ library standards.
1202 using difference_type= ptrdiff_t ; ///< Type definition as required by C++ library standards.
1203 using value_type = T ; ///< Type definition as required by C++ library standards.
1204 using is_always_equal= std::false_type; ///< Type definition as required by C++ library standards.
1205 using reference = T& ; ///< Type definition as required by C++ library standards.
1206 using const_reference= const T& ; ///< Type definition as required by C++ library standards.
1207 using AllocatorType = TAllocator ; ///< Exposes template parameter \p{TAllocator}.
1208
1209 //################################################################################################
1210 // ### Fields
1211 //################################################################################################
1212 /// The only member of this allocator type used to perform all duties.
1214
1215 //################################################################################################
1216 // ### Construction/Destruction
1217 //################################################################################################
1218 /// Defaulted copy constructor
1219 constexpr StdRecyclingAllocator( const StdRecyclingAllocator& ) noexcept =default;
1220
1221 /// Defaulted move constructor
1222 constexpr StdRecyclingAllocator( StdRecyclingAllocator&& ) noexcept =default;
1223
1224 /// Copy constructor using an instance of a different template type.
1225 /// @tparam TSibling The originating allocator's type (<b>StdRecyclingAllocator<X></b>).
1226 /// @param StdRecyclingAllocator The originating allocator of type \p{TSibling} .
1227 template<typename TSibling>
1230
1231 /// Constructor for the initial allocator instantiation.
1232 /// @param pRecycler The recycler to for allocations and deallocations.
1234 : recycler(pRecycler) {}
1235
1236 //################################################################################################
1237 // ### Comparison
1238 //################################################################################################
1239 /// Comparison operator.
1240 /// @tparam U The allocation type of the other allocator.
1241 /// @param rhs The right-hand side allocator.
1242 /// @return \c true if this and \p{rhs} use the same #".recycler", \c false otherwise.
1243 template< typename U >
1244 bool operator==( const StdRecyclingAllocator<U,TAllocator>& rhs ) const noexcept
1245 { return recycler == rhs.recycler; }
1246
1247 /// Comparison operator.
1248 /// @tparam U The allocation type of the other allocator.
1249 /// @param rhs The right-hand side allocator.
1250 /// @return \c false if this and \p{rhs} use the same #".recycler", \c true otherwise.
1251 template< typename U >
1252 bool operator!=( const StdRecyclingAllocator<U,TAllocator>& rhs ) const noexcept
1253 { return recycler != rhs.recycler; }
1254
1255 //################################################################################################
1256 // ### Allocate/de-allocate
1257 //################################################################################################
1258 /// Implementation of <c>std::allocator</c> interface.
1259 /// Dependent on whether a single or multiple object is requested, this method invokes
1260 /// either #"RTTRAllocator::Get;*" or #"RTTRAllocator::AllocUnrelated;*" on the
1261 /// field #".recycler".
1262 ///
1263 /// @param n The number of requested objects to allocate storage for.
1264 /// @return Pointer to the start of the array of \p{n} objects of type \p{T}.
1265 [[nodiscard]]
1266 T* allocate( size_t n ) {
1267 if( n == 1 )
1268 return static_cast<T*>(recycler.Get( sizeof(T), alignof(T)
1269 ALIB_DBG(, typeid(T)) ) );
1270
1271 return static_cast<T*>( recycler.AllocUnrelated( sizeof(T) * n, alignof(T)
1272 ALIB_DBG(, typeid(T) )) );
1273 }
1274
1275 /// Implementation of <c>std::allocator</c> interface.
1276 /// Dependent on whether a single or multiple objects are de-allocated (parameter \p{n}), this
1277 /// method invokes either #"RTTRAllocator::Recycle;*" or
1278 /// #"RTTRAllocator::RecycleChunk;*" on field #".recycler".
1279 ///
1280 /// @param p Pointer to the object to deallocate.
1281 /// @param n The number of objects to de-allocate.
1282 void deallocate( T* p, std::size_t n ) {
1283 // deallocate node
1284 if( n == 1 ) recycler.Recycle( p, sizeof(T) , alignof(T) ALIB_DBG(, typeid(T) ) );
1285 else recycler.RecycleChunk ( p, sizeof(T) * n ALIB_DBG(, typeid(T) ) );
1286 }
1287}; // struct StdRecyclingAllocator
1288
1289} // namespace alib[::lang]
1290
1291/// Type alias in namespace #"%alib".
1293
1294} // namespace [alib]
#define ALIB_MESSAGE(domain,...)
#define ALIB_DEBUG_MEMORY
#define ALIB_WARNING(domain,...)
#define ALIB_ALLOW_DOCS
#define ALIB_ERROR(domain,...)
#define ALIB_POP_ALLOWANCE
#define ALIB_EXPORT
#define ALIB_DBG(...)
#define ALIB_ASSERT_ERROR(cond, domain,...)
constexpr bool operator==(const StdAllocator< TLhs, TAllocator > &lhs, const StdAllocator< TRhs, TAllocator > &rhs) noexcept
Definition alox.cpp:14
lang::HeapAllocator HeapAllocator
Type alias in namespace #"%alib".
T * AllocArray(TLength length)
void Free(T *mem, TIntegral size)
T * New(TArgs &&... args)
void FreeArray(T *array, TIntegral length)
AllocatorInterface(TAllocator &pAllocator)
TAllocator & allocator
The allocator type to use.
void DeleteArray(T *array, TIntegral length)
T * NewArray(TSize length, TArgs &&... args)
void * Alloc(TSize size, TAlignment alignment)
static constexpr HeapAllocator HEAP_ALLOCATOR_INSTANCE
AllocatorInterface< HeapAllocator > AIF() const noexcept
HeapAllocator & GetAllocator() const noexcept
AllocatorMember()=default
Default Constructor.
AllocatorMember(const HeapAllocator &heapAllocator) noexcept
TAllocator * allocator
The allocator stored.
TAllocator & GetAllocator() const noexcept
AllocatorMember(TAllocator &pAllocator) noexcept
AllocatorInterface< TAllocator > AIF() const noexcept
TAllocator AllocatorType
Exposes the allocator type.
void dbgAcknowledgeIncreasedAllocSize(void *mem, TSize allocSize) const
TAllocator ChainedAllocator
void * allocate(size_t &size, size_t alignment)
static constexpr size_t MAX_ALIGNMENT
void free(void *mem, size_t size)
static constexpr size_t MIN_ALIGNMENT
void * reallocate(void *mem, size_t oldSize, size_t &newSize, size_t alignment)
static constexpr bool allowsMemSplit() noexcept
AllocatorInterface< Allocator > operator()()
void dbgCheckMemory(void *mem, TSize size) const
static constexpr void clearMem(T *mem, size_t size, unsigned char magic)
static constexpr void annotate(T *mem, size_t size, unsigned char magic)
static constexpr size_t extSize(TIntegral size)
static void checkMem(void *mem, const TSize size, unsigned char magic, const char *name)
static constexpr unsigned char MAGIC
AllocatorInterface< HeapAllocator > operator()() const noexcept
static constexpr bool allowsMemSplit()
static constexpr size_t MAX_ALIGNMENT
void dbgAcknowledgeIncreasedAllocSize(void *, TSize) const
void * allocate(size_t size, size_t alignment) const
void * reallocate(void *mem, size_t oldSize, size_t newSize, size_t)
void free(void *mem, size_t size) const
void dbgCheckMemory(void *mem, TSize size)
static constexpr unsigned char CLEAR
static constexpr size_t MIN_ALIGNMENT
static constexpr const char * DbgName
With this allocator, the debug-name is constexpr "HeapAllocator".
void * AllocUnrelated(size_t size, size_t alignment, const type_info &dbgTypeInfo)
void * Get(size_t size, size_t alignment, const type_info &dbgTypeInfo)
size_t detectedObjectSize
The object size of recyclables. Will be detected with the first invocation of #".Get".
void RecycleChunk(void *mem, size_t size, const type_info &dbgTypeInfo)
RTTRAllocator(TAllocator &pAllocator) noexcept
void Recycle(void *mem, size_t size, size_t alignment, const type_info &dbgTypeInfo)
SidiListHook< Node > stack
List of destructed objects available for recycling.
AllocatorMember< TAllocator > allocBase
The type of the base class that stores the allocator.
size_t detectedObjectAlignment
The required object alignment. Will be detected with the first invocation of #".Get".
const std::type_info * dbgDetectedObjectTypeInfo
void pushFront(TElement *elem) noexcept
Definition sidilist.hpp:218
TElement * popFront() noexcept
Definition sidilist.hpp:234
bool isEmpty() const noexcept
Definition sidilist.hpp:206
SidiNodeBase() noexcept=default
constexpr ~StdAllocator() noexcept=default
Parameterless destructor used with heap allocation.
std::false_type propagate_on_container_move_assignment
Type definition as required by C++ library standards.
constexpr void deallocate(T *p, std::size_t n)
TAllocator AllocatorType
Exposes template parameter TAllocator.
constexpr StdAllocator(StdAllocator &&) noexcept=default
Defaulted move constructor.
constexpr std::allocation_result< T *, std::size_t > allocate_at_least(std::size_t n)
constexpr StdAllocator(const StdAllocator &) noexcept=default
Defaulted copy constructor.
StdAllocator(const StdAllocator< TSibling, TAllocator > &origin)
constexpr StdAllocator() noexcept=default
Parameterless constructor used with heap allocation.
std::size_t size_type
Type definition as required by C++ library standards.
T value_type
Type definition as required by C++ library standards.
AllocatorMember< TAllocator > allocBase
The type of the base class that stores the allocator.
std::ptrdiff_t difference_type
Type definition as required by C++ library standards.
T & reference
Type definition as required by C++ library standards.
void deallocate(T *p, std::size_t n)
constexpr StdRecyclingAllocator(RTTRAllocator< TAllocator > &pRecycler)
TAllocator AllocatorType
Exposes template parameter TAllocator.
bool operator!=(const StdRecyclingAllocator< U, TAllocator > &rhs) const noexcept
const T & const_reference
Type definition as required by C++ library standards.
bool operator==(const StdRecyclingAllocator< U, TAllocator > &rhs) const noexcept
constexpr StdRecyclingAllocator(const StdRecyclingAllocator &) noexcept=default
Defaulted copy constructor.
RTTRAllocator< TAllocator > & recycler
The only member of this allocator type used to perform all duties.
ptrdiff_t difference_type
Type definition as required by C++ library standards.
std::false_type is_always_equal
Type definition as required by C++ library standards.
constexpr StdRecyclingAllocator(StdRecyclingAllocator &&) noexcept=default
Defaulted move constructor.
size_t size_type
Type definition as required by C++ library standards.
T value_type
Type definition as required by C++ library standards.