map.go 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. /*
  2. Copyright 2019 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package value
  14. import (
  15. "sort"
  16. )
  17. // Map represents a Map or go structure.
  18. type Map interface {
  19. // Set changes or set the value of the given key.
  20. Set(key string, val Value)
  21. // Get returns the value for the given key, if present, or (nil, false) otherwise.
  22. Get(key string) (Value, bool)
  23. // GetUsing uses the provided allocator and returns the value for the given key,
  24. // if present, or (nil, false) otherwise.
  25. // The returned Value should be given back to the Allocator when no longer needed
  26. // by calling Allocator.Free(Value).
  27. GetUsing(a Allocator, key string) (Value, bool)
  28. // Has returns true if the key is present, or false otherwise.
  29. Has(key string) bool
  30. // Delete removes the key from the map.
  31. Delete(key string)
  32. // Equals compares the two maps, and return true if they are the same, false otherwise.
  33. // Implementations can use MapEquals as a general implementation for this methods.
  34. Equals(other Map) bool
  35. // EqualsUsing uses the provided allocator and compares the two maps, and return true if
  36. // they are the same, false otherwise. Implementations can use MapEqualsUsing as a general
  37. // implementation for this methods.
  38. EqualsUsing(a Allocator, other Map) bool
  39. // Iterate runs the given function for each key/value in the
  40. // map. Returning false in the closure prematurely stops the
  41. // iteration.
  42. Iterate(func(key string, value Value) bool) bool
  43. // IterateUsing uses the provided allocator and runs the given function for each key/value
  44. // in the map. Returning false in the closure prematurely stops the iteration.
  45. IterateUsing(Allocator, func(key string, value Value) bool) bool
  46. // Length returns the number of items in the map.
  47. Length() int
  48. // Empty returns true if the map is empty.
  49. Empty() bool
  50. // Zip iterates over the entries of two maps together. If both maps contain a value for a given key, fn is called
  51. // with the values from both maps, otherwise it is called with the value of the map that contains the key and nil
  52. // for the map that does not contain the key. Returning false in the closure prematurely stops the iteration.
  53. Zip(other Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool
  54. // ZipUsing uses the provided allocator and iterates over the entries of two maps together. If both maps
  55. // contain a value for a given key, fn is called with the values from both maps, otherwise it is called with
  56. // the value of the map that contains the key and nil for the map that does not contain the key. Returning
  57. // false in the closure prematurely stops the iteration.
  58. ZipUsing(a Allocator, other Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool
  59. }
  60. // MapTraverseOrder defines the map traversal ordering available.
  61. type MapTraverseOrder int
  62. const (
  63. // Unordered indicates that the map traversal has no ordering requirement.
  64. Unordered = iota
  65. // LexicalKeyOrder indicates that the map traversal is ordered by key, lexically.
  66. LexicalKeyOrder
  67. )
  68. // MapZip iterates over the entries of two maps together. If both maps contain a value for a given key, fn is called
  69. // with the values from both maps, otherwise it is called with the value of the map that contains the key and nil
  70. // for the other map. Returning false in the closure prematurely stops the iteration.
  71. func MapZip(lhs, rhs Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool {
  72. return MapZipUsing(HeapAllocator, lhs, rhs, order, fn)
  73. }
  74. // MapZipUsing uses the provided allocator and iterates over the entries of two maps together. If both maps
  75. // contain a value for a given key, fn is called with the values from both maps, otherwise it is called with
  76. // the value of the map that contains the key and nil for the other map. Returning false in the closure
  77. // prematurely stops the iteration.
  78. func MapZipUsing(a Allocator, lhs, rhs Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool {
  79. if lhs != nil {
  80. return lhs.ZipUsing(a, rhs, order, fn)
  81. }
  82. if rhs != nil {
  83. return rhs.ZipUsing(a, lhs, order, func(key string, rhs, lhs Value) bool { // arg positions of lhs and rhs deliberately swapped
  84. return fn(key, lhs, rhs)
  85. })
  86. }
  87. return true
  88. }
  89. // defaultMapZip provides a default implementation of Zip for implementations that do not need to provide
  90. // their own optimized implementation.
  91. func defaultMapZip(a Allocator, lhs, rhs Map, order MapTraverseOrder, fn func(key string, lhs, rhs Value) bool) bool {
  92. switch order {
  93. case Unordered:
  94. return unorderedMapZip(a, lhs, rhs, fn)
  95. case LexicalKeyOrder:
  96. return lexicalKeyOrderedMapZip(a, lhs, rhs, fn)
  97. default:
  98. panic("Unsupported map order")
  99. }
  100. }
  101. func unorderedMapZip(a Allocator, lhs, rhs Map, fn func(key string, lhs, rhs Value) bool) bool {
  102. if (lhs == nil || lhs.Empty()) && (rhs == nil || rhs.Empty()) {
  103. return true
  104. }
  105. if lhs != nil {
  106. ok := lhs.IterateUsing(a, func(key string, lhsValue Value) bool {
  107. var rhsValue Value
  108. if rhs != nil {
  109. if item, ok := rhs.GetUsing(a, key); ok {
  110. rhsValue = item
  111. defer a.Free(rhsValue)
  112. }
  113. }
  114. return fn(key, lhsValue, rhsValue)
  115. })
  116. if !ok {
  117. return false
  118. }
  119. }
  120. if rhs != nil {
  121. return rhs.IterateUsing(a, func(key string, rhsValue Value) bool {
  122. if lhs == nil || !lhs.Has(key) {
  123. return fn(key, nil, rhsValue)
  124. }
  125. return true
  126. })
  127. }
  128. return true
  129. }
  130. func lexicalKeyOrderedMapZip(a Allocator, lhs, rhs Map, fn func(key string, lhs, rhs Value) bool) bool {
  131. var lhsLength, rhsLength int
  132. var orderedLength int // rough estimate of length of union of map keys
  133. if lhs != nil {
  134. lhsLength = lhs.Length()
  135. orderedLength = lhsLength
  136. }
  137. if rhs != nil {
  138. rhsLength = rhs.Length()
  139. if rhsLength > orderedLength {
  140. orderedLength = rhsLength
  141. }
  142. }
  143. if lhsLength == 0 && rhsLength == 0 {
  144. return true
  145. }
  146. ordered := make([]string, 0, orderedLength)
  147. if lhs != nil {
  148. lhs.IterateUsing(a, func(key string, _ Value) bool {
  149. ordered = append(ordered, key)
  150. return true
  151. })
  152. }
  153. if rhs != nil {
  154. rhs.IterateUsing(a, func(key string, _ Value) bool {
  155. if lhs == nil || !lhs.Has(key) {
  156. ordered = append(ordered, key)
  157. }
  158. return true
  159. })
  160. }
  161. sort.Strings(ordered)
  162. for _, key := range ordered {
  163. var litem, ritem Value
  164. if lhs != nil {
  165. litem, _ = lhs.GetUsing(a, key)
  166. }
  167. if rhs != nil {
  168. ritem, _ = rhs.GetUsing(a, key)
  169. }
  170. ok := fn(key, litem, ritem)
  171. if litem != nil {
  172. a.Free(litem)
  173. }
  174. if ritem != nil {
  175. a.Free(ritem)
  176. }
  177. if !ok {
  178. return false
  179. }
  180. }
  181. return true
  182. }
  183. // MapLess compares two maps lexically.
  184. func MapLess(lhs, rhs Map) bool {
  185. return MapCompare(lhs, rhs) == -1
  186. }
  187. // MapCompare compares two maps lexically.
  188. func MapCompare(lhs, rhs Map) int {
  189. return MapCompareUsing(HeapAllocator, lhs, rhs)
  190. }
  191. // MapCompareUsing uses the provided allocator and compares two maps lexically.
  192. func MapCompareUsing(a Allocator, lhs, rhs Map) int {
  193. c := 0
  194. var llength, rlength int
  195. if lhs != nil {
  196. llength = lhs.Length()
  197. }
  198. if rhs != nil {
  199. rlength = rhs.Length()
  200. }
  201. if llength == 0 && rlength == 0 {
  202. return 0
  203. }
  204. i := 0
  205. MapZipUsing(a, lhs, rhs, LexicalKeyOrder, func(key string, lhs, rhs Value) bool {
  206. switch {
  207. case i == llength:
  208. c = -1
  209. case i == rlength:
  210. c = 1
  211. case lhs == nil:
  212. c = 1
  213. case rhs == nil:
  214. c = -1
  215. default:
  216. c = CompareUsing(a, lhs, rhs)
  217. }
  218. i++
  219. return c == 0
  220. })
  221. return c
  222. }
  223. // MapEquals returns true if lhs == rhs, false otherwise. This function
  224. // acts on generic types and should not be used by callers, but can help
  225. // implement Map.Equals.
  226. // WARN: This is a naive implementation, calling lhs.Equals(rhs) is typically the most efficient.
  227. func MapEquals(lhs, rhs Map) bool {
  228. return MapEqualsUsing(HeapAllocator, lhs, rhs)
  229. }
  230. // MapEqualsUsing uses the provided allocator and returns true if lhs == rhs,
  231. // false otherwise. This function acts on generic types and should not be used
  232. // by callers, but can help implement Map.Equals.
  233. // WARN: This is a naive implementation, calling lhs.EqualsUsing(allocator, rhs) is typically the most efficient.
  234. func MapEqualsUsing(a Allocator, lhs, rhs Map) bool {
  235. if lhs == nil && rhs == nil {
  236. return true
  237. }
  238. if lhs == nil || rhs == nil {
  239. return false
  240. }
  241. if lhs.Length() != rhs.Length() {
  242. return false
  243. }
  244. return MapZipUsing(a, lhs, rhs, Unordered, func(key string, lhs, rhs Value) bool {
  245. if lhs == nil || rhs == nil {
  246. return false
  247. }
  248. return EqualsUsing(a, lhs, rhs)
  249. })
  250. }