Base64Stream.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Net / System / Net / Mail / Base64Stream.cs / 1305376 / Base64Stream.cs

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

namespace System.Net 
{ 
    using System;
    using System.IO; 
    using System.Net.Mime;
    using System.Text;
    using System.Diagnostics;
 
    internal class Base64Stream : DelegatedStream, IEncodableStream
    { 
        static byte[] base64DecodeMap = new byte[] { 
            //0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
            255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 0 
            255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 1
            255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63, // 2
             52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255,255,255,255, // 3
            255,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, // 4 
             15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255, // 5
            255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 6 
             41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255, // 7 
            255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 8
            255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 9 
            255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // A
            255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // B
            255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // C
            255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // D 
            255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // E
            255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // F 
        }; 

        static byte[] base64EncodeMap = new byte[] { 
             65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
             81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99,100,101,102,
            103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,
            119,120,121,122, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 43, 47, 
            61
            }; 
 
        int lineLength;
        ReadStateInfo readState; 
        Base64WriteStateInfo writeState;

        //the number of bytes needed to encode three bytes (see algorithm description in Encode method below)
        const int sizeOfBase64EncodedChar = 4; 

        //bytes with this value in the decode map are invalid 
        const byte invalidBase64Value = 255; 

        //length in bytes of a soft CRLF for folding 
        const int sizeOfSoftCRLF = 3;

        internal Base64Stream(Stream stream, Base64WriteStateInfo writeStateInfo)
            : base(stream) 
        {
            this.writeState = new Base64WriteStateInfo(); 
            this.lineLength = writeStateInfo.MaxLineLength; 
        }
 
        internal Base64Stream(Stream stream, int lineLength)
            : base(stream)
        {
            this.lineLength = lineLength; 
            this.writeState = new Base64WriteStateInfo();
        } 
 
        internal Base64Stream(Base64WriteStateInfo writeStateInfo)
        { 
            this.lineLength = writeStateInfo.MaxLineLength;
            this.writeState = writeStateInfo;
        }
 
        public override bool CanWrite
        { 
            get 
            {
                return base.CanWrite; 
            }
        }

        ReadStateInfo ReadState 
        {
            get 
            { 
                if (this.readState == null)
                    this.readState = new ReadStateInfo(); 
                return this.readState;
            }
        }
 
        internal Base64WriteStateInfo WriteState
        { 
            get 
            {
                Debug.Assert(writeState != null, "writeState was null"); 
                return this.writeState;
            }
        }
 
        public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
        { 
            if (buffer == null) 
                throw new ArgumentNullException("buffer");
 
            if (offset < 0 || offset > buffer.Length)
                throw new ArgumentOutOfRangeException("offset");

            if (offset + count > buffer.Length) 
                throw new ArgumentOutOfRangeException("count");
 
            ReadAsyncResult result = new ReadAsyncResult(this, buffer, offset, count, callback, state); 
            result.Read();
            return result; 
        }

        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
        { 
            if (buffer == null)
                throw new ArgumentNullException("buffer"); 
 
            if (offset < 0 || offset > buffer.Length)
                throw new ArgumentOutOfRangeException("offset"); 

            if (offset + count > buffer.Length)
                throw new ArgumentOutOfRangeException("count");
 
            WriteAsyncResult result = new WriteAsyncResult(this, buffer, offset, count, callback, state);
            result.Write(); 
            return result; 
        }
 

        public override void Close()
        {
            if (this.writeState != null && WriteState.Length > 0) 
            {
                switch (WriteState.Padding) 
                { 
                    case 2:
                        WriteState.Buffer[WriteState.Length++] = base64EncodeMap[WriteState.LastBits]; 
                        WriteState.Buffer[WriteState.Length++] = base64EncodeMap[64];
                        WriteState.Buffer[WriteState.Length++] = base64EncodeMap[64];
                        break;
                    case 1: 
                        WriteState.Buffer[WriteState.Length++] = base64EncodeMap[WriteState.LastBits];
                        WriteState.Buffer[WriteState.Length++] = base64EncodeMap[64]; 
                        break; 
                }
                WriteState.Padding = 0; 
                FlushInternal();
            }
            base.Close();
        } 

        public int DecodeBytes(byte[] buffer, int offset, int count) 
        { 
            unsafe
            { 
                fixed (byte* pBuffer = buffer)
                {
                    byte* start = pBuffer + offset;
                    byte* source = start; 
                    byte* dest = start;
                    byte* end = start + count; 
 
                    while (source < end)
                    { 
                        //space and tab are ok because folding must include a whitespace char.
                        if (*source == '\r' || *source == '\n' || *source == '=' || *source == ' ' || *source == '\t')
                        {
                            source++; 
                            continue;
                        } 
 
                        byte s = base64DecodeMap[*source];
 
                        if (s == invalidBase64Value)
                            throw new FormatException(SR.GetString(SR.MailBase64InvalidCharacter));

                        switch (ReadState.Pos) 
                        {
                            case 0: 
                                ReadState.Val = (byte)(s << 2); 
                                ReadState.Pos++;
                                break; 
                            case 1:
                                *dest++ = (byte)(ReadState.Val + (s >> 4));
                                ReadState.Val = (byte)(s << 4);
                                ReadState.Pos++; 
                                break;
                            case 2: 
                                *dest++ = (byte)(ReadState.Val + (s >> 2)); 
                                ReadState.Val = (byte)(s << 6);
                                ReadState.Pos++; 
                                break;
                            case 3:
                                *dest++ = (byte)(ReadState.Val + s);
                                ReadState.Pos = 0; 
                                break;
                        } 
                        source++; 
                    }
 
                    count = (int)(dest - start);
                }
            }
            return count; 
        }
 
