88 lines
2.4 KiB
Go
88 lines
2.4 KiB
Go
|
package erasurecode
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
|
||
|
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||
|
"github.com/klauspost/reedsolomon"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
// ErrMalformedSlice is returned when a slice of EC chunks is inconsistent.
|
||
|
ErrMalformedSlice = errors.New("inconsistent EC headers")
|
||
|
// ErrInvShardNum is returned from NewConstructor when the number of shards is invalid.
|
||
|
ErrInvShardNum = reedsolomon.ErrInvShardNum
|
||
|
// ErrMaxShardNum is returned from NewConstructor when the number of shards is too big.
|
||
|
ErrMaxShardNum = reedsolomon.ErrMaxShardNum
|
||
|
)
|
||
|
|
||
|
// MaxShardCount is the maximum number of shards.
|
||
|
const MaxShardCount = 256
|
||
|
|
||
|
// Constructor is a wrapper around encoder allowing to reconstruct objects.
|
||
|
// It's methods are not thread-safe.
|
||
|
type Constructor struct {
|
||
|
enc reedsolomon.Encoder
|
||
|
headerLength uint32
|
||
|
payloadShards [][]byte
|
||
|
headerShards [][]byte
|
||
|
}
|
||
|
|
||
|
// NewConstructor returns new constructor instance.
|
||
|
func NewConstructor(dataCount int, parityCount int) (*Constructor, error) {
|
||
|
// The library supports up to 65536 shards with some restrictions.
|
||
|
// This can easily result in OOM or panic, thus SDK declares it's own restriction.
|
||
|
if dataCount+parityCount > MaxShardCount {
|
||
|
return nil, ErrMaxShardNum
|
||
|
}
|
||
|
|
||
|
enc, err := reedsolomon.New(dataCount, parityCount)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return &Constructor{enc: enc}, nil
|
||
|
}
|
||
|
|
||
|
// clear clears internal state of the constructor, so it can be reused.
|
||
|
func (c *Constructor) clear() {
|
||
|
c.headerLength = 0
|
||
|
c.payloadShards = nil
|
||
|
c.headerShards = nil
|
||
|
}
|
||
|
|
||
|
func (c *Constructor) fillHeader(parts []*objectSDK.Object) error {
|
||
|
shards := make([][]byte, len(parts))
|
||
|
headerLength := 0
|
||
|
for i := range parts {
|
||
|
if parts[i] == nil {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
var err error
|
||
|
headerLength, err = validatePart(parts, i, headerLength)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
shards[i] = parts[i].GetECHeader().Header()
|
||
|
}
|
||
|
|
||
|
c.headerLength = uint32(headerLength)
|
||
|
c.headerShards = shards
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// fillPayload fills the payload shards.
|
||
|
// Currently there is no case when it can be called without reconstructing header,
|
||
|
// thus fillHeader() must be called before and this function performs no validation.
|
||
|
func (c *Constructor) fillPayload(parts []*objectSDK.Object) {
|
||
|
shards := make([][]byte, len(parts))
|
||
|
for i := range parts {
|
||
|
if parts[i] == nil {
|
||
|
continue
|
||
|
}
|
||
|
shards[i] = parts[i].Payload()
|
||
|
}
|
||
|
c.payloadShards = shards
|
||
|
}
|