123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533 |
- // 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"
- "notabug.org/themusicgod1/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
- func pointerToSlice(ptr Pointer) []interface{} {
- path := ptr.Pointer()
- s := make([]interface{}, len(path))
- for i, element := range path {
- s[i] = element.Key()
- }
- return s
- }
- func sliceToPointer(s []interface{}) pointer {
- path := make(Path, len(s))
- for i, intf := range s {
- path[i] = New(intf)
- }
- return pointer(path)
- }
- // 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 Pointer:
- return pointerKey{sentinel: sentinel, s: pointerToSlice(t)}
- case value.Value:
- return interfaceKey{key: intf}
- 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.(interfaceKey)
- 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)
- if !ok {
- return false
- }
- x, y := a.Pointer(), b.Pointer()
- if len(x) != len(y) {
- return false
- }
- for i := range x {
- if !x[i].Equal(y[i]) {
- return false
- }
- }
- return true
- }
- 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(%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(%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(%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(%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(%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(%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(%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(%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(%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(%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 {
- o, ok := other.(pointerKey)
- return ok && sliceEqual(k.s, o.s)
- }
|