ספריית קידוד Base64 עם Arduino
בסיס בסיס היא מערכת קידוד המשתמשת ב-64 סמלים המקובצים להודעות בעלות כפולת אורך של ארבע. הודעות אלה (מנות נתונים) מושלמות, במידת הצורך, עם סימן פלוס (לכן משתמשים ב-65), לעתים קרובות סימן השוויון (=), אם המידע השימושי המקודד מביא לאורך קצר יותר.
באמצעות 64 סימנים ניתן לעבוד עם 10 הספרות והאותיות הגדולות והקטנות (26+26) של הקוד ASCII, הבעיה היא שיש 62, נניח, סמלים חד משמעיים ועוד שניים המשתנים בהטמעות שונות. למרות שלפעמים מתייחסים אליו בביטוי "דמויות ASCII להדפסה", במציאות הם אלה שנעים בין זה שמיוצג על ידי הקוד 32 (רווח) ל-126 (~) ה-95 שניתן להדפסה באמת.
יישום הקידוד בסיס בסיס הכי בשימוש, זה של PEM, המשמש גם את Mime, עבדו עם הסימנים הנוספים "+" ו-"/" והסימן "=" כדי לרפד כך שלחבילות יש מכפלת אורך של ארבע. האותיות A-Z תופסות את המיקומים 0-25, האותיות a-z תופסות את המיקומים 26-51, הספרות 0-9 תופסות את המיקומים 52-61, סימן הפלוס (+) מיקומים 62, ומיקום 63 נתפס על ידי הלוכסן (/ ).
הדרך לייצוג נתונים בפורמט בסיס בסיס מורכב מלקיחת, מהנתונים המקוריים, קבוצות של 6 ביטים אשר מיוצגים עם הקוד המתאים. אם נשארו ביטים, הם מלאים באפסים מימין. אם מספר הקודים המתקבל אינו כפולה של ארבעה, הוא ממולא בסימני שווים מימין.
התמונה הבאה מציגה את הקידוד ASCII של טקסט ("אוהם") והאופן שבו הוא מומר בסיס בסיס. מכיוון שיש 7 סמלים, יהיה צורך למלא את ההודעה הסופית בסימן שוויון בסוף. אפשר לומר שהטקסט "אוהם" ב ASCII שווה ערך ל «b2htaW8=" in בסיס בסיס.
שימושים ספציפיים בקידוד בסיס בסיס בדרך כלל הם גם מטילים אורך קו מקסימלי. היישום Mime מגביל כל שורה ל-76 תווים. בדרך כלל השורות יופרדו על ידי קוד סוף שורה (CR, המיוצג על ידי הערך 0x0D ב ASCII) ועוד שורה חדשה (NL, המתאימה לקוד ASCII 0x0A).
אי הנוחות שמתווספת בעת יישום קידוד בסיס בסיס במכשיר עם מעט משאבים, כפי שקורה לעתים קרובות עם a מיקרו-בקר זה שאתה צריך לקודד כשהמידע מגיע או עם א חיץ מינימום, המחייבת גם לספק מערכת המעידה על הגעת סוף ההודעה המקורית, למשל על ידי הוספת קוד מיוחד, או על ידי שימוש בפין שרמתו (מסונכרן עם הקבלה) מציינת את מצב ההודעה.
הקוד לדוגמה שלהלן הוא א ספריה עבור Arduino לקידוד ב-Base64 אשר מיושם עם שני הקריטריונים: קידוד המידע שמגיע (ללא א חיץ) והמתן עד לסיום אות אזהרה.
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”
}
}
|
החלק הבסיסי של חישוב הקוד בסיס בסיס זה נעשה עם הביטוי:
(valor_original>>(2+(numero_valor%3)*2))|resto_base64
וחישוב היתרה עם הביטוי:
(valor_original&(MASCARA_B64>>desplazamiento))<<desplazamiento
,
siendo desplazamiento
ערך שמחושב עם הביטוי:
4-(numero_valor%3)*2
התהליך שאחריו להשגת ביטויים אלה מורכב מהכללת החישוב של כל אחד מארבעת הקודים בסיס בסיס הנובעים מייצוג שלושה בתים מהערך המקורי.
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
הפסאודוקוד למעלה מתייחס לקוד ב בסיס בסיס זה בחישוב. נעשה שימוש בביטוי byte_n
להתייחס לבייט ה-n המקודד. הטקסט resto
מייצג את הסיביות שנותרו מהבית המקודד. בתחילת החישוב מניחים שהשאר הוא אפס
לשם הבהירות, בפסאודוקוד הקודם נכללה מסכת 6 הסיביות בחישוב כל הקודים, אם כי יש צורך לקבוע רק את האחרון שבהם, מכיוון שהאחרים מסובבים כך ששני הסיביות ביותר הולכים לאיבוד תמיד. משמעותי.
כפי שניתן לראות, הקוד הרביעי הוא כולו שארית ואין צורך לחשב שארית לאחר מכן; לכן יש צורך לבצע רק שלושה שלבים, אחד לכל בייט מקודד. חשוב לזכור שאם בייט שלישי בחבילה לא היה מקודד, הקוד האחרון היה צריך להיות מלא באפסים בצד ימין. בסיס בסיס הושג.
כדי להכליל, הסיבוב הימני של הביטוי שמחשב את הקוד ב בסיס בסיס יכול להיות מיוצג כ 2+(numero_byte%3)*2
כך שהחלק בתוך הסוגריים יסתובב מאפס לשניים, וכתוצאה מכך יהיו 2, 4 ו-6 בכל שלב. כמובן שזו לא הדרך היחידה להכליל, אבל בחרתי בזו בשביל פונקציונליות ומעל לכל בשביל הבהירות. מכיוון שהמסכה (AND) הייתה נחוצה רק בקוד הרביעי וכבר נראה שאין צורך לחשב אותה (הכל השארית), היא לא נכללת בביטוי הסופי כדי לפשט אותה, למרות שעלינו לזכור שסוג הנתונים שבהם נעשה שימוש (בייט) נלקחים רק 6 הביטים הפחות משמעותיים.
ניתן להכליל את הסיבוב השמאלי של השאר בצורה אנלוגית לקודמתה. ניתן גם לראות שהמסכה המוחלת (AND) עוברת את אותו סיבוב סיביות אך בכיוון ההפוך. זו הסיבה לחישוב העקירה עם 4-(numero_valor%3)*2
לפני יישום זה במובן המתאים לכל חלק של הביטוי.
הדוגמה הבאה מראה כיצד להשתמש בספרייה כדי לקודד מחרוזת טקסט (זכור זאת בסיס בסיס יכול לשמש עבור כל מערך נתונים, כגון תמונה, למשל). בקוד הבא יש כמה פרטים שמעניינים להבהיר. ראשית, נעשה שימוש בסמל מיוחד (הסמל ~) לציון סוף הטקסט, במקום אות חומרה או ציון אורך הטקסט. באופן הגיוני, הסמל הזה לא יכול להיות חלק מהנתונים המקודדים.
הנושא השני שיש להתייחס אליו, חשוב ככל שהוא ברור מאליו, הוא שעל המפענח ביעד לדעת כיצד מיוצג המידע שמגיע אליו. הטקסט כולל דמויות שאינן שייכות לסט 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 תחילה מציג את הטקסט שיש לקודד בו בסיס בסיס, במקרה הזה, תחילת השיר המפורסם של הענקים Les Luthiers, ובהמשך התוצאה של הקידוד ב בסיס בסיס באמצעות אורך קו פורמט Mime.
לפרסם תגובה