HttpWriter.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / xsp / System / Web / HttpWriter.cs / 1305376 / HttpWriter.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

/* 
 * Response Writer and Stream implementation 
 *
 * Copyright (c) 1998 Microsoft Corporation 
 */

namespace System.Web {
    using System.Collections; 
    using System.Globalization;
    using System.IO; 
    using System.Runtime.Serialization.Formatters; 
    using System.Security.Permissions;
    using System.Text; 
    using System.Threading;
    using System.Web.Util;
    using System.Web.Hosting;
 
    using IIS = System.Web.Hosting.UnsafeIISMethods;
 
    // 
    //  HttpWriter buffer recycling support
    // 

    /*
     * Constants for buffering
     */ 
    internal static class BufferingParams {
        internal static readonly int INTEGRATED_MODE_BUFFER_SIZE = 16*1024 - 4*IntPtr.Size; // native buffer size for integrated mode 
        internal const int OUTPUT_BUFFER_SIZE         = 31*1024;    // output is a chain of this size buffers 
        internal const int MAX_FREE_BYTES_TO_CACHE    = 4096;       // don't compress when taking snapshot if free bytes < this
        internal const int MAX_FREE_OUTPUT_BUFFERS    = 64;         // keep this number of unused buffers 
        internal const int CHAR_BUFFER_SIZE           = 1024;       // size of the buffers for chat conversion to bytes
        internal const int MAX_FREE_CHAR_BUFFERS      = 64;         // keep this number of unused buffers
        internal const int MAX_BYTES_TO_COPY          = 128;        // copy results of char conversion vs using recycleable buffers
        internal const int MAX_RESOURCE_BYTES_TO_COPY = 4*1024;       // resource strings below this size are copied to buffers 
    }
 
    /* 
     * Interface implemented by elements of the response buffer list
     */ 
    internal interface IHttpResponseElement {
        long GetSize();
        byte[] GetBytes();                   // required for filtering
        void Send(HttpWorkerRequest wr); 
    }
 
    /* 
     * Base class for recyclable memory buffer elements
     */ 
    internal abstract class HttpBaseMemoryResponseBufferElement {

        protected int _size;
        protected int _free; 
        protected bool _recycle;
 
        internal int FreeBytes { 
            get { return _free;}
        } 

        internal void DisableRecycling() {
            _recycle = false;
        } 

        // abstract methods 
 
        internal abstract void Recycle();
 
        internal abstract HttpResponseBufferElement Clone();

        internal abstract int Append(byte[] data, int offset, int size);
 
        internal abstract int Append(IntPtr data, int offset, int size);
 
        internal abstract void AppendEncodedChars(char[] data, int offset, int size, Encoder encoder, bool flushEncoder); 
    }
 
    /*
     * Memory response buffer
     */
    internal sealed class HttpResponseBufferElement : HttpBaseMemoryResponseBufferElement, IHttpResponseElement { 
        private byte[] _data;
 
        private static UbyteBufferAllocator s_Allocator = 
        new UbyteBufferAllocator(BufferingParams.OUTPUT_BUFFER_SIZE,
                                 BufferingParams.MAX_FREE_OUTPUT_BUFFERS); 


        /*
         * Constructor that accepts the data buffer and holds on to it 
         */
        internal HttpResponseBufferElement(byte[] data, int size) { 
            _data = data; 
            _size = size;
            _free = 0; 
            _recycle = false;
        }

        /* 
         *  Close the buffer copying the data
         *  (needed to 'compress' buffers for caching) 
         */ 

        internal override HttpResponseBufferElement Clone() { 
            int clonedSize = _size - _free;
            byte[] clonedData = new byte[clonedSize];
            Buffer.BlockCopy(_data, 0, clonedData, 0, clonedSize);
            return new HttpResponseBufferElement(clonedData, clonedSize); 
        }
 
        internal override void Recycle() { 

        } 

        internal override int Append(byte[] data, int offset, int size) {
            if (_free == 0 || size == 0)
                return 0; 
            int n = (_free >= size) ? size : _free;
            Buffer.BlockCopy(data, offset, _data, _size-_free, n); 
            _free -= n; 
            return n;
        } 

        internal override int Append(IntPtr data, int offset, int size) {
            if (_free == 0 || size == 0)
                return 0; 
            int n = (_free >= size) ? size : _free;
            Misc.CopyMemory(data, offset, _data, _size-_free, n); 
            _free -= n; 
            return n;
        } 

        internal override void AppendEncodedChars(char[] data, int offset, int size, Encoder encoder, bool flushEncoder) {
            int byteSize = encoder.GetBytes(data, offset, size, _data, _size-_free, flushEncoder);
            _free -= byteSize; 
        }
 
        // 
        // IHttpResponseElement implementation
        // 

        /*
         * Get number of bytes
         */ 
        long IHttpResponseElement.GetSize() {
            return(_size - _free); 
        } 

        /* 
         * Get bytes (for filtering)
         */
        byte[] IHttpResponseElement.GetBytes() {
            return _data; 
        }
 
        /* 
         * Write HttpWorkerRequest
         */ 
        void IHttpResponseElement.Send(HttpWorkerRequest wr) {
            int n = _size - _free;
            if (n > 0)
                wr.SendResponseFromMemory(_data, n); 
        }
    } 
 
#if !FEATURE_PAL // FEATURE_PAL does not enable IIS-based hosting features
    /* 
     * Unmanaged memory response buffer
     */
    internal sealed class HttpResponseUnmanagedBufferElement : HttpBaseMemoryResponseBufferElement, IHttpResponseElement {
        private IntPtr _data; 
        private static IntPtr s_Pool;
 
        static HttpResponseUnmanagedBufferElement() { 
            if (HttpRuntime.UseIntegratedPipeline) {
                s_Pool = IIS.MgdGetBufferPool(BufferingParams.INTEGRATED_MODE_BUFFER_SIZE); 
            }
            else {
                s_Pool = UnsafeNativeMethods.BufferPoolGetPool(BufferingParams.OUTPUT_BUFFER_SIZE,
                    BufferingParams.MAX_FREE_OUTPUT_BUFFERS); 

            } 
        } 

        /* 
         * Constructor that creates an empty buffer
         */
        internal HttpResponseUnmanagedBufferElement() {
            if (HttpRuntime.UseIntegratedPipeline) { 
                _data = IIS.MgdGetBuffer(s_Pool);
                _size = BufferingParams.INTEGRATED_MODE_BUFFER_SIZE; 
            } 
            else {
                _data = UnsafeNativeMethods.BufferPoolGetBuffer(s_Pool); 
                _size = BufferingParams.OUTPUT_BUFFER_SIZE;
            }
            if (_data == IntPtr.Zero) {
                throw new OutOfMemoryException(); 
            }
            _free = _size; 
            _recycle = true; 
        }
 
        /*
         * dtor - frees the unmanaged buffer
         */
        ~HttpResponseUnmanagedBufferElement() { 
            IntPtr data = Interlocked.Exchange(ref _data, IntPtr.Zero);
            if (data != IntPtr.Zero) { 
                if (HttpRuntime.UseIntegratedPipeline) { 
                    IIS.MgdReturnBuffer(data);
                } 
                else {
                    UnsafeNativeMethods.BufferPoolReleaseBuffer(data);
                }
            } 
        }
 
        /* 
         *  Clone the buffer copying the data int managed buffer
         *  (needed to 'compress' buffers for caching) 
         */
        internal override HttpResponseBufferElement Clone() {
            int clonedSize = _size - _free;
            byte[] clonedData = new byte[clonedSize]; 
            Misc.CopyMemory(_data, 0, clonedData, 0, clonedSize);
            return new HttpResponseBufferElement(clonedData, clonedSize); 
        } 

        internal override void Recycle() { 
            if (_recycle)
                ForceRecycle();
        }
 
        private void ForceRecycle() {
            IntPtr data = Interlocked.Exchange(ref _data, IntPtr.Zero); 
            if (data != IntPtr.Zero) { 
                _free = 0;
                _recycle = false; 
                if (HttpRuntime.UseIntegratedPipeline) {
                    IIS.MgdReturnBuffer(data);
                }
                else { 
                    UnsafeNativeMethods.BufferPoolReleaseBuffer(data);
                } 
                System.GC.SuppressFinalize(this); 
            }
        } 

        internal override int Append(byte[] data, int offset, int size) {
            if (_free == 0 || size == 0)
                return 0; 
            int n = (_free >= size) ? size : _free;
            Misc.CopyMemory(data, offset, _data, _size-_free, n); 
            _free -= n; 
            return n;
        } 

        internal override int Append(IntPtr data, int offset, int size) {
            if (_free == 0 || size == 0)
                return 0; 
            int n = (_free >= size) ? size : _free;
            Misc.CopyMemory(data, offset, _data, _size-_free, n); 
            _free -= n; 
            return n;
        } 

        // manually adjust the size
        // used after file reads directly into a buffer
        internal void AdjustSize(int size) { 
            _free -= size;
        } 
 
        internal override void AppendEncodedChars(char[] data, int offset, int size, Encoder encoder, bool flushEncoder) {
            int byteSize = UnsafeAppendEncodedChars(data, offset, size, _data, _size - _free, _free, encoder, flushEncoder); 
            _free -= byteSize;
#if DBG
            Debug.Trace("UnmanagedBuffers", "Encoding chars, charCount=" + size + ", byteCount=" + byteSize);
#endif 
        }
 
        private unsafe static int UnsafeAppendEncodedChars(char[] src, int srcOffset, int srcSize, IntPtr dest, int destOffset, int destSize, Encoder encoder, bool flushEncoder) { 
            int numBytes = 0;
 
            byte* destBytes = ((byte*)dest) + destOffset;

            fixed (char* charSrc = src) {
                numBytes = encoder.GetBytes(charSrc+srcOffset, srcSize, destBytes, destSize, flushEncoder); 
            }
 
            return numBytes; 
        }
 
        //
        // IHttpResponseElement implementation
        //
 
        /*
         * Get number of bytes 
         */ 
        long IHttpResponseElement.GetSize() {
            return (_size - _free); 
        }

        /*
         * Get bytes (for filtering) 
         */
        byte[] IHttpResponseElement.GetBytes() { 
            int n = (_size - _free); 

            if (n > 0) { 
                byte[] data = new byte[n];
                Misc.CopyMemory(_data, 0, data, 0, n);
                return data;
            } 
            else {
                return null; 
            } 
        }
 
        /*
         * Write HttpWorkerRequest
         */
        void IHttpResponseElement.Send(HttpWorkerRequest wr) { 
            int n = _size - _free;
 
            if (n > 0) { 
                wr.SendResponseFromMemory(_data, n, true);
            } 

#if DBG
            Debug.Trace("UnmanagedBuffers", "Sending data, byteCount=" + n + ", freeBytes=" + _free);
#endif 
        }
 
        internal unsafe IntPtr FreeLocation { 
            get {
                int n = _size - _free; 
                byte * p = (byte*) _data.ToPointer();
                p += n;

                return new IntPtr(p); 
            }
        } 
    } 

#endif // !FEATURE_PAL 
    /*
     * Response element where data comes from resource
     */
    internal sealed class HttpResourceResponseElement : IHttpResponseElement { 
        private IntPtr _data;
        private int   _offset; 
        private int   _size; 

        internal HttpResourceResponseElement(IntPtr data, int offset, int size) { 
            _data = data;
            _offset = offset;
            _size = size;
        } 

        // 
        // IHttpResponseElement implementation 
        //
 
        /*
         * Get number of bytes
         */
        long IHttpResponseElement.GetSize() { 
            return _size;
        } 
 
        /*
         * Get bytes (used only for filtering) 
         */
        byte[] IHttpResponseElement.GetBytes() {
            if (_size > 0) {
                byte[] data = new byte[_size]; 
                Misc.CopyMemory(_data, _offset, data, 0, _size);
                return data; 
            } 
            else {
                return null; 
            }
        }

        /* 
         * Write HttpWorkerRequest
         */ 
        void IHttpResponseElement.Send(HttpWorkerRequest wr) { 
            if (_size > 0) {
                wr.SendResponseFromMemory(new IntPtr(_data.ToInt64()+_offset), _size, isBufferFromUnmanagedPool: false); 
            }
        }
    }
 
    /*
     * Response element where data comes from file 
     */ 
    internal sealed class HttpFileResponseElement : IHttpResponseElement {
        private String _filename; 
        private long   _offset;
        private long   _size;
        private bool   _isImpersonating;
        private bool   _useTransmitFile; 

        /** 
         * Constructor from filename, uses TransmitFile 
         */
        internal HttpFileResponseElement(String filename, long offset, long size, bool isImpersonating, bool supportsLongTransmitFile) : 
            this (filename, offset, size, isImpersonating, true, supportsLongTransmitFile) {
        }

        /* 
         * Constructor from filename and range (doesn't use TransmitFile)
         */ 
        internal HttpFileResponseElement(String filename, long offset, long size) : 
            this (filename, offset, size, false, false, false) {
        } 

