//! Arena allocation support for binary lowering fixtures. /// Error returned when an arena cannot satisfy an allocation. export union AllocError: Copy { /// The arena has insufficient storage. OutOfMemory, } /// Bump allocator backed by a byte slice. export record Arena { /// Backing storage. data: *mut [u8], /// Current allocation offset in bytes. offset: u32, } /// Create an arena backed by `data`. export fn new(data: *mut [u8]) -> Arena { return Arena { data, offset: 0 }; } /// Reset the arena to its initial empty state. export fn reset(arena: &mut Arena) { set arena.offset = 0; } /// Return the number of bytes committed in the arena. export fn used(arena: &Arena) -> u32 { return arena.offset; } /// Allocate aligned storage without changing the arena after failure. export unsafe fn alloc(arena: *unsafe mut Arena, size: u32, alignment: u32) -> *mut opaque throws (AllocError) { assert alignment > 0; assert size > 0; assert (alignment & (alignment - 1)) == 0; let capacity = arena.data.len; if arena.offset >= capacity { throw AllocError::OutOfMemory; } let address = &arena.data[0] as u64; let mask = (alignment - 1) as u64; let remainder = ((address & mask) + (arena.offset as u64 & mask)) & mask; let padding = ((alignment as u64 - remainder) & mask) as u32; let available = capacity - arena.offset; if padding > available or size > available - padding { throw AllocError::OutOfMemory; } let aligned = arena.offset + padding; let base: *mut u8 = &mut arena.data[aligned]; set arena.offset = aligned + size; return base as *mut opaque; } /// Allocate raw storage that remains valid until the arena is reset. export unsafe fn allocRaw(arena: &mut Arena, size: u32, alignment: u32) -> *unsafe mut opaque throws (AllocError) { let owner = try alloc(&mut *arena, size, alignment); let byte = owner as *mut u8; return &mut *byte as *unsafe mut opaque; } /// Allocate a raw slice that remains valid until the arena is reset. export unsafe fn allocRawSlice(arena: &mut Arena, size: u32, alignment: u32, count: u32) -> *unsafe mut [opaque] throws (AllocError) { if count == 0 { return &mut []; } if size <> 0 and count > 4294967295 / size { throw AllocError::OutOfMemory; } let raw = try allocRaw(arena, size * count, alignment); return @sliceOf(raw, count); } /// Raw storage provider for allocation sessions. export trait Alloc { /// Reserve one uninitialized object. unsafe fn (&mut Alloc) reserve(size: u32, alignment: u32) -> *unsafe mut opaque throws (AllocError); /// Reserve an uninitialized slice. unsafe fn (&mut Alloc) reserveSlice(itemSize: u32, itemAlignment: u32, count: u32) -> *unsafe mut [opaque] throws (AllocError); } instance Alloc for Arena { /// Reserve one uninitialized object in the arena. unsafe fn (arena: &mut Arena) reserve(size: u32, alignment: u32) -> *unsafe mut opaque throws (AllocError) { return try allocRaw(arena, size, alignment); } /// Reserve an uninitialized slice in the arena. unsafe fn (arena: &mut Arena) reserveSlice(size: u32, alignment: u32, count: u32) -> *unsafe mut [opaque] throws (AllocError) { return try allocRawSlice(arena, size, alignment, count); } }