2020-06-19 04:09:58 +00:00
|
|
|
package bounded_tree
|
|
|
|
|
|
|
|
import (
|
2020-06-26 17:00:48 +00:00
|
|
|
"sync"
|
|
|
|
|
2020-06-19 04:09:58 +00:00
|
|
|
"github.com/chrislusf/seaweedfs/weed/glog"
|
|
|
|
"github.com/chrislusf/seaweedfs/weed/util"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Node struct {
|
|
|
|
Parent *Node
|
|
|
|
Name string
|
|
|
|
Children map[string]*Node
|
|
|
|
}
|
|
|
|
|
|
|
|
type BoundedTree struct {
|
|
|
|
root *Node
|
2020-08-23 22:48:02 +00:00
|
|
|
sync.RWMutex
|
2020-10-13 04:58:37 +00:00
|
|
|
baseDir util.FullPath
|
2020-06-19 04:09:58 +00:00
|
|
|
}
|
|
|
|
|
2020-10-13 04:58:37 +00:00
|
|
|
func NewBoundedTree(baseDir util.FullPath) *BoundedTree {
|
2020-06-19 04:09:58 +00:00
|
|
|
return &BoundedTree{
|
|
|
|
root: &Node{
|
|
|
|
Name: "/",
|
|
|
|
},
|
2020-10-13 04:58:37 +00:00
|
|
|
baseDir: baseDir,
|
2020-06-19 04:09:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type VisitNodeFunc func(path util.FullPath) (childDirectories []string, err error)
|
|
|
|
|
|
|
|
// If the path is not visited, call the visitFn for each level of directory
|
|
|
|
// No action if the directory has been visited before or does not exist.
|
|
|
|
// A leaf node, which has no children, represents a directory not visited.
|
|
|
|
// A non-leaf node or a non-existing node represents a directory already visited, or does not need to visit.
|
2020-10-14 02:50:46 +00:00
|
|
|
func (t *BoundedTree) EnsureVisited(p util.FullPath, visitFn VisitNodeFunc) (visitErr error) {
|
2020-06-26 17:00:48 +00:00
|
|
|
t.Lock()
|
|
|
|
defer t.Unlock()
|
2020-06-19 04:09:58 +00:00
|
|
|
|
|
|
|
if t.root == nil {
|
|
|
|
return
|
|
|
|
}
|
2020-10-13 04:58:37 +00:00
|
|
|
if t.baseDir != "/" {
|
|
|
|
p = p[len(t.baseDir):]
|
|
|
|
}
|
2020-06-19 04:09:58 +00:00
|
|
|
components := p.Split()
|
2020-06-19 05:28:17 +00:00
|
|
|
// fmt.Printf("components %v %d\n", components, len(components))
|
2020-10-13 18:21:13 +00:00
|
|
|
canDelete, err := t.ensureVisited(t.root, t.baseDir, components, 0, visitFn)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if canDelete {
|
2020-06-19 04:09:58 +00:00
|
|
|
t.root = nil
|
|
|
|
}
|
2020-10-13 18:21:13 +00:00
|
|
|
return nil
|
2020-06-19 04:09:58 +00:00
|
|
|
}
|
|
|
|
|
2020-10-13 18:21:13 +00:00
|
|
|
func (t *BoundedTree) ensureVisited(n *Node, currentPath util.FullPath, components []string, i int, visitFn VisitNodeFunc) (canDeleteNode bool, visitErr error) {
|
2020-06-19 04:09:58 +00:00
|
|
|
|
2020-06-19 05:28:17 +00:00
|
|
|
// println("ensureVisited", currentPath, i)
|
2020-06-19 04:09:58 +00:00
|
|
|
|
|
|
|
if n == nil {
|
2020-06-19 05:28:17 +00:00
|
|
|
// fmt.Printf("%s null\n", currentPath)
|
2020-06-19 04:09:58 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if n.isVisited() {
|
2020-06-19 05:28:17 +00:00
|
|
|
// fmt.Printf("%s visited %v\n", currentPath, n.Name)
|
2020-06-19 04:09:58 +00:00
|
|
|
} else {
|
2020-06-19 05:28:17 +00:00
|
|
|
// fmt.Printf("ensure %v\n", currentPath)
|
2020-06-19 04:09:58 +00:00
|
|
|
|
2021-02-11 07:13:14 +00:00
|
|
|
children, err := visitFn(currentPath)
|
2020-06-19 04:09:58 +00:00
|
|
|
if err != nil {
|
|
|
|
glog.V(0).Infof("failed to visit %s: %v", currentPath, err)
|
2020-10-13 18:21:13 +00:00
|
|
|
return false, err
|
2020-06-19 04:09:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(children) == 0 {
|
2020-06-19 05:28:17 +00:00
|
|
|
// fmt.Printf(" canDelete %v without children\n", currentPath)
|
2020-10-13 18:21:13 +00:00
|
|
|
return true, nil
|
2020-06-19 04:09:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
n.Children = make(map[string]*Node)
|
|
|
|
for _, child := range children {
|
2020-06-19 05:28:17 +00:00
|
|
|
// fmt.Printf(" add child %v %v\n", currentPath, child)
|
2020-06-19 04:09:58 +00:00
|
|
|
n.Children[child] = &Node{
|
|
|
|
Name: child,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if i >= len(components) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-06-19 05:28:17 +00:00
|
|
|
// fmt.Printf(" check child %v %v\n", currentPath, components[i])
|
2020-06-19 04:09:58 +00:00
|
|
|
|
|
|
|
toVisitNode, found := n.Children[components[i]]
|
|
|
|
if !found {
|
2020-06-19 05:28:17 +00:00
|
|
|
// fmt.Printf(" did not find child %v %v\n", currentPath, components[i])
|
2020-06-19 04:09:58 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-06-19 05:28:17 +00:00
|
|
|
// fmt.Printf(" ensureVisited %v %v\n", currentPath, toVisitNode.Name)
|
2020-10-13 18:21:13 +00:00
|
|
|
canDelete, childVisitErr := t.ensureVisited(toVisitNode, currentPath.Child(components[i]), components, i+1, visitFn)
|
|
|
|
if childVisitErr != nil {
|
|
|
|
return false, childVisitErr
|
|
|
|
}
|
|
|
|
if canDelete {
|
2020-06-19 04:09:58 +00:00
|
|
|
|
2020-06-19 05:28:17 +00:00
|
|
|
// fmt.Printf(" delete %v %v\n", currentPath, components[i])
|
2020-06-19 04:09:58 +00:00
|
|
|
delete(n.Children, components[i])
|
|
|
|
|
|
|
|
if len(n.Children) == 0 {
|
2020-06-19 05:28:17 +00:00
|
|
|
// fmt.Printf(" canDelete %v\n", currentPath)
|
2020-10-13 18:21:13 +00:00
|
|
|
return true, nil
|
2020-06-19 04:09:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-13 18:21:13 +00:00
|
|
|
return false, nil
|
2020-06-19 04:09:58 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *Node) isVisited() bool {
|
|
|
|
if n == nil {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if len(n.Children) > 0 {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *Node) getChild(childName string) *Node {
|
|
|
|
if n == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if len(n.Children) > 0 {
|
|
|
|
return n.Children[childName]
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2020-06-19 05:28:17 +00:00
|
|
|
|
|
|
|
func (t *BoundedTree) HasVisited(p util.FullPath) bool {
|
|
|
|
|
2020-08-23 22:48:02 +00:00
|
|
|
t.RLock()
|
|
|
|
defer t.RUnlock()
|
|
|
|
|
2020-06-19 05:28:17 +00:00
|
|
|
if t.root == nil {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
components := p.Split()
|
2020-06-19 16:16:14 +00:00
|
|
|
// fmt.Printf("components %v %d\n", components, len(components))
|
2020-06-19 05:28:17 +00:00
|
|
|
return t.hasVisited(t.root, util.FullPath("/"), components, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *BoundedTree) hasVisited(n *Node, currentPath util.FullPath, components []string, i int) bool {
|
|
|
|
|
|
|
|
if n == nil {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
if !n.isVisited() {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-06-19 16:16:14 +00:00
|
|
|
// fmt.Printf(" hasVisited child %v %+v %d\n", currentPath, components, i)
|
2020-06-19 05:28:17 +00:00
|
|
|
|
2020-06-19 15:58:48 +00:00
|
|
|
if i >= len(components) {
|
2020-06-19 05:28:17 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-06-19 15:58:48 +00:00
|
|
|
toVisitNode, found := n.Children[components[i]]
|
|
|
|
if !found {
|
|
|
|
return true
|
2020-06-19 05:28:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return t.hasVisited(toVisitNode, currentPath.Child(components[i]), components, i+1)
|
|
|
|
|
|
|
|
}
|