Skip to content

Commit 5e1d256

Browse files
committed
refactor: return errors instead of panics
It is clearer and simpler for consumers if we use the type system instead of relying on them for extra checks. Fixes GHSA-2h6c-j3gf-xp9r
1 parent 2b62481 commit 5e1d256

File tree

2 files changed

+58
-23
lines changed

2 files changed

+58
-23
lines changed

bitfield.go

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,33 @@ package bitfield
33
// NOTE: Don't bother replacing the divisions/modulo with shifts/ands, go is smart.
44

55
import (
6+
"fmt"
67
"math/bits"
78
)
89

910
// NewBitfield creates a new fixed-sized Bitfield (allocated up-front).
10-
//
11-
// Panics if size is not a multiple of 8.
12-
func NewBitfield(size int) Bitfield {
11+
func NewBitfield(size int) (Bitfield, error) {
12+
if size < 0 {
13+
return nil, fmt.Errorf("bitfield size must be positive; got %d", size)
14+
}
1315
if size%8 != 0 {
14-
panic("Bitfield size must be a multiple of 8")
16+
return nil, fmt.Errorf("bitfield size must be a multiple of 8; got %d", size)
1517
}
16-
return make([]byte, size/8)
18+
return make([]byte, size/8), nil
1719
}
1820

1921
// FromBytes constructs a new bitfield from a serialized bitfield.
20-
func FromBytes(size int, bits []byte) Bitfield {
21-
bf := NewBitfield(size)
22+
func FromBytes(size int, bits []byte) (Bitfield, error) {
23+
bf, err := NewBitfield(size)
24+
if err != nil {
25+
return nil, err
26+
}
2227
start := len(bf) - len(bits)
2328
if start < 0 {
24-
panic("bitfield too small")
29+
return nil, fmt.Errorf("bitfield too small: got %d; need %d", size, len(bits)*8)
2530
}
2631
copy(bf[start:], bits)
27-
return bf
32+
return bf, nil
2833
}
2934

3035
func (bf Bitfield) offset(i int) (uint, uint8) {

bitfield_test.go

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ import (
99
)
1010

1111
func TestExhaustive24(t *testing.T) {
12-
bf := NewBitfield(24)
12+
bf, err := NewBitfield(24)
13+
assertNoError(t, err)
1314
max := 1 << 24
1415

1516
bint := new(big.Int)
@@ -58,7 +59,8 @@ func TestExhaustive24(t *testing.T) {
5859
}
5960

6061
func TestBitfield(t *testing.T) {
61-
bf := NewBitfield(128)
62+
bf, err := NewBitfield(128)
63+
assertNoError(t, err)
6264
if bf.OnesBefore(20) != 0 {
6365
t.Fatal("expected no bits set")
6466
}
@@ -91,10 +93,20 @@ func TestBitfield(t *testing.T) {
9193
}
9294
}
9395

96+
func TestBadSizeFails(t *testing.T) {
97+
for _, size := range [...]int{-8, 2, 1337, -3} {
98+
_, err := NewBitfield(size)
99+
if err == nil {
100+
t.Fatalf("missing error for %d sized bitfield", size)
101+
}
102+
}
103+
}
104+
94105
var benchmarkSize = 512
95106

96107
func BenchmarkBitfield(t *testing.B) {
97-
bf := NewBitfield(benchmarkSize)
108+
bf, err := NewBitfield(benchmarkSize)
109+
assertNoError(t, err)
98110
t.ResetTimer()
99111
for i := 0; i < t.N; i++ {
100112
if bf.Bit(i % benchmarkSize) {
@@ -123,13 +135,14 @@ func BenchmarkBitfield(t *testing.B) {
123135
}
124136
}
125137

126-
func BenchmarkOnes(t *testing.B) {
127-
bf := NewBitfield(benchmarkSize)
128-
t.ResetTimer()
129-
for i := 0; i < t.N; i++ {
138+
func BenchmarkOnes(b *testing.B) {
139+
bf, err := NewBitfield(benchmarkSize)
140+
assertNoError(b, err)
141+
b.ResetTimer()
142+
for i := 0; i < b.N; i++ {
130143
for j := 0; j*4 < benchmarkSize; j++ {
131144
if bf.Ones() != j {
132-
t.Fatal("bad", i)
145+
b.Fatal("bad", i)
133146
}
134147
bf.SetBit(j * 4)
135148
}
@@ -139,14 +152,16 @@ func BenchmarkOnes(t *testing.B) {
139152
}
140153
}
141154

142-
func BenchmarkBytes(t *testing.B) {
143-
bfa := NewBitfield(211)
144-
bfb := NewBitfield(211)
145-
for j := 0; j*4 < 211; j++ {
155+
func BenchmarkBytes(b *testing.B) {
156+
bfa, err := NewBitfield(216)
157+
assertNoError(b, err)
158+
bfb, err := NewBitfield(216)
159+
assertNoError(b, err)
160+
for j := 0; j*4 < 216; j++ {
146161
bfa.SetBit(j * 4)
147162
}
148-
t.ResetTimer()
149-
for i := 0; i < t.N; i++ {
163+
b.ResetTimer()
164+
for i := 0; i < b.N; i++ {
150165
bfb.SetBytes(bfa.Bytes())
151166
}
152167
}
@@ -180,3 +195,18 @@ func BenchmarkBigInt(t *testing.B) {
180195
}
181196
}
182197
}
198+
199+
func FuzzFromBytes(f *testing.F) {
200+
f.Fuzz(func(_ *testing.T, size int, bytes []byte) {
201+
if size > 1<<20 { // We relly on consumers for limit checks, hopefully they understand that a New... factory allocates memory.
202+
return
203+
}
204+
FromBytes(size, bytes)
205+
})
206+
}
207+
208+
func assertNoError(t testing.TB, e error) {
209+
if e != nil {
210+
t.Fatal(e)
211+
}
212+
}

0 commit comments

Comments
 (0)