        private HttpFileResponseElement(string filename,
                                        long offset,
                                        long size, 
                                        bool isImpersonating,
                                        bool useTransmitFile, 
                                        bool supportsLongTransmitFile) 
        {
            if ((!supportsLongTransmitFile && size > Int32.MaxValue) || (size < 0)) { 
                throw new ArgumentOutOfRangeException("size", size, SR.GetString(SR.Invalid_size));
            }
            if ((!supportsLongTransmitFile && offset > Int32.MaxValue) || (offset < 0)) {
                throw new ArgumentOutOfRangeException("offset", offset, SR.GetString(SR.Invalid_size)); 
            }
            _filename = filename; 
            _offset = offset; 
            _size = size;
            _isImpersonating = isImpersonating; 
            _useTransmitFile = useTransmitFile;
        }

 
        internal string FileName { get { return _filename; } }
 
        internal long   Offset   { get { return _offset; } } 

        // 
        // IHttpResponseElement implementation
        //

        /* 
         * Get number of bytes
         */ 
        long IHttpResponseElement.GetSize() { 
            return _size;
        } 

        /*
         * Get bytes (for filtering)
         */ 
        byte[] IHttpResponseElement.GetBytes() {
            if (_size == 0) 
                return null; 

            byte[] data = null; 
            FileStream f = null;

            try {
                f = new FileStream(_filename, FileMode.Open, FileAccess.Read, FileShare.Read); 

                long fileSize = f.Length; 
 
                if (_offset < 0 || _size > fileSize - _offset)
                    throw new HttpException(SR.GetString(SR.Invalid_range)); 

                if (_offset > 0)
                    f.Seek(_offset, SeekOrigin.Begin);
 
                int intSize = (int)_size;
                data = new byte[intSize]; 
                int bytesRead = 0; 
                do {
                    int n = f.Read(data, bytesRead, intSize); 
                    if (n == 0) {
                        break;
                    }
                    bytesRead += n; 
                    intSize -= n;
                } while (intSize > 0); 
                // Technically here, the buffer may not be full after the loop, but we choose to ignore 
                // this very rare condition (the file became shorter between the time we looked at its length
                // and the moment we read it). In this case, we would just have a few zero bytes at the end 
                // of the byte[], which is fine.
            }
            finally {
                if (f != null) 
                    f.Close();
            } 
 
            return data;
        } 

        /*
         * Write HttpWorkerRequest
         */ 
        void IHttpResponseElement.Send(HttpWorkerRequest wr) {
            if (_size > 0) { 
                if (_useTransmitFile) { 
                    wr.TransmitFile(_filename, _offset, _size, _isImpersonating); // This is for IIS 6, in-proc TransmitFile
                } 
                else {
                    wr.SendResponseFromFile(_filename, _offset, _size);
                }
            } 
        }
 
    } 

    /* 
     * Response element for substituiton
     */
    internal sealed class HttpSubstBlockResponseElement : IHttpResponseElement {
        private HttpResponseSubstitutionCallback _callback; 
        private IHttpResponseElement _firstSubstitution;
        private IntPtr _firstSubstData; 
        private int _firstSubstDataSize; 
        private bool _isIIS7WorkerRequest;
 
        // used by OutputCache
        internal HttpResponseSubstitutionCallback Callback { get { return _callback; } }

        /* 
         * Constructor given the name and the data (fill char converted to bytes)
         * holds on to the data 
         */ 
        internal HttpSubstBlockResponseElement(HttpResponseSubstitutionCallback callback, Encoding encoding, Encoder encoder, IIS7WorkerRequest iis7WorkerRequest) {
            _callback = callback; 
            if (iis7WorkerRequest != null) {
                _isIIS7WorkerRequest = true;
                String s = _callback(HttpContext.Current);
                if (s == null) { 
                    throw new ArgumentNullException("substitutionString");
                } 
                CreateFirstSubstData(s, iis7WorkerRequest, encoder); 
            }
            else { 
                _firstSubstitution = Substitute(encoding);
            }
        }
 
        // special constructor used by OutputCache
        internal HttpSubstBlockResponseElement(HttpResponseSubstitutionCallback callback) { 
            _callback = callback; 
        }
 
        // WOS 1926509: ASP.NET:  WriteSubstitution in integrated mode needs to support callbacks that return String.Empty
        private unsafe void CreateFirstSubstData(String s, IIS7WorkerRequest iis7WorkerRequest, Encoder encoder) {
            Debug.Assert(s != null, "s != null");
 
            IntPtr pbBuffer;
            int numBytes = 0; 
            int cch = s.Length; 
            if (cch > 0) {
                fixed (char * pch = s) { 
                    int cbBuffer = encoder.GetByteCount(pch, cch, true /*flush*/);
                    pbBuffer = iis7WorkerRequest.AllocateRequestMemory(cbBuffer);
                    if (pbBuffer != IntPtr.Zero) {
                        numBytes = encoder.GetBytes(pch, cch, (byte*)pbBuffer, cbBuffer, true /*flush*/); 
                    }
                } 
            } 
            else {
                // deal with empty string 
                pbBuffer = iis7WorkerRequest.AllocateRequestMemory(1);
            }

            if (pbBuffer == IntPtr.Zero) { 
                throw new OutOfMemoryException();
            } 
            _firstSubstData = pbBuffer; 
            _firstSubstDataSize = numBytes;
        } 

        /*
         * Performs substition -- return the resulting HttpResponseBufferElement
         * holds on to the data 
         */
        internal IHttpResponseElement Substitute(Encoding e) { 
            String s = _callback(HttpContext.Current); 
            byte[] data = e.GetBytes(s);
            return new HttpResponseBufferElement(data, data.Length); 
        }

        internal bool PointerEquals(IntPtr ptr) {
            Debug.Assert(HttpRuntime.UseIntegratedPipeline, "HttpRuntime.UseIntegratedPipeline"); 
            return _firstSubstData == ptr;
        } 
 
        //
        // IHttpResponseElement implementation (doesn't do anything) 
        //

        /*
         * Get number of bytes 
         */
        long IHttpResponseElement.GetSize() { 
            if (_isIIS7WorkerRequest) { 
                return _firstSubstDataSize;
            } 
            else {
                return _firstSubstitution.GetSize();
            }
        } 

        /* 
         * Get bytes (for filtering) 
         */
        byte[] IHttpResponseElement.GetBytes() { 
            if (_isIIS7WorkerRequest) {
                if (_firstSubstDataSize > 0) {
                    byte[] data = new byte[_firstSubstDataSize];
                    Misc.CopyMemory(_firstSubstData, 0, data, 0, _firstSubstDataSize); 
                    return data;
                } 
                else { 
                    // WOS 1926509: ASP.NET:  WriteSubstitution in integrated mode needs to support callbacks that return String.Empty
                    return (_firstSubstData == IntPtr.Zero) ? null : new byte[0]; 
                }
            }
            else {
                return _firstSubstitution.GetBytes(); 
            }
        } 
 
