Add a build system

This commit is contained in:
2026-04-29 22:28:21 +01:00
parent 5cb47a17a2
commit fb83c3114f
22 changed files with 761 additions and 93 deletions

6
.gitignore vendored
View File

@@ -3,11 +3,13 @@ file_hasher.ilk
file_hasher.rdi file_hasher.rdi
file_hasher.exe file_hasher.exe
file_hashes.txt file_hashes.txt
Binaries/file_hashes.txt /Binaries
file_list.txt file_list.txt
temp_code.c temp_code.c
/.cache/clangd/index /.cache
/file_hasher /file_hasher
/io_uring_test /io_uring_test
/file_hasher /file_hasher
/io_uring_test /io_uring_test
/compile_commands.json
/build

284
CMakeLists.txt Normal file
View File

@@ -0,0 +1,284 @@
cmake_minimum_required(VERSION 3.20)
project(filehasher
VERSION 1.0.0
DESCRIPTION "High-performance file hasher with I/O Ring/io_uring support"
LANGUAGES C
)
# ---------------------------------------------------------------------------
# Force compiler search order
# ---------------------------------------------------------------------------
# On Windows, prefer clang-cl, then GCC, then Clang
if(WIN32)
# Try to force compiler order if not already set
if(NOT CMAKE_C_COMPILER)
# Search in preferred order
find_program(CLANG_CL_COMPILER NAMES clang-cl)
find_program(GCC_COMPILER NAMES gcc)
find_program(CLANG_COMPILER NAMES clang)
if(CLANG_CL_COMPILER)
message(STATUS "Found clang-cl as preferred compiler: ${CLANG_CL_COMPILER}")
set(CMAKE_C_COMPILER "${CLANG_CL_COMPILER}" CACHE STRING "" FORCE)
elseif(GCC_COMPILER)
message(STATUS "Found GCC as fallback compiler: ${GCC_COMPILER}")
set(CMAKE_C_COMPILER "${GCC_COMPILER}" CACHE STRING "" FORCE)
elseif(CLANG_COMPILER)
message(STATUS "Found Clang as last-resort compiler: ${CLANG_COMPILER}")
set(CMAKE_C_COMPILER "${CLANG_COMPILER}" CACHE STRING "" FORCE)
endif()
endif()
else()
# On Linux, prefer GCC, then Clang
if(NOT CMAKE_C_COMPILER)
find_program(GCC_COMPILER NAMES gcc)
find_program(CLANG_COMPILER NAMES clang)
if(GCC_COMPILER)
message(STATUS "Found GCC as preferred compiler: ${GCC_COMPILER}")
set(CMAKE_C_COMPILER "${GCC_COMPILER}" CACHE STRING "" FORCE)
elseif(CLANG_COMPILER)
message(STATUS "Found Clang as fallback compiler: ${CLANG_COMPILER}")
set(CMAKE_C_COMPILER "${CLANG_COMPILER}" CACHE STRING "" FORCE)
endif()
endif()
endif()
# Now project() will use the compiler we found
# However, since we needed project() first to get C support,
# we check what we actually got
message(STATUS "Using compiler: ${CMAKE_C_COMPILER} (${CMAKE_C_COMPILER_ID})")
# ---------------------------------------------------------------------------
# Platform and Compiler Detection
# ---------------------------------------------------------------------------
if(WIN32)
set(PLATFORM_WINDOWS TRUE)
set(PLATFORM_NAME "Windows")
else()
set(PLATFORM_LINUX TRUE)
set(PLATFORM_NAME "Linux")
endif()
# Compiler type
if(CMAKE_C_COMPILER_ID STREQUAL "Clang")
# Check if it's clang-cl
if(CMAKE_C_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
set(COMPILER_CLANG_CL TRUE)
message(STATUS "Detected clang-cl (MSVC-compatible frontend)")
else()
set(COMPILER_CLANG_GNU TRUE)
message(STATUS "Detected Clang (GNU-compatible frontend)")
endif()
elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU")
set(COMPILER_GCC TRUE)
message(STATUS "Detected GCC")
elseif(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
# We don't want MSVC, but if it's found, warn user
message(FATAL_ERROR
"MSVC (cl.exe) detected!\n"
"This project requires clang-cl, GCC, or Clang.\n"
"Please install one of these compilers or specify manually:\n"
" cmake .. -DCMAKE_C_COMPILER=clang-cl\n"
" cmake .. -DCMAKE_C_COMPILER=gcc\n"
" cmake .. -DCMAKE_C_COMPILER=clang\n"
)
endif()
# ---------------------------------------------------------------------------
# Build System Selection
# ---------------------------------------------------------------------------
if(NOT CMAKE_GENERATOR OR CMAKE_GENERATOR STREQUAL "")
find_program(NINJA_EXECUTABLE NAMES ninja)
if(NINJA_EXECUTABLE)
message(STATUS "Using Ninja build system")
set(CMAKE_GENERATOR "Ninja")
else()
message(STATUS "Ninja not found, using default generator: ${CMAKE_GENERATOR}")
endif()
endif()
# ---------------------------------------------------------------------------
# Source Files
# ---------------------------------------------------------------------------
set(SOURCES
file_hasher.c
xxhash.c
xxh_x86dispatch.c
)
# Headers for dependency tracking and IDE
set(HEADERS
arena.h
base.h
xxhash.h
lf_mpmc.h
)
# ---------------------------------------------------------------------------
# Create Executable
# ---------------------------------------------------------------------------
add_executable(${PROJECT_NAME}
${SOURCES}
${HEADERS}
)
# Include directories
target_include_directories(${PROJECT_NAME} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
)
# ---------------------------------------------------------------------------
# Compiler Flags - Exact match to your commands
# ---------------------------------------------------------------------------
if(PLATFORM_WINDOWS)
if(COMPILER_CLANG_CL)
# === clang-cl flags ===
# Release: /O2
target_compile_options(${PROJECT_NAME} PRIVATE
$<$<CONFIG:Release>:/O2>
)
# Debug: /Zi /Od
target_compile_options(${PROJECT_NAME} PRIVATE
$<$<CONFIG:Debug>:/Zi /Od>
)
# Common warnings
target_compile_options(${PROJECT_NAME} PRIVATE /W4)
elseif(COMPILER_GCC)
# === GCC flags (Windows/MinGW) ===
# Release: -O3
target_compile_options(${PROJECT_NAME} PRIVATE
$<$<CONFIG:Release>:-O3>
)
# Debug: -g -O0
target_compile_options(${PROJECT_NAME} PRIVATE
$<$<CONFIG:Debug>:-g -O0>
)
# Common warnings
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra)
elseif(COMPILER_CLANG_GNU)
# === Clang flags (Windows, GNU frontend) ===
# Release: -O3
target_compile_options(${PROJECT_NAME} PRIVATE
$<$<CONFIG:Release>:-O3>
)
# Debug: -g -O0
target_compile_options(${PROJECT_NAME} PRIVATE
$<$<CONFIG:Debug>:-g -O0>
)
# Common warnings
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra)
endif()
# Windows-specific libraries
target_link_libraries(${PROJECT_NAME} PRIVATE
kernel32
)
# Windows-specific defines
target_compile_definitions(${PROJECT_NAME} PRIVATE
WIN32_LEAN_AND_MEAN
_WIN32_WINNT=0x0A00 # Windows 10+
)
# Set output name with .exe
set_target_properties(${PROJECT_NAME} PROPERTIES
SUFFIX ".exe"
)
elseif(PLATFORM_LINUX)
# === Linux GCC/Clang flags ===
if(COMPILER_GCC OR COMPILER_CLANG_GNU)
# Release: -O3
target_compile_options(${PROJECT_NAME} PRIVATE
$<$<CONFIG:Release>:-O3>
)
# Debug: -g -O0
target_compile_options(${PROJECT_NAME} PRIVATE
$<$<CONFIG:Debug>:-g -O0>
)
# Common warnings
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra)
endif()
# Linux-specific libraries
find_package(Threads REQUIRED)
find_library(LIBURING_LIBRARY NAMES uring)
if(LIBURING_LIBRARY)
message(STATUS "Found liburing: ${LIBURING_LIBRARY}")
target_link_libraries(${PROJECT_NAME} PRIVATE
Threads::Threads
${LIBURING_LIBRARY}
)
else()
message(FATAL_ERROR "liburing not found! Install liburing-dev or equivalent")
endif()
# Linux-specific defines
target_compile_definitions(${PROJECT_NAME} PRIVATE
_GNU_SOURCE
)
endif()
# ---------------------------------------------------------------------------
# C Standard
# ---------------------------------------------------------------------------
set_target_properties(${PROJECT_NAME} PROPERTIES
C_STANDARD 11
C_STANDARD_REQUIRED ON
C_EXTENSIONS OFF
)
# ---------------------------------------------------------------------------
# Build Configurations
# ---------------------------------------------------------------------------
# Set default build type if not specified (matching your Release command)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release" CACHE STRING
"Choose the type of build: Release or Debug" FORCE)
message(STATUS "No build type specified, defaulting to Release")
endif()
# ---------------------------------------------------------------------------
# IDE Support
# ---------------------------------------------------------------------------
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# ---------------------------------------------------------------------------
# Info Target
# ---------------------------------------------------------------------------
add_custom_target(info
COMMAND ${CMAKE_COMMAND} -E echo "=== Build Configuration ==="
COMMAND ${CMAKE_COMMAND} -E echo "Project: ${PROJECT_NAME}"
COMMAND ${CMAKE_COMMAND} -E echo "Compiler: ${CMAKE_C_COMPILER} (${CMAKE_C_COMPILER_ID})"
COMMAND ${CMAKE_COMMAND} -E echo "Frontend: $<IF:$<BOOL:${COMPILER_CLANG_CL}>,clang-cl,GNU>"
COMMAND ${CMAKE_COMMAND} -E echo "Generator: ${CMAKE_GENERATOR}"
COMMAND ${CMAKE_COMMAND} -E echo "Build Type: ${CMAKE_BUILD_TYPE}"
COMMAND ${CMAKE_COMMAND} -E echo "Platform: ${PLATFORM_NAME}"
COMMAND ${CMAKE_COMMAND} -E echo "============================"
)
# ---------------------------------------------------------------------------
# Print final configuration
# ---------------------------------------------------------------------------
message(STATUS "----------------------------------------")
message(STATUS "Configuration Summary:")
message(STATUS " Compiler: ${CMAKE_C_COMPILER}")
message(STATUS " Build Type: ${CMAKE_BUILD_TYPE}")
message(STATUS " Generator: ${CMAKE_GENERATOR}")
message(STATUS " Platform: ${PLATFORM_NAME}")
message(STATUS "----------------------------------------")

