A rewrite of Acorn's flex sliding heap allocator for RISC OS.
flex keeps Acorn's interface. Existing code should only need relinking
against the new library. The implementation is new: around 400 lines of C90
in place of the original's 1,000-odd, with the TRACE and LIB_DEBUGGING
scaffolding gone.
- The anchor model. Every block is owned by a
flex_ptranchor that flex updates whenever it moves the block. Access blocks through their anchors and never hold a raw pointer into a block across a flex call. - Every public function, with the same signature and return conventions:
flex_init,flex_alloc,flex_free,flex_size,flex_extend,flex_midextend,flex_reanchor,flex_set_budge,flex_set_deferred_compaction,flex_compactandflex_save_heap_info. flex_inittries a dynamic area of up todynamic_sizebytes, falls back to the Wimp slot if it can't create one, and returns the area number or 0. The dynamic area is removed at exit.- Budging is off by default and only applies to the Wimp slot.
- Compaction happens on every
flex_freeunless deferred. - Fatal errors are reported with
Wimp_ReportErrorand the program exits. Theflex1,flex2andflex3message tokens are unchanged, so existing Messages files still work.
- Include guard. The header's include guard is
FLEX_Hrather than__flex_h. flex_set_budge. Acorn's version swapped the C library's slot extend handler on each call, and would install any other value you passed as a handler of its own. flex registers its handler once inflex_initandflex_set_budgejust sets a flag. Pass 1 or 0 to switch budging on or off. Passing -1 now returns the current state, as Acorn's documentation promised (Acorn's code returned -1). It still returns -1 when a dynamic area is in use.- Wimp slot handling. When growing or shrinking the Wimp slot, flex
temporarily lifts the memory limit so that
Wimp_SlotSizedoesn't move the C library's idea of where its memory ends. - Failure leaves blocks alone.
flex_extendandflex_midextendreturn 0 with the block unchanged when memory runs out.flex_allocsets the anchor to NULL on failure or ifnis negative, as before. - Argument checking.
flex_midextendtreats anatoutside the block, or anat + bybelow zero, as a fatalflex1error. A negative size passed toflex_extendis fatal for the same reason. - Deferred compaction. With deferral on, compaction happens in
flex_alloc,flex_compact, when budging and whenflex_midextendwould otherwise run out of memory. Shrinking a block withflex_midextendonly gives memory back to the OS straight away when deferral is off. flex_compactalways completes and always returns 0.flex_save_heap_infoworks (Acorn's was marked untested). It appends a line for the heap followed by a line per block, marking free blocks.- Block rounding. Block sizes are rounded up to a multiple of the
eight-byte block header rather than to four bytes.
flex_sizestill returns the size you asked for.
flex builds for RISC OS only, with CMake 3.18 or later and a RISC OS
toolchain file that defines TARGET_RISCOS and riscos_set_flags():
cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=/path/to/riscos-toolchain.cmake
cmake --build buildThis produces a static flex library. It needs kernel.h and swis.h from
the RISC OS C library.
#include "flex.h"
static char *buf;
int main(void)
{
flex_init("MyApp", NULL, 16 * 1024 * 1024);
if (!flex_alloc((flex_ptr) &buf, 1024))
return 1;
buf[0] = 'x';
if (!flex_extend((flex_ptr) &buf, 4096))
return 1; /* buf still holds the original 1024 bytes */
flex_free((flex_ptr) &buf);
return 0;
}See flex.h for the full description of each function.