在计算机系统中,二进制与十六进制的相互转换是数据存储、网络传输、加密算法等场景中的基础操作。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"之后的位置。
总结:该函数高效地将二进制数据逐字节转换为十六进制字符串,适用于需要灵活处理多段数据拼接的场景。