ngx_vslprintf

发布于:2025-04-03 ⋅ 阅读:(21) ⋅ 点赞:(0)

定义在 src\core\ngx_string.c

u_char *
ngx_vslprintf(u_char *buf, u_char *last, const char *fmt, va_list args)
{
    u_char                *p, zero;
    int                    d;
    double                 f;
    size_t                 slen;
    int64_t                i64;
    uint64_t               ui64, frac;
    ngx_msec_t             ms;
    ngx_uint_t             width, sign, hex, max_width, frac_width, scale, n;
    ngx_str_t             *v;
    ngx_variable_value_t  *vv;

    while (*fmt && buf < last) {

        /*
         * "buf < last" means that we could copy at least one character:
         * the plain character, "%%", "%c", and minus without the checking
         */

        if (*fmt == '%') {

            i64 = 0;
            ui64 = 0;

            zero = (u_char) ((*++fmt == '0') ? '0' : ' ');
            width = 0;
            sign = 1;
            hex = 0;
            max_width = 0;
            frac_width = 0;
            slen = (size_t) -1;

            while (*fmt >= '0' && *fmt <= '9') {
                width = width * 10 + (*fmt++ - '0');
            }


            for ( ;; ) {
                switch (*fmt) {

                case 'u':
                    sign = 0;
                    fmt++;
                    continue;

                case 'm':
                    max_width = 1;
                    fmt++;
                    continue;

                case 'X':
                    hex = 2;
                    sign = 0;
                    fmt++;
                    continue;

                case 'x':
                    hex = 1;
                    sign = 0;
                    fmt++;
                    continue;

                case '.':
                    fmt++;

                    while (*fmt >= '0' && *fmt <= '9') {
                        frac_width = frac_width * 10 + (*fmt++ - '0');
                    }

                    break;

                case '*':
                    slen = va_arg(args, size_t);
                    fmt++;
                    continue;

                default:
                    break;
                }

                break;
            }


            switch (*fmt) {

            case 'V':
                v = va_arg(args, ngx_str_t *);

                buf = ngx_sprintf_str(buf, last, v->data, v->len, hex);
                fmt++;

                continue;

            case 'v':
                vv = va_arg(args, ngx_variable_value_t *);

                buf = ngx_sprintf_str(buf, last, vv->data, vv->len, hex);
                fmt++;

                continue;

            case 's':
                p = va_arg(args, u_char *);

                buf = ngx_sprintf_str(buf, last, p, slen, hex);
                fmt++;

                continue;

            case 'O':
                i64 = (int64_t) va_arg(args, off_t);
                sign = 1;
                break;

            case 'P':
                i64 = (int64_t) va_arg(args, ngx_pid_t);
                sign = 1;
                break;

            case 'T':
                i64 = (int64_t) va_arg(args, time_t);
                sign = 1;
                break;

            case 'M':
                ms = (ngx_msec_t) va_arg(args, ngx_msec_t);
                if ((ngx_msec_int_t) ms == -1) {
                    sign = 1;
                    i64 = -1;
                } else {
                    sign = 0;
                    ui64 = (uint64_t) ms;
                }
                break;

            case 'z':
                if (sign) {
                    i64 = (int64_t) va_arg(args, ssize_t);
                } else {
                    ui64 = (uint64_t) va_arg(args, size_t);
                }
                break;

            case 'i':
                if (sign) {
                    i64 = (int64_t) va_arg(args, ngx_int_t);
                } else {
                    ui64 = (uint64_t) va_arg(args, ngx_uint_t);
                }

                if (max_width) {
                    width = NGX_INT_T_LEN;
                }

                break;

            case 'd':
                if (sign) {
                    i64 = (int64_t) va_arg(args, int);
                } else {
                    ui64 = (uint64_t) va_arg(args, u_int);
                }
                break;

            case 'l':
                if (sign) {
                    i64 = (int64_t) va_arg(args, long);
                } else {
                    ui64 = (uint64_t) va_arg(args, u_long);
                }
                break;

            case 'D':
                if (sign) {
                    i64 = (int64_t) va_arg(args, int32_t);
                } else {
                    ui64 = (uint64_t) va_arg(args, uint32_t);
                }
                break;

            case 'L':
                if (sign) {
                    i64 = va_arg(args, int64_t);
                } else {
                    ui64 = va_arg(args, uint64_t);
                }
                break;

            case 'A':
                if (sign) {
                    i64 = (int64_t) va_arg(args, ngx_atomic_int_t);
                } else {
                    ui64 = (uint64_t) va_arg(args, ngx_atomic_uint_t);
                }

                if (max_width) {
                    width = NGX_ATOMIC_T_LEN;
                }

                break;

            case 'f':
                f = va_arg(args, double);

                if (f < 0) {
                    *buf++ = '-';
                    f = -f;
                }

                ui64 = (int64_t) f;
                frac = 0;

                if (frac_width) {

                    scale = 1;
                    for (n = frac_width; n; n--) {
                        scale *= 10;
                    }

                    frac = (uint64_t) ((f - (double) ui64) * scale + 0.5);

                    if (frac == scale) {
                        ui64++;
                        frac = 0;
                    }
                }

                buf = ngx_sprintf_num(buf, last, ui64, zero, 0, width);

                if (frac_width) {
                    if (buf < last) {
                        *buf++ = '.';
                    }

                    buf = ngx_sprintf_num(buf, last, frac, '0', 0, frac_width);
                }

                fmt++;

                continue;

#if !(NGX_WIN32)
            case 'r':
                i64 = (int64_t) va_arg(args, rlim_t);
                sign = 1;
                break;
#endif

            case 'p':
                ui64 = (uintptr_t) va_arg(args, void *);
                hex = 2;
                sign = 0;
                zero = '0';
                width = 2 * sizeof(void *);
                break;

            case 'c':
                d = va_arg(args, int);
                *buf++ = (u_char) (d & 0xff);
                fmt++;

                continue;

            case 'Z':
                *buf++ = '\0';
                fmt++;

                continue;

            case 'N':
#if (NGX_WIN32)
                *buf++ = CR;
                if (buf < last) {
                    *buf++ = LF;
                }
#else
                *buf++ = LF;
#endif
                fmt++;

                continue;

            case '%':
                *buf++ = '%';
                fmt++;

                continue;

            default:
                *buf++ = *fmt++;

                continue;
            }

            if (sign) {
                if (i64 < 0) {
                    *buf++ = '-';
                    ui64 = (uint64_t) -i64;

                } else {
                    ui64 = (uint64_t) i64;
                }
            }

            buf = ngx_sprintf_num(buf, last, ui64, zero, hex, width);

            fmt++;

        } else {
            *buf++ = *fmt++;
        }
    }

    return buf;
}

