package bytebufferpool

import (
	
	
	
)

const (
	minBitSize = 6 // 2**6=64 is a CPU cache line size
	steps      = 20

	minSize = 1 << minBitSize
	maxSize = 1 << (minBitSize + steps - 1)

	calibrateCallsThreshold = 42000
	maxPercentile           = 0.95
)

// Pool represents byte buffer pool.
//
// Distinct pools may be used for distinct types of byte buffers.
// Properly determined byte buffer types with their own pools may help reducing
// memory waste.
type Pool struct {
	calls       [steps]uint64
	calibrating uint64

	defaultSize uint64
	maxSize     uint64

	pool sync.Pool
}

var defaultPool Pool

// Get returns an empty byte buffer from the pool.
//
// Got byte buffer may be returned to the pool via Put call.
// This reduces the number of memory allocations required for byte buffer
// management.
func () *ByteBuffer { return defaultPool.Get() }

// Get returns new byte buffer with zero length.
//
// The byte buffer may be returned to the pool via Put after the use
// in order to minimize GC overhead.
func ( *Pool) () *ByteBuffer {
	 := .pool.Get()
	if  != nil {
		return .(*ByteBuffer)
	}
	return &ByteBuffer{
		B: make([]byte, 0, atomic.LoadUint64(&.defaultSize)),
	}
}

// Put returns byte buffer to the pool.
//
// ByteBuffer.B mustn't be touched after returning it to the pool.
// Otherwise data races will occur.
func ( *ByteBuffer) { defaultPool.Put() }

// Put releases byte buffer obtained via Get to the pool.
//
// The buffer mustn't be accessed after returning to the pool.
func ( *Pool) ( *ByteBuffer) {
	 := index(len(.B))

	if atomic.AddUint64(&.calls[], 1) > calibrateCallsThreshold {
		.calibrate()
	}

	 := int(atomic.LoadUint64(&.maxSize))
	if  == 0 || cap(.B) <=  {
		.Reset()
		.pool.Put()
	}
}

func ( *Pool) () {
	if !atomic.CompareAndSwapUint64(&.calibrating, 0, 1) {
		return
	}

	 := make(callSizes, 0, steps)
	var  uint64
	for  := uint64(0);  < steps; ++ {
		 := atomic.SwapUint64(&.calls[], 0)
		 += 
		 = append(, callSize{
			calls: ,
			size:  minSize << ,
		})
	}
	sort.Sort()

	 := [0].size
	 := 

	 := uint64(float64() * maxPercentile)
	 = 0
	for  := 0;  < steps; ++ {
		if  >  {
			break
		}
		 += [].calls
		 := [].size
		if  >  {
			 = 
		}
	}

	atomic.StoreUint64(&.defaultSize, )
	atomic.StoreUint64(&.maxSize, )

	atomic.StoreUint64(&.calibrating, 0)
}

type callSize struct {
	calls uint64
	size  uint64
}

type callSizes []callSize

func ( callSizes) () int {
	return len()
}

func ( callSizes) (,  int) bool {
	return [].calls > [].calls
}

func ( callSizes) (,  int) {
	[], [] = [], []
}

func index( int) int {
	--
	 >>= minBitSize
	 := 0
	for  > 0 {
		 >>= 1
		++
	}
	if  >= steps {
		 = steps - 1
	}
	return 
}