composite.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  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. package key
  5. import (
  6. "reflect"
  7. "unsafe"
  8. "notabug.org/themusicgod1/goarista/areflect"
  9. )
  10. func hashInterface(v interface{}) uintptr {
  11. switch v := v.(type) {
  12. case map[string]interface{}:
  13. return hashMapString(v)
  14. case map[Key]interface{}:
  15. return hashMapKey(v)
  16. case []interface{}:
  17. return hashSlice(v)
  18. case Pointer:
  19. // This case applies to pointers used
  20. // as values in maps or slices (i.e.
  21. // not wrapped in a key).
  22. return hashSlice(pointerToSlice(v))
  23. default:
  24. return _nilinterhash(v)
  25. }
  26. }
  27. func hashMapString(m map[string]interface{}) uintptr {
  28. h := uintptr(31 * (len(m) + 1))
  29. for k, v := range m {
  30. // Use addition so that the order of iteration doesn't matter.
  31. h += _strhash(k)
  32. h += hashInterface(v)
  33. }
  34. return h
  35. }
  36. func hashMapKey(m map[Key]interface{}) uintptr {
  37. h := uintptr(31 * (len(m) + 1))
  38. for k, v := range m {
  39. // Use addition so that the order of iteration doesn't matter.
  40. switch k := k.(type) {
  41. case interfaceKey:
  42. h += _nilinterhash(k.key)
  43. case compositeKey:
  44. h += hashMapString(k.m)
  45. }
  46. h += hashInterface(v)
  47. }
  48. return h
  49. }
  50. func hashSlice(s []interface{}) uintptr {
  51. h := uintptr(31 * (len(s) + 1))
  52. for _, v := range s {
  53. h += hashInterface(v)
  54. }
  55. return h
  56. }
  57. func hash(p unsafe.Pointer, seed uintptr) uintptr {
  58. ck := *(*compositeKey)(p)
  59. if ck.sentinel != sentinel {
  60. panic("use of unhashable type in a map")
  61. }
  62. if ck.m != nil {
  63. return seed ^ hashMapString(ck.m)
  64. }
  65. return seed ^ hashSlice(ck.s)
  66. }
  67. func equal(a unsafe.Pointer, b unsafe.Pointer) bool {
  68. ca := (*compositeKey)(a)
  69. cb := (*compositeKey)(b)
  70. if ca.sentinel != sentinel {
  71. panic("use of uncomparable type on the lhs of ==")
  72. }
  73. if cb.sentinel != sentinel {
  74. panic("use of uncomparable type on the rhs of ==")
  75. }
  76. if ca.m != nil {
  77. return mapStringEqual(ca.m, cb.m)
  78. }
  79. return sliceEqual(ca.s, cb.s)
  80. }
  81. func init() {
  82. typ := reflect.TypeOf(compositeKey{})
  83. alg := reflect.ValueOf(typ).Elem().FieldByName("alg").Elem()
  84. // Pretty certain that doing this voids your warranty.
  85. // This overwrites the typeAlg of either alg_NOEQ64 (on 32-bit platforms)
  86. // or alg_NOEQ128 (on 64-bit platforms), which means that all unhashable
  87. // types that were using this typeAlg are now suddenly hashable and will
  88. // attempt to use our equal/hash functions, which will lead to undefined
  89. // behaviors. But then these types shouldn't have been hashable in the
  90. // first place, so no one should have attempted to use them as keys in a
  91. // map. The compiler will emit an error if it catches someone trying to
  92. // do this, but if they do it through a map that uses an interface type as
  93. // the key, then the compiler can't catch it.
  94. // To prevent this we could instead override the alg pointer in the type,
  95. // but it's in a read-only data section in the binary (it's put there by
  96. // dcommontype() in gc/reflect.go), so changing it is also not without
  97. // perils. Basically: Here Be Dragons.
  98. areflect.ForceExport(alg.FieldByName("hash")).Set(reflect.ValueOf(hash))
  99. areflect.ForceExport(alg.FieldByName("equal")).Set(reflect.ValueOf(equal))
  100. }