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
96a5b4b6
Commit
96a5b4b6
authored
Mar 25, 2020
by
Petar Maymounkov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Refactor.
parent
d706c85b
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
106 additions
and
45 deletions
+106
-45
key/key.go
key/key.go
+22
-0
trie/add.go
trie/add.go
+8
-4
trie/add_test.go
trie/add_test.go
+11
-7
trie/equal.go
trie/equal.go
+15
-0
trie/intersect.go
trie/intersect.go
+6
-2
trie/intersect_test.go
trie/intersect_test.go
+19
-15
trie/xortrie.go
trie/xortrie.go
+16
-12
trie/xortrie_test.go
trie/xortrie_test.go
+9
-5
No files found.
key.go
→
key/
key.go
View file @
96a5b4b6
package
xor
package
key
import
"bytes"
import
"bytes"
//
Trie
Key is a vector of bits backed by a Go byte slice in big endian byte order and big-endian bit order.
// Key is a vector of bits backed by a Go byte slice in big endian byte order and big-endian bit order.
type
Trie
Key
[]
byte
type
Key
[]
byte
func
(
bs
Trie
Key
)
BitAt
(
offset
int
)
byte
{
func
(
bs
Key
)
BitAt
(
offset
int
)
byte
{
if
bs
[
offset
/
8
]
&
(
1
<<
(
offset
%
8
))
==
0
{
if
bs
[
offset
/
8
]
&
(
1
<<
(
offset
%
8
))
==
0
{
return
0
return
0
}
else
{
}
else
{
...
@@ -13,10 +13,10 @@ func (bs TrieKey) BitAt(offset int) byte {
...
@@ -13,10 +13,10 @@ func (bs TrieKey) BitAt(offset int) byte {
}
}
}
}
func
(
bs
Trie
Key
)
BitLen
()
int
{
func
(
bs
Key
)
BitLen
()
int
{
return
8
*
len
(
bs
)
return
8
*
len
(
bs
)
}
}
func
TrieKey
Equal
(
x
,
y
Trie
Key
)
bool
{
func
Equal
(
x
,
y
Key
)
bool
{
return
bytes
.
Equal
(
x
,
y
)
return
bytes
.
Equal
(
x
,
y
)
}
}
add.go
→
trie/
add.go
View file @
96a5b4b6
package
xor
package
trie
import
(
"github.com/libp2p/go-libp2p-xor/key"
)
// Add adds the key q to trie, returning a new trie.
// Add adds the key q to trie, returning a new trie.
// Add is immutable/non-destructive: The original trie remains unchanged.
// Add is immutable/non-destructive: The original trie remains unchanged.
func
Add
(
trie
*
XorTrie
,
q
Trie
Key
)
*
XorTrie
{
func
Add
(
trie
*
XorTrie
,
q
key
.
Key
)
*
XorTrie
{
return
add
(
0
,
trie
,
q
)
return
add
(
0
,
trie
,
q
)
}
}
func
add
(
depth
int
,
trie
*
XorTrie
,
q
Trie
Key
)
*
XorTrie
{
func
add
(
depth
int
,
trie
*
XorTrie
,
q
key
.
Key
)
*
XorTrie
{
dir
:=
q
.
BitAt
(
depth
)
dir
:=
q
.
BitAt
(
depth
)
if
!
trie
.
isLeaf
()
{
if
!
trie
.
isLeaf
()
{
s
:=
&
XorTrie
{}
s
:=
&
XorTrie
{}
...
@@ -17,7 +21,7 @@ func add(depth int, trie *XorTrie, q TrieKey) *XorTrie {
...
@@ -17,7 +21,7 @@ func add(depth int, trie *XorTrie, q TrieKey) *XorTrie {
if
trie
.
key
==
nil
{
if
trie
.
key
==
nil
{
return
&
XorTrie
{
key
:
q
}
return
&
XorTrie
{
key
:
q
}
}
else
{
}
else
{
if
TrieK
eyEqual
(
trie
.
key
,
q
)
{
if
k
ey
.
Equal
(
trie
.
key
,
q
)
{
return
trie
return
trie
}
else
{
}
else
{
s
:=
&
XorTrie
{}
s
:=
&
XorTrie
{}
...
...
add_test.go
→
trie/
add_test.go
View file @
96a5b4b6
package
xor
package
trie
import
"testing"
import
(
"testing"
"github.com/libp2p/go-libp2p-xor/key"
)
// Verify mutable and immutable add do the same thing.
// Verify mutable and immutable add do the same thing.
func
TestMutableAndImmutableAddSame
(
t
*
testing
.
T
)
{
func
TestMutableAndImmutableAddSame
(
t
*
testing
.
T
)
{
for
_
,
s
:=
range
testAddSameSamples
{
for
_
,
s
:=
range
testAddSameSamples
{
mut
:=
New
XorTrie
()
mut
:=
New
()
immut
:=
New
XorTrie
()
immut
:=
New
()
for
_
,
k
:=
range
s
.
Keys
{
for
_
,
k
:=
range
s
.
Keys
{
mut
.
Add
(
k
)
mut
.
Add
(
k
)
immut
=
Add
(
immut
,
k
)
immut
=
Add
(
immut
,
k
)
}
}
if
!
XorTrie
Equal
(
mut
,
immut
)
{
if
!
Equal
(
mut
,
immut
)
{
t
.
Errorf
(
"mutable trie %v differs from immutable trie %v"
,
mut
,
immut
)
t
.
Errorf
(
"mutable trie %v differs from immutable trie %v"
,
mut
,
immut
)
}
}
}
}
}
}
type
testAddSameSample
struct
{
type
testAddSameSample
struct
{
Keys
[]
Trie
Key
Keys
[]
key
.
Key
}
}
var
testAddSameSamples
=
[]
*
testAddSameSample
{
var
testAddSameSamples
=
[]
*
testAddSameSample
{
{
Keys
:
[]
Trie
Key
{{
1
,
3
,
5
,
7
,
11
,
13
}}},
{
Keys
:
[]
key
.
Key
{{
1
,
3
,
5
,
7
,
11
,
13
}}},
}
}
equal.go
→
trie/
equal.go
View file @
96a5b4b6
package
xor
package
trie
func
XorTrieEqual
(
p
,
q
*
XorTrie
)
bool
{
import
(
"github.com/libp2p/go-libp2p-xor/key"
)
func
Equal
(
p
,
q
*
XorTrie
)
bool
{
switch
{
switch
{
case
p
.
isLeaf
()
&&
q
.
isLeaf
()
:
case
p
.
isLeaf
()
&&
q
.
isLeaf
()
:
return
TrieK
eyEqual
(
p
.
key
,
q
.
key
)
return
k
ey
.
Equal
(
p
.
key
,
q
.
key
)
case
!
p
.
isLeaf
()
&&
!
q
.
isLeaf
()
:
case
!
p
.
isLeaf
()
&&
!
q
.
isLeaf
()
:
return
XorTrie
Equal
(
p
.
branch
[
0
],
q
.
branch
[
0
])
&&
XorTrie
Equal
(
p
.
branch
[
1
],
q
.
branch
[
1
])
return
Equal
(
p
.
branch
[
0
],
q
.
branch
[
0
])
&&
Equal
(
p
.
branch
[
1
],
q
.
branch
[
1
])
}
}
return
false
return
false
}
}
intersect.go
→
trie/
intersect.go
View file @
96a5b4b6
package
xor
package
trie
import
(
"github.com/libp2p/go-libp2p-xor/key"
)
// Intersect computes the intersection of the keys in p and q.
// Intersect computes the intersection of the keys in p and q.
// p and q must be non-nil. The returned trie is never nil.
// p and q must be non-nil. The returned trie is never nil.
...
@@ -12,7 +16,7 @@ func intersect(depth int, p, q *XorTrie) *XorTrie {
...
@@ -12,7 +16,7 @@ func intersect(depth int, p, q *XorTrie) *XorTrie {
if
p
.
isEmpty
()
||
q
.
isEmpty
()
{
if
p
.
isEmpty
()
||
q
.
isEmpty
()
{
return
&
XorTrie
{}
// empty set
return
&
XorTrie
{}
// empty set
}
else
{
}
else
{
if
TrieK
eyEqual
(
p
.
key
,
q
.
key
)
{
if
k
ey
.
Equal
(
p
.
key
,
q
.
key
)
{
return
&
XorTrie
{
key
:
p
.
key
}
// singleton
return
&
XorTrie
{
key
:
p
.
key
}
// singleton
}
else
{
}
else
{
return
&
XorTrie
{}
// empty set
return
&
XorTrie
{}
// empty set
...
...
intersect_test.go
→
trie/
intersect_test.go
View file @
96a5b4b6
package
xor
package
trie
import
"testing"
import
(
"testing"
"github.com/libp2p/go-libp2p-xor/key"
)
func
TestIntersectRandom
(
t
*
testing
.
T
)
{
func
TestIntersectRandom
(
t
*
testing
.
T
)
{
for
_
,
s
:=
range
testIntersectSamples
{
for
_
,
s
:=
range
testIntersectSamples
{
...
@@ -9,7 +13,7 @@ func TestIntersectRandom(t *testing.T) {
...
@@ -9,7 +13,7 @@ func TestIntersectRandom(t *testing.T) {
}
}
func
testIntersect
(
t
*
testing
.
T
,
sample
*
testIntersectSample
)
{
func
testIntersect
(
t
*
testing
.
T
,
sample
*
testIntersectSample
)
{
left
,
right
,
expected
:=
New
XorTrie
(),
NewXorTrie
(),
NewXorTrie
()
left
,
right
,
expected
:=
New
(),
New
(),
New
()
for
_
,
l
:=
range
sample
.
LeftKeys
{
for
_
,
l
:=
range
sample
.
LeftKeys
{
left
.
Add
(
l
)
left
.
Add
(
l
)
}
}
...
@@ -20,17 +24,17 @@ func testIntersect(t *testing.T, sample *testIntersectSample) {
...
@@ -20,17 +24,17 @@ func testIntersect(t *testing.T, sample *testIntersectSample) {
expected
.
Add
(
s
)
expected
.
Add
(
s
)
}
}
got
:=
Intersect
(
left
,
right
)
got
:=
Intersect
(
left
,
right
)
if
!
XorTrie
Equal
(
expected
,
got
)
{
if
!
Equal
(
expected
,
got
)
{
t
.
Errorf
(
"intersection of %v and %v: expected %v, got %v"
,
t
.
Errorf
(
"intersection of %v and %v: expected %v, got %v"
,
sample
.
LeftKeys
,
sample
.
RightKeys
,
expected
,
got
)
sample
.
LeftKeys
,
sample
.
RightKeys
,
expected
,
got
)
}
}
}
}
func
setIntersect
(
left
,
right
[]
Trie
Key
)
[]
Trie
Key
{
func
setIntersect
(
left
,
right
[]
key
.
Key
)
[]
key
.
Key
{
intersection
:=
[]
Trie
Key
{}
intersection
:=
[]
key
.
Key
{}
for
_
,
l
:=
range
left
{
for
_
,
l
:=
range
left
{
for
_
,
r
:=
range
right
{
for
_
,
r
:=
range
right
{
if
TrieK
eyEqual
(
l
,
r
)
{
if
k
ey
.
Equal
(
l
,
r
)
{
intersection
=
append
(
intersection
,
r
)
intersection
=
append
(
intersection
,
r
)
}
}
}
}
...
@@ -39,21 +43,21 @@ func setIntersect(left, right []TrieKey) []TrieKey {
...
@@ -39,21 +43,21 @@ func setIntersect(left, right []TrieKey) []TrieKey {
}
}
type
testIntersectSample
struct
{
type
testIntersectSample
struct
{
LeftKeys
[]
Trie
Key
LeftKeys
[]
key
.
Key
RightKeys
[]
Trie
Key
RightKeys
[]
key
.
Key
}
}
var
testIntersectSamples
=
[]
*
testIntersectSample
{
var
testIntersectSamples
=
[]
*
testIntersectSample
{
{
{
LeftKeys
:
[]
Trie
Key
{{
1
,
2
,
3
}},
LeftKeys
:
[]
key
.
Key
{{
1
,
2
,
3
}},
RightKeys
:
[]
Trie
Key
{{
1
,
3
,
5
}},
RightKeys
:
[]
key
.
Key
{{
1
,
3
,
5
}},
},
},
{
{
LeftKeys
:
[]
Trie
Key
{{
1
,
2
,
3
,
4
,
5
,
6
}},
LeftKeys
:
[]
key
.
Key
{{
1
,
2
,
3
,
4
,
5
,
6
}},
RightKeys
:
[]
Trie
Key
{{
3
,
5
,
7
}},
RightKeys
:
[]
key
.
Key
{{
3
,
5
,
7
}},
},
},
{
{
LeftKeys
:
[]
Trie
Key
{{
23
,
3
,
7
,
13
,
17
}},
LeftKeys
:
[]
key
.
Key
{{
23
,
3
,
7
,
13
,
17
}},
RightKeys
:
[]
Trie
Key
{{
2
,
11
,
17
,
19
,
23
}},
RightKeys
:
[]
key
.
Key
{{
2
,
11
,
17
,
19
,
23
}},
},
},
}
}
xortrie.go
→
trie/
xortrie.go
View file @
96a5b4b6
package
xor
package
trie
import
(
"github.com/libp2p/go-libp2p-xor/key"
)
// XorTrie is a trie for equal-length bit vectors, which stores values only in the leaves.
// XorTrie is a trie for equal-length bit vectors, which stores values only in the leaves.
// XorTrie node invariants:
// XorTrie node invariants:
...
@@ -6,10 +10,10 @@ package xor
...
@@ -6,10 +10,10 @@ package xor
// (2) If both branches are leaves, then they are both non-empty (have keys).
// (2) If both branches are leaves, then they are both non-empty (have keys).
type
XorTrie
struct
{
type
XorTrie
struct
{
branch
[
2
]
*
XorTrie
branch
[
2
]
*
XorTrie
key
Trie
Key
key
key
.
Key
}
}
func
New
XorTrie
()
*
XorTrie
{
func
New
()
*
XorTrie
{
return
&
XorTrie
{}
return
&
XorTrie
{}
}
}
...
@@ -32,29 +36,29 @@ func max(x, y int) int {
...
@@ -32,29 +36,29 @@ func max(x, y int) int {
return
y
return
y
}
}
func
(
trie
*
XorTrie
)
Find
(
q
Trie
Key
)
(
reachedDepth
int
,
found
bool
)
{
func
(
trie
*
XorTrie
)
Find
(
q
key
.
Key
)
(
reachedDepth
int
,
found
bool
)
{
return
trie
.
find
(
0
,
q
)
return
trie
.
find
(
0
,
q
)
}
}
func
(
trie
*
XorTrie
)
find
(
depth
int
,
q
Trie
Key
)
(
reachedDepth
int
,
found
bool
)
{
func
(
trie
*
XorTrie
)
find
(
depth
int
,
q
key
.
Key
)
(
reachedDepth
int
,
found
bool
)
{
if
qb
:=
trie
.
branch
[
q
.
BitAt
(
depth
)];
qb
!=
nil
{
if
qb
:=
trie
.
branch
[
q
.
BitAt
(
depth
)];
qb
!=
nil
{
return
qb
.
find
(
depth
+
1
,
q
)
return
qb
.
find
(
depth
+
1
,
q
)
}
else
{
}
else
{
if
trie
.
key
==
nil
{
if
trie
.
key
==
nil
{
return
depth
,
false
return
depth
,
false
}
else
{
}
else
{
return
depth
,
TrieK
eyEqual
(
trie
.
key
,
q
)
return
depth
,
k
ey
.
Equal
(
trie
.
key
,
q
)
}
}
}
}
}
}
// Add adds the key q to the trie. Add mutates the trie.
// Add adds the key q to the trie. Add mutates the trie.
// TODO: Also implement an immutable version of Add.
// TODO: Also implement an immutable version of Add.
func
(
trie
*
XorTrie
)
Add
(
q
Trie
Key
)
(
insertedDepth
int
,
insertedOK
bool
)
{
func
(
trie
*
XorTrie
)
Add
(
q
key
.
Key
)
(
insertedDepth
int
,
insertedOK
bool
)
{
return
trie
.
add
(
0
,
q
)
return
trie
.
add
(
0
,
q
)
}
}
func
(
trie
*
XorTrie
)
add
(
depth
int
,
q
Trie
Key
)
(
insertedDepth
int
,
insertedOK
bool
)
{
func
(
trie
*
XorTrie
)
add
(
depth
int
,
q
key
.
Key
)
(
insertedDepth
int
,
insertedOK
bool
)
{
if
qb
:=
trie
.
branch
[
q
.
BitAt
(
depth
)];
qb
!=
nil
{
if
qb
:=
trie
.
branch
[
q
.
BitAt
(
depth
)];
qb
!=
nil
{
return
qb
.
add
(
depth
+
1
,
q
)
return
qb
.
add
(
depth
+
1
,
q
)
}
else
{
}
else
{
...
@@ -62,7 +66,7 @@ func (trie *XorTrie) add(depth int, q TrieKey) (insertedDepth int, insertedOK bo
...
@@ -62,7 +66,7 @@ func (trie *XorTrie) add(depth int, q TrieKey) (insertedDepth int, insertedOK bo
trie
.
key
=
q
trie
.
key
=
q
return
depth
,
true
return
depth
,
true
}
else
{
}
else
{
if
TrieK
eyEqual
(
trie
.
key
,
q
)
{
if
k
ey
.
Equal
(
trie
.
key
,
q
)
{
return
depth
,
false
return
depth
,
false
}
else
{
}
else
{
p
:=
trie
.
key
p
:=
trie
.
key
...
@@ -78,11 +82,11 @@ func (trie *XorTrie) add(depth int, q TrieKey) (insertedDepth int, insertedOK bo
...
@@ -78,11 +82,11 @@ func (trie *XorTrie) add(depth int, q TrieKey) (insertedDepth int, insertedOK bo
// Remove removes the key q from the trie. Remove mutates the trie.
// Remove removes the key q from the trie. Remove mutates the trie.
// TODO: Also implement an immutable version of Add.
// TODO: Also implement an immutable version of Add.
func
(
trie
*
XorTrie
)
Remove
(
q
Trie
Key
)
(
removedDepth
int
,
removed
bool
)
{
func
(
trie
*
XorTrie
)
Remove
(
q
key
.
Key
)
(
removedDepth
int
,
removed
bool
)
{
return
trie
.
remove
(
0
,
q
)
return
trie
.
remove
(
0
,
q
)
}
}
func
(
trie
*
XorTrie
)
remove
(
depth
int
,
q
Trie
Key
)
(
reachedDepth
int
,
removed
bool
)
{
func
(
trie
*
XorTrie
)
remove
(
depth
int
,
q
key
.
Key
)
(
reachedDepth
int
,
removed
bool
)
{
if
qb
:=
trie
.
branch
[
q
.
BitAt
(
depth
)];
qb
!=
nil
{
if
qb
:=
trie
.
branch
[
q
.
BitAt
(
depth
)];
qb
!=
nil
{
if
d
,
ok
:=
qb
.
remove
(
depth
+
1
,
q
);
ok
{
if
d
,
ok
:=
qb
.
remove
(
depth
+
1
,
q
);
ok
{
trie
.
shrink
()
trie
.
shrink
()
...
@@ -91,7 +95,7 @@ func (trie *XorTrie) remove(depth int, q TrieKey) (reachedDepth int, removed boo
...
@@ -91,7 +95,7 @@ func (trie *XorTrie) remove(depth int, q TrieKey) (reachedDepth int, removed boo
return
d
,
false
return
d
,
false
}
}
}
else
{
}
else
{
if
trie
.
key
!=
nil
&&
TrieK
eyEqual
(
q
,
trie
.
key
)
{
if
trie
.
key
!=
nil
&&
k
ey
.
Equal
(
q
,
trie
.
key
)
{
trie
.
key
=
nil
trie
.
key
=
nil
return
depth
,
true
return
depth
,
true
}
else
{
}
else
{
...
...
xortrie_test.go
→
trie/
xortrie_test.go
View file @
96a5b4b6
package
xor
package
trie
import
"testing"
import
(
"testing"
"github.com/libp2p/go-libp2p-xor/key"
)
func
TestInsertRemove
(
t
*
testing
.
T
)
{
func
TestInsertRemove
(
t
*
testing
.
T
)
{
r
:=
New
XorTrie
()
r
:=
New
()
testSeq
(
r
,
t
)
testSeq
(
r
,
t
)
testSeq
(
r
,
t
)
testSeq
(
r
,
t
)
}
}
func
testSeq
(
r
*
XorTrie
,
t
*
testing
.
T
)
{
func
testSeq
(
r
*
XorTrie
,
t
*
testing
.
T
)
{
for
_
,
s
:=
range
testInsertSeq
{
for
_
,
s
:=
range
testInsertSeq
{
depth
,
_
:=
r
.
Add
(
Trie
Key
(
s
.
key
))
depth
,
_
:=
r
.
Add
(
key
.
Key
(
s
.
key
))
if
depth
!=
s
.
insertedDepth
{
if
depth
!=
s
.
insertedDepth
{
t
.
Errorf
(
"inserting expected %d, got %d"
,
s
.
insertedDepth
,
depth
)
t
.
Errorf
(
"inserting expected %d, got %d"
,
s
.
insertedDepth
,
depth
)
}
}
}
}
for
_
,
s
:=
range
testRemoveSeq
{
for
_
,
s
:=
range
testRemoveSeq
{
depth
,
_
:=
r
.
Remove
(
Trie
Key
(
s
.
key
))
depth
,
_
:=
r
.
Remove
(
key
.
Key
(
s
.
key
))
if
depth
!=
s
.
reachedDepth
{
if
depth
!=
s
.
reachedDepth
{
t
.
Errorf
(
"removing expected %d, got %d"
,
s
.
reachedDepth
,
depth
)
t
.
Errorf
(
"removing expected %d, got %d"
,
s
.
reachedDepth
,
depth
)
}
}
...
...
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