From 6726a9b22e404e086048472c24023f86dd81027e Mon Sep 17 00:00:00 2001 From: huangyi Date: Thu, 15 Feb 2024 11:08:59 +0800 Subject: [PATCH 1/3] add ReadOnly mode and change IsoCopy behavior --- btreeg.go | 41 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/btreeg.go b/btreeg.go index b83055d..ba87fb7 100644 --- a/btreeg.go +++ b/btreeg.go @@ -13,6 +13,7 @@ type BTreeG[T any] struct { locks bool copyItems bool isoCopyItems bool + readOnly bool less func(a, b T) bool empty T max int @@ -44,6 +45,8 @@ type Options struct { // NoLocks will disable locking. Otherwide a sync.RWMutex is used to // ensure all operations are safe across multiple goroutines. NoLocks bool + // ReadOnly marks the tree as read-only, any modifications will trigger panic. + ReadOnly bool } // New returns a new BTree @@ -58,9 +61,17 @@ func NewBTreeGOptions[T any](less func(a, b T) bool, opts Options) *BTreeG[T] { tr.locks = !opts.NoLocks tr.less = less tr.init(opts.Degree) + if opts.ReadOnly { + tr.Freeze() + } return tr } +// Freeze marks the tree as read-only. +func (tr *BTreeG[T]) Freeze() { + tr.readOnly = true +} + func (tr *BTreeG[T]) init(degree int) { if tr.min != 0 { return @@ -192,6 +203,9 @@ path_match: // SetHint sets or replace a value for a key using a path hint func (tr *BTreeG[T]) SetHint(item T, hint *PathHint) (prev T, replaced bool) { + if tr.readOnly { + panic("read-only tree") + } if tr.locks { tr.mu.Lock() prev, replaced = tr.setHint(item, hint) @@ -441,6 +455,9 @@ func (tr *BTreeG[T]) Delete(key T) (T, bool) { // deleted value. // Returns false if there was no value by that key found. func (tr *BTreeG[T]) DeleteHint(key T, hint *PathHint) (T, bool) { + if tr.readOnly { + panic("read-only tree") + } if tr.lock(true) { defer tr.unlock(true) } @@ -761,6 +778,9 @@ func (tr *BTreeG[T]) nodeDescend(cn **node[T], pivot T, hint *PathHint, // Load is for bulk loading pre-sorted items func (tr *BTreeG[T]) Load(item T) (T, bool) { + if tr.readOnly { + panic("read-only tree") + } if tr.lock(true) { defer tr.unlock(true) } @@ -849,6 +869,9 @@ func (tr *BTreeG[T]) maxMut(mut bool) (T, bool) { // PopMin removes the minimum item in tree and returns it. // Returns nil if the tree has no items. func (tr *BTreeG[T]) PopMin() (T, bool) { + if tr.readOnly { + panic("read-only tree") + } if tr.lock(true) { defer tr.unlock(true) } @@ -890,6 +913,9 @@ func (tr *BTreeG[T]) PopMin() (T, bool) { // PopMax removes the maximum item in tree and returns it. // Returns nil if the tree has no items. func (tr *BTreeG[T]) PopMax() (T, bool) { + if tr.readOnly { + panic("read-only tree") + } if tr.lock(true) { defer tr.unlock(true) } @@ -963,6 +989,9 @@ func (tr *BTreeG[T]) getAt(index int, mut bool) (T, bool) { // DeleteAt deletes the item at index. // Return nil if the tree is empty or the index is out of bounds. func (tr *BTreeG[T]) DeleteAt(index int) (T, bool) { + if tr.readOnly { + panic("read-only tree") + } if tr.lock(true) { defer tr.unlock(true) } @@ -1090,14 +1119,17 @@ func (tr *BTreeG[T]) Copy() *BTreeG[T] { } func (tr *BTreeG[T]) IsoCopy() *BTreeG[T] { - if tr.lock(true) { - defer tr.unlock(true) + if tr.lock(!tr.readOnly) { + defer tr.unlock(!tr.readOnly) + } + if !tr.readOnly { + tr.isoid = newIsoID() } - tr.isoid = newIsoID() tr2 := new(BTreeG[T]) *tr2 = *tr tr2.mu = new(sync.RWMutex) tr2.isoid = newIsoID() + tr2.readOnly = false return tr2 } @@ -1393,6 +1425,9 @@ func (tr *BTreeG[T]) nodeItems(cn **node[T], items []T, mut bool) []T { // Clear will delete all items. func (tr *BTreeG[T]) Clear() { + if tr.readOnly { + panic("read-only tree") + } if tr.lock(true) { defer tr.unlock(true) } From afb6d77bfc5690606a73d35b449b6a1163835c3b Mon Sep 17 00:00:00 2001 From: yihuang Date: Sun, 4 Jan 2026 11:30:41 +0800 Subject: [PATCH 2/3] Update btreeg.go --- btreeg.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/btreeg.go b/btreeg.go index 1fca94e..445f01f 100644 --- a/btreeg.go +++ b/btreeg.go @@ -1555,7 +1555,7 @@ func (tr *BTreeG[T]) Copy() *BTreeG[T] { func (tr *BTreeG[T]) IsoCopy() *BTreeG[T] { var mu *sync.RWMutex if tr.lock(!tr.readOnly) { - mu = new(sync.RWMutex) + mu = new(sync.RWMutex) defer tr.unlock(!tr.readOnly) } if !tr.readOnly { From 5d2331604e509e5e4e596522a336d2b8e0c59951 Mon Sep 17 00:00:00 2001 From: yihuang Date: Sun, 4 Jan 2026 11:30:47 +0800 Subject: [PATCH 3/3] Update btreeg.go --- btreeg.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/btreeg.go b/btreeg.go index 445f01f..693a62d 100644 --- a/btreeg.go +++ b/btreeg.go @@ -1553,7 +1553,7 @@ func (tr *BTreeG[T]) Copy() *BTreeG[T] { } func (tr *BTreeG[T]) IsoCopy() *BTreeG[T] { - var mu *sync.RWMutex + var mu *sync.RWMutex if tr.lock(!tr.readOnly) { mu = new(sync.RWMutex) defer tr.unlock(!tr.readOnly)