hash.go 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. // Copyright The OpenTelemetry Authors
  2. // SPDX-License-Identifier: Apache-2.0
  3. package attribute // import "go.opentelemetry.io/otel/attribute"
  4. import (
  5. "fmt"
  6. "reflect"
  7. "go.opentelemetry.io/otel/attribute/internal/xxhash"
  8. )
  9. // Type identifiers. These identifiers are hashed before the value of the
  10. // corresponding type. This is done to distinguish values that are hashed with
  11. // the same value representation (e.g. `int64(1)` and `true`, []int64{0} and
  12. // int64(0)).
  13. //
  14. // These are all 8 byte length strings converted to a uint64 representation. A
  15. // uint64 is used instead of the string directly as an optimization, it avoids
  16. // the for loop in [xxhash] which adds minor overhead.
  17. const (
  18. boolID uint64 = 7953749933313450591 // "_boolean" (little endian)
  19. int64ID uint64 = 7592915492740740150 // "64_bit_i" (little endian)
  20. float64ID uint64 = 7376742710626956342 // "64_bit_f" (little endian)
  21. stringID uint64 = 6874584755375207263 // "_string_" (little endian)
  22. boolSliceID uint64 = 6875993255270243167 // "_[]bool_" (little endian)
  23. int64SliceID uint64 = 3762322556277578591 // "_[]int64" (little endian)
  24. float64SliceID uint64 = 7308324551835016539 // "[]double" (little endian)
  25. stringSliceID uint64 = 7453010373645655387 // "[]string" (little endian)
  26. emptyID uint64 = 7305809155345288421 // "__empty_" (little endian)
  27. )
  28. // hashKVs returns a new xxHash64 hash of kvs.
  29. func hashKVs(kvs []KeyValue) uint64 {
  30. h := xxhash.New()
  31. for _, kv := range kvs {
  32. h = hashKV(h, kv)
  33. }
  34. return h.Sum64()
  35. }
  36. // hashKV returns the xxHash64 hash of kv with h as the base.
  37. func hashKV(h xxhash.Hash, kv KeyValue) xxhash.Hash {
  38. h = h.String(string(kv.Key))
  39. switch kv.Value.Type() {
  40. case BOOL:
  41. h = h.Uint64(boolID)
  42. h = h.Uint64(kv.Value.numeric)
  43. case INT64:
  44. h = h.Uint64(int64ID)
  45. h = h.Uint64(kv.Value.numeric)
  46. case FLOAT64:
  47. h = h.Uint64(float64ID)
  48. // Assumes numeric stored with math.Float64bits.
  49. h = h.Uint64(kv.Value.numeric)
  50. case STRING:
  51. h = h.Uint64(stringID)
  52. h = h.String(kv.Value.stringly)
  53. case BOOLSLICE:
  54. h = h.Uint64(boolSliceID)
  55. rv := reflect.ValueOf(kv.Value.slice)
  56. for i := 0; i < rv.Len(); i++ {
  57. h = h.Bool(rv.Index(i).Bool())
  58. }
  59. case INT64SLICE:
  60. h = h.Uint64(int64SliceID)
  61. rv := reflect.ValueOf(kv.Value.slice)
  62. for i := 0; i < rv.Len(); i++ {
  63. h = h.Int64(rv.Index(i).Int())
  64. }
  65. case FLOAT64SLICE:
  66. h = h.Uint64(float64SliceID)
  67. rv := reflect.ValueOf(kv.Value.slice)
  68. for i := 0; i < rv.Len(); i++ {
  69. h = h.Float64(rv.Index(i).Float())
  70. }
  71. case STRINGSLICE:
  72. h = h.Uint64(stringSliceID)
  73. rv := reflect.ValueOf(kv.Value.slice)
  74. for i := 0; i < rv.Len(); i++ {
  75. h = h.String(rv.Index(i).String())
  76. }
  77. case EMPTY:
  78. h = h.Uint64(emptyID)
  79. default:
  80. // Logging is an alternative, but using the internal logger here
  81. // causes an import cycle so it is not done.
  82. v := kv.Value.AsInterface()
  83. msg := fmt.Sprintf("unknown value type: %[1]v (%[1]T)", v)
  84. panic(msg)
  85. }
  86. return h
  87. }