| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630 |
- /*
- * SPDX-FileCopyrightText: 2024 Matthew Conte
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
- #pragma once
- #include "tlsf_block_functions.h"
- #if defined(__cplusplus)
- extern "C" {
- #define tlsf_decl static inline
- #else
- #define tlsf_decl rt_always_inline
- #endif
- enum tlsf_config
- {
- /* All allocation sizes and addresses are aligned to 4 bytes. */
- ALIGN_SIZE_LOG2 = 2,
- ALIGN_SIZE = (1 << ALIGN_SIZE_LOG2),
- };
- typedef struct
- {
- int32_t total;
- int32_t used;
- int32_t max_used;
- } mem_record_t;
- /* The TLSF control structure. */
- typedef struct control_t
- {
- /* Empty lists point at this block to indicate they are free. */
- block_header_t block_null;
- /* Local parameter for the pool. Given the maximum
- * value of each field, all the following parameters
- * can fit on 4 bytes when using bitfields
- */
- unsigned int fl_index_count: 5; // 5 cumulated bits
- unsigned int fl_index_shift: 3; // 8 cumulated bits
- unsigned int fl_index_max: 6; // 14 cumulated bits
- unsigned int sl_index_count: 6; // 20 cumulated bits
- /* log2 of number of linear subdivisions of block sizes. Larger
- ** values require more memory in the control structure. Values of
- ** 4 or 5 are typical.
- */
- unsigned int sl_index_count_log2: 3; // 23 cumulated bits
- unsigned int small_block_size: 8; // 31 cumulated bits
- /* size of the metadata ( size of control block,
- * sl_bitmap and blocks )
- */
- size_t size;
- /* Bitmaps for free lists. */
- unsigned int fl_bitmap;
- unsigned int *sl_bitmap;
- /* Head of free lists. */
- block_header_t **blocks;
- mem_record_t mem_rec;
- } control_t;
- /*
- ** Architecture-specific bit manipulation routines.
- **
- ** TLSF achieves O(1) cost for malloc and free operations by limiting
- ** the search for a free block to a free list of guaranteed size
- ** adequate to fulfill the request, combined with efficient free list
- ** queries using bitmasks and architecture-specific bit-manipulation
- ** routines.
- **
- ** Most modern processors provide instructions to count leading zeroes
- ** in a word, find the lowest and highest set bit, etc. These
- ** specific implementations will be used when available, falling back
- ** to a reasonably efficient generic implementation.
- **
- ** NOTE: TLSF spec relies on ffs/fls returning value 0..31.
- ** ffs/fls return 1-32 by default, returning 0 for error.
- */
- /*
- ** Detect whether or not we are building for a 32- or 64-bit (LP/LLP)
- ** architecture. There is no reliable portable method at compile-time.
- */
- #if defined(__alpha__) || defined(__ia64__) || defined(__x86_64__) || defined(_WIN64) || defined(__LP64__) || defined(__LLP64__)
- #define TLSF_64BIT
- #endif
- /*
- ** gcc 3.4 and above have builtin support, specialized for architecture.
- ** Some compilers masquerade as gcc; patchlevel test filters them out.
- */
- #if defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) && defined(__GNUC_PATCHLEVEL__)
- #if defined(__SNC__)
- /* SNC for Playstation 3. */
- tlsf_decl int tlsf_ffs(unsigned int word)
- {
- const unsigned int reverse = word & (~word + 1);
- const int bit = 32 - __builtin_clz(reverse);
- return bit - 1;
- }
- #else
- tlsf_decl int tlsf_ffs(unsigned int word) { return __builtin_ffs(word) - 1; }
- #endif
- tlsf_decl int tlsf_fls(unsigned int word)
- {
- const int bit = word ? 32 - __builtin_clz(word) : 0;
- return bit - 1;
- }
- #elif defined(_MSC_VER) && (_MSC_VER >= 1400) && (defined(_M_IX86) || defined(_M_X64))
- /* Microsoft Visual C++ support on x86/X64 architectures. */
- #include <intrin.h>
- #pragma intrinsic(_BitScanReverse)
- #pragma intrinsic(_BitScanForward)
- tlsf_decl int tlsf_fls(unsigned int word)
- {
- unsigned long index;
- return _BitScanReverse(&index, word) ? index : -1;
- }
- tlsf_decl int tlsf_ffs(unsigned int word)
- {
- unsigned long index;
- return _BitScanForward(&index, word) ? index : -1;
- }
- #elif defined(_MSC_VER) && defined(_M_PPC)
- /* Microsoft Visual C++ support on PowerPC architectures. */
- #include <ppcintrinsics.h>
- tlsf_decl int tlsf_fls(unsigned int word)
- {
- const int bit = 32 - _CountLeadingZeros(word);
- return bit - 1;
- }
- tlsf_decl int tlsf_ffs(unsigned int word)
- {
- const unsigned int reverse = word & (~word + 1);
- const int bit = 32 - _CountLeadingZeros(reverse);
- return bit - 1;
- }
- #elif defined(__ARMCC_VERSION)
- /* RealView Compilation Tools for ARM */
- tlsf_decl int tlsf_ffs(unsigned int word)
- {
- const unsigned int reverse = word & (~word + 1);
- const int bit = 32 - __clz(reverse);
- return bit - 1;
- }
- tlsf_decl int tlsf_fls(unsigned int word)
- {
- const int bit = word ? 32 - __clz(word) : 0;
- return bit - 1;
- }
- #elif defined(__ghs__)
- /* Green Hills support for PowerPC */
- #include <ppc_ghs.h>
- tlsf_decl int tlsf_ffs(unsigned int word)
- {
- const unsigned int reverse = word & (~word + 1);
- const int bit = 32 - __CLZ32(reverse);
- return bit - 1;
- }
- tlsf_decl int tlsf_fls(unsigned int word)
- {
- const int bit = word ? 32 - __CLZ32(word) : 0;
- return bit - 1;
- }
- #else
- /* Fall back to generic implementation. */
- tlsf_decl int tlsf_fls_generic(unsigned int word)
- {
- int bit = 32;
- if (!word) { bit -= 1; }
- if (!(word & 0xffff0000))
- {
- word <<= 16;
- bit -= 16;
- }
- if (!(word & 0xff000000))
- {
- word <<= 8;
- bit -= 8;
- }
- if (!(word & 0xf0000000))
- {
- word <<= 4;
- bit -= 4;
- }
- if (!(word & 0xc0000000))
- {
- word <<= 2;
- bit -= 2;
- }
- if (!(word & 0x80000000))
- {
- word <<= 1;
- bit -= 1;
- }
- return bit;
- }
- /* Implement ffs in terms of fls. */
- tlsf_decl int tlsf_ffs(unsigned int word) { return tlsf_fls_generic(word & (~word + 1)) - 1; }
- tlsf_decl int tlsf_fls(unsigned int word) { return tlsf_fls_generic(word) - 1; }
- #endif
- /* Possibly 64-bit version of tlsf_fls. */
- #if defined(TLSF_64BIT)
- tlsf_decl int tlsf_fls_sizet(size_t size)
- {
- int high = (int)(size >> 32);
- int bits = 0;
- if (high) { bits = 32 + tlsf_fls(high); }
- else
- {
- bits = tlsf_fls((int)size & 0xffffffff);
- }
- return bits;
- }
- #else
- #define tlsf_fls_sizet tlsf_fls
- #endif
- tlsf_decl size_t align_up(size_t x, size_t align)
- {
- tlsf_assert(0 == (align & (align - 1)) && "must align to a power of two");
- return (x + (align - 1)) & ~(align - 1);
- }
- tlsf_decl size_t align_down(size_t x, size_t align)
- {
- tlsf_assert(0 == (align & (align - 1)) && "must align to a power of two");
- return x - (x & (align - 1));
- }
- tlsf_decl void *align_ptr(const void *ptr, size_t align)
- {
- const tlsfptr_t aligned = (tlsf_cast(tlsfptr_t, ptr) + (align - 1)) & ~(align - 1);
- tlsf_assert(0 == (align & (align - 1)) && "must align to a power of two");
- return tlsf_cast(void *, aligned);
- }
- tlsf_decl size_t tlsf_align_size(void) { return ALIGN_SIZE; }
- tlsf_decl size_t tlsf_block_size_min(void) { return block_size_min; }
- tlsf_decl size_t tlsf_block_size_max(control_t *control)
- {
- if (control == NULL) { return 0; }
- return tlsf_cast(size_t, 1) << control->fl_index_max;
- }
- /*
- ** Adjust an allocation size to be aligned to word size, and no smaller
- ** than internal minimum.
- */
- tlsf_decl size_t adjust_request_size(control_t *control, size_t size, size_t align)
- {
- size_t adjust = 0;
- if (size)
- {
- const size_t aligned = align_up(size, align);
- /* aligned sized must not exceed block_size_max or we'll go out of bounds on sl_bitmap */
- if (aligned < tlsf_block_size_max(control)) { adjust = tlsf_max(aligned, block_size_min); }
- }
- return adjust;
- }
- /*
- ** TLSF utility functions. In most cases, these are direct translations of
- ** the documentation found in the white paper.
- */
- tlsf_decl void mapping_insert(control_t *control, size_t size, int *fli, int *sli)
- {
- int fl, sl;
- if (size < control->small_block_size)
- {
- /* Store small blocks in first list. */
- fl = 0;
- sl = tlsf_cast(int, size) / (control->small_block_size / control->sl_index_count);
- }
- else
- {
- fl = tlsf_fls_sizet(size);
- sl = tlsf_cast(int, size >> (fl - control->sl_index_count_log2)) ^ (1 << control->sl_index_count_log2);
- fl -= (control->fl_index_shift - 1);
- }
- *fli = fl;
- *sli = sl;
- }
- /* This version rounds up to the next block size (for allocations) */
- tlsf_decl void mapping_search(control_t *control, size_t *size, int *fli, int *sli)
- {
- if (*size >= control->small_block_size)
- {
- const size_t round = (1 << (tlsf_fls_sizet(*size) - control->sl_index_count_log2));
- *size = align_up(*size, round);
- }
- mapping_insert(control, *size, fli, sli);
- }
- tlsf_decl block_header_t *search_suitable_block(control_t *control, int *fli, int *sli)
- {
- int fl = *fli;
- int sl = *sli;
- /*
- ** First, search for a block in the list associated with the given
- ** fl/sl index.
- */
- unsigned int sl_map = control->sl_bitmap[fl] & (~0U << sl);
- if (!sl_map)
- {
- /* No block exists. Search in the next largest first-level list. */
- const unsigned int fl_map = control->fl_bitmap & (~0U << (fl + 1));
- if (!fl_map)
- {
- /* No free blocks available, memory has been exhausted. */
- return 0;
- }
- fl = tlsf_ffs(fl_map);
- *fli = fl;
- sl_map = control->sl_bitmap[fl];
- }
- tlsf_assert(sl_map && "internal error - second level bitmap is null");
- sl = tlsf_ffs(sl_map);
- *sli = sl;
- /* Return the first block in the free list. */
- return control->blocks[fl * control->sl_index_count + sl];
- }
- /* Remove a free block from the free list.*/
- tlsf_decl void remove_free_block(control_t *control, block_header_t *block, int fl, int sl)
- {
- block_header_t *prev = block->prev_free;
- block_header_t *next = block->next_free;
- tlsf_assert(prev && "prev_free field can not be null");
- tlsf_assert(next && "next_free field can not be null");
- next->prev_free = prev;
- prev->next_free = next;
- /* If this block is the head of the free list, set new head. */
- if (control->blocks[fl * control->sl_index_count + sl] == block)
- {
- control->blocks[fl * control->sl_index_count + sl] = next;
- /* If the new head is null, clear the bitmap. */
- if (next == &control->block_null)
- {
- control->sl_bitmap[fl] &= ~(1U << sl);
- /* If the second bitmap is now empty, clear the fl bitmap. */
- if (!control->sl_bitmap[fl]) { control->fl_bitmap &= ~(1U << fl); }
- }
- }
- }
- /* Insert a free block into the free block list. */
- tlsf_decl void insert_free_block(control_t *control, block_header_t *block, int fl, int sl)
- {
- block_header_t *current = control->blocks[fl * control->sl_index_count + sl];
- tlsf_assert(current && "free list cannot have a null entry");
- tlsf_assert(block && "cannot insert a null entry into the free list");
- block->next_free = current;
- block->prev_free = &control->block_null;
- current->prev_free = block;
- tlsf_assert(block_to_ptr(block) == align_ptr(block_to_ptr(block), ALIGN_SIZE) && "block not aligned properly");
- /*
- ** Insert the new block at the head of the list, and mark the first-
- ** and second-level bitmaps appropriately.
- */
- control->blocks[fl * control->sl_index_count + sl] = block;
- control->fl_bitmap |= (1U << fl);
- control->sl_bitmap[fl] |= (1U << sl);
- }
- /* Remove a given block from the free list. */
- tlsf_decl void block_remove(control_t *control, block_header_t *block)
- {
- int fl, sl;
- mapping_insert(control, block_size(block), &fl, &sl);
- remove_free_block(control, block, fl, sl);
- }
- /* Insert a given block into the free list. */
- tlsf_decl void block_insert(control_t *control, block_header_t *block)
- {
- int fl, sl;
- mapping_insert(control, block_size(block), &fl, &sl);
- insert_free_block(control, block, fl, sl);
- }
- tlsf_decl int block_can_split(block_header_t *block, size_t size) { return block_size(block) >= sizeof(block_header_t) + size; }
- /* Split a block into two, the second of which is free. */
- tlsf_decl block_header_t *block_split(block_header_t *block, size_t size)
- {
- /* Calculate the amount of space left in the remaining block.
- * REMINDER: remaining pointer's first field is `prev_phys_block` but this field is part of the
- * previous physical block. */
- block_header_t *remaining = offset_to_block(block_to_ptr(block), size - block_header_overhead);
- /* `size` passed as an argument is the first block's new size, thus, the remaining block's size
- * is `block_size(block) - size`. However, the block's data must be precedeed by the data size.
- * This field is NOT part of the size, so it has to be substracted from the calculation. */
- const size_t remain_size = block_size(block) - (size + block_header_overhead);
- tlsf_assert(block_to_ptr(remaining) == align_ptr(block_to_ptr(remaining), ALIGN_SIZE) && "remaining block not aligned properly");
- tlsf_assert(block_size(block) == remain_size + size + block_header_overhead);
- block_set_size(remaining, remain_size);
- tlsf_assert(block_size(remaining) >= block_size_min && "block split with invalid size");
- block_set_size(block, size);
- block_mark_as_free(remaining);
- /**
- * Here is the final outcome of this function:
- *
- * block remaining (block_ptr + size - BHO)
- * + +
- * | |
- * v v
- * +----------------------------------------------------------------------+
- * |0000| |xxxxxxxxxxxxxxxxxxxxxx|xxxx| |###########################|
- * |0000| |xxxxxxxxxxxxxxxxxxxxxx|xxxx| |###########################|
- * |0000| |xxxxxxxxxxxxxxxxxxxxxx|xxxx| |###########################|
- * |0000| |xxxxxxxxxxxxxxxxxxxxxx|xxxx| |###########################|
- * +----------------------------------------------------------------------+
- * | | | |
- * + +<------------------------->+ +<------------------------->
- * BHO `size` (argument) bytes BHO `remain_size` bytes
- *
- * Where BHO = block_header_overhead,
- * 0: part of the memory owned by a `block`'s previous neighbour,
- * x: part of the memory owned by `block`.
- * #: part of the memory owned by `remaining`.
- */
- return remaining;
- }
- /*!
- * @brief Weak function filling the given memory with a given fill pattern.
- *
- * @param start: pointer to the start of the memory region to fill
- * @param size: size of the memory region to fill
- * @param is_free: Indicate if the pattern to use the fill the region should be
- * an after free or after allocation pattern.
- */
- __attribute__((weak)) void block_absorb_post_hook(void *start, size_t size, bool is_free);
- /* Absorb a free block's storage into an adjacent previous free block. */
- tlsf_decl block_header_t *block_absorb(block_header_t *prev, block_header_t *block)
- {
- tlsf_assert(!block_is_last(prev) && "previous block can't be last");
- /* Note: Leaves flags untouched. */
- prev->size += block_size(block) + block_header_overhead;
- block_link_next(prev);
- if (block_absorb_post_hook != NULL) { block_absorb_post_hook(block, sizeof(block_header_t), POISONING_AFTER_FREE); }
- return prev;
- }
- /* Merge a just-freed block with an adjacent previous free block. */
- tlsf_decl block_header_t *block_merge_prev(control_t *control, block_header_t *block)
- {
- if (block_is_prev_free(block))
- {
- block_header_t *prev = block_prev(block);
- tlsf_assert(prev && "prev physical block can't be null");
- tlsf_assert(block_is_free(prev) && "prev block is not free though marked as such");
- block_remove(control, prev);
- block = block_absorb(prev, block);
- }
- return block;
- }
- /* Merge a just-freed block with an adjacent free block. */
- tlsf_decl block_header_t *block_merge_next(control_t *control, block_header_t *block)
- {
- block_header_t *next = block_next(block);
- tlsf_assert(next && "next physical block can't be null");
- if (block_is_free(next))
- {
- tlsf_assert(!block_is_last(block) && "previous block can't be last");
- block_remove(control, next);
- block = block_absorb(block, next);
- }
- return block;
- }
- /* Trim any trailing block space off the end of a block, return to pool. */
- tlsf_decl void block_trim_free(control_t *control, block_header_t *block, size_t size)
- {
- tlsf_assert(block_is_free(block) && "block must be free");
- if (block_can_split(block, size))
- {
- block_header_t *remaining_block = block_split(block, size);
- block_link_next(block);
- block_set_prev_free(remaining_block);
- block_insert(control, remaining_block);
- }
- }
- /* Trim any trailing block space off the end of a used block, return to pool. */
- tlsf_decl void block_trim_used(control_t *control, block_header_t *block, size_t size)
- {
- tlsf_assert(!block_is_free(block) && "block must be used");
- if (block_can_split(block, size))
- {
- /* If the next block is free, we must coalesce. */
- block_header_t *remaining_block = block_split(block, size);
- block_set_prev_used(remaining_block);
- remaining_block = block_merge_next(control, remaining_block);
- block_insert(control, remaining_block);
- }
- }
- tlsf_decl block_header_t *block_trim_free_leading(control_t *control, block_header_t *block, size_t size)
- {
- block_header_t *remaining_block = block;
- if (block_can_split(block, size))
- {
- /* We want to split `block` in two: the first block will be freed and the
- * second block will be returned. */
- remaining_block = block_split(block, size - block_header_overhead);
- /* `remaining_block` is the second block, mark its predecessor (first
- * block) as free. */
- block_set_prev_free(remaining_block);
- block_link_next(block);
- /* Put back the first block into the free memory list. */
- block_insert(control, block);
- }
- return remaining_block;
- }
- tlsf_decl block_header_t *block_locate_free(control_t *control, size_t *size)
- {
- int fl = 0, sl = 0;
- block_header_t *block = 0;
- if (*size)
- {
- mapping_search(control, size, &fl, &sl);
- /*
- ** mapping_search can futz with the size, so for excessively large sizes it can sometimes wind up
- ** with indices that are off the end of the block array.
- ** So, we protect against that here, since this is the only callsite of mapping_search.
- ** Note that we don't need to check sl, since it comes from a modulo operation that guarantees it's always in range.
- */
- if (fl < control->fl_index_count) { block = search_suitable_block(control, &fl, &sl); }
- }
- if (block)
- {
- tlsf_assert(block_size(block) >= *size);
- remove_free_block(control, block, fl, sl);
- }
- return block;
- }
- tlsf_decl void *block_prepare_used(control_t *control, block_header_t *block, size_t size)
- {
- void *p = 0;
- if (block)
- {
- tlsf_assert(size && "size must be non-zero");
- block_trim_free(control, block, size);
- block_mark_as_used(block);
- p = block_to_ptr(block);
- control->mem_rec.used += (block_size(block) + tlsf_alloc_overhead());
- if (control->mem_rec.used > control->mem_rec.max_used) { control->mem_rec.max_used = control->mem_rec.used; }
- }
- return p;
- }
- #undef tlsf_decl
- #if defined(__cplusplus)
- };
- #endif
|