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] }