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

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

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

相关推荐
LB211210 分钟前
Maven(黑马)
linux·服务器·maven
Steve lu15 分钟前
回归任务损失函数对比曲线
人工智能·pytorch·深度学习·神经网络·算法·回归·原力计划
UQI-LIUWJ22 分钟前
论文笔记:Towards Explainable Traffic Flow Prediction with Large Language Models
论文阅读·人工智能·语言模型
alex180135 分钟前
ubuntu磁盘挂载
linux·数据库·ubuntu
Stardep1 小时前
Linux下目录递归拷贝的单进程实现
linux·运维·服务器·实验
兔兔西1 小时前
【AI学习】检索增强生成(Retrieval Augmented Generation,RAG)
人工智能
hawk2014bj1 小时前
KVM 安装 Ubuntu 22
linux·运维·ubuntu
#guiyin111 小时前
基于机器学习的心脏病预测模型构建与可解释性分析
人工智能·机器学习
nako_sayuri1 小时前
Linux进程间通信----简易进程池实现
linux·服务器·进程池
IMA小队长1 小时前
06.概念二:神经网络
人工智能·深度学习·机器学习·transformer