diff --git a/CMakeLists.txt b/CMakeLists.txt index 321f2de..499bee9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,21 +5,22 @@ set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD_REQUIRED ON) include_directories(${PROJECT_SOURCE_DIR}/Include) -file(GLOB_RECURSE SRC_FILES "${PROJECT_SOURCE_DIR}/Src/Hallocy/*.c") +file(GLOB_RECURSE SRC_FILES "${PROJECT_SOURCE_DIR}/Src/*.c") add_library(Hallocy STATIC ${SRC_FILES}) -add_executable(HallocyApp ${PROJECT_SOURCE_DIR}/Src/Main.c) +add_executable(HallocyTest ${PROJECT_SOURCE_DIR}/Tests/Main.c) -target_link_libraries(HallocyApp Hallocy) +target_include_directories(HallocyTest PRIVATE ${PROJECT_SOURCE_DIR}/Tests) +target_link_libraries(HallocyTest Hallocy) if (MSVC) target_compile_options(Hallocy PRIVATE /W4 /Zl) else() target_compile_options(Hallocy PRIVATE -mavx512f -mavx512vl) - target_compile_options(HallocyApp PRIVATE -mavx512f -mavx512vl) + target_compile_options(HallocyTest PRIVATE -mavx512f -mavx512vl) target_compile_options(Hallocy PRIVATE -march=native) - target_compile_options(HallocyApp PRIVATE -march=native) + target_compile_options(HallocyTest PRIVATE -march=native) target_compile_options(Hallocy PRIVATE -Wall -Wextra -pedantic) endif() \ No newline at end of file diff --git a/Include/Hallocy/Allocator.h b/Include/Hallocy/Allocator.h new file mode 100644 index 0000000..a8f0805 --- /dev/null +++ b/Include/Hallocy/Allocator.h @@ -0,0 +1,34 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ----------------------------------------------------------------------------- + * File: Allocator.h + * Description: + * This file contains the functions for allocating, freeing and managing memory. + * It includes functions to allocate, reallocate, free, copy, move and set memory. + * + * Author: Mineplay + * ----------------------------------------------------------------------------- + */ +#ifndef HALLOCY_ALLOCATOR +#define HALLOCY_ALLOCATOR + +#include +#include + +#include "Error.h" + +void *hallocy_malloc(size_t size); +HallocyError hallocy_free(void *pointer); + +#endif \ No newline at end of file diff --git a/Include/Hallocy/Error.h b/Include/Hallocy/Error.h new file mode 100644 index 0000000..3e65784 --- /dev/null +++ b/Include/Hallocy/Error.h @@ -0,0 +1,34 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ----------------------------------------------------------------------------- + * File: Error.h + * Description: + * This file contains the ErrorCode enum that defines the error codes for the + * library. + * + * Author: Mineplay + * ----------------------------------------------------------------------------- + */ +#ifndef HALLOCY_ERROR +#define HALLOCY_ERROR + +typedef enum { + HALLOCY_ERROR_NONE = 0, + HALLOCY_ERROR_INVALID_POINTER = 1, + HALLOCY_ERROR_OUT_OF_MEMORY = 2, + HALLOCY_ERROR_UNKNOWN = 3, + HALLOCY_ERROR_INVALID_PARAM = 4 +} HallocyError; + +#endif \ No newline at end of file diff --git a/Src/Allocator.c b/Src/Allocator.c new file mode 100644 index 0000000..5471122 --- /dev/null +++ b/Src/Allocator.c @@ -0,0 +1,393 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ----------------------------------------------------------------------------- + * File: Allocator.h + * Description: + * This file implements the functions for allocating, freeing and managing memory. + * It includes functions to allocate, reallocate, free, copy, move and set memory. + * + * Author: Mineplay + * ----------------------------------------------------------------------------- + */ +#include "../Include/Hallocy/Allocator.h" + +#if defined(_WIN32) +#include + +static const INIT_ONCE HALLOCY_INIT_ONCE = INIT_ONCE_STATIC_INIT; + +static HANDLE hallocy_heap = NULL; +static CRITICAL_SECTION hallocy_critical_section; +#elif defined(__linux__) +#include +#include +#include +#include + +static const int HALLOCY_FUTEX_WAKE = 0; +static const int HALLOCY_FUTEX_WAIT = 1; + +static int hallocy_futex_address = 0; +#endif + +typedef struct HallocyMemoryHeader { + size_t size; + struct HallocyMemoryHeader *next; +} HallocyMemoryHeader; + +static size_t hallocy_medium_memory_freed = 0; +static size_t hallocy_medium_memory_allocated = 0; +static HallocyMemoryHeader *hallocy_medium_memory_bin = NULL; + +static _Thread_local size_t hallocy_small_memory_freed = 0; +static _Thread_local size_t hallocy_small_memory_allocated = 0; +static _Thread_local HallocyMemoryHeader *hallocy_small_memory_bin = NULL; + +static size_t page_size = 0; +static size_t hallocy_small_allocation_size = 0; +static size_t hallocy_medium_allocation_size = 0; + +#if defined(_WIN32) +static BOOL CALLBACK hallocy_initialize_mutex(PINIT_ONCE init_once, PVOID parameter, PVOID *context) { + (void)init_once; + (void)parameter; + (void)context; + + return InitializeCriticalSectionEx(&hallocy_critical_section, 0x00000400, 0); +} +#endif + +void *hallocy_malloc(size_t size) { + if (page_size == 0) { + #if defined(_WIN32) + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + + page_size = system_info.dwPageSize; + #elif defined(__linux__) + page_size = sysconf(_SC_PAGE_SIZE); + #endif + + hallocy_small_allocation_size = page_size; + hallocy_medium_allocation_size = page_size * 10; + } + + size_t aligned_size = page_size * (((size + sizeof(HallocyMemoryHeader)) / page_size) + 1); + HallocyMemoryHeader *memory_pointer = NULL; + if (aligned_size <= hallocy_small_allocation_size) { + HallocyMemoryHeader *previous_header = NULL; + memory_pointer = hallocy_small_memory_bin; + while (memory_pointer != NULL) { + if (memory_pointer->size >= aligned_size) { + hallocy_small_memory_allocated += memory_pointer->size; + hallocy_small_memory_freed -= memory_pointer->size; + + if (previous_header == NULL) { + hallocy_small_memory_bin = hallocy_small_memory_bin->next; + } else { + previous_header->next = memory_pointer->next; + } + + memory_pointer->next = NULL; + return (void*)(memory_pointer + 1); + } + + previous_header = memory_pointer; + memory_pointer = memory_pointer->next; + } + + #if defined(_WIN32) + if (hallocy_heap == NULL) { + hallocy_heap = GetProcessHeap(); + } + + memory_pointer = HeapAlloc(hallocy_heap, 0, aligned_size); + if (memory_pointer == NULL) { + return NULL; + } + #elif defined(__linux__) + memory_pointer = mmap(NULL, aligned_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (memory_pointer == MAP_FAILED) { + return NULL; + } + #endif + + hallocy_small_memory_allocated += aligned_size; + } else if (aligned_size <= hallocy_medium_allocation_size) { + #if defined(_WIN32) + InitOnceExecuteOnce(&HALLOCY_INIT_ONCE, hallocy_initialize_mutex, NULL, NULL); + EnterCriticalSection(&hallocy_critical_section); + #elif defined(__linux__) + bool locked = false; + while (!locked) { + if (__sync_bool_compare_and_swap(&hallocy_futex_address, 0, 1)) { + locked = true; + } else { + syscall(SYS_futex, &hallocy_futex_address, HALLOCY_FUTEX_WAIT, 1, NULL, NULL, 0); + } + } + #endif + + HallocyMemoryHeader *previous_header = NULL; + memory_pointer = hallocy_medium_memory_bin; + while (memory_pointer != NULL) { + if (memory_pointer->size >= aligned_size) { + hallocy_medium_memory_allocated += memory_pointer->size; + hallocy_medium_memory_freed -= memory_pointer->size; + + if (previous_header == NULL) { + hallocy_medium_memory_bin = hallocy_medium_memory_bin->next; + } else { + previous_header->next = memory_pointer->next; + } + + #if defined(_WIN32) + LeaveCriticalSection(&hallocy_critical_section); + #elif defined(__linux__) + hallocy_futex_address = 0; + syscall(SYS_futex, &hallocy_futex_address, HALLOCY_FUTEX_WAKE, 1, NULL, NULL, 0); + #endif + + memory_pointer->next = NULL; + return (void*)(memory_pointer + 1); + } + + previous_header = memory_pointer; + memory_pointer = memory_pointer->next; + } + + #if defined(_WIN32) + if (hallocy_heap == NULL) { + hallocy_heap = GetProcessHeap(); + } + + memory_pointer = HeapAlloc(hallocy_heap, 0, aligned_size); + if (memory_pointer == NULL) { + return NULL; + } + + hallocy_medium_allocation_size += aligned_size; + LeaveCriticalSection(&hallocy_critical_section); + #elif defined(__linux__) + memory_pointer = mmap(NULL, aligned_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (memory_pointer == MAP_FAILED) { + return NULL; + } + + hallocy_medium_allocation_size += aligned_size; + + hallocy_futex_address = 0; + syscall(SYS_futex, &hallocy_futex_address, HALLOCY_FUTEX_WAKE, 1, NULL, NULL, 0); + #endif + } else { + #if defined(_WIN32) + memory_pointer = (HallocyMemoryHeader*)VirtualAlloc(NULL, aligned_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + if (memory_pointer == NULL) { + return NULL; + } + #elif defined(__linux__) + memory_pointer = (HallocyMemoryHeader*)mmap(NULL, aligned_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (memory_pointer == MAP_FAILED) { + return NULL; + } + #endif + } + + memory_pointer->size = aligned_size; + memory_pointer->next = NULL; + + return (void*)(memory_pointer + 1); +} + +HallocyError hallocy_free(void *pointer) { + if (pointer == NULL) { + return HALLOCY_ERROR_INVALID_POINTER; + } + + HallocyMemoryHeader *memory_header = ((HallocyMemoryHeader*)pointer) - 1; + if (memory_header->size <= hallocy_small_allocation_size) { + memory_header->next = hallocy_small_memory_bin; + hallocy_small_memory_bin = memory_header; + + hallocy_small_memory_allocated -= memory_header->size; + hallocy_small_memory_freed += memory_header->size; + if (hallocy_small_memory_allocated == 0 && hallocy_small_memory_freed > hallocy_medium_allocation_size) { + HallocyMemoryHeader *previous_header = NULL; + HallocyMemoryHeader *current_header = hallocy_small_memory_bin; + while (current_header != NULL) { + previous_header = current_header; + current_header = current_header->next; + + #if defined(_WIN32) + bool result = HeapFree(hallocy_heap, 0, previous_header); + if (result == false) { + DWORD error = GetLastError(); + switch (error) { + case ERROR_INVALID_PARAMETER: + return HALLOCY_ERROR_INVALID_PARAM; + + case ERROR_NOT_ENOUGH_MEMORY: + return HALLOCY_ERROR_OUT_OF_MEMORY; + + case ERROR_INVALID_HANDLE: + return HALLOCY_ERROR_INVALID_POINTER; + + default: + return HALLOCY_ERROR_UNKNOWN; + } + } + #elif defined(__linux__) + int result = munmap(previous_header, previous_header->size); + if (result == -1) { + switch (errno) { + case EINVAL: + return HALLOCY_ERROR_INVALID_PARAM; + + case ENOMEM: + return HALLOCY_ERROR_OUT_OF_MEMORY; + + case EFAULT: + return HALLOCY_ERROR_INVALID_POINTER; + + default: + return HALLOCY_ERROR_UNKNOWN; + } + } + #endif + } + + hallocy_small_memory_bin = NULL; + hallocy_small_memory_allocated = 0; + hallocy_small_memory_freed = 0; + } + } else if (memory_header->size <= hallocy_medium_allocation_size) { + #if defined(_WIN32) + InitOnceExecuteOnce(&HALLOCY_INIT_ONCE, hallocy_initialize_mutex, NULL, NULL); + EnterCriticalSection(&hallocy_critical_section); + #elif defined(__linux__) + bool locked = false; + while (!locked) { + if (__sync_bool_compare_and_swap(&hallocy_futex_address, 0, 1)) { + locked = true; + } else { + syscall(SYS_futex, &hallocy_futex_address, HALLOCY_FUTEX_WAIT, 1, NULL, NULL, 0); + } + } + #endif + + memory_header->next = hallocy_medium_memory_bin; + hallocy_medium_memory_bin = memory_header; + + hallocy_medium_memory_allocated -= memory_header->size; + hallocy_medium_memory_freed += memory_header->size; + if (hallocy_medium_memory_allocated == 0 && hallocy_medium_memory_freed > hallocy_medium_allocation_size) { + HallocyMemoryHeader *previous_header = NULL; + HallocyMemoryHeader *current_header = hallocy_medium_memory_bin; + while (current_header != NULL) { + previous_header = current_header; + current_header = current_header->next; + + #if defined(_WIN32) + bool result = HeapFree(hallocy_heap, 0, previous_header); + if (result == false) { + DWORD error = GetLastError(); + switch (error) { + case ERROR_INVALID_PARAMETER: + return HALLOCY_ERROR_INVALID_PARAM; + + case ERROR_NOT_ENOUGH_MEMORY: + return HALLOCY_ERROR_OUT_OF_MEMORY; + + case ERROR_INVALID_HANDLE: + return HALLOCY_ERROR_INVALID_POINTER; + + default: + return HALLOCY_ERROR_UNKNOWN; + } + } + #elif defined(__linux__) + int result = munmap(previous_header, previous_header->size); + if (result == -1) { + switch (errno) { + case EINVAL: + return HALLOCY_ERROR_INVALID_PARAM; + + case ENOMEM: + return HALLOCY_ERROR_OUT_OF_MEMORY; + + case EFAULT: + return HALLOCY_ERROR_INVALID_POINTER; + + default: + return HALLOCY_ERROR_UNKNOWN; + } + } + #endif + } + + hallocy_medium_memory_bin = NULL; + hallocy_medium_memory_allocated = 0; + hallocy_medium_memory_freed = 0; + } + + #if defined(_WIN32) + LeaveCriticalSection(&hallocy_critical_section); + #elif defined(__linux__) + hallocy_futex_address = 0; + syscall(SYS_futex, &hallocy_futex_address, HALLOCY_FUTEX_WAKE, 1, NULL, NULL, 0); + #endif + } else { + #if defined(_WIN32) + bool result = VirtualFree(memory_header, 0, MEM_RELEASE); + if (result == false) { + DWORD error = GetLastError(); + switch (error) { + case ERROR_INVALID_PARAMETER: + return HALLOCY_ERROR_INVALID_PARAM; + + case ERROR_NOT_ENOUGH_MEMORY: + return HALLOCY_ERROR_OUT_OF_MEMORY; + + case ERROR_INVALID_HANDLE: + return HALLOCY_ERROR_INVALID_POINTER; + + case ERROR_INVALID_ADDRESS: + return HALLOCY_ERROR_INVALID_POINTER; + + default: + return HALLOCY_ERROR_UNKNOWN; + } + } + #elif defined(__linux__) + int result = munmap(memory_header, memory_header->size); + if (result == -1) { + switch (errno) { + case EINVAL: + return HALLOCY_ERROR_INVALID_PARAM; + + case ENOMEM: + return HALLOCY_ERROR_OUT_OF_MEMORY; + + case EFAULT: + return HALLOCY_ERROR_INVALID_POINTER; + + default: + return HALLOCY_ERROR_UNKNOWN; + } + } + #endif + } + + return HALLOCY_ERROR_NONE; +} \ No newline at end of file diff --git a/Src/Main.c b/Src/Main.c deleted file mode 100644 index 8321e25..0000000 --- a/Src/Main.c +++ /dev/null @@ -1,6 +0,0 @@ -#include - -int main() { - printf("Hello, World!\n"); - return 0; -} \ No newline at end of file diff --git a/Tests/Main.c b/Tests/Main.c new file mode 100644 index 0000000..3b13990 --- /dev/null +++ b/Tests/Main.c @@ -0,0 +1,44 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ----------------------------------------------------------------------------- + * File: Main.c + * Description: + * Executes all tests. + * + * Author: Mineplay + * ----------------------------------------------------------------------------- + */ + #include + #include + + int main() { + char *memory = (char *)hallocy_malloc(12288); + if (memory == NULL) { + printf("Failed it allocate memory!"); + return -1; + } + + memory[0] = 'H'; + memory[1] = 'i'; + memory[2] = '\0'; + + printf("%s\n", memory); + int code = hallocy_free(memory); + if (code != HALLOCY_ERROR_NONE) { + printf("Failed to free memory error code (%d)!", code); + return -1; + } + + return 0; + } \ No newline at end of file