Skip to content

Commit

Permalink
radixdb: support the parent restructure case
Browse files Browse the repository at this point in the history
Resolves #1
  • Loading branch information
toru committed Oct 5, 2024
1 parent ce9afd7 commit 3170c65
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 9 deletions.
37 changes: 33 additions & 4 deletions radixdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,30 @@ func (rdb *RadixDB) Insert(key []byte, value any) error {
return ErrDuplicateKey
}

// newNode's key matches the longest common prefix and is shorter than
// the current node's key. Therefore, newNode logically becomes the
// parent of the current node, which requires restructuring the tree.
//
// For example, suppose newNode.key is "app" and current.key is "apple".
// The common prefix is "app", and thus "app" becomes the parent of "le".
if len(prefix) == len(newNode.key) && len(prefix) < len(current.key) {
current.key = current.key[len(newNode.key):]
newNode.children = append(newNode.children, current)

if parent == nil {
rdb.root = newNode
} else {
for i, child := range parent.children {
if child == current {
parent.children[i] = newNode
}
}
}

rdb.numNodes += 1
return nil
}

// Partial match: Insert newNode by splitting the curent node.
// Meeting this condition means that the key has been exhausted.
if len(prefix) > 0 && len(prefix) < len(current.key) {
Expand All @@ -101,13 +125,18 @@ func (rdb *RadixDB) Insert(key []byte, value any) error {
nextNode := current.findCompatibleChild(key)

if nextNode == nil {
if len(current.children) > 0 {
current.children = append(current.children, newNode)
rdb.numNodes += 1
newNode.key = key

if parent == nil {
rdb.root = &node{
key: prefix,
children: []*node{current, newNode},
}
} else {
rdb.splitNode(parent, current, newNode, prefix)
parent.children = append(parent.children, newNode)
}

rdb.numNodes += 1
return nil
}

Expand Down
33 changes: 28 additions & 5 deletions radixdb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package radixdb

import (
"bytes"
"crypto/rand"
mrand "math/rand/v2"
"testing"
)

Expand Down Expand Up @@ -78,7 +80,7 @@ func TestSplitNode(t *testing.T) {
rdb.splitNode(nil, rdb.root, newNode, commonPrefix)

if rdb.Len() != 1 && len(rdb.root.children) != 1 {
t.Errorf("tree size: got:%d, want:1", rdb.Len())
t.Errorf("Len(): got:%d, want:1", rdb.Len())
}

if !bytes.Equal(rdb.root.key, commonPrefix) {
Expand All @@ -100,7 +102,7 @@ func TestSplitNode(t *testing.T) {
rdb.splitNode(rdb.root, newNode, strawberryNode, commonPrefix)

if rdb.Len() != 2 && len(rdb.root.children) != 2 {
t.Errorf("tree size: got:%d, want:2", rdb.Len())
t.Errorf("Len(): got:%d, want:2", rdb.Len())
}

// newNode should now be further split to "st[ore]".
Expand Down Expand Up @@ -136,7 +138,7 @@ func TestInsert(t *testing.T) {
}

if len := rdb.Len(); len != 1 {
t.Errorf("expected Len(): got:%d, want:1", len)
t.Errorf("Len(): got:%d, want:1", len)
}

// Test non-common key insertion. The node should be a direct child of root.
Expand All @@ -157,7 +159,7 @@ func TestInsert(t *testing.T) {
}

if len := rdb.Len(); len != 2 {
t.Errorf("expected Len(): got:%d, want:2", len)
t.Errorf("Len(): got:%d, want:2", len)
}

// Test common prefix insertion.
Expand All @@ -174,6 +176,27 @@ func TestInsert(t *testing.T) {
}

if len := rdb.Len(); len != 5 {
t.Errorf("expected Len(): got:%d, want:5", len)
t.Errorf("Len(): got:%d, want:5", len)
}

// Mild fuzzing: Insert random keys for memory errors.
numRandomInserts := 2000
numRecordsBefore := rdb.Len()
numRecordsExpected := uint64(numRandomInserts + int(numRecordsBefore))

for i := 0; i < numRandomInserts; i++ {
// Random key length between 1 and 32 bytes.
keyLength := mrand.IntN(32-1) + 1
randomKey := make([]byte, keyLength)

if _, err := rand.Read(randomKey); err != nil {
t.Fatal(err)
}

rdb.Insert(randomKey, randomKey)
}

if len := rdb.Len(); len != numRecordsExpected {
t.Errorf("Len(): got:%d, want:%d", len, numRecordsExpected)
}
}

0 comments on commit 3170c65

Please sign in to comment.