xref: /DragonOS/kernel/src/libs/printk.c (revision bb5f098a864cee36b7d2c1ab9c029c0280d94a8a)
1 //
2 // Created by longjin on 2022/1/22.
3 //
4 #include <common/kprint.h>
5 #include <common/printk.h>
6 
7 #include <common/spinlock.h>
8 #include <libs/libUI/textui.h>
9 #include <mm/mm.h>
10 
11 #include <common/math.h>
12 #include <common/string.h>
13 
14 static spinlock_t __printk_lock = {1};
15 /**
16  * @brief 将数字按照指定的要求转换成对应的字符串(2~36进制)
17  *
18  * @param str 要返回的字符串
19  * @param num 要打印的数值
20  * @param base 基数
21  * @param field_width 区域宽度
22  * @param precision 精度
23  * @param flags 标志位
24  */
25 static char *write_num(char *str, ul num, int base, int field_width, int precision, int flags);
26 
27 static char *write_float_point_num(char *str, double num, int field_width, int precision, int flags);
28 
29 static int skip_and_atoi(const char **s)
30 {
31     /**
32      * @brief 获取连续的一段字符对应整数的值
33      * @param:**s 指向 指向字符串的指针 的指针
34      */
35     int ans = 0;
36     while (is_digit(**s))
37     {
38         ans = ans * 10 + (**s) - '0';
39         ++(*s);
40     }
41     return ans;
42 }
43 
44 /**
45  * @brief 将字符串按照fmt和args中的内容进行格式化,当buf_size为-1时,字符串直接保存到buf中
46  * 否则将字符串前buf_size-1个字符放入,大小为buf_size的buf数组中
47  *
48  * @param buf 结果缓冲区
49  * @param fmt 格式化字符串
50  * @param args 内容
51  * @param buf_size buf_size为-1时,不指定buf的大小,否则buf大小为buf_size
52  * @return 最终字符串的长度
53  */
54 static int __do_vsprintf(char *buf, const char *fmt, int buf_size, va_list args)
55 {
56 
57     // 当需要输出的字符串的指针为空时,使用该字符填充目标字符串的指针
58     static const char __end_zero_char = '\0';
59 
60     char *str = NULL, *s = NULL, *end = NULL;
61 
62     str = buf;
63 
64     int flags;       // 用来存储格式信息的bitmap
65     int field_width; //区域宽度
66     int precision;   //精度
67     int qualifier;   //数据显示的类型
68     int len;
69 
70     if (buf_size != -1)
71     {
72         end = buf + buf_size;
73     }
74     //开始解析字符串
75     for (; *fmt; ++fmt)
76     {
77         //内容不涉及到格式化,直接输出
78         if (*fmt != '%')
79         {
80             *str = *fmt;
81             ++str;
82             continue;
83         }
84 
85         //开始格式化字符串
86 
87         //清空标志位和field宽度
88         field_width = flags = 0;
89 
90         bool flag_tmp = true;
91         bool flag_break = false;
92 
93         ++fmt;
94         while (flag_tmp)
95         {
96             switch (*fmt)
97             {
98             case '\0':
99                 //结束解析
100                 flag_break = true;
101                 flag_tmp = false;
102                 break;
103 
104             case '-':
105                 // 左对齐
106                 flags |= LEFT;
107                 ++fmt;
108                 break;
109             case '+':
110                 //在正数前面显示加号
111                 flags |= PLUS;
112                 ++fmt;
113                 break;
114             case ' ':
115                 flags |= SPACE;
116                 ++fmt;
117                 break;
118             case '#':
119                 //在八进制数前面显示 '0o',在十六进制数前面显示 '0x' 或 '0X'
120                 flags |= SPECIAL;
121                 ++fmt;
122                 break;
123             case '0':
124                 //显示的数字之前填充‘0’来取代空格
125                 flags |= PAD_ZERO;
126                 ++fmt;
127                 break;
128             default:
129                 flag_tmp = false;
130                 break;
131             }
132         }
133         if (flag_break)
134             break;
135 
136         //获取区域宽度
137         field_width = -1;
138         if (*fmt == '*')
139         {
140             field_width = va_arg(args, int);
141             ++fmt;
142         }
143         else if (is_digit(*fmt))
144         {
145             field_width = skip_and_atoi(&fmt);
146             if (field_width < 0)
147             {
148                 field_width = -field_width;
149                 flags |= LEFT;
150             }
151         }
152 
153         //获取小数精度
154         precision = -1;
155         if (*fmt == '.')
156         {
157             ++fmt;
158             if (*fmt == '*')
159             {
160                 precision = va_arg(args, int);
161                 ++fmt;
162             }
163             else if (is_digit(*fmt))
164             {
165                 precision = skip_and_atoi(&fmt);
166             }
167         }
168 
169         //获取要显示的数据的类型
170         if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || *fmt == 'Z')
171         {
172             qualifier = *fmt;
173             ++fmt;
174         }
175         //为了支持lld
176         if (qualifier == 'l' && *fmt == 'l', *(fmt + 1) == 'd')
177             ++fmt;
178 
179         //转化成字符串
180         long long *ip;
181         switch (*fmt)
182         {
183         //输出 %
184         case '%':
185             *str++ = '%';
186 
187             break;
188         // 显示一个字符
189         case 'c':
190             //靠右对齐
191             if (!(flags & LEFT))
192             {
193                 while (--field_width > 0)
194                 {
195                     *str = ' ';
196                     ++str;
197                 }
198             }
199 
200             *str++ = (unsigned char)va_arg(args, int);
201 
202             while (--field_width > 0)
203             {
204                 *str = ' ';
205                 ++str;
206             }
207 
208             break;
209 
210         //显示一个字符串
211         case 's':
212             s = va_arg(args, char *);
213             if (!s)
214                 s = &__end_zero_char;
215             len = strlen(s);
216             if (precision < 0)
217             {
218                 //未指定精度
219                 precision = len;
220             }
221 
222             else if (len > precision)
223             {
224                 len = precision;
225             }
226 
227             //靠右对齐
228             if (!(flags & LEFT))
229                 while (len < field_width--)
230                 {
231                     *str = ' ';
232                     ++str;
233                 }
234 
235             for (int i = 0; i < len; i++)
236             {
237                 *str = *s;
238                 ++s;
239                 ++str;
240             }
241 
242             while (len < field_width--)
243             {
244                 *str = ' ';
245                 ++str;
246             }
247 
248             break;
249         //以八进制显示字符串
250         case 'o':
251             flags |= SMALL;
252         case 'O':
253             flags |= SPECIAL;
254             if (qualifier == 'l')
255                 str = write_num(str, va_arg(args, long long), 8, field_width, precision, flags);
256             else
257                 str = write_num(str, va_arg(args, int), 8, field_width, precision, flags);
258             break;
259 
260         //打印指针指向的地址
261         case 'p':
262             if (field_width == 0)
263             {
264                 field_width = 2 * sizeof(void *);
265                 flags |= PAD_ZERO;
266             }
267 
268             str = write_num(str, (unsigned long)va_arg(args, void *), 16, field_width, precision, flags);
269 
270             break;
271 
272         //打印十六进制
273         case 'x':
274             flags |= SMALL;
275         case 'X':
276             // flags |= SPECIAL;
277             if (qualifier == 'l')
278                 str = write_num(str, va_arg(args, ll), 16, field_width, precision, flags);
279             else
280                 str = write_num(str, va_arg(args, int), 16, field_width, precision, flags);
281             break;
282 
283         //打印十进制有符号整数
284         case 'i':
285         case 'd':
286 
287             flags |= SIGN;
288             if (qualifier == 'l')
289                 str = write_num(str, va_arg(args, long long), 10, field_width, precision, flags);
290             else
291                 str = write_num(str, va_arg(args, int), 10, field_width, precision, flags);
292             break;
293 
294         //打印十进制无符号整数
295         case 'u':
296             if (qualifier == 'l')
297                 str = write_num(str, va_arg(args, unsigned long long), 10, field_width, precision, flags);
298             else
299                 str = write_num(str, va_arg(args, unsigned int), 10, field_width, precision, flags);
300             break;
301 
302         //输出有效字符数量到*ip对应的变量
303         case 'n':
304 
305             if (qualifier == 'l')
306                 ip = va_arg(args, long long *);
307             else
308                 ip = (ll *)va_arg(args, int *);
309 
310             *ip = str - buf;
311             break;
312         case 'f':
313             // 默认精度为3
314             // printk("1111\n");
315             // va_arg(args, double);
316             // printk("222\n");
317 
318             if (precision < 0)
319                 precision = 3;
320 
321             str = write_float_point_num(str, va_arg(args, double), field_width, precision, flags);
322 
323             break;
324 
325         //对于不识别的控制符,直接输出
326         default:
327             *str++ = '%';
328             if (*fmt)
329                 *str++ = *fmt;
330             else
331                 --fmt;
332             break;
333         }
334     }
335     //实现vsnprintf 的功能
336     if (buf_size > 0)
337     {
338         if (str < end)
339         {
340             *str = '\0';
341         }
342         else
343         {
344             *(end-1) = '\0';
345         }
346         return buf_size;
347     }
348     else
349     {
350         *str = '\0';
351     }
352 
353     //返回缓冲区已有字符串的长度。
354     return str - buf;
355 }
356 
357 /**
358  * 将字符串按照fmt和args中的内容进行格式化,然后保存到buf中
359  * @param buf 结果缓冲区
360  * @param fmt 格式化字符串
361  * @param args 内容
362  * @return 最终字符串的长度
363  */
364 int vsprintf(char *buf, const char *fmt, va_list args)
365 {
366     return __do_vsprintf(buf, fmt, -1, args);
367 }
368 
369 /**
370  * @brief 将字符串按照fmt和args中的内容进行格式化,截取字符串前buf_size-1,保存到buf中
371  *
372  * @param buf 结果缓冲区,大小为buf_size
373  * @param fmt 格式化字符串
374  * @param buf_size 缓冲区长度
375  * @param args 内容
376  * @return 最终字符串的长度
377  */
378 int vsnprintf(char *buf, const char *fmt, int buf_size, va_list args)
379 {
380     return __do_vsprintf(buf, fmt, buf_size, args);
381 }
382 
383 static char *write_num(char *str, ul num, int base, int field_width, int precision, int flags)
384 {
385     /**
386      * @brief 将数字按照指定的要求转换成对应的字符串
387      *
388      * @param str 要返回的字符串
389      * @param num 要打印的数值
390      * @param base 基数
391      * @param field_width 区域宽度
392      * @param precision 精度
393      * @param flags 标志位
394      */
395 
396     // 首先判断是否支持该进制
397     if (base < 2 || base > 36)
398         return 0;
399     char pad, sign, tmp_num[100];
400 
401     const char *digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
402     // 显示小写字母
403     if (flags & SMALL)
404         digits = "0123456789abcdefghijklmnopqrstuvwxyz";
405 
406     if (flags & LEFT)
407         flags &= ~PAD_ZERO;
408     // 设置填充元素
409     pad = (flags & PAD_ZERO) ? '0' : ' ';
410 
411     sign = 0;
412 
413     if (flags & SIGN)
414     {
415         int64_t signed_num = (int64_t)num;
416         if (signed_num < 0)
417         {
418             sign = '-';
419             num = -signed_num;
420         }
421         else
422             num = signed_num;
423     }
424     else
425     {
426         // 设置符号
427         sign = (flags & PLUS) ? '+' : ((flags & SPACE) ? ' ' : 0);
428     }
429 
430     // sign占用了一个宽度
431     if (sign)
432         --field_width;
433 
434     if (flags & SPECIAL)
435         if (base == 16) // 0x占用2个位置
436             field_width -= 2;
437         else if (base == 8) // O占用一个位置
438             --field_width;
439 
440     int js_num = 0; // 临时数字字符串tmp_num的长度
441 
442     if (num == 0)
443         tmp_num[js_num++] = '0';
444     else
445     {
446         num = ABS(num);
447         //进制转换
448         while (num > 0)
449         {
450             tmp_num[js_num++] = digits[num % base]; // 注意这里,输出的数字,是小端对齐的。低位存低位
451             num /= base;
452         }
453     }
454 
455     if (js_num > precision)
456         precision = js_num;
457 
458     field_width -= precision;
459 
460     // 靠右对齐
461     if (!(flags & (LEFT + PAD_ZERO)))
462         while (field_width-- > 0)
463             *str++ = ' ';
464 
465     if (sign)
466         *str++ = sign;
467     if (flags & SPECIAL)
468         if (base == 16)
469         {
470             *str++ = '0';
471             *str++ = digits[33];
472         }
473         else if (base == 8)
474             *str++ = digits[24]; //注意这里是英文字母O或者o
475     if (!(flags & LEFT))
476         while (field_width-- > 0)
477             *str++ = pad;
478     while (js_num < precision)
479     {
480         --precision;
481         *str++ = '0';
482     }
483 
484     while (js_num-- > 0)
485         *str++ = tmp_num[js_num];
486 
487     while (field_width-- > 0)
488         *str++ = ' ';
489 
490     return str;
491 }
492 
493 static char *write_float_point_num(char *str, double num, int field_width, int precision, int flags)
494 {
495     /**
496      * @brief 将浮点数按照指定的要求转换成对应的字符串
497      *
498      * @param str 要返回的字符串
499      * @param num 要打印的数值
500      * @param field_width 区域宽度
501      * @param precision 精度
502      * @param flags 标志位
503      */
504 
505     char pad, sign, tmp_num_z[100], tmp_num_d[350];
506 
507     const char *digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
508     // 显示小写字母
509     if (flags & SMALL)
510         digits = "0123456789abcdefghijklmnopqrstuvwxyz";
511 
512     // 设置填充元素
513     pad = (flags & PAD_ZERO) ? '0' : ' ';
514     sign = 0;
515     if (flags & SIGN && num < 0)
516     {
517         sign = '-';
518         num = -num;
519     }
520     else
521     {
522         // 设置符号
523         sign = (flags & PLUS) ? '+' : ((flags & SPACE) ? ' ' : 0);
524     }
525 
526     // sign占用了一个宽度
527     if (sign)
528         --field_width;
529 
530     int js_num_z = 0, js_num_d = 0;                                                     // 临时数字字符串tmp_num_z tmp_num_d的长度
531     uint64_t num_z = (uint64_t)(num);                                                   // 获取整数部分
532     uint64_t num_decimal = (uint64_t)(round(1.0 * (num - num_z) * pow(10, precision))); // 获取小数部分
533 
534     if (num == 0 || num_z == 0)
535         tmp_num_z[js_num_z++] = '0';
536     else
537     {
538         //存储整数部分
539         while (num_z > 0)
540         {
541             tmp_num_z[js_num_z++] = digits[num_z % 10]; // 注意这里,输出的数字,是小端对齐的。低位存低位
542             num_z /= 10;
543         }
544     }
545 
546     while (num_decimal > 0)
547     {
548         tmp_num_d[js_num_d++] = digits[num_decimal % 10];
549         num_decimal /= 10;
550     }
551 
552     field_width -= (precision + 1 + js_num_z);
553 
554     // 靠右对齐
555     if (!(flags & LEFT))
556         while (field_width-- > 0)
557             *str++ = pad;
558 
559     if (sign)
560         *str++ = sign;
561 
562     // 输出整数部分
563     while (js_num_z > 0)
564     {
565         *str++ = tmp_num_z[js_num_z - 1];
566         --js_num_z;
567     }
568     *str++ = '.';
569 
570     // 输出小数部分
571     int total_dec_count = js_num_d;
572     for (int i = 0; i < precision && js_num_d-- > 0; ++i)
573         *str++ = tmp_num_d[js_num_d];
574 
575     while (total_dec_count < precision)
576     {
577         ++total_dec_count;
578         *str++ = '0';
579     }
580 
581     while (field_width-- > 0)
582         *str++ = ' ';
583 
584     return str;
585 }
586 
587 /**
588  * @brief 格式化打印字符串
589  *
590  * @param FRcolor 前景色
591  * @param BKcolor 背景色
592  * @param ... 格式化字符串
593  */
594 int printk_color(unsigned int FRcolor, unsigned int BKcolor, const char *fmt, ...)
595 {
596     uint64_t rflags;
597     spin_lock_irqsave(&__printk_lock, rflags);
598     va_list args;
599     va_start(args, fmt);
600     char buf[4096]; // vsprintf()的缓冲区
601     int len = vsprintf(buf, fmt, args);
602 
603     va_end(args);
604     unsigned char current;
605 
606     int i; // 总共输出的字符数
607     for (i = 0; i < len; ++i)
608     {
609         current = *(buf + i);
610         // 输出
611         textui_putchar(current, FRcolor, BKcolor);
612     }
613     spin_unlock_irqrestore(&__printk_lock, rflags);
614     return i;
615 }
616 
617 int sprintk(char *buf, const char *fmt, ...)
618 {
619     int count = 0;
620     va_list args;
621 
622     va_start(args, fmt);
623     count = vsprintf(buf, fmt, args);
624     va_end(args);
625 
626     return count;
627 }
628