Improve kernel logging

Current function name is automatically prepended to all log entries.
Helper macros added to log local variables.
This commit is contained in:
2026-07-27 20:41:45 +03:00
parent 3b8125eb0c
commit 572cb7647f
6 changed files with 295 additions and 139 deletions
+1 -1
View File
@@ -30,7 +30,7 @@ __attribute__((interrupt)) void isr_page_fault(__attribute__((unused)) struct in
__asm__ volatile("mov %%cr2, %0" : "=r"(cr2));
char msg[80];
string_format("EXCEPTION: page fault; addr=%x err=%x", 80, msg, cr2, error_code);
string_format("EXCEPTION: page fault; addr=%lx err=%lx", 80, msg, cr2, error_code);
vga_set_string(VGA_HEIGHT - 1, 0, msg, 0x4F);
while (1)
+23 -1
View File
@@ -8,7 +8,7 @@ extern stream_t *kernel_log;
#define LOG(fmt, ...) \
do { \
uint64_t _size = string_length(fmt) * 2; \
uint64_t _size = string_length(fmt) * 10; \
char *_string = memory_allocate(_size); \
_size = string_format(fmt, _size, _string, ##__VA_ARGS__); \
kernel_log->write(kernel_log, _string, _size); \
@@ -17,4 +17,26 @@ extern stream_t *kernel_log;
#define TRACE(fmt, ...)
#define LOG_LN(fmt, ...) \
do { \
uint64_t _size = string_length(fmt) * 10; \
char *_string = memory_allocate(_size); \
_size = string_format(fmt, _size, _string, ##__VA_ARGS__); \
kernel_log->write(kernel_log, __func__, string_length(__func__)); \
kernel_log->write(kernel_log, ": ", 2); \
kernel_log->write(kernel_log, _string, _size); \
kernel_log->write(kernel_log, "\n", 1); \
memory_free(_string); \
} while (0)
#define LOG_VAL(v) LOG_LN(#v "=%x")
#define LOG_VAL_F(v, f) LOG_LN(#v "=" f)
#define TRACE_LN(fmt, ...)
#define TRACE_VAL(v) TRACE_LN(#v "=%lx")
#define TRACE_VAL_F(v, f) TRACE_LN(#v "=" f)
void log_init(stream_t *log);
+7 -9
View File
@@ -67,10 +67,10 @@ void memory_page_map(uint64_t *pml4, void *virt, void *phys, uint64_t flags) {
const uint64_t pd_index = (ivirt >> 21) & 0x1FF;
const uint64_t pt_index = (ivirt >> 12) & 0x1FF;
TRACE("memory_page_map: Mapping physical %x to virtual %x / [%u, %u, %u, %u]...\n", phys, virt, pml4_index, pdpt_index, pd_index, pt_index);
TRACE_LN("Mapping physical %lx to virtual %lx / [%u, %u, %u, %u]...", phys, virt, pml4_index, pdpt_index, pd_index, pt_index);
if (!(pml4[pml4_index] & PAGE_PRESENT)) {
TRACE("memory_page_map: PML4 entry %u does not exist, creating...\n", pml4_index);
TRACE_LN("PML4 entry %u does not exist, creating...", pml4_index);
uint64_t *pdpt = memory_page_allocate();
memory_set(0, PAGE_SIZE, PHYS_TO_VIRT(pdpt));
pml4[pml4_index] = (uint64_t)pdpt | PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER;
@@ -78,7 +78,7 @@ void memory_page_map(uint64_t *pml4, void *virt, void *phys, uint64_t flags) {
uint64_t *pdpt = PHYS_TO_VIRT(pml4[pml4_index] & ~(uint64_t)0xFFF);
if (!(pdpt[pdpt_index] & PAGE_PRESENT)) {
TRACE("memory_page_map: PDPT entry %u does not exist, creating...\n", pdpt_index);
TRACE_LN("PDPT entry %u does not exist, creating...", pdpt_index);
uint64_t *pd = memory_page_allocate();
memory_set(0, PAGE_SIZE, PHYS_TO_VIRT(pd));
pdpt[pdpt_index] = (uint64_t)pd | PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER;
@@ -87,7 +87,7 @@ void memory_page_map(uint64_t *pml4, void *virt, void *phys, uint64_t flags) {
uint64_t *pd = PHYS_TO_VIRT(pdpt[pdpt_index] & ~(uint64_t)0xFFF);
ASSERT(!(pd[pd_index] & 0x80), "memory_page_map: Huge page in PD.");
if (!(pd[pd_index] & PAGE_PRESENT)) {
TRACE("memory_page_map: PD entry %u does not exist, creating...\n", pd_index);
TRACE_LN("PD entry %u does not exist, creating...", pd_index);
uint64_t *pt = memory_page_allocate();
memory_set(0, PAGE_SIZE, PHYS_TO_VIRT(pt));
pd[pd_index] = (uint64_t)pt | PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER;
@@ -97,13 +97,11 @@ void memory_page_map(uint64_t *pml4, void *virt, void *phys, uint64_t flags) {
ASSERT(!(pt[pt_index] & PAGE_PRESENT), "memory_page_map: Page already mapped.")
TRACE("memory_page_map: PT entry %u does not exist, creating...\n", pt_index);
TRACE_LN("PT entry %u does not exist, creating...", pt_index);
pt[pt_index] = (uint64_t)phys | flags | PAGE_PRESENT;
TRACE_VAL(pt[pt_index]);
TRACE("memory_page_map: Created %x.\n", pt[pt_index]);
TRACE("memory_page_map: Done.\n");
TRACE_LN("Done.");
}
void memory_page_unmap(uint64_t *pml4, void *virt) {
+64 -69
View File
@@ -5,7 +5,6 @@
#include "src/kernel/pci.h"
#include "src/lib/layout.h"
#include "src/lib/memory.h"
#include <stdint.h>
#define SECTOR_SIZE 512
@@ -67,89 +66,83 @@ static uint16_t io_cq_head = 0;
static void admin_exec_sync(nvme_sqe_t *cmd) {
cmd->cid = admin_sq_tail;
TRACE("admin_exec_sync: Queueing submission...\n");
TRACE("admin_exec_sync: opc=%x\n", cmd->opc);
TRACE("admin_exec_sync: nsid=%x\n", cmd->nsid);
TRACE("admin_exec_sync: cdw10=%x\n", cmd->cdw10);
TRACE("admin_exec_sync: cdw11=%x\n", cmd->cdw11);
TRACE("admin_exec_sync: cdw12=%x\n", cmd->cdw12);
TRACE("admin_exec_sync: prp1=%x\n", cmd->prp1);
TRACE("admin_exec_sync: prp2=%x\n", cmd->prp2);
TRACE_LN("Queueing submission...");
TRACE_VAL_F(cmd->opc, "%hhx");
TRACE_VAL_F(cmd->nsid, "%x");
TRACE_VAL_F(cmd->cdw10, "%x");
TRACE_VAL_F(cmd->cdw11, "%x");
TRACE_VAL_F(cmd->cdw12, "%x");
TRACE_VAL_F(cmd->prp1, "%lx");
TRACE_VAL_F(cmd->prp2, "%lx");
memory_copy(cmd, sizeof(nvme_sqe_t), &admin_sq[admin_sq_tail]);
TRACE("admin_exec_sync: Advancing submission doorbell...\n");
TRACE_LN("Advancing submission doorbell...");
*(volatile uint32_t *)((uint8_t *)regs + 0x1000) = admin_sq_tail = (admin_sq_tail + 1) % QUEUE_DEPTH;
TRACE_VAL_F(admin_sq_tail, "%u");
TRACE("admin_exec_sync: Waiting for completion...\n");
while ((admin_cq[admin_cq_head].status & 1) != admin_cq_phase)
TRACE_LN("Waiting for completion...");
nvme_cqe_t *cmp = &admin_cq[admin_cq_head];
while ((cmp->status & 1) != admin_cq_phase)
;
TRACE("admin_exec_sync: Received completion...\n");
TRACE("admin_exec_sync: dw0=%x\n", io_cq[io_cq_head].dw0);
TRACE("admin_exec_sync: reserved=%x\n", io_cq[io_cq_head].reserved);
TRACE("admin_exec_sync: sqhd=%x\n", io_cq[io_cq_head].sqhd);
TRACE("admin_exec_sync: sqid=%x\n", io_cq[io_cq_head].sqid);
TRACE("admin_exec_sync: cid=%x\n", io_cq[io_cq_head].cid);
TRACE("admin_exec_sync: status=%x\n", io_cq[io_cq_head].status);
TRACE_LN("Received completion...");
TRACE_VAL_F(cmp->dw0, "%x");
TRACE_VAL_F(cmp->reserved, "%x");
TRACE_VAL_F(cmp->sqhd, "%hx");
TRACE_VAL_F(cmp->sqid, "%hx");
TRACE_VAL_F(cmp->cid, "%hx");
TRACE_VAL_F(cmp->status, "%hx");
ASSERT((admin_cq[admin_cq_head].status >> 1) == 0, "admin_exec_sync: NVMe command failed.");
TRACE("admin_exec_sync: Advancing completion doorbell...\n");
TRACE_LN("Advancing completion doorbell...");
*(volatile uint32_t *)((uint8_t *)regs + 0x1004) = admin_cq_head = (admin_cq_head + 1) % QUEUE_DEPTH; // TODO Get offset from caps.
admin_cq_phase ^= (admin_cq_head == 0);
TRACE("admin_exec_sync: Done.\n");
TRACE_LN("Done.");
}
static void io_exec_sync(nvme_sqe_t *cmd) {
cmd->cid = io_sq_tail;
TRACE("io_exec_sync: Queueing submission...\n");
TRACE("io_exec_sync: opc=%x\n", cmd->opc);
TRACE("io_exec_sync: nsid=%x\n", cmd->nsid);
TRACE("io_exec_sync: cdw10=%x\n", cmd->cdw10);
TRACE("io_exec_sync: cdw11=%x\n", cmd->cdw11);
TRACE("io_exec_sync: cdw12=%x\n", cmd->cdw12);
TRACE("io_exec_sync: prp1=%x\n", cmd->prp1);
TRACE("io_exec_sync: prp2=%x\n", cmd->prp2);
TRACE_LN("Queueing submission...");
TRACE_VAL_F(cmd->opc, "%hhx");
TRACE_VAL_F(cmd->nsid, "%x");
TRACE_VAL_F(cmd->cdw10, "%x");
TRACE_VAL_F(cmd->cdw11, "%x");
TRACE_VAL_F(cmd->cdw12, "%x");
TRACE_VAL_F(cmd->prp1, "%x");
TRACE_VAL_F(cmd->prp2, "%x");
memory_copy(cmd, sizeof(nvme_sqe_t), &io_sq[io_sq_tail]);
TRACE("io_exec_sync: Advancing submission doorbell...\n");
TRACE_LN("Advancing submission doorbell...");
*(volatile uint32_t *)((uint8_t *)regs + 0x1008) = io_sq_tail = (io_sq_tail + 1) % QUEUE_DEPTH; // TODO Get offset from caps.
TRACE("io_exec_sync: Waiting for completion...\n");
while ((io_cq[io_cq_head].status & 1) != io_cq_phase)
TRACE_LN("Waiting for completion...");
nvme_cqe_t *cmp = &io_cq[io_cq_head];
while ((cmp->status & 1) != io_cq_phase)
;
TRACE("io_exec_sync: Received completion...\n");
TRACE("io_exec_sync: dw0=%x\n", io_cq[io_cq_head].dw0);
TRACE("io_exec_sync: reserved=%x\n", io_cq[io_cq_head].reserved);
TRACE("io_exec_sync: sqhd=%x\n", io_cq[io_cq_head].sqhd);
TRACE("io_exec_sync: sqid=%x\n", io_cq[io_cq_head].sqid);
TRACE("io_exec_sync: cid=%x\n", io_cq[io_cq_head].cid);
TRACE("io_exec_sync: status=%x\n", io_cq[io_cq_head].status);
TRACE_LN("Received completion...");
TRACE_VAL_F(cmp->dw0, "%x");
TRACE_VAL_F(cmp->reserved, "%x");
TRACE_VAL_F(cmp->sqhd, "%hx");
TRACE_VAL_F(cmp->sqid, "%hx");
TRACE_VAL_F(cmp->cid, "%hx");
TRACE_VAL_F(cmp->status, "%hx");
ASSERT((io_cq[io_cq_head].status >> 1) == 0, "io_exec_sync: NVMe command failed.");
TRACE("io_exec_sync: Advancing completion doorbell...\n");
ASSERT((cmp->status >> 1) == 0, "io_exec_sync: NVMe command failed.");
TRACE_LN("Advancing completion doorbell...");
*(volatile uint32_t *)((uint8_t *)regs + 0x100C) = io_cq_head = (io_cq_head + 1) % QUEUE_DEPTH; // TODO Get offset from caps.
io_cq_phase ^= (io_cq_head == 0);
TRACE("io_exec_sync: Done.\n");
TRACE_LN("Done.");
}
void nvme_init() {
LOG("nvme_init: Searching for suitable device...\n");
LOG_LN("Searching for suitable device...");
uint8_t found = 0;
for (uint16_t bus = 0; bus < 256 && !found; bus++) {
for (uint8_t device = 0; device < 32 && !found; device++) {
@@ -169,11 +162,12 @@ void nvme_init() {
uint32_t bar1 = pci_read((uint8_t)bus, device, function, 0x14);
uint64_t phys = ((uint64_t)bar1 << 32) | bar0;
LOG("nvme_init: Mapping device to memory, phys %x <-> virt %x\n", phys, regs);
LOG_LN("Mapping device to memory, phys %lx <-> virt %lx...", phys, regs);
for (uint8_t i = 0; i < 4; i++) {
memory_page_map((void *)KERNEL_VIRTUAL_PML4, (void *)((uint8_t *)regs + i * PAGE_SIZE), (void *)((uint8_t *)phys + i * PAGE_SIZE),
PAGE_WRITABLE | PAGE_PCD | PAGE_USER);
}
found = 1;
}
}
@@ -182,27 +176,29 @@ void nvme_init() {
ASSERT(found, "nvme_init: No suitable devices found.");
ASSERT(regs->cap & (1ULL << 37), "nvme_init: NVM command set not supported.");
LOG("nvme_init: Disabling device...\n");
LOG_LN("Disabling device...");
regs->cc = 0;
TRACE_VAL_F(regs->cc, "%lx");
while (regs->csts & 1)
;
TRACE_VAL_F(regs->cc, "%lx");
LOG("nvme_init: Configuring device...\n");
LOG_LN("Configuring device...");
regs->asq = (uint64_t)(VIRT_TO_PHYS(admin_sq));
TRACE("nvme_init: Setting ASQ, expected=%x, set=%x...\n", (uint64_t)(VIRT_TO_PHYS(admin_sq)), regs->asq);
regs->acq = (uint64_t)(VIRT_TO_PHYS(admin_cq));
TRACE("nvme_init: Setting ACQ, expected=%x, set=%x...\n", (uint64_t)(VIRT_TO_PHYS(admin_cq)), regs->acq);
regs->aqa = (QUEUE_DEPTH - 1) << 16 | (QUEUE_DEPTH - 1);
TRACE("nvme_init: Setting AQA, expected=%x, set=%x...\n", (QUEUE_DEPTH - 1) << 16 | (QUEUE_DEPTH - 1), regs->aqa);
TRACE_VAL_F(regs->asq, "%lx");
TRACE_VAL_F(regs->acq, "%lx");
TRACE_VAL_F(regs->aqa, "%lx");
LOG_LN("Enabling device...");
regs->cc = (4 << 20) | (6 << 16) | (0 << 4) | 1;
LOG("nvme_init: Enabling device, expected=%x, set=%x...\n", (4 << 20) | (6 << 16) | (0 << 4) | 1, regs->cc);
TRACE_VAL_F(regs->cc, "%lx");
while (!(regs->csts & 1))
;
TRACE_VAL_F(regs->cc, "%lx");
LOG("nvme_init: Creating I/O completion queue...\n");
LOG_LN("Creating I/O completion queue...");
nvme_sqe_t create_io_cq = {
.opc = 0x05,
.prp1 = (uint64_t)VIRT_TO_PHYS(io_cq),
@@ -211,8 +207,7 @@ void nvme_init() {
};
admin_exec_sync(&create_io_cq);
LOG("nvme_init: Creating I/O submission queue...\n");
LOG_LN("Creating I/O submission queue...");
nvme_sqe_t create_io_sq = {
.opc = 0x01,
.prp1 = (uint64_t)VIRT_TO_PHYS(io_sq),
@@ -221,11 +216,11 @@ void nvme_init() {
};
admin_exec_sync(&create_io_sq);
LOG("nvme_init: Done.\n");
LOG_LN("Done.");
}
void nvme_read_sectors(uint32_t index, uint8_t count, void *to) {
TRACE("nvme_read_sectors: Reading %u sectors at %u...\n", count, index);
TRACE_LN("Reading %u sectors at %u...", count, index);
while (count) {
uint64_t remainder = PAGE_SIZE - (uint64_t)to % PAGE_SIZE;
@@ -249,11 +244,11 @@ void nvme_read_sectors(uint32_t index, uint8_t count, void *to) {
to = (void *)((uint8_t *)to + i_count * SECTOR_SIZE);
}
TRACE("nvme_read_sectors: Done.\n", count, index);
TRACE_LN("Done.");
}
void nvme_write_sectors(uint32_t index, uint8_t count, const void *from) {
TRACE("nvme_write_sectors: Reading %u sectors at %u...\n", count, index);
TRACE_LN("Writing %u sectors at %u...", count, index);
while (count) {
uint64_t remainder = PAGE_SIZE - (uint64_t)from % PAGE_SIZE;
@@ -277,5 +272,5 @@ void nvme_write_sectors(uint32_t index, uint8_t count, const void *from) {
from = (void *)((uint8_t *)from + i_count * SECTOR_SIZE);
}
TRACE("nvme_write_sectors: Done.\n", count, index);
TRACE_LN("Done.");
}
+192 -55
View File
@@ -73,7 +73,7 @@ uint64_t string_split(char *string, char separator, uint64_t max, char **result)
return count;
}
uint64_t string_byte_to_hex(uint8_t value, uint64_t max, char *result) {
uint64_t string_uint8_to_hex(uint8_t value, uint64_t max, char *result) {
if (max == 0) {
return 0;
}
@@ -94,7 +94,7 @@ uint64_t string_byte_to_hex(uint8_t value, uint64_t max, char *result) {
uint8_t i = 0;
while (value) {
uint8_t nibble = value & 0xF;
tmp[i++] = nibble < 10 ? '0' + nibble : 'a' + nibble - 10;
tmp[i++] = nibble < 10 ? '0' + nibble : 'A' + nibble - 10;
value >>= 4;
}
@@ -110,6 +110,117 @@ uint64_t string_byte_to_hex(uint8_t value, uint64_t max, char *result) {
return i;
}
uint64_t string_uint16_to_hex(uint16_t value, uint64_t max, char *result) {
if (max == 0) {
return 0;
}
if (max == 1) {
result[0] = '\0';
return 0;
}
if (max == 2) {
result[0] = '0';
result[1] = '\0';
return 1;
}
char tmp[5] = "0000";
uint8_t i = 0;
while (value) {
uint8_t nibble = value & 0xF;
tmp[i++] = nibble < 10 ? '0' + nibble : 'A' + nibble - 10;
value >>= 4;
}
result[0] = '0';
result[1] = 'x';
i = 2;
while (i < 6 && i < max - 1) {
result[i] = tmp[5 - i];
i++;
}
result[i] = '\0';
return i;
}
uint64_t string_uint32_to_hex(uint32_t value, uint64_t max, char *result) {
if (max == 0) {
return 0;
}
if (max == 1) {
result[0] = '\0';
return 0;
}
if (max == 2) {
result[0] = '0';
result[1] = '\0';
return 1;
}
char tmp[9] = "00000000";
uint8_t i = 0;
while (value) {
uint8_t nibble = value & 0xF;
tmp[i++] = nibble < 10 ? '0' + nibble : 'A' + nibble - 10;
value >>= 4;
}
result[0] = '0';
result[1] = 'x';
i = 2;
while (i < 10 && i < max - 1) {
result[i] = tmp[9 - i];
i++;
}
result[i] = '\0';
return i;
}
uint64_t string_uint64_to_hex(uint64_t value, uint64_t max, char *result) {
if (max == 0) {
return 0;
}
if (max == 1) {
result[0] = '\0';
return 0;
}
if (max == 2) {
result[0] = '0';
result[1] = '\0';
return 1;
}
char tmp[17] = "0000000000000000";
uint8_t i = 0;
while (value) {
uint8_t nibble = value & 0xF;
tmp[i++] = nibble < 10 ? '0' + nibble : 'A' + nibble - 10;
value >>= 4;
}
result[0] = '0';
result[1] = 'x';
i = 2;
while (i < 18 && i < max - 1) {
result[i] = tmp[17 - i];
i++;
}
result[i] = '\0';
return i;
}
uint64_t string_uint_to_decimal(uint64_t value, uint64_t max, char *result) {
if (max == 0) {
return 0;
@@ -149,43 +260,6 @@ uint64_t string_int_to_decimal(int64_t value, uint64_t max, char *result) {
return 1 + string_uint_to_decimal((uint64_t)(-value), max - 1, result + 1);
}
uint64_t string_uint_to_hex(uint64_t value, uint64_t max, char *result) {
if (max == 0) {
return 0;
}
if (max == 1) {
result[0] = '\0';
return 0;
}
if (max == 2) {
result[0] = '0';
result[1] = '\0';
return 1;
}
char tmp[17] = "0000000000000000";
uint8_t i = 0;
while (value) {
uint8_t nibble = value & 0xF;
tmp[i++] = nibble < 10 ? '0' + nibble : 'A' + nibble - 10;
value >>= 4;
}
result[0] = '0';
result[1] = 'x';
i = 2;
while (i < 18 && i < max - 1) {
result[i] = tmp[17 - i];
i++;
}
result[i] = '\0';
return i;
}
uint64_t string_format(const char *format, uint64_t max, char *output, ...) {
va_list args;
va_start(args, output);
@@ -198,31 +272,19 @@ uint64_t string_format(const char *format, uint64_t max, char *output, ...) {
output[ri++] = format[fi++];
} else {
switch (format[fi + 1]) {
case 'c':
case 'c': {
output[ri++] = (char)va_arg(args, int);
fi += 2;
break;
case 'd':
}
case 'd': {
string_int_to_decimal(va_arg(args, int64_t), limit - ri, output + ri);
while (output[ri]) {
ri++;
}
fi += 2;
break;
case 'u':
string_uint_to_decimal(va_arg(args, uint64_t), limit - ri, output + ri);
while (output[ri]) {
ri++;
}
fi += 2;
break;
case 'x':
string_uint_to_hex(va_arg(args, uint64_t), limit - ri, output + ri);
while (output[ri]) {
ri++;
}
fi += 2;
break;
case 's': {
char *s = va_arg(args, char *);
while (*s && ri < limit) {
@@ -232,17 +294,92 @@ uint64_t string_format(const char *format, uint64_t max, char *output, ...) {
fi += 2;
break;
}
case 'u': {
string_uint_to_decimal(va_arg(args, uint64_t), limit - ri, output + ri);
while (output[ri]) {
ri++;
}
fi += 2;
break;
}
case 'x': {
string_uint32_to_hex(va_arg(args, uint32_t), limit - ri, output + ri);
while (output[ri]) {
ri++;
}
fi += 2;
break;
}
case '%': {
output[ri++] = '%';
fi += 2;
break;
}
default:
case 'h': {
switch (format[fi + 2]) {
case 'x': {
string_uint16_to_hex(va_arg(args, uint64_t), limit - ri, output + ri);
while (output[ri]) {
ri++;
}
fi += 3;
break;
}
case 'h': {
switch (format[fi + 3]) {
case 'x': {
string_uint8_to_hex(va_arg(args, uint64_t), limit - ri, output + ri);
while (output[ri]) {
ri++;
}
fi += 4;
break;
}
default: {
output[ri++] = format[fi++];
output[ri++] = format[fi++];
output[ri++] = format[fi++];
output[ri++] = format[fi++];
break;
}
}
break;
}
default: {
output[ri++] = format[fi++];
output[ri++] = format[fi++];
output[ri++] = format[fi++];
break;
}
}
break;
}
case 'l': {
switch (format[fi + 2]) {
case 'x': {
string_uint64_to_hex(va_arg(args, uint64_t), limit - ri, output + ri);
while (output[ri]) {
ri++;
}
fi += 3;
break;
}
default: {
output[ri++] = format[fi++];
output[ri++] = format[fi++];
output[ri++] = format[fi++];
break;
}
}
break;
}
default: {
output[ri++] = format[fi++];
output[ri++] = format[fi++];
break;
}
}
}
}
output[ri] = '\0';
va_end(args);
+7 -3
View File
@@ -14,12 +14,16 @@ uint64_t string_trim(char *string, char character, char **result);
uint64_t string_split(char *string, char separator, uint64_t max, char **result);
uint64_t string_byte_to_hex(uint8_t value, uint64_t max, char *result);
uint64_t string_uint8_to_hex(uint8_t value, uint64_t max, char *result);
uint64_t string_uint16_to_hex(uint16_t value, uint64_t max, char *result);
uint64_t string_uint32_to_hex(uint32_t value, uint64_t max, char *result);
uint64_t string_uint64_to_hex(uint64_t value, uint64_t max, char *result);
uint64_t string_uint_to_decimal(uint64_t value, uint64_t max, char *result);
uint64_t string_int_to_decimal(int64_t value, uint64_t max, char *result);
uint64_t string_uint_to_hex(uint64_t value, uint64_t max, char *result);
uint64_t string_format(const char *format, uint64_t max, char *output, ...);