        public int EncodeBytes(byte[] buffer, int offset, int count) 
        {
            return this.EncodeBytes(buffer, offset, count, true, true); 
        }

        internal int EncodeBytes(byte[] buffer, int offset, int count,
            bool dontDeferFinalBytes, bool shouldAppendSpaceToCRLF) 
        {
            //if the header length is 0 then we should use a soft CRLF without appending the ?= footer 
            //always because this is part of the body encoding. 
            //otherwise we should append the footer, then the soft clrf, then whitespace, then the =?xxxx?B? header again
            bool ShouldAppendHeaderValue = writeState.HeaderLength != 0; 

            int cur = offset;
            Debug.Assert(buffer != null, "buffer was null");
            Debug.Assert(this.writeState != null, "writestate was null"); 
            Debug.Assert(this.writeState.Buffer != null, "writestate.buffer was null");
 
            WriteState.AppendHeader(); 
            WriteState.CurrentLineLength += writeState.MimeHeaderLength;
            switch (WriteState.Padding) 
            {
                case 2:
                    WriteState.Buffer[WriteState.Length++] = base64EncodeMap[WriteState.LastBits | ((buffer[cur]&0xf0)>>4)];
                    if (count == 1) 
                    {
                        WriteState.LastBits = (byte)((buffer[cur]&0x0f)<<2); 
                        WriteState.Padding = 1; 
                        return cur - offset;
                    } 
                    WriteState.Buffer[WriteState.Length++] = base64EncodeMap[((buffer[cur]&0x0f)<<2) | ((buffer[cur+1]&0xc0)>>6)];
                    WriteState.Buffer[WriteState.Length++] = base64EncodeMap[(buffer[cur+1]&0x3f)];
                    cur+=2;
                    count-=2; 
                    WriteState.Padding = 0;
                    WriteState.CurrentLineLength += 2; 
                    break; 
                case 1:
                    WriteState.Buffer[WriteState.Length++] = base64EncodeMap[WriteState.LastBits | ((buffer[cur]&0xc0)>>6)]; 
                    WriteState.Buffer[WriteState.Length++] = base64EncodeMap[(buffer[cur]&0x3f)];
                    cur++;
                    count--;
                    WriteState.Padding = 0; 
                    WriteState.CurrentLineLength ++;
                    break; 
            } 

            int calcLength = cur + (count - (count%3)); 

            //Convert three bytes at a time to base64 notation.  This will consume 4 chars.
            for (; cur < calcLength; cur+=3)
            { 
                if (lineLength != -1 && WriteState.CurrentLineLength + sizeOfBase64EncodedChar > lineLength - sizeOfSoftCRLF &&
                    !ShouldAppendHeaderValue) 
                { 
                    WriteState.Buffer[WriteState.Length++] = (byte)'\r';
                    WriteState.Buffer[WriteState.Length++] = (byte)'\n'; 
                    if (shouldAppendSpaceToCRLF)
                    {
                        WriteState.Buffer[WriteState.Length++] = (byte)' ';
                    } 
                    WriteState.CurrentLineLength = 0;
                } 
 
                //can we encode another three bytes with a buffer this long?
                if (WriteState.Length + sizeOfBase64EncodedChar > WriteState.Buffer.Length) 
                {
                    WriteState.ResizeBuffer();
                }
 
                //how we actually encode: get three bytes in the
                //buffer to be encoded.  Then, extract six bits at a time and encode each six bit chunk as a base-64 character. 
                //this means that three bytes of data will be encoded as four base64 characters.  It also means that to encode 
                //a character, we must have three bytes to encode so if the number of bytes is not divisible by three, we
                //must pad the buffer (this happens below) 
                WriteState.Buffer[WriteState.Length++] = base64EncodeMap[(buffer[cur]&0xfc)>>2];
                WriteState.Buffer[WriteState.Length++] = base64EncodeMap[((buffer[cur]&0x03)<<4) | ((buffer[cur+1]&0xf0)>>4)];
                WriteState.Buffer[WriteState.Length++] = base64EncodeMap[((buffer[cur+1]&0x0f)<<2) | ((buffer[cur+2]&0xc0)>>6)];
                WriteState.Buffer[WriteState.Length++] = base64EncodeMap[(buffer[cur+2]&0x3f)]; 
                WriteState.CurrentLineLength += 4;
 
                //should I fold? 
                //how many characters have we written on this line and how many spaces do I have left
                //if spaces left - 4 (enough for one more character) - the space the footer would occupy 
                //is less than the space it would take to write the footer
                //then fold, which is to add the trailing footer and then a soft crlf.
                //then we need to prepare the buffer for the new line by appending the header again and incrementing
                //the count appropriately 
                if (WriteState.CurrentLineLength + sizeOfBase64EncodedChar + writeState.FooterLength  >= EncodedStreamFactory.DefaultMaxLineLength && ShouldAppendHeaderValue)
                { 
 
                    //see if we should resize
                    if (WriteState.Length + writeState.FooterLength + writeState.HeaderLength > WriteState.Buffer.Length) 
                        WriteState.ResizeBuffer();

                    //add footer
                    WriteState.AppendFooter(); 

                    //add soft line break 
                    writeState.Buffer[WriteState.Length++] = (byte)'\r'; 
                    writeState.Buffer[WriteState.Length++] = (byte)'\n';
 
                    if (shouldAppendSpaceToCRLF)
                    {
                        //add whitespace to new line (RFC 2045, soft CRLF must be followed by whitespace char)
                        //space selected for parity with other MS email clients 
                        WriteState.Buffer[WriteState.Length++] = (byte)' ';
                    } 
 
                    //add header to new line
                    WriteState.AppendHeader(); 

                    //reset writeStateInfo
                    WriteState.CurrentLineLength = WriteState.HeaderLength + 1;
 
                }
            } 
 
            cur = calcLength; //Where we left off before
 
            if (WriteState.Length + sizeOfBase64EncodedChar > WriteState.Buffer.Length)
            {
                WriteState.ResizeBuffer();
            } 

            //now pad this thing if we need to.  Since it must be a number of bytes that is evenly divisble by 3, 
            //if there are extra bytes, pad with '=' until we have a number of bytes divisible by 3 
            switch(count%3)
            { 
                case 2: //One character padding needed
                    WriteState.Buffer[WriteState.Length++] = base64EncodeMap[(buffer[cur]&0xFC)>>2];
                    WriteState.Buffer[WriteState.Length++] = base64EncodeMap[((buffer[cur]&0x03)<<4)|((buffer[cur+1]&0xf0)>>4)];
                    if (dontDeferFinalBytes) { 
                        WriteState.Buffer[WriteState.Length++] = base64EncodeMap[((buffer[cur+1]&0x0f)<<2)];
                        WriteState.Buffer[WriteState.Length++] = base64EncodeMap[64]; 
                        WriteState.Padding = 0; 
                        WriteState.CurrentLineLength += 4;
                    } 
                    else{
                        WriteState.LastBits = (byte)((buffer[cur+1]&0x0F)<<2);
                        WriteState.Padding = 1;
                        WriteState.CurrentLineLength += 2; 
                    }
                    cur += 2; 
                    break; 

                case 1: // Two character padding needed 
                    WriteState.Buffer[WriteState.Length++] = base64EncodeMap[(buffer[cur]&0xFC)>>2];
                    if (dontDeferFinalBytes) {
                        WriteState.Buffer[WriteState.Length++] = base64EncodeMap[(byte)((buffer[cur]&0x03)<<4)];
                        WriteState.Buffer[WriteState.Length++] = base64EncodeMap[64]; 
                        WriteState.Buffer[WriteState.Length++] = base64EncodeMap[64];
                        WriteState.Padding = 0; 
                        WriteState.CurrentLineLength+=4; 
                    }
                    else{ 
                        WriteState.LastBits = (byte)((buffer[cur]&0x03)<<4);
                        WriteState.Padding = 2;
                        WriteState.CurrentLineLength ++;
                    } 
                    cur++;
                    break; 
            } 

            WriteState.AppendFooter(); 
            return cur - offset;
        }

