commit e44d0bff0d666e4fec2e1e25b278b6158f7ba9db Author: Mikhail Romanko Date: Sat Aug 8 20:33:32 2026 +0300 Initial commit 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; +}