storj/satellite/metabase/encoding.go
Egon Elbre 267506bb20 satellite/metabase: move package one level higher
metabase has become a central concept and it's more suitable for it to
be directly nested under satellite rather than being part of metainfo.

metainfo is going to be the "endpoint" logic for handling requests.

Change-Id: I53770d6761ac1e9a1283b5aa68f471b21e784198
2021-04-21 15:54:22 +03:00

193 lines
5.2 KiB
Go

// 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"
)
// !!!! NB !!!!
//
// Should we use protobuf here?
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 EncryptionParameters", 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
}