        public Stream GetStream() 
        {
            return this; 
        } 

        public string GetEncodedString() 
        {
            return ASCIIEncoding.ASCII.GetString(this.WriteState.Buffer, 0, this.WriteState.Length);
        }
 
        public override int EndRead(IAsyncResult asyncResult)
        { 
            if (asyncResult == null) 
                throw new ArgumentNullException("asyncResult");
 
            int read = ReadAsyncResult.End(asyncResult);
            return read;
        }
 
        public override void EndWrite(IAsyncResult asyncResult)
        { 
            if (asyncResult == null) 
                throw new ArgumentNullException("asyncResult");
 
            WriteAsyncResult.End(asyncResult);
        }

        public override void Flush() 
        {
            if (this.writeState != null && WriteState.Length > 0) 
            { 
                FlushInternal();
            } 
            base.Flush();
        }

        private void FlushInternal() 
        {
            base.Write(WriteState.Buffer, 0, WriteState.Length); 
            WriteState.Length = 0; 
        }
 
        public override int Read(byte[] buffer, int offset, int count)
        {
            if (buffer == null)
                throw new ArgumentNullException("buffer"); 

            if (offset < 0 || offset > buffer.Length) 
                throw new ArgumentOutOfRangeException("offset"); 

            if (offset + count > buffer.Length) 
                throw new ArgumentOutOfRangeException("count");

            for (;;)
            { 
                // read data from the underlying stream
                int read = base.Read(buffer, offset, count); 
 
                // if the underlying stream returns 0 then there
                // is no more data - ust return 0. 
                if (read == 0)
                    return 0;

                // while decoding, we may end up not having 
                // any bytes to return pending additional data
                // from the underlying stream. 
                read = DecodeBytes(buffer, offset, read); 
                if (read > 0)
                    return read; 
            }
        }

