Commit 2c359fc1 authored by Kirill Smelkov's avatar Kirill Smelkov Committed by Kamil Kisiel

decoder: Remember protocol version as last seen in a PROTO opcode

In the next patch decoding a pickle will depend on whether current
protocol is <= 2 or >= 3. For this let's teach PROTO opcode handler to
recall last seen protocol version.

For testing - prepare tests infrastructure for cases where protocol
version affects decoding semantic: if a test pickle in tests table comes
with \x80\xff prefix, on decode tests this prefix will be changed to
concrete

	\x80 ver

with all versions protperly iterated as specified in TestPickle.protov.

We will also need this functionality in the next patch.
parent 514123d4
...@@ -142,6 +142,9 @@ type Decoder struct { ...@@ -142,6 +142,9 @@ type Decoder struct {
// reusable buffer for readLine // reusable buffer for readLine
line []byte line []byte
// protocol version seen in last PROTO opcode; 0 by default.
protocol int
} }
// DecoderConfig allows to tune Decoder. // DecoderConfig allows to tune Decoder.
...@@ -170,10 +173,11 @@ func NewDecoder(r io.Reader) *Decoder { ...@@ -170,10 +173,11 @@ func NewDecoder(r io.Reader) *Decoder {
func NewDecoderWithConfig(r io.Reader, config *DecoderConfig) *Decoder { func NewDecoderWithConfig(r io.Reader, config *DecoderConfig) *Decoder {
reader := bufio.NewReader(r) reader := bufio.NewReader(r)
return &Decoder{ return &Decoder{
r: reader, r: reader,
config: config, config: config,
stack: make([]interface{}, 0), stack: make([]interface{}, 0),
memo: make(map[string]interface{}), memo: make(map[string]interface{}),
protocol: 0,
} }
} }
...@@ -311,6 +315,9 @@ loop: ...@@ -311,6 +315,9 @@ loop:
// So we allow all supported versions as PROTO argument. // So we allow all supported versions as PROTO argument.
err = ErrInvalidPickleVersion err = ErrInvalidPickleVersion
} }
if err == nil {
d.protocol = int(v)
}
default: default:
return nil, OpcodeError{key, insn} return nil, OpcodeError{key, insn}
......
...@@ -9,6 +9,7 @@ import ( ...@@ -9,6 +9,7 @@ import (
"math/big" "math/big"
"reflect" "reflect"
"strconv" "strconv"
"strings"
"testing" "testing"
) )
...@@ -61,7 +62,12 @@ const longLine = "28,34,30,55,100,130,87,169,194,202,232,252,267,274,286,315,308 ...@@ -61,7 +62,12 @@ const longLine = "28,34,30,55,100,130,87,169,194,202,232,252,267,274,286,315,308
// data. However the test data can still be used to feed ogórek decoder. // data. However the test data can still be used to feed ogórek decoder.
type TestPickle struct { type TestPickle struct {
protov []int protov []int
data string // without `PROTO <ver>` prefix
// pickle data without `PROTO <ver>` prefix.
// optionally the prefix template (\x80\xff) could be given for cases
// where initial `PROTO <ver>` presence affects decoding semantic.
data string
err error // !nil if encoding should fail err error // !nil if encoding should fail
} }
...@@ -385,6 +391,11 @@ type foo struct { ...@@ -385,6 +391,11 @@ type foo struct {
Bar int32 Bar int32
} }
// if test pickle starts from protoPrefixTemplate, this prefix is changed to
// concrete `PROTO ver` when checking decoding. When checking encoding the
// protocol prefix is always automatically prepended and is always concrete.
var protoPrefixTemplate = string([]byte{opProto, 0xff})
// TestDecode verifies ogórek decoder. // TestDecode verifies ogórek decoder.
func TestDecode(t *testing.T) { func TestDecode(t *testing.T) {
for _, test := range tests { for _, test := range tests {
...@@ -393,9 +404,22 @@ func TestDecode(t *testing.T) { ...@@ -393,9 +404,22 @@ func TestDecode(t *testing.T) {
continue continue
} }
t.Run(fmt.Sprintf("%s/%q", test.name, pickle.data), func(t *testing.T) { if strings.HasPrefix(pickle.data, protoPrefixTemplate) {
testDecode(t, test.objectOut, pickle.data) // test case asked to have concrete `PROTO ver` prefix.
}) // let's range over all pickle's protocols.
for _, proto := range pickle.protov {
data := string([]byte{opProto, byte(proto)}) +
pickle.data[len(protoPrefixTemplate):]
t.Run(fmt.Sprintf("%s/%q/proto=%d", test.name, data, proto), func(t *testing.T) {
testDecode(t, test.objectOut, data)
})
}
} else {
t.Run(fmt.Sprintf("%s/%q", test.name, pickle.data), func(t *testing.T) {
testDecode(t, test.objectOut, pickle.data)
})
}
} }
} }
} }
...@@ -406,7 +430,7 @@ func TestEncode(t *testing.T) { ...@@ -406,7 +430,7 @@ func TestEncode(t *testing.T) {
alreadyTested := make(map[int]bool) // protocols we tested encode with so far alreadyTested := make(map[int]bool) // protocols we tested encode with so far
for _, pickle := range test.picklev { for _, pickle := range test.picklev {
for _, proto := range pickle.protov { for _, proto := range pickle.protov {
dataOk := pickle.data dataOk := strings.TrimPrefix(pickle.data, protoPrefixTemplate)
// protocols >= 2 must include "PROTO <ver>" prefix // protocols >= 2 must include "PROTO <ver>" prefix
if proto >= 2 && pickle.err == nil { if proto >= 2 && pickle.err == nil {
dataOk = string([]byte{opProto, byte(proto)}) + dataOk dataOk = string([]byte{opProto, byte(proto)}) + dataOk
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment