pointer.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package deephash
  4. import (
  5. "net/netip"
  6. "reflect"
  7. "time"
  8. "unsafe"
  9. )
  10. // unsafePointer is an untyped pointer.
  11. // It is the caller's responsibility to call operations on the correct type.
  12. //
  13. // This pointer only ever points to a small set of kinds or types:
  14. // time.Time, netip.Addr, string, array, slice, struct, map, pointer, interface,
  15. // or a pointer to memory that is directly hashable.
  16. //
  17. // Arrays are represented as pointers to the first element.
  18. // Structs are represented as pointers to the first field.
  19. // Slices are represented as pointers to a slice header.
  20. // Pointers are represented as pointers to a pointer.
  21. //
  22. // We do not support direct operations on maps and interfaces, and instead
  23. // rely on pointer.asValue to convert the pointer back to a reflect.Value.
  24. // Conversion of an unsafe.Pointer to reflect.Value guarantees that the
  25. // read-only flag in the reflect.Value is unpopulated, avoiding panics that may
  26. // otherwise have occurred since the value was obtained from an unexported field.
  27. type unsafePointer struct{ p unsafe.Pointer }
  28. func unsafePointerOf(v reflect.Value) unsafePointer {
  29. return unsafePointer{v.UnsafePointer()}
  30. }
  31. func (p unsafePointer) isNil() bool {
  32. return p.p == nil
  33. }
  34. // pointerElem dereferences a pointer.
  35. // p must point to a pointer.
  36. func (p unsafePointer) pointerElem() unsafePointer {
  37. return unsafePointer{*(*unsafe.Pointer)(p.p)}
  38. }
  39. // sliceLen returns the slice length.
  40. // p must point to a slice.
  41. func (p unsafePointer) sliceLen() int {
  42. return (*reflect.SliceHeader)(p.p).Len
  43. }
  44. // sliceArray returns a pointer to the underlying slice array.
  45. // p must point to a slice.
  46. func (p unsafePointer) sliceArray() unsafePointer {
  47. return unsafePointer{unsafe.Pointer((*reflect.SliceHeader)(p.p).Data)}
  48. }
  49. // arrayIndex returns a pointer to an element in the array.
  50. // p must point to an array.
  51. func (p unsafePointer) arrayIndex(index int, size uintptr) unsafePointer {
  52. return unsafePointer{unsafe.Add(p.p, uintptr(index)*size)}
  53. }
  54. // structField returns a pointer to a field in a struct.
  55. // p must pointer to a struct.
  56. func (p unsafePointer) structField(index int, offset, size uintptr) unsafePointer {
  57. return unsafePointer{unsafe.Add(p.p, offset)}
  58. }
  59. // asString casts p as a *string.
  60. func (p unsafePointer) asString() *string {
  61. return (*string)(p.p)
  62. }
  63. // asTime casts p as a *time.Time.
  64. func (p unsafePointer) asTime() *time.Time {
  65. return (*time.Time)(p.p)
  66. }
  67. // asAddr casts p as a *netip.Addr.
  68. func (p unsafePointer) asAddr() *netip.Addr {
  69. return (*netip.Addr)(p.p)
  70. }
  71. // asValue casts p as a reflect.Value containing a pointer to value of t.
  72. func (p unsafePointer) asValue(typ reflect.Type) reflect.Value {
  73. return reflect.NewAt(typ, p.p)
  74. }
  75. // asMemory returns the memory pointer at by p for a specified size.
  76. func (p unsafePointer) asMemory(size uintptr) []byte {
  77. return unsafe.Slice((*byte)(p.p), size)
  78. }
  79. // visitStack is a stack of pointers visited.
  80. // Pointers are pushed onto the stack when visited, and popped when leaving.
  81. // The integer value is the depth at which the pointer was visited.
  82. // The length of this stack should be zero after every hashing operation.
  83. type visitStack map[unsafe.Pointer]int
  84. func (v visitStack) seen(p unsafe.Pointer) (int, bool) {
  85. idx, ok := v[p]
  86. return idx, ok
  87. }
  88. func (v *visitStack) push(p unsafe.Pointer) {
  89. if *v == nil {
  90. *v = make(map[unsafe.Pointer]int)
  91. }
  92. (*v)[p] = len(*v)
  93. }
  94. func (v visitStack) pop(p unsafe.Pointer) {
  95. delete(v, p)
  96. }