From e4eab01148653bb0400d00a0ce60bad8de718694 Mon Sep 17 00:00:00 2001 From: Andy Walker Date: Fri, 25 Apr 2025 14:06:33 -0400 Subject: [PATCH 1/2] demonstrate that bits.Len64 passes tests --- log.go | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/log.go b/log.go index b21b946..9a3ce6d 100644 --- a/log.go +++ b/log.go @@ -1,6 +1,8 @@ package puddle -import "unsafe" +import ( + "math/bits" +) type ints interface { int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64 @@ -12,21 +14,5 @@ func log2Int[T ints](val T) uint8 { if val <= 0 { panic("log2 of non-positive number does not exist") } - - return log2IntRange(val, 0, uint8(8*unsafe.Sizeof(val))) -} - -func log2IntRange[T ints](val T, begin, end uint8) uint8 { - length := end - begin - if length == 1 { - return begin - } - - delim := begin + length/2 - mask := T(1) << delim - if mask > val { - return log2IntRange(val, begin, delim) - } else { - return log2IntRange(val, delim, end) - } + return uint8(bits.Len64(uint64(val)) - 1) } From 3f8c490739d3df345b61f9b353779faf0c4160f7 Mon Sep 17 00:00:00 2001 From: Andy Walker Date: Fri, 25 Apr 2025 14:07:26 -0400 Subject: [PATCH 2/2] replace custom log2 impl with bits.Len64 --- log.go | 18 ------------------ log_test.go | 49 ------------------------------------------------- pool.go | 7 +++++-- 3 files changed, 5 insertions(+), 69 deletions(-) delete mode 100644 log.go delete mode 100644 log_test.go diff --git a/log.go b/log.go deleted file mode 100644 index 9a3ce6d..0000000 --- a/log.go +++ /dev/null @@ -1,18 +0,0 @@ -package puddle - -import ( - "math/bits" -) - -type ints interface { - int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64 -} - -// log2Int returns log2 of an integer. This function panics if val < 0. For val -// == 0, returns 0. -func log2Int[T ints](val T) uint8 { - if val <= 0 { - panic("log2 of non-positive number does not exist") - } - return uint8(bits.Len64(uint64(val)) - 1) -} diff --git a/log_test.go b/log_test.go deleted file mode 100644 index d425313..0000000 --- a/log_test.go +++ /dev/null @@ -1,49 +0,0 @@ -package puddle - -import ( - "math" - "math/rand" - "testing" - "time" - - "github.com/stretchr/testify/require" -) - -func TestLog2Uint(t *testing.T) { - r := require.New(t) - - r.Equal(uint8(0), log2Int(1)) - r.Equal(uint8(0), log2Int[uint64](1)) - r.Equal(uint8(1), log2Int[uint32](2)) - r.Equal(uint8(7), log2Int[uint8](math.MaxUint8)) - r.Equal(uint8(15), log2Int[uint16](math.MaxUint16)) - r.Equal(uint8(31), log2Int[uint32](math.MaxUint32)) - r.Equal(uint8(63), log2Int[uint64](math.MaxUint64)) - - r.Panics(func() { log2Int[uint64](0) }) - r.Panics(func() { log2Int[int64](-1) }) -} - -func FuzzLog2Uint(f *testing.F) { - const cnt = 1000 - - rand := rand.New(rand.NewSource(time.Now().UnixNano())) - for i := 0; i < cnt; i++ { - val := uint64(rand.Int63()) - // val + 1 not to test val == 0. - f.Add(val + 1) - } - - f.Fuzz(func(t *testing.T, val uint64) { - var mx uint8 - for i := 63; i >= 0; i-- { - mask := uint64(1) << i - if mask&val != 0 { - mx = uint8(i) - break - } - } - - require.Equal(t, mx, log2Int(val)) - }) -} diff --git a/pool.go b/pool.go index c411d2f..4b8c361 100644 --- a/pool.go +++ b/pool.go @@ -3,6 +3,7 @@ package puddle import ( "context" "errors" + "math/bits" "sync" "sync/atomic" "time" @@ -543,12 +544,14 @@ func (p *Pool[T]) TryAcquire(ctx context.Context) (*Resource[T], error) { // TODO: Replace this with acquireSem.TryAcquireAll() if it gets to // upstream. https://github.com/golang/sync/pull/19 func acquireSemAll(sem *semaphore.Weighted, num int) int { + if num <= 0 { + panic("aquireSemAll: num <= 0") + } if sem.TryAcquire(int64(num)) { return num } - var acquired int - for i := int(log2Int(num)); i >= 0; i-- { + for i := bits.Len64(uint64(num)) - 1; i >= 0; i-- { val := 1 << i if sem.TryAcquire(int64(val)) { acquired += val