123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 |
- /*
- 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)
- })
- }
|