diff --git a/node.go b/node.go index b62b386..5370de4 100644 --- a/node.go +++ b/node.go @@ -16,8 +16,11 @@ type Node[T any] interface { isLeaf() bool matchPrefix([]byte) bool getChild(int) Node[T] + incrementLazyRefCount(delta int64) + getRefCount() int64 + processRefCount() setChild(int, Node[T]) - clone(bool) Node[T] + clone(bool, bool) Node[T] setMutateCh(chan struct{}) getKey() []byte getValue() T diff --git a/node_16.go b/node_16.go index 29c234e..4201599 100644 --- a/node_16.go +++ b/node_16.go @@ -10,14 +10,16 @@ import ( ) type Node16[T any] struct { - id uint64 - partialLen uint32 - numChildren uint8 - partial []byte - keys [16]byte - children [16]Node[T] - mutateCh atomic.Pointer[chan struct{}] - leaf *NodeLeaf[T] + id uint64 + partialLen uint32 + numChildren uint8 + partial []byte + keys [16]byte + children [16]Node[T] + mutateCh atomic.Pointer[chan struct{}] + leaf *NodeLeaf[T] + refCount int64 + lazyRefCount int64 } func (n *Node16[T]) getId() uint64 { @@ -88,27 +90,45 @@ func (n *Node16[T]) getChild(index int) Node[T] { return n.children[index] } -func (n *Node16[T]) clone(keepWatch bool) Node[T] { +func (n *Node16[T]) clone(keepWatch, deep bool) Node[T] { + n.processRefCount() newNode := &Node16[T]{ partialLen: n.getPartialLen(), numChildren: n.getNumChildren(), + refCount: n.getRefCount(), } if keepWatch { newNode.setMutateCh(n.getMutateCh()) } newPartial := make([]byte, maxPrefixLen) - newNode.setNodeLeaf(n.getNodeLeaf()) + if deep { + if n.getNodeLeaf() != nil { + newNode.setNodeLeaf(n.getNodeLeaf().clone(true, true).(*NodeLeaf[T])) + } + } else { + newNode.setNodeLeaf(n.getNodeLeaf()) + } copy(newPartial, n.partial) newNode.setPartial(newPartial) newNode.setId(n.getId()) copy(newNode.keys[:], n.keys[:]) - cpy := make([]Node[T], len(n.children)) - copy(cpy, n.children[:]) - for i := 0; i < 16; i++ { - newNode.setChild(i, cpy[i]) + if deep { + cpy := make([]Node[T], len(n.children)) + copy(cpy, n.children[:]) + for i := 0; i < 16; i++ { + if cpy[i] == nil { + continue + } + newNode.setChild(i, cpy[i].clone(keepWatch, true)) + } + } else { + cpy := make([]Node[T], len(n.children)) + copy(cpy, n.children[:]) + for i := 0; i < 16; i++ { + newNode.setChild(i, cpy[i]) + } } - nodeT := Node[T](newNode) - return nodeT + return newNode } func (n *Node16[T]) getKeyLen() uint32 { @@ -210,3 +230,28 @@ func (n *Node16[T]) LowerBoundIterator() *LowerBoundIterator[T] { node: n, } } + +func (n *Node16[T]) incrementLazyRefCount(inc int64) { + n.lazyRefCount += inc +} + +func (n *Node16[T]) processRefCount() { + if n.lazyRefCount == 0 { + return + } + n.refCount += n.lazyRefCount + if n.getNodeLeaf() != nil { + n.getNodeLeaf().incrementLazyRefCount(n.lazyRefCount) + } + for _, child := range n.children { + if child != nil { + child.incrementLazyRefCount(n.lazyRefCount) + } + } + n.lazyRefCount = 0 +} + +func (n *Node16[T]) getRefCount() int64 { + n.processRefCount() + return n.refCount +} diff --git a/node_256.go b/node_256.go index 5ad5db3..08401be 100644 --- a/node_256.go +++ b/node_256.go @@ -8,13 +8,15 @@ import ( ) type Node256[T any] struct { - id uint64 - partialLen uint32 - numChildren uint8 - partial []byte - children [256]Node[T] - mutateCh atomic.Pointer[chan struct{}] - leaf *NodeLeaf[T] + id uint64 + partialLen uint32 + numChildren uint8 + partial []byte + children [256]Node[T] + mutateCh atomic.Pointer[chan struct{}] + leaf *NodeLeaf[T] + lazyRefCount int64 + refCount int64 } func (n *Node256[T]) getId() uint64 { @@ -96,23 +98,42 @@ func (n *Node256[T]) getChild(index int) Node[T] { return n.children[index] } -func (n *Node256[T]) clone(keepWatch bool) Node[T] { +func (n *Node256[T]) clone(keepWatch, deep bool) Node[T] { + n.processRefCount() newNode := &Node256[T]{ partialLen: n.getPartialLen(), numChildren: n.getNumChildren(), + refCount: n.getRefCount(), } if keepWatch { newNode.setMutateCh(n.getMutateCh()) } - newNode.setNodeLeaf(n.getNodeLeaf()) + if deep { + if n.getNodeLeaf() != nil { + newNode.setNodeLeaf(n.getNodeLeaf().clone(true, true).(*NodeLeaf[T])) + } + } else { + newNode.setNodeLeaf(n.getNodeLeaf()) + } newPartial := make([]byte, maxPrefixLen) newNode.setId(n.getId()) copy(newPartial, n.partial) newNode.setPartial(newPartial) - cpy := make([]Node[T], len(n.children)) - copy(cpy, n.children[:]) - for i := 0; i < 256; i++ { - newNode.setChild(i, cpy[i]) + if deep { + cpy := make([]Node[T], len(n.children)) + copy(cpy, n.children[:]) + for i := 0; i < 256; i++ { + if cpy[i] == nil { + continue + } + newNode.setChild(i, cpy[i].clone(keepWatch, true)) + } + } else { + cpy := make([]Node[T], len(n.children)) + copy(cpy, n.children[:]) + for i := 0; i < 256; i++ { + newNode.setChild(i, cpy[i]) + } } return newNode } @@ -206,3 +227,28 @@ func (n *Node256[T]) LowerBoundIterator() *LowerBoundIterator[T] { node: nodeT, } } + +func (n *Node256[T]) incrementLazyRefCount(inc int64) { + n.lazyRefCount += inc +} + +func (n *Node256[T]) processRefCount() { + if n.lazyRefCount == 0 { + return + } + n.refCount += n.lazyRefCount + if n.getNodeLeaf() != nil { + n.getNodeLeaf().incrementLazyRefCount(n.lazyRefCount) + } + for _, child := range n.children { + if child != nil { + child.incrementLazyRefCount(n.lazyRefCount) + } + } + n.lazyRefCount = 0 +} + +func (n *Node256[T]) getRefCount() int64 { + n.processRefCount() + return n.refCount +} diff --git a/node_4.go b/node_4.go index a5f818c..a916c6f 100644 --- a/node_4.go +++ b/node_4.go @@ -10,14 +10,16 @@ import ( ) type Node4[T any] struct { - id uint64 - partialLen uint32 - numChildren uint8 - partial []byte - keys [4]byte - children [4]Node[T] - mutateCh atomic.Pointer[chan struct{}] - leaf *NodeLeaf[T] + id uint64 + partialLen uint32 + numChildren uint8 + partial []byte + keys [4]byte + children [4]Node[T] + mutateCh atomic.Pointer[chan struct{}] + leaf *NodeLeaf[T] + refCount int64 + lazyRefCount int64 } func (n *Node4[T]) getId() uint64 { @@ -87,24 +89,43 @@ func (n *Node4[T]) getChild(index int) Node[T] { return n.children[index] } -func (n *Node4[T]) clone(keepWatch bool) Node[T] { +func (n *Node4[T]) clone(keepWatch, deep bool) Node[T] { + n.processRefCount() newNode := &Node4[T]{ partialLen: n.getPartialLen(), numChildren: n.getNumChildren(), + refCount: n.getRefCount(), } newNode.setId(n.getId()) if keepWatch { newNode.setMutateCh(n.getMutateCh()) } - newNode.setNodeLeaf(n.getNodeLeaf()) + if deep { + if n.getNodeLeaf() != nil { + newNode.setNodeLeaf(n.getNodeLeaf().clone(true, true).(*NodeLeaf[T])) + } + } else { + newNode.setNodeLeaf(n.getNodeLeaf()) + } newPartial := make([]byte, maxPrefixLen) copy(newPartial, n.partial) newNode.setPartial(newPartial) copy(newNode.keys[:], n.keys[:]) - cpy := make([]Node[T], len(n.children)) - copy(cpy, n.children[:]) - for i := 0; i < 4; i++ { - newNode.setChild(i, cpy[i]) + if deep { + cpy := make([]Node[T], len(n.children)) + copy(cpy, n.children[:]) + for i := 0; i < 4; i++ { + if cpy[i] == nil { + continue + } + newNode.setChild(i, cpy[i].clone(keepWatch, true)) + } + } else { + cpy := make([]Node[T], len(n.children)) + copy(cpy, n.children[:]) + for i := 0; i < 4; i++ { + newNode.setChild(i, cpy[i]) + } } return newNode } @@ -209,3 +230,28 @@ func (n *Node4[T]) LowerBoundIterator() *LowerBoundIterator[T] { node: n, } } + +func (n *Node4[T]) incrementLazyRefCount(inc int64) { + n.lazyRefCount += inc +} + +func (n *Node4[T]) processRefCount() { + if n.lazyRefCount == 0 { + return + } + n.refCount += n.lazyRefCount + if n.getNodeLeaf() != nil { + n.getNodeLeaf().incrementLazyRefCount(n.lazyRefCount) + } + for _, child := range n.children { + if child != nil { + child.incrementLazyRefCount(n.lazyRefCount) + } + } + n.lazyRefCount = 0 +} + +func (n *Node4[T]) getRefCount() int64 { + n.processRefCount() + return n.refCount +} diff --git a/node_48.go b/node_48.go index 5a6949c..7daeaba 100644 --- a/node_48.go +++ b/node_48.go @@ -9,14 +9,16 @@ import ( ) type Node48[T any] struct { - id uint64 - partialLen uint32 - numChildren uint8 - partial []byte - keys [256]byte - children [48]Node[T] - mutateCh atomic.Pointer[chan struct{}] - leaf *NodeLeaf[T] + id uint64 + partialLen uint32 + numChildren uint8 + partial []byte + keys [256]byte + children [48]Node[T] + mutateCh atomic.Pointer[chan struct{}] + leaf *NodeLeaf[T] + lazyRefCount int64 + refCount int64 } func (n *Node48[T]) getId() uint64 { @@ -96,24 +98,43 @@ func (n *Node48[T]) getChild(index int) Node[T] { return n.children[index] } -func (n *Node48[T]) clone(keepWatch bool) Node[T] { +func (n *Node48[T]) clone(keepWatch, deep bool) Node[T] { + n.processRefCount() newNode := &Node48[T]{ partialLen: n.getPartialLen(), numChildren: n.getNumChildren(), + refCount: n.getRefCount(), } newNode.setId(n.getId()) newPartial := make([]byte, maxPrefixLen) copy(newPartial, n.partial) newNode.setPartial(newPartial) - newNode.setNodeLeaf(n.getNodeLeaf()) + if deep { + if n.getNodeLeaf() != nil { + newNode.setNodeLeaf(n.getNodeLeaf().clone(true, true).(*NodeLeaf[T])) + } + } else { + newNode.setNodeLeaf(n.getNodeLeaf()) + } if keepWatch { newNode.setMutateCh(n.getMutateCh()) } copy(newNode.keys[:], n.keys[:]) - cpy := make([]Node[T], len(n.children)) - copy(cpy, n.children[:]) - for i := 0; i < 48; i++ { - newNode.setChild(i, cpy[i]) + if deep { + cpy := make([]Node[T], len(n.children)) + copy(cpy, n.children[:]) + for i := 0; i < 48; i++ { + if cpy[i] == nil { + continue + } + newNode.setChild(i, cpy[i].clone(keepWatch, true)) + } + } else { + cpy := make([]Node[T], len(n.children)) + copy(cpy, n.children[:]) + for i := 0; i < 48; i++ { + newNode.setChild(i, cpy[i]) + } } return newNode } @@ -214,3 +235,28 @@ func (n *Node48[T]) LowerBoundIterator() *LowerBoundIterator[T] { node: nodeT, } } + +func (n *Node48[T]) incrementLazyRefCount(inc int64) { + n.lazyRefCount += inc +} + +func (n *Node48[T]) processRefCount() { + if n.lazyRefCount == 0 { + return + } + n.refCount += n.lazyRefCount + if n.getNodeLeaf() != nil { + n.getNodeLeaf().incrementLazyRefCount(n.lazyRefCount) + } + for _, child := range n.children { + if child != nil { + child.incrementLazyRefCount(n.lazyRefCount) + } + } + n.lazyRefCount = 0 +} + +func (n *Node48[T]) getRefCount() int64 { + n.processRefCount() + return n.refCount +} diff --git a/node_leaf.go b/node_leaf.go index 017626e..fb2a9a7 100644 --- a/node_leaf.go +++ b/node_leaf.go @@ -9,10 +9,12 @@ import ( ) type NodeLeaf[T any] struct { - id uint64 - value T - key []byte - mutateCh atomic.Pointer[chan struct{}] + id uint64 + value T + key []byte + mutateCh atomic.Pointer[chan struct{}] + lazyRefCount int64 + refCount int64 } func (n *NodeLeaf[T]) getId() uint64 { @@ -118,18 +120,19 @@ func (n *NodeLeaf[T]) getChild(index int) Node[T] { return nil } -func (n *NodeLeaf[T]) clone(keepWatch bool) Node[T] { +func (n *NodeLeaf[T]) clone(keepWatch, deep bool) Node[T] { + n.processRefCount() newNode := &NodeLeaf[T]{ - key: make([]byte, len(n.getKey())), - value: n.getValue(), + key: make([]byte, len(n.getKey())), + value: n.getValue(), + refCount: n.getRefCount(), } if keepWatch { newNode.setMutateCh(n.getMutateCh()) } newNode.setId(n.getId()) copy(newNode.key[:], n.key[:]) - nodeT := Node[T](newNode) - return nodeT + return newNode } func (n *NodeLeaf[T]) setChild(int, Node[T]) { @@ -201,3 +204,17 @@ func (n *NodeLeaf[T]) LowerBoundIterator() *LowerBoundIterator[T] { node: n, } } + +func (n *NodeLeaf[T]) incrementLazyRefCount(inc int64) { + n.lazyRefCount += inc +} + +func (n *NodeLeaf[T]) processRefCount() { + n.refCount += n.lazyRefCount + n.lazyRefCount = 0 +} + +func (n *NodeLeaf[T]) getRefCount() int64 { + n.processRefCount() + return n.refCount +} diff --git a/reverse_iterator_test.go b/reverse_iterator_test.go index 29887a9..bf2e9f0 100644 --- a/reverse_iterator_test.go +++ b/reverse_iterator_test.go @@ -396,7 +396,7 @@ func TestReverseIterator_SeekPrefixWatch(t *testing.T) { ch := it.SeekPrefixWatch(key) // Change prefix - tx := r.Txn() + tx := r.Txn(false) tx.TrackMutate(true) tx.Insert(key, "value") tx.Commit() diff --git a/tree.go b/tree.go index 7ebcbc4..5b82b4f 100644 --- a/tree.go +++ b/tree.go @@ -43,6 +43,23 @@ func NewRadixTree[T any]() *RadixTree[T] { return rt } +func (t *RadixTree[T]) Clone(deep bool) *RadixTree[T] { + if deep { + nt := &RadixTree[T]{ + root: t.root.clone(true, true), + size: t.size, + maxNodeId: t.maxNodeId, + } + return nt + } + nt := &RadixTree[T]{ + root: t.root.clone(true, false), + size: t.size, + maxNodeId: t.maxNodeId, + } + return nt +} + // Len is used to return the number of elements in the tree func (t *RadixTree[T]) Len() int { return int(t.size) @@ -53,7 +70,7 @@ func (t *RadixTree[T]) GetPathIterator(path []byte) *PathIterator[T] { } func (t *RadixTree[T]) Insert(key []byte, value T) (*RadixTree[T], T, bool) { - txn := t.Txn() + txn := t.Txn(false) old, ok := txn.Insert(key, value) return txn.Commit(), old, ok } @@ -63,7 +80,7 @@ func (t *RadixTree[T]) Get(key []byte) (T, bool) { } func (t *RadixTree[T]) Delete(key []byte) (*RadixTree[T], T, bool) { - txn := t.Txn() + txn := t.Txn(false) old, ok := txn.Delete(key) return txn.Commit(), old, ok } @@ -318,7 +335,7 @@ func (t *RadixTree[T]) iterativeSearchWithWatch(key []byte) (T, bool, <-chan str } func (t *RadixTree[T]) DeletePrefix(key []byte) (*RadixTree[T], bool) { - txn := t.Txn() + txn := t.Txn(false) ok := txn.DeletePrefix(key) return txn.Commit(), ok } @@ -348,7 +365,6 @@ func (t *RadixTree[T]) DFSPrintTreeUtil(node Node[T], depth int) { for i := 0; i < depth*5; i++ { stPadding += " " } - fmt.Println() fmt.Print(stPadding + "id -> " + strconv.Itoa(int(node.getId())) + " type -> " + strconv.Itoa(int(node.getArtNodeType()))) fmt.Print(" key -> " + string(node.getKey())) fmt.Print(" partial -> " + string(node.getPartial())) diff --git a/tree_test.go b/tree_test.go index c47277e..2eed0b6 100644 --- a/tree_test.go +++ b/tree_test.go @@ -20,7 +20,7 @@ func TestRadix_HugeTxn(t *testing.T) { r := NewRadixTree[int]() // Insert way more nodes than the cache can fit - txn1 := r.Txn() + txn1 := r.Txn(false) var expect []string for i := 0; i < defaultModifiedCache*100; i++ { gen, err := uuid.GenerateUUID() @@ -54,7 +54,7 @@ func TestRadix_HugeTxn(t *testing.T) { func TestInsert_UpdateFeedback(t *testing.T) { r := NewRadixTree[any]() - txn1 := r.Txn() + txn1 := r.Txn(false) for i := 0; i < 10; i++ { var old interface{} @@ -1149,7 +1149,7 @@ func TestTrackMutate_DeletePrefix(t *testing.T) { } // Verify that deleting prefixes triggers the right set of watches - txn := r.Txn() + txn := r.Txn(false) txn.TrackMutate(true) ok := txn.DeletePrefix([]byte("foo")) @@ -1258,7 +1258,7 @@ func TestTrackMutate_SeekPrefixWatch(t *testing.T) { otherWatch := iter.SeekPrefixWatch([]byte("foo/b")) // Write to a sub-child should trigger the leaf! - txn := r.Txn() + txn := r.Txn(false) txn.TrackMutate(true) txn.Insert([]byte("foobarbaz"), nil) switch i { @@ -1315,7 +1315,7 @@ func TestTrackMutate_SeekPrefixWatch(t *testing.T) { missingWatch = iter.SeekPrefixWatch([]byte("foobarbaz")) // Delete to a sub-child should trigger the leaf! - txn = r.Txn() + txn = r.Txn(false) txn.TrackMutate(true) txn.Delete([]byte("foobarbaz")) switch i { @@ -1403,7 +1403,7 @@ func TestTrackMutate_GetWatch(t *testing.T) { t.Fatalf("bad") } // Write to a sub-child should not trigger the leaf! - txn := r.Txn() + txn := r.Txn(false) txn.TrackMutate(true) txn.Insert([]byte("foobarbaz"), nil) switch i { @@ -1454,7 +1454,7 @@ func TestTrackMutate_GetWatch(t *testing.T) { } // Write to a exactly leaf should trigger the leaf! - txn = r.Txn() + txn = r.Txn(false) txn.TrackMutate(true) txn.Insert([]byte("foobar"), nil) switch i { @@ -1511,7 +1511,7 @@ func TestTrackMutate_GetWatch(t *testing.T) { } // Delete to a sub-child should not trigger the leaf! - txn = r.Txn() + txn = r.Txn(false) txn.TrackMutate(true) txn.Delete([]byte("foobarbaz")) switch i { @@ -1561,7 +1561,7 @@ func TestTrackMutate_GetWatch(t *testing.T) { } // Write to a exactly leaf should trigger the leaf! - txn = r.Txn() + txn = r.Txn(false) txn.TrackMutate(true) txn.Delete([]byte("foobar")) switch i { @@ -1663,7 +1663,7 @@ func TestTrackMutate_HugeTxn(t *testing.T) { } // Start the transaction. - txn := r.Txn() + txn := r.Txn(false) txn.TrackMutate(true) // Add new nodes on both sides of the tree and delete enough nodes to @@ -1736,7 +1736,7 @@ func TestLenTxn(t *testing.T) { t.Fatalf("not starting with empty tree") } - txn := r.Txn() + txn := r.Txn(false) keys := []string{ "foo/bar/baz", "foo/baz/bar", @@ -1753,7 +1753,7 @@ func TestLenTxn(t *testing.T) { t.Fatalf("bad: expected %d, got %d", len(keys), r.Len()) } - txn = r.Txn() + txn = r.Txn(false) for _, k := range keys { txn.Delete([]byte(k)) } @@ -1935,7 +1935,7 @@ func BenchmarkSeekPrefixWatchART(b *testing.B) { uuid1, _ := uuid.GenerateUUID() r, _, _ = r.Insert([]byte(uuid1), n) iter := r.root.Iterator() - iter.SeekPrefixWatch([]byte(uuid1[:5])) + iter.SeekPrefixWatch([]byte("")) count := 0 for { _, _, f := iter.Next() diff --git a/txn.go b/txn.go index 92eabe7..73c705e 100644 --- a/txn.go +++ b/txn.go @@ -22,28 +22,33 @@ type Txn[T any] struct { } func (t *Txn[T]) writeNode(n Node[T], trackCh bool) Node[T] { - if n.getId() > t.oldMaxNodeId { - return n - } if trackCh { t.trackChannel(n) if n.getNodeLeaf() != nil { t.trackChannel(n.getNodeLeaf()) } } - nc := n.clone(!trackCh) + if n.getId() > t.oldMaxNodeId { + return n + } + if n.getRefCount() <= 1 { + return n + } + nc := n.clone(!trackCh, false) t.tree.maxNodeId++ nc.setId(t.tree.maxNodeId) return nc } // Txn starts a new transaction that can be used to mutate the tree -func (t *RadixTree[T]) Txn() *Txn[T] { +func (t *RadixTree[T]) Txn(clone bool) *Txn[T] { newTree := &RadixTree[T]{ - t.root, + t.root.clone(true, clone), t.size, t.maxNodeId, } + newTree.root.incrementLazyRefCount(1) + newTree.root.processRefCount() txn := &Txn[T]{ size: t.size, tree: newTree, @@ -57,7 +62,7 @@ func (t *RadixTree[T]) Txn() *Txn[T] { func (t *Txn[T]) Clone() *Txn[T] { // reset the writable node cache to avoid leaking future writes into the clone newTree := &RadixTree[T]{ - t.tree.root, + t.tree.root.clone(true, false), t.size, t.tree.maxNodeId, } @@ -97,6 +102,8 @@ func (t *Txn[T]) Insert(key []byte, value T) (T, bool) { func (t *Txn[T]) recursiveInsert(node Node[T], key []byte, value T, depth int, old *int) (Node[T], T, bool) { var zero T + node.processRefCount() + if t.tree.size == 0 { node = t.writeNode(node, true) newLeaf := t.allocNode(leafType) @@ -288,10 +295,13 @@ func (t *Txn[T]) Delete(key []byte) (T, bool) { func (t *Txn[T]) recursiveDelete(node Node[T], key []byte, depth int) (Node[T], Node[T], bool) { // Get terminated + if node == nil { return nil, nil, false } + node.processRefCount() + if node.isLeaf() { t.trackChannel(node) if leafMatches(node.getKey(), key) == 0 { @@ -389,6 +399,8 @@ func (t *Txn[T]) Commit() *RadixTree[T] { // CommitOnly is used to finalize the transaction and return a new tree, but // does not issue any notifications until Notify is called. func (t *Txn[T]) CommitOnly() *RadixTree[T] { + t.tree.root.incrementLazyRefCount(-1) + t.tree.root.processRefCount() nt := &RadixTree[T]{t.tree.root, t.size, t.tree.maxNodeId, @@ -541,15 +553,25 @@ func (t *Txn[T]) allocNode(ntype nodeType) Node[T] { var n Node[T] switch ntype { case leafType: - n = &NodeLeaf[T]{} + n = &NodeLeaf[T]{ + refCount: 1, + } case node4: - n = &Node4[T]{} + n = &Node4[T]{ + refCount: 1, + } case node16: - n = &Node16[T]{} + n = &Node16[T]{ + refCount: 1, + } case node48: - n = &Node48[T]{} + n = &Node48[T]{ + refCount: 1, + } case node256: - n = &Node256[T]{} + n = &Node256[T]{ + refCount: 1, + } default: panic("Unknown node type") }