mirror of
https://github.com/tezc/sc.git
synced 2025-01-28 07:03:06 +08:00
thread & url (#3)
* thread & url * fix cirrus * fix thread * fix thread * split actions * test * fix test * fix windows thread * cond & fail-fast
This commit is contained in:
parent
e3d95ee816
commit
42383fd590
@ -1,6 +1,4 @@
|
||||
freebsd_task:
|
||||
only_if: $CIRRUS_BRANCH == 'master'
|
||||
use_compute_credits: $CIRRUS_USER_COLLABORATOR == 'true'
|
||||
freebsd_instance:
|
||||
image_family: freebsd-12-1
|
||||
cpu: 1
|
||||
@ -8,5 +6,5 @@ freebsd_task:
|
||||
test_script:
|
||||
- pkg install -y git cmake
|
||||
- mkdir build && cd build
|
||||
- cmake -DSANITIZER=address .. && make -j && make check && rm -rf *
|
||||
- cmake -DSANITIZER=undefined .. && make -j && make check && rm -rf *
|
||||
- cmake -DSANITIZER=address .. && make && make check && rm -rf *
|
||||
- cmake -DSANITIZER=undefined .. && make && make check && rm -rf *
|
34
.github/workflows/.aarch64.yml
vendored
Normal file
34
.github/workflows/.aarch64.yml
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
archs:
|
||||
# The host should always be linux
|
||||
runs-on: ubuntu-18.04
|
||||
name: Build on aarch64
|
||||
steps:
|
||||
- uses: actions/checkout@v2.1.0
|
||||
- uses: uraimo/run-on-arch-action@v2.0.7
|
||||
name: Build artifact
|
||||
id: build
|
||||
with:
|
||||
arch: aarch64
|
||||
distro: ubuntu20.04
|
||||
|
||||
# Not required, but speeds up builds
|
||||
githubToken: ${{ github.token }}
|
||||
|
||||
# The shell to run commands with in the container
|
||||
shell: /bin/sh
|
||||
|
||||
# Produce a binary artifact and place it in the mounted volume
|
||||
run: |
|
||||
apt-get update -q -y
|
||||
apt-get install -q -y build-essential git gcc valgrind cmake
|
||||
uname -a;id;uname -m;lscpu | grep Endian
|
||||
mkdir build && cd build
|
||||
cmake .. && make -j && make check
|
152
.github/workflows/.actions.yml
vendored
152
.github/workflows/.actions.yml
vendored
@ -1,152 +0,0 @@
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
archs:
|
||||
# The host should always be linux
|
||||
runs-on: ubuntu-18.04
|
||||
name: Build on ${{ matrix.distro }} ${{ matrix.arch }}
|
||||
|
||||
# Run steps on a matrix of 3 arch/distro combinations
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- arch: armv6
|
||||
distro: buster
|
||||
- arch: armv7
|
||||
distro: buster
|
||||
- arch: aarch64
|
||||
distro: fedora_latest
|
||||
- arch: ppc64le
|
||||
distro: fedora_latest
|
||||
- arch: s390x
|
||||
distro: fedora_latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2.1.0
|
||||
- uses: uraimo/run-on-arch-action@v2.0.7
|
||||
name: Build artifact
|
||||
id: build
|
||||
with:
|
||||
arch: ${{ matrix.arch }}
|
||||
distro: ${{ matrix.distro }}
|
||||
|
||||
# Not required, but speeds up builds
|
||||
githubToken: ${{ github.token }}
|
||||
|
||||
# Pass some environment variables to the container
|
||||
env: | # YAML, but pipe character is necessary
|
||||
artifact_name: git-${{ matrix.distro }}_${{ matrix.arch }}
|
||||
|
||||
# The shell to run commands with in the container
|
||||
shell: /bin/sh
|
||||
|
||||
# Install some dependencies in the container. This speeds up builds if
|
||||
# you are also using githubToken. Any dependencies installed here will
|
||||
# be part of the container image that gets cached, so subsequent
|
||||
# builds don't have to re-install them. The image layer is cached
|
||||
# publicly in your project's package repository, so it is vital that
|
||||
# no secrets are present in the container state or logs.
|
||||
install: |
|
||||
case "${{ matrix.distro }}" in
|
||||
ubuntu*|jessie|stretch|buster)
|
||||
apt-get update -q -y
|
||||
apt-get install -q -y build-essential git gcc valgrind cmake
|
||||
;;
|
||||
fedora*)
|
||||
dnf -y update
|
||||
dnf -y install git gcc valgrind cmake
|
||||
;;
|
||||
alpine*)
|
||||
apk update
|
||||
apk add libexecinfo-dev linux-headers util-linux alpine-sdk perf libc-dev
|
||||
apk add git gcc clang valgrind cmake
|
||||
;;
|
||||
esac
|
||||
|
||||
# Produce a binary artifact and place it in the mounted volume
|
||||
run: |
|
||||
uname -a;id;uname -m;lscpu | grep Endian
|
||||
mkdir build && cd build
|
||||
cmake .. && make -j && make check
|
||||
ubuntu:
|
||||
runs-on: ubuntu-latest
|
||||
name: Build on Ubuntu
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
compiler: [ gcc, clang ]
|
||||
steps:
|
||||
- uses: actions/checkout@v2.1.0
|
||||
- name: build
|
||||
env:
|
||||
CC: ${{ matrix.compiler }}
|
||||
run: |
|
||||
sudo apt-get install valgrind cmake
|
||||
mkdir build-debug && cd build-debug
|
||||
cmake .. -DSANITIZER=address
|
||||
make -j
|
||||
make check
|
||||
rm -rf *
|
||||
cmake .. -DSANITIZER=undefined
|
||||
make -j
|
||||
make check
|
||||
rm -rf *
|
||||
cmake ..
|
||||
make -j
|
||||
make valgrind
|
||||
macos:
|
||||
runs-on: macos-latest
|
||||
name: Build on Mac OS
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
compiler: [ gcc, clang ]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2.1.0
|
||||
- name: build
|
||||
env:
|
||||
CC: ${{ matrix.compiler }}
|
||||
run: |
|
||||
mkdir build-debug && cd build-debug
|
||||
cmake .. -DSANITIZER=address
|
||||
make -j
|
||||
make check
|
||||
rm -rf *
|
||||
cmake .. -DSANITIZER=undefined
|
||||
make -j
|
||||
make check
|
||||
|
||||
windows:
|
||||
runs-on: windows-latest
|
||||
name: Build on Windows
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2.1.0
|
||||
- name: build
|
||||
run: |
|
||||
mkdir build-debug && cd build-debug
|
||||
cmake -G "Visual Studio 16 2019" -A x64 ..
|
||||
cmake --build .
|
||||
ctest -C Debug
|
||||
coverage:
|
||||
runs-on: ubuntu-latest
|
||||
name: Coverage
|
||||
steps:
|
||||
- uses: actions/checkout@v2.1.0
|
||||
- name: build
|
||||
run: |
|
||||
sudo apt-get install cmake lcov
|
||||
mkdir build-debug && cd build-debug
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Coverage
|
||||
make -j
|
||||
make coverage
|
||||
bash <(curl -s https://codecov.io/bash) -f coverage.info -t ${{ secrets.CODECOV }}
|
34
.github/workflows/.armv6.yml
vendored
Normal file
34
.github/workflows/.armv6.yml
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
archs:
|
||||
# The host should always be linux
|
||||
runs-on: ubuntu-18.04
|
||||
name: Build on armv6
|
||||
steps:
|
||||
- uses: actions/checkout@v2.1.0
|
||||
- uses: uraimo/run-on-arch-action@v2.0.7
|
||||
name: Build artifact
|
||||
id: build
|
||||
with:
|
||||
arch: armv6
|
||||
distro: buster
|
||||
|
||||
# Not required, but speeds up builds
|
||||
githubToken: ${{ github.token }}
|
||||
|
||||
# The shell to run commands with in the container
|
||||
shell: /bin/sh
|
||||
|
||||
# Produce a binary artifact and place it in the mounted volume
|
||||
run: |
|
||||
apt-get update -q -y
|
||||
apt-get install -q -y build-essential git gcc valgrind cmake
|
||||
uname -a;id;uname -m;lscpu | grep Endian
|
||||
mkdir build && cd build
|
||||
cmake .. && make -j && make check
|
34
.github/workflows/.armv7.yml
vendored
Normal file
34
.github/workflows/.armv7.yml
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
archs:
|
||||
# The host should always be linux
|
||||
runs-on: ubuntu-18.04
|
||||
name: Build on armv7
|
||||
steps:
|
||||
- uses: actions/checkout@v2.1.0
|
||||
- uses: uraimo/run-on-arch-action@v2.0.7
|
||||
name: Build artifact
|
||||
id: build
|
||||
with:
|
||||
arch: armv7
|
||||
distro: buster
|
||||
|
||||
# Not required, but speeds up builds
|
||||
githubToken: ${{ github.token }}
|
||||
|
||||
# The shell to run commands with in the container
|
||||
shell: /bin/sh
|
||||
|
||||
# Produce a binary artifact and place it in the mounted volume
|
||||
run: |
|
||||
apt-get update -q -y
|
||||
apt-get install -q -y build-essential git gcc valgrind cmake
|
||||
uname -a;id;uname -m;lscpu | grep Endian
|
||||
mkdir build && cd build
|
||||
cmake .. && make -j && make check
|
21
.github/workflows/.coverage.yml
vendored
Normal file
21
.github/workflows/.coverage.yml
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
coverage:
|
||||
runs-on: ubuntu-latest
|
||||
name: Coverage
|
||||
steps:
|
||||
- uses: actions/checkout@v2.1.0
|
||||
- name: build
|
||||
run: |
|
||||
sudo apt-get install cmake lcov
|
||||
mkdir build-debug && cd build-debug
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Coverage
|
||||
make -j
|
||||
make coverage
|
||||
bash <(curl -s https://codecov.io/bash) -f coverage.info -t ${{ secrets.CODECOV }}
|
31
.github/workflows/.macos.yml
vendored
Normal file
31
.github/workflows/.macos.yml
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
macos:
|
||||
runs-on: macos-latest
|
||||
name: Build on Mac OS
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
compiler: [ gcc, clang ]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2.1.0
|
||||
- name: build
|
||||
env:
|
||||
CC: ${{ matrix.compiler }}
|
||||
run: |
|
||||
mkdir build-debug && cd build-debug
|
||||
cmake .. -DSANITIZER=address
|
||||
make -j
|
||||
make check
|
||||
rm -rf *
|
||||
cmake .. -DSANITIZER=undefined
|
||||
make -j
|
||||
make check
|
34
.github/workflows/.ppc64le.yml
vendored
Normal file
34
.github/workflows/.ppc64le.yml
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
archs:
|
||||
# The host should always be linux
|
||||
runs-on: ubuntu-18.04
|
||||
name: Build on ppc64le
|
||||
steps:
|
||||
- uses: actions/checkout@v2.1.0
|
||||
- uses: uraimo/run-on-arch-action@v2.0.7
|
||||
name: Build artifact
|
||||
id: build
|
||||
with:
|
||||
arch: ppc64le
|
||||
distro: ubuntu20.04
|
||||
|
||||
# Not required, but speeds up builds
|
||||
githubToken: ${{ github.token }}
|
||||
|
||||
# The shell to run commands with in the container
|
||||
shell: /bin/sh
|
||||
|
||||
# Produce a binary artifact and place it in the mounted volume
|
||||
run: |
|
||||
apt-get update -q -y
|
||||
apt-get install -q -y build-essential git gcc valgrind cmake
|
||||
uname -a;id;uname -m;lscpu | grep Endian
|
||||
mkdir build && cd build
|
||||
cmake .. && make -j && make check
|
35
.github/workflows/.s390x.yml
vendored
Normal file
35
.github/workflows/.s390x.yml
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
archs:
|
||||
# The host should always be linux
|
||||
runs-on: ubuntu-18.04
|
||||
name: Build on s390x
|
||||
steps:
|
||||
- uses: actions/checkout@v2.1.0
|
||||
- uses: uraimo/run-on-arch-action@v2.0.7
|
||||
name: Build artifact
|
||||
id: build
|
||||
with:
|
||||
arch: s390x
|
||||
distro: ubuntu20.04
|
||||
|
||||
# Not required, but speeds up builds
|
||||
githubToken: ${{ github.token }}
|
||||
|
||||
# The shell to run commands with in the container
|
||||
shell: /bin/sh
|
||||
|
||||
# Produce a binary artifact and place it in the mounted volume
|
||||
run: |
|
||||
apt-get update -q -y
|
||||
apt-get install -q -y build-essential git gcc valgrind cmake
|
||||
uname -a;id;uname -m;lscpu | grep Endian
|
||||
mkdir build && cd build
|
||||
cmake .. && make -j && make check
|
||||
|
50
.github/workflows/.ubuntu.yml
vendored
Normal file
50
.github/workflows/.ubuntu.yml
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
ubuntu:
|
||||
runs-on: ubuntu-latest
|
||||
name: Build on Ubuntu
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
compiler: [ gcc, clang ]
|
||||
steps:
|
||||
- uses: actions/checkout@v2.1.0
|
||||
- name: build
|
||||
env:
|
||||
CC: ${{ matrix.compiler }}
|
||||
run: |
|
||||
sudo apt-get install valgrind cmake
|
||||
mkdir build-debug && cd build-debug
|
||||
cmake .. -DSANITIZER=address
|
||||
make -j
|
||||
make check
|
||||
rm -rf *
|
||||
cmake .. -DSANITIZER=undefined
|
||||
make -j
|
||||
make check
|
||||
rm -rf *
|
||||
cmake ..
|
||||
make -j
|
||||
make valgrind
|
||||
|
||||
ubuntu-gcc-thread:
|
||||
runs-on: ubuntu-latest
|
||||
name: Thread sanitizer
|
||||
steps:
|
||||
- uses: actions/checkout@v2.1.0
|
||||
- name: build
|
||||
env:
|
||||
CC: gcc
|
||||
run: |
|
||||
sudo apt-get install cmake
|
||||
mkdir build-debug && cd build-debug
|
||||
cmake .. -DSANITIZER=thread
|
||||
make -j
|
||||
make check
|
20
.github/workflows/.windows.yml
vendored
Normal file
20
.github/workflows/.windows.yml
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
windows:
|
||||
runs-on: windows-latest
|
||||
name: Build on Windows
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2.1.0
|
||||
- name: build
|
||||
run: |
|
||||
mkdir build-debug && cd build-debug
|
||||
cmake -G "Visual Studio 16 2019" -A x64 ..
|
||||
cmake --build .
|
||||
ctest -C Debug
|
@ -12,6 +12,7 @@ endif ()
|
||||
message(STATUS "Build type ${CMAKE_BUILD_TYPE}")
|
||||
|
||||
add_subdirectory(array)
|
||||
add_subdirectory(condition)
|
||||
add_subdirectory(crc32)
|
||||
add_subdirectory(heap)
|
||||
add_subdirectory(ini)
|
||||
@ -25,6 +26,7 @@ add_subdirectory(pipe)
|
||||
add_subdirectory(string)
|
||||
add_subdirectory(time)
|
||||
add_subdirectory(timer)
|
||||
add_subdirectory(thread)
|
||||
add_subdirectory(url)
|
||||
|
||||
|
||||
|
@ -41,6 +41,8 @@ static const struct sc_array sc_empty = {.size = 0, .cap = 0};
|
||||
|
||||
bool sc_array_init(void **arr, size_t elem_size, size_t cap)
|
||||
{
|
||||
const size_t max = SC_SIZE_MAX / elem_size;
|
||||
size_t bytes;
|
||||
struct sc_array *meta;
|
||||
|
||||
if (cap == 0) {
|
||||
@ -49,13 +51,16 @@ bool sc_array_init(void **arr, size_t elem_size, size_t cap)
|
||||
}
|
||||
|
||||
// Check overflow
|
||||
if (cap > SC_SIZE_MAX / elem_size) {
|
||||
if (cap > max) {
|
||||
sc_array_on_error("Max capacity(%zu) has been reached. ", max);
|
||||
*arr = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
meta = sc_array_realloc(NULL, sizeof(*meta) + (elem_size * cap));
|
||||
bytes = sizeof(*meta) + (elem_size * cap);
|
||||
meta = sc_array_realloc(NULL, bytes);
|
||||
if (meta == NULL) {
|
||||
sc_array_on_error("Failed to allocate %zu bytes. ", bytes);
|
||||
*arr = NULL;
|
||||
return false;
|
||||
}
|
||||
@ -80,13 +85,15 @@ void sc_array_term(void **arr)
|
||||
|
||||
bool sc_array_expand(void **arr, size_t elem_size)
|
||||
{
|
||||
size_t size, cap;
|
||||
const size_t max = SC_SIZE_MAX / elem_size;
|
||||
size_t size, cap, bytes;
|
||||
struct sc_array *prev, *meta = sc_array_meta(*arr);
|
||||
|
||||
if (meta->size == meta->cap) {
|
||||
|
||||
// Check overflow
|
||||
if (meta->cap > SC_SIZE_MAX / elem_size / 2) {
|
||||
if (meta->cap > max / 2) {
|
||||
sc_array_on_error("Max capacity(%zu) has been reached. ", max / 2);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -94,8 +101,10 @@ bool sc_array_expand(void **arr, size_t elem_size)
|
||||
cap = (meta != &sc_empty) ? meta->cap * 2 : 2;
|
||||
prev = (meta != &sc_empty) ? meta : NULL;
|
||||
|
||||
meta = sc_array_realloc(prev, sizeof(*meta) + (elem_size * cap));
|
||||
bytes = sizeof(*meta) + (elem_size * cap);
|
||||
meta = sc_array_realloc(prev, bytes);
|
||||
if (meta == NULL) {
|
||||
sc_array_on_error("Failed to allocate %zu bytes. ", bytes);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -54,6 +54,14 @@ bool sc_array_expand(void **arr, size_t elem_size);
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* If you want to log or abort on errors like out of memory,
|
||||
* put your error function here. It will be called with printf like error msg.
|
||||
*
|
||||
* my_on_error(const char* fmt, ...);
|
||||
*/
|
||||
#define sc_array_on_error(...)
|
||||
|
||||
/**
|
||||
* Configure memory allocators here. You can plug your allocator if you want,
|
||||
* replace 'realloc' and 'free' with your allocator, make sure you include
|
||||
|
94
condition/CMakeLists.txt
Normal file
94
condition/CMakeLists.txt
Normal file
@ -0,0 +1,94 @@
|
||||
cmake_minimum_required(VERSION 3.5.1)
|
||||
project(sc_cond C)
|
||||
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_C_EXTENSIONS OFF)
|
||||
|
||||
add_executable(sc_cond cond_example.c sc_cond.h sc_cond.c)
|
||||
|
||||
if (NOT CMAKE_C_COMPILER_ID MATCHES "MSVC")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -pthread -pedantic -Werror -D_GNU_SOURCE")
|
||||
endif ()
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# --------------------- Test Configuration Start ---------------------------- #
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
||||
include(CTest)
|
||||
include(CheckCCompilerFlag)
|
||||
|
||||
enable_testing()
|
||||
|
||||
add_executable(${PROJECT_NAME}_test cond_test.c sc_cond.c)
|
||||
|
||||
target_compile_options(${PROJECT_NAME}_test PRIVATE -DSC_SIZE_MAX=140000ul)
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
if ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR
|
||||
"${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
|
||||
target_compile_options(${PROJECT_NAME}_test PRIVATE -DSC_HAVE_WRAP)
|
||||
target_compile_options(${PROJECT_NAME}_test PRIVATE -fno-builtin)
|
||||
target_link_options(${PROJECT_NAME}_test PRIVATE
|
||||
-Wl,--wrap=pthread_mutexattr_init,--wrap=pthread_mutex_init,--wrap=pthread_cond_wait)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR
|
||||
"${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang" OR
|
||||
"${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
|
||||
target_compile_options(${PROJECT_NAME}_test PRIVATE -fno-omit-frame-pointer)
|
||||
|
||||
if (SANITIZER)
|
||||
target_compile_options(${PROJECT_NAME}_test PRIVATE -fsanitize=${SANITIZER})
|
||||
target_link_options(${PROJECT_NAME}_test PRIVATE -fsanitize=${SANITIZER})
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
add_test(NAME ${PROJECT_NAME}_test COMMAND ${PROJECT_NAME}_test)
|
||||
|
||||
SET(MEMORYCHECK_COMMAND_OPTIONS
|
||||
"-q --log-fd=2 --trace-children=yes --track-origins=yes \
|
||||
--leak-check=full --show-leak-kinds=all --show-reachable=yes \
|
||||
--error-exitcode=255")
|
||||
|
||||
add_custom_target(valgrind_${PROJECT_NAME} ${CMAKE_COMMAND}
|
||||
-E env CTEST_OUTPUT_ON_FAILURE=1
|
||||
${CMAKE_CTEST_COMMAND} -C $<CONFIG>
|
||||
--overwrite MemoryCheckCommandOptions=${MEMORYCHECK_COMMAND_OPTIONS}
|
||||
--verbose -T memcheck WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
|
||||
add_custom_target(check_${PROJECT_NAME} ${CMAKE_COMMAND}
|
||||
-E env CTEST_OUTPUT_ON_FAILURE=1
|
||||
${CMAKE_CTEST_COMMAND} -C $<CONFIG> --verbose
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
|
||||
# ----------------------- - Code Coverage Start ----------------------------- #
|
||||
|
||||
if (${CMAKE_BUILD_TYPE} MATCHES "Coverage")
|
||||
if ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
|
||||
target_compile_options(${PROJECT_NAME}_test PRIVATE --coverage)
|
||||
target_link_libraries(${PROJECT_NAME}_test gcov)
|
||||
else()
|
||||
message(FATAL_ERROR "Only GCC is supported for coverage")
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
add_custom_target(coverage_${PROJECT_NAME})
|
||||
add_custom_command(
|
||||
TARGET coverage_${PROJECT_NAME}
|
||||
COMMAND lcov --capture --directory ..
|
||||
--output-file coverage.info --rc lcov_branch_coverage=1
|
||||
COMMAND lcov --remove coverage.info '/usr/*' '*example*' '*test*'
|
||||
--output-file coverage.info --rc lcov_branch_coverage=1
|
||||
COMMAND lcov --list coverage.info --rc lcov_branch_coverage=1
|
||||
)
|
||||
|
||||
add_dependencies(coverage_${PROJECT_NAME} check_${PROJECT_NAME})
|
||||
|
||||
# -------------------------- Code Coverage End ------------------------------ #
|
||||
|
||||
|
||||
# ----------------------- Test Configuration End ---------------------------- #
|
||||
|
6
condition/cond_example.c
Normal file
6
condition/cond_example.c
Normal file
@ -0,0 +1,6 @@
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
return 0;
|
||||
}
|
252
condition/cond_test.c
Normal file
252
condition/cond_test.c
Normal file
@ -0,0 +1,252 @@
|
||||
#include "sc_cond.h"
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
struct sc_thread
|
||||
{
|
||||
HANDLE id;
|
||||
void* (*fn)(void*);
|
||||
void* arg;
|
||||
void* ret;
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
struct sc_thread
|
||||
{
|
||||
pthread_t id;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
void sc_thread_init(struct sc_thread* thread);
|
||||
int sc_thread_term(struct sc_thread* thread);
|
||||
int sc_thread_start(struct sc_thread* thread, void* (*fn)(void*), void* arg);
|
||||
int sc_thread_stop(struct sc_thread* thread, void** ret);
|
||||
|
||||
void sc_thread_init(struct sc_thread* thread)
|
||||
{
|
||||
thread->id = 0;
|
||||
}
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#include <process.h>
|
||||
|
||||
unsigned int __stdcall sc_thread_fn(void* arg)
|
||||
{
|
||||
struct sc_thread* thread = arg;
|
||||
|
||||
thread->ret = thread->fn(thread->arg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sc_thread_start(struct sc_thread* thread, void* (*fn)(void*), void* arg)
|
||||
{
|
||||
int rc;
|
||||
|
||||
thread->fn = fn;
|
||||
thread->arg = arg;
|
||||
|
||||
thread->id = (HANDLE)_beginthreadex(NULL, 0, sc_thread_fn, thread, 0, NULL);
|
||||
rc = thread->id == 0 ? -1 : 0;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int sc_thread_stop(struct sc_thread* thread, void** ret)
|
||||
{
|
||||
int rc = 0;
|
||||
DWORD rv;
|
||||
BOOL brc;
|
||||
|
||||
if (thread->id == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rv = WaitForSingleObject(thread->id, INFINITE);
|
||||
if (rv == WAIT_FAILED) {
|
||||
rc = -1;
|
||||
}
|
||||
|
||||
brc = CloseHandle(thread->id);
|
||||
if (!brc) {
|
||||
rc = -1;
|
||||
}
|
||||
|
||||
thread->id = 0;
|
||||
if (ret != NULL) {
|
||||
*ret = thread->ret;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
#else
|
||||
|
||||
int sc_thread_start(struct sc_thread* thread, void* (*fn)(void*), void* arg)
|
||||
{
|
||||
int rc;
|
||||
pthread_attr_t hndl;
|
||||
|
||||
rc = pthread_attr_init(&hndl);
|
||||
if (rc != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// This may only fail with EINVAL.
|
||||
pthread_attr_setdetachstate(&hndl, PTHREAD_CREATE_JOINABLE);
|
||||
|
||||
rc = pthread_create(&thread->id, &hndl, fn, arg);
|
||||
|
||||
// This may only fail with EINVAL.
|
||||
pthread_attr_destroy(&hndl);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int sc_thread_stop(struct sc_thread* thread, void** ret)
|
||||
{
|
||||
int rc;
|
||||
void* val;
|
||||
|
||||
if (thread->id == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = pthread_join(thread->id, &val);
|
||||
thread->id = 0;
|
||||
|
||||
if (ret != NULL) {
|
||||
*ret = val;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int sc_thread_term(struct sc_thread* thread)
|
||||
{
|
||||
return sc_thread_stop(thread, NULL);
|
||||
}
|
||||
|
||||
void* thread1_fn(void* arg)
|
||||
{
|
||||
char* data;
|
||||
struct sc_cond* cond = arg;
|
||||
|
||||
assert(sc_cond_sync(cond, (void**)&data) == 0);
|
||||
assert(strcmp(data, "finish") == 0);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void* thread2_fn(void* arg)
|
||||
{
|
||||
struct sc_cond* cond = arg;
|
||||
assert(sc_cond_finish(cond, "finish") == 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef SC_HAVE_WRAP
|
||||
bool mock_attrinit = false;
|
||||
extern int __real_pthread_mutexattr_init(pthread_mutexattr_t *attr);
|
||||
int __wrap_pthread_mutexattr_init(pthread_mutexattr_t *attr)
|
||||
{
|
||||
if (!mock_attrinit) {
|
||||
return __real_pthread_mutexattr_init(attr);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool mock_mutexinit = false;
|
||||
extern int __real_pthread_mutex_init(pthread_mutex_t *__mutex,
|
||||
const pthread_mutexattr_t *__mutexattr);
|
||||
int __wrap_pthread_mutex_init(pthread_mutex_t *__mutex,
|
||||
const pthread_mutexattr_t *__mutexattr)
|
||||
{
|
||||
if (!mock_mutexinit) {
|
||||
return __real_pthread_mutex_init(__mutex, __mutexattr);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool mock_condwait = false;
|
||||
extern int __real_pthread_cond_wait (pthread_cond_t *__restrict __cond,
|
||||
pthread_mutex_t *__restrict __mutex);
|
||||
int __wrap_pthread_cond_wait (pthread_cond_t *__restrict __cond,
|
||||
pthread_mutex_t *__restrict __mutex)
|
||||
{
|
||||
if (!mock_condwait) {
|
||||
return __real_pthread_cond_wait(__cond, __mutex);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void fail_test()
|
||||
{
|
||||
struct sc_cond cond;
|
||||
char* var;
|
||||
|
||||
mock_attrinit = true;
|
||||
assert(sc_cond_init(&cond) == -1);
|
||||
mock_attrinit = false;
|
||||
assert(sc_cond_init(&cond) == 0);
|
||||
assert(sc_cond_term(&cond) == 0);
|
||||
mock_mutexinit = true;
|
||||
assert(sc_cond_init(&cond) == -1);
|
||||
mock_mutexinit = false;
|
||||
assert(sc_cond_init(&cond) == 0);
|
||||
assert(sc_cond_term(&cond) == 0);
|
||||
|
||||
assert(sc_cond_init(&cond) == 0);
|
||||
mock_condwait = true;
|
||||
assert(sc_cond_sync(&cond, (void**) &var) == -1);
|
||||
mock_condwait = false;
|
||||
assert(sc_cond_term(&cond) == 0);
|
||||
}
|
||||
|
||||
#else
|
||||
void fail_test()
|
||||
{
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
void test1()
|
||||
{
|
||||
struct sc_cond cond;
|
||||
struct sc_thread thread1;
|
||||
struct sc_thread thread2;
|
||||
|
||||
assert(sc_cond_init(&cond) == 0);
|
||||
|
||||
sc_thread_init(&thread1);
|
||||
sc_thread_init(&thread2);
|
||||
|
||||
assert(sc_thread_start(&thread1, thread1_fn, &cond) == 0);
|
||||
assert(sc_thread_start(&thread2, thread2_fn, &cond) == 0);
|
||||
|
||||
assert(sc_thread_term(&thread1) == 0);
|
||||
assert(sc_thread_term(&thread2) == 0);
|
||||
|
||||
assert(sc_cond_term(&cond) == 0);
|
||||
}
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
test1();
|
||||
fail_test();
|
||||
|
||||
return 0;
|
||||
}
|
191
condition/sc_cond.c
Normal file
191
condition/sc_cond.c
Normal file
@ -0,0 +1,191 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020 Ozan Tezcan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "sc_cond.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
|
||||
int sc_cond_init(struct sc_cond *cond)
|
||||
{
|
||||
cond->data = NULL;
|
||||
cond->done = false;
|
||||
|
||||
InitializeCriticalSection(&cond->mtx);
|
||||
InitializeConditionVariable(&cond->cond);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sc_cond_term(struct sc_cond *cond)
|
||||
{
|
||||
DeleteCriticalSection(&cond->mtx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sc_cond_finish(struct sc_cond *cond, void *var)
|
||||
{
|
||||
EnterCriticalSection(&cond->mtx);
|
||||
|
||||
cond->data = var;
|
||||
cond->done = true;
|
||||
|
||||
WakeConditionVariable(&cond->cond);
|
||||
LeaveCriticalSection(&cond->mtx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sc_cond_sync(struct sc_cond *cond, void **data)
|
||||
{
|
||||
int rc = 0;
|
||||
BOOL rv;
|
||||
|
||||
EnterCriticalSection(&cond->mtx);
|
||||
|
||||
while (cond->done == false) {
|
||||
rv = SleepConditionVariableCS(&cond->cond, &cond->mtx, INFINITE);
|
||||
if (rv == 0) {
|
||||
sc_cond_on_error("SleepConditionVariableCS: errcode(%d) ",
|
||||
(int) GetLastError());
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (data != NULL) {
|
||||
*data = cond->data;
|
||||
}
|
||||
|
||||
cond->data = NULL;
|
||||
cond->done = false;
|
||||
|
||||
out:
|
||||
LeaveCriticalSection(&cond->mtx);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
|
||||
int sc_cond_init(struct sc_cond *cond)
|
||||
{
|
||||
int rc;
|
||||
|
||||
cond->data = NULL;
|
||||
cond->done = false;
|
||||
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
cond->mtx = mut;
|
||||
|
||||
// May fail on OOM
|
||||
rc = pthread_mutexattr_init(&attr);
|
||||
if (rc != 0) {
|
||||
sc_cond_on_error("pthread_mutexattr_init : errno(%d) \n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
// This won't fail as long as we pass correct params.
|
||||
rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
|
||||
assert(rc == 0);
|
||||
|
||||
// May fail on OOM
|
||||
rc = pthread_mutex_init(&cond->mtx, &attr);
|
||||
if (rc != 0) {
|
||||
sc_cond_on_error("pthread_mutex_init : errno(%d) \n", rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// This won't fail as long as we pass correct param.
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
|
||||
return pthread_cond_init(&cond->cond, NULL);
|
||||
}
|
||||
|
||||
int sc_cond_term(struct sc_cond *cond)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = pthread_mutex_destroy(&cond->mtx);
|
||||
rc |= pthread_cond_destroy(&cond->cond);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int sc_cond_finish(struct sc_cond *cond, void *var)
|
||||
{
|
||||
int rc, rv;
|
||||
|
||||
rv = pthread_mutex_lock(&cond->mtx);
|
||||
assert(rv == 0);
|
||||
|
||||
cond->data = var;
|
||||
cond->done = true;
|
||||
|
||||
rc = pthread_cond_signal(&cond->cond);
|
||||
if (rc != 0) {
|
||||
sc_cond_on_error("pthread_cond_signal : errno(%d) \n", rc);
|
||||
}
|
||||
rv = pthread_mutex_unlock(&cond->mtx);
|
||||
assert(rv == 0);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int sc_cond_sync(struct sc_cond *cond, void **data)
|
||||
{
|
||||
int rc, rv;
|
||||
|
||||
rv = pthread_mutex_lock(&cond->mtx);
|
||||
assert(rv == 0);
|
||||
|
||||
while (cond->done == false) {
|
||||
rc = pthread_cond_wait(&cond->cond, &cond->mtx);
|
||||
if (rc != 0) {
|
||||
sc_cond_on_error("pthread_mutex_init : errno(%d) \n", rc);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (data != NULL) {
|
||||
*data = cond->data;
|
||||
}
|
||||
|
||||
cond->data = NULL;
|
||||
cond->done = false;
|
||||
|
||||
out:
|
||||
rv = pthread_mutex_unlock(&cond->mtx);
|
||||
assert(rv == 0);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif
|
63
condition/sc_cond.h
Normal file
63
condition/sc_cond.h
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020 Ozan Tezcan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef SC_COND_H
|
||||
#define SC_COND_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
struct sc_cond
|
||||
{
|
||||
bool done;
|
||||
void* data;
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
CONDITION_VARIABLE cond;
|
||||
CRITICAL_SECTION mtx;
|
||||
#else
|
||||
pthread_cond_t cond;
|
||||
pthread_mutex_t mtx;
|
||||
#endif
|
||||
};
|
||||
|
||||
int sc_cond_init(struct sc_cond* cond);
|
||||
int sc_cond_term(struct sc_cond* cond);
|
||||
int sc_cond_finish(struct sc_cond* cond, void* data);
|
||||
int sc_cond_sync(struct sc_cond* cond, void** data);
|
||||
|
||||
/**
|
||||
* If you want to log or abort on errors like out of memory,
|
||||
* put your error function here. It will be called with printf like error msg.
|
||||
*
|
||||
* my_on_error(const char* fmt, ...);
|
||||
*/
|
||||
#define sc_cond_on_error(...)
|
||||
|
||||
#endif
|
@ -45,6 +45,7 @@ bool sc_heap_init(struct sc_heap *heap, size_t cap)
|
||||
|
||||
// Check overflow
|
||||
if (cap > SC_CAP_MAX || (elems = sc_heap_malloc(alloc)) == NULL) {
|
||||
sc_heap_on_error("Out of memory. cap(%zu) alloc(%zu) ", cap, alloc);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -76,10 +77,11 @@ bool sc_heap_add(struct sc_heap *heap, int64_t key, void *data)
|
||||
|
||||
if (++heap->size >= heap->cap) {
|
||||
const size_t cap = heap->cap != 0 ? heap->cap * 2 : 4;
|
||||
const size_t alloc = cap * 2 * sizeof(struct sc_heap_data);
|
||||
const size_t m = cap * 2 * sizeof(struct sc_heap_data);
|
||||
// Check overflow
|
||||
if (heap->cap >= SC_CAP_MAX / 2 ||
|
||||
(exp = sc_heap_realloc(heap->elems, alloc)) == NULL) {
|
||||
(exp = sc_heap_realloc(heap->elems, m)) == NULL) {
|
||||
sc_heap_on_error("Out of memory. cap(%zu) m(%zu) ", heap->cap, m);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -44,6 +44,14 @@ struct sc_heap
|
||||
struct sc_heap_data *elems;
|
||||
};
|
||||
|
||||
/**
|
||||
* If you want to log or abort on errors like out of memory,
|
||||
* put your error function here. It will be called with printf like error msg.
|
||||
*
|
||||
* my_on_error(const char* fmt, ...);
|
||||
*/
|
||||
#define sc_heap_on_error(...)
|
||||
|
||||
/**
|
||||
* Plug your memory allocator.
|
||||
*/
|
||||
|
@ -30,7 +30,8 @@ if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
target_compile_options(${PROJECT_NAME}_test PRIVATE -DSC_HAVE_WRAP)
|
||||
target_compile_options(${PROJECT_NAME}_test PRIVATE -fno-builtin)
|
||||
target_link_options(${PROJECT_NAME}_test PRIVATE
|
||||
-Wl,--wrap=fprintf,--wrap=vfprintf,--wrap=fopen,--wrap=localtime,--wrap=pthread_mutexattr_init)
|
||||
-Wl,--wrap=fprintf,--wrap=vfprintf,--wrap=fopen,--wrap=localtime
|
||||
-Wl,--wrap=pthread_mutexattr_init,--wrap=pthread_mutex_init)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
|
@ -164,12 +164,28 @@ int __wrap_pthread_mutexattr_init(pthread_mutexattr_t *attr)
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool mock_mutexinit = false;
|
||||
extern int __real_pthread_mutex_init(pthread_mutex_t *__mutex,
|
||||
const pthread_mutexattr_t *__mutexattr);
|
||||
int __wrap_pthread_mutex_init(pthread_mutex_t *__mutex,
|
||||
const pthread_mutexattr_t *__mutexattr)
|
||||
{
|
||||
if (!mock_mutexinit) {
|
||||
return __real_pthread_mutex_init(__mutex, __mutexattr);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
void fail_test(void)
|
||||
{
|
||||
mock_attrinit = true;
|
||||
assert(sc_log_init() < 0);
|
||||
mock_attrinit = false;
|
||||
mock_mutexinit = true;
|
||||
assert(sc_log_init() < 0);
|
||||
mock_mutexinit = false;
|
||||
assert(sc_log_init() == 0);
|
||||
|
||||
mock_fprintf = true;
|
||||
@ -297,6 +313,8 @@ void example(void)
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
sc_log_set_thread_name("My thread");
|
||||
|
||||
fail_test();
|
||||
example();
|
||||
test1();
|
||||
|
@ -24,12 +24,29 @@
|
||||
|
||||
#include "sc_log.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifndef thread_local
|
||||
#if __STDC_VERSION__ >= 201112 && !defined __STDC_NO_THREADS__
|
||||
#define thread_local _Thread_local
|
||||
#elif defined _WIN32 && (defined _MSC_VER || defined __ICL || \
|
||||
defined __DMC__ || defined __BORLANDC__)
|
||||
#define thread_local __declspec(thread)
|
||||
/* note that ICC (linux) and Clang are covered by __GNUC__ */
|
||||
#elif defined __GNUC__ || defined __SUNPRO_C || defined __xlC__
|
||||
#define thread_local __thread
|
||||
#else
|
||||
#error "Cannot define thread_local"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
thread_local char sc_name[32] = "Thread";
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
|
||||
#pragma warning(disable : 4996)
|
||||
@ -75,14 +92,24 @@ struct sc_log_mutex
|
||||
|
||||
int sc_log_mutex_init(struct sc_log_mutex *mtx)
|
||||
{
|
||||
int rc;
|
||||
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
mtx->mtx = mut;
|
||||
|
||||
if (pthread_mutexattr_init(&attr) != 0 ||
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL) != 0 ||
|
||||
pthread_mutex_init(&mtx->mtx, &attr) != 0) {
|
||||
rc = pthread_mutexattr_init(&attr);
|
||||
if (rc != 0) {
|
||||
sc_log_on_error("pthread_mutexattr_init : errcode(%d) ", rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
|
||||
|
||||
rc = pthread_mutex_init(&mtx->mtx, &attr);
|
||||
if (rc != 0) {
|
||||
sc_log_on_error("pthread_mutex_init : errcode(%d) ", rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -92,17 +119,30 @@ int sc_log_mutex_init(struct sc_log_mutex *mtx)
|
||||
|
||||
int sc_log_mutex_term(struct sc_log_mutex *mtx)
|
||||
{
|
||||
return pthread_mutex_destroy(&mtx->mtx);
|
||||
int rc;
|
||||
|
||||
rc = pthread_mutex_destroy(&mtx->mtx);
|
||||
if (rc != 0) {
|
||||
sc_log_on_error("pthread_mutex_destroy : errcode(%d) ", rc);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void sc_log_mutex_lock(struct sc_log_mutex *mtx)
|
||||
{
|
||||
pthread_mutex_lock(&mtx->mtx);
|
||||
int rc;
|
||||
|
||||
rc = pthread_mutex_lock(&mtx->mtx);
|
||||
assert(rc == 0);
|
||||
}
|
||||
|
||||
void sc_log_mutex_unlock(struct sc_log_mutex *mtx)
|
||||
{
|
||||
pthread_mutex_unlock(&mtx->mtx);
|
||||
int rc;
|
||||
|
||||
rc = pthread_mutex_unlock(&mtx->mtx);
|
||||
assert(rc == 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -126,17 +166,10 @@ struct sc_log sc_log;
|
||||
|
||||
int sc_log_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = sc_log_mutex_init(&sc_log.mtx);
|
||||
if (rc != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
sc_log.level = SC_LOG_INFO;
|
||||
sc_log.to_stdout = true;
|
||||
|
||||
return 0;
|
||||
return sc_log_mutex_init(&sc_log.mtx);
|
||||
}
|
||||
|
||||
int sc_log_term(void)
|
||||
@ -145,6 +178,7 @@ int sc_log_term(void)
|
||||
|
||||
if (sc_log.fp) {
|
||||
rc = fclose(sc_log.fp);
|
||||
assert(rc == 0);
|
||||
}
|
||||
|
||||
sc_log_mutex_term(&sc_log.mtx);
|
||||
@ -153,6 +187,11 @@ int sc_log_term(void)
|
||||
return rc;
|
||||
}
|
||||
|
||||
void sc_log_set_thread_name(const char *name)
|
||||
{
|
||||
strncpy(sc_name, name, sizeof(sc_name));
|
||||
}
|
||||
|
||||
int sc_log_set_level(const char *str)
|
||||
{
|
||||
size_t count = sizeof(sc_log_levels) / sizeof(struct sc_log_level_pair);
|
||||
@ -240,9 +279,9 @@ static int sc_log_print_header(FILE *fp, enum sc_log_level level)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fprintf(fp, "[%d-%02d-%02d %02d:%02d:%02d][%-5s] ",
|
||||
return fprintf(fp, "[%d-%02d-%02d %02d:%02d:%02d][%-5s][%s] ",
|
||||
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour,
|
||||
tm->tm_min, tm->tm_sec, sc_log_levels[level].str);
|
||||
tm->tm_min, tm->tm_sec, sc_log_levels[level].str, sc_name);
|
||||
}
|
||||
|
||||
static int sc_log_stdout(enum sc_log_level level, const char *fmt, va_list va)
|
||||
|
@ -53,6 +53,15 @@ const static struct sc_log_level_pair
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
/**
|
||||
* If you want to log or abort on errors like mutex init which is not supposed
|
||||
* to fail ever(?), put your error function here. It will be called with printf
|
||||
* like error msg.
|
||||
*
|
||||
* my_on_error(const char* fmt, ...);
|
||||
*/
|
||||
#define sc_log_on_error(...)
|
||||
|
||||
/**
|
||||
* User callback
|
||||
*
|
||||
@ -76,6 +85,14 @@ int sc_log_init(void);
|
||||
*/
|
||||
int sc_log_term(void);
|
||||
|
||||
/**
|
||||
* Call once from each thread, it will copy the name passed into a thread
|
||||
* local buffer. Max size is 31 characters.
|
||||
*
|
||||
* @param name Thread name
|
||||
*/
|
||||
void sc_log_set_thread_name(const char* name);
|
||||
|
||||
/**
|
||||
* Thread-safe.
|
||||
*
|
||||
@ -154,5 +171,4 @@ int sc_log_log(enum sc_log_level level, const char *fmt, ...);
|
||||
#define sc_log_warn(...) (sc_log_log(SC_LOG_WARN, sc_log_ap(__VA_ARGS__, "")))
|
||||
#define sc_log_error(...) (sc_log_log(SC_LOG_ERROR, sc_log_ap(__VA_ARGS__, "")))
|
||||
|
||||
#define sc_log_(args)
|
||||
#endif
|
||||
|
@ -336,7 +336,7 @@ void test4()
|
||||
char* c;
|
||||
struct sc_map_64s map64s;
|
||||
|
||||
assert(sc_map_init_64s(&map64s, 0, 87));
|
||||
assert(sc_map_init_64s(&map64s, 1, 87));
|
||||
for (int i = 0 ; i < 100; i++) {
|
||||
assert(sc_map_put_64s(&map64s, i, NULL));
|
||||
assert(sc_map_get_64s(&map64s, i, &c));
|
||||
@ -358,7 +358,7 @@ void test4()
|
||||
void* v;
|
||||
struct sc_map_64v map64v;
|
||||
|
||||
assert(sc_map_init_64v(&map64v, 0, 87));
|
||||
assert(sc_map_init_64v(&map64v, 1, 87));
|
||||
for (int i = 0 ; i < 100; i++) {
|
||||
assert(sc_map_put_64v(&map64v, i, NULL));
|
||||
assert(sc_map_get_64v(&map64v, i, &v));
|
||||
@ -377,19 +377,27 @@ void test4()
|
||||
|
||||
sc_map_term_64v(&map64v);
|
||||
|
||||
char *keys[128];
|
||||
char *values[128];
|
||||
|
||||
for (int i = 0; i < 128; i++) {
|
||||
keys[i] = str_random((rand() % 64) + 32);
|
||||
values[i] = str_random((rand() % 64) + 32);
|
||||
}
|
||||
|
||||
struct sc_map_sv mapsv;
|
||||
|
||||
assert(sc_map_init_sv(&mapsv, 0, 87));
|
||||
assert(sc_map_init_sv(&mapsv, 1, 87));
|
||||
for (int i = 0 ; i < 100; i++) {
|
||||
assert(sc_map_put_sv(&mapsv, "", NULL));
|
||||
assert(sc_map_get_sv(&mapsv, "", &v));
|
||||
assert(v == NULL);
|
||||
assert(sc_map_put_sv(&mapsv, keys[i], values[i]));
|
||||
assert(sc_map_get_sv(&mapsv, keys[i], &v));
|
||||
assert(v == values[i]);
|
||||
}
|
||||
assert(sc_map_size_sv(&mapsv) == 1);
|
||||
assert(sc_map_del_sv(&mapsv, "", &v));
|
||||
assert(v == NULL);
|
||||
assert(sc_map_size_sv(&mapsv) == 0);
|
||||
assert(!sc_map_del_sv(&mapsv, "", &v));
|
||||
assert(sc_map_size_sv(&mapsv) == 100);
|
||||
assert(sc_map_del_sv(&mapsv, keys[0], &v));
|
||||
assert(v == values[0]);
|
||||
assert(sc_map_size_sv(&mapsv) == 99);
|
||||
assert(!sc_map_del_sv(&mapsv, keys[0], &v));
|
||||
sc_map_clear_sv(&mapsv);
|
||||
assert(sc_map_size_sv(&mapsv) == 0);
|
||||
sc_map_term_sv(&mapsv);
|
||||
@ -398,16 +406,23 @@ void test4()
|
||||
struct sc_map_s64 maps64;
|
||||
|
||||
assert(sc_map_init_s64(&maps64, 0, 26));
|
||||
assert(sc_map_put_s64(&maps64, "", 511));
|
||||
assert(sc_map_put_s64(&maps64, "", 511));
|
||||
assert(sc_map_get_s64(&maps64, "", &val));
|
||||
assert(val == 511);
|
||||
assert(sc_map_size_s64(&maps64) == 1);
|
||||
assert(sc_map_del_s64(&maps64, "", &val));
|
||||
assert(val == 511);
|
||||
assert(sc_map_size_s64(&maps64) == 0);
|
||||
for (int i =0 ; i< 64; i++) {
|
||||
assert(sc_map_put_s64(&maps64, keys[i], i));
|
||||
}
|
||||
|
||||
assert(sc_map_get_s64(&maps64, keys[0], &val));
|
||||
assert(val == 0);
|
||||
assert(sc_map_size_s64(&maps64) == 64);
|
||||
assert(sc_map_del_s64(&maps64, keys[12], &val));
|
||||
assert(val == 12);
|
||||
assert(sc_map_size_s64(&maps64) == 63);
|
||||
sc_map_clear_s64(&maps64);
|
||||
sc_map_term_s64(&maps64);
|
||||
|
||||
for (int i = 0; i < 128; i++) {
|
||||
free(keys[i]);
|
||||
free(values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
11
map/sc_map.c
11
map/sc_map.c
@ -1,7 +1,7 @@
|
||||
#include "sc_map.h"
|
||||
|
||||
#include <memory.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef SC_SIZE_MAX
|
||||
#define SC_SIZE_MAX UINT32_MAX
|
||||
@ -57,9 +57,11 @@
|
||||
static void *sc_map_alloc_##name(uint32_t *cap, uint32_t factor) \
|
||||
{ \
|
||||
uint32_t v = *cap; \
|
||||
void *p; \
|
||||
struct sc_map_item_##name *t; \
|
||||
\
|
||||
if (*cap > SC_SIZE_MAX / factor) { \
|
||||
sc_map_on_error("Out of memory. cap(%zu).", *cap); \
|
||||
return NULL; \
|
||||
} \
|
||||
\
|
||||
@ -72,7 +74,12 @@
|
||||
v++; \
|
||||
\
|
||||
*cap = v; \
|
||||
return sc_map_calloc(sizeof(*t), v); \
|
||||
p = sc_map_calloc(sizeof(*t), v); \
|
||||
if (p == NULL) { \
|
||||
sc_map_on_error("Out of memory. t(%zu) v(%zu).", sizeof(*t), v); \
|
||||
} \
|
||||
\
|
||||
return p; \
|
||||
} \
|
||||
\
|
||||
bool sc_map_init_##name(struct sc_map_##name *map, uint32_t cap, \
|
||||
|
21
map/sc_map.h
21
map/sc_map.h
@ -59,17 +59,17 @@
|
||||
uint32_t load_factor; \
|
||||
uint32_t remap; \
|
||||
V value; \
|
||||
bool used; \
|
||||
bool used; \
|
||||
}; \
|
||||
\
|
||||
bool sc_map_init_##name(struct sc_map_##name *map, uint32_t cap, \
|
||||
uint32_t load_factor); \
|
||||
bool sc_map_init_##name(struct sc_map_##name *map, uint32_t cap, \
|
||||
uint32_t load_factor); \
|
||||
void sc_map_term_##name(struct sc_map_##name *map); \
|
||||
uint32_t sc_map_size_##name(struct sc_map_##name *map); \
|
||||
void sc_map_clear_##name(struct sc_map_##name *map); \
|
||||
bool sc_map_put_##name(struct sc_map_##name *map, K key, V val); \
|
||||
bool sc_map_get_##name(struct sc_map_##name *map, K key, V *value); \
|
||||
bool sc_map_del_##name(struct sc_map_##name *map, K key, V* value);
|
||||
bool sc_map_put_##name(struct sc_map_##name *map, K key, V val); \
|
||||
bool sc_map_get_##name(struct sc_map_##name *map, K key, V *value); \
|
||||
bool sc_map_del_##name(struct sc_map_##name *map, K key, V *value);
|
||||
|
||||
#define sc_map_foreach(map, K, V) \
|
||||
for (uint32_t __i = 0, __b = 0; __i < (map)->cap; __i++) \
|
||||
@ -99,4 +99,13 @@ sc_map_of_strkey(sv, char *, void*)
|
||||
sc_map_of_strkey(s64, char *, uint64_t)
|
||||
|
||||
// clang-format on
|
||||
|
||||
/**
|
||||
* If you want to log or abort on errors like out of memory,
|
||||
* put your error function here. It will be called with printf like error msg.
|
||||
*
|
||||
* my_on_error(const char* fmt, ...);
|
||||
*/
|
||||
#define sc_map_on_error(...)
|
||||
|
||||
#endif
|
||||
|
@ -54,7 +54,7 @@ void sc_mutex_unlock(struct sc_mutex *mtx)
|
||||
|
||||
int sc_mutex_init(struct sc_mutex *mtx)
|
||||
{
|
||||
int rc;
|
||||
int rc, rv;
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
@ -63,6 +63,7 @@ int sc_mutex_init(struct sc_mutex *mtx)
|
||||
// May fail on OOM
|
||||
rc = pthread_mutexattr_init(&attr);
|
||||
if (rc != 0) {
|
||||
sc_mutex_on_error("pthread_mutexattr_init : errcode(%d) ", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -72,27 +73,45 @@ int sc_mutex_init(struct sc_mutex *mtx)
|
||||
|
||||
// May fail on OOM
|
||||
rc = pthread_mutex_init(&mtx->mtx, &attr);
|
||||
if (rc != 0) {
|
||||
sc_mutex_on_error("pthread_mutex_init : errcode(%d) ", rc);
|
||||
}
|
||||
|
||||
// This won't fail as long as we pass correct param.
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
rv = pthread_mutexattr_destroy(&attr);
|
||||
assert(rv == 0);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int sc_mutex_term(struct sc_mutex *mtx)
|
||||
{
|
||||
return pthread_mutex_destroy(&mtx->mtx);
|
||||
int rc;
|
||||
|
||||
rc = pthread_mutex_destroy(&mtx->mtx);
|
||||
if (rc != 0) {
|
||||
sc_mutex_on_error("pthread_mutex_destroy : errcode(%d) ", rc);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void sc_mutex_lock(struct sc_mutex *mtx)
|
||||
{
|
||||
int rc;
|
||||
|
||||
// This won't fail as long as we pass correct param.
|
||||
pthread_mutex_lock(&mtx->mtx);
|
||||
rc = pthread_mutex_lock(&mtx->mtx);
|
||||
assert(rc == 0);
|
||||
}
|
||||
|
||||
void sc_mutex_unlock(struct sc_mutex *mtx)
|
||||
{
|
||||
int rc;
|
||||
|
||||
// This won't fail as long as we pass correct param.
|
||||
pthread_mutex_unlock(&mtx->mtx);
|
||||
rc = pthread_mutex_unlock(&mtx->mtx);
|
||||
assert(rc == 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -45,4 +45,12 @@ int sc_mutex_term(struct sc_mutex *mtx);
|
||||
void sc_mutex_lock(struct sc_mutex *mtx);
|
||||
void sc_mutex_unlock(struct sc_mutex *mtx);
|
||||
|
||||
/**
|
||||
* If you want to log or abort on errors like mutex init,
|
||||
* put your error function here. It will be called with printf like error msg.
|
||||
*
|
||||
* my_on_error(const char* fmt, ...);
|
||||
*/
|
||||
#define sc_mutex_on_error(...)
|
||||
|
||||
#endif
|
||||
|
@ -8,7 +8,7 @@ set(CMAKE_C_EXTENSIONS OFF)
|
||||
add_executable(sc_pipe pipe_example.c sc_pipe.h sc_pipe.c)
|
||||
|
||||
if (NOT CMAKE_C_COMPILER_ID MATCHES "MSVC")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -pedantic -Werror -D_GNU_SOURCE")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -Wall -pedantic -Werror -D_GNU_SOURCE")
|
||||
endif ()
|
||||
|
||||
|
||||
@ -23,9 +23,21 @@ enable_testing()
|
||||
|
||||
add_executable(${PROJECT_NAME}_test pipe_test.c sc_pipe.c)
|
||||
|
||||
target_compile_options(${PROJECT_NAME}_test PRIVATE -DSC_SIZE_MAX=4000ul)
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
if ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR
|
||||
"${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
|
||||
|
||||
target_compile_options(${PROJECT_NAME}_test PRIVATE -DSC_HAVE_WRAP)
|
||||
target_compile_options(${PROJECT_NAME}_test PRIVATE -fno-builtin)
|
||||
target_link_options(${PROJECT_NAME}_test PRIVATE
|
||||
-Wl,--wrap=pipe,--wrap=close)
|
||||
endif ()
|
||||
endif ()
|
||||
if ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR
|
||||
"${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang" OR
|
||||
"${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
|
||||
"${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang" OR
|
||||
"${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
|
||||
|
||||
target_compile_options(${PROJECT_NAME}_test PRIVATE -fno-omit-frame-pointer)
|
||||
|
||||
|
@ -26,6 +26,53 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef SC_HAVE_WRAP
|
||||
#include <unistd.h>
|
||||
|
||||
bool fail_close = false;
|
||||
int __real_close(int fd);
|
||||
int __wrap_close(int fd)
|
||||
{
|
||||
if (fail_close) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return __real_close(fd);
|
||||
}
|
||||
|
||||
bool fail_pipe = false;
|
||||
int __real_pipe(int __pipedes[2]);
|
||||
int __wrap_pipe(int __pipedes[2])
|
||||
{
|
||||
if (fail_pipe) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return __real_pipe(__pipedes);
|
||||
}
|
||||
|
||||
void fail_test()
|
||||
{
|
||||
struct sc_pipe pipe;
|
||||
fail_pipe = true;
|
||||
assert(sc_pipe_init(&pipe, 0) != 0);
|
||||
fail_pipe = false;
|
||||
assert(sc_pipe_init(&pipe, 0) == 0);
|
||||
fail_close = true;
|
||||
assert(sc_pipe_term(&pipe) == -1);
|
||||
fail_close = false;
|
||||
assert(sc_pipe_term(&pipe) == 0);
|
||||
}
|
||||
|
||||
#else
|
||||
void fail_test()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void test1(void)
|
||||
{
|
||||
@ -51,10 +98,12 @@ int main(int argc, char* argv[])
|
||||
HIBYTE(data.wVersion) == 2);
|
||||
#endif
|
||||
test1();
|
||||
fail_test();
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
rc = WSACleanup();
|
||||
assert(rc == 0);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -22,23 +22,24 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "sc_pipe.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "sc_pipe.h"
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
|
||||
int sc_pipe_init(struct sc_pipe* p)
|
||||
int sc_pipe_init(struct sc_pipe *p)
|
||||
{
|
||||
SOCKET listener;
|
||||
int rc;
|
||||
struct sockaddr_in addr;
|
||||
size_t addrlen;
|
||||
int one;
|
||||
BOOL nodelay;
|
||||
int addrlen = sizeof(addr);
|
||||
int val = 1;
|
||||
BOOL nodelay = 1;
|
||||
u_long nonblock;
|
||||
|
||||
p->w = INVALID_SOCKET;
|
||||
@ -50,26 +51,23 @@ int sc_pipe_init(struct sc_pipe* p)
|
||||
goto wsafail;
|
||||
}
|
||||
|
||||
one = 1;
|
||||
rc = setsockopt(listener, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
|
||||
(char*)&one, sizeof(one));
|
||||
rc = setsockopt(listener, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *) &val,
|
||||
sizeof(val));
|
||||
if (rc == SOCKET_ERROR) {
|
||||
goto wsafail;
|
||||
}
|
||||
|
||||
/* Bind the listening socket to the local port. */
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
addr.sin_port = 0;
|
||||
|
||||
rc = bind(listener, (const struct sockaddr*)&addr, sizeof(addr));
|
||||
rc = bind(listener, (const struct sockaddr *) &addr, sizeof(addr));
|
||||
if (rc == SOCKET_ERROR) {
|
||||
goto wsafail;
|
||||
}
|
||||
|
||||
addrlen = sizeof(addr);
|
||||
rc = getsockname(listener, (struct sockaddr*)&addr, &addrlen);
|
||||
rc = getsockname(listener, (struct sockaddr *) &addr, &addrlen);
|
||||
if (rc == SOCKET_ERROR) {
|
||||
goto wsafail;
|
||||
}
|
||||
@ -84,70 +82,70 @@ int sc_pipe_init(struct sc_pipe* p)
|
||||
goto wsafail;
|
||||
}
|
||||
|
||||
/* Set TCP_NODELAY on the writer socket to make efd as fast as possible.
|
||||
There's only one byte going to be written, so batching would not make
|
||||
sense anyway. */
|
||||
nodelay = 1;
|
||||
rc = setsockopt(p->w, IPPROTO_TCP, TCP_NODELAY, (char*)&nodelay,
|
||||
sizeof(nodelay));
|
||||
rc = setsockopt(p->w, IPPROTO_TCP, TCP_NODELAY, (char *) &nodelay,
|
||||
sizeof(nodelay));
|
||||
if (rc == SOCKET_ERROR) {
|
||||
goto wsafail;
|
||||
}
|
||||
|
||||
rc = connect(p->w, (struct sockaddr*)&addr, sizeof(addr));
|
||||
rc = connect(p->w, (struct sockaddr *) &addr, sizeof(addr));
|
||||
if (rc == SOCKET_ERROR) {
|
||||
goto wsafail;
|
||||
}
|
||||
|
||||
|
||||
addrlen = sizeof(addr);
|
||||
p->r = accept(listener, (struct sockaddr*)&addr, &addrlen);
|
||||
p->r = accept(listener, (struct sockaddr *) &addr, &addrlen);
|
||||
if (p->r == INVALID_SOCKET) {
|
||||
goto wsafail;
|
||||
}
|
||||
|
||||
(void)closesocket(listener);
|
||||
closesocket(listener);
|
||||
|
||||
return 0;
|
||||
|
||||
wsafail:
|
||||
sc_pipe_on_error("sc_pipe_init() : %d ", WSAGetLastError())
|
||||
return -1;
|
||||
}
|
||||
|
||||
int sc_pipe_term(struct sc_pipe* p)
|
||||
int sc_pipe_term(struct sc_pipe *p)
|
||||
{
|
||||
int rc;
|
||||
int rc = 0, rv;
|
||||
SOCKET s;
|
||||
|
||||
rc = closesocket(p->r);
|
||||
rc |= closesocket(p->w);
|
||||
rv = closesocket(p->r);
|
||||
if (rv != 0) {
|
||||
rc = -1;
|
||||
sc_pipe_on_error("closesocket() : errcode(%d) ", WSAGetLastError());
|
||||
}
|
||||
|
||||
if (rc != 0) {
|
||||
sc_pipe_abort();
|
||||
rv = closesocket(p->w);
|
||||
if (rv != 0) {
|
||||
rc = -1;
|
||||
sc_pipe_on_error("closesocket() : errcode(%d) ", WSAGetLastError());
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int sc_pipe_write(struct sc_pipe* p, void* data, int len)
|
||||
int sc_pipe_write(struct sc_pipe *p, void *data, int len)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = send(p->w, data, len, 0);
|
||||
if (rc == SOCKET_ERROR || rc != len) {
|
||||
sc_pipe_abort();
|
||||
sc_pipe_on_error("send() : errcode(%d) ", WSAGetLastError());
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int sc_pipe_read(struct sc_pipe* p, void* data, int len)
|
||||
int sc_pipe_read(struct sc_pipe *p, void *data, int len)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = recv(p->r, (char*)data, len, 0);
|
||||
rc = recv(p->r, (char *) data, len, 0);
|
||||
if (rc == SOCKET_ERROR || rc != len) {
|
||||
sc_pipe_abort();
|
||||
sc_pipe_on_error("recv() : errcode(%d) ", WSAGetLastError());
|
||||
}
|
||||
|
||||
return rc;
|
||||
@ -155,15 +153,15 @@ int sc_pipe_read(struct sc_pipe* p, void* data, int len)
|
||||
|
||||
#else
|
||||
|
||||
#include <unistd.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int sc_pipe_init(struct sc_pipe *p, int type)
|
||||
int sc_pipe_init(struct sc_pipe *p, int type)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = pipe(p->fds);
|
||||
if (rc == -1) {
|
||||
sc_pipe_abort();
|
||||
sc_pipe_on_error("pipe() : %d ", errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -174,10 +172,19 @@ int sc_pipe_init(struct sc_pipe *p, int type)
|
||||
|
||||
int sc_pipe_term(struct sc_pipe *nfd)
|
||||
{
|
||||
int rc;
|
||||
int rc = 0, rv;
|
||||
|
||||
rc = close(nfd->fds[0]);
|
||||
rc |= close(nfd->fds[1]);
|
||||
rv = close(nfd->fds[0]);
|
||||
if (rv != 0) {
|
||||
rc = -1;
|
||||
sc_pipe_on_error("pipe() : %d ", errno);
|
||||
}
|
||||
|
||||
rv = close(nfd->fds[1]);
|
||||
if (rv != 0) {
|
||||
rc = -1;
|
||||
sc_pipe_on_error("pipe() : %d ", errno);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
@ -188,7 +195,7 @@ int sc_pipe_write(struct sc_pipe *nfd, void *data, int len)
|
||||
|
||||
n = write(nfd->fds[1], data, len);
|
||||
if (n != len) {
|
||||
sc_pipe_abort();
|
||||
sc_pipe_on_error("pipe() : %d ", errno);
|
||||
}
|
||||
|
||||
return n;
|
||||
@ -199,8 +206,8 @@ int sc_pipe_read(struct sc_pipe *nfd, void *data, int len)
|
||||
ssize_t n;
|
||||
|
||||
n = read(nfd->fds[0], data, len);
|
||||
if (n == -1) {
|
||||
sc_pipe_abort();
|
||||
if (n != len) {
|
||||
sc_pipe_on_error("pipe() : %d ", errno);
|
||||
}
|
||||
|
||||
return n;
|
||||
|
@ -27,8 +27,6 @@
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define sc_pipe_abort()
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
@ -55,4 +53,12 @@ int sc_pipe_term(struct sc_pipe *pipe);
|
||||
int sc_pipe_write(struct sc_pipe *pipe, void *data, int len);
|
||||
int sc_pipe_read(struct sc_pipe *pipe, void *data, int len);
|
||||
|
||||
/**
|
||||
* If you want to log or abort on errors like mutex init,
|
||||
* put your error function here. It will be called with printf like error msg.
|
||||
*
|
||||
* my_on_error(const char* fmt, ...);
|
||||
*/
|
||||
#define sc_pipe_on_error(...)
|
||||
|
||||
#endif
|
||||
|
@ -36,10 +36,12 @@ static const struct sc_queue sc_empty = {.cap = 1, .first = 0, .last = 0};
|
||||
|
||||
static void *queue_alloc(void *prev, size_t elem_size, size_t *cap)
|
||||
{
|
||||
size_t alloc;
|
||||
size_t v = *cap;
|
||||
void *t;
|
||||
|
||||
if (*cap > SC_MAX_CAP) {
|
||||
sc_queue_on_error("Max capacity has been exceed. cap(%zu). ", (*cap));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -51,7 +53,12 @@ static void *queue_alloc(void *prev, size_t elem_size, size_t *cap)
|
||||
}
|
||||
v++;
|
||||
|
||||
t = sc_queue_realloc(prev, sizeof(struct sc_queue) + (elem_size * v));
|
||||
alloc = sizeof(struct sc_queue) + (elem_size * v);
|
||||
t = sc_queue_realloc(prev, alloc);
|
||||
if (t == NULL) {
|
||||
sc_queue_on_error("Out of memory. alloc(%zu). ", alloc);
|
||||
}
|
||||
|
||||
*cap = v;
|
||||
|
||||
return t;
|
||||
|
@ -85,6 +85,14 @@ void sc_queue_term(void **q);
|
||||
bool sc_queue_expand(void **q, size_t elem_size);
|
||||
|
||||
|
||||
/**
|
||||
* If you want to log or abort on errors like out of memory,
|
||||
* put your error function here. It will be called with printf like error msg.
|
||||
*
|
||||
* my_on_error(const char* fmt, ...);
|
||||
*/
|
||||
#define sc_queue_on_error(...)
|
||||
|
||||
/**
|
||||
* Plug your allocator if you want.
|
||||
*/
|
||||
|
@ -32,10 +32,7 @@ if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
target_compile_options(${PROJECT_NAME}_test PRIVATE -DSC_HAVE_WRAP)
|
||||
target_compile_options(${PROJECT_NAME}_test PRIVATE -fno-builtin)
|
||||
target_link_options(${PROJECT_NAME}_test PRIVATE
|
||||
-Wl,--wrap=malloc,--wrap=vsnprintf,--wrap=realloc)
|
||||
|
||||
target_link_options(${PROJECT_NAME}_test PRIVATE
|
||||
-Wl,--wrap=malloc,--wrap=vsnprintf,--wrap=realloc)
|
||||
-Wl,--wrap=malloc,--wrap=vsnprintf,--wrap=realloc,--wrap=strlen)
|
||||
endif ()
|
||||
endif ()
|
||||
if ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR
|
||||
|
@ -60,6 +60,7 @@ char *sc_str_create(const char *str)
|
||||
|
||||
size_t size = strlen(str);
|
||||
if (size > SC_SIZE_MAX) {
|
||||
sc_str_on_error("Max size has been exceed. size(%zu). ", size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -74,6 +75,7 @@ char *sc_str_create_len(const char *str, uint32_t len)
|
||||
|
||||
copy = sc_str_malloc(sc_str_bytes(len));
|
||||
if (copy == NULL) {
|
||||
sc_str_on_error("Out of memory. size(%zu). ", sc_str_bytes(len));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -94,12 +96,14 @@ char *sc_str_create_va(const char *fmt, va_list va)
|
||||
va_copy(args, va);
|
||||
rc = vsnprintf(tmp, sizeof(tmp), fmt, args);
|
||||
if (rc < 0) {
|
||||
sc_str_on_error("vsnprintf: %d. ", rc);
|
||||
return NULL;
|
||||
}
|
||||
va_end(args);
|
||||
|
||||
str = sc_str_malloc(sc_str_bytes(rc));
|
||||
if (str == NULL) {
|
||||
sc_str_on_error("Out of memory. size(%zu). ", sc_str_bytes(rc));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -113,6 +117,7 @@ char *sc_str_create_va(const char *fmt, va_list va)
|
||||
va_end(args);
|
||||
|
||||
if (rc < 0 || rc > str->len) {
|
||||
sc_str_on_error("vsnprintf: %d. ", rc);
|
||||
sc_str_free(str);
|
||||
return NULL;
|
||||
}
|
||||
@ -197,6 +202,7 @@ bool sc_str_append(char **str, const char *param)
|
||||
size_t alloc = sc_str_bytes(meta->len + len);
|
||||
|
||||
if (alloc > SC_SIZE_MAX || (meta = sc_str_realloc(meta, alloc)) == NULL) {
|
||||
sc_str_on_error("Out of memory. alloc(%zu). ", alloc);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -304,10 +310,7 @@ bool sc_str_replace(char **str, const char *replace, const char *with)
|
||||
|
||||
size_t replace_len = strlen(replace);
|
||||
size_t with_len = strlen(with);
|
||||
if (replace_len > UINT32_MAX || with_len > UINT32_MAX) {
|
||||
return false;
|
||||
}
|
||||
int64_t diff = (int64_t)with_len - replace_len;
|
||||
int64_t diff = (int64_t) with_len - (int64_t) replace_len;
|
||||
size_t len_unmatch;
|
||||
size_t count, size;
|
||||
struct sc_str *dest;
|
||||
@ -316,6 +319,13 @@ bool sc_str_replace(char **str, const char *replace, const char *with)
|
||||
char *orig_end = *str + meta->len;
|
||||
char *tmp;
|
||||
|
||||
if (replace_len >= UINT32_MAX || with_len >= UINT32_MAX) {
|
||||
sc_str_on_error(
|
||||
"Replace size is too big. replace_len(%zu), with_len(%zu). ",
|
||||
replace_len, with_len);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fast path, same size replacement.
|
||||
if (diff == 0) {
|
||||
while ((orig = strstr(orig, replace)) != NULL) {
|
||||
@ -333,6 +343,9 @@ bool sc_str_replace(char **str, const char *replace, const char *with)
|
||||
tmp += replace_len;
|
||||
// Check overflow.
|
||||
if (size > SC_SIZE_MAX - diff) {
|
||||
sc_str_on_error("Replace size is too big. replace_len(%zu), "
|
||||
"with_len(%zu). ",
|
||||
replace_len, with_len);
|
||||
return false;
|
||||
}
|
||||
size += diff;
|
||||
@ -345,6 +358,7 @@ bool sc_str_replace(char **str, const char *replace, const char *with)
|
||||
|
||||
dest = sc_str_malloc(sc_str_bytes(size));
|
||||
if (!dest) {
|
||||
sc_str_on_error("Out of memory. size(%zu). ", sc_str_bytes(size));
|
||||
return false;
|
||||
}
|
||||
dest->len = size;
|
||||
|
@ -49,6 +49,14 @@
|
||||
#define sc_str_realloc realloc
|
||||
#define sc_str_free free
|
||||
|
||||
/**
|
||||
* If you want to log or abort on errors like out of memory,
|
||||
* put your error function here. It will be called with printf like error msg.
|
||||
*
|
||||
* my_on_error(const char* fmt, ...);
|
||||
*/
|
||||
#define sc_str_on_error(...)
|
||||
|
||||
/**
|
||||
* @param str '\0' terminated C string, must not be NULL.
|
||||
* @return Length prefixed string. NULL on out of memory.
|
||||
|
@ -28,6 +28,17 @@ void *__wrap_realloc(void *p, size_t n)
|
||||
return __real_realloc(p, n);
|
||||
}
|
||||
|
||||
bool fail_strlen = false;
|
||||
size_t __real_strlen(const char* str);
|
||||
size_t __wrap_strlen(const char* str)
|
||||
{
|
||||
if (fail_strlen) {
|
||||
return UINT32_MAX;
|
||||
}
|
||||
|
||||
return __real_strlen(str);
|
||||
}
|
||||
|
||||
bool fail_vsnprintf;
|
||||
int fail_vsnprintf_at = -1;
|
||||
extern int __real_vsnprintf(char *str, size_t size, const char *format,
|
||||
@ -175,6 +186,9 @@ void test2()
|
||||
assert(strcmp(c, "test----") == 0);
|
||||
sc_str_replace(&c, "--", "0");
|
||||
assert(strcmp(c, "test00") == 0);
|
||||
fail_strlen = true;
|
||||
assert(sc_str_replace(&c, "*", "2") == false);
|
||||
fail_strlen = false;
|
||||
sc_str_destroy(c);
|
||||
}
|
||||
|
||||
|
95
thread/CMakeLists.txt
Normal file
95
thread/CMakeLists.txt
Normal file
@ -0,0 +1,95 @@
|
||||
cmake_minimum_required(VERSION 3.5.1)
|
||||
project(sc_thread C)
|
||||
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_C_EXTENSIONS OFF)
|
||||
|
||||
add_executable(sc_thread thread_example.c sc_thread.h sc_thread.c)
|
||||
|
||||
if (NOT CMAKE_C_COMPILER_ID MATCHES "MSVC")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -pthread -Wall -pedantic -Werror -D_GNU_SOURCE")
|
||||
endif ()
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# --------------------- Test Configuration Start ---------------------------- #
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
||||
include(CTest)
|
||||
include(CheckCCompilerFlag)
|
||||
|
||||
enable_testing()
|
||||
|
||||
add_executable(${PROJECT_NAME}_test thread_test.c sc_thread.c)
|
||||
|
||||
target_compile_options(${PROJECT_NAME}_test PRIVATE -DSC_SIZE_MAX=4000ul)
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
if ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR
|
||||
"${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
|
||||
|
||||
target_compile_options(${PROJECT_NAME}_test PRIVATE -DSC_HAVE_WRAP)
|
||||
target_compile_options(${PROJECT_NAME}_test PRIVATE -fno-builtin)
|
||||
target_link_options(${PROJECT_NAME}_test PRIVATE
|
||||
-Wl,--wrap=pthread_attr_init)
|
||||
endif ()
|
||||
endif ()
|
||||
if ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR
|
||||
"${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang" OR
|
||||
"${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
|
||||
|
||||
target_compile_options(${PROJECT_NAME}_test PRIVATE -fno-omit-frame-pointer)
|
||||
|
||||
if (SANITIZER)
|
||||
target_compile_options(${PROJECT_NAME}_test PRIVATE -fsanitize=${SANITIZER})
|
||||
target_link_options(${PROJECT_NAME}_test PRIVATE -fsanitize=${SANITIZER})
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
add_test(NAME ${PROJECT_NAME}_test COMMAND ${PROJECT_NAME}_test)
|
||||
|
||||
SET(MEMORYCHECK_COMMAND_OPTIONS
|
||||
"-q --log-fd=2 --trace-children=yes --track-origins=yes \
|
||||
--leak-check=full --show-leak-kinds=all --show-reachable=yes \
|
||||
--error-exitcode=255")
|
||||
|
||||
add_custom_target(valgrind_${PROJECT_NAME} ${CMAKE_COMMAND}
|
||||
-E env CTEST_OUTPUT_ON_FAILURE=1
|
||||
${CMAKE_CTEST_COMMAND} -C $<CONFIG>
|
||||
--overwrite MemoryCheckCommandOptions=${MEMORYCHECK_COMMAND_OPTIONS}
|
||||
--verbose -T memcheck WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
|
||||
add_custom_target(check_${PROJECT_NAME} ${CMAKE_COMMAND}
|
||||
-E env CTEST_OUTPUT_ON_FAILURE=1
|
||||
${CMAKE_CTEST_COMMAND} -C $<CONFIG> --verbose
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
|
||||
# ----------------------- - Code Coverage Start ----------------------------- #
|
||||
|
||||
if (${CMAKE_BUILD_TYPE} MATCHES "Coverage")
|
||||
if ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
|
||||
target_compile_options(${PROJECT_NAME}_test PRIVATE --coverage)
|
||||
target_link_libraries(${PROJECT_NAME}_test gcov)
|
||||
else()
|
||||
message(FATAL_ERROR "Only GCC is supported for coverage")
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
add_custom_target(coverage_${PROJECT_NAME})
|
||||
add_custom_command(
|
||||
TARGET coverage_${PROJECT_NAME}
|
||||
COMMAND lcov --capture --directory ..
|
||||
--output-file coverage.info --rc lcov_branch_coverage=1
|
||||
COMMAND lcov --remove coverage.info '/usr/*' '*example*' '*test*'
|
||||
--output-file coverage.info --rc lcov_branch_coverage=1
|
||||
COMMAND lcov --list coverage.info --rc lcov_branch_coverage=1
|
||||
)
|
||||
|
||||
add_dependencies(coverage_${PROJECT_NAME} check_${PROJECT_NAME})
|
||||
|
||||
# -------------------------- Code Coverage End ------------------------------ #
|
||||
|
||||
|
||||
# ----------------------- Test Configuration End ---------------------------- #
|
||||
|
130
thread/sc_thread.c
Normal file
130
thread/sc_thread.c
Normal file
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020 Ozan Tezcan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "sc_thread.h"
|
||||
|
||||
void sc_thread_init(struct sc_thread* thread)
|
||||
{
|
||||
thread->id = 0;
|
||||
}
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#include <process.h>
|
||||
|
||||
unsigned int __stdcall sc_thread_fn(void* arg)
|
||||
{
|
||||
struct sc_thread* thread = arg;
|
||||
|
||||
thread->ret = thread->fn(thread->arg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sc_thread_start(struct sc_thread* thread, void* (*fn)(void*), void* arg)
|
||||
{
|
||||
int rc;
|
||||
|
||||
thread->fn = fn;
|
||||
thread->arg = arg;
|
||||
|
||||
thread->id = (HANDLE)_beginthreadex(NULL, 0, sc_thread_fn, thread, 0, NULL);
|
||||
rc = thread->id == 0 ? -1 : 0;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int sc_thread_stop(struct sc_thread* thread, void** ret)
|
||||
{
|
||||
int rc = 0;
|
||||
DWORD rv;
|
||||
BOOL brc;
|
||||
|
||||
if (thread->id == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rv = WaitForSingleObject(thread->id, INFINITE);
|
||||
if (rv == WAIT_FAILED) {
|
||||
rc = -1;
|
||||
}
|
||||
|
||||
brc = CloseHandle(thread->id);
|
||||
if (!brc) {
|
||||
rc = -1;
|
||||
}
|
||||
|
||||
thread->id = 0;
|
||||
if (ret != NULL) {
|
||||
*ret = thread->ret;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
#else
|
||||
|
||||
int sc_thread_start(struct sc_thread* thread, void* (*fn)(void*), void* arg)
|
||||
{
|
||||
int rc;
|
||||
pthread_attr_t hndl;
|
||||
|
||||
rc = pthread_attr_init(&hndl);
|
||||
if (rc != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// This may only fail with EINVAL.
|
||||
pthread_attr_setdetachstate(&hndl, PTHREAD_CREATE_JOINABLE);
|
||||
|
||||
rc = pthread_create(&thread->id, &hndl, fn, arg);
|
||||
|
||||
// This may only fail with EINVAL.
|
||||
pthread_attr_destroy(&hndl);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int sc_thread_stop(struct sc_thread* thread, void** ret)
|
||||
{
|
||||
int rc;
|
||||
void* val;
|
||||
|
||||
if (thread->id == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = pthread_join(thread->id, &val);
|
||||
thread->id = 0;
|
||||
|
||||
if (ret != NULL) {
|
||||
*ret = val;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int sc_thread_term(struct sc_thread* thread)
|
||||
{
|
||||
return sc_thread_stop(thread, NULL);
|
||||
}
|
55
thread/sc_thread.h
Normal file
55
thread/sc_thread.h
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020 Ozan Tezcan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef SC_THREAD_H
|
||||
#define SC_THREAD_H
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
struct sc_thread
|
||||
{
|
||||
HANDLE id;
|
||||
void* (*fn)(void*);
|
||||
void* arg;
|
||||
void* ret;
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
struct sc_thread
|
||||
{
|
||||
pthread_t id;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
void sc_thread_init(struct sc_thread* thread);
|
||||
int sc_thread_term(struct sc_thread* thread);
|
||||
int sc_thread_start(struct sc_thread* thread, void* (*fn)(void*), void* arg);
|
||||
int sc_thread_stop(struct sc_thread* thread, void** ret);
|
||||
|
||||
#endif
|
19
thread/thread_example.c
Normal file
19
thread/thread_example.c
Normal file
@ -0,0 +1,19 @@
|
||||
#include "sc_thread.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
void* fn(void* arg)
|
||||
{
|
||||
printf("%s \n", (char*) arg);
|
||||
return arg;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct sc_thread thread;
|
||||
|
||||
sc_thread_init(&thread);
|
||||
sc_thread_start(&thread, fn, "first");
|
||||
sc_thread_term(&thread);
|
||||
return 0;
|
||||
}
|
73
thread/thread_test.c
Normal file
73
thread/thread_test.c
Normal file
@ -0,0 +1,73 @@
|
||||
#include "sc_thread.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
void* fn(void* arg)
|
||||
{
|
||||
printf("%s \n", (char*) arg);
|
||||
return arg;
|
||||
}
|
||||
|
||||
void test1()
|
||||
{
|
||||
int rc;
|
||||
void* ret;
|
||||
struct sc_thread thread;
|
||||
|
||||
sc_thread_init(&thread);
|
||||
rc = sc_thread_start(&thread, fn, "first");
|
||||
assert(rc == 0);
|
||||
|
||||
rc = sc_thread_stop(&thread, &ret);
|
||||
assert(rc == 0);
|
||||
assert(strcmp((char*)ret, "first") == 0);
|
||||
|
||||
rc = sc_thread_term(&thread);
|
||||
assert(rc == -1);
|
||||
|
||||
sc_thread_init(&thread);
|
||||
rc = sc_thread_start(&thread, fn, "first");
|
||||
assert(rc == 0);
|
||||
rc = sc_thread_term(&thread);
|
||||
assert(rc == 0);
|
||||
}
|
||||
|
||||
#ifdef SC_HAVE_WRAP
|
||||
bool fail_pthread_attr_init = false;
|
||||
int __real_pthread_attr_init (pthread_attr_t *__attr);
|
||||
int __wrap_pthread_attr_init (pthread_attr_t *__attr)
|
||||
{
|
||||
if (fail_pthread_attr_init) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return __real_pthread_attr_init(__attr);
|
||||
}
|
||||
void fail_test()
|
||||
{
|
||||
struct sc_thread thread;
|
||||
|
||||
sc_thread_init(&thread);
|
||||
fail_pthread_attr_init = true;
|
||||
assert(sc_thread_start(&thread, fn, "first") != 0);
|
||||
fail_pthread_attr_init = false;
|
||||
assert(sc_thread_start(&thread, fn, "first") == 0);
|
||||
assert(sc_thread_term(&thread) == 0);
|
||||
}
|
||||
|
||||
#else
|
||||
void fail_test()
|
||||
{
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
test1();
|
||||
fail_test();
|
||||
return 0;
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
#include "sc_time.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
|
@ -26,7 +26,6 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <memory.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define TICK 16u
|
||||
#define WHEEL_COUNT 16u
|
||||
@ -39,8 +38,8 @@
|
||||
|
||||
bool sc_timer_init(struct sc_timer *timer, uint64_t timestamp)
|
||||
{
|
||||
const size_t wheel_cap = 4;
|
||||
const size_t cap = WHEEL_COUNT * wheel_cap;
|
||||
const uint32_t wheel_cap = 4;
|
||||
const uint32_t cap = WHEEL_COUNT * wheel_cap;
|
||||
const size_t size = cap * sizeof(struct sc_timer_data);
|
||||
|
||||
timer->count = 0;
|
||||
@ -50,6 +49,7 @@ bool sc_timer_init(struct sc_timer *timer, uint64_t timestamp)
|
||||
|
||||
timer->list = sc_timer_malloc(size);
|
||||
if (timer->list == NULL) {
|
||||
sc_timer_on_error("Out of memory. size(%zu) ", size);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -68,7 +68,7 @@ void sc_timer_term(struct sc_timer *timer)
|
||||
|
||||
void sc_timer_clear(struct sc_timer *timer)
|
||||
{
|
||||
const size_t cap = timer->wheel * WHEEL_COUNT;
|
||||
const uint32_t cap = timer->wheel * WHEEL_COUNT;
|
||||
|
||||
timer->count = 0;
|
||||
timer->head = 0;
|
||||
@ -81,17 +81,19 @@ void sc_timer_clear(struct sc_timer *timer)
|
||||
|
||||
static bool expand(struct sc_timer *timer)
|
||||
{
|
||||
size_t cap = timer->wheel * WHEEL_COUNT * 2;
|
||||
uint32_t cap = timer->wheel * WHEEL_COUNT * 2;
|
||||
size_t size = cap * sizeof(struct sc_timer_data);
|
||||
struct sc_timer_data *alloc;
|
||||
|
||||
// Check overflow
|
||||
if (timer->wheel > SC_CAP_MAX / 2) {
|
||||
sc_timer_on_error("Out of memory. timer->wheel(%zu) ", timer->wheel);
|
||||
return false;
|
||||
}
|
||||
|
||||
alloc = sc_timer_malloc(size);
|
||||
if (alloc == NULL) {
|
||||
sc_timer_on_error("Out of memory. size(%zu) ", size);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -121,7 +123,7 @@ uint64_t sc_timer_add(struct sc_timer *timer, void *data, uint64_t timeout)
|
||||
{
|
||||
const size_t pos = (timeout / TICK + timer->head) & (WHEEL_COUNT - 1);
|
||||
uint64_t id;
|
||||
size_t seq, index, wheel_pos;
|
||||
uint32_t seq, index, wheel_pos;
|
||||
|
||||
assert(timeout < UINT64_MAX);
|
||||
|
||||
@ -154,7 +156,7 @@ out:
|
||||
|
||||
void sc_timer_cancel(struct sc_timer *timer, uint64_t *id)
|
||||
{
|
||||
size_t pos;
|
||||
uint64_t pos;
|
||||
|
||||
if (*id == SC_TIMER_INVALID) {
|
||||
return;
|
||||
|
@ -41,15 +41,23 @@ struct sc_timer_data
|
||||
struct sc_timer
|
||||
{
|
||||
uint64_t timestamp;
|
||||
size_t head;
|
||||
size_t wheel;
|
||||
size_t count;
|
||||
uint32_t head;
|
||||
uint32_t wheel;
|
||||
uint32_t count;
|
||||
struct sc_timer_data *list;
|
||||
};
|
||||
|
||||
#define sc_timer_malloc malloc
|
||||
#define sc_timer_free free
|
||||
|
||||
/**
|
||||
* If you want to log or abort on errors like out of memory,
|
||||
* put your error function here. It will be called with printf like error msg.
|
||||
*
|
||||
* my_on_error(const char* fmt, ...);
|
||||
*/
|
||||
#define sc_timer_on_error(...)
|
||||
|
||||
/**
|
||||
* @param timer Timer
|
||||
* @param timestamp Current timestamp. Use monotonic timer source.
|
||||
|
@ -23,10 +23,20 @@ enable_testing()
|
||||
|
||||
add_executable(${PROJECT_NAME}_test url_test.c sc_url.c)
|
||||
|
||||
if ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR
|
||||
"${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang" OR
|
||||
"${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
|
||||
target_compile_options(${PROJECT_NAME}_test PRIVATE -DSC_SIZE_MAX=140000ul)
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
if ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR
|
||||
"${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
|
||||
target_compile_options(${PROJECT_NAME}_test PRIVATE -DSC_HAVE_WRAP)
|
||||
target_compile_options(${PROJECT_NAME}_test PRIVATE -fno-builtin)
|
||||
target_link_options(${PROJECT_NAME}_test PRIVATE -Wl,--wrap=malloc)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR
|
||||
"${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang" OR
|
||||
"${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
|
||||
target_compile_options(${PROJECT_NAME}_test PRIVATE -fno-omit-frame-pointer)
|
||||
|
||||
if (SANITIZER)
|
||||
|
219
url/sc_url.c
219
url/sc_url.c
@ -26,150 +26,135 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
struct sc_url *url_create(const char *str, const char *default_scheme,
|
||||
const char *default_port)
|
||||
struct sc_url *sc_url_create(const char *str)
|
||||
{
|
||||
size_t total_len = 0;
|
||||
size_t str_len = 0;
|
||||
bool ipv6 = false;
|
||||
const char *s1 = "%.*s%.*s%.*s%.*s%.*s%.*s%.*s%.*s";
|
||||
const char *s2 = "%.*s%c%.*s%c%.*s%c%.*s%c%.*s%c%.*s%c%.*s%c";
|
||||
const char *authority = "//";
|
||||
|
||||
size_t scheme_len = 0;
|
||||
size_t user_len = 0;
|
||||
size_t host_len = 0;
|
||||
size_t port_len = 0;
|
||||
size_t path_len = 0;
|
||||
size_t query_len = 0;
|
||||
size_t fragment_len = 0;
|
||||
|
||||
char *scheme = (char *) (default_scheme != NULL ? default_scheme : "");
|
||||
char *userinfo = "";
|
||||
char *host = NULL;
|
||||
char *port = (char *) (default_port != NULL ? default_port : "");
|
||||
char *path = "";
|
||||
char *query = "";
|
||||
char *fragment = "";
|
||||
|
||||
char *pos, *ptr;
|
||||
const char *end;
|
||||
unsigned long val;
|
||||
size_t len, full_len, parts_len;
|
||||
size_t scheme_len = 0, authority_len = 0, userinfo_len = 0;
|
||||
size_t host_len = 0, port_len = 0, path_len = 0;
|
||||
size_t query_len = 0, fragment_len = 0;
|
||||
|
||||
char *scheme = "", *userinfo = "", *host = "", *port = "";
|
||||
char *path = "", *query = "", *fragment = "";
|
||||
char *ptr, *dest, *parse_end;
|
||||
char* pos = (char*) str;
|
||||
struct sc_url *url;
|
||||
|
||||
end = str + strlen(str);
|
||||
pos = (char *) str;
|
||||
|
||||
ptr = strstr(pos, "://");
|
||||
if (ptr != NULL) {
|
||||
scheme = pos;
|
||||
scheme_len = ptr - str;
|
||||
pos = ptr + strlen("://");
|
||||
if (str == NULL || (ptr = strstr(pos, ":")) == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ptr = strchr(pos, '@');
|
||||
if (ptr != NULL) {
|
||||
userinfo = pos;
|
||||
user_len = ptr - pos;
|
||||
pos = ptr + strlen("@");
|
||||
}
|
||||
scheme = pos;
|
||||
scheme_len = ptr - str + 1;
|
||||
pos = ptr + 1;
|
||||
|
||||
ptr = strchr(pos, '[');
|
||||
if (ptr != NULL) {
|
||||
char *bracket = strrchr(pos, ']');
|
||||
if (bracket == NULL) {
|
||||
return NULL;
|
||||
if (*pos == '/' && *(pos + 1) == '/') {
|
||||
authority_len = 2;
|
||||
pos += authority_len;
|
||||
|
||||
ptr = strchr(pos, '@');
|
||||
if (ptr != NULL) {
|
||||
userinfo = pos;
|
||||
userinfo_len = ptr - pos + strlen("@");
|
||||
pos = ptr + 1;
|
||||
}
|
||||
|
||||
host = ptr + strlen("[");
|
||||
host_len = bracket - ptr - 1;
|
||||
pos = bracket + 1;
|
||||
ipv6 = true;
|
||||
}
|
||||
|
||||
ptr = strrchr(pos, ':');
|
||||
if (ptr != NULL) {
|
||||
char *parse_end;
|
||||
unsigned long val;
|
||||
|
||||
if (*(ptr + 1) == '\0') {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
val = strtoul(ptr + 1, &parse_end, 10);
|
||||
if (errno != 0 || val > 65536) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
port = ptr + 1;
|
||||
port_len = parse_end - ptr - 1;
|
||||
} else {
|
||||
ptr = (char *) end;
|
||||
}
|
||||
|
||||
ptr = strchr(pos, '/');
|
||||
if (ptr != NULL) {
|
||||
path = pos + strcspn(pos, "?");
|
||||
path_len = path - pos;
|
||||
}
|
||||
|
||||
if (host == NULL) {
|
||||
ptr = pos + strcspn(pos, *pos == '[' ? "]" : ":/?#");
|
||||
host = pos;
|
||||
host_len = ptr - pos;
|
||||
}
|
||||
host_len = ptr - pos + (*host == '[');
|
||||
pos = host + host_len;
|
||||
|
||||
const char *s1 = "%.*s://%.*s%.*s:%.*s";
|
||||
const char *s2 = "%.*s://%.*s[%.*s]:%.*s";
|
||||
const char *s3 = "%.*s://%.*s@%.*s:%.*s";
|
||||
const char *s4 = "%.*s://%.*s@[%.*s]:%.*s";
|
||||
const char *n1 = "%.*s%c://%.*s%.*s%c:%.*s";
|
||||
const char *n2 = "%.*s%c://%.*s[%.*s]%c:%.*s";
|
||||
const char *n3 = "%.*s%c://%.*s%c@%.*s%c:%.*s";
|
||||
const char *n4 = "%.*s%c://%.*s%c@[%.*s]%c:%.*s";
|
||||
const char *s5;
|
||||
const char *n5;
|
||||
|
||||
if (ipv6) {
|
||||
if (*userinfo == '\0') {
|
||||
s5 = s2;
|
||||
n5 = n2;
|
||||
} else {
|
||||
s5 = s4;
|
||||
n5 = n4;
|
||||
if (*host == '[' && *(host + host_len - 1) != ']') {
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
if (*userinfo == '\0') {
|
||||
s5 = s1;
|
||||
n5 = n1;
|
||||
} else {
|
||||
s5 = s3;
|
||||
n5 = n3;
|
||||
|
||||
ptr = strchr(pos, ':');
|
||||
if (ptr != NULL) {
|
||||
if (*(ptr + 1) == '\0') {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
val = strtoul(ptr + 1, &parse_end, 10);
|
||||
if (errno != 0 || val > 65536) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
port = ptr;
|
||||
port_len = parse_end - ptr;
|
||||
pos = port + port_len;
|
||||
}
|
||||
}
|
||||
|
||||
str_len = scheme_len + strlen("://") + user_len + (user_len ? 1 : 0) +
|
||||
host_len + port_len + 1;
|
||||
total_len = str_len + 1 + 1 + 1;
|
||||
path = pos;
|
||||
path_len = strcspn(path, "?#");
|
||||
pos = path + path_len;
|
||||
|
||||
url = sc_url_malloc(sizeof(struct sc_url) + total_len + str_len);
|
||||
ptr = strchr(pos, '?');
|
||||
if (ptr != NULL) {
|
||||
query = ptr;
|
||||
query_len = strcspn(query, "#");
|
||||
pos = query + query_len;
|
||||
}
|
||||
|
||||
if (*pos == '#') {
|
||||
fragment = pos;
|
||||
fragment_len = strlen(pos);
|
||||
}
|
||||
|
||||
full_len = scheme_len + authority_len + userinfo_len + host_len + port_len +
|
||||
path_len + query_len + fragment_len + 1;
|
||||
|
||||
parts_len = full_len - authority_len;
|
||||
parts_len += 7; // NULL characters for each part.
|
||||
parts_len -= (scheme_len != 0);
|
||||
parts_len -= (userinfo_len != 0);
|
||||
parts_len -= (port_len != 0);
|
||||
parts_len -= (query_len != 0);
|
||||
parts_len -= (fragment_len != 0);
|
||||
|
||||
url = sc_url_malloc(sizeof(struct sc_url) + parts_len + full_len);
|
||||
if (url == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sprintf(url->buf, s5, scheme_len, scheme, user_len, userinfo, host_len,
|
||||
host, port_len, port);
|
||||
char *dest = url->buf + strlen(url->buf) + 1;
|
||||
sprintf(dest, n5, scheme_len, scheme, 0, user_len, userinfo, 0, host_len,
|
||||
host, 0, port_len, port);
|
||||
len = snprintf(url->buf, full_len, s1, scheme_len, scheme, authority_len,
|
||||
authority, userinfo_len, userinfo, host_len, host, port_len,
|
||||
port, path_len, path, query_len, query, fragment_len,
|
||||
fragment);
|
||||
assert(len == full_len - 1);
|
||||
|
||||
dest = url->buf + strlen(url->buf) + 1;
|
||||
|
||||
scheme_len -= (scheme_len != 0); // Skip ":"
|
||||
userinfo_len -= (userinfo_len != 0); // Skip "@"
|
||||
port_len -= (port_len != 0); // Skip ":"
|
||||
port += (port_len != 0); // Skip ":"
|
||||
query_len -= (query_len != 0); // Skip "?"
|
||||
query += (query_len != 0); // Skip "?"
|
||||
fragment_len -= (fragment_len != 0); // Skip "#"
|
||||
fragment += (fragment_len != 0); // Skip "#"
|
||||
|
||||
len = sprintf(dest, s2, scheme_len, scheme, 0, userinfo_len, userinfo, 0,
|
||||
host_len, host, 0, port_len, port, 0, path_len, path, 0,
|
||||
query_len, query, 0, fragment_len, fragment, 0);
|
||||
assert(len == parts_len - 1);
|
||||
|
||||
url->str = url->buf;
|
||||
url->scheme = dest;
|
||||
url->userinfo = dest + scheme_len + strlen("://") + 1;
|
||||
url->host = url->userinfo + user_len + 1 + (*userinfo ? 1 : 0);
|
||||
url->port = url->host + host_len + 1 + (strlen(":"));
|
||||
url->ipv6 = ipv6;
|
||||
url->userinfo = dest + scheme_len + 1;
|
||||
url->host = url->userinfo + userinfo_len + 1;
|
||||
url->port = url->host + host_len + 1;
|
||||
url->path = url->port + port_len + 1;
|
||||
url->query = url->path + path_len + 1;
|
||||
url->fragment = url->query + query_len + 1;
|
||||
|
||||
return url;
|
||||
}
|
||||
|
14
url/sc_url.h
14
url/sc_url.h
@ -46,9 +46,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#define sc_url_malloc malloc
|
||||
#define sc_url_free free
|
||||
|
||||
struct sc_url
|
||||
{
|
||||
const char *str;
|
||||
@ -56,14 +53,17 @@ struct sc_url
|
||||
const char *host;
|
||||
const char *userinfo;
|
||||
const char *port;
|
||||
|
||||
bool ipv6;
|
||||
const char *path;
|
||||
const char *query;
|
||||
const char *fragment;
|
||||
|
||||
char buf[];
|
||||
};
|
||||
|
||||
struct sc_url *url_create(const char *str, const char *default_scheme,
|
||||
const char *default_port);
|
||||
#define sc_url_malloc malloc
|
||||
#define sc_url_free free
|
||||
|
||||
struct sc_url *sc_url_create(const char *str);
|
||||
void sc_url_destroy(struct sc_url *url);
|
||||
|
||||
#endif
|
||||
|
@ -1,26 +1,3 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020 Ozan Tezcan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
270
url/url_test.c
270
url/url_test.c
@ -1,42 +1,260 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020 Ozan Tezcan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "sc_url.h"
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
void test1(void)
|
||||
{
|
||||
struct sc_url *url;
|
||||
const char* f = "foo://user:password@example.com:8042/over/there?name=ferret#nose";
|
||||
|
||||
url = url_create("foo://user:password@example.com:8042/over/there?name=ferret#nose", NULL, NULL);
|
||||
url = sc_url_create(f);
|
||||
assert(url != NULL);
|
||||
assert(strcmp(url->str, f) == 0);
|
||||
assert(strcmp(url->scheme, "foo") == 0);
|
||||
assert(strcmp(url->userinfo, "user:password") == 0);
|
||||
assert(strcmp(url->host, "example.com") == 0);
|
||||
assert(strcmp(url->port, "8042") == 0);
|
||||
assert(strcmp(url->path, "/over/there") == 0);
|
||||
assert(strcmp(url->query, "name=ferret") == 0);
|
||||
assert(strcmp(url->fragment, "nose") == 0);
|
||||
|
||||
printf("%s \n", url->str);
|
||||
sc_url_destroy(url);
|
||||
}
|
||||
|
||||
void test2(void)
|
||||
{
|
||||
struct sc_url *url;
|
||||
const char* f = "https://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top";
|
||||
|
||||
url = sc_url_create(f);
|
||||
assert(url != NULL);
|
||||
assert(strcmp(url->str, f) == 0);
|
||||
assert(strcmp(url->scheme, "https") == 0);
|
||||
assert(strcmp(url->userinfo, "john.doe") == 0);
|
||||
assert(strcmp(url->host, "www.example.com") == 0);
|
||||
assert(strcmp(url->port, "123") == 0);
|
||||
assert(strcmp(url->path, "/forum/questions/") == 0);
|
||||
assert(strcmp(url->query, "tag=networking&order=newest") == 0);
|
||||
assert(strcmp(url->fragment, "top") == 0);
|
||||
|
||||
sc_url_destroy(url);
|
||||
}
|
||||
|
||||
void test3(void)
|
||||
{
|
||||
struct sc_url *url;
|
||||
const char* f = "ldap://[2001:db8::7]/c=GB?objectClass?one";
|
||||
|
||||
url = sc_url_create(f);
|
||||
assert(url != NULL);
|
||||
assert(strcmp(url->str, f) == 0);
|
||||
assert(strcmp(url->scheme, "ldap") == 0);
|
||||
assert(strcmp(url->userinfo, "") == 0);
|
||||
assert(strcmp(url->host, "[2001:db8::7]") == 0);
|
||||
assert(strcmp(url->port, "") == 0);
|
||||
assert(strcmp(url->path, "/c=GB") == 0);
|
||||
assert(strcmp(url->query, "objectClass?one") == 0);
|
||||
assert(strcmp(url->fragment, "") == 0);
|
||||
|
||||
sc_url_destroy(url);
|
||||
}
|
||||
|
||||
void test4(void)
|
||||
{
|
||||
struct sc_url *url;
|
||||
const char* f = "mailto:John.Doe@example.com";
|
||||
|
||||
url = sc_url_create(f);
|
||||
assert(url != NULL);
|
||||
assert(strcmp(url->str, f) == 0);
|
||||
assert(strcmp(url->scheme, "mailto") == 0);
|
||||
assert(strcmp(url->userinfo, "") == 0);
|
||||
assert(strcmp(url->host, "") == 0);
|
||||
assert(strcmp(url->port, "") == 0);
|
||||
assert(strcmp(url->path, "John.Doe@example.com") == 0);
|
||||
assert(strcmp(url->query, "") == 0);
|
||||
assert(strcmp(url->fragment, "") == 0);
|
||||
|
||||
sc_url_destroy(url);
|
||||
}
|
||||
|
||||
void test5(void)
|
||||
{
|
||||
struct sc_url *url;
|
||||
const char* f = "news:comp.infosystems.www.servers.unix";
|
||||
|
||||
url = sc_url_create(f);
|
||||
assert(url != NULL);
|
||||
assert(strcmp(url->str, f) == 0);
|
||||
assert(strcmp(url->scheme, "news") == 0);
|
||||
assert(strcmp(url->userinfo, "") == 0);
|
||||
assert(strcmp(url->host, "") == 0);
|
||||
assert(strcmp(url->port, "") == 0);
|
||||
assert(strcmp(url->path, "comp.infosystems.www.servers.unix") == 0);
|
||||
assert(strcmp(url->query, "") == 0);
|
||||
assert(strcmp(url->fragment, "") == 0);
|
||||
|
||||
sc_url_destroy(url);
|
||||
}
|
||||
|
||||
void test6(void)
|
||||
{
|
||||
struct sc_url *url;
|
||||
const char* f = "tel:+1-816-555-1212";
|
||||
|
||||
url = sc_url_create(f);
|
||||
assert(url != NULL);
|
||||
assert(strcmp(url->str, f) == 0);
|
||||
assert(strcmp(url->scheme, "tel") == 0);
|
||||
assert(strcmp(url->userinfo, "") == 0);
|
||||
assert(strcmp(url->host, "") == 0);
|
||||
assert(strcmp(url->port, "") == 0);
|
||||
assert(strcmp(url->path, "+1-816-555-1212") == 0);
|
||||
assert(strcmp(url->query, "") == 0);
|
||||
assert(strcmp(url->fragment, "") == 0);
|
||||
|
||||
sc_url_destroy(url);
|
||||
}
|
||||
|
||||
void test7(void)
|
||||
{
|
||||
struct sc_url *url;
|
||||
const char* f = "telnet://192.0.2.16:80/";
|
||||
|
||||
url = sc_url_create(f);
|
||||
assert(url != NULL);
|
||||
assert(strcmp(url->str, f) == 0);
|
||||
assert(strcmp(url->scheme, "telnet") == 0);
|
||||
assert(strcmp(url->userinfo, "") == 0);
|
||||
assert(strcmp(url->host, "192.0.2.16") == 0);
|
||||
assert(strcmp(url->port, "80") == 0);
|
||||
assert(strcmp(url->path, "/") == 0);
|
||||
assert(strcmp(url->query, "") == 0);
|
||||
assert(strcmp(url->fragment, "") == 0);
|
||||
|
||||
sc_url_destroy(url);
|
||||
}
|
||||
|
||||
void test8(void)
|
||||
{
|
||||
struct sc_url *url;
|
||||
const char* f = "urn:oasis:names:specification:docbook:dtd:xml:4.1.2";
|
||||
|
||||
url = sc_url_create(f);
|
||||
assert(url != NULL);
|
||||
assert(strcmp(url->str, f) == 0);
|
||||
assert(strcmp(url->scheme, "urn") == 0);
|
||||
assert(strcmp(url->userinfo, "") == 0);
|
||||
assert(strcmp(url->host, "") == 0);
|
||||
assert(strcmp(url->port, "") == 0);
|
||||
assert(strcmp(url->path, "oasis:names:specification:docbook:dtd:xml:4.1.2") == 0);
|
||||
assert(strcmp(url->query, "") == 0);
|
||||
assert(strcmp(url->fragment, "") == 0);
|
||||
|
||||
sc_url_destroy(url);
|
||||
}
|
||||
|
||||
void test9(void)
|
||||
{
|
||||
struct sc_url *url;
|
||||
const char* f = "foo://info.example.com?fred";
|
||||
|
||||
url = sc_url_create(f);
|
||||
assert(url != NULL);
|
||||
assert(strcmp(url->str, f) == 0);
|
||||
assert(strcmp(url->scheme, "foo") == 0);
|
||||
assert(strcmp(url->userinfo, "") == 0);
|
||||
assert(strcmp(url->host, "info.example.com") == 0);
|
||||
assert(strcmp(url->port, "") == 0);
|
||||
assert(strcmp(url->path, "") == 0);
|
||||
assert(strcmp(url->query, "fred") == 0);
|
||||
assert(strcmp(url->fragment, "") == 0);
|
||||
|
||||
sc_url_destroy(url);
|
||||
}
|
||||
|
||||
void test10(void)
|
||||
{
|
||||
struct sc_url *url;
|
||||
const char* f = "tcp://127.0.0.1:9090";
|
||||
|
||||
url = sc_url_create(f);
|
||||
assert(url != NULL);
|
||||
assert(strcmp(url->str, f) == 0);
|
||||
assert(strcmp(url->scheme, "tcp") == 0);
|
||||
assert(strcmp(url->userinfo, "") == 0);
|
||||
assert(strcmp(url->host, "127.0.0.1") == 0);
|
||||
assert(strcmp(url->port, "9090") == 0);
|
||||
assert(strcmp(url->path, "") == 0);
|
||||
assert(strcmp(url->query, "") == 0);
|
||||
assert(strcmp(url->fragment, "") == 0);
|
||||
|
||||
sc_url_destroy(url);
|
||||
}
|
||||
|
||||
void test11(void)
|
||||
{
|
||||
struct sc_url *url = NULL;
|
||||
|
||||
assert(sc_url_create("127.0.0.1") == NULL);
|
||||
assert(sc_url_create("") == NULL);
|
||||
assert(sc_url_create("/dsad") == NULL);
|
||||
assert(sc_url_create(NULL) == NULL);
|
||||
assert(sc_url_create("http://127.0.0.1:") == NULL);
|
||||
assert(sc_url_create("http://127.0.0.1:88888") == NULL);
|
||||
assert(sc_url_create("ldap://[2001:db8::7/c=GB?objectClass?one") == NULL);
|
||||
assert(sc_url_create("ldap://[2001:db8::7") == NULL);
|
||||
|
||||
|
||||
sc_url_destroy(url);
|
||||
}
|
||||
|
||||
#ifdef SC_HAVE_WRAP
|
||||
|
||||
bool fail_malloc = false;
|
||||
void *__real_malloc(size_t n);
|
||||
void *__wrap_malloc(size_t n)
|
||||
{
|
||||
if (fail_malloc) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return __real_malloc(n);
|
||||
}
|
||||
|
||||
void fail_test()
|
||||
{
|
||||
struct sc_url *url;
|
||||
fail_malloc = true;
|
||||
assert(sc_url_create("tcp://127.0.0.1") == NULL);
|
||||
fail_malloc = false;
|
||||
|
||||
url = sc_url_create("tcp://127.0.0.1");
|
||||
assert(url != NULL);
|
||||
|
||||
sc_url_destroy(url);
|
||||
}
|
||||
#else
|
||||
void fail_test()
|
||||
{
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
test1();
|
||||
test2();
|
||||
test3();
|
||||
test4();
|
||||
test5();
|
||||
test6();
|
||||
test7();
|
||||
test8();
|
||||
test9();
|
||||
test10();
|
||||
test11();
|
||||
fail_test();
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user