Skip to content

Commit 12d9b05

Browse files
committed
Rewrite read_bits_int_{be,le}()
See kaitai-io/kaitai_struct#949
1 parent e308d37 commit 12d9b05

File tree

1 file changed

+31
-24
lines changed

1 file changed

+31
-24
lines changed

lib/kaitai/struct/struct.rb

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -292,28 +292,29 @@ def align_to_byte
292292
end
293293

294294
def read_bits_int_be(n)
295+
res = 0
296+
295297
bits_needed = n - @bits_left
298+
@bits_left = -bits_needed % 8
299+
296300
if bits_needed > 0
297301
# 1 bit => 1 byte
298302
# 8 bits => 1 byte
299303
# 9 bits => 2 bytes
300-
bytes_needed = ((bits_needed - 1) / 8) + 1
304+
bytes_needed = ((bits_needed - 1) / 8) + 1 # `ceil(bits_needed / 8)`
301305
buf = read_bytes(bytes_needed)
302306
buf.each_byte { |byte|
303-
@bits <<= 8
304-
@bits |= byte
305-
@bits_left += 8
307+
res = res << 8 | byte
306308
}
309+
310+
new_bits = res
311+
res = res >> @bits_left | @bits << bits_needed
312+
@bits = new_bits # will be masked at the end of the function
313+
else
314+
res = @bits >> -bits_needed # shift unneeded bits out
307315
end
308316

309-
# raw mask with required number of 1s, starting from lowest bit
310-
mask = (1 << n) - 1
311-
# shift @bits to align the highest bits with the mask & derive reading result
312-
shift_bits = @bits_left - n
313-
res = (@bits >> shift_bits) & mask
314-
# clear top bits that we've just read => AND with 1s
315-
@bits_left -= n
316-
mask = (1 << @bits_left) - 1
317+
mask = (1 << @bits_left) - 1 # `@bits_left` is in range 0..7
317318
@bits &= mask
318319

319320
res
@@ -326,28 +327,34 @@ def read_bits_int(n)
326327
end
327328

328329
def read_bits_int_le(n)
330+
res = 0
329331
bits_needed = n - @bits_left
330-
if bits_needed > 0
332+
333+
if bits_needed > 0 then
331334
# 1 bit => 1 byte
332335
# 8 bits => 1 byte
333336
# 9 bits => 2 bytes
334-
bytes_needed = ((bits_needed - 1) / 8) + 1
337+
bytes_needed = ((bits_needed - 1) / 8) + 1 # `ceil(bits_needed / 8)`
335338
buf = read_bytes(bytes_needed)
339+
i = 0
336340
buf.each_byte { |byte|
337-
@bits |= (byte << @bits_left)
338-
@bits_left += 8
341+
res |= byte << (i * 8)
342+
i += 1
339343
}
344+
345+
new_bits = res >> bits_needed
346+
res = res << @bits_left | @bits
347+
@bits = new_bits
348+
else
349+
res = @bits
350+
@bits >>= n
340351
end
341352

342-
# raw mask with required number of 1s, starting from lowest bit
343-
mask = (1 << n) - 1
344-
# derive reading result
345-
res = @bits & mask
346-
# remove bottom bits that we've just read by shifting
347-
@bits >>= n
348-
@bits_left -= n
353+
@bits_left = -bits_needed % 8
349354

350-
res
355+
mask = (1 << n) - 1 # no problem with this in Ruby (arbitrary precision integers)
356+
res &= mask
357+
return res
351358
end
352359

353360
# @!endgroup

0 commit comments

Comments
 (0)