Base85 编码简介

Base85(也称为 Ascii85)是一种二进制到文本的编码方案,用于将二进制数据转换为可打印的ASCII字符。它的效率高于Base64,但生成的字符串可能包含特殊字符(如引号或反斜杠),需在特定场景(如JSON)中谨慎使用。

编码原理

Base85将每4个字节(32位)的二进制数据转换为5个Base85字符。计算公式如下:

  1. 将4字节数据视为一个32位无符号整数(大端序)。
  2. 重复除以85,取余数作为Base85字符的索引值。
  3. 将索引映射到字符集(如!u)。

示例:
假设4字节数据为0x4A3B2C1D,转换步骤如下:

  • 数值为1,246,434,333
  • 依次除以85:
    • 1246434333 ÷ 85 = 14663933,余数28 → 字符<(索引28)。
    • 14663933 ÷ 85 = 172517,余数48 → 字符o(索引48)。
    • 继续计算剩余字符。

常用字符集

不同实现可能使用不同字符集,常见两种:

  1. RFC 1924版本
    !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstu  
    

  2. Adobe Ascii85
    • 字符范围:!(33)到u(117)。
    • 特殊标记:z表示4字节全零,~~~~~为数据流结束符。

注意事项

  • 数据对齐:输入数据长度若非4字节倍数,需补零处理。
  • 特殊字符:避免在XML/JSON中直接使用,需额外转义。
  • 效率权衡:Base85比Base64节省约1/4空间,但复杂度更高。

应用场景

  • Adobe PDF:用于内嵌二进制数据(如字体)。
  • 网络传输:需要紧凑编码的场景。
  • Git存储:Git的二进制补丁可能使用Base85。
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>#define BLOCK_SIZE 4
#define ENCODED_BLOCK_SIZE 5// Base85编码函数
char *base85_encode(const uint8_t *data, size_t len) {if (data == NULL || len == 0) return NULL;// 计算输出缓冲区大小(每4字节→5字符)size_t max_out_len = ((len + BLOCK_SIZE - 1) / BLOCK_SIZE) * ENCODED_BLOCK_SIZE + 1;char *output = malloc(max_out_len);if (!output) return NULL;size_t out_index = 0;uint32_t block = 0;for (size_t i = 0; i < len; i += BLOCK_SIZE) {size_t remaining = len - i;size_t block_len = (remaining < BLOCK_SIZE) ? remaining : BLOCK_SIZE;// 构建32位大端序块block = 0;for (size_t j = 0; j < block_len; j++) {block |= (uint32_t)data[i + j] << (24 - 8 * j);}// 全零块缩写为'z'if (block_len == BLOCK_SIZE && block == 0) {output[out_index++] = 'z';continue;}// 计算5个Base85字符(从高位到低位)char encoded[ENCODED_BLOCK_SIZE];for (int j = ENCODED_BLOCK_SIZE - 1; j >= 0; j--) {encoded[j] = (block % 85) + '!';block /= 85;}// 根据实际字节数复制有效字符size_t chars_to_copy = block_len + 1;  // 字节数+1for (size_t j = 0; j < chars_to_copy; j++) {output[out_index++] = encoded[j];}}output[out_index] = '\0';return output;
}// Base85解码函数
uint8_t *base85_decode(const char *input, size_t *out_len) {if (input == NULL || out_len == NULL) return NULL;size_t in_len = strlen(input);if (in_len == 0) return NULL;// 计算最大输出长度(每5字符→4字节)size_t max_out_len = (in_len * BLOCK_SIZE) / ENCODED_BLOCK_SIZE;uint8_t *output = malloc(max_out_len);if (!output) return NULL;size_t in_index = 0;size_t out_index = 0;uint32_t block = 0;while (in_index < in_len) {// 处理全零块缩写if (input[in_index] == 'z') {for (int j = 0; j < BLOCK_SIZE; j++) {output[out_index++] = 0;}in_index++;continue;}// 读取5个字符(不足时用'u'填充)size_t chars_in_block = 0;block = 0;for (int j = 0; j < ENCODED_BLOCK_SIZE; j++) {if (in_index >= in_len) break;char c = input[in_index++];if (c < '!' || c > 'u') continue;  // 跳过无效字符block = block * 85 + (c - '!');chars_in_block++;}// 填充不足的字符while (chars_in_block < ENCODED_BLOCK_SIZE) {block = block * 85 + ('u' - '!');chars_in_block++;}// 提取4个字节(大端序)size_t bytes_to_write = chars_in_block - 1;  // 字符数-1=原始字节数for (int j = 0; j < bytes_to_write; j++) {output[out_index++] = (block >> (24 - 8 * j)) & 0xFF;}}*out_len = out_index;return output;
}// 测试函数
int main() {// 编码测试uint8_t data[] = {0x86, 0x4F, 0xD2, 0x6F, 0xB5, 0x59, 0x00, 0x00};char *encoded = base85_encode(data, sizeof(data));printf("Encoded: %s\n", encoded);  // 输出: L/Ch[+>Gz// 解码测试size_t decoded_len;uint8_t *decoded = base85_decode(encoded, &decoded_len);printf("Decoded: ");for (size_t i = 0; i < decoded_len; i++) {printf("%02X ", decoded[i]);  // 输出: 86 4F D2 6F B5 59 00 00}printf("\n");// 清理内存free(encoded);free(decoded);return 0;
}

