forked from TrueCloudLab/frostfs-node
Alexander Chuprov
033eaf77e1
Standardize the alias of the import frostfs-sdk-go/object as objectSDK. Signed-off-by: Alexander Chuprov <a.chuprov@yadro.com>
184 lines
4.6 KiB
Go
184 lines
4.6 KiB
Go
package util
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
|
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
|
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
|
)
|
|
|
|
// HeadReceiver is an interface of entity that can receive
|
|
// object header or the information about the object relations.
|
|
type HeadReceiver interface {
|
|
// Head must return one of:
|
|
// * object header (*object.Object);
|
|
// * structured information about split-chain (*object.SplitInfo).
|
|
Head(id oid.Address) (any, error)
|
|
}
|
|
|
|
// SplitMemberHandler is a handler of next split-chain element.
|
|
//
|
|
// If reverseDirection arg is true, then the traversal is done in reverse order.
|
|
// Stop boolean result provides the ability to interrupt the traversal.
|
|
type SplitMemberHandler func(member *objectSDK.Object, reverseDirection bool) (stop bool)
|
|
|
|
// IterateAllSplitLeaves is an iterator over all object split-tree leaves in direct order.
|
|
func IterateAllSplitLeaves(r HeadReceiver, addr oid.Address, h func(*objectSDK.Object)) error {
|
|
return IterateSplitLeaves(r, addr, func(leaf *objectSDK.Object) bool {
|
|
h(leaf)
|
|
return false
|
|
})
|
|
}
|
|
|
|
// IterateSplitLeaves is an iterator over object split-tree leaves in direct order.
|
|
//
|
|
// If member handler returns true, then the iterator aborts without error.
|
|
func IterateSplitLeaves(r HeadReceiver, addr oid.Address, h func(*objectSDK.Object) bool) error {
|
|
var (
|
|
reverse bool
|
|
leaves []*objectSDK.Object
|
|
)
|
|
|
|
if err := TraverseSplitChain(r, addr, func(member *objectSDK.Object, reverseDirection bool) (stop bool) {
|
|
reverse = reverseDirection
|
|
|
|
if reverse {
|
|
leaves = append(leaves, member)
|
|
return false
|
|
}
|
|
|
|
return h(member)
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
|
|
for i := len(leaves) - 1; i >= 0; i-- {
|
|
if h(leaves[i]) {
|
|
break
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// TraverseSplitChain is an iterator over object split-tree leaves.
|
|
//
|
|
// Traversal occurs in one of two directions, which depends on what pslit info was received:
|
|
// * in direct order for link part;
|
|
// * in reverse order for last part.
|
|
func TraverseSplitChain(r HeadReceiver, addr oid.Address, h SplitMemberHandler) error {
|
|
_, err := traverseSplitChain(r, addr, h)
|
|
return err
|
|
}
|
|
|
|
func traverseSplitChain(r HeadReceiver, addr oid.Address, h SplitMemberHandler) (bool, error) {
|
|
v, err := r.Head(addr)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
cnr := addr.Container()
|
|
|
|
switch res := v.(type) {
|
|
default:
|
|
panic(fmt.Sprintf("unexpected result of %T: %T", r, v))
|
|
case *objectSDK.Object:
|
|
return h(res, false), nil
|
|
case *objectSDK.SplitInfo:
|
|
link, withLink := res.Link()
|
|
last, withLast := res.LastPart()
|
|
|
|
switch {
|
|
default:
|
|
return false, errors.New("lack of split information")
|
|
case withLink:
|
|
return traverseByLink(cnr, link, r, h)
|
|
case withLast:
|
|
return traverseByLast(cnr, last, withLast, res, r, h)
|
|
}
|
|
}
|
|
}
|
|
|
|
func traverseByLink(cnr cid.ID, link oid.ID, r HeadReceiver, h SplitMemberHandler) (bool, error) {
|
|
var addr oid.Address
|
|
addr.SetContainer(cnr)
|
|
addr.SetObject(link)
|
|
|
|
chain := make([]oid.ID, 0)
|
|
|
|
if _, err := traverseSplitChain(r, addr, func(member *objectSDK.Object, reverseDirection bool) (stop bool) {
|
|
children := member.Children()
|
|
|
|
if reverseDirection {
|
|
chain = append(children, chain...)
|
|
} else {
|
|
chain = append(chain, children...)
|
|
}
|
|
|
|
return false
|
|
}); err != nil {
|
|
return false, err
|
|
}
|
|
|
|
var reverseChain []*objectSDK.Object
|
|
|
|
for i := range chain {
|
|
addr.SetObject(chain[i])
|
|
|
|
if stop, err := traverseSplitChain(r, addr, func(member *objectSDK.Object, reverseDirection bool) (stop bool) {
|
|
if !reverseDirection {
|
|
return h(member, false)
|
|
}
|
|
|
|
reverseChain = append(reverseChain, member)
|
|
return false
|
|
}); err != nil || stop {
|
|
return stop, err
|
|
}
|
|
}
|
|
|
|
for i := len(reverseChain) - 1; i >= 0; i-- {
|
|
if h(reverseChain[i], false) {
|
|
return true, nil
|
|
}
|
|
}
|
|
return false, nil
|
|
}
|
|
|
|
func traverseByLast(cnr cid.ID, last oid.ID, withLast bool, res *objectSDK.SplitInfo, r HeadReceiver, h SplitMemberHandler) (bool, error) {
|
|
var addr oid.Address
|
|
addr.SetContainer(cnr)
|
|
|
|
for last, withLast = res.LastPart(); withLast; {
|
|
addr.SetObject(last)
|
|
|
|
var directChain []*objectSDK.Object
|
|
|
|
if _, err := traverseSplitChain(r, addr, func(member *objectSDK.Object, reverseDirection bool) (stop bool) {
|
|
if reverseDirection {
|
|
last, withLast = member.PreviousID()
|
|
return h(member, true)
|
|
}
|
|
|
|
directChain = append(directChain, member)
|
|
|
|
return false
|
|
}); err != nil {
|
|
return false, err
|
|
}
|
|
|
|
for i := len(directChain) - 1; i >= 0; i-- {
|
|
if h(directChain[i], true) {
|
|
return true, nil
|
|
}
|
|
}
|
|
|
|
if len(directChain) > 0 {
|
|
last, withLast = directChain[len(directChain)-1].PreviousID()
|
|
}
|
|
}
|
|
|
|
return false, nil
|
|
}
|