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

在计算机系统中,二进制与十六进制的相互转换是数据存储、网络传输、加密算法等场景中的基础操作。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

cpp 复制代码
**
 * 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-9, A-F, a-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. 掩码生成

    • 同上,生成掩码 0xFFFFFF0
  4. 值计算

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

    • 若非法,结果为 0


5. 合并结果

复制代码
return -1 + part1 + part2;
逻辑解释
  • 合法数字part11-10part20 → 结果为 0-9

  • 合法字母part10part211-16 → 结果为 10-15

  • 非法字符part1part2 均为 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

cpp 复制代码
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:提示编译器错误是低概率事件,优化分支预测。

  • hilo 转换失败(非法字符),立即返回 -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

cpp 复制代码
/**
 * 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"之后的位置。

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

相关推荐
云边有个稻草人37 分钟前
【Linux系统】第四节—详解yum+vim
linux·vim·yum·软件包管理器·linux软件生态·linux编辑器-vim使⽤·yum具体操作
wei_shuo40 分钟前
OB Cloud 云数据库V4.3:SQL +AI全新体验
数据库·人工智能·sql
努力的搬砖人.42 分钟前
AI生成视频推荐
人工智能
想要成为计算机高手2 小时前
Helix:一种用于通用人形控制的视觉语言行动模型
人工智能·计算机视觉·自然语言处理·大模型·vla
Mory_Herbert2 小时前
5.1 神经网络: 层和块
人工智能·深度学习·神经网络
可乐鸡翅好好吃3 小时前
not a genuine st device abort connection的问题
c语言·stm32·单片机·keil
Evand J3 小时前
MATLAB程序演示与编程思路,相对导航,四个小车的形式,使用集中式扩展卡尔曼滤波(fullyCN-EKF)
人工智能·算法
知来者逆4 小时前
在与大语言模型交互中的礼貌现象:技术影响、社会行为与文化意义的多维度探讨
人工智能·深度学习·语言模型·自然语言处理·llm
dz小伟4 小时前
vim的配置
linux·编辑器·vim
江湖人称-杰6 小时前
CentOS配置了镜像源之后依旧下载元数据失败
linux·运维·centos