xref: /aosp_15_r20/external/mesa3d/src/util/u_printf.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 //
2 // Copyright 2020 Serge Martin
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a
5 // copy of this software and associated documentation files (the "Software"),
6 // to deal in the Software without restriction, including without limitation
7 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 // and/or sell copies of the Software, and to permit persons to whom the
9 // Software is furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 // OTHER DEALINGS IN THE SOFTWARE.
21 //
22 // Extract from Serge's printf clover code by airlied.
23 
24 #include <assert.h>
25 #include <stdarg.h>
26 #include <stdbool.h>
27 #include <stdlib.h>
28 #include <string.h>
29 
30 #include "macros.h"
31 #include "strndup.h"
32 #include "u_math.h"
33 #include "u_printf.h"
34 
35 /* Some versions of MinGW are missing _vscprintf's declaration, although they
36  * still provide the symbol in the import library. */
37 #ifdef __MINGW32__
38 _CRTIMP int _vscprintf(const char *format, va_list argptr);
39 #endif
40 
41 const char*
util_printf_prev_tok(const char * str)42 util_printf_prev_tok(const char *str)
43 {
44    while (*str != '%')
45       str--;
46    return str;
47 }
48 
util_printf_next_spec_pos(const char * str,size_t pos)49 size_t util_printf_next_spec_pos(const char *str, size_t pos)
50 {
51    if (str == NULL)
52       return -1;
53 
54    const char *str_found = str + pos;
55    do {
56       str_found = strchr(str_found, '%');
57       if (str_found == NULL)
58          return -1;
59 
60       ++str_found;
61       if (*str_found == '%') {
62          ++str_found;
63          continue;
64       }
65 
66       char *spec_pos = strpbrk(str_found, "cdieEfFgGaAosuxXp%");
67       if (spec_pos == NULL) {
68          return -1;
69       } else if (*spec_pos == '%') {
70          str_found = spec_pos;
71       } else {
72          return spec_pos - str;
73       }
74    } while (1);
75 }
76 
u_printf_length(const char * fmt,va_list untouched_args)77 size_t u_printf_length(const char *fmt, va_list untouched_args)
78 {
79    int size;
80    char junk;
81 
82    /* Make a copy of the va_list so the original caller can still use it */
83    va_list args;
84    va_copy(args, untouched_args);
85 
86 #ifdef _WIN32
87    /* We need to use _vcsprintf to calculate the size as vsnprintf returns -1
88     * if the number of characters to write is greater than count.
89     */
90    size = _vscprintf(fmt, args);
91    (void)junk;
92 #else
93    size = vsnprintf(&junk, 1, fmt, args);
94 #endif
95    assert(size >= 0);
96 
97    va_end(args);
98 
99    return size;
100 }
101 
102 /**
103  * Used to print plain format strings without arguments as some post-processing
104  * will be required:
105  *  - %% needs to be printed as %
106  */
107 static void
u_printf_plain_sized(FILE * out,const char * format,size_t len)108 u_printf_plain_sized(FILE *out, const char* format, size_t len)
109 {
110    bool found = false;
111    size_t last = 0;
112 
113    for (size_t i = 0; i < len; i++) {
114       if (!found && format[i] == '%') {
115          found = true;
116       } else if (found && format[i] == '%') {
117          /* print one character less so we only print a single % */
118          fwrite(format + last, i - last - 1, 1, out);
119 
120          last = i;
121          found = false;
122       } else {
123          /* We should never end up here with an actual format token */
124          assert(!found);
125          found = false;
126       }
127    }
128 
129    fwrite(format + last, len - last, 1, out);
130 }
131 
132 static void
u_printf_plain(FILE * out,const char * format)133 u_printf_plain(FILE *out, const char* format)
134 {
135    u_printf_plain_sized(out, format, strlen(format));
136 }
137 
138 static void
u_printf_impl(FILE * out,const char * buffer,size_t buffer_size,const u_printf_info * info,const u_printf_info ** info_ptr,unsigned info_size)139 u_printf_impl(FILE *out, const char *buffer, size_t buffer_size,
140               const u_printf_info *info,
141               const u_printf_info **info_ptr,
142               unsigned info_size)
143 {
144    for (size_t buf_pos = 0; buf_pos < buffer_size;) {
145       uint32_t fmt_idx = *(uint32_t*)&buffer[buf_pos];
146 
147       /* the idx is 1 based */
148       assert(fmt_idx > 0);
149       fmt_idx -= 1;
150 
151       /* The API allows more arguments than the format uses */
152       if (fmt_idx >= info_size)
153          return;
154 
155       const u_printf_info *fmt = info != NULL ?
156          &info[fmt_idx] : info_ptr[fmt_idx];
157       const char *format = fmt->strings;
158       buf_pos += sizeof(fmt_idx);
159 
160       if (!fmt->num_args) {
161          u_printf_plain(out, format);
162          continue;
163       }
164 
165       for (int i = 0; i < fmt->num_args; i++) {
166          int arg_size = fmt->arg_sizes[i];
167          size_t spec_pos = util_printf_next_spec_pos(format, 0);
168 
169          /* If we hit an unused argument we skip all remaining ones */
170          if (spec_pos == -1)
171             break;
172 
173          const char *token = util_printf_prev_tok(&format[spec_pos]);
174          const char *next_format = &format[spec_pos + 1];
175 
176          /* print the part before the format token */
177          if (token != format)
178             u_printf_plain_sized(out, format, token - format);
179 
180          char *print_str = strndup(token, next_format - token);
181          /* rebase spec_pos so we can use it with print_str */
182          spec_pos += format - token;
183 
184          /* print the formatted part */
185          if (print_str[spec_pos] == 's') {
186             uint64_t idx;
187             memcpy(&idx, &buffer[buf_pos], 8);
188             fprintf(out, print_str, &fmt->strings[idx]);
189 
190          /* Never pass a 'n' spec to the host printf */
191          } else if (print_str[spec_pos] != 'n') {
192             char *vec_pos = strchr(print_str, 'v');
193             char *mod_pos = strpbrk(print_str, "hl");
194 
195             int component_count = 1;
196             if (vec_pos != NULL) {
197                /* non vector part of the format */
198                size_t base = mod_pos ? mod_pos - print_str : spec_pos;
199                size_t l = base - (vec_pos - print_str) - 1;
200                char *vec = strndup(&vec_pos[1], l);
201                component_count = atoi(vec);
202                free(vec);
203 
204                /* remove the vector and precision stuff */
205                memmove(&print_str[vec_pos - print_str], &print_str[spec_pos], 2);
206             }
207 
208             /* in fact vec3 are vec4 */
209             int men_components = component_count == 3 ? 4 : component_count;
210             size_t elmt_size = arg_size / men_components;
211             bool is_float = strpbrk(print_str, "fFeEgGaA") != NULL;
212 
213             for (int i = 0; i < component_count; i++) {
214                size_t elmt_buf_pos = buf_pos + i * elmt_size;
215                switch (elmt_size) {
216                case 1: {
217                   uint8_t v;
218                   memcpy(&v, &buffer[elmt_buf_pos], elmt_size);
219                   fprintf(out, print_str, v);
220                   break;
221                }
222                case 2: {
223                   uint16_t v;
224                   memcpy(&v, &buffer[elmt_buf_pos], elmt_size);
225                   fprintf(out, print_str, v);
226                   break;
227                }
228                case 4: {
229                   if (is_float) {
230                      float v;
231                      memcpy(&v, &buffer[elmt_buf_pos], elmt_size);
232                      fprintf(out, print_str, v);
233                   } else {
234                      uint32_t v;
235                      memcpy(&v, &buffer[elmt_buf_pos], elmt_size);
236                      fprintf(out, print_str, v);
237                   }
238                   break;
239                }
240                case 8: {
241                   if (is_float) {
242                      double v;
243                      memcpy(&v, &buffer[elmt_buf_pos], elmt_size);
244                      fprintf(out, print_str, v);
245                   } else {
246                      uint64_t v;
247                      memcpy(&v, &buffer[elmt_buf_pos], elmt_size);
248                      fprintf(out, print_str, v);
249                   }
250                   break;
251                }
252                default:
253                   assert(false);
254                   break;
255                }
256 
257                if (i < component_count - 1)
258                   fprintf(out, ",");
259             }
260          }
261 
262          /* rebase format */
263          format = next_format;
264          free(print_str);
265 
266          buf_pos += arg_size;
267          buf_pos = align_uintptr(buf_pos, 4);
268       }
269 
270       /* print remaining */
271       u_printf_plain(out, format);
272    }
273 }
274 
u_printf(FILE * out,const char * buffer,size_t buffer_size,const u_printf_info * info,unsigned info_size)275 void u_printf(FILE *out, const char *buffer, size_t buffer_size,
276               const u_printf_info *info, unsigned info_size)
277 {
278    u_printf_impl(out, buffer, buffer_size, info, NULL, info_size);
279 }
280 
u_printf_ptr(FILE * out,const char * buffer,size_t buffer_size,const u_printf_info ** info,unsigned info_size)281 void u_printf_ptr(FILE *out, const char *buffer, size_t buffer_size,
282                   const u_printf_info **info, unsigned info_size)
283 {
284    u_printf_impl(out, buffer, buffer_size, NULL, info, info_size);
285 }
286