123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- /*
- Copyright 2020 The Kubernetes 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 value
- // Allocator provides a value object allocation strategy.
- // Value objects can be allocated by passing an allocator to the "Using"
- // receiver functions on the value interfaces, e.g. Map.ZipUsing(allocator, ...).
- // Value objects returned from "Using" functions should be given back to the allocator
- // once longer needed by calling Allocator.Free(Value).
- type Allocator interface {
- // Free gives the allocator back any value objects returned by the "Using"
- // receiver functions on the value interfaces.
- // interface{} may be any of: Value, Map, List or Range.
- Free(interface{})
- // The unexported functions are for "Using" receiver functions of the value types
- // to request what they need from the allocator.
- allocValueUnstructured() *valueUnstructured
- allocListUnstructuredRange() *listUnstructuredRange
- allocValueReflect() *valueReflect
- allocMapReflect() *mapReflect
- allocStructReflect() *structReflect
- allocListReflect() *listReflect
- allocListReflectRange() *listReflectRange
- }
- // HeapAllocator simply allocates objects to the heap. It is the default
- // allocator used receiver functions on the value interfaces that do not accept
- // an allocator and should be used whenever allocating objects that will not
- // be given back to an allocator by calling Allocator.Free(Value).
- var HeapAllocator = &heapAllocator{}
- type heapAllocator struct{}
- func (p *heapAllocator) allocValueUnstructured() *valueUnstructured {
- return &valueUnstructured{}
- }
- func (p *heapAllocator) allocListUnstructuredRange() *listUnstructuredRange {
- return &listUnstructuredRange{vv: &valueUnstructured{}}
- }
- func (p *heapAllocator) allocValueReflect() *valueReflect {
- return &valueReflect{}
- }
- func (p *heapAllocator) allocStructReflect() *structReflect {
- return &structReflect{}
- }
- func (p *heapAllocator) allocMapReflect() *mapReflect {
- return &mapReflect{}
- }
- func (p *heapAllocator) allocListReflect() *listReflect {
- return &listReflect{}
- }
- func (p *heapAllocator) allocListReflectRange() *listReflectRange {
- return &listReflectRange{vr: &valueReflect{}}
- }
- func (p *heapAllocator) Free(_ interface{}) {}
- // NewFreelistAllocator creates freelist based allocator.
- // This allocator provides fast allocation and freeing of short lived value objects.
- //
- // The freelists are bounded in size by freelistMaxSize. If more than this amount of value objects is
- // allocated at once, the excess will be returned to the heap for garbage collection when freed.
- //
- // This allocator is unsafe and must not be accessed concurrently by goroutines.
- //
- // This allocator works well for traversal of value data trees. Typical usage is to acquire
- // a freelist at the beginning of the traversal and use it through out
- // for all temporary value access.
- func NewFreelistAllocator() Allocator {
- return &freelistAllocator{
- valueUnstructured: &freelist{new: func() interface{} {
- return &valueUnstructured{}
- }},
- listUnstructuredRange: &freelist{new: func() interface{} {
- return &listUnstructuredRange{vv: &valueUnstructured{}}
- }},
- valueReflect: &freelist{new: func() interface{} {
- return &valueReflect{}
- }},
- mapReflect: &freelist{new: func() interface{} {
- return &mapReflect{}
- }},
- structReflect: &freelist{new: func() interface{} {
- return &structReflect{}
- }},
- listReflect: &freelist{new: func() interface{} {
- return &listReflect{}
- }},
- listReflectRange: &freelist{new: func() interface{} {
- return &listReflectRange{vr: &valueReflect{}}
- }},
- }
- }
- // Bound memory usage of freelists. This prevents the processing of very large lists from leaking memory.
- // This limit is large enough for endpoints objects containing 1000 IP address entries. Freed objects
- // that don't fit into the freelist are orphaned on the heap to be garbage collected.
- const freelistMaxSize = 1000
- type freelistAllocator struct {
- valueUnstructured *freelist
- listUnstructuredRange *freelist
- valueReflect *freelist
- mapReflect *freelist
- structReflect *freelist
- listReflect *freelist
- listReflectRange *freelist
- }
- type freelist struct {
- list []interface{}
- new func() interface{}
- }
- func (f *freelist) allocate() interface{} {
- var w2 interface{}
- if n := len(f.list); n > 0 {
- w2, f.list = f.list[n-1], f.list[:n-1]
- } else {
- w2 = f.new()
- }
- return w2
- }
- func (f *freelist) free(v interface{}) {
- if len(f.list) < freelistMaxSize {
- f.list = append(f.list, v)
- }
- }
- func (w *freelistAllocator) Free(value interface{}) {
- switch v := value.(type) {
- case *valueUnstructured:
- v.Value = nil // don't hold references to unstructured objects
- w.valueUnstructured.free(v)
- case *listUnstructuredRange:
- v.vv.Value = nil // don't hold references to unstructured objects
- w.listUnstructuredRange.free(v)
- case *valueReflect:
- v.ParentMapKey = nil
- v.ParentMap = nil
- w.valueReflect.free(v)
- case *mapReflect:
- w.mapReflect.free(v)
- case *structReflect:
- w.structReflect.free(v)
- case *listReflect:
- w.listReflect.free(v)
- case *listReflectRange:
- v.vr.ParentMapKey = nil
- v.vr.ParentMap = nil
- w.listReflectRange.free(v)
- }
- }
- func (w *freelistAllocator) allocValueUnstructured() *valueUnstructured {
- return w.valueUnstructured.allocate().(*valueUnstructured)
- }
- func (w *freelistAllocator) allocListUnstructuredRange() *listUnstructuredRange {
- return w.listUnstructuredRange.allocate().(*listUnstructuredRange)
- }
- func (w *freelistAllocator) allocValueReflect() *valueReflect {
- return w.valueReflect.allocate().(*valueReflect)
- }
- func (w *freelistAllocator) allocStructReflect() *structReflect {
- return w.structReflect.allocate().(*structReflect)
- }
- func (w *freelistAllocator) allocMapReflect() *mapReflect {
- return w.mapReflect.allocate().(*mapReflect)
- }
- func (w *freelistAllocator) allocListReflect() *listReflect {
- return w.listReflect.allocate().(*listReflect)
- }
- func (w *freelistAllocator) allocListReflectRange() *listReflectRange {
- return w.listReflectRange.allocate().(*listReflectRange)
- }
|