* Add unit-test to test `SplitByMaxChunkLength`. Signed-off-by: Airat Arifullin <a.arifullin@yadro.com>
139 lines
3.3 KiB
Go
139 lines
3.3 KiB
Go
package object
|
|
|
|
import (
|
|
v2object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
|
)
|
|
|
|
// Patch is a patch that's applied for an object.
|
|
type Patch struct {
|
|
// The address of the object for which the patch is being applied.
|
|
Address oid.Address
|
|
|
|
// The list of new attributes to set in the object's header.
|
|
NewAttributes []Attribute
|
|
|
|
// If ReplaceAttributes flag is true, then the header's attributes are reset and
|
|
// filled with NewAttributes. Otherwise, the attributes are just merged.
|
|
ReplaceAttributes bool
|
|
|
|
// Payload patch. If this field is not set, then it assumed such Patch patches only
|
|
// header (see NewAttributes, ReplaceAttributes).
|
|
PayloadPatch *PayloadPatch
|
|
}
|
|
|
|
func (p *Patch) ToV2() *v2object.PatchRequestBody {
|
|
if p == nil {
|
|
return nil
|
|
}
|
|
|
|
v2 := new(v2object.PatchRequestBody)
|
|
|
|
addrV2 := new(refs.Address)
|
|
p.Address.WriteToV2(addrV2)
|
|
v2.SetAddress(addrV2)
|
|
|
|
attrs := make([]v2object.Attribute, len(p.NewAttributes))
|
|
for i := range p.NewAttributes {
|
|
attrs[i] = *p.NewAttributes[i].ToV2()
|
|
}
|
|
v2.SetNewAttributes(attrs)
|
|
v2.SetReplaceAttributes(p.ReplaceAttributes)
|
|
|
|
v2.SetPatch(p.PayloadPatch.ToV2())
|
|
|
|
return v2
|
|
}
|
|
|
|
func (p *Patch) FromV2(patch *v2object.PatchRequestBody) {
|
|
if patch == nil {
|
|
return
|
|
}
|
|
|
|
if addr := patch.GetAddress(); addr != nil {
|
|
_ = p.Address.ReadFromV2(*addr)
|
|
}
|
|
|
|
newAttrs := patch.GetNewAttributes()
|
|
p.NewAttributes = make([]Attribute, len(newAttrs))
|
|
for i := range newAttrs {
|
|
p.NewAttributes[i] = *NewAttributeFromV2(&newAttrs[i])
|
|
}
|
|
|
|
p.ReplaceAttributes = patch.GetReplaceAttributes()
|
|
|
|
if v2patch := patch.GetPatch(); v2patch != nil {
|
|
p.PayloadPatch = new(PayloadPatch)
|
|
p.PayloadPatch.FromV2(v2patch)
|
|
}
|
|
}
|
|
|
|
// Patch is a patch that's applied for an object's payload.
|
|
type PayloadPatch struct {
|
|
// Range of the patch application.
|
|
Range *Range
|
|
|
|
// Chunk is the payload that replaces (or is appended to) the original object payload.
|
|
Chunk []byte
|
|
}
|
|
|
|
// SplitByMaxChunkLength splits a payload patch into a few payload patches if its `Chunk` size
|
|
// exceeds `maxChunkLen`.
|
|
func (p *PayloadPatch) SplitByMaxChunkLength(maxChunkLen int) []*PayloadPatch {
|
|
if len(p.Chunk) <= maxChunkLen {
|
|
return []*PayloadPatch{p}
|
|
}
|
|
|
|
var result []*PayloadPatch
|
|
remainingChunk := p.Chunk
|
|
currentOffset := p.Range.GetOffset()
|
|
remainingLength := p.Range.GetLength()
|
|
|
|
for len(remainingChunk) > 0 {
|
|
chunkSize := uint64(min(len(remainingChunk), maxChunkLen))
|
|
newLength := min(remainingLength, chunkSize)
|
|
|
|
rng := NewRange()
|
|
rng.SetOffset(currentOffset)
|
|
rng.SetLength(newLength)
|
|
|
|
newPatch := &PayloadPatch{
|
|
Range: rng,
|
|
Chunk: remainingChunk[:chunkSize],
|
|
}
|
|
result = append(result, newPatch)
|
|
|
|
remainingChunk = remainingChunk[chunkSize:]
|
|
currentOffset += chunkSize
|
|
if remainingLength > 0 {
|
|
remainingLength -= newLength
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func (p *PayloadPatch) ToV2() *v2object.PatchRequestBodyPatch {
|
|
if p == nil {
|
|
return nil
|
|
}
|
|
|
|
v2 := new(v2object.PatchRequestBodyPatch)
|
|
|
|
v2.Chunk = p.Chunk
|
|
v2.Range = p.Range.ToV2()
|
|
|
|
return v2
|
|
}
|
|
|
|
func (p *PayloadPatch) FromV2(patch *v2object.PatchRequestBodyPatch) {
|
|
if patch == nil {
|
|
return
|
|
}
|
|
|
|
p.Chunk = patch.Chunk
|
|
if patch.Range != nil {
|
|
p.Range = NewRangeFromV2(patch.Range)
|
|
}
|
|
}
|