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  }