XamlStream.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / Framework / System / Windows / Markup / XamlStream.cs / 1 / XamlStream.cs

                            /****************************************************************************\ 
*
* File: XamlStream.cs
*
* Purpose:  Contains the Reader/Writer stream implementation for 
*           Doing async parsing on a separate thread.
* 
*       Todo: Make Sure Reader/Writer calls are on proper Threads 
*       Todo: Add logic to stop the Writer if buffer >> than
*               What is being loaded into the Tree. 
*       Todo: Investigate if worthwhile keeping a cache of Buffers
*               That get re-used instead of allocating one each time.
*       Todo: Review if ReadByte needs to be optimized
*       Todo: Need End Of File Markers so reader doesn't have to rely on 
*               getting the BamlEndRecord.
* 
// History: 
//    6/06/02:    rogerg        Created
//    5/27/03:    peterost      Ported to wcp 
*
* Copyright (C) 2002 by Microsoft Corporation.  All rights reserved.
*
\***************************************************************************/ 

using System; 
using System.Xml; 
using System.IO;
using System.Windows; 
using System.Collections;
using System.ComponentModel;

using System.Diagnostics; 
using System.Reflection;
using System.Threading; 
 
using MS.Utility;
 

#if PBTCOMPILER
namespace MS.Internal.Markup
#else 
namespace System.Windows.Markup
#endif 
{ 

    ///  
    /// Main class for setting up Reader and Writer streams as well as
    /// keeping common data and any synchronization work.
    ///
    /// Writer is allowed write and seek back to any position in the file 
    /// until it calls UpdateReaderLength(position). The position passed in is
    /// an absolute position in the file. After making this call the Writer is 
    /// only allowed to seek and write past this position. 
    ///
    /// The Reader only sees the part of the file that the Writer says is ready 
    /// to read. Initially the length of the Reader stream is zero. When the Writer
    /// calls UpdateReaderLength(position)the length of the Reader stream is set
    /// to that position. The Reader calls ReaderDoneWithFileUpToPosition(position) to indicate
    /// it no longer needs the file up to and including that position. After this call 
    /// it is an error for the Reader to try read bits before the position.
    ///  
    internal class ReadWriteStreamManager 
    {
 


        #region Constructors
 
#if PBTCOMPILER
        // The compiler only needs the class definition, and none of the implementation. 
        private ReadWriteStreamManager() 
#else
        internal ReadWriteStreamManager() 
#endif
        {

            ReaderFirstBufferPosition = 0; 
            WriterFirstBufferPosition = 0;
            ReaderBufferArrayList = new ArrayList(); 
            WriterBufferArrayList = new ArrayList(); 

            _writerStream = new WriterStream(this); 
            _readerStream = new ReaderStream(this);

            _bufferLock = new ReaderWriterLock();
        } 

        #endregion Constructors 
 
        #region WriterCallbacks
 
        /// 
        /// Writes the counts of bytes into the stream.
        /// 
        /// input buffer 
        /// starting offset into the buffer
        /// number of bytes to write 
        internal void Write(byte[] buffer, int offset, int count) 
        {
            int bufferOffset; 
            int bufferIndex;
#if DEBUG
            Debug.Assert(!WriteComplete,"Write called after close");
#endif 
            Debug.Assert(null != buffer,"Null buffer past to the Writer");
 
            // if nothing to write then just return. 
            if (0 == count)
            { 
                return;
            }

            byte[] writeBuffer = GetBufferFromFilePosition( 
                                    WritePosition,false /*writer*/,
                                    out bufferOffset, out bufferIndex); 
 
            Debug.Assert(null != writeBuffer,"Null writeBuffer returned");
 
            // see how many bits fit into this buffer.
            int availableBytesInBuffer =  BufferSize - bufferOffset;
            int leftOverBytes = 0;
            int bufferWriteCount = 0; 

            // check if ther is enough room in the write buffer to meet the 
            // request or if there will be leftOverBytes. 
            if (count > availableBytesInBuffer)
            { 
                bufferWriteCount = availableBytesInBuffer;
                leftOverBytes = count - availableBytesInBuffer;
            }
            else 
            {
                leftOverBytes = 0; 
                bufferWriteCount = count; 
            }
 
            Debug.Assert(0 < bufferWriteCount,"Not writing any bytes to the buffer");

            // now loop through writing out all or the number of bits that can fit in the buffer.
            for (int loopCount = 0; loopCount < bufferWriteCount; loopCount++) 
            {
                Debug.Assert(bufferOffset < BufferSize,"Trying to Read past bufer"); 
 
                writeBuffer[bufferOffset++] = buffer[offset++];
            } 

            // update the writePosition
            WritePosition += bufferWriteCount;
 
            // check if need to update length of the file that the writer sees.
            if (WritePosition > WriteLength) 
            { 
                WriteLength = WritePosition;
            } 

            // if we have any leftOver Bytes call Write Again.
            if (leftOverBytes > 0)
            { 
                Write(buffer,offset,leftOverBytes);
            } 
 
        }
 
        /// 
        /// Adjust the Writer's Seek Pointer.
        /// Writer is not allowed to Seek before where the ReaderLength or
        /// Seek past the writeLength. 
        /// 
        /// seek offset 
        /// specifies how to interpret the seeek offset 
        /// 
        internal long WriterSeek(long offset, SeekOrigin loc) 
        {
            switch(loc)
            {
                case SeekOrigin.Begin: 
                    WritePosition = (int) offset;
                    break; 
                case SeekOrigin.Current: 
                    WritePosition = (int) (WritePosition + offset);
                    break; 
                case SeekOrigin.End:
                    throw new NotSupportedException(SR.Get(SRID.ParserWriterNoSeekEnd));
                default:
                    throw new ArgumentException(SR.Get(SRID.ParserWriterUnknownOrigin)); 
            }
 
            if( (!( WritePosition <= WriteLength )) 
                ||
                (!( WritePosition >= ReadLength  )) ) 
            {
                throw new ArgumentOutOfRangeException( "offset" );
            }
 
            return WritePosition;
        } 
 

        ///  
        /// Called by the Writer to indicate its okay for the reader to see
        /// the file up to and including the position. Once the Writer calls
        /// this it cannot go back and change the content.
        ///  
        /// Absolute position in the stream
        internal void UpdateReaderLength(long position) 
        { 
            if(!(ReadLength <= position))
            { 
                throw new ArgumentOutOfRangeException( "position" );
            }
#if DEBUG
            Debug.Assert(!WriteComplete,"UpdateReaderLength called after close"); 
#endif
 
            ReadLength = position; 

            if(!(ReadLength <= WriteLength)) 
            {
                throw new ArgumentOutOfRangeException( "position" );
            }
 
            // safe for them to check and remove unused buffers.
            CheckIfCanRemoveFromArrayList(position,WriterBufferArrayList, 
                                                ref _writerFirstBufferPosition); 
        }
 
        /// 
        /// Closes the Writer Stream
        /// 
        internal void WriterClose() 
        {
#if DEBUG 
            _writeComplete = true; 
#endif
        } 

        #endregion WriterCallbacks

 
        #region ReaderCallbacks
 
        ///  
        /// Reads the specified number of bytes into the buffer
        ///  
        /// buffer to add the bytes
        /// zero base starting offset
        /// number of bytes to read
        ///  
        internal int Read(byte[] buffer, int offset, int count)
        { 
            if(!(count  + ReadPosition <= ReadLength)) 
            {
                throw new ArgumentOutOfRangeException( "count" ); 
            }
            int bufferOffset;
            int bufferIndex;
 
            byte[] readBuffer = GetBufferFromFilePosition(
                ReadPosition,true /*reader*/, 
                out bufferOffset, out bufferIndex); 

 
            Debug.Assert(bufferOffset < BufferSize,"Calculated bufferOffset is greater than buffer");

            // see how many bytes we can read from this buffer.
            int availableBytesInBuffer =  BufferSize - bufferOffset; 
            int leftOverBytes = 0;
            int bufferReadCount = 0; 
 
            // check if ther is enough room in the write buffer to meet the
            // request or if there will be leftOverBytes. 
            if (count > availableBytesInBuffer)
            {
                bufferReadCount = availableBytesInBuffer;
                leftOverBytes = count - availableBytesInBuffer; 
            }
            else 
            { 
                leftOverBytes = 0;
                bufferReadCount = count; 
            }

            Debug.Assert(0 < bufferReadCount,"Not reading any bytes to the buffer");
 
            for (int loopCount = 0; loopCount < bufferReadCount; loopCount++)
            { 
                // make sure not going over the buffer. 
                Debug.Assert(bufferOffset < BufferSize,"Trying ot read past buffer");
                buffer[offset++] = readBuffer[bufferOffset++]; 
            }

            // update the read position
            ReadPosition += bufferReadCount; 

 
            if (leftOverBytes > 0) 
            {
                Read(buffer,offset,(int) leftOverBytes); 
            }

            return count;
        } 

 
        ///  
        /// Called to Read a Byte from the file. for now we allocate a byte
        /// and call the standard Read method. 
        /// 
        /// 
        internal int ReadByte()
        { 
            // todo, make a better implementation.
            byte[] buffer = new byte[1]; 
 
            // uses Read to validate if reading past the end of the file.
            Read(buffer,0,1); 
            return (int) buffer[0];
        }

        ///  
        /// Adjusts the Reader Seek position.
        ///  
        /// offset for the seek 
        /// defines relative offset 
        ///  
        internal long ReaderSeek(long offset, SeekOrigin loc)
        {
            switch(loc)
            { 
                case SeekOrigin.Begin:
                    ReadPosition = (int) offset; 
                    break; 
                case SeekOrigin.Current:
                    ReadPosition = (int) (ReadPosition + offset); 
                    break;
                case SeekOrigin.End:
                    throw new NotSupportedException(SR.Get(SRID.ParserWriterNoSeekEnd));
                default: 
                    throw new ArgumentException(SR.Get(SRID.ParserWriterUnknownOrigin));
            } 
 
            // validate if at a good readPosition.
            if((!(ReadPosition >= ReaderFirstBufferPosition)) 
                ||
               (!(ReadPosition < ReadLength)))
            {
                throw new ArgumentOutOfRangeException( "offset" ); 
            }
 
            return ReadPosition; 
        }
 
        /// 
        /// called by Reader to tell us it is done with everything
        /// up to  the given position. Once making this call the
        /// Reader can no long reader at and before the position. 
        /// 
        ///  
        internal void ReaderDoneWithFileUpToPosition(long position) 
        {
            // call CheckIfCanRemove to update the readers BufferPositions 
            // and do any cleanup.
            CheckIfCanRemoveFromArrayList(position,ReaderBufferArrayList,
                                ref _readerFirstBufferPosition);
        } 

 
        #endregion ReaderCallbacks 

 
        #region PrivateMethods

        /// 
        /// Given a position in the Stream returns the buffer and 
        /// start offset position in the buffer
         ///  
        byte[] GetBufferFromFilePosition(long position,bool reader, 
            out int bufferOffset,out int bufferIndex)
        { 
            byte[] buffer = null;

            // get bufferArray and firstBuffer position based
            // on if being called by the reader or the writer. 
            ArrayList bufferArray; // arraylist of buffers
            long firstBufferPosition; // absolute file position of first buffer in the arrayList 
 

            // Ensure that while buffer and buffer position are stable while calculating 
            // buffer offsets and retrieving the buffer.  The tokenizer thread can call
            // CheckIfCanRemoveFromArrayList, which uses this same lock when modifying the
            // writer buffer.
            _bufferLock.AcquireWriterLock(-1); 

            if (reader) 
            { 
                bufferArray = ReaderBufferArrayList;
                firstBufferPosition = ReaderFirstBufferPosition; 
            }
            else
            {
                bufferArray = WriterBufferArrayList; 
                firstBufferPosition =  WriterFirstBufferPosition;
 
            } 

            // calc get the bufferIndex 
            bufferIndex = (int) ((position - firstBufferPosition)/BufferSize);

            // calc the byte offset in the buffer for the position
            bufferOffset = 
                (int) ((position - firstBufferPosition) - (bufferIndex*BufferSize));
 
            Debug.Assert(bufferOffset < BufferSize,"Calculated bufferOffset is greater than buffer"); 

            // check if we need to allocate a new buffer. 
            if (bufferArray.Count <= bufferIndex)
            {
                Debug.Assert(bufferArray.Count == bufferIndex,"Need to allocate more than one buffer");
                Debug.Assert(false == reader,"Allocating a buffer on Read"); 

                buffer = new byte[BufferSize]; 
 
                // add to both the reader and writer ArrayLists.
                ReaderBufferArrayList.Add(buffer); 
                WriterBufferArrayList.Add(buffer);
            }
            else
            { 
                // just use the buffer that is there.
                buffer =  bufferArray[bufferIndex] as byte[]; 
            } 
            _bufferLock.ReleaseWriterLock();
 
            return buffer;
        }

        ///  
        /// helper function called by to check is any memory buffers
        /// can be safely removed. 
        ///  
        /// Absolute File Position
        /// ArrayList containing the Memory 
        /// If any arrays are cleaned up returns the
        /// updated position that the first array in the buffer starts at
         void CheckIfCanRemoveFromArrayList(long position,ArrayList arrayList,ref long firstBufferPosition)
        { 

            // see if there are any buffers we can get rid of. 
            int bufferIndex = (int) ((position - firstBufferPosition)/BufferSize); 

            if (bufferIndex > 0) 
            {
                // we can safely remove all previous buffers from the ArrayList.
                int numBuffersToRemove = bufferIndex;
 
                // Ensure that while modifying the buffer position and buffer list that
                // another thread can't get partially updated information while 
                // calling GetBufferFromFilePosition(). 
                _bufferLock.AcquireWriterLock(-1);
 
                // update buffer position offset for number of buffers to be
                // removed.
                firstBufferPosition += numBuffersToRemove*BufferSize;
 
                arrayList.RemoveRange(0,bufferIndex);
 
                _bufferLock.ReleaseWriterLock(); 

            } 

        }

        #endregion PrivateMethods 

 
        #region Properties 

        ///  
        /// WriterStream instance
        /// 
        internal WriterStream WriterStream
        { 
            get { return _writerStream; }
        } 
 
        /// 
        /// ReaderStream instance 
        /// 
        internal ReaderStream ReaderStream
        {
            get { return _readerStream; } 
        }
 
        ///  
        /// Current position inthe Reader stream
        ///  
        internal long ReadPosition
        {
            get { return _readPosition; }
            set { _readPosition = value; } 
        }
 
 
        /// 
        /// Length of the Reader stream 
        /// 
        internal long ReadLength
        {
            get { return _readLength; } 
            set { _readLength = value; }
        } 
 
        /// 
        /// Current position in the writer stream 
        /// 
        internal long WritePosition
        {
            get { return _writePosition ; } 
            set { _writePosition = value; }
        } 
 
        /// 
        /// current length of the writer stream 
        /// 
        internal long WriteLength
        {
            get { return _writeLength ; } 
            set { _writeLength = value; }
        } 
 

        ///  
        /// Constant Buffer size to be used for all allocated buffers
        /// 
        int BufferSize
        { 
            get { return _bufferSize; }
        } 
 

        ///  
        /// File Position that the first buffer in the Readers array of buffer starts at
        /// 
        long ReaderFirstBufferPosition
        { 
            get { return _readerFirstBufferPosition ; }
            set { _readerFirstBufferPosition = value; } 
        } 

        ///  
        /// File Position that the first buffer in the Writers array of buffer starts at
        /// 
        long WriterFirstBufferPosition
        { 
            get { return _writerFirstBufferPosition ; }
            set { _writerFirstBufferPosition = value; } 
        } 

        ///  
        /// ArrayList containing all the buffers used by the Reader
        /// 
        ArrayList ReaderBufferArrayList
        { 
            get { return _readerBufferArrayList ; }
            set { _readerBufferArrayList = value; } 
        } 

        ///  
        /// ArrayList of all the buffers used by the Writer.
        /// 
        ArrayList WriterBufferArrayList
        { 
            get { return _writerBufferArrayList ; }
            set { _writerBufferArrayList = value; } 
        } 

#if DEBUG 
        /// 
        /// Set when all bytes have been written
        /// 
        internal bool WriteComplete 
        {
            get { return _writeComplete; } 
        } 
#endif
        #endregion Properties 


        #region Data
 
        long _readPosition;
        long _readLength; 
        long _writePosition; 
        long _writeLength;
 
        ReaderWriterLock _bufferLock;

        WriterStream _writerStream;
        ReaderStream _readerStream; 
        long _readerFirstBufferPosition;
        long _writerFirstBufferPosition; 
        ArrayList _readerBufferArrayList; 
        ArrayList _writerBufferArrayList;
#if DEBUG 
        bool _writeComplete;
#endif

        // size of each allocated buffer. 
        private const int _bufferSize = 4096;
 
        #endregion Data 
    }
 
    /// 
    /// Writer Stream class.
    /// This is the Stream implementation the Writer sees.
    ///  
    internal class WriterStream : Stream
    { 
 
        #region Constructor
 
        /// 
        /// Constructor
        /// 
        /// StreamManager that the writer stream should use 
        internal WriterStream(ReadWriteStreamManager streamManager)
        { 
            _streamManager = streamManager; 
        }
 
        #endregion Constructor

        #region overrides
 
        /// 
        /// Override of Stream.CanRead 
        ///  
        public override bool CanRead
        { 
            get { return false; }
        }

        ///  
        /// Override of Stream.CanSeek
        ///  
        public override bool CanSeek 
        {
            get { return true; } 
        }

        /// 
        /// Override of Stream.CanWrite 
        /// 
        public override bool CanWrite 
        { 
            get { return true; }
        } 

        /// 
        /// Override of Stream.Close
        ///  
        public override void Close()
        { 
             StreamManager.WriterClose(); 
        }
 
        /// 
        /// Override of Stream.Flush
        /// 
        public override void Flush() 
        {
            return; // nothing to Flush 
        } 

        ///  
        /// Override of Stream.Length
        /// 
        public override long Length
        { 
            get
            { 
                return StreamManager.WriteLength; 
            }
        } 

        /// 
        /// Override of Stream.Position
        ///  
        public override long Position
        { 
            get 
            {
                return -1; 
            }
            set
            {
                throw new NotSupportedException(); 
            }
        } 
 
        /// 
        /// Override of Stream.Read 
        /// 
        public override int Read(byte[] buffer, int offset, int count)
        {
            throw new NotSupportedException(); 
        }
 
        ///  
        /// Override of Stream.ReadByte
        ///  
        public override int ReadByte()
        {
            throw new NotSupportedException();
        } 

        ///  
        /// Override of Stream.Seek 
        /// 
        public override long Seek(long offset, SeekOrigin loc) 
        {
            return StreamManager.WriterSeek(offset,loc);
        }
 
        /// 
        /// Override of Stream.SetLength 
        ///  
        public override void SetLength(long value)
        { 
            throw new NotSupportedException();
        }

 
        /// 
        /// Override of Stream.Write 
        ///  
        public override void Write(byte[] buffer, int offset, int count)
        { 
            StreamManager.Write(buffer,offset,count);
        }

        #endregion overrides 

 
        #region Methods 

        ///  
        /// Called by the writer to say its okay to let the reader see what
        /// it has written up to the position.
        /// 
        /// Absolute position inthe file 
        internal void UpdateReaderLength(long position)
        { 
            StreamManager.UpdateReaderLength(position); 
        }
 

        #endregion Methods

        #region Properties 

        ///  
        /// StreamManager for the writer stream 
        /// 
        ReadWriteStreamManager StreamManager 
        {
            get { return _streamManager; }
        }
 

        #endregion Properties 
 
        #region Data
 
        ReadWriteStreamManager _streamManager;

        #endregion Data
 
    }
 
 
    ///  
    ///  Reader Stream class 
    /// This is the Stream implementation the Writer sees.
    /// 
    internal class ReaderStream : Stream
    { 

        #region Constructor 
 
        internal ReaderStream(ReadWriteStreamManager streamManager)
        { 
            _streamManager = streamManager;
        }

        #endregion Constructor 

        #region overrides 
 
        /// 
        /// Override of Stream.CanRead 
        /// 
        public override bool CanRead
        {
            get { return true; } 
        }
 
        ///  
        /// Override of Stream.CanSeek
        ///  
        public override bool CanSeek
        {
            get { return true; }
        } 

        ///  
        /// Override of Stream.CanWrite 
        /// 
        public override bool CanWrite 
        {
            get { return false; }
        }
 
        /// 
        /// Override of Stream.Close 
        ///  
        public override void Close()
        { 
            Debug.Assert(false,"Close called on ReaderStream");
        }

        ///  
        /// Override of Stream.Flush
        ///  
        public override void Flush() 
        {
            Debug.Assert(false,"Flush called on ReaderStream"); 

        }

        ///  
        /// Override of Stream.Length
        ///  
        public override long Length 
        {
            get 
            {
                return StreamManager.ReadLength;
            }
        } 

        ///  
        /// Override of Stream.Position 
        /// 
        public override long Position 
        {
            get
            {
                return StreamManager.ReadPosition; 
            }
            set 
            { 
                StreamManager.ReaderSeek(value,SeekOrigin.Begin);
            } 
        }

        /// 
        /// Override of Stream.Read 
        /// 
        public override int Read(byte[] buffer, int offset, int count) 
        { 
            return StreamManager.Read(buffer,offset,count);
        } 

        /// 
        /// Override of Stream.ReadByte
        ///  
        public override int ReadByte()
        { 
            return StreamManager.ReadByte(); 
        }
 

        /// 
        /// Override of Stream.Seek
        ///  
        public override long Seek(long offset, SeekOrigin loc)
        { 
            return StreamManager.ReaderSeek(offset,loc); 
        }
 
        /// 
        /// Override of Stream.SetLength
        /// 
        public override void SetLength(long value) 
        {
            throw new NotSupportedException(); 
        } 

 
        /// 
        /// Override of Stream.Write
        /// 
        public override void Write(byte[] buffer, int offset, int count) 
        {
             throw new NotSupportedException(); 
        } 

        #endregion Overrides 

        #region Methods

        ///  
        /// Called by the reader to indicate all bytes up to and
        /// including the position are no longer needed 
        /// After making this call the reader cannot go back and 
        /// read this data
        ///  
        /// 
        internal void ReaderDoneWithFileUpToPosition(long position)
        {
            StreamManager.ReaderDoneWithFileUpToPosition(position); 
        }
 
#if DEBUG 
        internal bool IsWriteComplete
        { 
             get { return StreamManager.WriteComplete; }
        }
#endif
        #endregion Methods 

        #region Properties 
 
        /// 
        /// StreamManager that this class should use 
        /// 
        ReadWriteStreamManager StreamManager
        {
            get { return _streamManager; } 
        }
 
        #endregion Properties 

        #region Data 

        ReadWriteStreamManager _streamManager;

        #endregion Data 

    } 
 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
/****************************************************************************\ 
*
* File: XamlStream.cs
*
* Purpose:  Contains the Reader/Writer stream implementation for 
*           Doing async parsing on a separate thread.
* 
*       Todo: Make Sure Reader/Writer calls are on proper Threads 
*       Todo: Add logic to stop the Writer if buffer >> than
*               What is being loaded into the Tree. 
*       Todo: Investigate if worthwhile keeping a cache of Buffers
*               That get re-used instead of allocating one each time.
*       Todo: Review if ReadByte needs to be optimized
*       Todo: Need End Of File Markers so reader doesn't have to rely on 
*               getting the BamlEndRecord.
* 
// History: 
//    6/06/02:    rogerg        Created
//    5/27/03:    peterost      Ported to wcp 
*
* Copyright (C) 2002 by Microsoft Corporation.  All rights reserved.
*
\***************************************************************************/ 

using System; 
using System.Xml; 
using System.IO;
using System.Windows; 
using System.Collections;
using System.ComponentModel;

using System.Diagnostics; 
using System.Reflection;
using System.Threading; 
 
using MS.Utility;
 

#if PBTCOMPILER
namespace MS.Internal.Markup
#else 
namespace System.Windows.Markup
#endif 
{ 

    ///  
    /// Main class for setting up Reader and Writer streams as well as
    /// keeping common data and any synchronization work.
    ///
    /// Writer is allowed write and seek back to any position in the file 
    /// until it calls UpdateReaderLength(position). The position passed in is
    /// an absolute position in the file. After making this call the Writer is 
    /// only allowed to seek and write past this position. 
    ///
    /// The Reader only sees the part of the file that the Writer says is ready 
    /// to read. Initially the length of the Reader stream is zero. When the Writer
    /// calls UpdateReaderLength(position)the length of the Reader stream is set
    /// to that position. The Reader calls ReaderDoneWithFileUpToPosition(position) to indicate
    /// it no longer needs the file up to and including that position. After this call 
    /// it is an error for the Reader to try read bits before the position.
    ///  
    internal class ReadWriteStreamManager 
    {
 


        #region Constructors
 
#if PBTCOMPILER
        // The compiler only needs the class definition, and none of the implementation. 
        private ReadWriteStreamManager() 
#else
        internal ReadWriteStreamManager() 
#endif
        {

            ReaderFirstBufferPosition = 0; 
            WriterFirstBufferPosition = 0;
            ReaderBufferArrayList = new ArrayList(); 
            WriterBufferArrayList = new ArrayList(); 

            _writerStream = new WriterStream(this); 
            _readerStream = new ReaderStream(this);

            _bufferLock = new ReaderWriterLock();
        } 

        #endregion Constructors 
 
        #region WriterCallbacks
 
        /// 
        /// Writes the counts of bytes into the stream.
        /// 
        /// input buffer 
        /// starting offset into the buffer
        /// number of bytes to write 
        internal void Write(byte[] buffer, int offset, int count) 
        {
            int bufferOffset; 
            int bufferIndex;
#if DEBUG
            Debug.Assert(!WriteComplete,"Write called after close");
#endif 
            Debug.Assert(null != buffer,"Null buffer past to the Writer");
 
            // if nothing to write then just return. 
            if (0 == count)
            { 
                return;
            }

            byte[] writeBuffer = GetBufferFromFilePosition( 
                                    WritePosition,false /*writer*/,
                                    out bufferOffset, out bufferIndex); 
 
            Debug.Assert(null != writeBuffer,"Null writeBuffer returned");
 
            // see how many bits fit into this buffer.
            int availableBytesInBuffer =  BufferSize - bufferOffset;
            int leftOverBytes = 0;
            int bufferWriteCount = 0; 

            // check if ther is enough room in the write buffer to meet the 
            // request or if there will be leftOverBytes. 
            if (count > availableBytesInBuffer)
            { 
                bufferWriteCount = availableBytesInBuffer;
                leftOverBytes = count - availableBytesInBuffer;
            }
            else 
            {
                leftOverBytes = 0; 
                bufferWriteCount = count; 
            }
 
            Debug.Assert(0 < bufferWriteCount,"Not writing any bytes to the buffer");

            // now loop through writing out all or the number of bits that can fit in the buffer.
            for (int loopCount = 0; loopCount < bufferWriteCount; loopCount++) 
            {
                Debug.Assert(bufferOffset < BufferSize,"Trying to Read past bufer"); 
 
                writeBuffer[bufferOffset++] = buffer[offset++];
            } 

            // update the writePosition
            WritePosition += bufferWriteCount;
 
            // check if need to update length of the file that the writer sees.
            if (WritePosition > WriteLength) 
            { 
                WriteLength = WritePosition;
            } 

            // if we have any leftOver Bytes call Write Again.
            if (leftOverBytes > 0)
            { 
                Write(buffer,offset,leftOverBytes);
            } 
 
        }
 
        /// 
        /// Adjust the Writer's Seek Pointer.
        /// Writer is not allowed to Seek before where the ReaderLength or
        /// Seek past the writeLength. 
        /// 
        /// seek offset 
        /// specifies how to interpret the seeek offset 
        /// 
        internal long WriterSeek(long offset, SeekOrigin loc) 
        {
            switch(loc)
            {
                case SeekOrigin.Begin: 
                    WritePosition = (int) offset;
                    break; 
                case SeekOrigin.Current: 
                    WritePosition = (int) (WritePosition + offset);
                    break; 
                case SeekOrigin.End:
                    throw new NotSupportedException(SR.Get(SRID.ParserWriterNoSeekEnd));
                default:
                    throw new ArgumentException(SR.Get(SRID.ParserWriterUnknownOrigin)); 
            }
 
            if( (!( WritePosition <= WriteLength )) 
                ||
                (!( WritePosition >= ReadLength  )) ) 
            {
                throw new ArgumentOutOfRangeException( "offset" );
            }
 
            return WritePosition;
        } 
 

        ///  
        /// Called by the Writer to indicate its okay for the reader to see
        /// the file up to and including the position. Once the Writer calls
        /// this it cannot go back and change the content.
        ///  
        /// Absolute position in the stream
        internal void UpdateReaderLength(long position) 
        { 
            if(!(ReadLength <= position))
            { 
                throw new ArgumentOutOfRangeException( "position" );
            }
#if DEBUG
            Debug.Assert(!WriteComplete,"UpdateReaderLength called after close"); 
#endif
 
            ReadLength = position; 

            if(!(ReadLength <= WriteLength)) 
            {
                throw new ArgumentOutOfRangeException( "position" );
            }
 
            // safe for them to check and remove unused buffers.
            CheckIfCanRemoveFromArrayList(position,WriterBufferArrayList, 
                                                ref _writerFirstBufferPosition); 
        }
 
        /// 
        /// Closes the Writer Stream
        /// 
        internal void WriterClose() 
        {
#if DEBUG 
            _writeComplete = true; 
#endif
        } 

        #endregion WriterCallbacks

 
        #region ReaderCallbacks
 
        ///  
        /// Reads the specified number of bytes into the buffer
        ///  
        /// buffer to add the bytes
        /// zero base starting offset
        /// number of bytes to read
        ///  
        internal int Read(byte[] buffer, int offset, int count)
        { 
            if(!(count  + ReadPosition <= ReadLength)) 
            {
                throw new ArgumentOutOfRangeException( "count" ); 
            }
            int bufferOffset;
            int bufferIndex;
 
            byte[] readBuffer = GetBufferFromFilePosition(
                ReadPosition,true /*reader*/, 
                out bufferOffset, out bufferIndex); 

 
            Debug.Assert(bufferOffset < BufferSize,"Calculated bufferOffset is greater than buffer");

            // see how many bytes we can read from this buffer.
            int availableBytesInBuffer =  BufferSize - bufferOffset; 
            int leftOverBytes = 0;
            int bufferReadCount = 0; 
 
            // check if ther is enough room in the write buffer to meet the
            // request or if there will be leftOverBytes. 
            if (count > availableBytesInBuffer)
            {
                bufferReadCount = availableBytesInBuffer;
                leftOverBytes = count - availableBytesInBuffer; 
            }
            else 
            { 
                leftOverBytes = 0;
                bufferReadCount = count; 
            }

            Debug.Assert(0 < bufferReadCount,"Not reading any bytes to the buffer");
 
            for (int loopCount = 0; loopCount < bufferReadCount; loopCount++)
            { 
                // make sure not going over the buffer. 
                Debug.Assert(bufferOffset < BufferSize,"Trying ot read past buffer");
                buffer[offset++] = readBuffer[bufferOffset++]; 
            }

            // update the read position
            ReadPosition += bufferReadCount; 

 
            if (leftOverBytes > 0) 
            {
                Read(buffer,offset,(int) leftOverBytes); 
            }

            return count;
        } 

 
        ///  
        /// Called to Read a Byte from the file. for now we allocate a byte
        /// and call the standard Read method. 
        /// 
        /// 
        internal int ReadByte()
        { 
            // todo, make a better implementation.
            byte[] buffer = new byte[1]; 
 
            // uses Read to validate if reading past the end of the file.
            Read(buffer,0,1); 
            return (int) buffer[0];
        }

        ///  
        /// Adjusts the Reader Seek position.
        ///  
        /// offset for the seek 
        /// defines relative offset 
        ///  
        internal long ReaderSeek(long offset, SeekOrigin loc)
        {
            switch(loc)
            { 
                case SeekOrigin.Begin:
                    ReadPosition = (int) offset; 
                    break; 
                case SeekOrigin.Current:
                    ReadPosition = (int) (ReadPosition + offset); 
                    break;
                case SeekOrigin.End:
                    throw new NotSupportedException(SR.Get(SRID.ParserWriterNoSeekEnd));
                default: 
                    throw new ArgumentException(SR.Get(SRID.ParserWriterUnknownOrigin));
            } 
 
            // validate if at a good readPosition.
            if((!(ReadPosition >= ReaderFirstBufferPosition)) 
                ||
               (!(ReadPosition < ReadLength)))
            {
                throw new ArgumentOutOfRangeException( "offset" ); 
            }
 
            return ReadPosition; 
        }
 
        /// 
        /// called by Reader to tell us it is done with everything
        /// up to  the given position. Once making this call the
        /// Reader can no long reader at and before the position. 
        /// 
        ///  
        internal void ReaderDoneWithFileUpToPosition(long position) 
        {
            // call CheckIfCanRemove to update the readers BufferPositions 
            // and do any cleanup.
            CheckIfCanRemoveFromArrayList(position,ReaderBufferArrayList,
                                ref _readerFirstBufferPosition);
        } 

 
        #endregion ReaderCallbacks 

 
        #region PrivateMethods

        /// 
        /// Given a position in the Stream returns the buffer and 
        /// start offset position in the buffer
         ///  
        byte[] GetBufferFromFilePosition(long position,bool reader, 
            out int bufferOffset,out int bufferIndex)
        { 
            byte[] buffer = null;

            // get bufferArray and firstBuffer position based
            // on if being called by the reader or the writer. 
            ArrayList bufferArray; // arraylist of buffers
            long firstBufferPosition; // absolute file position of first buffer in the arrayList 
 

            // Ensure that while buffer and buffer position are stable while calculating 
            // buffer offsets and retrieving the buffer.  The tokenizer thread can call
            // CheckIfCanRemoveFromArrayList, which uses this same lock when modifying the
            // writer buffer.
            _bufferLock.AcquireWriterLock(-1); 

            if (reader) 
            { 
                bufferArray = ReaderBufferArrayList;
                firstBufferPosition = ReaderFirstBufferPosition; 
            }
            else
            {
                bufferArray = WriterBufferArrayList; 
                firstBufferPosition =  WriterFirstBufferPosition;
 
            } 

            // calc get the bufferIndex 
            bufferIndex = (int) ((position - firstBufferPosition)/BufferSize);

            // calc the byte offset in the buffer for the position
            bufferOffset = 
                (int) ((position - firstBufferPosition) - (bufferIndex*BufferSize));
 
            Debug.Assert(bufferOffset < BufferSize,"Calculated bufferOffset is greater than buffer"); 

            // check if we need to allocate a new buffer. 
            if (bufferArray.Count <= bufferIndex)
            {
                Debug.Assert(bufferArray.Count == bufferIndex,"Need to allocate more than one buffer");
                Debug.Assert(false == reader,"Allocating a buffer on Read"); 

                buffer = new byte[BufferSize]; 
 
                // add to both the reader and writer ArrayLists.
                ReaderBufferArrayList.Add(buffer); 
                WriterBufferArrayList.Add(buffer);
            }
            else
            { 
                // just use the buffer that is there.
                buffer =  bufferArray[bufferIndex] as byte[]; 
            } 
            _bufferLock.ReleaseWriterLock();
 
            return buffer;
        }

        ///  
        /// helper function called by to check is any memory buffers
        /// can be safely removed. 
        ///  
        /// Absolute File Position
        /// ArrayList containing the Memory 
        /// If any arrays are cleaned up returns the
        /// updated position that the first array in the buffer starts at
         void CheckIfCanRemoveFromArrayList(long position,ArrayList arrayList,ref long firstBufferPosition)
        { 

            // see if there are any buffers we can get rid of. 
            int bufferIndex = (int) ((position - firstBufferPosition)/BufferSize); 

            if (bufferIndex > 0) 
            {
                // we can safely remove all previous buffers from the ArrayList.
                int numBuffersToRemove = bufferIndex;
 
                // Ensure that while modifying the buffer position and buffer list that
                // another thread can't get partially updated information while 
                // calling GetBufferFromFilePosition(). 
                _bufferLock.AcquireWriterLock(-1);
 
                // update buffer position offset for number of buffers to be
                // removed.
                firstBufferPosition += numBuffersToRemove*BufferSize;
 
                arrayList.RemoveRange(0,bufferIndex);
 
                _bufferLock.ReleaseWriterLock(); 

            } 

        }

        #endregion PrivateMethods 

 
        #region Properties 

        ///  
        /// WriterStream instance
        /// 
        internal WriterStream WriterStream
        { 
            get { return _writerStream; }
        } 
 
        /// 
        /// ReaderStream instance 
        /// 
        internal ReaderStream ReaderStream
        {
            get { return _readerStream; } 
        }
 
        ///  
        /// Current position inthe Reader stream
        ///  
        internal long ReadPosition
        {
            get { return _readPosition; }
            set { _readPosition = value; } 
        }
 
 
        /// 
        /// Length of the Reader stream 
        /// 
        internal long ReadLength
        {
            get { return _readLength; } 
            set { _readLength = value; }
        } 
 
        /// 
        /// Current position in the writer stream 
        /// 
        internal long WritePosition
        {
            get { return _writePosition ; } 
            set { _writePosition = value; }
        } 
 
        /// 
        /// current length of the writer stream 
        /// 
        internal long WriteLength
        {
            get { return _writeLength ; } 
            set { _writeLength = value; }
        } 
 

        ///  
        /// Constant Buffer size to be used for all allocated buffers
        /// 
        int BufferSize
        { 
            get { return _bufferSize; }
        } 
 

        ///  
        /// File Position that the first buffer in the Readers array of buffer starts at
        /// 
        long ReaderFirstBufferPosition
        { 
            get { return _readerFirstBufferPosition ; }
            set { _readerFirstBufferPosition = value; } 
        } 

        ///  
        /// File Position that the first buffer in the Writers array of buffer starts at
        /// 
        long WriterFirstBufferPosition
        { 
            get { return _writerFirstBufferPosition ; }
            set { _writerFirstBufferPosition = value; } 
        } 

        ///  
        /// ArrayList containing all the buffers used by the Reader
        /// 
        ArrayList ReaderBufferArrayList
        { 
            get { return _readerBufferArrayList ; }
            set { _readerBufferArrayList = value; } 
        } 

        ///  
        /// ArrayList of all the buffers used by the Writer.
        /// 
        ArrayList WriterBufferArrayList
        { 
            get { return _writerBufferArrayList ; }
            set { _writerBufferArrayList = value; } 
        } 

#if DEBUG 
        /// 
        /// Set when all bytes have been written
        /// 
        internal bool WriteComplete 
        {
            get { return _writeComplete; } 
        } 
#endif
        #endregion Properties 


        #region Data
 
        long _readPosition;
        long _readLength; 
        long _writePosition; 
        long _writeLength;
 
        ReaderWriterLock _bufferLock;

        WriterStream _writerStream;
        ReaderStream _readerStream; 
        long _readerFirstBufferPosition;
        long _writerFirstBufferPosition; 
        ArrayList _readerBufferArrayList; 
        ArrayList _writerBufferArrayList;
#if DEBUG 
        bool _writeComplete;
#endif

        // size of each allocated buffer. 
        private const int _bufferSize = 4096;
 
        #endregion Data 
    }
 
    /// 
    /// Writer Stream class.
    /// This is the Stream implementation the Writer sees.
    ///  
    internal class WriterStream : Stream
    { 
 
        #region Constructor
 
        /// 
        /// Constructor
        /// 
        /// StreamManager that the writer stream should use 
        internal WriterStream(ReadWriteStreamManager streamManager)
        { 
            _streamManager = streamManager; 
        }
 
        #endregion Constructor

        #region overrides
 
        /// 
        /// Override of Stream.CanRead 
        ///  
        public override bool CanRead
        { 
            get { return false; }
        }

        ///  
        /// Override of Stream.CanSeek
        ///  
        public override bool CanSeek 
        {
            get { return true; } 
        }

        /// 
        /// Override of Stream.CanWrite 
        /// 
        public override bool CanWrite 
        { 
            get { return true; }
        } 

        /// 
        /// Override of Stream.Close
        ///  
        public override void Close()
        { 
             StreamManager.WriterClose(); 
        }
 
        /// 
        /// Override of Stream.Flush
        /// 
        public override void Flush() 
        {
            return; // nothing to Flush 
        } 

        ///  
        /// Override of Stream.Length
        /// 
        public override long Length
        { 
            get
            { 
                return StreamManager.WriteLength; 
            }
        } 

        /// 
        /// Override of Stream.Position
        ///  
        public override long Position
        { 
            get 
            {
                return -1; 
            }
            set
            {
                throw new NotSupportedException(); 
            }
        } 
 
        /// 
        /// Override of Stream.Read 
        /// 
        public override int Read(byte[] buffer, int offset, int count)
        {
            throw new NotSupportedException(); 
        }
 
        ///  
        /// Override of Stream.ReadByte
        ///  
        public override int ReadByte()
        {
            throw new NotSupportedException();
        } 

        ///  
        /// Override of Stream.Seek 
        /// 
        public override long Seek(long offset, SeekOrigin loc) 
        {
            return StreamManager.WriterSeek(offset,loc);
        }
 
        /// 
        /// Override of Stream.SetLength 
        ///  
        public override void SetLength(long value)
        { 
            throw new NotSupportedException();
        }

 
        /// 
        /// Override of Stream.Write 
        ///  
        public override void Write(byte[] buffer, int offset, int count)
        { 
            StreamManager.Write(buffer,offset,count);
        }

        #endregion overrides 

 
        #region Methods 

        ///  
        /// Called by the writer to say its okay to let the reader see what
        /// it has written up to the position.
        /// 
        /// Absolute position inthe file 
        internal void UpdateReaderLength(long position)
        { 
            StreamManager.UpdateReaderLength(position); 
        }
 

        #endregion Methods

        #region Properties 

        ///  
        /// StreamManager for the writer stream 
        /// 
        ReadWriteStreamManager StreamManager 
        {
            get { return _streamManager; }
        }
 

        #endregion Properties 
 
        #region Data
 
        ReadWriteStreamManager _streamManager;

        #endregion Data
 
    }
 
 
    ///  
    ///  Reader Stream class 
    /// This is the Stream implementation the Writer sees.
    /// 
    internal class ReaderStream : Stream
    { 

        #region Constructor 
 
        internal ReaderStream(ReadWriteStreamManager streamManager)
        { 
            _streamManager = streamManager;
        }

        #endregion Constructor 

        #region overrides 
 
        /// 
        /// Override of Stream.CanRead 
        /// 
        public override bool CanRead
        {
            get { return true; } 
        }
 
        ///  
        /// Override of Stream.CanSeek
        ///  
        public override bool CanSeek
        {
            get { return true; }
        } 

        ///  
        /// Override of Stream.CanWrite 
        /// 
        public override bool CanWrite 
        {
            get { return false; }
        }
 
        /// 
        /// Override of Stream.Close 
        ///  
        public override void Close()
        { 
            Debug.Assert(false,"Close called on ReaderStream");
        }

        ///  
        /// Override of Stream.Flush
        ///  
        public override void Flush() 
        {
            Debug.Assert(false,"Flush called on ReaderStream"); 

        }

        ///  
        /// Override of Stream.Length
        ///  
        public override long Length 
        {
            get 
            {
                return StreamManager.ReadLength;
            }
        } 

        ///  
        /// Override of Stream.Position 
        /// 
        public override long Position 
        {
            get
            {
                return StreamManager.ReadPosition; 
            }
            set 
            { 
                StreamManager.ReaderSeek(value,SeekOrigin.Begin);
            } 
        }

        /// 
        /// Override of Stream.Read 
        /// 
        public override int Read(byte[] buffer, int offset, int count) 
        { 
            return StreamManager.Read(buffer,offset,count);
        } 

        /// 
        /// Override of Stream.ReadByte
        ///  
        public override int ReadByte()
        { 
            return StreamManager.ReadByte(); 
        }
 

        /// 
        /// Override of Stream.Seek
        ///  
        public override long Seek(long offset, SeekOrigin loc)
        { 
            return StreamManager.ReaderSeek(offset,loc); 
        }
 
        /// 
        /// Override of Stream.SetLength
        /// 
        public override void SetLength(long value) 
        {
            throw new NotSupportedException(); 
        } 

 
        /// 
        /// Override of Stream.Write
        /// 
        public override void Write(byte[] buffer, int offset, int count) 
        {
             throw new NotSupportedException(); 
        } 

        #endregion Overrides 

        #region Methods

        ///  
        /// Called by the reader to indicate all bytes up to and
        /// including the position are no longer needed 
        /// After making this call the reader cannot go back and 
        /// read this data
        ///  
        /// 
        internal void ReaderDoneWithFileUpToPosition(long position)
        {
            StreamManager.ReaderDoneWithFileUpToPosition(position); 
        }
 
#if DEBUG 
        internal bool IsWriteComplete
        { 
             get { return StreamManager.WriteComplete; }
        }
#endif
        #endregion Methods 

        #region Properties 
 
        /// 
        /// StreamManager that this class should use 
        /// 
        ReadWriteStreamManager StreamManager
        {
            get { return _streamManager; } 
        }
 
        #endregion Properties 

        #region Data 

        ReadWriteStreamManager _streamManager;

        #endregion Data 

    } 
 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK