// Do I need array offsets/lengths in the public API? Might get a little hard to keep track of positions. // I can add them later if needed but I'll have to revisit the logic here because it's almost certainly wrong. // Alternate note: This function assumes it's being passed a big-endian bit arrays private void WriteInternal(int bitCount, byte[] data, int?offset, int?length) { offset = offset ?? 0; length = (length ?? data.Length) - offset.GetValueOrDefault() - 1; if (BitConverter.IsLittleEndian) { Array.Reverse(data); } int bytePos = ParseBitCountAndExpandStreamAsNeeded(bitCount); int srcBytePos = offset.GetValueOrDefault() + length.GetValueOrDefault(); int srcBitPos = 1; int consumedBits = 0; while (consumedBits < bitCount) { int bitsToConsume = Math.Min(bitCount - consumedBits, Constants.ByteSizeInBits); byte rawValue = (byte)(data[srcBytePos] & PackedBitMasks.GetNarrowingMask(bitsToConsume)); int remainingBits = Constants.ByteSizeInBits - (BitPos - 1); // Extract only the bits we need for the current byte // Assuming we have more bits than our current byte boundary, we have to apply some bits to the next byte if (bitsToConsume > remainingBits) { InternalStream[bytePos++] |= (byte)((byte)(rawValue >> (bitsToConsume - remainingBits)) & PackedBitMasks.GetNarrowingMask(remainingBits)); BitPos = 1; remainingBits = bitsToConsume - remainingBits; InternalStream[bytePos] |= (byte)(rawValue << (Constants.ByteSizeInBits - remainingBits)); BitPos += remainingBits; ForceAddByte = false; } else { InternalStream[bytePos] |= (byte)(rawValue << (remainingBits - bitsToConsume)); BitPos += bitsToConsume; if (BitPos > Constants.ByteSizeInBits) { BitPos = 1; bytePos++; // If the bits are directly on the border of a byte boundary (e.g. packed 32 bits) // Then we must indicate to the expansion function that it must add another byte // Because it uses the position in the current byte to determine how many are needed // But only if we end on this byte ForceAddByte = true; } else { ForceAddByte = false; } } srcBitPos += bitsToConsume; if (srcBitPos > Constants.ByteSizeInBits) { srcBitPos = 1; srcBytePos--; } consumedBits += bitsToConsume; } }
private bool TryReadInternal(int bitCount, out byte[] data, int typeBytes) { if (bitCount > RemainingBits) { data = null; return(false); } data = new byte[typeBytes]; int destBytePos = data.Length - 1; int destBitPos = 1; int consumedBits = 0; while (consumedBits < bitCount) { int bitsToConsume = Math.Min(bitCount - consumedBits, Constants.ByteSizeInBits); int remainingBits = Constants.ByteSizeInBits - (BitPos - 1); int attemptConsumeBits = Math.Min(bitsToConsume, remainingBits); byte rawValue = (byte)(InternalStream[BytePos] & PackedBitMasks.GetWideningMask(attemptConsumeBits, BitPos - 1)); BitPos += attemptConsumeBits; if (BitPos > Constants.ByteSizeInBits) { BitPos = 1; BytePos++; } if (bitsToConsume > attemptConsumeBits) { data[destBytePos] |= (byte)(rawValue << (bitsToConsume - attemptConsumeBits)); destBitPos += attemptConsumeBits; if (destBitPos > Constants.ByteSizeInBits) { destBitPos = 1; destBytePos--; } remainingBits = bitsToConsume - attemptConsumeBits; rawValue = (byte)(InternalStream[BytePos] & PackedBitMasks.GetWideningMask(remainingBits, BitPos - 1)); data[destBytePos] |= (byte)(rawValue >> (Constants.ByteSizeInBits - remainingBits)); destBitPos += remainingBits; if (destBitPos > Constants.ByteSizeInBits) { destBitPos = 1; destBytePos--; } BitPos += remainingBits; if (BitPos > Constants.ByteSizeInBits) { BitPos = 1; BytePos++; } } else { data[destBytePos] |= (byte)(rawValue >> (remainingBits - bitsToConsume)); destBitPos += bitsToConsume; if (destBitPos > Constants.ByteSizeInBits) { destBitPos = 1; destBytePos--; } } consumedBits += bitsToConsume; } if (BitConverter.IsLittleEndian) { Array.Reverse(data); } return(true); }