1////////////////////////////////////////////////////////////////////////////////
2//
3//  ADOBE SYSTEMS INCORPORATED
4//  Copyright 2004-2007 Adobe Systems Incorporated
5//  All Rights Reserved.
6//
7//  NOTICE: Adobe permits you to use, modify, and distribute this file
8//  in accordance with the terms of the license agreement accompanying it.
9//
10////////////////////////////////////////////////////////////////////////////////
11
12package mx.utils
13{
14
15import flash.utils.ByteArray;
16
17/**
18 * A utility class to encode a String or ByteArray as a Base64 encoded String.
19 *
20 *  @langversion 3.0
21 *  @playerversion Flash 9
22 *  @playerversion AIR 1.1
23 *  @productversion Flex 3
24 */
25public class Base64Encoder
26{
27    //--------------------------------------------------------------------------
28    //
29    //  Static Class Variables
30    //
31    //--------------------------------------------------------------------------
32
33    /**
34     *  Constant definition for the string "UTF-8".
35     *
36     *  @langversion 3.0
37     *  @playerversion Flash 9
38     *  @playerversion AIR 1.1
39     *  @productversion Flex 3
40     */
41    public static const CHARSET_UTF_8:String = "UTF-8";
42
43    /**
44     * The character codepoint to be inserted into the encoded output to
45     * denote a new line if <code>insertNewLines</code> is true.
46     *
47     * The default is <code>10</code> to represent the line feed <code>\n</code>.
48     *
49     *  @langversion 3.0
50     *  @playerversion Flash 9
51     *  @playerversion AIR 1.1
52     *  @productversion Flex 3
53     */
54    public static var newLine:int = 10;
55
56    //--------------------------------------------------------------------------
57    //
58    //  Constructor
59    //
60    //--------------------------------------------------------------------------
61
62    /**
63     * Constructor.
64     *
65     *  @langversion 3.0
66     *  @playerversion Flash 9
67     *  @playerversion AIR 1.1
68     *  @productversion Flex 3
69     */
70    public function Base64Encoder()
71    {
72        super();
73        reset();
74    }
75
76    //--------------------------------------------------------------------------
77    //
78    //  Variables
79    //
80    //--------------------------------------------------------------------------
81
82    /**
83     * A Boolean flag to control whether the sequence of characters specified
84     * for <code>Base64Encoder.newLine</code> are inserted every 76 characters
85     * to wrap the encoded output.
86     *
87     * The default is true.
88     *
89     *  @langversion 3.0
90     *  @playerversion Flash 9
91     *  @playerversion AIR 1.1
92     *  @productversion Flex 3
93     */
94    public var insertNewLines:Boolean = true;
95
96    //--------------------------------------------------------------------------
97    //
98    //  Public Methods
99    //
100    //--------------------------------------------------------------------------
101
102    /**
103     * @private
104     */
105    public function drain():String
106    {
107        var result:String = "";
108
109        for (var i:uint = 0; i < _buffers.length; i++)
110        {
111            var buffer:Array = _buffers[i] as Array;
112            result += String.fromCharCode.apply(null, buffer);
113        }
114
115        _buffers = [];
116        _buffers.push([]);
117
118        return result;
119    }
120
121    /**
122     * Encodes the characters of a String in Base64 and adds the result to
123     * an internal buffer. Strings must be in ASCII format.
124     *
125     * <p>Subsequent calls to this method add on to the
126     * internal buffer. After all data have been encoded, call
127     * <code>toString()</code> to obtain a Base64 encoded String.</p>
128     *
129     * @param data The String to encode.
130     * @param offset The character position from which to start encoding.
131     * @param length The number of characters to encode from the offset.
132     *
133     *  @langversion 3.0
134     *  @playerversion Flash 9
135     *  @playerversion AIR 1.1
136     *  @productversion Flex 3
137     */
138    public function encode(data:String, offset:uint=0, length:uint=0):void
139    {
140        if (length == 0)
141            length = data.length;
142
143        var currentIndex:uint = offset;
144
145        var endIndex:uint = offset + length;
146        if (endIndex > data.length)
147            endIndex = data.length;
148
149        while (currentIndex < endIndex)
150        {
151            _work[_count] = data.charCodeAt(currentIndex);
152            _count++;
153
154            if (_count == _work.length || endIndex - currentIndex == 1)
155            {
156                encodeBlock();
157                _count = 0;
158                _work[0] = 0;
159                _work[1] = 0;
160                _work[2] = 0;
161            }
162            currentIndex++;
163        }
164    }
165
166    /**
167     * Encodes the UTF-8 bytes of a String in Base64 and adds the result to an
168     * internal buffer. The UTF-8 information does not contain a length prefix.
169     * Subsequent calls to this method add on to the internal buffer. After all
170     * data have been encoded, call <code>toString()</code> to obtain a Base64
171     * encoded String.
172     *
173     * @param data The String to encode.
174     *
175     *  @langversion 3.0
176     *  @playerversion Flash 9
177     *  @playerversion AIR 1.1
178     *  @productversion Flex 3
179     */
180    public function encodeUTFBytes(data:String):void
181    {
182        var bytes:ByteArray = new ByteArray();
183        bytes.writeUTFBytes(data);
184        bytes.position = 0;
185        encodeBytes(bytes);
186    }
187
188    /**
189     * Encodes a ByteArray in Base64 and adds the result to an internal buffer.
190     * Subsequent calls to this method add on to the internal buffer. After all
191     * data have been encoded, call <code>toString()</code> to obtain a
192     * Base64 encoded String.
193     *
194     * @param data The ByteArray to encode.
195     * @param offset The index from which to start encoding.
196     * @param length The number of bytes to encode from the offset.
197     *
198     *  @langversion 3.0
199     *  @playerversion Flash 9
200     *  @playerversion AIR 1.1
201     *  @productversion Flex 3
202     */
203    public function encodeBytes(data:ByteArray, offset:uint=0, length:uint=0):void
204    {
205        if (length == 0)
206            length = data.length;
207
208        var oldPosition:uint = data.position;
209        data.position = offset;
210        var currentIndex:uint = offset;
211
212        var endIndex:uint = offset + length;
213        if (endIndex > data.length)
214            endIndex = data.length;
215
216        while (currentIndex < endIndex)
217        {
218            _work[_count] = data[currentIndex];
219            _count++;
220
221            if (_count == _work.length || endIndex - currentIndex == 1)
222            {
223                encodeBlock();
224                _count = 0;
225                _work[0] = 0;
226                _work[1] = 0;
227                _work[2] = 0;
228            }
229            currentIndex++;
230        }
231
232        data.position = oldPosition;
233    }
234
235    /**
236     * @private
237     */
238    public function flush():String
239    {
240        if (_count > 0)
241            encodeBlock();
242
243        var result:String = drain();
244        reset();
245        return result;
246    }
247
248    /**
249     * Clears all buffers and resets the encoder to its initial state.
250     *
251     *  @langversion 3.0
252     *  @playerversion Flash 9
253     *  @playerversion AIR 1.1
254     *  @productversion Flex 3
255     */
256    public function reset():void
257    {
258        _buffers = [];
259        _buffers.push([]);
260        _count = 0;
261        _line = 0;
262        _work[0] = 0;
263        _work[1] = 0;
264        _work[2] = 0;
265    }
266
267    /**
268     * Returns the current buffer as a Base64 encoded String. Note that
269     * calling this method also clears the buffer and resets the
270     * encoder to its initial state.
271     *
272     * @return The Base64 encoded String.
273     *
274     *  @langversion 3.0
275     *  @playerversion Flash 9
276     *  @playerversion AIR 1.1
277     *  @productversion Flex 3
278     */
279    public function toString():String
280    {
281        return flush();
282    }
283
284    //--------------------------------------------------------------------------
285    //
286    //  Private Methods
287    //
288    //--------------------------------------------------------------------------
289
290    /**
291     * @private
292     */
293    private function encodeBlock():void
294    {
295        var currentBuffer:Array = _buffers[_buffers.length - 1] as Array;
296        if (currentBuffer.length >= MAX_BUFFER_SIZE)
297        {
298            currentBuffer = [];
299            _buffers.push(currentBuffer);
300        }
301
302        currentBuffer.push(ALPHABET_CHAR_CODES[(_work[0] & 0xFF) >> 2]);
303        currentBuffer.push(ALPHABET_CHAR_CODES[((_work[0] & 0x03) << 4) | ((_work[1] & 0xF0) >> 4)]);
304
305        if (_count > 1)
306            currentBuffer.push(ALPHABET_CHAR_CODES[((_work[1] & 0x0F) << 2) | ((_work[2] & 0xC0) >> 6) ]);
307        else
308            currentBuffer.push(ESCAPE_CHAR_CODE);
309
310        if (_count > 2)
311            currentBuffer.push(ALPHABET_CHAR_CODES[_work[2] & 0x3F]);
312        else
313            currentBuffer.push(ESCAPE_CHAR_CODE);
314
315        if (insertNewLines)
316        {
317            if ((_line += 4) == 76)
318            {
319                currentBuffer.push(newLine);
320                _line = 0;
321            }
322        }
323    }
324
325    //--------------------------------------------------------------------------
326    //
327    //  Private Variables
328    //
329    //--------------------------------------------------------------------------
330
331    /**
332     * An Array of buffer Arrays.
333     *
334     *  @langversion 3.0
335     *  @playerversion Flash 9
336     *  @playerversion AIR 1.1
337     *  @productversion Flex 3
338     */
339    private var _buffers:Array;
340    private var _count:uint;
341    private var _line:uint;
342    private var _work:Array = [ 0, 0, 0 ];
343
344    /**
345     * This value represents a safe number of characters (i.e. arguments) that
346     * can be passed to String.fromCharCode.apply() without exceeding the AVM+
347     * stack limit.
348     *
349     * @private
350     */
351    public static const MAX_BUFFER_SIZE:uint = 32767;
352
353    private static const ESCAPE_CHAR_CODE:Number = 61; // The '=' char
354
355    /*
356        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
357        'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
358        'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
359        'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
360        'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
361        'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
362        'w', 'x', 'y', 'z', '0', '1', '2', '3',
363        '4', '5', '6', '7', '8', '9', '+', '/'
364    */
365    private static const ALPHABET_CHAR_CODES:Array =
366    [
367        65,   66,  67,  68,  69,  70,  71,  72,
368        73,   74,  75,  76,  77,  78,  79,  80,
369        81,   82,  83,  84,  85,  86,  87,  88,
370        89,   90,  97,  98,  99, 100, 101, 102,
371        103, 104, 105, 106, 107, 108, 109, 110,
372        111, 112, 113, 114, 115, 116, 117, 118,
373        119, 120, 121, 122,  48,  49,  50,  51,
374        52,   53,  54,  55,  56,  57,  43,  47
375    ];
376
377}
378
379}
380