Commit 57be0126 authored by Kirill Smelkov's avatar Kirill Smelkov

X RangedMap - like RangedSet but for dict

This data structure will be used to implement concurrency in ΔBtail.

* t2+RangedMap: (45 commits)
  .
  .
  .
  X fixes for empty set/range
  .
  .
  .
  .
  X Tests for Get
  .
  X SetRange draftly works
  .
  .
  .
  .
  .
  .
  .
  X need to keep Value first so that sizeof(set-entry) = sizeof(KeyRange)
  .
  ...
parents 1a2c3afd d5d1b9b3
#!/bin/bash -e
# rangemap.go.in -> specialized with concrete types
# gen-rangemap TYPE VALUE out
# Copyright (C) 2018-2021 Nexedi SA and Contributors.
# Kirill Smelkov <kirr@nexedi.com>
#
# This program is free software: you can Use, Study, Modify and Redistribute
# it under the terms of the GNU General Public License version 3, or (at your
# option) any later version, as published by the Free Software Foundation.
#
# You can also Link and Combine this program with other software covered by
# the terms of any of the Free Software licenses or any of the Open Source
# Initiative approved licenses and Convey the resulting work. Corresponding
# source of such a combination shall include the source code for all other
# software used.
#
# This program is distributed WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# See COPYING file for full licensing terms.
# See https://www.nexedi.com/licensing for rationale and options.
TYPE=$1
VALUE=$2
out=$3
input=$(dirname $0)/rangemap.go.in
echo "// Code generated by gen-rangemap $TYPE $VALUE; DO NOT EDIT." >$out
echo >>$out
sed \
-e "s/VALUE/$VALUE/g" \
-e "s/\bRangedMap\b/${TYPE}/g" \
-e "s/\bRangedMapEntry\b/${TYPE}Entry/g" \
-e "s/\bvInsert\b/vInsert_${TYPE}/g" \
-e "s/\bvDeleteSlice\b/vDeleteSlice_${TYPE}/g" \
-e "s/\bvReplaceSlice\b/vReplaceSlice_${TYPE}/g"\
-e "s/\btraceRangeMap\b/trace${TYPE}/g" \
-e "s/\bdebugRangeMap\b/debug${TYPE}/g" \
-e "s/\bdebugfRMap\b/debugf${TYPE}/g" \
$input >>$out
// Copyright (C) 2021 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
// it under the terms of the GNU General Public License version 3, or (at your
// option) any later version, as published by the Free Software Foundation.
//
// You can also Link and Combine this program with other software covered by
// the terms of any of the Free Software licenses or any of the Open Source
// Initiative approved licenses and Convey the resulting work. Corresponding
// source of such a combination shall include the source code for all other
// software used.
//
// This program is distributed WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// See COPYING file for full licensing terms.
// See https://www.nexedi.com/licensing for rationale and options.
package blib
// range of keys.
import (
"fmt"
)
// 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)
}
This diff is collapsed.
// Copyright (C) 2021 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
// it under the terms of the GNU General Public License version 3, or (at your
// option) any later version, as published by the Free Software Foundation.
//
// You can also Link and Combine this program with other software covered by
// the terms of any of the Free Software licenses or any of the Open Source
// Initiative approved licenses and Convey the resulting work. Corresponding
// source of such a combination shall include the source code for all other
// software used.
//
// This program is distributed WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// See COPYING file for full licensing terms.
// See https://www.nexedi.com/licensing for rationale and options.
package blib
//go:generate ./gen-rangemap _RangedMap_str string zrangemap_str.go
import (
"reflect"
"testing"
)
type (
RangedMap = _RangedMap_str
RangedMapEntry = _RangedMap_strEntry
)
const (
oo = KeyMax
noo = KeyMin
)
func TestRangedMap(t *testing.T) {
type testEntry struct {
M *RangedMap
X RangedMapEntry
Set *RangedMap // M.SetRange(X.keycov, X.value)
Del *RangedMap // M.DelRange(X.keycov)
Has bool // M.HasRange(X.keycov)
}
E := func(M *RangedMap, X RangedMapEntry, S, D *RangedMap, H bool) testEntry {
return testEntry{M, X, S, D, H}
}
// M is shorthand to create RangedMap, e.g. M(1,2,a, 3,4,b) will return {[1,2):a [3,4):b}.
M := func(argv ...interface{}) *RangedMap {
l := len(argv)
if l % 3 != 0 {
panic("non 3x number of arguments")
}
// asKey converts arg to Key
asKey := func(arg interface{}) Key {
// go through reflect to accept all int, int64, Key, ...
return Key(reflect.ValueOf(arg).Int())
}
M := &RangedMap{}
for i := 0; i < l/3; i++ {
// construct .entryv directly, not via SetRange
lo := asKey(argv[3*i])
hi := asKey(argv[3*i+1])
v := argv[3*i+2].(string)
hi_ := hi
if hi_ != oo {
hi_--
}
M.entryv = append(M.entryv, RangedMapEntry{
v,
KeyRange{lo, hi_},
})
}
M.verify()
return M
}
// X creates RangedMapEntry{v, [lo,hi)}
X := func(lo,hi Key, v string) RangedMapEntry {
hi_ := hi
if hi_ != oo {
hi_--
}
return RangedMapEntry{v, KeyRange{lo, hi_}}
}
// y, n alias true/false
const y, n = true, false
// a, b, c, ... are shorthands for corresponding strings
const a, b, c, x = "a", "b", "c", "x"
testv := []testEntry{
// empty vs empty
E(
M(), // M
X(0,0,x), // X
M(), // Set
M(), // Del
y), // Has
// empty vs !empty
E(
M(), // M
X(1,2,x), // X
M(1,2,x), // Set
M(), // Del
n), // Has
// !empty vs empty
E(
M(1,2,a), // M
X(0,0,x), // X
M(1,2,a), // Set
M(1,2,a), // Del
y), // Has
// basic change
E(
M(1,2,a), // M
X(1,2,x), // X
M(1,2,x), // Set
M(), // Del
y), // Has
// adjacent [1,3) [3,5)
E(
M(1,3,a), // M
X(3,5,x), // X
M(1,3,a, 3,5,x), // Set
M(1,3,a), // Del
n), // Has
// overlapping [1,3) [2,4)
E(
M(1,3,a), // M
X(2,4,x), // X
M(1,2,a, 2,4,x), // Set
M(1,2,a), // Del
n), // Has
// [1,7) vs [3,5) -> split
E(
M(1,7,a), // M
X(3,5,x), // X
M(1,3,a, 3,5,x, 5,7,a), // Set
M(1,3,a, 5,7,a), // Del
y), // Has
// several ranges vs [-∞,∞)
E(
M(1,3,a, 5,7,b, 8,9,c), // M
X(noo,oo,x), // X
M(noo,oo,x), // Set
M(), // Del
n), // Has
E(
M(1,2,a, 2,3,b), // M
X(1,3,x), // X
M(1,3,x), // Set
M(), // Del
y), // Has
// coalesce (same value, no overlap)
E(
M(1,2,a, 4,5,a), // M
X(2,4,a), // X
M(1,5,a), // Set
M(1,2,a, 4,5,a), // Del
n), // Has
// coalesce (same value, overlap)
E(
M(1,4,a, 5,8,a), // M
X(2,6,a), // X
M(1,8,a), // Set
M(1,2,a, 6,8,a), // Del
n), // Has
// - shrink left/right (value !same) + new entry
E(
M(1,4,a), // M
X(2,6,x), // X
M(1,2,a, 2,6,x), // Set
M(1,2,a), // Del
n), // Has
E(
M(5,8,b), // M
X(2,6,x), // X
M(2,6,x, 6,8,b), // Set
M( 6,8,b), // Del
n), // Has
E(
M(1,4,a, 5,8,b), // M
X(2,6,x), // X
M(1,2,a, 2,6,x, 6,8,b), // Set
M(1,2,a, 6,8,b), // Del
n), // Has
}
for _, tt := range testv {
M := tt.M
X := tt.X
r := X.KeyRange
v := X.Value
assertMapHasRange(t, M, r, tt.Has)
Mset := M.Clone()
Mdel := M.Clone()
Mset.SetRange(r, v)
Mdel.DelRange(r)
if !Mset.Equal(tt.Set) {
t.Errorf("SetRange:\n M: %s\n e: %s\n ->·: %s\n ok·: %s\n", M, X, Mset, tt.Set)
}
if !Mdel.Equal(tt.Del) {
t.Errorf("DelRange:\n M: %s\n r: %s\n ->·: %s\n ok·: %s\n", M, r, Mdel, tt.Del)
}
assertMapHasRange(t, Mset, r, true)
rInMdel := false
if r.Empty() {
rInMdel = true
}
assertMapHasRange(t, Mdel, r, rInMdel)
verifyGet(t, M)
verifyGet(t, Mset)
verifyGet(t, Mdel)
}
}
// assertMapHasRange asserts that RangeMap M.HasRange(r) == hasOK.
func assertMapHasRange(t *testing.T, M *RangedMap, r KeyRange, hasOK bool) {
t.Helper()
has := M.HasRange(r)
if !(has == hasOK) {
t.Errorf("HasRange:\n M: %s\n r: %s\n ->·: %t\n ok·: %t\n", M, r, has, hasOK)
}
}
// verifyGet verifies RangedMap.Get .
func verifyGet(t *testing.T, M *RangedMap) {
t.Helper()
var Mranges []RangedMapEntry
Mranges = append(Mranges, M.AllRanges()...) // copy just in case it changes on Get
// verify "has-data"
Z := KeyRange{-100,+100} // models [-∞,∞)
for _, e := range Mranges {
lo := kmax(e.Lo, Z.Lo)
hi_ := kmin(e.Hi_, Z.Hi_)
for k := lo; k <= hi_; k++ {
v, ok := M.Get_(k)
if !(v == e.Value && ok) {
t.Errorf("%s\tGet(%s):\nhave: %q, %t\nwant: %q, true",
M, KStr(k), v, ok, e.Value)
}
}
}
// verify "no-data"
// NA = [-∞,∞) \ M
na := RangedKeySet{}
na.AddRange(Z)
for _, e := range Mranges {
na.DelRange(e.KeyRange)
}
for _, r := range na.AllRanges() {
lo := kmax(r.Lo, Z.Lo)
hi_ := kmin(r.Hi_, Z.Hi_)
for k := lo; k <= hi_; k++ {
v, ok := M.Get_(k)
if !(v == "" && !ok) {
t.Errorf("%s\tGet(%s):\nhave: %q, %t\nwant: %q, false",
M, KStr(k), v, ok, "")
}
}
}
}
// kmin returns min(a,b).
func kmin(a, b Key) Key {
if a < b {
return a
} else {
return b
}
}
// kmax returns max(a,b).
func kmax(a, b Key) Key {
if a > b {
return a
} else {
return b
}
}
...@@ -20,35 +20,20 @@ ...@@ -20,35 +20,20 @@
package blib package blib
// set of [lo,hi) Key ranges. // set of [lo,hi) Key ranges.
import ( //go:generate ./gen-rangemap _RangedMap_void void zrangemap_void.go
"fmt"
"sort"
)
const traceRangeSet = false
const debugRangeSet = false
// KeyRange represents [lo,hi) Key range.
type KeyRange struct {
Lo Key
Hi_ Key // NOTE _not_ hi) to avoid overflow at ∞; hi = hi_ + 1
}
// RangedKeySet is set of Keys with adjacent keys coalesced into Ranges. // RangedKeySet is set of Keys with adjacent keys coalesced into Ranges.
// //
// Zero value represents empty set. // Zero value represents empty set.
type RangedKeySet struct { type RangedKeySet struct {
// TODO rework to use BTree lo->hi_ instead if in practice in treediff, m _RangedMap_void
// and other usage places, N(ranges) turns out to be not small
// (i.e. performance turns out to be not acceptable)
rangev []KeyRange // lo↑
} }
// void is used as value type for RangedMap to be used as set.
type void struct{}
func (_ void) String() string { return "" }
// Has returns whether key k belongs to the range.
func (r *KeyRange) Has(k Key) bool {
return (r.Lo <= k && k <= r.Hi_)
}
// Add adds key k to the set. // Add adds key k to the set.
...@@ -69,184 +54,17 @@ func (S *RangedKeySet) Has(k Key) bool { ...@@ -69,184 +54,17 @@ func (S *RangedKeySet) Has(k Key) bool {
// AddRange adds range r to the set. // AddRange adds range r to the set.
func (S *RangedKeySet) AddRange(r KeyRange) { func (S *RangedKeySet) AddRange(r KeyRange) {
if traceRangeSet { S.m.SetRange(r, void{})
fmt.Printf("\n\nAddRange:\n")
fmt.Printf(" S: %s\n", S)
fmt.Printf(" r: %s\n", r)
defer fmt.Printf("->u: %s\n", S)
}
S.verify()
defer S.verify()
// find first ilo: r.Lo < [ilo].hi
l := len(S.rangev)
ilo := sort.Search(l, func(i int) bool {
return r.Lo <= S.rangev[i].Hi_
})
debugfRSet("\tilo: %d\n", ilo)
if ilo == l { // not found
S.rangev = append(S.rangev, r)
l++
debugfRSet("\tappend %s\t-> %s\n", r, S)
}
// find last jhi: [jhi].Lo < r.hi
jhi := ilo
for ;; jhi++ {
if jhi == l {
break
}
if S.rangev[jhi].Lo <= r.Hi_ {
continue
}
break
}
debugfRSet("\tjhi: %d\n", jhi)
// entries in [ilo:jhi) ∈ [r.Lo,r.hi) and should be merged into one
if (jhi - ilo) > 1 {
lo := S.rangev[ilo].Lo
hi_ := S.rangev[jhi-1].Hi_
vReplaceSlice(&S.rangev, ilo,jhi, KeyRange{lo,hi_})
debugfRSet("\tmerge S[%d:%d]\t-> %s\n", ilo, jhi, S)
}
jhi = -1 // no longer valid
// if [r.lo,r.hi) was outside of any entry - create new entry
if r.Hi_ < S.rangev[ilo].Lo {
vInsert(&S.rangev, ilo, r)
debugfRSet("\tinsert %s\t-> %s\n", r, S)
}
// now we have covered entries merged as needed into [ilo]
// extend this entry if r coverage is wider
if r.Lo < S.rangev[ilo].Lo {
S.rangev[ilo].Lo = r.Lo
debugfRSet("\textend left\t-> %s\n", S)
}
if r.Hi_ > S.rangev[ilo].Hi_ {
S.rangev[ilo].Hi_ = r.Hi_
debugfRSet("\textend right\t-> %s\n", S)
}
// and check if we should merge it with right/left neighbours
if ilo+1 < len(S.rangev) { // right
if S.rangev[ilo].Hi_+1 == S.rangev[ilo+1].Lo {
vReplaceSlice(&S.rangev, ilo,ilo+2,
KeyRange{S.rangev[ilo].Lo, S.rangev[ilo+1].Hi_})
debugfRSet("\tmerge right\t-> %s\n", S)
}
}
if ilo > 0 { // left
if S.rangev[ilo-1].Hi_+1 == S.rangev[ilo].Lo {
vReplaceSlice(&S.rangev, ilo-1,ilo+1,
KeyRange{S.rangev[ilo-1].Lo, S.rangev[ilo].Hi_})
debugfRSet("\tmerge left\t-> %s\n", S)
}
}
// done
} }
// DelRange removes range r from the set. // DelRange removes range r from the set.
func (S *RangedKeySet) DelRange(r KeyRange) { func (S *RangedKeySet) DelRange(r KeyRange) {
if traceRangeSet { S.m.DelRange(r)
fmt.Printf("\n\nDelRange:\n")
fmt.Printf(" S: %s\n", S)
fmt.Printf(" r: %s\n", r)
defer fmt.Printf("->d: %s\n", S)
}
S.verify()
defer S.verify()
// find first ilo: r.Lo < [ilo].hi
l := len(S.rangev)
ilo := sort.Search(l, func(i int) bool {
return r.Lo <= S.rangev[i].Hi_
})
debugfRSet("\tilo: %d\n", ilo)
if ilo == l { // not found
debugfRSet("\tnon-overlap right\n")
return
}
// find last jhi: [jhi].Lo < r.hi
jhi := ilo
for ;; jhi++ {
if jhi == l {
break
}
if S.rangev[jhi].Lo <= r.Hi_ {
continue
}
break
}
debugfRSet("\tjhi: %d\n", jhi)
if jhi == 0 {
debugfRSet("\tnon-overlap left\n")
return
}
// [ilo+1:jhi-1] should be deleted
// [ilo] and [jhi-1] overlap with [r.lo,r.hi) - they should be deleted, or shrinked,
// or split+shrinked if ilo==jhi-1 and r is inside [ilo]
if jhi-ilo == 1 && S.rangev[ilo].Lo < r.Lo && r.Hi_ < S.rangev[ilo].Hi_ {
x := S.rangev[ilo]
vInsert(&S.rangev, ilo, x)
jhi++
debugfRSet("\tpresplit copy %s\t-> %s\n", x, S)
}
if S.rangev[ilo].Lo < r.Lo { // shrink left
S.rangev[ilo] = KeyRange{S.rangev[ilo].Lo, r.Lo-1}
ilo++
debugfRSet("\tshrink [%d] left\t-> %s\n", ilo, S)
}
if r.Hi_ < S.rangev[jhi-1].Hi_ { // shrink right
S.rangev[jhi-1] = KeyRange{r.Hi_+1, S.rangev[jhi-1].Hi_}
jhi--
debugfRSet("\tshrink [%d] right\t-> %s\n", jhi-1, S)
}
if (jhi - ilo) > 0 {
vDeleteSlice(&S.rangev, ilo,jhi)
debugfRSet("\tdelete S[%d:%d]\t-> %s\n", ilo, jhi, S)
}
// done
} }
// HasRange returns whether all keys from range r belong to the set. // HasRange returns whether all keys from range r belong to the set.
func (S *RangedKeySet) HasRange(r KeyRange) (yes bool) { func (S *RangedKeySet) HasRange(r KeyRange) (yes bool) {
if traceRangeSet { return S.m.HasRange(r)
fmt.Printf("\n\nHasRange:\n")
fmt.Printf(" S: %s\n", S)
fmt.Printf(" r: %s\n", r)
defer func() {
fmt.Printf("->·: %v\n", yes)
}()
}
S.verify()
// find first ilo: r.lo < [ilo].hi
l := len(S.rangev)
ilo := sort.Search(l, func(i int) bool {
return r.Lo <= S.rangev[i].Hi_
})
debugfRSet("\tilo: %d\n", ilo)
if ilo == l { // not found
return false
}
// all keys from r are in S if r ∈ [ilo]
return (S.rangev[ilo].Lo <= r.Lo && r.Hi_ <= S.rangev[ilo].Hi_)
} }
...@@ -264,7 +82,7 @@ func (A *RangedKeySet) Difference(B *RangedKeySet) *RangedKeySet { ...@@ -264,7 +82,7 @@ func (A *RangedKeySet) Difference(B *RangedKeySet) *RangedKeySet {
return D return D
} }
// XXX Intersection // TODO Intersection
func (A *RangedKeySet) UnionInplace(B *RangedKeySet) { func (A *RangedKeySet) UnionInplace(B *RangedKeySet) {
A.verify() A.verify()
...@@ -272,8 +90,8 @@ func (A *RangedKeySet) UnionInplace(B *RangedKeySet) { ...@@ -272,8 +90,8 @@ func (A *RangedKeySet) UnionInplace(B *RangedKeySet) {
defer A.verify() defer A.verify()
// XXX dumb // XXX dumb
for _, r := range B.rangev { for _, e := range B.m.entryv {
A.AddRange(r) A.AddRange(e.KeyRange)
} }
} }
...@@ -283,143 +101,57 @@ func (A *RangedKeySet) DifferenceInplace(B *RangedKeySet) { ...@@ -283,143 +101,57 @@ func (A *RangedKeySet) DifferenceInplace(B *RangedKeySet) {
defer A.verify() defer A.verify()
// XXX dumb // XXX dumb
for _, r := range B.rangev { for _, e := range B.m.entryv {
if len(A.rangev) == 0 { if A.Empty() {
break break
} }
A.DelRange(r) A.DelRange(e.KeyRange)
} }
} }
// -------- // --------
// verify check RangedKeySet for internal consistency: // verify checks RangedKeySet for internal consistency.
// - ranges must be not overlapping nor adjacent and ↑
func (S *RangedKeySet) verify() { func (S *RangedKeySet) verify() {
// TODO !debug -> return S.m.verify()
var badv []string
badf := func(format string, argv ...interface{}) {
badv = append(badv, fmt.Sprintf(format, argv...))
}
defer func() {
if badv != nil {
emsg := "S.verify: fail:\n\n"
for _, bad := range badv {
emsg += fmt.Sprintf("- %s\n", bad)
}
emsg += fmt.Sprintf("\nS: %s\n", S)
panic(emsg)
}
}()
hi_Prev := KeyMin
for i, r := range S.rangev {
hiPrev := hi_Prev + 1
if i > 0 && !(hiPrev < r.Lo) { // NOTE not ≤ - adjacent ranges must be merged
badf("[%d]: !(hiPrev < r.lo)", i)
}
if !(r.Lo <= r.Hi_) {
badf("[%d]: !(r.lo <= r.hi_)", i)
}
hi_Prev = r.Hi_
}
} }
// Clone returns copy of the set. // Clone returns copy of the set.
func (orig *RangedKeySet) Clone() *RangedKeySet { func (orig *RangedKeySet) Clone() *RangedKeySet {
klon := &RangedKeySet{} return &RangedKeySet{*orig.m.Clone()}
klon.rangev = append(klon.rangev, orig.rangev...)
return klon
} }
// Empty returns whether the set is empty. // Empty returns whether the set is empty.
func (S *RangedKeySet) Empty() bool { func (S *RangedKeySet) Empty() bool {
return len(S.rangev) == 0 return S.m.Empty()
} }
// Equal returns whether A == B. // Equal returns whether A == B.
func (A *RangedKeySet) Equal(B *RangedKeySet) bool { func (A *RangedKeySet) Equal(B *RangedKeySet) bool {
if len(A.rangev) != len(B.rangev) { return A.m.Equal(&B.m)
return false
}
for i, ra := range A.rangev {
rb := B.rangev[i]
if ra != rb {
return false
}
}
return true
} }
// Clear removes all elements from the set. // Clear removes all elements from the set.
func (S *RangedKeySet) Clear() { func (S *RangedKeySet) Clear() {
S.rangev = nil S.m.Clear()
} }
// AllRanges returns slice of all key ranges in the set. // AllRanges returns slice of all key ranges in the set.
// //
// TODO -> iter? // TODO -> iter?
func (S *RangedKeySet) AllRanges() /*readonly*/[]KeyRange { func (S *RangedKeySet) AllRanges() /*readonly*/[]KeyRange {
return S.rangev // TODO we could use unsafe and cast .m.AllRanges to []KeyRange to avoid extra alloc/copy
} // return []KeyRange(S.m.AllRanges())
ev := S.m.AllRanges()
func (S RangedKeySet) String() string { rv := make([]KeyRange, len(ev))
s := "{" for i := range ev {
for i, r := range S.rangev { rv[i] = ev[i].KeyRange
if i > 0 {
s += " "
}
s += r.String()
}
s += "}"
return s
}
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)
}
func debugfRSet(format string, argv ...interface{}) {
if !debugRangeSet {
return
} }
fmt.Printf(format, argv...) return rv
}
// ---- slice ops ----
// vInsert inserts r into *pv[i].
func vInsert(pv *[]KeyRange, i int, r KeyRange) {
v := *pv
v = append(v, KeyRange{})
copy(v[i+1:], v[i:])
v[i] = r
*pv = v
} }
// vDeleteSlice deletes *pv[lo:hi]. func (S RangedKeySet) String() string {
func vDeleteSlice(pv *[]KeyRange, lo,hi int) { // RangeMap<void> supports formatting for set out of the box
v := *pv return S.m.String()
n := copy(v[lo:], v[hi:])
v = v[:lo+n]
*pv = v
}
// vReplaceSlice replaces *pv[lo:hi] with r.
func vReplaceSlice(pv *[]KeyRange, lo,hi int, r KeyRange) {
v := *pv
n := copy(v[lo+1:], v[hi:])
v[lo] = r
v = v[:lo+1+n]
*pv = v
} }
...@@ -21,14 +21,28 @@ package blib ...@@ -21,14 +21,28 @@ package blib
import ( import (
"testing" "testing"
"unsafe"
) )
func TestRangedKeySet(t *testing.T) { func TestRangedKeySetTypes(t *testing.T) {
const ( // verify that size(void) == 0 and that _RangedMap_voidEntry has the same layout as KeyRange
oo = KeyMax sizeVoid := unsafe.Sizeof(void{})
noo = KeyMin if sizeVoid != 0 {
) t.Errorf("sizeof(void) = %d ; want 0", sizeVoid)
}
// verify that sizeof(set-entry) = sizeof(KeyRange)
// NOTE for this to be true Value needs to come first in RangedMapEntry
// (see github.com/golang/go/issues/48651)
sizeKeyRange := unsafe.Sizeof(KeyRange{})
sizeRangedMap_voidEntry := unsafe.Sizeof(_RangedMap_voidEntry{})
if sizeRangedMap_voidEntry != sizeKeyRange {
t.Errorf("sizeof(RangeMap_voidEntry)(%d) != sizeof(KeyRange)(%d)",
sizeRangedMap_voidEntry, sizeKeyRange)
}
}
func TestRangedKeySet(t *testing.T) {
type testEntry struct { type testEntry struct {
A, B *RangedKeySet A, B *RangedKeySet
Union *RangedKeySet Union *RangedKeySet
...@@ -46,14 +60,17 @@ func TestRangedKeySet(t *testing.T) { ...@@ -46,14 +60,17 @@ func TestRangedKeySet(t *testing.T) {
} }
S := &RangedKeySet{} S := &RangedKeySet{}
for i := 0; i < l/2; i++ { for i := 0; i < l/2; i++ {
// construct .rangev directly, not via AddRange // construct .entryv directly, not via AddRange
lo := kv[2*i] lo := kv[2*i]
hi := kv[2*i+1] hi := kv[2*i+1]
hi_ := hi hi_ := hi
if hi_ != oo { if hi_ != oo {
hi_-- hi_--
} }
S.rangev = append(S.rangev, KeyRange{lo, hi_}) S.m.entryv = append(S.m.entryv, _RangedMap_voidEntry{
void{},
KeyRange{lo, hi_},
})
} }
S.verify() S.verify()
return S return S
...@@ -66,6 +83,18 @@ func TestRangedKeySet(t *testing.T) { ...@@ -66,6 +83,18 @@ func TestRangedKeySet(t *testing.T) {
S(), // U S(), // U
S()), // D S()), // D
E(
S(), // A
S(1,2), // B
S(1,2), // U
S()), // D
E(
S(1,2), // A
S(), // B
S(1,2), // U
S(1,2)),// D
E( E(
S(1,2), // A S(1,2), // A
S(1,2), // B S(1,2), // B
...@@ -93,7 +122,7 @@ func TestRangedKeySet(t *testing.T) { ...@@ -93,7 +122,7 @@ func TestRangedKeySet(t *testing.T) {
S(1,7), S(1,7),
S(1,3, 5,7)), S(1,3, 5,7)),
// several ranges \ [-∞, ∞) -> ø // several ranges \ [-∞,∞) -> ø
E( E(
S(1,3, 5,7, 11,100), // A S(1,3, 5,7, 11,100), // A
S(noo, oo), // B S(noo, oo), // B
...@@ -123,29 +152,29 @@ func TestRangedKeySet(t *testing.T) { ...@@ -123,29 +152,29 @@ func TestRangedKeySet(t *testing.T) {
D := A.Difference(B) D := A.Difference(B)
if !U.Equal(tt.Union) { if !U.Equal(tt.Union) {
t.Errorf("Union:\n A: %s\n B: %s\n ->u: %s\n okU: %s\n", tt.A, tt.B, U, tt.Union) t.Errorf("Union:\n A: %s\n B: %s\n ->u: %s\n okU: %s\n", A, B, U, tt.Union)
} }
if !D.Equal(tt.Difference) { if !D.Equal(tt.Difference) {
t.Errorf("Difference:\n A: %s\n B: %s\n ->d: %s\n okD: %s\n", tt.A, tt.B, D, tt.Difference) t.Errorf("Difference:\n A: %s\n B: %s\n ->d: %s\n okD: %s\n", A, B, D, tt.Difference)
} }
// HasRange // HasRange
assertHasRange(t, A.AllRanges(), A, true) assertSetHasRanges(t, A, A.AllRanges(), true)
assertHasRange(t, B.AllRanges(), B, true) assertSetHasRanges(t, B, B.AllRanges(), true)
assertHasRange(t, A.AllRanges(), U, true) assertSetHasRanges(t, U, A.AllRanges(), true)
assertHasRange(t, B.AllRanges(), U, true) assertSetHasRanges(t, U, B.AllRanges(), true)
Dab := D Dab := D
Dba := B.Difference(A) Dba := B.Difference(A)
assertHasRange(t, Dab.AllRanges(), A, true) assertSetHasRanges(t, A, Dab.AllRanges(), true)
assertHasRange(t, Dab.AllRanges(), B, false) assertSetHasRanges(t, B, Dab.AllRanges(), false)
assertHasRange(t, Dba.AllRanges(), B, true) assertSetHasRanges(t, B, Dba.AllRanges(), true)
assertHasRange(t, Dba.AllRanges(), A, false) assertSetHasRanges(t, A, Dba.AllRanges(), false)
} }
} }
// assertHasRange asserts for all ranges from rangev that S.HasRange(r) == hasOK // assertSetHasRanges asserts for all ranges from rangev that RangedSet S.HasRange(r) == hasOK.
func assertHasRange(t *testing.T, rangev []KeyRange, S *RangedKeySet, hasOK bool) { func assertSetHasRanges(t *testing.T, S *RangedKeySet, rangev []KeyRange, hasOK bool) {
t.Helper() t.Helper()
for _, r := range rangev { for _, r := range rangev {
has := S.HasRange(r) has := S.HasRange(r)
......
This diff is collapsed.
This diff is collapsed.
...@@ -160,7 +160,9 @@ type _ΔTtail struct { ...@@ -160,7 +160,9 @@ type _ΔTtail struct {
// set of nodes that were requested to be tracked in this tree, but for // set of nodes that were requested to be tracked in this tree, but for
// which vδT was not yet rebuilt // which vδT was not yet rebuilt
trackNew blib.PPTreeSubSet trackNew blib.PPTreeSubSet
// XXX + trackNewKeys RangedKeySet (concurrency) // XXX + trackNewKeys RangedKeySet (concurrency)
// XXX + trackSetKeys RangedKeySet
} }
// _ΔBroots represents roots-only part of ΔB. // _ΔBroots represents roots-only part of ΔB.
...@@ -882,6 +884,8 @@ func (δBtail *ΔBtail) GetAt(root zodb.Oid, key Key, at zodb.Tid) (value Value, ...@@ -882,6 +884,8 @@ func (δBtail *ΔBtail) GetAt(root zodb.Oid, key Key, at zodb.Tid) (value Value,
rev = tail rev = tail
revExact = false revExact = false
// XXX need to rebuild only if key was not rebuilt yet
// XXX need to rebuild only for key, not for whole trackNew
err = δBtail.rebuild1IfNeeded(root) err = δBtail.rebuild1IfNeeded(root)
if err != nil { if err != nil {
return value, rev, valueExact, revExact, err return value, rev, valueExact, revExact, err
......
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