当前位置: 首页 > news >正文

二进制与十六进制数据转换:原理、实现与应用

在计算机系统中,二进制与十六进制的相互转换是数据存储、网络传输、加密算法等场景中的基础操作。Linux内核中的lib/hexdump.c模块提供了三个关键函数:bin2hexhex2binhex_to_bin,分别实现二进制到十六进制的转换、十六进制到二进制的转换,以及单个字符的十六进制解析。本文将从设计原理、实现细节和应用场景三个方面深入解析这些函数。


一、bin2hex:二进制数据转十六进制字符串

1. 功能与参数

  • 功能:将二进制数据转换为可读的十六进制ASCII字符串。

  • 参数

    • char *dst:目标缓冲区,需预分配至少2*count字节。

    • const void *src:输入的二进制数据指针。

    • size_t count:待转换的字节数。

2. 实现逻辑

char *bin2hex(char *dst, const void *src, size_t count) {const unsigned char *_src = src;while (count--) dst = hex_byte_pack(dst, *_src++);return dst;
}
  • 逐字节处理:通过循环遍历每个字节,调用hex_byte_pack将其转换为两个十六进制字符。

  • hex_byte_pack:将单个字节拆分为高4位和低4位,分别转换为对应的ASCII字符(如0xA3转为"A3")。

  • 返回值:返回目标缓冲区的末尾指针,便于链式操作。

3. 注意事项

  • 缓冲区安全:调用者需确保dst有足够空间。

  • 无终止符:函数不会自动添加\0,需手动处理字符串终止。

4. 应用场景

  • 日志输出:将二进制数据转为可读字符串以便调试。

  • 密钥显示:在加密场景中安全展示二进制密钥。


二、hex2bin:十六进制字符串转二进制数据

1. 功能与参数

  • 功能:将十六进制ASCII字符串还原为二进制数据。

  • 参数

    • u8 *dst:目标二进制缓冲区,需预分配至少count字节。

    • const char *src:输入的十六进制字符串。

    • size_t count:待转换的字节数(需满足strlen(src) >= 2*count)。

2. 实现逻辑

int hex2bin(u8 *dst, const char *src, size_t count) {while (count--) {int hi = hex_to_bin(*src++); // 高4位int lo = hex_to_bin(*src++); // 低4位if (hi < 0 || lo < 0) return -EINVAL;*dst++ = (hi << 4) | lo;}return 0;
}
  • 字符对处理:每两个字符组成一个字节,高4位在前,低4位在后。

  • 错误检查:若字符非法(如'G'),立即返回错误码-EINVAL

3. 注意事项

  • 输入合法性:字符串必须为偶数长度,且字符范围为0-9a-fA-F

  • 大小写兼容hex_to_bin函数统一处理大小写字母。

4. 应用场景

  • 协议解析:解析网络协议中的十六进制字段。

  • 密钥加载:将配置文件中存储的十六进制密钥转为二进制。


三、hex_to_bin:无分支的字符解析核心

1. 功能与设计目标

  • 功能:将单个十六进制字符转为4位二进制值(0-15),非法字符返回-1

  • 设计目标:避免条件分支,防止侧信道攻击(对加密场景至关重要)。

2. 实现逻辑

int hex_to_bin(unsigned char ch) {unsigned char cu = ch & 0xdf; // 小写转大写int part1 = (ch - '0' + 1) & (unsigned)((ch - '9' - 1) & ('0' - 1 - ch)) >> 8;int part2 = (cu - 'A' + 11) & (unsigned)((cu - 'F' - 1) & ('A' - 1 - cu)) >> 8;return -1 + part1 + part2;
}
  • 数字字符处理0-9):

    • 通过算术运算生成掩码,合法字符返回1-10,非法返回0

  • 字母字符处理A-F/a-f):

    • 统一转为大写后,合法字符返回11-16,非法返回0

  • 合并结果-1 + part1 + part2确保非法字符返回-1,合法字符返回正确值。

3. 无分支设计解析

  • 掩码生成:通过符号位运算生成合法字符的掩码(如0xFFFFFF)。

  • 值计算:将字符偏移量(如ch - '0')与掩码按位与,避免if判断。

4. 示例验证

  • '3' → 3'A' → 10'f' → 15'X' → -1


四、综合应用与总结

1. 协作流程

  • 加密密钥处理

    1. 存储:使用bin2hex将二进制密钥转为十六进制字符串。

    2. 加载:使用hex2bin反向解析,依赖hex_to_bin逐字符校验。