while (*fmt && buf < last) {

逐个解析格式化字符串(fmt)中的字符,并根据格式说明符将参数数据写入目标缓冲区(buf

*fmt:检查格式化字符串是否尚未结束(\0)。

buf < last:确保目标缓冲区仍有空间(last指向缓冲区末尾的下一个位置,因此buf必须严格小于last才允许写入)


if (*fmt == '%') {

 当格式化字符串(fmt)遇到%字符时,表示进入一个格式说明符的解析过程,

该过程负责提取参数、应用格式规则(如宽度、精度、符号等),并将数据安全写入缓冲区。

此时

*fmt=%

进入 if


            i64 = 0;
            ui64 = 0;

            zero = (u_char) ((*++fmt == '0') ? '0' : ' ');
            width = 0;
            sign = 1;
            hex = 0;
            max_width = 0;
            frac_width = 0;
            slen = (size_t) -1;

初始化一些关键变量


i64 = 0;
初始化一个 64 位有符号整数变量 i64,用于存储后续可能的有符号整数。


ui64 = 0;
初始化一个 64 位无符号整数变量 ui64,用于存储后续可能的无符号整数。


zero = (u_char) ((*++fmt == '0') ? '0' : ' ');                                                          

检查格式化字符串的下一个字符是否是 '0'。                                                                                

如果是 '0',则将 zero 设置为 '0',表示后续的数字格式化会用零填充;

否则设置为 ' ',表示用空格填充。

fmt 指针会向前移动一位。

此时 

zero= (空格)


width=0;                                    

初始化宽度变量 width,用于存储格式化指令中指定的宽度


sign = 1;
初始化符号标志 sign,默认值为 1,表示后续的数字是有符的。    

如果后续遇到 u 或其他无符号修饰符,会将 sign 设置为 0。


hex = 0;
初始化十六进制标志 hex,默认值为 0,表示后续的数字不是十六进制格式。                              

如果后续遇到 x 或 X,会将 hex 设置为 1 或 2。


max_width = 0;
初始化最大宽度变量 max_width,用于存储格式化指令中可能的最大宽度限制。


frac_width = 0;
初始化小数部分宽度变量 frac_width,用于存储浮点数格式化指令中指定的小数部分宽度(例如 %.2f 中的 2)。


slen = (size_t) -1;
初始化字符串长度变量 slen,默认值为 (size_t) -1,表示字符串长度未指定。如果后续遇到 *,会从 va_list args 中取出实际的长度值。


            while (*fmt >= '0' && *fmt <= '9') {
                width = width * 10 + (*fmt++ - '0');
            }

解析格式说明符中的宽度字段(例如`%10d`中的`10`),并将其转换为整数值存储在`width`变量中。

width 用于控制后续格式化输出的宽度

此时 

*fmt=s 

条件不成立


            for ( ;; ) {
                switch (*fmt) {

循环,解析修饰符

此时 

*fmt=s


               case 'u':
                    sign = 0;
                    fmt++;
                    continue;

                case 'm':
                    max_width = 1;
                    fmt++;
                    continue;

                case 'X':
                    hex = 2;
                    sign = 0;
                    fmt++;
                    continue;

                case 'x':
                    hex = 1;
                    sign = 0;
                    fmt++;
                    continue;

                case '.':
                    fmt++;

                    while (*fmt >= '0' && *fmt <= '9') {
                        frac_width = frac_width * 10 + (*fmt++ - '0');
                    }

                    break;

                case '*':
                    slen = va_arg(args, size_t);
                    fmt++;
                    continue;

                default:
                    break;
                }

此时进入 default 分支


break;

跳出循环


switch (*fmt) {

处理数据类型修饰符

此时

*fmt=s


接下来 进入这个分支 

            case 's':
                p = va_arg(args, u_char *);

                buf = ngx_sprintf_str(buf, last, p, slen, hex);
                fmt++;

                continue;

处理格式化字符串中的 s 修饰符。

s 表示需要插入一个普通的字符串,这个字符串的地址存储在参数列表 args 中。

代码会从 args 中取出这个字符串的地址,并将其内容格式化到输出缓冲区 buf 中。

p = va_arg(args, u_char *);

这行代码从参数列表 args 中取出一个 u_char * 类型的指针,并将其存储到变量 p 中。

buf = ngx_sprintf_str(buf, last, p, slen, hex);

这行代码调用 ngx_sprintf_str 函数,将字符串 p 格式化到输出缓冲区 buf 中


fmt++;

指针后移

continue;

进入下一次循环,处理下一个字符


 

ngx_sprintf_str-CSDN博客


    while (*fmt && buf < last) {

此时

*fmt=,


        else {
            *buf++ = *fmt++;
        }

 直接复制

然后进入下一次循环


while (*fmt && buf < last) {

此时

*fmt= (空格)

 直接复制

然后进入下一次循环

*fmt=%


if (*fmt == '%') {
zero = (u_char) ((*++fmt == '0') ? '0' : ' ');

此时

zero=0


            while (*fmt >= '0' && *fmt <= '9') {
                width = width * 10 + (*fmt++ - '0');
            }

 解析宽度,转换为整数

此时

width=2


            for ( ;; ) {
                switch (*fmt) {

此时

*fmt=d


default:
                    break;

 进入这个分支

break;

 结束循环


 

switch (*fmt) {

此时

*fmt=d


            case 'd':
                if (sign) {
                    i64 = (int64_t) va_arg(args, int);
                } else {
                    ui64 = (uint64_t) va_arg(args, u_int);
                }
                break;

进入这个 分支

此时

sign=1(默认值)

取下一个参数,参数类型是 int

转换为 int64_t 赋值给 i64

此时

i64=2


 

            if (sign) {
                if (i64 < 0) {
                    *buf++ = '-';
                    ui64 = (uint64_t) -i64;

                } else {
                    ui64 = (uint64_t) i64;
                }
            }

检查是否需要处理数值的符号位

  • sign=1:表示当前处理的数值是有符号类型

  • sign=0:表示数值是无符号类型,直接跳过符号处理。

此时条件成立

判断数值是否为负数

此时条件不成立

数值非负,直接将其转换为无符号整数


buf = ngx_sprintf_num(buf, last, ui64, zero, hex, width);

作用:调用函数将数值ui64格式化为字符串,并写入缓冲区。
参数解析:
 buf:当前缓冲区写入位置(函数返回后更新到新位置)。
 last:缓冲区末尾地址(防止溢出)。
 ui64:待格式化的无符号数值
 zero:填充字符('0'或' '),用于宽度不足时补位。
 hex:进制模式(0=十进制,1=小写十六进制,2=大写十六进制)。
width:最小输出宽度(若实际位数不足,左侧填充zero字符
)。

此时

ui64=2
zero=0
hex=0
width=2

 

ngx_sprintf_num-CSDN博客


fmt++;

指针后移,处理下一个字符


进入下一次循环 

while (*fmt && buf < last) {

此时

*fmt= (空格)


        } else {
            *buf++ = *fmt++;
        }

 直接 复制


进入下一次循环 

    while (*fmt && buf < last) {

*fmt=%


if (*fmt == '%') {

条件成立


zero = (u_char) ((*++fmt == '0') ? '0' : ' ');

此时

zero= (空格)


            while (*fmt >= '0' && *fmt <= '9') {
                width = width * 10 + (*fmt++ - '0');
            }

  *fmt=s

条件不成立


            for ( ;; ) {
                switch (*fmt) {

   *fmt=s


进入这个分支

                default:
                    break;

 然后

break;

结束 for 循环


 

           switch (*fmt) {

*fmt=s


所以进入这个分支

            case 's':
                p = va_arg(args, u_char *);

                buf = ngx_sprintf_str(buf, last, p, slen, hex);
                fmt++;

                continue;

 取下一个参数,类型是 u_char *

把 p 指向的字符串 格式化到 buf

然后 指针后移处理下一个字符


下一次循环

    while (*fmt && buf < last) {

*fmt= (空格)


        } else {
            *buf++ = *fmt++;
        }

直接复制


下一次循环

while (*fmt && buf < last) {

此时

*fmt=%


if (*fmt == '%') {
zero = (u_char) ((*++fmt == '0') ? '0' : ' ');

此时

zero= (空格)


            while (*fmt >= '0' && *fmt <= '9') {
                width = width * 10 + (*fmt++ - '0');
            }

*fmt=4

width=4

下一个字符

*fmt=d


            for ( ;; ) {
                switch (*fmt) {

*fmt=d


进入这个分支

                default:
                    break;
break;

switch (*fmt) {

此时

*fmt=d


进入这个分支

            case 'd':
                if (sign) {
                    i64 = (int64_t) va_arg(args, int);
                } else {
                    ui64 = (uint64_t) va_arg(args, u_int);
                }
                break;

sign=1 是默认值

取下一个参数,类型为 int

i64=2025


            if (sign) {
                if (i64 < 0) {
                    *buf++ = '-';
                    ui64 = (uint64_t) -i64;

                } else {
                    ui64 = (uint64_t) i64;
                }
            }

 进入 else

转换为 

uint64_t


buf = ngx_sprintf_num(buf, last, ui64, zero, hex, width);

ui64 转换为 字符串 到 buf 指向的缓冲区 

ui64=2025
zero= (空格)
hex=0
width=4


 

fmt++;

指针后移,处理下一个字符

 


下一次循环

    while (*fmt && buf < last) {

*fmt= (空格)


        } else {
            *buf++ = *fmt++;
        }

 直接复制


下一次循环 

while (*fmt && buf < last) {

 *fmt=%


 

zero = (u_char) ((*++fmt == '0') ? '0' : ' ');

 zero=0


            while (*fmt >= '0' && *fmt <= '9') {
                width = width * 10 + (*fmt++ - '0');
            }

width=2


            for ( ;; ) {
                switch (*fmt) {

 *fmt=d


进入这个分支

                default:
                    break;
                }
break;

结束 for 循环 


switch (*fmt) {

  *fmt=d


进入这个分支

            case 'd':
                if (sign) {
                    i64 = (int64_t) va_arg(args, int);
                } else {
                    ui64 = (uint64_t) va_arg(args, u_int);
                }
                break;

 sign=1

取下一个参数,类型是 int

i64=10


            if (sign) {
                if (i64 < 0) {
                    *buf++ = '-';
                    ui64 = (uint64_t) -i64;

                } else {
                    ui64 = (uint64_t) i64;
                }
            }

            buf = ngx_sprintf_num(buf, last, ui64, zero, hex, width);

整数转换为字符

ui64=10
zero=0
hex=0
width=2


fmt++;

 指向下一个要处理的字符

后面的逻辑 同上 

return buf;

最后返回 最后有效字符的下一个字节位置


网站公告

今日签到

点亮在社区的每一天
去签到