File: Shared\MS\Internal\Ink\BitStream.cs
Project: wpf\src\PresentationCore.csproj (PresentationCore)
using MS.Utility;
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Windows;
using System.Diagnostics;
 
using SR=MS.Internal.PresentationCore.SR;
using SRID=MS.Internal.PresentationCore.SRID;
 
namespace MS.Internal.Ink
{
    /// <summary>
    /// A stream-style reader for retrieving packed bits from a byte array
    /// </summary>
    /// <remarks>This bits should packed into the leftmost position in each byte.
    /// For compatibility purposes with the v1 ISF encoder and decoder, the order of the
    /// packing must not be changed. This code is a from-scratch rewrite of the BitStream
    /// natice C++ class in the v1 Ink code, but still maintaining the same packing
    /// behavior.</remarks>
    internal class BitStreamReader
    {
        /// <summary>
        /// Create a new BitStreamReader to unpack the bits in a buffer of bytes
        /// </summary>
        /// <param name="buffer">Buffer of bytes</param>
        internal BitStreamReader(byte[] buffer)
        {
            Debug.Assert(buffer != null);
 
            _byteArray = buffer;
            _bufferLengthInBits = (uint)buffer.Length * (uint)Native.BitsPerByte;
        }
 
        /// <summary>
        /// Create a new BitStreamReader to unpack the bits in a buffer of bytes
        /// </summary>
        /// <param name="buffer">Buffer of bytes</param>
        /// <param name="startIndex">The index to start reading at</param>
        internal BitStreamReader(byte[] buffer, int startIndex)
        {
            Debug.Assert(buffer != null);
 
            if (startIndex < 0 || startIndex >= buffer.Length)
            {
                throw new ArgumentOutOfRangeException("startIndex");
            }
            _byteArray = buffer;
            _byteArrayIndex = startIndex;
            _bufferLengthInBits = (uint)(buffer.Length - startIndex) * (uint)Native.BitsPerByte;
        }
 
        /// <summary>
        /// Create a new BitStreamReader to unpack the bits in a buffer of bytes
        /// and enforce a maximum buffer read length
        /// </summary>
        /// <param name="buffer">Buffer of bytes</param>
        /// <param name="bufferLengthInBits">Maximum number of bytes to read from the buffer</param>
        internal BitStreamReader(byte[] buffer, uint bufferLengthInBits)
            : this(buffer)
        {
            if (bufferLengthInBits > (buffer.Length * Native.BitsPerByte))
            {
                throw new ArgumentOutOfRangeException("bufferLengthInBits", SR.Get(SRID.InvalidBufferLength));
            }
 
            _bufferLengthInBits = bufferLengthInBits;
        }
 
        /// <summary>
        /// Read a specified number of bits from the stream into a long
        /// </summary>
        internal long ReadUInt64(int countOfBits)
        {
            // we only support 1-64 bits currently, not multiple bytes, and not 0 bits
            if (countOfBits > Native.BitsPerLong || countOfBits <= 0)
            {
                throw new ArgumentOutOfRangeException("countOfBits", countOfBits, SR.Get(SRID.CountOfBitsOutOfRange));
            }
            long retVal = 0;
            while (countOfBits > 0)
            {
                int countToRead = (int)Native.BitsPerByte;
                if (countOfBits < 8)
                {
                    countToRead = countOfBits;
                }
                //make room
                retVal <<= countToRead;
                byte b = ReadByte(countToRead);
                retVal |= (long)b;
                countOfBits -= countToRead;
            }
            return retVal;
        }
 
        /// <summary>
        /// Read a single UInt16 from the byte[]
        /// </summary>
        /// <param name="countOfBits"></param>
        /// <returns></returns>
        internal ushort ReadUInt16(int countOfBits)
        {
            // we only support 1-16 bits currently, not multiple bytes, and not 0 bits
            if (countOfBits > Native.BitsPerShort || countOfBits <= 0)
            {
                throw new ArgumentOutOfRangeException("countOfBits", countOfBits, SR.Get(SRID.CountOfBitsOutOfRange));
            }
 
            ushort retVal = 0;
            while (countOfBits > 0)
            {
                int countToRead = (int)Native.BitsPerByte;
                if (countOfBits < 8)
                {
                    countToRead = countOfBits;
                }
                //make room
                retVal <<= countToRead;
                byte b = ReadByte(countToRead);
                retVal |= (ushort)b;
                countOfBits -= countToRead;
            }
            return retVal;
        }
 