        /*
         * Write HttpWorkerRequest 
         */
        void IHttpResponseElement.Send(HttpWorkerRequest wr) {
            if (_isIIS7WorkerRequest) {
                IIS7WorkerRequest iis7WorkerRequest = wr as IIS7WorkerRequest; 
                if (iis7WorkerRequest != null) {
                    // buffer can have size of zero if the subst block is an emptry string 
                    iis7WorkerRequest.SendResponseFromIISAllocatedRequestMemory(_firstSubstData, _firstSubstDataSize); 
                }
            } 
            else {
                _firstSubstitution.Send(wr);
            }
        } 
    }
 
    /* 
     * Stream object synchronized with Writer
     */ 
    internal class HttpResponseStream : Stream {
        private HttpWriter _writer;

        internal HttpResponseStream(HttpWriter writer) { 
            _writer = writer;
        } 
 
        //
        // Public Stream method implementations 
        //

        public override bool CanRead {
            get { return false;} 
        }
 
        public override bool CanSeek { 
            get { return false;}
        } 

        public override bool CanWrite {
            get { return true;}
        } 

        public override long Length { 
            get {throw new NotSupportedException();} 
        }
 
        public override long Position {
            get {throw new NotSupportedException();}

            set {throw new NotSupportedException();} 
        }
 
        protected override void Dispose(bool disposing) { 
            try {
                if (disposing) 
                    _writer.Close();
            }
            finally {
                base.Dispose(disposing); 
            }
        } 
 
        public override void Flush() {
            _writer.Flush(); 
        }

        public override long Seek(long offset, SeekOrigin origin) {
            throw new NotSupportedException(); 
        }
 
        public override void SetLength(long value) { 
            throw new NotSupportedException();
        } 

        public override int Read(byte[] buffer, int offset, int count) {
            throw new NotSupportedException();
        } 

        public override void Write(byte[] buffer, int offset, int count) { 
            if (_writer.IgnoringFurtherWrites) { 
                return;
            } 

            // Dev10 Bug 507392: Do as Stream does.
            if (buffer == null)
                throw new ArgumentNullException("buffer"); 
            if (offset < 0)
                throw new ArgumentOutOfRangeException("offset"); 
            if (count < 0) 
                throw new ArgumentOutOfRangeException("count");
            if (buffer.Length - offset < count) 
                throw new ArgumentException(SR.GetString(SR.InvalidOffsetOrCount, "offset", "count"));
            if (count == 0)
                return;
 
            _writer.WriteFromStream(buffer, offset, count);
        } 
    } 

    /* 
     * Stream serving as sink for filters
     */
    internal sealed class HttpResponseStreamFilterSink : HttpResponseStream {
        private bool _filtering = false; 

        internal HttpResponseStreamFilterSink(HttpWriter writer) : base(writer) { 
        } 

        private void VerifyState() { 
            // throw exception on unexpected filter writes

            if (!_filtering)
                throw new HttpException(SR.GetString(SR.Invalid_use_of_response_filter)); 
        }
 
        internal bool Filtering { 
            get { return _filtering;}
            set { _filtering = value;} 
        }

        //
        // Stream methods just go to the base class with exception of Close and Flush that do nothing 
        //
 
        protected override void Dispose(bool disposing) { 
            // do nothing
            base.Dispose(disposing); 
        }

        public override void Flush() {
            // do nothing (this is not a buffering stream) 
        }
 
        public override void Write(byte[] buffer, int offset, int count) { 
            VerifyState();
            base.Write(buffer, offset, count); 
        }
    }

    /* 
     * TextWriter synchronized with the response object
     */ 
 
    /// 
    ///    A TextWriter class synchronized with the Response object. 
    /// 
    public sealed class HttpWriter : TextWriter {
        private HttpResponse _response;
        private HttpResponseStream _stream; 

        private HttpResponseStreamFilterSink _filterSink;       // sink stream for the filter writes 
        private Stream                       _installedFilter;  // installed filtering stream 

        private HttpBaseMemoryResponseBufferElement _lastBuffer; 
        private ArrayList _buffers;

        private char[] _charBuffer;
        private int _charBufferLength; 
        private int _charBufferFree;
        private ArrayList _substElements = null; 
 
        private static CharBufferAllocator s_Allocator =
        new CharBufferAllocator(BufferingParams.CHAR_BUFFER_SIZE, 
                                BufferingParams.MAX_FREE_CHAR_BUFFERS);

        // cached data from the response
        // can be invalidated via UpdateResponseXXX methods 

        private bool _responseBufferingOn; 
        private Encoding _responseEncoding; 
        private bool     _responseEncodingUsed;
        private bool     _responseEncodingUpdated; 
        private Encoder  _responseEncoder;
        private int      _responseCodePage;
        private bool     _responseCodePageIsAsciiCompat;
 
        private bool _ignoringFurtherWrites;
 
        private bool _hasBeenClearedRecently; 

        internal HttpWriter(HttpResponse response): base(null) { 
            _response = response;
            _stream = new HttpResponseStream(this);

            _buffers = new ArrayList(); 
            _lastBuffer = null;
 
            _charBuffer = (char[])s_Allocator.GetBuffer(); 
            _charBufferLength = _charBuffer.Length;
            _charBufferFree = _charBufferLength; 

            UpdateResponseBuffering();

            // delay getting response encoding until it is really needed 
            // UpdateResponseEncoding();
        } 
 
        internal ArrayList SubstElements {
            get { 
                if (_substElements == null) {
                    _substElements = new ArrayList();
                    // dynamic compression is not compatible with post cache substitution
                    _response.Context.Request.SetDynamicCompression(false /*enable*/); 
                }
                return _substElements; 
            } 
        }
 
        /// 
        /// True if the writer is ignoring all writes
        /// 
        internal bool IgnoringFurtherWrites { 
            get {
                return _ignoringFurtherWrites; 
            } 
        }
 
        /// 
        /// 
        internal void IgnoreFurtherWrites() {
            _ignoringFurtherWrites = true; 
        }
 
        internal void UpdateResponseBuffering() { 
            _responseBufferingOn = _response.BufferOutput;
        } 

