Skip to content

Commit f24288c

Browse files
committed
sn/object: Refactor header extraction from local BLOB in HEAD
Refactoring to get closer to #3783. In the future, this will help read the header directly into the response buffer. Previous behavior is preserved as much as possible. Signed-off-by: Leonard Lyubich <[email protected]>
1 parent 71a287b commit f24288c

File tree

3 files changed

+386
-7
lines changed

3 files changed

+386
-7
lines changed

internal/object/wire.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,3 +241,84 @@ loop:
241241

242242
return
243243
}
244+
245+
// TODO: docs.
246+
func RestoreLayoutWithCutPayload(b []byte) (idf, sigf, hdrf iprotobuf.FieldBounds, err error) {
247+
idf.From = -1
248+
sigf.From = -1
249+
hdrf.From = -1
250+
251+
var off int
252+
var prevNum protowire.Number
253+
loop:
254+
for {
255+
num, typ, tagLn := protowire.ConsumeTag(b[off:])
256+
if err = protowire.ParseError(tagLn); err != nil {
257+
err = iprotobuf.WrapParseFieldTagError(err)
258+
return
259+
}
260+
261+
if num < prevNum {
262+
err = iprotobuf.NewUnorderedFieldsError(prevNum, num)
263+
return
264+
}
265+
prevNum = num
266+
267+
switch num {
268+
case fieldObjectID:
269+
if typ != protowire.BytesType {
270+
err = iprotobuf.WrapParseFieldError(fieldObjectID, protowire.BytesType, iprotobuf.NewWrongFieldTypeError(typ))
271+
return
272+
}
273+
274+
idf, err = iprotobuf.ParseBytesFieldBounds(b, off, tagLn)
275+
if err != nil {
276+
err = iprotobuf.WrapParseFieldError(fieldObjectID, protowire.BytesType, err)
277+
return
278+
}
279+
280+
off = idf.To
281+
case fieldObjectSignature:
282+
if typ != protowire.BytesType {
283+
err = iprotobuf.WrapParseFieldError(fieldObjectSignature, protowire.BytesType, iprotobuf.NewWrongFieldTypeError(typ))
284+
return
285+
}
286+
287+
sigf, err = iprotobuf.ParseBytesFieldBounds(b, off, tagLn)
288+
if err != nil {
289+
err = iprotobuf.WrapParseFieldError(fieldObjectSignature, protowire.BytesType, err)
290+
return
291+
}
292+
293+
off = sigf.To
294+
case fieldObjectHeader:
295+
if typ != protowire.BytesType {
296+
err = iprotobuf.WrapParseFieldError(fieldObjectHeader, protowire.BytesType, iprotobuf.NewWrongFieldTypeError(typ))
297+
return
298+
}
299+
300+
hdrf, err = iprotobuf.ParseBytesFieldBounds(b, off, tagLn)
301+
if err != nil {
302+
err = iprotobuf.WrapParseFieldError(fieldObjectHeader, protowire.BytesType, err)
303+
return
304+
}
305+
306+
break loop
307+
case fieldObjectPayload:
308+
if _, n := protowire.ConsumeVarint(b[off+tagLn:]); n < 0 {
309+
err = iprotobuf.WrapParseFieldError(fieldObjectPayload, protowire.BytesType, protowire.ParseError(n))
310+
return
311+
}
312+
313+
break loop
314+
default:
315+
break loop
316+
}
317+
318+
if off == len(b) {
319+
break
320+
}
321+
}
322+
323+
return
324+
}

internal/object/wire_test.go

Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,22 @@ package object_test
33
import (
44
"bytes"
55
"crypto/rand"
6+
"encoding/binary"
67
"io"
78
"testing"
89

10+
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
911
iobject "github.com/nspcc-dev/neofs-node/internal/object"
12+
iprotobuf "github.com/nspcc-dev/neofs-node/internal/protobuf"
13+
"github.com/nspcc-dev/neofs-node/internal/testutil"
14+
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
1015
"github.com/nspcc-dev/neofs-sdk-go/object"
16+
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
17+
oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test"
1118
objecttest "github.com/nspcc-dev/neofs-sdk-go/object/test"
19+
protoobject "github.com/nspcc-dev/neofs-sdk-go/proto/object"
1220
"github.com/stretchr/testify/require"
21+
"google.golang.org/protobuf/proto"
1322
)
1423

