Skip to content

Commit 111c51a

Browse files
Fix descriptor.Table buffer growth calc (#2311)
Signed-off-by: Edward McFarlane <emcfarlane@buf.build> Co-authored-by: Takeshi Yoneda <t.y.mathetake@gmail.com>
1 parent dc058c0 commit 111c51a

File tree

3 files changed

+118
-48
lines changed

3 files changed

+118
-48
lines changed

internal/descriptor/export_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package descriptor
2+
3+
// Masks returns the masks of the table for testing purposes.
4+
func Masks[Key ~int32, Item any](t *Table[Key, Item]) []uint64 {
5+
return t.masks
6+
}
7+
8+
// Items returns the items of the table for testing purposes.
9+
func Items[Key ~int32, Item any](t *Table[Key, Item]) []Item {
10+
return t.items
11+
}

internal/descriptor/table.go

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package descriptor
22

3-
import "math/bits"
3+
import (
4+
"math/bits"
5+
"slices"
6+
)
47

58
// Table is a data structure mapping 32 bit descriptor to items.
69
//
@@ -37,23 +40,13 @@ func (t *Table[Key, Item]) Len() (n int) {
3740
return n
3841
}
3942

40-
// grow ensures that t has enough room for n items, potentially reallocating the
41-
// internal buffers if their capacity was too small to hold this many items.
43+
// grow grows the table by n * 64 items.
4244
func (t *Table[Key, Item]) grow(n int) {
43-
// Round up to a multiple of 64 since this is the smallest increment due to
44-
// using 64 bits masks.
45-
n = (n*64 + 63) / 64
45+
total := len(t.masks) + n
46+
t.masks = slices.Grow(t.masks, n)[:total]
4647

47-
if n > len(t.masks) {
48-
masks := make([]uint64, n)
49-
copy(masks, t.masks)
50-
51-
items := make([]Item, n*64)
52-
copy(items, t.items)
53-
54-
t.masks = masks
55-
t.items = items
56-
}
48+
total = len(t.items) + n*64
49+
t.items = slices.Grow(t.items, n*64)[:total]
5750
}
5851

5952
// Insert inserts the given item to the table, returning the key that it is
@@ -78,13 +71,9 @@ insert:
7871
}
7972
}
8073

74+
// No free slot found, grow the table and retry.
8175
offset = len(t.masks)
82-
n := 2 * len(t.masks)
83-
if n == 0 {
84-
n = 1
85-
}
86-
87-
t.grow(n)
76+
t.grow(1)
8877
goto insert
8978
}
9079