View File

@@ -20,36 +20,55 @@ It is a high performance cross platform Windows and Linux compatible program, it
* Fallback to buffered I/O if there is errors in the IO Ring path. * Fallback to buffered I/O if there is errors in the IO Ring path.
# Building # Building
## Windows
### Release
**Note**: Make sur to use UCRT64 environment from MSYS2 instead of the standard MinGW environment. ## Windows
**Requirements**: Make sur to use UCRT64 environment from MSYS2 instead of the standard MinGW environment.
UCRT64 uses the modern Universal C Runtime (ucrtbase.dll), which supports the newest APIs, UCRT64 uses the modern Universal C Runtime (ucrtbase.dll), which supports the newest APIs,
the standard MSYS2 uses the legacy msvcrt.dll and does not support IO Ring. the standard MSYS2 uses the legacy msvcrt.dll and does not support IO Ring.
To install: To install:
pacman -S mingw-w64-ucrt-x86_64-gcc pacman -S mingw-w64-ucrt-x86_64-gcc
pacman -S mingw-w64-ucrt-x86_64-clang pacman -S mingw-w64-ucrt-x86_64-clang
pacman -S mingw-w64-ucrt-x86_64-cmake
pacman -Syu pacman -Syu
And add to path: And add to path:
C:\msys64\ucrt64\bin C:\msys64\ucrt64\bin
gcc -O3 file_hasher.c xxhash.c xxh_x86dispatch.c -o file_hasher ### Using a build system
clang -O3 file_hasher.c xxhash.c xxh_x86dispatch.c -o file_hasher | Command | Description|
| :--- | :--- |
| ./build.bat | Build Release with best available compiler |
| ./build.bat Debug | Build Debug |
| ./build.bat clean | Clean and build Release |
| ./build.bat Debug clean | Clean and build Debug |
### Release
gcc -O3 file_hasher.c xxhash.c xxh_x86dispatch.c -o filehasher
clang -O3 file_hasher.c xxhash.c xxh_x86dispatch.c -o filehasher
clang-cl /O2 file_hasher.c xxhash.c xxh_x86dispatch.c clang-cl /O2 file_hasher.c xxhash.c xxh_x86dispatch.c
### Debug ### Debug
gcc -g -O0 file_hasher.c xxhash.c xxh_x86dispatch.c -o file_hasher gcc -g -O0 file_hasher.c xxhash.c xxh_x86dispatch.c -o filehasher
clang -g -O0 file_hasher.c xxhash.c xxh_x86dispatch.c -o file_hasher clang -g -O0 file_hasher.c xxhash.c xxh_x86dispatch.c -o filehasher
clang-cl /Zi /Od file_hasher.c xxhash.c xxh_x86dispatch.c clang-cl /Zi /Od file_hasher.c xxhash.c xxh_x86dispatch.c
## Linux ## Linux
**Requirements**: GCC, CMake and Ninja
### Using a build system
| Command | Description|
| :--- | :--- |
| ./build.sh | Build Release with best available compiler |
| ./build.sh Debug | Build Debug |
| ./build.sh clean | Clean and build Release |
| ./build.sh Debug clean | Clean and build Debug |
### Release ### Release
gcc -O3 file_hasher.c xxhash.c xxh_x86dispatch.c -pthread -luring -o file_hasher gcc -O3 file_hasher.c xxhash.c xxh_x86dispatch.c -pthread -luring -o filehasher
clang -O3 file_hasher.c xxhash.c xxh_x86dispatch.c -pthread -luring -o file_hasher clang -O3 file_hasher.c xxhash.c xxh_x86dispatch.c -pthread -luring -o filehasher
### Debug ### Debug
gcc -g -O0 file_hasher.c xxhash.c xxh_x86dispatch.c -pthread -luring -o file_hasher gcc -g -O0 file_hasher.c xxhash.c xxh_x86dispatch.c -pthread -luring -o filehasher
clang -g -O0 file_hasher.c xxhash.c xxh_x86dispatch.c -pthread -luring -o file_hasher clang -g -O0 file_hasher.c xxhash.c xxh_x86dispatch.c -pthread -luring -o filehasher
# Notes about the IO Ring implementations # Notes about the IO Ring implementations
## IO Ring ## IO Ring

