Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
N
neo
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Labels
Merge Requests
2
Merge Requests
2
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Jobs
Commits
Open sidebar
Kirill Smelkov
neo
Commits
863312bc
Commit
863312bc
authored
Oct 01, 2021
by
Kirill Smelkov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
X Start reworking BTree to provide keycov on visit callback
parent
87199da2
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
315 additions
and
48 deletions
+315
-48
go/zodb/btree/btree.go.in
go/zodb/btree/btree.go.in
+102
-16
go/zodb/btree/gen-btree
go/zodb/btree/gen-btree
+9
-0
go/zodb/btree/ziobtree.go
go/zodb/btree/ziobtree.go
+102
-16
go/zodb/btree/zlobtree.go
go/zodb/btree/zlobtree.go
+102
-16
No files found.
go/zodb/btree/btree.go.in
View file @
863312bc
...
...
@@ -65,7 +65,7 @@ type BTree struct {
//
Entry
is
one
BTree
node
entry
.
//
//
It
contains
key
and
child
,
wh
o
is
either
BTree
or
Bucket
.
//
It
contains
key
and
child
,
wh
ich
is
either
BTree
or
Bucket
.
//
//
Key
limits
child
's keys - see BTree.Entryv for details.
type Entry struct {
...
...
@@ -102,6 +102,10 @@ type BucketEntry struct {
value interface{}
}
const _KeyMin KEY = math.Min<Key>
const _KeyMax KEY = math.Max<Key>
// ---- access []entry ----
// Key returns BTree entry key.
...
...
@@ -123,7 +127,7 @@ func (e *Entry) Child() Node { return e.child }
// Children of all entries are guaranteed to be of the same kind - either all BTree, or all Bucket.
//
// The caller must not modify returned array.
func (t *BTree) Entryv() []Entry {
func (t *BTree) Entryv()
/*readonly*/
[]Entry {
return t.data
}
...
...
@@ -168,36 +172,54 @@ func (t *BTree) Get(ctx context.Context, key KEY) (_ interface{}, _ bool, err er
// VGet is like Get but also calls visit while traversing the tree.
//
// Visit is called with node being activated.
func (t *BTree) VGet(ctx context.Context, key KEY, visit func(node Node)) (_ interface{}, _ bool, err error) {
func (t *BTree) VGet(ctx context.Context, key KEY, visit func(node Node
, keycov KeyRange
)) (_ interface{}, _ bool, err error) {
defer xerr.Contextf(&err, "btree(%s): get %v", t.POid(), key)
err = t.PActivate(ctx)
if err != nil {
return nil, false, err
}
keycov := KeyRange{Lo: _KeyMin, Hi_: _KeyMax}
if visit != nil {
visit(t)
visit(t
, keycov
)
}
if len(t.data) == 0 {
// empty btree
for {
l := len(t.data)
if l == 0 {
// empty btree (top, or in leaf)
t.PDeactivate()
return nil, false, nil
}
for {
// search i: K(i) ≤ k < K(i+1) ; K(0) = -∞, K(len) = +∞
i := sort.Search(l
en(t.data)
, func(i int) bool {
i := sort.Search(l, func(i int) bool {
j := i + 1
if j == len(t.data) {
return true // [len].key = +∞
}
return key < t.data[j].key
})
// FIXME panic index out of range (empty T without children;
// logically incorrect, but Restructure generated it once)
// i < l
child := t.data[i].child
// shorten global keycov by local [lo,hi) for this child
lo := _KeyMin
if i > 0 {
lo = t.data[i].key
}
i++
hi_ := _KeyMax
if i < l {
hi_ = t.data[i].key
}
if hi_ != _KeyMax {
hi_--
}
keycov.Lo = kmax(keycov.Lo, lo)
keycov.Hi_ = kmin(keycov.Hi_, hi_)
t.PDeactivate()
err = child.PActivate(ctx)
if err != nil {
...
...
@@ -207,7 +229,7 @@ func (t *BTree) VGet(ctx context.Context, key KEY, visit func(node Node)) (_ int
// XXX verify child keys are in valid range according to parent
if visit != nil {
visit(child)
visit(child
, keycov
)
}
switch child := child.(type) {
...
...
@@ -324,6 +346,7 @@ func (t *BTree) VMaxKey(ctx context.Context, visit func(node Node)) (_ KEY, _ bo
}
for {
// FIXME need to refresh l
child := t.data[l-1].child
t.PDeactivate()
err = child.PActivate(ctx)
...
...
@@ -591,7 +614,7 @@ func (bt *btreeState) PySetState(pystate interface{}) (err error) {
var kprev int64
var childrenKind int // 1 - BTree, 2 - Bucket
for i, idx := 0, 0; i < n; i++ {
key := int64(
math.Min<Key>
) // KEY(-∞) (qualifies for ≤)
key := int64(
_KeyMin
) // KEY(-∞) (qualifies for ≤)
if i > 0 {
// key[0] is unused and not saved
key, ok = t[idx].(int64) // XXX Xint
...
...
@@ -646,3 +669,66 @@ func init() {
zodb.RegisterClass("BTrees.BTree.BTree", t(BTree{}), t(btreeState{}))
zodb.RegisterClass("BTrees.BTree.Bucket", t(Bucket{}), t(bucketState{}))
}
// XXX place
// KeyRange represents [lo,hi) key range.
type KeyRange struct {
Lo KEY
Hi_ KEY // NOTE _not_ hi) to avoid overflow at ∞; hi = hi_ + 1
}
// Has returns whether key k belongs to the range.
func (r *KeyRange) Has(k KEY) bool {
return (r.Lo <= k && k <= r.Hi_)
}
// Empty returns whether key range is empty.
func (r *KeyRange) Empty() bool {
hi := r.Hi_
if hi == _KeyMax {
// [x,∞] cannot be empty because max x is ∞ and [∞,∞] has one element: ∞
return false
}
hi++ // no overflow
return r.Lo >= hi
}
func (r KeyRange) String() string {
var shi string
if r.Hi_ == _KeyMax {
shi = kstr(r.Hi_) // ∞
} else {
shi = fmt.Sprintf("%d", r.Hi_+1)
}
return fmt.Sprintf("[%s,%s)", kstr(r.Lo), shi)
}
// XXX place
func kmin(a, b KEY) KEY {
if a < b {
return a
} else {
return b
}
}
func kmax(a, b KEY) KEY {
if a > b {
return a
} else {
return b
}
}
// kstr formats key as string.
func kstr(k KEY) string {
if k == _KeyMin {
return "-∞"
}
if k == _KeyMax {
return "∞"
}
return fmt.Sprintf("%d", k)
}
go/zodb/btree/gen-btree
View file @
863312bc
...
...
@@ -28,6 +28,9 @@ out=$3
kind
=
${
KIND
,,
}
# IO -> io
Key
=
${
KEY
^
}
KEYKIND
=
${
KIND
:0:1
}
# IO -> I
keykind
=
${
KEYKIND
,,
}
# I -> i
input
=
$(
dirname
$0
)
/btree.go.in
echo
"// Code generated by gen-btree; DO NOT EDIT."
>
$out
...
...
@@ -45,4 +48,10 @@ sed \
-e
"s/
\b
BucketEntry
\b
/
${
KIND
}
BucketEntry/g"
\
-e
"s/
\b
btreeState
\b
/
${
kind
}
btreeState/g"
\
-e
"s/
\b
bucketState
\b
/
${
kind
}
bucketState/g"
\
-e
"s/
\b
_KeyMin
\b
/_
${
KEYKIND
}
KeyMin/g"
\
-e
"s/
\b
_KeyMax
\b
/_
${
KEYKIND
}
KeyMax/g"
\
-e
"s/
\b
KeyRange
\b
/_
${
KEYKIND
}
KeyRange/g"
\
-e
"s/
\b
kmin
\b
/
${
keykind
}
kmin/g"
\
-e
"s/
\b
kmax
\b
/
${
keykind
}
kmax/g"
\
-e
"s/
\b
kstr
\b
/
${
keykind
}
kstr/g"
\
$input
>>
$out
go/zodb/btree/ziobtree.go
View file @
863312bc
...
...
@@ -67,7 +67,7 @@ type IOBTree struct {
// IOEntry is one IOBTree node entry.
//
// It contains key and child, wh
o
is either IOBTree or IOBucket.
// It contains key and child, wh
ich
is either IOBTree or IOBucket.
//
// Key limits child's keys - see IOBTree.Entryv for details.
type
IOEntry
struct
{
...
...
@@ -104,6 +104,10 @@ type IOBucketEntry struct {
value
interface
{}
}
const
_IKeyMin
int32
=
math
.
MinInt32
const
_IKeyMax
int32
=
math
.
MaxInt32
// ---- access []entry ----
// Key returns IOBTree entry key.
...
...
@@ -125,7 +129,7 @@ func (e *IOEntry) Child() IONode { return e.child }
// Children of all entries are guaranteed to be of the same kind - either all IOBTree, or all IOBucket.
//
// The caller must not modify returned array.
func
(
t
*
IOBTree
)
Entryv
()
[]
IOEntry
{
func
(
t
*
IOBTree
)
Entryv
()
/*readonly*/
[]
IOEntry
{
return
t
.
data
}
...
...
@@ -170,36 +174,54 @@ func (t *IOBTree) Get(ctx context.Context, key int32) (_ interface{}, _ bool, er
// VGet is like Get but also calls visit while traversing the tree.
//
// Visit is called with node being activated.
func
(
t
*
IOBTree
)
VGet
(
ctx
context
.
Context
,
key
int32
,
visit
func
(
node
IONode
))
(
_
interface
{},
_
bool
,
err
error
)
{
func
(
t
*
IOBTree
)
VGet
(
ctx
context
.
Context
,
key
int32
,
visit
func
(
node
IONode
,
keycov
_IKeyRange
))
(
_
interface
{},
_
bool
,
err
error
)
{
defer
xerr
.
Contextf
(
&
err
,
"btree(%s): get %v"
,
t
.
POid
(),
key
)
err
=
t
.
PActivate
(
ctx
)
if
err
!=
nil
{
return
nil
,
false
,
err
}
keycov
:=
_IKeyRange
{
Lo
:
_IKeyMin
,
Hi_
:
_IKeyMax
}
if
visit
!=
nil
{
visit
(
t
)
visit
(
t
,
keycov
)
}
if
len
(
t
.
data
)
==
0
{
// empty btree
for
{
l
:=
len
(
t
.
data
)
if
l
==
0
{
// empty btree (top, or in leaf)
t
.
PDeactivate
()
return
nil
,
false
,
nil
}
for
{
// search i: K(i) ≤ k < K(i+1) ; K(0) = -∞, K(len) = +∞
i
:=
sort
.
Search
(
l
en
(
t
.
data
)
,
func
(
i
int
)
bool
{
i
:=
sort
.
Search
(
l
,
func
(
i
int
)
bool
{
j
:=
i
+
1
if
j
==
len
(
t
.
data
)
{
return
true
// [len].key = +∞
}
return
key
<
t
.
data
[
j
]
.
key
})
// FIXME panic index out of range (empty T without children;
// logically incorrect, but Restructure generated it once)
// i < l
child
:=
t
.
data
[
i
]
.
child
// shorten global keycov by local [lo,hi) for this child
lo
:=
_IKeyMin
if
i
>
0
{
lo
=
t
.
data
[
i
]
.
key
}
i
++
hi_
:=
_IKeyMax
if
i
<
l
{
hi_
=
t
.
data
[
i
]
.
key
}
if
hi_
!=
_IKeyMax
{
hi_
--
}
keycov
.
Lo
=
ikmax
(
keycov
.
Lo
,
lo
)
keycov
.
Hi_
=
ikmin
(
keycov
.
Hi_
,
hi_
)
t
.
PDeactivate
()
err
=
child
.
PActivate
(
ctx
)
if
err
!=
nil
{
...
...
@@ -209,7 +231,7 @@ func (t *IOBTree) VGet(ctx context.Context, key int32, visit func(node IONode))
// XXX verify child keys are in valid range according to parent
if
visit
!=
nil
{
visit
(
child
)
visit
(
child
,
keycov
)
}
switch
child
:=
child
.
(
type
)
{
...
...
@@ -326,6 +348,7 @@ func (t *IOBTree) VMaxKey(ctx context.Context, visit func(node IONode)) (_ int32
}
for
{
// FIXME need to refresh l
child
:=
t
.
data
[
l
-
1
]
.
child
t
.
PDeactivate
()
err
=
child
.
PActivate
(
ctx
)
...
...
@@ -593,7 +616,7 @@ func (bt *iobtreeState) PySetState(pystate interface{}) (err error) {
var
kprev
int64
var
childrenKind
int
// 1 - IOBTree, 2 - IOBucket
for
i
,
idx
:=
0
,
0
;
i
<
n
;
i
++
{
key
:=
int64
(
math
.
MinInt32
)
// int32(-∞) (qualifies for ≤)
key
:=
int64
(
_IKeyMin
)
// int32(-∞) (qualifies for ≤)
if
i
>
0
{
// key[0] is unused and not saved
key
,
ok
=
t
[
idx
]
.
(
int64
)
// XXX Xint
...
...
@@ -648,3 +671,66 @@ func init() {
zodb
.
RegisterClass
(
"BTrees.IOBTree.IOBTree"
,
t
(
IOBTree
{}),
t
(
iobtreeState
{}))
zodb
.
RegisterClass
(
"BTrees.IOBTree.IOBucket"
,
t
(
IOBucket
{}),
t
(
iobucketState
{}))
}
// XXX place
// _IKeyRange represents [lo,hi) key range.
type
_IKeyRange
struct
{
Lo
int32
Hi_
int32
// NOTE _not_ hi) to avoid overflow at ∞; hi = hi_ + 1
}
// Has returns whether key k belongs to the range.
func
(
r
*
_IKeyRange
)
Has
(
k
int32
)
bool
{
return
(
r
.
Lo
<=
k
&&
k
<=
r
.
Hi_
)
}
// Empty returns whether key range is empty.
func
(
r
*
_IKeyRange
)
Empty
()
bool
{
hi
:=
r
.
Hi_
if
hi
==
_IKeyMax
{
// [x,∞] cannot be empty because max x is ∞ and [∞,∞] has one element: ∞
return
false
}
hi
++
// no overflow
return
r
.
Lo
>=
hi
}
func
(
r
_IKeyRange
)
String
()
string
{
var
shi
string
if
r
.
Hi_
==
_IKeyMax
{
shi
=
ikstr
(
r
.
Hi_
)
// ∞
}
else
{
shi
=
fmt
.
Sprintf
(
"%d"
,
r
.
Hi_
+
1
)
}
return
fmt
.
Sprintf
(
"[%s,%s)"
,
ikstr
(
r
.
Lo
),
shi
)
}
// XXX place
func
ikmin
(
a
,
b
int32
)
int32
{
if
a
<
b
{
return
a
}
else
{
return
b
}
}
func
ikmax
(
a
,
b
int32
)
int32
{
if
a
>
b
{
return
a
}
else
{
return
b
}
}
// ikstr formats key as string.
func
ikstr
(
k
int32
)
string
{
if
k
==
_IKeyMin
{
return
"-∞"
}
if
k
==
_IKeyMax
{
return
"∞"
}
return
fmt
.
Sprintf
(
"%d"
,
k
)
}
go/zodb/btree/zlobtree.go
View file @
863312bc
...
...
@@ -67,7 +67,7 @@ type LOBTree struct {
// LOEntry is one LOBTree node entry.
//
// It contains key and child, wh
o
is either LOBTree or LOBucket.
// It contains key and child, wh
ich
is either LOBTree or LOBucket.
//
// Key limits child's keys - see LOBTree.Entryv for details.
type
LOEntry
struct
{
...
...
@@ -104,6 +104,10 @@ type LOBucketEntry struct {
value
interface
{}
}
const
_LKeyMin
int64
=
math
.
MinInt64
const
_LKeyMax
int64
=
math
.
MaxInt64
// ---- access []entry ----
// Key returns LOBTree entry key.
...
...
@@ -125,7 +129,7 @@ func (e *LOEntry) Child() LONode { return e.child }
// Children of all entries are guaranteed to be of the same kind - either all LOBTree, or all LOBucket.
//
// The caller must not modify returned array.
func
(
t
*
LOBTree
)
Entryv
()
[]
LOEntry
{
func
(
t
*
LOBTree
)
Entryv
()
/*readonly*/
[]
LOEntry
{
return
t
.
data
}
...
...
@@ -170,36 +174,54 @@ func (t *LOBTree) Get(ctx context.Context, key int64) (_ interface{}, _ bool, er
// VGet is like Get but also calls visit while traversing the tree.
//
// Visit is called with node being activated.
func
(
t
*
LOBTree
)
VGet
(
ctx
context
.
Context
,
key
int64
,
visit
func
(
node
LONode
))
(
_
interface
{},
_
bool
,
err
error
)
{
func
(
t
*
LOBTree
)
VGet
(
ctx
context
.
Context
,
key
int64
,
visit
func
(
node
LONode
,
keycov
_LKeyRange
))
(
_
interface
{},
_
bool
,
err
error
)
{
defer
xerr
.
Contextf
(
&
err
,
"btree(%s): get %v"
,
t
.
POid
(),
key
)
err
=
t
.
PActivate
(
ctx
)
if
err
!=
nil
{
return
nil
,
false
,
err
}
keycov
:=
_LKeyRange
{
Lo
:
_LKeyMin
,
Hi_
:
_LKeyMax
}
if
visit
!=
nil
{
visit
(
t
)
visit
(
t
,
keycov
)
}
if
len
(
t
.
data
)
==
0
{
// empty btree
for
{
l
:=
len
(
t
.
data
)
if
l
==
0
{
// empty btree (top, or in leaf)
t
.
PDeactivate
()
return
nil
,
false
,
nil
}
for
{
// search i: K(i) ≤ k < K(i+1) ; K(0) = -∞, K(len) = +∞
i
:=
sort
.
Search
(
l
en
(
t
.
data
)
,
func
(
i
int
)
bool
{
i
:=
sort
.
Search
(
l
,
func
(
i
int
)
bool
{
j
:=
i
+
1
if
j
==
len
(
t
.
data
)
{
return
true
// [len].key = +∞
}
return
key
<
t
.
data
[
j
]
.
key
})
// FIXME panic index out of range (empty T without children;
// logically incorrect, but Restructure generated it once)
// i < l
child
:=
t
.
data
[
i
]
.
child
// shorten global keycov by local [lo,hi) for this child
lo
:=
_LKeyMin
if
i
>
0
{
lo
=
t
.
data
[
i
]
.
key
}
i
++
hi_
:=
_LKeyMax
if
i
<
l
{
hi_
=
t
.
data
[
i
]
.
key
}
if
hi_
!=
_LKeyMax
{
hi_
--
}
keycov
.
Lo
=
lkmax
(
keycov
.
Lo
,
lo
)
keycov
.
Hi_
=
lkmin
(
keycov
.
Hi_
,
hi_
)
t
.
PDeactivate
()
err
=
child
.
PActivate
(
ctx
)
if
err
!=
nil
{
...
...
@@ -209,7 +231,7 @@ func (t *LOBTree) VGet(ctx context.Context, key int64, visit func(node LONode))
// XXX verify child keys are in valid range according to parent
if
visit
!=
nil
{
visit
(
child
)
visit
(
child
,
keycov
)
}
switch
child
:=
child
.
(
type
)
{
...
...
@@ -326,6 +348,7 @@ func (t *LOBTree) VMaxKey(ctx context.Context, visit func(node LONode)) (_ int64
}
for
{
// FIXME need to refresh l
child
:=
t
.
data
[
l
-
1
]
.
child
t
.
PDeactivate
()
err
=
child
.
PActivate
(
ctx
)
...
...
@@ -593,7 +616,7 @@ func (bt *lobtreeState) PySetState(pystate interface{}) (err error) {
var
kprev
int64
var
childrenKind
int
// 1 - LOBTree, 2 - LOBucket
for
i
,
idx
:=
0
,
0
;
i
<
n
;
i
++
{
key
:=
int64
(
math
.
MinInt64
)
// int64(-∞) (qualifies for ≤)
key
:=
int64
(
_LKeyMin
)
// int64(-∞) (qualifies for ≤)
if
i
>
0
{
// key[0] is unused and not saved
key
,
ok
=
t
[
idx
]
.
(
int64
)
// XXX Xint
...
...
@@ -648,3 +671,66 @@ func init() {
zodb
.
RegisterClass
(
"BTrees.LOBTree.LOBTree"
,
t
(
LOBTree
{}),
t
(
lobtreeState
{}))
zodb
.
RegisterClass
(
"BTrees.LOBTree.LOBucket"
,
t
(
LOBucket
{}),
t
(
lobucketState
{}))
}
// XXX place
// _LKeyRange represents [lo,hi) key range.
type
_LKeyRange
struct
{
Lo
int64
Hi_
int64
// NOTE _not_ hi) to avoid overflow at ∞; hi = hi_ + 1
}
// Has returns whether key k belongs to the range.
func
(
r
*
_LKeyRange
)
Has
(
k
int64
)
bool
{
return
(
r
.
Lo
<=
k
&&
k
<=
r
.
Hi_
)
}
// Empty returns whether key range is empty.
func
(
r
*
_LKeyRange
)
Empty
()
bool
{
hi
:=
r
.
Hi_
if
hi
==
_LKeyMax
{
// [x,∞] cannot be empty because max x is ∞ and [∞,∞] has one element: ∞
return
false
}
hi
++
// no overflow
return
r
.
Lo
>=
hi
}
func
(
r
_LKeyRange
)
String
()
string
{
var
shi
string
if
r
.
Hi_
==
_LKeyMax
{
shi
=
lkstr
(
r
.
Hi_
)
// ∞
}
else
{
shi
=
fmt
.
Sprintf
(
"%d"
,
r
.
Hi_
+
1
)
}
return
fmt
.
Sprintf
(
"[%s,%s)"
,
lkstr
(
r
.
Lo
),
shi
)
}
// XXX place
func
lkmin
(
a
,
b
int64
)
int64
{
if
a
<
b
{
return
a
}
else
{
return
b
}
}
func
lkmax
(
a
,
b
int64
)
int64
{
if
a
>
b
{
return
a
}
else
{
return
b
}
}
// lkstr formats key as string.
func
lkstr
(
k
int64
)
string
{
if
k
==
_LKeyMin
{
return
"-∞"
}
if
k
==
_LKeyMax
{
return
"∞"
}
return
fmt
.
Sprintf
(
"%d"
,
k
)
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment