The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <math.h>
#include <stdint.h>

#include "spvm_runtime_allocator.h"
#include "spvm_runtime_api.h"
#include "spvm_util_allocator.h"
#include "spvm_memory_pool.h"
#include "spvm_dynamic_array.h"
#include "spvm_runtime.h"
#include "spvm_constant_pool.h"
#include "spvm_api.h"

SPVM_RUNTIME_ALLOCATOR* SPVM_RUNTIME_ALLOCATOR_new(SPVM_RUNTIME* runtime) {
  (void)runtime;
  
  SPVM_RUNTIME_ALLOCATOR* allocator = SPVM_UTIL_ALLOCATOR_safe_malloc_i32(1, sizeof(SPVM_RUNTIME_ALLOCATOR));
  
  // Memory pool
  allocator->memory_pool = SPVM_MEMORY_POOL_new(0);
  
  // Free lists
  allocator->freelists = SPVM_UTIL_ALLOCATOR_safe_malloc_i32(16, sizeof(SPVM_DYNAMIC_ARRAY));
  
  // Initialize free list
  {
    int32_t i;
    for (i = 0; i < 16; i++) {
      allocator->freelists[i] = SPVM_DYNAMIC_ARRAY_new(0);
    }
  }
  
  // use memory pool max reference byte size
  allocator->base_object_max_byte_size_use_memory_pool = 0xFFFF;
  
  return allocator;
}

int32_t SPVM_RUNTIME_ALLOCATOR_get_freelist_index(SPVM_API* api, SPVM_RUNTIME_ALLOCATOR* allocator, int64_t size) {
  (void)api;
  (void)allocator;
  
  assert(size > 0);
  
  // To 2 ^ n
  // This algorizm is from http://ideone.com/EStSRd
  int64_t N = size;
  int64_t _N1 = N-1;
  int64_t _N2 = _N1 | (_N1 >>  1);
  int64_t _N3 = _N2 | (_N2 >>  2);
  int64_t _N4 = _N3 | (_N3 >>  4);
  int64_t _N5 = _N4 | (_N4 >>  8);
  int64_t _N6 = _N5 | (_N5 >> 16);
  int64_t _N7 = _N6 | (_N6 >> 32);
  int64_t Value = _N7 + 1;
  
  // Category
  int64_t div_size = Value;
  int32_t index = -1;
  while (1) {
    div_size = div_size / 2;
    if (div_size == 0) {
      break;
    }
    else {
      index++;
    }
  }
  assert(index >= 0);
  
  return index;
}

void* SPVM_RUNTIME_ALLOCATOR_malloc(SPVM_API* api, SPVM_RUNTIME_ALLOCATOR* allocator, int64_t size) {
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)api->runtime;

  assert(size > 0);
  
  void* block;
  if (size > allocator->base_object_max_byte_size_use_memory_pool) {
    block = SPVM_UTIL_ALLOCATOR_safe_malloc_i64(1, size);
  }
  else {
    int32_t index = SPVM_RUNTIME_ALLOCATOR_get_freelist_index(api, allocator, size);
    
    void* free_address = SPVM_DYNAMIC_ARRAY_pop(allocator->freelists[index]);
    if (free_address) {
      block = free_address;
    }
    else {
      block = SPVM_MEMORY_POOL_alloc(allocator->memory_pool, size);
    }
  }

#ifdef DEBUG
  if (block != NULL) {
    runtime->object_count++;
  }
  printf("MALLOC OBJECT COUNT %d\n", runtime->object_count);
#endif
  
  return block;
}

void SPVM_RUNTIME_ALLOCATOR_free_base_object(SPVM_API* api, SPVM_RUNTIME_ALLOCATOR* allocator, SPVM_BASE_OBJECT* base_object) {
  SPVM_RUNTIME* runtime = (SPVM_RUNTIME*)api->runtime;
  
  if (base_object == NULL) {
    return;
  }
  else {
    // Byte size
    int64_t byte_size = SPVM_RUNTIME_API_calcurate_base_object_byte_size(api, base_object);
    
    assert(byte_size > 0);
    
#ifdef DEBUG
    runtime->object_count--;
    printf("FREE OBJECT COUNT %d\n", runtime->object_count);
    assert(runtime->object_count >= 0);
#endif
    
    if (byte_size > allocator->base_object_max_byte_size_use_memory_pool) {
      free(base_object);
    }
    else {
      // Freelist index
      int32_t freelist_index = SPVM_RUNTIME_ALLOCATOR_get_freelist_index(api, allocator, byte_size);
      
      // Push free address
      SPVM_DYNAMIC_ARRAY_push(allocator->freelists[freelist_index], base_object);
    }
  }
}

void SPVM_RUNTIME_ALLOCATOR_free(SPVM_RUNTIME* runtime, SPVM_RUNTIME_ALLOCATOR* allocator) {
  (void)runtime;
  
  // Free memory pool */
  SPVM_MEMORY_POOL_free(allocator->memory_pool);
  
  free(allocator->freelists);
  
  free(allocator);
}