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 { ObjID oid.ID Headers map[string]string CreationEpoch uint64 } func (o *ObjectVersion) VersionID() string { return o.ObjID.EncodeToString() } 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{ ObjID: objID, 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 { versionID1, versionID2 := ov1.VersionID(), ov2.VersionID() if strings.Contains(ov1.Headers[versionsAddAttr], versionID2) { return false } if strings.Contains(ov2.Headers[versionsAddAttr], versionID1) { return true } if ov1.CreationEpoch == ov2.CreationEpoch { return versionID1 < versionID2 } return ov1.CreationEpoch < ov2.CreationEpoch } func contains(list []string, elem string) bool { for _, item := range list { if elem == item { return true } } return false }