mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-04 00:02:25 -06:00 
			
		
		
		
	
		
			
	
	
		
			195 lines
		
	
	
	
		
			5.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			195 lines
		
	
	
	
		
			5.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * Copyright 2024 gRPC 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 mem
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import (
							 | 
						||
| 
								 | 
							
									"sort"
							 | 
						||
| 
								 | 
							
									"sync"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									"google.golang.org/grpc/internal"
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// BufferPool is a pool of buffers that can be shared and reused, resulting in
							 | 
						||
| 
								 | 
							
								// decreased memory allocation.
							 | 
						||
| 
								 | 
							
								type BufferPool interface {
							 | 
						||
| 
								 | 
							
									// Get returns a buffer with specified length from the pool.
							 | 
						||
| 
								 | 
							
									Get(length int) *[]byte
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Put returns a buffer to the pool.
							 | 
						||
| 
								 | 
							
									Put(*[]byte)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var defaultBufferPoolSizes = []int{
							 | 
						||
| 
								 | 
							
									256,
							 | 
						||
| 
								 | 
							
									4 << 10,  // 4KB (go page size)
							 | 
						||
| 
								 | 
							
									16 << 10, // 16KB (max HTTP/2 frame size used by gRPC)
							 | 
						||
| 
								 | 
							
									32 << 10, // 32KB (default buffer size for io.Copy)
							 | 
						||
| 
								 | 
							
									1 << 20,  // 1MB
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var defaultBufferPool BufferPool
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func init() {
							 | 
						||
| 
								 | 
							
									defaultBufferPool = NewTieredBufferPool(defaultBufferPoolSizes...)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									internal.SetDefaultBufferPoolForTesting = func(pool BufferPool) {
							 | 
						||
| 
								 | 
							
										defaultBufferPool = pool
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									internal.SetBufferPoolingThresholdForTesting = func(threshold int) {
							 | 
						||
| 
								 | 
							
										bufferPoolingThreshold = threshold
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// DefaultBufferPool returns the current default buffer pool. It is a BufferPool
							 | 
						||
| 
								 | 
							
								// created with NewBufferPool that uses a set of default sizes optimized for
							 | 
						||
| 
								 | 
							
								// expected workflows.
							 | 
						||
| 
								 | 
							
								func DefaultBufferPool() BufferPool {
							 | 
						||
| 
								 | 
							
									return defaultBufferPool
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// NewTieredBufferPool returns a BufferPool implementation that uses multiple
							 | 
						||
| 
								 | 
							
								// underlying pools of the given pool sizes.
							 | 
						||
| 
								 | 
							
								func NewTieredBufferPool(poolSizes ...int) BufferPool {
							 | 
						||
| 
								 | 
							
									sort.Ints(poolSizes)
							 | 
						||
| 
								 | 
							
									pools := make([]*sizedBufferPool, len(poolSizes))
							 | 
						||
| 
								 | 
							
									for i, s := range poolSizes {
							 | 
						||
| 
								 | 
							
										pools[i] = newSizedBufferPool(s)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return &tieredBufferPool{
							 | 
						||
| 
								 | 
							
										sizedPools: pools,
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// tieredBufferPool implements the BufferPool interface with multiple tiers of
							 | 
						||
| 
								 | 
							
								// buffer pools for different sizes of buffers.
							 | 
						||
| 
								 | 
							
								type tieredBufferPool struct {
							 | 
						||
| 
								 | 
							
									sizedPools   []*sizedBufferPool
							 | 
						||
| 
								 | 
							
									fallbackPool simpleBufferPool
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (p *tieredBufferPool) Get(size int) *[]byte {
							 | 
						||
| 
								 | 
							
									return p.getPool(size).Get(size)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (p *tieredBufferPool) Put(buf *[]byte) {
							 | 
						||
| 
								 | 
							
									p.getPool(cap(*buf)).Put(buf)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (p *tieredBufferPool) getPool(size int) BufferPool {
							 | 
						||
| 
								 | 
							
									poolIdx := sort.Search(len(p.sizedPools), func(i int) bool {
							 | 
						||
| 
								 | 
							
										return p.sizedPools[i].defaultSize >= size
							 | 
						||
| 
								 | 
							
									})
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if poolIdx == len(p.sizedPools) {
							 | 
						||
| 
								 | 
							
										return &p.fallbackPool
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return p.sizedPools[poolIdx]
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// sizedBufferPool is a BufferPool implementation that is optimized for specific
							 | 
						||
| 
								 | 
							
								// buffer sizes. For example, HTTP/2 frames within gRPC have a default max size
							 | 
						||
| 
								 | 
							
								// of 16kb and a sizedBufferPool can be configured to only return buffers with a
							 | 
						||
| 
								 | 
							
								// capacity of 16kb. Note that however it does not support returning larger
							 | 
						||
| 
								 | 
							
								// buffers and in fact panics if such a buffer is requested. Because of this,
							 | 
						||
| 
								 | 
							
								// this BufferPool implementation is not meant to be used on its own and rather
							 | 
						||
| 
								 | 
							
								// is intended to be embedded in a tieredBufferPool such that Get is only
							 | 
						||
| 
								 | 
							
								// invoked when the required size is smaller than or equal to defaultSize.
							 | 
						||
| 
								 | 
							
								type sizedBufferPool struct {
							 | 
						||
| 
								 | 
							
									pool        sync.Pool
							 | 
						||
| 
								 | 
							
									defaultSize int
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (p *sizedBufferPool) Get(size int) *[]byte {
							 | 
						||
| 
								 | 
							
									buf := p.pool.Get().(*[]byte)
							 | 
						||
| 
								 | 
							
									b := *buf
							 | 
						||
| 
								 | 
							
									clear(b[:cap(b)])
							 | 
						||
| 
								 | 
							
									*buf = b[:size]
							 | 
						||
| 
								 | 
							
									return buf
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (p *sizedBufferPool) Put(buf *[]byte) {
							 | 
						||
| 
								 | 
							
									if cap(*buf) < p.defaultSize {
							 | 
						||
| 
								 | 
							
										// Ignore buffers that are too small to fit in the pool. Otherwise, when
							 | 
						||
| 
								 | 
							
										// Get is called it will panic as it tries to index outside the bounds
							 | 
						||
| 
								 | 
							
										// of the buffer.
							 | 
						||
| 
								 | 
							
										return
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									p.pool.Put(buf)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func newSizedBufferPool(size int) *sizedBufferPool {
							 | 
						||
| 
								 | 
							
									return &sizedBufferPool{
							 | 
						||
| 
								 | 
							
										pool: sync.Pool{
							 | 
						||
| 
								 | 
							
											New: func() any {
							 | 
						||
| 
								 | 
							
												buf := make([]byte, size)
							 | 
						||
| 
								 | 
							
												return &buf
							 | 
						||
| 
								 | 
							
											},
							 | 
						||
| 
								 | 
							
										},
							 | 
						||
| 
								 | 
							
										defaultSize: size,
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var _ BufferPool = (*simpleBufferPool)(nil)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// simpleBufferPool is an implementation of the BufferPool interface that
							 | 
						||
| 
								 | 
							
								// attempts to pool buffers with a sync.Pool. When Get is invoked, it tries to
							 | 
						||
| 
								 | 
							
								// acquire a buffer from the pool but if that buffer is too small, it returns it
							 | 
						||
| 
								 | 
							
								// to the pool and creates a new one.
							 | 
						||
| 
								 | 
							
								type simpleBufferPool struct {
							 | 
						||
| 
								 | 
							
									pool sync.Pool
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (p *simpleBufferPool) Get(size int) *[]byte {
							 | 
						||
| 
								 | 
							
									bs, ok := p.pool.Get().(*[]byte)
							 | 
						||
| 
								 | 
							
									if ok && cap(*bs) >= size {
							 | 
						||
| 
								 | 
							
										*bs = (*bs)[:size]
							 | 
						||
| 
								 | 
							
										return bs
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// A buffer was pulled from the pool, but it is too small. Put it back in
							 | 
						||
| 
								 | 
							
									// the pool and create one large enough.
							 | 
						||
| 
								 | 
							
									if ok {
							 | 
						||
| 
								 | 
							
										p.pool.Put(bs)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									b := make([]byte, size)
							 | 
						||
| 
								 | 
							
									return &b
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (p *simpleBufferPool) Put(buf *[]byte) {
							 | 
						||
| 
								 | 
							
									p.pool.Put(buf)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var _ BufferPool = NopBufferPool{}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// NopBufferPool is a buffer pool that returns new buffers without pooling.
							 | 
						||
| 
								 | 
							
								type NopBufferPool struct{}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Get returns a buffer with specified length from the pool.
							 | 
						||
| 
								 | 
							
								func (NopBufferPool) Get(length int) *[]byte {
							 | 
						||
| 
								 | 
							
									b := make([]byte, length)
							 | 
						||
| 
								 | 
							
									return &b
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Put returns a buffer to the pool.
							 | 
						||
| 
								 | 
							
								func (NopBufferPool) Put(*[]byte) {
							 | 
						||
| 
								 | 
							
								}
							 |