        internal void UpdateResponseEncoding() {
            if (_responseEncodingUpdated) {  // subsequent update
                if (_charBufferLength != _charBufferFree) 
                    FlushCharBuffer(true);
            } 
 
            _responseEncoding = _response.ContentEncoding;
            _responseEncoder = _response.ContentEncoder; 
            _responseCodePage = _responseEncoding.CodePage;
            _responseCodePageIsAsciiCompat = CodePageUtils.IsAsciiCompatibleCodePage(_responseCodePage);
            _responseEncodingUpdated = true;
        } 

 
        ///  
        ///    [To be supplied.]
        ///  
        public override Encoding Encoding {
            get {
                if (!_responseEncodingUpdated) {
                    UpdateResponseEncoding(); 
                }
 
                return _responseEncoding; 
            }
        } 

        internal Encoder Encoder {
            get {
                if (!_responseEncodingUpdated) { 
                    UpdateResponseEncoding();
                } 
                return _responseEncoder; 
            }
        } 

        private HttpBaseMemoryResponseBufferElement CreateNewMemoryBufferElement() {
            return new HttpResponseUnmanagedBufferElement(); /* using unmanaged buffers */
        } 

    internal void DisposeIntegratedBuffers() { 
            Debug.Assert(HttpRuntime.UseIntegratedPipeline); 

            // don't recycle char buffers here (ClearBuffers will) 
            // do recycle native output buffers
            if (_buffers != null) {

                int n = _buffers.Count; 
                for (int i = 0; i < n; i++) {
                    HttpBaseMemoryResponseBufferElement buf = _buffers[i] as HttpBaseMemoryResponseBufferElement; 
 
                    // if this is a native buffer, this will bump down the ref count
                    // the native side also keeps a ref count (see mgdhandler.cxx) 
                    if (buf != null) {
                        buf.Recycle();
                    }
                } 

                _buffers = null; 
            } 

            // finish by clearing buffers 
            ClearBuffers();
    }

        internal void RecycleBuffers() { 
            // recycle char buffers
 
            if (_charBuffer != null) { 
                s_Allocator.ReuseBuffer(_charBuffer);
                _charBuffer = null; 
            }

            // recycle output buffers
            RecycleBufferElements(); 
        }
 
        internal void ClearSubstitutionBlocks() { 
            _substElements = null;
        } 

        private void RecycleBufferElements() {
            if (_buffers != null) {
 
                int n = _buffers.Count;
                for (int i = 0; i < n; i++) { 
                    HttpBaseMemoryResponseBufferElement buf = _buffers[i] as HttpBaseMemoryResponseBufferElement; 
                    if (buf != null) {
                        buf.Recycle(); 
                    }
                }

                _buffers = null; 
            }
        } 
 
        private void ClearCharBuffer() {
            _charBufferFree = _charBufferLength; 
        }

        private void FlushCharBuffer(bool flushEncoder) {
            int numChars = _charBufferLength - _charBufferFree; 

            Debug.Assert(numChars > 0); 
 
            // remember that response required encoding (to indicate the charset= is needed)
            if (!_responseEncodingUpdated) { 
                UpdateResponseEncoding();
            }

            _responseEncodingUsed = true; 

            // estimate conversion size 
            int estByteSize = _responseEncoding.GetMaxByteCount(numChars); 

            if (estByteSize <= BufferingParams.MAX_BYTES_TO_COPY || !_responseBufferingOn) { 
                // small size -- allocate intermediate buffer and copy into the output buffer
                byte[] byteBuffer = new byte[estByteSize];
                int realByteSize = _responseEncoder.GetBytes(_charBuffer, 0, numChars,
                                                             byteBuffer, 0, flushEncoder); 
                BufferData(byteBuffer, 0, realByteSize, false);
            } 
            else { 
                // convert right into the output buffer
 
                int free = (_lastBuffer != null) ? _lastBuffer.FreeBytes : 0;

                if (free < estByteSize) {
                    // need new buffer -- last one doesn't have enough space 
                    _lastBuffer = CreateNewMemoryBufferElement();
                    _buffers.Add(_lastBuffer); 
                    free = _lastBuffer.FreeBytes; 
                }
 
                // byte buffers must be long enough to keep everything in char buffer
                Debug.Assert(free >= estByteSize);
                _lastBuffer.AppendEncodedChars(_charBuffer, 0, numChars, _responseEncoder, flushEncoder);
            } 

            _charBufferFree = _charBufferLength; 
        } 

        private void BufferData(byte[] data, int offset, int size, bool needToCopyData) { 
            int n;

            // try last buffer
            if (_lastBuffer != null) { 
                n = _lastBuffer.Append(data, offset, size);
                size -= n; 
                offset += n; 
            }
            else if (!needToCopyData && offset == 0 && !_responseBufferingOn) { 
                // when not buffering, there is no need for big buffer accumulating multiple writes
                // the byte[] data can be sent as is

                _buffers.Add(new HttpResponseBufferElement(data, size)); 
                return;
            } 
 
            // do other buffers if needed
            while (size > 0) { 
                _lastBuffer = CreateNewMemoryBufferElement();
                _buffers.Add(_lastBuffer);
                n = _lastBuffer.Append(data, offset, size);
                offset += n; 
                size -= n;
            } 
        } 

        private void BufferResource(IntPtr data, int offset, int size) { 
            if (size > BufferingParams.MAX_RESOURCE_BYTES_TO_COPY || !_responseBufferingOn) {
                // for long response strings create its own buffer element to avoid copy cost
                // also, when not buffering, no need for an extra copy (nothing will get accumulated anyway)
                _lastBuffer = null; 
                _buffers.Add(new HttpResourceResponseElement(data, offset, size));
                return; 
            } 

            int n; 

            // try last buffer
            if (_lastBuffer != null) {
                n = _lastBuffer.Append(data, offset, size); 
                size -= n;
                offset += n; 
            } 

            // do other buffers if needed 
            while (size > 0) {
                _lastBuffer = CreateNewMemoryBufferElement();
                _buffers.Add(_lastBuffer);
                n = _lastBuffer.Append(data, offset, size); 
                offset += n;
                size -= n; 
            } 
        }
 
        //
        // 'Write' methods to be called from other internal classes
        //
 
        internal void WriteFromStream(byte[] data, int offset, int size) {
            if (_charBufferLength != _charBufferFree) 
                FlushCharBuffer(true); 

            BufferData(data, offset, size, true); 

            if (!_responseBufferingOn)
                _response.Flush();
        } 

