diff --git a/Makefile b/Makefile index 4a0a45c..59505c3 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -SOURCES = src/debug/misc.c src/io/term/flanterm_backends/fb.c src/io/term/flanterm.c src/debug/panic.c src/debug/stacktrace.c src/boot/boot.c src/sched/scheduler.c src/sched/process.c src/mem/heap/kheap.c src/mem/paging/vmm.c src/mem/paging/paging.c src/mem/paging/pmm.c src/string/string.c src/io/kbd/ps2.c src/io/serial/serial.c src/io/term/printf.c src/io/term/term.c src/idt/idt.c src/mem/gdt/gdt.c src/mem/misc/utils.c src/time/timer.c src/kmain.c +SOURCES = src/sched/spinlock.c src/debug/misc.c src/io/term/flanterm_backends/fb.c src/io/term/flanterm.c src/debug/panic.c src/debug/stacktrace.c src/boot/boot.c src/sched/scheduler.c src/sched/process.c src/mem/heap/kheap.c src/mem/paging/vmm.c src/mem/paging/paging.c src/mem/paging/pmm.c src/string/string.c src/io/kbd/ps2.c src/io/serial/serial.c src/io/term/term.c src/idt/idt.c src/mem/gdt/gdt.c src/mem/misc/utils.c src/time/timer.c src/kmain.c CC_FLAGS=-Wall -Wextra -std=gnu99 -nostdlib -ffreestanding -fno-stack-protector -fno-omit-frame-pointer -fno-stack-check -fno-PIC -ffunction-sections -fdata-sections -mcmodel=kernel CC_PROBLEMATIC_FLAGS=-Wno-unused-parameter -Wno-unused-variable diff --git a/src/debug/misc.c b/src/debug/misc.c index 874c328..36f8d88 100644 --- a/src/debug/misc.c +++ b/src/debug/misc.c @@ -7,6 +7,7 @@ #include #include "limine.h" #include "string/string.h" +#include extern struct boot_context boot_ctx; diff --git a/src/debug/panic.c b/src/debug/panic.c index d15a27d..2915cf7 100644 --- a/src/debug/panic.c +++ b/src/debug/panic.c @@ -10,6 +10,54 @@ #include "kernel.h" extern struct init_status init; +extern int panic_count; + +/* + * reaf_rflags - provide easy reading of the RFLAGS register + * @rflags: RFLAGS register value + */ +void read_rflags(uint64_t rflags) +{ + DEBUG("\x1b[38;5;226m%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\x1b[38;5;231m", + CHECK_BIT(rflags, 0) ? "CF " : "", /*carry flag*/ + CHECK_BIT(rflags, 2) ? "PF " : "", /*parity flag*/ + CHECK_BIT(rflags, 4) ? "AF " : "", /*auxiliary carry flag*/ + CHECK_BIT(rflags, 6) ? "ZF " : "", /*zero flag*/ + CHECK_BIT(rflags, 7) ? "SF " : "", /*sign flag*/ + CHECK_BIT(rflags, 8) ? "TF " : "", /*trap flag*/ + CHECK_BIT(rflags, 9) ? "IF " : "", /*interrupt enable flag*/ + CHECK_BIT(rflags, 10) ? "DF " : "", /*direction flag*/ + CHECK_BIT(rflags, 11) ? "OF " : "", /*overflow flag*/ + (CHECK_BIT(rflags, 12) && CHECK_BIT(rflags, 13)) ? "IOPL3 " : "IOPL0 ", /*io privilege lvl*/ + CHECK_BIT(rflags, 14) ? "NT " : "", /*nested task*/ + CHECK_BIT(rflags, 16) ? "RF " : "", /*resume flag*/ + CHECK_BIT(rflags, 17) ? "VM " : "", /*virtual 8086 mode*/ + CHECK_BIT(rflags, 18) ? "AC " : "", /*alignment check/access control*/ + CHECK_BIT(rflags, 19) ? "VIF " : "", /*virtual interrupt flag*/ + CHECK_BIT(rflags, 20) ? "VIP " : "", /*virtual interrupt pending*/ + CHECK_BIT(rflags, 21) ? "ID " : ""); /*id flag*/ + + if (init.terminal) { + printf("\x1b[38;5;226m%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\x1b[38;5;231m\r\n", + CHECK_BIT(rflags, 0) ? "CF " : "", + CHECK_BIT(rflags, 2) ? "PF " : "", + CHECK_BIT(rflags, 4) ? "AF " : "", + CHECK_BIT(rflags, 6) ? "ZF " : "", + CHECK_BIT(rflags, 7) ? "SF " : "", + CHECK_BIT(rflags, 8) ? "TF " : "", + CHECK_BIT(rflags, 9) ? "IF " : "", + CHECK_BIT(rflags, 10) ? "DF " : "", + CHECK_BIT(rflags, 11) ? "OF " : "", + (CHECK_BIT(rflags, 12) && CHECK_BIT(rflags, 13)) ? "IOPL3 " : "IOPL0 ", + CHECK_BIT(rflags, 14) ? "NT " : "", + CHECK_BIT(rflags, 16) ? "RF " : "", + CHECK_BIT(rflags, 17) ? "VM " : "", + CHECK_BIT(rflags, 18) ? "AC " : "", + CHECK_BIT(rflags, 19) ? "VIF " : "", + CHECK_BIT(rflags, 20) ? "VIP " : "", + CHECK_BIT(rflags, 21) ? "ID " : ""); + } +} /* * panic - Kernel panic @@ -23,36 +71,21 @@ extern struct init_status init; void panic(struct cpu_status_t* ctx, const char* str) { CLEAR_INTERRUPTS; + panic_count += 1; if (ctx == NULL) { - DEBUG("\x1b[38;5;231m\x1b[48;5;196mKernel panic!!!\x1b[0m Something went horribly wrong! (no cpu ctx)"); - fctprintf((void*)&skputc, 0, "\x1b[38;5;231m\x1b[48;5;27m"); - DIE_DEBUG(str); - fctprintf((void*)&skputc, 0, "\x1b[0m"); - skputc('\r'); - skputc('\n'); - DEBUG("\x1b[38;5;231m\x1b[48;5;196mend Kernel panic - halting...\x1b[0m"); - - if (init.terminal) { - printf("\r\n\x1b[38;5;231m\x1b[48;5;196mKernel panic!!!\x1b[48;5;232m Something went horribly wrong! (no cpu ctx)"); - printf("\r\n%s\r\n\x1b[38;5;231mend Kernel panic - halting...\x1b[0m", str); - } - + printf("\r\n\x1b[38;5;231m\x1b[48;5;196mKernel panic!!!\x1b[48;5;232m Something went horribly wrong! (no cpu ctx)"); + printf("\r\n%s\r\n\x1b[38;5;231m\x1b[0m", str); + debug_stack_trace(100); hcf(); } - DEBUG("\x1b[38;5;231m\x1b[48;5;196mKernel panic!!!\x1b[0m at rip=%p\r\nSomething went horribly wrong! (%s) vect=0x%.2x errcode=0x%x\n\rrax=%p rbx=%p rcx=%p rdx=%p\n\rrsi=%p rdi=%p r8=%p r9=%p\n\rr10=%p r11=%p r12=%p r13=%p\n\rr14=%p r15=%p\n\n\rflags=%p\n\rHalting...\x1b[0m", - ctx->iret_rip, - str, - ctx->vector_number, ctx->error_code, ctx->rax, ctx->rbx, ctx->rcx, ctx->rdx, ctx->rsi, ctx->rdi, - ctx->r8, ctx->r9, ctx->r10, ctx->r11, ctx->r12, ctx->r13, ctx->r14, ctx->r15, ctx->iret_flags); - - if (init.terminal) { - printf("\r\n\x1b[38;5;231m\x1b[48;5;196mKernel panic!!!\x1b[48;5;232mat rip=%p\r\nSomething went horribly wrong! (%s) vect=0x%.2x errcode=0x%x\n\rrax=%p rbx=%p rcx=%p rdx=%p\n\rrsi=%p rdi=%p r8=%p r9=%p\n\rr10=%p r11=%p r12=%p r13=%p\n\rr14=%p r15=%p\n\n\rflags=%p\n\rHalting...\x1b[0m", - ctx->iret_rip, - str, - ctx->vector_number, ctx->error_code, ctx->rax, ctx->rbx, ctx->rcx, ctx->rdx, ctx->rsi, ctx->rdi, - ctx->r8, ctx->r9, ctx->r10, ctx->r11, ctx->r12, ctx->r13, ctx->r14, ctx->r15, ctx->iret_flags); - } - + + printf("\r\n\x1b[38;5;231m\x1b[48;5;196mKernel panic!!!\x1b[48;5;232mat rip=%p\r\nSomething went horribly wrong! (%s) vect=0x%.2x errcode=0x%x\n\rrax=%p rbx=%p rcx=%p rdx=%p\n\rrsi=%p rdi=%p r8=%p r9=%p\n\rr10=%p r11=%p r12=%p r13=%p\n\rr14=%p r15=%p\n\n\rflags=%p ", + ctx->iret_rip, + str, + ctx->vector_number, ctx->error_code, ctx->rax, ctx->rbx, ctx->rcx, ctx->rdx, ctx->rsi, ctx->rdi, + ctx->r8, ctx->r9, ctx->r10, ctx->r11, ctx->r12, ctx->r13, ctx->r14, ctx->r15, ctx->iret_flags); + + read_rflags(ctx->iret_flags); debug_stack_trace(100); hcf(); diff --git a/src/debug/stacktrace.c b/src/debug/stacktrace.c index 430bf94..91910e8 100644 --- a/src/debug/stacktrace.c +++ b/src/debug/stacktrace.c @@ -6,6 +6,7 @@ #include #include "kernel.h" +#include extern struct init_status init; @@ -18,10 +19,8 @@ extern struct init_status init; */ void debug_stack_trace(unsigned int max_frames) { - DEBUG("*** begin stack trace ***"); - if (init.terminal) { - printf("\r\n\x1b[48;5;232m\x1b[38;5;231m*** begin stack trace ***\r\n"); - } + printf("\r\n\x1b[48;5;232m\x1b[38;5;231m*** begin stack trace ***\r\n"); + // Thanks GCC :) uintptr_t* rbp = (uintptr_t*)__builtin_frame_address(0); @@ -30,11 +29,7 @@ void debug_stack_trace(unsigned int max_frames) uintptr_t rip = rbp[1]; uintptr_t offset = 0; const char* name = debug_find_symbol(rip, &offset); - DEBUG("[%u] <0x%p> (%s+0x%x)", frame, (void*)rip, name, offset); - - if (init.terminal) { - printf("[%u] <0x%p> (%s+0x%x)\r\n", frame, (void*)rip, name, offset); - } + printf("[%u] <0x%p> (%s+0x%x)\r\n", frame, (void*)rip, name, offset); uintptr_t* next_rbp = (uintptr_t*)rbp[0]; @@ -45,10 +40,8 @@ void debug_stack_trace(unsigned int max_frames) rbp = next_rbp; } - if (init.terminal) { - printf("*** end stack trace ***\x1b[0m"); - } - DEBUG("*** end stack trace ***"); + + printf("*** end stack trace ***\r\n[end Kernel panic]\r\nHalting system...\x1b[0m"); } typedef struct { diff --git a/src/idt/idt.c b/src/idt/idt.c index dd465e9..f70959f 100644 --- a/src/idt/idt.c +++ b/src/idt/idt.c @@ -154,6 +154,12 @@ static void gp_fault_handler(struct cpu_status_t* ctx) panic(ctx, "gp fault"); } +// DEBUG +void kbdproc_main(void* arg) +{ + printf("Key pressed/released.\r\n"); +} + /* * interrupt_dispatch - Interrupt dispatcher * @context: CPU context @@ -252,6 +258,7 @@ struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context) case 33: // Keyboard Interrupt keyboard_handler(); + process_create("keyboard-initiated", kbdproc_main, NULL); // DEBUG outb(0x20, 0x20); break; diff --git a/src/io/kbd/ps2.c b/src/io/kbd/ps2.c index 32c0d3c..25b18b2 100644 --- a/src/io/kbd/ps2.c +++ b/src/io/kbd/ps2.c @@ -9,6 +9,7 @@ #include #include "io/term/term.h" #include +#include // The key status bitfield will be used to see if ALT, CONTROL, or SHIFT is pressed uint8_t key_status = 0b00000000; diff --git a/src/io/term/nanoprintf.h b/src/io/term/nanoprintf.h new file mode 100644 index 0000000..63ac4c5 --- /dev/null +++ b/src/io/term/nanoprintf.h @@ -0,0 +1,1597 @@ +/* nanoprintf v0.5.5: a tiny embeddable printf replacement written in C. + https://github.com/charlesnicholson/nanoprintf + charles.nicholson+nanoprintf@gmail.com + dual-licensed under 0bsd and unlicense, take your pick. see eof for details. */ + +#ifndef NPF_H_INCLUDED +#define NPF_H_INCLUDED + +#ifdef NANOPRINTF_CONFIG_FILE +#include NANOPRINTF_CONFIG_FILE +#endif + +#include +#include + +typedef void (*npf_putc)(int c, void *ctx); + +// Define this to fully sandbox nanoprintf inside of a translation unit. +#ifdef NANOPRINTF_VISIBILITY_STATIC + #define NPF_VISIBILITY static +#else + #define NPF_VISIBILITY extern +#endif + +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #define NPF_PRINTF_ATTR(FORMAT_INDEX, VARGS_INDEX) \ + __attribute__((format(printf, FORMAT_INDEX, VARGS_INDEX))) +#else + #define NPF_PRINTF_ATTR(FORMAT_INDEX, VARGS_INDEX) +#endif + +#if defined(NANOPRINTF_USE_FLOAT_SINGLE_PRECISION) && NANOPRINTF_USE_FLOAT_SINGLE_PRECISION == 1 +#define NPF_PRINTF_SP_ATTR NPF_PRINTF_ATTR(3, 0) +#define NPF_MAP_ARGS(...) NPF__MAP(NPF__WRAP, __VA_ARGS__) +typedef struct { float val; } npf_float_t; +#define npf_snprintf_ npf_snprintf_sp_ +#define npf_pprintf_ npf_pprintf_sp_ +#define npf_vsnprintf npf_vsnprintf_sp +#define npf_vpprintf npf_vpprintf_sp +#else +#define NPF_PRINTF_SP_ATTR NPF_PRINTF_ATTR(3, 4) +#define NPF_MAP_ARGS(...) __VA_ARGS__ +#endif + +#ifdef __cplusplus +#define NPF_RESTRICT +extern "C" { +#else +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) +#define NPF_RESTRICT restrict +#else +#define NPF_RESTRICT +#endif +#endif + +NPF_VISIBILITY int npf_snprintf_(char * NPF_RESTRICT buffer, + size_t bufsz, + const char * NPF_RESTRICT format, ...) + NPF_PRINTF_SP_ATTR; + +NPF_VISIBILITY int npf_pprintf_(npf_putc pc, + void * NPF_RESTRICT pc_ctx, + char const * NPF_RESTRICT format, ...) + NPF_PRINTF_SP_ATTR; + +// Public API + +// The npf_ functions all return the number of bytes required to express the +// fully-formatted string, not including the null terminator character. +// The npf_ functions do not return negative values, since the lack of 'l' length +// modifier support makes encoding errors impossible. + +#define npf_snprintf(buf, sz, ...) npf_snprintf_((buf), (sz), NPF_MAP_ARGS(__VA_ARGS__)) +#define npf_pprintf(pc, ctx, ...) npf_pprintf_((pc), (ctx), NPF_MAP_ARGS(__VA_ARGS__)) + +NPF_VISIBILITY int npf_vsnprintf(char * NPF_RESTRICT buffer, + size_t bufsz, + char const * NPF_RESTRICT format, + va_list vlist) NPF_PRINTF_ATTR(3, 0); + +NPF_VISIBILITY int npf_vpprintf(npf_putc pc, + void * NPF_RESTRICT pc_ctx, + char const * NPF_RESTRICT format, + va_list vlist) NPF_PRINTF_ATTR(3, 0); + +#ifdef __cplusplus +} +#endif + +#endif // NPF_H_INCLUDED + +/* The implementation of nanoprintf begins here, to be compiled only if + NANOPRINTF_IMPLEMENTATION is defined. In a multi-file library what follows would + be nanoprintf.c. */ + +#ifdef NANOPRINTF_IMPLEMENTATION + +#ifndef NPF_IMPLEMENTATION_INCLUDED +#define NPF_IMPLEMENTATION_INCLUDED + +#include +#include + +// Pick reasonable defaults if nothing's been configured. +#if !defined(NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS) && \ + !defined(NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS) && \ + !defined(NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS) && \ + !defined(NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS) && \ + !defined(NANOPRINTF_USE_SMALL_FORMAT_SPECIFIERS) && \ + !defined(NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS) && \ + !defined(NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS) && \ + !defined(NANOPRINTF_USE_ALT_FORM_FLAG) && \ + !defined(NANOPRINTF_USE_FLOAT_SINGLE_PRECISION) + #define NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS 1 + #define NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS 1 + #define NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS 1 + #define NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS 0 + #define NANOPRINTF_USE_SMALL_FORMAT_SPECIFIERS 1 + #define NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS 0 + #define NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS 0 + #define NANOPRINTF_USE_ALT_FORM_FLAG 1 + #define NANOPRINTF_USE_FLOAT_SINGLE_PRECISION 0 +#endif + +// Single-precision mode defaults to off unless explicitly enabled. +#ifndef NANOPRINTF_USE_FLOAT_SINGLE_PRECISION + #define NANOPRINTF_USE_FLOAT_SINGLE_PRECISION 0 +#endif + +// Optional flag, defaults to 0 if not explicitly configured. +#ifndef NANOPRINTF_USE_FLOAT_HEX_FORMAT_SPECIFIER + #define NANOPRINTF_USE_FLOAT_HEX_FORMAT_SPECIFIER 0 +#endif + +// If anything's been configured, everything must be configured. +#ifndef NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS + #error NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS must be #defined to 0 or 1 +#endif +#ifndef NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS + #error NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS must be #defined to 0 or 1 +#endif +#ifndef NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS + #error NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS must be #defined to 0 or 1 +#endif +#ifndef NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS + #error NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS must be #defined to 0 or 1 +#endif +#ifndef NANOPRINTF_USE_SMALL_FORMAT_SPECIFIERS + #error NANOPRINTF_USE_SMALL_FORMAT_SPECIFIERS must be #defined to 0 or 1 +#endif +#ifndef NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS + #error NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS must be #defined to 0 or 1 +#endif +#ifndef NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS + #error NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS must be #defined to 0 or 1 +#endif + +// Ensure flags are compatible. +#if (NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1) && \ + (NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 0) + #error Precision format specifiers must be enabled if float support is enabled. +#endif + +#if (NANOPRINTF_USE_FLOAT_HEX_FORMAT_SPECIFIER == 1) && \ + (NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 0) + #error Float format specifiers must be enabled if float hex support is enabled. +#endif + +#if (NANOPRINTF_USE_FLOAT_SINGLE_PRECISION == 1) && \ + (NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 0) + #error Single precision requires float format specifiers to be enabled. +#endif + +#if NANOPRINTF_USE_FLOAT_SINGLE_PRECISION == 1 + #if defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL + #error Single-precision float mode requires /Zc:preprocessor on MSVC. + #endif + #ifdef __cplusplus + #if __cplusplus < 201103L && !defined(_MSC_VER) + #error Single-precision float wrapping requires C++11 or later. + #endif + #else + #if !(defined(__GNUC__) || defined(__clang__)) + #if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 201112L) + #error Single-precision float wrapping requires C11 or later (or GCC/Clang). + #endif + #endif + #endif +#endif + +// The conversion buffer must fit at least UINT64_MAX in octal format with the leading '0'. +// When floats are enabled, a larger buffer is needed for values like FLT_MAX / DBL_MAX. +#ifndef NANOPRINTF_CONVERSION_BUFFER_SIZE + #if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + #define NANOPRINTF_CONVERSION_BUFFER_SIZE 64 + #else + #define NANOPRINTF_CONVERSION_BUFFER_SIZE 23 + #endif +#endif +#if NANOPRINTF_CONVERSION_BUFFER_SIZE < 23 + #error The size of the conversion buffer must be at least 23 bytes. +#endif + +// intmax_t / uintmax_t require stdint from c99 / c++11 +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + #ifndef _MSC_VER + #ifdef __cplusplus + #if __cplusplus < 201103L + #error large format specifier support requires C++11 or later. + #endif + #else + #if __STDC_VERSION__ < 199409L + #error nanoprintf requires C99 or later. + #endif + #endif + #endif +#endif + +// Figure out if we can disable warnings with pragmas. +#ifdef __clang__ + #define NPF_CLANG 1 + #define NPF_GCC_PAST_4_6 0 +#else + #define NPF_CLANG 0 + #if defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 6))) + #define NPF_GCC_PAST_4_6 1 + #else + #define NPF_GCC_PAST_4_6 0 + #endif +#endif + +#if NPF_CLANG || NPF_GCC_PAST_4_6 + #define NPF_HAVE_GCC_WARNING_PRAGMAS 1 +#else + #define NPF_HAVE_GCC_WARNING_PRAGMAS 0 +#endif + +#if NPF_HAVE_GCC_WARNING_PRAGMAS + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpragmas" + #pragma GCC diagnostic ignored "-Wfloat-equal" + #pragma GCC diagnostic ignored "-Wgnu-statement-expression-from-macro-expansion" + #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" + #pragma GCC diagnostic ignored "-Wpadded" + #pragma GCC diagnostic ignored "-Wpedantic" + #pragma GCC diagnostic ignored "-Wunused-function" + + #ifdef __cplusplus + #pragma GCC diagnostic ignored "-Wold-style-cast" + #endif + + #if NPF_CLANG + #pragma GCC diagnostic ignored "-Wc++98-compat-pedantic" + #pragma GCC diagnostic ignored "-Wcovered-switch-default" + #pragma GCC diagnostic ignored "-Wdeclaration-after-statement" + #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" + #ifndef __APPLE__ + #pragma GCC diagnostic ignored "-Wunsafe-buffer-usage" + #endif + #elif NPF_GCC_PAST_4_6 + #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" + #endif +#endif + +#ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable:4619) // there is no warning number 'number' + // C4619 has to be disabled first! + #pragma warning(disable:4127) // conditional expression is constant + #pragma warning(disable:4505) // unreferenced local function has been removed + #pragma warning(disable:4514) // unreferenced inline function has been removed + #pragma warning(disable:4701) // potentially uninitialized local variable used + #pragma warning(disable:4706) // assignment within conditional expression + #pragma warning(disable:4710) // function not inlined + #pragma warning(disable:4711) // function selected for inline expansion + #pragma warning(disable:4820) // padding added after struct member + #pragma warning(disable:5039) // potentially throwing function passed to extern C function + #pragma warning(disable:5045) // compiler will insert Spectre mitigation for memory load + #pragma warning(disable:5262) // implicit switch fall-through + #pragma warning(disable:26812) // enum type is unscoped +#endif + +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #define NPF_NOINLINE __attribute__((noinline)) + #define NPF_FORCE_INLINE inline __attribute__((always_inline)) + + #define NPF_MIN(X, Y) ({ \ + __typeof__(X) const x = (X); __typeof__(Y) const y = (Y); x <= y ? x : y; }) + #define NPF_MAX(X, Y) ({ \ + __typeof__(X) const x = (X); __typeof__(Y) const y = (Y); x >= y ? x : y; }) +#else + #if defined(_MSC_VER) + #define NPF_NOINLINE __declspec(noinline) + #define NPF_FORCE_INLINE inline __forceinline + #else + #define NPF_NOINLINE + #define NPF_FORCE_INLINE + #endif + + #define NPF_MIN(X, Y) ((X) <= (Y) ? (X) : (Y)) + #define NPF_MAX(X, Y) ((X) >= (Y) ? (X) : (Y)) +#endif + +#if (NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1) || \ + (NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1) +enum { + NPF_FMT_SPEC_OPT_NONE, + NPF_FMT_SPEC_OPT_LITERAL, + NPF_FMT_SPEC_OPT_STAR, +}; +#endif + +enum { + NPF_FMT_SPEC_LEN_MOD_NONE, +#if NANOPRINTF_USE_SMALL_FORMAT_SPECIFIERS == 1 + NPF_FMT_SPEC_LEN_MOD_SHORT, // 'h' + NPF_FMT_SPEC_LEN_MOD_CHAR, // 'hh' +#endif + NPF_FMT_SPEC_LEN_MOD_LONG, // 'l' + NPF_FMT_SPEC_LEN_MOD_LONG_DOUBLE, // 'L' +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + NPF_FMT_SPEC_LEN_MOD_LARGE_LONG_LONG, // 'll' + NPF_FMT_SPEC_LEN_MOD_LARGE_INTMAX, // 'j' + NPF_FMT_SPEC_LEN_MOD_LARGE_SIZET, // 'z' + NPF_FMT_SPEC_LEN_MOD_LARGE_PTRDIFFT, // 't' +#endif +}; + +enum { + NPF_FMT_SPEC_CONV_NONE, + NPF_FMT_SPEC_CONV_PERCENT, // '%' + NPF_FMT_SPEC_CONV_CHAR, // 'c' + NPF_FMT_SPEC_CONV_STRING, // 's' + NPF_FMT_SPEC_CONV_SIGNED_INT, // 'i', 'd' +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 + NPF_FMT_SPEC_CONV_BINARY, // 'b' +#endif + NPF_FMT_SPEC_CONV_OCTAL, // 'o' + NPF_FMT_SPEC_CONV_HEX_INT, // 'x', 'X' + NPF_FMT_SPEC_CONV_UNSIGNED_INT, // 'u' + NPF_FMT_SPEC_CONV_POINTER, // 'p' +#if NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS == 1 + NPF_FMT_SPEC_CONV_WRITEBACK, // 'n' +#endif +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + NPF_FMT_SPEC_CONV_FLOAT_DEC, // 'f', 'F' + NPF_FMT_SPEC_CONV_FLOAT_SCI, // 'e', 'E' + NPF_FMT_SPEC_CONV_FLOAT_SHORTEST, // 'g', 'G' +#if NANOPRINTF_USE_FLOAT_HEX_FORMAT_SPECIFIER == 1 + NPF_FMT_SPEC_CONV_FLOAT_HEX, // 'a', 'A' +#endif +#endif +}; + +typedef struct npf_format_spec { +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + int field_width; +#endif +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + int prec; + uint8_t prec_opt; +#endif +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + uint8_t field_width_opt; + char left_justified; // '-' + char leading_zero_pad; // '0' +#endif + char prepend; // ' ' or '+' +#if NANOPRINTF_USE_ALT_FORM_FLAG == 1 + char alt_form; // '#' +#endif + char case_adjust; // 'a' - 'A' , or 0 (must be non-negative to work) + uint8_t length_modifier; + uint8_t conv_spec; +} npf_format_spec_t; + +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + typedef intmax_t npf_int_t; + typedef uintmax_t npf_uint_t; +#elif ULONG_MAX > UINTPTR_MAX + typedef long npf_int_t; + typedef unsigned long npf_uint_t; +#else + typedef intptr_t npf_int_t; + typedef uintptr_t npf_uint_t; +#endif + +typedef struct npf_bufputc_ctx { + char *dst; + size_t len; + size_t cur; +} npf_bufputc_ctx_t; + +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + typedef char npf_size_is_ptrdiff[(sizeof(size_t) == sizeof(ptrdiff_t)) ? 1 : -1]; + typedef ptrdiff_t npf_ssize_t; + typedef size_t npf_uptrdiff_t; +#endif + +#ifdef _MSC_VER + #include +#endif + +static int npf_parse_format_spec(char const *format, npf_format_spec_t *out_spec) { + char const *cur = format; + +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + out_spec->left_justified = 0; + out_spec->leading_zero_pad = 0; +#endif + out_spec->case_adjust = 'a' - 'A'; // lowercase + out_spec->prepend = 0; +#if NANOPRINTF_USE_ALT_FORM_FLAG == 1 + out_spec->alt_form = 0; +#endif + + while (*++cur) { // cur points at the leading '%' character + switch (*cur) { // Optional flags +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + case '-': out_spec->left_justified = '-'; continue; + case '0': out_spec->leading_zero_pad = 1; continue; +#endif + case '+': out_spec->prepend = '+'; continue; + case ' ': if (out_spec->prepend == 0) { out_spec->prepend = ' '; } continue; +#if NANOPRINTF_USE_ALT_FORM_FLAG == 1 + case '#': out_spec->alt_form = '#'; continue; +#endif + default: break; + } + break; + } + +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + out_spec->field_width = 0; + out_spec->field_width_opt = NPF_FMT_SPEC_OPT_NONE; + if (*cur == '*') { + out_spec->field_width_opt = NPF_FMT_SPEC_OPT_STAR; + ++cur; + } else { + while ((*cur >= '0') && (*cur <= '9')) { + out_spec->field_width_opt = NPF_FMT_SPEC_OPT_LITERAL; + out_spec->field_width = (out_spec->field_width * 10) + (*cur++ - '0'); + } + } +#endif + +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + out_spec->prec = 0; + out_spec->prec_opt = NPF_FMT_SPEC_OPT_NONE; + if (*cur == '.') { + ++cur; + if (*cur == '*') { + out_spec->prec_opt = NPF_FMT_SPEC_OPT_STAR; + ++cur; + } else { + if (*cur == '-') { + ++cur; + } else { + out_spec->prec_opt = NPF_FMT_SPEC_OPT_LITERAL; + } + while ((*cur >= '0') && (*cur <= '9')) { + out_spec->prec = (out_spec->prec * 10) + (*cur++ - '0'); + } + } + } +#endif + + uint_fast8_t tmp_conv = NPF_FMT_SPEC_CONV_NONE; + out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_NONE; + switch (*cur++) { // Length modifier +#if NANOPRINTF_USE_SMALL_FORMAT_SPECIFIERS == 1 + case 'h': + out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_SHORT; + if (*cur == 'h') { + out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_CHAR; + ++cur; + } + break; +#endif + case 'l': + out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LONG; +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + if (*cur == 'l') { + out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LARGE_LONG_LONG; + ++cur; + } +#endif + break; +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + case 'L': out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LONG_DOUBLE; break; +#endif +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + case 'j': out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LARGE_INTMAX; break; + case 'z': out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LARGE_SIZET; break; + case 't': out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LARGE_PTRDIFFT; break; +#endif + default: --cur; break; + } + + switch (*cur++) { // Conversion specifier + case '%': out_spec->conv_spec = NPF_FMT_SPEC_CONV_PERCENT; + break; + + case 'c': out_spec->conv_spec = NPF_FMT_SPEC_CONV_CHAR; + break; + + case 's': out_spec->conv_spec = NPF_FMT_SPEC_CONV_STRING; + break; + + case 'i': + case 'd': tmp_conv = NPF_FMT_SPEC_CONV_SIGNED_INT; goto finish; + case 'o': tmp_conv = NPF_FMT_SPEC_CONV_OCTAL; goto finish; + case 'u': tmp_conv = NPF_FMT_SPEC_CONV_UNSIGNED_INT; goto finish; + case 'X': out_spec->case_adjust = 0; + case 'x': tmp_conv = NPF_FMT_SPEC_CONV_HEX_INT; goto finish; + finish: + out_spec->conv_spec = (uint8_t)tmp_conv; + break; + +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + case 'F': out_spec->case_adjust = 0; + case 'f': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_FLOAT_DEC; + break; + + case 'E': out_spec->case_adjust = 0; + case 'e': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_FLOAT_SCI; + break; + + case 'G': out_spec->case_adjust = 0; + case 'g': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_FLOAT_SHORTEST; + break; + +#if NANOPRINTF_USE_FLOAT_HEX_FORMAT_SPECIFIER == 1 + case 'A': out_spec->case_adjust = 0; + case 'a': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_FLOAT_HEX; + break; +#endif +#endif + +#if NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS == 1 + case 'n': + // todo: reject string if flags or width or precision exist + out_spec->conv_spec = NPF_FMT_SPEC_CONV_WRITEBACK; + break; +#endif + + case 'p': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_POINTER; + break; + +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 + case 'B': + out_spec->case_adjust = 0; + case 'b': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_BINARY; + break; +#endif + + default: return 0; + } + + return (int)(cur - format); +} + +static NPF_NOINLINE int npf_utoa_rev( + npf_uint_t val, char *buf, uint_fast8_t base, char case_adj) { + uint_fast8_t n = 0; + do { + int_fast8_t const d = (int_fast8_t)(val % base); + *buf++ = (char)(((d < 10) ? '0' : ('A' - 10 + case_adj)) + d); + ++n; + val /= base; + } while (val); + return (int)n; +} + +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + +#include + +#if NANOPRINTF_USE_FLOAT_SINGLE_PRECISION == 1 + typedef float npf_real_t; + #define NPF_REAL_MANT_DIG FLT_MANT_DIG + #define NPF_REAL_MAX_EXP FLT_MAX_EXP +#else + typedef double npf_real_t; + #define NPF_REAL_MANT_DIG DBL_MANT_DIG + #define NPF_REAL_MAX_EXP DBL_MAX_EXP +#endif + +#if (NPF_REAL_MANT_DIG <= 11) && (NPF_REAL_MAX_EXP <= 16) + typedef uint_fast16_t npf_real_bin_t; + typedef int_fast8_t npf_ftoa_exp_t; +#elif (NPF_REAL_MANT_DIG <= 24) && (NPF_REAL_MAX_EXP <= 128) + typedef uint_fast32_t npf_real_bin_t; + typedef int_fast16_t npf_ftoa_exp_t; +#elif (NPF_REAL_MANT_DIG <= 53) && (NPF_REAL_MAX_EXP <= 1024) + typedef uint_fast64_t npf_real_bin_t; + typedef int_fast16_t npf_ftoa_exp_t; +#else + #error Unsupported width of the real type. +#endif + +// The floating point conversion code works with an unsigned integer type of any size. +#ifndef NANOPRINTF_CONVERSION_FLOAT_TYPE + #define NANOPRINTF_CONVERSION_FLOAT_TYPE unsigned int +#endif +typedef NANOPRINTF_CONVERSION_FLOAT_TYPE npf_ftoa_man_t; + +#if (NANOPRINTF_CONVERSION_BUFFER_SIZE <= UINT_FAST8_MAX) && (UINT_FAST8_MAX <= INT_MAX) + typedef uint_fast8_t npf_ftoa_dec_t; +#else + typedef int npf_ftoa_dec_t; +#endif + +enum { + NPF_REAL_EXP_MASK = NPF_REAL_MAX_EXP * 2 - 1, + NPF_REAL_EXP_BIAS = NPF_REAL_MAX_EXP - 1, + NPF_REAL_MAN_BITS = NPF_REAL_MANT_DIG - 1, + NPF_REAL_BIN_BITS = sizeof(npf_real_bin_t) * CHAR_BIT, + NPF_REAL_SIGN_POS = sizeof(npf_real_t) * CHAR_BIT - 1, + NPF_FTOA_MAN_BITS = sizeof(npf_ftoa_man_t) * CHAR_BIT, + NPF_FTOA_SHIFT_BITS = + ((NPF_FTOA_MAN_BITS < NPF_REAL_MANT_DIG) ? NPF_FTOA_MAN_BITS : NPF_REAL_MANT_DIG) - 1 +}; + +/* Generally, floating-point conversion implementations use + grisu2 (https://bit.ly/2JgMggX) and ryu (https://bit.ly/2RLXSg0) algorithms, + which are mathematically exact and fast, but require large lookup tables. + + This implementation was inspired by Wojciech Muła's (zdjęcia@garnek.pl) + algorithm (http://0x80.pl/notesen/2015-12-29-float-to-string.html) and + extended further by adding dynamic scaling and configurable integer width by + Oskars Rubenis (https://github.com/Okarss). */ + +static NPF_FORCE_INLINE npf_real_bin_t npf_real_to_int_rep(npf_real_t f) { + // Union-cast is UB pre-C11 and in all C++; the compiler optimizes the code below. + npf_real_bin_t bin = 0; + char const *src = (char const *)&f; + char *dst = (char *)&bin; + for (uint_fast8_t i = 0; i < sizeof(f); ++i) { dst[i] = src[i]; } + return bin; +} + +static int npf_ftoa_rev(char *buf, npf_format_spec_t const *spec, npf_real_t f) { + char const *ret = NULL; + npf_real_bin_t bin = npf_real_to_int_rep(f); + + // Unsigned -> signed int casting is IB and can raise a signal but generally doesn't. + npf_ftoa_exp_t exp = + (npf_ftoa_exp_t)((npf_ftoa_exp_t)(bin >> NPF_REAL_MAN_BITS) & NPF_REAL_EXP_MASK); + + bin &= ((npf_real_bin_t)0x1 << NPF_REAL_MAN_BITS) - 1; + if (exp == (npf_ftoa_exp_t)NPF_REAL_EXP_MASK) { // special value + ret = (bin) ? "NAN" : "FNI"; + goto exit; + } + if (spec->prec > (NANOPRINTF_CONVERSION_BUFFER_SIZE - 2)) { goto exit; } + if (exp) { // normal number + bin |= (npf_real_bin_t)0x1 << NPF_REAL_MAN_BITS; + } else { // subnormal number + ++exp; + } + exp = (npf_ftoa_exp_t)(exp - NPF_REAL_EXP_BIAS); + + uint_fast8_t carry; carry = 0; + npf_ftoa_dec_t end, dec; dec = (npf_ftoa_dec_t)spec->prec; + if (dec +#if NANOPRINTF_USE_ALT_FORM_FLAG == 1 + || spec->alt_form +#endif + ) { + buf[dec++] = '.'; + } + + { // Integer part + npf_ftoa_man_t man_i; + + if (exp >= 0) { + int_fast8_t shift_i = + (int_fast8_t)((exp > NPF_FTOA_SHIFT_BITS) ? (int)NPF_FTOA_SHIFT_BITS : exp); + npf_ftoa_exp_t exp_i = (npf_ftoa_exp_t)(exp - shift_i); + shift_i = (int_fast8_t)(NPF_REAL_MAN_BITS - shift_i); + man_i = (npf_ftoa_man_t)(bin >> shift_i); + + if (exp_i) { + if (shift_i) { + carry = (bin >> (shift_i - 1)) & 0x1; + } + exp = NPF_REAL_MAN_BITS; // invalidate the fraction part + } + + // Scale the exponent from base-2 to base-10. + for (; exp_i; --exp_i) { + if (!(man_i & ((npf_ftoa_man_t)0x1 << (NPF_FTOA_MAN_BITS - 1)))) { + man_i = (npf_ftoa_man_t)(man_i << 1); + man_i = (npf_ftoa_man_t)(man_i | carry); carry = 0; + } else { + if (dec >= NANOPRINTF_CONVERSION_BUFFER_SIZE) { goto exit; } + buf[dec++] = '0'; + carry = (((uint_fast8_t)(man_i % 5) + carry) > 2); + man_i /= 5; + } + } + } else { + man_i = 0; + } + end = dec; + + do { // Print the integer + if (end >= NANOPRINTF_CONVERSION_BUFFER_SIZE) { goto exit; } + buf[end++] = (char)('0' + (char)(man_i % 10)); + man_i /= 10; + } while (man_i); + } + + { // Fraction part + npf_ftoa_man_t man_f; + npf_ftoa_dec_t dec_f = (npf_ftoa_dec_t)spec->prec; + + if (exp < NPF_REAL_MAN_BITS) { + int_fast8_t shift_f = (int_fast8_t)((exp < 0) ? -1 : exp); + npf_ftoa_exp_t exp_f = (npf_ftoa_exp_t)(exp - shift_f); + npf_real_bin_t bin_f = + bin << ((NPF_REAL_BIN_BITS - NPF_REAL_MAN_BITS) + shift_f); + + // This if-else statement can be completely optimized at compile time. + if (NPF_REAL_BIN_BITS > NPF_FTOA_MAN_BITS) { + man_f = (npf_ftoa_man_t)(bin_f >> ((unsigned)(NPF_REAL_BIN_BITS - + NPF_FTOA_MAN_BITS) % + NPF_REAL_BIN_BITS)); + carry = (uint_fast8_t)((bin_f >> ((unsigned)(NPF_REAL_BIN_BITS - + NPF_FTOA_MAN_BITS - 1) % + NPF_REAL_BIN_BITS)) & 0x1); + } else { + man_f = (npf_ftoa_man_t)((npf_ftoa_man_t)bin_f + << ((unsigned)(NPF_FTOA_MAN_BITS - + NPF_REAL_BIN_BITS) % NPF_FTOA_MAN_BITS)); + carry = 0; + } + + // Scale the exponent from base-2 to base-10 and prepare the first digit. + for (uint_fast8_t digit = 0; dec_f && (exp_f < 4); ++exp_f) { + if ((man_f > ((npf_ftoa_man_t)-4 / 5)) || digit) { + carry = (uint_fast8_t)(man_f & 0x1); + man_f = (npf_ftoa_man_t)(man_f >> 1); + } else { + man_f = (npf_ftoa_man_t)(man_f * 5); + if (carry) { man_f = (npf_ftoa_man_t)(man_f + 3); carry = 0; } + if (exp_f < 0) { + buf[--dec_f] = '0'; + } else { + ++digit; + } + } + } + man_f = (npf_ftoa_man_t)(man_f + carry); + carry = (exp_f >= 0); + dec = 0; + } else { + man_f = 0; + } + + if (dec_f) { + // Print the fraction + for (;;) { + buf[--dec_f] = (char)('0' + (char)(man_f >> (NPF_FTOA_MAN_BITS - 4))); + man_f = (npf_ftoa_man_t)(man_f & ~((npf_ftoa_man_t)0xF << (NPF_FTOA_MAN_BITS - 4))); + if (!dec_f) { break; } + man_f = (npf_ftoa_man_t)(man_f * 10); + } + man_f = (npf_ftoa_man_t)(man_f << 4); + } + if (exp < NPF_REAL_MAN_BITS) { + carry &= (uint_fast8_t)(man_f >> (NPF_FTOA_MAN_BITS - 1)); + } + } + + // Round the number + for (; carry; ++dec) { + if (dec >= NANOPRINTF_CONVERSION_BUFFER_SIZE) { goto exit; } + if (dec >= end) { buf[end++] = '0'; } + if (buf[dec] == '.') { continue; } + carry = (buf[dec] == '9'); + buf[dec] = (char)(carry ? '0' : (buf[dec] + 1)); + } + + return (int)end; +exit: + if (!ret) { ret = "RRE"; } + uint_fast8_t i; + for (i = 0; ret[i]; ++i) { buf[i] = (char)(ret[i] + spec->case_adjust); } + return -(int)i; +} + +#if NANOPRINTF_USE_FLOAT_HEX_FORMAT_SPECIFIER == 1 + +// Hex float always operates on IEEE 754 binary64 (double). +// When not in single-precision mode, npf_real_* already handles double. +#if NANOPRINTF_USE_FLOAT_SINGLE_PRECISION == 1 +typedef uint_fast64_t npf_double_bin_t; +enum { + NPF_DOUBLE_EXP_MASK = 2047, + NPF_DOUBLE_EXP_BIAS = 1023, + NPF_DOUBLE_MAN_BITS = 52, +}; +static NPF_FORCE_INLINE npf_double_bin_t npf_double_to_int_rep(double f) { + npf_double_bin_t bin = 0; + char const *src = (char const *)&f; + char *dst = (char *)&bin; + for (uint_fast8_t i = 0; i < sizeof(f); ++i) { dst[i] = src[i]; } + return bin; +} +#else +typedef npf_real_bin_t npf_double_bin_t; +#define NPF_DOUBLE_EXP_MASK NPF_REAL_EXP_MASK +#define NPF_DOUBLE_EXP_BIAS NPF_REAL_EXP_BIAS +#define NPF_DOUBLE_MAN_BITS NPF_REAL_MAN_BITS +#define npf_double_to_int_rep(f) npf_real_to_int_rep(f) +#endif + +static NPF_NOINLINE int npf_atoa_rev( + char *buf, npf_format_spec_t const *spec, double f) { + npf_double_bin_t bin = npf_double_to_int_rep(f); + npf_ftoa_exp_t exp = + (npf_ftoa_exp_t)((npf_ftoa_exp_t)(bin >> NPF_DOUBLE_MAN_BITS) & NPF_DOUBLE_EXP_MASK); + bin &= ((npf_double_bin_t)0x1 << NPF_DOUBLE_MAN_BITS) - 1; + + if (exp == (npf_ftoa_exp_t)NPF_DOUBLE_EXP_MASK) { return 0; } // caller uses ftoa_rev + + if (exp) { + bin |= (npf_double_bin_t)0x1 << NPF_DOUBLE_MAN_BITS; + exp = (npf_ftoa_exp_t)(exp - NPF_DOUBLE_EXP_BIAS); + } else if (bin) { + exp = (npf_ftoa_exp_t)(1 - NPF_DOUBLE_EXP_BIAS); + } + + { int const n_frac_dig = (NPF_DOUBLE_MAN_BITS + 3) / 4; + int const prec = NPF_MIN(spec->prec, n_frac_dig); + int end, i; + + // Discard low nibbles and round (only constant shifts of 3 and 4) + { npf_double_bin_t carry = 0; + for (i = n_frac_dig - prec; i > 0; --i) { + carry = (bin >> 3) & 1; + bin >>= 4; + } + bin += carry; + } + + { npf_ftoa_exp_t const ae = (exp < 0) ? (npf_ftoa_exp_t)-exp : exp; + end = npf_utoa_rev((npf_uint_t)ae, buf, 10, 0); + buf[end++] = (exp < 0) ? '-' : '+'; + buf[end++] = (char)('P' + spec->case_adjust); + } + + for (i = 0; i < prec; ++i) { + int_fast8_t const d = (int_fast8_t)(bin & 0xF); + buf[end++] = (char)(((d < 10) ? '0' : ('A' - 10 + spec->case_adjust)) + d); + bin >>= 4; + } + + if (prec > 0 +#if NANOPRINTF_USE_ALT_FORM_FLAG == 1 + || spec->alt_form +#endif + ) { buf[end++] = '.'; } + + buf[end++] = (char)('0' + (int_fast8_t)(bin & 0xF)); + return end; + } +} + +#endif // NANOPRINTF_USE_FLOAT_HEX_FORMAT_SPECIFIER + +#endif // NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS + +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 +static int npf_bin_len(npf_uint_t u) { + // Return the length of the binary string format of 'u', preferring intrinsics. + if (!u) { return 1; } + +#ifdef _MSC_VER // Win64, use _BSR64 for everything. If x86, use _BSR when non-large. + #ifdef _M_X64 + #define NPF_HAVE_BUILTIN_CLZ + #define NPF_CLZ _BitScanReverse64 + #elif NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 0 + #define NPF_HAVE_BUILTIN_CLZ + #define NPF_CLZ _BitScanReverse + #endif + #ifdef NPF_HAVE_BUILTIN_CLZ + unsigned long idx; + NPF_CLZ(&idx, u); + return (int)(idx + 1); + #endif +#elif NPF_CLANG || NPF_GCC_PAST_4_6 + #define NPF_HAVE_BUILTIN_CLZ + #if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + #define NPF_CLZ(X) ((sizeof(long long) * CHAR_BIT) - (size_t)__builtin_clzll(X)) + #else + #define NPF_CLZ(X) ((sizeof(long) * CHAR_BIT) - (size_t)__builtin_clzl(X)) + #endif + return (int)NPF_CLZ(u); +#endif + +#ifndef NPF_HAVE_BUILTIN_CLZ + int n; + for (n = 0; u; ++n, u >>= 1); // slow but small software fallback + return n; +#else + #undef NPF_HAVE_BUILTIN_CLZ + #undef NPF_CLZ +#endif +} +#endif + +static void npf_bufputc(int c, void *ctx) { + npf_bufputc_ctx_t *bpc = (npf_bufputc_ctx_t *)ctx; + if (bpc->cur < bpc->len) { bpc->dst[bpc->cur++] = (char)c; } +} + +static void npf_bufputc_nop(int c, void *ctx) { (void)c; (void)ctx; } + +typedef struct npf_cnt_putc_ctx { + npf_putc pc; + void *ctx; + int n; +} npf_cnt_putc_ctx_t; + +static void npf_putc_cnt(int c, void *ctx) { + npf_cnt_putc_ctx_t *pc_cnt = (npf_cnt_putc_ctx_t *)ctx; + ++pc_cnt->n; + pc_cnt->pc(c, pc_cnt->ctx); // sibling-call optimization +} + +#define NPF_PUTC(VAL) do { npf_putc_cnt((int)(VAL), &pc_cnt); } while (0) + +#define NPF_EXTRACT(MOD, CAST_TO, EXTRACT_AS) \ + case NPF_FMT_SPEC_LEN_MOD_##MOD: val = (CAST_TO)va_arg(args, EXTRACT_AS); break + +#define NPF_WRITEBACK(MOD, TYPE) \ + case NPF_FMT_SPEC_LEN_MOD_##MOD: *(va_arg(args, TYPE *)) = (TYPE)pc_cnt.n; break + +int npf_vpprintf(npf_putc pc, void *pc_ctx, char const *format, va_list args) { + npf_format_spec_t fs; + char const *cur = format; + npf_cnt_putc_ctx_t pc_cnt; + pc_cnt.pc = pc; + pc_cnt.ctx = pc_ctx; + pc_cnt.n = 0; + + while (*cur) { + int const fs_len = (*cur != '%') ? 0 : npf_parse_format_spec(cur, &fs); + if (!fs_len) { NPF_PUTC(*cur++); continue; } + cur += fs_len; + + // Extract star-args immediately +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + if (fs.field_width_opt == NPF_FMT_SPEC_OPT_STAR) { + fs.field_width = va_arg(args, int); + if (fs.field_width < 0) { + fs.field_width = -fs.field_width; + fs.left_justified = 1; + } + } +#endif +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + if (fs.prec_opt == NPF_FMT_SPEC_OPT_STAR) { + fs.prec = va_arg(args, int); + if (fs.prec < 0) { fs.prec = 0; fs.prec_opt = NPF_FMT_SPEC_OPT_NONE; } + } +#endif + +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + // Set default precision (we can do that only now that we have extracted the + // argument-provided precision (".*"), and know whether to ignore that or not + #if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + if (fs.prec_opt == NPF_FMT_SPEC_OPT_NONE) { + switch (fs.conv_spec) { + case NPF_FMT_SPEC_CONV_FLOAT_DEC: + case NPF_FMT_SPEC_CONV_FLOAT_SCI: + case NPF_FMT_SPEC_CONV_FLOAT_SHORTEST: + fs.prec = 6; + break; +#if NANOPRINTF_USE_FLOAT_HEX_FORMAT_SPECIFIER == 1 + case NPF_FMT_SPEC_CONV_FLOAT_HEX: + fs.prec = (NPF_DOUBLE_MAN_BITS + 3) / 4; + break; +#endif + default: + break; + } + } + #endif + + if ((fs.prec_opt == NPF_FMT_SPEC_OPT_NONE) && + (fs.conv_spec == NPF_FMT_SPEC_CONV_POINTER)) { + fs.prec = (sizeof(void *) * CHAR_BIT + 3) / 4; + } +#endif + +#if (NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1) && \ + (NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1) + // For d i o u x X, the '0' flag must be ignored if a precision is provided + if (fs.prec_opt != NPF_FMT_SPEC_OPT_NONE) { + switch (fs.conv_spec) { + case NPF_FMT_SPEC_CONV_SIGNED_INT: + case NPF_FMT_SPEC_CONV_OCTAL: + case NPF_FMT_SPEC_CONV_HEX_INT: + case NPF_FMT_SPEC_CONV_UNSIGNED_INT: + fs.leading_zero_pad = 0; + break; + default: + break; + } + } +#endif + + union { char cbuf_mem[NANOPRINTF_CONVERSION_BUFFER_SIZE]; npf_uint_t binval; } u; + char *cbuf = u.cbuf_mem, sign_c = 0; + int cbuf_len = 0; + char need_0x = 0; +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + int field_pad = 0; + char pad_c = 0; +#endif +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + int prec_pad = 0; +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + uint_fast8_t zero = 0; +#endif +#endif + + // Extract and convert the argument to string, point cbuf at the text. + switch (fs.conv_spec) { + case NPF_FMT_SPEC_CONV_PERCENT: + *cbuf = '%'; + cbuf_len = 1; + break; + + case NPF_FMT_SPEC_CONV_CHAR: + *cbuf = (char)va_arg(args, int); + cbuf_len = 1; + break; + + case NPF_FMT_SPEC_CONV_STRING: { + cbuf = va_arg(args, char *); +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + for (char const *s = cbuf; + ((fs.prec_opt == NPF_FMT_SPEC_OPT_NONE) || (cbuf_len < fs.prec)) && cbuf && *s; + ++s, ++cbuf_len); +#else + for (char const *s = cbuf; cbuf && *s; ++s, ++cbuf_len); // strlen +#endif + } break; + + case NPF_FMT_SPEC_CONV_SIGNED_INT: { + npf_int_t val = 0; + switch (fs.length_modifier) { + NPF_EXTRACT(NONE, int, int); +#if NANOPRINTF_USE_SMALL_FORMAT_SPECIFIERS == 1 + NPF_EXTRACT(SHORT, short, int); + NPF_EXTRACT(CHAR, signed char, int); +#endif + NPF_EXTRACT(LONG, long, long); +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + NPF_EXTRACT(LARGE_LONG_LONG, long long, long long); + NPF_EXTRACT(LARGE_INTMAX, intmax_t, intmax_t); + NPF_EXTRACT(LARGE_SIZET, npf_ssize_t, npf_ssize_t); + NPF_EXTRACT(LARGE_PTRDIFFT, ptrdiff_t, ptrdiff_t); +#endif + default: break; + } + + sign_c = (val < 0) ? '-' : fs.prepend; + +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + zero = !val; +#endif + // special case, if prec and value are 0, skip + if (!val && (fs.prec_opt != NPF_FMT_SPEC_OPT_NONE) && !fs.prec) { + cbuf_len = 0; + } else +#endif + { + npf_uint_t uval = (npf_uint_t)val; + if (val < 0) { uval = 0 - uval; } + cbuf_len = npf_utoa_rev(uval, cbuf, 10, fs.case_adjust); + } + } break; + +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 + case NPF_FMT_SPEC_CONV_BINARY: +#endif + case NPF_FMT_SPEC_CONV_OCTAL: + case NPF_FMT_SPEC_CONV_HEX_INT: + case NPF_FMT_SPEC_CONV_UNSIGNED_INT: + case NPF_FMT_SPEC_CONV_POINTER: { + npf_uint_t val = 0; + + if (fs.conv_spec == NPF_FMT_SPEC_CONV_POINTER) { + val = (npf_uint_t)(uintptr_t)va_arg(args, void *); + } else { + switch (fs.length_modifier) { + NPF_EXTRACT(NONE, unsigned, unsigned); +#if NANOPRINTF_USE_SMALL_FORMAT_SPECIFIERS == 1 + NPF_EXTRACT(SHORT, unsigned short, unsigned); + NPF_EXTRACT(CHAR, unsigned char, unsigned); +#endif + NPF_EXTRACT(LONG, unsigned long, unsigned long); +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + NPF_EXTRACT(LARGE_LONG_LONG, unsigned long long, unsigned long long); + NPF_EXTRACT(LARGE_INTMAX, uintmax_t, uintmax_t); + NPF_EXTRACT(LARGE_SIZET, size_t, size_t); + NPF_EXTRACT(LARGE_PTRDIFFT, npf_uptrdiff_t, npf_uptrdiff_t); +#endif + default: break; + } + } + +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + zero = !val; +#endif + if (!val && (fs.prec_opt != NPF_FMT_SPEC_OPT_NONE) && !fs.prec) { + // Zero value and explicitly-requested zero precision means "print nothing". +#if NANOPRINTF_USE_ALT_FORM_FLAG == 1 + if ((fs.conv_spec == NPF_FMT_SPEC_CONV_OCTAL) && fs.alt_form) { + fs.prec = 1; // octal special case, print a single '0' + } +#endif + } else +#endif +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 + if (fs.conv_spec == NPF_FMT_SPEC_CONV_BINARY) { + cbuf_len = npf_bin_len(val); u.binval = val; + } else +#endif + { + uint_fast8_t const base = (fs.conv_spec == NPF_FMT_SPEC_CONV_OCTAL) ? + 8u : ((fs.conv_spec == NPF_FMT_SPEC_CONV_UNSIGNED_INT) ? 10u : 16u); + cbuf_len = npf_utoa_rev(val, cbuf, base, fs.case_adjust); + } + +#if NANOPRINTF_USE_ALT_FORM_FLAG == 1 + if (val && fs.alt_form && (fs.conv_spec == NPF_FMT_SPEC_CONV_OCTAL)) { + cbuf[cbuf_len++] = '0'; // OK to add leading octal '0' immediately. + } + + if (val && fs.alt_form) { // 0x or 0b but can't write it yet. + if ((fs.conv_spec == NPF_FMT_SPEC_CONV_HEX_INT) || + (fs.conv_spec == NPF_FMT_SPEC_CONV_POINTER)) { need_0x = 'X'; } +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 + else if (fs.conv_spec == NPF_FMT_SPEC_CONV_BINARY) { need_0x = 'B'; } +#endif + if (need_0x) { need_0x = (char)(need_0x + fs.case_adjust); } + } +#endif + } break; + +#if NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS == 1 + case NPF_FMT_SPEC_CONV_WRITEBACK: + switch (fs.length_modifier) { + NPF_WRITEBACK(NONE, int); +#if NANOPRINTF_USE_SMALL_FORMAT_SPECIFIERS == 1 + NPF_WRITEBACK(SHORT, short); + NPF_WRITEBACK(CHAR, signed char); +#endif + NPF_WRITEBACK(LONG, long); +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + NPF_WRITEBACK(LARGE_LONG_LONG, long long); + NPF_WRITEBACK(LARGE_INTMAX, intmax_t); + NPF_WRITEBACK(LARGE_SIZET, npf_ssize_t); + NPF_WRITEBACK(LARGE_PTRDIFFT, ptrdiff_t); +#endif + default: break; + } break; +#endif + +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + case NPF_FMT_SPEC_CONV_FLOAT_DEC: + case NPF_FMT_SPEC_CONV_FLOAT_SCI: + case NPF_FMT_SPEC_CONV_FLOAT_SHORTEST: +#if NANOPRINTF_USE_FLOAT_HEX_FORMAT_SPECIFIER == 1 + case NPF_FMT_SPEC_CONV_FLOAT_HEX: +#endif + { + npf_real_t val; +#if NANOPRINTF_USE_FLOAT_SINGLE_PRECISION == 1 + val = va_arg(args, npf_float_t).val; +#else + if (fs.length_modifier == NPF_FMT_SPEC_LEN_MOD_LONG_DOUBLE) { + val = (npf_real_t)va_arg(args, long double); + } else { + val = va_arg(args, double); + } +#endif + + { npf_real_bin_t const b = npf_real_to_int_rep(val); + sign_c = (b >> NPF_REAL_SIGN_POS) ? '-' : fs.prepend; +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + zero = !(b & ~((npf_real_bin_t)1 << NPF_REAL_SIGN_POS)); +#endif + } +#if NANOPRINTF_USE_FLOAT_HEX_FORMAT_SPECIFIER == 1 + if ((fs.conv_spec == NPF_FMT_SPEC_CONV_FLOAT_HEX) && + ((cbuf_len = npf_atoa_rev(cbuf, &fs, (double)val)) > 0)) { + need_0x = (char)('X' + fs.case_adjust); + } else +#endif + { cbuf_len = npf_ftoa_rev(cbuf, &fs, val); } + if (cbuf_len < 0) { // negative means text (not number), so ignore the '0' flag + cbuf_len = -cbuf_len; +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + fs.leading_zero_pad = 0; +#endif + } + } break; +#endif + default: break; + } + +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + // Compute the field width pad character + if (fs.field_width_opt != NPF_FMT_SPEC_OPT_NONE) { + // The '0' flag is only legal with numeric types, and '-' overrides '0'. + if (fs.leading_zero_pad && !fs.left_justified) { +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + if ((fs.prec_opt != NPF_FMT_SPEC_OPT_NONE) && !fs.prec && zero) { + pad_c = ' '; + } else +#endif + { pad_c = '0'; } + } else { pad_c = ' '; } + } +#endif + + // Compute the number of bytes to truncate or '0'-pad. +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + if (fs.conv_spec != NPF_FMT_SPEC_CONV_STRING) { +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + // float precision is after the decimal point + if ((fs.conv_spec != NPF_FMT_SPEC_CONV_FLOAT_DEC) && + (fs.conv_spec != NPF_FMT_SPEC_CONV_FLOAT_SCI) && + (fs.conv_spec != NPF_FMT_SPEC_CONV_FLOAT_SHORTEST) +#if NANOPRINTF_USE_FLOAT_HEX_FORMAT_SPECIFIER == 1 + && (fs.conv_spec != NPF_FMT_SPEC_CONV_FLOAT_HEX) +#endif + ) +#endif + { prec_pad = NPF_MAX(0, fs.prec - cbuf_len); } + } +#endif + +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + // Given the full converted length, how many pad bytes? + field_pad = fs.field_width - cbuf_len - !!sign_c; + if (need_0x) { field_pad -= 2; } +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + field_pad -= prec_pad; +#endif + field_pad = NPF_MAX(0, field_pad); + + // Apply right-justified field width if requested + if (!fs.left_justified && pad_c) { // If leading zeros pad, sign goes first. + if (pad_c == '0') { + if (sign_c) { NPF_PUTC(sign_c); sign_c = 0; } + // Pad byte is '0', write '0x' before '0' pad chars. + if (need_0x) { NPF_PUTC('0'); NPF_PUTC(need_0x); } + } + while (field_pad-- > 0) { NPF_PUTC(pad_c); } + // Pad byte is ' ', write '0x' after ' ' pad chars but before number. + if ((pad_c != '0') && need_0x) { + if (sign_c) { NPF_PUTC(sign_c); sign_c = 0; } + NPF_PUTC('0'); NPF_PUTC(need_0x); + } + } else +#endif + { // no pad, '0x' requested. + if (need_0x) { + if (sign_c) { NPF_PUTC(sign_c); sign_c = 0; } + NPF_PUTC('0'); NPF_PUTC(need_0x); + } + } + + // Write the converted payload + if (fs.conv_spec == NPF_FMT_SPEC_CONV_STRING) { + for (int i = 0; cbuf && (i < cbuf_len); ++i) { NPF_PUTC(cbuf[i]); } + } else { + if (sign_c) { NPF_PUTC(sign_c); } +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + while (prec_pad-- > 0) { NPF_PUTC('0'); } // int precision leads. +#endif +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 + if (fs.conv_spec == NPF_FMT_SPEC_CONV_BINARY) { + while (cbuf_len) { NPF_PUTC('0' + ((u.binval >> --cbuf_len) & 1)); } + } else +#endif + { while (cbuf_len-- > 0) { NPF_PUTC(cbuf[cbuf_len]); } } // payload is reversed + } + +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + if (fs.left_justified && pad_c) { // Apply left-justified field width + while (field_pad-- > 0) { NPF_PUTC(pad_c); } + } +#endif + } + + return pc_cnt.n; +} + +#undef NPF_PUTC +#undef NPF_EXTRACT +#undef NPF_WRITEBACK + +int npf_vsnprintf(char * NPF_RESTRICT buffer, + size_t bufsz, + char const * NPF_RESTRICT format, + va_list vlist) { + npf_bufputc_ctx_t bufputc_ctx; + bufputc_ctx.dst = buffer; + bufputc_ctx.len = bufsz; + bufputc_ctx.cur = 0; + + npf_putc const pc = buffer ? npf_bufputc : npf_bufputc_nop; + int const n = npf_vpprintf(pc, &bufputc_ctx, format, vlist); + + if (buffer && bufsz) { +#ifdef NANOPRINTF_SNPRINTF_SAFE_EMPTY_STRING_ON_OVERFLOW + buffer[(n < 0 || (unsigned)n >= bufsz) ? 0 : n] = '\0'; +#else + buffer[n < 0 ? 0 : NPF_MIN((unsigned)n, bufsz - 1)] = '\0'; +#endif + } + + return n; +} + +int npf_pprintf_(npf_putc pc, + void * NPF_RESTRICT pc_ctx, + char const * NPF_RESTRICT format, + ...) { + va_list val; + va_start(val, format); + int const rv = npf_vpprintf(pc, pc_ctx, format, val); + va_end(val); + return rv; +} + +int npf_snprintf_(char * NPF_RESTRICT buffer, + size_t bufsz, + const char * NPF_RESTRICT format, + ...) { + va_list val; + va_start(val, format); + int const rv = npf_vsnprintf(buffer, bufsz, format, val); + va_end(val); + return rv; +} + +#if NPF_HAVE_GCC_WARNING_PRAGMAS + #pragma GCC diagnostic pop +#endif + +#ifdef _MSC_VER + #pragma warning(pop) +#endif + +#endif // NPF_IMPLEMENTATION_INCLUDED +#endif // NANOPRINTF_IMPLEMENTATION + +// Single-precision argument wrapping and MAP macro expansion machinery. +// The npf_snprintf / npf_pprintf / NPF_MAP_ARGS macros defined above reference +// these, but that's fine: macro bodies are only expanded at the point of +// invocation, not at the point of definition. + +#ifndef NPF_MAP_INCLUDED +#define NPF_MAP_INCLUDED + +#if defined(NANOPRINTF_USE_FLOAT_SINGLE_PRECISION) && NANOPRINTF_USE_FLOAT_SINGLE_PRECISION == 1 + +// NPF__WRAP: wrap float/double args into npf_float_t, pass other types through. +// C++ uses function overloading; C uses _Generic with function-pointer selection. +// _Generic picks a function, then (x) calls it. Non-selected branches are bare +// function names (always valid), sidestepping _Generic's type-check-all-branches +// behavior that would reject (float)(string_ptr) in non-selected branches. +#if defined(__cplusplus) + extern "C++" { + static inline npf_float_t npf__wrap_impl(float f) { npf_float_t r; r.val = f; return r; } + static inline npf_float_t npf__wrap_impl(double d) { npf_float_t r; r.val = (float)d; return r; } + template static inline T npf__wrap_impl(T v) { return v; } + } + #define NPF__WRAP(x) npf__wrap_impl(x) +#elif defined(__GNUC__) || defined(__clang__) + #define NPF__IS_REAL(x) (__builtin_types_compatible_p(__typeof__(x), float) || \ + __builtin_types_compatible_p(__typeof__(x), double)) + #define NPF__WRAP(x) __builtin_choose_expr(NPF__IS_REAL(x), \ + ({ npf_float_t _npf_r; \ + _npf_r.val = (float)__builtin_choose_expr(NPF__IS_REAL(x), (x), 0); \ + _npf_r; }), \ + (x)) +#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) + static inline npf_float_t npf__wf(float f) { npf_float_t r; r.val = f; return r; } + static inline npf_float_t npf__wd(double d) { npf_float_t r; r.val = (float)d; return r; } + static inline npf_float_t npf__wld(long double d) { npf_float_t r; r.val = (float)d; return r; } + static inline int npf__id_i(int v) { return v; } + static inline unsigned npf__id_u(unsigned v) { return v; } + static inline long npf__id_l(long v) { return v; } + static inline unsigned long npf__id_ul(unsigned long v) { return v; } + static inline long long npf__id_ll(long long v) { return v; } + static inline unsigned long long npf__id_ull(unsigned long long v) { return v; } + static inline char npf__id_c(char v) { return v; } + static inline signed char npf__id_sc(signed char v) { return v; } + static inline unsigned char npf__id_uc(unsigned char v) { return v; } + static inline short npf__id_s(short v) { return v; } + static inline unsigned short npf__id_us(unsigned short v) { return v; } + static inline char *npf__id_cp(char *v) { return v; } + static inline char const *npf__id_ccp(char const *v) { return v; } + static inline void *npf__id_vp(void *v) { return v; } + static inline void const *npf__id_cvp(void const *v) { return v; } + static inline int *npf__id_ip(int *v) { return v; } + static inline short *npf__id_sp(short *v) { return v; } + static inline long *npf__id_lp(long *v) { return v; } + static inline long long *npf__id_llp(long long *v) { return v; } + static inline signed char *npf__id_scp(signed char *v) { return v; } + #define NPF__WRAP(x) _Generic((x), \ + float: npf__wf, \ + double: npf__wd, \ + long double: npf__wld, \ + int: npf__id_i, \ + unsigned: npf__id_u, \ + long: npf__id_l, \ + unsigned long: npf__id_ul, \ + long long: npf__id_ll, \ + unsigned long long: npf__id_ull, \ + char: npf__id_c, \ + signed char: npf__id_sc, \ + unsigned char: npf__id_uc, \ + short: npf__id_s, \ + unsigned short: npf__id_us, \ + char *: npf__id_cp, \ + char const *: npf__id_ccp, \ + void *: npf__id_vp, \ + void const *: npf__id_cvp, \ + int *: npf__id_ip, \ + short *: npf__id_sp, \ + long *: npf__id_lp, \ + long long *: npf__id_llp, \ + signed char *: npf__id_scp)(x) +#else + #error Single-precision float wrapping requires C11, GCC/Clang, or C++. +#endif + +// Argument counting (up to 64 variadic args) +#define NPF__NARG(...) NPF__NARG_(__VA_ARGS__, NPF__RSEQ()) +#define NPF__NARG_(...) NPF__ARG_N(__VA_ARGS__) +#define NPF__ARG_N( \ + _1, _2, _3, _4, _5, _6, _7, _8, _9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \ + _21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \ + _41,_42,_43,_44,_45,_46,_47,_48,_49,_50,_51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \ + _61,_62,_63,_64,N,...) N +#define NPF__RSEQ() \ + 64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38, \ + 37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11, \ + 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 + +// Token pasting +#define NPF__CAT(a,b) NPF__CAT_(a,b) +#define NPF__CAT_(a,b) a##b + +// MAP: apply f to each argument +#define NPF__MAP(f,...) NPF__CAT(NPF__MAP_,NPF__NARG(__VA_ARGS__))(f,__VA_ARGS__) +#define NPF__MAP_1(f,a) f(a) +#define NPF__MAP_2(f,a,...) f(a), NPF__MAP_1(f,__VA_ARGS__) +#define NPF__MAP_3(f,a,...) f(a), NPF__MAP_2(f,__VA_ARGS__) +#define NPF__MAP_4(f,a,...) f(a), NPF__MAP_3(f,__VA_ARGS__) +#define NPF__MAP_5(f,a,...) f(a), NPF__MAP_4(f,__VA_ARGS__) +#define NPF__MAP_6(f,a,...) f(a), NPF__MAP_5(f,__VA_ARGS__) +#define NPF__MAP_7(f,a,...) f(a), NPF__MAP_6(f,__VA_ARGS__) +#define NPF__MAP_8(f,a,...) f(a), NPF__MAP_7(f,__VA_ARGS__) +#define NPF__MAP_9(f,a,...) f(a), NPF__MAP_8(f,__VA_ARGS__) +#define NPF__MAP_10(f,a,...) f(a), NPF__MAP_9(f,__VA_ARGS__) +#define NPF__MAP_11(f,a,...) f(a), NPF__MAP_10(f,__VA_ARGS__) +#define NPF__MAP_12(f,a,...) f(a), NPF__MAP_11(f,__VA_ARGS__) +#define NPF__MAP_13(f,a,...) f(a), NPF__MAP_12(f,__VA_ARGS__) +#define NPF__MAP_14(f,a,...) f(a), NPF__MAP_13(f,__VA_ARGS__) +#define NPF__MAP_15(f,a,...) f(a), NPF__MAP_14(f,__VA_ARGS__) +#define NPF__MAP_16(f,a,...) f(a), NPF__MAP_15(f,__VA_ARGS__) +#define NPF__MAP_17(f,a,...) f(a), NPF__MAP_16(f,__VA_ARGS__) +#define NPF__MAP_18(f,a,...) f(a), NPF__MAP_17(f,__VA_ARGS__) +#define NPF__MAP_19(f,a,...) f(a), NPF__MAP_18(f,__VA_ARGS__) +#define NPF__MAP_20(f,a,...) f(a), NPF__MAP_19(f,__VA_ARGS__) +#define NPF__MAP_21(f,a,...) f(a), NPF__MAP_20(f,__VA_ARGS__) +#define NPF__MAP_22(f,a,...) f(a), NPF__MAP_21(f,__VA_ARGS__) +#define NPF__MAP_23(f,a,...) f(a), NPF__MAP_22(f,__VA_ARGS__) +#define NPF__MAP_24(f,a,...) f(a), NPF__MAP_23(f,__VA_ARGS__) +#define NPF__MAP_25(f,a,...) f(a), NPF__MAP_24(f,__VA_ARGS__) +#define NPF__MAP_26(f,a,...) f(a), NPF__MAP_25(f,__VA_ARGS__) +#define NPF__MAP_27(f,a,...) f(a), NPF__MAP_26(f,__VA_ARGS__) +#define NPF__MAP_28(f,a,...) f(a), NPF__MAP_27(f,__VA_ARGS__) +#define NPF__MAP_29(f,a,...) f(a), NPF__MAP_28(f,__VA_ARGS__) +#define NPF__MAP_30(f,a,...) f(a), NPF__MAP_29(f,__VA_ARGS__) +#define NPF__MAP_31(f,a,...) f(a), NPF__MAP_30(f,__VA_ARGS__) +#define NPF__MAP_32(f,a,...) f(a), NPF__MAP_31(f,__VA_ARGS__) +#define NPF__MAP_33(f,a,...) f(a), NPF__MAP_32(f,__VA_ARGS__) +#define NPF__MAP_34(f,a,...) f(a), NPF__MAP_33(f,__VA_ARGS__) +#define NPF__MAP_35(f,a,...) f(a), NPF__MAP_34(f,__VA_ARGS__) +#define NPF__MAP_36(f,a,...) f(a), NPF__MAP_35(f,__VA_ARGS__) +#define NPF__MAP_37(f,a,...) f(a), NPF__MAP_36(f,__VA_ARGS__) +#define NPF__MAP_38(f,a,...) f(a), NPF__MAP_37(f,__VA_ARGS__) +#define NPF__MAP_39(f,a,...) f(a), NPF__MAP_38(f,__VA_ARGS__) +#define NPF__MAP_40(f,a,...) f(a), NPF__MAP_39(f,__VA_ARGS__) +#define NPF__MAP_41(f,a,...) f(a), NPF__MAP_40(f,__VA_ARGS__) +#define NPF__MAP_42(f,a,...) f(a), NPF__MAP_41(f,__VA_ARGS__) +#define NPF__MAP_43(f,a,...) f(a), NPF__MAP_42(f,__VA_ARGS__) +#define NPF__MAP_44(f,a,...) f(a), NPF__MAP_43(f,__VA_ARGS__) +#define NPF__MAP_45(f,a,...) f(a), NPF__MAP_44(f,__VA_ARGS__) +#define NPF__MAP_46(f,a,...) f(a), NPF__MAP_45(f,__VA_ARGS__) +#define NPF__MAP_47(f,a,...) f(a), NPF__MAP_46(f,__VA_ARGS__) +#define NPF__MAP_48(f,a,...) f(a), NPF__MAP_47(f,__VA_ARGS__) +#define NPF__MAP_49(f,a,...) f(a), NPF__MAP_48(f,__VA_ARGS__) +#define NPF__MAP_50(f,a,...) f(a), NPF__MAP_49(f,__VA_ARGS__) +#define NPF__MAP_51(f,a,...) f(a), NPF__MAP_50(f,__VA_ARGS__) +#define NPF__MAP_52(f,a,...) f(a), NPF__MAP_51(f,__VA_ARGS__) +#define NPF__MAP_53(f,a,...) f(a), NPF__MAP_52(f,__VA_ARGS__) +#define NPF__MAP_54(f,a,...) f(a), NPF__MAP_53(f,__VA_ARGS__) +#define NPF__MAP_55(f,a,...) f(a), NPF__MAP_54(f,__VA_ARGS__) +#define NPF__MAP_56(f,a,...) f(a), NPF__MAP_55(f,__VA_ARGS__) +#define NPF__MAP_57(f,a,...) f(a), NPF__MAP_56(f,__VA_ARGS__) +#define NPF__MAP_58(f,a,...) f(a), NPF__MAP_57(f,__VA_ARGS__) +#define NPF__MAP_59(f,a,...) f(a), NPF__MAP_58(f,__VA_ARGS__) +#define NPF__MAP_60(f,a,...) f(a), NPF__MAP_59(f,__VA_ARGS__) +#define NPF__MAP_61(f,a,...) f(a), NPF__MAP_60(f,__VA_ARGS__) +#define NPF__MAP_62(f,a,...) f(a), NPF__MAP_61(f,__VA_ARGS__) +#define NPF__MAP_63(f,a,...) f(a), NPF__MAP_62(f,__VA_ARGS__) +#define NPF__MAP_64(f,a,...) f(a), NPF__MAP_63(f,__VA_ARGS__) + +#endif // NANOPRINTF_USE_FLOAT_SINGLE_PRECISION + +#endif // NPF_MAP_INCLUDED + +/* + nanoprintf is dual-licensed under both the "Unlicense" and the + "Zero-Clause BSD" (0BSD) licenses. The intent of this dual-licensing + structure is to make nanoprintf as consumable as possible in as many + environments / countries / companies as possible without any + encumberances. + + The text of the two licenses follows below: + + ============================== UNLICENSE ============================== + + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + For more information, please refer to + + ================================ 0BSD ================================= + + Copyright (C) 2019- by Charles Nicholson + + Permission to use, copy, modify, and/or distribute this software for + any purpose with or without fee is hereby granted. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ \ No newline at end of file diff --git a/src/io/term/printf.c b/src/io/term/printf.c deleted file mode 100644 index d34279a..0000000 --- a/src/io/term/printf.c +++ /dev/null @@ -1,914 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// \author (c) Marco Paland (info@paland.com) -// 2014-2019, PALANDesign Hannover, Germany -// -// \license The MIT License (MIT) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// -// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on -// embedded systems with a very limited resources. These routines are thread -// safe and reentrant! -// Use this instead of the bloated standard/newlib printf cause these use -// malloc for printf (and may not be thread safe). -// -/////////////////////////////////////////////////////////////////////////////// - -#include -#include - -#include "printf.h" - - -// define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the -// printf_config.h header file -// default: undefined -#ifdef PRINTF_INCLUDE_CONFIG_H -#include "printf_config.h" -#endif - - -// 'ntoa' conversion buffer size, this must be big enough to hold one converted -// numeric number including padded zeros (dynamically created on stack) -// default: 32 byte -#ifndef PRINTF_NTOA_BUFFER_SIZE -#define PRINTF_NTOA_BUFFER_SIZE 32U -#endif - -// 'ftoa' conversion buffer size, this must be big enough to hold one converted -// float number including padded zeros (dynamically created on stack) -// default: 32 byte -#ifndef PRINTF_FTOA_BUFFER_SIZE -#define PRINTF_FTOA_BUFFER_SIZE 32U -#endif - -// support for the floating point type (%f) -// default: activated -#ifndef PRINTF_DISABLE_SUPPORT_FLOAT -#define PRINTF_SUPPORT_FLOAT -#endif - -// support for exponential floating point notation (%e/%g) -// default: activated -#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL -#define PRINTF_SUPPORT_EXPONENTIAL -#endif - -// define the default floating point precision -// default: 6 digits -#ifndef PRINTF_DEFAULT_FLOAT_PRECISION -#define PRINTF_DEFAULT_FLOAT_PRECISION 6U -#endif - -// define the largest float suitable to print with %f -// default: 1e9 -#ifndef PRINTF_MAX_FLOAT -#define PRINTF_MAX_FLOAT 1e9 -#endif - -// support for the long long types (%llu or %p) -// default: activated -#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG -#define PRINTF_SUPPORT_LONG_LONG -#endif - -// support for the ptrdiff_t type (%t) -// ptrdiff_t is normally defined in as long or long long type -// default: activated -#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T -#define PRINTF_SUPPORT_PTRDIFF_T -#endif - -/////////////////////////////////////////////////////////////////////////////// - -// internal flag definitions -#define FLAGS_ZEROPAD (1U << 0U) -#define FLAGS_LEFT (1U << 1U) -#define FLAGS_PLUS (1U << 2U) -#define FLAGS_SPACE (1U << 3U) -#define FLAGS_HASH (1U << 4U) -#define FLAGS_UPPERCASE (1U << 5U) -#define FLAGS_CHAR (1U << 6U) -#define FLAGS_SHORT (1U << 7U) -#define FLAGS_LONG (1U << 8U) -#define FLAGS_LONG_LONG (1U << 9U) -#define FLAGS_PRECISION (1U << 10U) -#define FLAGS_ADAPT_EXP (1U << 11U) - - -// import float.h for DBL_MAX -#if defined(PRINTF_SUPPORT_FLOAT) -#include -#endif - - -// output function type -typedef void (*out_fct_type)(char character, void* buffer, size_t idx, size_t maxlen); - - -// wrapper (used as buffer) for output function type -typedef struct { - void (*fct)(char character, void* arg); - void* arg; -} out_fct_wrap_type; - - -// internal buffer output -static inline void _out_buffer(char character, void* buffer, size_t idx, size_t maxlen) -{ - if (idx < maxlen) { - ((char*)buffer)[idx] = character; - } -} - - -// internal null output -static inline void _out_null(char character, void* buffer, size_t idx, size_t maxlen) -{ - (void)character; (void)buffer; (void)idx; (void)maxlen; -} - - -// internal _putchar wrapper -static inline void _out_char(char character, void* buffer, size_t idx, size_t maxlen) -{ - (void)buffer; (void)idx; (void)maxlen; - if (character) { - _putchar(character); - } -} - - -// internal output function wrapper -static inline void _out_fct(char character, void* buffer, size_t idx, size_t maxlen) -{ - (void)idx; (void)maxlen; - if (character) { - // buffer is the output fct pointer - ((out_fct_wrap_type*)buffer)->fct(character, ((out_fct_wrap_type*)buffer)->arg); - } -} - - -// internal secure strlen -// \return The length of the string (excluding the terminating 0) limited by 'maxsize' -static inline unsigned int _strnlen_s(const char* str, size_t maxsize) -{ - const char* s; - for (s = str; *s && maxsize--; ++s); - return (unsigned int)(s - str); -} - - -// internal test if char is a digit (0-9) -// \return true if char is a digit -static inline bool _is_digit(char ch) -{ - return (ch >= '0') && (ch <= '9'); -} - - -// internal ASCII string to unsigned int conversion -static unsigned int _atoi(const char** str) -{ - unsigned int i = 0U; - while (_is_digit(**str)) { - i = i * 10U + (unsigned int)(*((*str)++) - '0'); - } - return i; -} - - -// output the specified string in reverse, taking care of any zero-padding -static size_t _out_rev(out_fct_type out, char* buffer, size_t idx, size_t maxlen, const char* buf, size_t len, unsigned int width, unsigned int flags) -{ - const size_t start_idx = idx; - - // pad spaces up to given width - if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { - for (size_t i = len; i < width; i++) { - out(' ', buffer, idx++, maxlen); - } - } - - // reverse string - while (len) { - out(buf[--len], buffer, idx++, maxlen); - } - - // append pad spaces up to given width - if (flags & FLAGS_LEFT) { - while (idx - start_idx < width) { - out(' ', buffer, idx++, maxlen); - } - } - - return idx; -} - - -// internal itoa format -static size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t maxlen, char* buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags) -{ - // pad leading zeros - if (!(flags & FLAGS_LEFT)) { - if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { - width--; - } - while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = '0'; - } - while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = '0'; - } - } - - // handle hash - if (flags & FLAGS_HASH) { - if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) { - len--; - if (len && (base == 16U)) { - len--; - } - } - if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = 'x'; - } - else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = 'X'; - } - else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) { - buf[len++] = 'b'; - } - if (len < PRINTF_NTOA_BUFFER_SIZE) { - buf[len++] = '0'; - } - } - - if (len < PRINTF_NTOA_BUFFER_SIZE) { - if (negative) { - buf[len++] = '-'; - } - else if (flags & FLAGS_PLUS) { - buf[len++] = '+'; // ignore the space if the '+' exists - } - else if (flags & FLAGS_SPACE) { - buf[len++] = ' '; - } - } - - return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); -} - - -// internal itoa for 'long' type -static size_t _ntoa_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base, unsigned int prec, unsigned int width, unsigned int flags) -{ - char buf[PRINTF_NTOA_BUFFER_SIZE]; - size_t len = 0U; - - // no hash for 0 values - if (!value) { - flags &= ~FLAGS_HASH; - } - - // write if precision != 0 and value is != 0 - if (!(flags & FLAGS_PRECISION) || value) { - do { - const char digit = (char)(value % base); - buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; - value /= base; - } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); - } - - return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); -} - - -// internal itoa for 'long long' type -#if defined(PRINTF_SUPPORT_LONG_LONG) -static size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags) -{ - char buf[PRINTF_NTOA_BUFFER_SIZE]; - size_t len = 0U; - - // no hash for 0 values - if (!value) { - flags &= ~FLAGS_HASH; - } - - // write if precision != 0 and value is != 0 - if (!(flags & FLAGS_PRECISION) || value) { - do { - const char digit = (char)(value % base); - buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; - value /= base; - } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); - } - - return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); -} -#endif // PRINTF_SUPPORT_LONG_LONG - - -#if defined(PRINTF_SUPPORT_FLOAT) - -#if defined(PRINTF_SUPPORT_EXPONENTIAL) -// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT -static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags); -#endif - - -// internal ftoa for fixed decimal floating point -static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) -{ - char buf[PRINTF_FTOA_BUFFER_SIZE]; - size_t len = 0U; - double diff = 0.0; - - // powers of 10 - static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; - - // test for special values - if (value != value) - return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags); - if (value < -DBL_MAX) - return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags); - if (value > DBL_MAX) - return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags); - - // test for very large values - // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad - if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) { -#if defined(PRINTF_SUPPORT_EXPONENTIAL) - return _etoa(out, buffer, idx, maxlen, value, prec, width, flags); -#else - return 0U; -#endif - } - - // test for negative - bool negative = false; - if (value < 0) { - negative = true; - value = 0 - value; - } - - // set default precision, if not set explicitly - if (!(flags & FLAGS_PRECISION)) { - prec = PRINTF_DEFAULT_FLOAT_PRECISION; - } - // limit precision to 9, cause a prec >= 10 can lead to overflow errors - while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) { - buf[len++] = '0'; - prec--; - } - - int whole = (int)value; - double tmp = (value - whole) * pow10[prec]; - unsigned long frac = (unsigned long)tmp; - diff = tmp - frac; - - if (diff > 0.5) { - ++frac; - // handle rollover, e.g. case 0.99 with prec 1 is 1.0 - if (frac >= pow10[prec]) { - frac = 0; - ++whole; - } - } - else if (diff < 0.5) { - } - else if ((frac == 0U) || (frac & 1U)) { - // if halfway, round up if odd OR if last digit is 0 - ++frac; - } - - if (prec == 0U) { - diff = value - (double)whole; - if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) { - // exactly 0.5 and ODD, then round up - // 1.5 -> 2, but 2.5 -> 2 - ++whole; - } - } - else { - unsigned int count = prec; - // now do fractional part, as an unsigned number - while (len < PRINTF_FTOA_BUFFER_SIZE) { - --count; - buf[len++] = (char)(48U + (frac % 10U)); - if (!(frac /= 10U)) { - break; - } - } - // add extra 0s - while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) { - buf[len++] = '0'; - } - if (len < PRINTF_FTOA_BUFFER_SIZE) { - // add decimal - buf[len++] = '.'; - } - } - - // do whole part, number is reversed - while (len < PRINTF_FTOA_BUFFER_SIZE) { - buf[len++] = (char)(48 + (whole % 10)); - if (!(whole /= 10)) { - break; - } - } - - // pad leading zeros - if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) { - if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { - width--; - } - while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) { - buf[len++] = '0'; - } - } - - if (len < PRINTF_FTOA_BUFFER_SIZE) { - if (negative) { - buf[len++] = '-'; - } - else if (flags & FLAGS_PLUS) { - buf[len++] = '+'; // ignore the space if the '+' exists - } - else if (flags & FLAGS_SPACE) { - buf[len++] = ' '; - } - } - - return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); -} - - -#if defined(PRINTF_SUPPORT_EXPONENTIAL) -// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse -static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) -{ - // check for NaN and special values - if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) { - return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags); - } - - // determine the sign - const bool negative = value < 0; - if (negative) { - value = -value; - } - - // default precision - if (!(flags & FLAGS_PRECISION)) { - prec = PRINTF_DEFAULT_FLOAT_PRECISION; - } - - // determine the decimal exponent - // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) - union { - uint64_t U; - double F; - } conv; - - conv.F = value; - int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2 - conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2) - // now approximate log10 from the log2 integer part and an expansion of ln around 1.5 - int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168); - // now we want to compute 10^expval but we want to be sure it won't overflow - exp2 = (int)(expval * 3.321928094887362 + 0.5); - const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; - const double z2 = z * z; - conv.U = (uint64_t)(exp2 + 1023) << 52U; - // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex - conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); - // correct for rounding errors - if (value < conv.F) { - expval--; - conv.F /= 10; - } - - // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters - unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U; - - // in "%g" mode, "prec" is the number of *significant figures* not decimals - if (flags & FLAGS_ADAPT_EXP) { - // do we want to fall-back to "%f" mode? - if ((value >= 1e-4) && (value < 1e6)) { - if ((int)prec > expval) { - prec = (unsigned)((int)prec - expval - 1); - } - else { - prec = 0; - } - flags |= FLAGS_PRECISION; // make sure _ftoa respects precision - // no characters in exponent - minwidth = 0U; - expval = 0; - } - else { - // we use one sigfig for the whole part - if ((prec > 0) && (flags & FLAGS_PRECISION)) { - --prec; - } - } - } - - // will everything fit? - unsigned int fwidth = width; - if (width > minwidth) { - // we didn't fall-back so subtract the characters required for the exponent - fwidth -= minwidth; - } else { - // not enough characters, so go back to default sizing - fwidth = 0U; - } - if ((flags & FLAGS_LEFT) && minwidth) { - // if we're padding on the right, DON'T pad the floating part - fwidth = 0U; - } - - // rescale the float value - if (expval) { - value /= conv.F; - } - - // output the floating part - const size_t start_idx = idx; - idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP); - - // output the exponent part - if (minwidth) { - // output the exponential symbol - out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen); - // output the exponent value - idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth-1, FLAGS_ZEROPAD | FLAGS_PLUS); - // might need to right-pad spaces - if (flags & FLAGS_LEFT) { - while (idx - start_idx < width) out(' ', buffer, idx++, maxlen); - } - } - return idx; -} -#endif // PRINTF_SUPPORT_EXPONENTIAL -#endif // PRINTF_SUPPORT_FLOAT - - -// internal vsnprintf -static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va) -{ - unsigned int flags, width, precision, n; - size_t idx = 0U; - - if (!buffer) { - // use null output function - out = _out_null; - } - - while (*format) - { - // format specifier? %[flags][width][.precision][length] - if (*format != '%') { - // no - out(*format, buffer, idx++, maxlen); - format++; - continue; - } - else { - // yes, evaluate it - format++; - } - - // evaluate flags - flags = 0U; - do { - switch (*format) { - case '0': flags |= FLAGS_ZEROPAD; format++; n = 1U; break; - case '-': flags |= FLAGS_LEFT; format++; n = 1U; break; - case '+': flags |= FLAGS_PLUS; format++; n = 1U; break; - case ' ': flags |= FLAGS_SPACE; format++; n = 1U; break; - case '#': flags |= FLAGS_HASH; format++; n = 1U; break; - default : n = 0U; break; - } - } while (n); - - // evaluate width field - width = 0U; - if (_is_digit(*format)) { - width = _atoi(&format); - } - else if (*format == '*') { - const int w = va_arg(va, int); - if (w < 0) { - flags |= FLAGS_LEFT; // reverse padding - width = (unsigned int)-w; - } - else { - width = (unsigned int)w; - } - format++; - } - - // evaluate precision field - precision = 0U; - if (*format == '.') { - flags |= FLAGS_PRECISION; - format++; - if (_is_digit(*format)) { - precision = _atoi(&format); - } - else if (*format == '*') { - const int prec = (int)va_arg(va, int); - precision = prec > 0 ? (unsigned int)prec : 0U; - format++; - } - } - - // evaluate length field - switch (*format) { - case 'l' : - flags |= FLAGS_LONG; - format++; - if (*format == 'l') { - flags |= FLAGS_LONG_LONG; - format++; - } - break; - case 'h' : - flags |= FLAGS_SHORT; - format++; - if (*format == 'h') { - flags |= FLAGS_CHAR; - format++; - } - break; -#if defined(PRINTF_SUPPORT_PTRDIFF_T) - case 't' : - flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); - format++; - break; -#endif - case 'j' : - flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); - format++; - break; - case 'z' : - flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); - format++; - break; - default : - break; - } - - // evaluate specifier - switch (*format) { - case 'd' : - case 'i' : - case 'u' : - case 'x' : - case 'X' : - case 'o' : - case 'b' : { - // set the base - unsigned int base; - if (*format == 'x' || *format == 'X') { - base = 16U; - } - else if (*format == 'o') { - base = 8U; - } - else if (*format == 'b') { - base = 2U; - } - else { - base = 10U; - flags &= ~FLAGS_HASH; // no hash for dec format - } - // uppercase - if (*format == 'X') { - flags |= FLAGS_UPPERCASE; - } - - // no plus or space flag for u, x, X, o, b - if ((*format != 'i') && (*format != 'd')) { - flags &= ~(FLAGS_PLUS | FLAGS_SPACE); - } - - // ignore '0' flag when precision is given - if (flags & FLAGS_PRECISION) { - flags &= ~FLAGS_ZEROPAD; - } - - // convert the integer - if ((*format == 'i') || (*format == 'd')) { - // signed - if (flags & FLAGS_LONG_LONG) { -#if defined(PRINTF_SUPPORT_LONG_LONG) - const long long value = va_arg(va, long long); - idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); -#endif - } - else if (flags & FLAGS_LONG) { - const long value = va_arg(va, long); - idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); - } - else { - const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : va_arg(va, int); - idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); - } - } - else { - // unsigned - if (flags & FLAGS_LONG_LONG) { -#if defined(PRINTF_SUPPORT_LONG_LONG) - idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags); -#endif - } - else if (flags & FLAGS_LONG) { - idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags); - } - else { - const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : va_arg(va, unsigned int); - idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags); - } - } - format++; - break; - } -#if defined(PRINTF_SUPPORT_FLOAT) - case 'f' : - case 'F' : - if (*format == 'F') flags |= FLAGS_UPPERCASE; - idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); - format++; - break; -#if defined(PRINTF_SUPPORT_EXPONENTIAL) - case 'e': - case 'E': - case 'g': - case 'G': - if ((*format == 'g')||(*format == 'G')) flags |= FLAGS_ADAPT_EXP; - if ((*format == 'E')||(*format == 'G')) flags |= FLAGS_UPPERCASE; - idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); - format++; - break; -#endif // PRINTF_SUPPORT_EXPONENTIAL -#endif // PRINTF_SUPPORT_FLOAT - case 'c' : { - unsigned int l = 1U; - // pre padding - if (!(flags & FLAGS_LEFT)) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } - } - // char output - out((char)va_arg(va, int), buffer, idx++, maxlen); - // post padding - if (flags & FLAGS_LEFT) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } - } - format++; - break; - } - - case 's' : { - const char* p = va_arg(va, char*); - unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1); - // pre padding - if (flags & FLAGS_PRECISION) { - l = (l < precision ? l : precision); - } - if (!(flags & FLAGS_LEFT)) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } - } - // string output - while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) { - out(*(p++), buffer, idx++, maxlen); - } - // post padding - if (flags & FLAGS_LEFT) { - while (l++ < width) { - out(' ', buffer, idx++, maxlen); - } - } - format++; - break; - } - - case 'p' : { - width = sizeof(void*) * 2U; - flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE; -#if defined(PRINTF_SUPPORT_LONG_LONG) - const bool is_ll = sizeof(uintptr_t) == sizeof(long long); - if (is_ll) { - idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void*), false, 16U, precision, width, flags); - } - else { -#endif - idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void*)), false, 16U, precision, width, flags); -#if defined(PRINTF_SUPPORT_LONG_LONG) - } -#endif - format++; - break; - } - - case '%' : - out('%', buffer, idx++, maxlen); - format++; - break; - - default : - out(*format, buffer, idx++, maxlen); - format++; - break; - } - } - - // termination - out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); - - // return written chars without terminating \0 - return (int)idx; -} - - -/////////////////////////////////////////////////////////////////////////////// - -int printf_(const char* format, ...) -{ - va_list va; - va_start(va, format); - char buffer[1]; - const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va); - va_end(va); - return ret; -} - - -int sprintf_(char* buffer, const char* format, ...) -{ - va_list va; - va_start(va, format); - const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va); - va_end(va); - return ret; -} - - -int snprintf_(char* buffer, size_t count, const char* format, ...) -{ - va_list va; - va_start(va, format); - const int ret = _vsnprintf(_out_buffer, buffer, count, format, va); - va_end(va); - return ret; -} - - -int vprintf_(const char* format, va_list va) -{ - char buffer[1]; - return _vsnprintf(_out_char, buffer, (size_t)-1, format, va); -} - - -int vsnprintf_(char* buffer, size_t count, const char* format, va_list va) -{ - return _vsnprintf(_out_buffer, buffer, count, format, va); -} - - -int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...) -{ - va_list va; - va_start(va, format); - const out_fct_wrap_type out_fct_wrap = { out, arg }; - const int ret = _vsnprintf(_out_fct, (char*)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va); - va_end(va); - return ret; -} \ No newline at end of file diff --git a/src/io/term/printf.h b/src/io/term/printf.h deleted file mode 100644 index e58f666..0000000 --- a/src/io/term/printf.h +++ /dev/null @@ -1,117 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// \author (c) Marco Paland (info@paland.com) -// 2014-2019, PALANDesign Hannover, Germany -// -// \license The MIT License (MIT) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// -// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on -// embedded systems with a very limited resources. -// Use this instead of bloated standard/newlib printf. -// These routines are thread safe and reentrant. -// -/////////////////////////////////////////////////////////////////////////////// - -#ifndef _PRINTF_H_ -#define _PRINTF_H_ - -#include -#include - - -#ifdef __cplusplus -extern "C" { -#endif - - -/** - * Output a character to a custom device like UART, used by the printf() function - * This function is declared here only. You have to write your custom implementation somewhere - * \param character Character to output - */ -void _putchar(char character); - - -/** - * Tiny printf implementation - * You have to implement _putchar if you use printf() - * To avoid conflicts with the regular printf() API it is overridden by macro defines - * and internal underscore-appended functions like printf_() are used - * \param format A string that specifies the format of the output - * \return The number of characters that are written into the array, not counting the terminating null character - */ -#define printf printf_ -int printf_(const char* format, ...); - - -/** - * Tiny sprintf implementation - * Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD! - * \param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output! - * \param format A string that specifies the format of the output - * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character - */ -#define sprintf sprintf_ -int sprintf_(char* buffer, const char* format, ...); - - -/** - * Tiny snprintf/vsnprintf implementation - * \param buffer A pointer to the buffer where to store the formatted string - * \param count The maximum number of characters to store in the buffer, including a terminating null character - * \param format A string that specifies the format of the output - * \param va A value identifying a variable arguments list - * \return The number of characters that COULD have been written into the buffer, not counting the terminating - * null character. A value equal or larger than count indicates truncation. Only when the returned value - * is non-negative and less than count, the string has been completely written. - */ -#define snprintf snprintf_ -#define vsnprintf vsnprintf_ -int snprintf_(char* buffer, size_t count, const char* format, ...); -int vsnprintf_(char* buffer, size_t count, const char* format, va_list va); - - -/** - * Tiny vprintf implementation - * \param format A string that specifies the format of the output - * \param va A value identifying a variable arguments list - * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character - */ -#define vprintf vprintf_ -int vprintf_(const char* format, va_list va); - - -/** - * printf with output function - * You may use this as dynamic alternative to printf() with its fixed _putchar() output - * \param out An output function which takes one character and an argument pointer - * \param arg An argument pointer for user data passed to output function - * \param format A string that specifies the format of the output - * \return The number of characters that are sent to the output function, not counting the terminating null character - */ -int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...); - - -#ifdef __cplusplus -} -#endif - - -#endif // _PRINTF_H_ \ No newline at end of file diff --git a/src/io/term/term.c b/src/io/term/term.c index ebd979e..20aa132 100644 --- a/src/io/term/term.c +++ b/src/io/term/term.c @@ -19,12 +19,22 @@ because this shitty implementation will be replaced one day by Flanterm #include "flanterm_backends/fb.h" #include "mem/heap/kheap.h" #include "limine.h" +#include +#include "sched/spinlock.h" +#include "io/serial/serial.h" + +#define NANOPRINTF_IMPLEMENTATION +#include "nanoprintf.h" extern struct flanterm_context* ft_ctx; extern struct init_status init; +struct spinlock_t term_lock = {0}; + +extern int panic_count; + /* - * _putchar - Writes a character to terminal + * _putchar - Writes a character to terminal (DEPRECATED) * @character: character to write */ void _putchar(char character) @@ -33,6 +43,56 @@ void _putchar(char character) flanterm_write(ft_ctx, &character, 1); } +/* + * internal_putc - Internal putchar function + * @c: char to print + * @_: (unused, for nanoprintf) + * + * Prints a character to the terminal if it's ready, + * and also to the serial interface if it's ready. + */ +void internal_putc(int c, void *_) +{ + (void)_; + char ch = (char)c; + + if (init.terminal) { + if (panic_count == 0) { + spinlock_acquire(&term_lock); + flanterm_write(ft_ctx, &ch, 1); + spinlock_release(&term_lock); + } else { + flanterm_write(ft_ctx, &ch, 1); + } + } + + if (init.serial) { + if (ch == '\n') { + skputc('\r'); + } + skputc(ch); + } +} + +/* + * printf - Fromatted printing + * @fmt: format string + * @...: variadic arguments + * + * Wrapper for nanoprintf + * + * Return: + * - number of characters sent to the callback + */ +int printf(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + int ret = npf_vpprintf(internal_putc, NULL, fmt, args); + va_end(args); + return ret; +} + /* * kputs - Kernel puts * @str: String to write diff --git a/src/io/term/term.h b/src/io/term/term.h index ee15ea6..3ce0722 100644 --- a/src/io/term/term.h +++ b/src/io/term/term.h @@ -10,5 +10,6 @@ void kputs(const char* str); void _putchar(char character); void term_init(void); +int printf(const char* fmt, ...); #endif diff --git a/src/kernel.h b/src/kernel.h index f15e53e..bf5c324 100644 --- a/src/kernel.h +++ b/src/kernel.h @@ -16,20 +16,19 @@ enum ErrorCodes { #define SET_INTERRUPTS __asm__ volatile("sti") #include "io/serial/serial.h" -#include "io/term/printf.h" +#include "io/term/term.h" #include "idt/idt.h" #include extern volatile uint64_t ticks; - -#define DEBUG(log, ...) fctprintf((void*)&skputc, 0, "[%8u] debug: <%s>: " log "\r\n", ticks, __func__, ##__VA_ARGS__) +#define DEBUG(log, ...) printf("[%8u] debug: <%s>: " log "\r\n", ticks, __func__, ##__VA_ARGS__) /* #define DEBUG(log, ...) \ printf("debug: [%s]: " log "\r\n", __FILE__, ##__VA_ARGS__); \ fctprintf((void*)&skputc, 0, "debug: [%s]: " log "\r\n", __FILE__, ##__VA_ARGS__) */ -#define DIE_DEBUG(str) fctprintf((void*)&skputc, 0, str) +#define DIE_DEBUG(str) printf(str) #define CHECK_BIT(var,pos) ((var) & (1<<(pos))) diff --git a/src/kmain.c b/src/kmain.c index 0f527e4..07b0d17 100644 --- a/src/kmain.c +++ b/src/kmain.c @@ -8,7 +8,7 @@ #include #include #include "io/term/term.h" -#include "io/term/printf.h" +#include "io/term/term.h" #include "io/serial/serial.h" #include "mem/gdt/gdt.h" #include "mem/misc/utils.h" @@ -30,6 +30,8 @@ __attribute__((used, section(".limine_requests"))) volatile LIMINE_BASE_REVISION(3); +int panic_count = 0; + /* * hcf - Halt and catch fire * @@ -124,6 +126,5 @@ void kmain() scheduler_init(); kputs(PEPPEROS_SPLASH); - idle(); } diff --git a/src/sched/spinlock.c b/src/sched/spinlock.c new file mode 100644 index 0000000..2a50ecd --- /dev/null +++ b/src/sched/spinlock.c @@ -0,0 +1,44 @@ +/* + * @author xamidev + * @brief Spinlock implementation + * @license GPL-3.0-only + */ + +#include +#include +#include "kernel.h" +#include "spinlock.h" + +/* + * spinlock_acquire - Lock a lock + * @lock: pointer to desired spinlock + * + * Saves the RFLAGS register, then acquires a lock. + * Pause instruction is used to ease the CPU. + */ +void spinlock_acquire(struct spinlock_t* lock) +{ + uint64_t rflags; + asm volatile("pushfq ; pop %0 ; cli" : "=rm"(rflags) : : "memory"); + + while (__atomic_test_and_set(&lock->locked, __ATOMIC_ACQUIRE)) { + __builtin_ia32_pause(); + } + + lock->rflags = rflags; +} + +/* + * spinlock_release - Unlock a lock + * @lock: pointer to desired spinlock + * + * Gets saved RFLAGS register from the lock and + * unlocks it (clears locked state). + * RFLAGS is then restored. + */ +void spinlock_release(struct spinlock_t* lock) +{ + uint64_t rflags = lock->rflags; + __atomic_clear(&lock->locked, __ATOMIC_RELEASE); + asm volatile("push %0 ; popfq" : : "rm"(rflags) : "memory"); +} \ No newline at end of file diff --git a/src/sched/spinlock.h b/src/sched/spinlock.h new file mode 100644 index 0000000..849d99d --- /dev/null +++ b/src/sched/spinlock.h @@ -0,0 +1,22 @@ +/* + * @author xamidev + * @brief Spinlock implementation + * @license GPL-3.0-only + */ + +#ifndef SPINLOCK_H +#define SPINLOCK_H + +#include +#include + +struct spinlock_t +{ + bool locked; + uint64_t rflags; +}; + +void spinlock_acquire(struct spinlock_t* lock); +void spinlock_release(struct spinlock_t* lock); + +#endif \ No newline at end of file diff --git a/src/string/string.h b/src/string/string.h index 7a59a3a..dca35bc 100644 --- a/src/string/string.h +++ b/src/string/string.h @@ -7,6 +7,8 @@ #ifndef STRING_H #define STRING_H +#include + char *strcpy(char *dest, const char *src); char *strcat(char *dest, const char *src); void strncpy(char* dst, const char* src, size_t n);