mirror of
https://github.com/status-im/nim-stew.git
synced 2025-01-09 11:45:42 +00:00
134 lines
5.0 KiB
Nim
134 lines
5.0 KiB
Nim
## Core integer primitives suitable as building blocks for higher-level
|
|
## functionality such as bigints, saturating integer types etc - where
|
|
## applicable, these use compiler builtins - otherwise, they fall back on native
|
|
## Nim code that may be less efficient.
|
|
##
|
|
## In using these functions, it is recommended that you always call the function
|
|
## that returns the least information needed - for example, `mulOverflow` may
|
|
## be implemented more efficiently than `mulWiden`, meaning that if overflow
|
|
## detection is all that is needed, use the former.
|
|
##
|
|
## The API strives to map functions to platform-specific CPU instructions
|
|
## via compiler intrinsics or other compiler/target-specific implementations.
|
|
## Where this is not possible, the API instead emulates the instructions - such
|
|
## emulation may result in the loss of properies important to some applications
|
|
## such as constant-time:ness, atomicity or performance.
|
|
|
|
# Implementation notes:
|
|
#
|
|
# * `uintN` is assumed to be wrapping
|
|
# * "*Overflow" perform wrapping arithmetic while returning a bool for overflow
|
|
# * "*Widen" return full result in multiple words
|
|
# * overloads with carry/borrow exposed for chaining limbs
|
|
#
|
|
# TODO
|
|
# * use compiler intrinsics
|
|
# * signed ops
|
|
# * saturating ops
|
|
# * more primitives commonly available on CPU:s / intrinsics (pow / divmod / etc)
|
|
# * discovery mechanism to determine implementation quality
|
|
#
|
|
# References:
|
|
# https://llvm.org/docs/LangRef.html#arithmetic-with-overflow-intrinsics
|
|
# https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html
|
|
# https://doc.rust-lang.org/std/primitive.u32.html#implementations
|
|
|
|
func addOverflow*(x, y: SomeUnsignedInt):
|
|
tuple[result: SomeUnsignedInt, overflow: bool] =
|
|
## Add the two integers using wrapping arithmetic, returning the result and a
|
|
## boolean indicating that overflow happened.
|
|
##
|
|
## When used to construct bigint arithmetic, the overflow flag can be passed
|
|
## as carry to the next more significant word.
|
|
|
|
let r = x + y
|
|
(r, r < x)
|
|
|
|
func addOverflow*(x, y: SomeUnsignedInt, carry: bool):
|
|
tuple[result: SomeUnsignedInt, overflow: bool] =
|
|
## Add two integers and carry using wrapping arithmetic, returning the
|
|
## result and a boolean indicating that overflow happened.
|
|
##
|
|
## When used to construct bigint arithmetic, the overflow flag can be passed
|
|
## as carry to the next more significant word.
|
|
|
|
let
|
|
(a, b) = addOverflow(x, y)
|
|
(c, d) = addOverflow(a, typeof(a)(carry))
|
|
(c, b or d)
|
|
|
|
func subOverflow*(x, y: SomeUnsignedInt):
|
|
tuple[result: SomeUnsignedInt, overflow: bool] =
|
|
## Subtract y and borrow from x using wrapping arithmetic, returning the
|
|
## result and a boolean indicating whether overflow happened.
|
|
|
|
let r = x - y
|
|
(r, y > x)
|
|
|
|
func subOverflow*(x, y: SomeUnsignedInt, borrow: bool):
|
|
tuple[result: SomeUnsignedInt, overflow: bool] =
|
|
## Subtract y and borrow from x using wrapping arithmetic, returning the
|
|
## result and a boolean indicating whether overflow happened.
|
|
##
|
|
## When used to construct bigint arithmetic, the overflow flag can be passed
|
|
## as carry to the next more significant word.
|
|
|
|
let
|
|
(a, b) = subOverflow(x, y)
|
|
(c, d) = subOverflow(a, typeof(a)(borrow))
|
|
(c, b or d)
|
|
|
|
func mulWiden*(x, y: uint64): tuple[lo, hi: uint64] =
|
|
let
|
|
x0 = x and uint32.high
|
|
x1 = x shr 32
|
|
y0 = y and uint32.high
|
|
y1 = y shr 32
|
|
p11 = x1 * y1
|
|
p01 = x0 * y1
|
|
p10 = x1 * y0
|
|
p00 = x0 * y0
|
|
middle = p10 + (p00 shr 32) + (p01 and uint32.high)
|
|
rhi = p11 + (middle shr 32) + (p01 shr 32)
|
|
rlo = (middle shl 32) or (p00 and uint32.high)
|
|
|
|
(rlo, rhi)
|
|
|
|
func mulWiden*(x, y: uint32): tuple[lo, hi: uint32] =
|
|
let r = x.uint64 * y.uint64
|
|
(cast[uint32](r and uint32.high), cast[uint32](r shr 32))
|
|
func mulWiden*(x, y: uint16): tuple[lo, hi: uint16] =
|
|
let r = x.uint32 * y.uint32
|
|
(cast[uint16](r and uint16.high), cast[uint16](r shr 16))
|
|
func mulWiden*(x, y: uint8): tuple[lo, hi: uint8] =
|
|
let r = x.uint16 * y.uint16
|
|
(cast[uint8](r and uint8.high), cast[uint8](r shr 8))
|
|
func mulWiden*(x, y: uint): tuple[lo, hi: uint] =
|
|
## Perform `(x * y)` as if the computiation had been carried out in twice as
|
|
## wide a type returning the low and high words.
|
|
when sizeof(uint) == sizeof(uint64):
|
|
let (a, b) = mulWiden(uint64(x), uint64(y))
|
|
else:
|
|
let (a, b) = mulWiden(uint32(x), uint32(y))
|
|
(uint(a), uint(b))
|
|
|
|
func mulWiden*(x, y, carry: SomeUnsignedInt): tuple[lo, hi: SomeUnsignedInt] =
|
|
## Perform `((x * y) + carry)` as if the computiation had been carried out in
|
|
## twice as wide a type returning the low and high words
|
|
let
|
|
(lo, hi) = mulWiden(x, y)
|
|
(a, b) = addOverflow(lo, carry)
|
|
# The carry from this overflowing add can be ignored since the result of
|
|
# a multiplication always leaves room for adding one more `high`
|
|
(c, _) = addOverflow(hi, typeof(hi)(0), b)
|
|
|
|
(a, c)
|
|
|
|
func mulOverflow*(x, y: SomeUnsignedInt):
|
|
tuple[result: SomeUnsignedInt, overflow: bool] =
|
|
## Perform `(x * y)` using wrapping arithmetic, returning the result and a
|
|
## boolean indicating that overflow happened.
|
|
let
|
|
(a, b) = mulWiden(x, y)
|
|
(a, b > 0)
|