package memtable import ( "errors" "sync" "sync/atomic" ) // ErrArenaFull is returned when the arena cannot satisfy an allocation request. var ErrArenaFull = errors.New("arena: insufficient capacity") // Arena is a bump allocator backed by a fixed-size byte slice. // It provides thread-safe allocation with 8-byte alignment. // // The arena is used by the memtable to store keys, values, and skip-list // node structures. Once allocated, bytes are never freed — the entire // arena is discarded when the memtable is flushed. type Arena struct { buf []byte offset uint32 // next allocation offset (atomic for reads) capacity uint32 // total capacity (immutable) mu sync.Mutex } // NewArena creates a new Arena with the given capacity in bytes. // The entire buffer is allocated upfront. func NewArena(capacity uint32) *Arena { return &Arena{ buf: make([]byte, capacity), capacity: capacity, } } // Allocate reserves alignedSize bytes in the arena and returns the offset // at which the caller can write. The size is aligned up to 8 bytes. func (a *Arena) Allocate(size uint32) (uint32, error) { alignedSize := (size + 7) &^ uint32(7) a.mu.Lock() rem := a.capacity - a.offset if rem < alignedSize { a.mu.Unlock() return 0, ErrArenaFull } off := a.offset a.offset += alignedSize a.mu.Unlock() return off, nil } // GetBytes returns a slice of the arena buffer at [offset, offset+size). // It panics if the range is out of bounds. func (a *Arena) GetBytes(offset, size uint32) []byte { end := offset + size if end > a.capacity { panic("arena: GetBytes out of bounds") } return a.buf[offset:end] } // Remaining returns the number of bytes still available for allocation. func (a *Arena) Remaining() uint32 { return a.capacity - atomic.LoadUint32(&a.offset) } // Capacity returns the total capacity of the arena. func (a *Arena) Capacity() uint32 { return a.capacity } // Reserve checks whether the arena has at least totalSize bytes remaining // without actually allocating. It is used by the WAL writer to verify // capacity before appending entries. func (a *Arena) Reserve(totalSize uint32) error { a.mu.Lock() rem := a.capacity - a.offset a.mu.Unlock() if rem < totalSize { return ErrArenaFull } return nil }