Commit adc0be21 authored by Levin Zimmermann's avatar Levin Zimmermann

go/proto/msgpack: Fix Error -> ErrorCode is int on the wire and not extension type

It was already 'int' before the migration to msgpack:

https://lab.nexedi.com/nexedi/neoppod/-/blob/6332112cb/neo/lib/protocol.py#L653

It is 'int' after the migration to msgpack:

https://lab.nexedi.com/nexedi/neoppod/-/blob/9d0bf97a1/neo/lib/protocol.py#L842

(it's explicitly cast into an integer here)

Another verification that 'ErrorCode' is 'int' on the wire can be found
here:

https://lab.nexedi.com/nexedi/neoppod/-/blob/3ddb6663b/neo/tests/protocol#L76

In the Nexedi NEO protocol specification document v1 this is however not
specified (or I couldn't find it):

https://neo.nexedi.com/P-NEO-Protocol.Specification.2019
parent b4a96d1c
......@@ -176,7 +176,7 @@ func TestMsgMarshal(t *testing.T) {
// uint32(N)/enum(M), string
{&Error{Code: 0x00000045, Message: "hello"},
"\x00\x00\x00\x45\x00\x00\x00\x05hello",
hex("92") + hex("d40245") + "\xc4\x05hello",
hex("92") + hex("45") + "\xc4\x05hello",
},
// Oid, Tid, bool, Checksum, []byte
......
......@@ -985,6 +985,12 @@ func (s *sizerM) genBasic(path string, typ *types.Basic, userType types.Type) {
return
}
// Code argument of Error packet is not encoded as enum, but as int
if typeName(userType) == "ErrorCode" {
s.size.AddExpr("msgpack.Uint32Size(%s)", upath)
return
}
// enums are encoded as extensions
if _, isEnum := enumRegistry[userType]; isEnum {
s.size.Add(1+1+1) // fixext1 enumType value
......@@ -1030,17 +1036,6 @@ func (e *encoderM) genBasic(path string, typ *types.Basic, userType types.Type)
return
}
// enums are encoded as `fixext1 enumType fixint<value>`
if enum, ok := enumRegistry[userType]; ok {
e.emit("data[%v] = byte(msgpack.FixExt1)", e.n); e.n++
e.emit("data[%v] = %d", e.n, enum); e.n++
e.emit("if !(0 <= %s && %s <= 0x7f) {", path, path) // mposfixint
e.emit(` panic("%s: invalid %s enum value)")`, path, typeName(userType))
e.emit("}")
e.emit("data[%v] = byte(%s)", e.n, path); e.n++
return
}
// mputint emits mput<kind>int<size>(path)
mputint := func(kind string, size int) {
KI := "I" // I or <Kind>i
......@@ -1054,6 +1049,23 @@ func (e *encoderM) genBasic(path string, typ *types.Basic, userType types.Type)
e.n = 0
}
// Code argument of Error packet is not encoded as enum, but as int
if typeName(userType) == "ErrorCode" {
mputint("U", 32)
return
}
// enums are encoded as `fixext1 enumType fixint<value>`
if enum, ok := enumRegistry[userType]; ok {
e.emit("data[%v] = byte(msgpack.FixExt1)", e.n); e.n++
e.emit("data[%v] = %d", e.n, enum); e.n++
e.emit("if !(0 <= %s && %s <= 0x7f) {", path, path) // mposfixint
e.emit(` panic("%s: invalid %s enum value)")`, path, typeName(userType))
e.emit("}")
e.emit("data[%v] = byte(%s)", e.n, path); e.n++
return
}
switch typ.Kind() {
case types.Bool:
e.emit("data[%v] = byte(msgpack.Bool(%s))", e.n, path)
......@@ -1105,43 +1117,6 @@ func (d *decoderM) expectEnum(assignto string, enumType int) {
}
func (d *decoderM) genBasic(assignto string, typ *types.Basic, userType types.Type) {
// zodb.Tid and zodb.Oid are encoded as [8]bin or Nil
if userType == zodbTid || userType == zodbOid {
d.resetPos()
d.overflowCheck()
defer d.overflowCheck()
// Size depends on if we have an INVALID_{TID,OID}
// which is encoded as NIL or if we have a valid
// {TID,OID} which is encdoed as Bin8Fix.
d.overflow.AddExpr("msgpack.TidOrOidSize(data)")
d.emit("if data[%v] == byte(msgpack.Nil) {", d.n)
d.emit("%s = %s(%v)", assignto, typeName(userType), INVALID_ID)
d.n += 1
d.resetPos()
d.emit("} else {")
d.expectBin8Fix(assignto, 8, false)
d.emit("%s= %s(binary.BigEndian.Uint64(data[%v:]))", assignto, typeName(userType), d.n)
d.n += 8
d.resetPos()
d.emit("}")
return
}
// enums are encoded as `fixext1 enumType fixint<value>`
if enum, ok := enumRegistry[userType]; ok {
d.expectEnum(assignto, enum)
d.emit("{")
d.emit("v := data[%v]", d.n); d.n++
d.emit("if !(0 <= v && v <= 0x7f) {") // mposfixint
d.emit(" return 0, mdecodeEnumValueErr(%q, v)", d.pathName(assignto))
d.emit("}")
d.emit("%s= %s(v)", assignto, typeName(userType))
d.emit("}")
d.overflow.Add(1)
return
}
// v represents basic decoded value casted to user type if needed
v := "v"
if userType.Underlying() != userType {
......@@ -1203,6 +1178,49 @@ func (d *decoderM) genBasic(assignto string, typ *types.Basic, userType types.Ty
d.emit("}")
}
// zodb.Tid and zodb.Oid are encoded as [8]bin or Nil
if userType == zodbTid || userType == zodbOid {
d.resetPos()
d.overflowCheck()
defer d.overflowCheck()
// Size depends on if we have an INVALID_{TID,OID}
// which is encoded as NIL or if we have a valid
// {TID,OID} which is encdoed as Bin8Fix.
d.overflow.AddExpr("msgpack.TidOrOidSize(data)")
d.emit("if data[%v] == byte(msgpack.Nil) {", d.n)
d.emit("%s = %s(%v)", assignto, typeName(userType), INVALID_ID)
d.n += 1
d.resetPos()
d.emit("} else {")
d.expectBin8Fix(assignto, 8, false)
d.emit("%s= %s(binary.BigEndian.Uint64(data[%v:]))", assignto, typeName(userType), d.n)
d.n += 8
d.resetPos()
d.emit("}")
return
}
// Code argument of Error packet is not encoded as enum, but as int
if typeName(userType) == "ErrorCode" {
mgetint("", 32, "")
return
}
// enums are encoded as `fixext1 enumType fixint<value>`
if enum, ok := enumRegistry[userType]; ok {
d.expectEnum(assignto, enum)
d.emit("{")
d.emit("v := data[%v]", d.n); d.n++
d.emit("if !(0 <= v && v <= 0x7f) {") // mposfixint
d.emit(" return 0, mdecodeEnumValueErr(%q, v)", d.pathName(assignto))
d.emit("}")
d.emit("%s= %s(v)", assignto, typeName(userType))
d.emit("}")
d.overflow.Add(1)
return
}
// IdTime can be nil ('None' in py), in this case we use
// infinite -1, see
// https://lab.nexedi.com/kirr/neo/-/blob/1ad088c8/go/neo/proto/proto.go#L352-357
......
......@@ -63,21 +63,19 @@ overflow:
}
func (p *Error) neoMsgEncodedLenM() int {
return 3 + msgpack.ArrayHeadSize(reflect.TypeOf((*p)).NumField()) + msgpack.BinHeadSize(len(p.Message)) + len(p.Message)
return msgpack.ArrayHeadSize(reflect.TypeOf((*p)).NumField()) + msgpack.Uint32Size(uint32(p.Code)) + msgpack.BinHeadSize(len(p.Message)) + len(p.Message)
}
func (p *Error) neoMsgEncodeM(data []byte) {
data[0] = byte(msgpack.FixArray_4 | 2)
data[1] = byte(msgpack.FixExt1)
data[2] = 2
if !(0 <= p.Code && p.Code <= 0x7f) {
panic("p.Code: invalid ErrorCode enum value)")
{
n := msgpack.PutUint32(data[1:], uint32(p.Code))
data = data[1+n:]
}
data[3] = byte(p.Code)
{
l := len(p.Message)
n := msgpack.PutBinHead(data[4:], l)
data = data[4+n:]
n := msgpack.PutBinHead(data[0:], l)
data = data[0+n:]
copy(data, p.Message)
data = data[l:]
}
......@@ -93,23 +91,15 @@ func (p *Error) neoMsgDecodeM(data []byte) (int, error) {
nread += uint64(len(data) - len(tail))
data = tail
}
if len(data) < 3 {
goto overflow
}
if op := msgpack.Op(data[0]); op != msgpack.FixExt1 {
return 0, mdecodeOpErr("Error.Code", op, msgpack.FixExt1)
}
if enumType := data[1]; enumType != 2 {
return 0, mdecodeEnumTypeErr("Error.Code", enumType, 2)
}
{
v := data[2]
if !(0 <= v && v <= 0x7f) {
return 0, mdecodeEnumValueErr("Error.Code", v)
v, tail, err := msgp.ReadInt32Bytes(data)
if err != nil {
return 0, mdecodeErr("Error.Code", err)
}
p.Code = ErrorCode(v)
nread += uint64(len(data) - len(tail))
data = tail
}
data = data[3:]
{
b, tail, err := msgp.ReadBytesZC(data)
if err != nil {
......@@ -119,10 +109,7 @@ func (p *Error) neoMsgDecodeM(data []byte) (int, error) {
nread += uint64(len(data) - len(tail))
data = tail
}
return 3 + int(nread), nil
overflow:
return 0, &ErrDecodeOverflow{"Error"}
return 0 + int(nread), nil
}
// 1. RequestIdentification
......
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