-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathdatatypes.nim
155 lines (129 loc) · 5.21 KB
/
datatypes.nim
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# Stint
# Copyright 2018 Status Research & Development GmbH
# Licensed under either of
#
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
#
# at your option. This file may not be copied, modified, or distributed except according to those terms.
# TODO: test if GCC/Clang support uint128 natively
import macros
# The macro uintImpl must be exported
when defined(mpint_test):
macro stintImpl*(bits: static[int], typeImpl: untyped): untyped =
# Test version, StUint[64] = 2 uint32. Test the logic of the library
assert (bits and (bits-1)) == 0, $bits & " is not a power of 2"
assert bits >= 16, "The number of bits in a should be greater or equal to 16"
if bits >= 32:
let nb_words = bits div 32
result = quote do: `typeImpl`[`nb_words`, uint32]
elif bits == 16:
result = quote do: `typeImpl`[1, uint16]
elif bits == 8:
result = quote do: `typeImpl`[1, uint8]
else:
error "Fatal: unreachable"
else:
macro stintImpl*(bits: static[int], typeImpl: untyped): untyped =
# Release version, StUint[64] = uint64.
assert (bits and (bits-1)) == 0, $bits & " is not a power of 2"
assert bits >= 8, "The number of bits in a should be greater or equal to 8"
if bits >= 64:
let nb_words = bits div 64
result = quote do: `typeImpl`[`nb_words`, uint64]
elif bits == 32:
result = quote do: `typeImpl`[1, uint32]
elif bits == 16:
result = quote do: `typeImpl`[1, uint16]
elif bits == 8:
result = quote do: `typeImpl`[1, uint8]
else:
error "Fatal: unreachable"
type
# ### Private ### #
UintImpl*[N: static[int], T: SomeUnsignedInt] = object
raw_data*: array[N, T]
IntImpl*[N: static[int], T: SomeUnsignedInt] = object
raw_data*: array[N, T]
# ### Private ### #
StUint*[bits: static[int]] = object
data*: stintImpl(bits, UintImpl)
StInt*[bits: static[int]] = object
data*: stintImpl(bits, IntImpl)
AnyImpl*[N: static[int], T: SomeUnsignedInt] = UintImpl[N, T] or IntImpl[N, T]
# #################################
func getSize*[N: static[int], T: SomeUnsignedInt](x: AnyImpl[N,T]): static[int] =
## Get size of int or uint implementation in bits
N * T.sizeof * 8
# ###### lo and hi accessors ######
# macro used as workaround because template crashes - https://github.com/nim-lang/Nim/issues/8052
macro loImpl(dst, src: untyped, N: static[int]): untyped =
assert N >= 2
result = quote do:
const halfSize = `N` div 2
when cpuEndian == littleEndian:
for i in 0 ..< halfSize:
{.unroll.}
`dst`.raw_data[i] = `src`.raw_data[i]
else:
for i in halfSize ..< `N`:
{.unroll.}
`dst`.raw_data[i] = `src`.raw_data[i]
macro hiImpl(dst, src: untyped, N: static[int]): untyped =
assert N >= 2
result = quote do:
const halfSize = `N` div 2
when cpuEndian == littleEndian:
for i in halfSize ..< `N`:
{.unroll.}
`dst`.raw_data[i] = `src`.raw_data[i]
else:
for i in 0 ..< halfSize:
{.unroll.}
`dst`.raw_data[i] = `src`.raw_data[i]
proc loProc[N: static[int], T: SomeUnsignedInt](x: AnyImpl[N, T]): AnyImpl[N div 2, T] {.inline.}=
loImpl(result, x, N)
proc hiProc[N: static[int], T: SomeUnsignedInt](x: AnyImpl[N, T]): AnyImpl[N div 2, T] {.inline.}=
hiImpl(result, x, N)
proc loProc[N: static[int], T: SomeUnsignedInt](x: var AnyImpl[N, T]): var AnyImpl[N div 2, T] {.inline.}=
loImpl(result, x, N)
proc hiProc[N: static[int], T: SomeUnsignedInt](x: var AnyImpl[N, T]): var AnyImpl[N div 2, T] {.inline.}=
hiImpl(result, x, N)
macro lo*[N: static[int], T: SomeUnsignedInt](x: AnyImpl[N,T]): untyped=
## Get the low part of an unsigned integer
# TODO: as this is called extensively and it is not a field
# we should make sure repeated calls are optimized away by the compiler
if N == 1:
result = quote do: `x`.raw_data[0]
elif N > 1:
let loProc = bindSym"loProc"
result = quote do: `loProc`(`x`)
else:
result = quote do: {.fatal: "Unreachable".}
macro `lo=`*[N: static[int], T: SomeUnsignedInt](dst: var AnyImpl[N,T], src: AnyImpl[N div 2, T]): untyped =
## Get the mutable part of an unsigned integer
if N == 1:
result = quote do: `dst`.raw_data[0] = `src`.raw_data[0]
elif N > 1:
result = getAST(loImpl(dst, src, N))
else:
result = quote do: {.fatal: "Unreachable".}
macro hi*[N: static[int], T: SomeUnsignedInt](x: AnyImpl[N,T]): untyped =
## Get the high part of an unsigned integer
# TODO: as this is called extensively and it is not a field
# we should make sure repeated calls are optimized away by the compiler
if N == 1:
result = quote do: `x`.raw_data[0]
elif N > 1:
let hiProc = bindSym"hiProc"
result = quote do: `hiProc`(`x`)
else:
result = quote do: {.fatal: "Unreachable".}
macro `hi=`*[N: static[int], T: SomeUnsignedInt](dst: var AnyImpl[N,T], src: AnyImpl[N div 2, T]): untyped =
## Get the high part of an unsigned integer
if N == 1:
result = quote do: `dst`.raw_data[0] = `src`.raw_data[0]
elif N > 1:
result = getAST(hiImpl(dst, src, N))
else:
result = quote do: {.fatal: "Unreachable".}