Click here to Skip to main content
15,884,628 members
Articles / Programming Languages / C#

Project Tool

Rate me:
Please Sign up or sign in to vote.
4.69/5 (10 votes)
23 Sep 2007CPOL3 min read 54.5K   1.7K   73  
Backup your C# solution and projects for archive or source code sharing. Temporary or unused files are excluded.
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace QiHe.CodeLib.Compress
{
    /// <summary>
    /// pattern:
    /// Use two sections of rails to carry a moving train.
    /// </summary>
    public class DoubleBufferedStreamReader
    {
        Stream baseStream;
        int bufferSize;
        int markerPos;
        int position;
        public int dataLength;
        long basePosition;

        byte[] firstBuffer;
        byte[] secondBuffer;

        public enum WorkMode { Reading, Seeking }

        bool isReading = true;

        /// <summary>
        /// In Reading mode, when cursor goes beyond backwardSize the bufer swap and new data is fetched.
        /// In Seeking mode, the cursor only goes within the buffer.
        /// </summary>
        public WorkMode Mode
        {
            get
            {
                if (isReading)
                {
                    return WorkMode.Reading;
                }
                else
                {
                    return WorkMode.Seeking;
                }
            }
            set
            {
                switch (value)
                {
                    case WorkMode.Reading:
                        isReading = true;
                        break;
                    case WorkMode.Seeking:
                        isReading = false;
                        break;
                    default:
                        throw new Exception("Invalid WorkMode value.");
                }
            }
        }

        /// <summary>
        /// constrain:
        /// At any reading position, the seeking range should 
        /// between CurrentPosition - backwardSize and CurrentPosition + forwardSize
        /// and within the BaseStream.
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="forwardSize"></param>
        /// <param name="backwardSize"></param>
        public DoubleBufferedStreamReader(Stream stream, int forwardSize, int backwardSize)
        {
            this.baseStream = stream;
            this.bufferSize = forwardSize + backwardSize;
            this.markerPos = bufferSize + backwardSize;

            this.firstBuffer = new byte[bufferSize];
            this.secondBuffer = new byte[bufferSize];

            BeginRead();
        }

        public void BeginRead()
        {
            if (dataLength == 0)
            {
                if (baseStream.Position > 0) //need test
                {
                    long pos = Math.Max(0, baseStream.Position - bufferSize);
                    int count = (int)(baseStream.Position - pos);
                    baseStream.Position = pos;
                    baseStream.Read(firstBuffer, bufferSize - count, count);
                }

                FillBuffer();
                position = bufferSize;
            }
        }

        public byte PeekByte()
        {
            if (position < bufferSize)
            {
                return firstBuffer[position];
            }
            else
            {
                return secondBuffer[position - bufferSize];
            }
        }

        public byte ReadByte()
        {
            byte value = PeekByte();
            position++;
            CheckPosition();
            return value;
        }

        private void CheckPosition()
        {
            if (ShouldSwapBuffer())
            {
                SwapBuffer();
            }
        }

        bool ShouldSwapBuffer()
        {
            return isReading && position >= markerPos && dataLength == bufferSize;
        }

        bool OnFirstBuffer(int position)
        {
            if (position >= 0 && position < bufferSize)
            {
                return true;
            }
            else if (position >= bufferSize && position <= bufferSize + dataLength)
            {
                return false;
            }
            else //Invalid buffer position.
            {
                throw new Exception("Out of seeking range.");
            }
        }

        private void SwapBuffer()
        {
            byte[] temp = firstBuffer;
            firstBuffer = secondBuffer;
            secondBuffer = temp;
            position -= bufferSize;

            FillBuffer();
        }

        private void FillBuffer()
        {
            dataLength = baseStream.Read(secondBuffer, 0, bufferSize);
            basePosition = baseStream.Position;
        }

        public byte[] ReadBytes(int count)
        {
            byte[] data = PeekBytes(count);

            position += count;
            CheckPosition();
            return data;
        }

        public byte[] PeekBytes(int count)
        {
            if (count < 0)
            {
                throw new ArgumentOutOfRangeException("count can't be negative");
            }
            else if (position + count > bufferSize + dataLength)
            {
                //count too big
                count = bufferSize + dataLength - position;
            }
            byte[] data = new byte[count];

            if (position + count <= bufferSize)
            {
                Buffer.BlockCopy(firstBuffer, position, data, 0, count);
            }
            else// if (position + count <= bufferSize + dataLength)
            {
                if (position < bufferSize)
                {
                    int firstBytes = bufferSize - position;
                    Buffer.BlockCopy(firstBuffer, position, data, 0, firstBytes);
                    Buffer.BlockCopy(secondBuffer, 0, data, firstBytes, count - firstBytes);
                }
                else
                {
                    Buffer.BlockCopy(secondBuffer, position - bufferSize, data, 0, count);
                }
            }
            return data;
        }

        public byte[] ReadToEnd()
        {
            int bytesRemained = (int)(Length - Position);
            return ReadBytes(bytesRemained);
        }

        public long Position
        {
            get
            {
                long delta = bufferSize + dataLength - position;
                return basePosition - delta;
            }
            set
            {
                int delta = (int)(value - this.Position);
                position += delta;
                CheckPosition();
            }
        }

        public long Length
        {
            get { return baseStream.Length; }
        }

        public bool IsEndReached
        {
            get { return Position == Length; }
        }

        public Stream BaseStream
        {
            get { return baseStream; }
            set { baseStream = value; }
        }

        /// <summary>
        /// clear buffer and fix stream position.
        /// To read again, call BeginRead().
        /// </summary>
        public void EndRead()
        {
            baseStream.Position = this.Position;
            position = -1;
            dataLength = 0;
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect YunCheDa Hangzhou
China China
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions