brintos

brintos / linux-shallow public Read only

0
0
Text · 8.3 KiB · 1d57038 Raw
283 lines · c
1/* SPDX-License-Identifier: LGPL-2.1+ */2/* Copyright (C) 2022 Kent Overstreet */3 4#ifndef _BCACHEFS_PRINTBUF_H5#define _BCACHEFS_PRINTBUF_H6 7/*8 * Printbufs: Simple strings for printing to, with optional heap allocation9 *10 * This code has provisions for use in userspace, to aid in making other code11 * portable between kernelspace and userspace.12 *13 * Basic example:14 *   struct printbuf buf = PRINTBUF;15 *16 *   prt_printf(&buf, "foo=");17 *   foo_to_text(&buf, foo);18 *   printk("%s", buf.buf);19 *   printbuf_exit(&buf);20 *21 * Or22 *   struct printbuf buf = PRINTBUF_EXTERN(char_buf, char_buf_size)23 *24 * We can now write pretty printers instead of writing code that dumps25 * everything to the kernel log buffer, and then those pretty-printers can be26 * used by other code that outputs to kernel log, sysfs, debugfs, etc.27 *28 * Memory allocation: Outputing to a printbuf may allocate memory. This29 * allocation is done with GFP_KERNEL, by default: use the newer30 * memalloc_*_(save|restore) functions as needed.31 *32 * Since no equivalent yet exists for GFP_ATOMIC/GFP_NOWAIT, memory allocations33 * will be done with GFP_NOWAIT if printbuf->atomic is nonzero.34 *35 * It's allowed to grab the output buffer and free it later with kfree() instead36 * of using printbuf_exit(), if the user just needs a heap allocated string at37 * the end.38 *39 * Memory allocation failures: We don't return errors directly, because on40 * memory allocation failure we usually don't want to bail out and unwind - we41 * want to print what we've got, on a best-effort basis. But code that does want42 * to return -ENOMEM may check printbuf.allocation_failure.43 *44 * Indenting, tabstops:45 *46 * To aid is writing multi-line pretty printers spread across multiple47 * functions, printbufs track the current indent level.48 *49 * printbuf_indent_push() and printbuf_indent_pop() increase and decrease the current indent50 * level, respectively.51 *52 * To use tabstops, set printbuf->tabstops[]; they are in units of spaces, from53 * start of line. Once set, prt_tab() will output spaces up to the next tabstop.54 * prt_tab_rjust() will also advance the current line of text up to the next55 * tabstop, but it does so by shifting text since the previous tabstop up to the56 * next tabstop - right justifying it.57 *58 * Make sure you use prt_newline() instead of \n in the format string for indent59 * level and tabstops to work corretly.60 *61 * Output units: printbuf->units exists to tell pretty-printers how to output62 * numbers: a raw value (e.g. directly from a superblock field), as bytes, or as63 * human readable bytes. prt_units() obeys it.64 */65 66#include <linux/kernel.h>67#include <linux/string.h>68 69enum printbuf_si {70	PRINTBUF_UNITS_2,	/* use binary powers of 2^10 */71	PRINTBUF_UNITS_10,	/* use powers of 10^3 (standard SI) */72};73 74#define PRINTBUF_INLINE_TABSTOPS	675 76struct printbuf {77	char			*buf;78	unsigned		size;79	unsigned		pos;80	unsigned		last_newline;81	unsigned		last_field;82	unsigned		indent;83	/*84	 * If nonzero, allocations will be done with GFP_ATOMIC:85	 */86	u8			atomic;87	bool			allocation_failure:1;88	bool			heap_allocated:1;89	bool			overflow:1;90	enum printbuf_si	si_units:1;91	bool			human_readable_units:1;92	bool			has_indent_or_tabstops:1;93	bool			suppress_indent_tabstop_handling:1;94	u8			nr_tabstops;95 96	/*97	 * Do not modify directly: use printbuf_tabstop_add(),98	 * printbuf_tabstop_get()99	 */100	u8			cur_tabstop;101	u8			_tabstops[PRINTBUF_INLINE_TABSTOPS];102};103 104int bch2_printbuf_make_room(struct printbuf *, unsigned);105__printf(2, 3) void bch2_prt_printf(struct printbuf *out, const char *fmt, ...);106__printf(2, 0) void bch2_prt_vprintf(struct printbuf *out, const char *fmt, va_list);107const char *bch2_printbuf_str(const struct printbuf *);108void bch2_printbuf_exit(struct printbuf *);109 110void bch2_printbuf_tabstops_reset(struct printbuf *);111void bch2_printbuf_tabstop_pop(struct printbuf *);112int bch2_printbuf_tabstop_push(struct printbuf *, unsigned);113 114void bch2_printbuf_indent_add(struct printbuf *, unsigned);115void bch2_printbuf_indent_sub(struct printbuf *, unsigned);116 117void bch2_prt_newline(struct printbuf *);118void bch2_printbuf_strip_trailing_newline(struct printbuf *);119void bch2_prt_tab(struct printbuf *);120void bch2_prt_tab_rjust(struct printbuf *);121 122void bch2_prt_bytes_indented(struct printbuf *, const char *, unsigned);123void bch2_prt_human_readable_u64(struct printbuf *, u64);124void bch2_prt_human_readable_s64(struct printbuf *, s64);125void bch2_prt_units_u64(struct printbuf *, u64);126void bch2_prt_units_s64(struct printbuf *, s64);127void bch2_prt_string_option(struct printbuf *, const char * const[], size_t);128void bch2_prt_bitflags(struct printbuf *, const char * const[], u64);129void bch2_prt_bitflags_vector(struct printbuf *, const char * const[],130			      unsigned long *, unsigned);131 132/* Initializer for a heap allocated printbuf: */133#define PRINTBUF ((struct printbuf) { .heap_allocated = true })134 135/* Initializer a printbuf that points to an external buffer: */136#define PRINTBUF_EXTERN(_buf, _size)			\137((struct printbuf) {					\138	.buf	= _buf,					\139	.size	= _size,				\140})141 142/*143 * Returns size remaining of output buffer:144 */145static inline unsigned printbuf_remaining_size(struct printbuf *out)146{147	if (WARN_ON(out->size && out->pos >= out->size))148		out->pos = out->size - 1;149	return out->size - out->pos;150}151 152/*153 * Returns number of characters we can print to the output buffer - i.e.154 * excluding the terminating nul:155 */156static inline unsigned printbuf_remaining(struct printbuf *out)157{158	return out->size ? printbuf_remaining_size(out) - 1 : 0;159}160 161static inline unsigned printbuf_written(struct printbuf *out)162{163	return out->size ? min(out->pos, out->size - 1) : 0;164}165 166static inline void printbuf_nul_terminate_reserved(struct printbuf *out)167{168	if (WARN_ON(out->size && out->pos >= out->size))169		out->pos = out->size - 1;170	if (out->size)171		out->buf[out->pos] = 0;172}173 174static inline void printbuf_nul_terminate(struct printbuf *out)175{176	bch2_printbuf_make_room(out, 1);177	printbuf_nul_terminate_reserved(out);178}179 180/* Doesn't call bch2_printbuf_make_room(), doesn't nul terminate: */181static inline void __prt_char_reserved(struct printbuf *out, char c)182{183	if (printbuf_remaining(out))184		out->buf[out->pos++] = c;185}186 187/* Doesn't nul terminate: */188static inline void __prt_char(struct printbuf *out, char c)189{190	bch2_printbuf_make_room(out, 1);191	__prt_char_reserved(out, c);192}193 194static inline void prt_char(struct printbuf *out, char c)195{196	bch2_printbuf_make_room(out, 2);197	__prt_char_reserved(out, c);198	printbuf_nul_terminate_reserved(out);199}200 201static inline void __prt_chars_reserved(struct printbuf *out, char c, unsigned n)202{203	unsigned can_print = min(n, printbuf_remaining(out));204 205	for (unsigned i = 0; i < can_print; i++)206		out->buf[out->pos++] = c;207}208 209static inline void prt_chars(struct printbuf *out, char c, unsigned n)210{211	bch2_printbuf_make_room(out, n);212	__prt_chars_reserved(out, c, n);213	printbuf_nul_terminate_reserved(out);214}215 216static inline void prt_bytes(struct printbuf *out, const void *b, unsigned n)217{218	bch2_printbuf_make_room(out, n);219 220	unsigned can_print = min(n, printbuf_remaining(out));221 222	for (unsigned i = 0; i < can_print; i++)223		out->buf[out->pos++] = ((char *) b)[i];224 225	printbuf_nul_terminate(out);226}227 228static inline void prt_str(struct printbuf *out, const char *str)229{230	prt_bytes(out, str, strlen(str));231}232 233static inline void prt_str_indented(struct printbuf *out, const char *str)234{235	bch2_prt_bytes_indented(out, str, strlen(str));236}237 238static inline void prt_hex_byte(struct printbuf *out, u8 byte)239{240	bch2_printbuf_make_room(out, 3);241	__prt_char_reserved(out, hex_asc_hi(byte));242	__prt_char_reserved(out, hex_asc_lo(byte));243	printbuf_nul_terminate_reserved(out);244}245 246static inline void prt_hex_byte_upper(struct printbuf *out, u8 byte)247{248	bch2_printbuf_make_room(out, 3);249	__prt_char_reserved(out, hex_asc_upper_hi(byte));250	__prt_char_reserved(out, hex_asc_upper_lo(byte));251	printbuf_nul_terminate_reserved(out);252}253 254/**255 * printbuf_reset - re-use a printbuf without freeing and re-initializing it:256 */257static inline void printbuf_reset(struct printbuf *buf)258{259	buf->pos		= 0;260	buf->allocation_failure	= 0;261	buf->indent		= 0;262	buf->nr_tabstops	= 0;263	buf->cur_tabstop	= 0;264}265 266/**267 * printbuf_atomic_inc - mark as entering an atomic section268 */269static inline void printbuf_atomic_inc(struct printbuf *buf)270{271	buf->atomic++;272}273 274/**275 * printbuf_atomic_inc - mark as leaving an atomic section276 */277static inline void printbuf_atomic_dec(struct printbuf *buf)278{279	buf->atomic--;280}281 282#endif /* _BCACHEFS_PRINTBUF_H */283