// 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;
}
