pool.go 2.68 KB
Newer Older
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
1 2 3
// Package mpool provides a sync.Pool equivalent that buckets incoming
// requests to one of 32 sub-pools, one for each power of 2, 0-32.
//
Steven Allen's avatar
Steven Allen committed
4
//	import "github.com/libp2p/go-msgio/mpool"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
//	var p mpool.Pool
//
//	small := make([]byte, 1024)
//	large := make([]byte, 4194304)
//	p.Put(1024, small)
//	p.Put(4194304, large)
//
//	small2 := p.Get(1024).([]byte)
//	large2 := p.Get(4194304).([]byte)
//	fmt.Println("small2 len:", len(small2))
//	fmt.Println("large2 len:", len(large2))
//
//	// Output:
//	// small2 len: 1024
//	// large2 len: 4194304
//
package mpool

import (
	"fmt"
	"sync"
)

// ByteSlicePool is a static Pool for reusing byteslices of various sizes.
29 30
var ByteSlicePool = &Pool{
	New: func(length int) interface{} {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
31
		return make([]byte, length)
32
	},
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
33 34 35 36 37 38 39 40
}

// MaxLength is the maximum length of an element that can be added to the Pool.
const MaxLength = 1 << 32

// Pool is a pool to handle cases of reusing elements of varying sizes.
// It maintains up to  32 internal pools, for each power of 2 in 0-32.
type Pool struct {
Steven Allen's avatar
Steven Allen committed
41
	pools [32]sync.Pool // a list of singlePools
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
42 43 44 45 46 47 48 49 50

	// New is a function that constructs a new element in the pool, with given len
	New func(len int) interface{}
}

func (p *Pool) getPool(idx uint32) *sync.Pool {
	if idx > uint32(len(p.pools)) {
		panic(fmt.Errorf("index too large: %d", idx))
	}
Steven Allen's avatar
Steven Allen committed
51
	return &p.pools[idx]
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
52 53 54 55 56 57 58 59 60 61
}

// Get selects an arbitrary item from the Pool, removes it from the Pool,
// and returns it to the caller. Get may choose to ignore the pool and
// treat it as empty. Callers should not assume any relation between values
// passed to Put and the values returned by Get.
//
// If Get would otherwise return nil and p.New is non-nil, Get returns the
// result of calling p.New.
func (p *Pool) Get(length uint32) interface{} {
62
	idx := nextPowerOfTwo(length)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
63
	sp := p.getPool(idx)
64
	// fmt.Printf("Get(%d) idx(%d)\n", length, idx)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
65 66 67 68 69 70 71 72 73
	val := sp.Get()
	if val == nil && p.New != nil {
		val = p.New(0x1 << idx)
	}
	return val
}

// Put adds x to the pool.
func (p *Pool) Put(length uint32, val interface{}) {
74 75
	idx := prevPowerOfTwo(length)
	// fmt.Printf("Put(%d, -) idx(%d)\n", length, idx)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
76 77 78 79
	sp := p.getPool(idx)
	sp.Put(val)
}

80 81 82 83 84 85 86 87 88 89 90 91 92
func nextPowerOfTwo(v uint32) uint32 {
	// fmt.Printf("nextPowerOfTwo(%d) ", v)
	v--
	v |= v >> 1
	v |= v >> 2
	v |= v >> 4
	v |= v >> 8
	v |= v >> 16
	v++

	// fmt.Printf("-> %d", v)

	i := uint32(0)
Steven Allen's avatar
Steven Allen committed
93
	for ; v > 1; i++ {
94
		v = v >> 1
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
95 96
	}

97 98
	// fmt.Printf("-> %d\n", i)
	return i
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
99 100
}

101 102 103 104 105 106 107 108
func prevPowerOfTwo(num uint32) uint32 {
	next := nextPowerOfTwo(num)
	// fmt.Printf("prevPowerOfTwo(%d) next: %d", num, next)
	switch {
	case num == (1 << next): // num is a power of 2
	case next == 0:
	default:
		next = next - 1 // smaller
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
109
	}
110 111
	// fmt.Printf(" = %d\n", next)
	return next
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
112
}