Ubuntu 下 nginx-1.24.0 源码分析 - ngx_sprintf_str 函数

server/2025/2/7 18:55:09/

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 的情况,也就是将字节转成 大写十六进制字符串

逻辑同上


http://www.ppmy.cn/server/165738.html

相关文章

IDEA中Resolving Maven dependencies卡着不动解决方案

一、修改settings.xml Maven配置阿里云仓库主要通过修改Maven的settings.xml文件来实现‌。以下是具体步骤: ‌1、找到settings.xml文件‌: 通常位于Maven安装目录下的conf文件夹中,或者在用户目录下的.m2文件夹中(如果用户自定义了settings.xml的位置)。 2、‌编辑se…

关于大模型 AGI 应知应会_生在AI发展的时代

在 AI 时代&#xff0c;大模型和通用人工智能&#xff08;AGI&#xff09;正在深刻改变我们的生活和工作方式。以下是一些关于大模型和 AGI 的关键知识点&#xff0c;帮助我们更好地理解这一技术浪潮。 一、大模型的核心概念与特点 &#xff08;一&#xff09;什么是大模型 …

react的antd表单校验,禁止输入空格并触发校验提示

首先需要用到form组件&#xff0c;在form.item内添加rules属性&#xff0c;写正则表达式 <Form.Itemlabel"员工姓名"name"name"rules{[{ required: true, message: 员工姓名 },{ pattern: /^(?!\s*$).$/, message: 不能全是空格 },]}> <Input p…

Java进阶14 TCP日志枚举

Java进阶14 TCP&日志&枚举 一、网络编程TCP Java对基于TCP协议得网络提供了良好的封装&#xff0c;使用Socket对象来代表两端的通信端口&#xff0c;并通过Socket产生IO流来进行网络通信。 1、TCP协议发数据 1.1 构造方法 方法 说明 Socket(InetAddress address…

Linux——基础命令1

$&#xff1a;普通用户 #&#xff1a;超级用户 cd 切换目录 cd 目录 &#xff08;进入目录&#xff09; cd ../ &#xff08;返回上一级目录&#xff09; cd ~ &#xff08;切换到当前用户的家目录&#xff09; cd - &#xff08;返回上次目录&#xff09; pwd 输出当前目录…

99.24 金融难点通俗解释:MLF(中期借贷便利)vs LPR(贷款市场报价利率)

目录 0. 承前1. 什么是MLF&#xff1f;1.1 专业解释1.2 通俗解释1.3 MLF的三个关键点&#xff1a; 2. 什么是LPR&#xff1f;2.1 专业解释2.2 通俗解释2.3 LPR的三个关键点&#xff1a; 3. MLF和LPR的关系4. 传导机制4.1 第一步&#xff1a;央行调整MLF4.2 第二步&#xff1a;银…

C# 字符串与正则表达式介绍

.NET学习资料 .NET学习资料 .NET学习资料 在 C# 编程中&#xff0c;字符串和正则表达式是处理文本数据时不可或缺的工具。深入理解它们的特性和用法&#xff0c;能够显著提升开发效率和代码质量。 一、C# 字符串 &#xff08;一&#xff09;字符串的不可变性 在 C# 中&…

Three.js 实现海面效果

Three.js 实现海面效果 https://threehub.cn/#/codeMirror?navigationThreeJS&classifyshader&idoceanShader import * as THREE from three import { OrbitControls } from three/examples/jsm/controls/OrbitControls.js import { Water } from three/examples/js…