2023-06-13 09:33:03 +00:00
|
|
|
package crdt
|
|
|
|
|
|
|
|
import (
|
|
|
|
"sort"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
|
|
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
versionsAddAttr = "S3-CRDT-Versions-Add"
|
|
|
|
)
|
|
|
|
|
|
|
|
type ObjectVersions struct {
|
|
|
|
name string
|
|
|
|
objects []*ObjectVersion
|
|
|
|
addList []string
|
|
|
|
isSorted bool
|
|
|
|
}
|
|
|
|
|
|
|
|
type ObjectVersion struct {
|
2024-05-24 18:31:11 +00:00
|
|
|
ObjID oid.ID
|
2023-06-13 09:33:03 +00:00
|
|
|
Headers map[string]string
|
|
|
|
CreationEpoch uint64
|
|
|
|
}
|
|
|
|
|
|
|
|
func (o *ObjectVersion) VersionID() string {
|
2024-05-24 18:31:11 +00:00
|
|
|
return o.ObjID.EncodeToString()
|
2023-06-13 09:33:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewObjectVersions(name string) *ObjectVersions {
|
|
|
|
return &ObjectVersions{name: name}
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewObjectVersion(obj *object.Object) *ObjectVersion {
|
|
|
|
objID, _ := obj.ID()
|
|
|
|
headers := make(map[string]string)
|
|
|
|
|
|
|
|
for _, attr := range obj.Attributes() {
|
|
|
|
headers[attr.Key()] = attr.Value()
|
|
|
|
}
|
|
|
|
|
|
|
|
return &ObjectVersion{
|
2024-05-24 18:31:11 +00:00
|
|
|
ObjID: objID,
|
2023-06-13 09:33:03 +00:00
|
|
|
Headers: headers,
|
|
|
|
CreationEpoch: obj.CreationEpoch(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *ObjectVersions) Name() string {
|
|
|
|
return v.name
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *ObjectVersions) AppendVersion(ov *ObjectVersion) {
|
|
|
|
addVers := append(splitVersions(ov.Headers[versionsAddAttr]), ov.VersionID())
|
|
|
|
v.objects = append(v.objects, ov)
|
|
|
|
for _, add := range addVers {
|
|
|
|
if !contains(v.addList, add) {
|
|
|
|
v.addList = append(v.addList, add)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
v.isSorted = false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *ObjectVersions) GetCRDTHeaders() map[string]string {
|
|
|
|
if len(v.objects) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
headers := make(map[string]string, 2)
|
|
|
|
|
|
|
|
if len(v.addList) != 0 {
|
|
|
|
headers[versionsAddAttr] = v.getAddHeader()
|
|
|
|
}
|
|
|
|
|
|
|
|
return headers
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *ObjectVersions) GetLast() *ObjectVersion {
|
|
|
|
if len(v.objects) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
v.sort()
|
|
|
|
return v.objects[len(v.objects)-1]
|
|
|
|
}
|
|
|
|
|
|
|
|
func splitVersions(header string) []string {
|
|
|
|
if len(header) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return strings.Split(header, ",")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *ObjectVersions) sort() {
|
|
|
|
if !v.isSorted {
|
|
|
|
sort.Slice(v.objects, func(i, j int) bool {
|
|
|
|
return less(v.objects[i], v.objects[j])
|
|
|
|
})
|
|
|
|
v.isSorted = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *ObjectVersions) getAddHeader() string {
|
|
|
|
return strings.Join(v.addList, ",")
|
|
|
|
}
|
|
|
|
|
|
|
|
func less(ov1, ov2 *ObjectVersion) bool {
|
2024-05-24 18:31:11 +00:00
|
|
|
versionID1, versionID2 := ov1.VersionID(), ov2.VersionID()
|
|
|
|
if strings.Contains(ov1.Headers[versionsAddAttr], versionID2) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if strings.Contains(ov2.Headers[versionsAddAttr], versionID1) {
|
|
|
|
return true
|
|
|
|
}
|
2023-06-13 09:33:03 +00:00
|
|
|
if ov1.CreationEpoch == ov2.CreationEpoch {
|
2024-05-24 18:31:11 +00:00
|
|
|
return versionID1 < versionID2
|
2023-06-13 09:33:03 +00:00
|
|
|
}
|
|
|
|
return ov1.CreationEpoch < ov2.CreationEpoch
|
|
|
|
}
|
|
|
|
|
|
|
|
func contains(list []string, elem string) bool {
|
|
|
|
for _, item := range list {
|
|
|
|
if elem == item {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|