github.com/pingcap/badger@v1.5.1-0.20230103063557-828f39b09b6d/table/memtable/arena.go (about) 1 /* 2 * Copyright 2017 Dgraph Labs, Inc. and Contributors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package memtable 18 19 import ( 20 "encoding/binary" 21 "sync/atomic" 22 "unsafe" 23 24 "github.com/pingcap/badger/y" 25 ) 26 27 const ( 28 offsetSize = int(unsafe.Sizeof(uint32(0))) 29 30 // Always align nodes on 64-bit boundaries, even on 32-bit architectures, 31 // so that the node.value field is 64-bit aligned. This is necessary because 32 // node.getValueAddr uses atomic.LoadUint64, which expects its input 33 // pointer to be 64-bit aligned. 34 nodeAlign = int(unsafe.Sizeof(uint64(0))) - 1 35 ) 36 37 // Arena should be lock-free. 38 type arena struct { 39 n uint32 40 buf []byte 41 } 42 43 // newArena returns a new arena. 44 func newArena(n int64) *arena { 45 // Don't store data at position 0 in order to reserve offset=0 as a kind 46 // of nil pointer. 47 out := &arena{ 48 n: 1, 49 buf: make([]byte, n), 50 } 51 return out 52 } 53 54 func (s *arena) size() int64 { 55 return int64(atomic.LoadUint32(&s.n)) 56 } 57 58 func (s *arena) reset() { 59 atomic.StoreUint32(&s.n, 0) 60 } 61 62 // putNode allocates a node in the arena. The node is aligned on a pointer-sized 63 // boundary. The arena offset of the node is returned. 64 func (s *arena) putNode(height int) uint32 { 65 // Compute the amount of the tower that will never be used, since the height 66 // is less than maxHeight. 67 unusedSize := (maxHeight - height) * offsetSize 68 69 // Pad the allocation with enough bytes to ensure pointer alignment. 70 l := uint32(MaxNodeSize - unusedSize + nodeAlign) 71 n := atomic.AddUint32(&s.n, l) 72 y.Assert(int(n) <= len(s.buf)) 73 74 // Return the aligned offset. 75 m := (n - l + uint32(nodeAlign)) & ^uint32(nodeAlign) 76 return m 77 } 78 79 // Put will *copy* val into arena. To make better use of this, reuse your input 80 // val buffer. Returns an offset into buf. User is responsible for remembering 81 // size of val. We could also store this size inside arena but the encoding and 82 // decoding will incur some overhead. 83 func (s *arena) putVal(v y.ValueStruct) uint32 { 84 l := v.EncodedSize() 85 n := atomic.AddUint32(&s.n, l) 86 y.Assert(int(n) <= len(s.buf)) 87 m := n - l 88 v.Encode(s.buf[m:]) 89 return m 90 } 91 92 func (s *arena) putKey(key []byte) uint32 { 93 l := uint32(len(key)) 94 n := atomic.AddUint32(&s.n, l) 95 y.Assert(int(n) <= len(s.buf)) 96 m := n - l 97 copy(s.buf[m:], key) 98 return m 99 } 100 101 // getNode returns a pointer to the node located at offset. If the offset is 102 // zero, then the nil node pointer is returned. 103 func (s *arena) getNode(offset uint32) *node { 104 if offset == 0 { 105 return nil 106 } 107 108 return (*node)(unsafe.Pointer(&s.buf[offset])) 109 } 110 111 // getKey returns byte slice at offset. 112 func (s *arena) getKey(offset uint32, size uint16) (k []byte) { 113 return s.buf[offset : offset+uint32(size)] 114 } 115 116 // getVal returns byte slice at offset. The given size should be just the value 117 // size and should NOT include the meta bytes. 118 func (s *arena) getVal(offset uint32, size uint32) (ret y.ValueStruct) { 119 ret.Decode(s.buf[offset : offset+size]) 120 return 121 } 122 123 func (s *arena) fillVal(vs *y.ValueStruct, offset uint32, size uint32) { 124 vs.Decode(s.buf[offset : offset+size]) 125 } 126 127 // getNodeOffset returns the offset of node in the arena. If the node pointer is 128 // nil, then the zero offset is returned. 129 func (s *arena) getNodeOffset(nd *node) uint32 { 130 if nd == nil { 131 return 0 132 } 133 134 return uint32(uintptr(unsafe.Pointer(nd)) - uintptr(unsafe.Pointer(&s.buf[0]))) 135 } 136 137 const valueNodeSize = uint32(unsafe.Sizeof(valueNode{})) 138 139 // valueNode is used to store multiple versions of a key. 140 // We enforce that the newly put value must have a greater version, so the single linked list is in descending order 141 // by version. 142 type valueNode struct { 143 valAddr uint64 144 nextValAddr uint64 145 } 146 147 func (vn valueNode) encode(b []byte) { 148 binary.LittleEndian.PutUint64(b, vn.valAddr) 149 binary.LittleEndian.PutUint64(b[8:], vn.nextValAddr) 150 } 151 152 func (vn *valueNode) decode(b []byte) { 153 vn.valAddr = binary.LittleEndian.Uint64(b) 154 vn.nextValAddr = binary.LittleEndian.Uint64(b[8:]) 155 } 156 157 func (s *arena) putValueNode(vn valueNode) uint32 { 158 n := atomic.AddUint32(&s.n, valueNodeSize) 159 y.Assert(int(n) <= len(s.buf)) 160 m := n - valueNodeSize 161 vn.encode(s.buf[m:]) 162 return m 163 } 164 165 func (s *arena) getValueNode(offset uint32) valueNode { 166 var vl valueNode 167 vl.decode(s.buf[offset : offset+valueNodeSize]) 168 return vl 169 }