/ binary_serialization / reader.nim
reader.nim
 1  
 2  import
 3    std/[tables, strutils, typetraits, macros, strformat],
 4    stew/[endians2, objects],
 5    faststreams/inputs, serialization/[formats, object_serialization, errors],
 6    format
 7  
 8  export
 9    inputs
10  
11  type
12    BinaryReader* = object
13      stream: InputStream
14      arrayLen: int
15  
16      bitSize: int
17      workingByte: byte
18      remaining: int
19  
20  Binary.setReader BinaryReader
21  
22  proc init*(T: type BinaryReader, stream: InputStream): T =
23    T(stream: stream)
24  
25  proc readValue*[T](r: var BinaryReader, value: var T) =
26    when sizeof(value) == 1:
27      type E = uint8
28    elif sizeof(value) == 2:
29      type E = uint16
30    elif sizeof(value) == 4:
31      type E = uint32
32    elif sizeof(value) == 8:
33      type E = uint64
34  
35    when value is SomeUnsignedInt:
36      when value is uint8:
37        if r.bitSize > 0:
38          let mask = ((1 shl r.bitSize) - 1).uint8
39          r.remaining.dec(r.bitSize)
40          value = (r.workingByte shr r.remaining) and mask
41          return
42      let length = sizeof(value)
43      value = fromBytes(type value, r.stream.read(length), bigEndian)
44    elif value is enum:
45      var asUInt: E
46      r.readValue(asUInt)
47      #TODO raise instead
48      assert value.checkedEnumAssign(asUInt)
49    elif value is set:
50      var asUInt: E
51      r.readValue(asUInt)
52      #TODO is this safe enough?
53      value = cast[type(value)](asUInt)
54    elif value is object:
55      value.enumInstanceSerializedFields(fieldName, field):
56        when field.hasCustomPragma(bin_len):
57          let it = value
58          #TODO safer conversion
59          r.arrayLen = int(field.getCustomPragmaVal(bin_len))
60  
61        when field.hasCustomPragma(bin_bitsize):
62          when field isnot uint8:
63            {.error: "bit_bitsize only applies to byte".}
64          r.bitSize = (int)field.getCustomPragmaVal(bin_bitsize)
65          assert r.bitSize in 1 ..< 8
66  
67          if r.remaining == 0:
68            r.workingByte = r.stream.read
69            r.remaining = 8
70  
71        var vall: type(field)
72        r.readValue(vall)
73        field = vall
74        r.bitSize = 0
75    elif value is seq:
76      for _ in 0 ..< r.arrayLen:
77        var subValue: type(value[0])
78        r.readValue(subValue)
79        value.add(subValue)
80    elif value is array:
81      for i in 0 ..< value.len:
82        r.readValue(value[i])
83    else:
84      {.error: "Unsupported type " & $type(value).}