2. 性能与安全

  • 无分支优势hex_to_bin避免分支预测错误和时序攻击风险。

  • 内存安全:调用者需确保缓冲区大小,防止溢出。

3. 扩展思考

  • Unicode支持:当前实现仅限ASCII字符,需扩展以支持宽字符。

  • 错误处理增强hex2bin可返回更详细的错误类型(如非法字符位置)。

4. 总结

bin2hexhex2binhex_to_bin共同构建了一套高效、安全的进制转换工具链。其设计体现了以下原则:

  • 效率优先:通过循环和位运算减少开销。

  • 安全可靠:严格的输入校验和无分支设计。

  • 接口简洁:清晰的参数约定和返回值语义。

这些函数不仅是Linux内核的基石,也可为其他系统开发提供借鉴,尤其在需要高频数据转换或安全敏感的场景中,其实现思路具有重要参考价值。

hex_to_bin

*** hex_to_bin - convert a hex digit to its real value* @ch: ascii character represents hex digit** hex_to_bin() converts one hex digit to its actual value or -1 in case of bad* input.** This function is used to load cryptographic keys, so it is coded in such a* way that there are no conditions or memory accesses that depend on data.** Explanation of the logic:* (ch - '9' - 1) is negative if ch <= '9'* ('0' - 1 - ch) is negative if ch >= '0'* we "and" these two values, so the result is negative if ch is in the range*	'0' ... '9'* we are only interested in the sign, so we do a shift ">> 8"; note that right*	shift of a negative value is implementation-defined, so we cast the*	value to (unsigned) before the shift --- we have 0xffffff if ch is in*	the range '0' ... '9', 0 otherwise* we "and" this value with (ch - '0' + 1) --- we have a value 1 ... 10 if ch is*	in the range '0' ... '9', 0 otherwise* we add this value to -1 --- we have a value 0 ... 9 if ch is in the range '0'*	... '9', -1 otherwise* the next line is similar to the previous one, but we need to decode both*	uppercase and lowercase letters, so we use (ch & 0xdf), which converts*	lowercase to uppercase*/
int hex_to_bin(unsigned char ch)
{unsigned char cu = ch & 0xdf;return -1 +((ch - '0' +  1) & (unsigned)((ch - '9' - 1) & ('0' - 1 - ch)) >> 8) +((cu - 'A' + 11) & (unsigned)((cu - 'F' - 1) & ('A' - 1 - cu)) >> 8);
}
EXPORT_SYMBOL(hex_to_bin);

hex_to_bin 函数的作用是将单个十六进制字符(0-9A-Fa-f)转换为对应的 4 位二进制值(0-15),若字符非法则返回 -1。以下是分步讲解:


1. 函数参数与目标

  • 输入unsigned char ch(一个 ASCII 字符)。

  • 输出

    • 合法字符:返回对应的数值(0-15)。

    • 非法字符:返回 -1


2. 核心逻辑

函数通过位运算和算术运算,避免条件分支,直接计算字符的合法性及其对应的值。逻辑分为两部分:

  1. 处理数字 0-9

  2. 处理字母 A-F/a-f(统一转为大写处理)。


3. 处理数字字符 0-9

// 判断字符是否在 '0'-'9' 范围内,并计算对应值
int part1 = (ch - '0' + 1) & (unsigned)((ch - '9' - 1) & ('0' - 1 - ch)) >> 8);
步骤拆解
  1. 范围检测

    • (ch - '9' - 1) < 0:若 ch <= '9',结果为负。

    • ('0' - 1 - ch) < 0:若 ch >= '0',结果为负。

    • 两者按位与后,结果为负 当且仅当 ch 在 '0'-'9' 范围内。

  2. 掩码生成

    • 将结果转为 unsigned 并右移 8 位,生成掩码 0xFFFFFF(合法)或 0(非法)。

  3. 值计算

    • 若合法,ch - '0' + 1 生成值 1-10,与掩码按位与后保留原值。

    • 若非法,结果为 0


4. 处理字母字符 A-F/a-f

// 统一转为大写后,判断是否在 'A'-'F' 范围内,并计算对应值
unsigned char cu = ch & 0xdf;  // 小写转大写(如 'a' → 'A')
int part2 = (cu - 'A' + 11) & (unsigned)((cu - 'F' - 1) & ('A' - 1 - cu)) >> 8);
步骤拆解
  1. 小写转大写

    • ch & 0xdf 将小写字母转为大写(如 'a' → 'A')。

  2. 范围检测

    • (cu - 'F' - 1) < 0:若 cu <= 'F',结果为负。

    • ('A' - 1 - cu) < 0:若 cu >= 'A',结果为负。

    • 两者按位与后,结果为负 当且仅当 cu 在 'A'-'F' 范围内。

  3. 掩码生成

    • 同上,生成掩码 0xFFFFFF 或 0

  4. 值计算

    • 若合法,cu - 'A' + 11 生成值 11-16,与掩码按位与后保留原值。

    • 若非法,结果为 0


