package bufpool

import (
	
	
)

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

	minSize     = 1 << minBitSize               // 64 bytes
	maxSize     = 1 << (minBitSize + steps - 1) // 32 mb
	maxPoolSize = maxSize << 1                  // 64 mb

	defaultServePctile      = 0.95
	calibrateCallsThreshold = 42000
	defaultSize             = 4096
)

// Pool represents byte buffer pool.
//
// Different pools should be used for different usage patterns to achieve better
// performance and lower memory usage.
type Pool struct {
	calls       [steps]uint32
	calibrating uint32

	ServePctile float64 // default is 0.95
	serveSize   uint32
}

func ( *Pool) () int {
	 := atomic.LoadUint32(&.serveSize)
	if  > 0 {
		return int()
	}

	for  := 0;  < len(.calls); ++ {
		 := atomic.LoadUint32(&.calls[])
		if  > 10 {
			 := indexSize()
			atomic.CompareAndSwapUint32(&.serveSize, 0, uint32())
			return 
		}
	}

	return defaultSize
}

// Get returns an empty buffer from the pool. Returned buffer capacity
// is determined by accumulated usage stats and changes over time.
//
// The buffer may be returned to the pool using Put or retained for further
// usage. In latter case buffer length must be updated using UpdateLen.
func ( *Pool) () *Buffer {
	 := Get(.getServeSize())
	.Reset()
	return 
}

// New returns an empty buffer bypassing the pool. Returned buffer capacity
// is determined by accumulated usage stats and changes over time.
func ( *Pool) () *Buffer {
	return NewBuffer(make([]byte, 0, .getServeSize()))
}

// Put returns buffer to the pool.
func ( *Pool) ( *Buffer) {
	 := .Len()
	if  == 0 {
		 = .Cap()
	}

	.UpdateLen()

	// Always put buf to the pool.
	Put()
}

// UpdateLen updates stats about buffer length.
func ( *Pool) ( int) {
	 := index()
	if atomic.AddUint32(&.calls[], 1) > calibrateCallsThreshold {
		.calibrate()
	}
}

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

	var  uint64
	var  [steps]uint32

	for  := 0;  < len(.calls); ++ {
		 := atomic.SwapUint32(&.calls[], 0)
		[] = 
		 += uint64()
	}

	 := uint64(float64() * .getServePctile())
	var  int

	 = 0
	for ,  := range & {
		 += uint64()

		if  == 0 &&  >=  {
			 = indexSize()
			break
		}
	}

	atomic.StoreUint32(&.serveSize, uint32())
	atomic.StoreUint32(&.calibrating, 0)
}

func ( *Pool) () float64 {
	if .ServePctile > 0 {
		return .ServePctile
	}
	return defaultServePctile
}

func ( int) int {
	if  == 0 {
		return 0
	}
	 := bits.Len32(uint32(( - 1) >> minBitSize))
	if  >= steps {
		 = steps - 1
	}
	return 
}

func ( int) int {
	 := index()
	if  == 0 ||  == indexSize() {
		return 
	}
	return  - 1
}

func ( int) int {
	return minSize << uint()
}