github.com/psiphon-labs/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  }