arena.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. /*
  2. * SPDX-FileCopyrightText: © Hypermode Inc. <hello@hypermode.com>
  3. * SPDX-License-Identifier: Apache-2.0
  4. */
  5. package skl
  6. import (
  7. "sync/atomic"
  8. "unsafe"
  9. "github.com/dgraph-io/badger/v4/y"
  10. )
  11. const (
  12. offsetSize = int(unsafe.Sizeof(uint32(0)))
  13. // Always align nodes on 64-bit boundaries, even on 32-bit architectures,
  14. // so that the node.value field is 64-bit aligned. This is necessary because
  15. // node.getValueOffset uses atomic.LoadUint64, which expects its input
  16. // pointer to be 64-bit aligned.
  17. nodeAlign = int(unsafe.Sizeof(uint64(0))) - 1
  18. )
  19. // Arena should be lock-free.
  20. type Arena struct {
  21. n atomic.Uint32
  22. buf []byte
  23. }
  24. // newArena returns a new arena.
  25. func newArena(n int64) *Arena {
  26. // Don't store data at position 0 in order to reserve offset=0 as a kind
  27. // of nil pointer.
  28. out := &Arena{buf: make([]byte, n)}
  29. out.n.Store(1)
  30. return out
  31. }
  32. func (s *Arena) size() int64 {
  33. return int64(s.n.Load())
  34. }
  35. // putNode allocates a node in the arena. The node is aligned on a pointer-sized
  36. // boundary. The arena offset of the node is returned.
  37. func (s *Arena) putNode(height int) uint32 {
  38. // Compute the amount of the tower that will never be used, since the height
  39. // is less than maxHeight.
  40. unusedSize := (maxHeight - height) * offsetSize
  41. // Pad the allocation with enough bytes to ensure pointer alignment.
  42. l := uint32(MaxNodeSize - unusedSize + nodeAlign)
  43. n := s.n.Add(l)
  44. y.AssertTruef(int(n) <= len(s.buf),
  45. "Arena too small, toWrite:%d newTotal:%d limit:%d",
  46. l, n, len(s.buf))
  47. // Return the aligned offset.
  48. m := (n - l + uint32(nodeAlign)) & ^uint32(nodeAlign)
  49. return m
  50. }
  51. // Put will *copy* val into arena. To make better use of this, reuse your input
  52. // val buffer. Returns an offset into buf. User is responsible for remembering
  53. // size of val. We could also store this size inside arena but the encoding and
  54. // decoding will incur some overhead.
  55. func (s *Arena) putVal(v y.ValueStruct) uint32 {
  56. l := v.EncodedSize()
  57. n := s.n.Add(l)
  58. y.AssertTruef(int(n) <= len(s.buf),
  59. "Arena too small, toWrite:%d newTotal:%d limit:%d",
  60. l, n, len(s.buf))
  61. m := n - l
  62. v.Encode(s.buf[m:])
  63. return m
  64. }
  65. func (s *Arena) putKey(key []byte) uint32 {
  66. l := uint32(len(key))
  67. n := s.n.Add(l)
  68. y.AssertTruef(int(n) <= len(s.buf),
  69. "Arena too small, toWrite:%d newTotal:%d limit:%d",
  70. l, n, len(s.buf))
  71. // m is the offset where you should write.
  72. // n = new len - key len give you the offset at which you should write.
  73. m := n - l
  74. // Copy to buffer from m:n
  75. y.AssertTrue(len(key) == copy(s.buf[m:n], key))
  76. return m
  77. }
  78. // getNode returns a pointer to the node located at offset. If the offset is
  79. // zero, then the nil node pointer is returned.
  80. func (s *Arena) getNode(offset uint32) *node {
  81. if offset == 0 {
  82. return nil
  83. }
  84. return (*node)(unsafe.Pointer(&s.buf[offset]))
  85. }
  86. // getKey returns byte slice at offset.
  87. func (s *Arena) getKey(offset uint32, size uint16) []byte {
  88. return s.buf[offset : offset+uint32(size)]
  89. }
  90. // getVal returns byte slice at offset. The given size should be just the value
  91. // size and should NOT include the meta bytes.
  92. func (s *Arena) getVal(offset uint32, size uint32) (ret y.ValueStruct) {
  93. ret.Decode(s.buf[offset : offset+size])
  94. return
  95. }
  96. // getNodeOffset returns the offset of node in the arena. If the node pointer is
  97. // nil, then the zero offset is returned.
  98. func (s *Arena) getNodeOffset(nd *node) uint32 {
  99. if nd == nil {
  100. return 0
  101. }
  102. return uint32(uintptr(unsafe.Pointer(nd)) - uintptr(unsafe.Pointer(&s.buf[0])))
  103. }