在计算机系统中,二进制与十六进制的相互转换是数据存储、网络传输、加密算法等场景中的基础操作。Linux内核中的lib/hexdump.c模块提供了三个关键函数:bin2hex、hex2bin和hex_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-9、a-f、A-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. 协作流程
-
加密密钥处理:
-
存储:使用
bin2hex将二进制密钥转为十六进制字符串。 -
加载:使用
hex2bin反向解析,依赖hex_to_bin逐字符校验。
-
2. 性能与安全
-
无分支优势 :
hex_to_bin避免分支预测错误和时序攻击风险。 -
内存安全:调用者需确保缓冲区大小,防止溢出。
3. 扩展思考
-
Unicode支持:当前实现仅限ASCII字符,需扩展以支持宽字符。
-
错误处理增强 :
hex2bin可返回更详细的错误类型(如非法字符位置)。
4. 总结
bin2hex、hex2bin和hex_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. 核心逻辑
函数通过位运算和算术运算,避免条件分支,直接计算字符的合法性及其对应的值。逻辑分为两部分:
-
处理数字
0-9。 -
处理字母
A-F/a-f(统一转为大写处理)。
3. 处理数字字符 0-9
// 判断字符是否在 '0'-'9' 范围内,并计算对应值
int part1 = (ch - '0' + 1) & (unsigned)((ch - '9' - 1) & ('0' - 1 - ch)) >> 8);
步骤拆解:
-
范围检测:
-
(ch - '9' - 1) < 0:若ch <= '9',结果为负。 -
('0' - 1 - ch) < 0:若ch >= '0',结果为负。 -
两者按位与后,结果为负 当且仅当
ch在'0'-'9'范围内。
-
-
掩码生成:
- 将结果转为
unsigned并右移 8 位,生成掩码0xFFFFFF(合法)或0(非法)。
- 将结果转为
-
值计算:
-
若合法,
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);
步骤拆解:
-
小写转大写:
ch & 0xdf将小写字母转为大写(如'a' → 'A')。
-
范围检测:
-
(cu - 'F' - 1) < 0:若cu <= 'F',结果为负。 -
('A' - 1 - cu) < 0:若cu >= 'A',结果为负。 -
两者按位与后,结果为负 当且仅当
cu在'A'-'F'范围内。
-
-
掩码生成:
- 同上,生成掩码
0xFFFFFF或0。
- 同上,生成掩码
-
值计算:
-
若合法,
cu - 'A' + 11生成值11-16,与掩码按位与后保留原值。 -
若非法,结果为
0。
-
5. 合并结果
return -1 + part1 + part2;
逻辑解释:
-
合法数字 :
part1为1-10,part2为0→ 结果为0-9。 -
合法字母 :
part1为0,part2为11-16→ 结果为10-15。 -
非法字符 :
part1和part2均为0→ 结果为-1。
6. 示例验证
-
输入
'3':part1 = 4(3 - '0' + 1 = 4),part2 = 0→-1 + 4 + 0 = 3。
-
输入
'A':part1 = 0,part2 = 11→-1 + 0 + 11 = 10。
-
输入
'f'(转为'F'):part1 = 0,part2 = 16→-1 + 0 + 16 = 15。
-
输入
'G':part1 = 0,part2 = 0→-1 + 0 + 0 = -1。
7. 设计优点
-
无分支:避免条件跳转,防止侧信道攻击(对加密场景至关重要)。
-
统一处理:小写字母通过位运算转为大写,简化逻辑。
-
紧凑高效:纯算术和位运算,适合高频调用场景。
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-9、a-f、A-F),返回负数。
-
-
字符处理顺序:
-
先处理高 4 位(如
'A'对应0xA,左移 4 位后为0xA0)。 -
再处理低 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. 注意事项
-
输入合法性:
-
src必须是有效的十六进制字符串,每个字节对应 2 个字符。 -
若
src长度不足2*count,可能导致越界访问(需调用者确保)。
-
-
目标缓冲区大小:
dst必须有至少count字节的空间。
-
大小写不敏感:
hex_to_bin应正确处理大小写(如'a'和'A'均转换为0xA)。
示例
假设 src = "1a2B",count = 2:
-
第一个字节:
hi = '1' → 0x1,lo = 'a' → 0xA→ 合并为0x1A。
-
第二个字节:
hi = '2' → 0x2,lo = 'B' → 0xB→ 合并为0x2B。
-
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字符串。以下是分步讲解:
-
函数参数:
-
char *dst:目标缓冲区,用于存储转换后的十六进制字符串。 -
const void *src:源二进制数据的指针。 -
size_t count:要转换的字节数。
-
-
类型转换:
- 将
src转换为const unsigned char *类型,便于按字节处理。
- 将
-
循环处理每个字节:
-
使用
while (count--)循环处理每个字节,共处理count次。 -
每次循环调用
hex_byte_pack函数处理当前字节(*_src++),并将结果写入dst。
-
-
hex_byte_pack函数的作用:
-
将单个字节转换为两个十六进制字符,并写入
dst。 -
例如,字节
0xAB会被转换为字符'A'和'B'。 -
更新
dst指针,使其指向下一个写入位置(即每次写入后dst增加2)。
-
-
返回值:
- 返回最终的
dst指针,指向转换后的字符串末尾的下一个位置。这便于连续调用或计算长度。
- 返回最终的
-
注意事项:
-
目标缓冲区大小 :调用者需确保
dst有足够空间(至少2*count字节)。 -
字符串终止符:函数不会自动添加'\0',调用者需手动添加以确保字符串正确终止。
-
示例 :
假设src指向数据0x1A, 0x2B,count为2:
-
hex_byte_pack将0x1A转换为"1a",0x2B转换为"2b"。 -
dst最终存储"1a2b",返回指针指向"b"之后的位置。
总结:该函数高效地将二进制数据逐字节转换为十六进制字符串,适用于需要灵活处理多段数据拼接的场景。