HttpWriter.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 / whidbey / NetFxQFE / ndp / fx / src / xsp / System / Web / HttpWriter.cs / 1 / 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); 


#if UNUSED
        /* 
         * Constructor that creates an empty buffer
         */ 
        internal HttpResponseBufferElement() { 
            _data = (byte[])s_Allocator.GetBuffer();
            _size = BufferingParams.OUTPUT_BUFFER_SIZE; 
            _free = _size;
            _recycle = true;
        }
#endif 

        /* 
         * 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() {
            // Since the no-arg constructor has been #ifdef'd out, _recycle will
            // always be false, so this block will never be executed.
#if UNUSED 
            if (_recycle && _data != null) {
                s_Allocator.ReuseBuffer(_data); 
                _data = null; 
                _free = 0;
                _recycle = false; 
            }
#endif
        }
 
        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);
            } 
        }
    } 
 
    /*
     * 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; 
        }
 
        // 
        // 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; 
 
        /*
         * 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);
            } 
        } 

        // 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; 
            }
 
            if (buffer == null) {
                throw new ArgumentNullException("buffer");
            }
            int len = buffer.Length - offset; 
            if ((offset < 0) || (len <= 0)) {
                throw new ArgumentOutOfRangeException("offset"); 
            } 
            if (count < 0) {
                throw new ArgumentOutOfRangeException("count"); 
            }

            if (len < count)
                count = len; 

            _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.
    /// 
    [AspNetHostingPermission(SecurityAction.LinkDemand, Level=AspNetHostingPermissionLevel.Minimal)]
    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
            if (_buffers.Count == 0)
                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; 
            }
 
            if (buffer == null || index < 0 || count < 0 || 
                buffer.Length - index < count) {
                throw new ArgumentOutOfRangeException("index"); 
            }

            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.
//------------------------------------------------------------------------------ 
// 
//     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); 


#if UNUSED
        /* 
         * Constructor that creates an empty buffer
         */ 
        internal HttpResponseBufferElement() { 
            _data = (byte[])s_Allocator.GetBuffer();
            _size = BufferingParams.OUTPUT_BUFFER_SIZE; 
            _free = _size;
            _recycle = true;
        }
#endif 

        /* 
         * 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() {
            // Since the no-arg constructor has been #ifdef'd out, _recycle will
            // always be false, so this block will never be executed.
#if UNUSED 
            if (_recycle && _data != null) {
                s_Allocator.ReuseBuffer(_data); 
                _data = null; 
                _free = 0;
                _recycle = false; 
            }
#endif
        }
 
        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);
            } 
        }
    } 
 
    /*
     * 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; 
        }
 
        // 
        // 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; 
 
        /*
         * 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);
            } 
        } 

        // 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; 
            }
 
            if (buffer == null) {
                throw new ArgumentNullException("buffer");
            }
            int len = buffer.Length - offset; 
            if ((offset < 0) || (len <= 0)) {
                throw new ArgumentOutOfRangeException("offset"); 
            } 
            if (count < 0) {
                throw new ArgumentOutOfRangeException("count"); 
            }

            if (len < count)
                count = len; 

            _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.
    /// 
    [AspNetHostingPermission(SecurityAction.LinkDemand, Level=AspNetHostingPermissionLevel.Minimal)]
    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
            if (_buffers.Count == 0)
                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; 
            }
 
            if (buffer == null || index < 0 || count < 0 || 
                buffer.Length - index < count) {
                throw new ArgumentOutOfRangeException("index"); 
            }

            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