Implement a Go Arena Allocator
This challenge asks you to implement an arena allocator in Go. Arena allocators, also known as region or pool allocators, are a memory management technique where memory is allocated from a large pre-allocated chunk (the arena). Deallocation typically happens all at once by resetting the arena, rather than freeing individual allocations. This can be significantly more efficient for scenarios with many short-lived allocations.
Problem Description
You need to create a Go package that provides an arena allocator. The allocator should allow users to allocate blocks of memory of a specified size. The key feature is that all memory allocated from a single arena instance should be deallocated simultaneously when the arena is reset.
Requirements:
NewArena(size int): A constructor function that creates a new arena with a pre-allocated buffer ofsizebytes. Ifsizeis not positive, it should panic.Allocate(size int): A method on the arena that returns a[]byteslice representing an allocated block of memory.- The returned slice should have a length of
size. - The underlying array of the returned slice must be part of the arena's buffer.
- If
sizeis not positive, it should panic. - If there isn't enough contiguous space left in the arena for the requested
size, it should panic. (For simplicity, we won't implement growth or complex fragmentation handling in this initial version).
- The returned slice should have a length of
Reset(): A method that deallocates all memory previously allocated from the arena. AfterReset(), all subsequentAllocatecalls should start from the beginning of the arena's buffer.Capacity(): A method that returns the total size of the arena in bytes.Used(): A method that returns the number of bytes currently allocated from the arena.
Expected Behavior:
- Multiple calls to
Allocateshould return distinct memory regions within the arena. - The
Used()count should increase with each successful allocation. - After
Reset(),Used()should return 0, and subsequentAllocatecalls should reuse memory from the beginning of the arena. - The arena should not allow allocations that exceed its
Capacity().
Edge Cases:
- Allocating 0 bytes.
- Attempting to allocate more memory than is available.
- Resetting an empty arena.
- Allocating precisely the full capacity of the arena.
Examples
Example 1:
arena := NewArena(1024) // Create an arena with 1KB capacity
data1 := arena.Allocate(100) // Allocate 100 bytes
// data1 is a []byte of length 100, backed by the arena's buffer
data2 := arena.Allocate(200) // Allocate another 200 bytes
// data2 is a different []byte of length 200, also backed by the arena
fmt.Printf("Capacity: %d, Used: %d\n", arena.Capacity(), arena.Used())
// Expected Output: Capacity: 1024, Used: 300
arena.Reset() // Deallocate all memory
fmt.Printf("Capacity: %d, Used: %d\n", arena.Capacity(), arena.Used())
// Expected Output: Capacity: 1024, Used: 0
data3 := arena.Allocate(500) // Allocate 500 bytes from the reset arena
fmt.Printf("Capacity: %d, Used: %d\n", arena.Capacity(), arena.Used())
// Expected Output: Capacity: 1024, Used: 500
Explanation: The arena starts with 1024 bytes. Two allocations totaling 300 bytes are made. Reset() clears the used space, and a new allocation of 500 bytes is successful from the beginning.
Example 2:
arena := NewArena(50) // Small arena
data1 := arena.Allocate(20)
data2 := arena.Allocate(25)
fmt.Printf("Used: %d\n", arena.Used()) // Expected Output: Used: 45
// Attempting to allocate more than remaining space should panic
// arena.Allocate(10) // This would panic
Explanation: Two allocations are made, totaling 45 bytes. The remaining 5 bytes are not enough for a subsequent 10-byte allocation, leading to a panic.
Example 3: (Edge Case - Full Allocation and Reset)
arena := NewArena(32) // Small arena
data1 := arena.Allocate(32) // Allocate the entire arena
fmt.Printf("Used: %d\n", arena.Used()) // Expected Output: Used: 32
arena.Reset() // Reset the arena
fmt.Printf("Used: %d\n", arena.Used()) // Expected Output: Used: 0
data2 := arena.Allocate(16) // A new allocation should be possible
fmt.Printf("Used: %d\n", arena.Used()) // Expected Output: Used: 16
Explanation: The arena is fully allocated. Reset() makes the entire buffer available again, and a smaller allocation succeeds.
Constraints
- The arena's initial
sizemust be a positive integer. Allocaterequests forsizemust be positive integers.- Allocations will not exceed the arena's capacity in a single call.
- For this challenge, the arena does not need to implement dynamic growth.
- No garbage collection of individual allocations within the arena; deallocation only occurs via
Reset(). - Concurrency: The arena implementation is not required to be goroutine-safe.
Notes
- Consider how to keep track of the current allocation offset within the arena's buffer.
- The
[]byteslices returned byAllocateshould have their capacity equal to their length and be backed by the arena's internal buffer. You can achieve this by creating a new slice that shares the underlying array of the arena's buffer using slicing:arena.buffer[offset : offset+size]. - Panicking on invalid input or insufficient space is the expected behavior for this simplified arena allocator.
- Think about the underlying data structure to hold the arena's buffer. A
[]byteis a natural choice.