k8s.io/kube-openapi@v0.0.0-20240228011516-70dd3763d340/pkg/internal/third_party/go-json-experiment/json/pools.go (about) 1 // Copyright 2020 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package json 6 7 import ( 8 "bytes" 9 "io" 10 "math/bits" 11 "sort" 12 "sync" 13 ) 14 15 // TODO(https://go.dev/issue/47657): Use sync.PoolOf. 16 17 var ( 18 // This owns the internal buffer since there is no io.Writer to output to. 19 // Since the buffer can get arbitrarily large in normal usage, 20 // there is statistical tracking logic to determine whether to recycle 21 // the internal buffer or not based on a history of utilization. 22 bufferedEncoderPool = &sync.Pool{New: func() any { return new(Encoder) }} 23 24 // This owns the internal buffer, but it is only used to temporarily store 25 // buffered JSON before flushing it to the underlying io.Writer. 26 // In a sufficiently efficient streaming mode, we do not expect the buffer 27 // to grow arbitrarily large. Thus, we avoid recycling large buffers. 28 streamingEncoderPool = &sync.Pool{New: func() any { return new(Encoder) }} 29 30 // This does not own the internal buffer since 31 // it is taken directly from the provided bytes.Buffer. 32 bytesBufferEncoderPool = &sync.Pool{New: func() any { return new(Encoder) }} 33 ) 34 35 // bufferStatistics is statistics to track buffer utilization. 36 // It is used to determine whether to recycle a buffer or not 37 // to avoid https://go.dev/issue/23199. 38 type bufferStatistics struct { 39 strikes int // number of times the buffer was under-utilized 40 prevLen int // length of previous buffer 41 } 42 43 func getBufferedEncoder(o EncodeOptions) *Encoder { 44 e := bufferedEncoderPool.Get().(*Encoder) 45 if e.buf == nil { 46 // Round up to nearest 2ⁿ to make best use of malloc size classes. 47 // See runtime/sizeclasses.go on Go1.15. 48 // Logical OR with 63 to ensure 64 as the minimum buffer size. 49 n := 1 << bits.Len(uint(e.bufStats.prevLen|63)) 50 e.buf = make([]byte, 0, n) 51 } 52 e.reset(e.buf[:0], nil, o) 53 return e 54 } 55 func putBufferedEncoder(e *Encoder) { 56 // Recycle large buffers only if sufficiently utilized. 57 // If a buffer is under-utilized enough times sequentially, 58 // then it is discarded, ensuring that a single large buffer 59 // won't be kept alive by a continuous stream of small usages. 60 // 61 // The worst case utilization is computed as: 62 // MIN_UTILIZATION_THRESHOLD / (1 + MAX_NUM_STRIKES) 63 // 64 // For the constants chosen below, this is (25%)/(1+4) ⇒ 5%. 65 // This may seem low, but it ensures a lower bound on 66 // the absolute worst-case utilization. Without this check, 67 // this would be theoretically 0%, which is infinitely worse. 68 // 69 // See https://go.dev/issue/27735. 70 switch { 71 case cap(e.buf) <= 4<<10: // always recycle buffers smaller than 4KiB 72 e.bufStats.strikes = 0 73 case cap(e.buf)/4 <= len(e.buf): // at least 25% utilization 74 e.bufStats.strikes = 0 75 case e.bufStats.strikes < 4: // at most 4 strikes 76 e.bufStats.strikes++ 77 default: // discard the buffer; too large and too often under-utilized 78 e.bufStats.strikes = 0 79 e.bufStats.prevLen = len(e.buf) // heuristic for size to allocate next time 80 e.buf = nil 81 } 82 bufferedEncoderPool.Put(e) 83 } 84 85 func getStreamingEncoder(w io.Writer, o EncodeOptions) *Encoder { 86 if _, ok := w.(*bytes.Buffer); ok { 87 e := bytesBufferEncoderPool.Get().(*Encoder) 88 e.reset(nil, w, o) // buffer taken from bytes.Buffer 89 return e 90 } else { 91 e := streamingEncoderPool.Get().(*Encoder) 92 e.reset(e.buf[:0], w, o) // preserve existing buffer 93 return e 94 } 95 } 96 func putStreamingEncoder(e *Encoder) { 97 if _, ok := e.wr.(*bytes.Buffer); ok { 98 bytesBufferEncoderPool.Put(e) 99 } else { 100 if cap(e.buf) > 64<<10 { 101 e.buf = nil // avoid pinning arbitrarily large amounts of memory 102 } 103 streamingEncoderPool.Put(e) 104 } 105 } 106 107 var ( 108 // This does not own the internal buffer since it is externally provided. 109 bufferedDecoderPool = &sync.Pool{New: func() any { return new(Decoder) }} 110 111 // This owns the internal buffer, but it is only used to temporarily store 112 // buffered JSON fetched from the underlying io.Reader. 113 // In a sufficiently efficient streaming mode, we do not expect the buffer 114 // to grow arbitrarily large. Thus, we avoid recycling large buffers. 115 streamingDecoderPool = &sync.Pool{New: func() any { return new(Decoder) }} 116 117 // This does not own the internal buffer since 118 // it is taken directly from the provided bytes.Buffer. 119 bytesBufferDecoderPool = bufferedDecoderPool 120 ) 121 122 func getBufferedDecoder(b []byte, o DecodeOptions) *Decoder { 123 d := bufferedDecoderPool.Get().(*Decoder) 124 d.reset(b, nil, o) 125 return d 126 } 127 func putBufferedDecoder(d *Decoder) { 128 bufferedDecoderPool.Put(d) 129 } 130 131 func getStreamingDecoder(r io.Reader, o DecodeOptions) *Decoder { 132 if _, ok := r.(*bytes.Buffer); ok { 133 d := bytesBufferDecoderPool.Get().(*Decoder) 134 d.reset(nil, r, o) // buffer taken from bytes.Buffer 135 return d 136 } else { 137 d := streamingDecoderPool.Get().(*Decoder) 138 d.reset(d.buf[:0], r, o) // preserve existing buffer 139 return d 140 } 141 } 142 func putStreamingDecoder(d *Decoder) { 143 if _, ok := d.rd.(*bytes.Buffer); ok { 144 bytesBufferDecoderPool.Put(d) 145 } else { 146 if cap(d.buf) > 64<<10 { 147 d.buf = nil // avoid pinning arbitrarily large amounts of memory 148 } 149 streamingDecoderPool.Put(d) 150 } 151 } 152 153 var stringsPools = &sync.Pool{New: func() any { return new(stringSlice) }} 154 155 type stringSlice []string 156 157 // getStrings returns a non-nil pointer to a slice with length n. 158 func getStrings(n int) *stringSlice { 159 s := stringsPools.Get().(*stringSlice) 160 if cap(*s) < n { 161 *s = make([]string, n) 162 } 163 *s = (*s)[:n] 164 return s 165 } 166 167 func putStrings(s *stringSlice) { 168 if cap(*s) > 1<<10 { 169 *s = nil // avoid pinning arbitrarily large amounts of memory 170 } 171 stringsPools.Put(s) 172 } 173 174 // Sort sorts the string slice according to RFC 8785, section 3.2.3. 175 func (ss *stringSlice) Sort() { 176 // TODO(https://go.dev/issue/47619): Use slices.SortFunc instead. 177 sort.Sort(ss) 178 } 179 180 func (ss *stringSlice) Len() int { return len(*ss) } 181 func (ss *stringSlice) Less(i, j int) bool { return lessUTF16((*ss)[i], (*ss)[j]) } 182 func (ss *stringSlice) Swap(i, j int) { (*ss)[i], (*ss)[j] = (*ss)[j], (*ss)[i] }