Hone logo
Hone
Problems

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:

  1. NewArena(size int): A constructor function that creates a new arena with a pre-allocated buffer of size bytes. If size is not positive, it should panic.
  2. Allocate(size int): A method on the arena that returns a []byte slice 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 size is 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).
  3. Reset(): A method that deallocates all memory previously allocated from the arena. After Reset(), all subsequent Allocate calls should start from the beginning of the arena's buffer.
  4. Capacity(): A method that returns the total size of the arena in bytes.
  5. Used(): A method that returns the number of bytes currently allocated from the arena.

Expected Behavior:

  • Multiple calls to Allocate should return distinct memory regions within the arena.
  • The Used() count should increase with each successful allocation.
  • After Reset(), Used() should return 0, and subsequent Allocate calls 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 size must be a positive integer.
  • Allocate requests for size must 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 []byte slices returned by Allocate should 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 []byte is a natural choice.
Loading editor...
go