        public override void Write(byte[] buffer, int offset, int count) 
        {
            if (buffer == null) 
                throw new ArgumentNullException("buffer"); 

            if (offset < 0 || offset > buffer.Length) 
                throw new ArgumentOutOfRangeException("offset");

            if (offset + count > buffer.Length)
                throw new ArgumentOutOfRangeException("count"); 

            int written = 0; 
 
            // do not append a space when writing from a stream since this means
            // it's writing the email body 
            for (;;)
            {
                written += EncodeBytes(buffer, offset + written, count - written, false, false);
                if (written < count) 
                    FlushInternal();
                else 
                    break; 
            }
        } 

        class ReadAsyncResult : LazyAsyncResult
        {
            Base64Stream parent; 
            byte[] buffer;
            int offset; 
            int count; 
            int read;
 
            static AsyncCallback onRead = new AsyncCallback(OnRead);

            internal ReadAsyncResult(Base64Stream parent, byte[] buffer, int offset, int count, AsyncCallback callback, object state) : base(null,state,callback)
            { 
                this.parent = parent;
                this.buffer = buffer; 
                this.offset = offset; 
                this.count = count;
            } 

            bool CompleteRead(IAsyncResult result)
            {
                this.read = this.parent.BaseStream.EndRead(result); 

                // if the underlying stream returns 0 then there 
                // is no more data - ust return 0. 
                if (read == 0)
                { 
                    InvokeCallback();
                    return true;
                }
 
                // while decoding, we may end up not having
                // any bytes to return pending additional data 
                // from the underlying stream. 
                this.read = this.parent.DecodeBytes(this.buffer, this.offset, this.read);
                if (this.read > 0) 
                {
                    InvokeCallback();
                    return true;
                } 

                return false; 
            } 

            internal void Read() 
            {
                for (;;)
                {
                    IAsyncResult result = this.parent.BaseStream.BeginRead(this.buffer, this.offset, this.count, onRead, this); 
                    if (!result.CompletedSynchronously || CompleteRead(result))
                        break; 
                } 
            }
 
            static void OnRead(IAsyncResult result)
            {
                if (!result.CompletedSynchronously)
                { 
                    ReadAsyncResult thisPtr = (ReadAsyncResult)result.AsyncState;
                    try 
                    { 
                        if (!thisPtr.CompleteRead(result))
                            thisPtr.Read(); 
                    }
                    catch (Exception e)
                    {
                        if (thisPtr.IsCompleted) 
                            throw;
                        thisPtr.InvokeCallback(e); 
                    } 
                }
            } 

            internal static int End(IAsyncResult result)
            {
                ReadAsyncResult thisPtr = (ReadAsyncResult)result; 
                thisPtr.InternalWaitForCompletion();
                return thisPtr.read; 
            } 
        }
 
        class WriteAsyncResult : LazyAsyncResult
        {
            Base64Stream parent;
            byte[] buffer; 
            int offset;
            int count; 
            static AsyncCallback onWrite = new AsyncCallback(OnWrite); 
            int written;
 
            internal WriteAsyncResult(Base64Stream parent, byte[] buffer, int offset, int count, AsyncCallback callback, object state) : base(null, state, callback)
            {
                this.parent = parent;
                this.buffer = buffer; 
                this.offset = offset;
                this.count = count; 
            } 

            internal void Write() 
            {
                for (;;)
                {
                    // do not append a space when writing from a stream since this means 
                    // it's writing the email body
                    this.written += this.parent.EncodeBytes(this.buffer, this.offset + this.written, 
                        this.count - this.written, false, false); 
                    if (this.written < this.count)
                    { 
                        IAsyncResult result = this.parent.BaseStream.BeginWrite(this.parent.WriteState.Buffer, 0, this.parent.WriteState.Length, onWrite, this);
                        if (!result.CompletedSynchronously)
                            break;
                        CompleteWrite(result); 
                    }
                    else 
                    { 
                        InvokeCallback();
                        break; 
                    }
                }
            }
 
            void CompleteWrite(IAsyncResult result)
            { 
                this.parent.BaseStream.EndWrite(result); 
                this.parent.WriteState.Length = 0;
            } 

            static void OnWrite(IAsyncResult result)
            {
                if (!result.CompletedSynchronously) 
                {
                    WriteAsyncResult thisPtr = (WriteAsyncResult)result.AsyncState; 
                    try 
                    {
                        thisPtr.CompleteWrite(result); 
                        thisPtr.Write();
                    }
                    catch (Exception e)
                    { 
                        if (thisPtr.IsCompleted)
                            throw; 
                        thisPtr.InvokeCallback(e); 
                    }
                } 
            }

            internal static void End(IAsyncResult result)
            { 
                WriteAsyncResult thisPtr = (WriteAsyncResult)result;
                thisPtr.InternalWaitForCompletion(); 
                Debug.Assert(thisPtr.written == thisPtr.count); 
            }
        } 

        class ReadStateInfo
        {
            byte val; 
            byte pos;
 
            internal byte Val 
            {
                get { return this.val; } 
                set { this.val = value; }
            }

            internal byte Pos 
            {
                get { return this.pos; } 
                set { this.pos = value; } 
            }
        } 
    }
}

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