        internal void WriteUTF8ResourceString(IntPtr pv, int offset, int size, bool asciiOnly) { 
 
            if (!_responseEncodingUpdated) {
                UpdateResponseEncoding(); 
            }

            if (_responseCodePage == CodePageUtils.CodePageUT8 || // response encoding is UTF8
                (asciiOnly && _responseCodePageIsAsciiCompat)) {  // ASCII resource and ASCII-compat encoding 

                _responseEncodingUsed = true;  // note the we used encoding (means that we need to generate charset=) see RAID#93415 
 
                // write bytes directly
                if (_charBufferLength != _charBufferFree) 
                    FlushCharBuffer(true);

                BufferResource(pv, offset, size);
 
                if (!_responseBufferingOn)
                    _response.Flush(); 
            } 
            else {
                // have to re-encode with response's encoding -- use public Write(String) 
                Write(StringResourceManager.ResourceToString(pv, offset, size));
            }
        }
 
        internal void TransmitFile(string filename, long offset, long size, bool isImpersonating, bool supportsLongTransmitFile) {
            if (_charBufferLength != _charBufferFree) 
                FlushCharBuffer(true); 

            _lastBuffer = null; 
            _buffers.Add(new HttpFileResponseElement(filename, offset, size, isImpersonating, supportsLongTransmitFile));

            if (!_responseBufferingOn)
                _response.Flush(); 
        }
 
        internal void WriteFile(String filename, long offset, long size) { 
            if (_charBufferLength != _charBufferFree)
                FlushCharBuffer(true); 

            _lastBuffer = null;
            _buffers.Add(new HttpFileResponseElement(filename, offset, size));
 
            if (!_responseBufferingOn)
                _response.Flush(); 
        } 

        // 
        // Support for substitution blocks
        //

        internal void WriteSubstBlock(HttpResponseSubstitutionCallback callback, IIS7WorkerRequest iis7WorkerRequest) { 
            if (_charBufferLength != _charBufferFree)
                FlushCharBuffer(true); 
            _lastBuffer = null; 

            // add new substitution block to the buffer list 
            IHttpResponseElement element = new HttpSubstBlockResponseElement(callback, Encoding, Encoder, iis7WorkerRequest);
            _buffers.Add(element);

            if (iis7WorkerRequest != null) { 
                SubstElements.Add(element);
            } 
 
            if (!_responseBufferingOn)
                _response.Flush(); 
        }

        //
        // Support for response buffer manipulation: HasBeenClearedRecently, GetResponseBufferCountAfterFlush, 
        // and MoveResponseBufferRangeForward.  The intended use of these functions is to rearrange
        // the order of the buffers.  Improper use of these functions may result in excessive memory use. 
        // They were added specifically so that custom hidden form data could be moved to the beginning 
        // of the form.
 
        internal bool HasBeenClearedRecently {
            get {
                return _hasBeenClearedRecently;
            } 
            set {
                _hasBeenClearedRecently = value; 
            } 
        }
 
        // Gets the response buffer count after flushing the char buffer.  Note that _lastBuffer is cleared,
        // and therefore may not be filled, so calling this can lead to inefficient use of response buffers.
        internal int GetResponseBufferCountAfterFlush() {
            if (_charBufferLength != _charBufferFree) { 
                FlushCharBuffer(true);
            } 
 
            // set _lastBuffer to null to prevent more data from being added to it
            _lastBuffer = null; 

            return _buffers.Count;
        }
 
        // Move the specified range of buffers forward in the buffer list.
        internal void MoveResponseBufferRangeForward(int srcIndex, int srcCount, int dstIndex) { 
            Debug.Assert(dstIndex <= srcIndex); 

            // DevDiv Bugs 154630: No need to copy the form between temporary array and the buffer list when 
            // no hidden fields are written.
            if (srcCount > 0) {
                // create temporary storage for buffers that will be moved backwards
                object[] temp = new object[srcIndex - dstIndex]; 

                // copy buffers that will be moved backwards 
                _buffers.CopyTo(dstIndex, temp, 0, temp.Length); 

                // move the range forward from srcIndex to dstIndex 
                for (int i = 0; i < srcCount; i++) {
                    _buffers[dstIndex + i] = _buffers[srcIndex + i];
                }
 
                // insert buffers that were placed in temporary storage
                for (int i = 0; i < temp.Length; i++) { 
                    _buffers[dstIndex + srcCount + i] = temp[i]; 
                }
            } 

            // set _lastBuffer
            HttpBaseMemoryResponseBufferElement buf = _buffers[_buffers.Count-1] as HttpBaseMemoryResponseBufferElement;
            if (buf != null && buf.FreeBytes > 0) { 
                _lastBuffer = buf;
            } 
            else { 
                _lastBuffer = null;
            } 
        }

        //
        // Buffer management 
        //
 
        internal void ClearBuffers() { 
            ClearCharBuffer();
 
            // re-enable dynamic compression if we are about to clear substitution blocks
            if (_substElements != null) {
                _response.Context.Request.SetDynamicCompression(true /*enable*/);
            } 

            //VSWhidbey 559434: Private Bytes goes thru roof because unmanaged buffers are not recycled when Response.Flush is called 
            RecycleBufferElements(); 

            _buffers = new ArrayList(); 
            _lastBuffer = null;
            _hasBeenClearedRecently = true;
        }
 
        internal long GetBufferedLength() {
            if (_charBufferLength != _charBufferFree) 
                FlushCharBuffer(true); 

            long size = 0; 
            if (_buffers != null) {
                int n = _buffers.Count;
                for (int i = 0; i < n; i++) {
                    size += ((IHttpResponseElement)_buffers[i]).GetSize(); 
                }
            } 
            return size; 
        }
 
        internal bool ResponseEncodingUsed {
            get { return _responseEncodingUsed; }
        }
 
        // in integrated mode, snapshots need to pull the chunks from the IIS
        // buffers since they may have already been pushed through 
        // Therefore, we can't rely solely on what's in the HttpWriter 
        // at the moment
        internal ArrayList GetIntegratedSnapshot(out bool hasSubstBlocks, IIS7WorkerRequest wr) { 
            ArrayList buffers = null;

            // first, get what's in our buffers
            ArrayList writerBuffers = GetSnapshot(out hasSubstBlocks); 

            // now, get what's in the IIS buffers 
            ArrayList nativeBuffers = wr.GetBufferedResponseChunks(true, _substElements, ref hasSubstBlocks); 

            // try to append the current buffers to what we just 
            // got from the native buffer
            if (null != nativeBuffers) {
                for (int i = 0; i < writerBuffers.Count; i++) {
                    nativeBuffers.Add(writerBuffers[i]); 
                }
                buffers = nativeBuffers; 
            } 
            else {
                buffers = writerBuffers; 
            }

            // if we have substitution blocks:
            // 1) throw exception if someone modified the subst blocks 
            // 2) re-enable compression
            if (_substElements != null && _substElements.Count > 0) { 
                int substCount = 0; 
                // scan buffers for subst blocks
                for(int i = 0; i < buffers.Count; i++) { 
                    if (buffers[i] is HttpSubstBlockResponseElement) {
                        substCount++;
                        if (substCount == _substElements.Count) {
                            break; 
                        }
                    } 
                } 

                if (substCount != _substElements.Count) { 
                    throw new InvalidOperationException(SR.GetString(SR.Substitution_blocks_cannot_be_modified));
                }

                // re-enable dynamic compression when we have a snapshot of the subst blocks. 
                _response.Context.Request.SetDynamicCompression(true /*enable*/);
            } 
 
            return buffers;
        } 