base85的实现规则相对比较多,如果不替换原始字母表,建议使用openssl接口。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/87989.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/87989.shtml
英文地址,请注明出处:http://en.pswp.cn/web/87989.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Docker企业级应用:从入门到生产环境最佳实践

一、Docker核心概念与架构 1.1 Docker技术栈 #mermaid-svg-CUEiyGo05ZYG524v {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-CUEiyGo05ZYG524v .error-icon{fill:#552222;}#mermaid-svg-CUEiyGo05ZYG524v .error-te…

8、保存应用数据

目录用户首选项的使用用户首选项主要API用户首选项开发流程用户首选项开发实践关系型数据库的使用关系型数据库工作流程关系型数据库开发实践用户首选项的使用 用户首选项主要API 用户首选项开发流程 成功的获取了一个名为myStore的Preferences实例 保存了一个键值对&#x…

(C++)list列表相关基础用法(C++教程)(STL库基础教程)

源代码&#xff1a;#include <iostream> #include <list>using namespace std;int main(){list<int> numbers{10,20,30};numbers.push_front(5);numbers.push_back(40);auto it numbers.begin();advance(it,2);numbers.insert(it,15);cout<<"该列…

Spring CGLIB私有方法访问成员变量为null问题

场景 代码 RestController public class TestJob {Autowiredprivate XxService xxService;XxlJob("testCGLIB")private void doTest(){System.out.println("方法调用");System.out.println("成员变量注入:"(xxService!null));this.doInnerTest()…

Paimon本地表查询引擎LocalTableQuery详解

LocalTableQueryLocalTableQuery 是 Paimon 中实现本地化、带缓存的表查询的核心引擎。它的主要应用场景是 Flink 中的 Lookup Join。当 Flink 作业需要根据一个流中的 Key 去关联一个 Paimon 维表时&#xff0c;LocalTableQuery 可以在 Flink 的 TaskManager 节点上&#xff0…

使用协程简化异步资源获取操作

异步编程的两种场景 在异步编程中&#xff0c;回调函数通常服务于两种不同场景&#xff1a; 一次性资源获取&#xff1a;等待异步操作完成并返回结果。持续事件通知。监听并响应多个状态变更。 Kotlin为这两种场景提供了解决方案&#xff1a;使用挂起函数简化一次性资源获取…

ABP VNext + Cosmos DB Change Feed:搭建实时数据变更流服务

ABP VNext Cosmos DB Change Feed&#xff1a;搭建实时数据变更流服务 &#x1f680; &#x1f4da; 目录ABP VNext Cosmos DB Change Feed&#xff1a;搭建实时数据变更流服务 &#x1f680;TL;DR ✨&#x1f680;1. 环境与依赖 &#x1f3d7;️2. 服务注册与依赖注入 &…

STM32-定时器

定时器&#xff1a;有4个独立通道&#xff1a;输入捕获&#xff1b;输出比较PWM生成&#xff1b;单脉冲模式输出&#xff1b;可通外部信号控制定时器&#xff08;TIMx-ETR&#xff09;&#xff1b;支持针对定时的增量&#xff08;正交&#xff09;编码器、霍尔传感器电路通用定…

Windows Server 2019--职业技能大赛B模块Windows服务器配置样题

一、赛题说明 &#xff08;一&#xff09;竞赛介绍 请详细阅读网络拓扑图&#xff0c;为所有计算机修改默认防火墙以便允许ICMP和相应的流量&#xff0c;不允许直接关闭主机的防火墙。除了CD-ROM/HDD驱动器&#xff0c;请不要修改虚拟机本身的硬件设置。 &#xff08;二&…

vue3+Echarts实现立体柱状图

Echarts柱状图中文网&#xff1a;https://echarts.apache.org/examples/zh/index.html#chart-type-bar 效果展示&#xff1a; 主要实现过程是三部分的组合&#xff0c;最上面是一个椭圆&#xff0c;中间是正常的柱子&#xff0c;下方再加上一个椭圆&#xff0c;就出来立体的效…

【UE5】虚幻引擎小百科

一、类名前面的大写字母的含义是什么UE5常见前缀分类表前缀含义实例用于AActorACharacter&#xff0c;AWeaponBase可放入世界中的对象&#xff08;有位置、可碰撞等&#xff09;UUObject派生类UUserWidget&#xff0c;UWeaponComponent引擎对象、逻辑模块&#xff0c;不具备Tra…

【Linux系统】vim编辑器 | 编译器gcc/g++ | make/Makefile

1. vim编辑器一、历史发展与Vim vs Vi的区别起源与演进Vi&#xff08;1976年&#xff09; &#xff1a;由Bill Joy开发&#xff0c;嵌入BSD Unix系统&#xff0c;是首个面向屏幕的文本编辑器&#xff0c;但功能有限&#xff08;如无多级撤销&#xff09;。Vim&#xff08;1991年…

国产飞腾主板,赋能网络安全防御硬手段

​ 当前&#xff0c;网络安全形势严峻&#xff0c;网络攻击手段不断翻新&#xff0c;从数据泄露到电脑中毒&#xff0c;企业、机构乃至国家的数字资产都面临着巨大风险。在此背景下&#xff0c;国产硬件技术的突破对筑牢网络安全防线意义重大。 高能计算机基于市场需求&#…

Spring AI 概述与架构设计

目录一、前言二、简介三、核心能力概览四、理解模块架构图五、模型适配能力六、最小应用示例七、与传统 LLM 调用相比八、总结九、参考一、前言 在 AI 正以前所未有的速度“下沉”到各类系统与业务的当下&#xff0c;Spring 官方推出的 Spring AI 项目&#xff0c;为 Java 开发…

UI前端与数字孪生融合新领域:智慧环保的污染源监测与治理

hello宝子们...我们是艾斯视觉擅长ui设计、前端开发、数字孪生、大数据、三维建模、三维动画10年经验!希望我的分享能帮助到您!如需帮助可以评论关注私信我们一起探讨!致敬感谢感恩!一、引言&#xff1a;数字孪生重构智慧环保的技术范式在环境污染治理压力持续增大的背景下&…

【go/wails】wails入门系列(一)环境安装与demo

文章目录说在前面go安装nodejs安装wails创建项目运行说在前面 操作系统&#xff1a;win11go版本&#xff1a;1.24.4nodejs版本&#xff1a;v22.16.0wails版本&#xff1a;v2.10.1 go安装 官网 这里 下载安装即可 nodejs 官网 这里 下载安装即可 安装wails 设置go国内代理g…

linux qt 使用log4cpp库

一、日志库下载 下载地址&#xff1a;https://log4cpp.sourceforge.net/二、日志库解压&#xff0c;编译 1.将文件夹解压出来2.进入文件夹内部&#xff0c;打开终端3.终端中依次输入以下命令 mkdir build ./configure --prefix$(pwd)/build make make install 一般来说不会报错…

探索阿里云Data Integration:数据同步的魔法工具

引言在当今数字化时代&#xff0c;数据已成为企业的核心资产&#xff0c;如同企业发展的 “燃料”&#xff0c;驱动着业务的增长与创新。从用户行为数据到业务运营数据&#xff0c;从市场趋势数据到供应链数据&#xff0c;每一个数据点都蕴含着巨大的价值&#xff0c;能够为企业…

【Java面试】Redis的poll函数epoll函数区别?

Redis 在选择 poll 和 epoll 时主要基于性能需求、连接规模、操作系统支持等因素。以下是具体场景的对比与选择建议&#xff1a;1. 何时使用 poll 函数&#xff1f;适用场景&#xff1a; 跨平台兼容性需求&#xff1a;poll 在几乎所有操作系统&#xff08;如 Windows、BSD、Lin…

RPC--RPCHandler的实现

在RPC框架中&#xff0c;Handler用于接收RpcRequest&#xff0c;经过处理后返回RpcResponseSlf4jpublic class RpcRequestHandler {private final ServiceProvider serviceProvider;//获取一个单例模式的服务提供类public RpcRequestHandler() {serviceProvider SingletonFact…