CookMem 1.4.0
A C++ memory context / allocator
Loading...
Searching...
No Matches
pool.h
1/*
2 * Copyright (c) 2018-2026 Heng Yuan
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16#ifndef COOK_MEM_POOL_H
17#define COOK_MEM_POOL_H
18
19#include <cstddef>
20#include <cstdint>
21#include <cstdio>
22#include <cstring>
23
24#ifdef _WIN32
25#include <windows.h>
26#endif /* _WIN32 */
27
28#include "config.h"
29#include "exception.h"
30#include "logger.h"
31#include "rbtree.h"
32#include "circularlist.h"
33
34namespace cookmem
35{
36
38// Bit helpers used by the bin maps (from dlmalloc).
39template<class T>
40T least_bit(T x)
41{
42 return ((x) & -(static_cast<int>(x)));
43}
44
45template<class T>
46T left_bits(T x)
47{
48 return ((x << 1) | -static_cast<int>(x << 1));
49}
51
52/* assign tree index for size S to variable I. Use x86 asm if possible */
53#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
54#define cookmem_getLargeBinIndex(S, I)\
55{\
56 unsigned int X = S >> TREEBIN_SHIFT;\
57 if (X == 0)\
58 I = 0;\
59 else if (X > 0xFFFF)\
60 I = NTREEBINS-1;\
61 else {\
62 unsigned int K = (unsigned) sizeof(X)*__CHAR_BIT__ - 1 - (unsigned) __builtin_clz(X); \
63 I = (BinIndexType)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\
64 }\
65}
66
67#elif defined (__INTEL_COMPILER)
68#define cookmem_getLargeBinIndex(S, I)\
69{\
70 std::size_t X = S >> TREEBIN_SHIFT;\
71 if (X == 0)\
72 I = 0;\
73 else if (X > 0xFFFF)\
74 I = NTREEBINS-1;\
75 else {\
76 unsigned int K = _bit_scan_reverse (X); \
77 I = (BinIndexType)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\
78 }\
79}
80
81#elif defined(_MSC_VER) && _MSC_VER>=1300
82#define cookmem_getLargeBinIndex(S, I)\
83{\
84 std::size_t X = S >> TREEBIN_SHIFT;\
85 if (X == 0)\
86 I = 0;\
87 else if (X > 0xFFFF)\
88 I = NTREEBINS-1;\
89 else {\
90 unsigned int K;\
91 _BitScanReverse((DWORD *) &K, (DWORD) X);\
92 I = (BinIndexType)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\
93 }\
94}
95
96#else /* GNUC */
97#define cookmem_getLargeBinIndex(S, I)\
98{\
99 std::size_t X = S >> TREEBIN_SHIFT;\
100 if (X == 0)\
101 I = 0;\
102 else if (X > 0xFFFF)\
103 I = NTREEBINS-1;\
104 else {\
105 unsigned int Y = (unsigned int)X;\
106 unsigned int N = ((Y - 0x100) >> 16) & 8;\
107 unsigned int K = (((Y <<= N) - 0x1000) >> 16) & 4;\
108 N += K;\
109 N += K = (((Y <<= K) - 0x4000) >> 16) & 2;\
110 K = 14 - N + ((Y <<= K) >> 15);\
111 I = (K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1));\
112 }\
113}
114#endif /* GNUC */
115
116#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
117#define cookmem_bit2treeIndex(X, I)\
118{\
119 unsigned int J;\
120 J = __builtin_ctz(X); \
121 I = (BinIndexType)J;\
122}
123
124#elif defined (__INTEL_COMPILER)
125#define cookmem_bit2treeIndex(X, I)\
126{\
127 unsigned int J;\
128 J = _bit_scan_forward (X); \
129 I = (BinIndexType)J;\
130}
131
132#elif defined(_MSC_VER) && _MSC_VER>=1300
133#define cookmem_bit2treeIndex(X, I)\
134{\
135 unsigned int J;\
136 _BitScanForward((DWORD *) &J, X);\
137 I = (BinIndexType)J;\
138}
139
140#elif USE_BUILTIN_FFS
141#define cookmem_bit2treeIndex(X, I) I = ffs(X)-1
142
143#else
144#define cookmem_bit2treeIndex(X, I)\
145{\
146 unsigned int Y = X - 1;\
147 unsigned int K = Y >> (16-4) & 16;\
148 unsigned int N = K; Y >>= K;\
149 N += K = Y >> (8-3) & 8; Y >>= K;\
150 N += K = Y >> (4-2) & 4; Y >>= K;\
151 N += K = Y >> (2-1) & 2; Y >>= K;\
152 N += K = Y >> (1-0) & 1; Y >>= K;\
153 I = (BinIndexType)(N + Y);\
154}
155#endif /* GNUC */
156
164template<class Arena, class Logger, class TreeType = PtrRBTree, class T=void>
166{
167public:
169 typedef std::size_t size_type;
171 typedef std::ptrdiff_t difference_type;
173 typedef T value_type;
175 typedef T* pointer;
177 typedef const T* const_pointer;
178
179private:
180 typedef unsigned int BinIndexType;
181 typedef TreeType PtrTreeType;
182
183private:
192 class MemChunk
193 {
194 public:
199 static const size_type BIT_MASK = ~static_cast<size_type> (ALLOCATION_ALIGNMENT - 1);
200 static const size_type BIT_USED = 1;
201 static const size_type BIT_NOTEXACTSIZE = 2;
202
203 private:
204 size_type m_prevFootSize; /* Size of previous chunk (if free). */
205 size_type m_size; /* Size */
206
207 public:
208 inline size_type
209 getChunkSize () const
210 {
211 return m_size & BIT_MASK;
212 }
213
214 inline size_type
215 getPrevChunkSize () const
216 {
217 return m_prevFootSize & BIT_MASK;
218 }
219
220 inline void
221 setFreeChunkSize (size_type chunkSize)
222 {
223 m_size = chunkSize;
224 getNextChunk (chunkSize)->m_prevFootSize = chunkSize;
225 }
226
227 inline void
228 setFence ()
229 {
230 m_size = BIT_USED;
231 }
232
238 inline void
239 clearUsed ()
240 {
241 m_size &= ~BIT_USED;
242 }
243
244 inline bool
245 isUsed () const
246 {
247 return m_size & BIT_USED;
248 }
249
250 inline bool
251 isPrevChunkUsed () const
252 {
253 return m_prevFootSize & BIT_USED;
254 }
255
256 inline size_type
257 getUserSize () const
258 {
259 size_type chunkSize = getChunkSize ();
260 if (m_size & BIT_NOTEXACTSIZE)
261 {
262 char diff = *((char*)(this) + chunkSize - 1);
263 if (diff <= 0)
264 {
265 throw Exception (MEM_ERROR_PADDING, "Invalid padding byte value", __FILE__, __LINE__);
266 }
267 return chunkSize - CHUNK_OVERHEAD - diff;
268 }
269 return chunkSize - CHUNK_OVERHEAD;
270 }
271
272 inline void
273 setUsedSize (bool storingExactSize, size_type userSize)
274 {
275 size_type chunkSize = getChunkSize ();
276 // Reset the flags, since the chunk may have been in use with a
277 // different size (see reallocate ()).
278 m_size = chunkSize | BIT_USED;
280 {
281 userSize += CHUNK_OVERHEAD;
282 if (userSize != chunkSize)
283 {
284 m_size |= BIT_NOTEXACTSIZE;
285 char diff = (char)(chunkSize - userSize);
286 *(((char*)this) + chunkSize - 1) = diff;
287 }
288 }
289 getNextChunk (chunkSize)->m_prevFootSize = chunkSize | BIT_USED;
290 }
291
292 inline MemChunk*
293 getPrevChunk ()
294 {
295 return (MemChunk*)((char*)this - getPrevChunkSize ());
296 }
297
298 inline MemChunk*
299 getNextChunk (size_type chunkSize)
300 {
301 return (MemChunk*)((char*)this + chunkSize);
302 }
303
304 inline const MemChunk*
305 getNextChunk () const
306 {
307 return (const MemChunk*)((char*)this + getChunkSize ());
308 }
309
310 bool
311 isValidSize ()
312 {
313 const MemChunk* nextChunk = getNextChunk (getChunkSize ());
314 return getChunkSize () == nextChunk->getPrevChunkSize () &&
315 isUsed () == nextChunk->isPrevChunkUsed ();
316 }
317
318 void
319 print () const
320 {
321 printf ("-- chunk[ %p ]: size: %zu, used = %s, exact size: %zu\n",
322 static_cast<const void*> (this),
323 getChunkSize (),
324 (isUsed () ? "true " : "false"),
325 getUserSize ());
326 }
327 };
328
332 struct SmallMemChunk : public MemChunk
333 {
334 SmallMemChunk* next; /* double links -- used only if free. */
335 SmallMemChunk* prev;
336 };
337
338 class MemSegment
339 {
340 // MemPool takes the offset of m_pad (see SEGMENT_HEADER).
341 friend class MemPool;
342
343 private:
347 size_type m_size;
351 MemSegment* m_next;
357 size_type m_pad;
358
359 public:
367 MemChunk*
368 init (size_type segSize)
369 {
370 m_size = segSize;
371 m_pad = MemChunk::BIT_USED;
372
373 // We do not initiate m_next since it will be assigned
374 // by the caller anyways.
375
376 size_type chunkSize = getMaxChunkSize ();
377 MemChunk* chunk = getFirstChunk ();
378 chunk->setFreeChunkSize (chunkSize);
379 chunk->getNextChunk (chunkSize)->setFence ();
380 return chunk;
381 }
382
383 MemChunk*
384 getFirstChunk ()
385 {
386 return (MemChunk*)(((char*)this) + offsetof (MemSegment, m_pad));
387 }
388
389 const MemChunk*
390 getFirstChunk () const
391 {
392 return (const MemChunk*)(((char*)this) + offsetof (MemSegment, m_pad));
393 }
394
395 MemChunk*
396 getPastLastChunk ()
397 {
398 return (MemChunk*)(((char*)getFirstChunk ()) + getMaxChunkSize ());
399 }
400
401 const MemChunk*
402 getPastLastChunk () const
403 {
404 return (MemChunk*)(((char*)getFirstChunk ()) + getMaxChunkSize ());
405 }
406
408 getSize () const
409 {
410 return m_size;
411 }
412
413 MemSegment*
414 getNext () const
415 {
416 return m_next;
417 }
418
419 void
420 setNext (MemSegment* seg)
421 {
422 m_next = seg;
423 }
424
426 getMaxChunkSize () const
427 {
428 return (m_size - SEGMENT_OVERHEAD) & MemChunk::BIT_MASK;
429 }
430
431 void
432 print () const
433 {
434 printf ("== segment %p, size: %zu, header: %zu, footer: %zu\n", static_cast<const void*> (this), m_size, offsetof(MemSegment, m_pad), sizeof(MemChunk));
435 const MemChunk* chunk = getFirstChunk ();
436 const MemChunk* lastChunk = getPastLastChunk ();
437 for (;;)
438 {
439 if (chunk < lastChunk)
440 {
441 chunk->print ();
442 chunk = chunk->getNextChunk ();
443 }
444 else
445 {
446 printf ("-- chunk[ %p ]: size: %zu\n",
447 static_cast<const void*> (chunk),
448 chunk->getChunkSize ());
449 break;
450 }
451 }
452 }
453 };
454
455private:
462 static const size_type ALIGNMENT = ALLOCATION_ALIGNMENT;
466 static const size_type ALIGN_MASK = ALIGNMENT - 1;
467 // MemChunk stores BIT_USED and BIT_NOTEXACTSIZE in the low bits of chunk
468 // sizes, so the alignment must leave at least two low bits free.
469 static_assert (ALIGNMENT >= 4, "ALLOCATION_ALIGNMENT must be at least 4.");
470 static_assert (ALIGN_MASK == ~MemChunk::BIT_MASK,
471 "ALLOCATION_ALIGNMENT must match MemChunk::BIT_MASK.");
478 static const size_type NSMALLBINS = 32;
482 static const size_type NTREEBINS = 32;
486 static const size_type SMALLBIN_SHIFT = 3;
490 static const size_type TREEBIN_SHIFT = 8;
494 static const size_type MIN_LARGE_SIZE = (1UL << TREEBIN_SHIFT);
498 static const size_type CHUNK_OVERHEAD = sizeof(MemChunk);
502 static const size_type MIN_LARGE_REQUEST = MIN_LARGE_SIZE - CHUNK_OVERHEAD - ALIGN_MASK;
506 static const size_type MIN_CHUNK_SIZE = ((sizeof(SmallMemChunk) + ALIGN_MASK) & ~ALIGN_MASK);
510 static const size_type MAX_REQUEST = (static_cast<size_type>(-static_cast<long>(MIN_CHUNK_SIZE)) << 2);
515 static const size_type MIN_REQUEST = MIN_CHUNK_SIZE - CHUNK_OVERHEAD;
521 static const size_type SEGMENT_HEADER = offsetof (MemSegment, m_pad);
525 static const size_type SEGMENT_OVERHEAD = ((((SEGMENT_HEADER + ALIGN_MASK) & ~ALIGN_MASK) + sizeof(MemChunk) + +ALIGN_MASK) & ~ALIGN_MASK);
526private:
530 Arena& m_arena;
534 Logger& m_logger;
535
539 size_type m_footprintLimit;
540
544 MemSegment* m_segList;
548 size_type m_trimEmptySegmentsInterval;
552 size_type m_numFrees;
557 size_type m_immediateReleaseThreshold;
558 BinIndexType m_smallMap;
559 BinIndexType m_treeMap;
563 MemChunk* m_dv;
564 size_type m_dvSize;
565
569 CircularList<SmallMemChunk> m_smallLists[NSMALLBINS];
573 PtrTreeType m_largeTrees[NTREEBINS];
574
575 // memory foot print tracking
579 size_type m_footprint;
583 size_type m_maxFootprint;
587 bool m_storingExactSize;
593 const std::uint8_t m_padding;
597 char m_paddingByte;
598public:
610 MemPool (Arena& arena,
611 Logger& logger,
612 std::uint8_t paddingSize = DEFAULT_PADDING_SIZE)
613 : m_arena (arena),
614 m_logger (logger),
615 m_footprintLimit (0),
616 m_segList (nullptr),
617 m_trimEmptySegmentsInterval (DEFAULT_TRIM_EMPTY_SEGMENTS_INTERVAL),
618 m_numFrees (0),
619 m_immediateReleaseThreshold (DEFAULT_IMMEDIATE_RELEASE_THRESHOLD),
620 m_smallMap (0),
621 m_treeMap (0),
622 m_dv (nullptr),
623 m_dvSize (0),
624 m_smallLists (),
625 m_largeTrees (),
626 m_footprint (0),
627 m_maxFootprint (0),
628 m_storingExactSize (paddingSize != 0),
629 m_padding (paddingSize),
630 m_paddingByte (DEFAULT_PADDING_BYTE)
631 {
632 if (m_padding > MAX_PADDING_SIZE)
633 {
634 throw Exception (MEM_ERROR_GENERAL, "padding size cannot exceed 8 bytes.", __FILE__, __LINE__);
635 }
636 }
637
644 {
645 MemSegment* seg = m_segList;
646 while (seg)
647 {
648 void* ptr = seg;
649 size_type size = seg->getSize ();
650 seg = seg->getNext ();
651 m_logger.logFreeSegment (ptr, size);
652 m_arena.freeSegment (ptr, size);
653 }
654 }
655
664 void
666 {
667 m_footprintLimit = footprintLimit;
668 }
669
677 {
678 return m_footprintLimit;
679 }
680
694 void
696 {
697 m_trimEmptySegmentsInterval = interval;
698 }
699
708 {
709 return m_trimEmptySegmentsInterval;
710 }
711
727 void
729 {
730 m_immediateReleaseThreshold = threshold;
731 }
732
741 {
742 return m_immediateReleaseThreshold;
743 }
744
753 T*
755 {
757 MemChunk* chunk = allocateChunk (getMinAllocSize (userSize), allocType);
758 if (chunk == nullptr)
759 {
760 m_logger.logAllocation (nullptr, userSize, MEM_ALLOC_BIG);
761 return nullptr;
762 }
763 setUsed (chunk, userSize);
764 return getUserPointer (chunk, userSize, allocType);
765 }
766
783 T*
785 {
786 if (alignment == 0 || (alignment & (alignment - 1)) != 0)
787 {
788 m_logger.logAllocation (nullptr, userSize, MEM_ALLOC_BIG);
789 return nullptr;
790 }
791 if (alignment <= ALIGNMENT)
792 {
793 return allocate (userSize);
794 }
795
796 size_type allocSize = getMinAllocSize (userSize);
797 if (allocSize >= MAX_REQUEST || alignment >= MAX_REQUEST - allocSize)
798 {
799 m_logger.logAllocation (nullptr, userSize, MEM_ALLOC_BIG);
800 return nullptr;
801 }
802 size_type chunkSize = (allocSize < MIN_REQUEST) ? MIN_CHUNK_SIZE : calcChunkSize (allocSize);
803
804 // Allocate enough room to find an aligned position inside the chunk
805 // that leaves a free chunk of at least MIN_CHUNK_SIZE in front of it.
807 MemChunk* chunk = allocateChunk (chunkSize + alignment + MIN_CHUNK_SIZE - CHUNK_OVERHEAD, allocType);
808 if (chunk == nullptr)
809 {
810 m_logger.logAllocation (nullptr, userSize, MEM_ALLOC_BIG);
811 return nullptr;
812 }
813
814 std::uintptr_t mem = reinterpret_cast<std::uintptr_t> (chunk2Mem (chunk));
815 if ((mem & (alignment - 1)) != 0)
816 {
817 // Free the memory in front of the aligned position. The chunk
818 // before it is in use, since free chunks are always coalesced,
819 // so the leading chunk does not need to be coalesced.
820 std::uintptr_t alignedMem = (mem + alignment - 1) & ~static_cast<std::uintptr_t> (alignment - 1);
821 size_type leadSize = static_cast<size_type> (alignedMem - mem);
822 if (leadSize < MIN_CHUNK_SIZE)
823 {
825 }
826 MemChunk* alignedChunk = chunk->getNextChunk (leadSize);
827 size_type alignedSize = chunk->getChunkSize () - leadSize;
828
829 chunk->setFreeChunkSize (leadSize);
830 addChunk (chunk);
831 alignedChunk->setFreeChunkSize (alignedSize);
833 }
834
835 // Free the memory after the requested size.
836 size_type remainSize = chunk->getChunkSize () - chunkSize;
837 if (remainSize >= MIN_CHUNK_SIZE)
838 {
839 MemChunk* remainChunk = chunk->getNextChunk (chunkSize);
840 MemChunk* nextChunk = remainChunk->getNextChunk (remainSize);
841 if (!nextChunk->isUsed ())
842 {
843 removeChunk (nextChunk);
844 remainSize += nextChunk->getChunkSize ();
845 }
846 chunk->setFreeChunkSize (chunkSize);
847 remainChunk->setFreeChunkSize (remainSize);
848 addChunk (remainChunk);
849 }
850
851 setUsed (chunk, userSize);
852 return getUserPointer (chunk, userSize, allocType);
853 }
854
866 T*
868 {
869 if (ptr == nullptr)
870 {
871 return allocate(newUserSize);
872 }
873
874 MemChunk* chunk = mem2Chunk (ptr);
875 size_type oldUserSize = chunk->getUserSize ();
876
877 // The new chunk must also hold the padding, and a request too large
878 // to represent must not wrap around to a small chunk size.
879 size_type newAllocSize = getMinAllocSize (newUserSize);
880 if (newAllocSize >= MAX_REQUEST)
881 {
882 m_logger.logAllocation (nullptr, newUserSize, MEM_ALLOC_BIG);
883 return nullptr;
884 }
885 size_type newChunkSize = (newAllocSize < MIN_REQUEST) ? MIN_CHUNK_SIZE : calcChunkSize (newAllocSize);
886
887 size_type chunkSize = chunk->getChunkSize ();
889 {
890 // Grow in place if the next chunk is free and large enough.
891 MemChunk* nextChunk = chunk->getNextChunk (chunkSize);
892 if (!nextChunk->isUsed () &&
893 chunkSize + nextChunk->getChunkSize () >= newChunkSize)
894 {
895 m_logger.logReallocation (ptr, oldUserSize, newUserSize);
896
897 size_type mergedSize = chunkSize + nextChunk->getChunkSize ();
898 removeChunk (nextChunk);
899
900 // The chunk after nextChunk is in use, since free chunks are
901 // always coalesced, so the remainder does not need merging.
903 if (remainSize >= MIN_CHUNK_SIZE)
904 {
905 chunk->setFreeChunkSize (newChunkSize);
906 MemChunk* remainChunk = chunk->getNextChunk (newChunkSize);
907 remainChunk->setFreeChunkSize (remainSize);
908 addChunk (remainChunk);
909 }
910 else
911 {
912 chunk->setFreeChunkSize (mergedSize);
913 }
914 setUsed (chunk, newUserSize);
915 return ptr;
916 }
917
918 // Otherwise allocate new memory and copy.
920 if (newPtr)
921 {
923 deallocate (ptr);
924 }
925 return newPtr;
926 }
927 else
928 {
929 m_logger.logReallocation (ptr, oldUserSize, newUserSize);
930
932 if (remainSize >= MIN_CHUNK_SIZE)
933 {
934 MemChunk* remainChunk = (MemChunk*)((char*)chunk + newChunkSize);
935 MemChunk* nextChunk = remainChunk->getNextChunk (remainSize);
936
937 if (!nextChunk->isUsed ())
938 {
939 removeChunk (nextChunk);
940 remainSize += nextChunk->getChunkSize ();
941 }
942
943 chunk->setFreeChunkSize (newChunkSize);
944 setUsed (chunk, newUserSize);
945 remainChunk->setFreeChunkSize (remainSize);
946 addChunk (remainChunk);
947 }
948 else
949 {
950 setUsed (chunk, newUserSize);
951 }
952
953 return ptr;
954 }
955 }
956
967 T*
969 {
970 if (num != 0 && size > (MAX_REQUEST / num))
971 {
972 m_logger.logAllocation (nullptr, MAX_REQUEST, MEM_ALLOC_BIG);
973 return nullptr;
974 }
975 size_type totalSize = size * num;
976 T* ptr = allocate (totalSize);
977 if (ptr)
978 {
979 memset (ptr, 0, totalSize);
980 }
981 return ptr;
982 }
983
992 void
994 {
995 if (ptr != nullptr)
996 {
997 MemChunk* chunk = mem2Chunk (ptr);
998 if (!chunk->isUsed())
999 {
1000 throw Exception (MEM_ERROR_DOUBLE_FREE, "potentially freeing an unused pointer", __FILE__, __LINE__);
1001 }
1002 COOKMEM_ASSERT (chunk->isValidSize ());
1003 size_type chunkSize = chunk->getChunkSize ();
1004 if (m_padding != 0)
1005 {
1006 COOKMEM_ASSERT (chunkSize > CHUNK_OVERHEAD);
1007 size_type userSize = chunk->getUserSize () + CHUNK_OVERHEAD;
1008 COOKMEM_ASSERT (userSize < chunkSize);
1009#if COOKMEM_DEBUG > 0
1010 char diff = (char)(chunkSize - userSize);
1011 COOKMEM_ASSERT ((diff > 0) && ((userSize + diff) == chunkSize));
1012 COOKMEM_ASSERT (*(((char*)chunk) + chunkSize - 1) == diff);
1013#endif // COOKMEM_DEBUG
1014
1015 char* paddingPtr = ((char*)chunk) + userSize;
1016 for (std::uint8_t i = 0; i < m_padding; ++i)
1017 {
1018 if (paddingPtr[i] != m_paddingByte)
1019 {
1020 throw Exception (MEM_ERROR_PADDING, "padding byte got modified.", __FILE__, __LINE__);
1021 }
1022 }
1023 }
1024 m_logger.logDeallocation (ptr, chunk->getUserSize ());
1025 chunk->clearUsed ();
1026
1027 bool prevChunkUsed = chunk->isPrevChunkUsed ();
1028 MemChunk* nextChunk = chunk->getNextChunk (chunkSize);
1029 bool nextChunkUsed = nextChunk->isUsed ();
1030
1031 MemChunk* freeChunk;
1032
1033 // Fast path: when only the previous chunk is free, it may be able
1034 // to absorb this chunk without being moved to a different place.
1035 // This is common when memory is freed in allocation order.
1036 if (!prevChunkUsed &&
1037 nextChunkUsed &&
1038 growFreeChunkInPlace (chunk->getPrevChunk (), chunkSize))
1039 {
1040 freeChunk = chunk->getPrevChunk ();
1041 }
1042 else
1043 {
1044 freeChunk = chunk;
1046
1047 if (!prevChunkUsed)
1048 {
1049 MemChunk* prevChunk = chunk->getPrevChunk ();
1050 removeChunk (prevChunk);
1052 freeChunkSize += prevChunk->getChunkSize ();
1053 }
1054
1055 if (!nextChunkUsed)
1056 {
1057 freeChunkSize += nextChunk->getChunkSize ();
1058 removeChunk (nextChunk);
1059 }
1060
1061 freeChunk->setFreeChunkSize (freeChunkSize);
1062 addChunk (freeChunk);
1063 }
1064
1065 // A large allocation that had a segment of its own is returned
1066 // to the arena right away, rather than at the next trimming pass.
1067 if (m_immediateReleaseThreshold != 0 &&
1068 chunkSize >= m_immediateReleaseThreshold &&
1069 isWholeSegment (freeChunk))
1070 {
1071 releaseSegment (freeChunk);
1072 }
1073
1074 ++m_numFrees;
1075 if (m_numFrees >= m_trimEmptySegmentsInterval)
1076 {
1077 trimEmptySegments ();
1078 }
1079 }
1080 else
1081 {
1082 m_logger.logDeallocation (ptr, size);
1083 }
1084 }
1085
1099 bool
1100 contains (T* ptr, bool checkUsed = false)
1101 {
1102 MemSegment* seg = m_segList;
1103 while (seg)
1104 {
1105 if (reinterpret_cast<char*>(ptr) >= reinterpret_cast<char*>(seg) &&
1106 reinterpret_cast<char*>(ptr) <= (reinterpret_cast<char*>(seg) + seg->getSize ()))
1107 {
1108 if (checkUsed)
1109 {
1110 MemChunk* chunk = mem2Chunk (ptr);
1111 return chunk->isUsed ();
1112 }
1113 return true;
1114 }
1115 seg = seg->getNext ();
1116 }
1117 return false;
1118 }
1119
1123 void
1125 {
1126 COOKMEM_ASSERT ((m_dv == nullptr && m_dvSize == 0) ||
1127 (m_dv != nullptr &&
1128 !m_dv->isUsed () &&
1129 m_dv->getChunkSize () == m_dvSize));
1130
1131 // A tree bin is marked in the tree map if and only if its tree is
1132 // not empty.
1133 for (BinIndexType binIndex = 0; binIndex < NTREEBINS; ++binIndex)
1134 {
1135 COOKMEM_ASSERT (isTreeMapMarked (binIndex) == !treeAt (binIndex).isEmpty ());
1136 }
1137
1138 MemSegment* seg = m_segList;
1139 while (seg)
1140 {
1141 size_type segSize = seg->getSize ();
1143
1144 MemChunk* firstChunk = seg->getFirstChunk ();
1145 MemChunk* chunk = seg->getPastLastChunk ();
1146
1147 do
1148 {
1149 size_type prevChunkSize = chunk->getPrevChunkSize ();
1150
1151 if (prevChunkSize == 0)
1152 {
1153 break;
1154 }
1155
1156 COOKMEM_ASSERT (prevChunkSize < remainSize);
1158
1159 MemChunk* prevChunk = chunk->getPrevChunk ();
1160 COOKMEM_ASSERT (prevChunkSize == prevChunk->getChunkSize ());
1161
1162 if (!chunk->isPrevChunkUsed ())
1163 {
1164 COOKMEM_ASSERT (isValidChunk (prevChunk));
1165 // Free chunks are always coalesced.
1166 COOKMEM_ASSERT (chunk->isUsed ());
1167 }
1168
1169 chunk = prevChunk;
1170 }
1171 while (chunk >= firstChunk);
1172
1173 COOKMEM_ASSERT (remainSize == SEGMENT_OVERHEAD &&
1174 chunk == firstChunk);
1175 // Only used by the assertions, which release builds omit.
1176 (void) remainSize;
1177
1178 seg = seg->getNext ();
1179 }
1180 }
1181
1182
1197 size_type
1199 {
1200 if (ptr == nullptr)
1201 {
1202 return 0;
1203 }
1204 MemChunk* chunk = mem2Chunk (ptr);
1205 if (chunk->isUsed ())
1206 {
1207 return chunk->getUserSize ();
1208 }
1209 return 0;
1210 }
1211
1215 void
1217 {
1218 MemSegment* seg = m_segList;
1219 while (seg)
1220 {
1221 void* ptr = seg;
1222 size_type size = seg->getSize ();
1223 seg = seg->getNext ();
1224 m_logger.logFreeSegment (ptr, size);
1225 m_arena.freeSegment (ptr, size);
1226 }
1227 m_segList = nullptr;
1228 m_smallMap = 0;
1229 m_treeMap = 0;
1230 m_footprint = 0;
1231 m_dv = nullptr;
1232 m_dvSize = 0;
1233 memset ((void*)m_largeTrees, 0, sizeof(m_largeTrees));
1234 memset ((void*)m_smallLists, 0, sizeof(m_smallLists));
1235 }
1236
1242 size_type
1244 {
1245 return m_footprint;
1246 }
1247
1253 size_type
1255 {
1256 return m_maxFootprint;
1257 }
1258
1264 bool
1266 {
1267 return m_storingExactSize;
1268 }
1269
1278 void
1280 {
1281 if (m_padding == 0)
1282 {
1283 m_storingExactSize = b;
1284 }
1285 }
1286
1294 bool
1295 isPadding () const
1296 {
1297 return m_padding != 0;
1298 }
1299
1303 std::uint8_t
1305 {
1306 return m_padding;
1307 }
1308
1314 std::uint8_t
1316 {
1317 return (std::uint8_t)m_paddingByte;
1318 }
1319
1326 void
1328 {
1329 m_paddingByte = (char)paddingByte;
1330 }
1331
1336 void
1337 print () const
1338 {
1339 printf ("**** context: %p ****\n", static_cast<const void*> (this));
1340
1341 MemSegment* seg = m_segList;
1342 while (seg)
1343 {
1344 seg->print ();
1345 seg = seg->getNext ();
1346 }
1347 }
1348
1349private:
1355 void
1356 trimEmptySegments ()
1357 {
1358 m_logger.logTrimEmptySegments ();
1359 MemSegment* seg = m_segList;
1360 MemSegment* prevSeg = nullptr;
1361 while (seg)
1362 {
1363 MemSegment* nextSeg = seg->getNext ();
1364
1365 size_type segSize = seg->getSize ();
1366 MemChunk* firstChunk = seg->getFirstChunk ();
1367 if (!firstChunk->isUsed () &&
1368 firstChunk->getChunkSize () == seg->getMaxChunkSize ())
1369 {
1370 // The entire segment is free, so we can just release the
1371 // segment.
1372 //
1373 // If users want to keep the segment around for caching, they
1374 // can always use CacheMemArena.
1375 removeChunk (firstChunk);
1376
1377 m_footprint -= segSize;
1378 m_logger.logFreeSegment (seg, segSize);
1379 m_arena.freeSegment (seg, segSize);
1380
1381 if (prevSeg == nullptr)
1382 {
1383 m_segList = nextSeg;
1384 }
1385 if (prevSeg != nullptr)
1386 {
1387 prevSeg->setNext (nextSeg);
1388 }
1389 }
1390 else
1391 {
1392 prevSeg = seg;
1393 }
1394 seg = nextSeg;
1395 }
1396 m_numFrees = 0;
1397 }
1398
1406 static inline bool
1407 isWholeSegment (MemChunk* chunk)
1408 {
1409 // Only the first chunk of a segment has a previous size of 0, and
1410 // only the fence at the end of a segment has a size of 0.
1411 return chunk->getPrevChunkSize () == 0 &&
1412 chunk->getNextChunk (chunk->getChunkSize ())->getChunkSize () == 0;
1413 }
1414
1421 void
1422 releaseSegment (MemChunk* chunk)
1423 {
1424 MemSegment* seg = reinterpret_cast<MemSegment*> (reinterpret_cast<char*> (chunk) - SEGMENT_HEADER);
1425 COOKMEM_ASSERT (seg->getFirstChunk () == chunk &&
1426 chunk->getChunkSize () == seg->getMaxChunkSize ());
1427
1428 MemSegment* prevSeg = nullptr;
1429 for (MemSegment* s = m_segList; s != seg; s = s->getNext ())
1430 {
1431 COOKMEM_ASSERT (s != nullptr);
1432 prevSeg = s;
1433 }
1434 if (prevSeg == nullptr)
1435 {
1436 m_segList = seg->getNext ();
1437 }
1438 else
1439 {
1440 prevSeg->setNext (seg->getNext ());
1441 }
1442
1443 removeChunk (chunk);
1444
1445 size_type segSize = seg->getSize ();
1446 m_footprint -= segSize;
1447 m_logger.logFreeSegment (seg, segSize);
1448 m_arena.freeSegment (seg, segSize);
1449 }
1450
1451 static inline BinIndexType
1452 idx2bit (BinIndexType binIndex)
1453 {
1454 return ((BinIndexType) (1) << (binIndex));
1455 }
1456
1463 static inline T*
1464 chunk2Mem (MemChunk* chunk)
1465 {
1466 return ((T*)((char*)(chunk) + sizeof(MemChunk)));
1467 }
1468
1475 static inline MemChunk*
1476 mem2Chunk (T* mem)
1477 {
1478 return ((MemChunk*)((char*)(mem) - sizeof(MemChunk)));
1479 }
1480
1489 static inline size_type
1490 calcChunkSize (size_type requestSize)
1491 {
1492 return (((requestSize) + CHUNK_OVERHEAD + ALIGN_MASK) & ~ALIGN_MASK);
1493 }
1494
1495 static inline size_type
1496 getSmallBinSize (BinIndexType binIndex)
1497 {
1498 return ((binIndex) << SMALLBIN_SHIFT);
1499 }
1500
1501 static inline BinIndexType
1502 getSmallBinIndex (size_type size)
1503 {
1504 return (BinIndexType) ((size) >> SMALLBIN_SHIFT);
1505 }
1506
1507 static inline bool
1508 isSmallChunk (size_type size)
1509 {
1510 return ((size) >> SMALLBIN_SHIFT) < NSMALLBINS;
1511 }
1512
1513 inline PtrTreeType&
1514 treeAt (BinIndexType i)
1515 {
1516 return m_largeTrees[i];
1517 }
1518
1526 static inline size_type
1527 getTreeBinMinSize (BinIndexType binIndex)
1528 {
1529 return (static_cast<size_type> (1) << ((binIndex >> 1) + TREEBIN_SHIFT)) |
1530 (static_cast<size_type> (binIndex & 1) << ((binIndex >> 1) + TREEBIN_SHIFT - 1));
1531 }
1532
1544 inline MemChunk*
1545 treeMalloc (size_type size, size_type limit)
1546 {
1547 MemChunk* chunk = nullptr;
1548 size_type actualSize = size;
1549
1550 BinIndexType binIndex;
1551 cookmem_getLargeBinIndex (size, binIndex);
1552
1553 // Bins hold increasing sizes, so stop at a bin whose chunks are all
1554 // at least the limit.
1555 for (; binIndex < NTREEBINS && getTreeBinMinSize (binIndex) < limit; ++binIndex)
1556 {
1557 PtrTreeType& tree = treeAt (binIndex);
1558 if (!tree.isEmpty ())
1559 {
1560 chunk = reinterpret_cast<MemChunk*>(tree.remove(actualSize, limit));
1561 if (chunk != nullptr)
1562 {
1563 if (tree.isEmpty ())
1564 {
1565 clearTreeMap (binIndex);
1566 }
1567 break;
1568 }
1569 }
1570 }
1571
1572 if (chunk != nullptr)
1573 {
1574 return splitChunk (chunk, size);
1575 }
1576 return nullptr;
1577 }
1578
1589 inline MemChunk*
1590 allocateChunk (size_type allocSize, MemAllocType_et& allocType)
1591 {
1592 size_type chunkSize;
1593
1594 if (allocSize < MIN_LARGE_REQUEST)
1595 {
1596 BinIndexType binIndex;
1597 BinIndexType smallBits;
1598 chunkSize = (allocSize < MIN_REQUEST) ? MIN_CHUNK_SIZE : calcChunkSize (allocSize);
1599 binIndex = getSmallBinIndex (chunkSize);
1600 smallBits = m_smallMap >> binIndex;
1601
1602 /*
1603 * Check if two smallest chunk lists that can satisfy the request.
1604 * Note that since the chunk is quite tight, it is not necessary
1605 * to split the chunk since there will not be enough rooms.
1606 */
1607 if ((smallBits & 0x3U) != 0)
1608 {
1609 MemChunk* chunk;
1610 binIndex += ~smallBits & 1; /* Uses next bin if idx empty */
1611
1612 chunk = removeSmallChunkList (binIndex);
1613 COOKMEM_ASSERT(chunk != nullptr && chunk->getChunkSize () == getSmallBinSize (binIndex));
1614
1615 allocType = MEM_ALLOC_SMALL;
1616 return chunk;
1617 }
1618 else
1619 {
1620 if (chunkSize > m_dvSize &&
1621 smallBits != 0)
1622 {
1623 /* Use chunk in next nonempty smallbin */
1624 MemChunk* chunk;
1625 BinIndexType i;
1626 BinIndexType leftbits = (smallBits << binIndex) & left_bits(idx2bit(binIndex));
1627 BinIndexType leastbit = least_bit(leftbits);
1628 cookmem_bit2treeIndex(leastbit, i);
1629
1630 chunk = removeSmallChunkList (i);
1631 COOKMEM_ASSERT(chunk != nullptr && chunk->getChunkSize () == getSmallBinSize (i));
1632
1633 allocType = MEM_ALLOC_SMALL;
1634 return splitChunk (chunk, chunkSize);
1635 }
1636 }
1637 }
1638 else if (allocSize >= MAX_REQUEST)
1639 {
1640 // The request size is too big.
1641 return nullptr;
1642 }
1643 else
1644 {
1645 chunkSize = calcChunkSize (allocSize);
1646 }
1647
1648 MemChunk* chunk;
1649
1650 if (allocSize < MIN_LARGE_REQUEST &&
1651 m_dvSize >= chunkSize)
1652 {
1653 allocType = MEM_ALLOC_DV;
1654 return takeDesignatedVictim (chunkSize);
1655 }
1656
1657 allocType = MEM_ALLOC_NONE;
1658
1659 /*
1660 * Check if we have enough space in existing chunks.
1661 */
1662 if (m_treeMap != 0)
1663 {
1664 // As in dlmalloc, a tree chunk is only used if it fits better
1665 // than the designated victim.
1666 chunk = treeMalloc (chunkSize, (m_dvSize >= chunkSize) ? m_dvSize : ~static_cast<size_type> (0));
1667 if (chunk != nullptr)
1668 {
1669 allocType = MEM_ALLOC_TREE;
1670 }
1671 }
1672 else
1673 {
1674 chunk = nullptr;
1675 }
1676
1677 if (chunk == nullptr &&
1678 m_dvSize >= chunkSize)
1679 {
1680 chunk = takeDesignatedVictim (chunkSize);
1681 allocType = MEM_ALLOC_DV;
1682 }
1683
1684 /*
1685 * At this point, we have to allocate memory from arena.
1686 */
1687 if (chunk == nullptr)
1688 {
1689 chunk = arenaAlloc (chunkSize);
1690 allocType = MEM_ALLOC_ARENA;
1691 }
1692
1693 return chunk;
1694 }
1695
1696
1707 inline MemChunk*
1708 splitChunk (MemChunk* chunk, size_type size)
1709 {
1710 // the size of the 2nd half after the split
1711 size_type remainSize = chunk->getChunkSize () - size;
1712
1713 if (remainSize >= MIN_CHUNK_SIZE)
1714 {
1715 MemChunk* remainChunk = (MemChunk*)((char*)chunk + size);
1716
1717 chunk->setFreeChunkSize (size);
1718
1719 remainChunk->setFreeChunkSize (remainSize);
1720
1721 addChunk (remainChunk);
1722 }
1723 return chunk;
1724 }
1725
1736 inline MemChunk*
1737 takeDesignatedVictim (size_type size)
1738 {
1739 MemChunk* chunk = m_dv;
1740 size_type remainSize = m_dvSize - size;
1741
1742 if (remainSize >= MIN_CHUNK_SIZE)
1743 {
1744 MemChunk* remainChunk = (MemChunk*)((char*)chunk + size);
1745 chunk->setFreeChunkSize (size);
1746 remainChunk->setFreeChunkSize (remainSize);
1747
1748 if (!isSmallChunk (remainSize))
1749 {
1750 // The remainder is still a large chunk, so it simply becomes
1751 // the new designated victim.
1752 m_dv = remainChunk;
1753 m_dvSize = remainSize;
1754 return chunk;
1755 }
1756
1757 m_dv = nullptr;
1758 m_dvSize = 0;
1759 addSmallChunk (remainChunk);
1760 return chunk;
1761 }
1762
1763 m_dv = nullptr;
1764 m_dvSize = 0;
1765 return chunk;
1766 }
1767
1776 MemChunk*
1777 arenaAlloc (size_type chunkSize)
1778 {
1779 size_type estSize; /* allocation size */
1780
1781 estSize = chunkSize + SEGMENT_OVERHEAD;
1782 if (estSize <= chunkSize)
1783 {
1784 // Technically can never reach here due to the MAX_REQUEST value
1785 //
1786 // However it is always good to have a check.
1787 return nullptr;
1788 }
1789
1790 /*
1791 * Check user specified foot print limit
1792 */
1793 if (m_footprintLimit != 0)
1794 {
1795 size_type fp = m_footprint + estSize;
1796 if (fp <= m_footprint || fp > m_footprintLimit)
1797 {
1798 return nullptr;
1799 }
1800 }
1801
1802 /*
1803 * Request memory segments from arena.
1804 */
1805 size_type segSize = estSize;
1806 MemSegment* seg = (MemSegment*)m_arena.getSegment (segSize);
1807 m_logger.logGetSegment (seg, segSize);
1808
1809 /*
1810 * Initialize the segment obtained.
1811 */
1812 if (seg)
1813 {
1814 if ((m_footprint += segSize) > m_maxFootprint)
1815 {
1816 m_maxFootprint = m_footprint;
1817 }
1818
1819 MemChunk* chunk = seg->init (segSize);
1820
1821 if (m_segList == nullptr)
1822 {
1823 m_segList = seg;
1824 seg->setNext (nullptr);
1825 }
1826 else
1827 {
1828 seg->setNext (m_segList);
1829 m_segList = seg;
1830 }
1831
1832 return splitChunk (chunk, chunkSize);
1833 }
1834 return nullptr;
1835 }
1836
1837 inline CircularList<SmallMemChunk>&
1838 getSmallChunkList (BinIndexType binIndex)
1839 {
1840 return m_smallLists[binIndex];
1841 }
1842
1843 inline void
1844 addSmallChunk (MemChunk* chunk)
1845 {
1846 BinIndexType I = getSmallBinIndex(chunk->getChunkSize());
1847
1848 CircularList<SmallMemChunk>& freeList = getSmallChunkList(I);
1849 freeList.add ((SmallMemChunk*)chunk);
1850 if (!isSmallMapMarked(I))
1851 markSmallMap(I);
1852 }
1853
1854 inline MemChunk*
1855 removeSmallChunkList (BinIndexType binIndex)
1856 {
1857 CircularList<SmallMemChunk>& freeList = getSmallChunkList (binIndex);
1858 SmallMemChunk* chunk = freeList.remove();
1859 if (freeList.isEmpty ())
1860 {
1861 clearSmallMap (binIndex);
1862 }
1863 return chunk;
1864 }
1865
1866 inline void
1867 removeSmallChunk (MemChunk* chunk)
1868 {
1869 BinIndexType binIndex = getSmallBinIndex (chunk->getChunkSize ());
1870
1871 CircularList<SmallMemChunk>& freeList = getSmallChunkList (binIndex);
1872 if (freeList.remove ((SmallMemChunk*)chunk))
1873 {
1874 clearSmallMap (binIndex);
1875 }
1876 }
1877
1878 inline void
1879 markSmallMap (BinIndexType binIndex)
1880 {
1881 m_smallMap |= idx2bit(binIndex);
1882 }
1883
1884 inline void
1885 clearSmallMap (BinIndexType binIndex)
1886 {
1887 m_smallMap &= ~idx2bit(binIndex);
1888 }
1889
1890 inline bool
1891 isSmallMapMarked (BinIndexType binIndex)
1892 {
1893 return m_smallMap & idx2bit (binIndex);
1894 }
1895
1896 inline void
1897 markTreeMap (BinIndexType binIndex)
1898 {
1899 m_treeMap |= idx2bit (binIndex);
1900 }
1901
1902 inline void
1903 clearTreeMap (BinIndexType binIndex)
1904 {
1905 m_treeMap &= ~idx2bit (binIndex);
1906 }
1907
1908 inline bool
1909 isTreeMapMarked (BinIndexType binIndex)
1910 {
1911 return m_treeMap & idx2bit (binIndex);
1912 }
1913
1914 void
1915 addLargeChunk (MemChunk* chunk)
1916 {
1917 BinIndexType treeBinIndex;
1918 cookmem_getLargeBinIndex(chunk->getChunkSize(), treeBinIndex);
1919 PtrTreeType& tree = treeAt (treeBinIndex);
1920 if (!isTreeMapMarked (treeBinIndex))
1921 {
1922 markTreeMap (treeBinIndex);
1923 }
1924 tree.add(chunk, chunk->getChunkSize());
1925 }
1926
1927 inline void
1928 removeLargeChunk (MemChunk* chunk)
1929 {
1930 BinIndexType treeBinIndex;
1931 cookmem_getLargeBinIndex (chunk->getChunkSize(), treeBinIndex);
1932 PtrTreeType& tree = treeAt (treeBinIndex);
1933
1934 tree.remove (chunk);
1935 if (tree.isEmpty ())
1936 {
1937 clearTreeMap (treeBinIndex);
1938 }
1939 }
1940
1941 inline bool
1942 isValidChunk (MemChunk* chunk)
1943 {
1944 if (chunk == m_dv)
1945 {
1946 return true;
1947 }
1948
1949 if (isSmallChunk (chunk->getChunkSize ()))
1950 {
1951 BinIndexType I = getSmallBinIndex (chunk->getChunkSize ());
1952
1953 CircularList<SmallMemChunk>& freeList = getSmallChunkList (I);
1954 return freeList.contains ((SmallMemChunk*)chunk);
1955 }
1956 else
1957 {
1958 BinIndexType treeBinIndex;
1959 cookmem_getLargeBinIndex (chunk->getChunkSize (), treeBinIndex);
1960 PtrTreeType& tree = treeAt (treeBinIndex);
1961 return tree.contains (chunk);
1962 }
1963 }
1964
1980 inline bool
1981 growFreeChunkInPlace (MemChunk* chunk, size_type extraSize)
1982 {
1983 size_type oldSize = chunk->getChunkSize ();
1984 size_type newSize = oldSize + extraSize;
1985
1986 if (chunk == m_dv)
1987 {
1988 chunk->setFreeChunkSize (newSize);
1989 m_dvSize = newSize;
1990 return true;
1991 }
1992
1993 if (isSmallChunk (oldSize))
1994 {
1995 return false;
1996 }
1997
1998 // The tree bin is determined by the two most significant bits of the
1999 // size (see cookmem_getLargeBinIndex). If the bits that differ are
2000 // all below oldSize / 4, the two most significant bits are unchanged,
2001 // so the chunk stays in the same bin. This check is conservative.
2002 if ((oldSize ^ newSize) > (oldSize >> 2))
2003 {
2004 return false;
2005 }
2006
2007 BinIndexType treeBinIndex;
2008 cookmem_getLargeBinIndex (oldSize, treeBinIndex);
2009 if (!treeAt (treeBinIndex).canGrowInPlace (chunk, newSize))
2010 {
2011 return false;
2012 }
2013
2014 // The size field of the chunk is also the value of its tree node, so
2015 // this updates the tree as well.
2016 chunk->setFreeChunkSize (newSize);
2017 return true;
2018 }
2019
2020 inline void
2021 addChunk (MemChunk* chunk)
2022 {
2023 if (isSmallChunk(chunk->getChunkSize ()))
2024 {
2025 addSmallChunk (chunk);
2026 }
2027 else if (m_dv == nullptr)
2028 {
2029 m_dv = chunk;
2030 m_dvSize = chunk->getChunkSize ();
2031 }
2032 else
2033 {
2034 addLargeChunk (chunk);
2035 }
2036 }
2037
2038 inline void
2039 removeChunk (MemChunk* chunk)
2040 {
2041 if (chunk == m_dv)
2042 {
2043 m_dv = nullptr;
2044 m_dvSize = 0;
2045 }
2046 else if (isSmallChunk (chunk->getChunkSize ()))
2047 {
2048 removeSmallChunk (chunk);
2049 }
2050 else
2051 {
2052 removeLargeChunk (chunk);
2053 }
2054 }
2055
2059 inline size_t
2060 getMinAllocSize (size_type userSize)
2061 {
2062 if (m_padding == 0)
2063 {
2064 return userSize;
2065 }
2066 if (userSize > (MAX_REQUEST - m_padding - 1))
2067 {
2068 return MAX_REQUEST;
2069 }
2070 // +1 for the size metadata
2071 return userSize + m_padding + 1;
2072 }
2073
2074 inline void
2075 setUsed (MemChunk* chunk, size_type userSize)
2076 {
2077 chunk->setUsedSize (m_storingExactSize, userSize);
2078
2079 // if we are padding, userSize will always be smaller
2080 // than the chunkSize
2081 if (m_padding != 0)
2082 {
2083 char* paddingPtr = ((char*)chunk) + userSize + CHUNK_OVERHEAD;
2084 for (std::uint8_t i = 0; i < m_padding; ++i)
2085 {
2086 paddingPtr[i] = m_paddingByte;
2087 }
2088 }
2089 }
2090
2091 inline T*
2092 getUserPointer (MemChunk* chunk, size_type userSize, MemAllocType_et allocType)
2093 {
2094 T* userPtr = chunk2Mem (chunk);
2095 m_logger.logAllocation (userPtr, userSize, allocType);
2096 return userPtr;
2097 }
2098};
2099
2100#undef cookmem_getLargeBinIndex
2101#undef cookmem_bit2treeIndex
2102
2103} // namespace cookmem
2104
2105#endif // COOK_MEM_POOL_H
Definition exception.h:39
Definition allocator.h:39
Definition pool.h:166
size_type getFootprintLimit() const
Definition pool.h:676
void setTrimEmptySegmentsInterval(size_type interval)
Definition pool.h:695
bool contains(T *ptr, bool checkUsed=false)
Definition pool.h:1100
bool isStoringExactSize() const
Definition pool.h:1265
~MemPool()
Definition pool.h:643
size_type getUserSize(T *ptr)
Definition pool.h:1198
T * callocate(size_type num, size_type size)
Definition pool.h:968
void setStoringExactSize(bool b)
Definition pool.h:1279
void validate()
Definition pool.h:1124
T * reallocate(T *ptr, size_type newUserSize)
Definition pool.h:867
void print() const
Definition pool.h:1337
std::uint8_t getPaddingSize() const
Definition pool.h:1304
std::uint8_t getPaddingByte() const
Definition pool.h:1315
std::size_t size_type
Definition pool.h:169
MemPool(Arena &arena, Logger &logger, std::uint8_t paddingSize=DEFAULT_PADDING_SIZE)
Definition pool.h:610
const T * const_pointer
Definition pool.h:177
void deallocate(T *ptr, size_type size=0)
Definition pool.h:993
size_type getFootprint() const
Definition pool.h:1243
void setImmediateReleaseThreshold(size_type threshold)
Definition pool.h:728
void releaseAll()
Definition pool.h:1216
size_type getImmediateReleaseThreshold() const
Definition pool.h:740
size_type getMaxFootprint() const
Definition pool.h:1254
void setPaddingByte(std::uint8_t paddingByte)
Definition pool.h:1327
std::ptrdiff_t difference_type
Definition pool.h:171
size_type getTrimEmptySegmentsInterval() const
Definition pool.h:707
bool isPadding() const
Definition pool.h:1295
T * allocateAligned(size_type userSize, size_type alignment)
Definition pool.h:784
T * allocate(size_type userSize)
Definition pool.h:754
T value_type
Definition pool.h:173
T * pointer
Definition pool.h:175
void setFootprintLimit(size_type footprintLimit)
Definition pool.h:665
Definition allocator.h:29
const char DEFAULT_PADDING_BYTE
Definition config.h:64
const std::size_t DEFAULT_IMMEDIATE_RELEASE_THRESHOLD
Definition config.h:46
@ MEM_ERROR_PADDING
padding bytes after an allocation were overwritten
Definition exception.h:32
@ MEM_ERROR_GENERAL
general error
Definition exception.h:29
@ MEM_ERROR_DOUBLE_FREE
freeing an already free pointer
Definition exception.h:31
const std::size_t DEFAULT_TRIM_EMPTY_SEGMENTS_INTERVAL
Definition config.h:41
const std::uint8_t DEFAULT_PADDING_SIZE
Definition config.h:56
const std::uint8_t MAX_PADDING_SIZE
Definition config.h:60
MemAllocType_et
Definition logger.h:31
@ MEM_ALLOC_TREE
Definition logger.h:37
@ MEM_ALLOC_BIG
Definition logger.h:43
@ MEM_ALLOC_ARENA
Definition logger.h:41
@ MEM_ALLOC_DV
Definition logger.h:39
@ MEM_ALLOC_SMALL
Definition logger.h:35
@ MEM_ALLOC_NONE
Definition logger.h:33
const std::size_t ALLOCATION_ALIGNMENT
Definition config.h:36