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:
|
freebsd_task:
|
||||||
only_if: $CIRRUS_BRANCH == 'master'
|
|
||||||
use_compute_credits: $CIRRUS_USER_COLLABORATOR == 'true'
|
|
||||||
freebsd_instance:
|
freebsd_instance:
|
||||||
image_family: freebsd-12-1
|
image_family: freebsd-12-1
|
||||||
cpu: 1
|
cpu: 1
|
||||||
@ -8,5 +6,5 @@ freebsd_task:
|
|||||||
test_script:
|
test_script:
|
||||||
- pkg install -y git cmake
|
- pkg install -y git cmake
|
||||||
- mkdir build && cd build
|
- mkdir build && cd build
|
||||||
- cmake -DSANITIZER=address .. && make -j && make check && rm -rf *
|
- cmake -DSANITIZER=address .. && make && make check && rm -rf *
|
||||||
- cmake -DSANITIZER=undefined .. && make -j && 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}")
|
message(STATUS "Build type ${CMAKE_BUILD_TYPE}")
|
||||||
|
|
||||||
add_subdirectory(array)
|
add_subdirectory(array)
|
||||||
|
add_subdirectory(condition)
|
||||||
add_subdirectory(crc32)
|
add_subdirectory(crc32)
|
||||||
add_subdirectory(heap)
|
add_subdirectory(heap)
|
||||||
add_subdirectory(ini)
|
add_subdirectory(ini)
|
||||||
@ -25,6 +26,7 @@ add_subdirectory(pipe)
|
|||||||
add_subdirectory(string)
|
add_subdirectory(string)
|
||||||
add_subdirectory(time)
|
add_subdirectory(time)
|
||||||
add_subdirectory(timer)
|
add_subdirectory(timer)
|
||||||
|
add_subdirectory(thread)
|
||||||
add_subdirectory(url)
|
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)
|
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;
|
struct sc_array *meta;
|
||||||
|
|
||||||
if (cap == 0) {
|
if (cap == 0) {
|
||||||
@ -49,13 +51,16 @@ bool sc_array_init(void **arr, size_t elem_size, size_t cap)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check overflow
|
// 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;
|
*arr = NULL;
|
||||||
return false;
|
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) {
|
if (meta == NULL) {
|
||||||
|
sc_array_on_error("Failed to allocate %zu bytes. ", bytes);
|
||||||
*arr = NULL;
|
*arr = NULL;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -80,13 +85,15 @@ void sc_array_term(void **arr)
|
|||||||
|
|
||||||
bool sc_array_expand(void **arr, size_t elem_size)
|
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);
|
struct sc_array *prev, *meta = sc_array_meta(*arr);
|
||||||
|
|
||||||
if (meta->size == meta->cap) {
|
if (meta->size == meta->cap) {
|
||||||
|
|
||||||
// Check overflow
|
// 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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,8 +101,10 @@ bool sc_array_expand(void **arr, size_t elem_size)
|
|||||||
cap = (meta != &sc_empty) ? meta->cap * 2 : 2;
|
cap = (meta != &sc_empty) ? meta->cap * 2 : 2;
|
||||||
prev = (meta != &sc_empty) ? meta : NULL;
|
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) {
|
if (meta == NULL) {
|
||||||
|
sc_array_on_error("Failed to allocate %zu bytes. ", bytes);
|
||||||
return false;
|
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,
|
* Configure memory allocators here. You can plug your allocator if you want,
|
||||||
* replace 'realloc' and 'free' with your allocator, make sure you include
|
* 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
|
// Check overflow
|
||||||
if (cap > SC_CAP_MAX || (elems = sc_heap_malloc(alloc)) == NULL) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,10 +77,11 @@ bool sc_heap_add(struct sc_heap *heap, int64_t key, void *data)
|
|||||||
|
|
||||||
if (++heap->size >= heap->cap) {
|
if (++heap->size >= heap->cap) {
|
||||||
const size_t cap = heap->cap != 0 ? heap->cap * 2 : 4;
|
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
|
// Check overflow
|
||||||
if (heap->cap >= SC_CAP_MAX / 2 ||
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +44,14 @@ struct sc_heap
|
|||||||
struct sc_heap_data *elems;
|
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.
|
* 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 -DSC_HAVE_WRAP)
|
||||||
target_compile_options(${PROJECT_NAME}_test PRIVATE -fno-builtin)
|
target_compile_options(${PROJECT_NAME}_test PRIVATE -fno-builtin)
|
||||||
target_link_options(${PROJECT_NAME}_test PRIVATE
|
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 ()
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
@ -164,12 +164,28 @@ int __wrap_pthread_mutexattr_init(pthread_mutexattr_t *attr)
|
|||||||
return -1;
|
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)
|
void fail_test(void)
|
||||||
{
|
{
|
||||||
mock_attrinit = true;
|
mock_attrinit = true;
|
||||||
assert(sc_log_init() < 0);
|
assert(sc_log_init() < 0);
|
||||||
mock_attrinit = false;
|
mock_attrinit = false;
|
||||||
|
mock_mutexinit = true;
|
||||||
|
assert(sc_log_init() < 0);
|
||||||
|
mock_mutexinit = false;
|
||||||
assert(sc_log_init() == 0);
|
assert(sc_log_init() == 0);
|
||||||
|
|
||||||
mock_fprintf = true;
|
mock_fprintf = true;
|
||||||
@ -297,6 +313,8 @@ void example(void)
|
|||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
sc_log_set_thread_name("My thread");
|
||||||
|
|
||||||
fail_test();
|
fail_test();
|
||||||
example();
|
example();
|
||||||
test1();
|
test1();
|
||||||
|
@ -24,12 +24,29 @@
|
|||||||
|
|
||||||
#include "sc_log.h"
|
#include "sc_log.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.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)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
|
|
||||||
#pragma warning(disable : 4996)
|
#pragma warning(disable : 4996)
|
||||||
@ -75,14 +92,24 @@ struct sc_log_mutex
|
|||||||
|
|
||||||
int sc_log_mutex_init(struct sc_log_mutex *mtx)
|
int sc_log_mutex_init(struct sc_log_mutex *mtx)
|
||||||
{
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
pthread_mutexattr_t attr;
|
pthread_mutexattr_t attr;
|
||||||
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
|
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
mtx->mtx = mut;
|
mtx->mtx = mut;
|
||||||
|
|
||||||
if (pthread_mutexattr_init(&attr) != 0 ||
|
rc = pthread_mutexattr_init(&attr);
|
||||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL) != 0 ||
|
if (rc != 0) {
|
||||||
pthread_mutex_init(&mtx->mtx, &attr) != 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;
|
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)
|
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)
|
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)
|
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
|
#endif
|
||||||
@ -126,17 +166,10 @@ struct sc_log sc_log;
|
|||||||
|
|
||||||
int sc_log_init(void)
|
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.level = SC_LOG_INFO;
|
||||||
sc_log.to_stdout = true;
|
sc_log.to_stdout = true;
|
||||||
|
|
||||||
return 0;
|
return sc_log_mutex_init(&sc_log.mtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
int sc_log_term(void)
|
int sc_log_term(void)
|
||||||
@ -145,6 +178,7 @@ int sc_log_term(void)
|
|||||||
|
|
||||||
if (sc_log.fp) {
|
if (sc_log.fp) {
|
||||||
rc = fclose(sc_log.fp);
|
rc = fclose(sc_log.fp);
|
||||||
|
assert(rc == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
sc_log_mutex_term(&sc_log.mtx);
|
sc_log_mutex_term(&sc_log.mtx);
|
||||||
@ -153,6 +187,11 @@ int sc_log_term(void)
|
|||||||
return rc;
|
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)
|
int sc_log_set_level(const char *str)
|
||||||
{
|
{
|
||||||
size_t count = sizeof(sc_log_levels) / sizeof(struct sc_log_level_pair);
|
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 -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_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)
|
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
|
// 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
|
* User callback
|
||||||
*
|
*
|
||||||
@ -76,6 +85,14 @@ int sc_log_init(void);
|
|||||||
*/
|
*/
|
||||||
int sc_log_term(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.
|
* 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_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_error(...) (sc_log_log(SC_LOG_ERROR, sc_log_ap(__VA_ARGS__, "")))
|
||||||
|
|
||||||
#define sc_log_(args)
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -336,7 +336,7 @@ void test4()
|
|||||||
char* c;
|
char* c;
|
||||||
struct sc_map_64s map64s;
|
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++) {
|
for (int i = 0 ; i < 100; i++) {
|
||||||
assert(sc_map_put_64s(&map64s, i, NULL));
|
assert(sc_map_put_64s(&map64s, i, NULL));
|
||||||
assert(sc_map_get_64s(&map64s, i, &c));
|
assert(sc_map_get_64s(&map64s, i, &c));
|
||||||
@ -358,7 +358,7 @@ void test4()
|
|||||||
void* v;
|
void* v;
|
||||||
struct sc_map_64v map64v;
|
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++) {
|
for (int i = 0 ; i < 100; i++) {
|
||||||
assert(sc_map_put_64v(&map64v, i, NULL));
|
assert(sc_map_put_64v(&map64v, i, NULL));
|
||||||
assert(sc_map_get_64v(&map64v, i, &v));
|
assert(sc_map_get_64v(&map64v, i, &v));
|
||||||
@ -377,19 +377,27 @@ void test4()
|
|||||||
|
|
||||||
sc_map_term_64v(&map64v);
|
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;
|
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++) {
|
for (int i = 0 ; i < 100; i++) {
|
||||||
assert(sc_map_put_sv(&mapsv, "", NULL));
|
assert(sc_map_put_sv(&mapsv, keys[i], values[i]));
|
||||||
assert(sc_map_get_sv(&mapsv, "", &v));
|
assert(sc_map_get_sv(&mapsv, keys[i], &v));
|
||||||
assert(v == NULL);
|
assert(v == values[i]);
|
||||||
}
|
}
|
||||||
assert(sc_map_size_sv(&mapsv) == 1);
|
assert(sc_map_size_sv(&mapsv) == 100);
|
||||||
assert(sc_map_del_sv(&mapsv, "", &v));
|
assert(sc_map_del_sv(&mapsv, keys[0], &v));
|
||||||
assert(v == NULL);
|
assert(v == values[0]);
|
||||||
assert(sc_map_size_sv(&mapsv) == 0);
|
assert(sc_map_size_sv(&mapsv) == 99);
|
||||||
assert(!sc_map_del_sv(&mapsv, "", &v));
|
assert(!sc_map_del_sv(&mapsv, keys[0], &v));
|
||||||
sc_map_clear_sv(&mapsv);
|
sc_map_clear_sv(&mapsv);
|
||||||
assert(sc_map_size_sv(&mapsv) == 0);
|
assert(sc_map_size_sv(&mapsv) == 0);
|
||||||
sc_map_term_sv(&mapsv);
|
sc_map_term_sv(&mapsv);
|
||||||
@ -398,16 +406,23 @@ void test4()
|
|||||||
struct sc_map_s64 maps64;
|
struct sc_map_s64 maps64;
|
||||||
|
|
||||||
assert(sc_map_init_s64(&maps64, 0, 26));
|
assert(sc_map_init_s64(&maps64, 0, 26));
|
||||||
assert(sc_map_put_s64(&maps64, "", 511));
|
for (int i =0 ; i< 64; i++) {
|
||||||
assert(sc_map_put_s64(&maps64, "", 511));
|
assert(sc_map_put_s64(&maps64, keys[i], i));
|
||||||
assert(sc_map_get_s64(&maps64, "", &val));
|
}
|
||||||
assert(val == 511);
|
|
||||||
assert(sc_map_size_s64(&maps64) == 1);
|
assert(sc_map_get_s64(&maps64, keys[0], &val));
|
||||||
assert(sc_map_del_s64(&maps64, "", &val));
|
assert(val == 0);
|
||||||
assert(val == 511);
|
assert(sc_map_size_s64(&maps64) == 64);
|
||||||
assert(sc_map_size_s64(&maps64) == 0);
|
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_clear_s64(&maps64);
|
||||||
sc_map_term_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 "sc_map.h"
|
||||||
|
|
||||||
#include <memory.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#ifndef SC_SIZE_MAX
|
#ifndef SC_SIZE_MAX
|
||||||
#define SC_SIZE_MAX UINT32_MAX
|
#define SC_SIZE_MAX UINT32_MAX
|
||||||
@ -57,9 +57,11 @@
|
|||||||
static void *sc_map_alloc_##name(uint32_t *cap, uint32_t factor) \
|
static void *sc_map_alloc_##name(uint32_t *cap, uint32_t factor) \
|
||||||
{ \
|
{ \
|
||||||
uint32_t v = *cap; \
|
uint32_t v = *cap; \
|
||||||
|
void *p; \
|
||||||
struct sc_map_item_##name *t; \
|
struct sc_map_item_##name *t; \
|
||||||
\
|
\
|
||||||
if (*cap > SC_SIZE_MAX / factor) { \
|
if (*cap > SC_SIZE_MAX / factor) { \
|
||||||
|
sc_map_on_error("Out of memory. cap(%zu).", *cap); \
|
||||||
return NULL; \
|
return NULL; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
@ -72,7 +74,12 @@
|
|||||||
v++; \
|
v++; \
|
||||||
\
|
\
|
||||||
*cap = 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, \
|
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 load_factor; \
|
||||||
uint32_t remap; \
|
uint32_t remap; \
|
||||||
V value; \
|
V value; \
|
||||||
bool used; \
|
bool used; \
|
||||||
}; \
|
}; \
|
||||||
\
|
\
|
||||||
bool sc_map_init_##name(struct sc_map_##name *map, uint32_t cap, \
|
bool sc_map_init_##name(struct sc_map_##name *map, uint32_t cap, \
|
||||||
uint32_t load_factor); \
|
uint32_t load_factor); \
|
||||||
void sc_map_term_##name(struct sc_map_##name *map); \
|
void sc_map_term_##name(struct sc_map_##name *map); \
|
||||||
uint32_t sc_map_size_##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); \
|
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_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_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_del_##name(struct sc_map_##name *map, K key, V *value);
|
||||||
|
|
||||||
#define sc_map_foreach(map, K, V) \
|
#define sc_map_foreach(map, K, V) \
|
||||||
for (uint32_t __i = 0, __b = 0; __i < (map)->cap; __i++) \
|
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)
|
sc_map_of_strkey(s64, char *, uint64_t)
|
||||||
|
|
||||||
// clang-format on
|
// 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
|
#endif
|
||||||
|
@ -54,7 +54,7 @@ void sc_mutex_unlock(struct sc_mutex *mtx)
|
|||||||
|
|
||||||
int sc_mutex_init(struct sc_mutex *mtx)
|
int sc_mutex_init(struct sc_mutex *mtx)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc, rv;
|
||||||
pthread_mutexattr_t attr;
|
pthread_mutexattr_t attr;
|
||||||
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
|
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
@ -63,6 +63,7 @@ int sc_mutex_init(struct sc_mutex *mtx)
|
|||||||
// May fail on OOM
|
// May fail on OOM
|
||||||
rc = pthread_mutexattr_init(&attr);
|
rc = pthread_mutexattr_init(&attr);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
|
sc_mutex_on_error("pthread_mutexattr_init : errcode(%d) ", rc);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,27 +73,45 @@ int sc_mutex_init(struct sc_mutex *mtx)
|
|||||||
|
|
||||||
// May fail on OOM
|
// May fail on OOM
|
||||||
rc = pthread_mutex_init(&mtx->mtx, &attr);
|
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.
|
// 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;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sc_mutex_term(struct sc_mutex *mtx)
|
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)
|
void sc_mutex_lock(struct sc_mutex *mtx)
|
||||||
{
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
// This won't fail as long as we pass correct param.
|
// 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)
|
void sc_mutex_unlock(struct sc_mutex *mtx)
|
||||||
{
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
// This won't fail as long as we pass correct param.
|
// 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
|
#endif
|
||||||
|
@ -45,4 +45,12 @@ int sc_mutex_term(struct sc_mutex *mtx);
|
|||||||
void sc_mutex_lock(struct sc_mutex *mtx);
|
void sc_mutex_lock(struct sc_mutex *mtx);
|
||||||
void sc_mutex_unlock(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
|
#endif
|
||||||
|
@ -8,7 +8,7 @@ set(CMAKE_C_EXTENSIONS OFF)
|
|||||||
add_executable(sc_pipe pipe_example.c sc_pipe.h sc_pipe.c)
|
add_executable(sc_pipe pipe_example.c sc_pipe.h sc_pipe.c)
|
||||||
|
|
||||||
if (NOT CMAKE_C_COMPILER_ID MATCHES "MSVC")
|
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 ()
|
endif ()
|
||||||
|
|
||||||
|
|
||||||
@ -23,9 +23,21 @@ enable_testing()
|
|||||||
|
|
||||||
add_executable(${PROJECT_NAME}_test pipe_test.c sc_pipe.c)
|
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
|
if ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR
|
||||||
"${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang" OR
|
"${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang" OR
|
||||||
"${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
|
"${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
|
||||||
|
|
||||||
target_compile_options(${PROJECT_NAME}_test PRIVATE -fno-omit-frame-pointer)
|
target_compile_options(${PROJECT_NAME}_test PRIVATE -fno-omit-frame-pointer)
|
||||||
|
|
||||||
|
@ -26,6 +26,53 @@
|
|||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.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)
|
void test1(void)
|
||||||
{
|
{
|
||||||
@ -51,10 +98,12 @@ int main(int argc, char* argv[])
|
|||||||
HIBYTE(data.wVersion) == 2);
|
HIBYTE(data.wVersion) == 2);
|
||||||
#endif
|
#endif
|
||||||
test1();
|
test1();
|
||||||
|
fail_test();
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
rc = WSACleanup();
|
rc = WSACleanup();
|
||||||
assert(rc == 0);
|
assert(rc == 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -22,23 +22,24 @@
|
|||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <string.h>
|
#include "sc_pipe.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
#include "sc_pipe.h"
|
|
||||||
|
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
|
|
||||||
int sc_pipe_init(struct sc_pipe* p)
|
int sc_pipe_init(struct sc_pipe *p)
|
||||||
{
|
{
|
||||||
SOCKET listener;
|
SOCKET listener;
|
||||||
int rc;
|
int rc;
|
||||||
struct sockaddr_in addr;
|
struct sockaddr_in addr;
|
||||||
size_t addrlen;
|
int addrlen = sizeof(addr);
|
||||||
int one;
|
int val = 1;
|
||||||
BOOL nodelay;
|
BOOL nodelay = 1;
|
||||||
u_long nonblock;
|
u_long nonblock;
|
||||||
|
|
||||||
p->w = INVALID_SOCKET;
|
p->w = INVALID_SOCKET;
|
||||||
@ -50,26 +51,23 @@ int sc_pipe_init(struct sc_pipe* p)
|
|||||||
goto wsafail;
|
goto wsafail;
|
||||||
}
|
}
|
||||||
|
|
||||||
one = 1;
|
rc = setsockopt(listener, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *) &val,
|
||||||
rc = setsockopt(listener, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
|
sizeof(val));
|
||||||
(char*)&one, sizeof(one));
|
|
||||||
if (rc == SOCKET_ERROR) {
|
if (rc == SOCKET_ERROR) {
|
||||||
goto wsafail;
|
goto wsafail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Bind the listening socket to the local port. */
|
|
||||||
memset(&addr, 0, sizeof(addr));
|
memset(&addr, 0, sizeof(addr));
|
||||||
addr.sin_family = AF_INET;
|
addr.sin_family = AF_INET;
|
||||||
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||||
addr.sin_port = 0;
|
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) {
|
if (rc == SOCKET_ERROR) {
|
||||||
goto wsafail;
|
goto wsafail;
|
||||||
}
|
}
|
||||||
|
|
||||||
addrlen = sizeof(addr);
|
rc = getsockname(listener, (struct sockaddr *) &addr, &addrlen);
|
||||||
rc = getsockname(listener, (struct sockaddr*)&addr, &addrlen);
|
|
||||||
if (rc == SOCKET_ERROR) {
|
if (rc == SOCKET_ERROR) {
|
||||||
goto wsafail;
|
goto wsafail;
|
||||||
}
|
}
|
||||||
@ -84,70 +82,70 @@ int sc_pipe_init(struct sc_pipe* p)
|
|||||||
goto wsafail;
|
goto wsafail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set TCP_NODELAY on the writer socket to make efd as fast as possible.
|
rc = setsockopt(p->w, IPPROTO_TCP, TCP_NODELAY, (char *) &nodelay,
|
||||||
There's only one byte going to be written, so batching would not make
|
sizeof(nodelay));
|
||||||
sense anyway. */
|
|
||||||
nodelay = 1;
|
|
||||||
rc = setsockopt(p->w, IPPROTO_TCP, TCP_NODELAY, (char*)&nodelay,
|
|
||||||
sizeof(nodelay));
|
|
||||||
if (rc == SOCKET_ERROR) {
|
if (rc == SOCKET_ERROR) {
|
||||||
goto wsafail;
|
goto wsafail;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = connect(p->w, (struct sockaddr*)&addr, sizeof(addr));
|
rc = connect(p->w, (struct sockaddr *) &addr, sizeof(addr));
|
||||||
if (rc == SOCKET_ERROR) {
|
if (rc == SOCKET_ERROR) {
|
||||||
goto wsafail;
|
goto wsafail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p->r = accept(listener, (struct sockaddr *) &addr, &addrlen);
|
||||||
addrlen = sizeof(addr);
|
|
||||||
p->r = accept(listener, (struct sockaddr*)&addr, &addrlen);
|
|
||||||
if (p->r == INVALID_SOCKET) {
|
if (p->r == INVALID_SOCKET) {
|
||||||
goto wsafail;
|
goto wsafail;
|
||||||
}
|
}
|
||||||
|
|
||||||
(void)closesocket(listener);
|
closesocket(listener);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
wsafail:
|
wsafail:
|
||||||
|
sc_pipe_on_error("sc_pipe_init() : %d ", WSAGetLastError())
|
||||||
return -1;
|
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;
|
SOCKET s;
|
||||||
|
|
||||||
rc = closesocket(p->r);
|
rv = closesocket(p->r);
|
||||||
rc |= closesocket(p->w);
|
if (rv != 0) {
|
||||||
|
rc = -1;
|
||||||
|
sc_pipe_on_error("closesocket() : errcode(%d) ", WSAGetLastError());
|
||||||
|
}
|
||||||
|
|
||||||
if (rc != 0) {
|
rv = closesocket(p->w);
|
||||||
sc_pipe_abort();
|
if (rv != 0) {
|
||||||
|
rc = -1;
|
||||||
|
sc_pipe_on_error("closesocket() : errcode(%d) ", WSAGetLastError());
|
||||||
}
|
}
|
||||||
|
|
||||||
return rc;
|
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;
|
int rc;
|
||||||
|
|
||||||
rc = send(p->w, data, len, 0);
|
rc = send(p->w, data, len, 0);
|
||||||
if (rc == SOCKET_ERROR || rc != len) {
|
if (rc == SOCKET_ERROR || rc != len) {
|
||||||
sc_pipe_abort();
|
sc_pipe_on_error("send() : errcode(%d) ", WSAGetLastError());
|
||||||
}
|
}
|
||||||
|
|
||||||
return rc;
|
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;
|
int rc;
|
||||||
|
|
||||||
rc = recv(p->r, (char*)data, len, 0);
|
rc = recv(p->r, (char *) data, len, 0);
|
||||||
if (rc == SOCKET_ERROR || rc != len) {
|
if (rc == SOCKET_ERROR || rc != len) {
|
||||||
sc_pipe_abort();
|
sc_pipe_on_error("recv() : errcode(%d) ", WSAGetLastError());
|
||||||
}
|
}
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
@ -155,15 +153,15 @@ int sc_pipe_read(struct sc_pipe* p, void* data, int len)
|
|||||||
|
|
||||||
#else
|
#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;
|
int rc;
|
||||||
|
|
||||||
rc = pipe(p->fds);
|
rc = pipe(p->fds);
|
||||||
if (rc == -1) {
|
if (rc == -1) {
|
||||||
sc_pipe_abort();
|
sc_pipe_on_error("pipe() : %d ", errno);
|
||||||
return -1;
|
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 sc_pipe_term(struct sc_pipe *nfd)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc = 0, rv;
|
||||||
|
|
||||||
rc = close(nfd->fds[0]);
|
rv = close(nfd->fds[0]);
|
||||||
rc |= close(nfd->fds[1]);
|
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;
|
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);
|
n = write(nfd->fds[1], data, len);
|
||||||
if (n != len) {
|
if (n != len) {
|
||||||
sc_pipe_abort();
|
sc_pipe_on_error("pipe() : %d ", errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
return n;
|
return n;
|
||||||
@ -199,8 +206,8 @@ int sc_pipe_read(struct sc_pipe *nfd, void *data, int len)
|
|||||||
ssize_t n;
|
ssize_t n;
|
||||||
|
|
||||||
n = read(nfd->fds[0], data, len);
|
n = read(nfd->fds[0], data, len);
|
||||||
if (n == -1) {
|
if (n != len) {
|
||||||
sc_pipe_abort();
|
sc_pipe_on_error("pipe() : %d ", errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
return n;
|
return n;
|
||||||
|
@ -27,8 +27,6 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
#define sc_pipe_abort()
|
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
#include <windows.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_write(struct sc_pipe *pipe, void *data, int len);
|
||||||
int sc_pipe_read(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
|
#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)
|
static void *queue_alloc(void *prev, size_t elem_size, size_t *cap)
|
||||||
{
|
{
|
||||||
|
size_t alloc;
|
||||||
size_t v = *cap;
|
size_t v = *cap;
|
||||||
void *t;
|
void *t;
|
||||||
|
|
||||||
if (*cap > SC_MAX_CAP) {
|
if (*cap > SC_MAX_CAP) {
|
||||||
|
sc_queue_on_error("Max capacity has been exceed. cap(%zu). ", (*cap));
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,7 +53,12 @@ static void *queue_alloc(void *prev, size_t elem_size, size_t *cap)
|
|||||||
}
|
}
|
||||||
v++;
|
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;
|
*cap = v;
|
||||||
|
|
||||||
return t;
|
return t;
|
||||||
|
@ -85,6 +85,14 @@ void sc_queue_term(void **q);
|
|||||||
bool sc_queue_expand(void **q, size_t elem_size);
|
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.
|
* 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 -DSC_HAVE_WRAP)
|
||||||
target_compile_options(${PROJECT_NAME}_test PRIVATE -fno-builtin)
|
target_compile_options(${PROJECT_NAME}_test PRIVATE -fno-builtin)
|
||||||
target_link_options(${PROJECT_NAME}_test PRIVATE
|
target_link_options(${PROJECT_NAME}_test PRIVATE
|
||||||
-Wl,--wrap=malloc,--wrap=vsnprintf,--wrap=realloc)
|
-Wl,--wrap=malloc,--wrap=vsnprintf,--wrap=realloc,--wrap=strlen)
|
||||||
|
|
||||||
target_link_options(${PROJECT_NAME}_test PRIVATE
|
|
||||||
-Wl,--wrap=malloc,--wrap=vsnprintf,--wrap=realloc)
|
|
||||||
endif ()
|
endif ()
|
||||||
endif ()
|
endif ()
|
||||||
if ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR
|
if ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR
|
||||||
|
@ -60,6 +60,7 @@ char *sc_str_create(const char *str)
|
|||||||
|
|
||||||
size_t size = strlen(str);
|
size_t size = strlen(str);
|
||||||
if (size > SC_SIZE_MAX) {
|
if (size > SC_SIZE_MAX) {
|
||||||
|
sc_str_on_error("Max size has been exceed. size(%zu). ", size);
|
||||||
return NULL;
|
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));
|
copy = sc_str_malloc(sc_str_bytes(len));
|
||||||
if (copy == NULL) {
|
if (copy == NULL) {
|
||||||
|
sc_str_on_error("Out of memory. size(%zu). ", sc_str_bytes(len));
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,12 +96,14 @@ char *sc_str_create_va(const char *fmt, va_list va)
|
|||||||
va_copy(args, va);
|
va_copy(args, va);
|
||||||
rc = vsnprintf(tmp, sizeof(tmp), fmt, args);
|
rc = vsnprintf(tmp, sizeof(tmp), fmt, args);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
|
sc_str_on_error("vsnprintf: %d. ", rc);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
str = sc_str_malloc(sc_str_bytes(rc));
|
str = sc_str_malloc(sc_str_bytes(rc));
|
||||||
if (str == NULL) {
|
if (str == NULL) {
|
||||||
|
sc_str_on_error("Out of memory. size(%zu). ", sc_str_bytes(rc));
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,6 +117,7 @@ char *sc_str_create_va(const char *fmt, va_list va)
|
|||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
if (rc < 0 || rc > str->len) {
|
if (rc < 0 || rc > str->len) {
|
||||||
|
sc_str_on_error("vsnprintf: %d. ", rc);
|
||||||
sc_str_free(str);
|
sc_str_free(str);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -197,6 +202,7 @@ bool sc_str_append(char **str, const char *param)
|
|||||||
size_t alloc = sc_str_bytes(meta->len + len);
|
size_t alloc = sc_str_bytes(meta->len + len);
|
||||||
|
|
||||||
if (alloc > SC_SIZE_MAX || (meta = sc_str_realloc(meta, alloc)) == NULL) {
|
if (alloc > SC_SIZE_MAX || (meta = sc_str_realloc(meta, alloc)) == NULL) {
|
||||||
|
sc_str_on_error("Out of memory. alloc(%zu). ", alloc);
|
||||||
return false;
|
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 replace_len = strlen(replace);
|
||||||
size_t with_len = strlen(with);
|
size_t with_len = strlen(with);
|
||||||
if (replace_len > UINT32_MAX || with_len > UINT32_MAX) {
|
int64_t diff = (int64_t) with_len - (int64_t) replace_len;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
int64_t diff = (int64_t)with_len - replace_len;
|
|
||||||
size_t len_unmatch;
|
size_t len_unmatch;
|
||||||
size_t count, size;
|
size_t count, size;
|
||||||
struct sc_str *dest;
|
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 *orig_end = *str + meta->len;
|
||||||
char *tmp;
|
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.
|
// Fast path, same size replacement.
|
||||||
if (diff == 0) {
|
if (diff == 0) {
|
||||||
while ((orig = strstr(orig, replace)) != NULL) {
|
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;
|
tmp += replace_len;
|
||||||
// Check overflow.
|
// Check overflow.
|
||||||
if (size > SC_SIZE_MAX - diff) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
size += diff;
|
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));
|
dest = sc_str_malloc(sc_str_bytes(size));
|
||||||
if (!dest) {
|
if (!dest) {
|
||||||
|
sc_str_on_error("Out of memory. size(%zu). ", sc_str_bytes(size));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
dest->len = size;
|
dest->len = size;
|
||||||
|
@ -49,6 +49,14 @@
|
|||||||
#define sc_str_realloc realloc
|
#define sc_str_realloc realloc
|
||||||
#define sc_str_free free
|
#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.
|
* @param str '\0' terminated C string, must not be NULL.
|
||||||
* @return Length prefixed string. NULL on out of memory.
|
* @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);
|
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;
|
bool fail_vsnprintf;
|
||||||
int fail_vsnprintf_at = -1;
|
int fail_vsnprintf_at = -1;
|
||||||
extern int __real_vsnprintf(char *str, size_t size, const char *format,
|
extern int __real_vsnprintf(char *str, size_t size, const char *format,
|
||||||
@ -175,6 +186,9 @@ void test2()
|
|||||||
assert(strcmp(c, "test----") == 0);
|
assert(strcmp(c, "test----") == 0);
|
||||||
sc_str_replace(&c, "--", "0");
|
sc_str_replace(&c, "--", "0");
|
||||||
assert(strcmp(c, "test00") == 0);
|
assert(strcmp(c, "test00") == 0);
|
||||||
|
fail_strlen = true;
|
||||||
|
assert(sc_str_replace(&c, "*", "2") == false);
|
||||||
|
fail_strlen = false;
|
||||||
sc_str_destroy(c);
|
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 "sc_time.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
|
@ -26,7 +26,6 @@
|
|||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <memory.h>
|
#include <memory.h>
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#define TICK 16u
|
#define TICK 16u
|
||||||
#define WHEEL_COUNT 16u
|
#define WHEEL_COUNT 16u
|
||||||
@ -39,8 +38,8 @@
|
|||||||
|
|
||||||
bool sc_timer_init(struct sc_timer *timer, uint64_t timestamp)
|
bool sc_timer_init(struct sc_timer *timer, uint64_t timestamp)
|
||||||
{
|
{
|
||||||
const size_t wheel_cap = 4;
|
const uint32_t wheel_cap = 4;
|
||||||
const size_t cap = WHEEL_COUNT * wheel_cap;
|
const uint32_t cap = WHEEL_COUNT * wheel_cap;
|
||||||
const size_t size = cap * sizeof(struct sc_timer_data);
|
const size_t size = cap * sizeof(struct sc_timer_data);
|
||||||
|
|
||||||
timer->count = 0;
|
timer->count = 0;
|
||||||
@ -50,6 +49,7 @@ bool sc_timer_init(struct sc_timer *timer, uint64_t timestamp)
|
|||||||
|
|
||||||
timer->list = sc_timer_malloc(size);
|
timer->list = sc_timer_malloc(size);
|
||||||
if (timer->list == NULL) {
|
if (timer->list == NULL) {
|
||||||
|
sc_timer_on_error("Out of memory. size(%zu) ", size);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ void sc_timer_term(struct sc_timer *timer)
|
|||||||
|
|
||||||
void sc_timer_clear(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->count = 0;
|
||||||
timer->head = 0;
|
timer->head = 0;
|
||||||
@ -81,17 +81,19 @@ void sc_timer_clear(struct sc_timer *timer)
|
|||||||
|
|
||||||
static bool expand(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);
|
size_t size = cap * sizeof(struct sc_timer_data);
|
||||||
struct sc_timer_data *alloc;
|
struct sc_timer_data *alloc;
|
||||||
|
|
||||||
// Check overflow
|
// Check overflow
|
||||||
if (timer->wheel > SC_CAP_MAX / 2) {
|
if (timer->wheel > SC_CAP_MAX / 2) {
|
||||||
|
sc_timer_on_error("Out of memory. timer->wheel(%zu) ", timer->wheel);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
alloc = sc_timer_malloc(size);
|
alloc = sc_timer_malloc(size);
|
||||||
if (alloc == NULL) {
|
if (alloc == NULL) {
|
||||||
|
sc_timer_on_error("Out of memory. size(%zu) ", size);
|
||||||
return false;
|
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);
|
const size_t pos = (timeout / TICK + timer->head) & (WHEEL_COUNT - 1);
|
||||||
uint64_t id;
|
uint64_t id;
|
||||||
size_t seq, index, wheel_pos;
|
uint32_t seq, index, wheel_pos;
|
||||||
|
|
||||||
assert(timeout < UINT64_MAX);
|
assert(timeout < UINT64_MAX);
|
||||||
|
|
||||||
@ -154,7 +156,7 @@ out:
|
|||||||
|
|
||||||
void sc_timer_cancel(struct sc_timer *timer, uint64_t *id)
|
void sc_timer_cancel(struct sc_timer *timer, uint64_t *id)
|
||||||
{
|
{
|
||||||
size_t pos;
|
uint64_t pos;
|
||||||
|
|
||||||
if (*id == SC_TIMER_INVALID) {
|
if (*id == SC_TIMER_INVALID) {
|
||||||
return;
|
return;
|
||||||
|
@ -41,15 +41,23 @@ struct sc_timer_data
|
|||||||
struct sc_timer
|
struct sc_timer
|
||||||
{
|
{
|
||||||
uint64_t timestamp;
|
uint64_t timestamp;
|
||||||
size_t head;
|
uint32_t head;
|
||||||
size_t wheel;
|
uint32_t wheel;
|
||||||
size_t count;
|
uint32_t count;
|
||||||
struct sc_timer_data *list;
|
struct sc_timer_data *list;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define sc_timer_malloc malloc
|
#define sc_timer_malloc malloc
|
||||||
#define sc_timer_free free
|
#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 timer Timer
|
||||||
* @param timestamp Current timestamp. Use monotonic timer source.
|
* @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)
|
add_executable(${PROJECT_NAME}_test url_test.c sc_url.c)
|
||||||
|
|
||||||
if ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR
|
target_compile_options(${PROJECT_NAME}_test PRIVATE -DSC_SIZE_MAX=140000ul)
|
||||||
"${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang" OR
|
|
||||||
"${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
|
|
||||||
|
|
||||||
|
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)
|
target_compile_options(${PROJECT_NAME}_test PRIVATE -fno-omit-frame-pointer)
|
||||||
|
|
||||||
if (SANITIZER)
|
if (SANITIZER)
|
||||||
|
219
url/sc_url.c
219
url/sc_url.c
@ -26,150 +26,135 @@
|
|||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
struct sc_url *url_create(const char *str, const char *default_scheme,
|
struct sc_url *sc_url_create(const char *str)
|
||||||
const char *default_port)
|
|
||||||
{
|
{
|
||||||
size_t total_len = 0;
|
const char *s1 = "%.*s%.*s%.*s%.*s%.*s%.*s%.*s%.*s";
|
||||||
size_t str_len = 0;
|
const char *s2 = "%.*s%c%.*s%c%.*s%c%.*s%c%.*s%c%.*s%c%.*s%c";
|
||||||
bool ipv6 = false;
|
const char *authority = "//";
|
||||||
|
|
||||||
size_t scheme_len = 0;
|
unsigned long val;
|
||||||
size_t user_len = 0;
|
size_t len, full_len, parts_len;
|
||||||
size_t host_len = 0;
|
size_t scheme_len = 0, authority_len = 0, userinfo_len = 0;
|
||||||
size_t port_len = 0;
|
size_t host_len = 0, port_len = 0, path_len = 0;
|
||||||
size_t path_len = 0;
|
size_t query_len = 0, fragment_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;
|
|
||||||
|
|
||||||
|
char *scheme = "", *userinfo = "", *host = "", *port = "";
|
||||||
|
char *path = "", *query = "", *fragment = "";
|
||||||
|
char *ptr, *dest, *parse_end;
|
||||||
|
char* pos = (char*) str;
|
||||||
struct sc_url *url;
|
struct sc_url *url;
|
||||||
|
|
||||||
end = str + strlen(str);
|
if (str == NULL || (ptr = strstr(pos, ":")) == NULL) {
|
||||||
pos = (char *) str;
|
return NULL;
|
||||||
|
|
||||||
ptr = strstr(pos, "://");
|
|
||||||
if (ptr != NULL) {
|
|
||||||
scheme = pos;
|
|
||||||
scheme_len = ptr - str;
|
|
||||||
pos = ptr + strlen("://");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr = strchr(pos, '@');
|
scheme = pos;
|
||||||
if (ptr != NULL) {
|
scheme_len = ptr - str + 1;
|
||||||
userinfo = pos;
|
pos = ptr + 1;
|
||||||
user_len = ptr - pos;
|
|
||||||
pos = ptr + strlen("@");
|
|
||||||
}
|
|
||||||
|
|
||||||
ptr = strchr(pos, '[');
|
if (*pos == '/' && *(pos + 1) == '/') {
|
||||||
if (ptr != NULL) {
|
authority_len = 2;
|
||||||
char *bracket = strrchr(pos, ']');
|
pos += authority_len;
|
||||||
if (bracket == NULL) {
|
|
||||||
return NULL;
|
ptr = strchr(pos, '@');
|
||||||
|
if (ptr != NULL) {
|
||||||
|
userinfo = pos;
|
||||||
|
userinfo_len = ptr - pos + strlen("@");
|
||||||
|
pos = ptr + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
host = ptr + strlen("[");
|
ptr = pos + strcspn(pos, *pos == '[' ? "]" : ":/?#");
|
||||||
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) {
|
|
||||||
host = pos;
|
host = pos;
|
||||||
host_len = ptr - pos;
|
host_len = ptr - pos + (*host == '[');
|
||||||
}
|
pos = host + host_len;
|
||||||
|
|
||||||
const char *s1 = "%.*s://%.*s%.*s:%.*s";
|
if (*host == '[' && *(host + host_len - 1) != ']') {
|
||||||
const char *s2 = "%.*s://%.*s[%.*s]:%.*s";
|
return NULL;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (*userinfo == '\0') {
|
ptr = strchr(pos, ':');
|
||||||
s5 = s1;
|
if (ptr != NULL) {
|
||||||
n5 = n1;
|
if (*(ptr + 1) == '\0') {
|
||||||
} else {
|
return NULL;
|
||||||
s5 = s3;
|
}
|
||||||
n5 = n3;
|
|
||||||
|
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) +
|
path = pos;
|
||||||
host_len + port_len + 1;
|
path_len = strcspn(path, "?#");
|
||||||
total_len = str_len + 1 + 1 + 1;
|
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) {
|
if (url == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
sprintf(url->buf, s5, scheme_len, scheme, user_len, userinfo, host_len,
|
len = snprintf(url->buf, full_len, s1, scheme_len, scheme, authority_len,
|
||||||
host, port_len, port);
|
authority, userinfo_len, userinfo, host_len, host, port_len,
|
||||||
char *dest = url->buf + strlen(url->buf) + 1;
|
port, path_len, path, query_len, query, fragment_len,
|
||||||
sprintf(dest, n5, scheme_len, scheme, 0, user_len, userinfo, 0, host_len,
|
fragment);
|
||||||
host, 0, port_len, port);
|
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->str = url->buf;
|
||||||
url->scheme = dest;
|
url->scheme = dest;
|
||||||
url->userinfo = dest + scheme_len + strlen("://") + 1;
|
url->userinfo = dest + scheme_len + 1;
|
||||||
url->host = url->userinfo + user_len + 1 + (*userinfo ? 1 : 0);
|
url->host = url->userinfo + userinfo_len + 1;
|
||||||
url->port = url->host + host_len + 1 + (strlen(":"));
|
url->port = url->host + host_len + 1;
|
||||||
url->ipv6 = ipv6;
|
url->path = url->port + port_len + 1;
|
||||||
|
url->query = url->path + path_len + 1;
|
||||||
|
url->fragment = url->query + query_len + 1;
|
||||||
|
|
||||||
return url;
|
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
|
struct sc_url
|
||||||
{
|
{
|
||||||
const char *str;
|
const char *str;
|
||||||
@ -56,14 +53,17 @@ struct sc_url
|
|||||||
const char *host;
|
const char *host;
|
||||||
const char *userinfo;
|
const char *userinfo;
|
||||||
const char *port;
|
const char *port;
|
||||||
|
const char *path;
|
||||||
bool ipv6;
|
const char *query;
|
||||||
|
const char *fragment;
|
||||||
|
|
||||||
char buf[];
|
char buf[];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sc_url *url_create(const char *str, const char *default_scheme,
|
#define sc_url_malloc malloc
|
||||||
const char *default_port);
|
#define sc_url_free free
|
||||||
|
|
||||||
|
struct sc_url *sc_url_create(const char *str);
|
||||||
void sc_url_destroy(struct sc_url *url);
|
void sc_url_destroy(struct sc_url *url);
|
||||||
|
|
||||||
#endif
|
#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[])
|
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 "sc_url.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
void test1(void)
|
void test1(void)
|
||||||
{
|
{
|
||||||
struct sc_url *url;
|
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);
|
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[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
test1();
|
test1();
|
||||||
|
test2();
|
||||||
|
test3();
|
||||||
|
test4();
|
||||||
|
test5();
|
||||||
|
test6();
|
||||||
|
test7();
|
||||||
|
test8();
|
||||||
|
test9();
|
||||||
|
test10();
|
||||||
|
test11();
|
||||||
|
fail_test();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user