/
encoding.go
203 lines (168 loc) · 5.38 KB
/
encoding.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
package metabase
import (
"database/sql"
"database/sql/driver"
"encoding/binary"
"github.com/jackc/pgtype"
"storj.io/common/storj"
)
type nullableValue[T sql.Scanner] struct {
isnull bool
value T
}
func (v *nullableValue[T]) Scan(value interface{}) error {
if value == nil {
v.isnull = true
return nil
}
v.isnull = false
return v.value.Scan(value)
}
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
}