Commit b7c560c5 authored by Kirill Smelkov's avatar Kirill Smelkov

wcfs: xbtree: blib += RangedMap, RangedKeySet

RangedMap is Key->VALUE map with adjacent keys mapped to the same value coalesced into Ranges.
RangedKeySet is set of Keys with adjacent keys coalesced into Ranges.

This data structures will be needed for ΔBtail.

For now the implementation is simple since it keeps whole map in a
linear slice because both RangedMap and RangedKeySet will be used in
ΔBtail to keep something proportional to δ of a change, which is assumed
to be small or medium most of the time.

Some preliminary history:

6ea5920a    X xbtree: Less copy/garbage in RangedKeySet ops
3ecacd99    X need to keep Value first so that sizeof(set-entry) = sizeof(KeyRange)
a5b9b19b    X SetRange draftly works
ed2de0de    X Tests for Get
3b7b69e6    X fixes for empty set/range
6972f999    X xbtree/blib: RangedMap, RangedSet += IntersectsRange, Intersection
parent 828da0e1
#!/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
blib=$(cd $(dirname $0) && go list) # fullpath for blib package
curr=$(go list) # ----//---- current package
pkgname=$(go list -f {{.Name}}) # name of current package
echo "// Code generated by gen-rangemap $TYPE $VALUE; DO NOT EDIT." >$out
echo >>$out
# fiximports adjusts rangemap.go code to work outside of blib packages.
fiximports() {
if [ "$curr" == "$blib" ]; then
cat
return
fi
sed \
-e "/package blib/a \\\\nimport \"$blib\"\\n" \
-e "s/package blib/package $pkgname/g" \
-e 's/\([^\w.]\)KeyRange\b/\1blib.KeyRange/g' \
-e 's/\bKStr\b/blib.KStr/g'
}
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 |fiximports >>$out
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)
Intersects bool // M.IntersectsRange(X.Keycov)
}
E := func(M *RangedMap, X RangedMapEntry, S, D *RangedMap, H, I bool) testEntry {
return testEntry{M, X, S, D, H, I}
}
// 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
n), // Intersects
// empty vs !empty
E(
M(), // M
X(1,2,x), // X
M(1,2,x), // Set
M(), // Del
n, // Has
n), // Intersects
// !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
n), // Intersects
// basic change
E(
M(1,2,a), // M
X(1,2,x), // X
M(1,2,x), // Set
M(), // Del
y, // Has
y), // Intersects
// 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
n), // Intersects
// 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
y), // Intersects
// [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
y), // Intersects
// 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
y), // Intersects
E(
M(1,2,a, 2,3,b), // M
X(1,3,x), // X
M(1,3,x), // Set
M(), // Del
y, // Has
y), // Intersects
// 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
n), // Intersects
// 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
y), // Intersects
// - 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
y), // Intersects
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
y), // Intersects
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
y), // Intersects
}
for _, tt := range testv {
M := tt.M
X := tt.X
r := X.KeyRange
v := X.Value
assertMapHasRange(t, M, r, tt.Has)
assertMapIntersectsRange(t, M, r, tt.Intersects)
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)
assertMapHasRange(t, Mdel, r, r.Empty())
assertMapIntersectsRange(t, Mset, r, !r.Empty())
assertMapIntersectsRange(t, Mdel, r, false)
verifyGet(t, M)
verifyGet(t, Mset)
verifyGet(t, Mdel)
}
}
// assertMapHasRange asserts that RangedMap 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)
}
}
// assertMapIntersectsRange asserts that RangedMap M.IntersectsRange(r) == intersectsOK.
func assertMapIntersectsRange(t *testing.T, M *RangedMap, r KeyRange, intersectsOK bool) {
t.Helper()
intersects := M.IntersectsRange(r)
if !(intersects == intersectsOK) {
t.Errorf("IntersectsRange:\n M: %s\n r: %s\n ->·: %t\n ok·: %t\n", M, r, intersects, intersectsOK)
}
}
// 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, r, ok := M.Get_(k)
if !(v == e.Value && r == e.KeyRange && ok) {
t.Errorf("%s\tGet(%s):\nhave: %q%s, %t\nwant: %q%s, true",
M, KStr(k), v, r, ok, e.Value, e.KeyRange)
}
}
}
// 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, r_, ok := M.Get_(k)
if !(v == "" && r_.Empty() && !ok) {
t.Errorf("%s\tGet(%s):\nhave: %q%s, %t\nwant: %q[), false",
M, KStr(k), v, r_, 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
}
}
// 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
// set of [lo,hi) Key ranges.
//go:generate ./gen-rangemap _RangedMap_void void zrangemap_void.go
// RangedKeySet is set of Keys with adjacent keys coalesced into Ranges.
//
// Zero value represents empty set.
type RangedKeySet struct {
m _RangedMap_void
}
// void is used as value type for RangedMap to be used as set.
type void struct{}
func (_ void) String() string { return "" }
// Add adds key k to the set.
func (S *RangedKeySet) Add(k Key) {
S.AddRange(KeyRange{Lo: k, Hi_: k})
}
// Del removes key k from the set.
func (S *RangedKeySet) Del(k Key) {
S.DelRange(KeyRange{Lo: k, Hi_: k})
}
// Has returns whether key k belongs to the set.
func (S *RangedKeySet) Has(k Key) bool {
return S.HasRange(KeyRange{Lo: k, Hi_: k})
}
// AddRange adds range r to the set.
func (S *RangedKeySet) AddRange(r KeyRange) {
S.m.SetRange(r, void{})
}
// DelRange removes range r from the set.
func (S *RangedKeySet) DelRange(r KeyRange) {
S.m.DelRange(r)
}
// HasRange returns whether all keys from range r belong to the set.
func (S *RangedKeySet) HasRange(r KeyRange) bool {
return S.m.HasRange(r)
}
// IntersectsRange returns whether some keys from range r belong to the set.
func (S *RangedKeySet) IntersectsRange(r KeyRange) bool {
return S.m.IntersectsRange(r)
}
// Union returns RangedKeySet(A.keys | B.keys).
func (A *RangedKeySet) Union(B *RangedKeySet) *RangedKeySet {
U := A.Clone()
U.UnionInplace(B)
return U
}
// Difference returns RangedKeySet(A.keys \ B.keys).
func (A *RangedKeySet) Difference(B *RangedKeySet) *RangedKeySet {
D := A.Clone()
D.DifferenceInplace(B)
return D
}
// Intersection returns RangedKeySet(A.keys ^ B.keys).
func (A *RangedKeySet) Intersection(B *RangedKeySet) *RangedKeySet {
I := A.Clone()
I.IntersectionInplace(B)
return I
}
func (A *RangedKeySet) UnionInplace(B *RangedKeySet) {
A.verify()
B.verify()
defer A.verify()
// XXX dumb
for _, e := range B.m.entryv {
A.AddRange(e.KeyRange)
}
}
func (A *RangedKeySet) DifferenceInplace(B *RangedKeySet) {
A.verify()
B.verify()
defer A.verify()
// XXX dumb
for _, e := range B.m.entryv {
if A.Empty() {
break
}
A.DelRange(e.KeyRange)
}
}
func (A *RangedKeySet) IntersectionInplace(B *RangedKeySet) {
A.verify()
B.verify()
defer A.verify()
// XXX very dumb
// A^B = (A∪B) \ (A\B ∪ B\A)
AdB := A.Difference(B)
BdA := B.Difference(A)
ddd := AdB
ddd.UnionInplace(BdA)
A.UnionInplace(B)
A.DifferenceInplace(ddd)
}
// --------
// verify checks RangedKeySet for internal consistency.
func (S *RangedKeySet) verify() {
S.m.verify()
}
// Clone returns copy of the set.
func (orig *RangedKeySet) Clone() *RangedKeySet {
return &RangedKeySet{*orig.m.Clone()}
}
// Empty returns whether the set is empty.
func (S *RangedKeySet) Empty() bool {
return S.m.Empty()
}
// Equal returns whether A == B.
func (A *RangedKeySet) Equal(B *RangedKeySet) bool {
return A.m.Equal(&B.m)
}
// Clear removes all elements from the set.
func (S *RangedKeySet) Clear() {
S.m.Clear()
}
// AllRanges returns slice of all key ranges in the set.
//
// TODO -> iter?
func (S *RangedKeySet) AllRanges() /*readonly*/[]KeyRange {
// 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()
rv := make([]KeyRange, len(ev))
for i := range ev {
rv[i] = ev[i].KeyRange
}
return rv
}
func (S RangedKeySet) String() string {
// RangedMap<void> supports formatting for set out of the box
return S.m.String()
}
// 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
import (
"testing"
"unsafe"
)
func TestRangedKeySetTypes(t *testing.T) {
// verify that size(void) == 0 and that _RangedMap_voidEntry has the same layout as KeyRange
sizeVoid := unsafe.Sizeof(void{})
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 {
A, B *RangedKeySet
Union *RangedKeySet
Difference *RangedKeySet
Intersection *RangedKeySet
}
E := func(A, B, U, D, I *RangedKeySet) testEntry {
return testEntry{A, B, U, D, I}
}
// S is shorthand to create RangedKeySet, e.g. S(1,2, 4,5) will return {[1,2) [4,5)}
S := func(kv ...Key) *RangedKeySet {
l := len(kv)
if l % 2 != 0 {
panic("odd number of keys")
}
S := &RangedKeySet{}
for i := 0; i < l/2; i++ {
// construct .entryv directly, not via AddRange
lo := kv[2*i]
hi := kv[2*i+1]
hi_ := hi
if hi_ != oo {
hi_--
}
S.m.entryv = append(S.m.entryv, _RangedMap_voidEntry{
void{},
KeyRange{lo, hi_},
})
}
S.verify()
return S
}
testv := []testEntry{
E(
S(), // A
S(), // B
S(), // U
S(), // D
S()), // I
E(
S(), // A
S(1,2), // B
S(1,2), // U
S(), // D
S()), // I
E(
S(1,2), // A
S(), // B
S(1,2), // U
S(1,2), // D
S()), // I
E(
S(1,2), // A
S(1,2), // B
S(1,2), // U
S(), // D
S(1,2)),// I
// adjacent [1,3) [3,5)
E(
S(1,3), // A
S(3,5), // B
S(1,5), // U
S(1,3), // D
S()), // I
// overlapping [1,3) [2,4)
E(
S(1,3), // A
S(2,4), // B
S(1,4), // U
S(1,2), // D
S(2,3)),// I
// [1,7) \ [3,5) -> [1,3) [5,7)
E(
S(1,7), // A
S(3,5), // B
S(1,7), // U
S(1,3, 5,7), // D
S(3,5)), // I
// several ranges \ [-∞,∞) -> ø
E(
S(1,3, 5,7, 11,100), // A
S(noo, oo), // B
S(noo, oo), // U
S(), // D
S(1,3, 5,7, 11,100)), // I
// [1,3) [5,7) + insert [3,5) -> [1,7)
E(
S(1,3, 5,7), // A
S(3,5), // B
S(1,7), // U
S(1,3, 5,7), // D
S()), // I
// delete covering several ranges
// [-1,0) [1,3) [5,7) [9,11) [15,20) [100,200) \ [2,17)
E(
S(-1,0, 1,3, 5,7, 9,11, 15,20, 100,200),// A
S(2,17), // B
S(-1,0, 1,20, 100,200), // U
S(-1,0, 1,2, 17,20, 100,200), // D
S(2,3, 5,7, 9,11, 15,17)), // I
}
for _, tt := range testv {
A := tt.A
B := tt.B
U := A.Union(B)
D := A.Difference(B)
I := A.Intersection(B)
if !U.Equal(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) {
t.Errorf("Difference:\n A: %s\n B: %s\n ->d: %s\n okD: %s\n", A, B, D, tt.Difference)
}
if !I.Equal(tt.Intersection) {
t.Errorf("Intersection:\n A: %s\n B: %s\n ->i: %s\n okI: %s\n", A, B, I, tt.Intersection)
}
// HasRange
assertSetHasRanges(t, A, A.AllRanges(), true)
assertSetHasRanges(t, B, B.AllRanges(), true)
assertSetHasRanges(t, U, A.AllRanges(), true)
assertSetHasRanges(t, U, B.AllRanges(), true)
assertSetHasRanges(t, A, I.AllRanges(), true)
assertSetHasRanges(t, B, I.AllRanges(), true)
assertSetHasRanges(t, U, I.AllRanges(), true)
Dab := D
Dba := B.Difference(A)
assertSetHasRanges(t, A, Dab.AllRanges(), true)
assertSetHasRanges(t, B, Dab.AllRanges(), false)
assertSetHasRanges(t, B, Dba.AllRanges(), true)
assertSetHasRanges(t, A, Dba.AllRanges(), false)
assertSetHasRanges(t, Dab, I.AllRanges(), false)
assertSetHasRanges(t, Dba, I.AllRanges(), false)
assertSetHasRanges(t, I, Dab.AllRanges(), false)
assertSetHasRanges(t, I, Dba.AllRanges(), false)
// IntersectsRange (= (A^B)!=ø)
assertSetIntersectsRanges(t, A, I.AllRanges(), !I.Empty())
assertSetIntersectsRanges(t, B, I.AllRanges(), !I.Empty())
assertSetIntersectsRanges(t, Dab, B.AllRanges(), false)
assertSetIntersectsRanges(t, Dba, A.AllRanges(), false)
assertSetIntersectsRanges(t, Dab, I.AllRanges(), false)
assertSetIntersectsRanges(t, Dba, I.AllRanges(), false)
assertSetIntersectsRanges(t, I, Dab.AllRanges(), false)
assertSetIntersectsRanges(t, I, Dba.AllRanges(), false)
}
}
// assertSetHasRanges asserts for all ranges from rangev that RangedSet S.HasRange(r) == hasOK.
func assertSetHasRanges(t *testing.T, S *RangedKeySet, rangev []KeyRange, hasOK bool) {
t.Helper()
for _, r := range rangev {
has := S.HasRange(r)
if has != hasOK {
t.Errorf("HasRange:\n S: %s\n r: %s\n ->: %v\n ok: %v\n", S, r, has, hasOK)
}
}
}
// assertSetIntersectsRanges asserts for all ranges from rangev that RangedSet S.IntersectsRange(r) == intersectsOK.
func assertSetIntersectsRanges(t *testing.T, S *RangedKeySet, rangev []KeyRange, intersectsOK bool) {
t.Helper()
for _, r := range rangev {
intersects := S.IntersectsRange(r)
if intersects != intersectsOK {
t.Errorf("IntersectsRange:\n S: %s\n r: %s\n ->: %v\n ok: %v\n", S, r, intersects, intersectsOK)
}
}
}
This diff is collapsed.
This diff is collapsed.
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