github.com/weaviate/weaviate@v1.24.6/adapters/repos/db/lsmkv/segmentindex/tree.go (about) 1 // _ _ 2 // __ _____ __ ___ ___ __ _| |_ ___ 3 // \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \ 4 // \ V V / __/ (_| |\ V /| | (_| | || __/ 5 // \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___| 6 // 7 // Copyright © 2016 - 2024 Weaviate B.V. All rights reserved. 8 // 9 // CONTACT: hello@weaviate.io 10 // 11 12 package segmentindex 13 14 import ( 15 "bytes" 16 "encoding/binary" 17 "fmt" 18 "io" 19 "math" 20 "sort" 21 22 "github.com/pkg/errors" 23 ) 24 25 type Tree struct { 26 nodes []*Node 27 } 28 29 type Node struct { 30 Key []byte 31 Start uint64 32 End uint64 33 } 34 35 func NewTree(capacity int) Tree { 36 return Tree{ 37 nodes: make([]*Node, 0, capacity), 38 } 39 } 40 41 func NewBalanced(nodes []Node) Tree { 42 t := Tree{nodes: make([]*Node, len(nodes))} 43 44 if len(nodes) > 0 { 45 // sort the slice just once 46 sort.Slice(nodes, func(a, b int) bool { 47 return bytes.Compare(nodes[a].Key, nodes[b].Key) < 0 48 }) 49 50 t.buildBalanced(nodes, 0, 0, len(nodes)-1) 51 } 52 53 return t 54 } 55 56 func (t *Tree) buildBalanced(nodes []Node, targetPos, leftBound, rightBound int) { 57 t.grow(targetPos) 58 59 if leftBound > rightBound { 60 return 61 } 62 63 mid := (leftBound + rightBound) / 2 64 t.nodes[targetPos] = &nodes[mid] 65 66 t.buildBalanced(nodes, t.left(targetPos), leftBound, mid-1) 67 t.buildBalanced(nodes, t.right(targetPos), mid+1, rightBound) 68 } 69 70 func (t *Tree) Insert(key []byte, start, end uint64) { 71 newNode := Node{ 72 Key: key, 73 Start: start, 74 End: end, 75 } 76 77 if len(t.nodes) == 0 { 78 t.nodes = append(t.nodes, &newNode) 79 return 80 } 81 82 t.insertAt(0, newNode) 83 } 84 85 func (t *Tree) insertAt(nodeID int, newNode Node) { 86 if !t.exists(nodeID) { 87 // we are at the target and can insert now 88 t.grow(nodeID) 89 t.nodes[nodeID] = &newNode 90 return 91 } 92 93 if bytes.Equal(newNode.Key, t.nodes[nodeID].Key) { 94 // this key already exists, which is an unexpected situation for an index 95 // key 96 panic(fmt.Sprintf("duplicate key %s", newNode.Key)) 97 } 98 99 if bytes.Compare(newNode.Key, t.nodes[nodeID].Key) < 0 { 100 t.insertAt(t.left(nodeID), newNode) 101 } else { 102 t.insertAt(t.right(nodeID), newNode) 103 } 104 } 105 106 func (t *Tree) Get(key []byte) ([]byte, uint64, uint64) { 107 if len(t.nodes) == 0 { 108 return nil, 0, 0 109 } 110 111 return t.getAt(0, key) 112 } 113 114 func (t *Tree) getAt(nodeID int, key []byte) ([]byte, uint64, uint64) { 115 if !t.exists(nodeID) { 116 return nil, 0, 0 117 } 118 119 node := t.nodes[nodeID] 120 if bytes.Equal(node.Key, key) { 121 return node.Key, node.Start, node.End 122 } 123 124 if bytes.Compare(key, node.Key) < 0 { 125 return t.getAt(t.left(nodeID), key) 126 } else { 127 return t.getAt(t.right(nodeID), key) 128 } 129 } 130 131 func (t Tree) left(i int) int { 132 return 2*i + 1 133 } 134 135 func (t Tree) right(i int) int { 136 return 2*i + 2 137 } 138 139 func (t *Tree) exists(i int) bool { 140 if i >= len(t.nodes) { 141 return false 142 } 143 144 return t.nodes[i] != nil 145 } 146 147 // size calculates the exact size of this node on disk which is helpful to 148 // figure out the personal offset 149 func (n *Node) size() int { 150 if n == nil { 151 return 0 152 } 153 size := 0 154 size += 4 // uint32 for key length 155 size += len(n.Key) 156 size += 8 // uint64 startPos 157 size += 8 // uint64 endPos 158 size += 8 // int64 pointer left child 159 size += 8 // int64 pointer right child 160 return size 161 } 162 163 func (t *Tree) grow(i int) { 164 if i < len(t.nodes) { 165 return 166 } 167 168 oldSize := len(t.nodes) 169 newSize := oldSize 170 for newSize <= i { 171 newSize += oldSize 172 } 173 174 newNodes := make([]*Node, newSize) 175 copy(newNodes, t.nodes) 176 for i := range t.nodes { 177 t.nodes[i] = nil 178 } 179 180 t.nodes = newNodes 181 } 182 183 func (t *Tree) MarshalBinary() ([]byte, error) { 184 offsets, size := t.calculateDiskOffsets() 185 186 buf := bytes.NewBuffer(nil) 187 188 for i, node := range t.nodes { 189 if node == nil { 190 continue 191 } 192 193 var leftOffset int64 194 var rightOffset int64 195 196 if t.exists(t.left(i)) { 197 leftOffset = int64(offsets[t.left(i)]) 198 } else { 199 leftOffset = -1 200 } 201 202 if t.exists(t.right(i)) { 203 rightOffset = int64(offsets[t.right(i)]) 204 } else { 205 rightOffset = -1 206 } 207 208 if len(node.Key) > math.MaxUint32 { 209 return nil, errors.Errorf("max key size is %d", math.MaxUint32) 210 } 211 212 keyLen := uint32(len(node.Key)) 213 if err := binary.Write(buf, binary.LittleEndian, keyLen); err != nil { 214 return nil, err 215 } 216 if _, err := buf.Write(node.Key); err != nil { 217 return nil, err 218 } 219 if err := binary.Write(buf, binary.LittleEndian, node.Start); err != nil { 220 return nil, err 221 } 222 if err := binary.Write(buf, binary.LittleEndian, node.End); err != nil { 223 return nil, err 224 } 225 if err := binary.Write(buf, binary.LittleEndian, leftOffset); err != nil { 226 return nil, err 227 } 228 if err := binary.Write(buf, binary.LittleEndian, rightOffset); err != nil { 229 return nil, err 230 } 231 } 232 bytes := buf.Bytes() 233 if size != len(bytes) { 234 return nil, errors.Errorf("corrupt: wrote %d bytes with target %d", len(bytes), size) 235 } 236 237 return bytes, nil 238 } 239 240 func (t *Tree) MarshalBinaryInto(w io.Writer) (int64, error) { 241 offsets, size := t.calculateDiskOffsets() 242 243 // create buf just once and reuse for each iteration, each iteration 244 // overwrites every single byte of the buffer, so no initializing or 245 // resetting after a round is required. 246 buf := make([]byte, 36) // 1x uint32 + 4x uint64 247 248 for i, node := range t.nodes { 249 if node == nil { 250 continue 251 } 252 253 var leftOffset int64 254 var rightOffset int64 255 256 if t.exists(t.left(i)) { 257 leftOffset = int64(offsets[t.left(i)]) 258 } else { 259 leftOffset = -1 260 } 261 262 if t.exists(t.right(i)) { 263 rightOffset = int64(offsets[t.right(i)]) 264 } else { 265 rightOffset = -1 266 } 267 268 if len(node.Key) > math.MaxUint32 { 269 return 0, errors.Errorf("max key size is %d", math.MaxUint32) 270 } 271 272 keyLen := uint32(len(node.Key)) 273 binary.LittleEndian.PutUint32(buf[0:4], keyLen) 274 binary.LittleEndian.PutUint64(buf[4:12], node.Start) 275 binary.LittleEndian.PutUint64(buf[12:20], node.End) 276 binary.LittleEndian.PutUint64(buf[20:28], uint64(leftOffset)) 277 binary.LittleEndian.PutUint64(buf[28:36], uint64(rightOffset)) 278 279 if _, err := w.Write(buf[:4]); err != nil { 280 return 0, err 281 } 282 if _, err := w.Write(node.Key); err != nil { 283 return 0, err 284 } 285 if _, err := w.Write(buf[4:36]); err != nil { 286 return 0, err 287 } 288 } 289 290 return int64(size), nil 291 } 292 293 // returns individual offsets and total size, nil nodes are skipped 294 func (t *Tree) calculateDiskOffsets() ([]int, int) { 295 current := 0 296 out := make([]int, len(t.nodes)) 297 for i, node := range t.nodes { 298 out[i] = current 299 size := node.size() 300 current += size 301 } 302 303 return out, current 304 } 305 306 func (t *Tree) Height() int { 307 var highestElem int 308 for i := len(t.nodes) - 1; i >= 0; i-- { 309 if t.nodes[i] != nil { 310 highestElem = i 311 break 312 } 313 } 314 315 return int(math.Ceil(math.Log2(float64(highestElem)))) 316 }