From e44d0bff0d666e4fec2e1e25b278b6158f7ba9db Mon Sep 17 00:00:00 2001 From: Mikhail Romanko Date: Sat, 8 Aug 2026 20:33:32 +0300 Subject: [PATCH] Initial commit --- .github/workflows/ci.yaml | 79 +++++++++++++++++++++++++++++++++++++ .gitignore | 68 ++++++++++++++++++++++++++++++++ CMakeLists.txt | 40 +++++++++++++++++++ CgeTimer.h | 10 +++++ LICENSE | 12 ++++++ Makefile.lint | 29 ++++++++++++++ Makefile.mingw | 23 +++++++++++ Makefile.posix | 33 ++++++++++++++++ Makefile.win32 | 23 +++++++++++ README.md | 4 ++ posix/Timer.c | 82 +++++++++++++++++++++++++++++++++++++++ win32/Timer.c | 71 +++++++++++++++++++++++++++++++++ 12 files changed, 474 insertions(+) create mode 100644 .github/workflows/ci.yaml create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 CgeTimer.h create mode 100644 LICENSE create mode 100644 Makefile.lint create mode 100644 Makefile.mingw create mode 100644 Makefile.posix create mode 100644 Makefile.win32 create mode 100644 README.md create mode 100644 posix/Timer.c create mode 100644 win32/Timer.c diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..369e537 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,79 @@ +name: CI + +on: + push: + branches: [trunk] + pull_request: + branches: [trunk] + +jobs: + build-and-analyze: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install tools + run: | + sudo apt-get update -qq + sudo apt-get install -y build-essential clang clang-tools clang-tidy cppcheck + + - name: Build with GCC + run: | + make -f Makefile.posix clean + make -f Makefile.posix CC=gcc CFLAGS="-std=c99 -Wall -Wextra -Wpedantic -Werror -O2" + + - name: Build with Clang + run: | + make -f Makefile.posix clean + make -f Makefile.posix CC=clang CFLAGS="-std=c99 -Wall -Wextra -Wpedantic -Werror -O2" + + - name: Linting checks + run: | + echo "Running all linting checks..." + FAILED=0 + + # cppcheck + echo "=== Check 1/4: cppcheck ===" + if make -f Makefile.lint cppcheck; then + echo "cppcheck: PASSED" + else + echo "cppcheck: FAILED" + FAILED=1 + fi + + # clang-tidy + echo "=== Check 2/4: clang-tidy ===" + if make -f Makefile.lint clang-tidy; then + echo "clang-tidy: PASSED" + else + echo "clang-tidy: FAILED" + FAILED=1 + fi + + # scan-build + echo "=== Check 3/4: scan-build ===" + if make -f Makefile.lint scan-build; then + echo "scan-build: PASSED" + else + echo "scan-build: FAILED" + FAILED=1 + fi + + # security-check + echo "=== Check 4/4: security-check ===" + if make -f Makefile.lint security-check; then + echo "security-check: PASSED" + else + echo "security-check: FAILED" + FAILED=1 + fi + + # Final result + if [ $FAILED -ne 0 ]; then + echo "One or more linting checks failed." + exit 1 + else + echo "All linting checks passed." + fi \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f765426 --- /dev/null +++ b/.gitignore @@ -0,0 +1,68 @@ +# ---> C +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +# ---> CMake +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps +CMakeUserPresets.json + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..b66c034 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.10) +project(CgeNet LANGUAGES C) + +set(CMAKE_C_STANDARD 99) +set(CMAKE_C_STANDARD_REQUIRED ON) + +set(SOURCES +) + +set(SOURCES_WIN32 + src/win32/Timer.c +) + +set(SOURCES_POSIX + src/posix/Timer.c +) + +set(HEADERS + CgeTimer.h +) + +if(WIN32) + message(STATUS "Platform - Win32") + list(APPEND SOURCES ${SOURCES_WIN32}) +elseif(UNIX) + message(STATUS "Platform: Unix (Linux/BSD/MacOS X)") + list(APPEND SOURCES ${SOURCES_POSIX}) +endif() + +add_library(CgeTimer STATIC ${SOURCES} ${HEADERS}) + +target_include_directories(CgeTimer PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + +install(TARGETS CgeTimer + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin +) + +install(FILES ${HEADERS} DESTINATION include) diff --git a/CgeTimer.h b/CgeTimer.h new file mode 100644 index 0000000..ba15f6d --- /dev/null +++ b/CgeTimer.h @@ -0,0 +1,10 @@ +#ifndef CGE_TIMER_H +#define CGE_TIMER_H + +#include + +int CgeTimerIsMonotonic(void); +uint64_t CgeTimerNanoseconds(void); +uint64_t CgeTimerMilliseconds(void); + +#endif /* CGE_TIMER_H */ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3d44ffd --- /dev/null +++ b/LICENSE @@ -0,0 +1,12 @@ +Copyright (C) 2026 by blankhex me@blankhex.com + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. diff --git a/Makefile.lint b/Makefile.lint new file mode 100644 index 0000000..cac9110 --- /dev/null +++ b/Makefile.lint @@ -0,0 +1,29 @@ +# Makefile.lint - Individual linting targets + +.PHONY: all +all: cppcheck clang-tidy scan-build security-check + @echo "All linting scheduled" + +.PHONY: cppcheck +cppcheck: + @echo "Running cppcheck..." + @cppcheck --enable=warning,performance,portability --std=c99 --quiet -Iinclude -I. . + +.PHONY: clang-tidy +clang-tidy: + @echo "Running clang-tidy..." + @for file in $$(find . -name "*.c" -type f -not -path "*/win32/*"); do \ + clang-tidy "$$file" -- -I. -Iinclude -std=c99; \ + done + +.PHONY: scan-build +scan-build: + @echo "Running scan-build..." + @scan-build --status-bugs make -f Makefile.posix clean all CC=clang + +.PHONY: security-check +security-check: + @echo "Scanning for unsafe C functions..." + @grep -rnE '\b(strcpy|strcat|sprintf|gets|scanf|sscanf|realpath|mktemp|tempnam|tmpnam|getwd|getlogin)\b' \ + . --include="*.c" --include="*.h" | grep -v "^Binary file" && \ + exit 1 || exit 0 \ No newline at end of file diff --git a/Makefile.mingw b/Makefile.mingw new file mode 100644 index 0000000..08f18ac --- /dev/null +++ b/Makefile.mingw @@ -0,0 +1,23 @@ +# MinGW Makefile for CgeTimer + +CC = gcc +AR = ar +CFLAGS = -std=c99 -O2 -Wall -Wextra +ARFLAGS = rcs +TARGET = libCgeTimer.a + +SOURCES = win32/Timer.c +OBJECTS = $(SOURCES:.c=.o) + +.PHONY: all clean + +all: $(TARGET) + +$(TARGET): $(OBJECTS) + $(AR) $(ARFLAGS) $@ $^ + +%.o: %.c CgeTimer.h + $(CC) $(CFLAGS) -c $< -o $@ + +clean: + del $(OBJECTS) $(TARGET) 2>nul || exit 0 diff --git a/Makefile.posix b/Makefile.posix new file mode 100644 index 0000000..ee08122 --- /dev/null +++ b/Makefile.posix @@ -0,0 +1,33 @@ +# POSIX Makefile for CgeTimer + +CC = gcc +AR = ar +CFLAGS = -std=c99 -O2 -Wall -Wextra -fPIC +ARFLAGS = rcs +TARGET = libCgeTimer.a + +SOURCES = posix/Timer.c +OBJECTS = $(SOURCES:.c=.o) + +.PHONY: all clean install + +all: $(TARGET) + +$(TARGET): $(OBJECTS) + $(AR) $(ARFLAGS) $@ $^ + +%.o: %.c CgeTimer.h + $(CC) $(CFLAGS) -c $< -o $@ + +clean: + rm -f $(OBJECTS) $(TARGET) + +install: $(TARGET) + cp $(TARGET) /usr/local/lib/ + cp CgeTimer.h /usr/local/include/ + ldconfig || echo "Run ldconfig manually if needed" + +uninstall: + rm -f /usr/local/lib/libCgeTimer.a + rm -f /usr/local/include/CgeTimer.h + ldconfig || true diff --git a/Makefile.win32 b/Makefile.win32 new file mode 100644 index 0000000..3856a06 --- /dev/null +++ b/Makefile.win32 @@ -0,0 +1,23 @@ +# Makefile.win32 for MSVC (NMake) +# Usage: Open "x86 Native Tools Command Prompt", then: +# nmake -f Makefile.win32 + +CC = cl +LIB = lib +CFLAGS = /c /nologo /W3 /O2 +LIBFLAGS = /nologo +TARGET = CgeTimer.lib + +SOURCES = win32/Timer.c +OBJECTS = $(SOURCES:.c=.obj) + +$(TARGET): $(OBJECTS) + $(LIB) $(LIBFLAGS) /OUT:$(TARGET) $(OBJECTS) + +{.}.c{}.obj: + $(CC) $(CFLAGS) /Fo$@ $< + +clean: + del $(OBJECTS) $(TARGET) 2>nul + +.PHONY: clean diff --git a/README.md b/README.md new file mode 100644 index 0000000..db587ec --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# CgeTimer - Timer library for C + +TODO + diff --git a/posix/Timer.c b/posix/Timer.c new file mode 100644 index 0000000..235bc52 --- /dev/null +++ b/posix/Timer.c @@ -0,0 +1,82 @@ +#define _POSIX_C_SOURCE 200112L +#include "../CgeTimer.h" +#include +#include +#include +#include + +enum Type { + TYPE_UNDEFINED, + TYPE_OLD_REALTIME, + TYPE_NEW_REALTIME, + TYPE_MONOTONIC +}; + +static int timerType = TYPE_UNDEFINED; + +static int checkAvailableTimer(void) { +#if (_POSIX_TIMERS > 0) + struct timespec ts; +#endif + + if (timerType != TYPE_UNDEFINED) + return timerType; + +#if (_POSIX_TIMERS > 0) + if (!clock_gettime(CLOCK_MONOTONIC, &ts)) { + timerType = TYPE_MONOTONIC; + } else if (!clock_gettime(CLOCK_REALTIME, &ts)) { + timerType = TYPE_NEW_REALTIME; + } else +#endif + timerType = TYPE_OLD_REALTIME; + return timerType; +} + +static void getTimer(uint64_t *sec, uint64_t *nsec) { +#if (_POSIX_TIMERS > 0) + struct timespec ts; +#endif + struct timeval tv; + + switch (checkAvailableTimer()) { +#if (_POSIX_TIMERS > 0) + case TYPE_MONOTONIC: + clock_gettime(CLOCK_MONOTONIC, &ts); + *sec = ts.tv_sec; + *nsec = ts.tv_nsec; + break; + + case TYPE_NEW_REALTIME: + clock_gettime(CLOCK_REALTIME, &ts); + *sec = ts.tv_sec; + *nsec = ts.tv_nsec; + break; +#endif + + default: + case TYPE_OLD_REALTIME: + gettimeofday(&tv, NULL); + *sec = tv.tv_sec; + *nsec = tv.tv_usec * (uint64_t)1000; + break; + } +} + +int CgeTimerIsMonotonic(void) { + return checkAvailableTimer() == TYPE_MONOTONIC; +} + +uint64_t BH_TimerMilliseconds(void) { + uint64_t newSec, newNsec; + + getTimer(&newSec, &newNsec); + return (newSec * 1000u) + (newNsec / 1000000ul); +} + +uint64_t BH_TimerNanoseconds(void) { + uint64_t newSec, newNsec; + + getTimer(&newSec, &newNsec); + return (newSec * 1000000000ul) + newNsec; +} diff --git a/win32/Timer.c b/win32/Timer.c new file mode 100644 index 0000000..9676e5d --- /dev/null +++ b/win32/Timer.c @@ -0,0 +1,71 @@ +#define WIN32_LEAN_AND_MEAN +#include "../CgeTimer.h" +#include +#include + +enum Type { + TYPE_UNDEFINED, + TYPE_TICKS, + TYPE_COUNTER, +}; + +static int timerType = TYPE_UNDEFINED; +static LARGE_INTEGER timerFrequency; + +static int checkAvailableTimer(void) { + LARGE_INTEGER dummy; + + if (timerType != TYPE_UNDEFINED) + return timerType; + + if (QueryPerformanceFrequency(&timerFrequency)) { + if (QueryPerformanceCounter(&dummy)) { + timerType = TYPE_COUNTER; + } else { + timerType = TYPE_TICKS; + } + } else { + timerType = TYPE_TICKS; + } + + return timerType; +} + +static void getTimer(uint64_t *sec, uint64_t *nsec) { + LARGE_INTEGER newCount; + uint64_t newTicks; + + switch (checkAvailableTimer()) + { + case TYPE_COUNTER: + QueryPerformanceCounter(&newCount); + *sec = newCount.QuadPart / timerFrequency->QuadPart; + *nsec = (newCount.QuadPart - *sec * timerFrequency->QuadPart) * 1000000000ul / timerFrequency->QuadPart; + break; + + default: + case TYPE_TICKS: + newTicks = GetTickCount64(); + *sec = newTicks / 1000; + *nsec = (newTicks - *sec * 1000) * 1000000ul; + break; + } +} + +int CgeTimerIsMonotonic(void) { + return checkAvailableTimer() == TYPE_COUNTER; +} + +uint64_t BH_TimerMilliseconds(void) { + uint64_t newSec, newNsec; + + getTimer(&newSec, &newNsec); + return (newSec * 1000u) + (newNsec / 1000000ul); +} + +uint64_t BH_TimerNanoseconds(void) { + uint64_t newSec, newNsec; + + getTimer(&newSec, &newNsec); + return (newSec * 1000000000ul) + newNsec; +}