Add preliminary encode/decode of varints
This commit is contained in:
parent
9d60f49e6f
commit
fe9c5fa1f3
2 changed files with 113 additions and 7 deletions
|
|
@ -1,7 +1,113 @@
|
||||||
# This is just an example to get you started. A typical library package
|
import faststreams
|
||||||
# exports the main API in this file. Note that you cannot rename this file
|
|
||||||
# but you can remove it if you wish.
|
|
||||||
|
|
||||||
proc add*(x, y: int): int =
|
const
|
||||||
## Adds two files together.
|
MaxMessageSize* = 1'u shl 22
|
||||||
return x + y
|
|
||||||
|
type
|
||||||
|
ProtoBuffer* = ref object
|
||||||
|
fieldNum: int
|
||||||
|
outstream: OutputStreamVar
|
||||||
|
|
||||||
|
ProtoWireType* = enum
|
||||||
|
## Protobuf's field types enum
|
||||||
|
Varint, Fixed64, Length, StartGroup, EndGroup, Fixed32
|
||||||
|
|
||||||
|
ProtoField* = object
|
||||||
|
## Protobuf's message field representation object
|
||||||
|
index: int
|
||||||
|
case kind: ProtoWireType
|
||||||
|
of Varint:
|
||||||
|
vint*: uint64
|
||||||
|
of Fixed64:
|
||||||
|
vfloat64*: float64
|
||||||
|
of Length:
|
||||||
|
vbuffer*: OutputStreamVar
|
||||||
|
of Fixed32:
|
||||||
|
vfloat32*: float32
|
||||||
|
of StartGroup, EndGroup:
|
||||||
|
discard
|
||||||
|
|
||||||
|
SomeSVarint* = int | int64 | int32 | int16 | int8 | enum
|
||||||
|
SomeUVarint* = uint | uint64 | uint32 | uint16 | uint8 | byte | bool
|
||||||
|
SomeVarint* = SomeSVarint | SomeUVarint
|
||||||
|
|
||||||
|
proc newProtoBuffer*(): ProtoBuffer =
|
||||||
|
ProtoBuffer(outstream: OutputStream.init(), fieldNum: 1)
|
||||||
|
|
||||||
|
# Main interface
|
||||||
|
proc encode*(): ProtoBuffer =
|
||||||
|
discard
|
||||||
|
|
||||||
|
proc decode*[T](source: ProtoBuffer): T =
|
||||||
|
discard
|
||||||
|
|
||||||
|
template wireType(firstByte: byte): ProtoWireType =
|
||||||
|
(firstByte and 0b111).ProtoWireType
|
||||||
|
|
||||||
|
template fieldNumber(firstByte: byte): uint =
|
||||||
|
(firstByte shr 3) and 0b1111
|
||||||
|
|
||||||
|
template protoHeader*(fieldNum: int, wire: ProtoWireType): byte =
|
||||||
|
## Get protobuf's field header integer for ``index`` and ``wire``.
|
||||||
|
((cast[uint](fieldNum) shl 3) or cast[uint](wire)).byte
|
||||||
|
|
||||||
|
proc encodeVarint(stream: OutputStreamVar, fieldNum: int, value: SomeVarint) {.inline.} =
|
||||||
|
let header = protoHeader(fieldNum, Varint)
|
||||||
|
stream.append header
|
||||||
|
|
||||||
|
when value is enum:
|
||||||
|
var value = cast[type(ord(value))](value)
|
||||||
|
elif value is bool:
|
||||||
|
var value = cast[byte](value)
|
||||||
|
else:
|
||||||
|
var value = value
|
||||||
|
|
||||||
|
when type(value) is SomeSVarint:
|
||||||
|
if value < type(value)(0):
|
||||||
|
value = not(value shl type(value)(1))
|
||||||
|
else:
|
||||||
|
value = value shl type(value)(1)
|
||||||
|
|
||||||
|
while value > type(value)(0b0111_1111):
|
||||||
|
stream.append byte((value and 0b0111_1111) or 0b1000_0000)
|
||||||
|
value = value shr 7
|
||||||
|
stream.append byte(value and 0b1111_1111)
|
||||||
|
|
||||||
|
proc encode(protobuf: ProtoBuffer, value: SomeVarint) {.inline.} =
|
||||||
|
protobuf.outstream.encodeVarint(protobuf.fieldNum, value)
|
||||||
|
inc protobuf.fieldNum
|
||||||
|
|
||||||
|
proc decode[T: SomeVarint](bytes: var seq[byte], ty: typedesc[T], offset = 0): tuple[fieldNum: uint, value: T] {.inline.} =
|
||||||
|
# Only up to 128 bits supported by the spec
|
||||||
|
assert (bytes.len - 1) <= 16
|
||||||
|
|
||||||
|
let wireTy = wireType(bytes[offset])
|
||||||
|
if wireTy != Varint:
|
||||||
|
raise newException(Exception, "Not a varint!")
|
||||||
|
|
||||||
|
result.fieldNum = fieldNumber(bytes[offset])
|
||||||
|
result.value = cast[ty](0)
|
||||||
|
var shiftAmount = 0
|
||||||
|
var i = offset + 1
|
||||||
|
while true:
|
||||||
|
result.value += T(bytes[i] and 0b0111_1111) shl shiftAmount
|
||||||
|
shiftAmount += 7
|
||||||
|
if (bytes[i] shr 7) == 0:
|
||||||
|
break
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
when ty is SomeSVarint:
|
||||||
|
if (result.value and T(1)) != T(0):
|
||||||
|
result.value = cast[T](not(result.value shr T(1)))
|
||||||
|
else:
|
||||||
|
result.value = cast[T](result.value shr T(1))
|
||||||
|
|
||||||
|
proc main() =
|
||||||
|
let proto = newProtoBuffer()
|
||||||
|
proto.encode(-1500000)
|
||||||
|
var input: seq[byte] = proto.outstream.getOutput
|
||||||
|
echo input
|
||||||
|
|
||||||
|
echo decode(input, int64)
|
||||||
|
|
||||||
|
main()
|
||||||
|
|
@ -11,4 +11,4 @@ skipDirs = @["tests"]
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
|
|
||||||
requires "nim >= 1.0.6"
|
requires "nim >= 1.0.6", "faststreams"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue