diff options
author | Luke T. Shumaker <lukeshu@lukeshu.com> | 2025-05-13 00:34:06 -0600 |
---|---|---|
committer | Luke T. Shumaker <lukeshu@lukeshu.com> | 2025-05-15 14:49:04 -0600 |
commit | 22336fb34f464dc751f86ad552fa1e40f06921ed (patch) | |
tree | 16eb74c38cc850c5f603ef366593b96db0273042 /libmisc/include | |
parent | 5415686b41adb8c75a6c1da999aeddd6894b3010 (diff) |
libmisc: Invent fmt.h to replace printf
Diffstat (limited to 'libmisc/include')
-rw-r--r-- | libmisc/include/libmisc/fmt.h | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/libmisc/include/libmisc/fmt.h b/libmisc/include/libmisc/fmt.h new file mode 100644 index 0000000..c29c085 --- /dev/null +++ b/libmisc/include/libmisc/fmt.h @@ -0,0 +1,156 @@ +/* libmisc/fmt.h - Write formatted text + * + * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#ifndef _LIBMISC_FMT_H_ +#define _LIBMISC_FMT_H_ + +#include <stddef.h> /* for size_t */ +#include <stdint.h> /* for (u)int{n}_t */ + +#include <libmisc/macro.h> +#include <libmisc/obj.h> + +/* destination interface ******************************************************/ + +#define fmt_dest_LO_IFACE \ + LO_FUNC(void , putb, uint8_t b) \ + LO_FUNC(size_t, tell) +LO_INTERFACE(fmt_dest); + +/* type-specific fmt_print_() functions ***************************************/ + +/* Simple bytes. */ +void fmt_print_byte(lo_interface fmt_dest w, uint8_t b); + +/* These are `static inline` so that the compiler can unroll the loops. */ +static inline void fmt_print_mem(lo_interface fmt_dest w, const void *_str, size_t size) { + const uint8_t *str = _str; + while (size--) + fmt_print_byte(w, *(str++)); +} +static inline void fmt_print_str(lo_interface fmt_dest w, const char *str) { + while (*str) + fmt_print_byte(w, *(str++)); +} +static inline void fmt_print_strn(lo_interface fmt_dest w, const char *str, size_t size) { + while (size-- && *str) + fmt_print_byte(w, *(str++)); +} + +/* Quoted bytes. */ +void fmt_print_qbyte(lo_interface fmt_dest w, uint8_t b); +void fmt_print_qmem(lo_interface fmt_dest w, const void *str, size_t size); +void fmt_print_qstr(lo_interface fmt_dest w, const char *str); +void fmt_print_qstrn(lo_interface fmt_dest w, const char *str, size_t size); + +/* Integers. */ +#define _fmt_declare_base(base) \ + void _fmt_print_base##base##_u8(lo_interface fmt_dest w, uint8_t val); \ + void _fmt_print_base##base##_u16(lo_interface fmt_dest w, uint16_t val); \ + void _fmt_print_base##base##_u32(lo_interface fmt_dest w, uint32_t val); \ + void _fmt_print_base##base##_u64(lo_interface fmt_dest w, uint64_t val); \ + void _fmt_print_base##base##_s8(lo_interface fmt_dest w, int8_t val); \ + void _fmt_print_base##base##_s16(lo_interface fmt_dest w, int16_t val); \ + void _fmt_print_base##base##_s32(lo_interface fmt_dest w, int32_t val); \ + void _fmt_print_base##base##_s64(lo_interface fmt_dest w, int64_t val); \ + LM_FORCE_SEMICOLON +_fmt_declare_base(2); +_fmt_declare_base(8); +_fmt_declare_base(10); +_fmt_declare_base(16); +#undef _fmt_declare_base + +#define _fmt_intswitch(val, prefix) _Generic((val) , \ + unsigned char : LM_CAT3_(prefix, u, __SCHAR_WIDTH__) , \ + unsigned short : LM_CAT3_(prefix, u, __SHRT_WIDTH__) , \ + unsigned int : LM_CAT3_(prefix, u, __INT_WIDTH__) , \ + unsigned long : LM_CAT3_(prefix, u, __LONG_WIDTH__) , \ + unsigned long long : LM_CAT3_(prefix, u, __LONG_LONG_WIDTH__) , \ + signed char : LM_CAT3_(prefix, s, __SCHAR_WIDTH__) , \ + signed short : LM_CAT3_(prefix, s, __SHRT_WIDTH__) , \ + signed int : LM_CAT3_(prefix, s, __INT_WIDTH__) , \ + signed long : LM_CAT3_(prefix, s, __LONG_WIDTH__) , \ + signed long long : LM_CAT3_(prefix, s, __LONG_LONG_WIDTH__) ) +#define fmt_print_base2(w, val) (_fmt_intswitch((val), _fmt_print_base2_)((w), (val))) +#define fmt_print_base8(w, val) (_fmt_intswitch((val), _fmt_print_base8_)((w), (val))) +#define fmt_print_base10(w, val) (_fmt_intswitch((val), _fmt_print_base10_)((w), (val))) +#define fmt_print_base16(w, val) (_fmt_intswitch((val), _fmt_print_base16_)((w), (val))) + +/* Booleans. */ +void fmt_print_bool(lo_interface fmt_dest w, bool b); + +/* The high-level fmt_print() interface ***************************************/ + +#define fmt_print(w, ...) do { LM_FOREACH_PARAM_(_fmt_param, (w), __VA_ARGS__) } while (0) +#define _fmt_param(w, arg) \ + LM_IF(LM_IS_TUPLE(arg))( \ + _fmt_param_tuple LM_EAT() (w, LM_EXPAND arg) \ + )( \ + _fmt_param_nontuple(w, arg) \ + ); +#define _fmt_param_tuple(w, fn, ...) fmt_print_##fn(w __VA_OPT__(,) __VA_ARGS__) +#define _fmt_param_nontuple(w, val) _Generic((val), \ + unsigned char : LM_CAT2_(_fmt_print_base10_u, __SCHAR_WIDTH__) , \ + unsigned short : LM_CAT2_(_fmt_print_base10_u, __SHRT_WIDTH__) , \ + unsigned int : LM_CAT2_(_fmt_print_base10_u, __INT_WIDTH__) , \ + unsigned long : LM_CAT2_(_fmt_print_base10_u, __LONG_WIDTH__) , \ + unsigned long long : LM_CAT2_(_fmt_print_base10_u, __LONG_LONG_WIDTH__) , \ + signed char : LM_CAT2_(_fmt_print_base10_s, __SCHAR_WIDTH__) , \ + signed short : LM_CAT2_(_fmt_print_base10_s, __SHRT_WIDTH__) , \ + signed int : LM_CAT2_(_fmt_print_base10_s, __INT_WIDTH__) , \ + signed long : LM_CAT2_(_fmt_print_base10_s, __LONG_WIDTH__) , \ + signed long long : LM_CAT2_(_fmt_print_base10_s, __LONG_LONG_WIDTH__) , \ + char * : fmt_print_str , \ + const char * : fmt_print_str , \ + bool : fmt_print_bool )(w, val) + +/* print-to-memory ************************************************************/ + +struct fmt_buf { + void *dat; + size_t len, cap; +}; +LO_IMPLEMENTATION_H(fmt_dest, struct fmt_buf, fmt_buf); + +#define fmt_snprint(buf, n, ...) ({ \ + struct fmt_buf _w = { .dat = buf, .cap = n }; \ + lo_interface fmt_dest w = lo_box_fmt_buf_as_fmt_dest(&_w); \ + fmt_print(w, __VA_ARGS__); \ + if (_w.len < _w.cap) \ + ((char *)_w.dat)[_w.len] = '\0'; \ + _w.len; \ +}) + +/* justify ********************************************************************/ + +/* *grubles about not being allowed to nest things* */ +#define _fmt_param_indirect() _fmt_param +#define _fmt_print2(w, ...) do { LM_FOREACH_PARAM2_(_fmt_param2, (w), __VA_ARGS__) } while (0) +#define _fmt_param2(...) _LM_DEFER2(_fmt_param_indirect)()(__VA_ARGS__) + +#define fmt_print_ljust(w, width, fillchar, ...) do { \ + size_t beg = LO_CALL(w, tell); \ + _fmt_print2(w, __VA_ARGS__); \ + while ((LO_CALL(w, tell) - beg) < width) \ + fmt_print_byte(w, fillchar); \ +} while (0) + +#define fmt_print_rjust(w, width, fillchar, ...) do { \ + struct fmt_buf _discard = {}; \ + lo_interface fmt_dest discard = lo_box_fmt_buf_as_fmt_dest(&_discard); \ + _fmt_print2(discard, __VA_ARGS__); \ + while (_discard.len++ < width) \ + fmt_print_byte(w, fillchar); \ + _fmt_print2(w, __VA_ARGS__); \ +} while (0) + +void fmt_print_base16_u8_(lo_interface fmt_dest w, uint8_t x); +void fmt_print_base16_u16_(lo_interface fmt_dest w, uint16_t x); +void fmt_print_base16_u32_(lo_interface fmt_dest w, uint32_t x); +void fmt_print_base16_u64_(lo_interface fmt_dest w, uint64_t x); +void fmt_print_ptr(lo_interface fmt_dest w, void *ptr); + +#endif /* _LIBMISC_FMT_H_ */ |