Ubuntu 下 nginx-1.24.0 源码分析 - ngx_sprintf_num 函数
ngx_sprintf_num
声明
就在 ngx_string.c 的开头
static u_char *ngx_sprintf_num(u_char *buf, u_char *last, uint64_t ui64,u_char zero, ngx_uint_t hexadecimal, ngx_uint_t width);
ngx_sprintf_num
实现
static u_char *
ngx_sprintf_num(u_char *buf, u_char *last, uint64_t ui64, u_char zero,ngx_uint_t hexadecimal, ngx_uint_t width)
{u_char *p, temp[NGX_INT64_LEN + 1];/** we need temp[NGX_INT64_LEN] only,* but icc issues the warning*/size_t len;uint32_t ui32;static u_char hex[] = "0123456789abcdef";static u_char HEX[] = "0123456789ABCDEF";p = temp + NGX_INT64_LEN;if (hexadecimal == 0) {if (ui64 <= (uint64_t) NGX_MAX_UINT32_VALUE) {/** To divide 64-bit numbers and to find remainders* on the x86 platform gcc and icc call the libc functions* [u]divdi3() and [u]moddi3(), they call another function* in its turn. On FreeBSD it is the qdivrem() function,* its source code is about 170 lines of the code.* The glibc counterpart is about 150 lines of the code.** For 32-bit numbers and some divisors gcc and icc use* a inlined multiplication and shifts. For example,* unsigned "i32 / 10" is compiled to** (i32 * 0xCCCCCCCD) >> 35*/ui32 = (uint32_t) ui64;do {*--p = (u_char) (ui32 % 10 + '0');} while (ui32 /= 10);} else {do {*--p = (u_char) (ui64 % 10 + '0');} while (ui64 /= 10);}} else if (hexadecimal == 1) {do {/* the "(uint32_t)" cast disables the BCC's warning */*--p = hex[(uint32_t) (ui64 & 0xf)];} while (ui64 >>= 4);} else { /* hexadecimal == 2 */do {/* the "(uint32_t)" cast disables the BCC's warning */*--p = HEX[(uint32_t) (ui64 & 0xf)];} while (ui64 >>= 4);}/* zero or space padding */len = (temp + NGX_INT64_LEN) - p;while (len++ < width && buf < last) {*buf++ = zero;}/* number safe copy */len = (temp + NGX_INT64_LEN) - p;if (buf + len > last) {len = last - buf;}return ngx_cpymem(buf, p, len);
}
作用:将给定的 64 位无符号整数格式化为字符串,并填充到目标缓冲区
buf
:目标缓冲区
last
:目标缓冲区的最后一个有效位置的下一个位置(防止溢出)
ui64
:64 位无符号整数,要转换的数值
zero
:用于填充的字符
hexadecimal
决定进制(0=十进制,1=小写十六进制,2=大写十六进制)
width
是输出字符串的总宽度(不足时填充)
返回指针指向最后一个有效字符的下一个位置,便于链式调用
u_char *p, temp[NGX_INT64_LEN + 1];
temp 临时存放转换后字符的地方
p 指向这个临时区的当前处理位置
NGX_INT64_LEN
NGX_INT64_LEN 的定义
在 ngx_config.h 中:
#define NGX_INT64_LEN (sizeof("-9223372036854775808") - 1)
"-9223372036854775808":这是一个字符串,表示64位整数的最小值。64位整数的范围是从-9223372036854775808到9223372036854775807,这个字符串的长度就是64位整数转换成字符串后字符串的最大长度。
-1:因为字符串的长度包括了结尾的空字符\0,所以减去1,得到实际的数字长度。
为什么数组声明是 NGX_INT64_LEN + 1
?
注释说实际只需要 NGX_INT64_LEN
,但为了消除 ICC 编译器的警告。可能的场景是:当从后向前填充 temp
数组时,严格检查数组越界的编译器可能误报,增加 +1
可以绕过这种警告。
size_t len;
len
:记录转换后的字符串的长度
uint32_t ui32;
如果 ui64
是 32 位以内的值则转为 uint32_t
处理,以减少 64 位除法的性能损耗
static u_char hex[] = "0123456789abcdef";
static u_char HEX[] = "0123456789ABCDEF";
为什么它们是静态(static
)的?
因为这两个表在函数多次调用时内容不变。声明为 static
可以避免每次调用时重新初始化,节省时间和栈空间。
且十六进制转换需要频繁查表,静态表能提升性能。
p = temp + NGX_INT64_LEN;
初始化时,p
指向 temp
数组的末尾之后?这会不会越界?
这里 p
是一个指针,初始指向 temp
数组的“虚拟”末尾位置(temp[NGX_INT64_LEN]
)。当从后向前填充数字时,*--p
会先减指针再写入,因此第一个写入的位置是 temp[NGX_INT64_LEN - 1]
,确保不会越界。
if (hexadecimal == 0) {
hexadecimal == 0 表示采用
十进制表示
if (ui64 <= (uint64_t) NGX_MAX_UINT32_VALUE) {ui32 = (uint32_t) ui64;
如果 ui64
是 32 位以内的值(<= NGX_MAX_UINT32_VALUE
),则转为 uint32_t
处理,以减少 64 位除法的性能损耗
NGX_MAX_UINT32_VALUE
的定义在ngx_config.h 中:
#define NGX_MAX_UINT32_VALUE (uint32_t) 0xffffffff
代表32位无符号整数的最大值
do {*--p = (u_char) (ui32 % 10 + '0');} while (ui32 /= 10);
通过循环取余 10,将每一位转为 ASCII 字符,从 temp
数组末尾向前填充
} else {do {*--p = (u_char) (ui64 % 10 + '0');} while (ui64 /= 10);}
大于32位无符号整数的最大值的情况
else if (hexadecimal == 1) {do {/* the "(uint32_t)" cast disables the BCC's warning */*--p = hex[(uint32_t) (ui64 & 0xf)];} while (ui64 >>= 4);
这段代码处理的是十六进制小写(hexadecimal == 1
)的情况
do-while
循环,每次取 ui64
的最低 4 位(ui64 & 0xf
),将其映射为十六进制字符(小写),然后右移 4 位,直到所有位处理完。结果从 temp
数组末尾向前填充。
每次循环后,右移 4 位相当于“丢弃”已处理的最低位,准备处理下一个 4 位组。例如,0xABC
第一次处理 C
,右移后变为 0xAB
,下一次处理 B
,依此类推。
do-while
而非 while
即使 ui64
初始为 0,也会执行一次循环,输出 '0'
,确保数值 0 能被正确表示
else { /* hexadecimal == 2 */do {/* the "(uint32_t)" cast disables the BCC's warning */*--p = HEX[(uint32_t) (ui64 & 0xf)];} while (ui64 >>= 4);}
这部分处理的是十六进制大写的情况。
条件为hexadecimal == 2
,对应大写十六进制。
类似于之前的十六进制小写处理,但使用的是HEX
数组,包含大写字母。
len = (temp + NGX_INT64_LEN) - p;while (len++ < width && buf < last) {*buf++ = zero;}/* number safe copy */len = (temp + NGX_INT64_LEN) - p;if (buf + len > last) {len = last - buf;}return ngx_cpymem(buf, p, len);
这部分代码负责填充和复制处理后的数字到缓冲区。
首先,len = (temp + NGX_INT64_LEN) - p;
这里计算转换后的数字字符串的长度。
因为p指向有效字符的起始位置,而temp数组的起始地址加上NGX_INT64_LEN得到的是数组末尾,所以两者相减得到数字的实际长度。
接下来是填充循环:
while (len++ < width && buf < last)
这里len初始值是数字转换字符串后的长度,width是用户指定的总宽度。
如果数字长度小于width,就需要在数字前面填充zero字符(如'0'或空格),直到达到指定宽度。
每次循环填充一个字符,同时检查buf是否超过last,避免缓冲区溢出。
这里需要注意的是,len在循环条件中被自增,所以循环次数是width - len_initial,例如初始len为3,width为5,循环会执行两次,填充两个字符,使得总长度变为5。
接下来的 len = (temp + NGX_INT64_LEN) - p;
前面的填充循环修改了len变量,因此需要重新获取正确的数字长度
然后,检查if (buf + len > last),确定缓冲区剩余空间是否足够复制整个数字字符串
如果不够,则调整len为剩余空间的大小,防止溢出
最后使用ngx_cpymem函数将转换后的字符串从临时区复制到缓冲区,并返回新的buf位置。
相关文章:
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_sprintf_num 函数
ngx_sprintf_num 声明就在 ngx_string.c 的开头 static u_char *ngx_sprintf_num(u_char *buf, u_char *last, uint64_t ui64,u_char zero, ngx_uint_t hexadecimal, ngx_uint_t width); ngx_sprintf_num 实现 static u_char * ngx_sprintf_num(u_char *buf, u_char *last,…...
Vue的状态管理:用响应式 API 做简单状态管理、状态管理库(Pinia )
文章目录 引言单向数据流多个组件共享一个共同的状态I 用响应式 API 做简单状态管理使用 reactive()创建一个在多个组件实例间共享的响应式对象使用ref()返回一个全局状态II 状态管理库Pinia枚举状态管理引言 单向数据流 每一个 Vue 组件实例都在“管理”它自己的响应式状态了…...

AI工具如何辅助写文章(科研版)
文章总览:[YuanDaiMa2048博客文章总览](https://blog.csdn.net/2301_79288416/article/details/137397359?spm=1001.2014.3001.5501)https://blog.csdn.net/2301_79288416/article/details/137397359?spm=1001.2014.3001.5501 在科研领域,撰写论文是一个复杂且耗时的过程。…...
LEED绿色建筑认证的重要意义
LEED(Leadership in Energy and Environmental Design)绿色建筑认证由美国绿色建筑委员会(USGBC)开发,是全球广泛认可的绿色建筑评估体系。其重要意义体现在以下几个方面: 1. 环境保护 资源节约࿱…...
阿里云 ubuntu22.04 中国区节点安装 Docker
下面是一份在 Ubuntu 22.04 (Jammy) 上,通过阿里云镜像源来安装并配置 Docker 的详细步骤示例,可在中国区阿里云节点使用: 一、卸载旧版本 (如已安装) 如果系统中已经安装了旧版 Docker (可能是 docker、docker-engine、docker.io、containe…...
【kafka的零拷贝原理】
kafka的零拷贝原理 一、零拷贝技术概述二、Kafka中的零拷贝原理三、零拷贝技术的优势四、零拷贝技术的实现细节五、注意事项一、零拷贝技术概述 零拷贝(Zero-Copy)是一种减少数据拷贝次数,提高数据传输效率的技术。 在传统的数据传输过程中,数据需要在用户态和内核态之间…...

Linux环境部署DeepSeek大模型
一、背景 【DeepSeek 深度求索】这个春节给了世界一个重磅炸弹,弄得美国都睡不好觉。这次与以往不同,之前我们都是跟随着美国的AI人工智能,现在DeepSeek通过算法上的优化,大大降低了训练模型所需的成本以及时间,短期造…...
React中key值的正确使用指南:为什么需要它以及如何选择
React中key值的正确使用指南:为什么需要它以及如何选择 一、key值的基本概念二、如何选择合适的key值1. 数据来源决定key策略2. key值的三大核心要求 三、React为何需要key值?1. 虚拟DOM优化机制2. 状态维护机制 四、常见误区及解决方案1. 索引作为key的…...

21.2.1 基本操作
版权声明:本文为博主原创文章,转载请在显著位置标明本文出处以及作者网名,未经作者允许不得用于商业目的。 Excel的基本操作步骤: 1、打开Excel:定义了一个Application对象: Microsoft.Office.Interop.E…...

车载以太网__传输层
车载以太网中,传输层和实际用的互联网相差无几。本篇文章对传输层中的IP进行介绍 目录 什么是IP? IP和MAC的关系 IP地址分类 私有IP NAT DHCP 为什么要防火墙穿透? 广播 本地广播 直接广播 本地广播VS直接广播 组播 …...

简单本地部署deepseek(软件版)
Download Ollama on Windows 下载 下载安装 winr 输入 cmd 然后输入ollama -v,出现ollama版本,安装成功 deepseek-r1 选择1.5b 输入 cmd 下面代码 ollama run deepseek-r1:1.5b 删除deepseek的代码如下: ollama rm deepseek-r1:1.5b 使用…...

AI绘画:解锁商业设计新宇宙(6/10)
1.AI 绘画:商业领域的潜力新星 近年来,AI 绘画技术以惊人的速度发展,从最初简单的图像生成,逐渐演变为能够创造出高度逼真、富有创意的艺术作品。随着深度学习算法的不断优化,AI 绘画工具如 Midjourney、Stable Diffu…...

20250202在Ubuntu22.04下使用Guvcview录像的时候降噪
20250202在Ubuntu22.04下使用Guvcview录像的时候降噪 2025/2/2 21:25 声卡:笔记本电脑的摄像头自带的【USB接口的】麦克风。没有外接3.5mm接口的耳机。 缘起:在安装Ubuntu18.04/20.04系统的笔记本电脑中直接使用Guvcview录像的时候底噪很大! …...
cors跨域是如何做的?
CORS 跨域资源共享详解 什么是 CORS? CORS(Cross-Origin Resource Sharing)是一种机制,允许浏览器向不同源的服务器发出请求,从而实现跨域资源共享。默认情况下,浏览器出于安全考虑,禁止跨域请求。这意味着,当一个网页尝试从不同的域名、协议或端口加载资源时,浏览器…...
系统通解:超多视角理解
在科学研究和工程应用中,我们常常面临各种复杂系统,需要精确描述其行为和变化规律。从物理世界的运动现象,到化学反应的进程,再到材料在受力时的响应,这些系统的行为往往由一系列数学方程来刻画。通解,正是…...
最大矩阵的和
最大矩阵的和 真题目录: 点击去查看 E 卷 100分题型 题目描述 给定一个二维整数矩阵,要在这个矩阵中选出一个子矩阵,使得这个子矩阵内所有的数字和尽量大,我们把这个子矩阵称为和最大子矩阵,子矩阵的选取原则是原矩阵中一块相互…...

深度学习 | 表示学习 | 卷积神经网络 | Batch Normalization 在 CNN 中的示例 | 20
如是我闻: 让我们来用一个具体的例子说明 Batch Normalization 在 CNN 里的计算过程,特别是如何对每个通道(channel)进行归一化。 1. 假设我们有一个 CNN 层的输出 假设某个 CNN 层的输出是一个 4D 张量,形状为&#…...
最短木板长度
最短木板长度 真题目录: 点击去查看 E 卷 100分题型 题目描述 小明有 n 块木板,第 i ( 1 ≤ i ≤ n ) 块木板长度为 ai。 小明买了一块长度为 m 的木料,这块木料可以切割成任意块,拼接到已有的木板上,用来加长木板。 小明想让最…...

团体程序设计天梯赛-练习集——L1-034 点赞
前言 20分的题目题目不难,理解也不难,做起来有点问题 L1-034 点赞 微博上有个“点赞”功能,你可以为你喜欢的博文点个赞表示支持。每篇博文都有一些刻画其特性的标签,而你点赞的博文的类型,也间接刻画了你的特性。本…...

利用腾讯云cloud studio云端免费部署deepseek-R1
1. cloud studio 1.1 cloud studio介绍 Cloud Studio(云端 IDE)是基于浏览器的集成式开发环境,为开发者提供了一个稳定的云端工作站。支持CPU与GPU的访问。用户在使用 Cloud Studio 时无需安装,随时随地打开浏览器即可使用。Clo…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...
【HTTP三个基础问题】
面试官您好!HTTP是超文本传输协议,是互联网上客户端和服务器之间传输超文本数据(比如文字、图片、音频、视频等)的核心协议,当前互联网应用最广泛的版本是HTTP1.1,它基于经典的C/S模型,也就是客…...

Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...