Бібліотека кодування Base64 з Arduino
База64 це система кодування, яка використовує 64 символи, згруповані в повідомлення, довжина яких кратна чотирьом. Ці повідомлення (пакети даних) завершуються, якщо необхідно, символом плюса (тому використовується 65), часто знаком рівності (=), якщо закодована корисна інформація призводить до меншої довжини.
За допомогою 64 знаків можна працювати з 10 цифрами, великими та малими літерами (26+26) коду ASCII, проблема полягає в тому, що існує 62, скажімо, однозначних символи плюс два, які відрізняються в різних реалізаціях. Хоча іноді його називають виразом «персонажі ASCII для друку", насправді це ті, які варіюються від коду, представленого кодом 32 (пробіл) до 126 (~) 95, який справді можна роздрукувати.
Здійснення кодування База64 найбільш використовуваний, що з PEM, який також використовується MIME, працюйте з додатковими знаками «+» і «/» і знаком «=», щоб додати довжину пакетів, кратну чотирьом. Букви A-Z займають позиції 0-25, літери a-z займають позиції 26-51, цифри 0-9 займають позиції 52-61, знак плюс (+) позиції 62, а позиція 63 займає косу риску (/).
Спосіб представлення даних у форматі База64 складається з взяття, з вихідних даних, груп 6 біт які представлені відповідним кодом. Якщо біти залишаються, вони заповнюються нулями праворуч. Якщо отримане число кодів не кратне чотирьом, воно заповнюється рівним праворуч.
На наступному зображенні показано кодування ASCII тексту ("ом") і способу його перетворення База64. Оскільки є 7 символів, остаточне повідомлення має бути заповнене знаком рівності в кінці. Можна сказати, що текст «ом» в ASCII еквівалент «b2htaW8=" в База64.
Особливе використання кодування База64 Вони також зазвичай встановлюють максимальну довжину лінії. Впровадження MIME Кожен рядок обмежує 76 символами. Зазвичай рядки будуть розділені кодом кінця рядка (CR, представлений значенням 0x0D у ASCII) і інший новий рядок (NL, який відповідає коду ASCII 0x0A).
Незручність, яка додається при реалізації кодування База64 на пристрої з невеликою кількістю ресурсів, як це часто буває з a мікроконтролер полягає в тому, що вам потрібно кодувати в міру надходження інформації або за допомогою a буфера мінімум, який також вимагає забезпечення системи, яка вказує, що кінець вихідного повідомлення досягнуто, наприклад, шляхом додавання спеціального коду або використання PIN-коду, рівень якого (синхронізований з отриманням) вказує на статус повідомлення.
Наведений нижче приклад коду a бібліотека для Arduino для кодування в Base64 яка реалізована з обома критеріями: кодування інформації, що надходить (без a буфера) і дочекайтеся завершення попереджувального сигналу.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
//base64.h
#include <string.h> // memcpy/strncpy
#define LONGITUD_LINEA 76
#define MASCARA_B64 0B00111111
#define ULTIMO_CODIGO_BASE64 64 // 64 caracteres más el signo igual (empezando a contar desde cero)
#define MAXIMA_LONGITUD_RESULTADO 6 // Máximo número de caracteres del resultado parcial de la codificación. Puede ser 1 si no se ha llegado al final de un bloque (2 bytes en el original, 4 en la conversión), 2 si se ha llegado al final de un bloque, 3 si no se ha llegado al final de un bloque pero se supera la longitud máxima de la línea, 4 si se llega al final de un bloque y se supera la longitud máxima de la línea, 5 si hay que rellenar con un signo igual o 6 si hay que rellenar con dos signos igual
class Base64
{
private:
unsigned char simbolo_base64[ULTIMO_CODIGO_BASE64+1]; // Espacio para la codificación Base64, el relleno (=) una terminación en \0
unsigned int numero_valor; // Posición (empezando en cero) que ocupa el valor que se desea convertir en el mensaje completo original
unsigned int numero_codigo; // Posición del último código calculado. Podría limitarse al ancho de la línea (LONGITUD_LINEA, 76 caracteres) pero usando un contenedor alto se podría implementar también una cuenta estadística
unsigned char resto_base64; // Último resto obtenido al calcular el último código
unsigned char resultado[MAXIMA_LONGITUD_RESULTADO+1]; // Resultado de la conversión actual. Si es terminal puede incluir el caracter 65 (=) una o dos veces
unsigned char contador_caracteres_resultado=0;
void acumular_resultado(unsigned char valor);
public:
Base64();
~Base64();
void iniciar_conversion();
unsigned char *convertir(unsigned char valor_original, bool terminar_conversion);
unsigned char *convertir(unsigned char valor_original);
unsigned char *terminar();
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
//base64.cpp
#include “base64.h”
Base64::Base64() // Constructor
{
memcpy(simbolo_base64,“ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=”,ULTIMO_CODIGO_BASE64+1);
iniciar_conversion();
}
Base64::~Base64() // Destructor
{
}
void Base64::iniciar_conversion()
{
numero_valor=0;
numero_codigo=0;
resto_base64=0;
}
unsigned char *Base64::convertir(unsigned char valor_original, bool terminar_conversion) // Valor que se desea convertir a Base64
{
convertir(valor_original);
if(terminar_conversion)
{
terminar();
}
return resultado;
}
unsigned char *Base64::convertir(unsigned char valor_original) // Valor que se desea convertir a Base64
{
unsigned char desplazamiento;
contador_caracteres_resultado=0;
acumular_resultado((valor_original>>(2+(numero_valor%3)*2))|resto_base64);
desplazamiento=4–(numero_valor%3)*2;
resto_base64=(valor_original&(MASCARA_B64>>desplazamiento))<<desplazamiento;
if(((numero_codigo+1)%4==0))
{
acumular_resultado(resto_base64);
resto_base64=0;
}
numero_valor++;
resultado[contador_caracteres_resultado]=0;
return resultado;
}
unsigned char *Base64::terminar()
{
if(numero_codigo%4)
{
acumular_resultado(resto_base64);
while(numero_codigo%4)
{
acumular_resultado(ULTIMO_CODIGO_BASE64);
}
}
resultado[contador_caracteres_resultado]=0;
iniciar_conversion();
return resultado;
}
void Base64::acumular_resultado(unsigned char valor)
{
numero_codigo++;
resultado[contador_caracteres_resultado++]=simbolo_base64[valor];
if((numero_codigo%LONGITUD_LINEA)==0)
{
resultado[contador_caracteres_resultado++]=13; // CR “\r”
resultado[contador_caracteres_resultado++]=10; // LF “\n”
}
}
|
Основна частина розрахунку коду База64 Це робиться за допомогою виразу:
(valor_original>>(2+(numero_valor%3)*2))|resto_base64
і обчислення залишку за виразом:
(valor_original&(MASCARA_B64>>desplazamiento))<<desplazamiento
,
буття desplazamiento
значення, яке обчислюється за виразом:
4-(numero_valor%3)*2
Процес отримання цих виразів полягає в узагальненні обчислення кожного з чотирьох кодів База64 які є результатом представлення трьох байтів вихідного значення.
Base64=((byte_1>>2)|resto)&0b00111111 |
resto=(byte_1&0b00000011)<<4 |
Base64=((byte_2>>4)|resto)&0b00111111 |
resto=(byte_2&0b00001111)<<2 |
Base64=((byte_3>>6)|resto)&0b00111111 |
resto=(byte_3&0b00111111)<<0 |
Base64=((byte_3>>0)|resto)&0b00111111 |
resto=(byte_3&0b00111111)<<0 |
З текстом Base64
Наведений вище псевдокод відноситься до коду в База64 що розраховується. Вираз використано byte_n
для посилання на n-й байт, який кодується. Текст resto
представляє залишкові біти байта, який кодується. На початку обчислення приймається, що залишок дорівнює нулю
Для ясності, у попередньому псевдокоді 6-бітна маска була включена в обчислення всіх кодів, хоча необхідно визначити лише останній з них, оскільки інші обертаються таким чином, що два найбільші біти завжди втрачаються. значний.
Як видно, четвертий код — це весь залишок, і немає необхідності обчислювати залишок після цього; Тому необхідно виконати лише три кроки, по одному на кожен закодований байт. Важливо пам'ятати, що якби третій байт у пакеті не був закодований, останній код мав би бути заповнений нулями праворуч. База64 отриманий.
Щоб узагальнити, правий поворот виразу, який обчислює код у База64 можна представити як 2+(numero_byte%3)*2
так що частина в дужках повертатиметься від нуля до двох, отримуючи 2, 4 і 6 на кожному кроці. Звичайно, це не єдиний спосіб узагальнення, але я вибрав цей для функціональності та перш за все для ясності. Оскільки маска (AND) була необхідною лише в четвертому коді, і вже було видно, що її не потрібно обчислювати (все це залишок), вона не включена в остаточний вираз, щоб спростити його, хоча ми повинні пам’ятати що тип використовуваних даних (байт) бере лише 6 молодших бітів.
Лівий поворот решти можна узагальнити аналогічно попередньому. Також можна побачити, що застосована маска (І) зазнає такого ж обертання бітів, але в протилежному напрямку. Це є причиною розрахунку переміщення з 4-(numero_valor%3)*2
перед застосуванням у значенні, що відповідає кожній частині виразу.
У наступному прикладі показано, як використовувати бібліотеку для кодування текстового рядка (пам’ятайте, що База64 можна використовувати для будь-якого набору даних, наприклад, зображення). У наступному коді є кілька деталей, які цікаво прояснити. По-перше, спеціальний символ (символ ~) використовувався для позначення кінця тексту замість апаратного сигналу чи позначення довжини тексту. Логічно цей символ не може бути частиною закодованих даних.
Друге питання, яке необхідно розглянути, настільки ж важливе, як і очевидне, полягає в тому, що декодер у пункті призначення повинен знати, як представлена інформація, яка доходить до нього. Текст містить символи, які не належать до набору ASCII друковані (від 32 до 126), літери з наголосом, наприклад. Arduino використовуватиме два байти (UTF-8), щоб представити ці символи. Звичайний не можна просто використовувати \0
як текстовий термінатор, оскільки в багатьох випадках перший байт, яким представлено символ, буде рівним нулю.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
#include “base64.h”
char texto_prueba[]=“La bella y graciosa moza marchose a lavar la ropa.\nLa mojó en el arroyuelo y cantando la lavó.\nLa frotó sobre una piedra, la colgó de un abedul.\nDespués de lavar la ropa, la niña se fue al mercado.\nUn pastor vendía ovejas pregonando a viva voz:\nved qué oveja, ved qué lana, ved qué bestia, qué animal.\nLa niña la vio muy flaca, sin embargo le gustó.\nYo te pago veinte escudos y no discutamos más.\nVuelve la niña cantando muy contenta con su oveja.\nCuando llegaron al bosque la ovejita se escapó.\nLa niña desesperada arrojose encima de ella.\nVelozmente y con destreza aferrola por detrás.\nLlegaba por el camino jinete de altivo porte.\nDescendió de su caballo y a la niña le cantó…~”;
char *resultado;
Base64 base64;
void setup()
{
Serial.begin(9600);
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__) // ¿Es un Arduino Leonardo (ATmega32U4)?
while(!Serial){}; // Esperar a Arduino Leonardo
#endif
// Mostrar el texto original
unsigned int contador=0;
while(texto_prueba[contador]!=‘~’)
{
//Serial.println(String(texto_prueba[contador])+”=”+String(texto_prueba[contador],DEC));
Serial.print(String(texto_prueba[contador]));
contador++;
}
Serial.println(“\n”);
// Mostrar el texto codificado en Base64
contador=0;
while(texto_prueba[contador]!=‘~’)
{
resultado=base64.convertir(texto_prueba[contador],texto_prueba[contador+1]==‘~’);
byte contador_resultado=0;
while(resultado[contador_resultado]>0)
{
Serial.print(String(resultado[contador_resultado]));
contador_resultado++;
}
contador++;
}
}
void loop()
{
}
|
Рядок 26 попереднього прикладу показує використання бібліотека для Arduino для кодування в Base64. Потрібно лише вказати спосіб convertir
кожен байт, який ви хочете закодувати, і за бажанням, чи він останній, або, якщо ні, зупиніть перетворення за допомогою методу terminar
коли ви досягнете кінця.
Як видно на скріншоті нижче, приклад програми бібліотека для Arduino для кодування в Base64 спочатку відображає текст, який потрібно закодувати База64, в даному випадку це початок відомої пісні велетнів Les Luthiers, а згодом результат кодування в База64 використовуючи довжину рядка формату MIME.
Дати коментар