csharp/Alexander-Scott/OctoMapSharp/Assets/OctoMapSharp/BitStream.cs

BitStream.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace OctoMapSharp
{
    /// 
    /// Stream wrapper to use bit-level operations
    /// 
    public clast BitStream
    {
        private long offset { get; set; }
        private int bit { get; set; }
        private bool MSB { get; set; }
        private Stream stream;
        private Encoding encoding;

        /// 
        /// Allows the  auto increase in size when needed
        /// 
        public bool AutoIncreaseStream { get; set; }

        /// 
        /// Get the stream length
        /// 
        public long Length
        {
            get
            {
                return stream.Length;
            }
        }

        /// 
        /// Get the current bit position in the stream
        /// 
        public long BitPosition
        {
            get
            {
                return bit;
            }
        }

        /// 
        /// Check if  offset is inside the stream length
        /// 
        private bool ValidPosition
        {
            get
            {
                return offset < Length;
            }
        }

        #region Constructors

        /// 
        /// Creates a  using a Stream
        /// 
        /// Stream to use
        /// true if Most Significant Bit will be used, if false LSB will be used
        public BitStream(Stream stream, bool MSB = false)
        {
            this.stream = new MemoryStream();
            stream.CopyTo(this.stream);
            this.MSB = MSB;
            offset = 0;
            bit = 0;
            encoding = Encoding.UTF8;
            AutoIncreaseStream = false;
        }

        /// 
        /// Creates a  using a Stream
        /// 
        /// Stream to use
        /// Encoding to use with chars
        /// true if Most Significant Bit will be used, if false LSB will be used
        public BitStream(Stream stream, Encoding encoding, bool MSB = false)
        {
            this.stream = new MemoryStream();
            stream.CopyTo(this.stream);
            this.MSB = MSB;
            offset = 0;
            bit = 0;
            this.encoding = encoding;
            AutoIncreaseStream = false;
        }

        /// 
        /// Creates a  using a byte[]
        /// 
        /// byte[] to use
        /// true if Most Significant Bit will be used, if false LSB will be used
        public BitStream(byte[] buffer, bool MSB = false)
        {
            this.stream = new MemoryStream();
            MemoryStream m = new MemoryStream(buffer);
            m.CopyTo(this.stream);
            this.MSB = MSB;
            offset = 0;
            bit = 0;
            encoding = Encoding.UTF8;
            AutoIncreaseStream = false;
        }

        /// 
        /// Creates a  using a byte[]
        /// 
        /// byte[] to use
        /// Encoding to use with chars
        /// true if Most Significant Bit will be used, if false LSB will be used
        public BitStream(byte[] buffer, Encoding encoding, bool MSB = false)
        {
            this.stream = new MemoryStream();
            MemoryStream m = new MemoryStream(buffer);
            m.CopyTo(this.stream);
            this.MSB = MSB;
            offset = 0;
            bit = 0;
            this.encoding = encoding;
            AutoIncreaseStream = false;
        }

        /// 
        /// Creates a  using a byte[]
        /// 
        /// byte[] to use
        /// true if Most Significant Bit will be used, if false LSB will be used
        public static BitStream Create(byte[] buffer, bool MSB = false)
        {
            return new BitStream(buffer, MSB);
        }

        /// 
        /// Creates a  using a byte[]
        /// 
        /// byte[] to use
        /// Encoding to use with chars/param>
        /// true if Most Significant Bit will be used, if false LSB will be used
        public static BitStream Create(byte[] buffer, Encoding encoding, bool MSB = false)
        {
            return new BitStream(buffer, encoding, MSB);
        }

        /// 
        /// Creates a  using a file path, throws IOException if file doesn't exists or path is not a file
        /// 
        /// File path
        /// Encoding of the file, if null default  will be used
        /// 
        public static BitStream CreateFromFile(string path, Encoding encoding = null)
        {
            if (!File.Exists(path))
            {
                throw new IOException("File doesn't exists!");
            }
            if(File.GetAttributes(path) == FileAttributes.Directory)
            {
                throw new IOException("Path is a directory!");
            }
            if (encoding == null)
            {
                encoding = Encoding.UTF8;
            }
            return new BitStream(File.ReadAllBytes(path), encoding);
        }

        #endregion

        #region Methods

        /// 
        /// Seek to the specified offset and check if it is a valid position for reading in the stream
        /// 
        /// offset on the stream
        /// bit position
        /// true if offset is valid to do reading, false otherwise
        public bool this[long offset, int bit]
        {
            get
            {
                Seek(offset, bit);
                return ValidPosition;
            }
            //set {
            //    Seek(offset, bit);
            //}
            private set { }
        }

        /// 
        /// Seek through the stream selecting the offset and bit using 
        /// 
        /// offset on the stream
        /// bit position
        public void Seek(long offset, int bit)
        {
            if (offset > Length)
            {
                this.offset = Length;
            }
            else
            {
                if (offset >= 0)
                {
                    this.offset = offset;
                }
                else
                {
                    offset = 0;
                }
            }
            if (bit >= 8)
            {
                int n = (int)(bit / 8);
                this.offset += n;
                this.bit = bit % 8;
            }
            else
            {
                this.bit = bit;
            }
            stream.Seek(offset, SeekOrigin.Begin);
        }

        /// 
        /// Advances the stream by one bit
        /// 
        public void AdvanceBit()
        {
            bit = (bit + 1) % 8;
            if (bit == 0)
            {
                offset++;
            }
        }

        /// 
        /// Returns the stream by one bit
        /// 
        public void ReturnBit()
        {
            bit = ((bit - 1) == -1 ? 7 : bit - 1);
            if (bit == 7)
            {
                offset--;
            }
            if(offset < 0)
            {
                offset = 0;
            }
        }

        /// 
        /// Get the edited stream
        /// 
        /// Modified stream
        public Stream GetStream()
        {
            return stream;
        }

        /// 
        /// Get the stream data as a byte[]
        /// 
        /// Stream as byte[]
        public byte[] GetStreamData()
        {
            stream.Seek(0, SeekOrigin.Begin);
            MemoryStream s = new MemoryStream();
            stream.CopyTo(s);
            Seek(offset, bit);
            return s.ToArray();
        }

        /// 
        /// Get the  used for chars and strings
        /// 
        ///  used
        public Encoding GetEncoding()
        {
            return encoding;
        }

        /// 
        /// Set the  that will be used for chars and strings
        /// 
        ///  to use
        public void SetEncoding(Encoding encoding)
        {
            this.encoding = encoding;
        }

        /// 
        /// Changes the length of the stream, if new length is less than current length stream data will be truncated
        /// 
        /// New stream length
        /// return true if stream changed length, false if it wasn't possible
        public bool ChangeLength(long length)
        {
            if (stream.CanSeek && stream.CanWrite)
            {
                stream.SetLength(length);
                return true;
            }
            else
            {
                return false;
            }
        }

        /// 
        /// Cuts the  from the specified offset and given length, will throw an exception when length + offset is higher than stream's length, offset and bit will be set to 0
        /// 
        /// Offset to start
        /// Length of the new 
        public void CutStream(long offset, long length)
        {
            byte[] data = GetStreamData();
            byte[] buffer = new byte[length];
            Array.Copy(data, offset, buffer, 0, length);
            this.stream = new MemoryStream();
            MemoryStream m = new MemoryStream(buffer);
            this.stream = new MemoryStream();
            m.CopyTo(this.stream);
            this.offset = 0;
            bit = 0;
        }

        /// 
        /// Copies the current  buffer to another 
        /// 
        ///  to copy buffer
        public void CopyStreamTo(Stream stream)
        {
            Seek(0, 0);
            stream.SetLength(this.stream.Length);
            this.stream.CopyTo(stream);
        }

        /// 
        /// Copies the current  buffer to another 
        /// 
        ///  to copy buffer
        public void CopyStreamTo(BitStream stream)
        {
            Seek(0, 0);
            stream.ChangeLength(this.stream.Length);
            this.stream.CopyTo(stream.stream);
            stream.Seek(0, 0);
        }

        /// 
        /// Saves current  buffer into a file
        /// 
        /// File to write data, if it exists it will be overwritten
        public void SaveStreamAsFile(string filename)
        {
            File.WriteAllBytes(filename, GetStreamData());
        }

        /// 
        /// Returns the current content of the stream as a 
        /// 
        ///  containing current  data
        public MemoryStream CloneAsMemoryStream()
        {
            return new MemoryStream(GetStreamData());
        }

        /// 
        /// Returns the current content of the stream as a 
        /// 
        ///  containing current  data
        public BufferedStream CloneAsBufferedStream()
        {
            BufferedStream bs = new BufferedStream(stream);
            StreamWriter sw = new StreamWriter(bs);
            sw.Write(GetStreamData());
            bs.Seek(0, SeekOrigin.Begin);
            return bs;
        }


        /// 
        /// Checks if the  will be in a valid position on its last bit read/write
        /// 
        /// Number of bits it will advance
        /// true if  will be inside the stream length
        private bool ValidPositionWhen(int bits)
        {
            long o = offset;
            int b = bit;
            b = (b + 1) % 8;
            if (b == 0)
            {
                o++;
            }
            return o < Length;
        }


        #endregion

        #region BitRead/Write

        /// 
        /// Read current position bit and advances the position within the stream by one bit
        /// 
        /// Returns the current position bit as 0 or 1
        public Bit ReadBit()
        {
            if (!ValidPosition)
            {
                throw new IOException("Cannot read in an offset bigger than the length of the stream");
            }
            stream.Seek(offset, SeekOrigin.Begin);
            byte value;
            if (!MSB)
            {
                value = (byte)((stream.ReadByte() >> (bit)) & 1);
            }
            else
            {
                value = (byte)((stream.ReadByte() >> (7 - bit)) & 1);
            }
            AdvanceBit();
            stream.Seek(offset, SeekOrigin.Begin);
            return value;
        }

        /// 
        /// Read from current position the specified number of bits
        /// 
        /// Bits to read
        /// [] containing read bits
        public Bit[] ReadBits(int length)
        {
            Bit[] bits = new Bit[length];
            for(int i=0;i< length; i++)
            {
                bits[i] = ReadBit();
            }
            return bits;
        }

        /// 
        /// Writes a bit in the current position
        /// 
        /// Bit to write, it data is not 0 or 1 data = data & 1
        public void WriteBit(Bit data)
        {
            stream.Seek(offset, SeekOrigin.Begin);
            byte value = (byte)stream.ReadByte();
            stream.Seek(offset, SeekOrigin.Begin);
            if (!MSB)
            {
                value &= (byte)~(1  (7 - p)) & 1);
                    }
                    WriteBit(value);
                }
                position++;
            }
        }

        /// 
        /// Write a byte value based on the current stream and bit position
        /// 
        public void WriteByte(byte value)
        {
            WriteBytes(new byte[] { value }, 8);
        }

        /// 
        /// Write a byte value based on the current stream and bit position
        /// 
        public void WriteByte(byte value, int bits)
        {
            if (bits < 0)
            {
                bits = 0;
            }
            if (bits > 8)
            {
                bits = 8;
            }
            WriteBytes(new byte[] { value }, bits);
        }

        /// 
        /// Write a byte value based on the current stream and bit position
        /// 
        public void WriteSByte(sbyte value)
        {
            WriteBytes(new byte[] { (byte)value }, 8);
        }

        /// 
        /// Write a byte value based on the current stream and bit position
        /// 
        public void WriteSByte(sbyte value, int bits)
        {
            if (bits < 0)
            {
                bits = 0;
            }
            if (bits > 8)
            {
                bits = 8;
            }
            WriteBytes(new byte[] { (byte)value }, bits);
        }

        /// 
        /// Write a bool value as 0:false, 1:true as byte based on the current stream and bit position
        /// 
        public void WriteBool(bool value)
        {
            WriteBytes(new byte[] { value ? (byte)1 : (byte)0 }, 8);
        }

        /// 
        /// Write a char value based on the  encoding
        /// 
        public void WriteChar(char value)
        {
            byte[] bytes = encoding.GetBytes(new char[] { value }, 0, 1);
            WriteBytes(bytes, bytes.Length*8);
        }

        /// 
        /// Write a string based on the  encoding
        /// 
        public void WriteString(string value)
        {
            byte[] bytes = encoding.GetBytes(value);
            WriteBytes(bytes, bytes.Length * 8);
        }

        /// 
        /// Write a short value based on the current stream and bit position
        /// 
        public void WriteInt16(short value)
        {
            WriteBytes(BitConverter.GetBytes(value), 16);
        }

        /// 
        /// Write a 24bit value based on the current stream and bit position
        /// 
        public void WriteInt24(Int24 value)
        {
            WriteBytes(BitConverter.GetBytes((int) value), 24);
        }

        /// 
        /// Write an int value based on the current stream and bit position
        /// 
        public void WriteInt32(int value)
        {
            WriteBytes(BitConverter.GetBytes(value), 32);
        }

        /// 
        /// Write a 48bit value based on the current stream and bit position
        /// 
        public void WriteInt48(Int48 value)
        {
            WriteBytes(BitConverter.GetBytes((long) value), 48);
        }

        /// 
        /// Write a long value based on the current stream and bit position
        /// 
        public void WriteInt64(long value)
        {
            WriteBytes(BitConverter.GetBytes(value), 64);
        }

        /// 
        /// Write an ushort value based on the current stream and bit position
        /// 
        public void WriteUInt16(ushort value)
        {
            WriteBytes(BitConverter.GetBytes(value), 16);
        }

        /// 
        /// Write an unsigned 24bit value based on the current stream and bit position
        /// 
        public void WriteUInt24(UInt24 value)
        {
            WriteBytes(BitConverter.GetBytes((uint) value), 24);
        }

        /// 
        /// Write an uint value based on the current stream and bit position
        /// 
        public void WriteUInt32(uint value)
        {
            WriteBytes(BitConverter.GetBytes(value), 32);
        }

        /// 
        /// Write an unsigned 48bit value based on the current stream and bit position
        /// 
        public void WriteUInt48(UInt48 value)
        {
            WriteBytes(BitConverter.GetBytes((ulong) value), 48);
        }

        /// 
        /// Write an ulong value based on the current stream and bit position
        /// 
        public void WriteUInt64(ulong value)
        {
            WriteBytes(BitConverter.GetBytes(value), 64);
        }

        #endregion

        #region Shifts

        /// 
        /// Do a bitwise shift on the current position of the stream on bit 0
        /// 
        /// bits to shift
        /// true to left shift, false to right shift
        public void bitwiseShift(int bits, bool leftShift)
        {
            if (!ValidPositionWhen(8))
            {
                throw new IOException("Cannot read in an offset bigger than the length of the stream");
            }
            Seek(offset, 0);
            if (bits != 0 && bits  bits);
                }
                Seek(offset, 0);
                stream.WriteByte(value);
            }
            bit = 0;
            offset++;
        }

        /// 
        /// Do a bitwise shift on the current position of the stream on current bit
        /// 
        /// bits to shift
        /// true to left shift, false to right shift
        public void bitwiseShiftOnBit(int bits, bool leftShift)
        {
            if (!ValidPositionWhen(8))
            {
                throw new IOException("Cannot read in an offset bigger than the length of the stream");
            }
            Seek(offset, bit);
            if (bits != 0 && bits  bits);
                }
                offset--;
                Seek(offset, bit);
                WriteByte(value);
            }
            offset++;
        }

        /// 
        /// Do a circular shift on the current position of the stream on bit 0
        /// 
        /// bits to shift
        /// true to left shift, false to right shift
        public void circularShift(int bits, bool leftShift)
        {
            if (!ValidPositionWhen(8))
            {
                throw new IOException("Cannot read in an offset bigger than the length of the stream");
            }
            Seek(offset, 0);
            if (bits != 0 && bits  (8 - bits));
                }
                else
                {
                    value = (byte)(value >> bits | value > bits | value