diff --git a/protobuf_serialization.nim b/protobuf_serialization.nim index fa2dcd3..b518b0a 100644 --- a/protobuf_serialization.nim +++ b/protobuf_serialization.nim @@ -65,7 +65,7 @@ template increaseBytesRead(amount = 1) = outBytesProcessed += amount if numBytesToRead.isSome(): if (bytesRead > numBytesToRead.get()).unlikely: - raise newException(Exception, "Number of bytes read exceeded") + raise newException(Exception, &"Number of bytes read ({bytesRead}) exceeded bytes requested ({numBytesToRead})") proc put(stream: OutputStreamVar, value: SomeVarint) {.inline.} = when value is enum: @@ -87,30 +87,30 @@ proc put(stream: OutputStreamVar, value: SomeVarint) {.inline.} = value = value shr 7 stream.append byte(value and 0b1111_1111) -proc encode(stream: OutputStreamVar, fieldNum: int, value: SomeVarint) {.inline.} = +proc encodeField(stream: OutputStreamVar, fieldNum: int, value: SomeVarint) {.inline.} = stream.append protoHeader(fieldNum, Varint) stream.put(value) -proc encode*(protobuf: var ProtoBuffer, value: SomeVarint) {.inline.} = - protobuf.outstream.encode(protobuf.fieldNum, value) +proc encodeField*(protobuf: var ProtoBuffer, value: SomeVarint) {.inline.} = + protobuf.outstream.encodeField(protobuf.fieldNum, value) inc protobuf.fieldNum proc put(stream: OutputStreamVar, value: SomeLengthDelimited) {.inline.} = for b in value: stream.append byte(b) -proc encode(stream: OutputStreamVar, fieldNum: int, value: SomeLengthDelimited) {.inline.} = +proc encodeField(stream: OutputStreamVar, fieldNum: int, value: SomeLengthDelimited) {.inline.} = stream.append protoHeader(fieldNum, LengthDelimited) stream.put(len(value).uint) stream.put(value) -proc encode*(protobuf: var ProtoBuffer, value: SomeLengthDelimited) {.inline.} = - protobuf.outstream.encode(protobuf.fieldNum, value) +proc encodeField*(protobuf: var ProtoBuffer, value: SomeLengthDelimited) {.inline.} = + protobuf.outstream.encodeField(protobuf.fieldNum, value) inc protobuf.fieldNum proc put(stream: OutputStreamVar, value: object) {.inline.} -proc encode(stream: OutputStreamVar, fieldNum: int, value: object) {.inline.} = +proc encodeField(stream: OutputStreamVar, fieldNum: int, value: object) {.inline.} = #TODO Encode generic objects stream.append protoHeader(fieldNum, LengthDelimited) let objStream = OutputStream.init() @@ -119,14 +119,14 @@ proc encode(stream: OutputStreamVar, fieldNum: int, value: object) {.inline.} = stream.put(len(objOutput).uint) stream.put(objOutput) -proc encode*(protobuf: var ProtoBuffer, value: object) {.inline.} = - protobuf.outstream.encode(protobuf.fieldNum, value) +proc encodeField*(protobuf: var ProtoBuffer, value: object) {.inline.} = + protobuf.outstream.encodeField(protobuf.fieldNum, value) inc protobuf.fieldNum proc put(stream: OutputStreamVar, value: object) {.inline.} = var fieldNum = 1 for field, val in value.fieldPairs: - stream.encode(fieldNum, val) + stream.encodeField(fieldNum, val) fieldNum += 1 proc getVarint[T: SomeVarint]( @@ -160,7 +160,7 @@ proc getVarint[T: SomeVarint]( else: result = value -proc decode*[T: SomeVarint]( +proc decodeField*[T: SomeVarint]( bytes: var seq[byte], ty: typedesc[T], outOffset: var int, @@ -205,7 +205,7 @@ proc getLengthDelimited*[T: SomeLengthDelimited]( increaseBytesRead(length) -proc decode*[T: SomeLengthDelimited]( +proc decodeField*[T: SomeLengthDelimited]( bytes: var seq[byte], ty: typedesc[T], outOffset: var int, @@ -283,7 +283,7 @@ macro setField(obj: typed, fieldNum: int, offset: int, bytesProcessed: int, byte ofBranch.add(newLit(i+1)) ofBranch.add( quote do: - `obj`.`field` = decode(`value`, type(`obj`.`field`), `offset`, `bytesProcessed`, `bytesToRead`).value + `obj`.`field` = decodeField(`value`, type(`obj`.`field`), `offset`, `bytesProcessed`, `bytesToRead`).value ) caseStmt.add(ofBranch) @@ -292,14 +292,13 @@ macro setField(obj: typed, fieldNum: int, offset: int, bytesProcessed: int, byte elseBranch.add( nnkStmtList.newTree( quote do: - `obj`.`field` = decode(`value`, type(`obj`.`field`), `offset`, `bytesProcessed`, `bytesToRead`).value + `obj`.`field` = decodeField(`value`, type(`obj`.`field`), `offset`, `bytesProcessed`, `bytesToRead`).value ) ) caseStmt.add(elseBranch) - result.add(caseStmt) -proc decode*[T: object]( +proc decodeField*[T: object]( bytes: var seq[byte], ty: typedesc[T], outOffset: var int, @@ -309,15 +308,28 @@ proc decode*[T: object]( var bytesRead = 0 let wireTy = wireType(bytes[outOffset]) + assert wireTy == LengthDelimited + result.index = fieldNumber(bytes[outOffset]) - if wireTy == LengthDelimited: - # read LD header - # then read only amount of bytes needed - increaseBytesRead() + # read LD header + # then read only amount of bytes needed + increaseBytesRead() + var index = 1 + let decodedSize = getVarint(bytes, uint, outOffset, outBytesProcessed, numBytesToRead) + let bytesToRead = some(decodedSize.int) + for field, val in result.value.fieldPairs: + setField(result.value, index, outOffset, outBytesProcessed, bytesToRead, bytes) + index += 1 - let decodedSize = getVarint(bytes, uint, outOffset, outBytesProcessed, numBytesToRead) - let bytesToRead = some(decodedSize.int) - setField(result.value, result.index, outOffset, outBytesProcessed, bytesToRead, bytes) - else: - setField(result.value, result.index, outOffset, outBytesProcessed, numBytesToRead, bytes) \ No newline at end of file +proc decode*[T: object]( + bytes: var seq[byte], + ty: typedesc[T], +): T {.inline.} = + var bytesRead = 0 + var offset = 0 + + var fieldNum = 1 + for field, val in result.fieldPairs: + setField(result, fieldNum, offset, bytesRead, none(int), bytes) + fieldNum += 1 \ No newline at end of file diff --git a/tests/test_serialization.nim b/tests/test_serialization.nim index 37ae148..86c97f9 100644 --- a/tests/test_serialization.nim +++ b/tests/test_serialization.nim @@ -18,17 +18,20 @@ suite "Test Varint Encoding": test "Can encode/decode enum": var proto = newProtoBuffer() var bytesProcessed: int - proto.encode(ME3) - proto.encode(ME2) + + proto.encodeField(ME3) + proto.encodeField(ME2) + var output = proto.output assert output == @[8.byte, 4, 16, 2] + var offset = 0 - let decodedME3 = decode(output, MyEnum, offset, bytesProcessed) + let decodedME3 = decodeField(output, MyEnum, offset, bytesProcessed) assert decodedME3.value == ME3 assert decodedME3.index == 1 - let decodedME2 = decode(output, MyEnum, offset, bytesProcessed) + let decodedME2 = decodeField(output, MyEnum, offset, bytesProcessed) assert decodedME2.value == ME2 assert decodedME2.index == 2 @@ -36,12 +39,14 @@ suite "Test Varint Encoding": var proto = newProtoBuffer() let num = -153452 var bytesProcessed: int - proto.encode(num) + + proto.encodeField(num) + var output = proto.output assert output == @[8.byte, 215, 221, 18] var offset = 0 - let decoded = decode(output, int, offset, bytesProcessed) + let decoded = decodeField(output, int, offset, bytesProcessed) assert decoded.value == num assert decoded.index == 1 @@ -49,12 +54,14 @@ suite "Test Varint Encoding": var proto = newProtoBuffer() let num = 123151.uint var bytesProcessed: int - proto.encode(num) + + proto.encodeField(num) + var output = proto.output assert output == @[8.byte, 143, 194, 7] var offset = 0 - let decoded = decode(output, uint, offset, bytesProcessed) + let decoded = decodeField(output, uint, offset, bytesProcessed) assert decoded.value == num assert decoded.index == 1 @@ -62,12 +69,14 @@ suite "Test Varint Encoding": var proto = newProtoBuffer() let str = "hey this is a string" var bytesProcessed: int - proto.encode(str) + + proto.encodeField(str) + var output = proto.output assert output == @[10.byte, 20, 104, 101, 121, 32, 116, 104, 105, 115, 32, 105, 115, 32, 97, 32, 115, 116, 114, 105, 110, 103] var offset = 0 - let decoded = decode(output, string, offset, bytesProcessed) + let decoded = decodeField(output, string, offset, bytesProcessed) assert decoded.value == str assert decoded.index == 1 @@ -76,12 +85,10 @@ suite "Test Varint Encoding": let obj = Test3(g: 300, h: 200, i: Test1(a: 100)) - proto.encode(obj) + proto.encodeField(obj) var offset, bytesProcessed: int var output = proto.output - let decoded = decode(output, Test3, offset, bytesProcessed) - echo decoded - - echo output - assert false \ No newline at end of file + let decoded = decodeField(output, Test3, offset, bytesProcessed) + assert decoded.value == obj + assert decoded.index == 1 \ No newline at end of file