tsizeof.nim 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735
  1. discard """
  2. targets: "c cpp"
  3. output: '''
  4. body executed
  5. body executed
  6. OK
  7. macros api OK
  8. '''
  9. """
  10. # This is for Azure. The keyword ``alignof`` only exists in ``c++11``
  11. # and newer. On Azure gcc does not default to c++11 yet.
  12. when defined(cpp) and not defined(windows):
  13. {.passC: "-std=c++11".}
  14. # Object offsets are different for inheritance objects when compiling
  15. # to c++.
  16. type
  17. TMyEnum = enum
  18. tmOne, tmTwo, tmThree, tmFour
  19. TMyArray1 = array[3, uint8]
  20. TMyArray2 = array[1..3, int32]
  21. TMyArray3 = array[TMyEnum, float64]
  22. var failed = false
  23. const
  24. mysize1 = sizeof(TMyArray1)
  25. mysize2 = sizeof(TMyArray2)
  26. mysize3 = sizeof(TMyArray3)
  27. doAssert mysize1 == 3
  28. doAssert mysize2 == 12
  29. doAssert mysize3 == 32
  30. import macros, typetraits
  31. macro testSizeAlignOf(args: varargs[untyped]): untyped =
  32. result = newStmtList()
  33. for arg in args:
  34. result.add quote do:
  35. let
  36. c_size = c_sizeof(`arg`)
  37. nim_size = sizeof(`arg`)
  38. c_align = c_alignof(type(`arg`))
  39. nim_align = alignof(`arg`)
  40. if nim_size != c_size or nim_align != c_align:
  41. var msg = strAlign(`arg`.type.name & ": ")
  42. if nim_size != c_size:
  43. msg.add " size(got, expected): " & $nim_size & " != " & $c_size
  44. if nim_align != c_align:
  45. msg.add " align(get, expected): " & $nim_align & " != " & $c_align
  46. echo msg
  47. failed = true
  48. macro testOffsetOf(a, b: untyped): untyped =
  49. let typeName = newLit(a.repr)
  50. let member = newLit(b.repr)
  51. result = quote do:
  52. let
  53. c_offset = c_offsetof(`a`,`b`)
  54. nim_offset = offsetof(`a`,`b`)
  55. if c_offset != nim_offset:
  56. echo `typeName`, ".", `member`, " offsetError, C: ", c_offset, " nim: ", nim_offset
  57. failed = true
  58. proc strAlign(arg: string): string =
  59. const minLen = 22
  60. result = arg
  61. for i in 0 ..< minLen - arg.len:
  62. result &= ' '
  63. macro c_offsetof(fieldAccess: typed): int32 =
  64. ## Bullet proof implementation that works on actual offsetof operator
  65. ## in the c backend. Assuming of course this implementation is
  66. ## correct.
  67. let s = if fieldAccess.kind == nnkCheckedFieldExpr: fieldAccess[0]
  68. else: fieldAccess
  69. let a = s[0].getTypeInst
  70. let b = s[1]
  71. result = quote do:
  72. var res: int32
  73. {.emit: [res, " = offsetof(", `a`, ", ", `b`, ");"] .}
  74. res
  75. template c_offsetof(t: typedesc, a: untyped): int32 =
  76. var x: ptr t
  77. c_offsetof(x[].a)
  78. macro c_sizeof(a: typed): int32 =
  79. ## Bullet proof implementation that works using the sizeof operator
  80. ## in the c backend. Assuming of course this implementation is
  81. ## correct.
  82. result = quote do:
  83. var res: int32
  84. {.emit: [res, " = sizeof(", `a`, ");"] .}
  85. res
  86. macro c_alignof(arg: untyped): untyped =
  87. ## Bullet proof implementation that works on actual alignment
  88. ## behavior measured at runtime.
  89. let typeSym = genSym(nskType, "AlignTestType"&arg.repr)
  90. result = quote do:
  91. type
  92. `typeSym` = object
  93. causeAlign: byte
  94. member: `arg`
  95. c_offsetof(`typeSym`, member)
  96. macro testAlign(arg:untyped):untyped =
  97. let prefix = newLit(arg.lineinfo & " alignof " & arg.repr & " ")
  98. result = quote do:
  99. let cAlign = c_alignof(`arg`)
  100. let nimAlign = alignof(`arg`)
  101. if cAlign != nimAlign:
  102. echo `prefix`, cAlign, " != ", nimAlign
  103. failed = true
  104. macro testSize(arg:untyped):untyped =
  105. let prefix = newLit(arg.lineinfo & " sizeof " & arg.repr & " ")
  106. result = quote do:
  107. let cSize = c_sizeof(`arg`)
  108. let nimSize = sizeof(`arg`)
  109. if cSize != nimSize:
  110. echo `prefix`, cSize, " != ", nimSize
  111. failed = true
  112. type
  113. MyEnum {.pure.} = enum
  114. ValueA
  115. ValueB
  116. ValueC
  117. OtherEnum {.pure, size: 8.} = enum
  118. ValueA
  119. ValueB
  120. Enum1 {.pure, size: 1.} = enum
  121. ValueA
  122. ValueB
  123. Enum2 {.pure, size: 2.} = enum
  124. ValueA
  125. ValueB
  126. Enum4 {.pure, size: 4.} = enum
  127. ValueA
  128. ValueB
  129. Enum8 {.pure, size: 8.} = enum
  130. ValueA
  131. ValueB
  132. # Must have more than 32 elements so that set[MyEnum33] will become compile to an int64.
  133. MyEnum33 {.pure.} = enum
  134. Value1, Value2, Value3, Value4, Value5, Value6,
  135. Value7, Value8, Value9, Value10, Value11, Value12,
  136. Value13, Value14, Value15, Value16, Value17, Value18,
  137. Value19, Value20, Value21, Value22, Value23, Value24,
  138. Value25, Value26, Value27, Value28, Value29, Value30,
  139. Value31, Value32, Value33
  140. proc transformObjectconfigPacked(arg: NimNode): NimNode =
  141. let debug = arg.kind == nnkPragmaExpr
  142. if arg.eqIdent("objectconfig"):
  143. result = ident"packed"
  144. else:
  145. result = copyNimNode(arg)
  146. for child in arg:
  147. result.add transformObjectconfigPacked(child)
  148. proc removeObjectconfig(arg: NimNode): NimNode =
  149. if arg.kind == nnkPragmaExpr and arg[1][0].eqIdent "objectconfig":
  150. result = arg[0]
  151. else:
  152. result = copyNimNode(arg)
  153. for child in arg:
  154. result.add removeObjectconfig(child)
  155. macro testinstance(body: untyped): untyped =
  156. let bodyPure = removeObjectconfig(body)
  157. let bodyPacked = transformObjectconfigPacked(body)
  158. result = quote do:
  159. proc pureblock(): void =
  160. const usePacked {.inject.} = false
  161. `bodyPure`
  162. pureblock()
  163. proc packedblock(): void =
  164. const usePacked {.inject.} = true
  165. `bodyPacked`
  166. packedblock()
  167. proc testPrimitiveTypes(): void =
  168. testAlign(pointer)
  169. testAlign(int)
  170. testAlign(uint)
  171. testAlign(int8)
  172. testAlign(int16)
  173. testAlign(int32)
  174. testAlign(int64)
  175. testAlign(uint8)
  176. testAlign(uint16)
  177. testAlign(uint32)
  178. testAlign(uint64)
  179. testAlign(float)
  180. testAlign(float32)
  181. testAlign(float64)
  182. testAlign(MyEnum)
  183. testAlign(OtherEnum)
  184. testAlign(Enum1)
  185. testAlign(Enum2)
  186. testAlign(Enum4)
  187. testAlign(Enum8)
  188. testPrimitiveTypes()
  189. testinstance:
  190. type
  191. EnumObjectA {.objectconfig.} = object
  192. a : Enum1
  193. b : Enum2
  194. c : Enum4
  195. d : Enum8
  196. EnumObjectB {.objectconfig.} = object
  197. a : Enum8
  198. b : Enum4
  199. c : Enum2
  200. d : Enum1
  201. TrivialType {.objectconfig.} = object
  202. x,y,z: int8
  203. SimpleAlignment {.objectconfig.} = object
  204. # behaves differently on 32bit Windows and 32bit Linux
  205. a,b: int8
  206. c: int64
  207. AlignAtEnd {.objectconfig.} = object
  208. a: int64
  209. b,c: int8
  210. SimpleBranch {.objectconfig.} = object
  211. case kind: MyEnum
  212. of MyEnum.ValueA:
  213. a: int16
  214. of MyEnum.ValueB:
  215. b: int32
  216. of MyEnum.ValueC:
  217. c: int64
  218. PaddingBeforeBranchA {.objectconfig.} = object
  219. cause: int8
  220. case kind: MyEnum
  221. of MyEnum.ValueA:
  222. a: int16
  223. of MyEnum.ValueB:
  224. b: int32
  225. of MyEnum.ValueC:
  226. c: int64
  227. PaddingBeforeBranchB {.objectconfig.} = object
  228. cause: int8
  229. case kind: MyEnum
  230. of MyEnum.ValueA:
  231. a: int8
  232. of MyEnum.ValueB:
  233. b: int16
  234. of MyEnum.ValueC:
  235. c: int32
  236. PaddingAfterBranch {.objectconfig.} = object
  237. case kind: MyEnum
  238. of MyEnum.ValueA:
  239. a: int8
  240. of MyEnum.ValueB:
  241. b: int16
  242. of MyEnum.ValueC:
  243. c: int32
  244. cause: int64
  245. RecursiveStuff {.objectconfig.} = object
  246. case kind: MyEnum # packedOffset: 0
  247. of MyEnum.ValueA: # packedOffset:
  248. a: int16 # packedOffset: 1
  249. of MyEnum.ValueB: # packedOffset:
  250. b: int32 # packedOffset: 1
  251. of MyEnum.ValueC: # packedOffset:
  252. case kind2: MyEnum # packedOffset: 1
  253. of MyEnum.ValueA: # packedOffset:
  254. ca1: int8
  255. ca2: int32
  256. of MyEnum.ValueB: # packedOffset:
  257. cb: int32 # packedOffset: 2
  258. of MyEnum.ValueC: # packedOffset:
  259. cc: int64 # packedOffset: 2
  260. d1: int8
  261. d2: int64
  262. Foobar {.objectconfig.} = object
  263. case kind: OtherEnum
  264. of OtherEnum.ValueA:
  265. a: uint8
  266. of OtherEnum.ValueB:
  267. b: int8
  268. c: int8
  269. PaddingOfSetEnum33 {.objectconfig.} = object
  270. cause: int8
  271. theSet: set[MyEnum33]
  272. Bazing {.objectconfig.} = object of RootObj
  273. a: int64
  274. # TODO test on 32 bit system
  275. # only there the object header is smaller than the first member
  276. InheritanceA {.objectconfig.} = object of RootObj
  277. a: char
  278. InheritanceB {.objectconfig.} = object of InheritanceA
  279. b: char
  280. InheritanceC {.objectconfig.} = object of InheritanceB
  281. c: char
  282. # from issue 4763
  283. GenericObject[T] {.objectconfig.} = object
  284. a: int32
  285. b: T
  286. # this type mixes `packed` with `align`.
  287. MyCustomAlignPackedObject {.objectconfig.} = object
  288. a: char
  289. b {.align: 32.}: int32 # align overrides `packed` for this field.
  290. c: char
  291. d: int32 # unaligned
  292. Kind = enum
  293. K1, K2
  294. AnotherEnum = enum
  295. X1, X2, X3
  296. MyObject = object
  297. s: string
  298. case k: Kind
  299. of K1: nil
  300. of K2:
  301. x: float
  302. y: int32
  303. z: AnotherEnum
  304. Stack[N: static int, T: object] = object
  305. pad: array[128 - sizeof(array[N, ptr T]) - sizeof(int) - sizeof(pointer), byte]
  306. stack: array[N, ptr T]
  307. len*: int
  308. rawMem: ptr array[N, T]
  309. Stack2[T: object] = object
  310. pad: array[128 - sizeof(array[sizeof(T), ptr T]), byte]
  311. const trivialSize = sizeof(TrivialType) # needs to be able to evaluate at compile time
  312. proc main(): void =
  313. var t : TrivialType
  314. var a : SimpleAlignment
  315. var b : AlignAtEnd
  316. var c : SimpleBranch
  317. var d : PaddingBeforeBranchA
  318. var e : PaddingBeforeBranchB
  319. var f : PaddingAfterBranch
  320. var g : RecursiveStuff
  321. var ro : RootObj
  322. var go : GenericObject[int64]
  323. var po : PaddingOfSetEnum33
  324. var capo: MyCustomAlignPackedObject
  325. var issue15516: MyObject
  326. var issue12636_1: Stack[5, MyObject]
  327. var issue12636_2: Stack2[MyObject]
  328. var
  329. e1: Enum1
  330. e2: Enum2
  331. e4: Enum4
  332. e8: Enum8
  333. var
  334. eoa: EnumObjectA
  335. eob: EnumObjectB
  336. testAlign(SimpleAlignment)
  337. # sanity check to ensure both branches are actually executed
  338. when usePacked:
  339. doAssert sizeof(SimpleAlignment) == 10
  340. else:
  341. doAssert sizeof(SimpleAlignment) > 10
  342. testSizeAlignOf(t,a,b,c,d,e,f,g,ro,go,po, e1, e2, e4, e8, eoa, eob, capo, issue15516, issue12636_1, issue12636_2)
  343. type
  344. WithBitsize {.objectconfig.} = object
  345. bitfieldA {.bitsize: 16.}: uint32
  346. bitfieldB {.bitsize: 16.}: uint32
  347. var wbs: WithBitsize
  348. testSize(wbs)
  349. testOffsetOf(TrivialType, x)
  350. testOffsetOf(TrivialType, y)
  351. testOffsetOf(TrivialType, z)
  352. testOffsetOf(SimpleAlignment, a)
  353. testOffsetOf(SimpleAlignment, b)
  354. testOffsetOf(SimpleAlignment, c)
  355. testOffsetOf(AlignAtEnd, a)
  356. testOffsetOf(AlignAtEnd, b)
  357. testOffsetOf(AlignAtEnd, c)
  358. testOffsetOf(SimpleBranch, a)
  359. testOffsetOf(SimpleBranch, b)
  360. testOffsetOf(SimpleBranch, c)
  361. testOffsetOf(PaddingBeforeBranchA, cause)
  362. testOffsetOf(PaddingBeforeBranchA, a)
  363. testOffsetOf(PaddingBeforeBranchB, cause)
  364. testOffsetOf(PaddingBeforeBranchB, a)
  365. testOffsetOf(PaddingAfterBranch, a)
  366. testOffsetOf(PaddingAfterBranch, cause)
  367. testOffsetOf(Foobar, c)
  368. testOffsetOf(PaddingOfSetEnum33, cause)
  369. testOffsetOf(PaddingOfSetEnum33, theSet)
  370. testOffsetOf(Bazing, a)
  371. testOffsetOf(InheritanceA, a)
  372. testOffsetOf(InheritanceB, b)
  373. testOffsetOf(InheritanceC, c)
  374. testOffsetOf(EnumObjectA, a)
  375. testOffsetOf(EnumObjectA, b)
  376. testOffsetOf(EnumObjectA, c)
  377. testOffsetOf(EnumObjectA, d)
  378. testOffsetOf(EnumObjectB, a)
  379. testOffsetOf(EnumObjectB, b)
  380. testOffsetOf(EnumObjectB, c)
  381. testOffsetOf(EnumObjectB, d)
  382. testOffsetOf(RecursiveStuff, kind)
  383. testOffsetOf(RecursiveStuff, a)
  384. testOffsetOf(RecursiveStuff, b)
  385. testOffsetOf(RecursiveStuff, kind2)
  386. testOffsetOf(RecursiveStuff, ca1)
  387. testOffsetOf(RecursiveStuff, ca2)
  388. testOffsetOf(RecursiveStuff, cb)
  389. testOffsetOf(RecursiveStuff, cc)
  390. testOffsetOf(RecursiveStuff, d1)
  391. testOffsetOf(RecursiveStuff, d2)
  392. testOffsetOf(MyCustomAlignPackedObject, a)
  393. testOffsetOf(MyCustomAlignPackedObject, b)
  394. testOffsetOf(MyCustomAlignPackedObject, c)
  395. testOffsetOf(MyCustomAlignPackedObject, d)
  396. echo "body executed" # sanity check to ensure this logic isn't skipped entirely
  397. main()
  398. {.emit: """/*TYPESECTION*/
  399. typedef struct{
  400. float a; float b;
  401. } Foo;
  402. """.}
  403. type
  404. Foo {.importc.} = object
  405. Bar = object
  406. b: byte
  407. foo: Foo
  408. assert sizeof(Bar) == 12
  409. # bug #10082
  410. type
  411. A = int8 # change to int16 and get sizeof(C)==6
  412. B = int16
  413. C {.packed.} = object
  414. d {.bitsize: 1.}: A
  415. e {.bitsize: 7.}: A
  416. f {.bitsize: 16.}: B
  417. assert sizeof(C) == 3
  418. type
  419. MixedBitsize {.packed.} = object
  420. a: uint32
  421. b {.bitsize: 8.}: uint8
  422. c {.bitsize: 1.}: uint8
  423. d {.bitsize: 7.}: uint8
  424. e {.bitsize: 16.}: uint16
  425. f: uint32
  426. doAssert sizeof(MixedBitsize) == 12
  427. type
  428. MyUnionType {.union.} = object
  429. a: int32
  430. b: float32
  431. MyCustomAlignUnion {.union.} = object
  432. c: char
  433. a {.align: 32.}: int
  434. MyCustomAlignObject = object
  435. c: char
  436. a {.align: 32.}: int
  437. doAssert sizeof(MyUnionType) == 4
  438. doAssert sizeof(MyCustomAlignUnion) == 32
  439. doAssert alignof(MyCustomAlignUnion) == 32
  440. doAssert sizeof(MyCustomAlignObject) == 64
  441. doAssert alignof(MyCustomAlignObject) == 32
  442. ##########################################
  443. # bug #9794
  444. ##########################################
  445. type
  446. imported_double {.importc: "double".} = object
  447. Pod = object
  448. v* : imported_double
  449. seed*: int32
  450. Pod2 = tuple[v: imported_double, seed: int32]
  451. proc foobar() =
  452. testAlign(Pod)
  453. testSize(Pod)
  454. testAlign(Pod2)
  455. testSize(Pod2)
  456. doAssert sizeof(Pod) == sizeof(Pod2)
  457. doAssert alignof(Pod) == alignof(Pod2)
  458. foobar()
  459. if failed:
  460. quit("FAIL")
  461. else:
  462. echo "OK"
  463. ##########################################
  464. # sizeof macros API
  465. ##########################################
  466. import macros
  467. type
  468. Vec2f = object
  469. x,y: float32
  470. Vec4f = object
  471. x,y,z,w: float32
  472. # this type is constructed to have no platform depended alignment.
  473. ParticleDataA = object
  474. pos, vel: Vec2f
  475. birthday: float32
  476. padding: float32
  477. moreStuff: Vec4f
  478. const expected = [
  479. # name size align offset
  480. ("pos", 8, 4, 0),
  481. ("vel", 8, 4, 8),
  482. ("birthday", 4, 4, 16),
  483. ("padding", 4, 4, 20),
  484. ("moreStuff", 16, 4, 24)
  485. ]
  486. macro typeProcessing(arg: typed): untyped =
  487. let recList = arg.getTypeImpl[2]
  488. recList.expectKind nnkRecList
  489. for i, identDefs in recList:
  490. identDefs.expectKind nnkIdentDefs
  491. identDefs.expectLen 3
  492. let sym = identDefs[0]
  493. sym.expectKind nnkSym
  494. doAssert expected[i][0] == sym.strVal
  495. doAssert expected[i][1] == getSize(sym)
  496. doAssert expected[i][2] == getAlign(sym)
  497. doAssert expected[i][3] == getOffset(sym)
  498. result = newCall(bindSym"echo", newLit("macros api OK"))
  499. proc main() =
  500. var mylocal: ParticleDataA
  501. typeProcessing(mylocal)
  502. main()
  503. # issue #11320 use UncheckedArray
  504. type
  505. Payload = object
  506. something: int8
  507. vals: UncheckedArray[int32]
  508. proc payloadCheck() =
  509. doAssert offsetOf(Payload, vals) == 4
  510. doAssert sizeof(Payload) == 4
  511. payloadCheck()
  512. # offsetof tuple types
  513. type
  514. MyTupleType = tuple
  515. a: float64
  516. b: float64
  517. c: float64
  518. MyOtherTupleType = tuple
  519. a: float64
  520. b: imported_double
  521. c: float64
  522. MyCaseObject = object
  523. val1: imported_double
  524. case kind: bool
  525. of true:
  526. val2,val3: float32
  527. else:
  528. val4,val5: int32
  529. doAssert offsetof(MyTupleType, a) == 0
  530. doAssert offsetof(MyTupleType, b) == 8
  531. doAssert offsetof(MyTupleType, c) == 16
  532. doAssert offsetof(MyOtherTupleType, a) == 0
  533. doAssert offsetof(MyOtherTupleType, b) == 8
  534. # The following expression can only work if the offsetof expression is
  535. # properly forwarded for the C code generator.
  536. doAssert offsetof(MyOtherTupleType, c) == 16
  537. doAssert offsetof(Bar, foo) == 4
  538. doAssert offsetof(MyCaseObject, val1) == 0
  539. doAssert offsetof(MyCaseObject, kind) == 8
  540. doAssert offsetof(MyCaseObject, val2) == 12
  541. doAssert offsetof(MyCaseObject, val3) == 16
  542. doAssert offsetof(MyCaseObject, val4) == 12
  543. doAssert offsetof(MyCaseObject, val5) == 16
  544. template reject(e) =
  545. static: assert(not compiles(e))
  546. reject:
  547. const off1 = offsetof(MyOtherTupleType, c)
  548. reject:
  549. const off2 = offsetof(MyOtherTupleType, b)
  550. reject:
  551. const off3 = offsetof(MyCaseObject, kind)
  552. type
  553. MyPackedCaseObject {.packed.} = object
  554. val1: imported_double
  555. case kind: bool
  556. of true:
  557. val2,val3: float32
  558. else:
  559. val4,val5: int32
  560. # packed case object
  561. doAssert offsetof(MyPackedCaseObject, val1) == 0
  562. doAssert offsetof(MyPackedCaseObject, val2) == 9
  563. doAssert offsetof(MyPackedCaseObject, val3) == 13
  564. doAssert offsetof(MyPackedCaseObject, val4) == 9
  565. doAssert offsetof(MyPackedCaseObject, val5) == 13
  566. reject:
  567. const off5 = offsetof(MyPackedCaseObject, val2)
  568. reject:
  569. const off6 = offsetof(MyPackedCaseObject, val3)
  570. reject:
  571. const off7 = offsetof(MyPackedCaseObject, val4)
  572. reject:
  573. const off8 = offsetof(MyPackedCaseObject, val5)
  574. type
  575. O0 = object
  576. T0 = tuple[]
  577. doAssert sizeof(O0) == 1
  578. doAssert sizeof(T0) == 1
  579. type
  580. # this thing may not have padding bytes at the end
  581. PackedUnion* {.union, packed.} = object
  582. a*: array[11, byte]
  583. b*: int64
  584. doAssert sizeof(PackedUnion) == 11
  585. doAssert alignof(PackedUnion) == 1