mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-10-26 19:15:27 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			271 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			271 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| /*
 | |
| Copyright 2019 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
 | |
| 
 | |
| import (
 | |
| 	"sort"
 | |
| )
 | |
| 
 | |
| // Map represents a Map or go structure.
 | |
| type Map interface {
 | |
| 	// Set changes or set the value of the given key.
 | |
| 	Set(key string, val Value)
 | |
| 	// Get returns the value for the given key, if present, or (nil, false) otherwise.
 | |
| 	Get(key string) (Value, bool)
 | |
| 	// GetUsing uses the provided allocator and returns the value for the given key,
 | |
| 	// if present, or (nil, false) otherwise.
 | |
| 	// The returned Value should be given back to the Allocator when no longer needed
 | |
| 	// by calling Allocator.Free(Value).
 | |
| 	GetUsing(a Allocator, key string) (Value, bool)
 | |
| 	// Has returns true if the key is present, or false otherwise.
 | |
| 	Has(key string) bool
 | |
| 	// Delete removes the key from the map.
 | |
| 	Delete(key string)
 | |
| 	// Equals compares the two maps, and return true if they are the same, false otherwise.
 | |
| 	// Implementations can use MapEquals as a general implementation for this methods.
 | |
| 	Equals(other Map) bool
 | |
| 	// EqualsUsing uses the provided allocator and compares the two maps, and return true if
 | |
| 	// they are the same, false otherwise. Implementations can use MapEqualsUsing as a general
 | |
| 	// implementation for this methods.
 | |
| 	EqualsUsing(a Allocator, other Map) bool
 | |
| 	// Iterate runs the given function for each key/value in the
 | |
| 	// map. Returning false in the closure prematurely stops the
 | |
| 	// iteration.
 | |
| 	Iterate(func(key string, value Value) bool) bool
 | |
| 	// IterateUsing uses the provided allocator and runs the given function for each key/value
 | |
| 	// in the map. Returning false in the closure prematurely stops the iteration.
 | |
| 	IterateUsing(Allocator, func(key string, value Value) bool) bool
 | |
| 	// Length returns the number of items in the map.
 | |
| 	Length() int
 | |
| 	// Empty returns true if the map is empty.
 | |
| 	Empty() bool
 | |
| 	// Zip iterates over the entries of two maps together. If both maps contain a value for a given key, fn is called
 | |
| 	// with the values from both maps, otherwise it is called with the value of the map that contains the key and nil
 | |
| 	// for the map that does not contain the key. Returning false in the closure prematurely stops the iteration.
 | |
| 	Zip(other Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool
 | |
| 	// ZipUsing uses the provided allocator and iterates over the entries of two maps together. If both maps
 | |
| 	// contain a value for a given key, fn is called with the values from both maps, otherwise it is called with
 | |
| 	// the value of the map that contains the key and nil for the map that does not contain the key. Returning
 | |
| 	// false in the closure prematurely stops the iteration.
 | |
| 	ZipUsing(a Allocator, other Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool
 | |
| }
 | |
| 
 | |
| // MapTraverseOrder defines the map traversal ordering available.
 | |
| type MapTraverseOrder int
 | |
| 
 | |
| const (
 | |
| 	// Unordered indicates that the map traversal has no ordering requirement.
 | |
| 	Unordered = iota
 | |
| 	// LexicalKeyOrder indicates that the map traversal is ordered by key, lexically.
 | |
| 	LexicalKeyOrder
 | |
| )
 | |
| 
 | |
| // MapZip iterates over the entries of two maps together. If both maps contain a value for a given key, fn is called
 | |
| // with the values from both maps, otherwise it is called with the value of the map that contains the key and nil
 | |
| // for the other map. Returning false in the closure prematurely stops the iteration.
 | |
| func MapZip(lhs, rhs Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool {
 | |
| 	return MapZipUsing(HeapAllocator, lhs, rhs, order, fn)
 | |
| }
 | |
| 
 | |
| // MapZipUsing uses the provided allocator and iterates over the entries of two maps together. If both maps
 | |
| // contain a value for a given key, fn is called with the values from both maps, otherwise it is called with
 | |
| // the value of the map that contains the key and nil for the other map. Returning false in the closure
 | |
| // prematurely stops the iteration.
 | |
| func MapZipUsing(a Allocator, lhs, rhs Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool {
 | |
| 	if lhs != nil {
 | |
| 		return lhs.ZipUsing(a, rhs, order, fn)
 | |
| 	}
 | |
| 	if rhs != nil {
 | |
| 		return rhs.ZipUsing(a, lhs, order, func(key string, rhs, lhs Value) bool { // arg positions of lhs and rhs deliberately swapped
 | |
| 			return fn(key, lhs, rhs)
 | |
| 		})
 | |
| 	}
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| // defaultMapZip provides a default implementation of Zip for implementations that do not need to provide
 | |
| // their own optimized implementation.
 | |
| func defaultMapZip(a Allocator, lhs, rhs Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool {
 | |
| 	switch order {
 | |
| 	case Unordered:
 | |
| 		return unorderedMapZip(a, lhs, rhs, fn)
 | |
| 	case LexicalKeyOrder:
 | |
| 		return lexicalKeyOrderedMapZip(a, lhs, rhs, fn)
 | |
| 	default:
 | |
| 		panic("Unsupported map order")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func unorderedMapZip(a Allocator, lhs, rhs Map, fn func(key string, lhs, rhs Value) bool) bool {
 | |
| 	if (lhs == nil || lhs.Empty()) && (rhs == nil || rhs.Empty()) {
 | |
| 		return true
 | |
| 	}
 | |
| 
 | |
| 	if lhs != nil {
 | |
| 		ok := lhs.IterateUsing(a, func(key string, lhsValue Value) bool {
 | |
| 			var rhsValue Value
 | |
| 			if rhs != nil {
 | |
| 				if item, ok := rhs.GetUsing(a, key); ok {
 | |
| 					rhsValue = item
 | |
| 					defer a.Free(rhsValue)
 | |
| 				}
 | |
| 			}
 | |
| 			return fn(key, lhsValue, rhsValue)
 | |
| 		})
 | |
| 		if !ok {
 | |
| 			return false
 | |
| 		}
 | |
| 	}
 | |
| 	if rhs != nil {
 | |
| 		return rhs.IterateUsing(a, func(key string, rhsValue Value) bool {
 | |
| 			if lhs == nil || !lhs.Has(key) {
 | |
| 				return fn(key, nil, rhsValue)
 | |
| 			}
 | |
| 			return true
 | |
| 		})
 | |
| 	}
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| func lexicalKeyOrderedMapZip(a Allocator, lhs, rhs Map, fn func(key string, lhs, rhs Value) bool) bool {
 | |
| 	var lhsLength, rhsLength int
 | |
| 	var orderedLength int // rough estimate of length of union of map keys
 | |
| 	if lhs != nil {
 | |
| 		lhsLength = lhs.Length()
 | |
| 		orderedLength = lhsLength
 | |
| 	}
 | |
| 	if rhs != nil {
 | |
| 		rhsLength = rhs.Length()
 | |
| 		if rhsLength > orderedLength {
 | |
| 			orderedLength = rhsLength
 | |
| 		}
 | |
| 	}
 | |
| 	if lhsLength == 0 && rhsLength == 0 {
 | |
| 		return true
 | |
| 	}
 | |
| 
 | |
| 	ordered := make([]string, 0, orderedLength)
 | |
| 	if lhs != nil {
 | |
| 		lhs.IterateUsing(a, func(key string, _ Value) bool {
 | |
| 			ordered = append(ordered, key)
 | |
| 			return true
 | |
| 		})
 | |
| 	}
 | |
| 	if rhs != nil {
 | |
| 		rhs.IterateUsing(a, func(key string, _ Value) bool {
 | |
| 			if lhs == nil || !lhs.Has(key) {
 | |
| 				ordered = append(ordered, key)
 | |
| 			}
 | |
| 			return true
 | |
| 		})
 | |
| 	}
 | |
| 	sort.Strings(ordered)
 | |
| 	for _, key := range ordered {
 | |
| 		var litem, ritem Value
 | |
| 		if lhs != nil {
 | |
| 			litem, _ = lhs.GetUsing(a, key)
 | |
| 		}
 | |
| 		if rhs != nil {
 | |
| 			ritem, _ = rhs.GetUsing(a, key)
 | |
| 		}
 | |
| 		ok := fn(key, litem, ritem)
 | |
| 		if litem != nil {
 | |
| 			a.Free(litem)
 | |
| 		}
 | |
| 		if ritem != nil {
 | |
| 			a.Free(ritem)
 | |
| 		}
 | |
| 		if !ok {
 | |
| 			return false
 | |
| 		}
 | |
| 	}
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| // MapLess compares two maps lexically.
 | |
| func MapLess(lhs, rhs Map) bool {
 | |
| 	return MapCompare(lhs, rhs) == -1
 | |
| }
 | |
| 
 | |
| // MapCompare compares two maps lexically.
 | |
| func MapCompare(lhs, rhs Map) int {
 | |
| 	return MapCompareUsing(HeapAllocator, lhs, rhs)
 | |
| }
 | |
| 
 | |
| // MapCompareUsing uses the provided allocator and compares two maps lexically.
 | |
| func MapCompareUsing(a Allocator, lhs, rhs Map) int {
 | |
| 	c := 0
 | |
| 	var llength, rlength int
 | |
| 	if lhs != nil {
 | |
| 		llength = lhs.Length()
 | |
| 	}
 | |
| 	if rhs != nil {
 | |
| 		rlength = rhs.Length()
 | |
| 	}
 | |
| 	if llength == 0 && rlength == 0 {
 | |
| 		return 0
 | |
| 	}
 | |
| 	i := 0
 | |
| 	MapZipUsing(a, lhs, rhs, LexicalKeyOrder, func(key string, lhs, rhs Value) bool {
 | |
| 		switch {
 | |
| 		case i == llength:
 | |
| 			c = -1
 | |
| 		case i == rlength:
 | |
| 			c = 1
 | |
| 		case lhs == nil:
 | |
| 			c = 1
 | |
| 		case rhs == nil:
 | |
| 			c = -1
 | |
| 		default:
 | |
| 			c = CompareUsing(a, lhs, rhs)
 | |
| 		}
 | |
| 		i++
 | |
| 		return c == 0
 | |
| 	})
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| // MapEquals returns true if lhs == rhs, false otherwise. This function
 | |
| // acts on generic types and should not be used by callers, but can help
 | |
| // implement Map.Equals.
 | |
| // WARN: This is a naive implementation, calling lhs.Equals(rhs) is typically the most efficient.
 | |
| func MapEquals(lhs, rhs Map) bool {
 | |
| 	return MapEqualsUsing(HeapAllocator, lhs, rhs)
 | |
| }
 | |
| 
 | |
| // MapEqualsUsing uses the provided allocator and returns true if lhs == rhs,
 | |
| // false otherwise. This function acts on generic types and should not be used
 | |
| // by callers, but can help implement Map.Equals.
 | |
| // WARN: This is a naive implementation, calling lhs.EqualsUsing(allocator, rhs) is typically the most efficient.
 | |
| func MapEqualsUsing(a Allocator, lhs, rhs Map) bool {
 | |
| 	if lhs == nil && rhs == nil {
 | |
| 		return true
 | |
| 	}
 | |
| 	if lhs == nil || rhs == nil {
 | |
| 		return false
 | |
| 	}
 | |
| 	if lhs.Length() != rhs.Length() {
 | |
| 		return false
 | |
| 	}
 | |
| 	return MapZipUsing(a, lhs, rhs, Unordered, func(key string, lhs, rhs Value) bool {
 | |
| 		if lhs == nil || rhs == nil {
 | |
| 			return false
 | |
| 		}
 | |
| 		return EqualsUsing(a, lhs, rhs)
 | |
| 	})
 | |
| }
 |