5. 合并结果

return -1 + part1 + part2;
逻辑解释
  • 合法数字part1 为 1-10part2 为 0 → 结果为 0-9

  • 合法字母part1 为 0part2 为 11-16 → 结果为 10-15

  • 非法字符part1 和 part2 均为 0 → 结果为 -1


6. 示例验证

  1. 输入 '3'

    • part1 = 43 - '0' + 1 = 4),part2 = 0 → -1 + 4 + 0 = 3

  2. 输入 'A'

    • part1 = 0part2 = 11 → -1 + 0 + 11 = 10

  3. 输入 'f'(转为 'F'

    • part1 = 0part2 = 16 → -1 + 0 + 16 = 15

  4. 输入 'G'

    • part1 = 0part2 = 0 → -1 + 0 + 0 = -1


7. 设计优点

  1. 无分支:避免条件跳转,防止侧信道攻击(对加密场景至关重要)。

  2. 统一处理:小写字母通过位运算转为大写,简化逻辑。

  3. 紧凑高效:纯算术和位运算,适合高频调用场景。


8. 注意事项

  • 依赖 ASCII 编码:假设字符编码为 ASCII(如 'A'-'F' 连续)。

  • 不可变内存访问:所有操作基于寄存器计算,无依赖数据的访存。


总结

hex_to_bin 通过巧妙的位运算,将字符合法性检查和值计算合并为无分支的算术操作,兼顾安全性与效率,是处理十六进制字符的经典实现。

hex2bin

lib\hexdump.c
/*** hex2bin - convert an ascii hexadecimal string to its binary representation* @dst: binary result* @src: ascii hexadecimal string* @count: result length** Return 0 on success, -EINVAL in case of bad input.*/
int hex2bin(u8 *dst, const char *src, size_t count)
{while (count--) {int hi, lo;hi = hex_to_bin(*src++);if (unlikely(hi < 0))return -EINVAL;lo = hex_to_bin(*src++);if (unlikely(lo < 0))return -EINVAL;*dst++ = (hi << 4) | lo;}return 0;
}
EXPORT_SYMBOL(hex2bin);

hex2bin 函数的作用是将十六进制的 ASCII 字符串转换为二进制数据。以下是分步讲解:


1. 函数参数

  • u8 *dst:目标缓冲区,用于存储转换后的二进制数据。

  • const char *src:输入的十六进制 ASCII 字符串。

  • size_t count:要转换的字节数(每个字节对应 2 个十六进制字符)。


2. 循环处理每个字节

while (count--) {// 处理高 4 位和低 4 位
}
  • 循环 count 次,每次处理一个字节(需要 2 个十六进制字符)。


3. 字符到二进制值的转换

hi = hex_to_bin(*src++);  // 处理高 4 位
lo = hex_to_bin(*src++);  // 处理低 4 位
  • hex_to_bin 函数

    • 将单个十六进制字符(如 'A''3')转换为对应的 4 位二进制值(0-15)。

    • 若输入字符非法(非 0-9a-fA-F),返回负数。

  • 字符处理顺序

    1. 先处理高 4 位(如 'A' 对应 0xA,左移 4 位后为 0xA0)。

    2. 再处理低 4 位(如 '3' 对应 0x3,直接合并)。


4. 错误检测

if (unlikely(hi < 0)) return -EINVAL;
if (unlikely(lo < 0)) return -EINVAL;
  • unlikely 宏:提示编译器错误是低概率事件,优化分支预测。

  • 若 hi 或 lo 转换失败(非法字符),立即返回 -EINVAL


5. 合并高 4 位和低 4 位

*dst++ = (hi << 4) | lo;  // 合并为一个字节
  • 将高 4 位左移后与低 4 位按位或,合并为一个完整的字节。

  • 例如:

    • hi = 0xA'A'),lo = 0x3'3') → 0xA3


6. 返回值

  • 成功:返回 0

  • 失败:返回 -EINVAL(输入包含非法字符)。


