--- /dev/null
+// https://github.com/32bitmicro/newlib-nano-1.0/blob/master/newlib/libc/machine/xstormy16/tiny-malloc.c
+
+/* A replacement malloc with:
+ - Much reduced code size;
+ - Smaller RAM footprint;
+ - The ability to handle downward-growing heaps;
+ but
+ - Slower;
+ - Probably higher memory fragmentation;
+ - Doesn't support threads (but, if it did support threads,
+ it wouldn't need a global lock, only a compare-and-swap instruction);
+ - Assumes the maximum alignment required is the alignment of a pointer;
+ - Assumes that memory is already there and doesn't need to be allocated.
+
+* Synopsis of public routines
+
+ malloc(size_t n);
+ Return a pointer to a newly allocated chunk of at least n bytes, or null
+ if no space is available.
+ free(void* p);
+ Release the chunk of memory pointed to by p, or no effect if p is null.
+ realloc(void* p, size_t n);
+ Return a pointer to a chunk of size n that contains the same data
+ as does chunk p up to the minimum of (n, p's size) bytes, or null
+ if no space is available. The returned pointer may or may not be
+ the same as p. If p is null, equivalent to malloc. Unless the
+ #define REALLOC_ZERO_BYTES_FREES below is set, realloc with a
+ size argument of zero (re)allocates a minimum-sized chunk.
+ memalign(size_t alignment, size_t n);
+ Return a pointer to a newly allocated chunk of n bytes, aligned
+ in accord with the alignment argument, which must be a power of
+ two. Will fail if 'alignment' is too large.
+ calloc(size_t unit, size_t quantity);
+ Returns a pointer to quantity * unit bytes, with all locations
+ set to zero.
+ cfree(void* p);
+ Equivalent to free(p).
+ malloc_trim(size_t pad);
+ Release all but pad bytes of freed top-most memory back
+ to the system. Return 1 if successful, else 0.
+ malloc_usable_size(void* p);
+ Report the number usable allocated bytes associated with allocated
+ chunk p. This may or may not report more bytes than were requested,
+ due to alignment and minimum size constraints.
+ malloc_stats();
+ Prints brief summary statistics on stderr.
+ mallinfo()
+ Returns (by copy) a struct containing various summary statistics.
+ mallopt(int parameter_number, int parameter_value)
+ Changes one of the tunable parameters described below. Returns
+ 1 if successful in changing the parameter, else 0. Actually, returns 0
+ always, as no parameter can be changed.
+*/
+
+#ifdef __xstormy16__
+#define MALLOC_DIRECTION -1
+#endif
+
+#ifndef MALLOC_DIRECTION
+#define MALLOC_DIRECTION 1
+#endif
+
+#include <stddef.h>
+
+void* malloc(size_t);
+void free(void*);
+void* realloc(void*, size_t);
+void* memalign(size_t, size_t);
+void* valloc(size_t);
+void* pvalloc(size_t);
+void* calloc(size_t, size_t);
+void cfree(void*);
+int malloc_trim(size_t);
+size_t malloc_usable_size(void*);
+void malloc_stats(void);
+int mallopt(int, int);
+struct mallinfo mallinfo(void);
+
+typedef struct freelist_entry {
+ size_t size;
+ struct freelist_entry *next;
+} *fle;
+
+extern void * __malloc_end;
+extern fle __malloc_freelist;
+
+/* Return the number of bytes that need to be added to X to make it
+ aligned to an ALIGN boundary. ALIGN must be a power of 2. */
+#define M_ALIGN(x, align) (-(size_t)(x) & ((align) - 1))
+
+/* Return the number of bytes that need to be subtracted from X to make it
+ aligned to an ALIGN boundary. ALIGN must be a power of 2. */
+#define M_ALIGN_SUB(x, align) ((size_t)(x) & ((align) - 1))
+
+extern char *__malloc_start;
+
+/* This is the minimum gap allowed between __malloc_end and the top of
+ the stack. This is only checked for when __malloc_end is
+ decreased; if instead the stack grows into the heap, silent data
+ corruption will result. */
+#define MALLOC_MINIMUM_GAP 32
+
+#ifdef __xstormy16__
+register void * stack_pointer asm ("r15");
+#define MALLOC_LIMIT stack_pointer
+#else
+#define MALLOC_LIMIT __builtin_frame_address (0)
+#endif
+
+#if MALLOC_DIRECTION < 0
+#define CAN_ALLOC_P(required) \
+ (((size_t) __malloc_end - (size_t)MALLOC_LIMIT \
+ - MALLOC_MINIMUM_GAP) >= (required))
+#else
+#define CAN_ALLOC_P(required) \
+ (((size_t)MALLOC_LIMIT - (size_t) __malloc_end \
+ - MALLOC_MINIMUM_GAP) >= (required))
+#endif
+
+/* real_size is the size we actually have to allocate, allowing for
+ overhead and alignment. */
+#define REAL_SIZE(sz) \
+ ((sz) < sizeof (struct freelist_entry) - sizeof (size_t) \
+ ? sizeof (struct freelist_entry) \
+ : sz + sizeof (size_t) + M_ALIGN(sz, sizeof (size_t)))
+
+#ifdef DEFINE_MALLOC
+
+void * __malloc_end = &__malloc_start;
+fle __malloc_freelist;
+
+void *
+malloc (size_t sz)
+{
+ fle *nextfree;
+ fle block;
+
+ /* real_size is the size we actually have to allocate, allowing for
+ overhead and alignment. */
+ size_t real_size = REAL_SIZE (sz);
+
+ /* Look for the first block on the freelist that is large enough. */
+ for (nextfree = &__malloc_freelist;
+ *nextfree;
+ nextfree = &(*nextfree)->next)
+ {
+ block = *nextfree;
+
+ if (block->size >= real_size)
+ {
+ /* If the block found is just the right size, remove it from
+ the free list. Otherwise, split it. */
+ if (block->size < real_size + sizeof (struct freelist_entry))
+ {
+ *nextfree = block->next;
+ return (void *)&block->next;
+ }
+ else
+ {
+ size_t newsize = block->size - real_size;
+ fle newnext = block->next;
+ *nextfree = (fle)((size_t)block + real_size);
+ (*nextfree)->size = newsize;
+ (*nextfree)->next = newnext;
+ goto done;
+ }
+ }
+
+ /* If this is the last block on the freelist, and it was too small,
+ enlarge it. */
+ if (! block->next
+ && __malloc_end == (void *)((size_t)block + block->size))
+ {
+ size_t moresize = real_size - block->size;
+ if (! CAN_ALLOC_P (moresize))
+ return NULL;
+
+ *nextfree = NULL;
+ if (MALLOC_DIRECTION < 0)
+ {
+ block = __malloc_end = (void *)((size_t)block - moresize);
+ }
+ else
+ {
+ __malloc_end = (void *)((size_t)block + real_size);
+ }
+
+ goto done;
+ }
+ }
+
+ /* No free space at the end of the free list. Allocate new space
+ and use that. */
+
+ if (! CAN_ALLOC_P (real_size))
+ return NULL;
+
+ if (MALLOC_DIRECTION > 0)
+ {
+ block = __malloc_end;
+ __malloc_end = (void *)((size_t)__malloc_end + real_size);
+ }
+ else
+ {
+ block = __malloc_end = (void *)((size_t)__malloc_end - real_size);
+ }
+ done:
+ block->size = real_size;
+ return (void *)&block->next;
+}
+
+#endif
+
+#ifdef DEFINE_FREE
+
+void
+free (void *block_p)
+{
+ fle *nextfree;
+ fle block = (fle)((size_t) block_p - offsetof (struct freelist_entry, next));
+
+ if (block_p == NULL)
+ return;
+
+ /* Look on the freelist to see if there's a free block just before
+ or just after this block. */
+ for (nextfree = &__malloc_freelist;
+ *nextfree;
+ nextfree = &(*nextfree)->next)
+ {
+ fle thisblock = *nextfree;
+ if ((size_t)thisblock + thisblock->size == (size_t) block)
+ {
+ thisblock->size += block->size;
+ if (MALLOC_DIRECTION > 0
+ && thisblock->next
+ && (size_t) block + block->size == (size_t) thisblock->next)
+ {
+ thisblock->size += thisblock->next->size;
+ thisblock->next = thisblock->next->next;
+ }
+ return;
+ }
+ else if ((size_t) thisblock == (size_t) block + block->size)
+ {
+ if (MALLOC_DIRECTION < 0
+ && thisblock->next
+ && (size_t) block == ((size_t) thisblock->next
+ + thisblock->next->size))
+ {
+ *nextfree = thisblock->next;
+ thisblock->next->size += block->size + thisblock->size;
+ }
+ else
+ {
+ block->size += thisblock->size;
+ block->next = thisblock->next;
+ *nextfree = block;
+ }
+ return;
+ }
+ else if ((MALLOC_DIRECTION > 0
+ && (size_t) thisblock > (size_t) block)
+ || (MALLOC_DIRECTION < 0
+ && (size_t) thisblock < (size_t) block))
+ break;
+ }
+
+ block->next = *nextfree;
+ *nextfree = block;
+ return;
+}
+#endif
+
+#ifdef DEFINE_REALLOC
+void *
+realloc (void *block_p, size_t sz)
+{
+ fle block = (fle)((size_t) block_p - offsetof (struct freelist_entry, next));
+ size_t real_size = REAL_SIZE (sz);
+ size_t old_real_size;
+
+ if (block_p == NULL)
+ return malloc (sz);
+
+ old_real_size = block->size;
+
+ /* Perhaps we need to allocate more space. */
+ if (old_real_size < real_size)
+ {
+ void *result;
+ size_t old_size = old_real_size - sizeof (size_t);
+
+ /* Need to allocate, copy, and free. */
+ result = malloc (sz);
+ if (result == NULL)
+ return NULL;
+ memcpy (result, block_p, old_size < sz ? old_size : sz);
+ free (block_p);
+ return result;
+ }
+ /* Perhaps we can free some space. */
+ if (old_real_size - real_size >= sizeof (struct freelist_entry))
+ {
+ fle newblock = (fle)((size_t)block + real_size);
+ block->size = real_size;
+ newblock->size = old_real_size - real_size;
+ free (&newblock->next);
+ }
+ return block_p;
+}
+#endif
+
+#ifdef DEFINE_CALLOC
+void *
+calloc (size_t n, size_t elem_size)
+{
+ void *result;
+ size_t sz = n * elem_size;
+ result = malloc (sz);
+ if (result != NULL)
+ memset (result, 0, sz);
+ return result;
+}
+#endif
+
+#ifdef DEFINE_CFREE
+void
+cfree (void *p)
+{
+ free (p);
+}
+#endif
+
+#ifdef DEFINE_MEMALIGN
+void *
+memalign (size_t align, size_t sz)
+{
+ fle *nextfree;
+ fle block;
+
+ /* real_size is the size we actually have to allocate, allowing for
+ overhead and alignment. */
+ size_t real_size = REAL_SIZE (sz);
+
+ /* Some sanity checking on 'align'. */
+ if ((align & (align - 1)) != 0
+ || align <= 0)
+ return NULL;
+
+ /* Look for the first block on the freelist that is large enough. */
+ /* One tricky part is this: We want the result to be a valid pointer
+ to free. That means that there has to be room for a size_t
+ before the block. If there's additional space before the block,
+ it should go on the freelist, or it'll be lost---we could add it
+ to the size of the block before it in memory, but finding the
+ previous block is expensive. */
+ for (nextfree = &__malloc_freelist;
+ ;
+ nextfree = &(*nextfree)->next)
+ {
+ size_t before_size;
+ size_t old_size;
+
+ /* If we've run out of free blocks, allocate more space. */
+ if (! *nextfree)
+ {
+ old_size = real_size;
+ if (MALLOC_DIRECTION < 0)
+ {
+ old_size += M_ALIGN_SUB (((size_t)__malloc_end
+ - old_size + sizeof (size_t)),
+ align);
+ if (! CAN_ALLOC_P (old_size))
+ return NULL;
+ block = __malloc_end = (void *)((size_t)__malloc_end - old_size);
+ }
+ else
+ {
+ block = __malloc_end;
+ old_size += M_ALIGN ((size_t)__malloc_end + sizeof (size_t),
+ align);
+ if (! CAN_ALLOC_P (old_size))
+ return NULL;
+ __malloc_end = (void *)((size_t)__malloc_end + old_size);
+ }
+ *nextfree = block;
+ block->size = old_size;
+ block->next = NULL;
+ }
+ else
+ {
+ block = *nextfree;
+ old_size = block->size;
+ }
+
+
+ before_size = M_ALIGN (&block->next, align);
+ if (before_size != 0)
+ before_size = sizeof (*block) + M_ALIGN (&(block+1)->next, align);
+
+ /* If this is the last block on the freelist, and it is too small,
+ enlarge it. */
+ if (! block->next
+ && old_size < real_size + before_size
+ && __malloc_end == (void *)((size_t)block + block->size))
+ {
+ if (MALLOC_DIRECTION < 0)
+ {
+ size_t moresize = real_size - block->size;
+ moresize += M_ALIGN_SUB ((size_t)&block->next - moresize, align);
+ if (! CAN_ALLOC_P (moresize))
+ return NULL;
+ block = __malloc_end = (void *)((size_t)block - moresize);
+ block->next = NULL;
+ block->size = old_size = old_size + moresize;
+ before_size = 0;
+ }
+ else
+ {
+ if (! CAN_ALLOC_P (before_size + real_size - block->size))
+ return NULL;
+ __malloc_end = (void *)((size_t)block + before_size + real_size);
+ block->size = old_size = before_size + real_size;
+ }
+
+ /* Two out of the four cases below will now be possible; which
+ two depends on MALLOC_DIRECTION. */
+ }
+
+ if (old_size >= real_size + before_size)
+ {
+ /* This block will do. If there needs to be space before it,
+ split the block. */
+ if (before_size != 0)
+ {
+ fle old_block = block;
+
+ old_block->size = before_size;
+ block = (fle)((size_t)block + before_size);
+
+ /* If there's no space after the block, we're now nearly
+ done; just make a note of the size required.
+ Otherwise, we need to create a new free space block. */
+ if (old_size - before_size
+ <= real_size + sizeof (struct freelist_entry))
+ {
+ block->size = old_size - before_size;
+ return (void *)&block->next;
+ }
+ else
+ {
+ fle new_block;
+ new_block = (fle)((size_t)block + real_size);
+ new_block->size = old_size - before_size - real_size;
+ if (MALLOC_DIRECTION > 0)
+ {
+ new_block->next = old_block->next;
+ old_block->next = new_block;
+ }
+ else
+ {
+ new_block->next = old_block;
+ *nextfree = new_block;
+ }
+ goto done;
+ }
+ }
+ else
+ {
+ /* If the block found is just the right size, remove it from
+ the free list. Otherwise, split it. */
+ if (old_size <= real_size + sizeof (struct freelist_entry))
+ {
+ *nextfree = block->next;
+ return (void *)&block->next;
+ }
+ else
+ {
+ size_t newsize = old_size - real_size;
+ fle newnext = block->next;
+ *nextfree = (fle)((size_t)block + real_size);
+ (*nextfree)->size = newsize;
+ (*nextfree)->next = newnext;
+ goto done;
+ }
+ }
+ }
+ }
+
+ done:
+ block->size = real_size;
+ return (void *)&block->next;
+}
+#endif
+
+#ifdef DEFINE_VALLOC
+void *
+valloc (size_t sz)
+{
+ return memalign (128, sz);
+}
+#endif
+#ifdef DEFINE_PVALLOC
+void *
+pvalloc (size_t sz)
+{
+ return memalign (128, sz + M_ALIGN (sz, 128));
+}
+#endif
+
+#ifdef DEFINE_MALLINFO
+#include "malloc.h"
+
+struct mallinfo
+mallinfo (void)
+{
+ struct mallinfo r;
+ fle fr;
+ size_t free_size;
+ size_t total_size;
+ size_t free_blocks;
+
+ memset (&r, 0, sizeof (r));
+
+ free_size = 0;
+ free_blocks = 0;
+ for (fr = __malloc_freelist; fr; fr = fr->next)
+ {
+ free_size += fr->size;
+ free_blocks++;
+ if (! fr->next)
+ {
+ int atend;
+ if (MALLOC_DIRECTION > 0)
+ atend = (void *)((size_t)fr + fr->size) == __malloc_end;
+ else
+ atend = (void *)fr == __malloc_end;
+ if (atend)
+ r.keepcost = fr->size;
+ }
+ }
+
+ if (MALLOC_DIRECTION > 0)
+ total_size = (char *)__malloc_end - (char *)&__malloc_start;
+ else
+ total_size = (char *)&__malloc_start - (char *)__malloc_end;
+
+#ifdef DEBUG
+ /* Fixme: should walk through all the in-use blocks and see if
+ they're valid. */
+#endif
+
+ r.arena = total_size;
+ r.fordblks = free_size;
+ r.uordblks = total_size - free_size;
+ r.ordblks = free_blocks;
+ return r;
+}
+#endif
+
+#ifdef DEFINE_MALLOC_STATS
+#include "malloc.h"
+#include <stdio.h>
+
+void
+malloc_stats(void)
+{
+ struct mallinfo i;
+ FILE *fp;
+
+ fp = stderr;
+ i = mallinfo();
+ fprintf (fp, "malloc has reserved %u bytes between %p and %p\n",
+ i.arena, &__malloc_start, __malloc_end);
+ fprintf (fp, "there are %u bytes free in %u chunks\n",
+ i.fordblks, i.ordblks);
+ fprintf (fp, "of which %u bytes are at the end of the reserved space\n",
+ i.keepcost);
+ fprintf (fp, "and %u bytes are in use.\n", i.uordblks);
+}
+#endif
+
+#ifdef DEFINE_MALLOC_USABLE_SIZE
+size_t
+malloc_usable_size (void *block_p)
+{
+ fle block = (fle)((size_t) block_p - offsetof (struct freelist_entry, next));
+ return block->size - sizeof (size_t);
+}
+#endif
+
+#ifdef DEFINE_MALLOPT
+int
+mallopt (int n, int v)
+{
+ (void)n; (void)v;
+ return 0;
+}
+#endif