Ascii85 use five ASCII characters to represent four bytes of binary data (encoded size 25% larger), it is more efficient than Base64, which use four characters to represent three bytes of data (33% increase). This encode method is suitable for posting small blocks of binary data to BBS or forum as plain text. It is also used in Portable Document Format. I implemented this algoritm in C# when doing my PDF processor project. You can find more description about ASCII85 in Wikipedia.
The code
///
<summary>
/// Adobe ASCII85 for encoding binary data in ASCII base-85
/// </summary>
public class ASCII85
{
/// <summary>
/// Maximum line length for encoded ASCII85 string;
/// set to zero for one unbroken line.
/// </summary>
public static int LineLength = 75 ;
static uint [] pow85 = { 85 * 85 * 85 * 85 , 85 * 85 * 85 , 85 * 85 , 85 , 1 };
/// <summary>
/// Encodes binary data into a plaintext ASCII85 format string
/// </summary>
/// <param name="data"> binary data to encode </param>
/// <returns> ASCII85 encoded string </returns>
public static string Encode( byte [] data)
{
MemoryStream input = new MemoryStream(data);
MemoryStream output = new MemoryStream();
Encode(input, output);
output.Position = 0 ;
StreamReader reader = new StreamReader(output, Encoding.ASCII);
return reader.ReadToEnd();
}
/// <summary>
/// Decodes an ASCII85 encoded string into the original binary data
/// </summary>
/// <param name="code"> ASCII85 encoded string </param>
/// <returns> byte array of decoded binary data </returns>
public static byte [] Decode( string code)
{
MemoryStream input = new MemoryStream(Encoding.ASCII.GetBytes(code));
MemoryStream output = new MemoryStream();
Decode(input, output);
return output.ToArray();
}
/// <summary>
/// Encodes the specified input.
/// </summary>
/// <param name="input"> The input. </param>
/// <param name="output"> The output. </param>
public static void Encode(Stream input, Stream output)
{
StreamWriter writer = new StreamWriter(output, Encoding.ASCII);
uint word = 0 ;
int count = 0 ;
int linepos = 0 ;
int code = input.ReadByte();
while (code != - 1 )
{
word |= ( uint )(code << ( 24 - (count * 8 )));
count ++ ;
if (count == 4 )
{
if (word == 0 )
{
writer.Write( ' z ' );
linepos ++ ;
}
else
{
writer.Write(Encode(word));
linepos += 5 ;
}
word = 0 ;
count = 0 ;
}
if (linepos >= LineLength)
{
writer.WriteLine();
linepos = 0 ;
}
code = input.ReadByte();
}
if (count > 0 )
{
writer.Write(Encode(word), 0 , count + 1 );
}
writer.Write( " ~> " );
writer.Flush();
}
private static char [] Encode( uint word)
{
char [] group = new char [ 5 ];
for ( int i = group.Length - 1 ; i >= 0 ; i -- )
{
group[i] = ( char )(word % 85 + 33 );
word /= 85 ;
}
return group;
}
/// <summary>
/// Decodes the specified input.
/// </summary>
/// <param name="input"> The input. </param>
/// <param name="output"> The output. </param>
public static void Decode(Stream input, Stream output)
{
BinaryWriter writer = new BinaryWriter(output);
uint word = 0 ;
int count = 0 ;
int code = input.ReadByte();
while (code != - 1 )
{
if (code == 122 ) // 'z'
{
if (count == 0 )
{
writer.Write(( uint ) 0 );
}
else
{
throw new Exception( " A z character occurs in the middle of a group. " );
}
}
else if (code >= 33 && code <= 117 )
{
word += ( uint )((code - 33 ) * pow85[count]);
count ++ ;
if (count == 5 )
{
writer.Write(Word2Bytes(word));
word = 0 ;
count = 0 ;
}
}
else
{
switch (code)
{
case 0 :
case 9 : // HT
case 10 : // LF
case 11 : // VT
case 12 : // FF
case 13 : // CR
case 32 : // SP
break ;
case 126 : // ~>
goto end;
default :
throw new Exception( " Invalid character in ASCII85Decode: " + code);
}
}
code = input.ReadByte();
}
end:
if (count > 0 )
{
count -- ;
word += pow85[count]; // add maximum remained value
writer.Write(Word2Bytes(word), 0 , count);
}
writer.Flush();
}
/// <summary>
/// split uint32 into bytes by big-endian order
/// </summary>
/// <param name="word"></param>
/// <returns></returns>
static byte [] Word2Bytes( uint word)
{
byte [] bytes = new byte [ 4 ];
bytes[ 0 ] = ( byte )((word & 0xFF000000 ) >> 24 );
bytes[ 1 ] = ( byte )((word & 0x00FF0000 ) >> 16 );
bytes[ 2 ] = ( byte )((word & 0x0000FF00 ) >> 8 );
bytes[ 3 ] = ( byte )(word & 0x000000FF );
return bytes;
}
}
/// Adobe ASCII85 for encoding binary data in ASCII base-85
/// </summary>
public class ASCII85
{
/// <summary>
/// Maximum line length for encoded ASCII85 string;
/// set to zero for one unbroken line.
/// </summary>
public static int LineLength = 75 ;
static uint [] pow85 = { 85 * 85 * 85 * 85 , 85 * 85 * 85 , 85 * 85 , 85 , 1 };
/// <summary>
/// Encodes binary data into a plaintext ASCII85 format string
/// </summary>
/// <param name="data"> binary data to encode </param>
/// <returns> ASCII85 encoded string </returns>
public static string Encode( byte [] data)
{
MemoryStream input = new MemoryStream(data);
MemoryStream output = new MemoryStream();
Encode(input, output);
output.Position = 0 ;
StreamReader reader = new StreamReader(output, Encoding.ASCII);
return reader.ReadToEnd();
}
/// <summary>
/// Decodes an ASCII85 encoded string into the original binary data
/// </summary>
/// <param name="code"> ASCII85 encoded string </param>
/// <returns> byte array of decoded binary data </returns>
public static byte [] Decode( string code)
{
MemoryStream input = new MemoryStream(Encoding.ASCII.GetBytes(code));
MemoryStream output = new MemoryStream();
Decode(input, output);
return output.ToArray();
}
/// <summary>
/// Encodes the specified input.
/// </summary>
/// <param name="input"> The input. </param>
/// <param name="output"> The output. </param>
public static void Encode(Stream input, Stream output)
{
StreamWriter writer = new StreamWriter(output, Encoding.ASCII);
uint word = 0 ;
int count = 0 ;
int linepos = 0 ;
int code = input.ReadByte();
while (code != - 1 )
{
word |= ( uint )(code << ( 24 - (count * 8 )));
count ++ ;
if (count == 4 )
{
if (word == 0 )
{
writer.Write( ' z ' );
linepos ++ ;
}
else
{
writer.Write(Encode(word));
linepos += 5 ;
}
word = 0 ;
count = 0 ;
}
if (linepos >= LineLength)
{
writer.WriteLine();
linepos = 0 ;
}
code = input.ReadByte();
}
if (count > 0 )
{
writer.Write(Encode(word), 0 , count + 1 );
}
writer.Write( " ~> " );
writer.Flush();
}
private static char [] Encode( uint word)
{
char [] group = new char [ 5 ];
for ( int i = group.Length - 1 ; i >= 0 ; i -- )
{
group[i] = ( char )(word % 85 + 33 );
word /= 85 ;
}
return group;
}
/// <summary>
/// Decodes the specified input.
/// </summary>
/// <param name="input"> The input. </param>
/// <param name="output"> The output. </param>
public static void Decode(Stream input, Stream output)
{
BinaryWriter writer = new BinaryWriter(output);
uint word = 0 ;
int count = 0 ;
int code = input.ReadByte();
while (code != - 1 )
{
if (code == 122 ) // 'z'
{
if (count == 0 )
{
writer.Write(( uint ) 0 );
}
else
{
throw new Exception( " A z character occurs in the middle of a group. " );
}
}
else if (code >= 33 && code <= 117 )
{
word += ( uint )((code - 33 ) * pow85[count]);
count ++ ;
if (count == 5 )
{
writer.Write(Word2Bytes(word));
word = 0 ;
count = 0 ;
}
}
else
{
switch (code)
{
case 0 :
case 9 : // HT
case 10 : // LF
case 11 : // VT
case 12 : // FF
case 13 : // CR
case 32 : // SP
break ;
case 126 : // ~>
goto end;
default :
throw new Exception( " Invalid character in ASCII85Decode: " + code);
}
}
code = input.ReadByte();
}
end:
if (count > 0 )
{
count -- ;
word += pow85[count]; // add maximum remained value
writer.Write(Word2Bytes(word), 0 , count);
}
writer.Flush();
}
/// <summary>
/// split uint32 into bytes by big-endian order
/// </summary>
/// <param name="word"></param>
/// <returns></returns>
static byte [] Word2Bytes( uint word)
{
byte [] bytes = new byte [ 4 ];
bytes[ 0 ] = ( byte )((word & 0xFF000000 ) >> 24 );
bytes[ 1 ] = ( byte )((word & 0x00FF0000 ) >> 16 );
bytes[ 2 ] = ( byte )((word & 0x0000FF00 ) >> 8 );
bytes[ 3 ] = ( byte )(word & 0x000000FF );
return bytes;
}
}