1 // SPDX-License-Identifier: LGPL-2.1+
2 /* Copyright (C) 2022 Kent Overstreet */
4 #include <linux/bitmap.h>
6 #include <linux/export.h>
7 #include <linux/kernel.h>
8 #include <linux/slab.h>
9 #include <linux/string_helpers.h>
13 static inline unsigned __printbuf_linelen(struct printbuf
*buf
, unsigned pos
)
15 return pos
- buf
->last_newline
;
18 static inline unsigned printbuf_linelen(struct printbuf
*buf
)
20 return __printbuf_linelen(buf
, buf
->pos
);
24 * Returns spaces from start of line, if set, or 0 if unset:
26 static inline unsigned cur_tabstop(struct printbuf
*buf
)
28 return buf
->cur_tabstop
< buf
->nr_tabstops
29 ? buf
->_tabstops
[buf
->cur_tabstop
]
33 int bch2_printbuf_make_room(struct printbuf
*out
, unsigned extra
)
35 /* Reserved space for terminating nul: */
38 if (out
->pos
+ extra
<= out
->size
)
41 if (!out
->heap_allocated
) {
46 unsigned new_size
= roundup_pow_of_two(out
->size
+ extra
);
49 if (new_size
> PAGE_SIZE
<< MAX_PAGE_ORDER
) {
50 out
->allocation_failure
= true;
56 * Note: output buffer must be freeable with kfree(), it's not required
57 * that the user use printbuf_exit().
59 char *buf
= krealloc(out
->buf
, new_size
, !out
->atomic
? GFP_KERNEL
: GFP_NOWAIT
);
62 out
->allocation_failure
= true;
72 static void printbuf_advance_pos(struct printbuf
*out
, unsigned len
)
74 out
->pos
+= min(len
, printbuf_remaining(out
));
77 static void printbuf_insert_spaces(struct printbuf
*out
, unsigned pos
, unsigned nr
)
79 unsigned move
= out
->pos
- pos
;
81 bch2_printbuf_make_room(out
, nr
);
83 if (pos
+ nr
< out
->size
)
84 memmove(out
->buf
+ pos
+ nr
,
86 min(move
, out
->size
- 1 - pos
- nr
));
89 memset(out
->buf
+ pos
, ' ', min(nr
, out
->size
- pos
));
91 printbuf_advance_pos(out
, nr
);
92 printbuf_nul_terminate_reserved(out
);
95 static void __printbuf_do_indent(struct printbuf
*out
, unsigned pos
)
99 unsigned len
= out
->pos
- pos
;
100 char *p
= out
->buf
+ pos
;
101 char *n
= memscan(p
, '\n', len
);
102 if (cur_tabstop(out
)) {
103 n
= min(n
, (char *) memscan(p
, '\r', len
));
104 n
= min(n
, (char *) memscan(p
, '\t', len
));
114 out
->last_newline
= pos
;
116 printbuf_insert_spaces(out
, pos
, out
->indent
);
118 pos
= min(pos
+ out
->indent
, out
->pos
);
119 out
->last_field
= pos
;
120 out
->cur_tabstop
= 0;
123 memmove(n
, n
+ 1, out
->pos
- pos
);
125 pad
= (int) cur_tabstop(out
) - (int) __printbuf_linelen(out
, pos
);
127 printbuf_insert_spaces(out
, out
->last_field
, pad
);
131 out
->last_field
= pos
;
135 pad
= (int) cur_tabstop(out
) - (int) __printbuf_linelen(out
, pos
) - 1;
138 printbuf_insert_spaces(out
, pos
, pad
- 1);
141 memmove(n
, n
+ 1, out
->pos
- pos
);
145 out
->last_field
= pos
;
152 static inline void printbuf_do_indent(struct printbuf
*out
, unsigned pos
)
154 if (out
->has_indent_or_tabstops
&& !out
->suppress_indent_tabstop_handling
)
155 __printbuf_do_indent(out
, pos
);
158 void bch2_prt_vprintf(struct printbuf
*out
, const char *fmt
, va_list args
)
165 va_copy(args2
, args
);
166 len
= vsnprintf(out
->buf
+ out
->pos
, printbuf_remaining_size(out
), fmt
, args2
);
168 } while (len
> printbuf_remaining(out
) &&
169 !bch2_printbuf_make_room(out
, len
));
171 unsigned indent_pos
= out
->pos
;
172 printbuf_advance_pos(out
, len
);
173 printbuf_do_indent(out
, indent_pos
);
176 void bch2_prt_printf(struct printbuf
*out
, const char *fmt
, ...)
183 len
= vsnprintf(out
->buf
+ out
->pos
, printbuf_remaining_size(out
), fmt
, args
);
185 } while (len
> printbuf_remaining(out
) &&
186 !bch2_printbuf_make_room(out
, len
));
188 unsigned indent_pos
= out
->pos
;
189 printbuf_advance_pos(out
, len
);
190 printbuf_do_indent(out
, indent_pos
);
194 * bch2_printbuf_str() - returns printbuf's buf as a C string, guaranteed to be
196 * @buf: printbuf to terminate
197 * Returns: Printbuf contents, as a nul terminated C string
199 const char *bch2_printbuf_str(const struct printbuf
*buf
)
202 * If we've written to a printbuf then it's guaranteed to be a null
203 * terminated string - but if we haven't, then we might not have
204 * allocated a buffer at all:
212 * bch2_printbuf_exit() - exit a printbuf, freeing memory it owns and poisoning it
213 * against accidental use.
214 * @buf: printbuf to exit
216 void bch2_printbuf_exit(struct printbuf
*buf
)
218 if (buf
->heap_allocated
) {
220 buf
->buf
= ERR_PTR(-EINTR
); /* poison value */
224 void bch2_printbuf_tabstops_reset(struct printbuf
*buf
)
226 buf
->nr_tabstops
= 0;
229 void bch2_printbuf_tabstop_pop(struct printbuf
*buf
)
231 if (buf
->nr_tabstops
)
236 * bch2_printbuf_tabstop_set() - add a tabstop, n spaces from the previous tabstop
238 * @buf: printbuf to control
239 * @spaces: number of spaces from previous tabpstop
241 * In the future this function may allocate memory if setting more than
242 * PRINTBUF_INLINE_TABSTOPS or setting tabstops more than 255 spaces from start
245 int bch2_printbuf_tabstop_push(struct printbuf
*buf
, unsigned spaces
)
247 unsigned prev_tabstop
= buf
->nr_tabstops
248 ? buf
->_tabstops
[buf
->nr_tabstops
- 1]
251 if (WARN_ON(buf
->nr_tabstops
>= ARRAY_SIZE(buf
->_tabstops
)))
254 buf
->_tabstops
[buf
->nr_tabstops
++] = prev_tabstop
+ spaces
;
255 buf
->has_indent_or_tabstops
= true;
260 * bch2_printbuf_indent_add() - add to the current indent level
262 * @buf: printbuf to control
263 * @spaces: number of spaces to add to the current indent level
265 * Subsequent lines, and the current line if the output position is at the start
266 * of the current line, will be indented by @spaces more spaces.
268 void bch2_printbuf_indent_add(struct printbuf
*buf
, unsigned spaces
)
270 if (WARN_ON_ONCE(buf
->indent
+ spaces
< buf
->indent
))
273 buf
->indent
+= spaces
;
274 prt_chars(buf
, ' ', spaces
);
276 buf
->has_indent_or_tabstops
= true;
280 * bch2_printbuf_indent_sub() - subtract from the current indent level
282 * @buf: printbuf to control
283 * @spaces: number of spaces to subtract from the current indent level
285 * Subsequent lines, and the current line if the output position is at the start
286 * of the current line, will be indented by @spaces less spaces.
288 void bch2_printbuf_indent_sub(struct printbuf
*buf
, unsigned spaces
)
290 if (WARN_ON_ONCE(spaces
> buf
->indent
))
291 spaces
= buf
->indent
;
293 if (buf
->last_newline
+ buf
->indent
== buf
->pos
) {
295 printbuf_nul_terminate(buf
);
297 buf
->indent
-= spaces
;
299 if (!buf
->indent
&& !buf
->nr_tabstops
)
300 buf
->has_indent_or_tabstops
= false;
303 void bch2_prt_newline(struct printbuf
*buf
)
305 bch2_printbuf_make_room(buf
, 1 + buf
->indent
);
307 __prt_char_reserved(buf
, '\n');
309 buf
->last_newline
= buf
->pos
;
311 __prt_chars_reserved(buf
, ' ', buf
->indent
);
313 printbuf_nul_terminate_reserved(buf
);
315 buf
->last_field
= buf
->pos
;
316 buf
->cur_tabstop
= 0;
319 void bch2_printbuf_strip_trailing_newline(struct printbuf
*out
)
321 for (int p
= out
->pos
- 1; p
>= 0; --p
) {
322 if (out
->buf
[p
] == '\n') {
326 if (out
->buf
[p
] != ' ')
330 printbuf_nul_terminate_reserved(out
);
333 static void __prt_tab(struct printbuf
*out
)
335 int spaces
= max_t(int, 0, cur_tabstop(out
) - printbuf_linelen(out
));
337 prt_chars(out
, ' ', spaces
);
339 out
->last_field
= out
->pos
;
344 * bch2_prt_tab() - Advance printbuf to the next tabstop
345 * @out: printbuf to control
347 * Advance output to the next tabstop by printing spaces.
349 void bch2_prt_tab(struct printbuf
*out
)
351 if (WARN_ON(!cur_tabstop(out
)))
357 static void __prt_tab_rjust(struct printbuf
*buf
)
359 int pad
= (int) cur_tabstop(buf
) - (int) printbuf_linelen(buf
);
361 printbuf_insert_spaces(buf
, buf
->last_field
, pad
);
363 buf
->last_field
= buf
->pos
;
368 * bch2_prt_tab_rjust - Advance printbuf to the next tabstop, right justifying
371 * @buf: printbuf to control
373 * Advance output to the next tabstop by inserting spaces immediately after the
374 * previous tabstop, right justifying previously outputted text.
376 void bch2_prt_tab_rjust(struct printbuf
*buf
)
378 if (WARN_ON(!cur_tabstop(buf
)))
381 __prt_tab_rjust(buf
);
385 * bch2_prt_bytes_indented() - Print an array of chars, handling embedded control characters
387 * @out: output printbuf
388 * @str: string to print
389 * @count: number of bytes to print
391 * The following contol characters are handled as so:
392 * \n: prt_newline newline that obeys current indent level
393 * \t: prt_tab advance to next tabstop
394 * \r: prt_tab_rjust advance to next tabstop, with right justification
396 void bch2_prt_bytes_indented(struct printbuf
*out
, const char *str
, unsigned count
)
398 unsigned indent_pos
= out
->pos
;
399 prt_bytes(out
, str
, count
);
400 printbuf_do_indent(out
, indent_pos
);
404 * bch2_prt_human_readable_u64() - Print out a u64 in human readable units
405 * @out: output printbuf
406 * @v: integer to print
408 * Units of 2^10 (default) or 10^3 are controlled via @out->si_units
410 void bch2_prt_human_readable_u64(struct printbuf
*out
, u64 v
)
412 bch2_printbuf_make_room(out
, 10);
413 unsigned len
= string_get_size(v
, 1, !out
->si_units
,
415 printbuf_remaining_size(out
));
416 printbuf_advance_pos(out
, len
);
420 * bch2_prt_human_readable_s64() - Print out a s64 in human readable units
421 * @out: output printbuf
422 * @v: integer to print
424 * Units of 2^10 (default) or 10^3 are controlled via @out->si_units
426 void bch2_prt_human_readable_s64(struct printbuf
*out
, s64 v
)
430 bch2_prt_human_readable_u64(out
, abs(v
));
434 * bch2_prt_units_u64() - Print out a u64 according to printbuf unit options
435 * @out: output printbuf
436 * @v: integer to print
438 * Units are either raw (default), or human reabable units (controlled via
439 * @buf->human_readable_units)
441 void bch2_prt_units_u64(struct printbuf
*out
, u64 v
)
443 if (out
->human_readable_units
)
444 bch2_prt_human_readable_u64(out
, v
);
446 bch2_prt_printf(out
, "%llu", v
);
450 * bch2_prt_units_s64() - Print out a s64 according to printbuf unit options
451 * @out: output printbuf
452 * @v: integer to print
454 * Units are either raw (default), or human reabable units (controlled via
455 * @buf->human_readable_units)
457 void bch2_prt_units_s64(struct printbuf
*out
, s64 v
)
461 bch2_prt_units_u64(out
, abs(v
));
464 void bch2_prt_string_option(struct printbuf
*out
,
465 const char * const list
[],
468 for (size_t i
= 0; list
[i
]; i
++)
469 bch2_prt_printf(out
, i
== selected
? "[%s] " : "%s ", list
[i
]);
472 void bch2_prt_bitflags(struct printbuf
*out
,
473 const char * const list
[], u64 flags
)
475 unsigned bit
, nr
= 0;
481 while (flags
&& (bit
= __ffs64(flags
)) < nr
) {
483 bch2_prt_printf(out
, ",");
485 bch2_prt_printf(out
, "%s", list
[bit
]);
486 flags
^= BIT_ULL(bit
);
490 void bch2_prt_bitflags_vector(struct printbuf
*out
,
491 const char * const list
[],
492 unsigned long *v
, unsigned nr
)
497 for (i
= 0; i
< nr
; i
++)
503 for_each_set_bit(i
, v
, nr
) {
505 bch2_prt_printf(out
, ",");
507 bch2_prt_printf(out
, "%s", list
[i
]);