github.com/psiphon-inc/goarista@v0.0.0-20160825065156-d002785f4c67/key/composite.go (about) 1 // Copyright (C) 2016 Arista Networks, Inc. 2 // Use of this source code is governed by the Apache License 2.0 3 // that can be found in the COPYING file. 4 5 package key 6 7 import ( 8 "encoding/json" 9 "fmt" 10 "reflect" 11 "unsafe" 12 13 "github.com/aristanetworks/goarista/areflect" 14 ) 15 16 // composite allows storing a map[string]interface{} as a key in a Go map. 17 // This is useful when the key isn't a fixed data structure known at compile 18 // time but rather something generic, like a bag of key-value pairs. 19 // Go does not allow storing a map inside the key of a map, because maps are 20 // not comparable or hashable, and keys in maps must be both. This file is 21 // a hack specific to the 'gc' implementation of Go (which is the one most 22 // people use when they use Go), to bypass this check, by abusing reflection 23 // to override how Go compares composite for equality or how it's hashed. 24 // The values allowed in this map are only the types whitelisted in New() as 25 // well as map[Key]interface{}. 26 // 27 // See also https://github.com/golang/go/issues/283 28 type composite struct { 29 // This value must always be set to the sentinel constant above. 30 sentinel uintptr 31 m map[string]interface{} 32 } 33 34 func (k composite) Key() interface{} { 35 return k.m 36 } 37 38 func (k composite) String() string { 39 return stringify(k.Key()) 40 } 41 42 func (k composite) GetFromMap(m map[Key]interface{}) (interface{}, bool) { 43 v, ok := m[k] 44 return v, ok 45 } 46 47 func (k composite) DeleteFromMap(m map[Key]interface{}) { 48 delete(m, k) 49 } 50 51 func (k composite) SetToMap(m map[Key]interface{}, value interface{}) { 52 m[k] = value 53 } 54 55 func (k composite) GoString() string { 56 return fmt.Sprintf("key.New(%#v)", k.Key()) 57 } 58 59 func (k composite) MarshalJSON() ([]byte, error) { 60 return json.Marshal(k.Key()) 61 } 62 63 func (k composite) Equal(other interface{}) bool { 64 o, ok := other.(Key) 65 if !ok { 66 return false 67 } 68 return keyEqual(k.Key(), o.Key()) 69 } 70 71 func hashInterface(v interface{}) uintptr { 72 switch v := v.(type) { 73 case map[string]interface{}: 74 return hashMapString(v) 75 case map[Key]interface{}: 76 return hashMapKey(v) 77 default: 78 return _nilinterhash(v) 79 } 80 } 81 82 func hashMapString(m map[string]interface{}) uintptr { 83 h := uintptr(31 * (len(m) + 1)) 84 for k, v := range m { 85 // Use addition so that the order of iteration doesn't matter. 86 h += _strhash(k) 87 h += hashInterface(v) 88 } 89 return h 90 } 91 92 func hashMapKey(m map[Key]interface{}) uintptr { 93 h := uintptr(31 * (len(m) + 1)) 94 for k, v := range m { 95 // Use addition so that the order of iteration doesn't matter. 96 switch k := k.(type) { 97 case keyImpl: 98 h += _nilinterhash(k.key) 99 case composite: 100 h += hashMapString(k.m) 101 } 102 h += hashInterface(v) 103 } 104 return h 105 } 106 107 func hash(p unsafe.Pointer, seed uintptr) uintptr { 108 ck := *(*composite)(p) 109 if ck.sentinel != sentinel { 110 panic("use of unhashable type in a map") 111 } 112 return seed ^ hashMapString(ck.m) 113 } 114 115 func equal(a unsafe.Pointer, b unsafe.Pointer) bool { 116 ca := (*composite)(a) 117 cb := (*composite)(b) 118 if ca.sentinel != sentinel { 119 panic("use of uncomparable type on the lhs of ==") 120 } 121 if cb.sentinel != sentinel { 122 panic("use of uncomparable type on the rhs of ==") 123 } 124 return ca.Equal(*cb) 125 } 126 127 func init() { 128 typ := reflect.TypeOf(composite{}) 129 alg := reflect.ValueOf(typ).Elem().FieldByName("alg").Elem() 130 // Pretty certain that doing this voids your warranty. 131 // This overwrites the typeAlg of either alg_NOEQ64 (on 32-bit platforms) 132 // or alg_NOEQ128 (on 64-bit platforms), which means that all unhashable 133 // types that were using this typeAlg are now suddenly hashable and will 134 // attempt to use our equal/hash functions, which will lead to undefined 135 // behaviors. But then these types shouldn't have been hashable in the 136 // first place, so no one should have attempted to use them as keys in a 137 // map. The compiler will emit an error if it catches someone trying to 138 // do this, but if they do it through a map that uses an interface type as 139 // the key, then the compiler can't catch it. 140 // To prevent this we could instead override the alg pointer in the type, 141 // but it's in a read-only data section in the binary (it's put there by 142 // dcommontype() in gc/reflect.go), so changing it is also not without 143 // perils. Basically: Here Be Dragons. 144 areflect.ForceExport(alg.FieldByName("hash")).Set(reflect.ValueOf(hash)) 145 areflect.ForceExport(alg.FieldByName("equal")).Set(reflect.ValueOf(equal)) 146 }