Base64 é um protocolo de codificação que usa apenas seis bits, o que significa um conjunto de sessenta e quatro (64) elementos – daí Base64.
A conversão de oito (byte) para seis bits é feita da seguinte forma:
xxxxxx.xx xxxx.xxxx xx.xxxxxx
Ou ainda:
aaaaaabb bbbbcccc ccdddddd
Os elementos usados são caracteres simples, começando com as letras maiúsculas,
A
(0) a Z
(25), então as letras minúsculas, a
(26) a z
(51), os números, 0
(52) a 9
(61), e os caracteres +
(62) e /
(63).Na conversão de bytes (8b) para Base64 (6b), cada três bytes é convertido em quatro dígitos, então um código Base64 é sempre pensado em grupos de quatro. Se o tamanho de um código Base64 não for múltiplo de quatro, caracteres
=
são acrescentados ao final até que o tamanho seja múltiplo de quatro.Python
Em Python, Base64 é tão simples que nem tem graça:
>>> print "Kodumaro".encode("base64")
S29kdW1hcm8=
>>> print "S29kdW1hcm8=".decode("base64")
Kodumaro
Lua
Em Lua, é preciso baixar o módulo Mime do LuaSocket:
> require "mime"
> print(mime.b64 "Kodumaro")
S29kdW1hcm8=
> print(mime.unb64 "S29kdW1hcm8=")
Kodumaro
Smalltalk
Sendo muito sincero sobre o assunto, não sei como fazer conversão de Base64 em Smalltalk. =(
Mas sei que os módulos do Seaside providenciam isso!
Se alguém souber, por favor informe!
[update 2009-11-27]
Sugestão do Hugo:
No Squeak tem isso aqui:(Base64MimeConverter mimeEncode: 'base64' readStream) contents
(Base64MimeConverter mimeDecode: 'S29kdW1hcm8' as: ByteString) contents
E no Pharo tem métodos para strings:'base64' base64Encoded
'S29kdW1hcm8' base64Decoded
A implementação usa as coisas do Squeak:String>>base64Decoded
↑ (Base64MimeConverter mimeDecode: self as: self class)
String>>base64Encoded
↑ (Base64MimeConverter mimeEncode: self readStream) contents
Para outros Smalltalks eu não sei…
Legal né? =)
Muito legal sim, Hugo, valeu!
[/update]
C
Aha! Aqui começa de verdade a brincadeira!
É claro que você pode usar as funcionalidades de Base64 da gLibC, mas descobri que nem todas as versões dela apresentam tais funcionalidades:
#include <glib/gbase64.h>
Vamos precisar usar os cabeçalhos
stdlib.h
, string.h
e sys/types.h
. Como também vamos criar um cabeçalho para «compartilhar» algumas funções, ele também será incluído no início de nosso arquivo base64.c
:#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include "base64.h"
Agora vamos criar um array com todos os possíveis caracteres Base64 em sua ordem natural:
static const char b64all[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef"
"ghijklmnopqrstuvwxyz0123456789+/";
Também vamos precisar de três funções locais: uma para obter o índice de um elemento (
_getindex
), outra para codificar para Base64 um grupo de três bytes (_encode
) e mais uma para decodificar um grupo de quatro elementos Base64 (_decode
):int _decode(u_int8_t *, const u_int8_t *);
void _encode(u_int8_t *, const u_int8_t *, int);
Não é preciso uma função para
_getindex
:#define _getindex(c) (int) (index(b64all, c) - b64all)
Repara que, em vez de
char
, estamos usando u_int8_t
, que é mais conveniente quando queremos lidar com bytes enquanto bytes, não caracteres.A partir daqui, se preferir, organize as funções em ordem alfabética – ou na ordem que quiser.
A primeira função que implementaremos será para codificar uma string C (
const char *
) para Base64. Como a string pode não ser bem formada, a função deverá receber também seu tamanho:const char *b64encode(const char *original, int length) {
// Se o tamanho não for informado, consideramos uma string bem
// formada
if (length == 0)
length = strlen(original);
// Inteiro com o tamanho do código a ser gerado
int b64length = ((length + 2) / 3) * 4 + 1;
// Contadores para percorrer as strings
int i=0, j=0;
// Alocando memória para o código
char *b64 = (char *) malloc(sizeof(char) * b64length);
memset(b64, 0, b64length);
while (i < length) {
// Codifica um grupo de três bytes...
_encode(
(u_int8_t *) b64 + j,
(const u_int8_t *) original + i,
(length - i)
);
// E segue para o próximo grupo
i += 3;
j += 4;
}
// Retorna o código
return (const char *) b64;
}
A próxima função deve fazer o contrário, converter um código Base64 para uma string. Como a string resultante pode não ser bem formada – pode não ser terminada em carácter nulo ou possuir caracteres nulos no meio –, é preciso uma forma de informar seu tamanho, então ela receberá um ponteiro para um inteiro:
const char *b64decode(const char *b64, int *length) {
// Inteiro com o tamanho do código
int b64length = strlen(b64);
// Se não for múltiplo de quatro, há algo errado
if (b64length % 4 != 0)
return NULL;
// Tamanho máximo da string decifrada
int prob = (b64length / 4) * 3 + 1;
// Contadores para percorrer as strings
int i=0, j=0;
// Alocando memória para o resultado
char *s = (char *) malloc(sizeof(char) * prob);
while (j < b64length) {
// Decifra um grupo de quatro elementos
// e conta o resultado
i += _decode(
(u_int8_t *) s + i,
(const u_int8_t *) b64 + j
);
// Segue para o próximo grupo
j += 4;
}
// Se foi fornecido um inteiro para contagem, informa o tamanho
if (length != NULL)
*length = i;
// Retorna a string decifrada
return (const char *) s;
}
Agora precisamos das funções específica para as conversões.
Primeiro para codificar:
void _encode(u_int8_t *dest, const u_int8_t *src, int len) {
// Menor que 1, nada a fazer
if (len < 1)
return;
// Dados a serem retornados
int aux[] = { 0, 0, 0, 0 };
// Primeiro elemento: os 6 bits mais significativos do primeiro
// byte
aux[0] = src[0] >> 2;
// Segundo elemento: os 2 bits menos significativos do primeiro e
// os quatro bits mais significativos do segundo byte
aux[1] = (src[0] & 0x03) << 4;
if (len > 1) {
// SE houver um segundo...
aux [1] |= (src[1] & 0xf0) >> 4;
// Terceiro elemento: os quatro bits menos significativos do
// segundo e os dois mais significativos do terceiro byte
aux [2] = (src[1] & 0x0f) << 2;
if (len > 2) {
// Se houver um terceiro...
aux[2] |= src[2] >> 6;
// Quarto elemento: os seis bits menos significatos do
// terceiro byte
aux[3] = src[2] & 0x3f;
}
}
// Codifica agora os valores numéricos para string
dest[0] = b64all[aux[0]];
dest[1] = b64all[aux[1]];
dest[2] = '=';
dest[3] = '=';
if (len > 1) {
dest[2] = b64all[aux[2]];
if (len > 2)
dest[3] = b64all[aux[3]];
}
}
int _decode(u_int8_t *dest, const u_int8_t *src) {
// Representação numérica do código
int aux[] = { 0, 0, 0, 0 };
// Contador
int i, c = 1;
// Converte código para valores numéricos
for (i = 0; i < 4; ++i)
aux[i] = _getindex(src[i]);
// Primeiro byte: primeiro elemento seguido dos quatro bits mais
// significativos do segundo
dest[0] = (u_int8_t) (aux[0] << 2) | ((aux[1] & 0x30) >> 4);
// Zera os bytes seguintes
dest[1] = '\0';
dest[2] = '\0';
if (aux[2] != -1) {
// Se houver um terceiro elemento...
++c;
// Segundo byte: quatro bits menos significativos do segundo
// elemento seguidos pelos quatro bits mais significativos do
// terceiro
dest[1] = (u_int8_t) ((aux[1] & 0x0f) << 4) | (aux[2] >> 2);
if (aux[3] != -1) {
// Se houver um quarto elemento...
++c;
// Terceiro byte: dois bits menos significativos do
// terceiro elemento seguidos pelo quarto elemento
dest[2] = (u_int8_t)
((aux[2] & 0x03) << 6) |
aux[3];
}
}
// Retorna o tamanho da string
return c;
}
Cabeçalho
Por último criamos o cabeçalho
base64.h
, tornando públicas as duas funções de codificação e decodificação:#ifndef _BASE64_H
#define _BASE64_H
const char *b64decode(const char *, int *);
const char *b64encode(const char *, int);
#endif
Conclusão
Mesmo que ninguém vá implementar uma biblioteca de conversão Base64, espero que este artigo sirva para ajudar a entender melhor do que se trata Base64.
Se quiser acrescentar algum açucar sintático, coloque em
base64.c
:#ifdef C_PLUS_PLUS
const char *b64decode(const char *b64, int &length) {
return b64decode(b64, &length);
}
#endif
E mude o conteúdo de
base64.h
para:#ifndef _BASE64_H
#define _BASE64_H
#ifdef C_PLUS_PLUS
extern "C" {
#endif
const char *b64decode(const char *, int *);
const char *b64encode(const char *, int);
#ifdef C_PLUS_PLUS
}
const char *b64decode(const char *, int &);
#else
#define _b64decode(s, len) b64decode(s, &len)
#endif
#endif
[]'s
Cacilhas, La Batalema