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