        /// <summary>
        /// Read a specified number of bits from the stream in reverse byte order
        /// </summary>
        internal uint ReadUInt16Reverse(int countOfBits)
        {
            // we only support 1-8 bits currently, not multiple bytes, and not 0 bits
            if (countOfBits > Native.BitsPerShort|| countOfBits <= 0)
            {
                throw new ArgumentOutOfRangeException("countOfBits", countOfBits, SR.Get(SRID.CountOfBitsOutOfRange));
            }
 
            ushort retVal = 0;
            int fullBytesRead = 0;
            while (countOfBits > 0)
            {
                int countToRead = (int)Native.BitsPerByte;
                if (countOfBits < 8)
                {
                    countToRead = countOfBits;
                }
                //make room
                ushort b = (ushort)ReadByte(countToRead);
                b <<= (fullBytesRead * Native.BitsPerByte);
                retVal |= b;
                fullBytesRead++;
                countOfBits -= countToRead;
            }
            return retVal;
        }
 
        /// <summary>
        /// Read a specified number of bits from the stream into a single byte
        /// </summary>
        internal uint ReadUInt32(int countOfBits)
        {
            // we only support 1-8 bits currently, not multiple bytes, and not 0 bits
            if (countOfBits > Native.BitsPerInt || countOfBits <= 0)
            {
                throw new ArgumentOutOfRangeException("countOfBits", countOfBits, SR.Get(SRID.CountOfBitsOutOfRange));
            }
 
            uint retVal = 0;
            while (countOfBits > 0)
            {
                int countToRead = (int)Native.BitsPerByte;
                if (countOfBits < 8)
                {
                    countToRead = countOfBits;
                }
                //make room
                retVal <<= countToRead;
                byte b = ReadByte(countToRead);
                retVal |= (uint)b;
                countOfBits -= countToRead;
            }
            return retVal;
        }
 
        /// <summary>
        /// Read a specified number of bits from the stream in reverse byte order
        /// </summary>
        internal uint ReadUInt32Reverse(int countOfBits)
        {
            // we only support 1-8 bits currently, not multiple bytes, and not 0 bits
            if (countOfBits > Native.BitsPerInt || countOfBits <= 0)
            {
                throw new ArgumentOutOfRangeException("countOfBits", countOfBits, SR.Get(SRID.CountOfBitsOutOfRange));
            }
 
            uint retVal = 0;
            int fullBytesRead = 0;
            while (countOfBits > 0)
            {
                int countToRead = (int)Native.BitsPerByte;
                if (countOfBits < 8)
                {
                    countToRead = countOfBits;
                }
                //make room
                uint b = (uint)ReadByte(countToRead);
                b <<= (fullBytesRead * Native.BitsPerByte);
                retVal |= b;
                fullBytesRead++;
                countOfBits -= countToRead;
            }
            return retVal;
        }
 
        /// <summary>
        /// Reads a single bit from the buffer
        /// </summary>
        /// <returns></returns>
        internal bool ReadBit()
        {
            byte b = ReadByte(1);
            return ((b & 1) == 1);
        }
 
