2019-07-21 23:48:03 +00:00
|
|
|
import
|
|
|
|
bitops2
|
|
|
|
|
|
|
|
type
|
|
|
|
Bytes = seq[byte]
|
|
|
|
BitSeq* = distinct Bytes
|
|
|
|
|
|
|
|
BitArray*[bits: static int] = object
|
|
|
|
bytes*: array[(bits + 7) div 8, byte]
|
|
|
|
|
2019-07-25 15:54:23 +00:00
|
|
|
func len*(s: BitSeq): int =
|
2019-07-21 23:48:03 +00:00
|
|
|
let
|
|
|
|
bytesCount = s.Bytes.len
|
|
|
|
lastByte = s.Bytes[bytesCount - 1]
|
|
|
|
markerPos = log2trunc(lastByte)
|
|
|
|
|
|
|
|
Bytes(s).len * 8 - (8 - markerPos)
|
|
|
|
|
2019-07-25 15:54:23 +00:00
|
|
|
template len*(a: BitArray): int =
|
|
|
|
a.bits
|
|
|
|
|
2019-07-21 23:48:03 +00:00
|
|
|
template bytes*(s: BitSeq): untyped =
|
|
|
|
Bytes(s)
|
|
|
|
|
2019-07-25 15:54:23 +00:00
|
|
|
func add*(s: var BitSeq, value: bool) =
|
2019-07-21 23:48:03 +00:00
|
|
|
let
|
|
|
|
lastBytePos = s.Bytes.len - 1
|
|
|
|
lastByte = s.Bytes[lastBytePos]
|
|
|
|
|
|
|
|
if (lastByte and byte(128)) == 0:
|
|
|
|
# There is at least one leading zero, so we have enough
|
|
|
|
# room to store the new bit
|
|
|
|
let markerPos = log2trunc(lastByte)
|
|
|
|
s.Bytes[lastBytePos].setBit markerPos, value
|
|
|
|
s.Bytes[lastBytePos].raiseBit markerPos + 1
|
|
|
|
else:
|
|
|
|
s.Bytes[lastBytePos].setBit 7, value
|
|
|
|
s.Bytes.add byte(1)
|
|
|
|
|
2019-07-25 15:54:23 +00:00
|
|
|
func `[]`*(s: BitSeq, pos: Natural): bool {.inline.} =
|
2019-07-21 23:48:03 +00:00
|
|
|
doAssert pos < s.len
|
|
|
|
s.Bytes.getBit pos
|
|
|
|
|
2019-07-25 15:54:23 +00:00
|
|
|
func `[]=`*(s: var BitSeq, pos: Natural, value: bool) {.inline.} =
|
2019-07-21 23:48:03 +00:00
|
|
|
doAssert pos < s.len
|
|
|
|
s.Bytes.setBit pos, value
|
|
|
|
|
2019-07-25 15:54:23 +00:00
|
|
|
func raiseBit*(s: var BitSeq, pos: Natural) {.inline.} =
|
2019-07-21 23:48:03 +00:00
|
|
|
doAssert pos < s.len
|
|
|
|
raiseBit s.Bytes, pos
|
|
|
|
|
2019-07-25 15:54:23 +00:00
|
|
|
func lowerBit*(s: var BitSeq, pos: Natural) {.inline.} =
|
2019-07-21 23:48:03 +00:00
|
|
|
doAssert pos < s.len
|
|
|
|
lowerBit s.Bytes, pos
|
|
|
|
|
2019-07-25 15:54:23 +00:00
|
|
|
func init*(T: type BitSeq, len: int): T =
|
2019-07-21 23:48:03 +00:00
|
|
|
result = BitSeq newSeq[byte](1 + len div 8)
|
|
|
|
Bytes(result).raiseBit len
|
|
|
|
|
2019-07-25 15:54:23 +00:00
|
|
|
func init*(T: type BitArray): T =
|
2019-07-21 23:48:03 +00:00
|
|
|
# The default zero-initializatio is fine
|
|
|
|
discard
|
|
|
|
|
|
|
|
template `[]`*(a: BitArray, pos: Natural): bool =
|
|
|
|
getBit a.bytes, pos
|
|
|
|
|
|
|
|
template `[]=`*(a: var BitArray, pos: Natural, value: bool) =
|
|
|
|
setBit a.bytes, pos, value
|
|
|
|
|
|
|
|
template raiseBit*(a: var BitArray, pos: Natural) =
|
|
|
|
raiseBit a.bytes, pos
|
|
|
|
|
|
|
|
template lowerBit*(a: var BitArray, pos: Natural) =
|
|
|
|
lowerBit a.bytes, pos
|
|
|
|
|
|
|
|
# TODO: Submit this to the standard library as `cmp`
|
|
|
|
# At the moment, it doesn't work quite well because Nim selects
|
|
|
|
# the generic cmp[T] from the system module instead of choosing
|
|
|
|
# the openarray overload
|
2019-07-25 15:54:23 +00:00
|
|
|
func compareArrays[T](a, b: openarray[T]): int =
|
2019-07-21 23:48:03 +00:00
|
|
|
result = cmp(a.len, b.len)
|
|
|
|
if result != 0: return
|
|
|
|
|
|
|
|
for i in 0 ..< a.len:
|
|
|
|
result = cmp(a[i], b[i])
|
|
|
|
if result != 0: return
|
|
|
|
|
|
|
|
template cmp*(a, b: BitSeq): int =
|
|
|
|
compareArrays(Bytes a, Bytes b)
|
|
|
|
|
|
|
|
template `==`*(a, b: BitSeq): bool =
|
|
|
|
cmp(a, b) == 0
|
|
|
|
|
2019-07-25 15:54:23 +00:00
|
|
|
func `$`*(a: BitSeq): string =
|
|
|
|
let length = a.len
|
|
|
|
result = newStringOfCap(2 + length)
|
|
|
|
result.add "0b"
|
|
|
|
for i in 0 ..< length:
|
|
|
|
result.add if a[i]: '1' else: '0'
|
|
|
|
|