ASCII 85 é uma forma de codificação de sequências binárias que utiliza 85 caracteres imprimíveis. O valor codificado fica aproximadamente 25% maior que o valor original. Por questões de comparação, o "base 64" deixa o valor codificado aproximadamente 33% maior que o valor original.
Porém, este algoritmo não é implementado nativamente no PHP como é o base 64 através das funções base64_encode e base64_decode.
Há alguns anos implementei uma classe com o algoritmo de ASCII 85, que posto aqui.
Linguagem: PHP
Copyright 2008 Rubens Takiguti Ribeiro
Licença: LGPL 3 ou superior
/**
* ASCII85: encode/decode binary data with text characters.
*
* About:
* - Generates a text 25% larger from de original (approximately);
* - Suport to Adobe version of algorithm;
* - Suport to btoa version of algorithm;
*
* @author Rubens Takiguti Ribeiro
* @date 2008-11-26
* @version 1.0 2008-11-27
* @see http://en.wikipedia.org/wiki/Ascii85
* @license http://www.gnu.org/licenses/lgpl-3.0.html LGPLv3
* @copyright Copyright (C) 2008 Rubens Takiguti Ribeiro
*/
final class ascii85 {
/// # CONSTANTS
/**
* Algorithm variations.
*/
const BASIC = 0; // Default implementation
const ADOBE = 1; // Adobe version
const BTOA = 2; // BTOA version
/**
* Exception codes
*/
const THROW_ADOBE_DELIMITER = 1;
const THROW_BTOA_CHECKS = 2;
const THROW_BTOA_VALUE = 3;
const THROW_TUPLE_CHAR = 4;
const THROW_TUPLE_SIZE = 5;
/// # METHODS
/**
* This class is not instantiable.
* Use static methods only.
*
* @return void
*/
private function __construct() {}
/**
* Encode to ASCII85 format.
*
* @see CONSTANTS section
* @param string $value String to be encoded.
* @param int $variation Constant code of algorithm variation.
* @param bool || int $split_pos Position to split returned string.
* @return string Encoded string in ASCII85 format or false on failure.
*/
public static function encode($value, $variation = self::BASIC, $split_pos = false) {
switch ($variation) {
case self::ADOBE:
$y_exception = false;
$z_exception = true;
break;
case self::BTOA:
$y_exception = true;
$z_exception = true;
break;
case self::BASIC:
$y_exception = false;
$z_exception = false;
break;
}
// Returned value
$return = '';
// Get string tuples of 4 bytes
$tuples = str_split($value, 4);
// Foreach tuple
foreach ($tuples as $tuple) {
// Convert tuple (string value) to binary (integer value)
$bin_tuple = 0;
$len = strlen($tuple);
for ($i = 0; $i < $len; $i++) {
$bin_tuple |= (ord($tuple[$i]) << ((3 - $i) * 8));
}
// Get unsigned value as string
$bin_tuple = sprintf('%u', $bin_tuple);
// Zero-tuple is represented as "z"
if ($z_exception && $bin_tuple == 0) {
$return .= 'z';
// Space-tuple is represented as "y"
} elseif ($y_exception && $bin_tuple == 0x20202020) {
$return .= 'y';
// Tuple
} else {
// Create a tuple 85 (string value)
$i = 5;
$tuple85 = '';
while ($i--) {
$c = bcmod($bin_tuple, '85');
$tuple85 = chr(bcadd($c, '33')).$tuple85;
$bin_tuple = bcdiv($bin_tuple, '85', 0);
}
$tuple85 = substr($tuple85, 0, $len + 1);
// Append to return value
$return .= $tuple85;
}
}
switch ($variation) {
case self::BASIC:
self::split($return, $split_pos);
break;
case self::ADOBE:
$return = '<~'.$return.'~>';
self::split($return, $split_pos);
break;
case self::BTOA:
self::btoa_create_checks($value, $size_dec, $size_hex, $check_xor, $check_sum, $check_rot);
self::split($return, $split_pos);
$return = sprintf(
"xbtoa Begin\n%s\nxbtoa End N %s %s E %s S %s R %s\n",
$return, $size_dec, $size_hex, $check_xor, $check_sum, $check_rot
);
break;
}
return $return;
}
/**
* Decode from ASCII85.
*
* @exception THROW_BTOA_VALUE if encoded value has invalid format.
* @exception THROW_BTOA_CHECKS If generated value did not match with checks.
* @exception THROW_ADOBE_DELIMITER If there are not Adobe delimiter.
* @exception THROW_TUPLE_CHAR If any tuple has invalid characters.
* @exception THROW_TUPLE_SIZE If any tuple is greater than 2^32.
* @param string $value Encoded text to be decoded.
* @param int $variation Constant code of algorithm variation.
* @return string Decoded string or false on failure.
*/
public static function decode($value, $variation = self::BASIC) {
// Get BTOA checks
if ($variation == self::BTOA) {
try {
self::btoa_get_checks($value, $size_dec, $size_hex, $check_xor, $check_sum, $check_rot);
} catch (Exception $e) {
throw $e;
}
}
// Clean value
try {
self::clean($value, $variation);
} catch (Exception $e) {
throw $e;
}
// Returned value
$return = '';
// Const values
$max_tuple = pow(2, 32);
$base85 = array();
$base85[0] = 1; // 85^0
$base85[1] = $base85[0] * 85; // 85^1
$base85[2] = $base85[1] * 85; // 85^2
$base85[3] = $base85[2] * 85; // 85^3
$base85[4] = $base85[3] * 85; // 85^4
// Get ASCII85 tuples
$value = str_split($value, 5);
// Foreach tuple 85 of 5 bytes
foreach ($value as $tuple85) {
// If zero-tuple was found
if ($tuple85 === 'zzzzz') {
$return .= str_repeat(chr(0), 4);
continue;
// If space-tupe was found
} elseif ($tuple85 === 'yyyyy') {
$return .= str_repeat(' ', 4);
continue;
}
// If the tuple has invalid chars
if (!preg_match('/^[\x21-\x75]{1,5}$/', $tuple85)) {
throw new Exception('Tuple has invalid characters ('.$tuple85.')', self::THROW_TUPLE_CHAR);
}
// Convert tuple 85 (text) to binary tuple (number)
$bin_tuple = '0';
$len = strlen($tuple85);
// Append "u" to missing positions to avoid rounding-down effect
$tuple85 .= str_repeat('u', 5 - $len);
for ($i = 0; $i < 5; $i++) {
$bin_char = strval(ord($tuple85[$i]) - 33);
$bin_tuple += bcmul($bin_char, $base85[4 - $i]);
}
// If binary value is greater than 2^32
if ($bin_tuple > $max_tuple) {
throw new Exception('Tuple is greater than '.$max_tuple, self::THROW_TUPLE_SIZE);
}
$bin_tuple = bindec(sprintf('%032b', $bin_tuple));
// Create a tuple (string value)
$i = 4;
$tuple = '';
$len -= 1;
while ($len--) {
$c = ($bin_tuple >> (--$i * 8)) & 0xFF;
$tuple .= chr($c);
}
// Append to return value
$return .= $tuple;
}
// Get BTOA checks
if ($variation == self::BTOA) {
$v = self::btoa_validate_checks($return, $size_dec, $size_hex, $check_xor, $check_sum, $check_rot);
if (!$v) {
throw new Exception('Generated text did not pass by validation of BTOA checks', self::THROW_BTOA_CHECKS);
}
}
return $return;
}
/**
* Split a string on a specific position.
*
* @param string $value Value to be splited
* @param int || bool $pos Position to split value.
* @return void
*/
private static function split(&$value, $pos) {
if (is_numeric($pos) && $pos > 0) {
$value = chunk_split($value, $pos, "\n");
$value = rtrim($value);
}
}
/**
* Convert some characters of an encoded text.
*
* @exception THROW_ADOBE_DELIMITER If there are not Adobe delimiter.
* @param string $value Encoded value.
* @param int $variation Constant code of algorithm variation.
* @return bool
*/
private static function clean(&$value, $variation) {
$value = trim($value);
switch ($variation) {
case self::BASIC:
// Remove spaces
$tr = array(' ' => '',
"\r" => '',
"\n" => '',
"\t" => '',
"\0" => '',
"\f" => ''
);
$value = strtr($value, $tr);
break;
case self::ADOBE:
if (substr($value, 0, 2) != '<~' || substr($value, -2, 2) != '~>') {
throw new Exception('Value does not have Adobe delimiter', self::THROW_ADOBE_DELIMITER);
}
// Remove <~ and ~>
$value = substr($value, 2, strlen($value) - 4);
// Remove spaces and convert "z" exception to a tuple
$tr = array(' ' => '',
"\r" => '',
"\n" => '',
"\t" => '',
"\0" => '',
"\f" => '',
'z' => 'zzzzz'
);
$value = strtr($value, $tr);
break;
case self::BTOA:
// Remove first line
$first = strpos($value, "\n");
$value = substr($value, $first + 1);
// Remove last line
$last = strrpos($value, "\n");
$value = substr($value, 0, $last);
// Remove spaces and convert "z" and "y" exception to a tuple
$tr = array(' ' => '',
"\r" => '',
"\n" => '',
"\t" => '',
"\0" => '',
"\f" => '',
'z' => 'zzzzz',
'y' => 'yyyyy'
);
$value = strtr($value, $tr);
break;
}
}
/**
* Gets check values from an encoded ASCII85 BTOA value.
*
* @exception THROW_BTOA_VALUE if encoded value has invalid format.
* @param string $value Original value.
* @param string $size_dec Decimal size.
* @param string $size_hex Hexadecinal size.
* @param string $check_xor Check XOR.
* @param string $check_sum Check SUM.
* @param string $check_rot Check ROT.
* @return void
*/
public static function btoa_get_checks($value, &$size_dec, &$size_hex, &$check_xor, &$check_sum, &$check_rot) {
$value = trim($value);
$exp = '/xbtoa[\040]+End[\040]+'.
'N[\040]+([\d]+)[\040]+([0-9a-f]+)[\040]+'.
'E[\040]+([0-9a-f]+)[\040]+'.
'S[\040]+([0-9a-f]+)[\040]+'.
'R[\040]+([0-9a-f]+)'.
'$/i';
if (!preg_match($exp, $value, $match)) {
throw new Exception('Invalid ASCII85 BTOA encoded data', self::THROW_BTOA_VALUE);
}
$size_dec = $match[1];
$size_hex = $match[2];
$check_xor = $match[3];
$check_sum = $match[4];
$check_rot = $match[5];
}
/**
* Generate BTOA values to validate a received text.
*
* @param string $value Original value.
* @param string $size_dec Decimal size.
* @param string $size_hex Hexadecinal size.
* @param string $check_xor Check XOR.
* @param string $check_sum Check SUM.
* @param string $check_rot Check ROT.
* @return void
*/
public static function btoa_create_checks($value, &$size_dec, &$size_hex, &$check_xor, &$check_sum, &$check_rot) {
$size = strlen($value);
// Check xor, sum, rot
$check_xor = 0;
$check_sum = 0;
$check_rot = 0;
for ($i = 0; $i < $size; $i++) {
$c = ord($value[$i]);
$check_xor ^= $c;
$check_sum += $c + 1;
$check_rot <<= 1;
if ($check_rot & 0x80000000) {
$check_rot += 1;
}
$check_rot += $c;
}
$size_dec = sprintf('%0.0f', $size);
$size_hex = sprintf('%x', $size);
$check_xor = sprintf('%x', $check_xor);
$check_sum = sprintf('%x', $check_sum);
$check_rot = sprintf('%x', $check_rot);
}
/**
* Validate a decoded ASCII85 BTOA value.
*
* @param string $value Decoded value.
* @param string $size_dec Decimal size.
* @param string $size_hex Hexadecinal size.
* @param string $check_xor Check XOR.
* @param string $check_sum Check SUM.
* @param string $check_rot Check ROT.
* @return bool
*/
public static function btoa_validate_checks($value, $size_dec, $size_hex, $check_xor, $check_sum, $check_rot) {
self::btoa_create_checks($value, $size_dec2, $size_hex2, $check_xor2, $check_sum2, $check_rot2);
return $size_dec === $size_dec2 &&
$size_hex === $size_hex2 &&
$check_xor === $check_xor2 &&
$check_sum === $check_sum2 &&
$check_rot === $check_rot2;
}
}//class
Para usar a classe, basta usar os métodos encode e decode.
0 comentários
Postar um comentário
Nota: fique a vontade para expressar o que achou deste artigo ou do blog.
Dica: para acompanhar as respostas, acesse com uma conta do Google e marque a opção "Notifique-me".
Atenção: o blogger não permite inclusão de tags nos comentários, por isso, use algum site externo para postar seu código com dúvidas e deixe o link aqui. Exemplo: pastebin.com