Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
W
wendelin.core
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Labels
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Commits
Open sidebar
Kirill Smelkov
wendelin.core
Commits
9acd5150
Commit
9acd5150
authored
May 15, 2021
by
Kirill Smelkov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
.
parent
ac3bb8cc
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
103 additions
and
108 deletions
+103
-108
wcfs/pptreenodeset.go
wcfs/pptreenodeset.go
+71
-51
wcfs/δbtail.go
wcfs/δbtail.go
+18
-23
wcfs/δbtail_test.go
wcfs/δbtail_test.go
+14
-34
No files found.
wcfs/pptreenodeset.go
View file @
9acd5150
...
@@ -27,41 +27,37 @@ import (
...
@@ -27,41 +27,37 @@ import (
)
)
// XXX is a set of PP(leafs) nodes XXX -> PTreeSubSet ? TreePSubSet ? TreePPSet ? PPSubSet ?
// PPTreeSubSet repesents PP-connected subset of tree node objects.
// XXX PPNodeSet? TreePPSubSet ? PPSet? PPNodeSet? PPOidSet?
// ~~ PPOidSet ~~ <- N
//
//
// YYY:
// where PP(node) maps node to {node, node.parent, leaf.parent,parent, ...} up
// XXX PPTreeNodeSet
// to top root from where the node is reached.
// XXX PPTreeNodeObjSet
// XXX PPTreeNodeOidSet
// XXX PPTreeObjSet
// XXX PPTreeOidSet
//
//
// where PP maps a leaf to {leaf, leaf.parent, leaf.parent.parent, ...} up to
// The nodes in the set are represented by their Oid.
// top root node from where the leaf is reached.
//
//
// Every node in the set also has .parent pointer.
// Usually PPTreeSubSet is built as PP(some-leafs), but in general the starting
// nodes are arbitrary. PPTreeSubSet can also have many root nodes, thus not
// necessarily representing a subset of a single tree.
//
//
// XXX place
// Usual set operations are provided: Union, Difference and Intersection.
type
trackIndex
map
[
zodb
.
Oid
]
*
nodeTrack
//
// Nodes can be added into the set via AddPath. Path is reverse operation - it
// returns path to tree node given node oid.
//
// XXX ΔPPTreeSubSet
//
// Every node in the set also has .parent pointer.
type
PPTreeSubSet
map
[
zodb
.
Oid
]
*
nodeInTree
// XXX place XXX PTreeSubSetNode ? PPNode? nodeP? nodePnC ?
// nodeInTree represents tracking information about a node.
// nodeTrack represents tracking information about a node.
type
nodeInTree
struct
{
type
nodeTrack
struct
{
parent
zodb
.
Oid
// parent node | InvalidOid for root
parent
zodb
.
Oid
// parent node | InvalidOid for root
nchild
int
// number of direct children in trackIdx referring to this node
nchild
int
// number of direct children in PPTreeSubSet referring to this node
// XXX + [lo,hi) range this node is coming under in its parent
// XXX + [lo,hi) range this node is coming under in its parent
/*
holes SetKey // missing keys tracked under this node; nil for !leaf
// XXX move holes into separate ΔBtail..holeIdx
*/
}
}
//
δtrackIndex represents change to trackIndex. XXX name
//
ΔPPTreeSubSet represents a change to PPTreeSubSet.
//
//
//
XXX refer to trackIndex.ApplyΔ
//
It can be applied via PPTreeSubSet.ApplyΔ .
//
//
// The result B of applying δ to A is:
// The result B of applying δ to A is:
//
//
...
@@ -98,16 +94,14 @@ type nodeTrack struct {
...
@@ -98,16 +94,14 @@ type nodeTrack struct {
// - xfixup(+1, δnchildNonLeafs)
// - xfixup(+1, δnchildNonLeafs)
//
//
// produce correctly PP-connected set.
// produce correctly PP-connected set.
//
type
ΔPPTreeSubSet
struct
{
// XXX place
Del
PPTreeSubSet
type
δtrackIndex
struct
{
Add
PPTreeSubSet
Del
trackIndex
Add
trackIndex
δnchildNonLeafs
map
[
zodb
.
Oid
]
int
δnchildNonLeafs
map
[
zodb
.
Oid
]
int
}
}
// Update updates δ to be combination of δ+δ2.
// Update updates δ to be combination of δ+δ2.
func
(
δ
*
δtrackIndex
)
Update
(
δ2
*
δtrackIndex
)
{
func
(
δ
*
ΔPPTreeSubSet
)
Update
(
δ2
*
ΔPPTreeSubSet
)
{
δ
.
Del
.
UnionInplace
(
δ2
.
Del
)
δ
.
Del
.
UnionInplace
(
δ2
.
Del
)
δ
.
Add
.
UnionInplace
(
δ2
.
Add
)
δ
.
Add
.
UnionInplace
(
δ2
.
Add
)
for
oid
,
δnc
:=
range
δ2
.
δnchildNonLeafs
{
for
oid
,
δnc
:=
range
δ2
.
δnchildNonLeafs
{
...
@@ -116,14 +110,14 @@ func (δ *δtrackIndex) Update(δ2 *δtrackIndex) {
...
@@ -116,14 +110,14 @@ func (δ *δtrackIndex) Update(δ2 *δtrackIndex) {
}
}
// Reverse changes δ=diff(A->B) to δ'=diff(A<-B).
// Reverse changes δ=diff(A->B) to δ'=diff(A<-B).
func
(
δ
*
δtrackIndex
)
Reverse
()
{
func
(
δ
*
ΔPPTreeSubSet
)
Reverse
()
{
δ
.
Del
,
δ
.
Add
=
δ
.
Add
,
δ
.
Del
δ
.
Del
,
δ
.
Add
=
δ
.
Add
,
δ
.
Del
// δnchildNonLeafs stays the same
// δnchildNonLeafs stays the same
}
}
// gc1 garbage-collects oid and cleans up its parent down-up.
// gc1 garbage-collects oid and cleans up its parent down-up.
func
(
tidx
trackIndex
)
gc1
(
oid
zodb
.
Oid
)
{
func
(
tidx
PPTreeSubSet
)
gc1
(
oid
zodb
.
Oid
)
{
t
,
present
:=
tidx
[
oid
]
t
,
present
:=
tidx
[
oid
]
if
!
present
{
if
!
present
{
return
// already not there
return
// already not there
...
@@ -146,7 +140,7 @@ func (tidx trackIndex) gc1(oid zodb.Oid) {
...
@@ -146,7 +140,7 @@ func (tidx trackIndex) gc1(oid zodb.Oid) {
}
}
// verify verifies internal consistency of tidx.
// verify verifies internal consistency of tidx.
func
(
tidx
trackIndex
)
verify
()
{
func
(
tidx
PPTreeSubSet
)
verify
()
{
// XXX !debug -> return
// XXX !debug -> return
var
badv
[]
string
var
badv
[]
string
...
@@ -196,7 +190,7 @@ func (tidx trackIndex) verify() {
...
@@ -196,7 +190,7 @@ func (tidx trackIndex) verify() {
// DifferenceInplace sets A = PP(A.leafs \ B.leafs)
// DifferenceInplace sets A = PP(A.leafs \ B.leafs)
//
//
// In other words it removes B nodes from A while still maintaining A as P-connected.
// In other words it removes B nodes from A while still maintaining A as P-connected.
func
(
A
trackIndex
)
DifferenceInplace
(
B
trackIndex
)
{
func
(
A
PPTreeSubSet
)
DifferenceInplace
(
B
PPTreeSubSet
)
{
if
debugPPSet
{
if
debugPPSet
{
fmt
.
Printf
(
"
\n\n
DifferenceInplace:
\n
"
)
fmt
.
Printf
(
"
\n\n
DifferenceInplace:
\n
"
)
fmt
.
Printf
(
" A: %s
\n
"
,
A
)
fmt
.
Printf
(
" A: %s
\n
"
,
A
)
...
@@ -211,7 +205,7 @@ func (A trackIndex) DifferenceInplace(B trackIndex) {
...
@@ -211,7 +205,7 @@ func (A trackIndex) DifferenceInplace(B trackIndex) {
A
.
xDifferenceInplace
(
B
)
A
.
xDifferenceInplace
(
B
)
}
}
func
(
A
trackIndex
)
xDifferenceInplace
(
B
trackIndex
)
{
func
(
A
PPTreeSubSet
)
xDifferenceInplace
(
B
PPTreeSubSet
)
{
if
debugPPSet
{
if
debugPPSet
{
fmt
.
Printf
(
"
\n\n
xDifferenceInplace:
\n
"
)
fmt
.
Printf
(
"
\n\n
xDifferenceInplace:
\n
"
)
fmt
.
Printf
(
" a: %s
\n
"
,
A
)
fmt
.
Printf
(
" a: %s
\n
"
,
A
)
...
@@ -250,7 +244,7 @@ func (A trackIndex) xDifferenceInplace(B trackIndex) {
...
@@ -250,7 +244,7 @@ func (A trackIndex) xDifferenceInplace(B trackIndex) {
A
.
fixup
(
δnchild
)
A
.
fixup
(
δnchild
)
}
}
func
(
A
trackIndex
)
xUnionInplace
(
B
trackIndex
)
{
func
(
A
PPTreeSubSet
)
xUnionInplace
(
B
PPTreeSubSet
)
{
if
debugPPSet
{
if
debugPPSet
{
fmt
.
Printf
(
"
\n\n
xUnionInplace:
\n
"
)
fmt
.
Printf
(
"
\n\n
xUnionInplace:
\n
"
)
fmt
.
Printf
(
" a: %s
\n
"
,
A
)
fmt
.
Printf
(
" a: %s
\n
"
,
A
)
...
@@ -263,7 +257,7 @@ func (A trackIndex) xUnionInplace(B trackIndex) {
...
@@ -263,7 +257,7 @@ func (A trackIndex) xUnionInplace(B trackIndex) {
for
oid
,
t2
:=
range
B
{
for
oid
,
t2
:=
range
B
{
t
,
already
:=
A
[
oid
]
t
,
already
:=
A
[
oid
]
if
!
already
{
if
!
already
{
t
=
&
node
Track
{
parent
:
t2
.
parent
,
nchild
:
0
}
t
=
&
node
InTree
{
parent
:
t2
.
parent
,
nchild
:
0
}
A
[
oid
]
=
t
A
[
oid
]
=
t
// remember to nchild++ in parent
// remember to nchild++ in parent
if
t
.
parent
!=
zodb
.
InvalidOid
{
if
t
.
parent
!=
zodb
.
InvalidOid
{
...
@@ -286,10 +280,10 @@ func (A trackIndex) xUnionInplace(B trackIndex) {
...
@@ -286,10 +280,10 @@ func (A trackIndex) xUnionInplace(B trackIndex) {
// fixup performs scheduled δnchild adjustment.
// fixup performs scheduled δnchild adjustment.
// XXX place
// XXX place
func
(
A
trackIndex
)
fixup
(
δnchild
map
[
zodb
.
Oid
]
int
)
{
func
(
A
PPTreeSubSet
)
fixup
(
δnchild
map
[
zodb
.
Oid
]
int
)
{
A
.
xfixup
(
+
1
,
δnchild
)
A
.
xfixup
(
+
1
,
δnchild
)
}
}
func
(
A
trackIndex
)
xfixup
(
sign
int
,
δnchild
map
[
zodb
.
Oid
]
int
)
{
func
(
A
PPTreeSubSet
)
xfixup
(
sign
int
,
δnchild
map
[
zodb
.
Oid
]
int
)
{
gcq
:=
[]
zodb
.
Oid
{}
gcq
:=
[]
zodb
.
Oid
{}
for
oid
,
δnc
:=
range
δnchild
{
for
oid
,
δnc
:=
range
δnchild
{
t
:=
A
[
oid
]
// XXX t can be nil -> XXX no must be there as A is connected
t
:=
A
[
oid
]
// XXX t can be nil -> XXX no must be there as A is connected
...
@@ -308,7 +302,7 @@ func (A trackIndex) xfixup(sign int, δnchild map[zodb.Oid]int) {
...
@@ -308,7 +302,7 @@ func (A trackIndex) xfixup(sign int, δnchild map[zodb.Oid]int) {
// UnionInplace sets A = PP(A.leafs | B.leafs)
// UnionInplace sets A = PP(A.leafs | B.leafs)
//
//
// In other words it adds B nodes to A.
// In other words it adds B nodes to A.
func
(
A
trackIndex
)
UnionInplace
(
B
trackIndex
)
{
func
(
A
PPTreeSubSet
)
UnionInplace
(
B
PPTreeSubSet
)
{
if
debugPPSet
{
if
debugPPSet
{
fmt
.
Printf
(
"
\n\n
UnionInplace:
\n
"
)
fmt
.
Printf
(
"
\n\n
UnionInplace:
\n
"
)
fmt
.
Printf
(
" A: %s
\n
"
,
A
)
fmt
.
Printf
(
" A: %s
\n
"
,
A
)
...
@@ -324,7 +318,7 @@ func (A trackIndex) UnionInplace(B trackIndex) {
...
@@ -324,7 +318,7 @@ func (A trackIndex) UnionInplace(B trackIndex) {
}
}
// ApplyΔ applies δ to trackIdx. XXX
// ApplyΔ applies δ to trackIdx. XXX
func
(
tidx
trackIndex
)
ApplyΔ
(
δ
*
δtrackIndex
)
{
func
(
tidx
PPTreeSubSet
)
ApplyΔ
(
δ
*
ΔPPTreeSubSet
)
{
if
debugPPSet
{
if
debugPPSet
{
fmt
.
Printf
(
"
\n\n
ApplyΔ
\n
"
)
fmt
.
Printf
(
"
\n\n
ApplyΔ
\n
"
)
fmt
.
Printf
(
" A: %s
\n
"
,
tidx
)
fmt
.
Printf
(
" A: %s
\n
"
,
tidx
)
...
@@ -349,7 +343,7 @@ func (tidx trackIndex) ApplyΔ(δ *δtrackIndex) {
...
@@ -349,7 +343,7 @@ func (tidx trackIndex) ApplyΔ(δ *δtrackIndex) {
//
//
// The node must be in the set.
// The node must be in the set.
// XXX place
// XXX place
func
(
tidx
trackIndex
)
Path
(
oid
zodb
.
Oid
)
(
path
[]
zodb
.
Oid
)
{
func
(
tidx
PPTreeSubSet
)
Path
(
oid
zodb
.
Oid
)
(
path
[]
zodb
.
Oid
)
{
for
{
for
{
t
,
ok
:=
tidx
[
oid
]
t
,
ok
:=
tidx
[
oid
]
if
!
ok
{
if
!
ok
{
...
@@ -369,7 +363,7 @@ func (tidx trackIndex) Path(oid zodb.Oid) (path []zodb.Oid) {
...
@@ -369,7 +363,7 @@ func (tidx trackIndex) Path(oid zodb.Oid) (path []zodb.Oid) {
// XXX place
// XXX place
// XXX doc
// XXX doc
func
(
tidx
trackIndex
)
AddNodePath
(
path
[]
Node
)
{
// XXX Tree|Bucket; path[0] = root
func
(
tidx
PPTreeSubSet
)
AddNodePath
(
path
[]
Node
)
{
// XXX Tree|Bucket; path[0] = root
// XXX assert Tree Tree ... Tree Bucket
// XXX assert Tree Tree ... Tree Bucket
// root := path[0].(*Tree).POid()
// root := path[0].(*Tree).POid()
...
@@ -380,7 +374,7 @@ func (tidx trackIndex) AddNodePath(path []Node) { // XXX Tree|Bucket; path[0] =
...
@@ -380,7 +374,7 @@ func (tidx trackIndex) AddNodePath(path []Node) { // XXX Tree|Bucket; path[0] =
tidx
.
AddPath
(
oidv
)
tidx
.
AddPath
(
oidv
)
}
}
func
(
tidx
trackIndex
)
AddPath
(
path
[]
zodb
.
Oid
)
{
func
(
tidx
PPTreeSubSet
)
AddPath
(
path
[]
zodb
.
Oid
)
{
tidx
.
verify
()
tidx
.
verify
()
defer
tidx
.
verify
()
defer
tidx
.
verify
()
...
@@ -397,8 +391,8 @@ func (tidx trackIndex) AddPath(path []zodb.Oid) {
...
@@ -397,8 +391,8 @@ func (tidx trackIndex) AddPath(path []zodb.Oid) {
}
}
parent
:=
zodb
.
InvalidOid
parent
:=
zodb
.
InvalidOid
var
ptrack
*
node
Track
=
nil
var
ptrack
*
node
InTree
=
nil
var
track
*
node
Track
// XXX kill here
var
track
*
node
InTree
// XXX kill here
var
oldTrack
bool
var
oldTrack
bool
for
_
,
oid
:=
range
path
{
for
_
,
oid
:=
range
path
{
if
oid
==
zodb
.
InvalidOid
{
if
oid
==
zodb
.
InvalidOid
{
...
@@ -407,7 +401,7 @@ func (tidx trackIndex) AddPath(path []zodb.Oid) {
...
@@ -407,7 +401,7 @@ func (tidx trackIndex) AddPath(path []zodb.Oid) {
track
,
oldTrack
=
tidx
[
oid
]
track
,
oldTrack
=
tidx
[
oid
]
if
!
oldTrack
{
if
!
oldTrack
{
track
=
&
node
Track
{
parent
:
parent
,
nchild
:
0
}
// XXX
track
=
&
node
InTree
{
parent
:
parent
,
nchild
:
0
}
// XXX
/*
/*
if i == l-1 { // leaf
if i == l-1 { // leaf
track.holes = SetKey{}
track.holes = SetKey{}
...
@@ -432,13 +426,39 @@ func (tidx trackIndex) AddPath(path []zodb.Oid) {
...
@@ -432,13 +426,39 @@ func (tidx trackIndex) AddPath(path []zodb.Oid) {
}
}
// Clone returns copy of the set.
// XXX place
// XXX place
func
(
orig
trackIndex
)
Clone
()
trackIndex
{
func
(
orig
PPTreeSubSet
)
Clone
()
PPTreeSubSet
{
klon
:=
make
(
trackIndex
,
len
(
orig
))
klon
:=
make
(
PPTreeSubSet
,
len
(
orig
))
for
oid
,
t
:=
range
orig
{
for
oid
,
t
:=
range
orig
{
klon
[
oid
]
=
&
node
Track
{
parent
:
t
.
parent
,
nchild
:
t
.
nchild
}
klon
[
oid
]
=
&
node
InTree
{
parent
:
t
.
parent
,
nchild
:
t
.
nchild
}
}
}
return
klon
return
klon
}
}
// equal returns whether a == b.
// XXX place
func
(
a
PPTreeSubSet
)
equal
(
b
PPTreeSubSet
)
bool
{
if
len
(
a
)
!=
len
(
b
)
{
return
false
}
for
oid
,
ta
:=
range
a
{
tb
,
ok
:=
b
[
oid
]
if
!
ok
{
return
false
}
if
!
(
ta
.
parent
==
tb
.
parent
&&
ta
.
nchild
==
tb
.
nchild
)
{
return
false
}
}
return
true
}
func
(
t
nodeInTree
)
String
()
string
{
return
fmt
.
Sprintf
(
"{p%s c%d}"
,
t
.
parent
,
t
.
nchild
)
}
const
debugPPSet
=
false
const
debugPPSet
=
false
wcfs/δbtail.go
View file @
9acd5150
...
@@ -178,10 +178,10 @@ type ΔBtail struct {
...
@@ -178,10 +178,10 @@ type ΔBtail struct {
// tracked nodes index: node -> parent + accessed holes under this node XXX -> holeIdx
// tracked nodes index: node -> parent + accessed holes under this node XXX -> holeIdx
// we only allow single parent/root case and report "tree corrupt" otherwise.
// we only allow single parent/root case and report "tree corrupt" otherwise.
// trackIdx describes @head state
// trackIdx describes @head state
trackIdx
trackIndex
trackIdx
PPTreeSubSet
// XXX -> trackSet
// tree(s) subset that was requested to be tracked, but for which vδB was not yet rebuilt
// tree(s) subset that was requested to be tracked, but for which vδB was not yet rebuilt
trackNew
trackIndex
trackNew
PPTreeSubSet
// XXX root -> tracked holes
// XXX root -> tracked holes
// XXX move -> ΔTtail ?
// XXX move -> ΔTtail ?
...
@@ -247,8 +247,8 @@ func NewΔBtail(at0 zodb.Tid, db *zodb.DB) *ΔBtail {
...
@@ -247,8 +247,8 @@ func NewΔBtail(at0 zodb.Tid, db *zodb.DB) *ΔBtail {
return
&
ΔBtail
{
return
&
ΔBtail
{
δZtail
:
zodb
.
NewΔTail
(
at0
),
δZtail
:
zodb
.
NewΔTail
(
at0
),
byRoot
:
map
[
zodb
.
Oid
]
*
ΔTtail
{},
byRoot
:
map
[
zodb
.
Oid
]
*
ΔTtail
{},
trackIdx
:
trackIndex
{},
trackIdx
:
PPTreeSubSet
{},
trackNew
:
trackIndex
{},
trackNew
:
PPTreeSubSet
{},
holeIdxByRoot
:
map
[
zodb
.
Oid
]
treeSetKey
{},
holeIdxByRoot
:
map
[
zodb
.
Oid
]
treeSetKey
{},
db
:
db
,
db
:
db
,
}
}
...
@@ -383,7 +383,7 @@ func (δBtail *ΔBtail) rebuild() (err error) {
...
@@ -383,7 +383,7 @@ func (δBtail *ΔBtail) rebuild() (err error) {
// XXX locking
// XXX locking
trackNew
:=
δBtail
.
trackNew
trackNew
:=
δBtail
.
trackNew
δBtail
.
trackNew
=
trackIndex
{}
δBtail
.
trackNew
=
PPTreeSubSet
{}
if
len
(
trackNew
)
==
0
{
if
len
(
trackNew
)
==
0
{
return
return
...
@@ -391,7 +391,7 @@ func (δBtail *ΔBtail) rebuild() (err error) {
...
@@ -391,7 +391,7 @@ func (δBtail *ΔBtail) rebuild() (err error) {
// go backwards and merge vδT <- treediff(lo..hi/trackNew)
// go backwards and merge vδT <- treediff(lo..hi/trackNew)
vδZ
:=
δBtail
.
δZtail
.
Data
()
vδZ
:=
δBtail
.
δZtail
.
Data
()
vδtrack
:=
[]
*
δtrackIndex
{}
vδtrack
:=
[]
*
ΔPPTreeSubSet
{}
for
i
:=
len
(
vδZ
)
-
1
;
i
>=
0
;
i
--
{
for
i
:=
len
(
vδZ
)
-
1
;
i
>=
0
;
i
--
{
δZ
:=
vδZ
[
i
]
δZ
:=
vδZ
[
i
]
...
@@ -439,7 +439,7 @@ func (δBtail *ΔBtail) rebuild() (err error) {
...
@@ -439,7 +439,7 @@ func (δBtail *ΔBtail) rebuild() (err error) {
tracef
(
"-> root<%s> δkv*: %v δtrack*: %v
\n
"
,
root
,
δT
,
δtrack
)
tracef
(
"-> root<%s> δkv*: %v δtrack*: %v
\n
"
,
root
,
δT
,
δtrack
)
trackNew
.
ApplyΔ
(
δtrack
)
trackNew
.
ApplyΔ
(
δtrack
)
vδtrack
=
append
([]
*
δtrackIndex
{
δtrack
},
vδtrack
...
)
vδtrack
=
append
([]
*
ΔPPTreeSubSet
{
δtrack
},
vδtrack
...
)
if
len
(
δT
)
==
0
{
// an object might be resaved without change
if
len
(
δT
)
==
0
{
// an object might be resaved without change
continue
continue
...
@@ -588,7 +588,7 @@ func (δBtail *ΔBtail) Update(δZ *zodb.EventCommit) (_ ΔB, err error) {
...
@@ -588,7 +588,7 @@ func (δBtail *ΔBtail) Update(δZ *zodb.EventCommit) (_ ΔB, err error) {
// for example for e.g. t₀->t₁->b₂ if δZ/T={t₀ b₂} -> δZ/TC=δZ/T+{t₁}
// for example for e.g. t₀->t₁->b₂ if δZ/T={t₀ b₂} -> δZ/TC=δZ/T+{t₁}
//
//
// δtopsByRoot = {} root -> {top changed nodes in that tree}
// δtopsByRoot = {} root -> {top changed nodes in that tree}
func
δZConnectTracked
(
δZv
[]
zodb
.
Oid
,
T
trackIndex
)
(
δZTC
SetOid
,
δtopsByRoot
map
[
zodb
.
Oid
]
SetOid
)
{
func
δZConnectTracked
(
δZv
[]
zodb
.
Oid
,
T
PPTreeSubSet
)
(
δZTC
SetOid
,
δtopsByRoot
map
[
zodb
.
Oid
]
SetOid
)
{
δZ
:=
SetOid
{};
for
_
,
δ
:=
range
δZv
{
δZ
.
Add
(
δ
)
}
δZ
:=
SetOid
{};
for
_
,
δ
:=
range
δZv
{
δZ
.
Add
(
δ
)
}
δZTC
=
SetOid
{}
δZTC
=
SetOid
{}
δtopsByRoot
=
map
[
zodb
.
Oid
]
SetOid
{}
δtopsByRoot
=
map
[
zodb
.
Oid
]
SetOid
{}
...
@@ -816,14 +816,14 @@ func (rs rangeSplit) String() string {
...
@@ -816,14 +816,14 @@ func (rs rangeSplit) String() string {
//
//
// XXX holeIdx is updated XXX -> return similarly to δtrack
// XXX holeIdx is updated XXX -> return similarly to δtrack
// XXX ^^^ -> but better kill holeIdx and do everything only via trackIdx
// XXX ^^^ -> but better kill holeIdx and do everything only via trackIdx
func
treediff
(
ctx
context
.
Context
,
root
zodb
.
Oid
,
δtops
SetOid
,
δZTC
SetOid
,
trackIdx
trackIndex
,
holeIdx
treeSetKey
,
zconnOld
,
zconnNew
*
zodb
.
Connection
)
(
δT
map
[
Key
]
ΔValue
,
δtrack
*
δtrackIndex
,
err
error
)
{
func
treediff
(
ctx
context
.
Context
,
root
zodb
.
Oid
,
δtops
SetOid
,
δZTC
SetOid
,
trackIdx
PPTreeSubSet
,
holeIdx
treeSetKey
,
zconnOld
,
zconnNew
*
zodb
.
Connection
)
(
δT
map
[
Key
]
ΔValue
,
δtrack
*
ΔPPTreeSubSet
,
err
error
)
{
defer
xerr
.
Contextf
(
&
err
,
"treediff %s..%s %s"
,
zconnOld
.
At
(),
zconnNew
.
At
(),
root
)
defer
xerr
.
Contextf
(
&
err
,
"treediff %s..%s %s"
,
zconnOld
.
At
(),
zconnNew
.
At
(),
root
)
tracef
(
"
\n
treediff %s δtops: %v δZTC: %v
\n
"
,
root
,
δtops
,
δZTC
)
tracef
(
"
\n
treediff %s δtops: %v δZTC: %v
\n
"
,
root
,
δtops
,
δZTC
)
defer
tracef
(
"
\n
"
)
defer
tracef
(
"
\n
"
)
δT
=
map
[
Key
]
ΔValue
{}
δT
=
map
[
Key
]
ΔValue
{}
δtrackv
:=
[]
*
δtrackIndex
{}
δtrackv
:=
[]
*
ΔPPTreeSubSet
{}
for
top
:=
range
δtops
{
// XXX -> sorted?
for
top
:=
range
δtops
{
// XXX -> sorted?
a
,
err1
:=
zgetNode
(
ctx
,
zconnOld
,
top
)
a
,
err1
:=
zgetNode
(
ctx
,
zconnOld
,
top
)
...
@@ -863,7 +863,7 @@ func treediff(ctx context.Context, root zodb.Oid, δtops SetOid, δZTC SetOid, t
...
@@ -863,7 +863,7 @@ func treediff(ctx context.Context, root zodb.Oid, δtops SetOid, δZTC SetOid, t
}
}
// adjust trackIdx by merge(δtrackTops)
// adjust trackIdx by merge(δtrackTops)
δtrack
=
&
δtrackIndex
{
Del
:
trackIndex
{},
Add
:
trackIndex
{},
δnchildNonLeafs
:
map
[
zodb
.
Oid
]
int
{}}
δtrack
=
&
ΔPPTreeSubSet
{
Del
:
PPTreeSubSet
{},
Add
:
PPTreeSubSet
{},
δnchildNonLeafs
:
map
[
zodb
.
Oid
]
int
{}}
for
_
,
δ
:=
range
δtrackv
{
for
_
,
δ
:=
range
δtrackv
{
δtrack
.
Update
(
δ
)
δtrack
.
Update
(
δ
)
}
}
...
@@ -880,9 +880,9 @@ func treediff(ctx context.Context, root zodb.Oid, δtops SetOid, δZTC SetOid, t
...
@@ -880,9 +880,9 @@ func treediff(ctx context.Context, root zodb.Oid, δtops SetOid, δZTC SetOid, t
//
//
// a/b can be nil; a=nil means addition, b=nil means deletion.
// a/b can be nil; a=nil means addition, b=nil means deletion.
//
//
// δtrackI
nde
x is trackIdx δ that needs to be applied to trackIdx to keep it
// δtrackI
d
x is trackIdx δ that needs to be applied to trackIdx to keep it
// consistent with b (= a + δ).
// consistent with b (= a + δ).
func
diffX
(
ctx
context
.
Context
,
a
,
b
Node
,
δZTC
SetOid
,
trackIdx
trackIndex
,
holeIdx
treeSetKey
)
(
δ
map
[
Key
]
ΔValue
,
δtrackIdx
*
δtrackIndex
,
err
error
)
{
func
diffX
(
ctx
context
.
Context
,
a
,
b
Node
,
δZTC
SetOid
,
trackIdx
PPTreeSubSet
,
holeIdx
treeSetKey
)
(
δ
map
[
Key
]
ΔValue
,
δtrackIdx
*
ΔPPTreeSubSet
,
err
error
)
{
if
a
==
nil
&&
b
==
nil
{
if
a
==
nil
&&
b
==
nil
{
panic
(
"BUG: both a & b == nil"
)
panic
(
"BUG: both a & b == nil"
)
}
}
...
@@ -919,10 +919,10 @@ func diffX(ctx context.Context, a, b Node, δZTC SetOid, trackIdx trackIndex, ho
...
@@ -919,10 +919,10 @@ func diffX(ctx context.Context, a, b Node, δZTC SetOid, trackIdx trackIndex, ho
if
isT
{
if
isT
{
return
diffT
(
ctx
,
aT
,
bT
,
δZTC
,
trackIdx
,
holeIdx
)
return
diffT
(
ctx
,
aT
,
bT
,
δZTC
,
trackIdx
,
holeIdx
)
}
else
{
}
else
{
var
δtrack
*
δtrackIndex
var
δtrack
*
ΔPPTreeSubSet
δ
,
err
:=
diffB
(
ctx
,
aB
,
bB
)
δ
,
err
:=
diffB
(
ctx
,
aB
,
bB
)
if
δ
!=
nil
{
if
δ
!=
nil
{
δtrack
=
&
δtrackIndex
{}
δtrack
=
&
ΔPPTreeSubSet
{}
}
}
return
δ
,
δtrack
,
err
return
δ
,
δtrack
,
err
}
}
...
@@ -932,7 +932,7 @@ func diffX(ctx context.Context, a, b Node, δZTC SetOid, trackIdx trackIndex, ho
...
@@ -932,7 +932,7 @@ func diffX(ctx context.Context, a, b Node, δZTC SetOid, trackIdx trackIndex, ho
//
//
// a, b point to top of subtrees @old and @new revisions.
// a, b point to top of subtrees @old and @new revisions.
// δZTC is connected set of objects covering δZT (objects changed in this tree in old..new).
// δZTC is connected set of objects covering δZT (objects changed in this tree in old..new).
func
diffT
(
ctx
context
.
Context
,
A
,
B
*
Tree
,
δZTC
SetOid
,
trackIdx
trackIndex
,
holeIdx
treeSetKey
)
(
δ
map
[
Key
]
ΔValue
,
δtrack
*
δtrackIndex
,
err
error
)
{
func
diffT
(
ctx
context
.
Context
,
A
,
B
*
Tree
,
δZTC
SetOid
,
trackIdx
PPTreeSubSet
,
holeIdx
treeSetKey
)
(
δ
map
[
Key
]
ΔValue
,
δtrack
*
ΔPPTreeSubSet
,
err
error
)
{
tracef
(
" diffT %s %s
\n
"
,
xidOf
(
A
),
xidOf
(
B
))
tracef
(
" diffT %s %s
\n
"
,
xidOf
(
A
),
xidOf
(
B
))
defer
xerr
.
Contextf
(
&
err
,
"diffT %s %s"
,
xidOf
(
A
),
xidOf
(
B
))
defer
xerr
.
Contextf
(
&
err
,
"diffT %s %s"
,
xidOf
(
A
),
xidOf
(
B
))
...
@@ -940,7 +940,7 @@ func diffT(ctx context.Context, A, B *Tree, δZTC SetOid, trackIdx trackIndex, h
...
@@ -940,7 +940,7 @@ func diffT(ctx context.Context, A, B *Tree, δZTC SetOid, trackIdx trackIndex, h
if
B
==
nil
{
panic
(
"B is nil"
)
}
if
B
==
nil
{
panic
(
"B is nil"
)
}
δ
=
map
[
Key
]
ΔValue
{}
δ
=
map
[
Key
]
ΔValue
{}
δtrack
=
&
δtrackIndex
{
Del
:
trackIndex
{},
Add
:
trackIndex
{},
δnchildNonLeafs
:
map
[
zodb
.
Oid
]
int
{}}
δtrack
=
&
ΔPPTreeSubSet
{
Del
:
PPTreeSubSet
{},
Add
:
PPTreeSubSet
{},
δnchildNonLeafs
:
map
[
zodb
.
Oid
]
int
{}}
defer
tracef
(
" -> δ: %v
\n
"
,
δ
)
defer
tracef
(
" -> δ: %v
\n
"
,
δ
)
// path prefix to A and B
// path prefix to A and B
...
@@ -978,7 +978,7 @@ func diffT(ctx context.Context, A, B *Tree, δZTC SetOid, trackIdx trackIndex, h
...
@@ -978,7 +978,7 @@ func diffT(ctx context.Context, A, B *Tree, δZTC SetOid, trackIdx trackIndex, h
// {} oid -> parent for all nodes in Bv: current and previously expanded - up till top B
// {} oid -> parent for all nodes in Bv: current and previously expanded - up till top B
// XXX requires A.oid == B.oid
// XXX requires A.oid == B.oid
BtrackIdx
:=
trackIndex
{}
BtrackIdx
:=
PPTreeSubSet
{}
BtrackIdx
.
AddPath
(
trackIdx
.
Path
(
B
.
POid
()))
BtrackIdx
.
AddPath
(
trackIdx
.
Path
(
B
.
POid
()))
// phase 1: expand A top->down driven by δZTC.
// phase 1: expand A top->down driven by δZTC.
...
@@ -1506,11 +1506,6 @@ func (rn nodeInRange) String() string {
...
@@ -1506,11 +1506,6 @@ func (rn nodeInRange) String() string {
return
fmt
.
Sprintf
(
"%s[%s,%s)%s"
,
done
,
slo
,
shi
,
vnode
(
rn
.
node
))
return
fmt
.
Sprintf
(
"%s[%s,%s)%s"
,
done
,
slo
,
shi
,
vnode
(
rn
.
node
))
}
}
func
(
track
nodeTrack
)
String
()
string
{
//return fmt.Sprintf("{p%s h%s}", track.parent, track.holes)
return
fmt
.
Sprintf
(
"{p%s c%d}"
,
track
.
parent
,
track
.
nchild
)
}
// push pushes element to node stack.
// push pushes element to node stack.
func
push
(
nodeStk
*
[]
*
nodeInRange
,
top
*
nodeInRange
)
{
func
push
(
nodeStk
*
[]
*
nodeInRange
,
top
*
nodeInRange
)
{
...
...
wcfs/δbtail_test.go
View file @
9acd5150
...
@@ -313,8 +313,9 @@ func (rbs RBucketSet) holeIdx(tracked SetKey) SetKey {
...
@@ -313,8 +313,9 @@ func (rbs RBucketSet) holeIdx(tracked SetKey) SetKey {
}
}
// trackIdx returns what should be ΔBtree.trackIdx for specified tracked key set.
// trackIdx returns what should be ΔBtree.trackIdx for specified tracked key set.
func
(
rbs
RBucketSet
)
trackIdx
(
tracked
SetKey
)
trackIndex
{
// XXX -> trackSet
trackIdx
:=
trackIndex
{}
func
(
rbs
RBucketSet
)
trackIdx
(
tracked
SetKey
)
PPTreeSubSet
{
trackIdx
:=
PPTreeSubSet
{}
for
k
:=
range
tracked
{
for
k
:=
range
tracked
{
kb
:=
rbs
.
Get
(
k
)
kb
:=
rbs
.
Get
(
k
)
// trackIdx explicitly records only regular buckets.
// trackIdx explicitly records only regular buckets.
...
@@ -323,7 +324,7 @@ func (rbs RBucketSet) trackIdx(tracked SetKey) trackIndex {
...
@@ -323,7 +324,7 @@ func (rbs RBucketSet) trackIdx(tracked SetKey) trackIndex {
if
kb
.
oid
!=
zodb
.
InvalidOid
{
if
kb
.
oid
!=
zodb
.
InvalidOid
{
track
,
already
:=
trackIdx
[
kb
.
oid
]
track
,
already
:=
trackIdx
[
kb
.
oid
]
if
!
already
{
if
!
already
{
track
=
&
node
Track
{
parent
:
kb
.
parent
.
oid
,
nchild
:
0
}
track
=
&
node
InTree
{
parent
:
kb
.
parent
.
oid
,
nchild
:
0
}
trackIdx
[
kb
.
oid
]
=
track
trackIdx
[
kb
.
oid
]
=
track
newNode
=
true
newNode
=
true
}
}
...
@@ -342,7 +343,7 @@ func (rbs RBucketSet) trackIdx(tracked SetKey) trackIndex {
...
@@ -342,7 +343,7 @@ func (rbs RBucketSet) trackIdx(tracked SetKey) trackIndex {
pt
,
already
:=
trackIdx
[
p
.
oid
]
pt
,
already
:=
trackIdx
[
p
.
oid
]
newParent
:=
false
newParent
:=
false
if
!
already
{
if
!
already
{
pt
=
&
node
Track
{
parent
:
ppoid
,
nchild
:
0
}
pt
=
&
node
InTree
{
parent
:
ppoid
,
nchild
:
0
}
trackIdx
[
p
.
oid
]
=
pt
trackIdx
[
p
.
oid
]
=
pt
newParent
=
true
newParent
=
true
}
}
...
@@ -754,7 +755,7 @@ func xverifyΔBTail_Update1(t *testing.T, subj string, db *zodb.DB, treeRoot zod
...
@@ -754,7 +755,7 @@ func xverifyΔBTail_Update1(t *testing.T, subj string, db *zodb.DB, treeRoot zod
// if !reflect.DeepEqual(trackIdx1, δbtail.trackIdx) {
// if !reflect.DeepEqual(trackIdx1, δbtail.trackIdx) {
// badf("δbtail.trackIdx1 wrong:\n\thave: %v\n\twant: %v", δbtail.trackIdx, trackIdx1)
// badf("δbtail.trackIdx1 wrong:\n\thave: %v\n\twant: %v", δbtail.trackIdx, trackIdx1)
// }
// }
δbtail
.
assertTrack
(
t
,
"1"
,
/*ø*/
trackIndex
{},
trackIdx1
)
δbtail
.
assertTrack
(
t
,
"1"
,
/*ø*/
PPTreeSubSet
{},
trackIdx1
)
// δB <- δZ
// δB <- δZ
...
@@ -777,7 +778,7 @@ func xverifyΔBTail_Update1(t *testing.T, subj string, db *zodb.DB, treeRoot zod
...
@@ -777,7 +778,7 @@ func xverifyΔBTail_Update1(t *testing.T, subj string, db *zodb.DB, treeRoot zod
// if !reflect.DeepEqual(trackIdx2, δbtail.trackIdx) {
// if !reflect.DeepEqual(trackIdx2, δbtail.trackIdx) {
// badf("δbtail.trackIdx2 wrong:\n\thave: %v\n\twant: %v", δbtail.trackIdx, trackIdx2)
// badf("δbtail.trackIdx2 wrong:\n\thave: %v\n\twant: %v", δbtail.trackIdx, trackIdx2)
// }
// }
δbtail
.
assertTrack
(
t
,
"2"
,
trackIdx2
,
/*ø*/
trackIndex
{})
δbtail
.
assertTrack
(
t
,
"2"
,
trackIdx2
,
/*ø*/
PPTreeSubSet
{})
// assert δB.ByRoot == {treeRoot -> ...} if δTok != ø
// assert δB.ByRoot == {treeRoot -> ...} if δTok != ø
...
@@ -837,30 +838,9 @@ func xverifyΔBTail_Update1(t *testing.T, subj string, db *zodb.DB, treeRoot zod
...
@@ -837,30 +838,9 @@ func xverifyΔBTail_Update1(t *testing.T, subj string, db *zodb.DB, treeRoot zod
}
}
}
}
// equal returns whether a == b.
// XXX place
func
(
a
trackIndex
)
equal
(
b
trackIndex
)
bool
{
if
len
(
a
)
!=
len
(
b
)
{
return
false
}
for
oid
,
ta
:=
range
a
{
tb
,
ok
:=
b
[
oid
]
if
!
ok
{
return
false
}
if
!
(
ta
.
parent
==
tb
.
parent
&&
ta
.
nchild
==
tb
.
nchild
)
{
return
false
}
}
return
true
}
// assertTrack verifies that trackIdx == trackIdxOK.
// assertTrack verifies that trackIdx == trackIdxOK.
// XXX place
// XXX place
func
assertTrack
(
t
*
testing
.
T
,
subj
string
,
trackIdx
,
trackIdxOK
trackIndex
)
{
func
assertTrack
(
t
*
testing
.
T
,
subj
string
,
trackIdx
,
trackIdxOK
PPTreeSubSet
)
{
t
.
Helper
()
t
.
Helper
()
// eq := reflect.DeepEqual(trackIdx, trackIdxOK) // slower
// eq := reflect.DeepEqual(trackIdx, trackIdxOK) // slower
eq
:=
trackIdx
.
equal
(
trackIdxOK
)
eq
:=
trackIdx
.
equal
(
trackIdxOK
)
...
@@ -871,7 +851,7 @@ func assertTrack(t *testing.T, subj string, trackIdx, trackIdxOK trackIndex) {
...
@@ -871,7 +851,7 @@ func assertTrack(t *testing.T, subj string, trackIdx, trackIdxOK trackIndex) {
// assertTrack verifies state of .trackIdx and .trackNew.
// assertTrack verifies state of .trackIdx and .trackNew.
// XXX place
// XXX place
func
(
δbtail
*
ΔBtail
)
assertTrack
(
t
*
testing
.
T
,
subj
string
,
trackIdxOK
,
trackNewOK
trackIndex
)
{
func
(
δbtail
*
ΔBtail
)
assertTrack
(
t
*
testing
.
T
,
subj
string
,
trackIdxOK
,
trackNewOK
PPTreeSubSet
)
{
t
.
Helper
()
t
.
Helper
()
assertTrack
(
t
,
subj
+
": trackIdx"
,
δbtail
.
trackIdx
,
trackIdxOK
)
assertTrack
(
t
,
subj
+
": trackIdx"
,
δbtail
.
trackIdx
,
trackIdxOK
)
assertTrack
(
t
,
subj
+
": trackNew"
,
δbtail
.
trackNew
,
trackNewOK
)
assertTrack
(
t
,
subj
+
": trackNew"
,
δbtail
.
trackNew
,
trackNewOK
)
...
@@ -905,7 +885,7 @@ func xverifyΔBTail_rebuild(t *testing.T, db *zodb.DB, treeRoot zodb.Oid, t0, t1
...
@@ -905,7 +885,7 @@ func xverifyΔBTail_rebuild(t *testing.T, db *zodb.DB, treeRoot zodb.Oid, t0, t1
kadj21
:=
KAdj
(
t2
,
t1
,
allTestKeys
(
t0
,
t1
,
t2
))
kadj21
:=
KAdj
(
t2
,
t1
,
allTestKeys
(
t0
,
t1
,
t2
))
kadj12
:=
KAdj
(
t1
,
t2
,
allTestKeys
(
t0
,
t1
,
t2
))
kadj12
:=
KAdj
(
t1
,
t2
,
allTestKeys
(
t0
,
t1
,
t2
))
ø
:=
trackIndex
{}
ø
:=
PPTreeSubSet
{}
// verify t0 -> t1 Track(keys1) Rebuild -> t2 Track(keys2) Rebuild
// verify t0 -> t1 Track(keys1) Rebuild -> t2 Track(keys2) Rebuild
// for all combinations of keys1 and keys2
// for all combinations of keys1 and keys2
...
@@ -995,10 +975,10 @@ func xverifyΔBTail_rebuild(t *testing.T, db *zodb.DB, treeRoot zodb.Oid, t0, t1
...
@@ -995,10 +975,10 @@ func xverifyΔBTail_rebuild(t *testing.T, db *zodb.DB, treeRoot zodb.Oid, t0, t1
}
}
// xverifyΔBTail_rebuild_U verifies ΔBTree state after Update(ti->tj).
// xverifyΔBTail_rebuild_U verifies ΔBTree state after Update(ti->tj).
func
xverifyΔBTail_rebuild_U
(
t
*
testing
.
T
,
δbtail
*
ΔBtail
,
ti
,
tj
*
tTreeCommit
,
xat
map
[
zodb
.
Tid
]
string
,
trackIdx
trackIndex
)
{
func
xverifyΔBTail_rebuild_U
(
t
*
testing
.
T
,
δbtail
*
ΔBtail
,
ti
,
tj
*
tTreeCommit
,
xat
map
[
zodb
.
Tid
]
string
,
trackIdx
PPTreeSubSet
)
{
t
.
Helper
()
t
.
Helper
()
X
:=
exc
.
Raiseif
X
:=
exc
.
Raiseif
ø
:=
trackIndex
{}
ø
:=
PPTreeSubSet
{}
// Update ati -> atj
// Update ati -> atj
δB
,
err
:=
δbtail
.
Update
(
tj
.
δZ
);
X
(
err
)
δB
,
err
:=
δbtail
.
Update
(
tj
.
δZ
);
X
(
err
)
...
@@ -1008,10 +988,10 @@ func xverifyΔBTail_rebuild_U(t *testing.T, δbtail *ΔBtail, ti, tj *tTreeCommi
...
@@ -1008,10 +988,10 @@ func xverifyΔBTail_rebuild_U(t *testing.T, δbtail *ΔBtail, ti, tj *tTreeCommi
}
}
// xverifyΔBTail_rebuild_TR verifies ΔBTree state after Track(keys) + rebuild.
// xverifyΔBTail_rebuild_TR verifies ΔBTree state after Track(keys) + rebuild.
func
xverifyΔBTail_rebuild_TR
(
t
*
testing
.
T
,
db
*
zodb
.
DB
,
δbtail
*
ΔBtail
,
tj
*
tTreeCommit
,
treeRoot
zodb
.
Oid
,
xat
map
[
zodb
.
Tid
]
string
,
keys
SetKey
,
trackIdx
trackIndex
,
trackNew
,
trackIdxAfterRebuild
trackIndex
,
vδTok
...
map
[
Key
]
Δstring
)
{
func
xverifyΔBTail_rebuild_TR
(
t
*
testing
.
T
,
db
*
zodb
.
DB
,
δbtail
*
ΔBtail
,
tj
*
tTreeCommit
,
treeRoot
zodb
.
Oid
,
xat
map
[
zodb
.
Tid
]
string
,
keys
SetKey
,
trackIdx
PPTreeSubSet
,
trackNew
,
trackIdxAfterRebuild
PPTreeSubSet
,
vδTok
...
map
[
Key
]
Δstring
)
{
t
.
Helper
()
t
.
Helper
()
X
:=
exc
.
Raiseif
X
:=
exc
.
Raiseif
ø
:=
trackIndex
{}
ø
:=
PPTreeSubSet
{}
// Track(keys)
// Track(keys)
txn
,
ctx
:=
transaction
.
New
(
context
.
Background
())
txn
,
ctx
:=
transaction
.
New
(
context
.
Background
())
...
...
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