diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/bitreader.nim | 52 | ||||
-rw-r--r-- | src/integers.nim | 7 |
2 files changed, 23 insertions, 36 deletions
diff --git a/src/bitreader.nim b/src/bitreader.nim index 757c1b3..7afb13d 100644 --- a/src/bitreader.nim +++ b/src/bitreader.nim | |||
@@ -17,49 +17,33 @@ | |||
17 | import streams | 17 | import streams |
18 | import integers | 18 | import integers |
19 | 19 | ||
20 | # Stream functions | ||
21 | |||
22 | proc newEIO(msg: string): ref IOError = | ||
23 | new(result) | ||
24 | result.msg = msg | ||
25 | |||
26 | proc read[T](s: Stream, t: typedesc[T]): T = | ||
27 | if readData(s, addr(result), sizeof(T)) != sizeof(T): | ||
28 | raise newEIO("cannot read from stream") | ||
29 | |||
30 | proc peek[T](s: Stream, t: typedesc[T]): T = | ||
31 | if peekData(s, addr(result), sizeof(T)) != sizeof(T): | ||
32 | raise newEIO("cannot read from stream") | ||
33 | |||
34 | # BitReader | ||
35 | |||
36 | type BitReader* = ref object | 20 | type BitReader* = ref object |
37 | stream: Stream | 21 | stream: Stream |
38 | bitOffset: int | 22 | bitOffset: int |
23 | overflowBuffer: uint8 | ||
39 | 24 | ||
40 | proc bitReader*(stream: Stream): BitReader = | 25 | proc bitReader*(stream: Stream): BitReader = |
41 | BitReader(stream: stream, bitOffset: 0) | 26 | BitReader(stream: stream, bitOffset: 0, overflowBuffer: 0) |
42 | 27 | ||
43 | proc atEnd*(bitReader: BitReader): bool = | 28 | proc atEnd*(bitReader: BitReader): bool = |
44 | bitReader.stream.atEnd() | 29 | bitReader.bitOffset == 0 and bitReader.stream.atEnd() |
45 | 30 | ||
46 | proc readBits*[T: SomeUnsignedInt](bitReader: BitReader, bits: int, to: typedesc[T]): T = | 31 | proc readBits*[T: SomeUnsignedInt](bitReader: BitReader, bits: int, to: typedesc[T]): T = |
47 | let targetBitLength = sizeof(T) * wordBitLength | 32 | if bits < 0 or bits > sizeof(T) * wordBitLength: raise newException(RangeError, "invalid bit length") |
48 | if bits < 0 or bits > targetBitLength: | 33 | if bits == 0: return 0 |
49 | raise newException(RangeError, "invalid bit length") | 34 | var bitsRead = 0 |
50 | elif bits == 0: | 35 | if bitReader.bitOffset > 0: |
51 | result = 0 | 36 | let bitsFromBuffer = min(bits, wordBitLength - bitReader.bitOffset) |
52 | elif bits < targetBitLength - bitReader.bitOffset: | 37 | result = (bitReader.overflowBuffer shr bitReader.bitOffset).leastSignificantBits(bitsFromBuffer) |
53 | result = bitReader.stream.peek(T) shl (targetBitLength - bits - bitReader.bitOffset) shr (targetBitLength - bits) | 38 | bitReader.bitOffset = (bitReader.bitOffset + bitsFromBuffer) mod wordBitLength |
54 | elif bits == targetBitLength - bitReader.bitOffset: | 39 | bitsRead += bitsFromBuffer |
55 | result = bitReader.stream.read(T) shl (targetBitLength - bits - bitReader.bitOffset) shr (targetBitLength - bits) | 40 | while bits - bitsRead >= wordBitLength: |
56 | else: | 41 | result = result or (bitReader.stream.readUint8().T shl bitsRead) |
57 | let rightBits = targetBitLength - bitReader.bitOffset | 42 | bitsRead += wordBitLength |
58 | let leftBits = bits - rightBits | 43 | if bits - bitsRead > 0: |
59 | let right = bitReader.stream.read(T) shr bitReader.bitOffset | 44 | bitReader.overflowBuffer = bitReader.stream.readUint8() |
60 | let left = bitReader.stream.peek(T) shl (targetBitLength - leftBits) shr (targetBitLength - bits) | 45 | bitReader.bitOffset = bits - bitsRead |
61 | result = left or right | 46 | result = result or (bitReader.overflowBuffer.leastSignificantBits(bitReader.bitOffset).T shl bitsRead) |
62 | bitReader.bitOffset = (bitReader.bitOffset + bits) mod wordBitLength | ||
63 | 47 | ||
64 | proc readBool*(bitReader: BitReader): bool = | 48 | proc readBool*(bitReader: BitReader): bool = |
65 | bitReader.readBits(1, uint8) != 0 | 49 | bitReader.readBits(1, uint8) != 0 |
diff --git a/src/integers.nim b/src/integers.nim index fddbfdc..7b0f166 100644 --- a/src/integers.nim +++ b/src/integers.nim | |||
@@ -15,13 +15,16 @@ | |||
15 | # along with this program. If not, see <https://www.gnu.org/licenses/>. | 15 | # along with this program. If not, see <https://www.gnu.org/licenses/>. |
16 | 16 | ||
17 | const wordBitLength* = 8 | 17 | const wordBitLength* = 8 |
18 | const wordBitMask* = 0b1111_1111'u8 | ||
19 | 18 | ||
20 | proc `/^`*[T: Natural](x, y: T): T = | 19 | proc `/^`*[T: Natural](x, y: T): T = |
21 | (x + y - 1) div y | 20 | (x + y - 1) div y |
22 | 21 | ||
23 | proc truncateToUint8*(x: SomeUnsignedInt): uint8 = | 22 | proc truncateToUint8*(x: SomeUnsignedInt): uint8 = |
24 | (x and wordBitMask).uint8 | 23 | (x and uint8.high).uint8 |
24 | |||
25 | proc leastSignificantBits*[T: SomeUnsignedInt](x: T, bits: int): T = | ||
26 | let maskOffset = sizeof(T) * wordBitLength - bits | ||
27 | if maskOffset >= 0: (x shl maskOffset) shr maskOffset else: x | ||
25 | 28 | ||
26 | iterator chunks*(totalBitLength: int, chunkType: typedesc[SomeInteger]): tuple[index: int, chunkBitLength: int] = | 29 | iterator chunks*(totalBitLength: int, chunkType: typedesc[SomeInteger]): tuple[index: int, chunkBitLength: int] = |
27 | let chunkBitLength = sizeof(chunkType) * wordBitLength | 30 | let chunkBitLength = sizeof(chunkType) * wordBitLength |