brintos

brintos / linux-shallow public Read only

0
0
Text · 11.7 KiB · 4cf5a2a Raw
510 lines · c
1// SPDX-License-Identifier: LGPL-2.1+2/* Copyright (C) 2022 Kent Overstreet */3 4#include <linux/bitmap.h>5#include <linux/err.h>6#include <linux/export.h>7#include <linux/kernel.h>8#include <linux/slab.h>9#include <linux/string_helpers.h>10 11#include "printbuf.h"12 13static inline unsigned __printbuf_linelen(struct printbuf *buf, unsigned pos)14{15	return pos - buf->last_newline;16}17 18static inline unsigned printbuf_linelen(struct printbuf *buf)19{20	return __printbuf_linelen(buf, buf->pos);21}22 23/*24 * Returns spaces from start of line, if set, or 0 if unset:25 */26static inline unsigned cur_tabstop(struct printbuf *buf)27{28	return buf->cur_tabstop < buf->nr_tabstops29		? buf->_tabstops[buf->cur_tabstop]30		: 0;31}32 33int bch2_printbuf_make_room(struct printbuf *out, unsigned extra)34{35	/* Reserved space for terminating nul: */36	extra += 1;37 38	if (out->pos + extra <= out->size)39		return 0;40 41	if (!out->heap_allocated) {42		out->overflow = true;43		return 0;44	}45 46	unsigned new_size = roundup_pow_of_two(out->size + extra);47 48	/* Sanity check... */49	if (new_size > PAGE_SIZE << MAX_PAGE_ORDER) {50		out->allocation_failure = true;51		out->overflow = true;52		return -ENOMEM;53	}54 55	/*56	 * Note: output buffer must be freeable with kfree(), it's not required57	 * that the user use printbuf_exit().58	 */59	char *buf = krealloc(out->buf, new_size, !out->atomic ? GFP_KERNEL : GFP_NOWAIT);60 61	if (!buf) {62		out->allocation_failure = true;63		out->overflow = true;64		return -ENOMEM;65	}66 67	out->buf	= buf;68	out->size	= new_size;69	return 0;70}71 72static void printbuf_advance_pos(struct printbuf *out, unsigned len)73{74	out->pos += min(len, printbuf_remaining(out));75}76 77static void printbuf_insert_spaces(struct printbuf *out, unsigned pos, unsigned nr)78{79	unsigned move = out->pos - pos;80 81	bch2_printbuf_make_room(out, nr);82 83	if (pos + nr < out->size)84		memmove(out->buf + pos + nr,85			out->buf + pos,86			min(move, out->size - 1 - pos - nr));87 88	if (pos < out->size)89		memset(out->buf + pos, ' ', min(nr, out->size - pos));90 91	printbuf_advance_pos(out, nr);92	printbuf_nul_terminate_reserved(out);93}94 95static void __printbuf_do_indent(struct printbuf *out, unsigned pos)96{97	while (true) {98		int pad;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));105		}106 107		pos = n - out->buf;108		if (pos == out->pos)109			break;110 111		switch (*n) {112		case '\n':113			pos++;114			out->last_newline = pos;115 116			printbuf_insert_spaces(out, pos, out->indent);117 118			pos = min(pos + out->indent, out->pos);119			out->last_field = pos;120			out->cur_tabstop = 0;121			break;122		case '\r':123			memmove(n, n + 1, out->pos - pos);124			--out->pos;125			pad = (int) cur_tabstop(out) - (int) __printbuf_linelen(out, pos);126			if (pad > 0) {127				printbuf_insert_spaces(out, out->last_field, pad);128				pos += pad;129			}130 131			out->last_field = pos;132			out->cur_tabstop++;133			break;134		case '\t':135			pad = (int) cur_tabstop(out) - (int) __printbuf_linelen(out, pos) - 1;136			if (pad > 0) {137				*n = ' ';138				printbuf_insert_spaces(out, pos, pad - 1);139				pos += pad;140			} else {141				memmove(n, n + 1, out->pos - pos);142				--out->pos;143			}144 145			out->last_field = pos;146			out->cur_tabstop++;147			break;148		}149	}150}151 152static inline void printbuf_do_indent(struct printbuf *out, unsigned pos)153{154	if (out->has_indent_or_tabstops && !out->suppress_indent_tabstop_handling)155		__printbuf_do_indent(out, pos);156}157 158void bch2_prt_vprintf(struct printbuf *out, const char *fmt, va_list args)159{160	int len;161 162	do {163		va_list args2;164 165		va_copy(args2, args);166		len = vsnprintf(out->buf + out->pos, printbuf_remaining_size(out), fmt, args2);167		va_end(args2);168	} while (len > printbuf_remaining(out) &&169		 !bch2_printbuf_make_room(out, len));170 171	unsigned indent_pos = out->pos;172	printbuf_advance_pos(out, len);173	printbuf_do_indent(out, indent_pos);174}175 176void bch2_prt_printf(struct printbuf *out, const char *fmt, ...)177{178	va_list args;179	int len;180 181	do {182		va_start(args, fmt);183		len = vsnprintf(out->buf + out->pos, printbuf_remaining_size(out), fmt, args);184		va_end(args);185	} while (len > printbuf_remaining(out) &&186		 !bch2_printbuf_make_room(out, len));187 188	unsigned indent_pos = out->pos;189	printbuf_advance_pos(out, len);190	printbuf_do_indent(out, indent_pos);191}192 193/**194 * bch2_printbuf_str() - returns printbuf's buf as a C string, guaranteed to be195 * null terminated196 * @buf:	printbuf to terminate197 * Returns:	Printbuf contents, as a nul terminated C string198 */199const char *bch2_printbuf_str(const struct printbuf *buf)200{201	/*202	 * If we've written to a printbuf then it's guaranteed to be a null203	 * terminated string - but if we haven't, then we might not have204	 * allocated a buffer at all:205	 */206	return buf->pos207		? buf->buf208		: "";209}210 211/**212 * bch2_printbuf_exit() - exit a printbuf, freeing memory it owns and poisoning it213 * against accidental use.214 * @buf:	printbuf to exit215 */216void bch2_printbuf_exit(struct printbuf *buf)217{218	if (buf->heap_allocated) {219		kfree(buf->buf);220		buf->buf = ERR_PTR(-EINTR); /* poison value */221	}222}223 224void bch2_printbuf_tabstops_reset(struct printbuf *buf)225{226	buf->nr_tabstops = 0;227}228 229void bch2_printbuf_tabstop_pop(struct printbuf *buf)230{231	if (buf->nr_tabstops)232		--buf->nr_tabstops;233}234 235/*236 * bch2_printbuf_tabstop_set() - add a tabstop, n spaces from the previous tabstop237 *238 * @buf: printbuf to control239 * @spaces: number of spaces from previous tabpstop240 *241 * In the future this function may allocate memory if setting more than242 * PRINTBUF_INLINE_TABSTOPS or setting tabstops more than 255 spaces from start243 * of line.244 */245int bch2_printbuf_tabstop_push(struct printbuf *buf, unsigned spaces)246{247	unsigned prev_tabstop = buf->nr_tabstops248		? buf->_tabstops[buf->nr_tabstops - 1]249		: 0;250 251	if (WARN_ON(buf->nr_tabstops >= ARRAY_SIZE(buf->_tabstops)))252		return -EINVAL;253 254	buf->_tabstops[buf->nr_tabstops++] = prev_tabstop + spaces;255	buf->has_indent_or_tabstops = true;256	return 0;257}258 259/**260 * bch2_printbuf_indent_add() - add to the current indent level261 *262 * @buf: printbuf to control263 * @spaces: number of spaces to add to the current indent level264 *265 * Subsequent lines, and the current line if the output position is at the start266 * of the current line, will be indented by @spaces more spaces.267 */268void bch2_printbuf_indent_add(struct printbuf *buf, unsigned spaces)269{270	if (WARN_ON_ONCE(buf->indent + spaces < buf->indent))271		spaces = 0;272 273	buf->indent += spaces;274	prt_chars(buf, ' ', spaces);275 276	buf->has_indent_or_tabstops = true;277}278 279/**280 * bch2_printbuf_indent_sub() - subtract from the current indent level281 *282 * @buf: printbuf to control283 * @spaces: number of spaces to subtract from the current indent level284 *285 * Subsequent lines, and the current line if the output position is at the start286 * of the current line, will be indented by @spaces less spaces.287 */288void bch2_printbuf_indent_sub(struct printbuf *buf, unsigned spaces)289{290	if (WARN_ON_ONCE(spaces > buf->indent))291		spaces = buf->indent;292 293	if (buf->last_newline + buf->indent == buf->pos) {294		buf->pos -= spaces;295		printbuf_nul_terminate(buf);296	}297	buf->indent -= spaces;298 299	if (!buf->indent && !buf->nr_tabstops)300		buf->has_indent_or_tabstops = false;301}302 303void bch2_prt_newline(struct printbuf *buf)304{305	bch2_printbuf_make_room(buf, 1 + buf->indent);306 307	__prt_char_reserved(buf, '\n');308 309	buf->last_newline	= buf->pos;310 311	__prt_chars_reserved(buf, ' ', buf->indent);312 313	printbuf_nul_terminate_reserved(buf);314 315	buf->last_field		= buf->pos;316	buf->cur_tabstop	= 0;317}318 319void bch2_printbuf_strip_trailing_newline(struct printbuf *out)320{321	for (int p = out->pos - 1; p >= 0; --p) {322		if (out->buf[p] == '\n') {323			out->pos = p;324			break;325		}326		if (out->buf[p] != ' ')327			break;328	}329 330	printbuf_nul_terminate_reserved(out);331}332 333static void __prt_tab(struct printbuf *out)334{335	int spaces = max_t(int, 0, cur_tabstop(out) - printbuf_linelen(out));336 337	prt_chars(out, ' ', spaces);338 339	out->last_field = out->pos;340	out->cur_tabstop++;341}342 343/**344 * bch2_prt_tab() - Advance printbuf to the next tabstop345 * @out:	printbuf to control346 *347 * Advance output to the next tabstop by printing spaces.348 */349void bch2_prt_tab(struct printbuf *out)350{351	if (WARN_ON(!cur_tabstop(out)))352		return;353 354	__prt_tab(out);355}356 357static void __prt_tab_rjust(struct printbuf *buf)358{359	int pad = (int) cur_tabstop(buf) - (int) printbuf_linelen(buf);360	if (pad > 0)361		printbuf_insert_spaces(buf, buf->last_field, pad);362 363	buf->last_field = buf->pos;364	buf->cur_tabstop++;365}366 367/**368 * bch2_prt_tab_rjust - Advance printbuf to the next tabstop, right justifying369 * previous output370 *371 * @buf: printbuf to control372 *373 * Advance output to the next tabstop by inserting spaces immediately after the374 * previous tabstop, right justifying previously outputted text.375 */376void bch2_prt_tab_rjust(struct printbuf *buf)377{378	if (WARN_ON(!cur_tabstop(buf)))379		return;380 381	__prt_tab_rjust(buf);382}383 384/**385 * bch2_prt_bytes_indented() - Print an array of chars, handling embedded control characters386 *387 * @out:	output printbuf388 * @str:	string to print389 * @count:	number of bytes to print390 *391 * The following contol characters are handled as so:392 *   \n: prt_newline	newline that obeys current indent level393 *   \t: prt_tab	advance to next tabstop394 *   \r: prt_tab_rjust	advance to next tabstop, with right justification395 */396void bch2_prt_bytes_indented(struct printbuf *out, const char *str, unsigned count)397{398	unsigned indent_pos = out->pos;399	prt_bytes(out, str, count);400	printbuf_do_indent(out, indent_pos);401}402 403/**404 * bch2_prt_human_readable_u64() - Print out a u64 in human readable units405 * @out:	output printbuf406 * @v:		integer to print407 *408 * Units of 2^10 (default) or 10^3 are controlled via @out->si_units409 */410void bch2_prt_human_readable_u64(struct printbuf *out, u64 v)411{412	bch2_printbuf_make_room(out, 10);413	unsigned len = string_get_size(v, 1, !out->si_units,414				       out->buf + out->pos,415				       printbuf_remaining_size(out));416	printbuf_advance_pos(out, len);417}418 419/**420 * bch2_prt_human_readable_s64() - Print out a s64 in human readable units421 * @out:	output printbuf422 * @v:		integer to print423 *424 * Units of 2^10 (default) or 10^3 are controlled via @out->si_units425 */426void bch2_prt_human_readable_s64(struct printbuf *out, s64 v)427{428	if (v < 0)429		prt_char(out, '-');430	bch2_prt_human_readable_u64(out, abs(v));431}432 433/**434 * bch2_prt_units_u64() - Print out a u64 according to printbuf unit options435 * @out:	output printbuf436 * @v:		integer to print437 *438 * Units are either raw (default), or human reabable units (controlled via439 * @buf->human_readable_units)440 */441void bch2_prt_units_u64(struct printbuf *out, u64 v)442{443	if (out->human_readable_units)444		bch2_prt_human_readable_u64(out, v);445	else446		bch2_prt_printf(out, "%llu", v);447}448 449/**450 * bch2_prt_units_s64() - Print out a s64 according to printbuf unit options451 * @out:	output printbuf452 * @v:		integer to print453 *454 * Units are either raw (default), or human reabable units (controlled via455 * @buf->human_readable_units)456 */457void bch2_prt_units_s64(struct printbuf *out, s64 v)458{459	if (v < 0)460		prt_char(out, '-');461	bch2_prt_units_u64(out, abs(v));462}463 464void bch2_prt_string_option(struct printbuf *out,465			    const char * const list[],466			    size_t selected)467{468	for (size_t i = 0; list[i]; i++)469		bch2_prt_printf(out, i == selected ? "[%s] " : "%s ", list[i]);470}471 472void bch2_prt_bitflags(struct printbuf *out,473		       const char * const list[], u64 flags)474{475	unsigned bit, nr = 0;476	bool first = true;477 478	while (list[nr])479		nr++;480 481	while (flags && (bit = __ffs64(flags)) < nr) {482		if (!first)483			bch2_prt_printf(out, ",");484		first = false;485		bch2_prt_printf(out, "%s", list[bit]);486		flags ^= BIT_ULL(bit);487	}488}489 490void bch2_prt_bitflags_vector(struct printbuf *out,491			      const char * const list[],492			      unsigned long *v, unsigned nr)493{494	bool first = true;495	unsigned i;496 497	for (i = 0; i < nr; i++)498		if (!list[i]) {499			nr = i - 1;500			break;501		}502 503	for_each_set_bit(i, v, nr) {504		if (!first)505			bch2_prt_printf(out, ",");506		first = false;507		bch2_prt_printf(out, "%s", list[i]);508	}509}510