7. 注意事项

  1. 输入合法性

    • src 必须是有效的十六进制字符串,每个字节对应 2 个字符。

    • 若 src 长度不足 2*count,可能导致越界访问(需调用者确保)。

  2. 目标缓冲区大小

    • dst 必须有至少 count 字节的空间。

  3. 大小写不敏感

    • hex_to_bin 应正确处理大小写(如 'a' 和 'A' 均转换为 0xA)。


示例

假设 src = "1a2B"count = 2

  1. 第一个字节

    • hi = '1' → 0x1lo = 'a' → 0xA → 合并为 0x1A

  2. 第二个字节

    • hi = '2' → 0x2lo = 'B' → 0xB → 合并为 0x2B

  3. dst 结果为 0x1A, 0x2B


总结

hex2bin 逐字节将十六进制字符串转换为二进制数据,严格检测输入合法性,适用于需要反向解析十六进制字符串的场景(如协议解析、数据恢复)。调用者需确保输入格式正确,避免潜在的内存越界问题。

bin2hex

/*** bin2hex - convert binary data to an ascii hexadecimal string* @dst: ascii hexadecimal result* @src: binary data* @count: binary data length*/
char *bin2hex(char *dst, const void *src, size_t count)
{const unsigned char *_src = src;while (count--)dst = hex_byte_pack(dst, *_src++);return dst;
}
EXPORT_SYMBOL(bin2hex);

这个函数的作用是将二进制数据转换为十六进制的ASCII字符串。以下是分步讲解:

  1. 函数参数

    • char *dst:目标缓冲区,用于存储转换后的十六进制字符串。

    • const void *src:源二进制数据的指针。

    • size_t count:要转换的字节数。

  2. 类型转换

    • src转换为const unsigned char *类型,便于按字节处理。

  3. 循环处理每个字节

    • 使用while (count--)循环处理每个字节,共处理count次。

    • 每次循环调用hex_byte_pack函数处理当前字节(*_src++),并将结果写入dst

  4. hex_byte_pack函数的作用

    • 将单个字节转换为两个十六进制字符,并写入dst

    • 例如,字节0xAB会被转换为字符'A'和'B'。

    • 更新dst指针,使其指向下一个写入位置(即每次写入后dst增加2)。

  5. 返回值

    • 返回最终的dst指针,指向转换后的字符串末尾的下一个位置。这便于连续调用或计算长度。

  6. 注意事项

    • 目标缓冲区大小:调用者需确保dst有足够空间(至少2*count字节)。

    • 字符串终止符:函数不会自动添加'\0',调用者需手动添加以确保字符串正确终止。

示例
假设src指向数据0x1A, 0x2Bcount为2:

  • hex_byte_pack将0x1A转换为"1a",0x2B转换为"2b"。

  • dst最终存储"1a2b",返回指针指向"b"之后的位置。

总结:该函数高效地将二进制数据逐字节转换为十六进制字符串,适用于需要灵活处理多段数据拼接的场景。

http://www.xdnf.cn/news/380773.html

相关文章:

  • DAY 21 常见的降维算法
  • 简述Web和HTTP
  • centos7.9上安装 freecad 指定安装位置
  • WinCC V7.2到V8.0与S71200/1500系列连接通讯教程以及避坑点
  • 码蹄集——向下取整(求立方根)、整理玩具、三角形斜边、完全平方数、个人所得税
  • MQTT协议介绍
  • 数据结构算法习题通关:树遍历 / 哈夫曼 / 拓扑 / 哈希 / Dijkstra 全解析
  • Python中的列表list使用详解
  • 重复的子字符串
  • 【ts】defineProps数组的类型声明
  • 人工智能100问☞第19问:什么是专家系统?
  • 自定义类型-结构体(二)
  • 基于ssm的超市库存商品管理系统(全套)
  • Vue.js框架的优缺点
  • 2025年PMP 学习六 -第5章 项目范围管理 (5.1,5.2,5.3)
  • ubunut20.04 安装运行lvi-sam
  • JavaSE核心知识点02面向对象编程02-05(方法)
  • 【比赛真题解析】混合可乐
  • 翻转数位题目解释和代码
  • C语言复习--动态内存管理
  • 同步、异步、并发的区别
  • Python与YOLO:自动驾驶中的实时物体检测
  • comfyui 如何优雅的从Hugging Face 下载模型,文件夹
  • 2025年特种作业操作证考试题库及答案(登高架设作业)
  • AST(抽象语法树)与 HBO(基于历史的优化)详解
  • 使用 Jackson 在 Java 中解析和生成 JSON
  • Spring事务管理实现机制
  • Windows右键管理工具:轻松添加/删除/修改右键菜单项!
  • xml与注解的区别
  • 机器学习 day01