/*********************************************************************************
* TotalCross Software Development Kit *
* Copyright (C) 2000-2012 SuperWaba Ltda. *
* All Rights Reserved *
* *
* This library and virtual machine is distributed in the hope that it will *
* be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* *
* This file is covered by the GNU LESSER GENERAL PUBLIC LICENSE VERSION 3.0 *
* A copy of this license is located in file license.txt at the root of this *
* SDK or can be downloaded here: *
* http://www.gnu.org/licenses/lgpl-3.0.txt *
* *
*********************************************************************************/
package totalcross.net;
import totalcross.io.*;
/**
* Contains methods to encode and decode to base 64. The base 64 format is used to convert binaries into text so that they can be sent over a stream.
* It may then be converted back to binary form.<p>
* Note that 3 bytes base 16 (hexadecimal) are encoded into 4 bytes base 64, so the final length is 33% bigger.
*
* @since SuperWaba 5.1
*/
public class Base64
{
/**
* Convertion table from base 10 to base 64
*/
public static final char[] toBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
/**
* Convertion table from base 64 to base 10
*/
public static int[]toBase10 = new int[256];
static
{
char[]v64 = toBase64;
int []v10 = toBase10;
v10['='] = 0;
for (int i =v64.length-1; i >= 0; i--)
v10[v64[i]] = i;
}
/**
* Decodes a given base 64 stream into a base 10 stream.<BR>
* Important: error detection is NOT made, so you must be sure that no invalid characters are given!
*
* @param in The base 64 input stream. Its position must be set to the current size of the input data.
* @param out The base 10 output stream. When returning, its position will be set with the output size in bytes.
*/
public static void decode(ByteArrayStream in, ByteArrayStream out)
{
int from = 0;
int to = 0;
int tuples = in.getPos()>>2; // 4 bytes-group
out.setSize(tuples*3,false); // final output size
// get direct access to the buffers to improve performance
byte []fromBytes = in.getBuffer();
byte []toBytes = out.getBuffer();
while (tuples-- > 0)
{
int tri = (toBase10[fromBytes[from++]] << 18) +
(toBase10[fromBytes[from++]] << 12) +
(toBase10[fromBytes[from++]] << 6) +
(toBase10[fromBytes[from++]]);
toBytes[to++] = (byte)((tri>>16) & 0xFF);
toBytes[to++] = (byte)((tri>>8) & 0xFF);
toBytes[to++] = (byte)(tri & 0xFF);
}
// remove padding
if (fromBytes[from-1] == '=')
to--;
if (fromBytes[from-2] == '=')
to--;
// set the current size
out.reset();
out.skipBytes(to);
}
/**
* Decodes the given string into a byte array with the exact size.
*
* @param inStr The input string in base 64.
*/
public static byte[] decode(String inStr)
{
byte []bytes = inStr.getBytes();
ByteArrayStream tempIn = new ByteArrayStream(bytes);
try
{
tempIn.setPos(bytes.length);
}
catch (IOException e)
{
// should never happen.
e.printStackTrace();
}
ByteArrayStream tempOut = new ByteArrayStream(bytes.length);
decode(tempIn,tempOut);
return tempOut.toByteArray();
}
/**
* Encodes the given byte array and returns a base 64 generated string.
*
* @param bytes The base 10 byte array to be encoded into base 64.
*/
public static String encode(byte []bytes)
{
ByteArrayStream tempIn = new ByteArrayStream(bytes);
try
{
tempIn.setPos(bytes.length);
}
catch (IOException e)
{
// should never happen.
e.printStackTrace();
}
ByteArrayStream tempOut = new ByteArrayStream(bytes.length);
encode(tempIn,tempOut);
return new String(tempOut.getBuffer(), 0, tempOut.getPos());
}
/**
* Encodes the given byte array and returns a base 64 generated string.
*
* @param bytes The base 10 byte array to be encoded into base 64.
* @param start The start position in the array.
* @param count The number of bytes to encode.
* @return The encoded string.
*
* @since TotalCross 1.13
*/
public static String encode(byte[] bytes, int start, int count) // flsobral@tc113_40: created to be used by totalcross.mail.BinaryContentHandler. - guich@tc123_40: no longer used there
{
ByteArrayStream tempIn = new ByteArrayStream(count);
ByteArrayStream tempOut = new ByteArrayStream(count);
tempIn.writeBytes(bytes, start, count);
encode(tempIn, tempOut);
return new String(tempOut.toByteArray());
}
/**
* Encodes the given byte array and returns a base 64 generated byte array.
*
* @param bytes The base 10 byte array to be encoded into base 64.
* @return The base 64 byte array.
*/
public static byte[] encode(byte []bytes, int count) // guich@tc123_40
{
ByteArrayStream tempIn = new ByteArrayStream(bytes);
ByteArrayStream tempOut = new ByteArrayStream(count);
try
{
tempIn.setPos(count);
}
catch (IOException e)
{
// should never happen.
e.printStackTrace();
}
encode(tempIn, tempOut);
return tempOut.toByteArray();
}
/**
* Encodes a given base 10 stream into a base 64 stream.
*
* @param in The Base 10 intput stream. Its position must be set to the current size of the input data.
* @param out The Base 64 ouput stream. When returning, its position will be set with the output size in bytes.
*/
public static void encode(ByteArrayStream in, ByteArrayStream out)
{
int len = in.getPos();
int size = len*3/2;
size = ((size+4)>>2)<<2; // guich@552_3: make sure that the computed number is multiple of 4
out.setSize(size,false);
int end = len - 3;
int from = 0;
int to = 0;
byte []fromBytes = in.getBuffer();
byte []toBytes = out.getBuffer();
for (;from <= end; from+=3)
{
//fdie@511_6: fixed base64 encoding of signed data
int d = ((((int)fromBytes[from]) & 0xFF) << 16) |
((((int)fromBytes[from+1]) & 0xFF) << 8) |
((int)fromBytes[from + 2] & 0xFF);
toBytes[to++] = (byte)toBase64[(d >> 18) & 0x3F];
toBytes[to++] = (byte)toBase64[(d >> 12) & 0x3F];
toBytes[to++] = (byte)toBase64[(d >> 6) & 0x3F];
toBytes[to++] = (byte)toBase64[d & 0x3F];
}
if (from == len - 2)
{
int d = ((((int)fromBytes[from]) & 0xFF) << 16) |
((((int)fromBytes[from+1]) & 0xFF) << 8); //fdie@511_6
toBytes[to++] = (byte)toBase64[(d >> 18) & 0x3F];
toBytes[to++] = (byte)toBase64[(d >> 12) & 0x3F];
toBytes[to++] = (byte)toBase64[(d >> 6) & 0x3F];
toBytes[to++] = (byte)'=';
}
else
if (from == len - 1)
{
int d = (((int)fromBytes[from]) & 0xFF) << 16; //fdie@511_6
toBytes[to++] = (byte)toBase64[(d >> 18) & 0x3F];
toBytes[to++] = (byte)toBase64[(d >> 12) & 0x3F];
toBytes[to++] = (byte)'=';
toBytes[to++] = (byte)'=';
}
// set the current size
out.reset();
out.skipBytes(to);
}
}