Skip to content

Commit 729d2e5

Browse files
committed
Rewrite ReadBitsInt{Be,Le}()
See kaitai-io/kaitai_struct#949
1 parent ac9add4 commit 729d2e5

File tree

1 file changed

+42
-34
lines changed

1 file changed

+42
-34
lines changed

KaitaiStream.cs

Lines changed: 42 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ public KaitaiStream(byte[] bytes) : base(new MemoryStream(bytes))
3333
{
3434
}
3535

36-
private ulong Bits = 0;
3736
private int BitsLeft = 0;
37+
private ulong Bits = 0;
3838

3939
static readonly bool IsLittleEndian = BitConverter.IsLittleEndian;
4040

@@ -283,8 +283,8 @@ public double ReadF8le()
283283

284284
public void AlignToByte()
285285
{
286-
Bits = 0;
287286
BitsLeft = 0;
287+
Bits = 0;
288288
}
289289

290290
/// <summary>
@@ -293,30 +293,33 @@ public void AlignToByte()
293293
/// <returns></returns>
294294
public ulong ReadBitsIntBe(int n)
295295
{
296+
ulong res = 0;
297+
296298
int bitsNeeded = n - BitsLeft;
299+
BitsLeft = -bitsNeeded & 7; // `-bitsNeeded mod 8`
300+
297301
if (bitsNeeded > 0)
298302
{
299303
// 1 bit => 1 byte
300304
// 8 bits => 1 byte
301305
// 9 bits => 2 bytes
302-
int bytesNeeded = ((bitsNeeded - 1) / 8) + 1;
306+
int bytesNeeded = ((bitsNeeded - 1) / 8) + 1; // `ceil(bitsNeeded / 8)`
303307
byte[] buf = ReadBytes(bytesNeeded);
304-
for (int i = 0; i < buf.Length; i++)
308+
for (int i = 0; i < bytesNeeded; i++)
305309
{
306-
Bits <<= 8;
307-
Bits |= buf[i];
308-
BitsLeft += 8;
310+
res = res << 8 | buf[i];
309311
}
312+
313+
ulong newBits = res;
314+
res = res >> BitsLeft | Bits << bitsNeeded;
315+
Bits = newBits; // will be masked at the end of the function
316+
}
317+
else
318+
{
319+
res = Bits >> -bitsNeeded; // shift unneeded bits out
310320
}
311321

312-
// raw mask with required number of 1s, starting from lowest bit
313-
ulong mask = GetMaskOnes(n);
314-
// shift "bits" to align the highest bits with the mask & derive reading result
315-
int shiftBits = BitsLeft - n;
316-
ulong res = (Bits >> shiftBits) & mask;
317-
// clear top bits that we've just read => AND with 1s
318-
BitsLeft -= n;
319-
mask = GetMaskOnes(BitsLeft);
322+
ulong mask = (1UL << BitsLeft) - 1; // `BitsLeft` is in range 0..7, so `(1UL << 64)` does not have to be considered
320323
Bits &= mask;
321324

322325
return res;
@@ -334,41 +337,46 @@ public ulong ReadBitsInt(int n)
334337
/// <returns></returns>
335338
public ulong ReadBitsIntLe(int n)
336339
{
340+
ulong res = 0;
337341
int bitsNeeded = n - BitsLeft;
338342

339343
if (bitsNeeded > 0)
340344
{
341345
// 1 bit => 1 byte
342346
// 8 bits => 1 byte
343347
// 9 bits => 2 bytes
344-
int bytesNeeded = ((bitsNeeded - 1) / 8) + 1;
348+
int bytesNeeded = ((bitsNeeded - 1) / 8) + 1; // `ceil(bitsNeeded / 8)`
345349
byte[] buf = ReadBytes(bytesNeeded);
346-
for (int i = 0; i < buf.Length; i++)
350+
for (int i = 0; i < bytesNeeded; i++)
347351
{
348-
ulong v = (ulong)((ulong)buf[i] << BitsLeft);
349-
Bits |= v;
350-
BitsLeft += 8;
352+
res |= ((ulong)buf[i]) << (i * 8);
351353
}
352-
}
353354

354-
// raw mask with required number of 1s, starting from lowest bit
355-
ulong mask = GetMaskOnes(n);
356-
357-
// derive reading result
358-
ulong res = (Bits & mask);
355+
// NB: in C#, bit shift operators on left-hand operand of type `ulong` work
356+
// as if the right-hand operand were subjected to `& 63` (`& 0b11_1111`) (see
357+
// https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/bitwise-and-shift-operators#shift-count-of-the-shift-operators),
358+
// so `res >> 64` is equivalent to `res >> 0` (but we don't want that)
359+
ulong newBits = bitsNeeded < 64 ? res >> bitsNeeded : 0;
360+
res = res << BitsLeft | Bits;
361+
Bits = newBits;
362+
}
363+
else
364+
{
365+
res = Bits;
366+
Bits >>= n;
367+
}
359368

360-
// remove bottom bits that we've just read by shifting
361-
Bits >>= n;
362-
BitsLeft -= n;
369+
BitsLeft = -bitsNeeded & 7; // `-bitsNeeded mod 8`
363370

371+
if (n < 64)
372+
{
373+
ulong mask = (1UL << n) - 1;
374+
res &= mask;
375+
}
376+
// if `n == 64`, do nothing
364377
return res;
365378
}
366379

367-
private static ulong GetMaskOnes(int n)
368-
{
369-
return n == 64 ? 0xffffffffffffffffUL : (1UL << n) - 1;
370-
}
371-
372380
#endregion
373381

374382
#region Byte arrays

0 commit comments

Comments
 (0)