about summary refs log tree commit diff stats
path: root/src/css/lunit.nim
blob: eb06cb32745db81c9f92f9a5c9769324f87fa27e (plain) (blame)
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
# 32-bit fixed-point number, with 6 bits of precision.

type LUnit* = distinct int32

{.push overflowChecks: off, rangeChecks: off.}
template satlu(a: int64): LUnit =
  if unlikely(a < int32.low):
    LUnit.low
  elif unlikely(a > int32.high):
    LUnit.high
  else:
    LUnit(a)

when sizeof(int) == 4 and not defined(nimEmulateOverflowChecks) and
    (defined(gcc) or defined(clang)):
  func nimAddInt(a, b: int; res: ptr int): bool {.importc, nodecl.}
  func nimSubInt(a, b: int; res: ptr int): bool {.importc, nodecl.}

  func `+`*(a, b: LUnit): LUnit {.inline.} =
    let a = int(a)
    let b = int(b)
    var res {.noinit.}: int
    if nimAddInt(a, b, addr res):
      if a > 0:
        return LUnit.high
      return LUnit.low
    return LUnit(res)

  func `-`*(a, b: LUnit): LUnit {.inline.} =
    let a = int(a)
    let b = int(b)
    var res {.noinit.}: int
    if nimSubInt(a, b, addr res):
      if b < 0:
        return LUnit.high
      return LUnit.low
    return LUnit(res)
else:
  when sizeof(int) == 4:
    {.warning: """Using 64-bit lunit ops on a 32-bit arch.
If you are using GCC/clang, report this at https://todo.sr.ht/~bptato/chawan""".}

  func `+`*(a, b: LUnit): LUnit {.inline.} =
    let ab = int64(a) + int64(b)
    return satlu(ab)

  func `-`*(a, b: LUnit): LUnit {.inline.} =
    let ab = int64(a) - int64(b)
    return satlu(ab)

func `*`*(a, b: LUnit): LUnit {.inline.} =
  let ab = (int64(a) * int64(b)) shr 6
  return satlu(ab)

func `div`*(a, b: LUnit): LUnit {.inline.} =
  let a = int64(uint64(a) shl 12)
  let b = int64(b)
  return LUnit((a div b) shr 6)

converter toLUnit*(a: int32): LUnit =
  let a = int64(a) shl 6
  return satlu(a)

converter toLUnit*(a: int): LUnit =
  let a = int64(a) shl 6
  return satlu(a)

func `-`*(a: LUnit): LUnit {.inline.} =
  let a = int32(a)
  if unlikely(a == int32.high):
    return LUnit.low
  return LUnit(-a)
{.pop.} # overflowChecks, rangeChecks

func `==`*(a, b: LUnit): bool {.borrow.}
func `<`*(a, b: LUnit): bool {.borrow.}
func `<=`*(a, b: LUnit): bool {.borrow.}

func toInt*(a: LUnit): int =
  if a < 0:
    return -(int32(-a) shr 6)
  return int32(a) shr 6

func `+=`*(a: var LUnit; b: LUnit) {.inline.} =
  a = a + b

func `-=`*(a: var LUnit; b: LUnit) {.inline.} =
  a = a - b

func `*=`*(a: var LUnit; b: LUnit) {.inline.} =
  a = a * b

func toLUnit*(a: float32): LUnit =
  let a = a * 64
  if unlikely(a == Inf):
    return LUnit(high(int32))
  elif unlikely(a == -Inf):
    return LUnit(low(int32))
  return LUnit(int32(a))

func toFloat32*(a: LUnit): float64 =
  return float32(int32(a)) / 64

func `$`*(a: LUnit): string =
  $toFloat32(a)

func min*(a, b: LUnit): LUnit {.borrow.}
func max*(a, b: LUnit): LUnit {.borrow.}

func ceilTo*(a: LUnit; prec: int): LUnit =
  return (1 + ((a - 1) div prec).toInt) * prec