namespace System.Net 
{ 
    using System;
    using System.IO; 
    using System.Net.Mime;
    using System.Text;
    using System.Diagnostics;
 
    internal class Base64Stream : DelegatedStream, IEncodableStream
    { 
        static byte[] base64DecodeMap = new byte[] { 
            //0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
            255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 0 
            255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 1
            255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63, // 2
             52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255,255,255,255, // 3
            255,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, // 4 
             15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255, // 5
            255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 6 
             41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255, // 7 
            255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 8
            255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 9 
            255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // A
            255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // B
            255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // C
            255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // D 
            255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // E
            255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // F 
        }; 

        static byte[] base64EncodeMap = new byte[] { 
             65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
             81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99,100,101,102,
            103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,
            119,120,121,122, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 43, 47, 
            61
            }; 
 
        int lineLength;
        ReadStateInfo readState; 
        Base64WriteStateInfo writeState;

        //the number of bytes needed to encode three bytes (see algorithm description in Encode method below)
        const int sizeOfBase64EncodedChar = 4; 

        //bytes with this value in the decode map are invalid 
        const byte invalidBase64Value = 255; 

        //length in bytes of a soft CRLF for folding 
        const int sizeOfSoftCRLF = 3;

        internal Base64Stream(Stream stream, Base64WriteStateInfo writeStateInfo)
            : base(stream) 
        {
            this.writeState = new Base64WriteStateInfo(); 
            this.lineLength = writeStateInfo.MaxLineLength; 
        }
 
        internal Base64Stream(Stream stream, int lineLength)
            : base(stream)
        {
            this.lineLength = lineLength; 
            this.writeState = new Base64WriteStateInfo();
        } 
 
        internal Base64Stream(Base64WriteStateInfo writeStateInfo)
        { 
            this.lineLength = writeStateInfo.MaxLineLength;
            this.writeState = writeStateInfo;
        }
 
        public override bool CanWrite
        { 
            get 
            {
                return base.CanWrite; 
            }
        }

        ReadStateInfo ReadState 
        {
            get 
            { 
                if (this.readState == null)
                    this.readState = new ReadStateInfo(); 
                return this.readState;
            }
        }
 
        internal Base64WriteStateInfo WriteState
        { 
            get 
            {
                Debug.Assert(writeState != null, "writeState was null"); 
                return this.writeState;
            }
        }
 
        public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
        { 
            if (buffer == null) 
                throw new ArgumentNullException("buffer");
 
            if (offset < 0 || offset > buffer.Length)
                throw new ArgumentOutOfRangeException("offset");

            if (offset + count > buffer.Length) 
                throw new ArgumentOutOfRangeException("count");
 
            ReadAsyncResult result = new ReadAsyncResult(this, buffer, offset, count, callback, state); 
            result.Read();
            return result; 
        }

        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
        { 
            if (buffer == null)
                throw new ArgumentNullException("buffer"); 
 
            if (offset < 0 || offset > buffer.Length)
                throw new ArgumentOutOfRangeException("offset"); 

            if (offset + count > buffer.Length)
                throw new ArgumentOutOfRangeException("count");
 
            WriteAsyncResult result = new WriteAsyncResult(this, buffer, offset, count, callback, state);
            result.Write(); 
            return result; 
        }
 

        public override void Close()
        {
            if (this.writeState != null && WriteState.Length > 0) 
            {
                switch (WriteState.Padding) 
                { 
                    case 2:
                        WriteState.Buffer[WriteState.Length++] = base64EncodeMap[WriteState.LastBits]; 
                        WriteState.Buffer[WriteState.Length++] = base64EncodeMap[64];
                        WriteState.Buffer[WriteState.Length++] = base64EncodeMap[64];
                        break;
                    case 1: 
                        WriteState.Buffer[WriteState.Length++] = base64EncodeMap[WriteState.LastBits];
                        WriteState.Buffer[WriteState.Length++] = base64EncodeMap[64]; 
                        break; 
                }
                WriteState.Padding = 0; 
                FlushInternal();
            }
            base.Close();
        } 

        public int DecodeBytes(byte[] buffer, int offset, int count) 
        { 
            unsafe
            { 
                fixed (byte* pBuffer = buffer)
                {
                    byte* start = pBuffer + offset;
                    byte* source = start; 
                    byte* dest = start;
                    byte* end = start + count; 
 
                    while (source < end)
                    { 
                        //space and tab are ok because folding must include a whitespace char.
                        if (*source == '\r' || *source == '\n' || *source == '=' || *source == ' ' || *source == '\t')
                        {
                            source++; 
                            continue;
                        } 
 
                        byte s = base64DecodeMap[*source];
 
                        if (s == invalidBase64Value)
                            throw new FormatException(SR.GetString(SR.MailBase64InvalidCharacter));

                        switch (ReadState.Pos) 
                        {
                            case 0: 
                                ReadState.Val = (byte)(s << 2); 
                                ReadState.Pos++;
                                break; 
                            case 1:
                                *dest++ = (byte)(ReadState.Val + (s >> 4));
                                ReadState.Val = (byte)(s << 4);
                                ReadState.Pos++; 
                                break;
                            case 2: 
                                *dest++ = (byte)(ReadState.Val + (s >> 2)); 
                                ReadState.Val = (byte)(s << 6);
                                ReadState.Pos++; 
                                break;
                            case 3:
                                *dest++ = (byte)(ReadState.Val + s);
                                ReadState.Pos = 0; 
                                break;
                        } 
                        source++; 
                    }
 
                    count = (int)(dest - start);
                }
            }
            return count; 
        }
 