View File

@@ -83,8 +83,7 @@ u64 arena_pos_from_ptr(mem_arena *arena, void *ptr) {
void *arena_ptr_from_pos(mem_arena *arena, u64 global_pos) { void *arena_ptr_from_pos(mem_arena *arena, u64 global_pos) {
ASSERT(arena); ASSERT(arena);
ASSERT(global_pos >= 0); if (!arena) {
if (!arena || global_pos < 0) {
return NULL; return NULL;
} }
@@ -488,7 +487,6 @@ void *arena_free(mem_arena **arena_ptr, u8 **ptr, u64 size) { // mk free
Find owning block Find owning block
------------------------------------------------------------ */ ------------------------------------------------------------ */
mem_arena *selected = arena;
mem_arena *owner = arena_block_from_ptr(arena, *ptr); mem_arena *owner = arena_block_from_ptr(arena, *ptr);
ASSERT(owner); ASSERT(owner);
if (!owner) { if (!owner) {
@@ -500,7 +498,7 @@ void *arena_free(mem_arena **arena_ptr, u8 **ptr, u64 size) { // mk free
------------------------------------------------------------ */ ------------------------------------------------------------ */
u64 global_offset = arena_pos_from_ptr(arena, *ptr); u64 global_offset = arena_pos_from_ptr(arena, *ptr);
if (global_offset == -1) { if (global_offset == UINT64_MAX) {
return NULL; return NULL;
} }
@@ -607,9 +605,6 @@ void *arena_swapback_pop(mem_arena **arena_ptr, u64 index) { // mk swapback
fprintf(stderr, "ERROR: Swapback pop failed, index out of range"); fprintf(stderr, "ERROR: Swapback pop failed, index out of range");
return NULL; return NULL;
} }
u8 *owner_base = (u8 *)owner + ALIGN_UP_POW2(sizeof(mem_arena), owner->align);
u8 *arena_base = (u8 *)arena + ALIGN_UP_POW2(sizeof(mem_arena), arena->align);
u8 *dst = arena_ptr_from_index(arena, index); u8 *dst = arena_ptr_from_index(arena, index);
u8 *src = arena_ptr_from_index(arena, count); u8 *src = arena_ptr_from_index(arena, count);

48
base.h
View File

@@ -104,7 +104,9 @@ typedef double f64;
#define ASSERT(x) assert(x) #define ASSERT(x) assert(x)
#endif #endif
#define NDEBUG // Comment to enable asserts #ifndef NDEBUG
#define NDEBUG 1 // 0 to enable asserts
#endif
/* ------------------------------------------------------------ /* ------------------------------------------------------------
Some helper functions Some helper functions
@@ -130,9 +132,9 @@ static b32 plat_mem_commit(void *ptr, u64 size) {
return ret != NULL; return ret != NULL;
} }
static b32 plat_mem_decommit(void *ptr, u64 size) { // static b32 plat_mem_decommit(void *ptr, u64 size) { // Comment to prevent warning: unused function
return VirtualFree(ptr, size, MEM_DECOMMIT); // return VirtualFree(ptr, size, MEM_DECOMMIT);
} // }
static b32 plat_mem_release(void *ptr, u64 size) { static b32 plat_mem_release(void *ptr, u64 size) {
return VirtualFree(ptr, size, MEM_RELEASE); return VirtualFree(ptr, size, MEM_RELEASE);
@@ -152,21 +154,21 @@ static void plat_sem_wait(plat_sem *s) {
WaitForSingleObject(s->handle, INFINITE); WaitForSingleObject(s->handle, INFINITE);
} }
static b32 plat_sem_trywait(HANDLE sem) { // static b32 plat_sem_trywait(HANDLE sem) { // Comment to prevent warning: unused function
DWORD r = WaitForSingleObject(sem, 0); // DWORD r = WaitForSingleObject(sem, 0);
return r == WAIT_OBJECT_0; // return r == WAIT_OBJECT_0;
} // }
static void plat_sem_post(plat_sem *s, u32 count) { static void plat_sem_post(plat_sem *s, u32 count) {
ReleaseSemaphore(s->handle, count, NULL); ReleaseSemaphore(s->handle, count, NULL);
} }
static void plat_sem_destroy(plat_sem *s) { // static void plat_sem_destroy(plat_sem *s) { // Comment to prevent warning: unused function
if (s->handle) { // if (s->handle) {
CloseHandle(s->handle); // CloseHandle(s->handle);
s->handle = NULL; // s->handle = NULL;
} // }
} // }
// Sleep // Sleep
static void sleep_ms(int ms) { Sleep(ms); } static void sleep_ms(int ms) { Sleep(ms); }
@@ -197,13 +199,13 @@ static b32 plat_mem_commit(void *ptr, u64 size) {
return ret == 0; return ret == 0;
} }
static b32 plat_mem_decommit(void *ptr, u64 size) { // static b32 plat_mem_decommit(void *ptr, u64 size) { // Comment to prevent warning: unused function
i32 ret = mprotect(ptr, size, PROT_NONE); // i32 ret = mprotect(ptr, size, PROT_NONE);
if (ret != 0) // if (ret != 0)
return false; // return false;
ret = madvise(ptr, size, MADV_DONTNEED); // ret = madvise(ptr, size, MADV_DONTNEED);
return ret == 0; // return ret == 0;
} // }
static b32 plat_mem_release(void *ptr, u64 size) { static b32 plat_mem_release(void *ptr, u64 size) {
i32 ret = munmap(ptr, size); i32 ret = munmap(ptr, size);
@@ -226,7 +228,7 @@ static void plat_sem_wait(plat_sem *s) {
} }
} }
static b32 plat_sem_trywait(sem_t *sem) { return sem_trywait(sem) == 0; } // static b32 plat_sem_trywait(sem_t *sem) { return sem_trywait(sem) == 0; } // Comment to prevent warning: unused function
static void plat_sem_post(plat_sem *s, u32 count) { static void plat_sem_post(plat_sem *s, u32 count) {
for (u32 i = 0; i < count; i++) { for (u32 i = 0; i < count; i++) {
@@ -234,7 +236,7 @@ static void plat_sem_post(plat_sem *s, u32 count) {
} }
} }
static void plat_sem_destroy(plat_sem *s) { sem_destroy(&s->sem); } // static void plat_sem_destroy(plat_sem *s) { sem_destroy(&s->sem); } // Comment to prevent warning: unused function
// Sleep // Sleep
static void sleep_ms(int ms) { usleep(ms * 1000); } static void sleep_ms(int ms) { usleep(ms * 1000); }

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

120
build.bat Normal file
View File

@@ -0,0 +1,120 @@
@echo off
setlocal enabledelayedexpansion
:: ============================================================================
:: build.bat - Build script with compiler preference: clang-cl > gcc > clang
:: Usage: build [Release|Debug] [clean]
:: ============================================================================
set BUILD_TYPE=Release
set CLEAN_BUILD=0
:: Parse arguments
:parse_args
if "%~1"=="" goto :main
if /i "%~1"=="Release" (
set BUILD_TYPE=Release
shift
goto :parse_args
)
if /i "%~1"=="Debug" (
set BUILD_TYPE=Debug
shift
goto :parse_args
)
if /i "%~1"=="clean" (
set CLEAN_BUILD=1
shift
goto :parse_args
)
:: Unknown argument fallback (the *)
echo Unknown argument: %~1
echo Usage: .\%~nx0 [Release^|Debug] [clean]
exit /b 1
:main
set BUILD_DIR=build\windows\%BUILD_TYPE%
echo === Building filehasher (%BUILD_TYPE%) ===
:: Clean if requested
if %CLEAN_BUILD%==1 (
echo Cleaning...
if exist "%BUILD_DIR%" rmdir /s /q "%BUILD_DIR%" 2>nul
)
:: Create build dir
if not exist "%BUILD_DIR%" mkdir "%BUILD_DIR%"
pushd "%BUILD_DIR%"
:: Find compiler in preferred order
set CC=
where clang-cl >nul 2>&1
if !ERRORLEVEL! equ 0 (
echo Compiler: clang-cl ^(preferred^)
set "CC=-DCMAKE_C_COMPILER=clang-cl"
goto :find_generator
)
where gcc >nul 2>&1
if !ERRORLEVEL! equ 0 (
echo Compiler: gcc ^(fallback^)
set "CC=-DCMAKE_C_COMPILER=gcc"
goto :find_generator
)
where clang >nul 2>&1
if !ERRORLEVEL! equ 0 (
echo Compiler: clang ^(last resort^)
set "CC=-DCMAKE_C_COMPILER=clang"
goto :find_generator
)
echo ERROR: No suitable compiler found! (clang-cl, gcc, or clang required)
popd
exit /b 1
:find_generator
:: Find Ninja for build system
set GEN=
where ninja >nul 2>&1
if !ERRORLEVEL! equ 0 (
echo Generator: Ninja
set "GEN=-G Ninja"
) else (
echo Generator: Default
)
:: Configure
echo.
echo Configuring CMake...
set CMD=cmake ../../.. %GEN% %CC% -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
echo !CMD!
!CMD!
if !ERRORLEVEL! neq 0 (echo ERROR: Configuration failed & popd & exit /b 1)
:: Build
echo.
echo Building...
cmake --build . --config %BUILD_TYPE%
if !ERRORLEVEL! neq 0 (echo ERROR: Build failed & popd & exit /b 1)
:: Check if compile_commands.json exists in the current build directory
if exist "compile_commands.json" (
echo.
echo clangd: compile_commands.json generated
:: Copy from current build dir up two levels to the project root
copy /Y "compile_commands.json" "..\..\..\compile_commands.json" >nul 2>&1
if !ERRORLEVEL! equ 0 (
echo clangd: Copied to project root
) else (
echo clangd: Could not copy to project root
)
)
popd
echo.
echo === Build Complete ===
echo Executable: %BUILD_DIR%\filehasher.exe

255
build.sh Normal file
View File

@@ -0,0 +1,255 @@
#!/usr/bin/env bash
# ============================================================================
# build.sh - Build script for filehasher (Linux)
# Usage: ./build.sh [Release|Debug] [clean]
#
# Compiler preference: gcc > clang
# Build system: Ninja (fallback to Make)
# ============================================================================
set -euo pipefail
# ---------------------------------------------------------------------------
# Colors
# ---------------------------------------------------------------------------
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly CYAN='\033[0;36m'
readonly NC='\033[0m'
# ---------------------------------------------------------------------------
# Default values
# ---------------------------------------------------------------------------
BUILD_TYPE="Release"
CLEAN_BUILD=0
# ---------------------------------------------------------------------------
# Parse arguments
# ---------------------------------------------------------------------------
while [[ $# -gt 0 ]]; do
case "$1" in
Release|release)
BUILD_TYPE="Release"
shift
;;
Debug|debug)
BUILD_TYPE="Debug"
shift
;;
clean|-clean|--clean)
CLEAN_BUILD=1
shift
;;
*)
echo -e "${RED}Unknown argument: $1${NC}"
echo "Usage: $0 [Release|Debug] [clean]"
exit 1
;;
esac
done
# ---------------------------------------------------------------------------
# Setup
# ---------------------------------------------------------------------------
readonly BUILD_DIR="build/linux/${BUILD_TYPE}"
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
echo -e "${GREEN}=== Building filehasher (${BUILD_TYPE}) ===${NC}"
echo "Project: ${SCRIPT_DIR}"
# ---------------------------------------------------------------------------
# Clean if requested
# ---------------------------------------------------------------------------
if [[ $CLEAN_BUILD -eq 1 ]]; then
echo -e "${YELLOW}Cleaning build directory...${NC}"
rm -rf "${BUILD_DIR}"
echo
fi
# ---------------------------------------------------------------------------
# Create build directory
# ---------------------------------------------------------------------------
mkdir -p "${BUILD_DIR}"
cd "${BUILD_DIR}"
# ---------------------------------------------------------------------------
# Find compiler (prefer gcc, fallback to clang)
# ---------------------------------------------------------------------------
echo -e "${YELLOW}Detecting compiler...${NC}"
CC_BINARY=""
CC_NAME=""
if command -v gcc &> /dev/null; then
CC_BINARY="gcc"
CC_VERSION=$(gcc --version | head -n1)
CC_NAME="GCC (${CC_VERSION})"
echo -e " ${GREEN}[OK]${NC} Found GCC (preferred): ${CC_VERSION}"
elif command -v clang &> /dev/null; then
CC_BINARY="clang"
CC_VERSION=$(clang --version | head -n1)
CC_NAME="Clang (${CC_VERSION})"
echo -e " ${YELLOW}[OK]${NC} Found Clang (fallback): ${CC_VERSION}"
else
echo -e "${RED}[FAIL] No suitable compiler found!${NC}"
echo "Please install gcc or clang:"
echo " Ubuntu/Debian: sudo apt install build-essential"
echo " Fedora/RHEL: sudo dnf install gcc"
echo " Arch: sudo pacman -S gcc"
exit 1
fi
# ---------------------------------------------------------------------------
# Check dependencies
# ---------------------------------------------------------------------------
echo -e "${YELLOW}Checking dependencies...${NC}"
# Check for liburing
HAVE_LIBURING=0
if ldconfig -p | grep -q liburing 2>/dev/null; then
HAVE_LIBURING=1
echo -e " ${GREEN}[OK]${NC} Found liburing"
elif pkg-config --exists liburing 2>/dev/null; then
HAVE_LIBURING=1
echo -e " ${GREEN}[OK]${NC} Found liburing (pkg-config)"
elif [[ -f /usr/lib/liburing.so ]] || [[ -f /usr/lib64/liburing.so ]] || [[ -f /usr/local/lib/liburing.so ]]; then
HAVE_LIBURING=1
echo -e " ${GREEN}[OK]${NC} Found liburing (manual detection)"
else
echo -e "${RED}[FAIL] liburing not found!${NC}"
echo "Please install liburing-dev:"
echo " Ubuntu/Debian: sudo apt install liburing-dev"
echo " Fedora/RHEL: sudo dnf install liburing-devel"
echo " Arch: sudo pacman -S liburing"
exit 1
fi
# Check for pthreads
# Check if pthreads is available (either in ldconfig or merged into libc)
if ldconfig -p | grep -q libpthread 2>/dev/null || ldd --version | grep -qP '2\.(3[4-9]|[4-9][0-9])'; then
echo -e " ${GREEN}[OK]${NC} Found pthreads (merged or standalone)"
else
echo -e " ${YELLOW}[NOTE]${NC} pthreads not found, attempting link"
fi
echo
# ---------------------------------------------------------------------------
# Find build system (prefer ninja)
# ---------------------------------------------------------------------------
echo -e "${YELLOW}Selecting build system...${NC}"
GENERATOR=""
GENERATOR_NAME=""
if command -v ninja &> /dev/null; then
GENERATOR="Ninja"
GENERATOR_NAME="Ninja"
echo -e " ${GREEN}[OK]${NC} Using Ninja"
elif command -v make &> /dev/null; then
GENERATOR="Unix Makefiles"
GENERATOR_NAME="Make"
echo -e " ${YELLOW}[OK]${NC} Ninja not found, using Make"
else
echo -e "${RED}[FAIL] No build system found!${NC}"
echo "Please install ninja or make:"
echo " Ubuntu/Debian: sudo apt install ninja-build"
echo " Fedora/RHEL: sudo dnf install ninja-build"
echo " Arch: sudo pacman -S ninja"
exit 1
fi
echo
# ---------------------------------------------------------------------------
# Configure
# ---------------------------------------------------------------------------
echo -e "${YELLOW}Configuring CMake...${NC}"
echo -e " Build type: ${BUILD_TYPE}"
echo -e " Compiler: ${CC_NAME}"
echo -e " Generator: ${GENERATOR_NAME}"
cmake "${SCRIPT_DIR}" \
-G "${GENERATOR}" \
-DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \
-DCMAKE_C_COMPILER="${CC_BINARY}" \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
if [[ $? -ne 0 ]]; then
echo -e "${RED}CMake configuration failed!${NC}"
exit 1
fi
echo -e "${GREEN}Configuration successful!${NC}"
echo
# ---------------------------------------------------------------------------
# Build
# ---------------------------------------------------------------------------
echo -e "${YELLOW}Building...${NC}"
# Get number of CPU cores
if command -v nproc &> /dev/null; then
CORES=$(nproc)
else
CORES=4
fi
cmake --build . --config "${BUILD_TYPE}" --parallel "${CORES}"
if [[ $? -ne 0 ]]; then
echo -e "${RED}Build failed!${NC}"
exit 1
fi
echo -e "${GREEN}Build successful!${NC}"
echo
# ---------------------------------------------------------------------------
# Verify output
# ---------------------------------------------------------------------------
cd "${SCRIPT_DIR}"
if [[ -f "${BUILD_DIR}/filehasher" ]]; then
echo -e "${GREEN}Executable: ${BUILD_DIR}/filehasher${NC}"
if command -v file &> /dev/null; then
echo -e " Type: $(file -b ${BUILD_DIR}/filehasher)"
fi
if command -v du &> /dev/null; then
echo -e " Size: $(du -h ${BUILD_DIR}/filehasher | cut -f1)"
fi
elif [[ -f "${BUILD_DIR}/filehasher.exe" ]]; then
echo -e "${GREEN}Executable: ${BUILD_DIR}/filehasher.exe${NC}"
else
echo -e "${YELLOW}Note: Could not locate executable${NC}"
echo "Checking build directory:"
find "${BUILD_DIR}" -type f -executable 2>/dev/null || echo " No executables found"
fi
# ---------------------------------------------------------------------------
# Summary
# ---------------------------------------------------------------------------
echo
echo -e "${CYAN}=== Build Complete ===${NC}"
echo
echo -e "${YELLOW}Build Information:${NC}"
echo -e " Configuration: ${BUILD_TYPE}"
echo -e " Compiler: ${CC_NAME}"
echo -e " Generator: ${GENERATOR_NAME}"
echo -e " Output: ${BUILD_DIR}/"
# ---------------------------------------------------------------------------
# Copy compile_commands.json for clangd
# ---------------------------------------------------------------------------
if [[ -f "${BUILD_DIR}/compile_commands.json" ]]; then
echo -e " clangd: compile_commands.json generated"
# Always copy the latest version
cp "${BUILD_DIR}/compile_commands.json" "${SCRIPT_DIR}/compile_commands.json"
echo -e " clangd: Copied to project root"
fi
echo
echo -e "${GREEN}Ready to run: ./${BUILD_DIR}/filehasher${NC}"

View File

@@ -77,14 +77,14 @@ int main(int argc, char **argv) {
// Detect hardware // Detect hardware
// ------------------------------- // -------------------------------
// --- Windows: detect PHYSICAL cores (not logical threads) --- // --- Windows: detect PHYSICAL cores (not logical threads) ---
uint32_t cpu_cores = platform_physical_cores(); uint8_t cpu_cores = platform_physical_cores();
// Logical threads = CPU cores * 2 // Logical threads = CPU cores * 2
uint32_t cpu_threads = cpu_cores * 2; uint8_t cpu_threads = cpu_cores * 2;
#if MULTI_THREADING #if MULTI_THREADING
uint32_t num_scan_threads = cpu_threads; uint8_t num_scan_threads = cpu_threads;
uint32_t num_hash_threads = cpu_threads; uint8_t num_hash_threads = cpu_threads;
printf("%d cores %d threads CPU detected with %s instruction set\n" printf("%d cores %d threads CPU detected with %s instruction set\n"
"Starting thread pool: %d scanning and %d hashing threads\n", "Starting thread pool: %d scanning and %d hashing threads\n",
@@ -123,7 +123,7 @@ int main(int argc, char **argv) {
Thread *hash_threads = Thread *hash_threads =
arena_push(&gp_arena, sizeof(Thread) * num_hash_threads, true); arena_push(&gp_arena, sizeof(Thread) * num_hash_threads, true);
for (size_t i = 0; i < num_hash_threads; ++i) { for (uint8_t i = 0; i < num_hash_threads; ++i) {
workers[i].arena = arena_create(&params); workers[i].arena = arena_create(&params);
workers[i].file_queue = &file_queue; workers[i].file_queue = &file_queue;
@@ -135,7 +135,7 @@ int main(int argc, char **argv) {
0) 0)
#endif #endif
{ {
fprintf(stderr, "Failed to create hash thread %zu\n", i); fprintf(stderr, "Failed to create hash thread %d\n", i);
exit(1); exit(1);
} }
} }
@@ -153,7 +153,7 @@ int main(int argc, char **argv) {
Thread *scan_threads = Thread *scan_threads =
arena_push(&gp_arena, sizeof(Thread) * num_scan_threads, true); arena_push(&gp_arena, sizeof(Thread) * num_scan_threads, true);
for (size_t i = 0; i < num_scan_threads; i++) { for (uint8_t i = 0; i < num_scan_threads; i++) {
scanners[i].num_threads = num_scan_threads; scanners[i].num_threads = num_scan_threads;
scanners[i].path_arena = arena_create(&params); scanners[i].path_arena = arena_create(&params);
scanners[i].meta_arena = arena_create(&params); scanners[i].meta_arena = arena_create(&params);
@@ -162,7 +162,7 @@ int main(int argc, char **argv) {
if (thread_create(&scan_threads[i], (ThreadFunc)scan_worker, if (thread_create(&scan_threads[i], (ThreadFunc)scan_worker,
&scanners[i]) != 0) { &scanners[i]) != 0) {
fprintf(stderr, "Failed to create scan thread %zu\n", i); fprintf(stderr, "Failed to create scan thread %d\n", i);
exit(1); exit(1);
} }
} }
@@ -178,7 +178,7 @@ int main(int argc, char **argv) {
// Stop scan threads // Stop scan threads
thread_wait_multiple(scan_threads, num_scan_threads); thread_wait_multiple(scan_threads, num_scan_threads);
for (size_t i = 0; i < num_scan_threads; ++i) { for (uint8_t i = 0; i < num_scan_threads; ++i) {
thread_close(&scan_threads[i]); thread_close(&scan_threads[i]);
} }
@@ -205,7 +205,7 @@ int main(int argc, char **argv) {
// Stop hashing threads // Stop hashing threads
thread_wait_multiple(hash_threads, num_hash_threads); thread_wait_multiple(hash_threads, num_hash_threads);
for (size_t i = 0; i < num_hash_threads; ++i) { for (uint8_t i = 0; i < num_hash_threads; ++i) {
thread_close(&hash_threads[i]); thread_close(&hash_threads[i]);
} }
@@ -222,7 +222,7 @@ int main(int argc, char **argv) {
FILE *f = fopen(FILE_HASHES_TXT, "wb"); FILE *f = fopen(FILE_HASHES_TXT, "wb");
for (int i = 0; i < num_hash_threads; i++) { for (uint8_t i = 0; i < num_hash_threads; i++) {
mem_arena *arena = workers[i].arena; mem_arena *arena = workers[i].arena;
u8 *arena_base = u8 *arena_base =
(u8 *)arena + ALIGN_UP_POW2(sizeof(mem_arena), arena->align); (u8 *)arena + ALIGN_UP_POW2(sizeof(mem_arena), arena->align);

View File

@@ -306,21 +306,21 @@ static void mpmc_task_done(MPMCQueue *q, u8 consumer_count) {
/* ----------------------------------------------------------- */ /* ----------------------------------------------------------- */
/* MPMC Cleanup */ /* MPMC Cleanup */
/* ----------------------------------------------------------- */ /* ----------------------------------------------------------- */
static void mpmc_finish(MPMCQueue *q) { // static void mpmc_finish(MPMCQueue *q) { // Comment to prevent warning: unused function
if (!q) // if (!q)
return; // return;
//
if (q->slots) { // if (q->slots) {
plat_mem_release(q->slots, 0); // plat_mem_release(q->slots, 0);
q->slots = NULL; // q->slots = NULL;
} // }
//
plat_sem_destroy(&q->items_sem); // plat_sem_destroy(&q->items_sem);
//
q->capacity = 0; // q->capacity = 0;
q->mask = 0; // q->mask = 0;
//
atomic_store_explicit(&q->head, 0, memory_order_relaxed); // atomic_store_explicit(&q->head, 0, memory_order_relaxed);
atomic_store_explicit(&q->tail, 0, memory_order_relaxed); // atomic_store_explicit(&q->tail, 0, memory_order_relaxed);
atomic_store_explicit(&q->committed, 0, memory_order_relaxed); // atomic_store_explicit(&q->committed, 0, memory_order_relaxed);
} // }

View File

@@ -247,13 +247,6 @@ typedef struct {
void *arg; void *arg;
} ThreadWrapper; } ThreadWrapper;
static void *thread_start_routine(void *arg) {
ThreadWrapper *wrapper = (ThreadWrapper *)arg;
void *result = wrapper->func(wrapper->arg);
free(wrapper);
return result;
}
// Thread creation function // Thread creation function
static int thread_create(Thread *thread, ThreadFunc func, void *arg) { static int thread_create(Thread *thread, ThreadFunc func, void *arg) {
int ret = pthread_create(&thread->handle, NULL, func, arg); int ret = pthread_create(&thread->handle, NULL, func, arg);
@@ -1200,9 +1193,9 @@ static int close_ioring(ThreadIoContext *thread_ctx) {
(IORING_BUFFER_INFO) { .Address = (a), .Length = (uint32_t)(l) } (IORING_BUFFER_INFO) { .Address = (a), .Length = (uint32_t)(l) }
static int ioring_submit(ThreadIoContext *thread_ctx, uint32_t wait_count, static int ioring_submit(ThreadIoContext *thread_ctx, uint32_t wait_count,
uint32_t timeout_ms, uint32_t *submitted) { uint32_t *submitted) {
HRESULT hr = SubmitIoRing(thread_ctx->ring, 0, timeout_ms, submitted); HRESULT hr = SubmitIoRing(thread_ctx->ring, 0, SUBMIT_TIMEOUT_MS, submitted);
// HRESULT hr = SubmitIoRing(ring, wait_count, timeout_ms, submitted); // HRESULT hr = SubmitIoRing(ring, wait_count, SUBMIT_TIMEOUT_MS, submitted);
// The wait_count in windows is not implemented yet, so we wait with a // The wait_count in windows is not implemented yet, so we wait with a
// completion event for a single completion // completion event for a single completion
@@ -1214,7 +1207,6 @@ static int ioring_submit(ThreadIoContext *thread_ctx, uint32_t wait_count,
} }
static void ioring_register_buffers(ThreadIoContext *thread_ctx, static void ioring_register_buffers(ThreadIoContext *thread_ctx,
uint32_t num_buffers,
IORING_BUFFER_INFO *buf_info) { IORING_BUFFER_INFO *buf_info) {
HRESULT hr = BuildIoRingRegisterBuffers( HRESULT hr = BuildIoRingRegisterBuffers(
@@ -1230,7 +1222,7 @@ static void ioring_register_buffers(ThreadIoContext *thread_ctx,
error_msg, (unsigned int)hr); error_msg, (unsigned int)hr);
} }
// Submit registration // Submit registration
ioring_submit(thread_ctx, 0, 0, NULL); ioring_submit(thread_ctx, 0, NULL);
} }
#if USE_REGISTERED_FILES #if USE_REGISTERED_FILES
@@ -1429,11 +1421,10 @@ static int close_ioring(ThreadIoContext *thread_ctx) {
(IORING_BUFFER_INFO) { .iov_base = (a), .iov_len = (size_t)(l) } (IORING_BUFFER_INFO) { .iov_base = (a), .iov_len = (size_t)(l) }
static void ioring_register_buffers(ThreadIoContext *thread_ctx, static void ioring_register_buffers(ThreadIoContext *thread_ctx,
uint32_t num_buffers,
IORING_BUFFER_INFO *buf_info) { IORING_BUFFER_INFO *buf_info) {
int ret = io_uring_register_buffers(&((IoUring *)thread_ctx->ring)->ring, int ret = io_uring_register_buffers(&((IoUring *)thread_ctx->ring)->ring,
buf_info, num_buffers); buf_info, NUM_BUFFERS_PER_THREAD);
if (ret < 0) { if (ret < 0) {
if (ret == -ENOMEM) { if (ret == -ENOMEM) {
@@ -1540,7 +1531,7 @@ static int ioring_build_read(ThreadIoContext *thread_ctx,
} }
static int ioring_submit(ThreadIoContext *thread_ctx, uint32_t wait_count, static int ioring_submit(ThreadIoContext *thread_ctx, uint32_t wait_count,
uint32_t timeout_ms, uint32_t *submitted) { uint32_t *submitted) {
int ret; int ret;
if (wait_count > 0) { if (wait_count > 0) {
@@ -1745,7 +1736,7 @@ static ThreadIoContext *ioring_init_thread(void) {
thread_ctx->free_count = NUM_BUFFERS_PER_THREAD; thread_ctx->free_count = NUM_BUFFERS_PER_THREAD;
// Register buffers // Register buffers
ioring_register_buffers(thread_ctx, NUM_BUFFERS_PER_THREAD, buf_info); ioring_register_buffers(thread_ctx, buf_info);
#if USE_REGISTERED_FILES #if USE_REGISTERED_FILES
ioring_register_files(thread_ctx); ioring_register_files(thread_ctx);
@@ -1799,7 +1790,7 @@ static void return_buffer(ThreadIoContext *ctx, IoBuffer *buf) {
} }
// -------------------------- Process completions --------------------------- // -------------------------- Process completions ---------------------------
static void process_completions(ThreadIoContext *thread_ctx, FileQueue *fq) { static void process_completions(ThreadIoContext *thread_ctx) {
IoRingCQE cqe; IoRingCQE cqe;
while (ioring_pop_completion(thread_ctx->ring, &cqe) == 1) { while (ioring_pop_completion(thread_ctx->ring, &cqe) == 1) {
@@ -2095,10 +2086,10 @@ static THREAD_RETURN hash_worker_ioring(void *arg) {
wait_count = MIN(thread_ctx->num_submissions, NUM_BUFFERS_PER_THREAD - 6); wait_count = MIN(thread_ctx->num_submissions, NUM_BUFFERS_PER_THREAD - 6);
submitted = 0; submitted = 0;
ioring_submit(thread_ctx, wait_count, 0, &submitted); ioring_submit(thread_ctx, wait_count, &submitted);
// Process completions // Process completions
process_completions(thread_ctx, &fq); process_completions(thread_ctx);
#if IORING_DEBUG_STATS #if IORING_DEBUG_STATS
printf( printf(