package memtable import ( "sync" "testing" "github.com/stretchr/testify/require" ) func TestArenaAllocate(t *testing.T) { a := NewArena(1024) off, err := a.Allocate(9) require.NoError(t, err) require.Equal(t, uint32(0), off) copy(a.GetBytes(off, 9), "test data") require.Equal(t, []byte("test data"), a.GetBytes(off, 9)) off2, err := a.Allocate(200) require.NoError(t, err) // 9 bytes aligned to 16, so second allocation starts at 16 require.Equal(t, uint32(16), off2) } func TestArenaFull(t *testing.T) { a := NewArena(64) off, err := a.Allocate(60) require.NoError(t, err) require.Equal(t, uint32(0), off) // 60 aligned to 64 _, err = a.Allocate(10) require.ErrorIs(t, err, ErrArenaFull) } func TestArenaAlignment(t *testing.T) { a := NewArena(1024) sizes := []uint32{1, 3, 5, 7, 8, 9, 13, 16, 100} for _, s := range sizes { off, err := a.Allocate(s) require.NoError(t, err, "size=%d", s) require.Equal(t, uint32(0), off%8, "offset %d not 8-byte aligned for size %d", off, s) } } func TestArenaConcurrent(t *testing.T) { a := NewArena(65536) const goroutines = 64 const perGoroutine = 32 var wg sync.WaitGroup wg.Add(goroutines) offsets := make([][]uint32, goroutines) for i := 0; i < goroutines; i++ { i := i go func() { defer wg.Done() locals := make([]uint32, 0, perGoroutine) for j := 0; j < perGoroutine; j++ { off, err := a.Allocate(8) if err == nil { // Write unique data to detect corruption copy(a.GetBytes(off, 8), []byte{byte(i), byte(j), 0, 0, 0, 0, 0, 0}) locals = append(locals, off) } } offsets[i] = locals }() } wg.Wait() // Verify data integrity — each byte pair must match its goroutine/j index for i, locals := range offsets { for j, off := range locals { data := a.GetBytes(off, 8) require.Equal(t, byte(i), data[0], "goroutine %d offset %d", i, off) require.Equal(t, byte(j), data[1], "iteration %d offset %d", j, off) } } } func TestArenaReserve(t *testing.T) { a := NewArena(128) require.NoError(t, a.Reserve(64)) require.NoError(t, a.Reserve(128)) require.ErrorIs(t, a.Reserve(129), ErrArenaFull) // Allocate some space _, err := a.Allocate(32) require.NoError(t, err) // 32 aligned = 32 // 128 - 32 = 96 remaining require.NoError(t, a.Reserve(96)) require.ErrorIs(t, a.Reserve(97), ErrArenaFull) } func TestArenaRemainingAndCapacity(t *testing.T) { cap := uint32(256) a := NewArena(cap) require.Equal(t, cap, a.Capacity()) require.Equal(t, cap, a.Remaining()) _, err := a.Allocate(10) require.NoError(t, err) // 10 aligned to 16 require.Equal(t, cap-16, a.Remaining()) }