- #ifndef BASE64_UTILS_H
- #define BASE64_UTILS_H
- #include <stdint.h>
- #include <stddef.h>
- #include <stdbool.h>
- // 预计算编码后长度(含可选填充)
- size_t base64_encode_len(size_t bin_len, bool use_padding);
- // 预计算解码后最大长度(含填充时自动减,不含填充时按输入长度估算)
- size_t base64_decode_max_len(size_t b64_len);
- // Base64编码(bin为输入二进制/文本,b64_out为输出缓冲区,use_padding控制是否加=)
- // 返回实际编码长度
- size_t base64_encode(const uint8_t *bin, size_t bin_len, uint8_t *b64_out, bool use_padding);
- // Base64解码(b64为输入Base64字符串,bin_out为输出缓冲区)
- // 返回实际解码长度,失败返回0
- size_t base64_decode(const uint8_t *b64, size_t b64_len, uint8_t *bin_out);
- #endif
实现文件(base64_utils.c)
- #include "base64_utils.h"
- // Base64标准编码表
- static const char b64_encode_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
- // Base64标准解码表(0xFF表示非法字符)
- static const uint8_t b64_decode_table[256] = {
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x3E,0xFF,0xFF,0xFF,0x3F,
- 0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,
- 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,
- 0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
- };
- size_t base64_encode_len(size_t bin_len, bool use_padding) {
- size_t len = (bin_len + 2) / 3 * 4;
- if (!use_padding) {
- size_t rem = bin_len % 3;
- if (rem == 1) len -= 2;
- else if (rem == 2) len -= 1;
- }
- return len;
- }
- size_t base64_decode_max_len(size_t b64_len) {
- return (b64_len + 3) / 4 * 3;
- }
- size_t base64_encode(const uint8_t *bin, size_t bin_len, uint8_t *b64_out, bool use_padding) {
- size_t i = 0, j = 0;
- uint32_t temp;
- while (i + 3 <= bin_len) {
- temp = (bin[i] << 16) | (bin[i+1] << 8) | bin[i+2];
- b64_out[j++] = b64_encode_table[(temp >> 18) & 0x3F];
- b64_out[j++] = b64_encode_table[(temp >> 12) & 0x3F];
- b64_out[j++] = b64_encode_table[(temp >> 6) & 0x3F];
- b64_out[j++] = b64_encode_table[temp & 0x3F];
- i += 3;
- }
- if (i < bin_len) {
- temp = 0;
- for (size_t k = 0; k < bin_len - i; k++) {
- temp |= (uint32_t)bin[i+k] << (16 - k*8);
- }
- b64_out[j++] = b64_encode_table[(temp >> 18) & 0x3F];
- b64_out[j++] = b64_encode_table[(temp >> 12) & 0x3F];
- if (bin_len - i == 2) {
- b64_out[j++] = b64_encode_table[(temp >> 6) & 0x3F];
- if (use_padding) b64_out[j++] = '=';
- } else {
- if (use_padding) {
- b64_out[j++] = '=';
- b64_out[j++] = '=';
- }
- }
- }
- return j;
- }
- size_t base64_decode(const uint8_t *b64, size_t b64_len, uint8_t *bin_out) {
- size_t i = 0, j = 0;
- uint32_t temp;
- uint8_t group[4];
- size_t group_len;
- while (i < b64_len) {
- group_len = 0;
- for (size_t k = 0; k < 4 && i < b64_len; k++) {
- uint8_t c = b64[i++];
- if (c == '=') break;
- uint8_t val = b64_decode_table[c];
- if (val == 0xFF) return 0; // 非法字符
- group[group_len++] = val;
- }
- if (group_len == 0) break;
- if (group_len < 2) return 0; // 非法分组
- temp = 0;
- for (size_t k = 0; k < group_len; k++) {
- temp |= (uint32_t)group[k] << (18 - k*6);
- }
- bin_out[j++] = (temp >> 16) & 0xFF;
- if (group_len >= 3) bin_out[j++] = (temp >> 8) & 0xFF;
- if (group_len == 4) bin_out[j++] = temp & 0xFF;
- }
- return j;
- }
二、使用方法
- 预计算长度:编码前用 base64_encode_len() 算输出缓冲区大小,解码前用 base64_decode_max_len() 算最大输出(实际解码长度会更小);
- 编码:传输入二进制 / 文本、长度、输出缓冲区、是否加=;
- 解码:传输入 Base64 字符串、长度、输出缓冲区,返回 0 表示失败(非法字符 / 非法分组)。
三、移植方法
- 将 base64_utils.c 和 base64_utils.h 复制到 e²s 工程目录;
- 在 e²s 中右键工程 → Add Files,将 base64_utils.c 加入编译;
- 在需要使用的文件中包含头文件:#include "base64_utils.h";
- 按 “预计算长度 - 编码 / 解码” 流程调用函数即可。
四、调用函数代码验证
以下面这组测试数据为例:
验证代码(基于瑞萨 RA6M5 + e²s IDE)
[url=]复制[/url]
- #include "hal_data.h"
- #include "base64_utils.h"
- #include <stdio.h>
- #include <string.h>
- void uart_print(const char *str) {
- R_SCI_UART_Write(&g_uart0_ctrl, (uint8_t *)str, strlen(str));
- }
- void base64_test(void) {
- char buf[256];
- uint8_t temp_b64[256];
- uint8_t temp_bin[256];
- size_t len;
- // --- 第一组:短文本编码/解码(带填充) ---
- uart_print("--- 第一组:短文本编码/解码(带填充) ---\r\n");
- const char *text1 = "Hello Base64!";
- size_t text1_len = strlen(text1);
- // 编码
- size_t b641_len = base64_encode_len(text1_len, true);
- base64_encode((const uint8_t *)text1, text1_len, temp_b64, true);
- temp_b64[b641_len] = '\0';
- snprintf(buf, sizeof(buf), "编码结果:%s\r\n", temp_b64);
- uart_print(buf);
- // 解码
- size_t bin1_max = base64_decode_max_len(b641_len);
- size_t bin1_len = base64_decode(temp_b64, b641_len, temp_bin);
- temp_bin[bin1_len] = '\0';
- snprintf(buf, sizeof(buf), "解码结果:%s(长度:%zu)\r\n\r\n", temp_bin, bin1_len);
- uart_print(buf);
- // --- 第二组:二进制数组编码/解码(不带填充) ---
- uart_print("--- 第二组:二进制数组编码/解码(不带填充) ---\r\n");
- const uint8_t bin2[] = {0x01, 0x02, 0x03, 0x04};
- size_t bin2_len = sizeof(bin2);
- // 编码
- size_t b642_len = base64_encode_len(bin2_len, false);
- base64_encode(bin2, bin2_len, temp_b64, false);
- temp_b64[b642_len] = '\0';
- snprintf(buf, sizeof(buf), "编码结果:%s\r\n", temp_b64);
- uart_print(buf);
- // 解码
- size_t bin2_max = base64_decode_max_len(b642_len);
- size_t bin2_dec_len = base64_decode(temp_b64, b642_len, temp_bin);
- snprintf(buf, sizeof(buf), "解码结果:");
- uart_print(buf);
- for (size_t i = 0; i < bin2_dec_len; i++) {
- snprintf(buf, sizeof(buf), "%02X ", temp_bin[i]);
- uart_print(buf);
- }
- snprintf(buf, sizeof(buf), "(长度:%zu)\r\n", bin2_dec_len);
- uart_print(buf);
- }
- void hal_entry(void) {
- R_SCI_UART_Open(&g_uart0_ctrl, &g_uart0_cfg);
- base64_test();
- while (1);
- }
五、结果对比
我们通过串口助手验证数据与与其是否相符:
我们换一组数据继续验证:
新验证代码(直接替换原 base64_test 函数即可)
- void base64_test(void) {
- char buf[256];
- uint8_t temp_b64[256];
- uint8_t temp_bin[256];
- size_t len;
- // 1. 边界值:空数据编码
- uart_print("--- 1. 边界值:空数据编码 ---\r\n");
- len = base64_encode_len(0, true);
- size_t encode_len = base64_encode(NULL, 0, temp_b64, true);
- snprintf(buf, sizeof(buf), "编码长度:%zu,预期长度:%zu\r\n\r\n", encode_len, len);
- uart_print(buf);
- // 2. 单字节边界:0x00 带填充编码
- uart_print("--- 2. 单字节0x00(带填充) ---\r\n");
- const uint8_t byte1[] = {0x00};
- len = base64_encode_len(sizeof(byte1), true);
- encode_len = base64_encode(byte1, sizeof(byte1), temp_b64, true);
- temp_b64[encode_len] = '\0';
- snprintf(buf, sizeof(buf), "编码结果:%s,长度:%zu\r\n", temp_b64, encode_len);
- uart_print(buf);
- // 反向解码验证
- size_t decode_len = base64_decode(temp_b64, encode_len, temp_bin);
- snprintf(buf, sizeof(buf), "解码结果:0x%02X,长度:%zu\r\n\r\n", temp_bin[0], decode_len);
- uart_print(buf);
- // 3. 单字节边界:0x00 不带填充编码
- uart_print("--- 3. 单字节0x00(不带填充) ---\r\n");
- len = base64_encode_len(sizeof(byte1), false);
- encode_len = base64_encode(byte1, sizeof(byte1), temp_b64, false);
- temp_b64[encode_len] = '\0';
- snprintf(buf, sizeof(buf), "编码结果:%s,长度:%zu\r\n\r\n", temp_b64, encode_len);
- uart_print(buf);
- // 4. 双字节边界:0x01,0x02 带填充编码
- uart_print("--- 4. 双字节{0x01,0x02}(带填充) ---\r\n");
- const uint8_t byte2[] = {0x01, 0x02};
- len = base64_encode_len(sizeof(byte2), true);
- encode_len = base64_encode(byte2, sizeof(byte2), temp_b64, true);
- temp_b64[encode_len] = '\0';
- snprintf(buf, sizeof(buf), "编码结果:%s,长度:%zu\r\n\r\n", temp_b64, encode_len);
- uart_print(buf);
- // 5. 固件版本号编码/解码
- uart_print("--- 5. 固件版本号编码/解码 ---\r\n");
- const char *fw_ver = "V1.2.3_20260426";
- size_t fw_len = strlen(fw_ver);
- // 编码
- len = base64_encode_len(fw_len, true);
- encode_len = base64_encode((const uint8_t *)fw_ver, fw_len, temp_b64, true);
- temp_b64[encode_len] = '\0';
- snprintf(buf, sizeof(buf), "编码结果:%s\r\n", temp_b64);
- uart_print(buf);
- // 解码
- decode_len = base64_decode(temp_b64, encode_len, temp_bin);
- temp_bin[decode_len] = '\0';
- snprintf(buf, sizeof(buf), "解码结果:%s,长度:%zu\r\n\r\n", temp_bin, decode_len);
- uart_print(buf);
- // 6. 传感器二进制数据编码/解码(不带填充)
- uart_print("--- 6. 传感器二进制数据(不带填充) ---\r\n");
- const uint8_t sensor_data[] = {0x12,0x34,0x56,0x78,0x9A,0xBC,0xDE,0xF0};
- size_t sensor_len = sizeof(sensor_data);
- // 编码
- len = base64_encode_len(sensor_len, false);
- encode_len = base64_encode(sensor_data, sensor_len, temp_b64, false);
- temp_b64[encode_len] = '\0';
- snprintf(buf, sizeof(buf), "编码结果:%s\r\n", temp_b64);
- uart_print(buf);
- // 解码
- decode_len = base64_decode(temp_b64, encode_len, temp_bin);
- snprintf(buf, sizeof(buf), "解码结果:");
- uart_print(buf);
- for (size_t i = 0; i < decode_len; i++) {
- snprintf(buf, sizeof(buf), "%02X ", temp_bin[i]);
- uart_print(buf);
- }
- snprintf(buf, sizeof(buf), ",长度:%zu\r\n\r\n", decode_len);
- uart_print(buf);
- // 7. 中文UTF-8文本编码/解码
- uart_print("--- 7. 中文UTF-8文本编码/解码 ---\r\n");
- const char *cn_text = "21ic瑞萨RA论坛";
- size_t cn_len = strlen(cn_text);
- // 编码
- len = base64_encode_len(cn_len, true);
- encode_len = base64_encode((const uint8_t *)cn_text, cn_len, temp_b64, true);
- temp_b64[encode_len] = '\0';
- snprintf(buf, sizeof(buf), "编码结果:%s\r\n", temp_b64);
- uart_print(buf);
- // 解码
- decode_len = base64_decode(temp_b64, encode_len, temp_bin);
- temp_bin[decode_len] = '\0';
- snprintf(buf, sizeof(buf), "解码结果:%s,长度:%zu\r\n\r\n", temp_bin, decode_len);
- uart_print(buf);
- // 8. 异常容错:非法字符解码
- uart_print("--- 8. 异常容错:非法字符解码 ---\r\n");
- const char *invalid_b64 = "SGVsbG8hIQ!!";
- decode_len = base64_decode((const uint8_t *)invalid_b64, strlen(invalid_b64), temp_bin);
- snprintf(buf, sizeof(buf), "解码返回值:%zu,预期:0(解码失败)\r\n", decode_len);
- uart_print(buf);
- }
注意事项
- 缓冲区大小:编码 / 解码前必须预计算长度,避免缓冲区溢出;
- 非法字符处理:解码时遇到非 Base64 标准字符(除=)会返回 0,需提前过滤输入;
- 填充控制:不同协议的 Base64 变体可能要求不带填充(如 URL-safe Base64),可通过use_padding控制;
- URL-safe 变体:本封装用的是标准+和/,若需 URL-safe,可将编码表的+//换成-/_,解码表对应位置也需修改;
- 资源占用:代码约 1.5KB Flash,解码表占 256 字节 RAM(可改为 Flash 存储以节省 RAM,适合 8 位机);
- 大文件处理:本封装支持分段处理(每次处理一部分数据,拼接输出),无需一次性加载所有数据。
以上就是 Base64 编码 / 解码通用封装的验证,代码量适中、功能完整、适配性强,非常适合嵌入式设备的文本转二进制 / 二进制转文本场景。