// SPDX-License-Identifier: MPL-2.0
#include "memory/pez/pez.h"
#include "memory/pez/pez_debug.h"
#include "kernel/log.h"
sh_bool sh_pez_is_pez_available=SH_FALSE;
sh_pez_PHYSICAL_PLANE *reference_phys_plane=SH_NULLPTR;
void sh_pez_set_available() {
  sh_pez_is_pez_available=SH_TRUE;
}
sh_bool sh_pez_is_available() {
  return sh_pez_is_pez_available;
}
static void sh_pez_set_reference_phys_plane(sh_pez_PHYSICAL_PLANE *phys_plane) {
  reference_phys_plane=phys_plane;
}
sh_pez_PHYSICAL_PLANE* sh_pez_get_reference_phys_plane() {
  return reference_phys_plane;
}
static inline void sh_pez_internal_pack_index(sh_uint8 *dest,sh_uint32 index) {
  dest[0]=(sh_uint8)(index & 0xFF);
  dest[1]=(sh_uint8)((index>>8)&0xFF);
  dest[2]=(sh_uint8)((index>>16)&0xFF);
  return;
}
static inline sh_uint32 sh_pez_internal_unpack_index(sh_uint8 *src) {
  return (sh_uint32)src[0] | ((sh_uint32)src[1]<<8) | ((sh_uint32)src[2]<<16);
}
typedef struct {
  sh_uint32 region_index;
  sh_uint32 previous_index;
} sh_pez_internal_BOUNDARY_ENTRY;
static inline sh_page_VIRTUAL_ADDRESS sh_pez_internal_pack_boundary(sh_uint32 region,sh_uint32 prev) {
  return (sh_page_VIRTUAL_ADDRESS)(((sh_uint64)prev<<32) | region);
}
static inline sh_pez_internal_BOUNDARY_ENTRY sh_pez_internal_unpack_boundary(sh_page_VIRTUAL_ADDRESS value) {
  sh_pez_internal_BOUNDARY_ENTRY entry;
  entry.region_index=(sh_uint32)(value & 0xFFFFFFFF);
  entry.previous_index=(sh_uint32)(value>>32);
  return entry;
}
static SH_STATUS sh_pez_internal_scan_physical_bitmap(sh_pez_PHYSICAL_PLANE *phys_plane) {
  if (phys_plane==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER;
  sh_log_fdebug(SH_LOG_SOURCE_PEZ,"Scanning physical bitmap:\n");
  sh_uint64 *bitmap64=(sh_uint64*)phys_plane->physical_bitmap;
  sh_uint32 current_page=0;
  sh_uint64 free_region_count=0;
  sh_pez_debug_BITMAP bitmap_struct;
  while (current_page<phys_plane->physical_page_count) {
    if ((current_page%64)==0) {
      while (current_page+64<=phys_plane->physical_page_count && bitmap64[current_page/64]==0xFFFFFFFFFFFFFFFF) {
        current_page+=64;
      }
    }
    if (current_page<phys_plane->physical_page_count && (sh_page_is_allocated(phys_plane->physical_bitmap,current_page)==0)) {
      sh_uint32 start=current_page;
      while (current_page<phys_plane->physical_page_count && sh_page_is_allocated(phys_plane->physical_bitmap,current_page)==0) {
        current_page++;
      }
      sh_uint32 size=current_page-start;
      bitmap_struct.regions[free_region_count].start=start;
      bitmap_struct.regions[free_region_count].size=size;
      free_region_count++;
      if (free_region_count==512) {
        return SH_STATUS_TOO_MUCH_DATA;
      }
    } else if (current_page<phys_plane->physical_page_count) {
      current_page++;
    }
  }
  bitmap_struct.count=(sh_uint32)free_region_count;
  sh_pez_debug_send_bitmap(&bitmap_struct);
  return SH_STATUS_SUCCESS;
}
static SH_STATUS sh_pez_internal_scan_boundary_radix_physical(sh_pez_PHYSICAL_PLANE *phys_plane) {
  if (phys_plane==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER;
  sh_log_fdebug(SH_LOG_SOURCE_PEZ,"Scanning boundary radix:\n");
  sh_uint64 free_region_count=0;
  sh_uint64 stop=sh_page_get_physical_memory_amount_pages();
  sh_pez_debug_BOUNDARY_RADIX boundary_radix;
  sh_bool is_in_region=SH_FALSE;
  for (sh_iter64 i=0;i<stop;i++) {
    sh_page_VIRTUAL_ADDRESS boundary;
    SH_STATUS status=sh_radix_tree_get_value(&phys_plane->boundary_radix_tree,i,&boundary);
    if (sh_status_error(status) && status!=SH_STATUS_NOT_FOUND) return status;
    if (status==SH_STATUS_NOT_FOUND) continue;
    if (is_in_region==SH_FALSE) {
      boundary_radix.regions[free_region_count].pos_start=(sh_uint32)i;
      sh_pez_internal_BOUNDARY_ENTRY boundary_unpack=sh_pez_internal_unpack_boundary(boundary);
      boundary_radix.regions[free_region_count].idx_start=boundary_unpack.region_index;
      boundary_radix.regions[free_region_count].prev_start=boundary_unpack.previous_index;
      is_in_region=SH_TRUE;
      continue;
    } else {
      sh_pez_internal_BOUNDARY_ENTRY boundary_unpack=sh_pez_internal_unpack_boundary(boundary);
      // check to see if region is one page wide or more
      if (boundary_unpack.region_index==boundary_radix.regions[free_region_count].idx_start) {
        boundary_radix.regions[free_region_count].pos_end=(sh_uint32)i;
        boundary_radix.regions[free_region_count].idx_end=boundary_unpack.region_index;
        boundary_radix.regions[free_region_count].prev_end=boundary_unpack.previous_index;
        is_in_region=SH_FALSE;
        free_region_count++;
        if (free_region_count==512) {
          return SH_STATUS_TOO_MUCH_DATA;
        }
        continue;
      } else {
        boundary_radix.regions[free_region_count].pos_end=boundary_radix.regions[free_region_count].pos_start;
        boundary_radix.regions[free_region_count].idx_end=boundary_radix.regions[free_region_count].idx_start;
        boundary_radix.regions[free_region_count].prev_end=boundary_radix.regions[free_region_count].prev_start;
        is_in_region=SH_FALSE;
        free_region_count++;
        if (free_region_count==512) {
          return SH_STATUS_TOO_MUCH_DATA;
        }
        continue;
      }
    }
  }
  boundary_radix.count=(sh_uint32)free_region_count;
  sh_pez_debug_send_boundary_radix(&boundary_radix);
  return SH_STATUS_SUCCESS;
}
static SH_STATUS sh_pez_internal_check_physical_radix(sh_pez_PHYSICAL_PLANE *phys_plane) {
  if (phys_plane==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER;
  sh_page_MEM_STATS mem_stats;
  SH_STATUS status=sh_page_get_memory_stats(&mem_stats);
  for (sh_iter64 i=1;i<mem_stats.largest_free_block+1;i++) {
    sh_uint64 pos=0;
    sh_pez_REGION_PHYSICAL_OBJECT *reg;
    sh_page_VIRTUAL_ADDRESS index;
    status=sh_radix_tree_get_value(&phys_plane->region_radix_tree,i,&index);
    if (sh_status_error(status) && status!=SH_STATUS_NOT_FOUND) return status;
    sh_slab_reg_phys_OBJECT_INDEX idx=(sh_uint32)index;
    if (status==SH_STATUS_NOT_FOUND) continue;
    while (SH_TRUE) {
      reg=sh_slab_reg_phys_ref_to_ptr(phys_plane->slab_reg_phys,idx);
      if (reg==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED;
      if (reg->region_size_pages==0 || reg->start_page_index==0) {
        sh_log_fcritical(SH_LOG_SOURCE_PEZ,"Found free region that have either his address or his size starting at 0 (#%8u in size list) : [0x%x - 0x%x[; Size in object: %8u pages; Size in list: %8u pages\n",pos,((sh_uint64)reg->start_page_index)*SH_PAGE_SIZE,(((sh_uint64)reg->start_page_index)+reg->region_size_pages)*SH_PAGE_SIZE,reg->region_size_pages,i);
        return SH_STATUS_PEZ_CORRUPTED;
      }
      if (reg->region_size_pages!=i) {
        sh_log_fcritical(SH_LOG_SOURCE_PEZ,"Found free region that doesn't have his placement (#%8u in size list) and size matching : [0x%x - 0x%x[; Size in object: %8u pages; Size in list: %8u pages\n",pos,((sh_uint64)reg->start_page_index)*SH_PAGE_SIZE,(((sh_uint64)reg->start_page_index)+reg->region_size_pages)*SH_PAGE_SIZE,reg->region_size_pages,i);
        return SH_STATUS_PEZ_CORRUPTED;
      }
      for (sh_uint64 y=reg->start_page_index;y<(sh_uint64)(reg->start_page_index+reg->region_size_pages);y++) {
        if (sh_page_is_allocated(phys_plane->physical_bitmap,y)) {
          sh_log_fcritical(SH_LOG_SOURCE_PEZ,"Found free region that have a page allocated (#%8u in size list) : [0x%x - 0x%x[; Size in object: %8u pages; Size in list: %8u pages; Concerned page: 0x%x\n",pos,((sh_uint64)reg->start_page_index)*SH_PAGE_SIZE,(((sh_uint64)reg->start_page_index)+reg->region_size_pages)*SH_PAGE_SIZE,reg->region_size_pages,i,((sh_uint64)y)*SH_PAGE_SIZE);
          return SH_STATUS_PEZ_CORRUPTED;
        }
      }
      if (!sh_page_is_allocated(phys_plane->physical_bitmap,reg->start_page_index-1)) {
        sh_log_fcritical(SH_LOG_SOURCE_PEZ,"Found free region that doesn't fully extend at left, found a page free at left (#%8u in size list) : [0x%x - 0x%x[; Size in object: %8u pages; Size in list: %8u pages; Concerned page: 0x%x\n",pos,((sh_uint64)reg->start_page_index)*SH_PAGE_SIZE,(((sh_uint64)reg->start_page_index)+reg->region_size_pages)*SH_PAGE_SIZE,reg->region_size_pages,i,((sh_uint64)reg->start_page_index-1)*SH_PAGE_SIZE);
        return SH_STATUS_PEZ_CORRUPTED;
      }
      if (!sh_page_is_allocated(phys_plane->physical_bitmap,reg->start_page_index+reg->region_size_pages) && reg->start_page_index+reg->region_size_pages!=sh_page_get_physical_memory_amount_pages()) {
        sh_log_fcritical(SH_LOG_SOURCE_PEZ,"Found free region that doesn't fully extend at right, found a page free at right (#%8u in size list) : [0x%x - 0x%x[; Size in object: %8u pages; Size in list: %8u pages; Concerned page: 0x%x\n",pos,((sh_uint64)reg->start_page_index)*SH_PAGE_SIZE,(((sh_uint64)reg->start_page_index)+reg->region_size_pages)*SH_PAGE_SIZE,reg->region_size_pages,i,((sh_uint64)(reg->start_page_index+reg->region_size_pages))*SH_PAGE_SIZE);
        return SH_STATUS_PEZ_CORRUPTED;
      }
      if (sh_pez_internal_unpack_index(reg->next_region_index)==0) {
        break;
      } else {
        idx=sh_pez_internal_unpack_index(reg->next_region_index);
        pos++;
      }
    }
  }
  return SH_STATUS_SUCCESS;
}
static SH_STATUS sh_pez_internal_scan_size_list_physical(sh_pez_PHYSICAL_PLANE *phys_plane) {
  if (phys_plane==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER;
  sh_page_MEM_STATS mem_stats;
  SH_STATUS status=sh_page_get_memory_stats(&mem_stats);
  sh_log_fdebug(SH_LOG_SOURCE_PEZ,"Scanning size list radix:\n");
  __attribute__((__unused__)) sh_uint64 free_region_count=0;
  sh_log_byte(SH_PEZ_DEBUG_SIZE_LIST_BLOCK_HEADER);
  sh_log_byte('\n');
  for (sh_iter64 i=1;i<mem_stats.largest_free_block+1;i++) {
    sh_pez_REGION_PHYSICAL_OBJECT *reg;
    sh_page_VIRTUAL_ADDRESS index;
    status=sh_radix_tree_get_value(&phys_plane->region_radix_tree,i,&index);
    if (sh_status_error(status) && status!=SH_STATUS_NOT_FOUND) return status;
    sh_slab_reg_phys_OBJECT_INDEX idx=(sh_uint32)index;
    if (status==SH_STATUS_NOT_FOUND) continue;
    sh_uint64 size_list_region_count=0;
    sh_pez_debug_SIZE_LIST size_list;
    size_list.size=(sh_uint32)i;
    while (SH_TRUE) {
      reg=sh_slab_reg_phys_ref_to_ptr(phys_plane->slab_reg_phys,idx);
      if (reg==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED;
      size_list.regions[size_list_region_count].start=reg->start_page_index;
      size_list.regions[size_list_region_count].size=reg->region_size_pages;
      size_list.regions[size_list_region_count].idx=idx;
      size_list.regions[size_list_region_count].next_idx=sh_pez_internal_unpack_index(reg->next_region_index);
      free_region_count++;
      size_list_region_count++;
      if (size_list_region_count==512) {
        return SH_STATUS_TOO_MUCH_DATA;
      }
      if (sh_pez_internal_unpack_index(reg->next_region_index)==0) {
        break;
      } else {
        idx=sh_pez_internal_unpack_index(reg->next_region_index);
      }
    }
    size_list.count=(sh_uint32)size_list_region_count;
    sh_pez_debug_send_size_list(&size_list);
  }
  return SH_STATUS_SUCCESS;
}
static SH_STATUS sh_pez_internal_create_free_region_physical(sh_pez_PHYSICAL_PLANE *phys_plane,sh_uint32 region_start,sh_uint32 region_size,sh_pez_REGION_PHYSICAL_OBJECT **region) {
  if (phys_plane==SH_NULLPTR || region_size==0 || region==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER;
  sh_slab_reg_phys_OBJECT_INDEX reg_idx;
  SH_STATUS status=sh_slab_reg_phys_alloc(phys_plane->slab_reg_phys,phys_plane->kernel_ptp,&reg_idx);
  if (sh_status_error(status)) return status;
  sh_pez_REGION_PHYSICAL_OBJECT *reg=sh_slab_reg_phys_ref_to_ptr(phys_plane->slab_reg_phys,reg_idx);
  if (reg==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED;
  reg->start_page_index=region_start;
  reg->region_size_pages=region_size;
  sh_page_VIRTUAL_ADDRESS head_index=0;
  status=sh_radix_tree_get_value(&phys_plane->region_radix_tree,(sh_uint64)region_size,&head_index);
  if (sh_status_error(status) && status!=SH_STATUS_NOT_FOUND) return status;
  sh_page_VIRTUAL_ADDRESS new_boundary_val=sh_pez_internal_pack_boundary(reg_idx,0);
  status=sh_radix_tree_insert_value(phys_plane->slab_radix_node,phys_plane->kernel_ptp,&phys_plane->boundary_radix_tree,(sh_uint64)region_start,new_boundary_val);
  if (sh_status_error(status)) return status;
  status=sh_radix_tree_insert_value(phys_plane->slab_radix_node,phys_plane->kernel_ptp,&phys_plane->boundary_radix_tree,(sh_uint64)region_start+region_size-1,new_boundary_val);
  if (sh_status_error(status)) return status;
  if (head_index!=0) {
    sh_pez_REGION_PHYSICAL_OBJECT *old_head_ptr=sh_slab_reg_phys_ref_to_ptr(phys_plane->slab_reg_phys,(sh_uint32)head_index);
    if (old_head_ptr==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED;
    sh_page_VIRTUAL_ADDRESS updated_old_head_boundary=sh_pez_internal_pack_boundary((sh_uint32)head_index,reg_idx);
    status=sh_radix_tree_insert_value(phys_plane->slab_radix_node,phys_plane->kernel_ptp,&phys_plane->boundary_radix_tree,(sh_uint64)old_head_ptr->start_page_index,updated_old_head_boundary);
    if (sh_status_error(status)) return status;
    sh_radix_tree_insert_value(phys_plane->slab_radix_node,phys_plane->kernel_ptp,&phys_plane->boundary_radix_tree,(sh_uint64)old_head_ptr->start_page_index+old_head_ptr->region_size_pages-1,updated_old_head_boundary);
    if (sh_status_error(status)) return status;
    sh_pez_internal_pack_index(reg->next_region_index,(sh_uint32)head_index);
  } else {
    sh_pez_internal_pack_index(reg->next_region_index,0);
  }
  status=sh_radix_tree_insert_value(phys_plane->slab_radix_node,phys_plane->kernel_ptp,&phys_plane->region_radix_tree,(sh_uint64)region_size,(sh_page_VIRTUAL_ADDRESS)reg_idx);
  if (sh_status_error(status)) return status;
  *region=reg;
  return SH_STATUS_SUCCESS;
}
static SH_STATUS sh_pez_internal_check_region_integrity_physical(sh_pez_PHYSICAL_PLANE *phys_plane,sh_pez_REGION_PHYSICAL_OBJECT *reg,sh_bool *optional_is_first,sh_slab_reg_phys_OBJECT_INDEX *optional_reg_idx,sh_slab_reg_phys_OBJECT_INDEX *optional_prev_idx) {
  if (phys_plane==SH_NULLPTR || reg==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER;
  sh_page_VIRTUAL_ADDRESS boundary_left;
  sh_page_VIRTUAL_ADDRESS boundary_right;
  SH_STATUS status=sh_radix_tree_get_value(&phys_plane->boundary_radix_tree,reg->start_page_index,&boundary_left);
  if (sh_status_error(status)) return status;
  status=sh_radix_tree_get_value(&phys_plane->boundary_radix_tree,reg->start_page_index+reg->region_size_pages-1,&boundary_right);
  if (sh_status_error(status)) return status;
  if (boundary_left!=boundary_right) return SH_STATUS_PEZ_CORRUPTED;
  sh_pez_internal_BOUNDARY_ENTRY boundary=sh_pez_internal_unpack_boundary(boundary_left);
  sh_bool should_be_first;
  if (boundary.previous_index==0) {
    should_be_first=SH_TRUE;
  } else {
    should_be_first=SH_FALSE;
  }
  sh_page_VIRTUAL_ADDRESS reg_idx;
  status=sh_radix_tree_get_value(&phys_plane->region_radix_tree,reg->region_size_pages,&reg_idx);
  if (sh_status_error(status)) return status;
  sh_pez_REGION_PHYSICAL_OBJECT *region=sh_slab_reg_phys_ref_to_ptr(phys_plane->slab_reg_phys,(sh_uint32)reg_idx);
  if (region==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED;
  if ((!should_be_first && region==reg) || (should_be_first && region!=reg)) {
    return SH_STATUS_PEZ_CORRUPTED;
  }
  if (optional_is_first!=SH_NULLPTR) {
    *optional_is_first=should_be_first;
  }
  if (optional_reg_idx!=SH_NULLPTR) {
    *optional_reg_idx=boundary.region_index;
  }
  if (optional_prev_idx!=SH_NULLPTR) {
    *optional_prev_idx=boundary.previous_index;
  }
  return SH_STATUS_SUCCESS;
}
static SH_STATUS sh_pez_internal_delete_free_region_physical(sh_pez_PHYSICAL_PLANE *phys_plane,sh_pez_REGION_PHYSICAL_OBJECT *reg) {
  if (phys_plane==SH_NULLPTR || reg==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER;
  sh_bool is_first;
  sh_slab_reg_phys_OBJECT_INDEX reg_idx;
  sh_slab_reg_phys_OBJECT_INDEX prev_idx;
  SH_STATUS status=sh_pez_internal_check_region_integrity_physical(phys_plane,reg,&is_first,&reg_idx,&prev_idx);
  if (sh_status_error(status)) return status;
  if (is_first) {
    // if region is the first of the list
    if (sh_pez_internal_unpack_index(reg->next_region_index)==0) {
      // if region was alone in his list
      // delete radix entry
      status=sh_radix_tree_delete_value(phys_plane->slab_radix_node,&phys_plane->region_radix_tree,reg->region_size_pages);
      if (sh_status_error(status)) return status;
      // delete left boundary
      status=sh_radix_tree_delete_value(phys_plane->slab_radix_node,&phys_plane->boundary_radix_tree,reg->start_page_index);
      if (sh_status_error(status)) return status;
      // delete right boundary
      if (reg->region_size_pages!=1) {
        status=sh_radix_tree_delete_value(phys_plane->slab_radix_node,&phys_plane->boundary_radix_tree,reg->start_page_index+reg->region_size_pages-1);
        if (sh_status_error(status)) return status;
      }
      // delete region object
      status=sh_slab_reg_phys_dealloc(phys_plane->slab_reg_phys,reg_idx);
      if (sh_status_error(status)) return status;
      return SH_STATUS_SUCCESS;
    } else {
      // if region isn't alone in his list
      // obtain next region informations
      sh_slab_reg_phys_OBJECT_INDEX next_reg_idx=sh_pez_internal_unpack_index(reg->next_region_index);
      sh_pez_REGION_PHYSICAL_OBJECT *next_region=sh_slab_reg_phys_ref_to_ptr(phys_plane->slab_reg_phys,next_reg_idx);
      if (next_region==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED;
      // insert next region as first inside size list
      status=sh_radix_tree_insert_value(phys_plane->slab_radix_node,phys_plane->kernel_ptp,&phys_plane->region_radix_tree,reg->region_size_pages,next_reg_idx);
      if (sh_status_error(status)) return status;
      // update left boundary of next region
      sh_page_VIRTUAL_ADDRESS new_boundary_for_next_region=sh_pez_internal_pack_boundary(next_reg_idx,0);
      status=sh_radix_tree_insert_value(phys_plane->slab_radix_node,phys_plane->kernel_ptp,&phys_plane->boundary_radix_tree,next_region->start_page_index,new_boundary_for_next_region);
      if (sh_status_error(status)) return status;
      // update right boundary of next region
      status=sh_radix_tree_insert_value(phys_plane->slab_radix_node,phys_plane->kernel_ptp,&phys_plane->boundary_radix_tree,next_region->start_page_index+next_region->region_size_pages-1,new_boundary_for_next_region);
      if (sh_status_error(status)) return status;
      // delete left boundary
      status=sh_radix_tree_delete_value(phys_plane->slab_radix_node,&phys_plane->boundary_radix_tree,reg->start_page_index);
      if (sh_status_error(status)) return status;
      // delete right boundary
      if (reg->region_size_pages!=1) {
        status=sh_radix_tree_delete_value(phys_plane->slab_radix_node,&phys_plane->boundary_radix_tree,reg->start_page_index+reg->region_size_pages-1);
        if (sh_status_error(status)) return status;
      }
      // delete region object
      status=sh_slab_reg_phys_dealloc(phys_plane->slab_reg_phys,reg_idx);
      if (sh_status_error(status)) return status;
      return SH_STATUS_SUCCESS;
    }
  } else {
    // if region isn't the first of his list
    // obtain information about prev region
    sh_pez_REGION_PHYSICAL_OBJECT *prev_region=sh_slab_reg_phys_ref_to_ptr(phys_plane->slab_reg_phys,prev_idx);
    if (prev_region==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED;
    if (sh_pez_internal_unpack_index(reg->next_region_index)==0) {
      // if region is at the end of the size list
      // put next_idx of prev region to 0
      sh_pez_internal_pack_index(prev_region->next_region_index,0);
      // delete left boundary
      status=sh_radix_tree_delete_value(phys_plane->slab_radix_node,&phys_plane->boundary_radix_tree,reg->start_page_index);
      if (sh_status_error(status)) return status;
      // delete right boundary
      if (reg->region_size_pages!=1) {
        status=sh_radix_tree_delete_value(phys_plane->slab_radix_node,&phys_plane->boundary_radix_tree,reg->start_page_index+reg->region_size_pages-1);
        if (sh_status_error(status)) return status;
      }
      // delete region object
      status=sh_slab_reg_phys_dealloc(phys_plane->slab_reg_phys,reg_idx);
      if (sh_status_error(status)) return status;
      return SH_STATUS_SUCCESS;
    } else {
      // if region is in the middle of the size list
      // obtain information about next region
      sh_slab_reg_phys_OBJECT_INDEX next_reg_idx=sh_pez_internal_unpack_index(reg->next_region_index);
      sh_pez_REGION_PHYSICAL_OBJECT *next_region=sh_slab_reg_phys_ref_to_ptr(phys_plane->slab_reg_phys,next_reg_idx);
      if (next_region==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED;
      // put next_idx of prev region to next_region_idx
      sh_pez_internal_pack_index(prev_region->next_region_index,next_reg_idx);
      // update left boundary of next region
      sh_page_VIRTUAL_ADDRESS new_boundary_for_next_region=sh_pez_internal_pack_boundary(next_reg_idx,prev_idx);
      status=sh_radix_tree_insert_value(phys_plane->slab_radix_node,phys_plane->kernel_ptp,&phys_plane->boundary_radix_tree,next_region->start_page_index,new_boundary_for_next_region);
      if (sh_status_error(status)) return status;
      // update right boundary of next region
      status=sh_radix_tree_insert_value(phys_plane->slab_radix_node,phys_plane->kernel_ptp,&phys_plane->boundary_radix_tree,next_region->start_page_index+next_region->region_size_pages-1,new_boundary_for_next_region);
      if (sh_status_error(status)) return status;
      // delete left boundary
      status=sh_radix_tree_delete_value(phys_plane->slab_radix_node,&phys_plane->boundary_radix_tree,reg->start_page_index);
      if (sh_status_error(status)) return status;
      // delete right boundary
      if (reg->region_size_pages!=1) {
        status=sh_radix_tree_delete_value(phys_plane->slab_radix_node,&phys_plane->boundary_radix_tree,reg->start_page_index+reg->region_size_pages-1);
        if (sh_status_error(status)) return status;
      }
      // delete region object
      status=sh_slab_reg_phys_dealloc(phys_plane->slab_reg_phys,reg_idx);
      if (sh_status_error(status)) return status;
      return SH_STATUS_SUCCESS;
    }
  }
}
SH_STATUS sh_pez_init_physical_plane(sh_uint8 *physical_bitmap,sh_uint64 physical_page_count,sh_slab_reg_phys_SLAB_ALLOCATOR *slab_reg_phys,struct sh_slab_radix_node_SLAB_ALLOCATOR *slab_radix_node,sh_page_PAGE_TABLE_POOL *kernel_ptp,sh_pez_PHYSICAL_PLANE *phys_plane) {
  if (physical_bitmap==SH_NULLPTR || physical_page_count==0 || slab_reg_phys==SH_NULLPTR || slab_radix_node==SH_NULLPTR || kernel_ptp==SH_NULLPTR || phys_plane==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER;
  sh_radix_tree_init(slab_radix_node,kernel_ptp,&phys_plane->region_radix_tree,8);
  sh_radix_tree_init(slab_radix_node,kernel_ptp,&phys_plane->boundary_radix_tree,8);
  phys_plane->free_pages=0;
  phys_plane->used_pages=0;
  phys_plane->physical_bitmap=physical_bitmap;
  phys_plane->physical_page_count=physical_page_count;
  phys_plane->slab_reg_phys=slab_reg_phys;
  phys_plane->slab_radix_node=slab_radix_node;
  phys_plane->kernel_ptp=kernel_ptp;
  sh_uint64 *bitmap64=(sh_uint64*)physical_bitmap;
  sh_uint32 current_page=0;
  while (current_page<physical_page_count) {
    if ((current_page%64)==0) {
      while (current_page+64<=physical_page_count && bitmap64[current_page/64]==0xFFFFFFFFFFFFFFFF) {
        current_page+=64;
      }
    }
    if (current_page<physical_page_count && (sh_page_is_allocated(physical_bitmap,current_page)==0)) {
      sh_uint32 start=current_page;
      while (current_page<physical_page_count && sh_page_is_allocated(physical_bitmap,current_page)==0) {
        current_page++;
      }
      sh_uint32 size=current_page-start;
      sh_pez_REGION_PHYSICAL_OBJECT *reg=SH_NULLPTR;
      SH_STATUS status=sh_pez_internal_create_free_region_physical(phys_plane,start,size,&reg);
      if (sh_status_error(status)) return status;
    } else if (current_page<physical_page_count) {
      current_page++;
    }
  }
  sh_page_MEM_STATS mem_stats;
  SH_STATUS status=sh_page_get_memory_stats(&mem_stats);
  if (sh_status_error(status)) return status;
  phys_plane->free_pages=(sh_uint32)mem_stats.free_pages;
  phys_plane->used_pages=(sh_uint32)mem_stats.used_pages;
  sh_pez_set_reference_phys_plane(phys_plane);
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_pez_alloc_physical_pages(sh_pez_PHYSICAL_PLANE *phys_plane,sh_uint32 pages_count,sh_page_PHYSICAL_ADDRESS *address) {
  if (phys_plane==SH_NULLPTR || pages_count==0 || address==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER;
  sh_page_VIRTUAL_ADDRESS reg_idx;
  SH_STATUS status=sh_radix_tree_search_smallest_min_bound(phys_plane->slab_radix_node,&phys_plane->region_radix_tree,pages_count,&reg_idx);
  if (sh_status_error(status) && status!=SH_STATUS_NOT_FOUND) return status;
  if (status==SH_STATUS_NOT_FOUND) return SH_STATUS_OUT_OF_MEMORY;
  sh_pez_REGION_PHYSICAL_OBJECT *reg=sh_slab_reg_phys_ref_to_ptr(phys_plane->slab_reg_phys,(sh_uint32)reg_idx);
  if (reg==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED;
  if (reg->region_size_pages<pages_count) {
    // something has gone bad with successor function for finding a big enougth region
    return SH_STATUS_PEZ_CORRUPTED;
  } else if (reg->region_size_pages==pages_count) {
    // exact fit case
    // save metadata for later modifications
    sh_uint32 start=reg->start_page_index;
    // delete free region
    status=sh_pez_internal_delete_free_region_physical(phys_plane,reg);
    if (sh_status_error(status)) return status;
    // mark pages as occupied inside physical bitmap
    status=sh_page_set_pages_range_bitmap(phys_plane->physical_bitmap,phys_plane->physical_page_count,start,pages_count,SH_TRUE);
    if (sh_status_error(status)) return status;
    // give the address
    *address=((sh_page_PHYSICAL_ADDRESS)start)*SH_PAGE_SIZE;
  } else if (reg->region_size_pages>pages_count) {
    // best fit case
    // save metadata for later modifications
    sh_uint32 start=reg->start_page_index;
    // compute new size and index
    sh_uint32 new_start=reg->start_page_index+pages_count;
    if (new_start==0) return SH_STATUS_PEZ_CORRUPTED;
    sh_uint32 new_size=reg->region_size_pages-pages_count;
    if (new_size==0) return SH_STATUS_PEZ_CORRUPTED;
    // delete free region
    status=sh_pez_internal_delete_free_region_physical(phys_plane,reg);
    if (sh_status_error(status)) return status;
    // mark pages as occupied inside physical bitmap
    status=sh_page_set_pages_range_bitmap(phys_plane->physical_bitmap,phys_plane->physical_page_count,start,pages_count,SH_TRUE);
    if (sh_status_error(status)) return status;
    // create new free region with splitted part
    sh_pez_REGION_PHYSICAL_OBJECT *new_reg;
    status=sh_pez_internal_create_free_region_physical(phys_plane,new_start,new_size,&new_reg);
    if (sh_status_error(status)) return status;
    // give the address
    *address=((sh_page_PHYSICAL_ADDRESS)start)*SH_PAGE_SIZE;
  }
  phys_plane->free_pages=phys_plane->free_pages-pages_count;
  phys_plane->used_pages=phys_plane->used_pages+pages_count;
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_pez_free_physical_pages(sh_pez_PHYSICAL_PLANE *phys_plane,sh_page_PHYSICAL_ADDRESS *address,sh_uint32 pages_count) {
  if (phys_plane==SH_NULLPTR || address==SH_NULLPTR || pages_count==0) return SH_STATUS_INVALID_PARAMETER;
  // save freeed region data
  sh_uint32 start_reg=(sh_uint32)((*address)/SH_PAGE_SIZE);
  if (start_reg==0) return SH_STATUS_INVALID_PARAMETER;
  sh_uint32 size_reg=pages_count;
  // checking if region is entirely used
  for (sh_iter64 i=0;i<pages_count;i++) {
    if (!sh_page_is_allocated(phys_plane->physical_bitmap,start_reg+i)) {
      return SH_STATUS_PAGE_ALREADY_FREE;
    }
  }
  // marking pages as free
  SH_STATUS status=sh_page_set_pages_range_bitmap(phys_plane->physical_bitmap,phys_plane->physical_page_count,start_reg,size_reg,SH_FALSE);
  if (sh_status_error(status)) return status;
  // identifying neighbours
  // left
  sh_page_VIRTUAL_ADDRESS left_neighbour;
  status=sh_radix_tree_get_value(&phys_plane->boundary_radix_tree,start_reg-1,&left_neighbour);
  if (sh_status_error(status) && status!=SH_STATUS_NOT_FOUND) return status;
  sh_bool left_fusion;
  if (status==SH_STATUS_NOT_FOUND) {
    left_fusion=SH_FALSE;
  } else {
    left_fusion=SH_TRUE;
  }
  // right
  sh_page_VIRTUAL_ADDRESS right_neighbour;
  status=sh_radix_tree_get_value(&phys_plane->boundary_radix_tree,start_reg+size_reg,&right_neighbour);
  if (sh_status_error(status) && status!=SH_STATUS_NOT_FOUND) return status;
  sh_bool right_fusion;
  if (status==SH_STATUS_NOT_FOUND) {
    right_fusion=SH_FALSE;
  } else {
    right_fusion=SH_TRUE;
  }
  // preparing fusion
  sh_uint32 new_start=start_reg;
  sh_uint32 new_size=size_reg;
  // fusionning left region
  if (left_fusion) {
    // obtaining information about left region
    sh_pez_internal_BOUNDARY_ENTRY left_boundary=sh_pez_internal_unpack_boundary(left_neighbour);
    sh_slab_reg_phys_OBJECT_INDEX left_reg_idx=left_boundary.region_index;
    sh_pez_REGION_PHYSICAL_OBJECT *left_reg=sh_slab_reg_phys_ref_to_ptr(phys_plane->slab_reg_phys,left_reg_idx);
    if (left_reg==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED;
    // updating new region informations
    new_start=left_reg->start_page_index;
    if (new_start==0 || new_start>sh_page_get_physical_memory_amount_pages()) return SH_STATUS_PEZ_CORRUPTED;
    new_size=new_size+left_reg->region_size_pages;
    if (new_size==0 || new_size>sh_page_get_physical_memory_amount_pages()) return SH_STATUS_PEZ_CORRUPTED;
    // deleting left region
    status=sh_pez_internal_delete_free_region_physical(phys_plane,left_reg);
    if (sh_status_error(status)) return status;
  }
  // fusionning right region
  if (right_fusion) {
    // obtaining information about right region
    sh_pez_internal_BOUNDARY_ENTRY right_boundary=sh_pez_internal_unpack_boundary(right_neighbour);
    sh_slab_reg_phys_OBJECT_INDEX right_reg_idx=right_boundary.region_index;
    sh_pez_REGION_PHYSICAL_OBJECT *right_reg=sh_slab_reg_phys_ref_to_ptr(phys_plane->slab_reg_phys,right_reg_idx);
    if (right_reg==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED;
    // updating new region informations
    new_size=new_size+right_reg->region_size_pages;
    if (new_size==0 || new_size>sh_page_get_physical_memory_amount_pages()) return SH_STATUS_PEZ_CORRUPTED;
    // deleting right region
    status=sh_pez_internal_delete_free_region_physical(phys_plane,right_reg);
    if (sh_status_error(status)) return status;
  }
  // inserting new region
  sh_pez_REGION_PHYSICAL_OBJECT *new_reg;
  status=sh_pez_internal_create_free_region_physical(phys_plane,new_start,new_size,&new_reg);
  if (sh_status_error(status)) return status;
  phys_plane->free_pages=phys_plane->free_pages+pages_count;
  phys_plane->used_pages=phys_plane->used_pages-pages_count;
  *address=0;
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_pez_debug_physical(sh_pez_PHYSICAL_PLANE *phys_plane) {
  sh_pez_internal_check_physical_radix(phys_plane);
  sh_pez_internal_scan_physical_bitmap(phys_plane);
  sh_pez_internal_scan_size_list_physical(phys_plane);
  sh_pez_internal_scan_boundary_radix_physical(phys_plane);
  return SH_STATUS_SUCCESS;
}
static SH_STATUS sh_pez_internal_create_free_region_virtual(sh_pez_VIRTUAL_PLANE *virt_plane,sh_uint32 region_start,sh_uint32 region_size,sh_pez_REGION_VIRTUAL_OBJECT **region) {
  if (virt_plane==SH_NULLPTR || region_size==0 || region==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER;
  sh_slab_reg_virt_OBJECT_INDEX reg_idx;
  SH_STATUS status=sh_slab_reg_virt_alloc(virt_plane->slab_reg_virt,virt_plane->kernel_ptp,&reg_idx);
  if (sh_status_error(status)) return status;
  sh_pez_REGION_VIRTUAL_OBJECT *reg=sh_slab_reg_virt_ref_to_ptr(virt_plane->slab_reg_virt,reg_idx);
  if (reg==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED;
  reg->start_page_index=region_start;
  reg->region_size_pages=region_size;
  sh_page_VIRTUAL_ADDRESS head_index=0;
  status=sh_radix_tree_get_value(&virt_plane->region_radix_tree,(sh_uint64)region_size,&head_index);
  if (sh_status_error(status) && status!=SH_STATUS_NOT_FOUND) return status;
  sh_page_VIRTUAL_ADDRESS new_boundary_val=sh_pez_internal_pack_boundary(reg_idx,0);
  status=sh_radix_tree_insert_value(virt_plane->slab_radix_node,virt_plane->kernel_ptp,&virt_plane->boundary_radix_tree,(sh_uint64)region_start,new_boundary_val);
  if (sh_status_error(status)) return status;
  status=sh_radix_tree_insert_value(virt_plane->slab_radix_node,virt_plane->kernel_ptp,&virt_plane->boundary_radix_tree,(sh_uint64)region_start+region_size-1,new_boundary_val);
  if (sh_status_error(status)) return status;
  if (head_index!=0) {
    sh_pez_REGION_PHYSICAL_OBJECT *old_head_ptr=sh_slab_reg_virt_ref_to_ptr(virt_plane->slab_reg_virt,(sh_uint32)head_index);
    if (old_head_ptr==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED;
    sh_page_VIRTUAL_ADDRESS updated_old_head_boundary=sh_pez_internal_pack_boundary((sh_uint32)head_index,reg_idx);
    status=sh_radix_tree_insert_value(virt_plane->slab_radix_node,virt_plane->kernel_ptp,&virt_plane->boundary_radix_tree,(sh_uint64)old_head_ptr->start_page_index,updated_old_head_boundary);
    if (sh_status_error(status)) return status;
    sh_radix_tree_insert_value(virt_plane->slab_radix_node,virt_plane->kernel_ptp,&virt_plane->boundary_radix_tree,(sh_uint64)old_head_ptr->start_page_index+old_head_ptr->region_size_pages-1,updated_old_head_boundary);
    if (sh_status_error(status)) return status;
    reg->next_region_index=(sh_uint32)head_index;
  } else {
    reg->next_region_index=0;
  }
  status=sh_radix_tree_insert_value(virt_plane->slab_radix_node,virt_plane->kernel_ptp,&virt_plane->region_radix_tree,(sh_uint64)region_size,(sh_page_VIRTUAL_ADDRESS)reg_idx);
  if (sh_status_error(status)) return status;
  *region=reg;
  return SH_STATUS_SUCCESS;
}
static SH_STATUS sh_pez_internal_check_region_integrity_virtual(sh_pez_VIRTUAL_PLANE *virt_plane,sh_pez_REGION_VIRTUAL_OBJECT *reg,sh_bool *optional_is_first,sh_slab_reg_virt_OBJECT_INDEX *optional_reg_idx,sh_slab_reg_virt_OBJECT_INDEX *optional_prev_idx) {
  if (virt_plane==SH_NULLPTR || reg==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER;
  sh_page_VIRTUAL_ADDRESS boundary_left;
  sh_page_VIRTUAL_ADDRESS boundary_right;
  SH_STATUS status=sh_radix_tree_get_value(&virt_plane->boundary_radix_tree,reg->start_page_index,&boundary_left);
  if (sh_status_error(status)) return status;
  status=sh_radix_tree_get_value(&virt_plane->boundary_radix_tree,reg->start_page_index+reg->region_size_pages-1,&boundary_right);
  if (sh_status_error(status)) return status;
  if (boundary_left!=boundary_right) return SH_STATUS_PEZ_CORRUPTED;
  sh_pez_internal_BOUNDARY_ENTRY boundary=sh_pez_internal_unpack_boundary(boundary_left);
  sh_bool should_be_first;
  if (boundary.previous_index==0) {
    should_be_first=SH_TRUE;
  } else {
    should_be_first=SH_FALSE;
  }
  sh_page_VIRTUAL_ADDRESS reg_idx;
  status=sh_radix_tree_get_value(&virt_plane->region_radix_tree,reg->region_size_pages,&reg_idx);
  if (sh_status_error(status)) return status;
  sh_pez_REGION_VIRTUAL_OBJECT *region=sh_slab_reg_virt_ref_to_ptr(virt_plane->slab_reg_virt,(sh_uint32)reg_idx);
  if (region==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED;
  if ((!should_be_first && region==reg) || (should_be_first && region!=reg)) {
    return SH_STATUS_PEZ_CORRUPTED;
  }
  if (optional_is_first!=SH_NULLPTR) {
    *optional_is_first=should_be_first;
  }
  if (optional_reg_idx!=SH_NULLPTR) {
    *optional_reg_idx=boundary.region_index;
  }
  if (optional_prev_idx!=SH_NULLPTR) {
    *optional_prev_idx=boundary.previous_index;
  }
  return SH_STATUS_SUCCESS;
}
static SH_STATUS sh_pez_internal_delete_free_region_virtual(sh_pez_VIRTUAL_PLANE *virt_plane,sh_pez_REGION_VIRTUAL_OBJECT *reg) {
  if (virt_plane==SH_NULLPTR || reg==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER;
  sh_bool is_first;
  sh_slab_reg_virt_OBJECT_INDEX reg_idx;
  sh_slab_reg_virt_OBJECT_INDEX prev_idx;
  SH_STATUS status=sh_pez_internal_check_region_integrity_virtual(virt_plane,reg,&is_first,&reg_idx,&prev_idx);
  if (sh_status_error(status)) return status;
  if (is_first) {
    // if region is the first of the list
    if (reg->next_region_index==0) {
      // if region was alone in his list
      // delete radix entry
      status=sh_radix_tree_delete_value(virt_plane->slab_radix_node,&virt_plane->region_radix_tree,reg->region_size_pages);
      if (sh_status_error(status)) return status;
      // delete left boundary
      status=sh_radix_tree_delete_value(virt_plane->slab_radix_node,&virt_plane->boundary_radix_tree,reg->start_page_index);
      if (sh_status_error(status)) return status;
      // delete right boundary
      if (reg->region_size_pages!=1) {
        status=sh_radix_tree_delete_value(virt_plane->slab_radix_node,&virt_plane->boundary_radix_tree,reg->start_page_index+reg->region_size_pages-1);
        if (sh_status_error(status)) return status;
      }
      // delete region object
      status=sh_slab_reg_virt_dealloc(virt_plane->slab_reg_virt,reg_idx);
      if (sh_status_error(status)) return status;
      return SH_STATUS_SUCCESS;
    } else {
      // if region isn't alone in his list
      // obtain next region informations
      sh_slab_reg_virt_OBJECT_INDEX next_reg_idx=reg->next_region_index;
      sh_pez_REGION_VIRTUAL_OBJECT *next_region=sh_slab_reg_virt_ref_to_ptr(virt_plane->slab_reg_virt,next_reg_idx);
      if (next_region==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED;
      // insert next region as first inside size list
      status=sh_radix_tree_insert_value(virt_plane->slab_radix_node,virt_plane->kernel_ptp,&virt_plane->region_radix_tree,reg->region_size_pages,next_reg_idx);
      if (sh_status_error(status)) return status;
      // update left boundary of next region
      sh_page_VIRTUAL_ADDRESS new_boundary_for_next_region=sh_pez_internal_pack_boundary(next_reg_idx,0);
      status=sh_radix_tree_insert_value(virt_plane->slab_radix_node,virt_plane->kernel_ptp,&virt_plane->boundary_radix_tree,next_region->start_page_index,new_boundary_for_next_region);
      if (sh_status_error(status)) return status;
      // update right boundary of next region
      status=sh_radix_tree_insert_value(virt_plane->slab_radix_node,virt_plane->kernel_ptp,&virt_plane->boundary_radix_tree,next_region->start_page_index+next_region->region_size_pages-1,new_boundary_for_next_region);
      if (sh_status_error(status)) return status;
      // delete left boundary
      status=sh_radix_tree_delete_value(virt_plane->slab_radix_node,&virt_plane->boundary_radix_tree,reg->start_page_index);
      if (sh_status_error(status)) return status;
      // delete right boundary
      if (reg->region_size_pages!=1) {
        status=sh_radix_tree_delete_value(virt_plane->slab_radix_node,&virt_plane->boundary_radix_tree,reg->start_page_index+reg->region_size_pages-1);
        if (sh_status_error(status)) return status;
      }
      // delete region object
      status=sh_slab_reg_virt_dealloc(virt_plane->slab_reg_virt,reg_idx);
      if (sh_status_error(status)) return status;
      return SH_STATUS_SUCCESS;
    }
  } else {
    // if region isn't the first of his list
    // obtain information about prev region
    sh_pez_REGION_VIRTUAL_OBJECT *prev_region=sh_slab_reg_virt_ref_to_ptr(virt_plane->slab_reg_virt,prev_idx);
    if (prev_region==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED;
    if (reg->next_region_index==0) {
      // if region is at the end of the size list
      // put next_idx of prev region to 0
      prev_region->next_region_index=0;
      // delete left boundary
      status=sh_radix_tree_delete_value(virt_plane->slab_radix_node,&virt_plane->boundary_radix_tree,reg->start_page_index);
      if (sh_status_error(status)) return status;
      // delete right boundary
      if (reg->region_size_pages!=1) {
        status=sh_radix_tree_delete_value(virt_plane->slab_radix_node,&virt_plane->boundary_radix_tree,reg->start_page_index+reg->region_size_pages-1);
        if (sh_status_error(status)) return status;
      }
      // delete region object
      status=sh_slab_reg_virt_dealloc(virt_plane->slab_reg_virt,reg_idx);
      if (sh_status_error(status)) return status;
      return SH_STATUS_SUCCESS;
    } else {
      // if region is in the middle of the size list
      // obtain information about next region
      sh_slab_reg_virt_OBJECT_INDEX next_reg_idx=reg->next_region_index;
      sh_pez_REGION_VIRTUAL_OBJECT *next_region=sh_slab_reg_virt_ref_to_ptr(virt_plane->slab_reg_virt,next_reg_idx);
      if (next_region==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED;
      // put next_idx of prev region to next_region_idx
      prev_region->next_region_index=next_reg_idx;
      // update left boundary of next region
      sh_page_VIRTUAL_ADDRESS new_boundary_for_next_region=sh_pez_internal_pack_boundary(next_reg_idx,prev_idx);
      status=sh_radix_tree_insert_value(virt_plane->slab_radix_node,virt_plane->kernel_ptp,&virt_plane->boundary_radix_tree,next_region->start_page_index,new_boundary_for_next_region);
      if (sh_status_error(status)) return status;
      // update right boundary of next region
      status=sh_radix_tree_insert_value(virt_plane->slab_radix_node,virt_plane->kernel_ptp,&virt_plane->boundary_radix_tree,next_region->start_page_index+next_region->region_size_pages-1,new_boundary_for_next_region);
      if (sh_status_error(status)) return status;
      // delete left boundary
      status=sh_radix_tree_delete_value(virt_plane->slab_radix_node,&virt_plane->boundary_radix_tree,reg->start_page_index);
      if (sh_status_error(status)) return status;
      // delete right boundary
      if (reg->region_size_pages!=1) {
        status=sh_radix_tree_delete_value(virt_plane->slab_radix_node,&virt_plane->boundary_radix_tree,reg->start_page_index+reg->region_size_pages-1);
        if (sh_status_error(status)) return status;
      }
      // delete region object
      status=sh_slab_reg_virt_dealloc(virt_plane->slab_reg_virt,reg_idx);
      if (sh_status_error(status)) return status;
      return SH_STATUS_SUCCESS;
    }
  }
}
SH_STATUS sh_pez_init_virtual_plane(sh_page_VIRTUAL_ADDRESS plane_offset,sh_slab_reg_virt_SLAB_ALLOCATOR *slab_reg_virt,struct sh_slab_radix_node_SLAB_ALLOCATOR *slab_radix_node,sh_page_PAGE_TABLE_POOL *kernel_ptp,sh_page_PAGE_TABLE_POOL *reference_ptp,sh_pez_VIRTUAL_PLANE *virt_plane) {
  if (slab_reg_virt==SH_NULLPTR || slab_radix_node==SH_NULLPTR || kernel_ptp==SH_NULLPTR || reference_ptp==SH_NULLPTR || virt_plane==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER;
  sh_radix_tree_init(slab_radix_node,kernel_ptp,&virt_plane->region_radix_tree,8);
  sh_radix_tree_init(slab_radix_node,kernel_ptp,&virt_plane->boundary_radix_tree,8);
  virt_plane->free_pages=0xFFFFFFFF-1;
  virt_plane->used_pages=0;
  virt_plane->slab_reg_virt=slab_reg_virt;
  virt_plane->slab_radix_node=slab_radix_node;
  virt_plane->kernel_ptp=kernel_ptp;
  virt_plane->reference_ptp=reference_ptp;
  virt_plane->plane_offset=plane_offset;
  sh_pez_REGION_VIRTUAL_OBJECT *reg;
  SH_STATUS status=sh_pez_internal_create_free_region_virtual(virt_plane,0,0xFFFFFFFF-1,&reg);
  if (sh_status_error(status)) return status;
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_pez_alloc_virtual_pages(sh_pez_VIRTUAL_PLANE *virt_plane,sh_uint32 pages_count,sh_page_VIRTUAL_ADDRESS *address) {
  if (virt_plane==SH_NULLPTR || pages_count==0 || address==SH_NULLPTR) return SH_STATUS_INVALID_PARAMETER;
  sh_page_VIRTUAL_ADDRESS reg_idx;
  SH_STATUS status=sh_radix_tree_search_smallest_min_bound(virt_plane->slab_radix_node,&virt_plane->region_radix_tree,pages_count,&reg_idx);
  if (sh_status_error(status) && status!=SH_STATUS_NOT_FOUND) return status;
  if (status==SH_STATUS_NOT_FOUND) return SH_STATUS_OUT_OF_MEMORY;
  sh_pez_REGION_VIRTUAL_OBJECT *reg=sh_slab_reg_virt_ref_to_ptr(virt_plane->slab_reg_virt,(sh_uint32)reg_idx);
  if (reg==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED;
  if (reg->region_size_pages<pages_count) {
    // something has gone bad with successor function for finding a big enougth region
    return SH_STATUS_PEZ_CORRUPTED;
  } else if (reg->region_size_pages==pages_count) {
    // exact fit case
    // save metadata for later modifications
    sh_uint32 start=reg->start_page_index;
    // delete free region
    status=sh_pez_internal_delete_free_region_virtual(virt_plane,reg);
    if (sh_status_error(status)) return status;
    // give the address
    *address=((sh_page_VIRTUAL_ADDRESS)start)*SH_PAGE_SIZE+virt_plane->plane_offset;
  } else if (reg->region_size_pages>pages_count) {
    // best fit case
    // save metadata for later modifications
    sh_uint32 start=reg->start_page_index;
    // compute new size and index
    sh_uint32 new_start=reg->start_page_index+pages_count;
    if (new_start==0) return SH_STATUS_PEZ_CORRUPTED;
    sh_uint32 new_size=reg->region_size_pages-pages_count;
    if (new_size==0) return SH_STATUS_PEZ_CORRUPTED;
    // delete free region
    status=sh_pez_internal_delete_free_region_virtual(virt_plane,reg);
    if (sh_status_error(status)) return status;
    // create new free region with splitted part
    sh_pez_REGION_VIRTUAL_OBJECT *new_reg;
    status=sh_pez_internal_create_free_region_virtual(virt_plane,new_start,new_size,&new_reg);
    if (sh_status_error(status)) return status;
    // give the address
    *address=((sh_page_VIRTUAL_ADDRESS)start)*SH_PAGE_SIZE+virt_plane->plane_offset;
  }
  virt_plane->free_pages=virt_plane->free_pages-pages_count;
  virt_plane->used_pages=virt_plane->used_pages+pages_count;
  return SH_STATUS_SUCCESS;
}
SH_STATUS sh_pez_free_virtual_pages(sh_pez_VIRTUAL_PLANE *virt_plane,sh_page_VIRTUAL_ADDRESS *address,sh_uint32 pages_count) {
  if (virt_plane==SH_NULLPTR || address==SH_NULLPTR || pages_count==0) return SH_STATUS_INVALID_PARAMETER;
  // save freeed region data
  sh_uint32 start_reg=(sh_uint32)((*address)/SH_PAGE_SIZE-virt_plane->plane_offset);
  sh_uint32 size_reg=pages_count;
  // identifying neighbours
  // left
  sh_page_VIRTUAL_ADDRESS left_neighbour;
  SH_STATUS status=sh_radix_tree_get_value(&virt_plane->boundary_radix_tree,start_reg-1,&left_neighbour);
  if (sh_status_error(status) && status!=SH_STATUS_NOT_FOUND) return status;
  sh_bool left_fusion;
  if (status==SH_STATUS_NOT_FOUND) {
    left_fusion=SH_FALSE;
  } else {
    left_fusion=SH_TRUE;
  }
  // right
  sh_page_VIRTUAL_ADDRESS right_neighbour;
  status=sh_radix_tree_get_value(&virt_plane->boundary_radix_tree,start_reg+size_reg,&right_neighbour);
  if (sh_status_error(status) && status!=SH_STATUS_NOT_FOUND) return status;
  sh_bool right_fusion;
  if (status==SH_STATUS_NOT_FOUND) {
    right_fusion=SH_FALSE;
  } else {
    right_fusion=SH_TRUE;
  }
  // preparing fusion
  sh_uint32 new_start=start_reg;
  sh_uint32 new_size=size_reg;
  // fusionning left region
  if (left_fusion) {
    // obtaining information about left region
    sh_pez_internal_BOUNDARY_ENTRY left_boundary=sh_pez_internal_unpack_boundary(left_neighbour);
    sh_slab_reg_virt_OBJECT_INDEX left_reg_idx=left_boundary.region_index;
    sh_pez_REGION_VIRTUAL_OBJECT *left_reg=sh_slab_reg_virt_ref_to_ptr(virt_plane->slab_reg_virt,left_reg_idx);
    if (left_reg==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED;
    // updating new region informations
    new_start=left_reg->start_page_index;
    new_size=new_size+left_reg->region_size_pages;
    if (new_size==0) return SH_STATUS_PEZ_CORRUPTED;
    // deleting left region
    status=sh_pez_internal_delete_free_region_virtual(virt_plane,left_reg);
    if (sh_status_error(status)) return status;
  }
  // fusionning right region
  if (right_fusion) {
    // obtaining information about right region
    sh_pez_internal_BOUNDARY_ENTRY right_boundary=sh_pez_internal_unpack_boundary(right_neighbour);
    sh_slab_reg_virt_OBJECT_INDEX right_reg_idx=right_boundary.region_index;
    sh_pez_REGION_VIRTUAL_OBJECT *right_reg=sh_slab_reg_virt_ref_to_ptr(virt_plane->slab_reg_virt,right_reg_idx);
    if (right_reg==SH_NULLPTR) return SH_STATUS_ERROR_NULLPTR_RETURNED;
    // updating new region informations
    new_size=new_size+right_reg->region_size_pages;
    if (new_size==0) return SH_STATUS_PEZ_CORRUPTED;
    // deleting right region
    status=sh_pez_internal_delete_free_region_virtual(virt_plane,right_reg);
    if (sh_status_error(status)) return status;
  }
  // inserting new region
  sh_pez_REGION_VIRTUAL_OBJECT *new_reg;
  status=sh_pez_internal_create_free_region_virtual(virt_plane,new_start,new_size,&new_reg);
  if (sh_status_error(status)) return status;
  virt_plane->free_pages=virt_plane->free_pages+pages_count;
  virt_plane->used_pages=virt_plane->used_pages-pages_count;
  *address=0;
  return SH_STATUS_SUCCESS;
}
