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