size computations (#40)

* size computations

* add `Protobuf.computeSize` to compute the encoded size of an object
* readd `VarIntLengthPrefix` support
* don't expose loosely typed `writeValue` for objects that might
conflict with other serializers
* ensure all `writeValue` and `writeField` are tagged with a `codec`
type
* avoid writing all-defaults proto3 object fields

* Fix sizer for reference

* Create TableObject in a template

---------

Co-authored-by: Ludovic Chenut <ludovic@status.im>
This commit is contained in:
Jacek Sieka 2023-03-01 12:19:11 +01:00 committed by GitHub
parent 43f77303bb
commit 0ef1a14564
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 254 additions and 186 deletions

View File

@ -3,8 +3,8 @@ import sets
import serialization
export serialization
import protobuf_serialization/[internal, types, reader, writer]
export types, reader, writer
import protobuf_serialization/[internal, types, reader, sizer, writer]
export types, reader, sizer, writer
serializationFormat Protobuf
@ -27,3 +27,7 @@ func supports*[T](_: type Protobuf, ty: typedesc[T]): bool =
# TODO return false when not supporting, instead of crashing compiler
static: supportsCompileTime(T)
true
func computeSize*[T: object](_: type Protobuf, value: T): int =
## Return the encoded size of the given value
computeObjectSize(value)

View File

@ -156,10 +156,26 @@ template toBytes*(x: pfloat): openArray[byte] =
template toBytes*(header: FieldHeader): openArray[byte] =
toBytes(uint32(header), Leb128).toOpenArray()
proc vsizeof*(x: SomeVarint): int =
func computeSize*(x: SomeVarint): int =
## Returns number of bytes required to encode integer ``x`` as varint.
Leb128.len(toUleb(x))
func computeSize*(x: SomeFixed64 | SomeFixed32): int =
## Returns number of bytes required to encode integer ``x`` as varint.
sizeof(x)
func computeSize*(x: pstring | pbytes): int =
let len = distinctBase(x).len()
computeSize(puint64(len)) + len
func computeSize*(x: FieldHeader): int =
## Returns number of bytes required to encode integer ``x`` as varint.
computeSize(puint32(x))
func computeSize*(field: int, x: SomeScalar): int =
computeSize(FieldHeader.init(field, wireKind(typeof(x)))) +
computeSize(x)
proc writeValue*(output: OutputStream, value: SomeVarint) =
output.write(toBytes(value))
@ -177,8 +193,11 @@ proc writeValue*(output: OutputStream, value: pbytes) =
proc writeValue*(output: OutputStream, value: SomeFixed32) =
output.write(toBytes(value))
proc writeValue*(output: OutputStream, value: FieldHeader) =
output.write(toBytes(value))
proc writeField*(output: OutputStream, field: int, value: SomeScalar) =
output.write(toBytes(FieldHeader.init(field, wireKind(typeof(value)))))
output.writeValue(FieldHeader.init(field, wireKind(typeof(value))))
output.writeValue(value)
proc readValue*[T: SomeVarint](input: InputStream, _: type T): T =

View File

@ -54,6 +54,28 @@ proc fieldNumberOf*(T: type, fieldName: static string): int {.compileTime.} =
else:
fieldNum
template tableObject*(TableObject, K, V) =
when K is SomePBInt and V is SomePBInt:
type
TableObject {.proto3.} = object
key {.fieldNumber: 1, pint.}: K
value {.fieldNumber: 2, pint.}: V
elif K is SomePBInt:
type
TableObject {.proto3.} = object
key {.fieldNumber: 1, pint.}: K
value {.fieldNumber: 2.}: V
elif V is SomePBInt:
type
TableObject {.proto3.} = object
key {.fieldNumber: 1.}: K
value {.fieldNumber: 2, pint.}: V
else:
type
TableObject {.proto3.} = object
key {.fieldNumber: 1.}: K
value {.fieldNumber: 2.}: V
template protoType*(InnerType, RootType, FieldType: untyped, fieldName: untyped) =
mixin flatType
@ -117,8 +139,10 @@ template protoType*(InnerType, RootType, FieldType: untyped, fieldName: untyped)
type InnerType = pbytes
elif FlatType is enum:
type InnerType = penum
elif FlatType is object or FlatType is ref:
type InnerType = FieldType
elif FlatType is object:
type InnerType = pbytes
elif FlatType is ref and defined(ConformanceTest):
type InnerType = pbytes
else:
type InnerType = UnsupportedType[FieldType, RootType, fieldName]

View File

@ -64,28 +64,7 @@ when defined(ConformanceTest):
header: FieldHeader,
ProtoType: type
) =
# I know it's ugly, but I cannot find a clean way to do it
# ... And nobody cares about map
when K is SomePBInt and V is SomePBInt:
type
TableObject {.proto3.} = object
key {.fieldNumber: 1, pint.}: K
value {.fieldNumber: 2, pint.}: V
elif K is SomePBInt:
type
TableObject {.proto3.} = object
key {.fieldNumber: 1, pint.}: K
value {.fieldNumber: 2.}: V
elif V is SomePBInt:
type
TableObject {.proto3.} = object
key {.fieldNumber: 1.}: K
value {.fieldNumber: 2, pint.}: V
else:
type
TableObject {.proto3.} = object
key {.fieldNumber: 1.}: K
value {.fieldNumber: 2.}: V
tableObject(TableObject, K, V)
var tmp = default(TableObject)
stream.readFieldInto(tmp, header, ProtoType)
value[tmp.key] = tmp.value
@ -146,6 +125,7 @@ proc readFieldPackedInto[T](
elif ProtoType is SomeFixed32:
WireKind.Fixed32
else:
static: doAssert ProtoType is SomeFixed64
WireKind.Fixed64
inner.readFieldInto(value[^1], FieldHeader.init(header.number, kind), ProtoType)
@ -184,8 +164,8 @@ proc readValueInternal[T: object](stream: InputStream, value: var T, silent: boo
stream.readFieldPackedInto(fieldVar, header, ProtoType)
else:
stream.readFieldInto(fieldVar, header, ProtoType)
elif ProtoType is ref and defined(ConformanceTest):
fieldVar = new ProtoType
elif typeof(fieldVar) is ref and defined(ConformanceTest):
fieldVar = new typeof(fieldVar)
stream.readFieldInto(fieldVar[], header, ProtoType)
else:
stream.readFieldInto(fieldVar, header, ProtoType)

View File

@ -0,0 +1,127 @@
import
std/[typetraits, tables],
stew/shims/macros,
serialization,
"."/[codec, internal, types]
func computeObjectSize*[T: object](value: T): int
func computeFieldSize(
fieldNum: int, fieldVal: auto, ProtoType: type UnsupportedType,
_: static bool) =
# TODO turn this into an extension point
unsupportedProtoType ProtoType.FieldType, ProtoType.RootType, ProtoType.fieldName
func computeFieldSize[T: object and not PBOption](
fieldNum: int, fieldVal: T, ProtoType: type pbytes,
skipDefault: static bool): int =
let
size = computeObjectSize(fieldVal)
when skipDefault:
if size == 0:
return 0
computeSize(FieldHeader.init(fieldNum, ProtoType.wireKind())) +
computeSize(puint64(size)) +
size
proc computeFieldSize*[T: not object](
fieldNum: int, fieldVal: T,
ProtoType: type SomeScalar, skipDefault: static bool): int =
when skipDefault:
const def = default(typeof(fieldVal))
if fieldVal == def:
return
computeSize(fieldNum, ProtoType(fieldVal))
proc computeFieldSize*(
fieldNum: int, fieldVal: PBOption, ProtoType: type,
skipDefault: static bool): int =
if fieldVal.isSome(): # TODO required field checking
computeFieldSize(fieldNum, fieldVal.get(), ProtoType, skipDefault)
else:
0
when defined(ConformanceTest):
proc computeFieldSize*[T](
fieldNum: int, fieldVal: ref T,
ProtoType: type pbytes, skipDefault: static bool): int =
if not fieldVal.isNil():
computeFieldSize(fieldNum, fieldVal[], ProtoType, skipDefault)
else:
0
proc writeField[T: enum](
stream: OutputStream, fieldNum: int, fieldVal: T, ProtoType: type) =
when 0 notin T:
{.fatal: $T & " definition must contain a constant that maps to zero".}
stream.writeField(fieldNum, pint32(fieldVal.ord()))
proc computeFieldSize*[K, V](
fieldNum: int, fieldVal: Table[K, V], ProtoType: type pbytes,
skipDefault: static bool): int =
tableObject(TableObject, K, V)
for k, v in fieldVal.pairs():
let tmp = TableObject(key: k, value: v)
result += computeFieldSize(fieldNum, tmp, ProtoType, false)
proc computeSizePacked*[T: not byte, ProtoType: SomePrimitive](
values: openArray[T], _: type ProtoType): int =
const canCopyMem =
ProtoType is SomeFixed32 or ProtoType is SomeFixed64 or ProtoType is pbool
when canCopyMem:
values.len() * sizeof(T)
else:
var total = 0
for item in values:
total += computeSize(ProtoType(item))
total
proc computeFieldSizePacked*[ProtoType: SomePrimitive](
field: int, values: openArray, _: type ProtoType): int =
# Packed encoding uses a length-delimited field byte length of the sum of the
# byte lengths of each field followed by the header-free contents
let
dataSize = computeSizePacked(values, ProtoType)
computeSize(FieldHeader.init(field, WireKind.LengthDelim)) +
computeSize(puint64(dataSize)) +
dataSize
func computeObjectSize*[T: object](value: T): int =
const
isProto2: bool = T.isProto2()
isProto3: bool = T.isProto3()
static:
doAssert isProto2 xor isProto3
var total = 0
enumInstanceSerializedFields(value, fieldName, fieldVal):
const
fieldNum = T.fieldNumberOf(fieldName)
type
FlatType = flatType(fieldVal)
protoType(ProtoType, T, FlatType, fieldName)
let fieldSize = when FlatType is seq and FlatType isnot seq[byte]:
const
isPacked = T.isPacked(fieldName).get(isProto3)
when isPacked and ProtoType is SomePrimitive:
computeFieldSizePacked(fieldNum, fieldVal, ProtoType)
else:
var dataSize = 0
for i in 0..<fieldVal.len:
# don't skip defaults so as to preserve length
dataSize += computeFieldSize(fieldNum, fieldVal[i], ProtoType, false)
dataSize
else:
computeFieldSize(fieldNum, fieldVal, ProtoType, isProto3)
total += fieldSize
total

View File

@ -13,8 +13,8 @@ type
ProtobufEOFError* = object of ProtobufReadError
ProtobufMessageError* = object of ProtobufReadError
ProtobufFlags* = uint8 # enum
# VarIntLengthPrefix, # TODO needs fixing
ProtobufFlags* = enum
VarIntLengthPrefix
ProtobufWriter* = object
stream*: OutputStream

View File

@ -6,42 +6,50 @@ import
stew/objects,
faststreams/outputs,
serialization,
"."/[codec, internal, types]
"."/[codec, internal, sizer, types]
export outputs, serialization, codec, types
proc writeValue*[T: object](stream: OutputStream, value: T)
proc writeObject[T: object](stream: OutputStream, value: T)
proc writeField(
stream: OutputStream, fieldNum: int, fieldVal: auto, ProtoType: type UnsupportedType) =
proc writeField*(
stream: OutputStream, fieldNum: int, fieldVal: auto,
ProtoType: type UnsupportedType, _: static bool = false) =
# TODO turn this into an extension point
unsupportedProtoType ProtoType.FieldType, ProtoType.RootType, ProtoType.fieldName
proc writeField*[T: object](stream: OutputStream, fieldNum: int, fieldVal: T) =
# TODO Pre-compute size of inner object then write it without the intermediate
# memory output
var inner = memoryOutput()
inner.writeValue(fieldVal)
let bytes = inner.getOutput()
stream.writeField(fieldNum, pbytes(bytes))
proc writeField*[T: object and not PBOption and not Table](
stream: OutputStream, fieldNum: int, fieldVal: T, ProtoType: type pbytes,
skipDefault: static bool = false) =
let
size = computeObjectSize(fieldVal)
proc writeField[T: object and not PBOption](
stream: OutputStream, fieldNum: int, fieldVal: T, ProtoType: type) =
stream.writeField(fieldNum, fieldVal)
when skipDefault:
if size == 0:
return
stream.writeValue(FieldHeader.init(fieldNum, ProtoType.wireKind()))
stream.writeValue(puint64(size))
stream.writeObject(fieldVal)
proc writeField*[T: not object and not enum](
stream: OutputStream, fieldNum: int, fieldVal: T,
ProtoType: type SomeScalar, skipDefault: static bool = false) =
when skipDefault:
const def = default(typeof(fieldVal))
if fieldVal == def:
return
proc writeField[T: not object and not enum](
stream: OutputStream, fieldNum: int, fieldVal: T, ProtoType: type) =
stream.writeField(fieldNum, ProtoType(fieldVal))
proc writeField(
stream: OutputStream, fieldNum: int, fieldVal: PBOption, ProtoType: type) =
if fieldVal.isSome(): # TODO required field checking
stream.writeField(fieldNum, fieldVal.get(), ProtoType)
proc writeField*(
stream: OutputStream, fieldNum: int, fieldVal: PBOption, ProtoType: type,
skipDefault: static bool = false) =
if fieldVal.isSome():
stream.writeField(fieldNum, fieldVal.get(), ProtoType, skipDefault)
proc writeFieldPacked*[T: not byte, ProtoType: SomePrimitive](
output: OutputStream, field: int, values: openArray[T], _: type ProtoType) =
doAssert validFieldNumber(field)
# Packed encoding uses a length-delimited field byte length of the sum of the
# byte lengths of each field followed by the header-free contents
output.write(
@ -49,63 +57,44 @@ proc writeFieldPacked*[T: not byte, ProtoType: SomePrimitive](
const canCopyMem =
ProtoType is SomeFixed32 or ProtoType is SomeFixed64 or ProtoType is pbool
let dlength =
when canCopyMem:
values.len() * sizeof(T)
else:
var total = 0
for item in values:
total += vsizeof(ProtoType(item))
total
output.write(toBytes(puint64(dlength)))
let
dataSize = computeSizePacked(values, ProtoType)
output.write(toBytes(puint64(dataSize)))
when canCopyMem:
if values.len > 0:
output.write(
cast[ptr UncheckedArray[byte]](
unsafeAddr values[0]).toOpenArray(0, dlength - 1))
unsafeAddr values[0]).toOpenArray(0, dataSize - 1))
else:
for value in values:
output.write(toBytes(ProtoType(value)))
when defined(ConformanceTest):
proc writeField[T: enum](
stream: OutputStream, fieldNum: int, fieldVal: T, ProtoType: type) =
stream: OutputStream,
fieldNum: int,
fieldVal: T,
ProtoType: type,
skipDefault: static bool = false
) =
when 0 notin T:
{.fatal: $T & " definition must contain a constant that maps to zero".}
stream.writeField(fieldNum, pint32(fieldVal.ord()))
proc writeField*[K, V](
proc writeField[K, V](
stream: OutputStream,
fieldNum: int,
value: Table[K, V],
ProtoType: type
ProtoType: type,
skipDefault: static bool = false
) =
when K is SomePBInt and V is SomePBInt:
type
TableObject {.proto3.} = object
key {.fieldNumber: 1, pint.}: K
value {.fieldNumber: 2, pint.}: V
elif K is SomePBInt:
type
TableObject {.proto3.} = object
key {.fieldNumber: 1, pint.}: K
value {.fieldNumber: 2.}: V
elif V is SomePBInt:
type
TableObject {.proto3.} = object
key {.fieldNumber: 1.}: K
value {.fieldNumber: 2, pint.}: V
else:
type
TableObject {.proto3.} = object
key {.fieldNumber: 1.}: K
value {.fieldNumber: 2.}: V
tableObject(TableObject, K, V)
for k, v in value.pairs():
let tmp = TableObject(key: k, value: v)
stream.writeField(fieldNum, tmp, ProtoType)
proc writeValue*[T: object](stream: OutputStream, value: T) =
proc writeObject[T: object](stream: OutputStream, value: T) =
const
isProto2: bool = T.isProto2()
isProto3: bool = T.isProto3()
@ -127,54 +116,21 @@ proc writeValue*[T: object](stream: OutputStream, value: T) =
stream.writeFieldPacked(fieldNum, fieldVal, ProtoType)
else:
for i in 0..<fieldVal.len:
stream.writeField(fieldNum, fieldVal[i], ProtoType)
# don't skip defaults so as to preserve length
stream.writeField(fieldNum, fieldVal[i], ProtoType, false)
elif FlatType is object:
# TODO avoid writing empty objects in proto3
stream.writeField(fieldNum, fieldVal, ProtoType)
elif FlatType is ref and defined(ConformanceTest):
if not fieldVal.isNil():
stream.writeField(fieldNum, fieldVal[], ProtoType)
else:
when isProto2:
stream.writeField(fieldNum, fieldVal, ProtoType)
else:
if fieldVal != static(default(typeof(fieldVal))): # TODO make this an extension point?
stream.writeField(fieldNum, fieldVal, ProtoType)
stream.writeField(fieldNum, fieldVal, ProtoType, isProto3)
proc writeValue*[T: object](writer: ProtobufWriter, value: T) =
static: verifySerializable(T)
# TODO cursors broken
# var
# cursor: VarSizeWriteCursor
# startPos: int
if ProtobufFlags.VarIntLengthPrefix in writer.flags:
let
size = computeObjectSize(value)
writer.stream.writeValue(puint64(size))
# if writer.flags.contains(VarIntLengthPrefix):
# cursor = writer.stream.delayVarSizeWrite(10)
# startPos = writer.stream.pos
writer.stream.writeValue(value)
# if writer.flags.contains(VarIntLengthPrefix):
# var len = uint32(writer.stream.pos - startPos)
# if len == 0:
# cursor.finalWrite([])
# elif writer.flags.contains(VarIntLengthPrefix):
# var viLen = encodeVarInt(PInt(len))
# if viLen.len == 0:
# cursor.finalWrite([byte(0)])
# else:
# cursor.finalWrite(viLen)
# elif writer.flags.contains(UIntLELengthPrefix):
# var temp: array[sizeof(len), byte]
# for i in 0 ..< sizeof(len):
# temp[i] = byte(len and LAST_BYTE)
# len = len shr 8
# cursor.finalWrite(temp)
# elif writer.flags.contains(UIntBELengthPrefix):
# var temp: array[sizeof(len), byte]
# for i in 0 ..< sizeof(len):
# temp[i] = byte(len shr ((sizeof(len) - 1) * 8))
# len = len shl 8
# cursor.finalWrite(temp)
writer.stream.writeObject(value)

View File

@ -1 +0,0 @@
switch("threads", "on")

View File

@ -942,36 +942,10 @@ Required.Proto3.ProtobufInput.ValidDataMap.UINT64.UINT64.Unordered.ProtobufOutpu
Required.Proto3.ProtobufInput.ValidDataMap.UINT64.UINT64.Unordered.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.BOOL.DefaultValue.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.BOOL.DefaultValue.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.BOOL.MultipleValuesForDifferentField.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.BOOL.MultipleValuesForDifferentField.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.BOOL.MultipleValuesForSameField.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.BOOL.MultipleValuesForSameField.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.BOOL.NonDefaultValue.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.BOOL.NonDefaultValue.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.BYTES.DefaultValue.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.BYTES.DefaultValue.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.BYTES.MultipleValuesForDifferentField.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.BYTES.MultipleValuesForDifferentField.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.BYTES.MultipleValuesForSameField.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.BYTES.MultipleValuesForSameField.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.BYTES.NonDefaultValue.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.BYTES.NonDefaultValue.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.DOUBLE.DefaultValue.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.DOUBLE.DefaultValue.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.DOUBLE.MultipleValuesForDifferentField.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.DOUBLE.MultipleValuesForDifferentField.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.DOUBLE.MultipleValuesForSameField.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.DOUBLE.MultipleValuesForSameField.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.DOUBLE.NonDefaultValue.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.DOUBLE.NonDefaultValue.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.ENUM.DefaultValue.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.ENUM.DefaultValue.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.ENUM.MultipleValuesForDifferentField.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.ENUM.MultipleValuesForDifferentField.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.ENUM.MultipleValuesForSameField.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.ENUM.MultipleValuesForSameField.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.ENUM.NonDefaultValue.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.ENUM.NonDefaultValue.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.FLOAT.DefaultValue.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.FLOAT.DefaultValue.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.FLOAT.MultipleValuesForDifferentField.ProtobufOutput
@ -980,40 +954,14 @@ Required.Proto3.ProtobufInput.ValidDataOneof.FLOAT.MultipleValuesForSameField.Pr
Required.Proto3.ProtobufInput.ValidDataOneof.FLOAT.MultipleValuesForSameField.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.FLOAT.NonDefaultValue.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.FLOAT.NonDefaultValue.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.MESSAGE.DefaultValue.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.MESSAGE.DefaultValue.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.MESSAGE.Merge.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.MESSAGE.Merge.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.MESSAGE.MultipleValuesForDifferentField.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.MESSAGE.MultipleValuesForDifferentField.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.MESSAGE.MultipleValuesForSameField.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.MESSAGE.MultipleValuesForSameField.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.MESSAGE.NonDefaultValue.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.MESSAGE.NonDefaultValue.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.STRING.DefaultValue.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.STRING.DefaultValue.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.STRING.MultipleValuesForDifferentField.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.STRING.MultipleValuesForDifferentField.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.STRING.MultipleValuesForSameField.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.STRING.MultipleValuesForSameField.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.STRING.NonDefaultValue.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.STRING.NonDefaultValue.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.UINT32.DefaultValue.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.UINT32.DefaultValue.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.UINT32.MultipleValuesForDifferentField.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.UINT32.MultipleValuesForDifferentField.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.UINT32.MultipleValuesForSameField.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.UINT32.MultipleValuesForSameField.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.UINT32.NonDefaultValue.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.UINT32.NonDefaultValue.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.UINT64.DefaultValue.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.UINT64.DefaultValue.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.UINT64.MultipleValuesForDifferentField.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.UINT64.MultipleValuesForDifferentField.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.UINT64.MultipleValuesForSameField.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.UINT64.MultipleValuesForSameField.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.UINT64.NonDefaultValue.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataOneof.UINT64.NonDefaultValue.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataRepeated.BOOL.PackedInput.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataRepeated.BOOL.PackedInput.ProtobufOutput
Required.Proto3.ProtobufInput.ValidDataRepeated.BOOL.UnpackedInput.ProtobufOutput

View File

@ -16,7 +16,10 @@ type
x {.fieldNumber: 1.}: bool
proc writeRead[W, R](toWrite: W, value: R) =
check Protobuf.decode(Protobuf.encode(toWrite), R) == value
let encoded = Protobuf.encode(toWrite)
check:
encoded.len == Protobuf.computeSize(toWrite)
Protobuf.decode(encoded, R) == value
suite "Test Boolean Encoding/Decoding":
test "Can encode/decode boolean without subtype specification":

View File

@ -109,6 +109,8 @@ suite "codec test suite":
let data = getVarintEncodedValue(VarintValues[i])
check:
toHex(data) == VarintVectors[i]
data.len == computeSize(puint64(VarintValues[i]))
getVarintDecodedValue(data) == VarintValues[i]
test "[varint] incorrect values test":
@ -125,6 +127,7 @@ suite "codec test suite":
let data = getFixed32EncodedValue(cast[float32](Fixed32Values[i]))
check:
toHex(data) == Fixed32Vectors[i]
data.len == computeSize(fixed32(Fixed32Values[i]))
getFixed32DecodedValue(data) == Fixed32Values[i]
test "[fixed32] incorrect values test":
@ -140,6 +143,7 @@ suite "codec test suite":
let data = getFixed64EncodedValue(cast[float64](Fixed64Values[i]))
check:
toHex(data) == Fixed64Vectors[i]
data.len == computeSize(fixed64(Fixed64Values[i]))
getFixed64DecodedValue(data) == Fixed64Values[i]
test "[fixed64] incorrect values test":
@ -153,10 +157,12 @@ suite "codec test suite":
test "[length] edge values test":
for i in 0 ..< len(LengthValues):
let data1 = getLengthEncodedValue(LengthValues[i])
let data2 = getLengthEncodedValue(cast[seq[byte]](LengthValues[i]))
let data2 = getLengthEncodedValue(toBytes(LengthValues[i]))
check:
toHex(data1) == LengthVectors[i]
computeSize(pstring(LengthValues[i])) == data1.len
toHex(data2) == LengthVectors[i]
computeSize(pbytes(toBytes(LengthValues[i]))) == data2.len
check:
getLengthDecodedValue(data1) == LengthValues[i]
getLengthDecodedValue(data2) == LengthValues[i]

View File

@ -56,7 +56,7 @@ suite "Test Object Encoding/Decoding":
writer = memoryOutput()
writer.writeField(1, sint32(obj.d))
writer.writeField(3, obj.f)
writer.writeField(3, obj.f, pbytes)
writer.writeField(4, pstring(obj.g))
let result = Protobuf.decode(writer.getOutput(), type(Wrapped))
@ -77,7 +77,7 @@ suite "Test Object Encoding/Decoding":
)
writer = memoryOutput()
writer.writeField(3, obj.f)
writer.writeField(3, obj.f, pbytes)
#writer.writeField(6, penum(obj.i))
writer.writeField(1, sint64(obj.d))
writer.writeField(2, sint64(obj.e))

View File

@ -34,6 +34,7 @@ z: ["zero", "one", "two"]
"080a080508d80408c7091001100010011001100010001000100110001a047a65726f1a036f6e651a0374776f")
check:
Protobuf.computeSize(v) == encoded.len
Protobuf.encode(v) == encoded
Protobuf.decode(encoded, typeof(v)) == v
@ -56,5 +57,6 @@ a: [5, -3, 300, -612]
"0a060a05d804c70912090100010100000001001a1005000000fdffffff2c0100009cfdffff22100000a040000040c000009643000019c4")
check:
Protobuf.computeSize(v) == encoded.len
Protobuf.encode(v) == encoded
Protobuf.decode(encoded, typeof(v)) == v