1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ビット単位のストリーム入出力

Posted at

BitStream

BitStream.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace System.IO
{
    public class BitStream : Stream
    {
        Stream stream;

        public enum Access
        {
            Read,
            Write,
        }

        Access access;

        public BitStream(Stream stream, Access access)
        {
            if (stream == null)
            {
                throw new ArgumentNullException();
            }

            this.stream = stream;

            this.access = access;

            read_buffer = 0;
            read_buffer_bit_length = 0;

            write_buffer = 0;
            write_buffer_bit_length = 0;
        }

        public override bool CanRead
        {
            get
            {
                return (access == Access.Read) && stream.CanRead;
            }
        }

        public override bool CanWrite
        {
            get
            {
                return (access == Access.Write) && stream.CanWrite;
            }
        }

        public override bool CanSeek => false;

        public override long Length => throw new InvalidOperationException();

        public override long Position
        {
            get => throw new InvalidOperationException();
            set => throw new InvalidOperationException();
        }

        public override long Seek(long offset, SeekOrigin origin) => throw new InvalidOperationException();

        public override void SetLength(long value) => throw new InvalidOperationException();

        public override int Read(byte[] buffer, int offset, int count)
        {
            return (ReadBit(buffer, offset, count * 8) + 7) / 8;
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            WriteBit(buffer, offset, count * 8);
        }

        byte[] byte_buffer = new byte[1];

        int read_buffer;

        int read_buffer_bit_length;

        public int ReadBit(byte[] buffer, int offset, int bit_count)
        {
            if (access != Access.Read)
            {
                throw new InvalidOperationException();
            }

            if (bit_count <= 0)
            {
                if (bit_count == 0)
                {
                    return 0;
                }

                throw new ArgumentOutOfRangeException();
            }

            int byte_count = bit_count / 8;

            if (read_buffer_bit_length == 0)
            {
                if (byte_count > 0)
                {
                    int read_count = stream.Read(buffer, offset, byte_count);

                    if (read_count < byte_count)
                    {
                        return 8 * read_count;
                    }
                }
            }
            else
            {
                for (int i = 0; i < byte_count; i++)
                {
                    while (read_buffer_bit_length < 8)
                    {
                        if (stream.Read(byte_buffer, 0, 1) < 1)
                        {
                            int read_count = 8 * i;

                            if (read_buffer_bit_length > 0)
                            {
                                read_count += read_buffer_bit_length;

                                buffer[offset + i] = (byte)read_buffer;

                                read_buffer = 0;

                                read_buffer_bit_length = 0;
                            }

                            return read_count;
                        }

                        read_buffer |= ((int)byte_buffer[0]) << read_buffer_bit_length;

                        read_buffer_bit_length += 8;
                    }

                    buffer[offset + i] = (byte)read_buffer;

                    read_buffer >>= 8;

                    read_buffer_bit_length -= 8;
                }
            }

            int remain = bit_count % 8;

            if (remain != 0)
            {
                while (read_buffer_bit_length < remain)
                {
                    if (stream.Read(byte_buffer, 0, 1) < 1)
                    {
                        int read_count = 8 * byte_count;

                        if (read_buffer_bit_length > 0)
                        {
                            read_count += read_buffer_bit_length;

                            buffer[offset + byte_count] = (byte)read_buffer;

                            read_buffer = 0;

                            read_buffer_bit_length = 0;
                        }

                        return read_count;
                    }

                    read_buffer |= ((int)byte_buffer[0]) << read_buffer_bit_length;

                    read_buffer_bit_length += 8;
                }

                buffer[offset + byte_count] = (byte)(read_buffer & ((1 << remain) - 1));

                read_buffer >>= remain;

                read_buffer_bit_length -= remain;
            }

            return bit_count;
        }

        int write_buffer;

        int write_buffer_bit_length;

        public void WriteBit(byte[] buffer, int offset, int bit_count)
        {
            if (access != Access.Write)
            {
                throw new InvalidOperationException();
            }

            if (bit_count <= 0)
            {
                if (bit_count == 0)
                {
                    return;
                }

                throw new ArgumentOutOfRangeException();
            }

            int byte_count = bit_count / 8;

            if (write_buffer_bit_length == 0)
            {
                if (byte_count > 0)
                {
                    stream.Write(buffer, offset, byte_count);
                }
            }
            else
            {
                for (int i = 0; i < byte_count; i++)
                {
                    write_buffer |= buffer[offset + i] << write_buffer_bit_length;

                    write_buffer_bit_length += 8;

                    while (write_buffer_bit_length >= 8)
                    {
                        byte_buffer[0] = (byte)write_buffer;

                        stream.Write(byte_buffer, 0, 1);

                        write_buffer >>= 8;

                        write_buffer_bit_length -= 8;
                    }
                }
            }

            int remain = bit_count % 8;

            if (remain != 0)
            {
                write_buffer |= buffer[offset + byte_count] << write_buffer_bit_length;

                write_buffer_bit_length += remain;

                while (write_buffer_bit_length >= 8)
                {
                    byte_buffer[0] = (byte)write_buffer;

                    stream.Write(byte_buffer, 0, 1);

                    write_buffer >>= 8;

                    write_buffer_bit_length -= 8;
                }
            }
        }

        public override void Flush()
        {
            if (access == Access.Write)
            {
                if (write_buffer_bit_length > 0)
                {
                    while (write_buffer_bit_length >= 8)
                    {
                        byte_buffer[0] = (byte)write_buffer;

                        stream.Write(byte_buffer, 0, 1);

                        write_buffer >>= 8;

                        write_buffer_bit_length -= 8;
                    }

                    if (write_buffer_bit_length > 0)
                    {
                        byte_buffer[0] = (byte)write_buffer;

                        stream.Write(byte_buffer, 0, 1);

                        write_buffer = 0;

                        write_buffer_bit_length = 0;
                    }
                }
            }
        }

        public override void Close()
        {
            if (access == Access.Write)
            {
                Flush();
            }

            base.Close();
        }

        public static int GetNecessaryBitLength(int value)
        {
            if (value <= 0)
            {
                if (value == 0)
                {
                    return 1;
                }

                return 32;
            }

            int i = 31;

            int bit = 0x40000000;

            while (true)
            {
                if ((value & bit) != 0)
                {
                    return i;
                }

                bit >>= 1;

                i--;
            }
        }

        public static int GetNecessaryBitLength(Int64 value)
        {
            if (value <= 0)
            {
                if (value == 0)
                {
                    return 1;
                }

                return 64;
            }

            int i = 63;

            Int64 bit = 0x4000000000000000L;

            while (true)
            {
                if ((value & bit) != 0)
                {
                    return i;
                }

                bit >>= 1;

                i--;
            }
        }

        public static int RestrictBitLength(int value, int bit_length)
        {
            if (bit_length <= 0 || bit_length >= 32)
            {
                if (bit_length == 0)
                {
                    return 0;
                }

                if (bit_length == 32)
                {
                    return value;
                }

                throw new ArgumentOutOfRangeException();
            }

            return value & ((1 << bit_length) - 1);
        }

        public static Int64 RestrictBitLength(Int64 value, int bit_length)
        {
            if (bit_length <= 0 || bit_length >= 64)
            {
                if (bit_length == 0)
                {
                    return 0;
                }

                if (bit_length == 64)
                {
                    return value;
                }

                throw new ArgumentOutOfRangeException();
            }

            return value & ((1L << bit_length) - 1);
        }
    }
}
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?