        /// <summary>
        /// Read a specified number of bits from the stream into a single byte
        /// </summary>
        /// <param name="countOfBits">The number of bits to unpack</param>
        /// <returns>A single byte that contains up to 8 packed bits</returns>
        /// <remarks>For example, if 2 bits are read from the stream, then a full byte
        /// will be created with the least significant bits set to the 2 unpacked bits
        /// from the stream</remarks>
        internal byte ReadByte(int countOfBits)
        {
            // if the end of the stream has been reached, then throw an exception
            if (EndOfStream)
            {
                throw new System.IO.EndOfStreamException(SR.Get(SRID.EndOfStreamReached));
            }
 
            // we only support 1-8 bits currently, not multiple bytes, and not 0 bits
            if (countOfBits > Native.BitsPerByte || countOfBits <= 0)
            {
                throw new ArgumentOutOfRangeException("countOfBits", countOfBits, SR.Get(SRID.CountOfBitsOutOfRange));
            }
 
            if (countOfBits > _bufferLengthInBits)
            {
                throw new ArgumentOutOfRangeException("countOfBits", countOfBits, SR.Get(SRID.CountOfBitsGreatThanRemainingBits));
            }
 
            _bufferLengthInBits -= (uint)countOfBits;
 
            // initialize return byte to 0 before reading from the cache
            byte returnByte = 0;
 
            // if the partial bit cache contains more bits than requested, then read the
            //      cache only
            if (_cbitsInPartialByte >= countOfBits)
            {
                // retrieve the requested count of most significant bits from the cache
                //      and store them in the least significant positions in the return byte
                int rightShiftPartialByteBy = Native.BitsPerByte - countOfBits;
                returnByte = (byte)(_partialByte >> rightShiftPartialByteBy);
 
                // reposition any unused portion of the cache in the most significant part of the bit cache
                unchecked // disable overflow checking since we are intentionally throwing away
                            //  the significant bits
                {
                    _partialByte <<= countOfBits;
                }
                // update the bit count in the cache
                _cbitsInPartialByte -= countOfBits;
            }
                // otherwise, we need to retrieve more full bytes from the stream
            else
            {
                // retrieve the next full byte from the stream
                byte nextByte = _byteArray[_byteArrayIndex];
                _byteArrayIndex++;
 
                //right shift partial byte to get it ready to or with the partial next byte
                int rightShiftPartialByteBy = Native.BitsPerByte - countOfBits;
                returnByte = (byte)(_partialByte >> rightShiftPartialByteBy);
 
                // now copy the remaining chunk of the newly retrieved full byte
                int rightShiftNextByteBy = Math.Abs((countOfBits - _cbitsInPartialByte) - Native.BitsPerByte);
                returnByte |= (byte)(nextByte >> rightShiftNextByteBy);
 
                // update the partial bit cache with the remainder of the newly retrieved full byte
                unchecked // disable overflow checking since we are intentionally throwing away
                            //  the significant bits
                {
                    _partialByte = (byte)(nextByte << (countOfBits - _cbitsInPartialByte));
                }
 
                _cbitsInPartialByte = Native.BitsPerByte - (countOfBits - _cbitsInPartialByte);
            }
            return returnByte;
        }
 
        /// <summary>
        /// Since the return value of Read cannot distinguish between valid and invalid
        /// data (e.g. 8 bits set), the EndOfStream property detects when there is no more
        /// data to read.
        /// </summary>
        /// <value>True if stream end has been reached</value>
        internal bool EndOfStream
        {
            get
            {
                return 0 == _bufferLengthInBits;
            }
        }
 
        /// <summary>
        /// The current read index in the array
        /// </summary>
        internal int CurrentIndex
        {
            get
            {
                //_byteArrayIndex is always advanced to the next index
                // so we always decrement before returning
                return _byteArrayIndex - 1;
            }
        }
 
 
        // Privates
        // reference to the source byte buffer to read from
        private byte[] _byteArray = null;
 
        // maximum length of buffer to read in bits
        private uint _bufferLengthInBits = 0;
 
            // the index in the source buffer for the next byte to be read
        private int _byteArrayIndex = 0;
 
            // since the bits from multiple inputs can be packed into a single byte
            //  (e.g. 2 bits per input fits 4 per byte), we use this field as a cache
            //  of the remaining partial bits.
        private byte _partialByte = 0;
 
            // the number of bits (partial byte) left to read in the overlapped byte field
        private int _cbitsInPartialByte = 0;
    }
 
    /// <summary>
    /// A stream-like writer for packing bits into a byte buffer
    /// </summary>
    /// <remarks>This class is to be used with the BitStreamReader for reading
    /// and writing bytes. Note that the bytes should be read in the same order
    /// and lengths as they were written to retrieve the same values.
    /// See remarks in BitStreamReader regarding compatibility with the native C++
    /// BitStream class.</remarks>
    internal class BitStreamWriter
    {
        /// <summary>
        /// Create a new bit writer that writes to the target buffer
        /// </summary>
        /// <param name="bufferToWriteTo"></param>
        internal BitStreamWriter(List<byte> bufferToWriteTo)
        {
            if (bufferToWriteTo == null)
            {
                throw new ArgumentNullException("bufferToWriteTo");
            }
            _targetBuffer = bufferToWriteTo;
        }
 
