Files
vystem-b/shelter/lib/src/std/print.c
2026-05-27 19:34:54 +02:00

887 lines
26 KiB
C

// SPDX-License-Identifier: MPL-2.0
#include "std/stdlib.h"
#include "devs/apic/lapic.h"
#include "memory/ring.h"
#include "cpu/serial.h"
sh_bool are_rings_buffer_setup=SH_FALSE;
sh_ring_RING_BUFFER_HEADER *ring_buffer_array=SH_NULLPTR;
sh_SPIN_LOCK serial_lock=SH_LOCK();
SH_STATUS sh_print_setup_ring_buffers_per_ap(sh_conf_BOOT_CONFIG *boot_config) {
if (are_rings_buffer_setup==SH_TRUE || ring_buffer_array!=SH_NULLPTR || boot_config==SH_NULLPTR) return SH_STATUS_UNAVAILABLE;
sh_uint64 cpu_count=0;
sh_lapic_DEVICE **lapic_dev_array=sh_lapic_get_by_apic_id_array();
sh_uint64 max_lapic_id=sh_lapic_get_max_apic_id();
for (sh_iter64 i=0;i<max_lapic_id+1;i++) {
sh_lapic_DEVICE *lapic_dev=lapic_dev_array[i];
if (lapic_dev==SH_NULLPTR) continue;
cpu_count++;
}
ring_buffer_array=(sh_ring_RING_BUFFER_HEADER*)sh_malloc(sizeof(sh_ring_RING_BUFFER_HEADER)*cpu_count);
if (!ring_buffer_array) return SH_STATUS_ERROR_NULLPTR_RETURNED;
sh_mem_set_8((sh_uint8*)ring_buffer_array,0x0,sizeof(sh_ring_RING_BUFFER_HEADER)*cpu_count);
sh_uint64 log_ring_size_ap=boot_config->log_ring_size/cpu_count;
if (log_ring_size_ap<4) log_ring_size_ap=4;
for (sh_iter64 i=0;i<cpu_count;i++) {
ring_buffer_array[i].data_start=sh_malloc(log_ring_size_ap*SH_PAGE_SIZE);
if (ring_buffer_array[i].data_start==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED;
ring_buffer_array[i].buffer_bytes_size=(sh_uint32)log_ring_size_ap*SH_PAGE_SIZE;
}
are_rings_buffer_setup=SH_TRUE;
return SH_STATUS_SUCCESS;
}
SH_STATUS sh_print_byte(sh_uint8 byte);
SH_STATUS sh_print_int8(sh_int8 n);
SH_STATUS sh_print_int16(sh_int16 n);
SH_STATUS sh_print_int32(sh_int32 n);
SH_STATUS sh_print_int64(sh_int64 n);
SH_STATUS sh_print_uint8(sh_uint8 n);
SH_STATUS sh_print_uint16(sh_uint16 n);
SH_STATUS sh_print_uint32(sh_uint32 n);
SH_STATUS sh_print_uint64(sh_uint64 n);
SH_STATUS sh_print_uint8_hex(sh_uint8 n);
SH_STATUS sh_print_uint16_hex(sh_uint16 n);
SH_STATUS sh_print_uint32_hex(sh_uint32 n);
SH_STATUS sh_print_uint64_hex(sh_uint64 n);
SH_STATUS sh_print_uint64_hex_fixed(sh_uint64 n);
SH_STATUS sh_print_double(double value);
SH_STATUS sh_print_flush();
SH_STATUS sh_print_flush() {
char *text_to_flush=sh_malloc(sh_smp_gs_base()->per_cpu->bytes_outputed);
if (!text_to_flush) return SH_STATUS_ERROR_NULLPTR_RETURNED;
sh_spin_lock(&serial_lock);
SH_STATUS status=sh_ring_read_bytes(&ring_buffer_array[sh_smp_gs_base()->per_cpu->cpu_id],sh_smp_gs_base()->per_cpu->bytes_outputed,text_to_flush);
if (sh_status_error(status)) {
sh_spin_unlock(&serial_lock);
return status;
}
sh_log_string(text_to_flush);
sh_spin_unlock(&serial_lock);
sh_free(text_to_flush);
return SH_STATUS_SUCCESS;
}
SH_STATUS sh_print_byte(sh_uint8 byte) {
if (!are_rings_buffer_setup) {
sh_log_byte(byte);
return SH_STATUS_SUCCESS;
} else {
SH_STATUS status=sh_ring_write_byte(&ring_buffer_array[sh_smp_gs_base()->per_cpu->cpu_id],byte);
if (sh_status_error(status)) return status;
sh_smp_gs_base()->per_cpu->bytes_outputed++;
return SH_STATUS_SUCCESS;
}
}
SH_STATUS sh_print_int8(sh_int8 n) {
return sh_print_int64((sh_int64)n);
}
SH_STATUS sh_print_int16(sh_int16 n) {
return sh_print_int64((sh_int64)n);
}
SH_STATUS sh_print_int32(sh_int32 n) {
return sh_print_int64((sh_int64)n);
}
SH_STATUS sh_print_int64(sh_int64 n) {
sh_uint64 absolute_val;
if (n<0) {
sh_print_byte('-');
absolute_val=(sh_uint64)(-(n+1))+1;
} else {
absolute_val=(sh_uint64)n;
}
return sh_print_uint64(absolute_val);
}
SH_STATUS sh_print_uint8(sh_uint8 n) {
return sh_print_uint64((sh_uint64)n);
}
SH_STATUS sh_print_uint16(sh_uint16 n) {
return sh_print_uint64((sh_uint64)n);
}
SH_STATUS sh_print_uint32(sh_uint32 n) {
return sh_print_uint64((sh_uint64)n);
}
SH_STATUS sh_print_uint64(sh_uint64 n) {
char buf[20];
sh_uint32 i=0;
if (n==0) {
sh_print_byte('0');
return SH_STATUS_SUCCESS;
}
while (n>0) {
buf[i++]='0'+(sh_int8)(n%10);
n/=10;
}
while (i--) {
sh_print_byte((sh_uint8)buf[i]);
}
return SH_STATUS_SUCCESS;
}
SH_STATUS sh_print_uint8_hex(sh_uint8 n) {
return sh_print_uint64_hex((sh_uint64)n);
}
SH_STATUS sh_print_uint16_hex(sh_uint16 n) {
return sh_print_uint64_hex((sh_uint64)n);
}
SH_STATUS sh_print_uint32_hex(sh_uint32 n) {
return sh_print_uint64_hex((sh_uint64)n);
}
SH_STATUS sh_print_uint64_hex(sh_uint64 n) {
char buf[16];
sh_uint32 i=0;
const char hex_digits[]="0123456789ABCDEF";
if (n==0) {
sh_print_byte('0');
return SH_STATUS_SUCCESS;
}
while (n>0) {
buf[i++]=hex_digits[n & 0xF];
n>>=4;
}
while (i--) {
sh_print_byte((sh_uint8)buf[i]);
}
return SH_STATUS_SUCCESS;
}
SH_STATUS sh_print_uint64_hex_fixed(sh_uint64 n) {
const char hex_digits[]="0123456789ABCDEF";
for (sh_int8 i=15;i>=0;i--) {
sh_uint8 nibble=(sh_uint8)((n>>(i*4))&0xF);
sh_print_byte((sh_uint8)hex_digits[nibble]);
}
return SH_STATUS_SUCCESS;
}
SH_STATUS sh_print_double(double value) {
if (value<0) {
sh_print_byte('-');
value=-value;
}
sh_uint64 integer_part=(sh_uint64)value;
double fractional_part=value-(double)integer_part;
SH_STATUS status=sh_print_uint64(integer_part);
if (status!=SH_STATUS_SUCCESS) return status;
sh_print_byte('.');
for (sh_iter64 i=0;i<6;i++) {
fractional_part*=10.0;
sh_uint64 digit=(sh_uint64)fractional_part;
sh_print_byte('0'+(sh_uint8)digit);
fractional_part-=(double)digit;
}
return SH_STATUS_SUCCESS;
}
SH_STATUS sh_print(sh_log_OUTPUT_TYPE output_type,char *text) {
if (text==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER;
sh_tsc_TSC_VALUE tsc_value=sh_tsc_get_kernel_current_tsc();
if (!sh_log_output_type_valid(output_type)) return SH_STATUS_INVALID_PARAMETER;
if (output_type<sh_log_get_log_level()) {
return SH_STATUS_SUCCESS;
}
sh_smp_gs_base()->per_cpu->bytes_outputed=0;
sh_asm_cli();
char *intro="[Shelter:";
while (*intro) {
sh_print_byte((sh_uint8)*intro++);
}
sh_print_uint64(sh_smp_gs_base()->per_cpu->cpu_id);
sh_print_byte('@');
char* type=SH_NULLPTR;
if (output_type==SH_DEBUG) {
type="Debug] ";
} else if (output_type==SH_LOG) {
type="Log] ";
} else if (output_type==SH_WARNING) {
type="Warning] ";
} else if (output_type==SH_ERROR) {
type="Error] ";
} else if (output_type==SH_CRITICAL) {
type="Critical] ";
} else if (output_type==SH_FATAL) {
type="Fatal] ";
} else if (output_type==SH_TEST) {
type="Test] ";
} else if (output_type==SH_FAULT) {
type="Fault] ";
}
while (*type) {
sh_print_byte((sh_uint8)*type++);
}
sh_print_uint64(tsc_value);
char *transition=" : ";
while (*transition) {
sh_print_byte((sh_uint8)*transition++);
}
while (*text) {
sh_print_byte((sh_uint8)*text++);
}
SH_STATUS status=sh_print_flush();
if (sh_status_error(status)) return status;
sh_asm_sti();
return SH_STATUS_SUCCESS;
}
SH_STATUS sh_printf(sh_log_OUTPUT_TYPE output_type,char* format,...) {
if (format==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER;
sh_tsc_TSC_VALUE tsc_value=sh_tsc_get_kernel_current_tsc();
if (!sh_log_output_type_valid(output_type)) return SH_STATUS_INVALID_PARAMETER;
if (output_type<sh_log_get_log_level()) {
return SH_STATUS_SUCCESS;
}
sh_smp_gs_base()->per_cpu->bytes_outputed=0;
sh_asm_cli();
char *intro="[Shelter:";
while (*intro) {
sh_print_byte((sh_uint8)*intro++);
}
sh_print_uint64(sh_smp_gs_base()->per_cpu->cpu_id);
sh_print_byte('@');
char* type=SH_NULLPTR;
if (output_type==SH_DEBUG) {
type="Debug] ";
} else if (output_type==SH_LOG) {
type="Log] ";
} else if (output_type==SH_WARNING) {
type="Warning] ";
} else if (output_type==SH_ERROR) {
type="Error] ";
} else if (output_type==SH_CRITICAL) {
type="Critical] ";
} else if (output_type==SH_FATAL) {
type="Fatal] ";
} else if (output_type==SH_TEST) {
type="Test] ";
} else if (output_type==SH_FAULT) {
type="Fault] ";
}
while (*type) {
sh_print_byte((sh_uint8)*type++);
}
sh_print_uint64(tsc_value);
char *transition=" : ";
while (*transition) {
sh_print_byte((sh_uint8)*transition++);
}
va_list args;
va_start(args,format);
for (const char *p=format;*p!='\0';p++) {
if (*p!='%') {
sh_print_byte((sh_uint8)*p);
continue;
}
p++;
switch (*p) {
case 's': {
char *s=va_arg(args,char *);
while (*s) {
sh_print_byte((sh_uint8)*s++);
}
break;
}
case 'd': {
double s=va_arg(args,double);
sh_print_double(s);
break;
}
case '1': {
if (p[1]=='u') {
sh_print_uint8((sh_uint8)va_arg(args,int));
p++;
} else if (p[1]=='s') {
sh_print_int8((sh_int8)va_arg(args,int));
p++;
} else if (p[1]=='U') {
sh_print_uint8_hex((sh_uint8)va_arg(args,int));
p++;
}
break;
}
case '2': {
if (p[1]=='u') {
sh_print_uint16((sh_uint16)va_arg(args,int));
p++;
} else if (p[1]=='s') {
sh_print_int16((sh_int16)va_arg(args,int));
p++;
} else if (p[1]=='U') {
sh_print_uint16_hex((sh_uint16)va_arg(args,int));
p++;
}
break;
}
case '4': {
if (p[1]=='u') {
sh_print_uint32((sh_uint32)va_arg(args,int));
p++;
} else if (p[1]=='s') {
sh_print_int32((sh_int32)va_arg(args,int));
p++;
} else if (p[1]=='U') {
sh_print_uint32_hex((sh_uint32)va_arg(args,int));
p++;
}
break;
}
case '8': {
if (p[1]=='u') {
sh_print_uint64(va_arg(args,unsigned long long));
p++;
} else if (p[1]=='s') {
sh_print_int64(va_arg(args,long long));
p++;
} else if (p[1]=='U') {
sh_print_uint64_hex(va_arg(args,unsigned long long));
p++;
}
break;
}
case 'x': {
sh_uint64 x=va_arg(args,sh_uint64);
sh_print_uint64_hex_fixed(x);
break;
}
case 'c': {
sh_print_byte((sh_uint8)va_arg(args,int));
break;
}
case '%': {
sh_print_byte('%');
break;
}
default:
sh_print_byte((sh_uint8)*p);
break;
}
}
va_end(args);
SH_STATUS status=sh_print_flush();
if (sh_status_error(status)) return status;
sh_asm_sti();
return SH_STATUS_SUCCESS;
}
SH_STATUS sh_print_ibyte(sh_uint8 byte);
SH_STATUS sh_print_iint8(sh_int8 n);
SH_STATUS sh_print_iint16(sh_int16 n);
SH_STATUS sh_print_iint32(sh_int32 n);
SH_STATUS sh_print_iint64(sh_int64 n);
SH_STATUS sh_print_iuint8(sh_uint8 n);
SH_STATUS sh_print_iuint16(sh_uint16 n);
SH_STATUS sh_print_iuint32(sh_uint32 n);
SH_STATUS sh_print_iuint64(sh_uint64 n);
SH_STATUS sh_print_iuint8_hex(sh_uint8 n);
SH_STATUS sh_print_iuint16_hex(sh_uint16 n);
SH_STATUS sh_print_iuint32_hex(sh_uint32 n);
SH_STATUS sh_print_iuint64_hex(sh_uint64 n);
SH_STATUS sh_print_iuint64_hex_fixed(sh_uint64 n);
SH_STATUS sh_print_idouble(double value);
SH_STATUS sh_print_ibyte(sh_uint8 byte) {
sh_serial_send_byte(byte);
return SH_STATUS_SUCCESS;
}
SH_STATUS sh_print_iint8(sh_int8 n) {
return sh_print_iint64((sh_int64)n);
}
SH_STATUS sh_print_iint16(sh_int16 n) {
return sh_print_iint64((sh_int64)n);
}
SH_STATUS sh_print_iint32(sh_int32 n) {
return sh_print_iint64((sh_int64)n);
}
SH_STATUS sh_print_iint64(sh_int64 n) {
sh_uint64 absolute_val;
if (n<0) {
sh_print_ibyte('-');
absolute_val=(sh_uint64)(-(n+1))+1;
} else {
absolute_val=(sh_uint64)n;
}
return sh_print_iuint64(absolute_val);
}
SH_STATUS sh_print_iuint8(sh_uint8 n) {
return sh_print_iuint64((sh_uint64)n);
}
SH_STATUS sh_print_iuint16(sh_uint16 n) {
return sh_print_iuint64((sh_uint64)n);
}
SH_STATUS sh_print_iuint32(sh_uint32 n) {
return sh_print_iuint64((sh_uint64)n);
}
SH_STATUS sh_print_iuint64(sh_uint64 n) {
char buf[20];
sh_uint32 i=0;
if (n==0) {
sh_print_ibyte('0');
return SH_STATUS_SUCCESS;
}
while (n>0) {
buf[i++]='0'+(sh_int8)(n%10);
n/=10;
}
while (i--) {
sh_print_ibyte((sh_uint8)buf[i]);
}
return SH_STATUS_SUCCESS;
}
SH_STATUS sh_print_iuint8_hex(sh_uint8 n) {
return sh_print_iuint64_hex((sh_uint64)n);
}
SH_STATUS sh_print_iuint16_hex(sh_uint16 n) {
return sh_print_iuint64_hex((sh_uint64)n);
}
SH_STATUS sh_print_iuint32_hex(sh_uint32 n) {
return sh_print_iuint64_hex((sh_uint64)n);
}
SH_STATUS sh_print_iuint64_hex(sh_uint64 n) {
char buf[16];
sh_uint32 i=0;
const char hex_digits[]="0123456789ABCDEF";
if (n==0) {
sh_print_ibyte('0');
return SH_STATUS_SUCCESS;
}
while (n>0) {
buf[i++]=hex_digits[n & 0xF];
n>>=4;
}
while (i--) {
sh_print_ibyte((sh_uint8)buf[i]);
}
return SH_STATUS_SUCCESS;
}
SH_STATUS sh_print_iuint64_hex_fixed(sh_uint64 n) {
const char hex_digits[]="0123456789ABCDEF";
for (sh_int8 i=15;i>=0;i--) {
sh_uint8 nibble=(sh_uint8)((n>>(i*4))&0xF);
sh_print_ibyte((sh_uint8)hex_digits[nibble]);
}
return SH_STATUS_SUCCESS;
}
SH_STATUS sh_print_idouble(double value) {
if (value<0) {
sh_print_ibyte('-');
value=-value;
}
sh_uint64 integer_part=(sh_uint64)value;
double fractional_part=value-(double)integer_part;
SH_STATUS status=sh_print_iuint64(integer_part);
if (status!=SH_STATUS_SUCCESS) return status;
sh_print_ibyte('.');
for (sh_iter64 i=0;i<6;i++) {
fractional_part*=10.0;
sh_uint64 digit=(sh_uint64)fractional_part;
sh_print_ibyte('0'+(sh_uint8)digit);
fractional_part-=(double)digit;
}
return SH_STATUS_SUCCESS;
}
SH_STATUS sh_iprint(sh_log_OUTPUT_TYPE output_type,char *text) {
if (text==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER;
sh_tsc_TSC_VALUE tsc_value=sh_tsc_get_kernel_current_tsc();
if (!sh_log_output_type_valid(output_type)) return SH_STATUS_INVALID_PARAMETER;
if (output_type<sh_log_get_log_level()) {
return SH_STATUS_SUCCESS;
}
sh_bool was_locked_by_me=SH_FALSE;
if (sh_spin_wholock(&serial_lock)==sh_smp_gs_base()->lapic_id) was_locked_by_me=SH_TRUE;
if (!was_locked_by_me) {
sh_spin_lock(&serial_lock);
}
char *intro="[Shelter:";
while (*intro) {
sh_print_ibyte((sh_uint8)*intro++);
}
sh_print_iuint64(sh_smp_gs_base()->per_cpu->cpu_id);
sh_print_ibyte('@');
char* type=SH_NULLPTR;
if (output_type==SH_DEBUG) {
type="Debug] ";
} else if (output_type==SH_LOG) {
type="Log] ";
} else if (output_type==SH_WARNING) {
type="Warning] ";
} else if (output_type==SH_ERROR) {
type="Error] ";
} else if (output_type==SH_CRITICAL) {
type="Critical] ";
} else if (output_type==SH_FATAL) {
type="Fatal] ";
} else if (output_type==SH_TEST) {
type="Test] ";
} else if (output_type==SH_FAULT) {
type="Fault] ";
}
while (*type) {
sh_print_ibyte((sh_uint8)*type++);
}
sh_print_iuint64(tsc_value);
char *transition=" : ";
while (*transition) {
sh_print_ibyte((sh_uint8)*transition++);
}
while (*text) {
sh_print_ibyte((sh_uint8)*text++);
}
if (!was_locked_by_me) sh_spin_unlock(&serial_lock);
return SH_STATUS_SUCCESS;
}
SH_STATUS sh_iprintf(sh_log_OUTPUT_TYPE output_type,char* format,...) {
if (format==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER;
sh_tsc_TSC_VALUE tsc_value=sh_tsc_get_kernel_current_tsc();
if (!sh_log_output_type_valid(output_type)) return SH_STATUS_INVALID_PARAMETER;
if (output_type<sh_log_get_log_level()) {
return SH_STATUS_SUCCESS;
}
sh_bool was_locked_by_me=SH_FALSE;
if (sh_spin_wholock(&serial_lock)==sh_smp_gs_base()->lapic_id) was_locked_by_me=SH_TRUE;
if (!was_locked_by_me) {
sh_spin_lock(&serial_lock);
}
char *intro="[Shelter:";
while (*intro) {
sh_print_ibyte((sh_uint8)*intro++);
}
sh_print_iuint64(sh_smp_gs_base()->per_cpu->cpu_id);
sh_print_ibyte('@');
char* type=SH_NULLPTR;
if (output_type==SH_DEBUG) {
type="Debug] ";
} else if (output_type==SH_LOG) {
type="Log] ";
} else if (output_type==SH_WARNING) {
type="Warning] ";
} else if (output_type==SH_ERROR) {
type="Error] ";
} else if (output_type==SH_CRITICAL) {
type="Critical] ";
} else if (output_type==SH_FATAL) {
type="Fatal] ";
} else if (output_type==SH_TEST) {
type="Test] ";
} else if (output_type==SH_FAULT) {
type="Fault] ";
}
while (*type) {
sh_print_ibyte((sh_uint8)*type++);
}
sh_print_iuint64(tsc_value);
char *transition=" : ";
while (*transition) {
sh_print_ibyte((sh_uint8)*transition++);
}
va_list args;
va_start(args,format);
for (const char *p=format;*p!='\0';p++) {
if (*p!='%') {
sh_print_ibyte((sh_uint8)*p);
continue;
}
p++;
switch (*p) {
case 's': {
char *s=va_arg(args,char *);
while (*s) {
sh_print_ibyte((sh_uint8)*s++);
}
break;
}
case 'd': {
double s=va_arg(args,double);
sh_print_idouble(s);
break;
}
case '1': {
if (p[1]=='u') {
sh_print_iuint8((sh_uint8)va_arg(args,int));
p++;
} else if (p[1]=='s') {
sh_print_iint8((sh_int8)va_arg(args,int));
p++;
} else if (p[1]=='U') {
sh_print_iuint8_hex((sh_uint8)va_arg(args,int));
p++;
}
break;
}
case '2': {
if (p[1]=='u') {
sh_print_iuint16((sh_uint16)va_arg(args,int));
p++;
} else if (p[1]=='s') {
sh_print_iint16((sh_int16)va_arg(args,int));
p++;
} else if (p[1]=='U') {
sh_print_iuint16_hex((sh_uint16)va_arg(args,int));
p++;
}
break;
}
case '4': {
if (p[1]=='u') {
sh_print_iuint32((sh_uint32)va_arg(args,int));
p++;
} else if (p[1]=='s') {
sh_print_iint32((sh_int32)va_arg(args,int));
p++;
} else if (p[1]=='U') {
sh_print_iuint32_hex((sh_uint32)va_arg(args,int));
p++;
}
break;
}
case '8': {
if (p[1]=='u') {
sh_print_iuint64(va_arg(args,unsigned long long));
p++;
} else if (p[1]=='s') {
sh_print_iint64(va_arg(args,long long));
p++;
} else if (p[1]=='U') {
sh_print_iuint64_hex(va_arg(args,unsigned long long));
p++;
}
break;
}
case 'x': {
sh_uint64 x=va_arg(args,sh_uint64);
sh_print_iuint64_hex_fixed(x);
break;
}
case 'c': {
sh_print_ibyte((sh_uint8)va_arg(args,int));
break;
}
case '%': {
sh_print_ibyte('%');
break;
}
default:
sh_print_ibyte((sh_uint8)*p);
break;
}
}
va_end(args);
if (!was_locked_by_me) sh_spin_unlock(&serial_lock);
return SH_STATUS_SUCCESS;
}
typedef struct sh_sprint_WRITER {
sh_uint8 *out;
sh_uint8 *end;
} sh_sprint_WRITER;
static inline SH_STATUS sh_sprint_byte(sh_sprint_WRITER *w,sh_uint8 c) {
if(w->out>=w->end) return SH_STATUS_TOO_MUCH_DATA;
*w->out++=c;
return SH_STATUS_SUCCESS;
}
SH_STATUS sh_sprint_int8(sh_sprint_WRITER *w,sh_int8 n);
SH_STATUS sh_sprint_int16(sh_sprint_WRITER *w,sh_int16 n);
SH_STATUS sh_sprint_int32(sh_sprint_WRITER *w,sh_int32 n);
SH_STATUS sh_sprint_int64(sh_sprint_WRITER *w,sh_int64 n);
SH_STATUS sh_sprint_uint8(sh_sprint_WRITER *w,sh_uint8 n);
SH_STATUS sh_sprint_uint16(sh_sprint_WRITER *w,sh_uint16 n);
SH_STATUS sh_sprint_uint32(sh_sprint_WRITER *w,sh_uint32 n);
SH_STATUS sh_sprint_uint64(sh_sprint_WRITER *w,sh_uint64 n);
SH_STATUS sh_sprint_uint8_hex(sh_sprint_WRITER *w,sh_uint8 n);
SH_STATUS sh_sprint_uint16_hex(sh_sprint_WRITER *w,sh_uint16 n);
SH_STATUS sh_sprint_uint32_hex(sh_sprint_WRITER *w,sh_uint32 n);
SH_STATUS sh_sprint_uint64_hex(sh_sprint_WRITER *w,sh_uint64 n);
SH_STATUS sh_sprint_uint64_hex_fixed(sh_sprint_WRITER *w,sh_uint64 n);
SH_STATUS sh_sprint_double(sh_sprint_WRITER *w,double value);
SH_STATUS sh_sprint_int8(sh_sprint_WRITER *w,sh_int8 n) {
return sh_sprint_int64(w,(sh_int64)n);
}
SH_STATUS sh_sprint_int16(sh_sprint_WRITER *w,sh_int16 n) {
return sh_sprint_int64(w,(sh_int64)n);
}
SH_STATUS sh_sprint_int32(sh_sprint_WRITER *w,sh_int32 n) {
return sh_sprint_int64(w,(sh_int64)n);
}
SH_STATUS sh_sprint_int64(sh_sprint_WRITER *w,sh_int64 n) {
sh_uint64 absolute_val;
if (n<0) {
SH_STATUS status=sh_sprint_byte(w,'-');
if (sh_status_error(status)) return status;
absolute_val=(sh_uint64)(-(n+1))+1;
} else {
absolute_val=(sh_uint64)n;
}
return sh_sprint_uint64(w,absolute_val);
}
SH_STATUS sh_sprint_uint8(sh_sprint_WRITER *w,sh_uint8 n) {
return sh_sprint_uint64(w,(sh_int64)n);
}
SH_STATUS sh_sprint_uint16(sh_sprint_WRITER *w,sh_uint16 n) {
return sh_sprint_uint64(w,(sh_int64)n);
}
SH_STATUS sh_sprint_uint32(sh_sprint_WRITER *w,sh_uint32 n) {
return sh_sprint_uint64(w,(sh_int64)n);
}
SH_STATUS sh_sprint_uint64(sh_sprint_WRITER *w,sh_uint64 n) {
char buf[20];
sh_uint32 i=0;
if (n==0) return sh_sprint_byte(w,'0');
while (n>0) {
buf[i++]='0'+(sh_int8)(n%10);
n/=10;
}
while (i--) {
SH_STATUS status=sh_sprint_byte(w,(sh_uint8)buf[i]);
if (sh_status_error(status)) return status;
}
return SH_STATUS_SUCCESS;
}
SH_STATUS sh_sprint_uint8_hex(sh_sprint_WRITER *w,sh_uint8 n) {
return sh_sprint_uint64_hex(w,(sh_uint64)n);
}
SH_STATUS sh_sprint_uint16_hex(sh_sprint_WRITER *w,sh_uint16 n) {
return sh_sprint_uint64_hex(w,(sh_uint64)n);
}
SH_STATUS sh_sprint_uint32_hex(sh_sprint_WRITER *w,sh_uint32 n) {
return sh_sprint_uint64_hex(w,(sh_uint64)n);
}
SH_STATUS sh_sprint_uint64_hex(sh_sprint_WRITER *w,sh_uint64 n) {
char buf[16];
sh_uint32 i=0;
const char hex_digits[]="0123456789ABCDEF";
if (n==0) return sh_sprint_byte(w,'0');
while (n>0) {
buf[i++]=hex_digits[n & 0xF];
n>>=4;
}
while (i--) {
SH_STATUS status=sh_sprint_byte(w,(sh_uint8)buf[i]);
if (sh_status_error(status)) return status;
}
return SH_STATUS_SUCCESS;
}
SH_STATUS sh_sprint_uint64_hex_fixed(sh_sprint_WRITER *w,sh_uint64 n) {
const char hex[]="0123456789ABCDEF";
for (sh_int8 i=15;i>=0;i--) {
char c=hex[(n>>(i*4))&0xF];
SH_STATUS status=sh_sprint_byte(w,(sh_uint8)c);
if (sh_status_error(status)) return status;
}
return SH_STATUS_SUCCESS;
}
SH_STATUS sh_sprint_double(sh_sprint_WRITER *w,double value) {
SH_STATUS status;
if(value<0) {
status=sh_sprint_byte(w,'-');
if (sh_status_error(status)) return status;
value= -value;
}
sh_uint64 int_part=(sh_uint64)value;
double frac=value-(double)int_part;
status=sh_sprint_uint64(w,int_part);
if (sh_status_error(status)) return status;
status=sh_sprint_byte(w,'.');
if (sh_status_error(status)) return status;
for(sh_iter64 i=0;i<6;i++) {
frac*=10.0;
sh_uint64 d=(sh_uint64)frac;
status=sh_sprint_byte(w,(sh_uint8)'0'+(sh_uint8)d);
if (sh_status_error(status)) return status;
frac-=(double)d;
}
return SH_STATUS_SUCCESS;
}
SH_STATUS sh_sprintf(char *output_string,sh_uint64 output_len,char *format,...) {
if (format==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER;
sh_asm_cli();
sh_sprint_WRITER w={
.out=(sh_uint8*)output_string,
.end=(sh_uint8*)output_string+output_len
};
va_list args;
va_start(args,format);
for (const char *p=format;*p!='\0';p++) {
if (*p!='%') {
sh_sprint_byte(&w,(sh_uint8)*p);
continue;
}
p++;
switch (*p) {
case 's': {
char *s=va_arg(args,char *);
while (*s) {
sh_sprint_byte(&w,(sh_uint8)*s++);
}
break;
}
case 'd': {
double s=va_arg(args,double);
sh_sprint_double(&w,s);
break;
}
case '1': {
if (p[1]=='u') {
sh_sprint_uint8(&w,(sh_uint8)va_arg(args,int));
p++;
} else if (p[1]=='s') {
sh_sprint_int8(&w,(sh_int8)va_arg(args,int));
p++;
} else if (p[1]=='U') {
sh_sprint_uint8_hex(&w,(sh_uint8)va_arg(args,int));
p++;
}
break;
}
case '2': {
if (p[1]=='u') {
sh_sprint_uint16(&w,(sh_uint16)va_arg(args,int));
p++;
} else if (p[1]=='s') {
sh_sprint_int16(&w,(sh_int16)va_arg(args,int));
p++;
} else if (p[1]=='U') {
sh_sprint_uint16_hex(&w,(sh_uint16)va_arg(args,int));
p++;
}
break;
}
case '4': {
if (p[1]=='u') {
sh_sprint_uint32(&w,(sh_uint32)va_arg(args,int));
p++;
} else if (p[1]=='s') {
sh_sprint_int32(&w,(sh_int32)va_arg(args,int));
p++;
} else if (p[1]=='U') {
sh_sprint_uint32_hex(&w,(sh_uint32)va_arg(args,int));
p++;
}
break;
}
case '8': {
if (p[1]=='u') {
sh_sprint_uint64(&w,va_arg(args,unsigned long long));
p++;
} else if (p[1]=='s') {
sh_sprint_int64(&w,va_arg(args,long long));
p++;
} else if (p[1]=='U') {
sh_sprint_uint64_hex(&w,va_arg(args,unsigned long long));
p++;
}
break;
}
case 'x': {
sh_uint64 x=va_arg(args,sh_uint64);
sh_sprint_uint64_hex_fixed(&w,x);
break;
}
case 'c': {
sh_sprint_byte(&w,(sh_uint8)va_arg(args,int));
break;
}
case '%': {
sh_sprint_byte(&w,'%');
break;
}
default:
sh_sprint_byte(&w,(sh_uint8)*p);
break;
}
}
va_end(args);
SH_STATUS status=sh_sprint_byte(&w,'\0');
if (sh_status_error(status)) {
output_string[output_len-1]='\0';
}
sh_asm_sti();
return SH_STATUS_SUCCESS;
}