| /* 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 */ |