        /// <summary>
        /// Writes the count of bits from the int to the left packed buffer
        /// </summary>
        /// <param name="bits"></param>
        /// <param name="countOfBits"></param>
        internal void Write(uint bits, int countOfBits)
        {
            // validate that a subset of the bits in a single byte are being written
            if (countOfBits <= 0 || countOfBits > Native.BitsPerInt)
                throw new ArgumentOutOfRangeException("countOfBits", countOfBits, SR.Get(SRID.CountOfBitsOutOfRange));
 
 
            // calculate the number of full bytes
            //   Example: 10 bits would require 1 full byte
            int fullBytes = countOfBits / Native.BitsPerByte;
 
            // calculate the number of bits that spill beyond the full byte boundary
            //   Example: 10 buttons would require 2 extra bits (8 fit in a full byte)
            int bitsToWrite = countOfBits % Native.BitsPerByte;
 
            for (; fullBytes >= 0; fullBytes--)
            {
                byte byteOfData = (byte)(bits >> (fullBytes * Native.BitsPerByte));
                //
                // write 8 or less bytes to the bitwriter
                // checking for 0 handles the case where we're writing 8, 16 or 24 bytes
                // and bitsToWrite is initialize to zero
                //
                if (bitsToWrite > 0)
                {
                    Write(byteOfData, bitsToWrite);
                }
                if (fullBytes > 0)
                {
                    bitsToWrite = Native.BitsPerByte;
                }
            }
        }
 
        /// <summary>
        /// Writes the count of bits from the int to the buffer in reverse order
        /// </summary>
        /// <param name="bits"></param>
        /// <param name="countOfBits"></param>
        internal void WriteReverse(uint bits, int countOfBits)
        {
            // validate that a subset of the bits in a single byte are being written
            if (countOfBits <= 0 || countOfBits > Native.BitsPerInt)
                throw new ArgumentOutOfRangeException("countOfBits", countOfBits, SR.Get(SRID.CountOfBitsOutOfRange));
 
            // calculate the number of full bytes
            //   Example: 10 bits would require 1 full byte
            int fullBytes = countOfBits / Native.BitsPerByte;
 
            // calculate the number of bits that spill beyond the full byte boundary
            //   Example: 10 buttons would require 2 extra bits (8 fit in a full byte)
            int bitsToWrite = countOfBits % Native.BitsPerByte;
            if (bitsToWrite > 0)
            {
                fullBytes++;
            }
            for (int x = 0; x < fullBytes; x++)
            {
                byte byteOfData = (byte)(bits >> (x * Native.BitsPerByte));
                Write(byteOfData, Native.BitsPerByte);
            }
        }
 
        /// <summary>
        /// Write a specific number of bits from byte input into the stream
        /// </summary>
        /// <param name="bits">The byte to read the bits from</param>
        /// <param name="countOfBits">The number of bits to read</param>
        internal void Write(byte bits, int countOfBits)
        {
            // validate that a subset of the bits in a single byte are being written
            if (countOfBits <= 0 || countOfBits > Native.BitsPerByte)
                throw new ArgumentOutOfRangeException("countOfBits", countOfBits, SR.Get(SRID.CountOfBitsOutOfRange));
 
            byte buffer;
                // if there is remaining bits in the last byte in the stream
                //      then use those first
            if (_remaining > 0)
            {
                // retrieve the last byte from the stream, update it, and then replace it
                buffer = _targetBuffer[_targetBuffer.Count - 1];
                    // if the remaining bits aren't enough then just copy the significant bits
                    //      of the input into the remainder
                if (countOfBits > _remaining)
                {
                    buffer |= (byte)((bits & (0xFF >> (Native.BitsPerByte - countOfBits))) >> (countOfBits - _remaining));
                }
                    // otherwise, copy the entire set of input bits into the remainder
                else
                {
                    buffer |= (byte)((bits & (0xFF >> (Native.BitsPerByte - countOfBits))) << (_remaining - countOfBits));
                }
                _targetBuffer[_targetBuffer.Count - 1] = buffer;
            }
 
            // if the remainder wasn't large enough to hold the entire input set
            if (countOfBits > _remaining)
            {
                  // then copy the uncontained portion of the input set into a temporary byte
                _remaining = Native.BitsPerByte - (countOfBits - _remaining);
                unchecked // disable overflow checking since we are intentionally throwing away
                            //  the significant bits
                {
                    buffer = (byte)(bits << _remaining);
                }
                    // and add it to the target buffer
                _targetBuffer.Add(buffer);
            }
            else
            {
                // otherwise, simply update the amount of remaining bits we have to spare
                _remaining -= countOfBits;
            }
        }
 
 
        // the buffer that the bits are written into
        private List<byte> _targetBuffer = null;
 
        // number of free bits remaining in the last byte added to the target buffer
        private int _remaining = 0;
    }
}