github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/x/thrift/tbinary_pool.go (about) 1 // Copyright (c) 2022 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 // Package thrift code has been copied from https://github.com/m3dbx/thrift branch 0.9.3-patch 22 // This provides support for multiple byte slice pools of different capacities. 23 // see:https://github.com/m3dbx/thrift/blob/0.9.3-patch/lib/go/thrift/binary_protocol.go#L40 24 package thrift 25 26 import ( 27 "sort" 28 "sync" 29 ) 30 31 var ( 32 sliceCapacitiesForPools []int 33 bytesPools []*sync.Pool 34 ) 35 36 // SetMaxBytesPoolAlloc sets the capacities of byte slices that are pooled for binary thrift 37 // fields and must be called before any thrift binary protocols are used 38 // since it is a global and is not thread safe to edit. 39 func SetMaxBytesPoolAlloc(capacities ...int) { 40 initPools(capacities) 41 } 42 43 func init() { 44 initPools([]int{1024}) 45 } 46 47 func initPools(capacities []int) { 48 // Make a defensive copy. 49 sliceCapacitiesForPools = append([]int{}, capacities...) 50 51 sort.Ints(sliceCapacitiesForPools) 52 53 bytesPools = make([]*sync.Pool, 0) 54 for _, capacity := range sliceCapacitiesForPools { 55 bytesPools = append(bytesPools, newPool(capacity)) 56 } 57 } 58 59 func newPool(capacity int) *sync.Pool { 60 return &sync.Pool{ 61 New: func() interface{} { 62 element := bytesWrapperPool.Get().(*bytesWrapper) 63 element.value = make([]byte, capacity) 64 return element 65 }, 66 } 67 } 68 69 // BytesPoolPut is a public func to call to return pooled bytes to, each 70 // the capacity of BytesPoolAlloc. TBinaryProtocol.ReadBinary uses this pool 71 // to allocate from if the size of the bytes required to return is is equal or 72 // less than BytesPoolAlloc. 73 func BytesPoolPut(b []byte) bool { 74 for i, capacity := range sliceCapacitiesForPools { 75 if capacity == cap(b) { 76 element := bytesWrapperPool.Get().(*bytesWrapper) 77 element.value = b 78 bytesPools[i].Put(element) 79 return true 80 } 81 } 82 return false 83 } 84 85 // BytesPoolGet returns a pooled byte slice of capacity BytesPoolAlloc. 86 func BytesPoolGet(size int) []byte { 87 for i, capacity := range sliceCapacitiesForPools { 88 if size <= capacity { 89 element := bytesPools[i].Get().(*bytesWrapper) 90 result := element.value 91 element.value = nil 92 bytesWrapperPool.Put(element) 93 return result[:size] 94 } 95 } 96 97 return make([]byte, size) 98 } 99 100 // bytesWrapper is used to wrap a byte slice to avoid allocing a interface{} 101 // when wrapping a byte slice which is usually passed on the stack 102 type bytesWrapper struct { 103 value []byte 104 } 105 106 var bytesWrapperPool = sync.Pool{ 107 New: func() interface{} { 108 return &bytesWrapper{} 109 }, 110 }