@@ -109,10 +98,10 @@ func (t *Table[Key, Item]) InsertAt(item Item, key Key) bool {
10998
if key < 0 {
11099
return false
111100
}
112-
if diff := int(key) - t.Len(); diff > 0 {
101+
index := uint(key) / 64
102+
if diff := int(index) - len(t.masks) + 1; diff > 0 {
113103
t.grow(diff)
114104
}
115-
index := uint(key) / 64
116105
shift := uint(key) % 64
117106
t.masks[index] |= 1 << shift
118107
t.items[key] = item
@@ -124,7 +113,8 @@ func (t *Table[Key, Item]) Delete(key Key) {
124113
if key < 0 { // invalid key
125114
return
126115
}
127-
if index, shift := key/64, key%64; int(index) < len(t.masks) {
116+
if index := uint(key) / 64; int(index) < len(t.masks) {
117+
shift := uint(key) % 64
128118
mask := t.masks[index]
129119
if (mask & (1 << shift)) != 0 {
130120
var zero Item

internal/descriptor/table_test.go

Lines changed: 92 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,16 @@ package descriptor_test
33
import (
44
"testing"
55

6+
"github.com/tetratelabs/wazero/internal/descriptor"
67
"github.com/tetratelabs/wazero/internal/sys"
78
"github.com/tetratelabs/wazero/internal/testing/require"
89
)
910

1011
func TestFileTable(t *testing.T) {
1112
table := new(sys.FileTable)
1213

13-
if n := table.Len(); n != 0 {
14-
t.Errorf("new table is not empty: length=%d", n)
15-
}
14+
n := table.Len()
15+
require.Equal(t, 0, n, "new table is not empty: length=%d", n)
1616

1717
// The id field is used as a sentinel value.
1818
v0 := &sys.FileEntry{Name: "1"}
@@ -38,16 +38,12 @@ func TestFileTable(t *testing.T) {
3838
{key: k1, val: v1},
3939
{key: k2, val: v2},
4040
} {
41-
if v, ok := table.Lookup(lookup.key); !ok {
42-
t.Errorf("value not found for key '%v'", lookup.key)
43-
} else if v.Name != lookup.val.Name {
44-
t.Errorf("wrong value returned for key '%v': want=%v got=%v", lookup.key, lookup.val.Name, v.Name)
45-
}
41+
v, ok := table.Lookup(lookup.key)
42+
require.True(t, ok, "value not found for key '%v'", lookup.key)
43+
require.Equal(t, lookup.val.Name, v.Name, "wrong value returned for key '%v'", lookup.key)
4644
}
4745

48-
if n := table.Len(); n != 3 {
49-
t.Errorf("wrong table length: want=3 got=%d", n)
50-
}
46+
require.Equal(t, 3, table.Len(), "wrong table length: want=3 got=%d", table.Len())
5147

5248
k0Found := false
5349
k1Found := false
@@ -62,9 +58,7 @@ func TestFileTable(t *testing.T) {
6258
case k2:
6359
k2Found, want = true, v2
6460
}
65-
if v.Name != want.Name {
66-
t.Errorf("wrong value found ranging over '%v': want=%v got=%v", k, want.Name, v.Name)
67-
}
61+
require.Equal(t, want.Name, v.Name, "wrong value found ranging over table")
6862
return true
6963
})
7064

@@ -76,9 +70,7 @@ func TestFileTable(t *testing.T) {
7670
{key: k1, ok: k1Found},
7771
{key: k2, ok: k2Found},
7872
} {
79-
if !found.ok {
80-
t.Errorf("key not found while ranging over table: %v", found.key)
81-
}
73+
require.True(t, found.ok, "key not found while ranging over table: %v", found.key)
8274
}
8375

8476
for i, deletion := range []struct {
@@ -89,12 +81,10 @@ func TestFileTable(t *testing.T) {
8981
{key: k2},
9082
} {
9183
table.Delete(deletion.key)
92-
if _, ok := table.Lookup(deletion.key); ok {
93-
t.Errorf("item found after deletion of '%v'", deletion.key)
94-
}
95-
if n, want := table.Len(), 3-(i+1); n != want {
96-
t.Errorf("wrong table length after deletion: want=%d got=%d", want, n)
97-
}
84+
_, ok := table.Lookup(deletion.key)
85+
require.False(t, ok, "item found after deletion of '%v'", deletion.key)
86+
n, want := table.Len(), 3-(i+1)
87+
require.Equal(t, want, n, "wrong table length after deletion: want=%d got=%d", want, n)
9888
}
9989
}
10090

@@ -134,3 +124,82 @@ func BenchmarkFileTableLookup(b *testing.B) {
134124
b.Error("wrong file returned by lookup")
135125
}
136126
}
127+
128+
func Test_sizeOfTable(t *testing.T) {
129+
tests := []struct {
130+
name string
131+
operation func(*descriptor.Table[int32, string])
132+
expectedSize int
133+
}{
134+
{
135+
name: "empty table",
136+
operation: func(table *descriptor.Table[int32, string]) {},
137+
expectedSize: 0,
138+
},
139+
{
140+
name: "1 insert",
141+
operation: func(table *descriptor.Table[int32, string]) {
142+
table.Insert("a")
143+
},
144+
expectedSize: 1,
145+
},
146+
{
147+
name: "32 inserts",
148+
operation: func(table *descriptor.Table[int32, string]) {
149+
for i := 0; i < 32; i++ {
150+
table.Insert("a")
151+
}
152+
},
153+
expectedSize: 1,
154+
},
155+
{
156+
name: "257 inserts",
157+
operation: func(table *descriptor.Table[int32, string]) {
158+
for i := 0; i < 257; i++ {
159+
table.Insert("a")
160+
}
161+
},
162+
expectedSize: 5,
163+
},
164+
{
165+
name: "1 insert at 63",
166+
operation: func(table *descriptor.Table[int32, string]) {
167+
table.InsertAt("a", 63)
168+
},
169+
expectedSize: 1,
170+
},
171+
{
172+
name: "1 insert at 64",
173+
operation: func(table *descriptor.Table[int32, string]) {
174+
table.InsertAt("a", 64)
175+
},
176+
expectedSize: 2,
177+
},
178+
{
179+
name: "1 insert at 257",
180+
operation: func(table *descriptor.Table[int32, string]) {
181+
table.InsertAt("a", 257)
182+
},
183+
expectedSize: 5,
184+
},
185+
{
186+
name: "insert at until 320",
187+
operation: func(table *descriptor.Table[int32, string]) {
188+
for i := int32(0); i < 320; i++ {
189+
table.InsertAt("a", i)
190+
}
191+
},
192+
expectedSize: 5,
193+
},
194+
}
195+
for _, tt := range tests {
196+
tc := tt
197+
198+
t.Run(tc.name, func(t *testing.T) {
199+
table := new(descriptor.Table[int32, string])
200+
tc.operation(table)
201+
require.Equal(t, tc.expectedSize, len(descriptor.Masks(table)))
202+
require.Equal(t, tc.expectedSize*64, len(descriptor.Items(table)))
203+
})
204+
}
205+
}

0 commit comments

Comments
 (0)