Add BSP support for F1C100s
This commit is contained in:
@@ -0,0 +1,834 @@
|
||||
/*
|
||||
* lib/libc/malloc/malloc.c
|
||||
*/
|
||||
|
||||
#include <malloc.h>
|
||||
|
||||
static void * __heap_pool = NULL;
|
||||
|
||||
/*
|
||||
* Some macros.
|
||||
*/
|
||||
#define tlsf_cast(t, exp) ((t)(exp))
|
||||
#define tlsf_min(a, b) ((a) < (b) ? (a) : (b))
|
||||
#define tlsf_max(a, b) ((a) > (b) ? (a) : (b))
|
||||
|
||||
#define tlsf_assert assert
|
||||
#define tlsf_insist(x) { tlsf_assert(x); if (!(x)) { status--; } }
|
||||
|
||||
#if defined(__ARM64__) || defined(__X64__)
|
||||
# define TLSF_64BIT
|
||||
#else
|
||||
# undef TLSF_64BIT
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Public constants
|
||||
*/
|
||||
enum tlsf_public
|
||||
{
|
||||
/*
|
||||
* log2 of number of linear subdivisions of block sizes
|
||||
*/
|
||||
SL_INDEX_COUNT_LOG2 = 5,
|
||||
};
|
||||
|
||||
/*
|
||||
* Private constants
|
||||
*/
|
||||
enum tlsf_private
|
||||
{
|
||||
#if defined(TLSF_64BIT)
|
||||
/*
|
||||
* All allocation sizes and addresses are aligned to 8 bytes
|
||||
*/
|
||||
ALIGN_SIZE_LOG2 = 3,
|
||||
#else
|
||||
/*
|
||||
* All allocation sizes and addresses are aligned to 4 bytes
|
||||
*/
|
||||
ALIGN_SIZE_LOG2 = 2,
|
||||
#endif
|
||||
ALIGN_SIZE = (1 << ALIGN_SIZE_LOG2),
|
||||
|
||||
#if defined(TLSF_64BIT)
|
||||
FL_INDEX_MAX = 32,
|
||||
#else
|
||||
FL_INDEX_MAX = 30,
|
||||
#endif
|
||||
SL_INDEX_COUNT = (1 << SL_INDEX_COUNT_LOG2),
|
||||
FL_INDEX_SHIFT = (SL_INDEX_COUNT_LOG2 + ALIGN_SIZE_LOG2),
|
||||
FL_INDEX_COUNT = (FL_INDEX_MAX - FL_INDEX_SHIFT + 1),
|
||||
|
||||
SMALL_BLOCK_SIZE = (1 << FL_INDEX_SHIFT),
|
||||
};
|
||||
|
||||
/*
|
||||
* Block header structure
|
||||
*/
|
||||
typedef struct block_header_t
|
||||
{
|
||||
/*
|
||||
* Points to the previous physical block
|
||||
*/
|
||||
struct block_header_t * prev_phys_block;
|
||||
|
||||
/*
|
||||
* The size of this block, excluding the block header
|
||||
*/
|
||||
size_t size;
|
||||
|
||||
/*
|
||||
* Next and previous free blocks
|
||||
*/
|
||||
struct block_header_t * next_free;
|
||||
struct block_header_t * prev_free;
|
||||
} block_header_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;
|
||||
|
||||
/*
|
||||
* Bitmaps for free lists.
|
||||
*/
|
||||
unsigned int fl_bitmap;
|
||||
unsigned int sl_bitmap[FL_INDEX_COUNT];
|
||||
|
||||
/*
|
||||
* Head of free lists.
|
||||
*/
|
||||
block_header_t * blocks[FL_INDEX_COUNT][SL_INDEX_COUNT];
|
||||
} control_t;
|
||||
|
||||
/*
|
||||
* A type used for casting when doing pointer arithmetic.
|
||||
*/
|
||||
typedef ptrdiff_t tlsfptr_t;
|
||||
|
||||
/*
|
||||
* Associated constants
|
||||
*/
|
||||
static const size_t block_header_free_bit = 1 << 0;
|
||||
static const size_t block_header_prev_free_bit = 1 << 1;
|
||||
static const size_t block_header_overhead = sizeof(size_t);
|
||||
static const size_t block_start_offset = offsetof(block_header_t, size) + sizeof(size_t);
|
||||
static const size_t block_size_min = sizeof(block_header_t) - sizeof(block_header_t *);
|
||||
static const size_t block_size_max = tlsf_cast(size_t, 1) << FL_INDEX_MAX;
|
||||
|
||||
#if defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) && defined(__GNUC_PATCHLEVEL__)
|
||||
static int tlsf_ffs(unsigned int word)
|
||||
{
|
||||
return __builtin_ffs(word) - 1;
|
||||
}
|
||||
|
||||
static int tlsf_fls(unsigned int word)
|
||||
{
|
||||
const int bit = word ? 32 - __builtin_clz(word) : 0;
|
||||
return bit - 1;
|
||||
}
|
||||
#else
|
||||
static 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;
|
||||
}
|
||||
|
||||
static int tlsf_ffs(unsigned int word)
|
||||
{
|
||||
return tlsf_fls_generic(word & (~word + 1)) - 1;
|
||||
}
|
||||
|
||||
static int tlsf_fls(unsigned int word)
|
||||
{
|
||||
return tlsf_fls_generic(word) - 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(TLSF_64BIT)
|
||||
static 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
|
||||
|
||||
static size_t block_get_size(const block_header_t * block)
|
||||
{
|
||||
return block->size & ~(block_header_free_bit | block_header_prev_free_bit);
|
||||
}
|
||||
|
||||
static void block_set_size(block_header_t * block, size_t size)
|
||||
{
|
||||
const size_t oldsize = block->size;
|
||||
block->size = size | (oldsize & (block_header_free_bit | block_header_prev_free_bit));
|
||||
}
|
||||
|
||||
static int block_is_last(const block_header_t * block)
|
||||
{
|
||||
return (0 == block_get_size(block));
|
||||
}
|
||||
|
||||
static int block_is_free(const block_header_t * block)
|
||||
{
|
||||
return tlsf_cast(int, block->size & block_header_free_bit);
|
||||
}
|
||||
|
||||
static void block_set_free(block_header_t * block)
|
||||
{
|
||||
block->size |= block_header_free_bit;
|
||||
}
|
||||
|
||||
static void block_set_used(block_header_t * block)
|
||||
{
|
||||
block->size &= ~block_header_free_bit;
|
||||
}
|
||||
|
||||
static int block_is_prev_free(const block_header_t * block)
|
||||
{
|
||||
return tlsf_cast(int, block->size & block_header_prev_free_bit);
|
||||
}
|
||||
|
||||
static void block_set_prev_free(block_header_t * block)
|
||||
{
|
||||
block->size |= block_header_prev_free_bit;
|
||||
}
|
||||
|
||||
static void block_set_prev_used(block_header_t * block)
|
||||
{
|
||||
block->size &= ~block_header_prev_free_bit;
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wcast-align"
|
||||
static block_header_t * block_from_ptr(void * ptr)
|
||||
{
|
||||
return tlsf_cast(block_header_t *, tlsf_cast(unsigned char*, ptr) - block_start_offset);
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
static void * block_to_ptr(block_header_t * block)
|
||||
{
|
||||
return tlsf_cast(void *, tlsf_cast(unsigned char*, block) + block_start_offset);
|
||||
}
|
||||
|
||||
static block_header_t * offset_to_block(void * ptr, size_t size)
|
||||
{
|
||||
return tlsf_cast(block_header_t *, tlsf_cast(tlsfptr_t, ptr) + size);
|
||||
}
|
||||
|
||||
static block_header_t * block_prev(block_header_t * block)
|
||||
{
|
||||
return block->prev_phys_block;
|
||||
}
|
||||
|
||||
static block_header_t * block_next(block_header_t * block)
|
||||
{
|
||||
block_header_t * next = offset_to_block(block_to_ptr(block), block_get_size(block) - block_header_overhead);
|
||||
tlsf_assert(!block_is_last(block));
|
||||
return next;
|
||||
}
|
||||
|
||||
static block_header_t * block_link_next(block_header_t * block)
|
||||
{
|
||||
block_header_t * next = block_next(block);
|
||||
next->prev_phys_block = block;
|
||||
return next;
|
||||
}
|
||||
|
||||
static void block_mark_as_free(block_header_t * block)
|
||||
{
|
||||
block_header_t * next = block_link_next(block);
|
||||
block_set_prev_free(next);
|
||||
block_set_free(block);
|
||||
}
|
||||
|
||||
static void block_mark_as_used(block_header_t * block)
|
||||
{
|
||||
block_header_t * next = block_next(block);
|
||||
block_set_prev_used(next);
|
||||
block_set_used(block);
|
||||
}
|
||||
|
||||
static 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);
|
||||
}
|
||||
|
||||
static 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));
|
||||
}
|
||||
|
||||
static 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);
|
||||
}
|
||||
|
||||
static size_t adjust_request_size(size_t size, size_t align)
|
||||
{
|
||||
size_t adjust = 0;
|
||||
if (size && size < block_size_max)
|
||||
{
|
||||
const size_t aligned = align_up(size, align);
|
||||
adjust = tlsf_max(aligned, block_size_min);
|
||||
}
|
||||
return adjust;
|
||||
}
|
||||
|
||||
static void mapping_insert(size_t size, int * fli, int * sli)
|
||||
{
|
||||
int fl, sl;
|
||||
if (size < SMALL_BLOCK_SIZE)
|
||||
{
|
||||
fl = 0;
|
||||
sl = tlsf_cast(int, size) / (SMALL_BLOCK_SIZE / SL_INDEX_COUNT);
|
||||
}
|
||||
else
|
||||
{
|
||||
fl = tlsf_fls_sizet(size);
|
||||
sl = tlsf_cast(int, size >> (fl - SL_INDEX_COUNT_LOG2)) ^ (1 << SL_INDEX_COUNT_LOG2);
|
||||
fl -= (FL_INDEX_SHIFT - 1);
|
||||
}
|
||||
*fli = fl;
|
||||
*sli = sl;
|
||||
}
|
||||
|
||||
static void mapping_search(size_t size, int * fli, int * sli)
|
||||
{
|
||||
if (size >= (1 << SL_INDEX_COUNT_LOG2))
|
||||
{
|
||||
const size_t round = (1 << (tlsf_fls_sizet(size) - SL_INDEX_COUNT_LOG2)) - 1;
|
||||
size += round;
|
||||
}
|
||||
mapping_insert(size, fli, sli);
|
||||
}
|
||||
|
||||
static block_header_t * search_suitable_block(control_t * control, int * fli, int * sli)
|
||||
{
|
||||
int fl = *fli;
|
||||
int sl = *sli;
|
||||
|
||||
unsigned int sl_map = control->sl_bitmap[fl] & (~0U << sl);
|
||||
if (!sl_map)
|
||||
{
|
||||
const unsigned int fl_map = control->fl_bitmap & (~0U << (fl + 1));
|
||||
if (!fl_map)
|
||||
{
|
||||
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 control->blocks[fl][sl];
|
||||
}
|
||||
|
||||
static 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 (control->blocks[fl][sl] == block)
|
||||
{
|
||||
control->blocks[fl][sl] = next;
|
||||
|
||||
if (next == &control->block_null)
|
||||
{
|
||||
control->sl_bitmap[fl] &= ~(1 << sl);
|
||||
|
||||
if (!control->sl_bitmap[fl])
|
||||
{
|
||||
control->fl_bitmap &= ~(1 << fl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void insert_free_block(control_t * control, block_header_t * block, int fl, int sl)
|
||||
{
|
||||
block_header_t * current = control->blocks[fl][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");
|
||||
|
||||
control->blocks[fl][sl] = block;
|
||||
control->fl_bitmap |= (1 << fl);
|
||||
control->sl_bitmap[fl] |= (1 << sl);
|
||||
}
|
||||
|
||||
static void block_remove(control_t * control, block_header_t * block)
|
||||
{
|
||||
int fl, sl;
|
||||
mapping_insert(block_get_size(block), &fl, &sl);
|
||||
remove_free_block(control, block, fl, sl);
|
||||
}
|
||||
|
||||
static void block_insert(control_t * control, block_header_t * block)
|
||||
{
|
||||
int fl, sl;
|
||||
mapping_insert(block_get_size(block), &fl, &sl);
|
||||
insert_free_block(control, block, fl, sl);
|
||||
}
|
||||
|
||||
static int block_can_split(block_header_t * block, size_t size)
|
||||
{
|
||||
return block_get_size(block) >= sizeof(block_header_t) + size;
|
||||
}
|
||||
|
||||
static block_header_t * block_split(block_header_t * block, size_t size)
|
||||
{
|
||||
block_header_t* remaining = offset_to_block(block_to_ptr(block), size - block_header_overhead);
|
||||
const size_t remain_size = block_get_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_get_size(block) == remain_size + size + block_header_overhead);
|
||||
block_set_size(remaining, remain_size);
|
||||
tlsf_assert(block_get_size(remaining) >= block_size_min && "block split with invalid size");
|
||||
|
||||
block_set_size(block, size);
|
||||
block_mark_as_free(remaining);
|
||||
|
||||
return remaining;
|
||||
}
|
||||
|
||||
static 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!");
|
||||
prev->size += block_get_size(block) + block_header_overhead;
|
||||
block_link_next(prev);
|
||||
return prev;
|
||||
}
|
||||
|
||||
static 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;
|
||||
}
|
||||
|
||||
static 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;
|
||||
}
|
||||
|
||||
static 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);
|
||||
}
|
||||
}
|
||||
|
||||
static 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))
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
static 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))
|
||||
{
|
||||
remaining_block = block_split(block, size - block_header_overhead);
|
||||
block_set_prev_free(remaining_block);
|
||||
|
||||
block_link_next(block);
|
||||
block_insert(control, block);
|
||||
}
|
||||
|
||||
return remaining_block;
|
||||
}
|
||||
|
||||
static 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(size, &fl, &sl);
|
||||
block = search_suitable_block(control, &fl, &sl);
|
||||
}
|
||||
|
||||
if (block)
|
||||
{
|
||||
tlsf_assert(block_get_size(block) >= size);
|
||||
remove_free_block(control, block, fl, sl);
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
static void * block_prepare_used(control_t * control, block_header_t * block, size_t size)
|
||||
{
|
||||
void* p = 0;
|
||||
if (block)
|
||||
{
|
||||
block_trim_free(control, block, size);
|
||||
block_mark_as_used(block);
|
||||
p = block_to_ptr(block);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
static void control_construct(control_t * control)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
control->block_null.next_free = &control->block_null;
|
||||
control->block_null.prev_free = &control->block_null;
|
||||
|
||||
control->fl_bitmap = 0;
|
||||
for (i = 0; i < FL_INDEX_COUNT; ++i)
|
||||
{
|
||||
control->sl_bitmap[i] = 0;
|
||||
for (j = 0; j < SL_INDEX_COUNT; ++j)
|
||||
{
|
||||
control->blocks[i][j] = &control->block_null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void * tlsf_add_pool(void * tlsf, void * mem, size_t bytes)
|
||||
{
|
||||
block_header_t * block;
|
||||
block_header_t * next;
|
||||
const size_t pool_overhead = 2 * block_header_overhead;
|
||||
const size_t pool_bytes = align_down(bytes - pool_overhead, ALIGN_SIZE);
|
||||
|
||||
if (((ptrdiff_t)mem % ALIGN_SIZE) != 0)
|
||||
return 0;
|
||||
|
||||
if (pool_bytes < block_size_min || pool_bytes > block_size_max)
|
||||
return 0;
|
||||
|
||||
block = offset_to_block(mem, -(tlsfptr_t)block_header_overhead);
|
||||
block_set_size(block, pool_bytes);
|
||||
block_set_free(block);
|
||||
block_set_prev_used(block);
|
||||
block_insert(tlsf_cast(control_t*, tlsf), block);
|
||||
|
||||
next = block_link_next(block);
|
||||
block_set_size(next, 0);
|
||||
block_set_used(next);
|
||||
block_set_prev_free(next);
|
||||
|
||||
return mem;
|
||||
}
|
||||
|
||||
static inline void tlsf_remove_pool(void * tlsf, void * pool)
|
||||
{
|
||||
control_t * control = tlsf_cast(control_t *, tlsf);
|
||||
block_header_t * block = offset_to_block(pool, -(int)block_header_overhead);
|
||||
int fl = 0, sl = 0;
|
||||
|
||||
tlsf_assert(block_is_free(block) && "block should be free");
|
||||
tlsf_assert(!block_is_free(block_next(block)) && "next block should not be free");
|
||||
tlsf_assert(block_get_size(block_next(block)) == 0 && "next block size should be zero");
|
||||
|
||||
mapping_insert(block_get_size(block), &fl, &sl);
|
||||
remove_free_block(control, block, fl, sl);
|
||||
}
|
||||
|
||||
static inline void * tlsf_create(void * mem)
|
||||
{
|
||||
if (((tlsfptr_t)mem % ALIGN_SIZE) != 0)
|
||||
return 0;
|
||||
|
||||
control_construct(tlsf_cast(control_t *, mem));
|
||||
return tlsf_cast(void *, mem);
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Warray-bounds"
|
||||
static inline void * tlsf_create_with_pool(void * mem, size_t bytes)
|
||||
{
|
||||
void * tlsf = tlsf_create(mem);
|
||||
tlsf_add_pool(tlsf, (char *)mem + sizeof(control_t), bytes - sizeof(control_t));
|
||||
return tlsf;
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
|
||||
static inline void tlsf_destroy(void * tlsf)
|
||||
{
|
||||
(void)tlsf;
|
||||
}
|
||||
|
||||
static inline void * tlsf_get_pool(void * tlsf)
|
||||
{
|
||||
return tlsf_cast(void *, (char *)tlsf + sizeof(control_t));
|
||||
}
|
||||
|
||||
static inline void * tlsf_malloc(void * tlsf, size_t size)
|
||||
{
|
||||
control_t * control = tlsf_cast(control_t *, tlsf);
|
||||
const size_t adjust = adjust_request_size(size, ALIGN_SIZE);
|
||||
block_header_t * block = block_locate_free(control, adjust);
|
||||
return block_prepare_used(control, block, adjust);
|
||||
}
|
||||
|
||||
static inline void * tlsf_memalign(void * tlsf, size_t align, size_t size)
|
||||
{
|
||||
control_t * control = tlsf_cast(control_t *, tlsf);
|
||||
const size_t adjust = adjust_request_size(size, ALIGN_SIZE);
|
||||
|
||||
const size_t gap_minimum = sizeof(block_header_t);
|
||||
const size_t size_with_gap = adjust_request_size(adjust + align + gap_minimum, align);
|
||||
|
||||
const size_t aligned_size = (align <= ALIGN_SIZE) ? adjust : size_with_gap;
|
||||
|
||||
block_header_t* block = block_locate_free(control, aligned_size);
|
||||
|
||||
tlsf_assert(sizeof(block_header_t) == block_size_min + block_header_overhead);
|
||||
|
||||
if (block)
|
||||
{
|
||||
void * ptr = block_to_ptr(block);
|
||||
void * aligned = align_ptr(ptr, align);
|
||||
size_t gap = tlsf_cast(size_t, tlsf_cast(tlsfptr_t, aligned) - tlsf_cast(tlsfptr_t, ptr));
|
||||
|
||||
if (gap && gap < gap_minimum)
|
||||
{
|
||||
const size_t gap_remain = gap_minimum - gap;
|
||||
const size_t offset = tlsf_max(gap_remain, align);
|
||||
const void * next_aligned = tlsf_cast(void *, tlsf_cast(tlsfptr_t, aligned) + offset);
|
||||
|
||||
aligned = align_ptr(next_aligned, align);
|
||||
gap = tlsf_cast(size_t, tlsf_cast(tlsfptr_t, aligned) - tlsf_cast(tlsfptr_t, ptr));
|
||||
}
|
||||
|
||||
if (gap)
|
||||
{
|
||||
tlsf_assert(gap >= gap_minimum && "gap size too small");
|
||||
block = block_trim_free_leading(control, block, gap);
|
||||
}
|
||||
}
|
||||
|
||||
return block_prepare_used(control, block, adjust);
|
||||
}
|
||||
|
||||
static inline void tlsf_free(void * tlsf, void * ptr)
|
||||
{
|
||||
if (ptr)
|
||||
{
|
||||
control_t * control = tlsf_cast(control_t *, tlsf);
|
||||
block_header_t * block = block_from_ptr(ptr);
|
||||
tlsf_assert(!block_is_free(block) && "block already marked as free");
|
||||
block_mark_as_free(block);
|
||||
block = block_merge_prev(control, block);
|
||||
block = block_merge_next(control, block);
|
||||
block_insert(control, block);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void * tlsf_realloc(void * tlsf, void * ptr, size_t size)
|
||||
{
|
||||
control_t * control = tlsf_cast(control_t *, tlsf);
|
||||
void * p = 0;
|
||||
|
||||
if (ptr && size == 0)
|
||||
{
|
||||
tlsf_free(tlsf, ptr);
|
||||
}
|
||||
else if (!ptr)
|
||||
{
|
||||
p = tlsf_malloc(tlsf, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
block_header_t * block = block_from_ptr(ptr);
|
||||
block_header_t * next = block_next(block);
|
||||
|
||||
const size_t cursize = block_get_size(block);
|
||||
const size_t combined = cursize + block_get_size(next) + block_header_overhead;
|
||||
const size_t adjust = adjust_request_size(size, ALIGN_SIZE);
|
||||
|
||||
tlsf_assert(!block_is_free(block) && "block already marked as free");
|
||||
|
||||
if (adjust > cursize && (!block_is_free(next) || adjust > combined))
|
||||
{
|
||||
p = tlsf_malloc(tlsf, size);
|
||||
if (p)
|
||||
{
|
||||
const size_t minsize = tlsf_min(cursize, size);
|
||||
memcpy(p, ptr, minsize);
|
||||
tlsf_free(tlsf, ptr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (adjust > cursize)
|
||||
{
|
||||
block_merge_next(control, block);
|
||||
block_mark_as_used(block);
|
||||
}
|
||||
|
||||
block_trim_used(control, block, adjust);
|
||||
p = ptr;
|
||||
}
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void * mm_create(void * mem, size_t bytes)
|
||||
{
|
||||
return tlsf_create_with_pool(mem, bytes);
|
||||
}
|
||||
|
||||
void mm_destroy(void * mm)
|
||||
{
|
||||
tlsf_destroy(mm);
|
||||
}
|
||||
|
||||
void * mm_get_pool(void * mm)
|
||||
{
|
||||
return tlsf_get_pool(mm);
|
||||
}
|
||||
|
||||
void * mm_add_pool(void * mm, void * mem, size_t bytes)
|
||||
{
|
||||
return tlsf_add_pool(mm, mem, bytes);
|
||||
}
|
||||
|
||||
void mm_remove_pool(void * mm, void * pool)
|
||||
{
|
||||
tlsf_remove_pool(mm, pool);
|
||||
}
|
||||
|
||||
void * mm_malloc(void * mm, size_t size)
|
||||
{
|
||||
return tlsf_malloc(mm, size);
|
||||
}
|
||||
|
||||
void * mm_memalign(void * mm, size_t align, size_t size)
|
||||
{
|
||||
return tlsf_memalign(mm, align, size);
|
||||
}
|
||||
|
||||
void * mm_realloc(void * mm, void * ptr, size_t size)
|
||||
{
|
||||
return tlsf_realloc(mm, ptr, size);
|
||||
}
|
||||
|
||||
void mm_free(void * mm, void * ptr)
|
||||
{
|
||||
tlsf_free(mm, ptr);
|
||||
}
|
||||
|
||||
void * malloc(size_t size)
|
||||
{
|
||||
return tlsf_malloc(__heap_pool, size);
|
||||
}
|
||||
|
||||
void * memalign(size_t align, size_t size)
|
||||
{
|
||||
return tlsf_memalign(__heap_pool, align, size);
|
||||
}
|
||||
|
||||
void * realloc(void * ptr, size_t size)
|
||||
{
|
||||
return tlsf_realloc(__heap_pool, ptr, size);
|
||||
}
|
||||
|
||||
void * calloc(size_t nmemb, size_t size)
|
||||
{
|
||||
void * ptr;
|
||||
|
||||
if((ptr = malloc(nmemb * size)))
|
||||
memset(ptr, 0, nmemb * size);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void free(void * ptr)
|
||||
{
|
||||
tlsf_free(__heap_pool, ptr);
|
||||
}
|
||||
|
||||
void do_init_mem_pool(void)
|
||||
{
|
||||
#ifndef __SANDBOX__
|
||||
extern unsigned char __heap_start;
|
||||
extern unsigned char __heap_end;
|
||||
__heap_pool = tlsf_create_with_pool((void *)&__heap_start, (size_t)(&__heap_end - &__heap_start));
|
||||
#else
|
||||
static char __heap_buf[SZ_16M];
|
||||
__heap_pool = tlsf_create_with_pool((void *)__heap_buf, (size_t)(sizeof(__heap_buf)));
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,404 @@
|
||||
/*
|
||||
* memcpy.S
|
||||
*/
|
||||
.text
|
||||
|
||||
.global memcpy
|
||||
.type memcpy, %function
|
||||
.align 4
|
||||
|
||||
memcpy:
|
||||
/* determine copy direction */
|
||||
cmp r1, r0
|
||||
bcc .Lmemcpy_backwards
|
||||
|
||||
moveq r0, #0 /* quick abort for len=0 */
|
||||
moveq pc, lr
|
||||
|
||||
stmdb sp!, {r0, lr} /* memcpy() returns dest addr */
|
||||
subs r2, r2, #4
|
||||
blt .Lmemcpy_fl4 /* less than 4 bytes */
|
||||
ands r12, r0, #3
|
||||
bne .Lmemcpy_fdestul /* oh unaligned destination addr */
|
||||
ands r12, r1, #3
|
||||
bne .Lmemcpy_fsrcul /* oh unaligned source addr */
|
||||
|
||||
.Lmemcpy_ft8:
|
||||
/* we have aligned source and destination */
|
||||
subs r2, r2, #8
|
||||
blt .Lmemcpy_fl12 /* less than 12 bytes (4 from above) */
|
||||
subs r2, r2, #0x14
|
||||
blt .Lmemcpy_fl32 /* less than 32 bytes (12 from above) */
|
||||
stmdb sp!, {r4} /* borrow r4 */
|
||||
|
||||
/* blat 32 bytes at a time */
|
||||
.Lmemcpy_floop32:
|
||||
ldmia r1!, {r3, r4, r12, lr}
|
||||
stmia r0!, {r3, r4, r12, lr}
|
||||
ldmia r1!, {r3, r4, r12, lr}
|
||||
stmia r0!, {r3, r4, r12, lr}
|
||||
subs r2, r2, #0x20
|
||||
bge .Lmemcpy_floop32
|
||||
|
||||
cmn r2, #0x10
|
||||
ldmgeia r1!, {r3, r4, r12, lr} /* blat a remaining 16 bytes */
|
||||
stmgeia r0!, {r3, r4, r12, lr}
|
||||
subge r2, r2, #0x10
|
||||
ldmia sp!, {r4} /* return r4 */
|
||||
|
||||
.Lmemcpy_fl32:
|
||||
adds r2, r2, #0x14
|
||||
|
||||
/* blat 12 bytes at a time */
|
||||
.Lmemcpy_floop12:
|
||||
ldmgeia r1!, {r3, r12, lr}
|
||||
stmgeia r0!, {r3, r12, lr}
|
||||
subges r2, r2, #0x0c
|
||||
bge .Lmemcpy_floop12
|
||||
|
||||
.Lmemcpy_fl12:
|
||||
adds r2, r2, #8
|
||||
blt .Lmemcpy_fl4
|
||||
|
||||
subs r2, r2, #4
|
||||
ldrlt r3, [r1], #4
|
||||
strlt r3, [r0], #4
|
||||
ldmgeia r1!, {r3, r12}
|
||||
stmgeia r0!, {r3, r12}
|
||||
subge r2, r2, #4
|
||||
|
||||
.Lmemcpy_fl4:
|
||||
/* less than 4 bytes to go */
|
||||
adds r2, r2, #4
|
||||
ldmeqia sp!, {r0, pc} /* done */
|
||||
|
||||
/* copy the crud byte at a time */
|
||||
cmp r2, #2
|
||||
ldrb r3, [r1], #1
|
||||
strb r3, [r0], #1
|
||||
ldrgeb r3, [r1], #1
|
||||
strgeb r3, [r0], #1
|
||||
ldrgtb r3, [r1], #1
|
||||
strgtb r3, [r0], #1
|
||||
ldmia sp!, {r0, pc}
|
||||
|
||||
/* erg - unaligned destination */
|
||||
.Lmemcpy_fdestul:
|
||||
rsb r12, r12, #4
|
||||
cmp r12, #2
|
||||
|
||||
/* align destination with byte copies */
|
||||
ldrb r3, [r1], #1
|
||||
strb r3, [r0], #1
|
||||
ldrgeb r3, [r1], #1
|
||||
strgeb r3, [r0], #1
|
||||
ldrgtb r3, [r1], #1
|
||||
strgtb r3, [r0], #1
|
||||
subs r2, r2, r12
|
||||
blt .Lmemcpy_fl4 /* less the 4 bytes */
|
||||
|
||||
ands r12, r1, #3
|
||||
beq .Lmemcpy_ft8 /* we have an aligned source */
|
||||
|
||||
/* erg - unaligned source */
|
||||
/* This is where it gets nasty ... */
|
||||
.Lmemcpy_fsrcul:
|
||||
bic r1, r1, #3
|
||||
ldr lr, [r1], #4
|
||||
cmp r12, #2
|
||||
bgt .Lmemcpy_fsrcul3
|
||||
beq .Lmemcpy_fsrcul2
|
||||
cmp r2, #0x0c
|
||||
blt .Lmemcpy_fsrcul1loop4
|
||||
sub r2, r2, #0x0c
|
||||
stmdb sp!, {r4, r5}
|
||||
|
||||
.Lmemcpy_fsrcul1loop16:
|
||||
mov r3, lr, lsr #8
|
||||
ldmia r1!, {r4, r5, r12, lr}
|
||||
orr r3, r3, r4, lsl #24
|
||||
mov r4, r4, lsr #8
|
||||
orr r4, r4, r5, lsl #24
|
||||
mov r5, r5, lsr #8
|
||||
orr r5, r5, r12, lsl #24
|
||||
mov r12, r12, lsr #8
|
||||
orr r12, r12, lr, lsl #24
|
||||
stmia r0!, {r3-r5, r12}
|
||||
subs r2, r2, #0x10
|
||||
bge .Lmemcpy_fsrcul1loop16
|
||||
ldmia sp!, {r4, r5}
|
||||
adds r2, r2, #0x0c
|
||||
blt .Lmemcpy_fsrcul1l4
|
||||
|
||||
.Lmemcpy_fsrcul1loop4:
|
||||
mov r12, lr, lsr #8
|
||||
ldr lr, [r1], #4
|
||||
orr r12, r12, lr, lsl #24
|
||||
str r12, [r0], #4
|
||||
subs r2, r2, #4
|
||||
bge .Lmemcpy_fsrcul1loop4
|
||||
|
||||
.Lmemcpy_fsrcul1l4:
|
||||
sub r1, r1, #3
|
||||
b .Lmemcpy_fl4
|
||||
|
||||
.Lmemcpy_fsrcul2:
|
||||
cmp r2, #0x0c
|
||||
blt .Lmemcpy_fsrcul2loop4
|
||||
sub r2, r2, #0x0c
|
||||
stmdb sp!, {r4, r5}
|
||||
|
||||
.Lmemcpy_fsrcul2loop16:
|
||||
mov r3, lr, lsr #16
|
||||
ldmia r1!, {r4, r5, r12, lr}
|
||||
orr r3, r3, r4, lsl #16
|
||||
mov r4, r4, lsr #16
|
||||
orr r4, r4, r5, lsl #16
|
||||
mov r5, r5, lsr #16
|
||||
orr r5, r5, r12, lsl #16
|
||||
mov r12, r12, lsr #16
|
||||
orr r12, r12, lr, lsl #16
|
||||
stmia r0!, {r3-r5, r12}
|
||||
subs r2, r2, #0x10
|
||||
bge .Lmemcpy_fsrcul2loop16
|
||||
ldmia sp!, {r4, r5}
|
||||
adds r2, r2, #0x0c
|
||||
blt .Lmemcpy_fsrcul2l4
|
||||
|
||||
.Lmemcpy_fsrcul2loop4:
|
||||
mov r12, lr, lsr #16
|
||||
ldr lr, [r1], #4
|
||||
orr r12, r12, lr, lsl #16
|
||||
str r12, [r0], #4
|
||||
subs r2, r2, #4
|
||||
bge .Lmemcpy_fsrcul2loop4
|
||||
|
||||
.Lmemcpy_fsrcul2l4:
|
||||
sub r1, r1, #2
|
||||
b .Lmemcpy_fl4
|
||||
|
||||
.Lmemcpy_fsrcul3:
|
||||
cmp r2, #0x0c
|
||||
blt .Lmemcpy_fsrcul3loop4
|
||||
sub r2, r2, #0x0c
|
||||
stmdb sp!, {r4, r5}
|
||||
|
||||
.Lmemcpy_fsrcul3loop16:
|
||||
mov r3, lr, lsr #24
|
||||
ldmia r1!, {r4, r5, r12, lr}
|
||||
orr r3, r3, r4, lsl #8
|
||||
mov r4, r4, lsr #24
|
||||
orr r4, r4, r5, lsl #8
|
||||
mov r5, r5, lsr #24
|
||||
orr r5, r5, r12, lsl #8
|
||||
mov r12, r12, lsr #24
|
||||
orr r12, r12, lr, lsl #8
|
||||
stmia r0!, {r3-r5, r12}
|
||||
subs r2, r2, #0x10
|
||||
bge .Lmemcpy_fsrcul3loop16
|
||||
ldmia sp!, {r4, r5}
|
||||
adds r2, r2, #0x0c
|
||||
blt .Lmemcpy_fsrcul3l4
|
||||
|
||||
.Lmemcpy_fsrcul3loop4:
|
||||
mov r12, lr, lsr #24
|
||||
ldr lr, [r1], #4
|
||||
orr r12, r12, lr, lsl #8
|
||||
str r12, [r0], #4
|
||||
subs r2, r2, #4
|
||||
bge .Lmemcpy_fsrcul3loop4
|
||||
|
||||
.Lmemcpy_fsrcul3l4:
|
||||
sub r1, r1, #1
|
||||
b .Lmemcpy_fl4
|
||||
|
||||
.Lmemcpy_backwards:
|
||||
add r1, r1, r2
|
||||
add r0, r0, r2
|
||||
subs r2, r2, #4
|
||||
blt .Lmemcpy_bl4 /* less than 4 bytes */
|
||||
ands r12, r0, #3
|
||||
bne .Lmemcpy_bdestul /* oh unaligned destination addr */
|
||||
ands r12, r1, #3
|
||||
bne .Lmemcpy_bsrcul /* oh unaligned source addr */
|
||||
|
||||
.Lmemcpy_bt8:
|
||||
/* we have aligned source and destination */
|
||||
subs r2, r2, #8
|
||||
blt .Lmemcpy_bl12 /* less than 12 bytes (4 from above) */
|
||||
stmdb sp!, {r4, lr}
|
||||
subs r2, r2, #0x14 /* less than 32 bytes (12 from above) */
|
||||
blt .Lmemcpy_bl32
|
||||
|
||||
/* blat 32 bytes at a time */
|
||||
.Lmemcpy_bloop32:
|
||||
ldmdb r1!, {r3, r4, r12, lr}
|
||||
stmdb r0!, {r3, r4, r12, lr}
|
||||
ldmdb r1!, {r3, r4, r12, lr}
|
||||
stmdb r0!, {r3, r4, r12, lr}
|
||||
subs r2, r2, #0x20
|
||||
bge .Lmemcpy_bloop32
|
||||
|
||||
.Lmemcpy_bl32:
|
||||
cmn r2, #0x10
|
||||
ldmgedb r1!, {r3, r4, r12, lr} /* blat a remaining 16 bytes */
|
||||
stmgedb r0!, {r3, r4, r12, lr}
|
||||
subge r2, r2, #0x10
|
||||
adds r2, r2, #0x14
|
||||
ldmgedb r1!, {r3, r12, lr} /* blat a remaining 12 bytes */
|
||||
stmgedb r0!, {r3, r12, lr}
|
||||
subge r2, r2, #0x0c
|
||||
ldmia sp!, {r4, lr}
|
||||
|
||||
.Lmemcpy_bl12:
|
||||
adds r2, r2, #8
|
||||
blt .Lmemcpy_bl4
|
||||
subs r2, r2, #4
|
||||
ldrlt r3, [r1, #-4]!
|
||||
strlt r3, [r0, #-4]!
|
||||
ldmgedb r1!, {r3, r12}
|
||||
stmgedb r0!, {r3, r12}
|
||||
subge r2, r2, #4
|
||||
|
||||
.Lmemcpy_bl4:
|
||||
/* less than 4 bytes to go */
|
||||
adds r2, r2, #4
|
||||
moveq pc, lr
|
||||
|
||||
/* copy the crud byte at a time */
|
||||
cmp r2, #2
|
||||
ldrb r3, [r1, #-1]!
|
||||
strb r3, [r0, #-1]!
|
||||
ldrgeb r3, [r1, #-1]!
|
||||
strgeb r3, [r0, #-1]!
|
||||
ldrgtb r3, [r1, #-1]!
|
||||
strgtb r3, [r0, #-1]!
|
||||
mov pc, lr
|
||||
|
||||
/* erg - unaligned destination */
|
||||
.Lmemcpy_bdestul:
|
||||
cmp r12, #2
|
||||
|
||||
/* align destination with byte copies */
|
||||
ldrb r3, [r1, #-1]!
|
||||
strb r3, [r0, #-1]!
|
||||
ldrgeb r3, [r1, #-1]!
|
||||
strgeb r3, [r0, #-1]!
|
||||
ldrgtb r3, [r1, #-1]!
|
||||
strgtb r3, [r0, #-1]!
|
||||
subs r2, r2, r12
|
||||
blt .Lmemcpy_bl4 /* less than 4 bytes to go */
|
||||
ands r12, r1, #3
|
||||
beq .Lmemcpy_bt8 /* we have an aligned source */
|
||||
|
||||
/* erg - unaligned source */
|
||||
/* This is where it gets nasty ... */
|
||||
.Lmemcpy_bsrcul:
|
||||
bic r1, r1, #3
|
||||
ldr r3, [r1, #0]
|
||||
cmp r12, #2
|
||||
blt .Lmemcpy_bsrcul1
|
||||
beq .Lmemcpy_bsrcul2
|
||||
cmp r2, #0x0c
|
||||
blt .Lmemcpy_bsrcul3loop4
|
||||
sub r2, r2, #0x0c
|
||||
stmdb sp!, {r4, r5, lr}
|
||||
|
||||
.Lmemcpy_bsrcul3loop16:
|
||||
mov lr, r3, lsl #8
|
||||
ldmdb r1!, {r3-r5, r12}
|
||||
orr lr, lr, r12, lsr #24
|
||||
mov r12, r12, lsl #8
|
||||
orr r12, r12, r5, lsr #24
|
||||
mov r5, r5, lsl #8
|
||||
orr r5, r5, r4, lsr #24
|
||||
mov r4, r4, lsl #8
|
||||
orr r4, r4, r3, lsr #24
|
||||
stmdb r0!, {r4, r5, r12, lr}
|
||||
subs r2, r2, #0x10
|
||||
bge .Lmemcpy_bsrcul3loop16
|
||||
ldmia sp!, {r4, r5, lr}
|
||||
adds r2, r2, #0x0c
|
||||
blt .Lmemcpy_bsrcul3l4
|
||||
|
||||
.Lmemcpy_bsrcul3loop4:
|
||||
mov r12, r3, lsl #8
|
||||
ldr r3, [r1, #-4]!
|
||||
orr r12, r12, r3, lsr #24
|
||||
str r12, [r0, #-4]!
|
||||
subs r2, r2, #4
|
||||
bge .Lmemcpy_bsrcul3loop4
|
||||
|
||||
.Lmemcpy_bsrcul3l4:
|
||||
add r1, r1, #3
|
||||
b .Lmemcpy_bl4
|
||||
|
||||
.Lmemcpy_bsrcul2:
|
||||
cmp r2, #0x0c
|
||||
blt .Lmemcpy_bsrcul2loop4
|
||||
sub r2, r2, #0x0c
|
||||
stmdb sp!, {r4, r5, lr}
|
||||
|
||||
.Lmemcpy_bsrcul2loop16:
|
||||
mov lr, r3, lsl #16
|
||||
ldmdb r1!, {r3-r5, r12}
|
||||
orr lr, lr, r12, lsr #16
|
||||
mov r12, r12, lsl #16
|
||||
orr r12, r12, r5, lsr #16
|
||||
mov r5, r5, lsl #16
|
||||
orr r5, r5, r4, lsr #16
|
||||
mov r4, r4, lsl #16
|
||||
orr r4, r4, r3, lsr #16
|
||||
stmdb r0!, {r4, r5, r12, lr}
|
||||
subs r2, r2, #0x10
|
||||
bge .Lmemcpy_bsrcul2loop16
|
||||
ldmia sp!, {r4, r5, lr}
|
||||
adds r2, r2, #0x0c
|
||||
blt .Lmemcpy_bsrcul2l4
|
||||
|
||||
.Lmemcpy_bsrcul2loop4:
|
||||
mov r12, r3, lsl #16
|
||||
ldr r3, [r1, #-4]!
|
||||
orr r12, r12, r3, lsr #16
|
||||
str r12, [r0, #-4]!
|
||||
subs r2, r2, #4
|
||||
bge .Lmemcpy_bsrcul2loop4
|
||||
|
||||
.Lmemcpy_bsrcul2l4:
|
||||
add r1, r1, #2
|
||||
b .Lmemcpy_bl4
|
||||
|
||||
.Lmemcpy_bsrcul1:
|
||||
cmp r2, #0x0c
|
||||
blt .Lmemcpy_bsrcul1loop4
|
||||
sub r2, r2, #0x0c
|
||||
stmdb sp!, {r4, r5, lr}
|
||||
|
||||
.Lmemcpy_bsrcul1loop32:
|
||||
mov lr, r3, lsl #24
|
||||
ldmdb r1!, {r3-r5, r12}
|
||||
orr lr, lr, r12, lsr #8
|
||||
mov r12, r12, lsl #24
|
||||
orr r12, r12, r5, lsr #8
|
||||
mov r5, r5, lsl #24
|
||||
orr r5, r5, r4, lsr #8
|
||||
mov r4, r4, lsl #24
|
||||
orr r4, r4, r3, lsr #8
|
||||
stmdb r0!, {r4, r5, r12, lr}
|
||||
subs r2, r2, #0x10
|
||||
bge .Lmemcpy_bsrcul1loop32
|
||||
ldmia sp!, {r4, r5, lr}
|
||||
adds r2, r2, #0x0c
|
||||
blt .Lmemcpy_bsrcul1l4
|
||||
|
||||
.Lmemcpy_bsrcul1loop4:
|
||||
mov r12, r3, lsl #24
|
||||
ldr r3, [r1, #-4]!
|
||||
orr r12, r12, r3, lsr #8
|
||||
str r12, [r0, #-4]!
|
||||
subs r2, r2, #4
|
||||
bge .Lmemcpy_bsrcul1loop4
|
||||
|
||||
.Lmemcpy_bsrcul1l4:
|
||||
add r1, r1, #1
|
||||
b .Lmemcpy_bl4
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* memcpy.S
|
||||
*/
|
||||
.text
|
||||
|
||||
.global memset
|
||||
.type memset, %function
|
||||
.align 4
|
||||
|
||||
memset:
|
||||
stmfd sp!, {r0} /* remember address for return value */
|
||||
and r1, r1, #0x000000ff /* we write bytes */
|
||||
|
||||
cmp r2, #0x00000004 /* do we have less than 4 bytes */
|
||||
blt .Lmemset_lessthanfour
|
||||
|
||||
/* first we will word align the address */
|
||||
ands r3, r0, #0x00000003 /* get the bottom two bits */
|
||||
beq .Lmemset_addraligned /* the address is word aligned */
|
||||
|
||||
rsb r3, r3, #0x00000004
|
||||
sub r2, r2, r3
|
||||
cmp r3, #0x00000002
|
||||
strb r1, [r0], #0x0001 /* set 1 byte */
|
||||
strgeb r1, [r0], #0x0001 /* set another byte */
|
||||
strgtb r1, [r0], #0x0001 /* and a third */
|
||||
|
||||
cmp r2, #0x00000004
|
||||
blt .Lmemset_lessthanfour
|
||||
|
||||
/* now we must be word aligned */
|
||||
.Lmemset_addraligned:
|
||||
orr r3, r1, r1, lsl #8 /* repeat the byte into a word */
|
||||
orr r3, r3, r3, lsl #16
|
||||
|
||||
/* we know we have at least 4 bytes ... */
|
||||
cmp r2, #0x00000020 /* if less than 32 then use words */
|
||||
blt .Lmemset_lessthan32
|
||||
|
||||
/* we have at least 32 so lets use quad words */
|
||||
stmfd sp!, {r4-r6} /* store registers */
|
||||
mov r4, r3 /* duplicate data */
|
||||
mov r5, r3
|
||||
mov r6, r3
|
||||
|
||||
.Lmemset_loop16:
|
||||
stmia r0!, {r3-r6} /* store 16 bytes */
|
||||
sub r2, r2, #0x00000010 /* adjust count */
|
||||
cmp r2, #0x00000010 /* still got at least 16 bytes ? */
|
||||
bgt .Lmemset_loop16
|
||||
|
||||
ldmfd sp!, {r4-r6} /* restore registers */
|
||||
|
||||
/* do we need to set some words as well ? */
|
||||
cmp r2, #0x00000004
|
||||
blt .Lmemset_lessthanfour
|
||||
|
||||
/* have either less than 16 or less than 32 depending on route taken */
|
||||
.Lmemset_lessthan32:
|
||||
|
||||
/* we have at least 4 bytes so copy as words */
|
||||
.Lmemset_loop4:
|
||||
str r3, [r0], #0x0004
|
||||
sub r2, r2, #0x0004
|
||||
cmp r2, #0x00000004
|
||||
bge .Lmemset_loop4
|
||||
|
||||
.Lmemset_lessthanfour:
|
||||
cmp r2, #0x00000000
|
||||
ldmeqfd sp!, {r0}
|
||||
moveq pc, lr /* zero length so exit */
|
||||
|
||||
cmp r2, #0x00000002
|
||||
strb r1, [r0], #0x0001 /* set 1 byte */
|
||||
strgeb r1, [r0], #0x0001 /* set another byte */
|
||||
strgtb r1, [r0], #0x0001 /* and a third */
|
||||
|
||||
ldmfd sp!, {r0}
|
||||
mov pc, lr /* exit */
|
||||
@@ -0,0 +1,757 @@
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// \author (c) Marco Paland (info@paland.com)
|
||||
// 2014-2018, PALANDesign Hannover, Germany
|
||||
//
|
||||
// \license The MIT License (MIT)
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on
|
||||
// embedded systems with a very limited resources. These routines are thread
|
||||
// safe and reentrant!
|
||||
// Use this instead of the bloated standard/newlib printf cause these use
|
||||
// malloc for printf (and may not be thread safe).
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <stdint.h>
|
||||
#include "printf.h"
|
||||
|
||||
|
||||
// ntoa conversion buffer size, this must be big enough to hold
|
||||
// one converted numeric number including padded zeros (dynamically created on stack)
|
||||
// 32 byte is a good default
|
||||
#define PRINTF_NTOA_BUFFER_SIZE 32U
|
||||
|
||||
// ftoa conversion buffer size, this must be big enough to hold
|
||||
// one converted float number including padded zeros (dynamically created on stack)
|
||||
// 32 byte is a good default
|
||||
#define PRINTF_FTOA_BUFFER_SIZE 32U
|
||||
|
||||
// define this to support floating point (%f)
|
||||
#define PRINTF_SUPPORT_FLOAT
|
||||
|
||||
// define this to support long long types (%llu or %p)
|
||||
#define PRINTF_SUPPORT_LONG_LONG
|
||||
|
||||
// define this to support the ptrdiff_t type (%t)
|
||||
// ptrdiff_t is normally defined in <stddef.h> as long or long long type
|
||||
#define PRINTF_SUPPORT_PTRDIFF_T
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// internal flag definitions
|
||||
#define FLAGS_ZEROPAD (1U << 0U)
|
||||
#define FLAGS_LEFT (1U << 1U)
|
||||
#define FLAGS_PLUS (1U << 2U)
|
||||
#define FLAGS_SPACE (1U << 3U)
|
||||
#define FLAGS_HASH (1U << 4U)
|
||||
#define FLAGS_UPPERCASE (1U << 5U)
|
||||
#define FLAGS_CHAR (1U << 6U)
|
||||
#define FLAGS_SHORT (1U << 7U)
|
||||
#define FLAGS_LONG (1U << 8U)
|
||||
#define FLAGS_LONG_LONG (1U << 9U)
|
||||
#define FLAGS_PRECISION (1U << 10U)
|
||||
|
||||
typedef unsigned char bool;
|
||||
#ifndef false
|
||||
#define false 0
|
||||
#endif
|
||||
#ifndef true
|
||||
#define true (!false)
|
||||
#endif
|
||||
extern void sys_uart_putc(char c);
|
||||
static char last_ch;
|
||||
void _putchar(char character)
|
||||
{
|
||||
if(character == 0x0a && last_ch != 0x0d)
|
||||
{
|
||||
sys_uart_putc(0x0d);
|
||||
}
|
||||
sys_uart_putc(character);
|
||||
|
||||
}
|
||||
|
||||
// output function type
|
||||
typedef void (*out_fct_type)(char character, void* buffer, size_t idx, size_t maxlen);
|
||||
|
||||
|
||||
// wrapper (used as buffer) for output function type
|
||||
typedef struct {
|
||||
void (*fct)(char character, void* arg);
|
||||
void* arg;
|
||||
} out_fct_wrap_type;
|
||||
|
||||
|
||||
// internal buffer output
|
||||
static inline void _out_buffer(char character, void* buffer, size_t idx, size_t maxlen)
|
||||
{
|
||||
if (idx < maxlen) {
|
||||
((char*)buffer)[idx] = character;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// internal null output
|
||||
static inline void _out_null(char character, void* buffer, size_t idx, size_t maxlen)
|
||||
{
|
||||
(void)character; (void)buffer; (void)idx; (void)maxlen;
|
||||
}
|
||||
|
||||
|
||||
// internal _putchar wrapper
|
||||
static inline void _out_char(char character, void* buffer, size_t idx, size_t maxlen)
|
||||
{
|
||||
(void)buffer; (void)idx; (void)maxlen;
|
||||
if (character) {
|
||||
_putchar(character);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// internal output function wrapper
|
||||
static inline void _out_fct(char character, void* buffer, size_t idx, size_t maxlen)
|
||||
{
|
||||
(void)idx; (void)maxlen;
|
||||
// buffer is the output fct pointer
|
||||
((out_fct_wrap_type*)buffer)->fct(character, ((out_fct_wrap_type*)buffer)->arg);
|
||||
}
|
||||
|
||||
|
||||
// internal strlen
|
||||
// \return The length of the string (excluding the terminating 0)
|
||||
static inline unsigned int _strlen(const char* str)
|
||||
{
|
||||
const char* s;
|
||||
for (s = str; *s; ++s);
|
||||
return (unsigned int)(s - str);
|
||||
}
|
||||
|
||||
|
||||
// internal test if char is a digit (0-9)
|
||||
// \return true if char is a digit
|
||||
static inline bool _is_digit(char ch)
|
||||
{
|
||||
return (ch >= '0') && (ch <= '9');
|
||||
}
|
||||
|
||||
|
||||
// internal ASCII string to unsigned int conversion
|
||||
static unsigned int _atoi(const char** str)
|
||||
{
|
||||
unsigned int i = 0U;
|
||||
while (_is_digit(**str)) {
|
||||
i = i * 10U + (unsigned int)(*((*str)++) - '0');
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
// internal itoa format
|
||||
static size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t maxlen, char* buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags)
|
||||
{
|
||||
const size_t start_idx = idx;
|
||||
|
||||
// pad leading zeros
|
||||
while (!(flags & FLAGS_LEFT) && (len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
|
||||
buf[len++] = '0';
|
||||
}
|
||||
while (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
|
||||
buf[len++] = '0';
|
||||
}
|
||||
|
||||
// handle hash
|
||||
if (flags & FLAGS_HASH) {
|
||||
if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) {
|
||||
len--;
|
||||
if (len && (base == 16U)) {
|
||||
len--;
|
||||
}
|
||||
}
|
||||
if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
|
||||
buf[len++] = 'x';
|
||||
}
|
||||
else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
|
||||
buf[len++] = 'X';
|
||||
}
|
||||
else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
|
||||
buf[len++] = 'b';
|
||||
}
|
||||
if (len < PRINTF_NTOA_BUFFER_SIZE) {
|
||||
buf[len++] = '0';
|
||||
}
|
||||
}
|
||||
|
||||
// handle sign
|
||||
if (len && (len == width) && (negative || (flags & FLAGS_PLUS) || (flags & FLAGS_SPACE))) {
|
||||
len--;
|
||||
}
|
||||
if (len < PRINTF_NTOA_BUFFER_SIZE) {
|
||||
if (negative) {
|
||||
buf[len++] = '-';
|
||||
}
|
||||
else if (flags & FLAGS_PLUS) {
|
||||
buf[len++] = '+'; // ignore the space if the '+' exists
|
||||
}
|
||||
else if (flags & FLAGS_SPACE) {
|
||||
buf[len++] = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
// pad spaces up to given width
|
||||
if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) {
|
||||
for (size_t i = len; i < width; i++) {
|
||||
out(' ', buffer, idx++, maxlen);
|
||||
}
|
||||
}
|
||||
|
||||
// reverse string
|
||||
for (size_t i = 0U; i < len; i++) {
|
||||
out(buf[len - i - 1U], buffer, idx++, maxlen);
|
||||
}
|
||||
|
||||
// append pad spaces up to given width
|
||||
if (flags & FLAGS_LEFT) {
|
||||
while (idx - start_idx < width) {
|
||||
out(' ', buffer, idx++, maxlen);
|
||||
}
|
||||
}
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
|
||||
// internal itoa for 'long' type
|
||||
static size_t _ntoa_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base, unsigned int prec, unsigned int width, unsigned int flags)
|
||||
{
|
||||
char buf[PRINTF_NTOA_BUFFER_SIZE];
|
||||
size_t len = 0U;
|
||||
|
||||
// no hash for 0 values
|
||||
if (!value) {
|
||||
flags &= ~FLAGS_HASH;
|
||||
}
|
||||
|
||||
// write if precision != 0 and value is != 0
|
||||
if (!(flags & FLAGS_PRECISION) || value) {
|
||||
do {
|
||||
const char digit = (char)(value % base);
|
||||
buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
|
||||
value /= base;
|
||||
} while (value && (len < PRINTF_NTOA_BUFFER_SIZE));
|
||||
}
|
||||
|
||||
return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags);
|
||||
}
|
||||
|
||||
|
||||
// internal itoa for 'long long' type
|
||||
#if defined(PRINTF_SUPPORT_LONG_LONG)
|
||||
static size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags)
|
||||
{
|
||||
char buf[PRINTF_NTOA_BUFFER_SIZE];
|
||||
size_t len = 0U;
|
||||
|
||||
// no hash for 0 values
|
||||
if (!value) {
|
||||
flags &= ~FLAGS_HASH;
|
||||
}
|
||||
|
||||
// write if precision != 0 and value is != 0
|
||||
if (!(flags & FLAGS_PRECISION) || value) {
|
||||
do {
|
||||
const char digit = (char)(value % base);
|
||||
buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
|
||||
value /= base;
|
||||
} while (value && (len < PRINTF_NTOA_BUFFER_SIZE));
|
||||
}
|
||||
|
||||
return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags);
|
||||
}
|
||||
#endif // PRINTF_SUPPORT_LONG_LONG
|
||||
|
||||
|
||||
#if defined(PRINTF_SUPPORT_FLOAT)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wfloat-equal"
|
||||
|
||||
static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags)
|
||||
{
|
||||
const size_t start_idx = idx;
|
||||
|
||||
char buf[PRINTF_FTOA_BUFFER_SIZE];
|
||||
size_t len = 0U;
|
||||
double diff = 0.0;
|
||||
|
||||
// if input is larger than thres_max, revert to exponential
|
||||
const double thres_max = (double)0x7FFFFFFF;
|
||||
|
||||
// powers of 10
|
||||
static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
|
||||
|
||||
// test for negative
|
||||
bool negative = false;
|
||||
if (value < 0) {
|
||||
negative = true;
|
||||
value = 0 - value;
|
||||
}
|
||||
|
||||
// set default precision to 6, if not set explicitly
|
||||
if (!(flags & FLAGS_PRECISION)) {
|
||||
prec = 6U;
|
||||
}
|
||||
// limit precision to 9, cause a prec >= 10 can lead to overflow errors
|
||||
while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) {
|
||||
buf[len++] = '0';
|
||||
prec--;
|
||||
}
|
||||
|
||||
int whole = (int)value;
|
||||
double tmp = (value - whole) * pow10[prec];
|
||||
unsigned long frac = (unsigned long)tmp;
|
||||
diff = tmp - frac;
|
||||
|
||||
if (diff > 0.5l) {
|
||||
++frac;
|
||||
// handle rollover, e.g. case 0.99 with prec 1 is 1.0
|
||||
if (frac >= pow10[prec]) {
|
||||
frac = 0;
|
||||
++whole;
|
||||
}
|
||||
}
|
||||
else if ((diff == 0.5l) && ((frac == 0U) || (frac & 1U))) {
|
||||
// if halfway, round up if odd, OR if last digit is 0
|
||||
++frac;
|
||||
}
|
||||
|
||||
// TBD: for very large numbers switch back to native sprintf for exponentials. Anyone want to write code to replace this?
|
||||
// Normal printf behavior is to print EVERY whole number digit which can be 100s of characters overflowing your buffers == bad
|
||||
if (value > thres_max) {
|
||||
return 0U;
|
||||
}
|
||||
|
||||
if (prec == 0U) {
|
||||
diff = value - (double)whole;
|
||||
if (diff > 0.5l) {
|
||||
// greater than 0.5, round up, e.g. 1.6 -> 2
|
||||
++whole;
|
||||
}
|
||||
else if ((diff == 0.5l) && (whole & 1)) {
|
||||
// exactly 0.5 and ODD, then round up
|
||||
// 1.5 -> 2, but 2.5 -> 2
|
||||
++whole;
|
||||
}
|
||||
}
|
||||
else {
|
||||
unsigned int count = prec;
|
||||
// now do fractional part, as an unsigned number
|
||||
while (len < PRINTF_FTOA_BUFFER_SIZE) {
|
||||
--count;
|
||||
buf[len++] = (char)(48U + (frac % 10U));
|
||||
if (!(frac /= 10U)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// add extra 0s
|
||||
while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) {
|
||||
buf[len++] = '0';
|
||||
}
|
||||
if (len < PRINTF_FTOA_BUFFER_SIZE) {
|
||||
// add decimal
|
||||
buf[len++] = '.';
|
||||
}
|
||||
}
|
||||
|
||||
// do whole part, number is reversed
|
||||
while (len < PRINTF_FTOA_BUFFER_SIZE) {
|
||||
buf[len++] = (char)(48 + (whole % 10));
|
||||
if (!(whole /= 10)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// pad leading zeros
|
||||
while (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) {
|
||||
buf[len++] = '0';
|
||||
}
|
||||
|
||||
// handle sign
|
||||
if ((len == width) && (negative || (flags & FLAGS_PLUS) || (flags & FLAGS_SPACE))) {
|
||||
len--;
|
||||
}
|
||||
if (len < PRINTF_FTOA_BUFFER_SIZE) {
|
||||
if (negative) {
|
||||
buf[len++] = '-';
|
||||
}
|
||||
else if (flags & FLAGS_PLUS) {
|
||||
buf[len++] = '+'; // ignore the space if the '+' exists
|
||||
}
|
||||
else if (flags & FLAGS_SPACE) {
|
||||
buf[len++] = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
// pad spaces up to given width
|
||||
if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) {
|
||||
for (size_t i = len; i < width; i++) {
|
||||
out(' ', buffer, idx++, maxlen);
|
||||
}
|
||||
}
|
||||
|
||||
// reverse string
|
||||
for (size_t i = 0U; i < len; i++) {
|
||||
out(buf[len - i - 1U], buffer, idx++, maxlen);
|
||||
}
|
||||
|
||||
// append pad spaces up to given width
|
||||
if (flags & FLAGS_LEFT) {
|
||||
while (idx - start_idx < width) {
|
||||
out(' ', buffer, idx++, maxlen);
|
||||
}
|
||||
}
|
||||
|
||||
return idx;
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
#endif // PRINTF_SUPPORT_FLOAT
|
||||
|
||||
|
||||
// internal vsnprintf
|
||||
static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va)
|
||||
{
|
||||
unsigned int flags, width, precision, n;
|
||||
size_t idx = 0U;
|
||||
|
||||
if (!buffer) {
|
||||
// use null output function
|
||||
out = _out_null;
|
||||
}
|
||||
|
||||
while (*format)
|
||||
{
|
||||
// format specifier? %[flags][width][.precision][length]
|
||||
if (*format != '%') {
|
||||
// no
|
||||
out(*format, buffer, idx++, maxlen);
|
||||
format++;
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
// yes, evaluate it
|
||||
format++;
|
||||
}
|
||||
|
||||
// evaluate flags
|
||||
flags = 0U;
|
||||
do {
|
||||
switch (*format) {
|
||||
case '0': flags |= FLAGS_ZEROPAD; format++; n = 1U; break;
|
||||
case '-': flags |= FLAGS_LEFT; format++; n = 1U; break;
|
||||
case '+': flags |= FLAGS_PLUS; format++; n = 1U; break;
|
||||
case ' ': flags |= FLAGS_SPACE; format++; n = 1U; break;
|
||||
case '#': flags |= FLAGS_HASH; format++; n = 1U; break;
|
||||
default : n = 0U; break;
|
||||
}
|
||||
} while (n);
|
||||
|
||||
// evaluate width field
|
||||
width = 0U;
|
||||
if (_is_digit(*format)) {
|
||||
width = _atoi(&format);
|
||||
}
|
||||
else if (*format == '*') {
|
||||
const int w = va_arg(va, int);
|
||||
if (w < 0) {
|
||||
flags |= FLAGS_LEFT; // reverse padding
|
||||
width = (unsigned int)-w;
|
||||
}
|
||||
else {
|
||||
width = (unsigned int)w;
|
||||
}
|
||||
format++;
|
||||
}
|
||||
|
||||
// evaluate precision field
|
||||
precision = 0U;
|
||||
if (*format == '.') {
|
||||
flags |= FLAGS_PRECISION;
|
||||
format++;
|
||||
if (_is_digit(*format)) {
|
||||
precision = _atoi(&format);
|
||||
}
|
||||
else if (*format == '*') {
|
||||
const int prec = (int)va_arg(va, int);
|
||||
precision = prec > 0 ? (unsigned int)prec : 0U;
|
||||
format++;
|
||||
}
|
||||
}
|
||||
|
||||
// evaluate length field
|
||||
switch (*format) {
|
||||
case 'l' :
|
||||
flags |= FLAGS_LONG;
|
||||
format++;
|
||||
if (*format == 'l') {
|
||||
flags |= FLAGS_LONG_LONG;
|
||||
format++;
|
||||
}
|
||||
break;
|
||||
case 'h' :
|
||||
flags |= FLAGS_SHORT;
|
||||
format++;
|
||||
if (*format == 'h') {
|
||||
flags |= FLAGS_CHAR;
|
||||
format++;
|
||||
}
|
||||
break;
|
||||
#if defined(PRINTF_SUPPORT_PTRDIFF_T)
|
||||
case 't' :
|
||||
flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
|
||||
format++;
|
||||
break;
|
||||
#endif
|
||||
case 'j' :
|
||||
flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
|
||||
format++;
|
||||
break;
|
||||
case 'z' :
|
||||
flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
|
||||
format++;
|
||||
break;
|
||||
default :
|
||||
break;
|
||||
}
|
||||
|
||||
// evaluate specifier
|
||||
switch (*format) {
|
||||
case 'd' :
|
||||
case 'i' :
|
||||
case 'u' :
|
||||
case 'x' :
|
||||
case 'X' :
|
||||
case 'o' :
|
||||
case 'b' : {
|
||||
// set the base
|
||||
unsigned int base;
|
||||
if (*format == 'x' || *format == 'X') {
|
||||
base = 16U;
|
||||
}
|
||||
else if (*format == 'o') {
|
||||
base = 8U;
|
||||
}
|
||||
else if (*format == 'b') {
|
||||
base = 2U;
|
||||
}
|
||||
else {
|
||||
base = 10U;
|
||||
flags &= ~FLAGS_HASH; // no hash for dec format
|
||||
}
|
||||
// uppercase
|
||||
if (*format == 'X') {
|
||||
flags |= FLAGS_UPPERCASE;
|
||||
}
|
||||
|
||||
// no plus or space flag for u, x, X, o, b
|
||||
if ((*format != 'i') && (*format != 'd')) {
|
||||
flags &= ~(FLAGS_PLUS | FLAGS_SPACE);
|
||||
}
|
||||
|
||||
// ignore '0' flag when precision is given
|
||||
if (flags & FLAGS_PRECISION) {
|
||||
flags &= ~FLAGS_ZEROPAD;
|
||||
}
|
||||
|
||||
// convert the integer
|
||||
if ((*format == 'i') || (*format == 'd')) {
|
||||
// signed
|
||||
if (flags & FLAGS_LONG_LONG) {
|
||||
#if defined(PRINTF_SUPPORT_LONG_LONG)
|
||||
const long long value = va_arg(va, long long);
|
||||
idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
|
||||
#endif
|
||||
}
|
||||
else if (flags & FLAGS_LONG) {
|
||||
const long value = va_arg(va, long);
|
||||
idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
|
||||
}
|
||||
else {
|
||||
const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : va_arg(va, int);
|
||||
idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// unsigned
|
||||
if (flags & FLAGS_LONG_LONG) {
|
||||
#if defined(PRINTF_SUPPORT_LONG_LONG)
|
||||
idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags);
|
||||
#endif
|
||||
}
|
||||
else if (flags & FLAGS_LONG) {
|
||||
idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags);
|
||||
}
|
||||
else {
|
||||
const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : va_arg(va, unsigned int);
|
||||
idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags);
|
||||
}
|
||||
}
|
||||
format++;
|
||||
break;
|
||||
}
|
||||
#if defined(PRINTF_SUPPORT_FLOAT)
|
||||
case 'f' :
|
||||
case 'F' :
|
||||
idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags);
|
||||
format++;
|
||||
break;
|
||||
#endif // PRINTF_SUPPORT_FLOAT
|
||||
case 'c' : {
|
||||
unsigned int l = 1U;
|
||||
// pre padding
|
||||
if (!(flags & FLAGS_LEFT)) {
|
||||
while (l++ < width) {
|
||||
out(' ', buffer, idx++, maxlen);
|
||||
}
|
||||
}
|
||||
// char output
|
||||
out((char)va_arg(va, int), buffer, idx++, maxlen);
|
||||
// post padding
|
||||
if (flags & FLAGS_LEFT) {
|
||||
while (l++ < width) {
|
||||
out(' ', buffer, idx++, maxlen);
|
||||
}
|
||||
}
|
||||
format++;
|
||||
break;
|
||||
}
|
||||
|
||||
case 's' : {
|
||||
char* p = va_arg(va, char*);
|
||||
unsigned int l = _strlen(p);
|
||||
// pre padding
|
||||
if (flags & FLAGS_PRECISION) {
|
||||
l = (l < precision ? l : precision);
|
||||
}
|
||||
if (!(flags & FLAGS_LEFT)) {
|
||||
while (l++ < width) {
|
||||
out(' ', buffer, idx++, maxlen);
|
||||
}
|
||||
}
|
||||
// string output
|
||||
while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) {
|
||||
out(*(p++), buffer, idx++, maxlen);
|
||||
}
|
||||
// post padding
|
||||
if (flags & FLAGS_LEFT) {
|
||||
while (l++ < width) {
|
||||
out(' ', buffer, idx++, maxlen);
|
||||
}
|
||||
}
|
||||
format++;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'p' : {
|
||||
width = sizeof(void*) * 2U;
|
||||
flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE;
|
||||
#if defined(PRINTF_SUPPORT_LONG_LONG)
|
||||
const bool is_ll = sizeof(uintptr_t) == sizeof(long long);
|
||||
if (is_ll) {
|
||||
idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void*), false, 16U, precision, width, flags);
|
||||
}
|
||||
else {
|
||||
#endif
|
||||
idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void*)), false, 16U, precision, width, flags);
|
||||
#if defined(PRINTF_SUPPORT_LONG_LONG)
|
||||
}
|
||||
#endif
|
||||
format++;
|
||||
break;
|
||||
}
|
||||
|
||||
case '%' :
|
||||
out('%', buffer, idx++, maxlen);
|
||||
format++;
|
||||
break;
|
||||
|
||||
default :
|
||||
out(*format, buffer, idx++, maxlen);
|
||||
format++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// termination
|
||||
out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
|
||||
|
||||
// return written chars without terminating \0
|
||||
return (int)idx;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
int printf(const char* format, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
char buffer[1];
|
||||
const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va);
|
||||
va_end(va);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int sprintf(char* buffer, const char* format, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va);
|
||||
va_end(va);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int snprintf(char* buffer, size_t count, const char* format, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
const int ret = _vsnprintf(_out_buffer, buffer, count, format, va);
|
||||
va_end(va);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int vsnprintf(char* buffer, size_t count, const char* format, va_list va)
|
||||
{
|
||||
return _vsnprintf(_out_buffer, buffer, count, format, va);
|
||||
}
|
||||
|
||||
|
||||
int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
out_fct_wrap_type out_fct_wrap = { out, arg };
|
||||
const int ret = _vsnprintf(_out_fct, (char*)&out_fct_wrap, (size_t)-1, format, va);
|
||||
va_end(va);
|
||||
return ret;
|
||||
}
|
||||
Reference in New Issue
Block a user