Files
tsnet-proxy/vendor/gvisor.dev/gvisor/pkg/buffer/chunk.go
2024-11-01 17:43:06 +00:00

114 lines
2.7 KiB
Go

// Copyright 2022 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package buffer
import (
"fmt"
"gvisor.dev/gvisor/pkg/bits"
"gvisor.dev/gvisor/pkg/sync"
)
const (
// This is log2(baseChunkSize). This number is used to calculate which pool
// to use for a payload size by right shifting the payload size by this
// number and passing the result to MostSignificantOne64.
baseChunkSizeLog2 = 6
// This is the size of the buffers in the first pool. Each subsequent pool
// creates payloads 2^(pool index) times larger than the first pool's
// payloads.
baseChunkSize = 1 << baseChunkSizeLog2 // 64
// MaxChunkSize is largest payload size that we pool. Payloads larger than
// this will be allocated from the heap and garbage collected as normal.
MaxChunkSize = baseChunkSize << (numPools - 1) // 64k
// The number of chunk pools we have for use.
numPools = 11
)
// chunkPools is a collection of pools for payloads of different sizes. The
// size of the payloads doubles in each successive pool.
var chunkPools [numPools]sync.Pool
func init() {
for i := 0; i < numPools; i++ {
chunkSize := baseChunkSize * (1 << i)
chunkPools[i].New = func() any {
return &chunk{
data: make([]byte, chunkSize),
}
}
}
}
// Precondition: 0 <= size <= maxChunkSize
func getChunkPool(size int) *sync.Pool {
idx := 0
if size > baseChunkSize {
idx = bits.MostSignificantOne64(uint64(size) >> baseChunkSizeLog2)
if size > 1<<(idx+baseChunkSizeLog2) {
idx++
}
}
if idx >= numPools {
panic(fmt.Sprintf("pool for chunk size %d does not exist", size))
}
return &chunkPools[idx]
}
// Chunk represents a slice of pooled memory.
//
// +stateify savable
type chunk struct {
chunkRefs
data []byte
}
func newChunk(size int) *chunk {
var c *chunk
if size > MaxChunkSize {
c = &chunk{
data: make([]byte, size),
}
} else {
pool := getChunkPool(size)
c = pool.Get().(*chunk)
clear(c.data)
}
c.InitRefs()
return c
}
func (c *chunk) destroy() {
if len(c.data) > MaxChunkSize {
c.data = nil
return
}
pool := getChunkPool(len(c.data))
pool.Put(c)
}
func (c *chunk) DecRef() {
c.chunkRefs.DecRef(c.destroy)
}
func (c *chunk) Clone() *chunk {
cpy := newChunk(len(c.data))
copy(cpy.data, c.data)
return cpy
}