560305f601
- uses newer version of go-ethereum required for go1.11
569 lines
12 KiB
Go
569 lines
12 KiB
Go
// Copyright (c) 2015 Arista Networks, Inc.
|
|
// Use of this source code is governed by the Apache License 2.0
|
|
// that can be found in the COPYING file.
|
|
|
|
package key
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"math"
|
|
"strconv"
|
|
|
|
"github.com/aristanetworks/goarista/value"
|
|
)
|
|
|
|
// Key represents the Key in the updates and deletes of the Notification
|
|
// objects. The only reason this exists is that Go won't let us define
|
|
// our own hash function for non-hashable types, and unfortunately we
|
|
// need to be able to index maps by map[string]interface{} objects
|
|
// and slices by []interface{} objects.
|
|
type Key interface {
|
|
Key() interface{}
|
|
String() string
|
|
Equal(other interface{}) bool
|
|
}
|
|
|
|
// compositeKey allows storing a map[string]interface{} or []interface{} as a key
|
|
// in a Go map. This is useful when the key isn't a fixed data structure known
|
|
// at compile time but rather something generic, like a bag of key-value pairs
|
|
// or a list of elements. Go does not allow storing a map or slice inside the
|
|
// key of a map, because maps and slices are not comparable or hashable, and
|
|
// keys in maps and slice elements must be both. This file is a hack specific
|
|
// to the 'gc' implementation of Go (which is the one most people use when they
|
|
// use Go), to bypass this check, by abusing reflection to override how Go
|
|
// compares compositeKey for equality or how it's hashed. The values allowed in
|
|
// this map are only the types whitelisted in New() as well as map[Key]interface{}
|
|
// and []interface{}.
|
|
//
|
|
// See also https://github.com/golang/go/issues/283
|
|
type compositeKey struct {
|
|
// This value must always be set to the sentinel constant above.
|
|
sentinel uintptr
|
|
m map[string]interface{}
|
|
s []interface{}
|
|
}
|
|
|
|
type interfaceKey struct {
|
|
key interface{}
|
|
}
|
|
|
|
type strKey string
|
|
|
|
type int8Key int8
|
|
type int16Key int16
|
|
type int32Key int32
|
|
type int64Key int64
|
|
|
|
type uint8Key int8
|
|
type uint16Key int16
|
|
type uint32Key int32
|
|
type uint64Key int64
|
|
|
|
type float32Key float32
|
|
type float64Key float64
|
|
|
|
type boolKey bool
|
|
|
|
type pointerKey compositeKey
|
|
|
|
type pathKey compositeKey
|
|
|
|
func pathToSlice(path Path) []interface{} {
|
|
s := make([]interface{}, len(path))
|
|
for i, element := range path {
|
|
s[i] = element.Key()
|
|
}
|
|
return s
|
|
}
|
|
|
|
func sliceToPath(s []interface{}) Path {
|
|
path := make(Path, len(s))
|
|
for i, intf := range s {
|
|
path[i] = New(intf)
|
|
}
|
|
return path
|
|
}
|
|
|
|
func pointerToSlice(ptr Pointer) []interface{} {
|
|
return pathToSlice(ptr.Pointer())
|
|
}
|
|
|
|
func sliceToPointer(s []interface{}) pointer {
|
|
return pointer(sliceToPath(s))
|
|
}
|
|
|
|
// New wraps the given value in a Key.
|
|
// This function panics if the value passed in isn't allowed in a Key or
|
|
// doesn't implement value.Value.
|
|
func New(intf interface{}) Key {
|
|
switch t := intf.(type) {
|
|
case map[string]interface{}:
|
|
return compositeKey{sentinel: sentinel, m: t}
|
|
case []interface{}:
|
|
return compositeKey{sentinel: sentinel, s: t}
|
|
case string:
|
|
return strKey(t)
|
|
case int8:
|
|
return int8Key(t)
|
|
case int16:
|
|
return int16Key(t)
|
|
case int32:
|
|
return int32Key(t)
|
|
case int64:
|
|
return int64Key(t)
|
|
case uint8:
|
|
return uint8Key(t)
|
|
case uint16:
|
|
return uint16Key(t)
|
|
case uint32:
|
|
return uint32Key(t)
|
|
case uint64:
|
|
return uint64Key(t)
|
|
case float32:
|
|
return float32Key(t)
|
|
case float64:
|
|
return float64Key(t)
|
|
case bool:
|
|
return boolKey(t)
|
|
case value.Value:
|
|
return interfaceKey{key: intf}
|
|
case Pointer:
|
|
return pointerKey{sentinel: sentinel, s: pointerToSlice(t)}
|
|
case Path:
|
|
return pathKey{sentinel: sentinel, s: pathToSlice(t)}
|
|
default:
|
|
panic(fmt.Sprintf("Invalid type for key: %T", intf))
|
|
}
|
|
}
|
|
|
|
func (k interfaceKey) Key() interface{} {
|
|
return k.key
|
|
}
|
|
|
|
func (k interfaceKey) String() string {
|
|
return stringify(k.key)
|
|
}
|
|
|
|
func (k interfaceKey) GoString() string {
|
|
return fmt.Sprintf("key.New(%#v)", k.Key())
|
|
}
|
|
|
|
func (k interfaceKey) MarshalJSON() ([]byte, error) {
|
|
return json.Marshal(k.Key())
|
|
}
|
|
|
|
func (k interfaceKey) Equal(other interface{}) bool {
|
|
o, ok := other.(Key)
|
|
return ok && keyEqual(k.key, o.Key())
|
|
}
|
|
|
|
// Comparable types have an equality-testing method.
|
|
type Comparable interface {
|
|
// Equal returns true if this object is equal to the other one.
|
|
Equal(other interface{}) bool
|
|
}
|
|
|
|
func mapStringEqual(a, b map[string]interface{}) bool {
|
|
if len(a) != len(b) {
|
|
return false
|
|
}
|
|
for k, av := range a {
|
|
if bv, ok := b[k]; !ok || !keyEqual(av, bv) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func sliceEqual(a, b []interface{}) bool {
|
|
if len(a) != len(b) {
|
|
return false
|
|
}
|
|
for i, v := range a {
|
|
if !keyEqual(v, b[i]) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func keyEqual(a, b interface{}) bool {
|
|
switch a := a.(type) {
|
|
case map[string]interface{}:
|
|
b, ok := b.(map[string]interface{})
|
|
return ok && mapStringEqual(a, b)
|
|
case map[Key]interface{}:
|
|
b, ok := b.(map[Key]interface{})
|
|
if !ok || len(a) != len(b) {
|
|
return false
|
|
}
|
|
for k, av := range a {
|
|
if bv, ok := b[k]; !ok || !keyEqual(av, bv) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
case []interface{}:
|
|
b, ok := b.([]interface{})
|
|
return ok && sliceEqual(a, b)
|
|
case Comparable:
|
|
return a.Equal(b)
|
|
case Pointer:
|
|
b, ok := b.(Pointer)
|
|
return ok && pointerEqual(a, b)
|
|
case Path:
|
|
b, ok := b.(Path)
|
|
return ok && pathEqual(a, b)
|
|
}
|
|
|
|
return a == b
|
|
}
|
|
|
|
// Key interface implementation for map[string]interface{} and []interface{}
|
|
func (k compositeKey) Key() interface{} {
|
|
if k.m != nil {
|
|
return k.m
|
|
}
|
|
return k.s
|
|
}
|
|
|
|
func (k compositeKey) String() string {
|
|
return stringify(k.Key())
|
|
}
|
|
|
|
func (k compositeKey) GoString() string {
|
|
return fmt.Sprintf("key.New(%#v)", k.Key())
|
|
}
|
|
|
|
func (k compositeKey) MarshalJSON() ([]byte, error) {
|
|
return json.Marshal(k.Key())
|
|
}
|
|
|
|
func (k compositeKey) Equal(other interface{}) bool {
|
|
o, ok := other.(compositeKey)
|
|
if k.m != nil {
|
|
return ok && mapStringEqual(k.m, o.m)
|
|
}
|
|
return ok && sliceEqual(k.s, o.s)
|
|
}
|
|
|
|
func (k strKey) Key() interface{} {
|
|
return string(k)
|
|
}
|
|
|
|
func (k strKey) String() string {
|
|
return string(k)
|
|
}
|
|
|
|
func (k strKey) GoString() string {
|
|
return fmt.Sprintf("key.New(%q)", string(k))
|
|
}
|
|
|
|
func (k strKey) MarshalJSON() ([]byte, error) {
|
|
return json.Marshal(escape(string(k)))
|
|
}
|
|
|
|
func (k strKey) Equal(other interface{}) bool {
|
|
o, ok := other.(strKey)
|
|
return ok && k == o
|
|
}
|
|
|
|
// Key interface implementation for int8
|
|
func (k int8Key) Key() interface{} {
|
|
return int8(k)
|
|
}
|
|
|
|
func (k int8Key) String() string {
|
|
return strconv.FormatInt(int64(k), 10)
|
|
}
|
|
|
|
func (k int8Key) GoString() string {
|
|
return fmt.Sprintf("key.New(int8(%d))", int8(k))
|
|
}
|
|
|
|
func (k int8Key) MarshalJSON() ([]byte, error) {
|
|
return []byte(strconv.FormatInt(int64(k), 10)), nil
|
|
}
|
|
|
|
func (k int8Key) Equal(other interface{}) bool {
|
|
o, ok := other.(int8Key)
|
|
return ok && k == o
|
|
}
|
|
|
|
// Key interface implementation for int16
|
|
func (k int16Key) Key() interface{} {
|
|
return int16(k)
|
|
}
|
|
|
|
func (k int16Key) String() string {
|
|
return strconv.FormatInt(int64(k), 10)
|
|
}
|
|
|
|
func (k int16Key) GoString() string {
|
|
return fmt.Sprintf("key.New(int16(%d))", int16(k))
|
|
}
|
|
|
|
func (k int16Key) MarshalJSON() ([]byte, error) {
|
|
return []byte(strconv.FormatInt(int64(k), 10)), nil
|
|
}
|
|
|
|
func (k int16Key) Equal(other interface{}) bool {
|
|
o, ok := other.(int16Key)
|
|
return ok && k == o
|
|
}
|
|
|
|
// Key interface implementation for int32
|
|
func (k int32Key) Key() interface{} {
|
|
return int32(k)
|
|
}
|
|
|
|
func (k int32Key) String() string {
|
|
return strconv.FormatInt(int64(k), 10)
|
|
}
|
|
|
|
func (k int32Key) GoString() string {
|
|
return fmt.Sprintf("key.New(int32(%d))", int32(k))
|
|
}
|
|
|
|
func (k int32Key) MarshalJSON() ([]byte, error) {
|
|
return []byte(strconv.FormatInt(int64(k), 10)), nil
|
|
}
|
|
|
|
func (k int32Key) Equal(other interface{}) bool {
|
|
o, ok := other.(int32Key)
|
|
return ok && k == o
|
|
}
|
|
|
|
// Key interface implementation for int64
|
|
func (k int64Key) Key() interface{} {
|
|
return int64(k)
|
|
}
|
|
|
|
func (k int64Key) String() string {
|
|
return strconv.FormatInt(int64(k), 10)
|
|
}
|
|
|
|
func (k int64Key) GoString() string {
|
|
return fmt.Sprintf("key.New(int64(%d))", int64(k))
|
|
}
|
|
|
|
func (k int64Key) MarshalJSON() ([]byte, error) {
|
|
return []byte(strconv.FormatInt(int64(k), 10)), nil
|
|
}
|
|
|
|
func (k int64Key) Equal(other interface{}) bool {
|
|
o, ok := other.(int64Key)
|
|
return ok && k == o
|
|
}
|
|
|
|
// Key interface implementation for uint8
|
|
func (k uint8Key) Key() interface{} {
|
|
return uint8(k)
|
|
}
|
|
|
|
func (k uint8Key) String() string {
|
|
return strconv.FormatUint(uint64(k), 10)
|
|
}
|
|
|
|
func (k uint8Key) GoString() string {
|
|
return fmt.Sprintf("key.New(uint8(%d))", uint8(k))
|
|
}
|
|
|
|
func (k uint8Key) MarshalJSON() ([]byte, error) {
|
|
return []byte(strconv.FormatUint(uint64(k), 10)), nil
|
|
}
|
|
|
|
func (k uint8Key) Equal(other interface{}) bool {
|
|
o, ok := other.(uint8Key)
|
|
return ok && k == o
|
|
}
|
|
|
|
// Key interface implementation for uint16
|
|
func (k uint16Key) Key() interface{} {
|
|
return uint16(k)
|
|
}
|
|
|
|
func (k uint16Key) String() string {
|
|
return strconv.FormatUint(uint64(k), 10)
|
|
}
|
|
|
|
func (k uint16Key) GoString() string {
|
|
return fmt.Sprintf("key.New(uint16(%d))", uint16(k))
|
|
}
|
|
|
|
func (k uint16Key) MarshalJSON() ([]byte, error) {
|
|
return []byte(strconv.FormatUint(uint64(k), 10)), nil
|
|
}
|
|
|
|
func (k uint16Key) Equal(other interface{}) bool {
|
|
o, ok := other.(uint16Key)
|
|
return ok && k == o
|
|
}
|
|
|
|
// Key interface implementation for uint32
|
|
func (k uint32Key) Key() interface{} {
|
|
return uint32(k)
|
|
}
|
|
|
|
func (k uint32Key) String() string {
|
|
return strconv.FormatUint(uint64(k), 10)
|
|
}
|
|
|
|
func (k uint32Key) GoString() string {
|
|
return fmt.Sprintf("key.New(uint32(%d))", uint32(k))
|
|
}
|
|
|
|
func (k uint32Key) MarshalJSON() ([]byte, error) {
|
|
return []byte(strconv.FormatUint(uint64(k), 10)), nil
|
|
}
|
|
|
|
func (k uint32Key) Equal(other interface{}) bool {
|
|
o, ok := other.(uint32Key)
|
|
return ok && k == o
|
|
}
|
|
|
|
// Key interface implementation for uint64
|
|
func (k uint64Key) Key() interface{} {
|
|
return uint64(k)
|
|
}
|
|
|
|
func (k uint64Key) String() string {
|
|
return strconv.FormatUint(uint64(k), 10)
|
|
}
|
|
|
|
func (k uint64Key) GoString() string {
|
|
return fmt.Sprintf("key.New(uint64(%d))", uint64(k))
|
|
}
|
|
|
|
func (k uint64Key) MarshalJSON() ([]byte, error) {
|
|
return []byte(strconv.FormatUint(uint64(k), 10)), nil
|
|
}
|
|
|
|
func (k uint64Key) Equal(other interface{}) bool {
|
|
o, ok := other.(uint64Key)
|
|
return ok && k == o
|
|
}
|
|
|
|
// Key interface implementation for float32
|
|
func (k float32Key) Key() interface{} {
|
|
return float32(k)
|
|
}
|
|
|
|
func (k float32Key) String() string {
|
|
return "f" + strconv.FormatInt(int64(math.Float32bits(float32(k))), 10)
|
|
}
|
|
|
|
func (k float32Key) GoString() string {
|
|
return fmt.Sprintf("key.New(float32(%v))", float32(k))
|
|
}
|
|
|
|
func (k float32Key) MarshalJSON() ([]byte, error) {
|
|
return []byte(strconv.FormatFloat(float64(k), 'g', -1, 32)), nil
|
|
}
|
|
|
|
func (k float32Key) Equal(other interface{}) bool {
|
|
o, ok := other.(float32Key)
|
|
return ok && k == o
|
|
}
|
|
|
|
// Key interface implementation for float64
|
|
func (k float64Key) Key() interface{} {
|
|
return float64(k)
|
|
}
|
|
|
|
func (k float64Key) String() string {
|
|
return "f" + strconv.FormatInt(int64(math.Float64bits(float64(k))), 10)
|
|
}
|
|
|
|
func (k float64Key) GoString() string {
|
|
return fmt.Sprintf("key.New(float64(%v))", float64(k))
|
|
}
|
|
|
|
func (k float64Key) MarshalJSON() ([]byte, error) {
|
|
return []byte(strconv.FormatFloat(float64(k), 'g', -1, 64)), nil
|
|
}
|
|
|
|
func (k float64Key) Equal(other interface{}) bool {
|
|
o, ok := other.(float64Key)
|
|
return ok && k == o
|
|
}
|
|
|
|
// Key interface implementation for bool
|
|
func (k boolKey) Key() interface{} {
|
|
return bool(k)
|
|
}
|
|
|
|
func (k boolKey) String() string {
|
|
return strconv.FormatBool(bool(k))
|
|
}
|
|
|
|
func (k boolKey) GoString() string {
|
|
return fmt.Sprintf("key.New(%v)", bool(k))
|
|
}
|
|
|
|
func (k boolKey) MarshalJSON() ([]byte, error) {
|
|
return []byte(strconv.FormatBool(bool(k))), nil
|
|
}
|
|
|
|
func (k boolKey) Equal(other interface{}) bool {
|
|
o, ok := other.(boolKey)
|
|
return ok && k == o
|
|
}
|
|
|
|
// Key interface implementation for Pointer
|
|
func (k pointerKey) Key() interface{} {
|
|
return sliceToPointer(k.s)
|
|
}
|
|
|
|
func (k pointerKey) String() string {
|
|
return sliceToPointer(k.s).String()
|
|
}
|
|
|
|
func (k pointerKey) GoString() string {
|
|
return fmt.Sprintf("key.New(%#v)", k.s)
|
|
}
|
|
|
|
func (k pointerKey) MarshalJSON() ([]byte, error) {
|
|
return sliceToPointer(k.s).MarshalJSON()
|
|
}
|
|
|
|
func (k pointerKey) Equal(other interface{}) bool {
|
|
if o, ok := other.(pointerKey); ok {
|
|
return sliceEqual(k.s, o.s)
|
|
}
|
|
key, ok := other.(Key)
|
|
if !ok {
|
|
return false
|
|
}
|
|
return ok && sliceToPointer(k.s).Equal(key.Key())
|
|
}
|
|
|
|
// Key interface implementation for Path
|
|
func (k pathKey) Key() interface{} {
|
|
return sliceToPath(k.s)
|
|
}
|
|
|
|
func (k pathKey) String() string {
|
|
return sliceToPath(k.s).String()
|
|
}
|
|
|
|
func (k pathKey) GoString() string {
|
|
return fmt.Sprintf("key.New(%#v)", k.s)
|
|
}
|
|
|
|
func (k pathKey) MarshalJSON() ([]byte, error) {
|
|
return sliceToPath(k.s).MarshalJSON()
|
|
}
|
|
|
|
func (k pathKey) Equal(other interface{}) bool {
|
|
if o, ok := other.(pathKey); ok {
|
|
return sliceEqual(k.s, o.s)
|
|
}
|
|
key, ok := other.(Key)
|
|
if !ok {
|
|
return false
|
|
}
|
|
return ok && sliceToPath(k.s).Equal(key.Key())
|
|
}
|