        //
        //  Snapshot for caching
        // 

        internal ArrayList GetSnapshot(out bool hasSubstBlocks) { 
            if (_charBufferLength != _charBufferFree) 
                FlushCharBuffer(true);
 
            _lastBuffer = null; // to make sure nothing gets appended after

            hasSubstBlocks = false;
 
            ArrayList buffers = new ArrayList();
 
            // copy buffer references to the returned list, make non-recyclable 
            int n = _buffers.Count;
            for (int i = 0; i < n; i++) { 
                Object responseElement = _buffers[i];

                HttpBaseMemoryResponseBufferElement buffer = responseElement as HttpBaseMemoryResponseBufferElement;
 
                if (buffer != null) {
                    if (buffer.FreeBytes > BufferingParams.MAX_FREE_BYTES_TO_CACHE) { 
                        // copy data if too much is free 
                        responseElement = buffer.Clone();
                    } 
                    else {
                        // cache the buffer as is with free bytes
                        buffer.DisableRecycling();
                    } 
                }
                else if (responseElement is HttpSubstBlockResponseElement) { 
                    hasSubstBlocks = true; 
                }
 
                buffers.Add(responseElement);
            }
            return buffers;
        } 

        internal void UseSnapshot(ArrayList buffers) { 
            ClearBuffers(); 

            // copy buffer references to the internal buffer list 
            // make substitution if needed

            int n = buffers.Count;
            for (int i = 0; i < n; i++) { 
                Object responseElement = buffers[i];
                HttpSubstBlockResponseElement substBlock = (responseElement as HttpSubstBlockResponseElement); 
 
                if (substBlock != null) {
                    _buffers.Add(substBlock.Substitute(Encoding)); 
                }
                else {
                    _buffers.Add(responseElement);
                } 
            }
        } 
 
        //
        //  Support for response stream filters 
        //

        internal Stream GetCurrentFilter() {
            if (_installedFilter != null) 
                return _installedFilter;
 
            if (_filterSink == null) 
                _filterSink = new HttpResponseStreamFilterSink(this);
 
            return _filterSink;
        }

        internal bool FilterInstalled { 
            get { return (_installedFilter != null); }
        } 
 
        internal void InstallFilter(Stream filter) {
            if (_filterSink == null)  // have to redirect to the sink -- null means sink wasn't ever asked for 
                throw new HttpException(SR.GetString(SR.Invalid_response_filter));

            _installedFilter = filter;
        } 

        internal void Filter(bool finalFiltering) { 
            // no filter? 
            if (_installedFilter == null)
                return; 

            // flush char buffer and remember old buffers
            if (_charBufferLength != _charBufferFree)
                FlushCharBuffer(true); 

            _lastBuffer = null; 
 
            // no content to filter
            // Allow the filter to be closed (Dev10 Bug 550168). 
            if (_buffers.Count == 0 && !finalFiltering)
                return;

            // remember old buffers 
            ArrayList oldBuffers = _buffers;
            _buffers = new ArrayList(); 
 
            // push old buffer list through the filter
 
            Debug.Assert(_filterSink != null);

            _filterSink.Filtering = true;
 
            try {
                int n = oldBuffers.Count; 
                for (int i = 0; i < n; i++) { 
                    IHttpResponseElement buf = (IHttpResponseElement)oldBuffers[i];
 
                    long len = buf.GetSize();

                    if (len > 0) {
                        // Convert.ToInt32 will throw for sizes larger than Int32.MaxValue. 
                        // Filtering large response sizes is not supported
                        _installedFilter.Write(buf.GetBytes(), 0, Convert.ToInt32(len)); 
                    } 
                }
 
                _installedFilter.Flush();

            }
            finally { 
                try {
                    if (finalFiltering) 
                        _installedFilter.Close(); 
                }
                finally { 
                    _filterSink.Filtering = false;
                }
            }
        } 

        internal void FilterIntegrated(bool finalFiltering, IIS7WorkerRequest wr) { 
            // no filter? 
            if (_installedFilter == null)
                return; 

            // flush char buffer and remember old buffers
            if (_charBufferLength != _charBufferFree)
                FlushCharBuffer(true); 

            _lastBuffer = null; 
 
            // ISAPI mode bails if it has no buffers
            // to filter, in integrated mode we need 
            // to check the unified response buffers
            // maintained by IIS for content, as well

            // remember current buffers (if any) that might be 
            // response entity from this transition
            // (not yet pushed through to IIS response buffers) 
            ArrayList oldBuffers = _buffers; 
            _buffers = new ArrayList();
 
            // now, get what's in the IIS buffers
            ArrayList nativeBuffers = null;
            bool fDummy = false;
            nativeBuffers = wr.GetBufferedResponseChunks(false, null, ref fDummy); 

            Debug.Assert(_filterSink != null); 
            _filterSink.Filtering = true; 

            try { 
                // push buffers through installed filters
                // push the IIS ones through first since we need to maintain order
                if (null != nativeBuffers) {
                    for (int i = 0; i < nativeBuffers.Count; i++) { 
                        IHttpResponseElement buf = (IHttpResponseElement)nativeBuffers[i];
 
                        long len = buf.GetSize(); 

                        if (len > 0) 
                            _installedFilter.Write(buf.GetBytes(), 0, Convert.ToInt32(len));

                    }
 
                    // if we had stuff there, we now need to clear it since we may have
                    // transformed it 
                    wr.ClearResponse(true /* entity */, false /* headers */); 
                }
 
                // current buffers, if any
                if (null != oldBuffers) {
                    for (int i = 0; i < oldBuffers.Count; i++) {
                        IHttpResponseElement buf = (IHttpResponseElement)oldBuffers[i]; 

                        long len = buf.GetSize(); 
 
                        if (len > 0)
                            _installedFilter.Write(buf.GetBytes(), 0, Convert.ToInt32(len)); 

                    }
                }
 
                _installedFilter.Flush();
            } 
            finally { 
                try {
                    if (finalFiltering) 
                        _installedFilter.Close();
                }
                finally {
                    _filterSink.Filtering = false; 
                }
            } 
        } 