        public int EncodeBytes(byte[] buffer, int offset, int count) 
        {
            return this.EncodeBytes(buffer, offset, count, true, true); 
        }

        internal int EncodeBytes(byte[] buffer, int offset, int count,
            bool dontDeferFinalBytes, bool shouldAppendSpaceToCRLF) 
        {
            //if the header length is 0 then we should use a soft CRLF without appending the ?= footer 
            //always because this is part of the body encoding. 
            //otherwise we should append the footer, then the soft clrf, then whitespace, then the =?xxxx?B? header again
            bool ShouldAppendHeaderValue = writeState.HeaderLength != 0; 

            int cur = offset;
            Debug.Assert(buffer != null, "buffer was null");
            Debug.Assert(this.writeState != null, "writestate was null"); 
            Debug.Assert(this.writeState.Buffer != null, "writestate.buffer was null");
 
            WriteState.AppendHeader(); 
            WriteState.CurrentLineLength += writeState.MimeHeaderLength;
            switch (WriteState.Padding) 
            {
                case 2:
                    WriteState.Buffer[WriteState.Length++] = base64EncodeMap[WriteState.LastBits | ((buffer[cur]&0xf0)>>4)];
                    if (count == 1) 
                    {
                        WriteState.LastBits = (byte)((buffer[cur]&0x0f)<<2); 
                        WriteState.Padding = 1; 
                        return cur - offset;
                    } 
                    WriteState.Buffer[WriteState.Length++] = base64EncodeMap[((buffer[cur]&0x0f)<<2) | ((buffer[cur+1]&0xc0)>>6)];
                    WriteState.Buffer[WriteState.Length++] = base64EncodeMap[(buffer[cur+1]&0x3f)];
                    cur+=2;
                    count-=2; 
                    WriteState.Padding = 0;
                    WriteState.CurrentLineLength += 2; 
                    break; 
                case 1:
                    WriteState.Buffer[WriteState.Length++] = base64EncodeMap[WriteState.LastBits | ((buffer[cur]&0xc0)>>6)]; 
                    WriteState.Buffer[WriteState.Length++] = base64EncodeMap[(buffer[cur]&0x3f)];
                    cur++;
                    count--;
                    WriteState.Padding = 0; 
                    WriteState.CurrentLineLength ++;
                    break; 
            } 

            int calcLength = cur + (count - (count%3)); 

            //Convert three bytes at a time to base64 notation.  This will consume 4 chars.
            for (; cur < calcLength; cur+=3)
            { 
                if (lineLength != -1 && WriteState.CurrentLineLength + sizeOfBase64EncodedChar > lineLength - sizeOfSoftCRLF &&
                    !ShouldAppendHeaderValue) 
                { 
                    WriteState.Buffer[WriteState.Length++] = (byte)'\r';
                    WriteState.Buffer[WriteState.Length++] = (byte)'\n'; 
                    if (shouldAppendSpaceToCRLF)
                    {
                        WriteState.Buffer[WriteState.Length++] = (byte)' ';
                    } 
                    WriteState.CurrentLineLength = 0;
                } 
 
                //can we encode another three bytes with a buffer this long?
                if (WriteState.Length + sizeOfBase64EncodedChar > WriteState.Buffer.Length) 
                {
                    WriteState.ResizeBuffer();
                }
 
                //how we actually encode: get three bytes in the
                //buffer to be encoded.  Then, extract six bits at a time and encode each six bit chunk as a base-64 character. 
                //this means that three bytes of data will be encoded as four base64 characters.  It also means that to encode 
                //a character, we must have three bytes to encode so if the number of bytes is not divisible by three, we
                //must pad the buffer (this happens below) 
                WriteState.Buffer[WriteState.Length++] = base64EncodeMap[(buffer[cur]&0xfc)>>2];
                WriteState.Buffer[WriteState.Length++] = base64EncodeMap[((buffer[cur]&0x03)<<4) | ((buffer[cur+1]&0xf0)>>4)];
                WriteState.Buffer[WriteState.Length++] = base64EncodeMap[((buffer[cur+1]&0x0f)<<2) | ((buffer[cur+2]&0xc0)>>6)];
                WriteState.Buffer[WriteState.Length++] = base64EncodeMap[(buffer[cur+2]&0x3f)]; 
                WriteState.CurrentLineLength += 4;
 
                //should I fold? 
                //how many characters have we written on this line and how many spaces do I have left
                //if spaces left - 4 (enough for one more character) - the space the footer would occupy 
                //is less than the space it would take to write the footer
                //then fold, which is to add the trailing footer and then a soft crlf.
                //then we need to prepare the buffer for the new line by appending the header again and incrementing
                //the count appropriately 
                if (WriteState.CurrentLineLength + sizeOfBase64EncodedChar + writeState.FooterLength  >= EncodedStreamFactory.DefaultMaxLineLength && ShouldAppendHeaderValue)
                { 
 
                    //see if we should resize
                    if (WriteState.Length + writeState.FooterLength + writeState.HeaderLength > WriteState.Buffer.Length) 
                        WriteState.ResizeBuffer();

                    //add footer
                    WriteState.AppendFooter(); 

                    //add soft line break 
                    writeState.Buffer[WriteState.Length++] = (byte)'\r'; 
                    writeState.Buffer[WriteState.Length++] = (byte)'\n';
 
                    if (shouldAppendSpaceToCRLF)
                    {
                        //add whitespace to new line (RFC 2045, soft CRLF must be followed by whitespace char)
                        //space selected for parity with other MS email clients 
                        WriteState.Buffer[WriteState.Length++] = (byte)' ';
                    } 
 
                    //add header to new line
                    WriteState.AppendHeader(); 

                    //reset writeStateInfo
                    WriteState.CurrentLineLength = WriteState.HeaderLength + 1;
 
                }
            } 
 
            cur = calcLength; //Where we left off before
 
            if (WriteState.Length + sizeOfBase64EncodedChar > WriteState.Buffer.Length)
            {
                WriteState.ResizeBuffer();
            } 

            //now pad this thing if we need to.  Since it must be a number of bytes that is evenly divisble by 3, 
            //if there are extra bytes, pad with '=' until we have a number of bytes divisible by 3 
            switch(count%3)
            { 
                case 2: //One character padding needed
                    WriteState.Buffer[WriteState.Length++] = base64EncodeMap[(buffer[cur]&0xFC)>>2];
                    WriteState.Buffer[WriteState.Length++] = base64EncodeMap[((buffer[cur]&0x03)<<4)|((buffer[cur+1]&0xf0)>>4)];
                    if (dontDeferFinalBytes) { 
                        WriteState.Buffer[WriteState.Length++] = base64EncodeMap[((buffer[cur+1]&0x0f)<<2)];
                        WriteState.Buffer[WriteState.Length++] = base64EncodeMap[64]; 
                        WriteState.Padding = 0; 
                        WriteState.CurrentLineLength += 4;
                    } 
                    else{
                        WriteState.LastBits = (byte)((buffer[cur+1]&0x0F)<<2);
                        WriteState.Padding = 1;
                        WriteState.CurrentLineLength += 2; 
                    }
                    cur += 2; 
                    break; 

                case 1: // Two character padding needed 
                    WriteState.Buffer[WriteState.Length++] = base64EncodeMap[(buffer[cur]&0xFC)>>2];
                    if (dontDeferFinalBytes) {
                        WriteState.Buffer[WriteState.Length++] = base64EncodeMap[(byte)((buffer[cur]&0x03)<<4)];
                        WriteState.Buffer[WriteState.Length++] = base64EncodeMap[64]; 
                        WriteState.Buffer[WriteState.Length++] = base64EncodeMap[64];
                        WriteState.Padding = 0; 
                        WriteState.CurrentLineLength+=4; 
                    }
                    else{ 
                        WriteState.LastBits = (byte)((buffer[cur]&0x03)<<4);
                        WriteState.Padding = 2;
                        WriteState.CurrentLineLength ++;
                    } 
                    cur++;
                    break; 
            } 

            WriteState.AppendFooter(); 
            return cur - offset;
        }

