// SPDX-License-Identifier: MPL-2.0
#include "kernel/tests/test_slabs.h"
#include "kernel/tests/test_utils.h"
#include "kernel/log.h"
#include "std/mem.h"
static sh_uint64 iter_num=10000;
__attribute__((section(".bss")))
static sh_uint32 indexs[10000];
__attribute__((section(".bss")))
static sh_uint64 ptrs[10000];
void sh_test_slabs_load_iterations_count(sh_uint64 iterations_num) {
  iter_num=iterations_num;
  return;
}
SH_STATUS sh_test_slabs_benchmark_region_physical(sh_slab_reg_phys_SLAB_ALLOCATOR *alloc,sh_page_PAGE_TABLE_POOL *ptp) {
  if (alloc==SH_NULLPTR || ptp==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER;
  sh_tsc_TSC_VALUE *tsc_value=sh_test_get_tsc_values_buffer_ptr();
  sh_mem_set_8((sh_uint8*)tsc_value,0x00,sizeof(indexs));
  sh_mem_set_8((sh_uint8*)indexs,0x00,sizeof(indexs));
  sh_log_test("Benchmarking slab allocator for physical objects region...");
  sh_tsc_TSC_VALUE start=0;
  sh_tsc_TSC_VALUE end=0;
  SH_STATUS status;
  for (sh_iter64 i=0;i<iter_num;i++) {
    sh_slab_reg_phys_OBJECT_INDEX reg_idx;
    start=sh_tsc_get_kernel_current_tsc();
    status=sh_slab_reg_phys_alloc(alloc,ptp,&reg_idx);
    end=sh_tsc_get_kernel_current_tsc();
    tsc_value[i]=end-start;
    if (status!=SH_STATUS_SUCCESS) {
      sh_log_error("Test failed for allocation on physical region objects slab allocator. See detail below:",SH_LOG_SOURCE_TEST);
      sh_log_ferror(SH_LOG_SOURCE_TEST,"Error code: %8s\n",status);
      sh_log_ferror(SH_LOG_SOURCE_TEST,"Index: %8u\n",i);
      return SH_STATUS_TEST_FAILED;
    } else {
      indexs[i]=reg_idx;
    }
  }
  sh_test_compute_print_stats("allocations",tsc_value,iter_num,SH_FALSE);
  sh_mem_set_8((sh_uint8*)tsc_value,0x00,sizeof(indexs));
  for (sh_iter64 i=0;i<iter_num;i++) {
    start=sh_tsc_get_kernel_current_tsc();
    status=sh_slab_reg_phys_dealloc(alloc,indexs[i]);
    end=sh_tsc_get_kernel_current_tsc();
    tsc_value[i]=end-start;
    if (status!=SH_STATUS_SUCCESS) {
      sh_log_error("Test failed for deallocation on physical region objects slab allocator. See detail below:",SH_LOG_SOURCE_TEST);
      sh_log_ferror(SH_LOG_SOURCE_TEST,"Error code: %8s\n",status);
      sh_log_ferror(SH_LOG_SOURCE_TEST,"Index: %8u\n",i);
      return SH_STATUS_TEST_FAILED;
    }
  }
  sh_test_compute_print_stats("deallocations",tsc_value,iter_num,SH_FALSE);
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_test_slabs_benchmark_region_virtual(sh_slab_reg_virt_SLAB_ALLOCATOR *alloc,sh_page_PAGE_TABLE_POOL *ptp) {
  if (alloc==SH_NULLPTR || ptp==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER;
  sh_tsc_TSC_VALUE *tsc_value=sh_test_get_tsc_values_buffer_ptr();
  sh_mem_set_8((sh_uint8*)tsc_value,0x00,sizeof(indexs));
  sh_mem_set_8((sh_uint8*)indexs,0x00,sizeof(indexs));
  sh_log_test("Benchmarking slab allocator for virtual objects region...");
  sh_tsc_TSC_VALUE start=0;
  sh_tsc_TSC_VALUE end=0;
  SH_STATUS status;
  for (sh_iter64 i=0;i<iter_num;i++) {
    sh_slab_reg_virt_OBJECT_INDEX reg_idx;
    start=sh_tsc_get_kernel_current_tsc();
    status=sh_slab_reg_virt_alloc(alloc,ptp,&reg_idx);
    end=sh_tsc_get_kernel_current_tsc();
    tsc_value[i]=end-start;
    if (status!=SH_STATUS_SUCCESS) {
      sh_log_error("Test failed for allocation on virtual region objects slab allocator. See detail below:",SH_LOG_SOURCE_TEST);
      sh_log_ferror(SH_LOG_SOURCE_TEST,"Error code: %8s\n",status);
      sh_log_ferror(SH_LOG_SOURCE_TEST,"Index: %8u\n",i);
      return SH_STATUS_TEST_FAILED;
    } else {
      indexs[i]=reg_idx;
    }
  }
  sh_test_compute_print_stats("allocations",tsc_value,iter_num,SH_FALSE);
  sh_mem_set_8((sh_uint8*)tsc_value,0x00,sizeof(indexs));
  for (sh_iter64 i=0;i<iter_num;i++) {
    start=sh_tsc_get_kernel_current_tsc();
    status=sh_slab_reg_virt_dealloc(alloc,indexs[i]);
    end=sh_tsc_get_kernel_current_tsc();
    tsc_value[i]=end-start;
    if (status!=SH_STATUS_SUCCESS) {
      sh_log_error("Test failed for deallocation on virtual region objects slab allocator. See detail below:",SH_LOG_SOURCE_TEST);
      sh_log_ferror(SH_LOG_SOURCE_TEST,"Error code: %8s\n",status);
      sh_log_ferror(SH_LOG_SOURCE_TEST,"Index: %8u\n",i);
      return SH_STATUS_TEST_FAILED;
    }
  }
  sh_test_compute_print_stats("deallocations",tsc_value,iter_num,SH_FALSE);
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_test_slabs_benchmark_radix_node(struct sh_slab_radix_node_SLAB_ALLOCATOR *alloc,sh_page_PAGE_TABLE_POOL *ptp) {
  if (alloc==SH_NULLPTR || ptp==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER;
  sh_tsc_TSC_VALUE *tsc_value=sh_test_get_tsc_values_buffer_ptr();
  sh_mem_set_8((sh_uint8*)tsc_value,0x00,sizeof(indexs));
  sh_mem_set_8((sh_uint8*)ptrs,0x00,sizeof(indexs));
  sh_log_test("Benchmarking slab allocator for radix nodes objects...");
  sh_tsc_TSC_VALUE start=0;
  sh_tsc_TSC_VALUE end=0;
  SH_STATUS status;
  for (sh_iter64 i=0;i<iter_num;i++) {
    sh_radix_NODE *node_ptr;
    start=sh_tsc_get_kernel_current_tsc();
    status=sh_slab_radix_node_alloc(alloc,ptp,&node_ptr);
    end=sh_tsc_get_kernel_current_tsc();
    tsc_value[i]=end-start;
    if (status!=SH_STATUS_SUCCESS) {
      sh_log_error("Test failed for allocation on radix nodes slab allocator. See detail below:",SH_LOG_SOURCE_TEST);
      sh_log_ferror(SH_LOG_SOURCE_TEST,"Error code: %8s\n",status);
      sh_log_ferror(SH_LOG_SOURCE_TEST,"Index: %8u\n",i);
      return SH_STATUS_TEST_FAILED;
    } else {
      ptrs[i]=(sh_uint64)node_ptr;
    }
  }
  sh_test_compute_print_stats("allocations",tsc_value,iter_num,SH_FALSE);
  sh_mem_set_8((sh_uint8*)tsc_value,0x00,sizeof(indexs));
  for (sh_iter64 i=0;i<iter_num;i++) {
    start=sh_tsc_get_kernel_current_tsc();
    status=sh_slab_radix_node_dealloc(alloc,(sh_radix_NODE*)ptrs[i]);
    end=sh_tsc_get_kernel_current_tsc();
    tsc_value[i]=end-start;
    if (status!=SH_STATUS_SUCCESS) {
      sh_log_error("Test failed for deallocation on radix nodes slab allocator. See detail below:",SH_LOG_SOURCE_TEST);
      sh_log_ferror(SH_LOG_SOURCE_TEST,"Error code: %8s\n",status);
      sh_log_ferror(SH_LOG_SOURCE_TEST,"Index: %8u\n",i);
      return SH_STATUS_TEST_FAILED;
    }
  }
  sh_test_compute_print_stats("deallocations",tsc_value,iter_num,SH_FALSE);
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_test_slabs_benchmark(sh_slab_reg_phys_SLAB_ALLOCATOR *alloc_reg_phys,sh_slab_reg_virt_SLAB_ALLOCATOR *alloc_reg_virt,struct sh_slab_radix_node_SLAB_ALLOCATOR *alloc_radix_node,sh_page_PAGE_TABLE_POOL *ptp) {
  if (alloc_reg_phys==SH_NULLPTR || alloc_reg_virt==SH_NULLPTR || alloc_radix_node==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER;
  SH_STATUS status=sh_test_slabs_benchmark_region_physical(alloc_reg_phys,ptp);
  if (sh_status_error(status)) return status;
  status=sh_test_slabs_benchmark_region_virtual(alloc_reg_virt,ptp);
  if (sh_status_error(status)) return status;
  status=sh_test_slabs_benchmark_radix_node(alloc_radix_node,ptp);
  if (sh_status_error(status)) return status;
  return SH_STATUS_SUCCESS;
}
