| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282 |
- /* SPDX-License-Identifier: LGPL-2.1+ */
- /* Copyright (C) 2022 Kent Overstreet */
- #ifndef _BCACHEFS_PRINTBUF_H
- #define _BCACHEFS_PRINTBUF_H
- /*
- * Printbufs: Simple strings for printing to, with optional heap allocation
- *
- * This code has provisions for use in userspace, to aid in making other code
- * portable between kernelspace and userspace.
- *
- * Basic example:
- * struct printbuf buf = PRINTBUF;
- *
- * prt_printf(&buf, "foo=");
- * foo_to_text(&buf, foo);
- * printk("%s", buf.buf);
- * printbuf_exit(&buf);
- *
- * Or
- * struct printbuf buf = PRINTBUF_EXTERN(char_buf, char_buf_size)
- *
- * We can now write pretty printers instead of writing code that dumps
- * everything to the kernel log buffer, and then those pretty-printers can be
- * used by other code that outputs to kernel log, sysfs, debugfs, etc.
- *
- * Memory allocation: Outputing to a printbuf may allocate memory. This
- * allocation is done with GFP_KERNEL, by default: use the newer
- * memalloc_*_(save|restore) functions as needed.
- *
- * Since no equivalent yet exists for GFP_ATOMIC/GFP_NOWAIT, memory allocations
- * will be done with GFP_NOWAIT if printbuf->atomic is nonzero.
- *
- * It's allowed to grab the output buffer and free it later with kfree() instead
- * of using printbuf_exit(), if the user just needs a heap allocated string at
- * the end.
- *
- * Memory allocation failures: We don't return errors directly, because on
- * memory allocation failure we usually don't want to bail out and unwind - we
- * want to print what we've got, on a best-effort basis. But code that does want
- * to return -ENOMEM may check printbuf.allocation_failure.
- *
- * Indenting, tabstops:
- *
- * To aid is writing multi-line pretty printers spread across multiple
- * functions, printbufs track the current indent level.
- *
- * printbuf_indent_push() and printbuf_indent_pop() increase and decrease the current indent
- * level, respectively.
- *
- * To use tabstops, set printbuf->tabstops[]; they are in units of spaces, from
- * start of line. Once set, prt_tab() will output spaces up to the next tabstop.
- * prt_tab_rjust() will also advance the current line of text up to the next
- * tabstop, but it does so by shifting text since the previous tabstop up to the
- * next tabstop - right justifying it.
- *
- * Make sure you use prt_newline() instead of \n in the format string for indent
- * level and tabstops to work corretly.
- *
- * Output units: printbuf->units exists to tell pretty-printers how to output
- * numbers: a raw value (e.g. directly from a superblock field), as bytes, or as
- * human readable bytes. prt_units() obeys it.
- */
- #include <linux/kernel.h>
- #include <linux/string.h>
- enum printbuf_si {
- PRINTBUF_UNITS_2, /* use binary powers of 2^10 */
- PRINTBUF_UNITS_10, /* use powers of 10^3 (standard SI) */
- };
- #define PRINTBUF_INLINE_TABSTOPS 6
- struct printbuf {
- char *buf;
- unsigned size;
- unsigned pos;
- unsigned last_newline;
- unsigned last_field;
- unsigned indent;
- /*
- * If nonzero, allocations will be done with GFP_ATOMIC:
- */
- u8 atomic;
- bool allocation_failure:1;
- bool heap_allocated:1;
- bool overflow:1;
- enum printbuf_si si_units:1;
- bool human_readable_units:1;
- bool has_indent_or_tabstops:1;
- bool suppress_indent_tabstop_handling:1;
- u8 nr_tabstops;
- /*
- * Do not modify directly: use printbuf_tabstop_add(),
- * printbuf_tabstop_get()
- */
- u8 cur_tabstop;
- u8 _tabstops[PRINTBUF_INLINE_TABSTOPS];
- };
- int bch2_printbuf_make_room(struct printbuf *, unsigned);
- __printf(2, 3) void bch2_prt_printf(struct printbuf *out, const char *fmt, ...);
- __printf(2, 0) void bch2_prt_vprintf(struct printbuf *out, const char *fmt, va_list);
- const char *bch2_printbuf_str(const struct printbuf *);
- void bch2_printbuf_exit(struct printbuf *);
- void bch2_printbuf_tabstops_reset(struct printbuf *);
- void bch2_printbuf_tabstop_pop(struct printbuf *);
- int bch2_printbuf_tabstop_push(struct printbuf *, unsigned);
- void bch2_printbuf_indent_add(struct printbuf *, unsigned);
- void bch2_printbuf_indent_sub(struct printbuf *, unsigned);
- void bch2_prt_newline(struct printbuf *);
- void bch2_printbuf_strip_trailing_newline(struct printbuf *);
- void bch2_prt_tab(struct printbuf *);
- void bch2_prt_tab_rjust(struct printbuf *);
- void bch2_prt_bytes_indented(struct printbuf *, const char *, unsigned);
- void bch2_prt_human_readable_u64(struct printbuf *, u64);
- void bch2_prt_human_readable_s64(struct printbuf *, s64);
- void bch2_prt_units_u64(struct printbuf *, u64);
- void bch2_prt_units_s64(struct printbuf *, s64);
- void bch2_prt_string_option(struct printbuf *, const char * const[], size_t);
- void bch2_prt_bitflags(struct printbuf *, const char * const[], u64);
- void bch2_prt_bitflags_vector(struct printbuf *, const char * const[],
- unsigned long *, unsigned);
- /* Initializer for a heap allocated printbuf: */
- #define PRINTBUF ((struct printbuf) { .heap_allocated = true })
- /* Initializer a printbuf that points to an external buffer: */
- #define PRINTBUF_EXTERN(_buf, _size) \
- ((struct printbuf) { \
- .buf = _buf, \
- .size = _size, \
- })
- /*
- * Returns size remaining of output buffer:
- */
- static inline unsigned printbuf_remaining_size(struct printbuf *out)
- {
- if (WARN_ON(out->size && out->pos >= out->size))
- out->pos = out->size - 1;
- return out->size - out->pos;
- }
- /*
- * Returns number of characters we can print to the output buffer - i.e.
- * excluding the terminating nul:
- */
- static inline unsigned printbuf_remaining(struct printbuf *out)
- {
- return out->size ? printbuf_remaining_size(out) - 1 : 0;
- }
- static inline unsigned printbuf_written(struct printbuf *out)
- {
- return out->size ? min(out->pos, out->size - 1) : 0;
- }
- static inline void printbuf_nul_terminate_reserved(struct printbuf *out)
- {
- if (WARN_ON(out->size && out->pos >= out->size))
- out->pos = out->size - 1;
- if (out->size)
- out->buf[out->pos] = 0;
- }
- static inline void printbuf_nul_terminate(struct printbuf *out)
- {
- bch2_printbuf_make_room(out, 1);
- printbuf_nul_terminate_reserved(out);
- }
- /* Doesn't call bch2_printbuf_make_room(), doesn't nul terminate: */
- static inline void __prt_char_reserved(struct printbuf *out, char c)
- {
- if (printbuf_remaining(out))
- out->buf[out->pos++] = c;
- }
- /* Doesn't nul terminate: */
- static inline void __prt_char(struct printbuf *out, char c)
- {
- bch2_printbuf_make_room(out, 1);
- __prt_char_reserved(out, c);
- }
- static inline void prt_char(struct printbuf *out, char c)
- {
- bch2_printbuf_make_room(out, 2);
- __prt_char_reserved(out, c);
- printbuf_nul_terminate_reserved(out);
- }
- static inline void __prt_chars_reserved(struct printbuf *out, char c, unsigned n)
- {
- unsigned can_print = min(n, printbuf_remaining(out));
- for (unsigned i = 0; i < can_print; i++)
- out->buf[out->pos++] = c;
- }
- static inline void prt_chars(struct printbuf *out, char c, unsigned n)
- {
- bch2_printbuf_make_room(out, n);
- __prt_chars_reserved(out, c, n);
- printbuf_nul_terminate_reserved(out);
- }
- static inline void prt_bytes(struct printbuf *out, const void *b, unsigned n)
- {
- bch2_printbuf_make_room(out, n);
- unsigned can_print = min(n, printbuf_remaining(out));
- for (unsigned i = 0; i < can_print; i++)
- out->buf[out->pos++] = ((char *) b)[i];
- printbuf_nul_terminate(out);
- }
- static inline void prt_str(struct printbuf *out, const char *str)
- {
- prt_bytes(out, str, strlen(str));
- }
- static inline void prt_str_indented(struct printbuf *out, const char *str)
- {
- bch2_prt_bytes_indented(out, str, strlen(str));
- }
- static inline void prt_hex_byte(struct printbuf *out, u8 byte)
- {
- bch2_printbuf_make_room(out, 3);
- __prt_char_reserved(out, hex_asc_hi(byte));
- __prt_char_reserved(out, hex_asc_lo(byte));
- printbuf_nul_terminate_reserved(out);
- }
- static inline void prt_hex_byte_upper(struct printbuf *out, u8 byte)
- {
- bch2_printbuf_make_room(out, 3);
- __prt_char_reserved(out, hex_asc_upper_hi(byte));
- __prt_char_reserved(out, hex_asc_upper_lo(byte));
- printbuf_nul_terminate_reserved(out);
- }
- /**
- * printbuf_reset - re-use a printbuf without freeing and re-initializing it:
- */
- static inline void printbuf_reset(struct printbuf *buf)
- {
- buf->pos = 0;
- buf->allocation_failure = 0;
- buf->indent = 0;
- buf->nr_tabstops = 0;
- buf->cur_tabstop = 0;
- }
- /**
- * printbuf_atomic_inc - mark as entering an atomic section
- */
- static inline void printbuf_atomic_inc(struct printbuf *buf)
- {
- buf->atomic++;
- }
- /**
- * printbuf_atomic_inc - mark as leaving an atomic section
- */
- static inline void printbuf_atomic_dec(struct printbuf *buf)
- {
- buf->atomic--;
- }
- #endif /* _BCACHEFS_PRINTBUF_H */
|