github.com/matrixorigin/matrixone@v1.2.0/pkg/common/arenaskl/node.go (about)

     1  /*
     2   * Copyright 2017 Dgraph Labs, Inc. and Contributors
     3   * Modifications copyright (C) 2017 Andy Kimball and Contributors
     4   * and copyright (C) 2024 MatrixOrigin Inc.
     5   *
     6   * Licensed under the Apache License, Version 2.0 (the "License");
     7   * you may not use this file except in compliance with the License.
     8   * You may obtain a copy of the License at
     9   *
    10   *     http://www.apache.org/licenses/LICENSE-2.0
    11   *
    12   * Unless required by applicable law or agreed to in writing, software
    13   * distributed under the License is distributed on an "AS IS" BASIS,
    14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15   * See the License for the specific language governing permissions and
    16   * limitations under the License.
    17   */
    18  
    19  package arenaskl
    20  
    21  import (
    22  	"math"
    23  	"sync/atomic"
    24  )
    25  
    26  // MaxNodeSize returns the maximum space needed for a node with the specified
    27  // key and value sizes. This could overflow a uint32, which is why a uint64
    28  // is used here. If a key/value overflows a uint32, it should not be added to
    29  // the skiplist.
    30  func MaxNodeSize(keySize, valueSize uint32) uint64 {
    31  	const maxPadding = nodeAlignment - 1
    32  	return uint64(maxNodeSize) + uint64(keySize) + uint64(valueSize) + maxPadding
    33  }
    34  
    35  type links struct {
    36  	nextOffset atomic.Uint32
    37  	prevOffset atomic.Uint32
    38  }
    39  
    40  func (l *links) init(prevOffset, nextOffset uint32) {
    41  	l.nextOffset.Store(nextOffset)
    42  	l.prevOffset.Store(prevOffset)
    43  }
    44  
    45  type node struct {
    46  	// Immutable fields, so no need to lock to access key.
    47  	keyOffset uint32
    48  	keySize   uint32
    49  	valueSize uint32
    50  
    51  	// Most nodes do not need to use the full height of the tower, since the
    52  	// probability of each successive level decreases exponentially. Because
    53  	// these elements are never accessed, they do not need to be allocated.
    54  	// Therefore, when a node is allocated in the arena, its memory footprint
    55  	// is deliberately truncated to not include unneeded tower elements.
    56  	//
    57  	// All accesses to elements should use CAS operations, with no need to lock.
    58  	tower [maxHeight]links
    59  }
    60  
    61  func newNode(
    62  	arena *Arena, height uint32, key []byte, value []byte,
    63  ) (nd *node, err error) {
    64  	if height < 1 || height > maxHeight {
    65  		panic("height cannot be less than one or greater than the max height")
    66  	}
    67  	keySize := len(key)
    68  	if int64(keySize) > math.MaxUint32 {
    69  		panic("key is too large")
    70  	}
    71  	valueSize := len(value)
    72  	if int64(len(value)) > math.MaxUint32 {
    73  		panic("value is too large")
    74  	}
    75  	if int64(len(value))+int64(keySize)+int64(maxNodeSize) > math.MaxUint32 {
    76  		panic("combined key and value size is too large")
    77  	}
    78  
    79  	nd, err = newRawNode(arena, height, uint32(keySize), uint32(valueSize))
    80  	if err != nil {
    81  		return
    82  	}
    83  
    84  	copy(nd.getKeyBytes(arena), key)
    85  	copy(nd.getValue(arena), value)
    86  	return
    87  }
    88  
    89  func newRawNode(arena *Arena, height uint32, keySize, valueSize uint32) (nd *node, err error) {
    90  	// Compute the amount of the tower that will never be used, since the height
    91  	// is less than maxHeight.
    92  	unusedSize := uint32((maxHeight - int(height)) * linksSize)
    93  	nodeSize := uint32(maxNodeSize) - unusedSize
    94  
    95  	nodeOffset, _, err := arena.alloc(nodeSize+keySize+valueSize, nodeAlignment, unusedSize)
    96  	if err != nil {
    97  		return
    98  	}
    99  
   100  	nd = (*node)(arena.getPointer(nodeOffset))
   101  	nd.keyOffset = nodeOffset + nodeSize
   102  	nd.keySize = keySize
   103  	nd.valueSize = valueSize
   104  	return
   105  }
   106  
   107  func (n *node) getKeyBytes(arena *Arena) []byte {
   108  	return arena.getBytes(n.keyOffset, n.keySize)
   109  }
   110  
   111  func (n *node) getValue(arena *Arena) []byte {
   112  	return arena.getBytes(n.keyOffset+n.keySize, uint32(n.valueSize))
   113  }
   114  
   115  func (n *node) nextOffset(h int) uint32 {
   116  	return n.tower[h].nextOffset.Load()
   117  }
   118  
   119  func (n *node) prevOffset(h int) uint32 {
   120  	return n.tower[h].prevOffset.Load()
   121  }
   122  
   123  func (n *node) casNextOffset(h int, old, val uint32) bool {
   124  	return n.tower[h].nextOffset.CompareAndSwap(old, val)
   125  }
   126  
   127  func (n *node) casPrevOffset(h int, old, val uint32) bool {
   128  	return n.tower[h].prevOffset.CompareAndSwap(old, val)
   129  }