Commit 863312bc authored by Kirill Smelkov's avatar Kirill Smelkov

X Start reworking BTree to provide keycov on visit callback

parent 87199da2
...@@ -65,7 +65,7 @@ type BTree struct { ...@@ -65,7 +65,7 @@ type BTree struct {
// Entry is one BTree node entry. // Entry is one BTree node entry.
// //
// It contains key and child, who is either BTree or Bucket. // It contains key and child, which is either BTree or Bucket.
// //
// Key limits child's keys - see BTree.Entryv for details. // Key limits child's keys - see BTree.Entryv for details.
type Entry struct { type Entry struct {
...@@ -102,6 +102,10 @@ type BucketEntry struct { ...@@ -102,6 +102,10 @@ type BucketEntry struct {
value interface{} value interface{}
} }
const _KeyMin KEY = math.Min<Key>
const _KeyMax KEY = math.Max<Key>
// ---- access []entry ---- // ---- access []entry ----
// Key returns BTree entry key. // Key returns BTree entry key.
...@@ -123,7 +127,7 @@ func (e *Entry) Child() Node { return e.child } ...@@ -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. // 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. // The caller must not modify returned array.
func (t *BTree) Entryv() []Entry { func (t *BTree) Entryv() /*readonly*/ []Entry {
return t.data return t.data
} }
...@@ -168,36 +172,54 @@ func (t *BTree) Get(ctx context.Context, key KEY) (_ interface{}, _ bool, err er ...@@ -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. // VGet is like Get but also calls visit while traversing the tree.
// //
// Visit is called with node being activated. // 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) defer xerr.Contextf(&err, "btree(%s): get %v", t.POid(), key)
err = t.PActivate(ctx) err = t.PActivate(ctx)
if err != nil { if err != nil {
return nil, false, err return nil, false, err
} }
keycov := KeyRange{Lo: _KeyMin, Hi_: _KeyMax}
if visit != nil { if visit != nil {
visit(t) visit(t, keycov)
} }
if len(t.data) == 0 { for {
// empty btree l := len(t.data)
if l == 0 {
// empty btree (top, or in leaf)
t.PDeactivate() t.PDeactivate()
return nil, false, nil return nil, false, nil
} }
for {
// search i: K(i) ≤ k < K(i+1) ; K(0) = -∞, K(len) = +∞ // search i: K(i) ≤ k < K(i+1) ; K(0) = -∞, K(len) = +∞
i := sort.Search(len(t.data), func(i int) bool { i := sort.Search(l, func(i int) bool {
j := i + 1 j := i + 1
if j == len(t.data) { if j == len(t.data) {
return true // [len].key = +∞ return true // [len].key = +∞
} }
return key < t.data[j].key return key < t.data[j].key
}) })
// i < l
// FIXME panic index out of range (empty T without children;
// logically incorrect, but Restructure generated it once)
child := t.data[i].child 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() t.PDeactivate()
err = child.PActivate(ctx) err = child.PActivate(ctx)
if err != nil { if err != nil {
...@@ -207,7 +229,7 @@ func (t *BTree) VGet(ctx context.Context, key KEY, visit func(node Node)) (_ int ...@@ -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 // XXX verify child keys are in valid range according to parent
if visit != nil { if visit != nil {
visit(child) visit(child, keycov)
} }
switch child := child.(type) { switch child := child.(type) {
...@@ -324,6 +346,7 @@ func (t *BTree) VMaxKey(ctx context.Context, visit func(node Node)) (_ KEY, _ bo ...@@ -324,6 +346,7 @@ func (t *BTree) VMaxKey(ctx context.Context, visit func(node Node)) (_ KEY, _ bo
} }
for { for {
// FIXME need to refresh l
child := t.data[l-1].child child := t.data[l-1].child
t.PDeactivate() t.PDeactivate()
err = child.PActivate(ctx) err = child.PActivate(ctx)
...@@ -591,7 +614,7 @@ func (bt *btreeState) PySetState(pystate interface{}) (err error) { ...@@ -591,7 +614,7 @@ func (bt *btreeState) PySetState(pystate interface{}) (err error) {
var kprev int64 var kprev int64
var childrenKind int // 1 - BTree, 2 - Bucket var childrenKind int // 1 - BTree, 2 - Bucket
for i, idx := 0, 0; i < n; i++ { 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 { if i > 0 {
// key[0] is unused and not saved // key[0] is unused and not saved
key, ok = t[idx].(int64) // XXX Xint key, ok = t[idx].(int64) // XXX Xint
...@@ -646,3 +669,66 @@ func init() { ...@@ -646,3 +669,66 @@ func init() {
zodb.RegisterClass("BTrees.BTree.BTree", t(BTree{}), t(btreeState{})) zodb.RegisterClass("BTrees.BTree.BTree", t(BTree{}), t(btreeState{}))
zodb.RegisterClass("BTrees.BTree.Bucket", t(Bucket{}), t(bucketState{})) 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)
}
...@@ -28,6 +28,9 @@ out=$3 ...@@ -28,6 +28,9 @@ out=$3
kind=${KIND,,} # IO -> io kind=${KIND,,} # IO -> io
Key=${KEY^} Key=${KEY^}
KEYKIND=${KIND:0:1} # IO -> I
keykind=${KEYKIND,,} # I -> i
input=$(dirname $0)/btree.go.in input=$(dirname $0)/btree.go.in
echo "// Code generated by gen-btree; DO NOT EDIT." >$out echo "// Code generated by gen-btree; DO NOT EDIT." >$out
...@@ -45,4 +48,10 @@ sed \ ...@@ -45,4 +48,10 @@ sed \
-e "s/\bBucketEntry\b/${KIND}BucketEntry/g" \ -e "s/\bBucketEntry\b/${KIND}BucketEntry/g" \
-e "s/\bbtreeState\b/${kind}btreeState/g" \ -e "s/\bbtreeState\b/${kind}btreeState/g" \
-e "s/\bbucketState\b/${kind}bucketState/g" \ -e "s/\bbucketState\b/${kind}bucketState/g" \
-e "s/\b_KeyMin\b/_${KEYKIND}KeyMin/g" \
-e "s/\b_KeyMax\b/_${KEYKIND}KeyMax/g" \
-e "s/\bKeyRange\b/_${KEYKIND}KeyRange/g" \
-e "s/\bkmin\b/${keykind}kmin/g" \
-e "s/\bkmax\b/${keykind}kmax/g" \
-e "s/\bkstr\b/${keykind}kstr/g" \
$input >>$out $input >>$out
...@@ -67,7 +67,7 @@ type IOBTree struct { ...@@ -67,7 +67,7 @@ type IOBTree struct {
// IOEntry is one IOBTree node entry. // IOEntry is one IOBTree node entry.
// //
// It contains key and child, who is either IOBTree or IOBucket. // It contains key and child, which is either IOBTree or IOBucket.
// //
// Key limits child's keys - see IOBTree.Entryv for details. // Key limits child's keys - see IOBTree.Entryv for details.
type IOEntry struct { type IOEntry struct {
...@@ -104,6 +104,10 @@ type IOBucketEntry struct { ...@@ -104,6 +104,10 @@ type IOBucketEntry struct {
value interface{} value interface{}
} }
const _IKeyMin int32 = math.MinInt32
const _IKeyMax int32 = math.MaxInt32
// ---- access []entry ---- // ---- access []entry ----
// Key returns IOBTree entry key. // Key returns IOBTree entry key.
...@@ -125,7 +129,7 @@ func (e *IOEntry) Child() IONode { return e.child } ...@@ -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. // 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. // The caller must not modify returned array.
func (t *IOBTree) Entryv() []IOEntry { func (t *IOBTree) Entryv() /*readonly*/ []IOEntry {
return t.data return t.data
} }
...@@ -170,36 +174,54 @@ func (t *IOBTree) Get(ctx context.Context, key int32) (_ interface{}, _ bool, er ...@@ -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. // VGet is like Get but also calls visit while traversing the tree.
// //
// Visit is called with node being activated. // 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) defer xerr.Contextf(&err, "btree(%s): get %v", t.POid(), key)
err = t.PActivate(ctx) err = t.PActivate(ctx)
if err != nil { if err != nil {
return nil, false, err return nil, false, err
} }
keycov := _IKeyRange{Lo: _IKeyMin, Hi_: _IKeyMax}
if visit != nil { if visit != nil {
visit(t) visit(t, keycov)
} }
if len(t.data) == 0 { for {
// empty btree l := len(t.data)
if l == 0 {
// empty btree (top, or in leaf)
t.PDeactivate() t.PDeactivate()
return nil, false, nil return nil, false, nil
} }
for {
// search i: K(i) ≤ k < K(i+1) ; K(0) = -∞, K(len) = +∞ // search i: K(i) ≤ k < K(i+1) ; K(0) = -∞, K(len) = +∞
i := sort.Search(len(t.data), func(i int) bool { i := sort.Search(l, func(i int) bool {
j := i + 1 j := i + 1
if j == len(t.data) { if j == len(t.data) {
return true // [len].key = +∞ return true // [len].key = +∞
} }
return key < t.data[j].key return key < t.data[j].key
}) })
// i < l
// FIXME panic index out of range (empty T without children;
// logically incorrect, but Restructure generated it once)
child := t.data[i].child 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() t.PDeactivate()
err = child.PActivate(ctx) err = child.PActivate(ctx)
if err != nil { if err != nil {
...@@ -209,7 +231,7 @@ func (t *IOBTree) VGet(ctx context.Context, key int32, visit func(node IONode)) ...@@ -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 // XXX verify child keys are in valid range according to parent
if visit != nil { if visit != nil {
visit(child) visit(child, keycov)
} }
switch child := child.(type) { switch child := child.(type) {
...@@ -326,6 +348,7 @@ func (t *IOBTree) VMaxKey(ctx context.Context, visit func(node IONode)) (_ int32 ...@@ -326,6 +348,7 @@ func (t *IOBTree) VMaxKey(ctx context.Context, visit func(node IONode)) (_ int32
} }
for { for {
// FIXME need to refresh l
child := t.data[l-1].child child := t.data[l-1].child
t.PDeactivate() t.PDeactivate()
err = child.PActivate(ctx) err = child.PActivate(ctx)
...@@ -593,7 +616,7 @@ func (bt *iobtreeState) PySetState(pystate interface{}) (err error) { ...@@ -593,7 +616,7 @@ func (bt *iobtreeState) PySetState(pystate interface{}) (err error) {
var kprev int64 var kprev int64
var childrenKind int // 1 - IOBTree, 2 - IOBucket var childrenKind int // 1 - IOBTree, 2 - IOBucket
for i, idx := 0, 0; i < n; i++ { for i, idx := 0, 0; i < n; i++ {
key := int64(math.MinInt32) // int32(-∞) (qualifies for ≤) key := int64(_IKeyMin) // int32(-∞) (qualifies for ≤)
if i > 0 { if i > 0 {
// key[0] is unused and not saved // key[0] is unused and not saved
key, ok = t[idx].(int64) // XXX Xint key, ok = t[idx].(int64) // XXX Xint
...@@ -648,3 +671,66 @@ func init() { ...@@ -648,3 +671,66 @@ func init() {
zodb.RegisterClass("BTrees.IOBTree.IOBTree", t(IOBTree{}), t(iobtreeState{})) zodb.RegisterClass("BTrees.IOBTree.IOBTree", t(IOBTree{}), t(iobtreeState{}))
zodb.RegisterClass("BTrees.IOBTree.IOBucket", t(IOBucket{}), t(iobucketState{})) 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)
}
...@@ -67,7 +67,7 @@ type LOBTree struct { ...@@ -67,7 +67,7 @@ type LOBTree struct {
// LOEntry is one LOBTree node entry. // LOEntry is one LOBTree node entry.
// //
// It contains key and child, who is either LOBTree or LOBucket. // It contains key and child, which is either LOBTree or LOBucket.
// //
// Key limits child's keys - see LOBTree.Entryv for details. // Key limits child's keys - see LOBTree.Entryv for details.
type LOEntry struct { type LOEntry struct {
...@@ -104,6 +104,10 @@ type LOBucketEntry struct { ...@@ -104,6 +104,10 @@ type LOBucketEntry struct {
value interface{} value interface{}
} }
const _LKeyMin int64 = math.MinInt64
const _LKeyMax int64 = math.MaxInt64
// ---- access []entry ---- // ---- access []entry ----
// Key returns LOBTree entry key. // Key returns LOBTree entry key.
...@@ -125,7 +129,7 @@ func (e *LOEntry) Child() LONode { return e.child } ...@@ -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. // 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. // The caller must not modify returned array.
func (t *LOBTree) Entryv() []LOEntry { func (t *LOBTree) Entryv() /*readonly*/ []LOEntry {
return t.data return t.data
} }
...@@ -170,36 +174,54 @@ func (t *LOBTree) Get(ctx context.Context, key int64) (_ interface{}, _ bool, er ...@@ -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. // VGet is like Get but also calls visit while traversing the tree.
// //
// Visit is called with node being activated. // 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) defer xerr.Contextf(&err, "btree(%s): get %v", t.POid(), key)
err = t.PActivate(ctx) err = t.PActivate(ctx)
if err != nil { if err != nil {
return nil, false, err return nil, false, err
} }
keycov := _LKeyRange{Lo: _LKeyMin, Hi_: _LKeyMax}
if visit != nil { if visit != nil {
visit(t) visit(t, keycov)
} }
if len(t.data) == 0 { for {
// empty btree l := len(t.data)
if l == 0 {
// empty btree (top, or in leaf)
t.PDeactivate() t.PDeactivate()
return nil, false, nil return nil, false, nil
} }
for {
// search i: K(i) ≤ k < K(i+1) ; K(0) = -∞, K(len) = +∞ // search i: K(i) ≤ k < K(i+1) ; K(0) = -∞, K(len) = +∞
i := sort.Search(len(t.data), func(i int) bool { i := sort.Search(l, func(i int) bool {
j := i + 1 j := i + 1
if j == len(t.data) { if j == len(t.data) {
return true // [len].key = +∞ return true // [len].key = +∞
} }
return key < t.data[j].key return key < t.data[j].key
}) })
// i < l
// FIXME panic index out of range (empty T without children;
// logically incorrect, but Restructure generated it once)
child := t.data[i].child 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() t.PDeactivate()
err = child.PActivate(ctx) err = child.PActivate(ctx)
if err != nil { if err != nil {
...@@ -209,7 +231,7 @@ func (t *LOBTree) VGet(ctx context.Context, key int64, visit func(node LONode)) ...@@ -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 // XXX verify child keys are in valid range according to parent
if visit != nil { if visit != nil {
visit(child) visit(child, keycov)
} }
switch child := child.(type) { switch child := child.(type) {
...@@ -326,6 +348,7 @@ func (t *LOBTree) VMaxKey(ctx context.Context, visit func(node LONode)) (_ int64 ...@@ -326,6 +348,7 @@ func (t *LOBTree) VMaxKey(ctx context.Context, visit func(node LONode)) (_ int64
} }
for { for {
// FIXME need to refresh l
child := t.data[l-1].child child := t.data[l-1].child
t.PDeactivate() t.PDeactivate()
err = child.PActivate(ctx) err = child.PActivate(ctx)
...@@ -593,7 +616,7 @@ func (bt *lobtreeState) PySetState(pystate interface{}) (err error) { ...@@ -593,7 +616,7 @@ func (bt *lobtreeState) PySetState(pystate interface{}) (err error) {
var kprev int64 var kprev int64
var childrenKind int // 1 - LOBTree, 2 - LOBucket var childrenKind int // 1 - LOBTree, 2 - LOBucket
for i, idx := 0, 0; i < n; i++ { for i, idx := 0, 0; i < n; i++ {
key := int64(math.MinInt64) // int64(-∞) (qualifies for ≤) key := int64(_LKeyMin) // int64(-∞) (qualifies for ≤)
if i > 0 { if i > 0 {
// key[0] is unused and not saved // key[0] is unused and not saved
key, ok = t[idx].(int64) // XXX Xint key, ok = t[idx].(int64) // XXX Xint
...@@ -648,3 +671,66 @@ func init() { ...@@ -648,3 +671,66 @@ func init() {
zodb.RegisterClass("BTrees.LOBTree.LOBTree", t(LOBTree{}), t(lobtreeState{})) zodb.RegisterClass("BTrees.LOBTree.LOBTree", t(LOBTree{}), t(lobtreeState{}))
zodb.RegisterClass("BTrees.LOBTree.LOBucket", t(LOBucket{}), t(lobucketState{})) 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)
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment