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  }