        public Stream GetStream() 
        {
            return this; 
        } 

        public string GetEncodedString() 
        {
            return ASCIIEncoding.ASCII.GetString(this.WriteState.Buffer, 0, this.WriteState.Length);
        }
 
        public override int EndRead(IAsyncResult asyncResult)
        { 
            if (asyncResult == null) 
                throw new ArgumentNullException("asyncResult");
 
            int read = ReadAsyncResult.End(asyncResult);
            return read;
        }
 
        public override void EndWrite(IAsyncResult asyncResult)
        { 
            if (asyncResult == null) 
                throw new ArgumentNullException("asyncResult");
 
            WriteAsyncResult.End(asyncResult);
        }

        public override void Flush() 
        {
            if (this.writeState != null && WriteState.Length > 0) 
            { 
                FlushInternal();
            } 
            base.Flush();
        }

        private void FlushInternal() 
        {
            base.Write(WriteState.Buffer, 0, WriteState.Length); 
            WriteState.Length = 0; 
        }
 
        public override int Read(byte[] buffer, int offset, int count)
        {
            if (buffer == null)
                throw new ArgumentNullException("buffer"); 

            if (offset < 0 || offset > buffer.Length) 
                throw new ArgumentOutOfRangeException("offset"); 

            if (offset + count > buffer.Length) 
                throw new ArgumentOutOfRangeException("count");

            for (;;)
            { 
                // read data from the underlying stream
                int read = base.Read(buffer, offset, count); 
 
                // if the underlying stream returns 0 then there
                // is no more data - ust return 0. 
                if (read == 0)
                    return 0;

                // while decoding, we may end up not having 
                // any bytes to return pending additional data
                // from the underlying stream. 
                read = DecodeBytes(buffer, offset, read); 
                if (read > 0)
                    return read; 
            }
        }

