Bump to latest SMD to pick up performance optimizations

This commit is contained in:
Joe Betz
2020-02-06 15:10:25 -08:00
parent 9617322727
commit d9faaca647
56 changed files with 1949 additions and 688 deletions

View File

@@ -18,7 +18,6 @@ package value
import (
"sort"
"strings"
)
// Map represents a Map or go structure.
@@ -27,6 +26,11 @@ type Map interface {
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.
@@ -34,12 +38,162 @@ type Map interface {
// 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.
@@ -49,65 +203,68 @@ func MapLess(lhs, rhs Map) bool {
// MapCompare compares two maps lexically.
func MapCompare(lhs, rhs Map) int {
lorder := make([]string, 0, lhs.Length())
lhs.Iterate(func(key string, _ Value) bool {
lorder = append(lorder, key)
return true
})
sort.Strings(lorder)
rorder := make([]string, 0, rhs.Length())
rhs.Iterate(func(key string, _ Value) bool {
rorder = append(rorder, key)
return true
})
sort.Strings(rorder)
return MapCompareUsing(HeapAllocator, lhs, rhs)
}
i := 0
for {
if i >= len(lorder) && i >= len(rorder) {
// Maps are the same length and all items are equal.
return 0
}
if i >= len(lorder) {
// LHS is shorter.
return -1
}
if i >= len(rorder) {
// RHS is shorter.
return 1
}
if c := strings.Compare(lorder[i], rorder[i]); c != 0 {
return c
}
litem, _ := lhs.Get(lorder[i])
ritem, _ := rhs.Get(rorder[i])
if c := Compare(litem, ritem); c != 0 {
return c
}
litem.Recycle()
ritem.Recycle()
// The items are equal; continue.
i++
// 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 lhs.Iterate(func(k string, v Value) bool {
vo, ok := rhs.Get(k)
if !ok {
return MapZipUsing(a, lhs, rhs, Unordered, func(key string, lhs, rhs Value) bool {
if lhs == nil || rhs == nil {
return false
}
if !Equals(v, vo) {
vo.Recycle()
return false
}
vo.Recycle()
return true
return EqualsUsing(a, lhs, rhs)
})
}