]> git.sesse.net Git - bcachefs-tools-debian/blob - c_src/libbcachefs/printbuf.c
accf246c32330919869bccff32a1ecfcc6d97856
[bcachefs-tools-debian] / c_src / libbcachefs / printbuf.c
1 // SPDX-License-Identifier: LGPL-2.1+
2 /* Copyright (C) 2022 Kent Overstreet */
3
4 #include <linux/bitmap.h>
5 #include <linux/err.h>
6 #include <linux/export.h>
7 #include <linux/kernel.h>
8 #include <linux/slab.h>
9 #include <linux/string_helpers.h>
10
11 #include "printbuf.h"
12
13 static inline unsigned printbuf_linelen(struct printbuf *buf)
14 {
15         return buf->pos - buf->last_newline;
16 }
17
18 int bch2_printbuf_make_room(struct printbuf *out, unsigned extra)
19 {
20         unsigned new_size;
21         char *buf;
22
23         if (!out->heap_allocated)
24                 return 0;
25
26         /* Reserved space for terminating nul: */
27         extra += 1;
28
29         if (out->pos + extra < out->size)
30                 return 0;
31
32         new_size = roundup_pow_of_two(out->size + extra);
33
34         /*
35          * Note: output buffer must be freeable with kfree(), it's not required
36          * that the user use printbuf_exit().
37          */
38         buf = krealloc(out->buf, new_size, !out->atomic ? GFP_KERNEL : GFP_NOWAIT);
39
40         if (!buf) {
41                 out->allocation_failure = true;
42                 return -ENOMEM;
43         }
44
45         out->buf        = buf;
46         out->size       = new_size;
47         return 0;
48 }
49
50 void bch2_prt_vprintf(struct printbuf *out, const char *fmt, va_list args)
51 {
52         int len;
53
54         do {
55                 va_list args2;
56
57                 va_copy(args2, args);
58                 len = vsnprintf(out->buf + out->pos, printbuf_remaining(out), fmt, args2);
59         } while (len + 1 >= printbuf_remaining(out) &&
60                  !bch2_printbuf_make_room(out, len + 1));
61
62         len = min_t(size_t, len,
63                   printbuf_remaining(out) ? printbuf_remaining(out) - 1 : 0);
64         out->pos += len;
65 }
66
67 void bch2_prt_printf(struct printbuf *out, const char *fmt, ...)
68 {
69         va_list args;
70         int len;
71
72         do {
73                 va_start(args, fmt);
74                 len = vsnprintf(out->buf + out->pos, printbuf_remaining(out), fmt, args);
75                 va_end(args);
76         } while (len + 1 >= printbuf_remaining(out) &&
77                  !bch2_printbuf_make_room(out, len + 1));
78
79         len = min_t(size_t, len,
80                   printbuf_remaining(out) ? printbuf_remaining(out) - 1 : 0);
81         out->pos += len;
82 }
83
84 /**
85  * bch2_printbuf_str() - returns printbuf's buf as a C string, guaranteed to be
86  * null terminated
87  * @buf:        printbuf to terminate
88  * Returns:     Printbuf contents, as a nul terminated C string
89  */
90 const char *bch2_printbuf_str(const struct printbuf *buf)
91 {
92         /*
93          * If we've written to a printbuf then it's guaranteed to be a null
94          * terminated string - but if we haven't, then we might not have
95          * allocated a buffer at all:
96          */
97         return buf->pos
98                 ? buf->buf
99                 : "";
100 }
101
102 /**
103  * bch2_printbuf_exit() - exit a printbuf, freeing memory it owns and poisoning it
104  * against accidental use.
105  * @buf:        printbuf to exit
106  */
107 void bch2_printbuf_exit(struct printbuf *buf)
108 {
109         if (buf->heap_allocated) {
110                 kfree(buf->buf);
111                 buf->buf = ERR_PTR(-EINTR); /* poison value */
112         }
113 }
114
115 void bch2_printbuf_tabstops_reset(struct printbuf *buf)
116 {
117         buf->nr_tabstops = 0;
118 }
119
120 void bch2_printbuf_tabstop_pop(struct printbuf *buf)
121 {
122         if (buf->nr_tabstops)
123                 --buf->nr_tabstops;
124 }
125
126 /*
127  * bch2_printbuf_tabstop_set() - add a tabstop, n spaces from the previous tabstop
128  *
129  * @buf: printbuf to control
130  * @spaces: number of spaces from previous tabpstop
131  *
132  * In the future this function may allocate memory if setting more than
133  * PRINTBUF_INLINE_TABSTOPS or setting tabstops more than 255 spaces from start
134  * of line.
135  */
136 int bch2_printbuf_tabstop_push(struct printbuf *buf, unsigned spaces)
137 {
138         unsigned prev_tabstop = buf->nr_tabstops
139                 ? buf->_tabstops[buf->nr_tabstops - 1]
140                 : 0;
141
142         if (WARN_ON(buf->nr_tabstops >= ARRAY_SIZE(buf->_tabstops)))
143                 return -EINVAL;
144
145         buf->_tabstops[buf->nr_tabstops++] = prev_tabstop + spaces;
146         buf->has_indent_or_tabstops = true;
147         return 0;
148 }
149
150 /**
151  * bch2_printbuf_indent_add() - add to the current indent level
152  *
153  * @buf: printbuf to control
154  * @spaces: number of spaces to add to the current indent level
155  *
156  * Subsequent lines, and the current line if the output position is at the start
157  * of the current line, will be indented by @spaces more spaces.
158  */
159 void bch2_printbuf_indent_add(struct printbuf *buf, unsigned spaces)
160 {
161         if (WARN_ON_ONCE(buf->indent + spaces < buf->indent))
162                 spaces = 0;
163
164         buf->indent += spaces;
165         prt_chars(buf, ' ', spaces);
166
167         buf->has_indent_or_tabstops = true;
168 }
169
170 /**
171  * bch2_printbuf_indent_sub() - subtract from the current indent level
172  *
173  * @buf: printbuf to control
174  * @spaces: number of spaces to subtract from the current indent level
175  *
176  * Subsequent lines, and the current line if the output position is at the start
177  * of the current line, will be indented by @spaces less spaces.
178  */
179 void bch2_printbuf_indent_sub(struct printbuf *buf, unsigned spaces)
180 {
181         if (WARN_ON_ONCE(spaces > buf->indent))
182                 spaces = buf->indent;
183
184         if (buf->last_newline + buf->indent == buf->pos) {
185                 buf->pos -= spaces;
186                 printbuf_nul_terminate(buf);
187         }
188         buf->indent -= spaces;
189
190         if (!buf->indent && !buf->nr_tabstops)
191                 buf->has_indent_or_tabstops = false;
192 }
193
194 void bch2_prt_newline(struct printbuf *buf)
195 {
196         unsigned i;
197
198         bch2_printbuf_make_room(buf, 1 + buf->indent);
199
200         __prt_char(buf, '\n');
201
202         buf->last_newline       = buf->pos;
203
204         for (i = 0; i < buf->indent; i++)
205                 __prt_char(buf, ' ');
206
207         printbuf_nul_terminate(buf);
208
209         buf->last_field         = buf->pos;
210         buf->cur_tabstop        = 0;
211 }
212
213 /*
214  * Returns spaces from start of line, if set, or 0 if unset:
215  */
216 static inline unsigned cur_tabstop(struct printbuf *buf)
217 {
218         return buf->cur_tabstop < buf->nr_tabstops
219                 ? buf->_tabstops[buf->cur_tabstop]
220                 : 0;
221 }
222
223 static void __prt_tab(struct printbuf *out)
224 {
225         int spaces = max_t(int, 0, cur_tabstop(out) - printbuf_linelen(out));
226
227         prt_chars(out, ' ', spaces);
228
229         out->last_field = out->pos;
230         out->cur_tabstop++;
231 }
232
233 /**
234  * bch2_prt_tab() - Advance printbuf to the next tabstop
235  * @out:        printbuf to control
236  *
237  * Advance output to the next tabstop by printing spaces.
238  */
239 void bch2_prt_tab(struct printbuf *out)
240 {
241         if (WARN_ON(!cur_tabstop(out)))
242                 return;
243
244         __prt_tab(out);
245 }
246
247 static void __prt_tab_rjust(struct printbuf *buf)
248 {
249         unsigned move = buf->pos - buf->last_field;
250         int pad = (int) cur_tabstop(buf) - (int) printbuf_linelen(buf);
251
252         if (pad > 0) {
253                 bch2_printbuf_make_room(buf, pad);
254
255                 if (buf->last_field + pad < buf->size)
256                         memmove(buf->buf + buf->last_field + pad,
257                                 buf->buf + buf->last_field,
258                                 min(move, buf->size - 1 - buf->last_field - pad));
259
260                 if (buf->last_field < buf->size)
261                         memset(buf->buf + buf->last_field, ' ',
262                                min((unsigned) pad, buf->size - buf->last_field));
263
264                 buf->pos += pad;
265                 printbuf_nul_terminate(buf);
266         }
267
268         buf->last_field = buf->pos;
269         buf->cur_tabstop++;
270 }
271
272 /**
273  * bch2_prt_tab_rjust - Advance printbuf to the next tabstop, right justifying
274  * previous output
275  *
276  * @buf: printbuf to control
277  *
278  * Advance output to the next tabstop by inserting spaces immediately after the
279  * previous tabstop, right justifying previously outputted text.
280  */
281 void bch2_prt_tab_rjust(struct printbuf *buf)
282 {
283         if (WARN_ON(!cur_tabstop(buf)))
284                 return;
285
286         __prt_tab_rjust(buf);
287 }
288
289 /**
290  * bch2_prt_bytes_indented() - Print an array of chars, handling embedded control characters
291  *
292  * @out:        output printbuf
293  * @str:        string to print
294  * @count:      number of bytes to print
295  *
296  * The following contol characters are handled as so:
297  *   \n: prt_newline    newline that obeys current indent level
298  *   \t: prt_tab        advance to next tabstop
299  *   \r: prt_tab_rjust  advance to next tabstop, with right justification
300  */
301 void bch2_prt_bytes_indented(struct printbuf *out, const char *str, unsigned count)
302 {
303         const char *unprinted_start = str;
304         const char *end = str + count;
305
306         if (!out->has_indent_or_tabstops || out->suppress_indent_tabstop_handling) {
307                 prt_bytes(out, str, count);
308                 return;
309         }
310
311         while (str != end) {
312                 switch (*str) {
313                 case '\n':
314                         prt_bytes(out, unprinted_start, str - unprinted_start);
315                         unprinted_start = str + 1;
316                         bch2_prt_newline(out);
317                         break;
318                 case '\t':
319                         if (likely(cur_tabstop(out))) {
320                                 prt_bytes(out, unprinted_start, str - unprinted_start);
321                                 unprinted_start = str + 1;
322                                 __prt_tab(out);
323                         }
324                         break;
325                 case '\r':
326                         if (likely(cur_tabstop(out))) {
327                                 prt_bytes(out, unprinted_start, str - unprinted_start);
328                                 unprinted_start = str + 1;
329                                 __prt_tab_rjust(out);
330                         }
331                         break;
332                 }
333
334                 str++;
335         }
336
337         prt_bytes(out, unprinted_start, str - unprinted_start);
338 }
339
340 /**
341  * bch2_prt_human_readable_u64() - Print out a u64 in human readable units
342  * @out:        output printbuf
343  * @v:          integer to print
344  *
345  * Units of 2^10 (default) or 10^3 are controlled via @out->si_units
346  */
347 void bch2_prt_human_readable_u64(struct printbuf *out, u64 v)
348 {
349         bch2_printbuf_make_room(out, 10);
350         out->pos += string_get_size(v, 1, !out->si_units,
351                                     out->buf + out->pos,
352                                     printbuf_remaining_size(out));
353 }
354
355 /**
356  * bch2_prt_human_readable_s64() - Print out a s64 in human readable units
357  * @out:        output printbuf
358  * @v:          integer to print
359  *
360  * Units of 2^10 (default) or 10^3 are controlled via @out->si_units
361  */
362 void bch2_prt_human_readable_s64(struct printbuf *out, s64 v)
363 {
364         if (v < 0)
365                 prt_char(out, '-');
366         bch2_prt_human_readable_u64(out, abs(v));
367 }
368
369 /**
370  * bch2_prt_units_u64() - Print out a u64 according to printbuf unit options
371  * @out:        output printbuf
372  * @v:          integer to print
373  *
374  * Units are either raw (default), or human reabable units (controlled via
375  * @buf->human_readable_units)
376  */
377 void bch2_prt_units_u64(struct printbuf *out, u64 v)
378 {
379         if (out->human_readable_units)
380                 bch2_prt_human_readable_u64(out, v);
381         else
382                 bch2_prt_printf(out, "%llu", v);
383 }
384
385 /**
386  * bch2_prt_units_s64() - Print out a s64 according to printbuf unit options
387  * @out:        output printbuf
388  * @v:          integer to print
389  *
390  * Units are either raw (default), or human reabable units (controlled via
391  * @buf->human_readable_units)
392  */
393 void bch2_prt_units_s64(struct printbuf *out, s64 v)
394 {
395         if (v < 0)
396                 prt_char(out, '-');
397         bch2_prt_units_u64(out, abs(v));
398 }
399
400 void bch2_prt_string_option(struct printbuf *out,
401                             const char * const list[],
402                             size_t selected)
403 {
404         size_t i;
405
406         for (i = 0; list[i]; i++)
407                 bch2_prt_printf(out, i == selected ? "[%s] " : "%s ", list[i]);
408 }
409
410 void bch2_prt_bitflags(struct printbuf *out,
411                        const char * const list[], u64 flags)
412 {
413         unsigned bit, nr = 0;
414         bool first = true;
415
416         while (list[nr])
417                 nr++;
418
419         while (flags && (bit = __ffs64(flags)) < nr) {
420                 if (!first)
421                         bch2_prt_printf(out, ",");
422                 first = false;
423                 bch2_prt_printf(out, "%s", list[bit]);
424                 flags ^= BIT_ULL(bit);
425         }
426 }
427
428 void bch2_prt_bitflags_vector(struct printbuf *out,
429                               const char * const list[],
430                               unsigned long *v, unsigned nr)
431 {
432         bool first = true;
433         unsigned i;
434
435         for (i = 0; i < nr; i++)
436                 if (!list[i]) {
437                         nr = i - 1;
438                         break;
439                 }
440
441         for_each_set_bit(i, v, nr) {
442                 if (!first)
443                         bch2_prt_printf(out, ",");
444                 first = false;
445                 bch2_prt_printf(out, "%s", list[i]);
446         }
447 }