        public override void Write(byte[] buffer, int offset, int count) 
        {
            if (buffer == null) 
                throw new ArgumentNullException("buffer"); 

            if (offset < 0 || offset > buffer.Length) 
                throw new ArgumentOutOfRangeException("offset");

            if (offset + count > buffer.Length)
                throw new ArgumentOutOfRangeException("count"); 

            int written = 0; 
 
            // do not append a space when writing from a stream since this means
            // it's writing the email body 
            for (;;)
            {
                written += EncodeBytes(buffer, offset + written, count - written, false, false);
                if (written < count) 
                    FlushInternal();
                else 
                    break; 
            }
        } 

        class ReadAsyncResult : LazyAsyncResult
        {
            Base64Stream parent; 
            byte[] buffer;
            int offset; 
            int count; 
            int read;
 
            static AsyncCallback onRead = new AsyncCallback(OnRead);

            internal ReadAsyncResult(Base64Stream parent, byte[] buffer, int offset, int count, AsyncCallback callback, object state) : base(null,state,callback)
            { 
                this.parent = parent;
                this.buffer = buffer; 
                this.offset = offset; 
                this.count = count;
            } 

            bool CompleteRead(IAsyncResult result)
            {
                this.read = this.parent.BaseStream.EndRead(result); 

                // if the underlying stream returns 0 then there 
                // is no more data - ust return 0. 
                if (read == 0)
                { 
                    InvokeCallback();
                    return true;
                }
 
                // while decoding, we may end up not having
                // any bytes to return pending additional data 
                // from the underlying stream. 
                this.read = this.parent.DecodeBytes(this.buffer, this.offset, this.read);
                if (this.read > 0) 
                {
                    InvokeCallback();
                    return true;
                } 

                return false; 
            } 

            internal void Read() 
            {
                for (;;)
                {
                    IAsyncResult result = this.parent.BaseStream.BeginRead(this.buffer, this.offset, this.count, onRead, this); 
                    if (!result.CompletedSynchronously || CompleteRead(result))
                        break; 
                } 
            }
 
            static void OnRead(IAsyncResult result)
            {
                if (!result.CompletedSynchronously)
                { 
                    ReadAsyncResult thisPtr = (ReadAsyncResult)result.AsyncState;
                    try 
                    { 
                        if (!thisPtr.CompleteRead(result))
                            thisPtr.Read(); 
                    }
                    catch (Exception e)
                    {
                        if (thisPtr.IsCompleted) 
                            throw;
                        thisPtr.InvokeCallback(e); 
                    } 
                }
            } 

            internal static int End(IAsyncResult result)
            {
                ReadAsyncResult thisPtr = (ReadAsyncResult)result; 
                thisPtr.InternalWaitForCompletion();
                return thisPtr.read; 
            } 
        }
 
        class WriteAsyncResult : LazyAsyncResult
        {
            Base64Stream parent;
            byte[] buffer; 
            int offset;
            int count; 
            static AsyncCallback onWrite = new AsyncCallback(OnWrite); 
            int written;
 
            internal WriteAsyncResult(Base64Stream parent, byte[] buffer, int offset, int count, AsyncCallback callback, object state) : base(null, state, callback)
            {
                this.parent = parent;
                this.buffer = buffer; 
                this.offset = offset;
                this.count = count; 
            } 

            internal void Write() 
            {
                for (;;)
                {
                    // do not append a space when writing from a stream since this means 
                    // it's writing the email body
                    this.written += this.parent.EncodeBytes(this.buffer, this.offset + this.written, 
                        this.count - this.written, false, false); 
                    if (this.written < this.count)
                    { 
                        IAsyncResult result = this.parent.BaseStream.BeginWrite(this.parent.WriteState.Buffer, 0, this.parent.WriteState.Length, onWrite, this);
                        if (!result.CompletedSynchronously)
                            break;
                        CompleteWrite(result); 
                    }
                    else 
                    { 
                        InvokeCallback();
                        break; 
                    }
                }
            }
 
            void CompleteWrite(IAsyncResult result)
            { 
                this.parent.BaseStream.EndWrite(result); 
                this.parent.WriteState.Length = 0;
            } 

            static void OnWrite(IAsyncResult result)
            {
                if (!result.CompletedSynchronously) 
                {
                    WriteAsyncResult thisPtr = (WriteAsyncResult)result.AsyncState; 
                    try 
                    {
                        thisPtr.CompleteWrite(result); 
                        thisPtr.Write();
                    }
                    catch (Exception e)
                    { 
                        if (thisPtr.IsCompleted)
                            throw; 
                        thisPtr.InvokeCallback(e); 
                    }
                } 
            }

            internal static void End(IAsyncResult result)
            { 
                WriteAsyncResult thisPtr = (WriteAsyncResult)result;
                thisPtr.InternalWaitForCompletion(); 
                Debug.Assert(thisPtr.written == thisPtr.count); 
            }
        } 

        class ReadStateInfo
        {
            byte val; 
            byte pos;
 
            internal byte Val 
            {
                get { return this.val; } 
                set { this.val = value; }
            }

            internal byte Pos 
            {
                get { return this.pos; } 
                set { this.pos = value; } 
            }
        } 
    }
}

// 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