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 }