From 21e624a0601085ac2526f1814ac7aeb1cb9d78dc Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Thu, 29 Oct 2026 12:13:29 -0700 Subject: [PATCH 1/3] Size new arena chunks from the previous chunk's capacity Arena.NewSlice computed the next chunk size from len(a.data), but it grows whenever the request does not fit in the remaining space, which usually happens before the current chunk is full. Doubling the used length rather than the chunk capacity could make the next chunk the same size as, or smaller than, the one being replaced. Arenas that mostly serve slices then kept allocating small chunks and never reached the intended steady-state size. Base the growth on cap(a.data) so each chunk doubles the previous one up to the size ceiling. Arena.New only grows when len == cap, so it already behaved correctly; it now uses cap as well for consistency. Co-authored-by: Steven Chong <25894545+teamchong@users.noreply.github.com> --- tsc/internal/core/arena.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tsc/internal/core/arena.go b/tsc/internal/core/arena.go index bbe802464138c..082572ae35e81 100644 --- a/tsc/internal/core/arena.go +++ b/tsc/internal/core/arena.go @@ -15,7 +15,7 @@ type Arena[T any] struct { // capacity and unrelated values retained by a surviving pointer into a chunk. func (a *Arena[T]) New() *T { if len(a.data) == cap(a.data) { - nextSize := nextArenaSize[T](len(a.data)) + nextSize := nextArenaSize[T](cap(a.data)) // Use the same trick as slices.Concat; Grow rounds up to the next size class. a.data = slices.Grow[[]T](nil, nextSize) } @@ -33,7 +33,7 @@ func (a *Arena[T]) NewSlice(size int) []T { return nil } if len(a.data)+size > cap(a.data) { - nextSize := nextArenaSize[T](len(a.data)) + nextSize := nextArenaSize[T](cap(a.data)) if size > nextSize { return make([]T, size) } From 20dfbb7f93c0c831b7c655e40c3ad12a24cda474 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Thu, 29 Oct 2026 14:11:07 -0700 Subject: [PATCH 2/3] Add regression test for arena growth from a partial chunk The existing arena tests only check the slices NewSlice returns, and those checks pass whether a slice is carved from a new chunk or allocated on its own. So they could not catch next-chunk sizing based on the used length instead of the chunk capacity. The new package-internal test starts from a partially used chunk and requests more than the remaining space. It checks that the replacement chunk grows from the previous capacity and that the slice is carved from it. --- tsc/internal/core/arena_internal_test.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 tsc/internal/core/arena_internal_test.go diff --git a/tsc/internal/core/arena_internal_test.go b/tsc/internal/core/arena_internal_test.go new file mode 100644 index 0000000000000..d7b7f7189e881 --- /dev/null +++ b/tsc/internal/core/arena_internal_test.go @@ -0,0 +1,17 @@ +package core + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func TestArenaNewSliceGrowsFromPartiallyUsedChunk(t *testing.T) { + t.Parallel() + arena := Arena[int64]{data: make([]int64, 1, 8)} + slice := arena.NewSlice(8) + assert.Equal(t, len(slice), 8) + assert.Equal(t, cap(slice), 8) + assert.Assert(t, cap(arena.data) >= 16, "next chunk capacity %d did not grow from previous capacity 8", cap(arena.data)) + assert.Equal(t, &arena.data[0], &slice[0], "slice was not carved from the new chunk") +} From 8480a060250e7ba39c0de842c931744330546907 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Thu, 29 Oct 2026 14:19:33 -0700 Subject: [PATCH 3/3] Remove arena growth white-box test The test asserted the arena's private chunk capacity, pinning one implementation detail rather than observable behavior. It adds maintenance cost without guarding anything users can see, so drop it. --- tsc/internal/core/arena_internal_test.go | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 tsc/internal/core/arena_internal_test.go diff --git a/tsc/internal/core/arena_internal_test.go b/tsc/internal/core/arena_internal_test.go deleted file mode 100644 index d7b7f7189e881..0000000000000 --- a/tsc/internal/core/arena_internal_test.go +++ /dev/null @@ -1,17 +0,0 @@ -package core - -import ( - "testing" - - "gotest.tools/v3/assert" -) - -func TestArenaNewSliceGrowsFromPartiallyUsedChunk(t *testing.T) { - t.Parallel() - arena := Arena[int64]{data: make([]int64, 1, 8)} - slice := arena.NewSlice(8) - assert.Equal(t, len(slice), 8) - assert.Equal(t, cap(slice), 8) - assert.Assert(t, cap(arena.data) >= 16, "next chunk capacity %d did not grow from previous capacity 8", cap(arena.data)) - assert.Equal(t, &arena.data[0], &slice[0], "slice was not carved from the new chunk") -}