// SPDX-License-Identifier: MPL-2.0
#include "kernel/tests/test_malloc.h"
#include "kernel/tests/test_utils.h"
#include "kernel/tests/payloads/test_malloc_payload.h"
#include "kernel/log.h"
#include "memory/page.h"
#include "std/mem.h"
#include "std/malloc.h"
static sh_uint64 iter_num=10000;
__attribute__((section(".bss")))
static sh_uint64 address_list[10000];
SH_STATUS sh_test_malloc_benchmark() {
  sh_tsc_TSC_VALUE *tsc_value=sh_test_get_tsc_values_buffer_ptr();
  sh_mem_set_8((sh_uint8*)tsc_value,0x00,10000*sizeof(sh_tsc_TSC_VALUE));
  sh_mem_set_8((sh_uint8*)address_list,0x00,sizeof(address_list));
  sh_log_test("Benchmarking Malloc subsystem...");
  sh_tsc_TSC_VALUE start=0;
  sh_tsc_TSC_VALUE end=0;
  for (sh_iter64 i=0;i<iter_num;i++) {
    start=sh_tsc_get_kernel_current_tsc();
    address_list[i]=(sh_uint64)sh_malloc(test_malloc_small_size[i]);
    end=sh_tsc_get_kernel_current_tsc();
    tsc_value[i]=end-start;
    if (sh_malloc_get_last_status()!=SH_STATUS_SUCCESS) {
      sh_log_error("Test failed for sh_malloc for small size. See detail below:",SH_LOG_SOURCE_TEST);
      sh_log_ferror(SH_LOG_SOURCE_TEST,"Error code: %8s\n",sh_malloc_get_last_status());
      sh_log_ferror(SH_LOG_SOURCE_TEST,"Index: %8u\n",i);
      sh_log_ferror(SH_LOG_SOURCE_TEST,"Size: %4u\n",test_malloc_small_size[i]);
      return SH_STATUS_TEST_FAILED;
    }
  }
  sh_test_compute_print_stats("sh_malloc for small size",tsc_value,iter_num,SH_FALSE);
  sh_mem_set_8((sh_uint8*)tsc_value,0x00,10000*sizeof(sh_tsc_TSC_VALUE));
  for (sh_iter64 i=0;i<iter_num;i++) {
    start=sh_tsc_get_kernel_current_tsc();
    sh_free((void*)address_list[i]);
    end=sh_tsc_get_kernel_current_tsc();
    tsc_value[i]=end-start;
    if (sh_malloc_get_last_status()!=SH_STATUS_SUCCESS) {
      sh_log_error("Test failed for sh_free for small size. See detail below:",SH_LOG_SOURCE_TEST);
      sh_log_ferror(SH_LOG_SOURCE_TEST,"Error code: %8s\n",sh_malloc_get_last_status());
      sh_log_ferror(SH_LOG_SOURCE_TEST,"Index: %8u\n",i);
      sh_log_ferror(SH_LOG_SOURCE_TEST,"Size: %4u\n",test_malloc_small_size[i]);
      return SH_STATUS_TEST_FAILED;
    }
  }
  sh_test_compute_print_stats("sh_free for small size",tsc_value,iter_num,SH_FALSE);
  sh_mem_set_8((sh_uint8*)tsc_value,0x00,10000*sizeof(sh_tsc_TSC_VALUE));
  sh_mem_set_8((sh_uint8*)address_list,0x00,sizeof(address_list));
  sh_uint16 alloc_count=0;
  sh_uint16 free_count=1000;
  for (sh_iter64 i=0;i<2000;i++) {
    sh_uint64 alloc_num=test_malloc_big_alloc[i];
    sh_uint64 alloc_size=test_malloc_big_size[alloc_num]*SH_PAGE_SIZE;
    if (address_list[alloc_num]==0) {
      start=sh_tsc_get_kernel_current_tsc();
      address_list[alloc_num]=(sh_uint64)sh_malloc(alloc_size);
      end=sh_tsc_get_kernel_current_tsc();
      tsc_value[alloc_count]=end-start;
      alloc_count++;
      if (sh_malloc_get_last_status()!=SH_STATUS_SUCCESS) {
        sh_log_error("Test failed for sh_malloc for big size. See detail below:",SH_LOG_SOURCE_TEST);
        sh_log_ferror(SH_LOG_SOURCE_TEST,"Error code: %8s\n",sh_malloc_get_last_status());
        sh_log_ferror(SH_LOG_SOURCE_TEST,"Index: %8u\n",i);
        sh_log_ferror(SH_LOG_SOURCE_TEST,"Size: %8u\n",alloc_size);
        sh_log_ferror(SH_LOG_SOURCE_TEST,"Alloc Num: %8u\n",alloc_num);
        sh_log_mem_stats(SH_LOG_SOURCE_TEST);
        return SH_STATUS_TEST_FAILED;
      }
    } else {
      start=sh_tsc_get_kernel_current_tsc();
      sh_free((void*)address_list[alloc_num]);
      end=sh_tsc_get_kernel_current_tsc();
      tsc_value[free_count]=end-start;
      free_count++;
      if (sh_malloc_get_last_status()!=SH_STATUS_SUCCESS) {
        sh_log_error("Test failed for sh_free for big size. See detail below:",SH_LOG_SOURCE_TEST);
        sh_log_ferror(SH_LOG_SOURCE_TEST,"Error code: %8s\n",sh_malloc_get_last_status());
        sh_log_ferror(SH_LOG_SOURCE_TEST,"Index: %8u\n",i);
        sh_log_ferror(SH_LOG_SOURCE_TEST,"Size: %8u\n",alloc_size);
        sh_log_ferror(SH_LOG_SOURCE_TEST,"Alloc Num: %8u\n",alloc_num);
        sh_log_mem_stats(SH_LOG_SOURCE_TEST);
        return SH_STATUS_TEST_FAILED;
      }
    }
  }
  sh_test_compute_print_stats("sh_malloc for pages allocations",tsc_value,alloc_count,SH_FALSE);
  sh_test_compute_print_stats("sh_free for pages allocations",tsc_value+1000,free_count-1000,SH_FALSE);
  sh_log_mem_stats(SH_LOG_SOURCE_TEST);
  return SH_STATUS_SUCCESS;
}
