storj/satellite/metabase/encoding.go

189 lines
5.1 KiB
Go
Raw Normal View History

// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
package metabase
import (
"database/sql/driver"
"encoding/binary"
"github.com/jackc/pgtype"
"storj.io/common/storj"
)
type encryptionParameters struct {
*storj.EncryptionParameters
}
// Check that EncryptionParameters layout doesn't change.
var _ struct {
CipherSuite storj.CipherSuite
BlockSize int32
} = storj.EncryptionParameters{}
// Value implements sql/driver.Valuer interface.
func (params encryptionParameters) Value() (driver.Value, error) {
var bytes [8]byte
bytes[0] = byte(params.CipherSuite)
binary.LittleEndian.PutUint32(bytes[1:], uint32(params.BlockSize))
return int64(binary.LittleEndian.Uint64(bytes[:])), nil
}
// Scan implements sql.Scanner interface.
func (params encryptionParameters) Scan(value interface{}) error {
switch value := value.(type) {
case int64:
var bytes [8]byte
binary.LittleEndian.PutUint64(bytes[:], uint64(value))
params.CipherSuite = storj.CipherSuite(bytes[0])
params.BlockSize = int32(binary.LittleEndian.Uint32(bytes[1:]))
return nil
default:
return Error.New("unable to scan %T into EncryptionParameters", value)
}
}
// Value implements sql/driver.Valuer interface.
func (params SegmentPosition) Value() (driver.Value, error) {
return int64(params.Encode()), nil
}
// Scan implements sql.Scanner interface.
func (params *SegmentPosition) Scan(value interface{}) error {
switch value := value.(type) {
case int64:
*params = SegmentPositionFromEncoded(uint64(value))
return nil
default:
return Error.New("unable to scan %T into SegmentPosition", value)
}
}
type redundancyScheme struct {
*storj.RedundancyScheme
}
// Check that RedundancyScheme layout doesn't change.
var _ struct {
Algorithm storj.RedundancyAlgorithm
ShareSize int32
RequiredShares int16
RepairShares int16
OptimalShares int16
TotalShares int16
} = storj.RedundancyScheme{}
func (params redundancyScheme) Value() (driver.Value, error) {
switch {
case params.ShareSize < 0 || params.ShareSize >= 1<<24:
return nil, Error.New("invalid share size %v", params.ShareSize)
case params.RequiredShares < 0 || params.RequiredShares >= 1<<8:
return nil, Error.New("invalid required shares %v", params.RequiredShares)
case params.RepairShares < 0 || params.RepairShares >= 1<<8:
return nil, Error.New("invalid repair shares %v", params.RepairShares)
case params.OptimalShares < 0 || params.OptimalShares >= 1<<8:
return nil, Error.New("invalid optimal shares %v", params.OptimalShares)
case params.TotalShares < 0 || params.TotalShares >= 1<<8:
return nil, Error.New("invalid total shares %v", params.TotalShares)
}
var bytes [8]byte
bytes[0] = byte(params.Algorithm)
// little endian uint32
bytes[1] = byte(params.ShareSize >> 0)
bytes[2] = byte(params.ShareSize >> 8)
bytes[3] = byte(params.ShareSize >> 16)
bytes[4] = byte(params.RequiredShares)
bytes[5] = byte(params.RepairShares)
bytes[6] = byte(params.OptimalShares)
bytes[7] = byte(params.TotalShares)
return int64(binary.LittleEndian.Uint64(bytes[:])), nil
}
func (params redundancyScheme) Scan(value interface{}) error {
switch value := value.(type) {
case int64:
var bytes [8]byte
binary.LittleEndian.PutUint64(bytes[:], uint64(value))
params.Algorithm = storj.RedundancyAlgorithm(bytes[0])
// little endian uint32
params.ShareSize = int32(bytes[1]) | int32(bytes[2])<<8 | int32(bytes[3])<<16
params.RequiredShares = int16(bytes[4])
params.RepairShares = int16(bytes[5])
params.OptimalShares = int16(bytes[6])
params.TotalShares = int16(bytes[7])
return nil
default:
return Error.New("unable to scan %T into RedundancyScheme", value)
}
}
// Value implements sql/driver.Valuer interface.
func (pieces Pieces) Value() (driver.Value, error) {
if len(pieces) == 0 {
arr := &pgtype.ByteaArray{Status: pgtype.Null}
return arr.Value()
}
elems := make([]pgtype.Bytea, len(pieces))
for i, piece := range pieces {
var buf [2 + len(piece.StorageNode)]byte
binary.BigEndian.PutUint16(buf[0:], piece.Number)
copy(buf[2:], piece.StorageNode[:])
elems[i].Bytes = buf[:]
elems[i].Status = pgtype.Present
}
arr := &pgtype.ByteaArray{
Elements: elems,
Dimensions: []pgtype.ArrayDimension{{Length: int32(len(pieces)), LowerBound: 1}},
Status: pgtype.Present,
}
return arr.Value()
}
type unexpectedDimension struct{}
type invalidElementLength struct{}
func (unexpectedDimension) Error() string { return "unexpected data dimension" }
func (invalidElementLength) Error() string { return "invalid element length" }
// Scan implements sql.Scanner interface.
func (pieces *Pieces) Scan(value interface{}) error {
var arr pgtype.ByteaArray
if err := arr.Scan(value); err != nil {
return err
}
if len(arr.Dimensions) == 0 {
*pieces = nil
return nil
} else if len(arr.Dimensions) != 1 {
return unexpectedDimension{}
}
scan := make(Pieces, len(arr.Elements))
for i, elem := range arr.Elements {
piece := Piece{}
if len(elem.Bytes) != 2+len(piece.StorageNode) {
return invalidElementLength{}
}
piece.Number = binary.BigEndian.Uint16(elem.Bytes[0:])
copy(piece.StorageNode[:], elem.Bytes[2:])
scan[i] = piece
}
*pieces = scan
return nil
}