Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
10
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Open sidebar
p2p
go-p2p-xor
Commits
64ea8239
Commit
64ea8239
authored
Mar 27, 2020
by
Petar Maymounkov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
CheckInvariant returns info instead of panicking.
parent
625914b4
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
124 additions
and
24 deletions
+124
-24
trie/add_test.go
trie/add_test.go
+12
-5
trie/check.go
trie/check.go
+51
-16
trie/intersect_test.go
trie/intersect_test.go
+61
-3
No files found.
trie/add_test.go
View file @
64ea8239
...
...
@@ -16,8 +16,12 @@ func TestMutableAndImmutableAddSame(t *testing.T) {
mut
.
Add
(
k
)
immut
=
Add
(
immut
,
k
)
}
mut
.
CheckInvariant
()
immut
.
CheckInvariant
()
if
d
:=
mut
.
CheckInvariant
();
d
!=
nil
{
t
.
Fatalf
(
"mutable trie invariant discrepancy: %v"
,
d
)
}
if
d
:=
immut
.
CheckInvariant
();
d
!=
nil
{
t
.
Fatalf
(
"immutable trie invariant discrepancy: %v"
,
d
)
}
if
!
Equal
(
mut
,
immut
)
{
t
.
Errorf
(
"mutable trie %v differs from immutable trie %v"
,
mut
,
immut
)
}
...
...
@@ -29,16 +33,19 @@ func TestAddIsOrderIndependent(t *testing.T) {
base
:=
New
()
for
_
,
k
:=
range
s
.
Keys
{
base
.
Add
(
k
)
base
.
CheckInvariant
()
}
base
.
CheckInvariant
()
if
d
:=
base
.
CheckInvariant
();
d
!=
nil
{
t
.
Fatalf
(
"base trie invariant discrepancy: %v"
,
d
)
}
for
j
:=
0
;
j
<
100
;
j
++
{
perm
:=
rand
.
Perm
(
len
(
s
.
Keys
))
reordered
:=
New
()
for
i
:=
range
s
.
Keys
{
reordered
.
Add
(
s
.
Keys
[
perm
[
i
]])
}
reordered
.
CheckInvariant
()
if
d
:=
reordered
.
CheckInvariant
();
d
!=
nil
{
t
.
Fatalf
(
"reordered trie invariant discrepancy: %v"
,
d
)
}
if
!
Equal
(
base
,
reordered
)
{
t
.
Errorf
(
"trie %v differs from trie %v"
,
base
,
reordered
)
}
...
...
trie/check.go
View file @
64ea8239
...
...
@@ -4,35 +4,70 @@ import (
"github.com/libp2p/go-libp2p-xor/key"
)
type
InvariantDiscrepancy
struct
{
Reason
string
PathToDiscrepancy
string
KeyAtDiscrepancy
string
}
// CheckInvariant panics of the trie does not meet its invariant.
func
(
trie
*
Trie
)
CheckInvariant
()
{
trie
.
checkInvariant
(
0
,
nil
)
func
(
trie
*
Trie
)
CheckInvariant
()
*
InvariantDiscrepancy
{
return
trie
.
checkInvariant
(
0
,
nil
)
}
func
(
trie
*
Trie
)
checkInvariant
(
depth
int
,
pathSoFar
*
triePath
)
{
func
(
trie
*
Trie
)
checkInvariant
(
depth
int
,
pathSoFar
*
triePath
)
*
InvariantDiscrepancy
{
switch
{
case
trie
.
IsEmptyLeaf
()
:
// ok
case
trie
.
IsEmptyLeaf
()
:
return
nil
case
trie
.
IsNonEmptyLeaf
()
:
if
!
pathSoFar
.
matchesKey
(
trie
.
Key
)
{
panic
(
"key found at invalid location in trie"
)
return
&
InvariantDiscrepancy
{
Reason
:
"key found at invalid location in trie"
,
PathToDiscrepancy
:
pathSoFar
.
BitString
(),
KeyAtDiscrepancy
:
trie
.
Key
.
BitString
(),
}
}
return
nil
default
:
if
trie
.
IsEmpty
()
{
b0
,
b1
:=
trie
.
Branch
[
0
],
trie
.
Branch
[
1
]
b0
.
checkInvariant
(
depth
+
1
,
pathSoFar
.
Push
(
0
))
b1
.
checkInvariant
(
depth
+
1
,
pathSoFar
.
Push
(
1
))
if
d0
:=
b0
.
checkInvariant
(
depth
+
1
,
pathSoFar
.
Push
(
0
));
d0
!=
nil
{
return
d0
}
if
d1
:=
b1
.
checkInvariant
(
depth
+
1
,
pathSoFar
.
Push
(
1
));
d1
!=
nil
{
return
d1
}
switch
{
case
b0
.
IsEmptyLeaf
()
&&
b1
.
IsEmptyLeaf
()
:
panic
(
"intermediate node with two empty leaves"
)
return
&
InvariantDiscrepancy
{
Reason
:
"intermediate node with two empty leaves"
,
PathToDiscrepancy
:
pathSoFar
.
BitString
(),
KeyAtDiscrepancy
:
"none"
,
}
case
b0
.
IsEmptyLeaf
()
&&
b1
.
IsNonEmptyLeaf
()
:
panic
(
"intermediate node with one empty leaf"
)
return
&
InvariantDiscrepancy
{
Reason
:
"intermediate node with one empty leaf/0"
,
PathToDiscrepancy
:
pathSoFar
.
BitString
(),
KeyAtDiscrepancy
:
"none"
,
}
case
b0
.
IsNonEmptyLeaf
()
&&
b1
.
IsEmptyLeaf
()
:
panic
(
"intermediate node with one empty leaf"
)
return
&
InvariantDiscrepancy
{
Reason
:
"intermediate node with one empty leaf/1"
,
PathToDiscrepancy
:
pathSoFar
.
BitString
(),
KeyAtDiscrepancy
:
"none"
,
}
default
:
return
nil
}
}
else
{
panic
(
"intermediate node with a key"
)
return
&
InvariantDiscrepancy
{
Reason
:
"intermediate node with a key"
,
PathToDiscrepancy
:
pathSoFar
.
BitString
(),
KeyAtDiscrepancy
:
trie
.
Key
.
BitString
(),
}
}
}
panic
(
"unreachable"
)
}
type
triePath
struct
{
...
...
@@ -73,19 +108,19 @@ func (p *triePath) walk(k key.Key, depthToLeaf int) (ok bool, depthToRoot int) {
}
}
func
(
p
*
triePath
)
String
()
string
{
return
p
.
s
tring
(
0
)
func
(
p
*
triePath
)
Bit
String
()
string
{
return
p
.
bitS
tring
(
0
)
}
func
(
p
*
triePath
)
s
tring
(
depthToLeaf
int
)
string
{
func
(
p
*
triePath
)
bitS
tring
(
depthToLeaf
int
)
string
{
if
p
==
nil
{
return
""
}
else
{
switch
{
case
p
.
bit
==
0
:
return
p
.
parent
.
s
tring
(
depthToLeaf
+
1
)
+
"0"
return
p
.
parent
.
bitS
tring
(
depthToLeaf
+
1
)
+
"0"
case
p
.
bit
==
1
:
return
p
.
parent
.
s
tring
(
depthToLeaf
+
1
)
+
"1"
return
p
.
parent
.
bitS
tring
(
depthToLeaf
+
1
)
+
"1"
default
:
panic
(
"bit digit > 1"
)
}
...
...
trie/intersect_test.go
View file @
64ea8239
...
...
@@ -32,16 +32,22 @@ func testIntersect(t *testing.T, sample *testIntersectSample) {
for
_
,
l
:=
range
sample
.
LeftKeys
{
left
.
Add
(
l
)
}
left
.
CheckInvariant
()
if
d
:=
left
.
CheckInvariant
();
d
!=
nil
{
t
.
Fatalf
(
"left trie invariant discrepancy: %v"
,
d
)
}
for
_
,
r
:=
range
sample
.
RightKeys
{
right
.
Add
(
r
)
}
right
.
CheckInvariant
()
if
d
:=
right
.
CheckInvariant
();
d
!=
nil
{
t
.
Fatalf
(
"right trie invariant discrepancy: %v"
,
d
)
}
for
_
,
s
:=
range
setIntersect
(
sample
.
LeftKeys
,
sample
.
RightKeys
)
{
expected
.
Add
(
s
)
}
got
:=
Intersect
(
left
,
right
)
got
.
CheckInvariant
()
if
d
:=
got
.
CheckInvariant
();
d
!=
nil
{
t
.
Fatalf
(
"right trie invariant discrepancy: %v"
,
d
)
}
if
!
Equal
(
expected
,
got
)
{
t
.
Errorf
(
"intersection of %v and %v: expected %v, got %v"
,
sample
.
LeftKeys
,
sample
.
RightKeys
,
expected
,
got
)
...
...
@@ -140,3 +146,55 @@ var testIntersectJSONSamples = []string{
}
`
,
}
func
TestIntersectTriesFromJSON
(
t
*
testing
.
T
)
{
for
_
,
json
:=
range
testIntersectJSONTries
{
s
:=
testIntersectTrieFromJSON
(
json
)
testIntersectTries
(
t
,
s
)
}
}
func
testIntersectTries
(
t
*
testing
.
T
,
sample
*
testIntersectTrie
)
{
if
d
:=
sample
.
LeftTrie
.
CheckInvariant
();
d
!=
nil
{
t
.
Fatalf
(
"left trie invariant discrepancy: %v"
,
d
)
}
if
d
:=
sample
.
RightTrie
.
CheckInvariant
();
d
!=
nil
{
t
.
Fatalf
(
"right trie invariant discrepancy: %v"
,
d
)
}
expected
:=
New
()
for
_
,
s
:=
range
setIntersect
(
sample
.
LeftTrie
.
List
(),
sample
.
RightTrie
.
List
())
{
expected
.
Add
(
s
)
}
got
:=
Intersect
(
sample
.
LeftTrie
,
sample
.
RightTrie
)
if
d
:=
got
.
CheckInvariant
();
d
!=
nil
{
t
.
Fatalf
(
"got trie invariant discrepancy: %v"
,
d
)
}
if
!
Equal
(
expected
,
got
)
{
t
.
Errorf
(
"intersection of %v and %v: expected %v, got %v"
,
sample
.
LeftTrie
,
sample
.
RightTrie
,
expected
,
got
)
}
}
type
testIntersectTrie
struct
{
LeftTrie
*
Trie
RightTrie
*
Trie
}
func
testIntersectTrieFromJSON
(
srcJSON
string
)
*
testIntersectTrie
{
s
:=
&
testIntersectTrie
{}
if
err
:=
json
.
Unmarshal
([]
byte
(
srcJSON
),
s
);
err
!=
nil
{
panic
(
err
)
}
return
s
}
var
testIntersectJSONTries
=
[]
string
{
// `
// {
// "LeftTrie": [
// ],
// "RightTrie": [
// ]
// }
// `,
}
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