rodfiles.nim 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2020 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## Low level binary format used by the compiler to store and load various AST
  10. ## and related data.
  11. ##
  12. ## NB: this is incredibly low level and if you're interested in how the
  13. ## compiler works and less a storage format, you're probably looking for
  14. ## the `ic` or `packed_ast` modules to understand the logical format.
  15. from std/typetraits import supportsCopyMem
  16. when defined(nimPreviewSlimSystem):
  17. import std/[syncio, assertions]
  18. ## Overview
  19. ## ========
  20. ## `RodFile` represents a Rod File (versioned binary format), and the
  21. ## associated data for common interactions such as IO and error tracking
  22. ## (`RodFileError`). The file format broken up into sections (`RodSection`)
  23. ## and preceded by a header (see: `cookie`). The precise layout, section
  24. ## ordering and data following the section are determined by the user. See
  25. ## `ic.loadRodFile`.
  26. ##
  27. ## A basic but "wrong" example of the lifecycle:
  28. ## ---------------------------------------------
  29. ## 1. `create` or `open` - create a new one or open an existing
  30. ## 2. `storeHeader` - header info
  31. ## 3. `storePrim` or `storeSeq` - save your stuff
  32. ## 4. `close` - and we're done
  33. ##
  34. ## Now read the bits below to understand what's missing.
  35. ##
  36. ## ### Issues with the Example
  37. ## Missing Sections:
  38. ## This is a low level API, so headers and sections need to be stored and
  39. ## loaded by the user, see `storeHeader` & `loadHeader` and `storeSection` &
  40. ## `loadSection`, respectively.
  41. ##
  42. ## No Error Handling:
  43. ## The API is centered around IO and prone to error, each operation checks or
  44. ## sets the `RodFile.err` field. A user of this API needs to handle these
  45. ## appropriately.
  46. ##
  47. ## API Notes
  48. ## =========
  49. ##
  50. ## Valid inputs for Rod files
  51. ## --------------------------
  52. ## ASTs, hopes, dreams, and anything as long as it and any children it may have
  53. ## support `copyMem`. This means anything that is not a pointer and that does not contain a pointer. At a glance these are:
  54. ## * string
  55. ## * objects & tuples (fields are recursed)
  56. ## * sequences AKA `seq[T]`
  57. ##
  58. ## Note on error handling style
  59. ## ----------------------------
  60. ## A flag based approach is used where operations no-op in case of a
  61. ## preexisting error and set the flag if they encounter one.
  62. ##
  63. ## Misc
  64. ## ----
  65. ## * 'Prim' is short for 'primitive', as in a non-sequence type
  66. type
  67. RodSection* = enum
  68. versionSection
  69. configSection
  70. stringsSection
  71. checkSumsSection
  72. depsSection
  73. numbersSection
  74. exportsSection
  75. hiddenSection
  76. reexportsSection
  77. compilerProcsSection
  78. trmacrosSection
  79. convertersSection
  80. methodsSection
  81. pureEnumsSection
  82. toReplaySection
  83. topLevelSection
  84. bodiesSection
  85. symsSection
  86. typesSection
  87. typeInstCacheSection
  88. procInstCacheSection
  89. attachedOpsSection
  90. methodsPerGenericTypeSection
  91. enumToStringProcsSection
  92. methodsPerTypeSection
  93. dispatchersSection
  94. typeInfoSection # required by the backend
  95. backendFlagsSection
  96. aliveSymsSection # beware, this is stored in a `.alivesyms` file.
  97. sideChannelSection
  98. namespaceSection
  99. symnamesSection
  100. RodFileError* = enum
  101. ok, tooBig, cannotOpen, ioFailure, wrongHeader, wrongSection, configMismatch,
  102. includeFileChanged
  103. RodFile* = object
  104. f*: File
  105. currentSection*: RodSection # for error checking
  106. err*: RodFileError # little experiment to see if this works
  107. # better than exceptions.
  108. const
  109. RodVersion = 2
  110. defaultCookie = [byte(0), byte('R'), byte('O'), byte('D'),
  111. byte(sizeof(int)*8), byte(system.cpuEndian), byte(0), byte(RodVersion)]
  112. proc setError(f: var RodFile; err: RodFileError) {.inline.} =
  113. f.err = err
  114. #raise newException(IOError, "IO error")
  115. proc storePrim*(f: var RodFile; s: string) =
  116. ## Stores a string.
  117. ## The len is prefixed to allow for later retreival.
  118. if f.err != ok: return
  119. if s.len >= high(int32):
  120. setError f, tooBig
  121. return
  122. var lenPrefix = int32(s.len)
  123. if writeBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
  124. setError f, ioFailure
  125. else:
  126. if s.len != 0:
  127. if writeBuffer(f.f, unsafeAddr(s[0]), s.len) != s.len:
  128. setError f, ioFailure
  129. proc storePrim*[T](f: var RodFile; x: T) =
  130. ## Stores a non-sequence/string `T`.
  131. ## If `T` doesn't support `copyMem` and is an object or tuple then the fields
  132. ## are written -- the user from context will need to know which `T` to load.
  133. if f.err != ok: return
  134. when supportsCopyMem(T):
  135. if writeBuffer(f.f, unsafeAddr(x), sizeof(x)) != sizeof(x):
  136. setError f, ioFailure
  137. elif T is tuple:
  138. for y in fields(x):
  139. storePrim(f, y)
  140. elif T is object:
  141. for y in fields(x):
  142. when y is seq:
  143. storeSeq(f, y)
  144. else:
  145. storePrim(f, y)
  146. else:
  147. {.error: "unsupported type for 'storePrim'".}
  148. proc storeSeq*[T](f: var RodFile; s: seq[T]) =
  149. ## Stores a sequence of `T`s, with the len as a prefix for later retrieval.
  150. if f.err != ok: return
  151. if s.len >= high(int32):
  152. setError f, tooBig
  153. return
  154. var lenPrefix = int32(s.len)
  155. if writeBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
  156. setError f, ioFailure
  157. else:
  158. for i in 0..<s.len:
  159. storePrim(f, s[i])
  160. proc loadPrim*(f: var RodFile; s: var string) =
  161. ## Read a string, the length was stored as a prefix
  162. if f.err != ok: return
  163. var lenPrefix = int32(0)
  164. if readBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
  165. setError f, ioFailure
  166. else:
  167. s = newString(lenPrefix)
  168. if lenPrefix > 0:
  169. if readBuffer(f.f, unsafeAddr(s[0]), s.len) != s.len:
  170. setError f, ioFailure
  171. proc loadPrim*[T](f: var RodFile; x: var T) =
  172. ## Load a non-sequence/string `T`.
  173. if f.err != ok: return
  174. when supportsCopyMem(T):
  175. if readBuffer(f.f, unsafeAddr(x), sizeof(x)) != sizeof(x):
  176. setError f, ioFailure
  177. elif T is tuple:
  178. for y in fields(x):
  179. loadPrim(f, y)
  180. elif T is object:
  181. for y in fields(x):
  182. when y is seq:
  183. loadSeq(f, y)
  184. else:
  185. loadPrim(f, y)
  186. else:
  187. {.error: "unsupported type for 'loadPrim'".}
  188. proc loadSeq*[T](f: var RodFile; s: var seq[T]) =
  189. ## `T` must be compatible with `copyMem`, see `loadPrim`
  190. if f.err != ok: return
  191. var lenPrefix = int32(0)
  192. if readBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
  193. setError f, ioFailure
  194. else:
  195. s = newSeq[T](lenPrefix)
  196. for i in 0..<lenPrefix:
  197. loadPrim(f, s[i])
  198. proc storeHeader*(f: var RodFile; cookie = defaultCookie) =
  199. ## stores the header which is described by `cookie`.
  200. if f.err != ok: return
  201. if f.f.writeBytes(cookie, 0, cookie.len) != cookie.len:
  202. setError f, ioFailure
  203. proc loadHeader*(f: var RodFile; cookie = defaultCookie) =
  204. ## Loads the header which is described by `cookie`.
  205. if f.err != ok: return
  206. var thisCookie: array[cookie.len, byte] = default(array[cookie.len, byte])
  207. if f.f.readBytes(thisCookie, 0, thisCookie.len) != thisCookie.len:
  208. setError f, ioFailure
  209. elif thisCookie != cookie:
  210. setError f, wrongHeader
  211. proc storeSection*(f: var RodFile; s: RodSection) =
  212. ## update `currentSection` and writes the bytes value of s.
  213. if f.err != ok: return
  214. assert f.currentSection < s
  215. f.currentSection = s
  216. storePrim(f, s)
  217. proc loadSection*(f: var RodFile; expected: RodSection) =
  218. ## read the bytes value of s, sets and error if the section is incorrect.
  219. if f.err != ok: return
  220. var s: RodSection = default(RodSection)
  221. loadPrim(f, s)
  222. if expected != s and f.err == ok:
  223. setError f, wrongSection
  224. proc create*(filename: string): RodFile =
  225. ## create the file and open it for writing
  226. result = default(RodFile)
  227. if not open(result.f, filename, fmWrite):
  228. setError result, cannotOpen
  229. proc close*(f: var RodFile) = close(f.f)
  230. proc open*(filename: string): RodFile =
  231. ## open the file for reading
  232. result = default(RodFile)
  233. if not open(result.f, filename, fmRead):
  234. setError result, cannotOpen