1524
func TestWriteWithoutPayload(t *testing.T) {
@@ -97,3 +106,255 @@ func TestReadHeaderPrefix(t *testing.T) {
97106
require.Equal(t, expectedSize, len(payloadPrefix))
98107
require.Equal(t, payload[:expectedSize], payloadPrefix)
99108
}
109+
110+
func TestRestoreLayoutWithCutPayload(t *testing.T) {
111+
const pubkeyLen = 33
112+
id := oidtest.ID()
113+
sig := neofscrypto.NewSignatureFromRawKey(neofscrypto.N3, testutil.RandByteSlice(pubkeyLen), testutil.RandByteSlice(keys.SignatureLen))
114+
payload := []byte("Hello, world!")
115+
116+
obj := objecttest.Object()
117+
obj.SetID(id)
118+
obj.SetSignature(&sig)
119+
obj.SetPayload(payload)
120+
121+
hdrLen := obj.HeaderLen()
122+
hdrLenVarint := binary.PutVarint(make([]byte, binary.MaxVarintLen64), int64(hdrLen))
123+
124+
encodeBuffer := func(obj []byte) []byte {
125+
b := testutil.RandByteSlice(object.MaxHeaderLen * 2)
126+
copy(b, obj)
127+
return b
128+
}
129+
130+
encodeObject := func(obj object.Object) []byte {
131+
return encodeBuffer(obj.Marshal())
132+
}
133+
134+
assertID := func(t *testing.T, data []byte, f iprotobuf.FieldBounds, off int) {
135+
require.EqualValues(t, off, f.From)
136+
require.EqualValues(t, f.From+(1+1), f.ValueFrom)
137+
const idFldLen = 1 + 1 + 32
138+
require.EqualValues(t, f.ValueFrom+idFldLen, f.To)
139+
require.Equal(t, []byte{10, idFldLen}, data[f.From:f.ValueFrom])
140+
var gotID oid.ID
141+
require.NoError(t, gotID.Unmarshal(data[f.ValueFrom:f.To]))
142+
require.Equal(t, obj.GetID(), gotID)
143+
}
144+
assertSignature := func(t *testing.T, data []byte, f iprotobuf.FieldBounds, off int) {
145+
require.EqualValues(t, off, f.From)
146+
require.EqualValues(t, f.From+(1+1), f.ValueFrom)
147+
const sigFldLen = (1 + 1 + pubkeyLen) + (1 + 1 + keys.SignatureLen) + (1 + 1)
148+
require.EqualValues(t, f.ValueFrom+sigFldLen, f.To)
149+
require.Equal(t, []byte{18, sigFldLen}, data[f.From:f.ValueFrom])
150+
var gotSig neofscrypto.Signature
151+
require.NoError(t, gotSig.Unmarshal(data[f.ValueFrom:f.To]))
152+
require.Equal(t, sig, gotSig)
153+
}
154+
assertHeader := func(t *testing.T, data []byte, f iprotobuf.FieldBounds, off int) {
155+
require.EqualValues(t, off, f.From)
156+
require.EqualValues(t, f.From+(1+hdrLenVarint), f.ValueFrom)
157+
require.EqualValues(t, f.ValueFrom+hdrLen, f.To)
158+
require.Equal(t, binary.AppendUvarint([]byte{26}, uint64(hdrLen)), data[f.From:f.ValueFrom])
159+
var gotHdr protoobject.Header
160+
require.NoError(t, proto.Unmarshal(data[f.ValueFrom:f.To], &gotHdr))
161+
require.True(t, proto.Equal(obj.ProtoMessage().Header, &gotHdr))
162+
}
163+
164+
t.Run("empty", func(t *testing.T) {
165+
_, _, _, err := iobject.RestoreLayoutWithCutPayload(nil)
166+
require.ErrorIs(t, err, io.ErrUnexpectedEOF)
167+
require.EqualError(t, err, "parse field tag: "+io.ErrUnexpectedEOF.Error())
168+
169+
_, _, _, err = iobject.RestoreLayoutWithCutPayload([]byte{})
170+
require.ErrorIs(t, err, io.ErrUnexpectedEOF)
171+
require.EqualError(t, err, "parse field tag: "+io.ErrUnexpectedEOF.Error())
172+
})
173+
t.Run("payload tag", func(t *testing.T) {
174+
_, _, _, err := iobject.RestoreLayoutWithCutPayload([]byte{34})
175+
require.ErrorIs(t, err, io.ErrUnexpectedEOF)
176+
require.EqualError(t, err, "parse field (#4,type=2): "+io.ErrUnexpectedEOF.Error())
177+
})
178+
t.Run("payload tag and len", func(t *testing.T) {
179+
idf, sigf, hdrf, err := iobject.RestoreLayoutWithCutPayload([]byte{34, 13})
180+
require.NoError(t, err)
181+
182+
require.Negative(t, idf.From)
183+
require.Negative(t, sigf.From)
184+
require.Negative(t, hdrf.From)
185+
})
186+
t.Run("payload", func(t *testing.T) {
187+
var obj object.Object
188+
obj.SetPayload(payload)
189+
190+
data := encodeObject(obj)
191+
192+
idf, sigf, hdrf, err := iobject.RestoreLayoutWithCutPayload(data)
193+
require.NoError(t, err)
194+
195+
require.Negative(t, idf.From)
196+
require.Negative(t, sigf.From)
197+
require.Negative(t, hdrf.From)
198+
})
199+
t.Run("id,signature,payload", func(t *testing.T) {
200+
var obj object.Object
201+
obj.SetID(id)
202+
obj.SetSignature(&sig)
203+
obj.SetPayload(payload)
204+
205+
data := encodeObject(obj)
206+
207+
idf, sigf, hdrf, err := iobject.RestoreLayoutWithCutPayload(data)
208+
require.NoError(t, err)
209+
210+
assertID(t, data, idf, 0)
211+
assertSignature(t, data, sigf, idf.To)
212+
require.Negative(t, hdrf.From)
213+
})
214+
t.Run("id,header,payload", func(t *testing.T) {
215+
obj := obj
216+
obj.SetSignature(nil)
217+
218+
data := encodeObject(obj)
219+
220+
idf, sigf, hdrf, err := iobject.RestoreLayoutWithCutPayload(data)
221+
require.NoError(t, err)
222+
223+
assertID(t, data, idf, 0)
224+
require.Negative(t, sigf.From)
225+
assertHeader(t, data, hdrf, idf.To)
226+
})
227+
t.Run("id,signature", func(t *testing.T) {
228+
var obj object.Object
229+
obj.SetID(id)
230+
obj.SetSignature(&sig)
231+
232+
data := obj.Marshal()
233+
234+
idf, sigf, hdrf, err := iobject.RestoreLayoutWithCutPayload(data)
235+
require.NoError(t, err)
236+
237+
assertID(t, data, idf, 0)
238+
assertSignature(t, data, sigf, idf.To)
239+
require.Negative(t, hdrf.From)
240+
})
241+
t.Run("id,header", func(t *testing.T) {
242+
obj := obj
243+
obj.SetSignature(nil)
244+
obj.SetPayload(nil)
245+
246+
data := encodeObject(obj)
247+
248+
idf, sigf, hdrf, err := iobject.RestoreLayoutWithCutPayload(data)
249+
require.NoError(t, err)
250+
251+
assertID(t, data, idf, 0)
252+
require.Negative(t, sigf.From)
253+
assertHeader(t, data, hdrf, idf.To)
254+
})
255+
t.Run("id,payload", func(t *testing.T) {
256+
var obj object.Object
257+
obj.SetID(id)
258+
obj.SetPayload(payload)
259+
260+
data := encodeObject(obj)
261+
262+
idf, sigf, hdrf, err := iobject.RestoreLayoutWithCutPayload(data)
263+
require.NoError(t, err)
264+
265+
assertID(t, data, idf, 0)
266+
require.Negative(t, sigf.From)
267+
require.Negative(t, hdrf.From)
268+
})
269+
t.Run("id", func(t *testing.T) {
270+
var obj object.Object
271+
obj.SetID(id)
272+
273+
data := obj.Marshal()
274+
275+
idf, sigf, hdrf, err := iobject.RestoreLayoutWithCutPayload(data)
276+
require.NoError(t, err)
277+
278+
assertID(t, data, idf, 0)
279+
require.Negative(t, sigf.From)
280+
require.Negative(t, hdrf.From)
281+
})
282+
t.Run("signature,header,payload", func(t *testing.T) {
283+
obj := obj
284+
obj.SetID(oid.ID{})
285+
286+
data := encodeObject(obj)
287+
288+
idf, sigf, hdrf, err := iobject.RestoreLayoutWithCutPayload(data)
289+
require.NoError(t, err)
290+
291+
require.Negative(t, idf.From)
292+
assertSignature(t, data, sigf, 0)
293+
assertHeader(t, data, hdrf, sigf.To)
294+
})
295+
t.Run("signature,payload", func(t *testing.T) {
296+
var obj object.Object
297+
obj.SetSignature(&sig)
298+
obj.SetPayload(payload)
299+
300+
data := encodeObject(obj)
301+
302+
idf, sigf, hdrf, err := iobject.RestoreLayoutWithCutPayload(data)
303+
require.NoError(t, err)
304+
305+
require.Negative(t, idf.From)
306+
assertSignature(t, data, sigf, 0)
307+
require.Negative(t, hdrf.From)
308+
})
309+
t.Run("signature", func(t *testing.T) {
310+
var obj object.Object
311+
obj.SetSignature(&sig)
312+
313+
data := obj.Marshal()
314+
315+
idf, sigf, hdrf, err := iobject.RestoreLayoutWithCutPayload(data)
316+
require.NoError(t, err)
317+
318+
require.Negative(t, idf.From)
319+
assertSignature(t, data, sigf, 0)
320+
require.Negative(t, hdrf.From)
321+
})
322+
t.Run("header,payload", func(t *testing.T) {
323+
obj := obj
324+
obj.SetID(oid.ID{})
325+
obj.SetSignature(nil)
326+
327+
data := encodeObject(obj)
328+
329+
idf, sigf, hdrf, err := iobject.RestoreLayoutWithCutPayload(data)
330+
require.NoError(t, err)
331+
332+
require.Negative(t, idf.From)
333+
require.Negative(t, sigf.From)
334+
assertHeader(t, data, hdrf, 0)
335+
})
336+
t.Run("header", func(t *testing.T) {
337+
obj := obj
338+
obj.SetID(oid.ID{})
339+
obj.SetSignature(nil)
340+
obj.SetPayload(nil)
341+
342+
data := encodeObject(obj)
343+
344+
idf, sigf, hdrf, err := iobject.RestoreLayoutWithCutPayload(data)
345+
require.NoError(t, err)
346+
347+
require.Negative(t, idf.From)
348+
require.Negative(t, sigf.From)
349+
assertHeader(t, data, hdrf, 0)
350+
})
351+
352+
data := encodeObject(obj)
353+
354+
idf, sigf, hdrf, err := iobject.RestoreLayoutWithCutPayload(data)
355+
require.NoError(t, err)
356+
357+
assertID(t, data, idf, 0)
358+
assertSignature(t, data, sigf, idf.To)
359+
assertHeader(t, data, hdrf, sigf.To)
360+
}

0 commit comments

Comments
 (0)