        // 
        //  Send via worker request
        //

        internal void Send(HttpWorkerRequest wr) { 
            if (_charBufferLength != _charBufferFree)
                FlushCharBuffer(true); 
 
            int n = _buffers.Count;
 
            if (n > 0) {
                // write data
                for (int i = 0; i < n; i++) {
                    ((IHttpResponseElement)_buffers[i]).Send(wr); 
                }
            } 
        } 

        // 
        // Public TextWriter method implementations
        //

 
        /// 
        ///     Sends all buffered output to the client and closes the socket connection. 
        ///  
        public override void Close() {
            // don't do anything (this could called from a wrapping text writer) 
        }


        ///  
        ///     Sends all buffered output to the client.
        ///  
        public override void Flush() { 
            // don't flush the response
        } 


        /// 
        ///     Sends a character to the client. 
        /// 
        public override void Write(char ch) { 
            if (_ignoringFurtherWrites) { 
                return;
            } 

            if (_charBufferFree == 0) {
                FlushCharBuffer(false);
            } 

            _charBuffer[_charBufferLength - _charBufferFree] = ch; 
            _charBufferFree--; 

            if (!_responseBufferingOn) { 
                _response.Flush();
            }
        }
 

        ///  
        ///     Sends a stream of buffered characters to the client 
        ///       using starting position and number of characters to send. 
        ///  
        public override void Write(char[] buffer, int index, int count) {
            if (_ignoringFurtherWrites) {
                return;
            } 

            // Dev10 Bug 507392: Do as TextWriter does. 
            if (buffer == null) 
                throw new ArgumentNullException("buffer");
            if (index < 0) 
                throw new ArgumentOutOfRangeException("index");
            if (count < 0)
                throw new ArgumentOutOfRangeException("count");
            if (buffer.Length - index < count) 
                throw new ArgumentException(SR.GetString(SR.InvalidOffsetOrCount, "index", "count"));
            if (count == 0) 
                return; 

            while (count > 0) { 
                if (_charBufferFree == 0) {
                    FlushCharBuffer(false);
                }
 
                int n = (count < _charBufferFree) ? count : _charBufferFree;
                System.Array.Copy(buffer, index, _charBuffer, _charBufferLength - _charBufferFree, n); 
                _charBufferFree -= n; 
                index += n;
                count -= n; 
            }

            if (!_responseBufferingOn) {
                _response.Flush(); 
            }
        } 
 

        ///  
        ///    Sends a string to the client.
        /// 
        public override void Write(String s) {
            if (_ignoringFurtherWrites) 
                return;
 
            if (s == null) 
                return;
 
            if (s.Length == 0) {
                // Ensure flush if string is empty
            }
            else if (s.Length < _charBufferFree) { 
                // fast path - 99% of string writes will not overrun the buffer
                // avoid redundant arg checking in string.CopyTo 
                StringUtil.UnsafeStringCopy(s, 0, _charBuffer, _charBufferLength - _charBufferFree, s.Length); 
                _charBufferFree -= s.Length;
            } 
            else {
                int count = s.Length;
                int index = 0;
                int n; 

                while (count > 0) { 
                    if (_charBufferFree == 0) { 
                        FlushCharBuffer(false);
                    } 

                    n = (count < _charBufferFree) ? count : _charBufferFree;

                    // avoid redundant arg checking in string.CopyTo 
                    StringUtil.UnsafeStringCopy(s, index, _charBuffer, _charBufferLength - _charBufferFree, n);
 
                    _charBufferFree -= n; 
                    index += n;
                    count -= n; 
                }
            }

            if (!_responseBufferingOn) { 
                _response.Flush();
            } 
        } 

 
        /// 
        ///    Sends a string or a sub-string to the client.
        /// 
        public void WriteString(String s, int index, int count) { 
            if (s == null)
                return; 
 
            if (index < 0) {
                throw new ArgumentOutOfRangeException("index"); 
            }

            if (count < 0) {
                throw new ArgumentOutOfRangeException("count"); 
            }
 
            if (index + count > s.Length) { 
                throw new ArgumentOutOfRangeException("index");
            } 

            if (_ignoringFurtherWrites) {
                return;
            } 

            if (count == 0) { 
                // Ensure flush if string is empty 
            }
            else if (count < _charBufferFree) { 
                // fast path - 99% of string writes will not overrun the buffer
                // avoid redundant arg checking in string.CopyTo
                StringUtil.UnsafeStringCopy(s, index, _charBuffer, _charBufferLength - _charBufferFree, count);
                _charBufferFree -= count; 
            }
            else { 
                int n; 

                while (count > 0) { 
                    if (_charBufferFree == 0) {
                        FlushCharBuffer(false);
                    }
 
                    n = (count < _charBufferFree) ? count : _charBufferFree;
 
                    // avoid redundant arg checking in string.CopyTo 
                    StringUtil.UnsafeStringCopy(s, index, _charBuffer, _charBufferLength - _charBufferFree, n);
 
                    _charBufferFree -= n;
                    index += n;
                    count -= n;
                } 
            }
 
            if (!_responseBufferingOn) { 
                _response.Flush();
            } 
        }


        ///  
        ///    Sends an object to the client.
        ///  
        public override void Write(Object obj) { 
            if (_ignoringFurtherWrites) {
                return; 
            }

            if (obj != null)
                Write(obj.ToString()); 
        }
 
        // 
        // Support for binary data
        // 


        /// 
        ///    Sends a buffered stream of bytes to the client. 
        /// 
        public void WriteBytes(byte[] buffer, int index, int count) { 
            if (_ignoringFurtherWrites) { 
                return;
            } 

            WriteFromStream(buffer, index, count);
        }
 

        ///  
        ///    Writes out a CRLF pair into the the stream. 
        /// 
        public override void WriteLine() { 
            if (_ignoringFurtherWrites) {
                return;
            }
 
            // It turns out this is way more efficient than the TextWriter version of
            // WriteLine which ends up calling Write with a 2 char array 
 
            if (_charBufferFree < 2)
                FlushCharBuffer(false); 

            int pos = _charBufferLength - _charBufferFree;
            _charBuffer[pos] = '\r';
            _charBuffer[pos + 1] = '\n'; 
            _charBufferFree -= 2;
 
            if (!_responseBufferingOn) 
                _response.Flush();
        } 

        /*
         * The Stream for writing binary data
         */ 

        ///  
        ///     Enables binary output to the client. 
        /// 
            public Stream OutputStream { 
            get { return _stream;}
        }

    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
                        

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