@@ -2,21 +2,46 @@ package share
2
2
3
3
import (
4
4
"bytes"
5
+ "encoding/binary"
6
+ "encoding/hex"
7
+ "encoding/json"
8
+ "errors"
5
9
"fmt"
10
+ "slices"
6
11
)
7
12
8
13
type Namespace struct {
9
14
data []byte
10
15
}
11
16
17
+ // MarshalJSON encodes namespace to the json encoded bytes.
18
+ func (n Namespace ) MarshalJSON () ([]byte , error ) {
19
+ return json .Marshal (n .data )
20
+ }
21
+
22
+ // UnmarshalJSON decodes json bytes to the namespace.
23
+ func (n * Namespace ) UnmarshalJSON (data []byte ) error {
24
+ var buf []byte
25
+ if err := json .Unmarshal (data , & buf ); err != nil {
26
+ return err
27
+ }
28
+
29
+ ns , err := NewNamespaceFromBytes (buf )
30
+ if err != nil {
31
+ return err
32
+ }
33
+ * n = ns
34
+ return nil
35
+ }
36
+
12
37
// NewNamespace validates the provided version and id and returns a new namespace.
13
38
// This should be used for user specified namespaces.
14
39
func NewNamespace (version uint8 , id []byte ) (Namespace , error ) {
15
- if err := ValidateUserNamespace (version , id ); err != nil {
40
+ ns := newNamespace (version , id )
41
+ if err := ns .validate (); err != nil {
16
42
return Namespace {}, err
17
43
}
18
-
19
- return newNamespace (version , id ), nil
44
+ return ns , nil
20
45
}
21
46
22
47
func newNamespace (version uint8 , id []byte ) Namespace {
@@ -44,13 +69,12 @@ func NewNamespaceFromBytes(bytes []byte) (Namespace, error) {
44
69
if len (bytes ) != NamespaceSize {
45
70
return Namespace {}, fmt .Errorf ("invalid namespace length: %d. Must be %d bytes" , len (bytes ), NamespaceSize )
46
71
}
47
- if err := ValidateUserNamespace (bytes [VersionIndex ], bytes [NamespaceVersionSize :]); err != nil {
72
+
73
+ ns := Namespace {data : bytes }
74
+ if err := ns .validate (); err != nil {
48
75
return Namespace {}, err
49
76
}
50
-
51
- return Namespace {
52
- data : bytes ,
53
- }, nil
77
+ return ns , nil
54
78
}
55
79
56
80
// NewV0Namespace returns a new namespace with version 0 and the provided subID. subID
@@ -92,35 +116,68 @@ func (n Namespace) ID() []byte {
92
116
return n .data [NamespaceVersionSize :]
93
117
}
94
118
95
- // ValidateUserNamespace returns an error if the provided version is not
119
+ // String stringifies the Namespace.
120
+ func (n Namespace ) String () string {
121
+ return hex .EncodeToString (n .data )
122
+ }
123
+
124
+ // validate returns an error if the provided version is not
96
125
// supported or the provided id does not meet the requirements
97
126
// for the provided version. This should be used for validating
98
127
// user specified namespaces
99
- func ValidateUserNamespace ( version uint8 , id [] byte ) error {
100
- err := validateVersionSupported (version )
128
+ func ( n Namespace ) validate ( ) error {
129
+ err := n . validateVersionSupported ()
101
130
if err != nil {
102
131
return err
103
132
}
104
- return validateID (version , id )
133
+ return n .validateID ()
134
+ }
135
+
136
+ // ValidateForData checks if the Namespace is of real/useful data.
137
+ func (n Namespace ) ValidateForData () error {
138
+ if ! n .IsUsableNamespace () {
139
+ return fmt .Errorf ("invalid data namespace(%s): parity and tail padding namespace are forbidden" , n )
140
+ }
141
+ return nil
142
+ }
143
+
144
+ // ValidateForBlob verifies whether the Namespace is appropriate for blob data.
145
+ // A valid blob namespace must meet two conditions: it cannot be reserved for special purposes,
146
+ // and its version must be supported by the system. If either of these conditions is not met,
147
+ // an error is returned indicating the issue. This ensures that only valid namespaces are
148
+ // used when dealing with blob data.
149
+ func (n Namespace ) ValidateForBlob () error {
150
+ if err := n .ValidateForData (); err != nil {
151
+ return err
152
+ }
153
+
154
+ if n .IsReserved () {
155
+ return fmt .Errorf ("invalid data namespace(%s): reserved data is forbidden" , n )
156
+ }
157
+
158
+ if ! slices .Contains (SupportedBlobNamespaceVersions , n .Version ()) {
159
+ return fmt .Errorf ("blob version %d is not supported" , n .Version ())
160
+ }
161
+ return nil
105
162
}
106
163
107
164
// validateVersionSupported returns an error if the version is not supported.
108
- func validateVersionSupported ( version uint8 ) error {
109
- if version != NamespaceVersionZero && version != NamespaceVersionMax {
110
- return fmt .Errorf ("unsupported namespace version %v" , version )
165
+ func ( n Namespace ) validateVersionSupported ( ) error {
166
+ if n . Version () != NamespaceVersionZero && n . Version () != NamespaceVersionMax {
167
+ return fmt .Errorf ("unsupported namespace version %v" , n . Version () )
111
168
}
112
169
return nil
113
170
}
114
171
115
172
// validateID returns an error if the provided id does not meet the requirements
116
173
// for the provided version.
117
- func validateID ( version uint8 , id [] byte ) error {
118
- if len (id ) != NamespaceIDSize {
119
- return fmt .Errorf ("unsupported namespace id length: id %v must be %v bytes but it was %v bytes" , id , NamespaceIDSize , len (id ))
174
+ func ( n Namespace ) validateID ( ) error {
175
+ if len (n . ID () ) != NamespaceIDSize {
176
+ return fmt .Errorf ("unsupported namespace id length: id %v must be %v bytes but it was %v bytes" , n . ID () , NamespaceIDSize , len (n . ID () ))
120
177
}
121
178
122
- if version == NamespaceVersionZero && ! bytes .HasPrefix (id , NamespaceVersionZeroPrefix ) {
123
- return fmt .Errorf ("unsupported namespace id with version %v. ID %v must start with %v leading zeros" , version , id , len (NamespaceVersionZeroPrefix ))
179
+ if n . Version () == NamespaceVersionZero && ! bytes .HasPrefix (n . ID () , NamespaceVersionZeroPrefix ) {
180
+ return fmt .Errorf ("unsupported namespace id with version %v. ID %v must start with %v leading zeros" , n . Version (), n . ID () , len (NamespaceVersionZeroPrefix ))
124
181
}
125
182
return nil
126
183
}
@@ -203,6 +260,52 @@ func (n Namespace) Compare(n2 Namespace) int {
203
260
return bytes .Compare (n .data , n2 .data )
204
261
}
205
262
263
+ // AddInt adds arbitrary int value to namespace, treating namespace as big-endian
264
+ // implementation of int. It could be helpful for users to create adjacent namespaces.
265
+ func (n Namespace ) AddInt (val int ) (Namespace , error ) {
266
+ if val == 0 {
267
+ return n , nil
268
+ }
269
+ // Convert the input integer to a byte slice and add it to result slice
270
+ result := make ([]byte , NamespaceSize )
271
+ if val > 0 {
272
+ binary .BigEndian .PutUint64 (result [NamespaceSize - 8 :], uint64 (val ))
273
+ } else {
274
+ binary .BigEndian .PutUint64 (result [NamespaceSize - 8 :], uint64 (- val ))
275
+ }
276
+
277
+ // Perform addition byte by byte
278
+ var carry int
279
+ nn := n .Bytes ()
280
+ for i := NamespaceSize - 1 ; i >= 0 ; i -- {
281
+ var sum int
282
+ if val > 0 {
283
+ sum = int (nn [i ]) + int (result [i ]) + carry
284
+ } else {
285
+ sum = int (nn [i ]) - int (result [i ]) + carry
286
+ }
287
+
288
+ switch {
289
+ case sum > 255 :
290
+ carry = 1
291
+ sum -= 256
292
+ case sum < 0 :
293
+ carry = - 1
294
+ sum += 256
295
+ default :
296
+ carry = 0
297
+ }
298
+
299
+ result [i ] = uint8 (sum )
300
+ }
301
+
302
+ // Handle any remaining carry
303
+ if carry != 0 {
304
+ return Namespace {}, errors .New ("namespace overflow" )
305
+ }
306
+ return Namespace {data : result }, nil
307
+ }
308
+
206
309
// leftPad returns a new byte slice with the provided byte slice left-padded to the provided size.
207
310
// If the provided byte slice is already larger than the provided size, the original byte slice is returned.
208
311
func leftPad (b []byte , size int ) []byte {
0 commit comments