From fbdce1712d9f0e6283c420f26447608503a19532 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sun, 5 Apr 2020 22:27:40 -0600 Subject: [PATCH] Add support for encoding/decoding custom types --- protobuf_serialization.nim | 32 ++++++++++++++++++++++++ tests/test_serialization.nim | 48 +++++++++++++++++++++++++++++++++--- 2 files changed, 76 insertions(+), 4 deletions(-) diff --git a/protobuf_serialization.nim b/protobuf_serialization.nim index c9d3b32..ba3110a 100644 --- a/protobuf_serialization.nim +++ b/protobuf_serialization.nim @@ -70,6 +70,10 @@ template increaseBytesRead(amount = 1) = if (bytesRead > numBytesToRead.get()).unlikely: raise newException(Exception, &"Number of bytes read ({bytesRead}) exceeded bytes requested ({numBytesToRead})") +proc encodeField*[T: not AnyProtoType](protobuf: var ProtoBuffer, value: T) {.inline.} +proc encodeField*[T: not AnyProtoType](protobuf: var ProtoBuffer, fieldNum: int, value: T) {.inline.} +proc encodeField[T: not AnyProtoType](stream: OutputStreamVar, fieldNum: int, value: T) {.inline.} + proc put(stream: OutputStreamVar, value: SomeVarint) {.inline.} = when value is enum: var value = cast[type(ord(value))](value) @@ -153,6 +157,16 @@ proc encodeField*(protobuf: var ProtoBuffer, value: AnyProtoType) {.inline.} = protobuf.encodeField(protobuf.fieldNum, value) inc protobuf.fieldNum +proc encodeField[T: not AnyProtoType](stream: OutputStreamVar, fieldNum: int, value: T) {.inline.} = + stream.encodeField(fieldNum, value.toBytes) + +proc encodeField*[T: not AnyProtoType](protobuf: var ProtoBuffer, fieldNum: int, value: T) {.inline.} = + protobuf.outstream.encodeField(fieldNum, value.toBytes) + +proc encodeField*[T: not AnyProtoType](protobuf: var ProtoBuffer, value: T) {.inline.} = + protobuf.encodeField(protobuf.fieldNum, value.toBytes) + inc protobuf.fieldNum + proc get*[T: SomeFixed]( bytes: var seq[byte], ty: typedesc[T], @@ -276,6 +290,24 @@ proc decodeField*[T: object]( numBytesToRead = none(int) ): ProtoField[T] {.inline.} +proc decodeField*[T: not AnyProtoType]( + bytes: var seq[byte], + ty: typedesc[T], + outOffset: var int, + outBytesProcessed: var int, + numBytesToRead = none(int) +): ProtoField[T] {.inline.} = + + var bytesRead = 0 + + checkType(bytes[outOffset], seq[byte], outOffset) + + result.index = fieldNumber(bytes[outOffset]) + increaseBytesRead() + + var value = bytes.get(seq[byte], outOffset, outBytesProcessed, numBytesToRead) + result.value = value.to(T) + macro setField(obj: typed, fieldNum: int, offset: int, bytesProcessed: int, bytesToRead: Option[int], value: untyped): untyped = let typeFields = obj.getTypeInst.getType diff --git a/tests/test_serialization.nim b/tests/test_serialization.nim index 9b81f5b..e3aa176 100644 --- a/tests/test_serialization.nim +++ b/tests/test_serialization.nim @@ -6,7 +6,7 @@ import protobuf_serialization type MyEnum = enum ME1, ME2, ME3 -type + Test1 = object a: uint b: string @@ -18,6 +18,30 @@ type i: Test1 j: string k: bool + l: MyInt + + MyInt = distinct int + +proc to*(bytes: var seq[byte], ty: typedesc[MyInt]): MyInt = + + var value: int + + var shiftAmount = 0 + + for i in 0 ..< len(bytes): + value += int(bytes[i]) shl shiftAmount + shiftAmount += 8 + + result = MyInt(value) + +proc toBytes*(value: MyInt): seq[byte] = + var value = value.int + + while value > 0: + result.add byte(value and 0b1111_1111) + value = value shr 8 + +proc `==`(a, b: MyInt): bool {.borrow.} suite "Test Varint Encoding": test "Can encode/decode enum field": @@ -55,6 +79,21 @@ suite "Test Varint Encoding": assert decoded.value == num assert decoded.index == 1 + test "Can encode/decode distinct number field": + var proto = newProtoBuffer() + let num = 114151.MyInt + var bytesProcessed: int + + proto.encodeField(num) + + var output = proto.output + assert output == @[10.byte, 3, 231, 189, 1] + + var offset = 0 + let decoded = decodeField(output, MyInt, offset, bytesProcessed) + assert decoded.value.int == num.int + assert decoded.index == 1 + test "Can encode/decode float32 number field": var proto = newProtoBuffer() let num = float32(1234.164423) @@ -180,7 +219,7 @@ suite "Test Varint Encoding": test "Can encode/decode object field": var proto = newProtoBuffer() - let obj = Test3(g: 300, h: 200, i: Test1(a: 100, b: "this is a test", c: 'H'), j: "testing", k: true) + let obj = Test3(g: 300, h: 200, i: Test1(a: 100, b: "this is a test", c: 'H'), j: "testing", k: true, l: 124521.MyInt) proto.encodeField(obj) var offset, bytesProcessed: int @@ -193,7 +232,7 @@ suite "Test Varint Encoding": test "Can encode/decode object": var proto = newProtoBuffer() - let obj = Test3(g: 300, h: 200, i: Test1(a: 100, b: "this is a test", c: 'H'), j: "testing", k: true) + let obj = Test3(g: 300, h: 200, i: Test1(a: 100, b: "this is a test", c: 'H'), j: "testing", k: true, l: 124521.MyInt) proto.encode(obj) var output = proto.output @@ -203,7 +242,8 @@ suite "Test Varint Encoding": test "Can encode/decode out of order object": var proto = newProtoBuffer() - let obj = Test3(g: 400, h: 100, i: Test1(a: 100, b: "this is a test", c: 'H'), j: "testing", k: true) + let obj = Test3(g: 400, h: 100, i: Test1(a: 100, b: "this is a test", c: 'H'), j: "testing", k: true, l: 14514.MyInt) + proto.encodeField(6, 14514.MyInt) proto.encodeField(2, 100) proto.encodeField(4, "testing") proto.encodeField(1, 400)