Ubuntu 下 nginx-1.24.0 源码分析 - ngx_sprintf_str 函数
ngx_sprintf_str 函数
ngx_sprintf_str 声明
在 ngx_string.c 的开头
static u_char *ngx_sprintf_str(u_char *buf, u_char *last, u_char *src,
size_t len, ngx_uint_t hexadecimal);
ngx_sprintf_str
实现在ngx_string.c中
static u_char *
ngx_sprintf_str(u_char *buf, u_char *last, u_char *src, size_t len,
ngx_uint_t hexadecimal)
{
static u_char hex[] = "0123456789abcdef";
static u_char HEX[] = "0123456789ABCDEF";
if (hexadecimal == 0) {
if (len == (size_t) -1) {
while (*src && buf < last) {
*buf++ = *src++;
}
} else {
len = ngx_min((size_t) (last - buf), len);
buf = ngx_cpymem(buf, src, len);
}
} else if (hexadecimal == 1) {
if (len == (size_t) -1) {
while (*src && buf < last - 1) {
*buf++ = hex[*src >> 4];
*buf++ = hex[*src++ & 0xf];
}
} else {
while (len-- && buf < last - 1) {
*buf++ = hex[*src >> 4];
*buf++ = hex[*src++ & 0xf];
}
}
} else { /* hexadecimal == 2 */
if (len == (size_t) -1) {
while (*src && buf < last - 1) {
*buf++ = HEX[*src >> 4];
*buf++ = HEX[*src++ & 0xf];
}
} else {
while (len-- && buf < last - 1) {
*buf++ = HEX[*src >> 4];
*buf++ = HEX[*src++ & 0xf];
}
}
}
return buf;
}
ngx_sprintf_str
函数,它负责将数据按不同格式写入缓冲区
u_char *buf
:目标缓冲区的起始位置,用来写入结果。
u_char *last
:缓冲区的末尾指针(指向最后一个有效位置的下一个位置),防止越界。
u_char *src
:源数据(比如要处理的字符串或二进制数据)。
size_t len
:要处理的源数据长度。如果传(size_t)-1
(即-1
的无符号形态)(未指定长度),表示src
是字符串,直到遇到\0
结束。
ngx_uint_t hexadecimal
:控制输出格式:0
是普通字符串,1
是小写十六进制,2
是大写十六进制。
返回值指向最后一个有效位置的下一个位置,也是下一个可写入位置,方便用于进行链式操作
static u_char hex[] = "0123456789abcdef";
static u_char HEX[] = "0123456789ABCDEF";
这两个数组是十六进制转换的核心工具
就像密码本一样,把4位二进制数(0-15)翻译成对应的字符
hex[]
是小写字母版(a-f)
HEX[]
是大写字母版(A-F)
假设我们要转换字节0xAE(二进制10101110):
// 拆分高4位和低4位
高4位 = 0xA(1010) → hex[0xA] → 'a'
低4位 = 0xE(1110) → hex[0xE] → 'e'
// 组合成"ae"
if (hexadecimal == 0) {
if (len == (size_t) -1) {
while (*src && buf < last) {
*buf++ = *src++;
}
} else {
len = ngx_min((size_t) (last - buf), len);
buf = ngx_cpymem(buf, src, len);
}
当hexadecimal为0时,处理的是非十六进制情况,直接复制原始字节
2种情况:
if (len == (size_t) -1)
表示未指定长度
while (*src && buf < last)
未指定长度的情况下循环的两个终止条件:
1:*src
遇到 \0 (字符串结束标志)
2: buf
触达缓冲区末尾 last
,防止溢出
*buf++ = *src++
:
逐字节复制字符,同时移动目标指针 buf
和源指针 src,继续进行下一个字符的处理
else 就是指定了长度的情况
len = ngx_min((size_t)(last - buf), len)
:
计算安全的复制长度,取「剩余缓冲区空间」和「用户指定长度」的较小值,防止溢出
ngx_min 的定义在 ngx_core.h:
#define ngx_min(val1, val2) ((val1 > val2) ? (val2) : (val1))
buf = ngx_cpymem(buf, src, len)
内存复制函数(类似 memcpy
),但返回值是目标地址末尾位置,直接更新 buf(
指向最后一个有效位置的下一个位置)
} else if (hexadecimal == 1) {
if (len == (size_t) -1) {
while (*src && buf < last - 1) {
*buf++ = hex[*src >> 4];
*buf++ = hex[*src++ & 0xf];
}
} else {
while (len-- && buf < last - 1) {
*buf++ = hex[*src >> 4];
*buf++ = hex[*src++ & 0xf];
}
}
else if (hexadecimal == 1)
进入小写十六进制模式,将每个字节拆解为两个十六进制字符(如 0xAB
→ 'a''b'
)
if (len == (size_t)-1)
表示未指定长度的情况下
while (*src && buf < last - 1)
终止条件 1:*src
遇到 \0
终止条件 2:缓冲区至少保留 2 字节空间
*buf++ = hex[*src >> 4]
取字节高 4 位(如 0xA0
→ 0xA
),查表 hex
得到字符('a'
),存入buf 指向的位置,buf 加 1,指向下一个可写入位置
*buf++ = hex[*src++ & 0xf]
:
取低 4 位(如 0xA5
→ 0x5
),查表得到字符('5'
),存入buf 指向的位置,buf 加 1,指向下一个可写入位置,同时移动 src
指针,指向下一个要处理的字符
else
指定长度的情况下
while (len-- && buf < last - 1)
:
通过 len--
控制循环次数,确保不超出指定长度
buf < last - 1
缓冲区至少保留 2 字节空间
之后操作同上
} else { /* hexadecimal == 2 */
if (len == (size_t) -1) {
while (*src && buf < last - 1) {
*buf++ = HEX[*src >> 4];
*buf++ = HEX[*src++ & 0xf];
}
} else {
while (len-- && buf < last - 1) {
*buf++ = HEX[*src >> 4];
*buf++ = HEX[*src++ & 0xf];
}
}
}
处理 hexadecimal == 2
的情况,也就是将字节转成 大写十六进制字符串
逻辑同上