[*] yoink (backport) b21b069e4b from v2 of o1heap

This commit is contained in:
Reece Wilson 2022-10-14 01:20:56 +01:00
parent b026aef3b1
commit f30d520343
2 changed files with 47 additions and 23 deletions

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Pavel Kirienko
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -34,8 +34,11 @@
/// The assertion macro defaults to the standard assert(). /// The assertion macro defaults to the standard assert().
/// It can be overridden to manually suppress assertion checks or use a different error handling policy. /// It can be overridden to manually suppress assertion checks or use a different error handling policy.
#ifndef O1HEAP_ASSERT #ifndef O1HEAP_ASSERT
// Intentional violation of MISRA: the assertion check macro cannot be replaced with a function definition. #if defined(AU_CFG_ID_DEBUG) || defined(AU_CFG_ID_INTERNAL)
# define O1HEAP_ASSERT(x) assert(x) // NOSONAR #define O1HEAP_ASSERT(x) SysAssert(x)
#else
#define O1HEAP_ASSERT(x)
#endif
#endif #endif
/// Branch probability annotations are used to improve the worst case execution time (WCET). They are entirely optional. /// Branch probability annotations are used to improve the worst case execution time (WCET). They are entirely optional.
@ -43,7 +46,6 @@
/// If you are using a different compiler, consider overriding this value. /// If you are using a different compiler, consider overriding this value.
#ifndef O1HEAP_LIKELY #ifndef O1HEAP_LIKELY
# if defined(__GNUC__) || defined(__clang__) || defined(__CC_ARM) # if defined(__GNUC__) || defined(__clang__) || defined(__CC_ARM)
// Intentional violation of MISRA: branch hinting macro cannot be replaced with a function definition.
# define O1HEAP_LIKELY(x) __builtin_expect((x), 1) // NOSONAR # define O1HEAP_LIKELY(x) __builtin_expect((x), 1) // NOSONAR
# else # else
# define O1HEAP_LIKELY(x) x # define O1HEAP_LIKELY(x) x
@ -134,26 +136,27 @@ O1HEAP_PRIVATE bool isPowerOf2(const size_t x)
} }
/// Special case: if the argument is zero, returns zero. /// Special case: if the argument is zero, returns zero.
O1HEAP_PRIVATE uint8_t log2Floor(const size_t x);
O1HEAP_PRIVATE uint8_t log2Floor(const size_t x) O1HEAP_PRIVATE uint8_t log2Floor(const size_t x)
{ {
size_t tmp = x; O1HEAP_ASSERT(x > 0);
uint8_t y = 0; AuUInt8 index {};
// This is currently the only exception to the statement "routines contain neither loops nor recursion". AuBitScanReverse(index, x);
// It is unclear if there is a better way to compute the binary logarithm than this. return (uint8_t)(((sizeof(x) * CHAR_BIT) - 1U) - index);
while (tmp > 1U)
{
tmp >>= 1U;
y++;
}
return y;
} }
/// Special case: if the argument is zero, returns zero. O1HEAP_PRIVATE uint_fast8_t log2Ceil(const size_t x)
O1HEAP_PRIVATE uint8_t log2Ceil(const size_t x);
O1HEAP_PRIVATE uint8_t log2Ceil(const size_t x)
{ {
return (uint8_t)(log2Floor(x) + (isPowerOf2(x) ? 0U : 1U)); AuUInt8 index {};
AuBitScanReverse(index, x - 1);
return (x <= 1U) ? 0U : (uint_fast8_t)((sizeof(x) * CHAR_BIT) - (index));
}
O1HEAP_PRIVATE size_t roundUpToPowerOf2(const size_t x)
{
O1HEAP_ASSERT(x >= 2U);
AuUInt8 index {};
AuBitScanReverse(index, x - 1);
return ((size_t)1U) << ((sizeof(x) * CHAR_BIT) - (index));
} }
/// Raise 2 into the specified power. /// Raise 2 into the specified power.
@ -316,11 +319,11 @@ void *o1heapAllocate(O1HeapInstance *const handle, const size_t amount)
{ {
// Add the header size and align the allocation size to the power of 2. // Add the header size and align the allocation size to the power of 2.
// See "Timing-Predictable Memory Allocation In Hard Real-Time Systems", Herter, page 27. // See "Timing-Predictable Memory Allocation In Hard Real-Time Systems", Herter, page 27.
const size_t fragment_size = pow2(log2Ceil(amount + O1HEAP_ALIGNMENT)); const size_t fragment_size = roundUpToPowerOf2(amount + O1HEAP_ALIGNMENT);
O1HEAP_ASSERT(fragment_size <= FRAGMENT_SIZE_MAX); O1HEAP_ASSERT(fragment_size <= FRAGMENT_SIZE_MAX);
O1HEAP_ASSERT(fragment_size >= FRAGMENT_SIZE_MIN); O1HEAP_ASSERT(fragment_size >= FRAGMENT_SIZE_MIN);
O1HEAP_ASSERT(fragment_size >= amount + O1HEAP_ALIGNMENT); O1HEAP_ASSERT(fragment_size >= amount + O1HEAP_ALIGNMENT);
O1HEAP_ASSERT(isPowerOf2(fragment_size)); O1HEAP_ASSERT((fragment_size & (fragment_size - 1U)) == 0U); // Is power of 2.
const uint8_t optimal_bin_index = log2Ceil(fragment_size / FRAGMENT_SIZE_MIN); // Use CEIL when fetching. const uint8_t optimal_bin_index = log2Ceil(fragment_size / FRAGMENT_SIZE_MIN); // Use CEIL when fetching.
O1HEAP_ASSERT(optimal_bin_index < NUM_BINS_MAX); O1HEAP_ASSERT(optimal_bin_index < NUM_BINS_MAX);
@ -333,8 +336,8 @@ void *o1heapAllocate(O1HeapInstance *const handle, const size_t amount)
const size_t smallest_bin_mask = suitable_bins & ~(suitable_bins - 1U); // Clear all bits but the lowest. const size_t smallest_bin_mask = suitable_bins & ~(suitable_bins - 1U); // Clear all bits but the lowest.
if (O1HEAP_LIKELY(smallest_bin_mask != 0)) if (O1HEAP_LIKELY(smallest_bin_mask != 0))
{ {
O1HEAP_ASSERT(isPowerOf2(smallest_bin_mask)); O1HEAP_ASSERT((smallest_bin_mask & (smallest_bin_mask - 1U)) == 0U); // Is power of 2.
const uint8_t bin_index = log2Floor(smallest_bin_mask); const uint_fast8_t bin_index = log2Floor(smallest_bin_mask);
O1HEAP_ASSERT(bin_index >= optimal_bin_index); O1HEAP_ASSERT(bin_index >= optimal_bin_index);
O1HEAP_ASSERT(bin_index < NUM_BINS_MAX); O1HEAP_ASSERT(bin_index < NUM_BINS_MAX);
@ -353,7 +356,7 @@ void *o1heapAllocate(O1HeapInstance *const handle, const size_t amount)
O1HEAP_ASSERT(leftover % FRAGMENT_SIZE_MIN == 0U); // Alignment check. O1HEAP_ASSERT(leftover % FRAGMENT_SIZE_MIN == 0U); // Alignment check.
if (O1HEAP_LIKELY(leftover >= FRAGMENT_SIZE_MIN)) if (O1HEAP_LIKELY(leftover >= FRAGMENT_SIZE_MIN))
{ {
Fragment *const new_frag = (Fragment *)(void *)(((uint8_t *)frag) + fragment_size); Fragment *const new_frag = (Fragment *)(void *)(((char *)frag) + fragment_size);
O1HEAP_ASSERT(((size_t)new_frag) % O1HEAP_ALIGNMENT == 0U); O1HEAP_ASSERT(((size_t)new_frag) % O1HEAP_ALIGNMENT == 0U);
new_frag->header.size = leftover; new_frag->header.size = leftover;
new_frag->header.used = false; new_frag->header.used = false;