tvaruintconv.nim 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. discard """
  2. action: compile
  3. """
  4. # https://github.com/status-im/nimbus-eth2/pull/6554#issuecomment-2354977102
  5. # failed with "for a 'var' type a variable needs to be passed; but 'uint64(result)' is immutable"
  6. import
  7. std/[typetraits, macros]
  8. type
  9. DefaultFlavor = object
  10. template serializationFormatImpl(Name: untyped) {.dirty.} =
  11. type Name = object
  12. template serializationFormat(Name: untyped) =
  13. serializationFormatImpl(Name)
  14. template setReader(Format, FormatReader: distinct type) =
  15. when arity(FormatReader) > 1:
  16. template Reader(T: type Format, F: distinct type = DefaultFlavor): type = FormatReader[F]
  17. else:
  18. template ReaderType(T: type Format): type = FormatReader
  19. template Reader(T: type Format): type = FormatReader
  20. template useDefaultReaderIn(T: untyped, Flavor: type) =
  21. mixin Reader
  22. template readValue(r: var Reader(Flavor), value: var T) =
  23. mixin readRecordValue
  24. readRecordValue(r, value)
  25. import mvaruintconv
  26. type
  27. FieldTag[RecordType: object; fieldName: static string] = distinct void
  28. func declval*(T: type): T {.compileTime.} =
  29. default(ptr T)[]
  30. macro enumAllSerializedFieldsImpl(T: type, body: untyped): untyped =
  31. var typeAst = getType(T)[1]
  32. var typeImpl: NimNode
  33. let isSymbol = not typeAst.isTuple
  34. if not isSymbol:
  35. typeImpl = typeAst
  36. else:
  37. typeImpl = getImpl(typeAst)
  38. result = newStmtList()
  39. var i = 0
  40. for field in recordFields(typeImpl):
  41. let
  42. fieldIdent = field.name
  43. realFieldName = newLit($fieldIdent.skipPragma)
  44. fieldName = realFieldName
  45. fieldIndex = newLit(i)
  46. let fieldNameDefs =
  47. if isSymbol:
  48. quote:
  49. const fieldName {.inject, used.} = `fieldName`
  50. const realFieldName {.inject, used.} = `realFieldName`
  51. else:
  52. quote:
  53. const fieldName {.inject, used.} = $`fieldIndex`
  54. const realFieldName {.inject, used.} = $`fieldIndex`
  55. let field =
  56. if isSymbol:
  57. quote do: declval(`T`).`fieldIdent`
  58. else:
  59. quote do: declval(`T`)[`fieldIndex`]
  60. result.add quote do:
  61. block:
  62. `fieldNameDefs`
  63. template FieldType: untyped {.inject, used.} = typeof(`field`)
  64. `body`
  65. # echo repr(result)
  66. template enumAllSerializedFields(T: type, body): untyped =
  67. enumAllSerializedFieldsImpl(T, body)
  68. type
  69. FieldReader[RecordType, Reader] = tuple[
  70. fieldName: string,
  71. reader: proc (rec: var RecordType, reader: var Reader)
  72. {.gcsafe, nimcall.}
  73. ]
  74. proc totalSerializedFieldsImpl(T: type): int =
  75. mixin enumAllSerializedFields
  76. enumAllSerializedFields(T): inc result
  77. template totalSerializedFields(T: type): int =
  78. (static(totalSerializedFieldsImpl(T)))
  79. template GetFieldType(FT: type FieldTag): type =
  80. typeof field(declval(FT.RecordType), FT.fieldName)
  81. proc makeFieldReadersTable(RecordType, ReaderType: distinct type,
  82. numFields: static[int]):
  83. array[numFields, FieldReader[RecordType, ReaderType]] =
  84. mixin enumAllSerializedFields, handleReadException
  85. var idx = 0
  86. enumAllSerializedFields(RecordType):
  87. proc readField(obj: var RecordType, reader: var ReaderType)
  88. {.gcsafe, nimcall.} =
  89. mixin readValue
  90. type F = FieldTag[RecordType, realFieldName]
  91. field(obj, realFieldName) = reader.readValue(GetFieldType(F))
  92. result[idx] = (fieldName, readField)
  93. inc idx
  94. proc fieldReadersTable(RecordType, ReaderType: distinct type): auto =
  95. mixin readValue
  96. type T = RecordType
  97. const numFields = totalSerializedFields(T)
  98. var tbl {.threadvar.}: ref array[numFields, FieldReader[RecordType, ReaderType]]
  99. if tbl == nil:
  100. tbl = new typeof(tbl)
  101. tbl[] = makeFieldReadersTable(RecordType, ReaderType, numFields)
  102. return addr(tbl[])
  103. proc readValue(reader: var auto, T: type): T =
  104. mixin readValue
  105. reader.readValue(result)
  106. template decode(Format: distinct type,
  107. input: string,
  108. RecordType: distinct type): auto =
  109. mixin Reader
  110. block: # https://github.com/nim-lang/Nim/issues/22874
  111. var reader: Reader(Format)
  112. reader.readValue(RecordType)
  113. template readValue(Format: type,
  114. ValueType: type): untyped =
  115. mixin Reader, init, readValue
  116. var reader: Reader(Format)
  117. readValue reader, ValueType
  118. template parseArrayImpl(numElem: untyped,
  119. actionValue: untyped) =
  120. actionValue
  121. serializationFormat Json
  122. template createJsonFlavor(FlavorName: untyped,
  123. skipNullFields = false) {.dirty.} =
  124. type FlavorName = object
  125. template Reader(T: type FlavorName): type = Reader(Json, FlavorName)
  126. type
  127. JsonReader[Flavor = DefaultFlavor] = object
  128. Json.setReader JsonReader
  129. template parseArray(r: var JsonReader; body: untyped) =
  130. parseArrayImpl(idx): body
  131. template parseArray(r: var JsonReader; idx: untyped; body: untyped) =
  132. parseArrayImpl(idx): body
  133. proc readRecordValue[T](r: var JsonReader, value: var T) =
  134. type
  135. ReaderType {.used.} = type r
  136. T = type value
  137. discard T.fieldReadersTable(ReaderType)
  138. proc readValue[T](r: var JsonReader, value: var T) =
  139. mixin readValue
  140. when value is seq:
  141. r.parseArray:
  142. readValue(r, value[0])
  143. elif value is object:
  144. readRecordValue(r, value)
  145. type
  146. RemoteSignerInfo = object
  147. id: uint32
  148. RemoteKeystore = object
  149. proc readValue(reader: var JsonReader, value: var RemoteKeystore) =
  150. discard reader.readValue(seq[RemoteSignerInfo])
  151. createJsonFlavor RestJson
  152. useDefaultReaderIn(RemoteSignerInfo, RestJson)
  153. proc readValue(reader: var JsonReader[RestJson], value: var uint64) =
  154. discard reader.readValue(string)
  155. discard Json.decode("", RemoteKeystore)
  156. block: # https://github.com/nim-lang/Nim/issues/22874
  157. var reader: Reader(RestJson)
  158. discard reader.readValue(RemoteSignerInfo)