mirror of
https://github.com/biojppm/rapidyaml.git
synced 2026-01-18 21:41:18 +01:00
wip [ci skip]
This commit is contained in:
24
.github/workflows-in/Makefile
vendored
24
.github/workflows-in/Makefile
vendored
@@ -1,14 +1,10 @@
|
||||
SHELL := bash
|
||||
|
||||
YS_VERSION := 0.2.4
|
||||
YS_PREFIX := $(shell cd ../.. ; pwd)/build/ys
|
||||
YS := $(YS_PREFIX)/bin/ys-$(YS_VERSION)
|
||||
include ../../proj/common.mk
|
||||
include ../../proj/ys.mk
|
||||
|
||||
INPUT_FILES := $(wildcard */*)
|
||||
SOURCE_FILES := $(wildcard *.ys)
|
||||
TARGET_FILES := $(SOURCE_FILES:%.ys=../workflows/%.yml)
|
||||
|
||||
export PATH := $(YS_PREFIX)/bin:$(PATH)
|
||||
export YSPATH := $(shell pwd -P)/ys
|
||||
|
||||
|
||||
@@ -44,20 +40,8 @@ force:
|
||||
|
||||
|
||||
../workflows/%.yml: %.ys $(YS) $(INPUT_FILES)
|
||||
@if [ -f "$@" ] ; then chmod a+w $@ ; fi
|
||||
@echo "# DO NOT EDIT - GENERATED FROM .github/workflows-in/$<" > $@
|
||||
@echo >> $@
|
||||
$(YS) -Y $< >> $@
|
||||
@chmod a-w $@
|
||||
@wc -lm --total=never $< $@ || wc -lm $< $@
|
||||
|
||||
|
||||
# Auto install a specific version of ys
|
||||
install-ys: $(YS)
|
||||
$(YS):
|
||||
mkdir -p $(YS_PREFIX)
|
||||
curl -s https://yamlscript.org/install | \
|
||||
BIN=1 VERSION=$(YS_VERSION) PREFIX=$(YS_PREFIX) bash
|
||||
@$(call ys_gen_file,--yaml,$<,$@)
|
||||
@$(call ys_wc_file,$<,$@)
|
||||
|
||||
|
||||
stats:
|
||||
|
||||
4
.github/workflows-in/benchmarks.ys
vendored
4
.github/workflows-in/benchmarks.ys
vendored
@@ -35,6 +35,10 @@ jobs:
|
||||
- name: Install python 3.10 for plotting
|
||||
uses: actions/setup-python@v5
|
||||
with: { python-version: '3.10' }
|
||||
- name: checkout c4core
|
||||
run: |
|
||||
cd ext
|
||||
./c4core.sh
|
||||
- name: install benchmark plotting dependencies
|
||||
run: |
|
||||
which python
|
||||
|
||||
2
.github/workflows-in/gcc.ys
vendored
2
.github/workflows-in/gcc.ys
vendored
@@ -204,7 +204,7 @@ jobs:
|
||||
matrix:
|
||||
item =:
|
||||
\({:std %1, :bt %2, :cxx 'arm-linux-gnueabihf-gcc',
|
||||
:toolchain 'ext/c4core/cmake/Toolchain-Arm-ubuntu.cmake'})
|
||||
:toolchain 'proj/c4proj/toolchains/Toolchain-Arm-ubuntu.cmake'})
|
||||
include:
|
||||
# these jobs take much longer, so run only one bitlink pair per job to
|
||||
# profit from parallelism
|
||||
|
||||
32
.github/workflows-in/infra.ys
vendored
32
.github/workflows-in/infra.ys
vendored
@@ -24,12 +24,32 @@ jobs:
|
||||
run: |
|
||||
wc --version
|
||||
wc --help
|
||||
make -C .github/workflows-in test
|
||||
status=$?
|
||||
if [ $status != 0 ] ; then
|
||||
echo "ERROR: generated workflows are out of date"
|
||||
fi
|
||||
exit $status
|
||||
make -C proj workflows-check || \
|
||||
( echo "ERROR: generated workflows are out of date" ; exit 1 )
|
||||
- name: check cmake presets
|
||||
run: |
|
||||
make -C proj presets-check || \
|
||||
( echo "ERROR: generated presets are out of date" ; exit 1 )
|
||||
|
||||
|
||||
check_c4core_src:
|
||||
:: setup-job('infra' 'check_c4core_src')
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- :: checkout-action
|
||||
- name: install colordiff
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y colordiff
|
||||
- name: check src list
|
||||
run: |
|
||||
make -C ext c4core-check-list || \
|
||||
( echo "ERROR: c4core source lists out of date" ; exit 1 )
|
||||
- name: check src sync
|
||||
run: |
|
||||
make -C ext c4core-check-sync || \
|
||||
( echo "ERROR: c4core source sync out of date" ; exit 1 )
|
||||
|
||||
|
||||
check_doc:
|
||||
:: setup-job('infra' 'check_doxygen')
|
||||
|
||||
4
.github/workflows-in/install.ys
vendored
4
.github/workflows-in/install.ys
vendored
@@ -64,6 +64,10 @@ jobs:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
with: {submodules: recursive}
|
||||
- name: checkout c4core
|
||||
run: |
|
||||
cd ext
|
||||
./c4core.sh
|
||||
- name: install requirements
|
||||
run: source .github/reqs.sh && c4_install_test_requirements $OS
|
||||
- name: show info
|
||||
|
||||
4
.github/workflows-in/samples.ys
vendored
4
.github/workflows-in/samples.ys
vendored
@@ -27,6 +27,10 @@ jobs:
|
||||
with: {python-version: 3.9}
|
||||
- name: install requirements
|
||||
run: source .github/reqs.sh && c4_install_test_requirements $OS
|
||||
- name: checkout c4core
|
||||
run: |
|
||||
cd ext
|
||||
./c4core.sh
|
||||
- name: show info
|
||||
run: source .github/setenv.sh && c4_show_info
|
||||
- name: singleheader
|
||||
|
||||
30
.github/workflows-in/ys/common.ys
vendored
30
.github/workflows-in/ys/common.ys
vendored
@@ -330,3 +330,33 @@ defn run-vs-manual-with-flags(gen arch buildtype shared flags="")::
|
||||
source .github/setenv.sh
|
||||
export CTEST_PARALLEL_LEVEL=`_c4getnumcores`
|
||||
cmake --build build/$buildtype --target ryml-test-run
|
||||
|
||||
|
||||
#----------------------------------------------------------
|
||||
|
||||
defn xcompile-steps(arch buildtype std flags="")::
|
||||
- name: "-------------------------------------------------"
|
||||
run:: "echo $arch/c++$std/$buildtype"
|
||||
- name:: "configure $arch/c++$std/$buildtype"
|
||||
run:: |
|
||||
touch build
|
||||
rm -rf build
|
||||
export C4_EXTERN_DIR=`pwd`/build/extern
|
||||
cmake -S . -B build \
|
||||
-DCMAKE_TOOLCHAIN_FILE=proj/c4proj/toolchains/${{matrix.arch}}.cmake \
|
||||
-DCMAKE_BUILD_TYPE=$buildtype \
|
||||
-DCMAKE_CXX_FLAGS=" $flags" \
|
||||
-DCMAKE_C_FLAGS=" $flags" \
|
||||
-DC4_CXX_STANDARD=$std \
|
||||
-DCXX_STANDARD=$std \
|
||||
-DRYML_DEV=ON \
|
||||
-DRYML_BUILD_BENCHMARKS=OFF \
|
||||
-DRYML_SANITIZE=OFF \
|
||||
-DRYML_LINT=OFF \
|
||||
-DRYML_VALGRIND=OFF
|
||||
- name:: "build $arch/c++$std/$buildtype"
|
||||
run: |
|
||||
cmake --build build --parallel --target ryml-test-build --verbose
|
||||
- name:: "test $arch/c++$std/$buildtype"
|
||||
run: |
|
||||
cmake --build build --target ryml-test-run
|
||||
|
||||
6
.github/workflows/benchmarks.yml
vendored
6
.github/workflows/benchmarks.yml
vendored
@@ -1,4 +1,4 @@
|
||||
# DO NOT EDIT - GENERATED FROM .github/workflows-in/benchmarks.ys
|
||||
# DO NOT EDIT - GENERATED FROM benchmarks.ys
|
||||
|
||||
name: benchmarks
|
||||
defaults:
|
||||
@@ -159,6 +159,10 @@ jobs:
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.10'
|
||||
- name: checkout c4core
|
||||
run: |
|
||||
cd ext
|
||||
./c4core.sh
|
||||
- name: install benchmark plotting dependencies
|
||||
run: |
|
||||
which python
|
||||
|
||||
2
.github/workflows/clang.yml
vendored
2
.github/workflows/clang.yml
vendored
@@ -1,4 +1,4 @@
|
||||
# DO NOT EDIT - GENERATED FROM .github/workflows-in/clang.ys
|
||||
# DO NOT EDIT - GENERATED FROM clang.ys
|
||||
|
||||
name: clang
|
||||
defaults:
|
||||
|
||||
2
.github/workflows/clang_tidy.yml
vendored
2
.github/workflows/clang_tidy.yml
vendored
@@ -1,4 +1,4 @@
|
||||
# DO NOT EDIT - GENERATED FROM .github/workflows-in/clang_tidy.ys
|
||||
# DO NOT EDIT - GENERATED FROM clang_tidy.ys
|
||||
|
||||
name: clang_tidy
|
||||
defaults:
|
||||
|
||||
2
.github/workflows/codeql.yml
vendored
2
.github/workflows/codeql.yml
vendored
@@ -1,4 +1,4 @@
|
||||
# DO NOT EDIT - GENERATED FROM .github/workflows-in/codeql.ys
|
||||
# DO NOT EDIT - GENERATED FROM codeql.ys
|
||||
|
||||
name: CodeQL
|
||||
'on':
|
||||
|
||||
2
.github/workflows/coverage.yml
vendored
2
.github/workflows/coverage.yml
vendored
@@ -1,4 +1,4 @@
|
||||
# DO NOT EDIT - GENERATED FROM .github/workflows-in/coverage.ys
|
||||
# DO NOT EDIT - GENERATED FROM coverage.ys
|
||||
|
||||
name: coverage
|
||||
defaults:
|
||||
|
||||
8
.github/workflows/embedded.yml
vendored
8
.github/workflows/embedded.yml
vendored
@@ -260,7 +260,7 @@ jobs:
|
||||
rm -rf build
|
||||
export C4_EXTERN_DIR=`pwd`/build/extern
|
||||
cmake -S . -B build \
|
||||
-DCMAKE_TOOLCHAIN_FILE=ext/c4core/.github/toolchains/${{matrix.arch}}.cmake \
|
||||
-DCMAKE_TOOLCHAIN_FILE=proj/c4proj/toolchains/${{matrix.arch}}.cmake \
|
||||
-DCMAKE_BUILD_TYPE=Debug \
|
||||
-DCMAKE_CXX_FLAGS=" ${{matrix.flags}}" \
|
||||
-DCMAKE_C_FLAGS=" ${{matrix.flags}}" \
|
||||
@@ -286,7 +286,7 @@ jobs:
|
||||
rm -rf build
|
||||
export C4_EXTERN_DIR=`pwd`/build/extern
|
||||
cmake -S . -B build \
|
||||
-DCMAKE_TOOLCHAIN_FILE=ext/c4core/.github/toolchains/${{matrix.arch}}.cmake \
|
||||
-DCMAKE_TOOLCHAIN_FILE=proj/c4proj/toolchains/${{matrix.arch}}.cmake \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_CXX_FLAGS=" ${{matrix.flags}}" \
|
||||
-DCMAKE_C_FLAGS=" ${{matrix.flags}}" \
|
||||
@@ -376,7 +376,7 @@ jobs:
|
||||
rm -rf build
|
||||
export C4_EXTERN_DIR=`pwd`/build/extern
|
||||
cmake -S . -B build \
|
||||
-DCMAKE_TOOLCHAIN_FILE=ext/c4core/.github/toolchains/${{matrix.arch}}.cmake \
|
||||
-DCMAKE_TOOLCHAIN_FILE=proj/c4proj/toolchains/${{matrix.arch}}.cmake \
|
||||
-DCMAKE_BUILD_TYPE=Debug \
|
||||
-DCMAKE_CXX_FLAGS=" ${{matrix.flags}}" \
|
||||
-DCMAKE_C_FLAGS=" ${{matrix.flags}}" \
|
||||
@@ -402,7 +402,7 @@ jobs:
|
||||
rm -rf build
|
||||
export C4_EXTERN_DIR=`pwd`/build/extern
|
||||
cmake -S . -B build \
|
||||
-DCMAKE_TOOLCHAIN_FILE=ext/c4core/.github/toolchains/${{matrix.arch}}.cmake \
|
||||
-DCMAKE_TOOLCHAIN_FILE=proj/c4proj/toolchains/${{matrix.arch}}.cmake \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_CXX_FLAGS=" ${{matrix.flags}}" \
|
||||
-DCMAKE_C_FLAGS=" ${{matrix.flags}}" \
|
||||
|
||||
2
.github/workflows/emscripten.yml
vendored
2
.github/workflows/emscripten.yml
vendored
@@ -1,4 +1,4 @@
|
||||
# DO NOT EDIT - GENERATED FROM .github/workflows-in/emscripten.ys
|
||||
# DO NOT EDIT - GENERATED FROM emscripten.ys
|
||||
|
||||
name: emscripten
|
||||
defaults:
|
||||
|
||||
14
.github/workflows/gcc.yml
vendored
14
.github/workflows/gcc.yml
vendored
@@ -1,4 +1,4 @@
|
||||
# DO NOT EDIT - GENERATED FROM .github/workflows-in/gcc.ys
|
||||
# DO NOT EDIT - GENERATED FROM gcc.ys
|
||||
|
||||
name: gcc
|
||||
defaults:
|
||||
@@ -1333,27 +1333,27 @@ jobs:
|
||||
- std: 11
|
||||
bt: Debug
|
||||
cxx: arm-linux-gnueabihf-gcc
|
||||
toolchain: ext/c4core/cmake/Toolchain-Arm-ubuntu.cmake
|
||||
toolchain: proj/c4proj/toolchains/Toolchain-Arm-ubuntu.cmake
|
||||
- std: 11
|
||||
bt: Release
|
||||
cxx: arm-linux-gnueabihf-gcc
|
||||
toolchain: ext/c4core/cmake/Toolchain-Arm-ubuntu.cmake
|
||||
toolchain: proj/c4proj/toolchains/Toolchain-Arm-ubuntu.cmake
|
||||
- std: 14
|
||||
bt: Debug
|
||||
cxx: arm-linux-gnueabihf-gcc
|
||||
toolchain: ext/c4core/cmake/Toolchain-Arm-ubuntu.cmake
|
||||
toolchain: proj/c4proj/toolchains/Toolchain-Arm-ubuntu.cmake
|
||||
- std: 14
|
||||
bt: Release
|
||||
cxx: arm-linux-gnueabihf-gcc
|
||||
toolchain: ext/c4core/cmake/Toolchain-Arm-ubuntu.cmake
|
||||
toolchain: proj/c4proj/toolchains/Toolchain-Arm-ubuntu.cmake
|
||||
- std: 17
|
||||
bt: Debug
|
||||
cxx: arm-linux-gnueabihf-gcc
|
||||
toolchain: ext/c4core/cmake/Toolchain-Arm-ubuntu.cmake
|
||||
toolchain: proj/c4proj/toolchains/Toolchain-Arm-ubuntu.cmake
|
||||
- std: 17
|
||||
bt: Release
|
||||
cxx: arm-linux-gnueabihf-gcc
|
||||
toolchain: ext/c4core/cmake/Toolchain-Arm-ubuntu.cmake
|
||||
toolchain: proj/c4proj/toolchains/Toolchain-Arm-ubuntu.cmake
|
||||
env:
|
||||
TOOLCHAIN: ${{matrix.toolchain}}
|
||||
STD: ${{matrix.std}}
|
||||
|
||||
36
.github/workflows/infra.yml
vendored
36
.github/workflows/infra.yml
vendored
@@ -1,4 +1,4 @@
|
||||
# DO NOT EDIT - GENERATED FROM .github/workflows-in/infra.ys
|
||||
# DO NOT EDIT - GENERATED FROM infra.ys
|
||||
|
||||
name: infra
|
||||
defaults:
|
||||
@@ -37,12 +37,34 @@ jobs:
|
||||
run: |
|
||||
wc --version
|
||||
wc --help
|
||||
make -C .github/workflows-in test
|
||||
status=$?
|
||||
if [ $status != 0 ] ; then
|
||||
echo "ERROR: generated workflows are out of date"
|
||||
fi
|
||||
exit $status
|
||||
make -C proj workflows-check || \
|
||||
( echo "ERROR: generated workflows are out of date" ; exit 1 )
|
||||
- name: check cmake presets
|
||||
run: |
|
||||
make -C proj presets-check || \
|
||||
( echo "ERROR: generated presets are out of date" ; exit 1 )
|
||||
check_c4core_src:
|
||||
if: always()
|
||||
continue-on-error: false
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: checkout (action)
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
- name: install colordiff
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y colordiff
|
||||
- name: check src list
|
||||
run: |
|
||||
make -C ext c4core-check-list || \
|
||||
( echo "ERROR: c4core source lists out of date" ; exit 1 )
|
||||
- name: check src sync
|
||||
run: |
|
||||
make -C ext c4core-check-sync || \
|
||||
( echo "ERROR: c4core source sync out of date" ; exit 1 )
|
||||
check_doc:
|
||||
if: always()
|
||||
continue-on-error: false
|
||||
|
||||
6
.github/workflows/install.yml
vendored
6
.github/workflows/install.yml
vendored
@@ -1,4 +1,4 @@
|
||||
# DO NOT EDIT - GENERATED FROM .github/workflows-in/install.ys
|
||||
# DO NOT EDIT - GENERATED FROM install.ys
|
||||
|
||||
name: test_install
|
||||
defaults:
|
||||
@@ -232,6 +232,10 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: checkout c4core
|
||||
run: |
|
||||
cd ext
|
||||
./c4core.sh
|
||||
- name: install requirements
|
||||
run: source .github/reqs.sh && c4_install_test_requirements $OS
|
||||
- name: show info
|
||||
|
||||
2
.github/workflows/macosx.yml
vendored
2
.github/workflows/macosx.yml
vendored
@@ -1,4 +1,4 @@
|
||||
# DO NOT EDIT - GENERATED FROM .github/workflows-in/macosx.ys
|
||||
# DO NOT EDIT - GENERATED FROM macosx.ys
|
||||
|
||||
name: macosx
|
||||
defaults:
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -1,4 +1,4 @@
|
||||
# DO NOT EDIT - GENERATED FROM .github/workflows-in/release.ys
|
||||
# DO NOT EDIT - GENERATED FROM release.ys
|
||||
|
||||
name: release
|
||||
defaults:
|
||||
|
||||
6
.github/workflows/samples.yml
vendored
6
.github/workflows/samples.yml
vendored
@@ -1,4 +1,4 @@
|
||||
# DO NOT EDIT - GENERATED FROM .github/workflows-in/samples.ys
|
||||
# DO NOT EDIT - GENERATED FROM samples.ys
|
||||
|
||||
name: samples
|
||||
defaults:
|
||||
@@ -61,6 +61,10 @@ jobs:
|
||||
python-version: 3.9
|
||||
- name: install requirements
|
||||
run: source .github/reqs.sh && c4_install_test_requirements $OS
|
||||
- name: checkout c4core
|
||||
run: |
|
||||
cd ext
|
||||
./c4core.sh
|
||||
- name: show info
|
||||
run: source .github/setenv.sh && c4_show_info
|
||||
- name: singleheader
|
||||
|
||||
2
.github/workflows/windows.yml
vendored
2
.github/workflows/windows.yml
vendored
@@ -1,4 +1,4 @@
|
||||
# DO NOT EDIT - GENERATED FROM .github/workflows-in/windows.ys
|
||||
# DO NOT EDIT - GENERATED FROM windows.ys
|
||||
|
||||
name: windows
|
||||
defaults:
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,3 +1,6 @@
|
||||
# external files
|
||||
ext/c4core.git/
|
||||
|
||||
# text editor files
|
||||
*.bck
|
||||
\#*
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,3 +0,0 @@
|
||||
[submodule "extern/c4core"]
|
||||
path = ext/c4core
|
||||
url = https://github.com/biojppm/c4core
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
include(./ext/c4core/cmake/c4Project.cmake)
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
project(ryml
|
||||
DESCRIPTION "Rapid YAML parsing and emitting"
|
||||
HOMEPAGE_URL "https://github.com/biojppm/rapidyaml"
|
||||
LANGUAGES CXX)
|
||||
include(./compat.cmake)
|
||||
include(./proj/compat.cmake)
|
||||
include(./proj/c4proj/c4Project.cmake)
|
||||
|
||||
c4_project(VERSION 0.10.0 STANDALONE
|
||||
AUTHOR "Joao Paulo Magalhaes <dev@jpmag.me>")
|
||||
@@ -28,15 +28,11 @@ option(RYML_INSTALL "Enable install target" ON)
|
||||
|
||||
#-------------------------------------------------------
|
||||
|
||||
c4_require_subproject(c4core INCORPORATE
|
||||
SUBDIRECTORY ${RYML_EXT_DIR}/c4core
|
||||
OVERRIDE C4CORE_INSTALL ${RYML_INSTALL}
|
||||
)
|
||||
|
||||
c4_add_library(ryml
|
||||
SOURCES
|
||||
ryml.hpp
|
||||
ryml_std.hpp
|
||||
ryml.natvis
|
||||
c4/yml/detail/checks.hpp
|
||||
c4/yml/detail/dbgprint.hpp
|
||||
c4/yml/detail/print.hpp
|
||||
@@ -77,15 +73,29 @@ c4_add_library(ryml
|
||||
c4/yml/version.cpp
|
||||
c4/yml/writer.hpp
|
||||
c4/yml/yml.hpp
|
||||
ryml.natvis
|
||||
SOURCE_ROOT ${RYML_SRC_DIR}
|
||||
INC_DIRS
|
||||
$<BUILD_INTERFACE:${RYML_SRC_DIR}>
|
||||
$<INSTALL_INTERFACE:include>
|
||||
LIBS c4core
|
||||
INCORPORATE c4core
|
||||
)
|
||||
|
||||
if(NOT RYML_STANDALONE)
|
||||
target_link_libraries(ryml PUBLIC c4core)
|
||||
else()
|
||||
function(_ryml_add_sources_to_lib lib filelist filedir)
|
||||
file(STRINGS ${filelist} _src)
|
||||
_c4_transform_to_full_path(_src _ ${filedir})
|
||||
target_sources(${lib} PRIVATE ${_src})
|
||||
target_include_directories(${lib} PUBLIC $<BUILD_INTERFACE:${filedir}>)
|
||||
list(FILTER _src EXCLUDE REGEX .*cpp)
|
||||
c4_install_files("${_src}" ${CMAKE_INSTALL_PREFIX}/include ${filedir})
|
||||
endfunction()
|
||||
_ryml_add_sources_to_lib(ryml ext/c4core.src.txt ${CMAKE_CURRENT_LIST_DIR}/ext/c4core.src)
|
||||
if(RYML_BUILD_TESTS OR RYML_BUILD_BENCHMARKS)
|
||||
_ryml_add_sources_to_lib(ryml ext/c4core.dev.txt ${CMAKE_CURRENT_LIST_DIR}/ext/c4core.dev)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(RYML_WITH_TAB_TOKENS)
|
||||
target_compile_definitions(ryml PUBLIC RYML_WITH_TAB_TOKENS)
|
||||
endif()
|
||||
@@ -121,10 +131,30 @@ endif()
|
||||
|
||||
if(RYML_INSTALL)
|
||||
c4_install_target(ryml)
|
||||
c4_install_exports(DEPENDENCIES c4core)
|
||||
if(RYML_STANDALONE)
|
||||
c4_install_exports()
|
||||
else()
|
||||
c4_install_exports(DEPENDENCIES c4core)
|
||||
endif()
|
||||
c4_pack_project()
|
||||
endif()
|
||||
|
||||
# generate and install the install manifest
|
||||
# https://stackoverflow.com/questions/66752091/is-there-a-way-in-cmake-to-save-away-the-install-manifest-txt-for-later
|
||||
install(CODE "
|
||||
string(REPLACE \"\$\{CMAKE_INSTALL_PREFIX\}/\" \"\" ryml_manifest \"\$\{CMAKE_INSTALL_MANIFEST_FILES\}\")\n\
|
||||
list(APPEND ryml_manifest share/ryml/MANIFEST.txt)\n\
|
||||
list(SORT ryml_manifest)\n\
|
||||
string(REPLACE \";\" \"\\n\" ryml_manifest \"\$\{ryml_manifest\}\")\n\
|
||||
file(WRITE ${CMAKE_BINARY_DIR}/MANIFEST.txt \"\$\{ryml_manifest\}\")
|
||||
")
|
||||
install(FILES "${CMAKE_BINARY_DIR}/MANIFEST.txt" DESTINATION ${CMAKE_INSTALL_PREFIX}/share/ryml)
|
||||
|
||||
# add a target to uninstall
|
||||
add_custom_target(ryml-uninstall
|
||||
"${CMAKE_COMMAND}" -P "${PROJECT_SOURCE_DIR}/proj/uninstall.cmake"
|
||||
)
|
||||
|
||||
|
||||
#-------------------------------------------------------
|
||||
# developer targets
|
||||
@@ -140,10 +170,6 @@ endif()
|
||||
|
||||
c4_add_dev_targets()
|
||||
|
||||
add_custom_target(ryml-uninstall
|
||||
"${CMAKE_COMMAND}" -P "${PROJECT_SOURCE_DIR}/cmake/uninstall.cmake"
|
||||
)
|
||||
|
||||
|
||||
#-------------------------------------------------------
|
||||
# clang-tidy
|
||||
|
||||
21
CMakePresets.json
Normal file
21
CMakePresets.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{"$comment":
|
||||
"DO NOT EDIT! Generated automatically from CMakePresets.ys",
|
||||
"version":10,
|
||||
"$schema":
|
||||
"https://cmake.org/cmake/help/latest/_downloads/3e2d73bff478d88a7de0de736ba5e361/schema.json",
|
||||
"configurePresets":
|
||||
[{"name":"debug",
|
||||
"binaryDir":
|
||||
"${sourceDir}/build/cmkps-${hostSystemName}-${presetName}",
|
||||
"cacheVariables":{"CMAKE_BUILD_TYPE":"Debug", "RYML_DEV":"ON"}},
|
||||
{"name":"release",
|
||||
"displayName":"Release",
|
||||
"binaryDir":
|
||||
"${sourceDir}/build/cmkps-${hostSystemName}-${presetName}",
|
||||
"cacheVariables":{"CMAKE_BUILD_TYPE":"Release", "RYML_DEV":"ON"}}],
|
||||
"buildPresets":
|
||||
[{"name":"debug", "configurePreset":"debug", "configuration":
|
||||
"Debug"},
|
||||
{"name":"release",
|
||||
"configurePreset":"release",
|
||||
"configuration":"Release"}]}
|
||||
50
CMakePresets.ys
Normal file
50
CMakePresets.ys
Normal file
@@ -0,0 +1,50 @@
|
||||
# https://martin-fieber.de/blog/cmake-presets/
|
||||
# https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html
|
||||
--- !yamlscript/v0:
|
||||
|
||||
.vars_debug: &vars_debug
|
||||
CMAKE_BUILD_TYPE: Debug
|
||||
RYML_DEV: ON
|
||||
|
||||
.vars_release: &vars_release
|
||||
CMAKE_BUILD_TYPE: Release
|
||||
RYML_DEV: ON
|
||||
|
||||
|
||||
#==============================================================================
|
||||
--- !yamlscript/v0:
|
||||
|
||||
$comment: DO NOT EDIT! Generated automatically from CMakePresets.ys
|
||||
|
||||
version: 10
|
||||
# see the schema URL here: https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html#schema
|
||||
$schema: https://cmake.org/cmake/help/latest/_downloads/3e2d73bff478d88a7de0de736ba5e361/schema.json
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
configurePresets:
|
||||
#------------------------------------------------------------------------------
|
||||
-
|
||||
name: debug
|
||||
binaryDir: ${sourceDir}/build/cmkps-${hostSystemName}-${presetName}
|
||||
cacheVariables:
|
||||
<<: *vars_debug
|
||||
-
|
||||
name: release
|
||||
displayName: Release
|
||||
binaryDir: ${sourceDir}/build/cmkps-${hostSystemName}-${presetName}
|
||||
cacheVariables:
|
||||
<<: *vars_release
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
buildPresets:
|
||||
#------------------------------------------------------------------------------
|
||||
-
|
||||
name: debug
|
||||
configurePreset: debug
|
||||
configuration: Debug
|
||||
-
|
||||
name: release
|
||||
configurePreset: release
|
||||
configuration: Release
|
||||
@@ -2,12 +2,11 @@
|
||||
|
||||
Thanks for your contribution!
|
||||
|
||||
* Make sure to clone the project with `git clone --recursive` so that
|
||||
the submodules are initialized correctly.
|
||||
* To enable both tests and benchmarks, configure ryml with `-DRYML_DEV=ON`
|
||||
when calling cmake. To enable only tests, use `-DRYML_BUILD_TESTS=ON`; to
|
||||
enable only benchmarks use `-DRYML_BUILD_BENCHMARKS=ON`. All these flags
|
||||
are disabled by default.
|
||||
* To enable both tests and benchmarks, configure ryml with
|
||||
`-DRYML_DEV=ON` when calling cmake. To enable only tests, use
|
||||
`-DRYML_BUILD_TESTS=ON`; to enable only benchmarks use
|
||||
`-DRYML_BUILD_BENCHMARKS=ON`. All these flags are disabled by
|
||||
default.
|
||||
* Code style for pull requests should respect the existing code style:
|
||||
```c++
|
||||
if(foo) // no space before parens
|
||||
|
||||
52
README.md
52
README.md
@@ -243,8 +243,7 @@ level API for accessing and traversing the data tree.
|
||||
The following snippet is a very quick overview taken from quickstart
|
||||
sample ([see on
|
||||
doxygen](https://rapidyaml.readthedocs.io/latest/group__doc__quickstart.html)/[see
|
||||
on github](samples/quickstart.cpp). After cloning ryml
|
||||
(don't forget the `--recursive` flag for git), you can very easily
|
||||
on github](samples/quickstart.cpp). After cloning ryml, you can very easily
|
||||
build and run this executable using any of the build samples, eg the
|
||||
[`add_subdirectory()` sample](samples/add_subdirectory/) (see [the relevant section](#quickstart-samples)).
|
||||
|
||||
@@ -436,16 +435,6 @@ CHECK(loc.col == 4u);
|
||||
|
||||
## Using ryml in your project
|
||||
|
||||
Note that ryml uses submodules. Take care to use the `--recursive`
|
||||
flag when cloning the repo, to ensure ryml's submodules are checked
|
||||
out as well:
|
||||
```bash
|
||||
git clone --recursive https://github.com/biojppm/rapidyaml
|
||||
```
|
||||
If you omit `--recursive`, after cloning you will have to do `git
|
||||
submodule update --init --recursive` to ensure ryml's submodules are
|
||||
checked out.
|
||||
|
||||
|
||||
### Single header file
|
||||
ryml is provided chiefly as a cmake library project, but it can also
|
||||
@@ -588,45 +577,8 @@ more about each sample:
|
||||
|
||||
|
||||
### CMake build settings for ryml
|
||||
The following cmake variables can be used to control the build behavior of
|
||||
ryml:
|
||||
|
||||
* `RYML_WITH_TAB_TOKENS=ON/OFF`. Enable/disable support for tabs as
|
||||
valid container tokens after `:` and `-`. Defaults to `OFF`,
|
||||
because this may cost up to 10% in processing time.
|
||||
* `RYML_DEFAULT_CALLBACKS=ON/OFF`. Enable/disable ryml's default
|
||||
implementation of error and allocation callbacks. Defaults to `ON`.
|
||||
* `RYML_DEFAULT_CALLBACK_USES_EXCEPTIONS=ON/OFF` - Enable/disable
|
||||
the same-named macro, which will make the default error handler
|
||||
provided by ryml throw a `std::runtime_error` exception.
|
||||
* `RYML_USE_ASSERT` - enable assertions in the code regardless of
|
||||
build type. This is disabled by default. Failed assertions will
|
||||
trigger a call to the error callback.
|
||||
* `RYML_SHORT_CHECK_MSG` - Use shorter error message from
|
||||
checks/asserts: do not show the check condition in the error
|
||||
message.
|
||||
* `RYML_STANDALONE=ON/OFF`. ryml uses
|
||||
[c4core](https://github.com/biojppm/c4core), a C++ library with low-level
|
||||
multi-platform utilities for C++. When `RYML_STANDALONE=ON`, c4core is
|
||||
incorporated into ryml as if it is the same library. Defaults to `ON`.
|
||||
* `RYML_INSTALL=ON/OFF`. enable/disable install target. Defaults to `ON`.
|
||||
|
||||
If you're developing ryml or just debugging problems with ryml itself, the
|
||||
following cmake variables can be helpful:
|
||||
* `RYML_DEV=ON/OFF`: a bool variable which enables development targets such as
|
||||
unit tests, benchmarks, etc. Defaults to `OFF`.
|
||||
* `RYML_DBG=ON/OFF`: a bool variable which enables verbose prints from
|
||||
parsing code; can be useful to figure out parsing problems. Defaults to
|
||||
`OFF`.
|
||||
|
||||
#### Forcing ryml to use a different c4core version
|
||||
|
||||
ryml is strongly coupled to c4core, and this is reinforced by the fact
|
||||
that c4core is a submodule of the current repo. However, it is still
|
||||
possible to use a c4core version different from the one in the repo
|
||||
(of course, only if there are no incompatibilities between the
|
||||
versions). You can find out how to achieve this by looking at the
|
||||
[`custom_c4core` sample](./samples/custom_c4core/CMakeLists.txt).
|
||||
See the [relevant documentation page](https://rapidyaml.readthedocs.io/latest/sphinx_using.html#cmake-build-settings-for-ryml).
|
||||
|
||||
|
||||
------
|
||||
|
||||
@@ -28,7 +28,7 @@ endif()
|
||||
set(RYML_API_DIR ${CMAKE_CURRENT_LIST_DIR})
|
||||
set(RYML_SWIG_SRC ${RYML_API_DIR}/ryml.i)
|
||||
|
||||
include(../ext/c4core/cmake/TargetArchitecture.cmake)
|
||||
include(../proj/c4proj/TargetArchitecture.cmake)
|
||||
c4_get_architecture_defines(RYML_SWIG_ARCH_DEFINES)
|
||||
c4_log("add CPU architecture defines ${RYML_SWIG_ARCH_DEFINES}")
|
||||
|
||||
|
||||
@@ -4,14 +4,15 @@ add_custom_target(ryml-bm-plot)
|
||||
c4_set_folder_remote_project_targets(bm/plot ryml-bm-plot)
|
||||
find_package(Python REQUIRED COMPONENTS Interpreter)
|
||||
|
||||
# thirdparty libs that will be compared with ryml
|
||||
|
||||
set(_ed ${CMAKE_CURRENT_BINARY_DIR}/subprojects) # casual ryml extern dir (these projects are not part of ryml and are downloaded and compiled on the fly)
|
||||
if(NOT (CMAKE_VERSION VERSION_LESS "4.0.0"))
|
||||
c4_log(STATUS "setting CMAKE_POLICY_VERSION_MINIMUM=3.5 to compile benchmarked libs")
|
||||
set(CMAKE_POLICY_VERSION_MINIMUM 3.5 CACHE STRING "" FORCE)
|
||||
endif()
|
||||
|
||||
# thirdparty libs that will be compared with ryml
|
||||
|
||||
set(_ed ${CMAKE_CURRENT_BINARY_DIR}/subprojects) # casual ryml extern dir (these projects are not part of ryml and are downloaded and compiled on the fly)
|
||||
|
||||
# libyaml
|
||||
c4_require_subproject(libyaml REMOTE
|
||||
GIT_REPOSITORY https://github.com/yaml/libyaml
|
||||
@@ -61,7 +62,29 @@ if(MSVC)
|
||||
target_compile_definitions(yaml-cpp PUBLIC -D_SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING)
|
||||
endif()
|
||||
|
||||
# jsoncpp needs to be compiled
|
||||
# jsoncpp needs to be compiled. the jsoncpp cmake file sets all these
|
||||
# variables; work around this by saving the to restore later.
|
||||
macro(variables_save_ varlist)
|
||||
foreach(varname ${ARGN})
|
||||
set(_saved_value_${varname} "${${varname}}")
|
||||
endforeach()
|
||||
set(${varlist} ${ARGN})
|
||||
endmacro()
|
||||
macro(variables_restore_ varlist)
|
||||
foreach(varname ${varlist})
|
||||
set(${varname} "${_saved_value_${varname}}" CACHE PATH "" FORCE)
|
||||
unset(_saved_value_${varname})
|
||||
endforeach()
|
||||
endmacro()
|
||||
variables_save_(vars_jsoncpp
|
||||
CCACHE_EXECUTABLE
|
||||
CMAKE_CXX_COMPILER_LAUNCHER
|
||||
CMAKE_C_COMPILER_LAUNCHER
|
||||
CMAKE_ARCHIVE_OUTPUT_DIRECTORY
|
||||
CMAKE_LIBRARY_OUTPUT_DIRECTORY
|
||||
CMAKE_PDB_OUTPUT_DIRECTORY
|
||||
CMAKE_RUNTIME_OUTPUT_DIRECTORY
|
||||
)
|
||||
c4_require_subproject(jsoncpp REMOTE
|
||||
GIT_REPOSITORY https://github.com/open-source-parsers/jsoncpp
|
||||
GIT_TAG 65bb1b1c1d8019dc72279c12bb74df92925dfd5e
|
||||
@@ -82,6 +105,7 @@ c4_set_folder_remote_project_targets(ext/jsoncpp
|
||||
readFromString
|
||||
streamWrite
|
||||
stringWrite)
|
||||
variables_restore_("${vars_jsoncpp}")
|
||||
|
||||
# nlohmannjson needs to be compiled
|
||||
c4_require_subproject(nlohmann_json REMOTE
|
||||
@@ -99,10 +123,10 @@ c4_download_remote_proj(rapidjson rapidjson_dir
|
||||
GIT_REPOSITORY https://github.com/Tencent/rapidjson
|
||||
GIT_TAG version1.1.0)
|
||||
set(RYML_RAPIDJSON_INC_DIR ${rapidjson_dir}/include)
|
||||
if(NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/rapidjson.was.patched)
|
||||
if(NOT EXISTS ${rapidjson_dir}.was.patched)
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E echo "cd ${rapidjson_dir} ; git --git-dir= apply ${CMAKE_CURRENT_LIST_DIR}/rapidjson.fix.diff"
|
||||
COMMAND git --git-dir= apply ${CMAKE_CURRENT_LIST_DIR}/rapidjson.fix.diff
|
||||
COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/rapidjson.was.patched
|
||||
COMMAND ${CMAKE_COMMAND} -E touch ${rapidjson_dir}.was.patched
|
||||
WORKING_DIRECTORY ${rapidjson_dir})
|
||||
endif()
|
||||
|
||||
|
||||
@@ -2,17 +2,6 @@ Using ryml in your project
|
||||
==========================
|
||||
|
||||
|
||||
Note that ryml uses submodules. Take care to use the `--recursive` flag
|
||||
when cloning the repo, to ensure ryml's submodules are checked out as well:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
git clone --recursive https://github.com/biojppm/rapidyaml
|
||||
|
||||
If you omit `--recursive`, after cloning you
|
||||
will have to do `git submodule update --init --recursive`
|
||||
to ensure ryml's submodules are checked out.
|
||||
|
||||
|
||||
Quickstart build samples
|
||||
------------------------
|
||||
@@ -227,19 +216,27 @@ of ryml:
|
||||
- ``RYML_DEFAULT_CALLBACKS=ON/OFF``. Enable/disable ryml’s default
|
||||
implementation of error and allocation callbacks. Defaults to ``ON``.
|
||||
- ``RYML_DEFAULT_CALLBACK_USES_EXCEPTIONS=ON/OFF`` - Enable/disable the
|
||||
same-named macro, which will make the default error handler provided
|
||||
by ryml throw a ``std::runtime_error`` exception.
|
||||
same-named macro, which will make the default error handlers provided
|
||||
by ryml throw exceptions. Defaults to ``OFF``.
|
||||
- ``RYML_USE_ASSERT=ON/OFF``. Enable/disable assertions regardless of
|
||||
of the ``NDEBUG`` macro. When set to ``OFF`` (the default), ryml
|
||||
asserts unless ``NDEBUG`` is defined, making it so that assertions
|
||||
are enabled in Debug builds and disabled in Release builds (which
|
||||
usually have ``NDEBUG`` defined). When ``RYML_USE_ASSERT`` is set to
|
||||
``ON``, assertions are enabled even if ``NDEBUG`` is defined; with
|
||||
this, assertions will be enabled in all build types regardless of
|
||||
``NDEBUG`` definition status. Failed assertions will trigger a call
|
||||
to the error callback.
|
||||
- ``RYML_USE_ASSERT`` - enable assertions in the code regardless of
|
||||
build type. This is disabled by default. Failed assertions will
|
||||
trigger a call to the error callback.
|
||||
- ``RYML_SHORT_CHECK_MSG`` - Use shorter error message from
|
||||
checks/asserts: do not show the check condition in the error
|
||||
message.
|
||||
- ``RYML_STANDALONE=ON/OFF``. ryml uses
|
||||
`c4core <https://github.com/biojppm/c4core>`__, a C++ library with
|
||||
low-level multi-platform utilities for C++. When
|
||||
``RYML_STANDALONE=ON``, c4core is incorporated into ryml as if it is
|
||||
the same library. Defaults to ``ON``.
|
||||
- ``RYML_STANDALONE=ON/OFF``. Use ryml in standalone mode, where the
|
||||
code from `c4core <https://github.com/biojppm/c4core>`__ is used as
|
||||
part of ryml (ie, as part of the ryml library). Defaults to
|
||||
``ON``. See the next section for further details.
|
||||
- ``RYML_INSTALL=ON/OFF``. enable/disable install target. Defaults to ``ON``.
|
||||
|
||||
If you’re developing ryml or just debugging problems with ryml itself,
|
||||
@@ -250,3 +247,35 @@ the following cmake variables can be helpful:
|
||||
- ``RYML_DBG=ON/OFF``: a bool variable which enables verbose prints from
|
||||
parsing code; can be useful to figure out parsing problems. Defaults
|
||||
to ``OFF``.
|
||||
|
||||
|
||||
Forcing ryml to use a different c4core version
|
||||
----------------------------------------------
|
||||
|
||||
ryml uses code from `c4core <https://github.com/biojppm/c4core>`__, a
|
||||
C++ library with low-level multi-platform utilities. (c4core in turn
|
||||
uses code from `fast_float
|
||||
<https://github.com/fastfloat/fast_float>`__ and `debugbreak
|
||||
<https://github.com/biojppm/debugbreak>`__). By default, the files
|
||||
from c4core that ryml uses are provided as part of ryml's source
|
||||
distribution and installation.
|
||||
|
||||
More importantly, **c4core is NOT a dependency** of ryml. To use ryml,
|
||||
just link with it, and you're done.
|
||||
|
||||
However, you may still use ryml while also linking directly with a
|
||||
full version of c4core. This is opt-in behavior: you need to set
|
||||
``RYML_STANDALONE=OFF`` while invoking cmake.
|
||||
|
||||
c4core was initially a submodule of this project, and maybe this
|
||||
prompted some users to see c4core as a hard dependency, despite this
|
||||
not being the intended usage in the general case. So it was decided to
|
||||
remove the submodule and provide the c4core files as part of the
|
||||
source code of ryml. This makes it necessary for users wishing to opt
|
||||
in to use a full c4core.
|
||||
|
||||
However, it is still possible to use a c4core version different from
|
||||
the one in the repo (of course, only if there are no incompatibilities
|
||||
between the versions). You can find out how to achieve this by
|
||||
looking at the `custom_c4core sample
|
||||
<https://github.com/biojppm/rapidyaml/blob/v0.9.0/samples/custom_c4core/CMakeLists.txt>`__.
|
||||
|
||||
86
ext/Makefile
Normal file
86
ext/Makefile
Normal file
@@ -0,0 +1,86 @@
|
||||
include ../proj/common.mk
|
||||
include ./c4core.mk
|
||||
|
||||
# c4core files used by ryml
|
||||
C4CORE_SRC := $(shell cat c4core.src.txt)
|
||||
# c4core files used by ryml in dev builds (debug/test)
|
||||
C4CORE_DEV := $(shell cat c4core.dev.txt)
|
||||
|
||||
C4CORE_DIR_SRC = c4core.src
|
||||
C4CORE_DIR_DEV = c4core.dev
|
||||
C4CORE_DIR_GIT = c4core.git
|
||||
|
||||
C4CORE_DIR_PROJ = $(C4CORE_DIR_GIT)/src
|
||||
|
||||
C4CORE_INPUT_SRC = $(C4CORE_SRC:%=$(C4CORE_DIR_PROJ)/%)
|
||||
C4CORE_INPUT_DEV = $(C4CORE_DEV:%=$(C4CORE_DIR_PROJ)/%)
|
||||
|
||||
C4CORE_OUTPUT_SRC = $(C4CORE_SRC:%=$(C4CORE_DIR_SRC)/%)
|
||||
C4CORE_OUTPUT_DEV = $(C4CORE_DEV:%=$(C4CORE_DIR_DEV)/%)
|
||||
|
||||
|
||||
c4core-check: c4core-check-sync c4core-check-list c4core-clone-check
|
||||
c4core-check-sync: c4core-check-sync-src c4core-check-sync-dev
|
||||
c4core-check-list: c4core-check-list-src c4core-check-list-dev
|
||||
c4core-check-sync-src: c4core-clone
|
||||
@echo "check c4core list src..."
|
||||
for s in $(C4CORE_SRC); do \
|
||||
( $(COLORDIFF) -u $(C4CORE_DIR_PROJ)/$$s $(C4CORE_DIR_SRC)/$$s ) ; \
|
||||
done
|
||||
@echo "check c4core sync src: PASS"
|
||||
c4core-check-sync-dev: c4core-clone
|
||||
@echo "check c4core list dev..."
|
||||
for s in $(C4CORE_DEV); do \
|
||||
( $(COLORDIFF) -u $(C4CORE_DIR_PROJ)/$$s $(C4CORE_DIR_DEV)/$$s ) ; \
|
||||
done
|
||||
@echo "check c4core sync dev: PASS"
|
||||
c4core-check-list-src: c4core-clone
|
||||
@echo "check c4core list src..."
|
||||
$(COLORDIFF) -u \
|
||||
<(cat c4core.src.txt | sort) \
|
||||
<(cd c4core.src ; find . ! -type d | sed s:\./c4:c4:g | sort)
|
||||
@echo "check c4core list src: PASS"
|
||||
c4core-check-list-dev: c4core-clone
|
||||
@echo "check c4core list dev..."
|
||||
$(COLORDIFF) -u \
|
||||
<(cat c4core.dev.txt | sort) \
|
||||
<(cd c4core.dev ; find . ! -type d | sed s:\./c4:c4:g | sort)
|
||||
@echo "check c4core list dev: PASS"
|
||||
|
||||
|
||||
c4core-import: c4core-import-src c4core-import-dev
|
||||
c4core-export: c4core-export-src c4core-export-dev
|
||||
|
||||
c4core-import-src: $(C4CORE_INPUT_SRC)
|
||||
$(call mk_safe_sync,$(C4CORE_SRC),$(C4CORE_DIR_PROJ),$(C4CORE_DIR_SRC))
|
||||
c4core-import-dev: $(C4CORE_INPUT_DEV)
|
||||
$(call mk_safe_sync,$(C4CORE_DEV),$(C4CORE_DIR_PROJ),$(C4CORE_DIR_DEV))
|
||||
|
||||
c4core-export-src: $(C4CORE_OUTPUT_SRC)
|
||||
$(call mk_safe_sync,$(C4CORE_SRC),$(C4CORE_DIR_SRC),$(C4CORE_DIR_PROJ))
|
||||
c4core-export-dev: $(C4CORE_OUTPUT_DEV)
|
||||
$(call mk_safe_sync,$(C4CORE_DEV),$(C4CORE_DIR_DEV),$(C4CORE_DIR_PROJ))
|
||||
|
||||
|
||||
$(C4CORE_INPUT_SRC): c4core-clone
|
||||
$(C4CORE_INPUT_DEV): c4core-clone
|
||||
|
||||
$(C4CORE_DIR_GIT):
|
||||
$(call mk_git_clone,$(C4CORE_DIR_GIT),$(C4CORE_TAG),"$(C4CORE_REPO)")
|
||||
|
||||
c4core-clone: $(C4CORE_DIR_GIT) c4core-clone-check
|
||||
c4core-check-clone: c4core-clone-check
|
||||
c4core-clone-check: $(C4CORE_DIR_GIT)
|
||||
@ \
|
||||
currsha=$$(git -C $(C4CORE_DIR_GIT) rev-parse HEAD) ; \
|
||||
currtag=$$(git -C $(C4CORE_DIR_GIT) tag --points-at HEAD || echo -n) ; \
|
||||
echo "c4core sha: $$currsha" ; \
|
||||
echo "c4core tag: $$currtag" ; \
|
||||
if [ "$$currtag" != "$(C4CORE_TAG)" ] && [ "$$currsha" != "$(C4CORE_TAG)" ] ; then \
|
||||
echo "$(C4CORE_DIR_GIT) has a different version:" ; \
|
||||
echo " expected=$(C4CORE_TAG)" ; \
|
||||
echo " actual tag=$$currtag" ; \
|
||||
echo " actual sha=$$currsha" ; \
|
||||
exit 1 ; \
|
||||
fi ; \
|
||||
echo "c4core tag ok! $(C4CORE_TAG)"
|
||||
13
ext/README.md
Normal file
13
ext/README.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# External source files
|
||||
|
||||
All the source files under this folder are from external projects; they are explicitly copied into rapidyaml to avoid use of git submodules, which some users prefer to avoid.
|
||||
|
||||
There is a [./Makefile](Makefile) to help manage the workflow to modify these files. (If you work in Windows, and a make executable is not available, you can [install one on the git bash bin directory](https://gist.github.com/evanwill/0207876c3243bbb6863e65ec5dc3f058), for example.)
|
||||
|
||||
|
||||
### Modifying files locally
|
||||
|
||||
|
||||
### Upgrading the c4core version
|
||||
|
||||
|
||||
Submodule ext/c4core deleted from 1f2c1a87ba
14
ext/c4core.dev.txt
Normal file
14
ext/c4core.dev.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
c4/enum.hpp
|
||||
c4/bitmask.hpp
|
||||
c4/c4_push.hpp
|
||||
c4/c4_pop.hpp
|
||||
c4/dump.hpp
|
||||
c4/memory_resource.hpp
|
||||
c4/memory_resource.cpp
|
||||
c4/restrict.hpp
|
||||
c4/span.hpp
|
||||
c4/szconv.hpp
|
||||
c4/unrestrict.hpp
|
||||
c4/windows.hpp
|
||||
c4/windows_push.hpp
|
||||
c4/windows_pop.hpp
|
||||
339
ext/c4core.dev/c4/bitmask.hpp
Normal file
339
ext/c4core.dev/c4/bitmask.hpp
Normal file
@@ -0,0 +1,339 @@
|
||||
#ifndef _C4_BITMASK_HPP_
|
||||
#define _C4_BITMASK_HPP_
|
||||
|
||||
/** @file bitmask.hpp bitmask utilities */
|
||||
|
||||
#include <cstring>
|
||||
#include <type_traits>
|
||||
|
||||
#include "c4/enum.hpp"
|
||||
#include "c4/format.hpp"
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable : 4996) // 'strncpy', fopen, etc: This function or variable may be unsafe
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wold-style-cast"
|
||||
#elif defined(__GNUC__)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wold-style-cast"
|
||||
# if __GNUC__ >= 8
|
||||
# pragma GCC diagnostic ignored "-Wstringop-truncation"
|
||||
# pragma GCC diagnostic ignored "-Wstringop-overflow"
|
||||
# pragma GCC diagnostic ignored "-Warray-bounds"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
namespace c4 {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** write a bitmask to a stream, formatted as a string */
|
||||
|
||||
template<class Enum, class Stream>
|
||||
Stream& bm2stream(Stream &s, typename std::underlying_type<Enum>::type bits, EnumOffsetType offst=EOFFS_PFX)
|
||||
{
|
||||
using I = typename std::underlying_type<Enum>::type;
|
||||
bool written = false;
|
||||
|
||||
auto const& pairs = esyms<Enum>();
|
||||
|
||||
// write non null value
|
||||
if(bits)
|
||||
{
|
||||
// do reverse iteration to give preference to composite enum symbols,
|
||||
// which are likely to appear at the end of the enum sequence
|
||||
for(size_t i = pairs.size() - 1; i != size_t(-1); --i)
|
||||
{
|
||||
auto p = pairs[i];
|
||||
I b(static_cast<I>(p.value));
|
||||
if(b && (bits & b) == b)
|
||||
{
|
||||
if(written) s << '|'; // append bit-or character
|
||||
written = true;
|
||||
s << p.name_offs(offst); // append bit string
|
||||
bits &= ~b;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
else
|
||||
{
|
||||
// write a null value
|
||||
for(size_t i = pairs.size() - 1; i != size_t(-1); --i)
|
||||
{
|
||||
auto p = pairs[i];
|
||||
I b(static_cast<I>(p.value));
|
||||
if(b == 0)
|
||||
{
|
||||
s << p.name_offs(offst);
|
||||
written = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!written)
|
||||
{
|
||||
s << '0';
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
template<class Enum, class Stream>
|
||||
typename std::enable_if<is_scoped_enum<Enum>::value, Stream&>::type
|
||||
bm2stream(Stream &s, Enum value, EnumOffsetType offst=EOFFS_PFX)
|
||||
{
|
||||
using I = typename std::underlying_type<Enum>::type;
|
||||
return bm2stream<Enum>(s, static_cast<I>(value), offst);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// some utility macros, undefed below
|
||||
|
||||
/// @cond dev
|
||||
|
||||
/* Execute `code` if the `num` of characters is available in the str
|
||||
* buffer. This macro simplifies the code for bm2str().
|
||||
* @todo improve performance by writing from the end and moving only once. */
|
||||
#define _c4prependchars(code, num) \
|
||||
if(str && (pos + (num) <= sz)) \
|
||||
{ \
|
||||
/* move the current string to the right */ \
|
||||
memmove(str + (num), str, pos); \
|
||||
/* now write in the beginning of the string */ \
|
||||
code; \
|
||||
} \
|
||||
else if(str && sz) \
|
||||
{ \
|
||||
C4_ERROR("cannot write to string pos=%d num=%d sz=%d", \
|
||||
(int)pos, (int)(num), (int)sz); \
|
||||
} \
|
||||
pos += num
|
||||
|
||||
/* Execute `code` if the `num` of characters is available in the str
|
||||
* buffer. This macro simplifies the code for bm2str(). */
|
||||
#define _c4appendchars(code, num) \
|
||||
if(str && (pos + (num) <= sz)) \
|
||||
{ \
|
||||
code; \
|
||||
} \
|
||||
else if(str && sz) \
|
||||
{ \
|
||||
C4_ERROR("cannot write to string pos=%d num=%d sz=%d", \
|
||||
(int)pos, (int)(num), (int)sz); \
|
||||
} \
|
||||
pos += num
|
||||
|
||||
/// @endcond
|
||||
|
||||
|
||||
/** convert a bitmask to string.
|
||||
* return the number of characters written. To find the needed size,
|
||||
* call first with str=nullptr and sz=0 */
|
||||
template<class Enum>
|
||||
size_t bm2str
|
||||
(
|
||||
typename std::underlying_type<Enum>::type bits,
|
||||
char *str=nullptr,
|
||||
size_t sz=0,
|
||||
EnumOffsetType offst=EOFFS_PFX
|
||||
)
|
||||
{
|
||||
using I = typename std::underlying_type<Enum>::type;
|
||||
C4_ASSERT((str == nullptr) == (sz == 0));
|
||||
|
||||
auto syms = esyms<Enum>();
|
||||
size_t pos = 0;
|
||||
typename EnumSymbols<Enum>::Sym const* C4_RESTRICT zero = nullptr;
|
||||
|
||||
// do reverse iteration to give preference to composite enum symbols,
|
||||
// which are likely to appear later in the enum sequence
|
||||
for(size_t i = syms.size()-1; i != size_t(-1); --i)
|
||||
{
|
||||
auto const &C4_RESTRICT p = syms[i]; // do not copy, we are assigning to `zero`
|
||||
I b = static_cast<I>(p.value);
|
||||
if(b == 0)
|
||||
{
|
||||
zero = &p; // save this symbol for later
|
||||
}
|
||||
else if((bits & b) == b)
|
||||
{
|
||||
bits &= ~b;
|
||||
// append bit-or character
|
||||
if(pos > 0)
|
||||
{
|
||||
_c4prependchars(*str = '|', 1);
|
||||
}
|
||||
// append bit string
|
||||
const char *pname = p.name_offs(offst);
|
||||
size_t len = strlen(pname);
|
||||
_c4prependchars(strncpy(str, pname, len), len);
|
||||
}
|
||||
}
|
||||
|
||||
C4_CHECK_MSG(bits == 0, "could not find all bits");
|
||||
if(pos == 0) // make sure at least something is written
|
||||
{
|
||||
if(zero) // if we have a zero symbol, use that
|
||||
{
|
||||
const char *pname = zero->name_offs(offst);
|
||||
size_t len = strlen(pname);
|
||||
_c4prependchars(strncpy(str, pname, len), len);
|
||||
}
|
||||
else // otherwise just write an integer zero
|
||||
{
|
||||
_c4prependchars(*str = '0', 1);
|
||||
}
|
||||
}
|
||||
_c4appendchars(str[pos] = '\0', 1);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
// cleanup!
|
||||
#undef _c4appendchars
|
||||
#undef _c4prependchars
|
||||
|
||||
|
||||
/** scoped enums do not convert automatically to their underlying type,
|
||||
* so this SFINAE overload will accept scoped enum symbols and cast them
|
||||
* to the underlying type */
|
||||
template<class Enum>
|
||||
typename std::enable_if<is_scoped_enum<Enum>::value, size_t>::type
|
||||
bm2str
|
||||
(
|
||||
Enum bits,
|
||||
char *str=nullptr,
|
||||
size_t sz=0,
|
||||
EnumOffsetType offst=EOFFS_PFX
|
||||
)
|
||||
{
|
||||
using I = typename std::underlying_type<Enum>::type;
|
||||
return bm2str<Enum>(static_cast<I>(bits), str, sz, offst);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
namespace detail {
|
||||
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
#elif defined(__GNUC__)
|
||||
# pragma GCC diagnostic push
|
||||
# if __GNUC__ >= 6
|
||||
# pragma GCC diagnostic ignored "-Wnull-dereference"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
template<class Enum>
|
||||
typename std::underlying_type<Enum>::type str2bm_read_one(const char *str, size_t sz, bool alnum)
|
||||
{
|
||||
using I = typename std::underlying_type<Enum>::type;
|
||||
auto pairs = esyms<Enum>();
|
||||
if(alnum)
|
||||
{
|
||||
auto *p = pairs.find(str, sz);
|
||||
C4_CHECK_MSG(p != nullptr, "no valid enum pair name for '%.*s'", (int)sz, str);
|
||||
return static_cast<I>(p->value);
|
||||
}
|
||||
I tmp{0};
|
||||
size_t len = uncat(csubstr(str, sz), tmp);
|
||||
C4_CHECK_MSG(len != csubstr::npos, "could not read string as an integral type: '%.*s'", (int)sz, str);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
#elif defined(__GNUC__)
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
} // namespace detail
|
||||
|
||||
/** convert a string to a bitmask */
|
||||
template<class Enum>
|
||||
typename std::underlying_type<Enum>::type str2bm(const char *str, size_t sz)
|
||||
{
|
||||
using I = typename std::underlying_type<Enum>::type;
|
||||
|
||||
I val = 0;
|
||||
bool started = false;
|
||||
bool alnum = false, num = false;
|
||||
const char *f = nullptr, *pc = str;
|
||||
for( ; pc < str+sz; ++pc)
|
||||
{
|
||||
const char c = *pc;
|
||||
if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_')
|
||||
{
|
||||
C4_CHECK(( ! num) || ((pc - f) == 1 && (c == 'x' || c == 'X'))); // accept hexadecimal numbers
|
||||
if( ! started)
|
||||
{
|
||||
f = pc;
|
||||
alnum = started = true;
|
||||
}
|
||||
}
|
||||
else if(c >= '0' && c <= '9')
|
||||
{
|
||||
C4_CHECK( ! alnum);
|
||||
if(!started)
|
||||
{
|
||||
f = pc;
|
||||
num = started = true;
|
||||
}
|
||||
}
|
||||
else if(c == ':' || c == ' ')
|
||||
{
|
||||
// skip this char
|
||||
}
|
||||
else if(c == '|' || c == '\0')
|
||||
{
|
||||
C4_ASSERT(num != alnum);
|
||||
C4_ASSERT(pc >= f);
|
||||
val |= detail::str2bm_read_one<Enum>(f, static_cast<size_t>(pc-f), alnum);
|
||||
started = num = alnum = false;
|
||||
if(c == '\0')
|
||||
{
|
||||
return val;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
C4_ERROR("bad character '%c' in bitmask string", c);
|
||||
}
|
||||
}
|
||||
|
||||
if(f)
|
||||
{
|
||||
C4_ASSERT(num != alnum);
|
||||
C4_ASSERT(pc >= f);
|
||||
val |= detail::str2bm_read_one<Enum>(f, static_cast<size_t>(pc-f), alnum);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/** convert a string to a bitmask */
|
||||
template<class Enum>
|
||||
typename std::underlying_type<Enum>::type str2bm(const char *str)
|
||||
{
|
||||
return str2bm<Enum>(str, strlen(str));
|
||||
}
|
||||
|
||||
} // namespace c4
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic pop
|
||||
#elif defined(__GNUC__)
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif // _C4_BITMASK_HPP_
|
||||
19
ext/c4core.dev/c4/c4_pop.hpp
Normal file
19
ext/c4core.dev/c4/c4_pop.hpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifdef _C4_PUSH_HPP_ // this must match the include guard from c4_push
|
||||
|
||||
/** @file c4_pop.hpp disables the macros and control directives
|
||||
* enabled in c4_push.hpp.
|
||||
* @see c4_push.hpp */
|
||||
|
||||
#include "c4/unrestrict.hpp"
|
||||
|
||||
#ifdef C4_WIN
|
||||
# include "c4/windows_pop.hpp"
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#undef _C4_PUSH_HPP_
|
||||
|
||||
#endif /* _C4_PUSH_HPP_ */
|
||||
37
ext/c4core.dev/c4/c4_push.hpp
Normal file
37
ext/c4core.dev/c4/c4_push.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef _C4_PUSH_HPP_
|
||||
#define _C4_PUSH_HPP_
|
||||
|
||||
|
||||
/** @file c4_push.hpp enables macros and warning control directives
|
||||
* needed by c4core. This is implemented in a push/pop way.
|
||||
* @see c4_pop.hpp */
|
||||
|
||||
|
||||
#ifndef _C4_CONFIG_HPP_
|
||||
#include "c4/config.hpp"
|
||||
#endif
|
||||
|
||||
#include "c4/restrict.hpp"
|
||||
|
||||
#ifdef C4_WIN
|
||||
# include "c4/windows_push.hpp"
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable : 4068) // unknown pragma
|
||||
# pragma warning(disable : 4100) // unreferenced formal parameter
|
||||
# pragma warning(disable : 4127) // conditional expression is constant -- eg do {} while(1);
|
||||
# pragma warning(disable : 4201) // nonstandard extension used : nameless struct/union
|
||||
//# pragma warning(disable : 4238) // nonstandard extension used: class rvalue used as lvalue
|
||||
# pragma warning(disable : 4244)
|
||||
# pragma warning(disable : 4503) // decorated name length exceeded, name was truncated
|
||||
# pragma warning(disable : 4702) // unreachable code
|
||||
# pragma warning(disable : 4714) // function marked as __forceinline not inlined
|
||||
# pragma warning(disable : 4996) // 'strncpy', fopen, etc: This function or variable may be unsafe
|
||||
# if C4_MSVC_VERSION != C4_MSVC_VERSION_2017
|
||||
# pragma warning(disable : 4800) // forcing value to bool 'true' or 'false' (performance warning)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#endif /* _C4_PUSH_HPP_ */
|
||||
798
ext/c4core.dev/c4/dump.hpp
Normal file
798
ext/c4core.dev/c4/dump.hpp
Normal file
@@ -0,0 +1,798 @@
|
||||
#ifndef C4_DUMP_HPP_
|
||||
#define C4_DUMP_HPP_
|
||||
|
||||
#include <c4/substr.hpp>
|
||||
|
||||
/** @file dump.hpp This file provides functions to dump several
|
||||
* arguments as strings to a user-provided function sink, for example
|
||||
* to implement a type-safe printf()-like function (where the sink
|
||||
* would just be a plain call to putchars()). The function sink can be
|
||||
* passed either by dynamic dispatching or by static dispatching (as a
|
||||
* template argument). There are analogs to @ref c4::cat() (@ref
|
||||
* c4::cat_dump() and @ref c4::cat_dump_resume()), @ref c4::catsep()
|
||||
* (@ref catsetp_dump() and @ref catsep_dump_resume()) and @ref
|
||||
* c4::format() (@ref c4::format_dump() and @ref
|
||||
* c4::format_dump_resume()). The analogs have two types: immediate
|
||||
* and resuming. An analog of immediate type cannot be retried when
|
||||
* the work buffer is too small; this means that successful dumps in
|
||||
* the first (successful) arguments will be dumped again in the
|
||||
* subsequent attempt to call. An analog of resuming type will only
|
||||
* ever dump as-yet-undumped arguments, through the use of @ref
|
||||
* DumpResults return type. */
|
||||
|
||||
namespace c4 {
|
||||
|
||||
C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/** @defgroup dump_building_blocks Basic building blocks for dumping.
|
||||
*
|
||||
* The basic building block: given an argument and a
|
||||
* buffer, serialize the argument to the buffer using @ref
|
||||
* c4::to_chars(), and dump the buffer to the provided sink
|
||||
* function. When the argument is a string, no serialization is
|
||||
* performed, and the argument is dumped directly to the sink.
|
||||
*
|
||||
* @{ */
|
||||
|
||||
|
||||
/** Type of the function to be used as the sink. This function
|
||||
* receives as its argument the string with characters to send to the
|
||||
* sink.
|
||||
*
|
||||
* @warning the string passed to the sink may have zero length. If the
|
||||
* user sink uses memcpy(), the call to memcpy() should be defended
|
||||
* with a check for zero length (calling memcpy with zero length is
|
||||
* undefined behavior).
|
||||
* */
|
||||
using SinkPfn = void (*)(csubstr str);
|
||||
|
||||
|
||||
/** a traits class to use in SFINAE with @ref c4::dump() to select if
|
||||
* a type is treated as string type (which is dumped directly to the
|
||||
* sink, using to_csubstr()), or if the type is treated as a value,
|
||||
* which is first serialized to a buffer using to_chars(), and then
|
||||
* the serialization serialized as */
|
||||
template<class T> struct dump_directly : public std::false_type {};
|
||||
template<> struct dump_directly<csubstr> : public std::true_type {};
|
||||
template<> struct dump_directly< substr> : public std::true_type {};
|
||||
template<> struct dump_directly<const char*> : public std::true_type {};
|
||||
template<> struct dump_directly< char*> : public std::true_type {};
|
||||
template<size_t N> struct dump_directly<const char (&)[N]> : public std::true_type {};
|
||||
template<size_t N> struct dump_directly< char (&)[N]> : public std::true_type {};
|
||||
template<size_t N> struct dump_directly<const char[N]> : public std::true_type {};
|
||||
template<size_t N> struct dump_directly< char[N]> : public std::true_type {};
|
||||
|
||||
|
||||
/** Dump a string-type object to the (statically dispatched) sink. The
|
||||
* string is dumped directly, without any intermediate serialization.
|
||||
*
|
||||
* @return the number of bytes needed to serialize the string-type
|
||||
* object, which is always 0 because there is no serialization
|
||||
*
|
||||
* @note the argument is considered a value when @ref
|
||||
* dump_directly<Arg> is a false type, which is the default. To enable
|
||||
* the argument to be treated as a string type, which is dumped
|
||||
* directly to the sink without intermediate serialization, define
|
||||
* dump_directly<T> to a true type.
|
||||
*
|
||||
* @warning the string passed to the sink may have zero length. If the
|
||||
* user sink uses memcpy(), the call to memcpy() should be defended
|
||||
* with a check for zero length (calling memcpy with zero length is
|
||||
* undefined behavior).
|
||||
*
|
||||
* @see dump_directly<T>
|
||||
*/
|
||||
template<SinkPfn sinkfn, class Arg>
|
||||
inline auto dump(substr buf, Arg const& a)
|
||||
-> typename std::enable_if<dump_directly<Arg>::value, size_t>::type
|
||||
{
|
||||
C4_ASSERT(!buf.overlaps(a));
|
||||
C4_UNUSED(buf);
|
||||
// dump directly, no need to serialize to the buffer
|
||||
sinkfn(to_csubstr(a));
|
||||
return 0; // no space was used in the buffer
|
||||
}
|
||||
/** Dump a string-type object to the (dynamically dispatched)
|
||||
* sink. The string is dumped directly, without any intermediate
|
||||
* serialization to the buffer.
|
||||
*
|
||||
* @return the number of bytes needed to serialize the string-type
|
||||
* object, which is always 0 because there is no serialization
|
||||
*
|
||||
* @note the argument is considered a value when @ref
|
||||
* dump_directly<Arg> is a false type, which is the default. To enable
|
||||
* the argument to be treated as a string type, which is dumped
|
||||
* directly to the sink without intermediate serialization, define
|
||||
* dump_directly<T> to a true type.
|
||||
*
|
||||
* @warning the string passed to the sink may have zero length. If the
|
||||
* user sink uses memcpy(), the call to memcpy() should be defended
|
||||
* with a check for zero length (calling memcpy with zero length is
|
||||
* undefined behavior).
|
||||
*
|
||||
* @see dump_directly<T>
|
||||
* */
|
||||
template<class SinkFn, class Arg>
|
||||
inline auto dump(SinkFn &&sinkfn, substr buf, Arg const& a)
|
||||
-> typename std::enable_if<dump_directly<Arg>::value, size_t>::type
|
||||
{
|
||||
C4_UNUSED(buf);
|
||||
C4_ASSERT(!buf.overlaps(a));
|
||||
// dump directly, no need to serialize to the buffer
|
||||
std::forward<SinkFn>(sinkfn)(to_csubstr(a));
|
||||
return 0; // no space was used in the buffer
|
||||
}
|
||||
|
||||
|
||||
/** Dump a value to the sink. Given an argument @p a and a buffer @p
|
||||
* buf, serialize the argument to the buffer using @ref to_chars(),
|
||||
* and then dump the buffer to the (statically dispatched) sink
|
||||
* function passed as the template argument. If the buffer is too
|
||||
* small to serialize the argument, the sink function is not called.
|
||||
*
|
||||
* @note the argument is considered a value when @ref
|
||||
* dump_directly<Arg> is a false type, which is the default. To enable
|
||||
* the argument to be treated as a string type, which is dumped
|
||||
* directly to the sink without intermediate serialization, define
|
||||
* dump_directly<T> to a true type.
|
||||
*
|
||||
* @see dump_directly<T>
|
||||
*
|
||||
* @return the number of characters required to serialize the
|
||||
* argument. */
|
||||
template<SinkPfn sinkfn, class Arg>
|
||||
inline auto dump(substr buf, Arg const& a)
|
||||
-> typename std::enable_if<!dump_directly<Arg>::value, size_t>::type
|
||||
{
|
||||
// serialize to the buffer
|
||||
const size_t sz = to_chars(buf, a);
|
||||
// dump the buffer to the sink
|
||||
if(C4_LIKELY(sz <= buf.len))
|
||||
{
|
||||
// NOTE: don't do this:
|
||||
//sinkfn(buf.first(sz));
|
||||
// ... but do this instead:
|
||||
sinkfn({buf.str, sz});
|
||||
// ... this is needed because Release builds for armv5 and
|
||||
// armv6 were failing for the first call, with the wrong
|
||||
// buffer being passed into the function (!)
|
||||
}
|
||||
return sz;
|
||||
}
|
||||
/** Dump a value to the sink. Given an argument @p a and a buffer @p
|
||||
* buf, serialize the argument to the buffer using @ref
|
||||
* c4::to_chars(), and then dump the buffer to the (dynamically
|
||||
* dispatched) sink function, passed as @p sinkfn. If the buffer is too
|
||||
* small to serialize the argument, the sink function is not called.
|
||||
*
|
||||
* @note the argument is considered a value when @ref
|
||||
* dump_directly<Arg> is a false type, which is the default. To enable
|
||||
* the argument to be treated as a string type, which is dumped
|
||||
* directly to the sink without intermediate serialization, define
|
||||
* dump_directly<T> to a true type.
|
||||
*
|
||||
* @see @ref dump_directly<T>
|
||||
*
|
||||
* @return the number of characters required to serialize the
|
||||
* argument. */
|
||||
template<class SinkFn, class Arg>
|
||||
inline auto dump(SinkFn &&sinkfn, substr buf, Arg const& a)
|
||||
-> typename std::enable_if<!dump_directly<Arg>::value, size_t>::type
|
||||
{
|
||||
// serialize to the buffer
|
||||
const size_t sz = to_chars(buf, a);
|
||||
// dump the buffer to the sink
|
||||
if(C4_LIKELY(sz <= buf.len))
|
||||
{
|
||||
// NOTE: don't do this:
|
||||
//std::forward<SinkFn>(sinkfn)(buf.first(sz));
|
||||
// ... but do this instead:
|
||||
std::forward<SinkFn>(sinkfn)({buf.str, sz});
|
||||
// ... this is needed because Release builds for armv5 and
|
||||
// armv6 were failing for the first call, with the wrong
|
||||
// buffer being passed into the function (!)
|
||||
}
|
||||
return sz;
|
||||
}
|
||||
|
||||
|
||||
/** An opaque type used by resumeable dump functions like @ref
|
||||
* cat_dump_resume(), @ref catsep_dump_resume() or @ref
|
||||
* format_dump_resume(). */
|
||||
struct DumpResults
|
||||
{
|
||||
enum : size_t { noarg = (size_t)-1 };
|
||||
size_t bufsize = 0;
|
||||
size_t lastok = noarg;
|
||||
bool success_until(size_t expected) const { return lastok == noarg ? false : lastok >= expected; }
|
||||
bool write_arg(size_t arg) const { return lastok == noarg || arg > lastok; }
|
||||
size_t argfail() const { return lastok + 1; }
|
||||
};
|
||||
|
||||
/** @} */
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
/** @defgroup cat_dump Dump several arguments to a sink,
|
||||
* concatenated. This is the analog to @ref c4::cat(), with the
|
||||
* significant difference that each argument is immediately sent to
|
||||
* the sink (resulting in multiple calls to the sink function, once
|
||||
* per argument), whereas equivalent usage of c4::cat() would first
|
||||
* serialize all the arguments to the buffer, and then call the sink
|
||||
* once at the end. As a consequence, the size needed for the buffer
|
||||
* is only the maximum of the size needed for the arguments, whereas
|
||||
* with c4::cat(), the size needed for the buffer would be the sum of
|
||||
* the size needed for the arguments. When the size of dump
|
||||
*
|
||||
* @{ */
|
||||
|
||||
/// @cond dev
|
||||
// terminates the variadic recursion
|
||||
template<class SinkFn>
|
||||
size_t cat_dump(SinkFn &&, substr) // NOLINT
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// terminates the variadic recursion
|
||||
template<SinkPfn sinkfn>
|
||||
size_t cat_dump(substr) // NOLINT
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
|
||||
/** Dump several arguments to the (dynamically dispatched) sink
|
||||
* function, as if through c4::cat(). For each argument, @ref dump()
|
||||
* is called with the buffer and sink. If any of the arguments is too
|
||||
* large for the buffer, no subsequent argument is sent to the sink,
|
||||
* (but all the arguments are still processed to compute the size
|
||||
* required for the buffer). This function can be safely called with an
|
||||
* empty buffer.
|
||||
*
|
||||
* @return the size required for the buffer, which is the maximum size
|
||||
* across all arguments
|
||||
*
|
||||
* @note subsequent calls with the same set of arguments will dump
|
||||
* again the first successful arguments. If each argument must only be
|
||||
* sent once to the sink (for example with printf-like behavior), use
|
||||
* instead @ref cat_dump_resume(). */
|
||||
template<class SinkFn, class Arg, class... Args>
|
||||
size_t cat_dump(SinkFn &&sinkfn, substr buf, Arg const& a, Args const& ...more)
|
||||
{
|
||||
const size_t size_for_a = dump(std::forward<SinkFn>(sinkfn), buf, a);
|
||||
if(C4_UNLIKELY(size_for_a > buf.len))
|
||||
buf.len = 0; // ensure no more calls to the sink
|
||||
const size_t size_for_more = cat_dump(std::forward<SinkFn>(sinkfn), buf, more...);
|
||||
return size_for_more > size_for_a ? size_for_more : size_for_a;
|
||||
}
|
||||
|
||||
|
||||
/** Dump several arguments to the (statically dispatched) sink
|
||||
* function, as if through c4::cat(). For each argument, @ref dump()
|
||||
* is called with the buffer and sink. If any of the arguments is too
|
||||
* large for the buffer, no subsequent argument is sent to the sink,
|
||||
* (but all the arguments are still processed to compute the size
|
||||
* required for the buffer). This function can be safely called with an
|
||||
* empty buffer.
|
||||
*
|
||||
* @return the size required for the buffer, which is the maximum size
|
||||
* across all arguments
|
||||
*
|
||||
* @note subsequent calls with the same set of arguments will dump
|
||||
* again the first successful arguments. If each argument must only be
|
||||
* sent once to the sink (for example with printf-like behavior), use
|
||||
* instead @ref cat_dump_resume(). */
|
||||
template<SinkPfn sinkfn, class Arg, class... Args>
|
||||
size_t cat_dump(substr buf, Arg const& a, Args const& ...more)
|
||||
{
|
||||
const size_t size_for_a = dump<sinkfn>(buf, a);
|
||||
if(C4_UNLIKELY(size_for_a > buf.len))
|
||||
buf.len = 0; // ensure no more calls to the sink
|
||||
const size_t size_for_more = cat_dump<sinkfn>(buf, more...);
|
||||
return size_for_more > size_for_a ? size_for_more : size_for_a;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/// @cond dev
|
||||
namespace detail {
|
||||
|
||||
// terminates the variadic recursion
|
||||
template<SinkPfn sinkfn>
|
||||
C4_ALWAYS_INLINE DumpResults cat_dump_resume(size_t, DumpResults results, substr)
|
||||
{
|
||||
return results;
|
||||
}
|
||||
|
||||
// terminates the variadic recursion
|
||||
template<class SinkFn>
|
||||
C4_ALWAYS_INLINE DumpResults cat_dump_resume(size_t, SinkFn &&, DumpResults results, substr) // NOLINT
|
||||
{
|
||||
return results;
|
||||
}
|
||||
|
||||
template<SinkPfn sinkfn, class Arg, class... Args>
|
||||
DumpResults cat_dump_resume(size_t currarg, DumpResults results, substr buf, Arg const& C4_RESTRICT a, Args const& ...more)
|
||||
{
|
||||
if(C4_LIKELY(results.write_arg(currarg)))
|
||||
{
|
||||
size_t sz = dump<sinkfn>(buf, a); // yield to the specialized function
|
||||
if(currarg == results.lastok + 1 && sz <= buf.len)
|
||||
results.lastok = currarg;
|
||||
results.bufsize = sz > results.bufsize ? sz : results.bufsize;
|
||||
}
|
||||
return detail::cat_dump_resume<sinkfn>(currarg + 1u, results, buf, more...);
|
||||
}
|
||||
|
||||
template<class SinkFn, class Arg, class... Args>
|
||||
DumpResults cat_dump_resume(size_t currarg, SinkFn &&sinkfn, DumpResults results, substr buf, Arg const& C4_RESTRICT a, Args const& ...more)
|
||||
{
|
||||
if(C4_LIKELY(results.write_arg(currarg)))
|
||||
{
|
||||
size_t sz = dump(std::forward<SinkFn>(sinkfn), buf, a); // yield to the specialized function
|
||||
if(currarg == results.lastok + 1 && sz <= buf.len)
|
||||
results.lastok = currarg;
|
||||
results.bufsize = sz > results.bufsize ? sz : results.bufsize;
|
||||
}
|
||||
return detail::cat_dump_resume(currarg + 1u, std::forward<SinkFn>(sinkfn), results, buf, more...);
|
||||
}
|
||||
} // namespace detail
|
||||
/// @endcond
|
||||
|
||||
|
||||
template<SinkPfn sinkfn, class Arg, class... Args>
|
||||
C4_ALWAYS_INLINE DumpResults cat_dump_resume(substr buf, Arg const& C4_RESTRICT a, Args const& ...more)
|
||||
{
|
||||
return detail::cat_dump_resume<sinkfn>(0u, DumpResults{}, buf, a, more...);
|
||||
}
|
||||
|
||||
template<class SinkFn, class Arg, class... Args>
|
||||
C4_ALWAYS_INLINE DumpResults cat_dump_resume(SinkFn &&sinkfn, substr buf, Arg const& C4_RESTRICT a, Args const& ...more)
|
||||
{
|
||||
return detail::cat_dump_resume(0u, std::forward<SinkFn>(sinkfn), DumpResults{}, buf, a, more...);
|
||||
}
|
||||
|
||||
|
||||
template<SinkPfn sinkfn, class Arg, class... Args>
|
||||
C4_ALWAYS_INLINE DumpResults cat_dump_resume(DumpResults results, substr buf, Arg const& C4_RESTRICT a, Args const& ...more)
|
||||
{
|
||||
if(results.bufsize > buf.len)
|
||||
return results;
|
||||
return detail::cat_dump_resume<sinkfn>(0u, results, buf, a, more...);
|
||||
}
|
||||
|
||||
template<class SinkFn, class Arg, class... Args>
|
||||
C4_ALWAYS_INLINE DumpResults cat_dump_resume(SinkFn &&sinkfn, DumpResults results, substr buf, Arg const& C4_RESTRICT a, Args const& ...more)
|
||||
{
|
||||
if(results.bufsize > buf.len)
|
||||
return results;
|
||||
return detail::cat_dump_resume(0u, std::forward<SinkFn>(sinkfn), results, buf, a, more...);
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/// @cond dev
|
||||
// terminate the recursion
|
||||
template<class SinkFn, class Sep>
|
||||
size_t catsep_dump(SinkFn &&, substr, Sep const& C4_RESTRICT) // NOLINT
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// terminate the recursion
|
||||
template<SinkPfn sinkfn, class Sep>
|
||||
size_t catsep_dump(substr, Sep const& C4_RESTRICT) // NOLINT
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
/** take the function pointer as a function argument */
|
||||
template<class SinkFn, class Sep, class Arg, class... Args>
|
||||
size_t catsep_dump(SinkFn &&sinkfn, substr buf, Sep const& sep, Arg const& a, Args const& ...more)
|
||||
{
|
||||
size_t sz = dump(std::forward<SinkFn>(sinkfn), buf, a);
|
||||
if(C4_UNLIKELY(sz > buf.len))
|
||||
buf.len = 0; // ensure no more calls
|
||||
if C4_IF_CONSTEXPR (sizeof...(more) > 0)
|
||||
{
|
||||
size_t szsep = dump(std::forward<SinkFn>(sinkfn), buf, sep);
|
||||
if(C4_UNLIKELY(szsep > buf.len))
|
||||
buf.len = 0; // ensure no more calls
|
||||
sz = sz > szsep ? sz : szsep;
|
||||
}
|
||||
size_t size_for_more = catsep_dump(std::forward<SinkFn>(sinkfn), buf, sep, more...);
|
||||
return size_for_more > sz ? size_for_more : sz;
|
||||
}
|
||||
|
||||
/** take the function pointer as a template argument */
|
||||
template<SinkPfn sinkfn, class Sep, class Arg, class... Args>
|
||||
size_t catsep_dump(substr buf, Sep const& sep, Arg const& a, Args const& ...more)
|
||||
{
|
||||
size_t sz = dump<sinkfn>(buf, a);
|
||||
if(C4_UNLIKELY(sz > buf.len))
|
||||
buf.len = 0; // ensure no more calls
|
||||
if C4_IF_CONSTEXPR (sizeof...(more) > 0)
|
||||
{
|
||||
size_t szsep = dump<sinkfn>(buf, sep);
|
||||
if(C4_UNLIKELY(szsep > buf.len))
|
||||
buf.len = 0; // ensure no more calls
|
||||
sz = sz > szsep ? sz : szsep;
|
||||
}
|
||||
size_t size_for_more = catsep_dump<sinkfn>(buf, sep, more...);
|
||||
return size_for_more > sz ? size_for_more : sz;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/// @cond dev
|
||||
namespace detail {
|
||||
template<SinkPfn sinkfn, class Arg>
|
||||
void catsep_dump_resume_(size_t currarg, DumpResults *C4_RESTRICT results, substr *buf, Arg const& a)
|
||||
{
|
||||
if(C4_LIKELY(results->write_arg(currarg)))
|
||||
{
|
||||
size_t sz = dump<sinkfn>(*buf, a);
|
||||
results->bufsize = sz > results->bufsize ? sz : results->bufsize;
|
||||
if(C4_LIKELY(sz <= buf->len))
|
||||
results->lastok = currarg;
|
||||
else
|
||||
buf->len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
template<class SinkFn, class Arg>
|
||||
void catsep_dump_resume_(size_t currarg, SinkFn &&sinkfn, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Arg const& C4_RESTRICT a)
|
||||
{
|
||||
if(C4_LIKELY(results->write_arg(currarg)))
|
||||
{
|
||||
size_t sz = dump(std::forward<SinkFn>(sinkfn), *buf, a);
|
||||
results->bufsize = sz > results->bufsize ? sz : results->bufsize;
|
||||
if(C4_LIKELY(sz <= buf->len))
|
||||
results->lastok = currarg;
|
||||
else
|
||||
buf->len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
template<SinkPfn sinkfn, class Sep, class Arg>
|
||||
C4_ALWAYS_INLINE void catsep_dump_resume(size_t currarg, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Sep const&, Arg const& a)
|
||||
{
|
||||
detail::catsep_dump_resume_<sinkfn>(currarg, results, buf, a);
|
||||
}
|
||||
|
||||
template<class SinkFn, class Sep, class Arg>
|
||||
C4_ALWAYS_INLINE void catsep_dump_resume(size_t currarg, SinkFn &&sinkfn, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Sep const&, Arg const& a)
|
||||
{
|
||||
detail::catsep_dump_resume_(currarg, std::forward<SinkFn>(sinkfn), results, buf, a);
|
||||
}
|
||||
|
||||
template<SinkPfn sinkfn, class Sep, class Arg, class... Args>
|
||||
C4_ALWAYS_INLINE void catsep_dump_resume(size_t currarg, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Sep const& sep, Arg const& a, Args const& ...more)
|
||||
{
|
||||
detail::catsep_dump_resume_<sinkfn>(currarg , results, buf, a);
|
||||
detail::catsep_dump_resume_<sinkfn>(currarg + 1u, results, buf, sep);
|
||||
detail::catsep_dump_resume <sinkfn>(currarg + 2u, results, buf, sep, more...);
|
||||
}
|
||||
|
||||
template<class SinkFn, class Sep, class Arg, class... Args>
|
||||
C4_ALWAYS_INLINE void catsep_dump_resume(size_t currarg, SinkFn &&sinkfn, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Sep const& sep, Arg const& a, Args const& ...more)
|
||||
{
|
||||
detail::catsep_dump_resume_(currarg , std::forward<SinkFn>(sinkfn), results, buf, a);
|
||||
detail::catsep_dump_resume_(currarg + 1u, std::forward<SinkFn>(sinkfn), results, buf, sep);
|
||||
detail::catsep_dump_resume (currarg + 2u, std::forward<SinkFn>(sinkfn), results, buf, sep, more...);
|
||||
}
|
||||
} // namespace detail
|
||||
/// @endcond
|
||||
|
||||
|
||||
template<SinkPfn sinkfn, class Sep, class... Args>
|
||||
C4_ALWAYS_INLINE DumpResults catsep_dump_resume(substr buf, Sep const& sep, Args const& ...args)
|
||||
{
|
||||
DumpResults results;
|
||||
detail::catsep_dump_resume<sinkfn>(0u, &results, &buf, sep, args...);
|
||||
return results;
|
||||
}
|
||||
|
||||
template<class SinkFn, class Sep, class... Args>
|
||||
C4_ALWAYS_INLINE DumpResults catsep_dump_resume(SinkFn &&sinkfn, substr buf, Sep const& sep, Args const& ...args)
|
||||
{
|
||||
DumpResults results;
|
||||
detail::catsep_dump_resume(0u, std::forward<SinkFn>(sinkfn), &results, &buf, sep, args...);
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
template<SinkPfn sinkfn, class Sep, class... Args>
|
||||
C4_ALWAYS_INLINE DumpResults catsep_dump_resume(DumpResults results, substr buf, Sep const& sep, Args const& ...args)
|
||||
{
|
||||
detail::catsep_dump_resume<sinkfn>(0u, &results, &buf, sep, args...);
|
||||
return results;
|
||||
}
|
||||
|
||||
template<class SinkFn, class Sep, class... Args>
|
||||
C4_ALWAYS_INLINE DumpResults catsep_dump_resume(SinkFn &&sinkfn, DumpResults results, substr buf, Sep const& sep, Args const& ...args)
|
||||
{
|
||||
detail::catsep_dump_resume(0u, std::forward<SinkFn>(sinkfn), &results, &buf, sep, args...);
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/// @cond dev
|
||||
namespace detail {
|
||||
// terminate the recursion
|
||||
C4_ALWAYS_INLINE size_t _format_dump_compute_size()
|
||||
{
|
||||
return 0u;
|
||||
}
|
||||
template<class T>
|
||||
C4_ALWAYS_INLINE auto _format_dump_compute_size(T const&)
|
||||
-> typename std::enable_if<dump_directly<T>::value, size_t>::type
|
||||
{
|
||||
return 0u; // no buffer needed
|
||||
}
|
||||
template<class T>
|
||||
C4_ALWAYS_INLINE auto _format_dump_compute_size(T const& v)
|
||||
-> typename std::enable_if<!dump_directly<T>::value, size_t>::type
|
||||
{
|
||||
return to_chars(substr{}, v);
|
||||
}
|
||||
template<class Arg, class... Args>
|
||||
size_t _format_dump_compute_size(Arg const& a, Args const& ...more)
|
||||
{
|
||||
const size_t sz = _format_dump_compute_size(a); // don't call to_chars() directly
|
||||
const size_t rest = _format_dump_compute_size(more...);
|
||||
return sz > rest ? sz : rest;
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
// terminate the recursion
|
||||
template<class SinkFn>
|
||||
C4_ALWAYS_INLINE size_t format_dump(SinkFn &&sinkfn, substr, csubstr fmt)
|
||||
{
|
||||
// we can dump without using buf, so no need to check it
|
||||
std::forward<SinkFn>(sinkfn)(fmt);
|
||||
return 0u;
|
||||
}
|
||||
// terminate the recursion
|
||||
/** take the function pointer as a template argument */
|
||||
template<SinkPfn sinkfn>
|
||||
C4_ALWAYS_INLINE size_t format_dump(substr, csubstr fmt)
|
||||
{
|
||||
// we can dump without using buf, so no need to check it
|
||||
sinkfn(fmt);
|
||||
return 0u;
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
|
||||
/** take the function pointer as a function argument */
|
||||
template<class SinkFn, class Arg, class... Args>
|
||||
C4_NO_INLINE size_t format_dump(SinkFn &&sinkfn, substr buf, csubstr fmt, Arg const& a, Args const& ...more)
|
||||
{
|
||||
// we can dump without using buf
|
||||
// but we'll only dump if the buffer is ok
|
||||
size_t pos = fmt.find("{}"); // @todo use _find_fmt()
|
||||
if(C4_UNLIKELY(pos == csubstr::npos))
|
||||
{
|
||||
std::forward<SinkFn>(sinkfn)(fmt);
|
||||
return 0u;
|
||||
}
|
||||
std::forward<SinkFn>(sinkfn)(fmt.first(pos)); // we can dump without using buf
|
||||
fmt = fmt.sub(pos + 2); // skip {} do this before assigning to pos again
|
||||
pos = dump(std::forward<SinkFn>(sinkfn), buf, a); // reuse pos to get needed_size
|
||||
// dump no more if the buffer was exhausted
|
||||
size_t size_for_more;
|
||||
if(C4_LIKELY(pos <= buf.len))
|
||||
size_for_more = format_dump(std::forward<SinkFn>(sinkfn), buf, fmt, more...);
|
||||
else
|
||||
size_for_more = detail::_format_dump_compute_size(more...);
|
||||
return size_for_more > pos ? size_for_more : pos;
|
||||
}
|
||||
|
||||
/** take the function pointer as a template argument */
|
||||
template<SinkPfn sinkfn, class Arg, class... Args>
|
||||
C4_NO_INLINE size_t format_dump(substr buf, csubstr fmt, Arg const& C4_RESTRICT a, Args const& ...more)
|
||||
{
|
||||
// we can dump without using buf
|
||||
// but we'll only dump if the buffer is ok
|
||||
size_t pos = fmt.find("{}"); // @todo use _find_fmt()
|
||||
if(C4_UNLIKELY(pos == csubstr::npos))
|
||||
{
|
||||
sinkfn(fmt);
|
||||
return 0u;
|
||||
}
|
||||
sinkfn(fmt.first(pos)); // we can dump without using buf
|
||||
fmt = fmt.sub(pos + 2); // skip {} do this before assigning to pos again
|
||||
pos = dump<sinkfn>(buf, a); // reuse pos to get needed_size
|
||||
// dump no more if the buffer was exhausted
|
||||
size_t size_for_more;
|
||||
if(C4_LIKELY(pos <= buf.len))
|
||||
size_for_more = format_dump<sinkfn>(buf, fmt, more...);
|
||||
else
|
||||
size_for_more = detail::_format_dump_compute_size(more...);
|
||||
return size_for_more > pos ? size_for_more : pos;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/// @cond dev
|
||||
namespace detail {
|
||||
// terminate the recursion
|
||||
template<SinkPfn sinkfn>
|
||||
DumpResults format_dump_resume(size_t currarg, DumpResults results, substr, csubstr fmt)
|
||||
{
|
||||
if(C4_LIKELY(results.write_arg(currarg)))
|
||||
{
|
||||
// we can dump without using buf
|
||||
sinkfn(fmt);
|
||||
results.lastok = currarg;
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
// terminate the recursion
|
||||
template<class SinkFn>
|
||||
DumpResults format_dump_resume(size_t currarg, SinkFn &&sinkfn, DumpResults results, substr, csubstr fmt)
|
||||
{
|
||||
if(C4_LIKELY(results.write_arg(currarg)))
|
||||
{
|
||||
// we can dump without using buf
|
||||
std::forward<SinkFn>(sinkfn)(fmt);
|
||||
results.lastok = currarg;
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
template<SinkPfn sinkfn, class Arg, class... Args>
|
||||
DumpResults format_dump_resume(size_t currarg, DumpResults results, substr buf, csubstr fmt, Arg const& a, Args const& ...more)
|
||||
{
|
||||
// we need to process the format even if we're not
|
||||
// going to print the first arguments because we're resuming
|
||||
const size_t pos = fmt.find("{}"); // @todo use _find_fmt()
|
||||
if(C4_LIKELY(pos != csubstr::npos))
|
||||
{
|
||||
if(C4_LIKELY(results.write_arg(currarg)))
|
||||
{
|
||||
sinkfn(fmt.first(pos));
|
||||
results.lastok = currarg;
|
||||
}
|
||||
if(C4_LIKELY(results.write_arg(currarg + 1u)))
|
||||
{
|
||||
const size_t len = dump<sinkfn>(buf, a);
|
||||
results.bufsize = len > results.bufsize ? len : results.bufsize;
|
||||
if(C4_LIKELY(len <= buf.len))
|
||||
{
|
||||
results.lastok = currarg + 1u;
|
||||
}
|
||||
else
|
||||
{
|
||||
const size_t rest = _format_dump_compute_size(more...);
|
||||
results.bufsize = rest > results.bufsize ? rest : results.bufsize;
|
||||
return results;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(C4_LIKELY(results.write_arg(currarg)))
|
||||
{
|
||||
sinkfn(fmt);
|
||||
results.lastok = currarg;
|
||||
}
|
||||
return results;
|
||||
}
|
||||
// NOTE: sparc64 had trouble with reassignment to fmt, and
|
||||
// was passing the original fmt to the recursion:
|
||||
//fmt = fmt.sub(pos + 2); // DONT!
|
||||
return detail::format_dump_resume<sinkfn>(currarg + 2u, results, buf, fmt.sub(pos + 2), more...);
|
||||
}
|
||||
|
||||
|
||||
template<class SinkFn, class Arg, class... Args>
|
||||
DumpResults format_dump_resume(size_t currarg, SinkFn &&sinkfn, DumpResults results, substr buf, csubstr fmt, Arg const& a, Args const& ...more)
|
||||
{
|
||||
// we need to process the format even if we're not
|
||||
// going to print the first arguments because we're resuming
|
||||
const size_t pos = fmt.find("{}"); // @todo use _find_fmt()
|
||||
if(C4_LIKELY(pos != csubstr::npos))
|
||||
{
|
||||
if(C4_LIKELY(results.write_arg(currarg)))
|
||||
{
|
||||
std::forward<SinkFn>(sinkfn)(fmt.first(pos));
|
||||
results.lastok = currarg;
|
||||
}
|
||||
if(C4_LIKELY(results.write_arg(currarg + 1u)))
|
||||
{
|
||||
const size_t len = dump(std::forward<SinkFn>(sinkfn), buf, a);
|
||||
results.bufsize = len > results.bufsize ? len : results.bufsize;
|
||||
if(C4_LIKELY(len <= buf.len))
|
||||
{
|
||||
results.lastok = currarg + 1u;
|
||||
}
|
||||
else
|
||||
{
|
||||
const size_t rest = _format_dump_compute_size(more...);
|
||||
results.bufsize = rest > results.bufsize ? rest : results.bufsize;
|
||||
return results;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(C4_LIKELY(results.write_arg(currarg)))
|
||||
{
|
||||
std::forward<SinkFn>(sinkfn)(fmt);
|
||||
results.lastok = currarg;
|
||||
}
|
||||
return results;
|
||||
}
|
||||
// NOTE: sparc64 had trouble with reassignment to fmt, and
|
||||
// was passing the original fmt to the recursion:
|
||||
//fmt = fmt.sub(pos + 2); // DONT!
|
||||
return detail::format_dump_resume(currarg + 2u, std::forward<SinkFn>(sinkfn), results, buf, fmt.sub(pos + 2), more...);
|
||||
}
|
||||
} // namespace detail
|
||||
/// @endcond
|
||||
|
||||
|
||||
template<SinkPfn sinkfn, class... Args>
|
||||
C4_ALWAYS_INLINE DumpResults format_dump_resume(substr buf, csubstr fmt, Args const& ...args)
|
||||
{
|
||||
return detail::format_dump_resume<sinkfn>(0u, DumpResults{}, buf, fmt, args...);
|
||||
}
|
||||
|
||||
template<class SinkFn, class... Args>
|
||||
C4_ALWAYS_INLINE DumpResults format_dump_resume(SinkFn &&sinkfn, substr buf, csubstr fmt, Args const& ...args)
|
||||
{
|
||||
return detail::format_dump_resume(0u, std::forward<SinkFn>(sinkfn), DumpResults{}, buf, fmt, args...);
|
||||
}
|
||||
|
||||
|
||||
template<SinkPfn sinkfn, class... Args>
|
||||
C4_ALWAYS_INLINE DumpResults format_dump_resume(DumpResults results, substr buf, csubstr fmt, Args const& ...args)
|
||||
{
|
||||
return detail::format_dump_resume<sinkfn>(0u, results, buf, fmt, args...);
|
||||
}
|
||||
|
||||
template<class SinkFn, class... Args>
|
||||
C4_ALWAYS_INLINE DumpResults format_dump_resume(SinkFn &&sinkfn, DumpResults results, substr buf, csubstr fmt, Args const& ...args)
|
||||
{
|
||||
return detail::format_dump_resume(0u, std::forward<SinkFn>(sinkfn), results, buf, fmt, args...);
|
||||
}
|
||||
|
||||
C4_SUPPRESS_WARNING_GCC_CLANG_POP
|
||||
|
||||
} // namespace c4
|
||||
|
||||
|
||||
#endif /* C4_DUMP_HPP_ */
|
||||
281
ext/c4core.dev/c4/enum.hpp
Normal file
281
ext/c4core.dev/c4/enum.hpp
Normal file
@@ -0,0 +1,281 @@
|
||||
#ifndef _C4_ENUM_HPP_
|
||||
#define _C4_ENUM_HPP_
|
||||
|
||||
#include "c4/error.hpp"
|
||||
#include <cstddef>
|
||||
#include <string.h>
|
||||
|
||||
/** @file enum.hpp utilities for enums: convert to/from string
|
||||
*/
|
||||
|
||||
|
||||
namespace c4 {
|
||||
|
||||
C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
|
||||
|
||||
//! taken from http://stackoverflow.com/questions/15586163/c11-type-trait-to-differentiate-between-enum-class-and-regular-enum
|
||||
template<typename Enum>
|
||||
using is_scoped_enum = std::integral_constant<bool, std::is_enum<Enum>::value && !std::is_convertible<Enum, int>::value>;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
typedef enum {
|
||||
EOFFS_NONE = 0, ///< no offset
|
||||
EOFFS_CLS = 1, ///< get the enum offset for the class name. @see eoffs_cls()
|
||||
EOFFS_PFX = 2, ///< get the enum offset for the enum prefix. @see eoffs_pfx()
|
||||
_EOFFS_LAST ///< reserved
|
||||
} EnumOffsetType;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
/** A simple (proxy) container for the value-name pairs of an enum type.
|
||||
* Uses linear search for finds; this could be improved for time-critical
|
||||
* code. */
|
||||
template<class Enum>
|
||||
class EnumSymbols
|
||||
{
|
||||
public:
|
||||
|
||||
struct Sym
|
||||
{
|
||||
Enum value;
|
||||
const char *name;
|
||||
|
||||
bool cmp(const char *s) const;
|
||||
bool cmp(const char *s, size_t len) const;
|
||||
|
||||
const char *name_offs(EnumOffsetType t) const;
|
||||
};
|
||||
|
||||
using const_iterator = Sym const*;
|
||||
|
||||
public:
|
||||
|
||||
template<size_t N>
|
||||
EnumSymbols(Sym const (&p)[N]) : m_symbols(p), m_num(N) {}
|
||||
EnumSymbols(Sym const *p, size_t N) : m_symbols(p), m_num(N) {}
|
||||
|
||||
size_t size() const { return m_num; }
|
||||
bool empty() const { return m_num == 0; }
|
||||
|
||||
Sym const* get(Enum v) const { auto p = find(v); C4_CHECK_MSG(p != nullptr, "could not find symbol=%zd", (std::ptrdiff_t)v); return p; }
|
||||
Sym const* get(const char *s) const { auto p = find(s); C4_CHECK_MSG(p != nullptr, "could not find symbol \"%s\"", s); return p; }
|
||||
Sym const* get(const char *s, size_t len) const { auto p = find(s, len); C4_CHECK_MSG(p != nullptr, "could not find symbol \"%.*s\"", len, s); return p; }
|
||||
|
||||
Sym const* find(Enum v) const;
|
||||
Sym const* find(const char *s) const;
|
||||
Sym const* find(const char *s, size_t len) const;
|
||||
|
||||
Sym const& operator[] (size_t i) const { C4_CHECK(i < m_num); return m_symbols[i]; }
|
||||
|
||||
Sym const* begin() const { return m_symbols; }
|
||||
Sym const* end () const { return m_symbols + m_num; }
|
||||
|
||||
private:
|
||||
|
||||
Sym const* m_symbols;
|
||||
size_t const m_num; // NOLINT(*avoid-const*)
|
||||
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** return an EnumSymbols object for the enum type T
|
||||
*
|
||||
* @warning SPECIALIZE! This needs to be specialized for each enum
|
||||
* type. Failure to provide a specialization will cause a linker
|
||||
* error. */
|
||||
template<class Enum>
|
||||
EnumSymbols<Enum> esyms();
|
||||
|
||||
|
||||
/** return the offset for an enum symbol class. For example,
|
||||
* eoffs_cls<MyEnumClass>() would be 13=strlen("MyEnumClass::").
|
||||
*
|
||||
* With this function you can announce that the full prefix (including
|
||||
* an eventual enclosing class or C++11 enum class) is of a certain
|
||||
* length.
|
||||
*
|
||||
* @warning Needs to be specialized for each enum class type that
|
||||
* wants to use this. When no specialization is given, will return
|
||||
* 0. */
|
||||
template<class Enum>
|
||||
size_t eoffs_cls()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/** return the offset for an enum symbol prefix. This includes
|
||||
* eoffs_cls(). With this function you can announce that the full
|
||||
* prefix (including an eventual enclosing class or C++11 enum class
|
||||
* plus the string prefix) is of a certain length.
|
||||
*
|
||||
* @warning Needs to be specialized for each enum class type that
|
||||
* wants to use this. When no specialization is given, will return
|
||||
* 0. */
|
||||
template<class Enum>
|
||||
size_t eoffs_pfx()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
template<class Enum>
|
||||
size_t eoffs(EnumOffsetType which)
|
||||
{
|
||||
switch(which)
|
||||
{
|
||||
case EOFFS_NONE:
|
||||
return 0;
|
||||
case EOFFS_CLS:
|
||||
return eoffs_cls<Enum>();
|
||||
case EOFFS_PFX:
|
||||
{
|
||||
size_t pfx = eoffs_pfx<Enum>();
|
||||
return pfx > 0 ? pfx : eoffs_cls<Enum>();
|
||||
}
|
||||
default:
|
||||
C4_ERROR("unknown offset type %d", (int)which);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** get the enum value corresponding to a c-string */
|
||||
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
#elif defined(__GNUC__)
|
||||
# pragma GCC diagnostic push
|
||||
# if __GNUC__ >= 6
|
||||
# pragma GCC diagnostic ignored "-Wnull-dereference"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
template<class Enum>
|
||||
Enum str2e(const char* str)
|
||||
{
|
||||
auto pairs = esyms<Enum>();
|
||||
auto *p = pairs.get(str);
|
||||
C4_CHECK_MSG(p != nullptr, "no valid enum pair name for '%s'", str);
|
||||
return p->value;
|
||||
}
|
||||
|
||||
/** get the c-string corresponding to an enum value */
|
||||
template<class Enum>
|
||||
const char* e2str(Enum e)
|
||||
{
|
||||
auto es = esyms<Enum>();
|
||||
auto *p = es.get(e);
|
||||
C4_CHECK_MSG(p != nullptr, "no valid enum pair name");
|
||||
return p->name;
|
||||
}
|
||||
|
||||
/** like e2str(), but add an offset. */
|
||||
template<class Enum>
|
||||
const char* e2stroffs(Enum e, EnumOffsetType ot=EOFFS_PFX)
|
||||
{
|
||||
const char *s = e2str<Enum>(e) + eoffs<Enum>(ot);
|
||||
return s;
|
||||
}
|
||||
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
#elif defined(__GNUC__)
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Find a symbol by value. Returns nullptr when none is found */
|
||||
template<class Enum>
|
||||
typename EnumSymbols<Enum>::Sym const* EnumSymbols<Enum>::find(Enum v) const
|
||||
{
|
||||
for(Sym const* p = this->m_symbols, *e = p+this->m_num; p < e; ++p)
|
||||
if(p->value == v)
|
||||
return p;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/** Find a symbol by name. Returns nullptr when none is found */
|
||||
template<class Enum>
|
||||
typename EnumSymbols<Enum>::Sym const* EnumSymbols<Enum>::find(const char *s) const
|
||||
{
|
||||
for(Sym const* p = this->m_symbols, *e = p+this->m_num; p < e; ++p)
|
||||
if(p->cmp(s))
|
||||
return p;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/** Find a symbol by name. Returns nullptr when none is found */
|
||||
template<class Enum>
|
||||
typename EnumSymbols<Enum>::Sym const* EnumSymbols<Enum>::find(const char *s, size_t len) const
|
||||
{
|
||||
for(Sym const* p = this->m_symbols, *e = p+this->m_num; p < e; ++p)
|
||||
if(p->cmp(s, len))
|
||||
return p;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template<class Enum>
|
||||
bool EnumSymbols<Enum>::Sym::cmp(const char *s) const
|
||||
{
|
||||
if(strcmp(name, s) == 0)
|
||||
return true;
|
||||
|
||||
for(int i = 1; i < _EOFFS_LAST; ++i)
|
||||
{
|
||||
auto o = eoffs<Enum>((EnumOffsetType)i);
|
||||
if(o > 0)
|
||||
if(strcmp(name + o, s) == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template<class Enum>
|
||||
bool EnumSymbols<Enum>::Sym::cmp(const char *s, size_t len) const
|
||||
{
|
||||
if(strncmp(name, s, len) == 0)
|
||||
return true;
|
||||
|
||||
size_t nlen = 0;
|
||||
for(int i = 1; i <_EOFFS_LAST; ++i)
|
||||
{
|
||||
auto o = eoffs<Enum>((EnumOffsetType)i);
|
||||
if(o > 0)
|
||||
{
|
||||
if(!nlen)
|
||||
{
|
||||
nlen = strlen(name);
|
||||
}
|
||||
C4_ASSERT(o < nlen);
|
||||
size_t rem = nlen - o;
|
||||
auto m = len > rem ? len : rem;
|
||||
if(len >= m && strncmp(name + o, s, m) == 0)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template<class Enum>
|
||||
const char* EnumSymbols<Enum>::Sym::name_offs(EnumOffsetType t) const
|
||||
{
|
||||
C4_ASSERT(eoffs<Enum>(t) < strlen(name));
|
||||
return name + eoffs<Enum>(t);
|
||||
}
|
||||
|
||||
C4_SUPPRESS_WARNING_GCC_CLANG_POP
|
||||
|
||||
} // namespace c4
|
||||
|
||||
#endif // _C4_ENUM_HPP_
|
||||
340
ext/c4core.dev/c4/memory_resource.cpp
Normal file
340
ext/c4core.dev/c4/memory_resource.cpp
Normal file
@@ -0,0 +1,340 @@
|
||||
#include "c4/memory_resource.hpp"
|
||||
#include "c4/memory_util.hpp"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#if defined(C4_POSIX) || defined(C4_IOS) || defined(C4_MACOS) || defined(C4_ARM)
|
||||
# include <errno.h>
|
||||
#endif
|
||||
#if defined(C4_ARM)
|
||||
# include <malloc.h>
|
||||
#endif
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace c4 {
|
||||
|
||||
C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
|
||||
|
||||
namespace detail {
|
||||
|
||||
|
||||
#ifdef C4_NO_ALLOC_DEFAULTS
|
||||
aalloc_pfn s_aalloc = nullptr;
|
||||
free_pfn s_afree = nullptr;
|
||||
arealloc_pfn s_arealloc = nullptr;
|
||||
#else
|
||||
|
||||
|
||||
void afree_impl(void *ptr)
|
||||
{
|
||||
#if defined(C4_WIN) || defined(C4_XBOX)
|
||||
::_aligned_free(ptr);
|
||||
#else
|
||||
::free(ptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void* aalloc_impl(size_t size, size_t alignment)
|
||||
{
|
||||
// alignment must be nonzero and a power of 2
|
||||
C4_CHECK(alignment > 0 && (alignment & (alignment - 1u)) == 0);
|
||||
// NOTE: alignment needs to be sized in multiples of sizeof(void*)
|
||||
if(C4_UNLIKELY(alignment < sizeof(void*)))
|
||||
alignment = sizeof(void*);
|
||||
static_assert((sizeof(void*) & (sizeof(void*)-1u)) == 0, "sizeof(void*) must be a power of 2");
|
||||
C4_CHECK(((alignment & (sizeof(void*) - 1u))) == 0u);
|
||||
void *mem;
|
||||
#if defined(C4_WIN) || defined(C4_XBOX)
|
||||
mem = ::_aligned_malloc(size, alignment);
|
||||
C4_CHECK(mem != nullptr || size == 0);
|
||||
#elif defined(C4_POSIX) || defined(C4_IOS) || defined(C4_MACOS)
|
||||
int ret = ::posix_memalign(&mem, alignment, size);
|
||||
if(C4_UNLIKELY(ret))
|
||||
{
|
||||
C4_ASSERT(ret != EINVAL); // this was already handled above
|
||||
if(ret == ENOMEM)
|
||||
{
|
||||
C4_ERROR("There was insufficient memory to fulfill the "
|
||||
"allocation request of %zu bytes (alignment=%lu)", size, size);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
#elif defined(C4_ARM) || defined(C4_ANDROID)
|
||||
// https://stackoverflow.com/questions/53614538/undefined-reference-to-posix-memalign-in-arm-gcc
|
||||
// https://electronics.stackexchange.com/questions/467382/e2-studio-undefined-reference-to-posix-memalign/467753
|
||||
mem = memalign(alignment, size);
|
||||
C4_CHECK(mem != nullptr || size == 0);
|
||||
#else
|
||||
(void)size;
|
||||
(void)alignment;
|
||||
mem = nullptr;
|
||||
C4_NOT_IMPLEMENTED_MSG("need to implement an aligned allocation for this platform");
|
||||
#endif
|
||||
C4_ASSERT_MSG((uintptr_t(mem) & (alignment-1)) == 0, "address %p is not aligned to %zu boundary", mem, alignment);
|
||||
return mem;
|
||||
}
|
||||
|
||||
|
||||
void* arealloc_impl(void* ptr, size_t oldsz, size_t newsz, size_t alignment)
|
||||
{
|
||||
/** @todo make this more efficient
|
||||
* @see https://stackoverflow.com/questions/9078259/does-realloc-keep-the-memory-alignment-of-posix-memalign
|
||||
* @see look for qReallocAligned() in http://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/global/qmalloc.cpp
|
||||
*/
|
||||
void *tmp = aalloc(newsz, alignment);
|
||||
size_t min = newsz < oldsz ? newsz : oldsz;
|
||||
if(mem_overlaps(ptr, tmp, oldsz, newsz))
|
||||
{
|
||||
::memmove(tmp, ptr, min);
|
||||
}
|
||||
else
|
||||
{
|
||||
::memcpy(tmp, ptr, min);
|
||||
}
|
||||
afree(ptr);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
aalloc_pfn s_aalloc = aalloc_impl;
|
||||
afree_pfn s_afree = afree_impl;
|
||||
arealloc_pfn s_arealloc = arealloc_impl;
|
||||
|
||||
#endif // C4_NO_ALLOC_DEFAULTS
|
||||
|
||||
} // namespace detail
|
||||
|
||||
|
||||
aalloc_pfn get_aalloc()
|
||||
{
|
||||
return detail::s_aalloc;
|
||||
}
|
||||
void set_aalloc(aalloc_pfn fn)
|
||||
{
|
||||
detail::s_aalloc = fn;
|
||||
}
|
||||
|
||||
afree_pfn get_afree()
|
||||
{
|
||||
return detail::s_afree;
|
||||
}
|
||||
void set_afree(afree_pfn fn)
|
||||
{
|
||||
detail::s_afree = fn;
|
||||
}
|
||||
|
||||
arealloc_pfn get_arealloc()
|
||||
{
|
||||
return detail::s_arealloc;
|
||||
}
|
||||
void set_arealloc(arealloc_pfn fn)
|
||||
{
|
||||
detail::s_arealloc = fn;
|
||||
}
|
||||
|
||||
|
||||
void* aalloc(size_t sz, size_t alignment)
|
||||
{
|
||||
C4_ASSERT_MSG(c4::get_aalloc() != nullptr, "did you forget to call set_aalloc()?");
|
||||
auto fn = c4::get_aalloc();
|
||||
void* ptr = fn(sz, alignment);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void afree(void* ptr)
|
||||
{
|
||||
C4_ASSERT_MSG(c4::get_afree() != nullptr, "did you forget to call set_afree()?");
|
||||
auto fn = c4::get_afree();
|
||||
fn(ptr);
|
||||
}
|
||||
|
||||
void* arealloc(void *ptr, size_t oldsz, size_t newsz, size_t alignment)
|
||||
{
|
||||
C4_ASSERT_MSG(c4::get_arealloc() != nullptr, "did you forget to call set_arealloc()?");
|
||||
auto fn = c4::get_arealloc();
|
||||
void* nptr = fn(ptr, oldsz, newsz, alignment);
|
||||
return nptr;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void detail::_MemoryResourceSingleChunk::release()
|
||||
{
|
||||
if(m_mem && m_owner)
|
||||
{
|
||||
impl_type::deallocate(m_mem, m_size);
|
||||
}
|
||||
m_mem = nullptr;
|
||||
m_size = 0;
|
||||
m_owner = false;
|
||||
m_pos = 0;
|
||||
}
|
||||
|
||||
void detail::_MemoryResourceSingleChunk::acquire(size_t sz)
|
||||
{
|
||||
clear();
|
||||
m_owner = true;
|
||||
m_mem = (char*) impl_type::allocate(sz, alignof(max_align_t));
|
||||
m_size = sz;
|
||||
m_pos = 0;
|
||||
}
|
||||
|
||||
void detail::_MemoryResourceSingleChunk::acquire(void *mem, size_t sz)
|
||||
{
|
||||
clear();
|
||||
m_owner = false;
|
||||
m_mem = (char*) mem;
|
||||
m_size = sz;
|
||||
m_pos = 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void* MemoryResourceLinear::do_allocate(size_t sz, size_t alignment, void *hint)
|
||||
{
|
||||
C4_UNUSED(hint);
|
||||
if(sz == 0) return nullptr;
|
||||
// make sure there's enough room to allocate
|
||||
if(m_pos + sz > m_size)
|
||||
{
|
||||
C4_ERROR("out of memory");
|
||||
}
|
||||
void *mem = m_mem + m_pos;
|
||||
size_t space = m_size - m_pos;
|
||||
if(std::align(alignment, sz, mem, space))
|
||||
{
|
||||
C4_ASSERT(m_pos <= m_size);
|
||||
C4_ASSERT(m_size - m_pos >= space);
|
||||
m_pos += (m_size - m_pos) - space;
|
||||
m_pos += sz;
|
||||
C4_ASSERT(m_pos <= m_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
C4_ERROR("could not align memory");
|
||||
}
|
||||
return mem;
|
||||
}
|
||||
|
||||
void MemoryResourceLinear::do_deallocate(void* ptr, size_t sz, size_t alignment)
|
||||
{
|
||||
C4_UNUSED(ptr);
|
||||
C4_UNUSED(sz);
|
||||
C4_UNUSED(alignment);
|
||||
// nothing to do!!
|
||||
}
|
||||
|
||||
void* MemoryResourceLinear::do_reallocate(void* ptr, size_t oldsz, size_t newsz, size_t alignment)
|
||||
{
|
||||
if(newsz == oldsz) return ptr;
|
||||
// is ptr the most recently allocated (MRA) block?
|
||||
char *cptr = (char*)ptr;
|
||||
bool same_pos = (m_mem + m_pos == cptr + oldsz);
|
||||
// no need to get more memory when shrinking
|
||||
if(newsz < oldsz)
|
||||
{
|
||||
// if this is the MRA, we can safely shrink the position
|
||||
if(same_pos)
|
||||
{
|
||||
m_pos -= oldsz - newsz;
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
// we're growing the block, and it fits in size
|
||||
else if(same_pos && cptr + newsz <= m_mem + m_size)
|
||||
{
|
||||
// if this is the MRA, we can safely shrink the position
|
||||
m_pos += newsz - oldsz;
|
||||
return ptr;
|
||||
}
|
||||
// we're growing the block or it doesn't fit -
|
||||
// delegate any of these situations to do_deallocate()
|
||||
return do_allocate(newsz, alignment, ptr);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/** @todo add a free list allocator. A good candidate because of its
|
||||
* small size is TLSF.
|
||||
*
|
||||
* @see https://github.com/mattconte/tlsf
|
||||
*
|
||||
* Comparisons:
|
||||
*
|
||||
* @see https://www.researchgate.net/publication/262375150_A_Comparative_Study_on_Memory_Allocators_in_Multicore_and_Multithreaded_Applications_-_SBESC_2011_-_Presentation_Slides
|
||||
* @see http://webkit.sed.hu/blog/20100324/war-allocators-tlsf-action
|
||||
* @see https://github.com/emeryberger/Malloc-Implementations/tree/master/allocators
|
||||
*
|
||||
* */
|
||||
|
||||
C4_SUPPRESS_WARNING_GCC_CLANG_POP
|
||||
|
||||
} // namespace c4
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifdef C4_REDEFINE_CPPNEW
|
||||
#include <new>
|
||||
void* operator new(size_t size)
|
||||
{
|
||||
auto *mr = ::c4::get_memory_resource();
|
||||
return mr->allocate(size);
|
||||
}
|
||||
void operator delete(void *p) noexcept
|
||||
{
|
||||
C4_NEVER_REACH();
|
||||
}
|
||||
void operator delete(void *p, size_t size)
|
||||
{
|
||||
auto *mr = ::c4::get_memory_resource();
|
||||
mr->deallocate(p, size);
|
||||
}
|
||||
void* operator new[](size_t size)
|
||||
{
|
||||
return operator new(size);
|
||||
}
|
||||
void operator delete[](void *p) noexcept
|
||||
{
|
||||
operator delete(p);
|
||||
}
|
||||
void operator delete[](void *p, size_t size)
|
||||
{
|
||||
operator delete(p, size);
|
||||
}
|
||||
void* operator new(size_t size, std::nothrow_t)
|
||||
{
|
||||
return operator new(size);
|
||||
}
|
||||
void operator delete(void *p, std::nothrow_t)
|
||||
{
|
||||
operator delete(p);
|
||||
}
|
||||
void operator delete(void *p, size_t size, std::nothrow_t)
|
||||
{
|
||||
operator delete(p, size);
|
||||
}
|
||||
void* operator new[](size_t size, std::nothrow_t)
|
||||
{
|
||||
return operator new(size);
|
||||
}
|
||||
void operator delete[](void *p, std::nothrow_t)
|
||||
{
|
||||
operator delete(p);
|
||||
}
|
||||
void operator delete[](void *p, size_t, std::nothrow_t)
|
||||
{
|
||||
operator delete(p, size);
|
||||
}
|
||||
#endif // C4_REDEFINE_CPPNEW
|
||||
562
ext/c4core.dev/c4/memory_resource.hpp
Normal file
562
ext/c4core.dev/c4/memory_resource.hpp
Normal file
@@ -0,0 +1,562 @@
|
||||
#ifndef _C4_MEMORY_RESOURCE_HPP_
|
||||
#define _C4_MEMORY_RESOURCE_HPP_
|
||||
|
||||
/** @file memory_resource.hpp Provides facilities to allocate typeless
|
||||
* memory, via the memory resource model consecrated with C++17. */
|
||||
|
||||
/** @defgroup memory memory utilities */
|
||||
|
||||
/** @defgroup raw_memory_alloc Raw memory allocation
|
||||
* @ingroup memory
|
||||
*/
|
||||
|
||||
/** @defgroup memory_resources Memory resources
|
||||
* @ingroup memory
|
||||
*/
|
||||
|
||||
#include "c4/error.hpp"
|
||||
#include "c4/types.hpp"
|
||||
|
||||
namespace c4 {
|
||||
|
||||
// need these forward decls here
|
||||
struct MemoryResource;
|
||||
struct MemoryResourceMalloc;
|
||||
struct MemoryResourceStack;
|
||||
MemoryResourceMalloc* get_memory_resource_malloc();
|
||||
MemoryResourceStack* get_memory_resource_stack();
|
||||
namespace detail { MemoryResource*& get_memory_resource(); }
|
||||
|
||||
|
||||
// c-style allocation ---------------------------------------------------------
|
||||
|
||||
// this API provides aligned allocation functions.
|
||||
// These functions forward the call to a user-modifiable function.
|
||||
|
||||
|
||||
// aligned allocation.
|
||||
|
||||
/** Aligned allocation. Merely calls the current get_aalloc() function.
|
||||
* @see get_aalloc()
|
||||
* @ingroup raw_memory_alloc */
|
||||
void* aalloc(size_t sz, size_t alignment);
|
||||
|
||||
/** Aligned free. Merely calls the current get_afree() function.
|
||||
* @see get_afree()
|
||||
* @ingroup raw_memory_alloc */
|
||||
void afree(void* ptr);
|
||||
|
||||
/** Aligned reallocation. Merely calls the current get_arealloc() function.
|
||||
* @see get_arealloc()
|
||||
* @ingroup raw_memory_alloc */
|
||||
void* arealloc(void* ptr, size_t oldsz, size_t newsz, size_t alignment);
|
||||
|
||||
|
||||
// allocation setup facilities.
|
||||
|
||||
/** Function pointer type for aligned allocation
|
||||
* @see set_aalloc()
|
||||
* @ingroup raw_memory_alloc */
|
||||
using aalloc_pfn = void* (*)(size_t size, size_t alignment);
|
||||
|
||||
/** Function pointer type for aligned deallocation
|
||||
* @see set_afree()
|
||||
* @ingroup raw_memory_alloc */
|
||||
using afree_pfn = void (*)(void *ptr);
|
||||
|
||||
/** Function pointer type for aligned reallocation
|
||||
* @see set_arealloc()
|
||||
* @ingroup raw_memory_alloc */
|
||||
using arealloc_pfn = void* (*)(void *ptr, size_t oldsz, size_t newsz, size_t alignment);
|
||||
|
||||
|
||||
// allocation function pointer setters/getters
|
||||
|
||||
/** Set the global aligned allocation function.
|
||||
* @see aalloc()
|
||||
* @see get_aalloc()
|
||||
* @ingroup raw_memory_alloc */
|
||||
void set_aalloc(aalloc_pfn fn);
|
||||
|
||||
/** Set the global aligned deallocation function.
|
||||
* @see afree()
|
||||
* @see get_afree()
|
||||
* @ingroup raw_memory_alloc */
|
||||
void set_afree(afree_pfn fn);
|
||||
|
||||
/** Set the global aligned reallocation function.
|
||||
* @see arealloc()
|
||||
* @see get_arealloc()
|
||||
* @ingroup raw_memory_alloc */
|
||||
void set_arealloc(arealloc_pfn fn);
|
||||
|
||||
|
||||
/** Get the global aligned reallocation function.
|
||||
* @see arealloc()
|
||||
* @ingroup raw_memory_alloc */
|
||||
aalloc_pfn get_aalloc();
|
||||
|
||||
/** Get the global aligned deallocation function.
|
||||
* @see afree()
|
||||
* @ingroup raw_memory_alloc */
|
||||
afree_pfn get_afree();
|
||||
|
||||
/** Get the global aligned reallocation function.
|
||||
* @see arealloc()
|
||||
* @ingroup raw_memory_alloc */
|
||||
arealloc_pfn get_arealloc();
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
// c++-style allocation -------------------------------------------------------
|
||||
|
||||
/** C++17-style memory_resource base class. See http://en.cppreference.com/w/cpp/experimental/memory_resource
|
||||
* @ingroup memory_resources */
|
||||
struct MemoryResource // NOLINT(*-member-functions)
|
||||
{
|
||||
const char *name = nullptr;
|
||||
virtual ~MemoryResource() = default;
|
||||
|
||||
void* allocate(size_t sz, size_t alignment=alignof(max_align_t), void *hint=nullptr)
|
||||
{
|
||||
void *mem = this->do_allocate(sz, alignment, hint);
|
||||
C4_CHECK_MSG(mem != nullptr, "could not allocate %lu bytes", sz);
|
||||
return mem;
|
||||
}
|
||||
|
||||
void* reallocate(void* ptr, size_t oldsz, size_t newsz, size_t alignment=alignof(max_align_t))
|
||||
{
|
||||
void *mem = this->do_reallocate(ptr, oldsz, newsz, alignment);
|
||||
C4_CHECK_MSG(mem != nullptr, "could not reallocate from %lu to %lu bytes", oldsz, newsz);
|
||||
return mem;
|
||||
}
|
||||
|
||||
void deallocate(void* ptr, size_t sz, size_t alignment=alignof(max_align_t))
|
||||
{
|
||||
this->do_deallocate(ptr, sz, alignment);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
virtual void* do_allocate(size_t sz, size_t alignment, void* hint) = 0;
|
||||
virtual void* do_reallocate(void* ptr, size_t oldsz, size_t newsz, size_t alignment) = 0;
|
||||
virtual void do_deallocate(void* ptr, size_t sz, size_t alignment) = 0;
|
||||
|
||||
};
|
||||
|
||||
/** get the current global memory resource. To avoid static initialization
|
||||
* order problems, this is implemented using a function call to ensure
|
||||
* that it is available when first used.
|
||||
* @ingroup memory_resources */
|
||||
C4_ALWAYS_INLINE MemoryResource* get_memory_resource()
|
||||
{
|
||||
return detail::get_memory_resource();
|
||||
}
|
||||
|
||||
/** set the global memory resource
|
||||
* @ingroup memory_resources */
|
||||
C4_ALWAYS_INLINE void set_memory_resource(MemoryResource* mr)
|
||||
{
|
||||
C4_ASSERT(mr != nullptr);
|
||||
detail::get_memory_resource() = mr;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
/** A c4::aalloc-based memory resource. Thread-safe if the implementation
|
||||
* called by c4::aalloc() is safe.
|
||||
* @ingroup memory_resources */
|
||||
struct MemoryResourceMalloc : public MemoryResource // NOLINT(*-member-functions)
|
||||
{
|
||||
|
||||
MemoryResourceMalloc() { name = "malloc"; }
|
||||
|
||||
protected:
|
||||
|
||||
void* do_allocate(size_t sz, size_t alignment, void *hint) override
|
||||
{
|
||||
C4_UNUSED(hint);
|
||||
return c4::aalloc(sz, alignment);
|
||||
}
|
||||
|
||||
void do_deallocate(void* ptr, size_t sz, size_t alignment) override
|
||||
{
|
||||
C4_UNUSED(sz);
|
||||
C4_UNUSED(alignment);
|
||||
c4::afree(ptr);
|
||||
}
|
||||
|
||||
void* do_reallocate(void* ptr, size_t oldsz, size_t newsz, size_t alignment) override
|
||||
{
|
||||
return c4::arealloc(ptr, oldsz, newsz, alignment);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/** returns a malloc-based memory resource
|
||||
* @ingroup memory_resources */
|
||||
C4_ALWAYS_INLINE MemoryResourceMalloc* get_memory_resource_malloc()
|
||||
{
|
||||
/** @todo use a nifty counter:
|
||||
* https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Nifty_Counter */
|
||||
static MemoryResourceMalloc mr;
|
||||
return &mr;
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
C4_ALWAYS_INLINE MemoryResource* & get_memory_resource()
|
||||
{
|
||||
/** @todo use a nifty counter:
|
||||
* https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Nifty_Counter */
|
||||
thread_local static MemoryResource* mr = get_memory_resource_malloc();
|
||||
return mr;
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
namespace detail {
|
||||
|
||||
/** Allows a memory resource to obtain its memory from another memory resource.
|
||||
* @ingroup memory_resources */
|
||||
struct DerivedMemoryResource : public MemoryResource
|
||||
{
|
||||
public:
|
||||
|
||||
DerivedMemoryResource(MemoryResource *mr_=nullptr) : m_local(mr_ ? mr_ : get_memory_resource()) {}
|
||||
|
||||
private:
|
||||
|
||||
MemoryResource *m_local;
|
||||
|
||||
protected:
|
||||
|
||||
void* do_allocate(size_t sz, size_t alignment, void* hint) override
|
||||
{
|
||||
return m_local->allocate(sz, alignment, hint);
|
||||
}
|
||||
|
||||
void* do_reallocate(void* ptr, size_t oldsz, size_t newsz, size_t alignment) override
|
||||
{
|
||||
return m_local->reallocate(ptr, oldsz, newsz, alignment);
|
||||
}
|
||||
|
||||
void do_deallocate(void* ptr, size_t sz, size_t alignment) override
|
||||
{
|
||||
m_local->deallocate(ptr, sz, alignment);
|
||||
}
|
||||
};
|
||||
|
||||
/** Provides common facilities for memory resource consisting of a single memory block
|
||||
* @ingroup memory_resources */
|
||||
struct _MemoryResourceSingleChunk : public DerivedMemoryResource
|
||||
{
|
||||
|
||||
C4_NO_COPY_OR_MOVE(_MemoryResourceSingleChunk);
|
||||
|
||||
using impl_type = DerivedMemoryResource;
|
||||
|
||||
public:
|
||||
|
||||
_MemoryResourceSingleChunk(MemoryResource *impl=nullptr) : DerivedMemoryResource(impl) { name = "linear_malloc"; }
|
||||
|
||||
/** initialize with owned memory, allocated from the given (or the global) memory resource */
|
||||
_MemoryResourceSingleChunk(size_t sz, MemoryResource *impl=nullptr) : _MemoryResourceSingleChunk(impl) { acquire(sz); }
|
||||
/** initialize with borrowed memory */
|
||||
_MemoryResourceSingleChunk(void *mem, size_t sz) : _MemoryResourceSingleChunk() { acquire(mem, sz); }
|
||||
|
||||
~_MemoryResourceSingleChunk() override { release(); }
|
||||
|
||||
public:
|
||||
|
||||
void const* mem() const { return m_mem; }
|
||||
|
||||
size_t capacity() const { return m_size; }
|
||||
size_t size() const { return m_pos; }
|
||||
size_t slack() const { C4_ASSERT(m_size >= m_pos); return m_size - m_pos; }
|
||||
|
||||
public:
|
||||
|
||||
char *m_mem{nullptr};
|
||||
size_t m_size{0};
|
||||
size_t m_pos{0};
|
||||
bool m_owner;
|
||||
|
||||
public:
|
||||
|
||||
/** set the internal pointer to the beginning of the linear buffer */
|
||||
void clear() { m_pos = 0; }
|
||||
|
||||
/** initialize with owned memory, allocated from the global memory resource */
|
||||
void acquire(size_t sz);
|
||||
/** initialize with borrowed memory */
|
||||
void acquire(void *mem, size_t sz);
|
||||
/** release the memory */
|
||||
void release();
|
||||
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
/** provides a linear memory resource. Allocates incrementally from a linear
|
||||
* buffer, without ever deallocating. Deallocations are a no-op, and the
|
||||
* memory is freed only when the resource is release()d. The memory used by
|
||||
* this object can be either owned or borrowed. When borrowed, no calls to
|
||||
* malloc/free take place.
|
||||
*
|
||||
* @ingroup memory_resources */
|
||||
struct MemoryResourceLinear : public detail::_MemoryResourceSingleChunk // NOLINT(*-member-functions)
|
||||
{
|
||||
|
||||
C4_NO_COPY_OR_MOVE(MemoryResourceLinear);
|
||||
|
||||
public:
|
||||
|
||||
using detail::_MemoryResourceSingleChunk::_MemoryResourceSingleChunk;
|
||||
|
||||
protected:
|
||||
|
||||
void* do_allocate(size_t sz, size_t alignment, void *hint) override;
|
||||
void do_deallocate(void* ptr, size_t sz, size_t alignment) override;
|
||||
void* do_reallocate(void* ptr, size_t oldsz, size_t newsz, size_t alignment) override;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
/** provides a stack-type malloc-based memory resource.
|
||||
* @ingroup memory_resources */
|
||||
struct MemoryResourceStack : public detail::_MemoryResourceSingleChunk // NOLINT(*-member-functions)
|
||||
{
|
||||
|
||||
C4_NO_COPY_OR_MOVE(MemoryResourceStack);
|
||||
|
||||
public:
|
||||
|
||||
using detail::_MemoryResourceSingleChunk::_MemoryResourceSingleChunk;
|
||||
|
||||
protected:
|
||||
|
||||
void* do_allocate(size_t sz, size_t alignment, void *hint) override;
|
||||
void do_deallocate(void* ptr, size_t sz, size_t alignment) override;
|
||||
void* do_reallocate(void* ptr, size_t oldsz, size_t newsz, size_t alignment) override;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
/** provides a linear array-based memory resource.
|
||||
* @see MemoryResourceLinear
|
||||
* @ingroup memory_resources */
|
||||
template<size_t N>
|
||||
struct MemoryResourceLinearArr : public MemoryResourceLinear
|
||||
{
|
||||
C4_SUPPRESS_WARNING_MSVC_WITH_PUSH(4324) // structure was padded due to alignment specifier
|
||||
alignas(alignof(max_align_t)) char m_arr[N];
|
||||
C4_SUPPRESS_WARNING_MSVC_POP
|
||||
MemoryResourceLinearArr() : MemoryResourceLinear(m_arr, N) { name = "linear_arr"; } // NOLINT
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
struct AllocationCounts
|
||||
{
|
||||
struct Item
|
||||
{
|
||||
ssize_t allocs;
|
||||
ssize_t size;
|
||||
|
||||
void add(size_t sz)
|
||||
{
|
||||
++allocs;
|
||||
size += static_cast<ssize_t>(sz);
|
||||
}
|
||||
void rem(size_t sz)
|
||||
{
|
||||
--allocs;
|
||||
size -= static_cast<ssize_t>(sz);
|
||||
}
|
||||
Item max(Item const& that) const
|
||||
{
|
||||
Item r(*this);
|
||||
r.allocs = r.allocs > that.allocs ? r.allocs : that.allocs;
|
||||
r.size = r.size > that.size ? r.size : that.size;
|
||||
return r;
|
||||
}
|
||||
};
|
||||
|
||||
Item curr = {0, 0};
|
||||
Item total = {0, 0};
|
||||
Item max = {0, 0};
|
||||
|
||||
void clear_counts()
|
||||
{
|
||||
curr = {0, 0};
|
||||
total = {0, 0};
|
||||
max = {0, 0};
|
||||
}
|
||||
|
||||
void update(AllocationCounts const& that)
|
||||
{
|
||||
curr.allocs += that.curr.allocs;
|
||||
curr.size += that.curr.size;
|
||||
total.allocs += that.total.allocs;
|
||||
total.size += that.total.size;
|
||||
max.allocs += that.max.allocs;
|
||||
max.size += that.max.size;
|
||||
}
|
||||
|
||||
void add_counts(void* ptr, size_t sz)
|
||||
{
|
||||
if(ptr == nullptr) return;
|
||||
curr.add(sz);
|
||||
total.add(sz);
|
||||
max = max.max(curr);
|
||||
}
|
||||
|
||||
void rem_counts(void *ptr, size_t sz)
|
||||
{
|
||||
if(ptr == nullptr) return;
|
||||
curr.rem(sz);
|
||||
}
|
||||
|
||||
AllocationCounts operator- (AllocationCounts const& that) const
|
||||
{
|
||||
AllocationCounts r(*this);
|
||||
r.curr.allocs -= that.curr.allocs;
|
||||
r.curr.size -= that.curr.size;
|
||||
r.total.allocs -= that.total.allocs;
|
||||
r.total.size -= that.total.size;
|
||||
r.max.allocs -= that.max.allocs;
|
||||
r.max.size -= that.max.size;
|
||||
return r;
|
||||
}
|
||||
|
||||
AllocationCounts operator+ (AllocationCounts const& that) const
|
||||
{
|
||||
AllocationCounts r(*this);
|
||||
r.curr.allocs += that.curr.allocs;
|
||||
r.curr.size += that.curr.size;
|
||||
r.total.allocs += that.total.allocs;
|
||||
r.total.size += that.total.size;
|
||||
r.max.allocs += that.max.allocs;
|
||||
r.max.size += that.max.size;
|
||||
return r;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
/** a MemoryResource which latches onto another MemoryResource
|
||||
* and counts allocations and sizes.
|
||||
* @ingroup memory_resources */
|
||||
class MemoryResourceCounts : public MemoryResource
|
||||
{
|
||||
public:
|
||||
|
||||
MemoryResourceCounts() : m_resource(get_memory_resource())
|
||||
{
|
||||
C4_ASSERT(m_resource != this);
|
||||
name = "MemoryResourceCounts";
|
||||
}
|
||||
MemoryResourceCounts(MemoryResource *res) : m_resource(res)
|
||||
{
|
||||
C4_ASSERT(m_resource != this);
|
||||
name = "MemoryResourceCounts";
|
||||
}
|
||||
|
||||
MemoryResource *resource() { return m_resource; }
|
||||
AllocationCounts const& counts() const { return m_counts; }
|
||||
|
||||
protected:
|
||||
|
||||
MemoryResource *m_resource; // NOLINT
|
||||
AllocationCounts m_counts; // NOLINT
|
||||
|
||||
protected:
|
||||
|
||||
void* do_allocate(size_t sz, size_t alignment, void * /*hint*/) override
|
||||
{
|
||||
void *ptr = m_resource->allocate(sz, alignment);
|
||||
m_counts.add_counts(ptr, sz);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void do_deallocate(void* ptr, size_t sz, size_t alignment) override
|
||||
{
|
||||
m_counts.rem_counts(ptr, sz);
|
||||
m_resource->deallocate(ptr, sz, alignment);
|
||||
}
|
||||
|
||||
void* do_reallocate(void* ptr, size_t oldsz, size_t newsz, size_t alignment) override
|
||||
{
|
||||
m_counts.rem_counts(ptr, oldsz);
|
||||
void* nptr = m_resource->reallocate(ptr, oldsz, newsz, alignment);
|
||||
m_counts.add_counts(nptr, newsz);
|
||||
return nptr;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** RAII class which binds a memory resource with a scope duration.
|
||||
* @ingroup memory_resources */
|
||||
struct ScopedMemoryResource // NOLINT(*-member-functions)
|
||||
{
|
||||
MemoryResource *m_original;
|
||||
|
||||
ScopedMemoryResource(MemoryResource *r)
|
||||
:
|
||||
m_original(get_memory_resource())
|
||||
{
|
||||
set_memory_resource(r);
|
||||
}
|
||||
|
||||
~ScopedMemoryResource()
|
||||
{
|
||||
set_memory_resource(m_original);
|
||||
}
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** RAII class which counts allocations and frees inside a scope. Can
|
||||
* optionally set also the memory resource to be used.
|
||||
* @ingroup memory_resources */
|
||||
struct ScopedMemoryResourceCounts // NOLINT(*-member-functions)
|
||||
{
|
||||
MemoryResourceCounts mr;
|
||||
|
||||
ScopedMemoryResourceCounts() : mr()
|
||||
{
|
||||
set_memory_resource(&mr);
|
||||
}
|
||||
ScopedMemoryResourceCounts(MemoryResource *m) : mr(m)
|
||||
{
|
||||
set_memory_resource(&mr);
|
||||
}
|
||||
~ScopedMemoryResourceCounts()
|
||||
{
|
||||
set_memory_resource(mr.resource());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace c4
|
||||
|
||||
#endif /* _C4_MEMORY_RESOURCE_HPP_ */
|
||||
51
ext/c4core.dev/c4/restrict.hpp
Normal file
51
ext/c4core.dev/c4/restrict.hpp
Normal file
@@ -0,0 +1,51 @@
|
||||
#ifndef _C4_RESTRICT_HPP_
|
||||
#define _C4_RESTRICT_HPP_
|
||||
|
||||
/** @file restrict.hpp macros defining shorthand symbols for restricted
|
||||
* pointers and references
|
||||
* @see unrestrict.hpp
|
||||
* @see restrict
|
||||
*/
|
||||
|
||||
/** @defgroup restrict Restrict utilities
|
||||
* macros defining shorthand symbols for restricted
|
||||
* pointers and references
|
||||
* ```cpp
|
||||
* void sum_arrays(size_t sz, float const* C4_RESTRICT a, float const *C4_RESTRICT b, float *result);
|
||||
* float * C4_RESTRICT ptr;
|
||||
* float & C4_RESTRICT ref = *ptr;
|
||||
* float const* C4_RESTRICT cptr;
|
||||
* float const& C4_RESTRICT cref = *cptr;
|
||||
*
|
||||
* // becomes this:
|
||||
* #include <c4/restrict.hpp>
|
||||
* void sum_arrays(size_t sz, float c$ a, float c$ b, float * result);
|
||||
* float $ ptr;
|
||||
* float $$ ref = *ptr;
|
||||
* float c$ cptr;
|
||||
* float c$$ cref = *cptr;
|
||||
* ```
|
||||
* @ingroup types
|
||||
* @{ */
|
||||
|
||||
/** @def \$ a restricted pointer */
|
||||
/** @def c\$ a restricted pointer to const data */
|
||||
|
||||
/** @def \$\$ a restricted reference */
|
||||
/** @def c\$\$ a restricted reference to const data */
|
||||
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wdollar-in-identifier-extension"
|
||||
#elif defined(__GNUC__)
|
||||
#endif
|
||||
|
||||
#define $ * C4_RESTRICT // a restricted pointer
|
||||
#define c$ const* C4_RESTRICT // a restricted pointer to const data
|
||||
|
||||
#define $$ & C4_RESTRICT // restricted reference
|
||||
#define c$$ const& C4_RESTRICT // restricted reference to const data
|
||||
|
||||
/** @} */
|
||||
|
||||
#endif /* _C4_RESTRICT_HPP_ */
|
||||
529
ext/c4core.dev/c4/span.hpp
Normal file
529
ext/c4core.dev/c4/span.hpp
Normal file
@@ -0,0 +1,529 @@
|
||||
#ifndef _C4_SPAN_HPP_
|
||||
#define _C4_SPAN_HPP_
|
||||
|
||||
/** @file span.hpp Provides span classes. */
|
||||
|
||||
#include "c4/config.hpp"
|
||||
#include "c4/types.hpp"
|
||||
#include "c4/error.hpp"
|
||||
#include "c4/szconv.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace c4 {
|
||||
|
||||
C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
|
||||
// NOLINTBEGIN(misc-confusable-identifiers)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
/** a crtp base for implementing span classes
|
||||
*
|
||||
* A span is a non-owning range of elements contiguously stored in memory.
|
||||
* Unlike STL's array_view, the span allows write-access to its members.
|
||||
*
|
||||
* To obtain subspans from a span, the following const member functions
|
||||
* are available:
|
||||
* - subspan(first, num)
|
||||
* - range(first, last)
|
||||
* - first(num)
|
||||
* - last(num)
|
||||
*
|
||||
* A span can also be resized via the following non-const member functions:
|
||||
* - resize(sz)
|
||||
* - ltrim(num)
|
||||
* - rtrim(num)
|
||||
*
|
||||
* @see span
|
||||
* @see cspan
|
||||
* @see spanrs
|
||||
* @see cspanrs
|
||||
* @see spanrsl
|
||||
* @see cspanrsl
|
||||
*/
|
||||
template<class T, class I, class SpanImpl>
|
||||
class span_crtp
|
||||
{
|
||||
// some utility defines, undefined at the end of this class
|
||||
#define _c4this ((SpanImpl *)this)
|
||||
#define _c4cthis ((SpanImpl const*)this)
|
||||
#define _c4ptr ((SpanImpl *)this)->m_ptr
|
||||
#define _c4cptr ((SpanImpl const*)this)->m_ptr
|
||||
#define _c4sz ((SpanImpl *)this)->m_size
|
||||
#define _c4csz ((SpanImpl const*)this)->m_size
|
||||
|
||||
public:
|
||||
|
||||
_c4_DEFINE_ARRAY_TYPES(T, I);
|
||||
|
||||
public:
|
||||
|
||||
C4_ALWAYS_INLINE constexpr I value_size() const noexcept { return sizeof(T); }
|
||||
C4_ALWAYS_INLINE constexpr I elm_size () const noexcept { return sizeof(T); }
|
||||
C4_ALWAYS_INLINE constexpr I type_size () const noexcept { return sizeof(T); }
|
||||
C4_ALWAYS_INLINE I byte_size () const noexcept { return _c4csz*sizeof(T); }
|
||||
|
||||
C4_ALWAYS_INLINE bool empty() const noexcept { return _c4csz == 0; }
|
||||
C4_ALWAYS_INLINE I size() const noexcept { return _c4csz; }
|
||||
//C4_ALWAYS_INLINE I capacity() const noexcept { return _c4sz; } // this must be defined by impl classes
|
||||
|
||||
C4_ALWAYS_INLINE void clear() noexcept { _c4sz = 0; }
|
||||
|
||||
C4_ALWAYS_INLINE T * data() noexcept { return _c4ptr; }
|
||||
C4_ALWAYS_INLINE T const* data() const noexcept { return _c4cptr; }
|
||||
|
||||
C4_ALWAYS_INLINE iterator begin() noexcept { return _c4ptr; }
|
||||
C4_ALWAYS_INLINE const_iterator begin() const noexcept { return _c4cptr; }
|
||||
C4_ALWAYS_INLINE const_iterator cbegin() const noexcept { return _c4cptr; }
|
||||
|
||||
C4_ALWAYS_INLINE iterator end() noexcept { return _c4ptr + _c4sz; }
|
||||
C4_ALWAYS_INLINE const_iterator end() const noexcept { return _c4cptr + _c4csz; }
|
||||
C4_ALWAYS_INLINE const_iterator cend() const noexcept { return _c4cptr + _c4csz; }
|
||||
|
||||
C4_ALWAYS_INLINE reverse_iterator rbegin() noexcept { return reverse_iterator(_c4ptr + _c4sz); }
|
||||
C4_ALWAYS_INLINE const_reverse_iterator rbegin() const noexcept { return reverse_iterator(_c4cptr + _c4csz); }
|
||||
C4_ALWAYS_INLINE const_reverse_iterator crbegin() const noexcept { return reverse_iterator(_c4cptr + _c4csz); }
|
||||
|
||||
C4_ALWAYS_INLINE reverse_iterator rend() noexcept { return const_reverse_iterator(_c4ptr); }
|
||||
C4_ALWAYS_INLINE const_reverse_iterator rend() const noexcept { return const_reverse_iterator(_c4cptr); }
|
||||
C4_ALWAYS_INLINE const_reverse_iterator crend() const noexcept { return const_reverse_iterator(_c4cptr); }
|
||||
|
||||
C4_ALWAYS_INLINE T & front() C4_NOEXCEPT_X { C4_XASSERT(!empty()); return _c4ptr [0]; }
|
||||
C4_ALWAYS_INLINE T const& front() const C4_NOEXCEPT_X { C4_XASSERT(!empty()); return _c4cptr[0]; }
|
||||
|
||||
C4_ALWAYS_INLINE T & back() C4_NOEXCEPT_X { C4_XASSERT(!empty()); return _c4ptr [_c4sz - 1]; }
|
||||
C4_ALWAYS_INLINE T const& back() const C4_NOEXCEPT_X { C4_XASSERT(!empty()); return _c4cptr[_c4csz - 1]; }
|
||||
|
||||
C4_ALWAYS_INLINE T & operator[] (I i) C4_NOEXCEPT_X { C4_XASSERT(i >= 0 && i < _c4sz ); return _c4ptr [i]; }
|
||||
C4_ALWAYS_INLINE T const& operator[] (I i) const C4_NOEXCEPT_X { C4_XASSERT(i >= 0 && i < _c4csz); return _c4cptr[i]; }
|
||||
|
||||
C4_ALWAYS_INLINE SpanImpl subspan(I first, I num) const C4_NOEXCEPT_X
|
||||
{
|
||||
C4_XASSERT((first >= 0 && first < _c4csz) || (first == _c4csz && num == 0));
|
||||
C4_XASSERT((first + num >= 0) && (first + num <= _c4csz));
|
||||
return _c4cthis->_select(_c4cptr + first, num);
|
||||
}
|
||||
C4_ALWAYS_INLINE SpanImpl subspan(I first) const C4_NOEXCEPT_X ///< goes up until the end of the span
|
||||
{
|
||||
C4_XASSERT(first >= 0 && first <= _c4csz);
|
||||
return _c4cthis->_select(_c4cptr + first, _c4csz - first);
|
||||
}
|
||||
|
||||
C4_ALWAYS_INLINE SpanImpl range(I first, I last) const C4_NOEXCEPT_X ///< last element is NOT included
|
||||
{
|
||||
C4_XASSERT(((first >= 0) && (first < _c4csz)) || (first == _c4csz && first == last));
|
||||
C4_XASSERT((last >= 0) && (last <= _c4csz));
|
||||
C4_XASSERT(last >= first);
|
||||
return _c4cthis->_select(_c4cptr + first, last - first);
|
||||
}
|
||||
C4_ALWAYS_INLINE SpanImpl range(I first) const C4_NOEXCEPT_X ///< goes up until the end of the span
|
||||
{
|
||||
C4_XASSERT(((first >= 0) && (first <= _c4csz)));
|
||||
return _c4cthis->_select(_c4cptr + first, _c4csz - first);
|
||||
}
|
||||
|
||||
C4_ALWAYS_INLINE SpanImpl first(I num) const C4_NOEXCEPT_X ///< get the first num elements, starting at 0
|
||||
{
|
||||
C4_XASSERT((num >= 0) && (num <= _c4csz));
|
||||
return _c4cthis->_select(_c4cptr, num);
|
||||
}
|
||||
C4_ALWAYS_INLINE SpanImpl last(I num) const C4_NOEXCEPT_X ///< get the last num elements, starting at size()-num
|
||||
{
|
||||
C4_XASSERT((num >= 0) && (num <= _c4csz));
|
||||
return _c4cthis->_select(_c4cptr + _c4csz - num, num);
|
||||
}
|
||||
|
||||
bool is_subspan(span_crtp const& ss) const noexcept
|
||||
{
|
||||
if(_c4cptr == nullptr) return false;
|
||||
auto *b = begin(), *e = end();
|
||||
auto *ssb = ss.begin(), *sse = ss.end();
|
||||
if(ssb >= b && sse <= e)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** COMPLement Left: return the complement to the left of the beginning of the given subspan.
|
||||
* If ss does not begin inside this, returns an empty substring. */
|
||||
SpanImpl compll(span_crtp const& ss) const C4_NOEXCEPT_X
|
||||
{
|
||||
auto ssb = ss.begin();
|
||||
auto b = begin();
|
||||
auto e = end();
|
||||
if(ssb >= b && ssb <= e)
|
||||
{
|
||||
return subspan(0, static_cast<size_t>(ssb - b));
|
||||
}
|
||||
else
|
||||
{
|
||||
return subspan(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/** COMPLement Right: return the complement to the right of the end of the given subspan.
|
||||
* If ss does not end inside this, returns an empty substring. */
|
||||
SpanImpl complr(span_crtp const& ss) const C4_NOEXCEPT_X
|
||||
{
|
||||
auto sse = ss.end();
|
||||
auto b = begin();
|
||||
auto e = end();
|
||||
if(sse >= b && sse <= e)
|
||||
{
|
||||
return subspan(static_cast<size_t>(sse - b), static_cast<size_t>(e - sse));
|
||||
}
|
||||
else
|
||||
{
|
||||
return subspan(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
C4_ALWAYS_INLINE bool same_span(span_crtp const& that) const noexcept
|
||||
{
|
||||
return size() == that.size() && data() == that.data();
|
||||
}
|
||||
template<class I2, class Impl2>
|
||||
C4_ALWAYS_INLINE bool same_span(span_crtp<T, I2, Impl2> const& that) const C4_NOEXCEPT_X
|
||||
{
|
||||
I tsz = szconv<I>(that.size()); // x-asserts that the size does not overflow
|
||||
return size() == tsz && data() == that.data();
|
||||
}
|
||||
|
||||
#undef _c4this
|
||||
#undef _c4cthis
|
||||
#undef _c4ptr
|
||||
#undef _c4cptr
|
||||
#undef _c4sz
|
||||
#undef _c4csz
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// NOLINTBEGIN(*-redundant-inline*)
|
||||
|
||||
template<class T, class Il, class Ir, class _Impll, class _Implr>
|
||||
inline constexpr bool operator==
|
||||
(
|
||||
span_crtp<T, Il, _Impll> const& l,
|
||||
span_crtp<T, Ir, _Implr> const& r
|
||||
)
|
||||
{
|
||||
#if C4_CPP >= 14
|
||||
return std::equal(l.begin(), l.end(), r.begin(), r.end());
|
||||
#else
|
||||
return l.same_span(r) || std::equal(l.begin(), l.end(), r.begin());
|
||||
#endif
|
||||
}
|
||||
|
||||
template<class T, class Il, class Ir, class _Impll, class _Implr>
|
||||
inline constexpr bool operator!=
|
||||
(
|
||||
span_crtp<T, Il, _Impll> const& l,
|
||||
span_crtp<T, Ir, _Implr> const& r
|
||||
)
|
||||
{
|
||||
return ! (l == r);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template<class T, class Il, class Ir, class _Impll, class _Implr>
|
||||
inline constexpr bool operator<
|
||||
(
|
||||
span_crtp<T, Il, _Impll> const& l,
|
||||
span_crtp<T, Ir, _Implr> const& r
|
||||
)
|
||||
{
|
||||
return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end());
|
||||
}
|
||||
|
||||
template<class T, class Il, class Ir, class _Impll, class _Implr>
|
||||
inline constexpr bool operator<=
|
||||
(
|
||||
span_crtp<T, Il, _Impll> const& l,
|
||||
span_crtp<T, Ir, _Implr> const& r
|
||||
)
|
||||
{
|
||||
return ! (l > r);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template<class T, class Il, class Ir, class _Impll, class _Implr>
|
||||
inline constexpr bool operator>
|
||||
(
|
||||
span_crtp<T, Il, _Impll> const& l,
|
||||
span_crtp<T, Ir, _Implr> const& r
|
||||
)
|
||||
{
|
||||
return r < l;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template<class T, class Il, class Ir, class _Impll, class _Implr>
|
||||
inline constexpr bool operator>=
|
||||
(
|
||||
span_crtp<T, Il, _Impll> const& l,
|
||||
span_crtp<T, Ir, _Implr> const& r
|
||||
)
|
||||
{
|
||||
return ! (l < r);
|
||||
}
|
||||
|
||||
// NOLINTEND(*-redundant-inline*)
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
/** A non-owning span of elements contiguously stored in memory. */
|
||||
template<class T, class I=C4_SIZE_TYPE>
|
||||
class span : public span_crtp<T, I, span<T, I>> // NOLINT(*-special-member-functions)
|
||||
{
|
||||
friend class span_crtp<T, I, span<T, I>>;
|
||||
|
||||
T * C4_RESTRICT m_ptr;
|
||||
I m_size;
|
||||
|
||||
C4_ALWAYS_INLINE span _select(T *p, I sz) const { return span(p, sz); }
|
||||
|
||||
public:
|
||||
|
||||
_c4_DEFINE_ARRAY_TYPES(T, I);
|
||||
using NCT = typename std::remove_const<T>::type; //!< NCT=non const type
|
||||
using CT = typename std::add_const<T>::type; //!< CT=const type
|
||||
using const_type = span<CT, I>;
|
||||
|
||||
/// convert automatically to span of const T
|
||||
operator span<CT, I> () const { span<CT, I> s(m_ptr, m_size); return s; }
|
||||
|
||||
public:
|
||||
|
||||
C4_ALWAYS_INLINE C4_CONSTEXPR14 span() noexcept : m_ptr{nullptr}, m_size{0} {}
|
||||
|
||||
span(span const&) = default;
|
||||
span(span &&) = default;
|
||||
|
||||
span& operator= (span const&) = default;
|
||||
span& operator= (span &&) = default;
|
||||
|
||||
public:
|
||||
|
||||
/** @name Construction and assignment from same type */
|
||||
/** @{ */
|
||||
|
||||
template<size_t N> C4_ALWAYS_INLINE C4_CONSTEXPR14 span (T (&arr)[N]) noexcept : m_ptr{arr}, m_size{N} {}
|
||||
template<size_t N> C4_ALWAYS_INLINE C4_CONSTEXPR14 void assign(T (&arr)[N]) noexcept { m_ptr = arr; m_size = N; }
|
||||
|
||||
C4_ALWAYS_INLINE C4_CONSTEXPR14 span(T *p, I sz) noexcept : m_ptr{p}, m_size{sz} {}
|
||||
C4_ALWAYS_INLINE C4_CONSTEXPR14 void assign(T *p, I sz) noexcept { m_ptr = p; m_size = sz; }
|
||||
|
||||
C4_ALWAYS_INLINE C4_CONSTEXPR14 span (c4::aggregate_t, std::initializer_list<T> il) noexcept : m_ptr{&*il.begin()}, m_size{il.size()} {}
|
||||
C4_ALWAYS_INLINE C4_CONSTEXPR14 void assign(c4::aggregate_t, std::initializer_list<T> il) noexcept { m_ptr = &*il.begin(); m_size = il.size(); }
|
||||
|
||||
/** @} */
|
||||
|
||||
public:
|
||||
|
||||
C4_ALWAYS_INLINE I capacity() const noexcept { return m_size; }
|
||||
|
||||
C4_ALWAYS_INLINE void resize(I sz) C4_NOEXCEPT_A { C4_ASSERT(sz <= m_size); m_size = sz; }
|
||||
C4_ALWAYS_INLINE void rtrim (I n ) C4_NOEXCEPT_A { C4_ASSERT(n >= 0 && n < m_size); m_size -= n; }
|
||||
C4_ALWAYS_INLINE void ltrim (I n ) C4_NOEXCEPT_A { C4_ASSERT(n >= 0 && n < m_size); m_size -= n; m_ptr += n; }
|
||||
|
||||
};
|
||||
template<class T, class I=C4_SIZE_TYPE> using cspan = span<const T, I>;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
/** A non-owning span resizeable up to a capacity. Subselection or resizing
|
||||
* will keep the original provided it starts at begin(). If subselection or
|
||||
* resizing change the pointer, then the original capacity information will
|
||||
* be lost.
|
||||
*
|
||||
* Thus, resizing via resize() and ltrim() and subselecting via first()
|
||||
* or any of subspan() or range() when starting from the beginning will keep
|
||||
* the original capacity. OTOH, using last(), or any of subspan() or range()
|
||||
* with an offset from the start will remove from capacity (shifting the
|
||||
* pointer) by the corresponding offset. If this is undesired, then consider
|
||||
* using spanrsl.
|
||||
*
|
||||
* @see spanrs for a span resizeable on the right
|
||||
* @see spanrsl for a span resizeable on the right and left
|
||||
*/
|
||||
|
||||
template<class T, class I=C4_SIZE_TYPE>
|
||||
class spanrs : public span_crtp<T, I, spanrs<T, I>> // NOLINT(*-special-member-functions)
|
||||
{
|
||||
friend class span_crtp<T, I, spanrs<T, I>>;
|
||||
|
||||
T * C4_RESTRICT m_ptr;
|
||||
I m_size;
|
||||
I m_capacity;
|
||||
|
||||
C4_ALWAYS_INLINE spanrs _select(T *p, I sz) const noexcept
|
||||
{
|
||||
C4_ASSERT(p >= m_ptr);
|
||||
size_t delta = static_cast<size_t>(p - m_ptr);
|
||||
C4_ASSERT(m_capacity >= delta);
|
||||
return spanrs(p, sz, static_cast<size_t>(m_capacity - delta));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
_c4_DEFINE_ARRAY_TYPES(T, I);
|
||||
using NCT = typename std::remove_const<T>::type; //!< NCT=non const type
|
||||
using CT = typename std::add_const<T>::type; //!< CT=const type
|
||||
using const_type = spanrs<CT, I>;
|
||||
|
||||
/// convert automatically to span of T
|
||||
C4_ALWAYS_INLINE operator span<T, I > () const noexcept { return span<T, I>(m_ptr, m_size); }
|
||||
/// convert automatically to span of const T
|
||||
//C4_ALWAYS_INLINE operator span<CT, I> () const noexcept { span<CT, I> s(m_ptr, m_size); return s; }
|
||||
/// convert automatically to spanrs of const T
|
||||
C4_ALWAYS_INLINE operator spanrs<CT, I> () const noexcept { spanrs<CT, I> s(m_ptr, m_size, m_capacity); return s; }
|
||||
|
||||
public:
|
||||
|
||||
C4_ALWAYS_INLINE spanrs() noexcept : m_ptr{nullptr}, m_size{0}, m_capacity{0} {}
|
||||
|
||||
spanrs(spanrs const&) = default;
|
||||
spanrs(spanrs &&) = default;
|
||||
|
||||
spanrs& operator= (spanrs const&) = default;
|
||||
spanrs& operator= (spanrs &&) = default;
|
||||
|
||||
public:
|
||||
|
||||
/** @name Construction and assignment from same type */
|
||||
/** @{ */
|
||||
|
||||
C4_ALWAYS_INLINE spanrs(T *p, I sz) noexcept : m_ptr{p}, m_size{sz}, m_capacity{sz} {}
|
||||
/** @warning will reset the capacity to sz */
|
||||
C4_ALWAYS_INLINE void assign(T *p, I sz) noexcept { m_ptr = p; m_size = sz; m_capacity = sz; }
|
||||
|
||||
C4_ALWAYS_INLINE spanrs(T *p, I sz, I cap) noexcept : m_ptr{p}, m_size{sz}, m_capacity{cap} {}
|
||||
C4_ALWAYS_INLINE void assign(T *p, I sz, I cap) noexcept { m_ptr = p; m_size = sz; m_capacity = cap; }
|
||||
|
||||
template<size_t N> C4_ALWAYS_INLINE spanrs(T (&arr)[N]) noexcept : m_ptr{arr}, m_size{N}, m_capacity{N} {}
|
||||
template<size_t N> C4_ALWAYS_INLINE void assign(T (&arr)[N]) noexcept { m_ptr = arr; m_size = N; m_capacity = N; }
|
||||
|
||||
C4_ALWAYS_INLINE spanrs(c4::aggregate_t, std::initializer_list<T> il) noexcept : m_ptr{il.begin()}, m_size{il.size()}, m_capacity{il.size()} {}
|
||||
C4_ALWAYS_INLINE void assign(c4::aggregate_t, std::initializer_list<T> il) noexcept { m_ptr = il.begin(); m_size = il.size(); m_capacity = il.size(); }
|
||||
|
||||
/** @} */
|
||||
|
||||
public:
|
||||
|
||||
C4_ALWAYS_INLINE I capacity() const noexcept { return m_capacity; }
|
||||
|
||||
C4_ALWAYS_INLINE void resize(I sz) C4_NOEXCEPT_A { C4_ASSERT(sz <= m_capacity); m_size = sz; }
|
||||
C4_ALWAYS_INLINE void rtrim (I n ) C4_NOEXCEPT_A { C4_ASSERT(n >= 0 && n < m_size); m_size -= n; }
|
||||
C4_ALWAYS_INLINE void ltrim (I n ) C4_NOEXCEPT_A { C4_ASSERT(n >= 0 && n < m_size); m_size -= n; m_ptr += n; m_capacity -= n; }
|
||||
|
||||
};
|
||||
template<class T, class I=C4_SIZE_TYPE> using cspanrs = spanrs<const T, I>;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
/** A non-owning span which always retains the capacity of the original
|
||||
* range it was taken from (though it may loose its original size).
|
||||
* The resizing methods resize(), ltrim(), rtrim() as well
|
||||
* as the subselection methods subspan(), range(), first() and last() can be
|
||||
* used at will without loosing the original capacity; the full capacity span
|
||||
* can always be recovered by calling original().
|
||||
*/
|
||||
template<class T, class I=C4_SIZE_TYPE>
|
||||
class spanrsl : public span_crtp<T, I, spanrsl<T, I>> // NOLINT(*-special-member-functions)
|
||||
{
|
||||
friend class span_crtp<T, I, spanrsl<T, I>>;
|
||||
|
||||
T *C4_RESTRICT m_ptr; ///< the current ptr. the original ptr is (m_ptr - m_offset).
|
||||
I m_size; ///< the current size. the original size is unrecoverable.
|
||||
I m_capacity; ///< the current capacity. the original capacity is (m_capacity + m_offset).
|
||||
I m_offset; ///< the offset of the current m_ptr to the start of the original memory block.
|
||||
|
||||
C4_ALWAYS_INLINE spanrsl _select(T *p, I sz) const noexcept
|
||||
{
|
||||
C4_ASSERT(p >= m_ptr);
|
||||
I delta = static_cast<I>(p - m_ptr);
|
||||
C4_ASSERT(m_capacity >= delta);
|
||||
return spanrsl(p, sz, static_cast<I>(m_capacity - delta), m_offset + delta);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
_c4_DEFINE_ARRAY_TYPES(T, I);
|
||||
using NCT = typename std::remove_const<T>::type; //!< NCT=non const type
|
||||
using CT = typename std::add_const<T>::type; //!< CT=const type
|
||||
using const_type = spanrsl<CT, I>;
|
||||
|
||||
C4_ALWAYS_INLINE operator span<T, I> () const noexcept { return span<T, I>(m_ptr, m_size); }
|
||||
C4_ALWAYS_INLINE operator spanrs<T, I> () const noexcept { return spanrs<T, I>(m_ptr, m_size, m_capacity); }
|
||||
C4_ALWAYS_INLINE operator spanrsl<CT, I> () const noexcept { return spanrsl<CT, I>(m_ptr, m_size, m_capacity, m_offset); }
|
||||
|
||||
public:
|
||||
|
||||
C4_ALWAYS_INLINE spanrsl() noexcept : m_ptr{nullptr}, m_size{0}, m_capacity{0}, m_offset{0} {}
|
||||
|
||||
spanrsl(spanrsl const&) = default;
|
||||
spanrsl(spanrsl &&) = default;
|
||||
|
||||
spanrsl& operator= (spanrsl const&) = default;
|
||||
spanrsl& operator= (spanrsl &&) = default;
|
||||
|
||||
public:
|
||||
|
||||
C4_ALWAYS_INLINE spanrsl(T *p, I sz) noexcept : m_ptr{p}, m_size{sz}, m_capacity{sz}, m_offset{0} {}
|
||||
C4_ALWAYS_INLINE void assign(T *p, I sz) noexcept { m_ptr = p; m_size = sz; m_capacity = sz; m_offset = 0; }
|
||||
|
||||
C4_ALWAYS_INLINE spanrsl(T *p, I sz, I cap) noexcept : m_ptr{p}, m_size{sz}, m_capacity{cap}, m_offset{0} {}
|
||||
C4_ALWAYS_INLINE void assign(T *p, I sz, I cap) noexcept { m_ptr = p; m_size = sz; m_capacity = cap; m_offset = 0; }
|
||||
|
||||
C4_ALWAYS_INLINE spanrsl(T *p, I sz, I cap, I offs) noexcept : m_ptr{p}, m_size{sz}, m_capacity{cap}, m_offset{offs} {}
|
||||
C4_ALWAYS_INLINE void assign(T *p, I sz, I cap, I offs) noexcept { m_ptr = p; m_size = sz; m_capacity = cap; m_offset = offs; }
|
||||
|
||||
template<size_t N> C4_ALWAYS_INLINE spanrsl(T (&arr)[N]) noexcept : m_ptr{arr}, m_size{N}, m_capacity{N}, m_offset{0} {}
|
||||
template<size_t N> C4_ALWAYS_INLINE void assign(T (&arr)[N]) noexcept { m_ptr = arr; m_size = N; m_capacity = N; m_offset = 0; }
|
||||
|
||||
C4_ALWAYS_INLINE spanrsl(c4::aggregate_t, std::initializer_list<T> il) noexcept : m_ptr{il.begin()}, m_size{il.size()}, m_capacity{il.size()}, m_offset{0} {}
|
||||
C4_ALWAYS_INLINE void assign (c4::aggregate_t, std::initializer_list<T> il) noexcept { m_ptr = il.begin(); m_size = il.size(); m_capacity = il.size(); m_offset = 0; }
|
||||
|
||||
public:
|
||||
|
||||
C4_ALWAYS_INLINE I offset() const noexcept { return m_offset; }
|
||||
C4_ALWAYS_INLINE I capacity() const noexcept { return m_capacity; }
|
||||
|
||||
C4_ALWAYS_INLINE void resize(I sz) C4_NOEXCEPT_A { C4_ASSERT(sz <= m_capacity); m_size = sz; }
|
||||
C4_ALWAYS_INLINE void rtrim (I n ) C4_NOEXCEPT_A { C4_ASSERT(n >= 0 && n < m_size); m_size -= n; }
|
||||
C4_ALWAYS_INLINE void ltrim (I n ) C4_NOEXCEPT_A { C4_ASSERT(n >= 0 && n < m_size); m_size -= n; m_ptr += n; m_offset += n; m_capacity -= n; }
|
||||
|
||||
/** recover the original span as an spanrsl */
|
||||
C4_ALWAYS_INLINE spanrsl original() const
|
||||
{
|
||||
return spanrsl(m_ptr - m_offset, m_capacity + m_offset, m_capacity + m_offset, 0);
|
||||
}
|
||||
/** recover the original span as a different span type. Example: spanrs<...> orig = s.original<spanrs>(); */
|
||||
template<template<class, class> class OtherSpanType>
|
||||
C4_ALWAYS_INLINE OtherSpanType<T, I> original()
|
||||
{
|
||||
return OtherSpanType<T, I>(m_ptr - m_offset, m_capacity + m_offset);
|
||||
}
|
||||
};
|
||||
template<class T, class I=C4_SIZE_TYPE> using cspanrsl = spanrsl<const T, I>;
|
||||
|
||||
// NOLINTEND(misc-confusable-identifiers)
|
||||
|
||||
C4_SUPPRESS_WARNING_GCC_CLANG_POP
|
||||
|
||||
} // namespace c4
|
||||
|
||||
|
||||
#endif /* _C4_SPAN_HPP_ */
|
||||
68
ext/c4core.dev/c4/szconv.hpp
Normal file
68
ext/c4core.dev/c4/szconv.hpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#ifndef _C4_SZCONV_HPP_
|
||||
#define _C4_SZCONV_HPP_
|
||||
|
||||
/** @file szconv.hpp utilities to deal safely with narrowing conversions */
|
||||
|
||||
#include "c4/config.hpp"
|
||||
#include "c4/error.hpp"
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace c4 {
|
||||
|
||||
C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
|
||||
|
||||
/** @todo this would be so much easier with calls to numeric_limits::max()... */
|
||||
template<class SizeOut, class SizeIn>
|
||||
struct is_narrower_size : std::conditional
|
||||
<
|
||||
(std::is_signed<SizeOut>::value == std::is_signed<SizeIn>::value)
|
||||
?
|
||||
(sizeof(SizeOut) < sizeof(SizeIn))
|
||||
:
|
||||
(
|
||||
(sizeof(SizeOut) < sizeof(SizeIn))
|
||||
||
|
||||
(
|
||||
(sizeof(SizeOut) == sizeof(SizeIn))
|
||||
&&
|
||||
(std::is_signed<SizeOut>::value && std::is_unsigned<SizeIn>::value)
|
||||
)
|
||||
),
|
||||
std::true_type,
|
||||
std::false_type
|
||||
>::type
|
||||
{
|
||||
static_assert(std::is_integral<SizeIn >::value, "must be integral type");
|
||||
static_assert(std::is_integral<SizeOut>::value, "must be integral type");
|
||||
};
|
||||
|
||||
|
||||
/** when SizeOut is wider than SizeIn, assignment can occur without reservations */
|
||||
template<class SizeOut, class SizeIn>
|
||||
C4_ALWAYS_INLINE
|
||||
typename std::enable_if< ! is_narrower_size<SizeOut, SizeIn>::value, SizeOut>::type
|
||||
szconv(SizeIn sz) noexcept
|
||||
{
|
||||
return static_cast<SizeOut>(sz);
|
||||
}
|
||||
|
||||
/** when SizeOut is narrower than SizeIn, narrowing will occur, so we check
|
||||
* for overflow. Note that this check is done only if C4_XASSERT is enabled.
|
||||
* @see C4_XASSERT */
|
||||
template<class SizeOut, class SizeIn>
|
||||
C4_ALWAYS_INLINE
|
||||
typename std::enable_if<is_narrower_size<SizeOut, SizeIn>::value, SizeOut>::type
|
||||
szconv(SizeIn sz)
|
||||
{
|
||||
C4_XASSERT(sz >= 0);
|
||||
C4_XASSERT_MSG((SizeIn)sz <= (SizeIn)std::numeric_limits<SizeOut>::max(), "size conversion overflow: in=%zu", (size_t)sz);
|
||||
SizeOut szo = static_cast<SizeOut>(sz);
|
||||
return szo;
|
||||
}
|
||||
|
||||
C4_SUPPRESS_WARNING_GCC_CLANG_POP
|
||||
|
||||
} // namespace c4
|
||||
|
||||
#endif /* _C4_SZCONV_HPP_ */
|
||||
17
ext/c4core.dev/c4/unrestrict.hpp
Normal file
17
ext/c4core.dev/c4/unrestrict.hpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifdef _C4_RESTRICT_HPP_ // must match the include guard from restrict.hpp
|
||||
|
||||
/** @file unrestrict.hpp cleans up restrict macros */
|
||||
|
||||
#undef $
|
||||
#undef $$
|
||||
#undef c$
|
||||
#undef c$$
|
||||
|
||||
#undef _C4_RESTRICT_HPP_
|
||||
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
#elif defined(__GNUC__)
|
||||
#endif
|
||||
|
||||
#endif /* _C4_RESTRICT_HPP_ */
|
||||
10
ext/c4core.dev/c4/windows.hpp
Normal file
10
ext/c4core.dev/c4/windows.hpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef _C4_WINDOWS_HPP_
|
||||
#define _C4_WINDOWS_HPP_
|
||||
|
||||
#if defined(_WIN64) || defined(_WIN32)
|
||||
#include "c4/windows_push.hpp"
|
||||
#include <windows.h>
|
||||
#include "c4/windows_pop.hpp"
|
||||
#endif
|
||||
|
||||
#endif /* _C4_WINDOWS_HPP_ */
|
||||
41
ext/c4core.dev/c4/windows_pop.hpp
Normal file
41
ext/c4core.dev/c4/windows_pop.hpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#ifndef _C4_WINDOWS_POP_HPP_
|
||||
#define _C4_WINDOWS_POP_HPP_
|
||||
|
||||
#if defined(_WIN64) || defined(_WIN32)
|
||||
|
||||
#ifdef _c4_AMD64_
|
||||
# undef _c4_AMD64_
|
||||
# undef _AMD64_
|
||||
#endif
|
||||
#ifdef _c4_X86_
|
||||
# undef _c4_X86_
|
||||
# undef _X86_
|
||||
#endif
|
||||
#ifdef _c4_ARM_
|
||||
# undef _c4_ARM_
|
||||
# undef _ARM_
|
||||
#endif
|
||||
|
||||
#ifdef _c4_NOMINMAX
|
||||
# undef _c4_NOMINMAX
|
||||
# undef NOMINMAX
|
||||
#endif
|
||||
|
||||
#ifdef NOGDI
|
||||
# undef _c4_NOGDI
|
||||
# undef NOGDI
|
||||
#endif
|
||||
|
||||
#ifdef VC_EXTRALEAN
|
||||
# undef _c4_VC_EXTRALEAN
|
||||
# undef VC_EXTRALEAN
|
||||
#endif
|
||||
|
||||
#ifdef WIN32_LEAN_AND_MEAN
|
||||
# undef _c4_WIN32_LEAN_AND_MEAN
|
||||
# undef WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
#endif /* defined(_WIN64) || defined(_WIN32) */
|
||||
|
||||
#endif /* _C4_WINDOWS_POP_HPP_ */
|
||||
102
ext/c4core.dev/c4/windows_push.hpp
Normal file
102
ext/c4core.dev/c4/windows_push.hpp
Normal file
@@ -0,0 +1,102 @@
|
||||
#ifndef _C4_WINDOWS_PUSH_HPP_
|
||||
#define _C4_WINDOWS_PUSH_HPP_
|
||||
|
||||
/** @file windows_push.hpp sets up macros to include windows header files
|
||||
* without pulling in all of <windows.h>
|
||||
*
|
||||
* @see #include windows_pop.hpp to undefine these macros
|
||||
*
|
||||
* @see https://aras-p.info/blog/2018/01/12/Minimizing-windows.h/ */
|
||||
|
||||
|
||||
#if defined(_WIN64) || defined(_WIN32)
|
||||
|
||||
#if defined(_M_AMD64)
|
||||
# ifndef _AMD64_
|
||||
# define _c4_AMD64_
|
||||
# define _AMD64_
|
||||
# endif
|
||||
#elif defined(_M_IX86)
|
||||
# ifndef _X86_
|
||||
# define _c4_X86_
|
||||
# define _X86_
|
||||
# endif
|
||||
#elif defined(_M_ARM64)
|
||||
# ifndef _ARM64_
|
||||
# define _c4_ARM64_
|
||||
# define _ARM64_
|
||||
# endif
|
||||
#elif defined(_M_ARM)
|
||||
# ifndef _ARM_
|
||||
# define _c4_ARM_
|
||||
# define _ARM_
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef NOMINMAX
|
||||
# define _c4_NOMINMAX
|
||||
# define NOMINMAX
|
||||
#endif
|
||||
|
||||
#ifndef NOGDI
|
||||
# define _c4_NOGDI
|
||||
# define NOGDI
|
||||
#endif
|
||||
|
||||
#ifndef VC_EXTRALEAN
|
||||
# define _c4_VC_EXTRALEAN
|
||||
# define VC_EXTRALEAN
|
||||
#endif
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
# define _c4_WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
/* If defined, the following flags inhibit definition
|
||||
* of the indicated items.
|
||||
*
|
||||
* NOGDICAPMASKS - CC_*, LC_*, PC_*, CP_*, TC_*, RC_
|
||||
* NOVIRTUALKEYCODES - VK_*
|
||||
* NOWINMESSAGES - WM_*, EM_*, LB_*, CB_*
|
||||
* NOWINSTYLES - WS_*, CS_*, ES_*, LBS_*, SBS_*, CBS_*
|
||||
* NOSYSMETRICS - SM_*
|
||||
* NOMENUS - MF_*
|
||||
* NOICONS - IDI_*
|
||||
* NOKEYSTATES - MK_*
|
||||
* NOSYSCOMMANDS - SC_*
|
||||
* NORASTEROPS - Binary and Tertiary raster ops
|
||||
* NOSHOWWINDOW - SW_*
|
||||
* OEMRESOURCE - OEM Resource values
|
||||
* NOATOM - Atom Manager routines
|
||||
* NOCLIPBOARD - Clipboard routines
|
||||
* NOCOLOR - Screen colors
|
||||
* NOCTLMGR - Control and Dialog routines
|
||||
* NODRAWTEXT - DrawText() and DT_*
|
||||
* NOGDI - All GDI defines and routines
|
||||
* NOKERNEL - All KERNEL defines and routines
|
||||
* NOUSER - All USER defines and routines
|
||||
* NONLS - All NLS defines and routines
|
||||
* NOMB - MB_* and MessageBox()
|
||||
* NOMEMMGR - GMEM_*, LMEM_*, GHND, LHND, associated routines
|
||||
* NOMETAFILE - typedef METAFILEPICT
|
||||
* NOMINMAX - Macros min(a,b) and max(a,b)
|
||||
* NOMSG - typedef MSG and associated routines
|
||||
* NOOPENFILE - OpenFile(), OemToAnsi, AnsiToOem, and OF_*
|
||||
* NOSCROLL - SB_* and scrolling routines
|
||||
* NOSERVICE - All Service Controller routines, SERVICE_ equates, etc.
|
||||
* NOSOUND - Sound driver routines
|
||||
* NOTEXTMETRIC - typedef TEXTMETRIC and associated routines
|
||||
* NOWH - SetWindowsHook and WH_*
|
||||
* NOWINOFFSETS - GWL_*, GCL_*, associated routines
|
||||
* NOCOMM - COMM driver routines
|
||||
* NOKANJI - Kanji support stuff.
|
||||
* NOHELP - Help engine interface.
|
||||
* NOPROFILER - Profiler interface.
|
||||
* NODEFERWINDOWPOS - DeferWindowPos routines
|
||||
* NOMCX - Modem Configuration Extensions
|
||||
*/
|
||||
|
||||
#endif /* defined(_WIN64) || defined(_WIN32) */
|
||||
|
||||
#endif /* _C4_WINDOWS_PUSH_HPP_ */
|
||||
5
ext/c4core.mk
Normal file
5
ext/c4core.mk
Normal file
@@ -0,0 +1,5 @@
|
||||
# Specify the c4core repo and commit
|
||||
|
||||
C4CORE_REPO := https://github.com/biojppm/c4core
|
||||
# must be tag or a FULL commit hash; cannot be a branch name.
|
||||
C4CORE_TAG := 1f2c1a87ba28344909ff4249d461d190c2dd5de1
|
||||
37
ext/c4core.src.txt
Normal file
37
ext/c4core.src.txt
Normal file
@@ -0,0 +1,37 @@
|
||||
c4/base64.cpp
|
||||
c4/base64.hpp
|
||||
c4/blob.hpp
|
||||
c4/c4core.natvis
|
||||
c4/charconv.hpp
|
||||
c4/compiler.hpp
|
||||
c4/config.hpp
|
||||
c4/cpu.hpp
|
||||
c4/error.cpp
|
||||
c4/error.hpp
|
||||
c4/export.hpp
|
||||
c4/ext/debugbreak/debugbreak.h
|
||||
c4/ext/fast_float.hpp
|
||||
c4/ext/fast_float_all.h
|
||||
c4/format.cpp
|
||||
c4/format.hpp
|
||||
c4/gcc-4.8.hpp
|
||||
c4/language.cpp
|
||||
c4/language.hpp
|
||||
c4/memory_util.cpp
|
||||
c4/memory_util.hpp
|
||||
c4/platform.hpp
|
||||
c4/preprocessor.hpp
|
||||
c4/std/std.hpp
|
||||
c4/std/std_fwd.hpp
|
||||
c4/std/string.hpp
|
||||
c4/std/string_fwd.hpp
|
||||
c4/std/string_view.hpp
|
||||
c4/std/vector.hpp
|
||||
c4/std/vector_fwd.hpp
|
||||
c4/substr.hpp
|
||||
c4/substr_fwd.hpp
|
||||
c4/types.hpp
|
||||
c4/utf.cpp
|
||||
c4/utf.hpp
|
||||
c4/version.cpp
|
||||
c4/version.hpp
|
||||
226
ext/c4core.src/c4/base64.cpp
Normal file
226
ext/c4core.src/c4/base64.cpp
Normal file
@@ -0,0 +1,226 @@
|
||||
#include "c4/base64.hpp"
|
||||
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wchar-subscripts" // array subscript is of type 'char'
|
||||
# pragma clang diagnostic ignored "-Wold-style-cast"
|
||||
#elif defined(__GNUC__)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wuseless-cast"
|
||||
# pragma GCC diagnostic ignored "-Wchar-subscripts"
|
||||
# pragma GCC diagnostic ignored "-Wtype-limits"
|
||||
# pragma GCC diagnostic ignored "-Wold-style-cast"
|
||||
#endif
|
||||
|
||||
// NOLINTBEGIN(bugprone-signed-char-misuse,cert-str34-c,hicpp-signed-bitwise)
|
||||
|
||||
namespace c4 {
|
||||
|
||||
namespace detail {
|
||||
|
||||
constexpr static const char base64_sextet_to_char_[64] = {
|
||||
/* 0/ 65*/ 'A', /* 1/ 66*/ 'B', /* 2/ 67*/ 'C', /* 3/ 68*/ 'D',
|
||||
/* 4/ 69*/ 'E', /* 5/ 70*/ 'F', /* 6/ 71*/ 'G', /* 7/ 72*/ 'H',
|
||||
/* 8/ 73*/ 'I', /* 9/ 74*/ 'J', /*10/ 75*/ 'K', /*11/ 74*/ 'L',
|
||||
/*12/ 77*/ 'M', /*13/ 78*/ 'N', /*14/ 79*/ 'O', /*15/ 78*/ 'P',
|
||||
/*16/ 81*/ 'Q', /*17/ 82*/ 'R', /*18/ 83*/ 'S', /*19/ 82*/ 'T',
|
||||
/*20/ 85*/ 'U', /*21/ 86*/ 'V', /*22/ 87*/ 'W', /*23/ 88*/ 'X',
|
||||
/*24/ 89*/ 'Y', /*25/ 90*/ 'Z', /*26/ 97*/ 'a', /*27/ 98*/ 'b',
|
||||
/*28/ 99*/ 'c', /*29/100*/ 'd', /*30/101*/ 'e', /*31/102*/ 'f',
|
||||
/*32/103*/ 'g', /*33/104*/ 'h', /*34/105*/ 'i', /*35/106*/ 'j',
|
||||
/*36/107*/ 'k', /*37/108*/ 'l', /*38/109*/ 'm', /*39/110*/ 'n',
|
||||
/*40/111*/ 'o', /*41/112*/ 'p', /*42/113*/ 'q', /*43/114*/ 'r',
|
||||
/*44/115*/ 's', /*45/116*/ 't', /*46/117*/ 'u', /*47/118*/ 'v',
|
||||
/*48/119*/ 'w', /*49/120*/ 'x', /*50/121*/ 'y', /*51/122*/ 'z',
|
||||
/*52/ 48*/ '0', /*53/ 49*/ '1', /*54/ 50*/ '2', /*55/ 51*/ '3',
|
||||
/*56/ 52*/ '4', /*57/ 53*/ '5', /*58/ 54*/ '6', /*59/ 55*/ '7',
|
||||
/*60/ 56*/ '8', /*61/ 57*/ '9', /*62/ 43*/ '+', /*63/ 47*/ '/',
|
||||
};
|
||||
|
||||
// https://www.cs.cmu.edu/~pattis/15-1XX/common/handouts/ascii.html
|
||||
constexpr static const char base64_char_to_sextet_[128] = {
|
||||
#define __ char(-1) // undefined below
|
||||
/* 0 NUL*/ __, /* 1 SOH*/ __, /* 2 STX*/ __, /* 3 ETX*/ __,
|
||||
/* 4 EOT*/ __, /* 5 ENQ*/ __, /* 6 ACK*/ __, /* 7 BEL*/ __,
|
||||
/* 8 BS */ __, /* 9 TAB*/ __, /* 10 LF */ __, /* 11 VT */ __,
|
||||
/* 12 FF */ __, /* 13 CR */ __, /* 14 SO */ __, /* 15 SI */ __,
|
||||
/* 16 DLE*/ __, /* 17 DC1*/ __, /* 18 DC2*/ __, /* 19 DC3*/ __,
|
||||
/* 20 DC4*/ __, /* 21 NAK*/ __, /* 22 SYN*/ __, /* 23 ETB*/ __,
|
||||
/* 24 CAN*/ __, /* 25 EM */ __, /* 26 SUB*/ __, /* 27 ESC*/ __,
|
||||
/* 28 FS */ __, /* 29 GS */ __, /* 30 RS */ __, /* 31 US */ __,
|
||||
/* 32 SPC*/ __, /* 33 ! */ __, /* 34 " */ __, /* 35 # */ __,
|
||||
/* 36 $ */ __, /* 37 % */ __, /* 38 & */ __, /* 39 ' */ __,
|
||||
/* 40 ( */ __, /* 41 ) */ __, /* 42 * */ __, /* 43 + */ 62,
|
||||
/* 44 , */ __, /* 45 - */ __, /* 46 . */ __, /* 47 / */ 63,
|
||||
/* 48 0 */ 52, /* 49 1 */ 53, /* 50 2 */ 54, /* 51 3 */ 55,
|
||||
/* 52 4 */ 56, /* 53 5 */ 57, /* 54 6 */ 58, /* 55 7 */ 59,
|
||||
/* 56 8 */ 60, /* 57 9 */ 61, /* 58 : */ __, /* 59 ; */ __,
|
||||
/* 60 < */ __, /* 61 = */ __, /* 62 > */ __, /* 63 ? */ __,
|
||||
/* 64 @ */ __, /* 65 A */ 0, /* 66 B */ 1, /* 67 C */ 2,
|
||||
/* 68 D */ 3, /* 69 E */ 4, /* 70 F */ 5, /* 71 G */ 6,
|
||||
/* 72 H */ 7, /* 73 I */ 8, /* 74 J */ 9, /* 75 K */ 10,
|
||||
/* 76 L */ 11, /* 77 M */ 12, /* 78 N */ 13, /* 79 O */ 14,
|
||||
/* 80 P */ 15, /* 81 Q */ 16, /* 82 R */ 17, /* 83 S */ 18,
|
||||
/* 84 T */ 19, /* 85 U */ 20, /* 86 V */ 21, /* 87 W */ 22,
|
||||
/* 88 X */ 23, /* 89 Y */ 24, /* 90 Z */ 25, /* 91 [ */ __,
|
||||
/* 92 \ */ __, /* 93 ] */ __, /* 94 ^ */ __, /* 95 _ */ __,
|
||||
/* 96 ` */ __, /* 97 a */ 26, /* 98 b */ 27, /* 99 c */ 28,
|
||||
/*100 d */ 29, /*101 e */ 30, /*102 f */ 31, /*103 g */ 32,
|
||||
/*104 h */ 33, /*105 i */ 34, /*106 j */ 35, /*107 k */ 36,
|
||||
/*108 l */ 37, /*109 m */ 38, /*110 n */ 39, /*111 o */ 40,
|
||||
/*112 p */ 41, /*113 q */ 42, /*114 r */ 43, /*115 s */ 44,
|
||||
/*116 t */ 45, /*117 u */ 46, /*118 v */ 47, /*119 w */ 48,
|
||||
/*120 x */ 49, /*121 y */ 50, /*122 z */ 51, /*123 { */ __,
|
||||
/*124 | */ __, /*125 } */ __, /*126 ~ */ __, /*127 DEL*/ __,
|
||||
#undef __
|
||||
};
|
||||
|
||||
#ifndef NDEBUG
|
||||
void base64_test_tables()
|
||||
{
|
||||
for(size_t i = 0; i < C4_COUNTOF(detail::base64_sextet_to_char_); ++i)
|
||||
{
|
||||
char s2c = base64_sextet_to_char_[i];
|
||||
char c2s = base64_char_to_sextet_[(unsigned)s2c];
|
||||
C4_CHECK((size_t)c2s == i);
|
||||
}
|
||||
for(size_t i = 0; i < C4_COUNTOF(detail::base64_char_to_sextet_); ++i)
|
||||
{
|
||||
char c2s = base64_char_to_sextet_[i];
|
||||
if(c2s == char(-1))
|
||||
continue;
|
||||
char s2c = base64_sextet_to_char_[(unsigned)c2s];
|
||||
C4_CHECK((size_t)s2c == i);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} // namespace detail
|
||||
|
||||
|
||||
bool base64_valid(csubstr encoded)
|
||||
{
|
||||
if((encoded.len & size_t(3u)) != size_t(0)) // (encoded.len % 4u)
|
||||
return false;
|
||||
for(const char c : encoded)
|
||||
{
|
||||
if(c < 0/* || c >= 128*/)
|
||||
return false;
|
||||
if(c == '=')
|
||||
continue;
|
||||
if(detail::base64_char_to_sextet_[c] == char(-1))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
size_t base64_encode(substr buf, cblob data)
|
||||
{
|
||||
#define c4append_(c) { if(pos < buf.len) { buf.str[pos] = (c); } ++pos; }
|
||||
#define c4append_idx_(char_idx) \
|
||||
{\
|
||||
C4_XASSERT((char_idx) < sizeof(detail::base64_sextet_to_char_));\
|
||||
c4append_(detail::base64_sextet_to_char_[(char_idx)]);\
|
||||
}
|
||||
size_t rem, pos = 0;
|
||||
constexpr const uint32_t sextet_mask = uint32_t(1 << 6) - 1;
|
||||
const unsigned char *C4_RESTRICT d = (const unsigned char *) data.buf; // cast to unsigned to avoid wrapping high-bits
|
||||
for(rem = data.len; rem >= 3; rem -= 3, d += 3)
|
||||
{
|
||||
const uint32_t val = ((uint32_t(d[0]) << 16) | (uint32_t(d[1]) << 8) | (uint32_t(d[2])));
|
||||
c4append_idx_((val >> 18) & sextet_mask);
|
||||
c4append_idx_((val >> 12) & sextet_mask);
|
||||
c4append_idx_((val >> 6) & sextet_mask);
|
||||
c4append_idx_((val ) & sextet_mask);
|
||||
}
|
||||
C4_ASSERT(rem < 3);
|
||||
if(rem == 2)
|
||||
{
|
||||
const uint32_t val = ((uint32_t(d[0]) << 16) | (uint32_t(d[1]) << 8));
|
||||
c4append_idx_((val >> 18) & sextet_mask);
|
||||
c4append_idx_((val >> 12) & sextet_mask);
|
||||
c4append_idx_((val >> 6) & sextet_mask);
|
||||
c4append_('=');
|
||||
}
|
||||
else if(rem == 1)
|
||||
{
|
||||
const uint32_t val = ((uint32_t(d[0]) << 16));
|
||||
c4append_idx_((val >> 18) & sextet_mask);
|
||||
c4append_idx_((val >> 12) & sextet_mask);
|
||||
c4append_('=');
|
||||
c4append_('=');
|
||||
}
|
||||
return pos;
|
||||
|
||||
#undef c4append_
|
||||
#undef c4append_idx_
|
||||
}
|
||||
|
||||
|
||||
size_t base64_decode(csubstr encoded, blob data)
|
||||
{
|
||||
#define c4append_(c) { if(wpos < data.len) { data.buf[wpos] = static_cast<c4::byte>(c); } ++wpos; }
|
||||
#define c4appendval_(c, shift)\
|
||||
{\
|
||||
C4_XASSERT((c) >= 0);\
|
||||
C4_XASSERT(size_t(c) < sizeof(detail::base64_char_to_sextet_));\
|
||||
val |= static_cast<uint32_t>(detail::base64_char_to_sextet_[(c)]) << ((shift) * 6);\
|
||||
}
|
||||
C4_ASSERT(base64_valid(encoded));
|
||||
C4_CHECK((encoded.len & 3u) == 0);
|
||||
size_t wpos = 0; // the write position
|
||||
const char *C4_RESTRICT d = encoded.str;
|
||||
constexpr const uint32_t full_byte = 0xff;
|
||||
// process every quartet of input 6 bits --> triplet of output bytes
|
||||
for(size_t rpos = 0; rpos < encoded.len; rpos += 4, d += 4)
|
||||
{
|
||||
if(d[2] == '=' || d[3] == '=') // skip the last quartet if it is padded
|
||||
{
|
||||
C4_ASSERT(d + 4 == encoded.str + encoded.len);
|
||||
break;
|
||||
}
|
||||
uint32_t val = 0;
|
||||
c4appendval_(d[3], 0);
|
||||
c4appendval_(d[2], 1);
|
||||
c4appendval_(d[1], 2);
|
||||
c4appendval_(d[0], 3);
|
||||
c4append_((val >> (2 * 8)) & full_byte);
|
||||
c4append_((val >> (1 * 8)) & full_byte);
|
||||
c4append_((val ) & full_byte);
|
||||
}
|
||||
// deal with the last quartet when it is padded
|
||||
if(d == encoded.str + encoded.len)
|
||||
return wpos;
|
||||
if(d[2] == '=') // 2 padding chars
|
||||
{
|
||||
C4_ASSERT(d + 4 == encoded.str + encoded.len);
|
||||
C4_ASSERT(d[3] == '=');
|
||||
uint32_t val = 0;
|
||||
c4appendval_(d[1], 2);
|
||||
c4appendval_(d[0], 3);
|
||||
c4append_((val >> (2 * 8)) & full_byte);
|
||||
}
|
||||
else if(d[3] == '=') // 1 padding char
|
||||
{
|
||||
C4_ASSERT(d + 4 == encoded.str + encoded.len);
|
||||
uint32_t val = 0;
|
||||
c4appendval_(d[2], 1);
|
||||
c4appendval_(d[1], 2);
|
||||
c4appendval_(d[0], 3);
|
||||
c4append_((val >> (2 * 8)) & full_byte);
|
||||
c4append_((val >> (1 * 8)) & full_byte);
|
||||
}
|
||||
return wpos;
|
||||
#undef c4append_
|
||||
#undef c4appendval_
|
||||
}
|
||||
|
||||
} // namespace c4
|
||||
|
||||
// NOLINTEND(bugprone-signed-char-misuse,cert-str34-c,hicpp-signed-bitwise)
|
||||
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
#elif defined(__GNUC__)
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
141
ext/c4core.src/c4/base64.hpp
Normal file
141
ext/c4core.src/c4/base64.hpp
Normal file
@@ -0,0 +1,141 @@
|
||||
#ifndef _C4_BASE64_HPP_
|
||||
#define _C4_BASE64_HPP_
|
||||
|
||||
/** @file base64.hpp encoding/decoding for base64.
|
||||
* @see https://en.wikipedia.org/wiki/Base64
|
||||
* @see https://www.base64encode.org/
|
||||
* */
|
||||
|
||||
#include "c4/substr.hpp"
|
||||
#include "c4/blob.hpp"
|
||||
|
||||
namespace c4 {
|
||||
|
||||
/** @defgroup doc_base64 Base64 encoding/decoding
|
||||
* @see https://en.wikipedia.org/wiki/Base64
|
||||
* @see https://www.base64encode.org/
|
||||
* @{ */
|
||||
|
||||
/** check that the given buffer is a valid base64 encoding
|
||||
* @see https://en.wikipedia.org/wiki/Base64 */
|
||||
C4CORE_EXPORT bool base64_valid(csubstr encoded);
|
||||
|
||||
|
||||
/** base64-encode binary data.
|
||||
* @param encoded [out] output buffer for encoded data
|
||||
* @param data [in] the input buffer with the binary data
|
||||
*
|
||||
* @return the number of bytes needed to return the output (ie the
|
||||
* required size for @p encoded). No writes occur beyond the end of
|
||||
* the output buffer, so it is safe to do a speculative call where the
|
||||
* encoded buffer is empty, or maybe too small. The caller should
|
||||
* ensure that the returned size is smaller than the size of the
|
||||
* encoded buffer.
|
||||
*
|
||||
* @note the result depends on endianness. If transfer between
|
||||
* little/big endian systems is desired, the caller should normalize
|
||||
* @p data before encoding.
|
||||
*
|
||||
* @see https://en.wikipedia.org/wiki/Base64 */
|
||||
C4CORE_EXPORT size_t base64_encode(substr encoded, cblob data);
|
||||
|
||||
|
||||
/** decode the base64 encoding in the given buffer
|
||||
* @param encoded [in] the encoded base64
|
||||
* @param data [out] the output buffer
|
||||
*
|
||||
* @return the number of bytes needed to return the output (ie the
|
||||
* required size for @p data). No writes occur beyond the end of the
|
||||
* output buffer, so it is safe to do a speculative call where the
|
||||
* data buffer is empty, or maybe too small. The caller should ensure
|
||||
* that the returned size is smaller than the size of the data buffer.
|
||||
*
|
||||
* @note the result depends on endianness. If transfer between
|
||||
* little/big endian systems is desired, the caller should normalize
|
||||
* @p data after decoding.
|
||||
*
|
||||
* @see https://en.wikipedia.org/wiki/Base64 */
|
||||
C4CORE_EXPORT size_t base64_decode(csubstr encoded, blob data);
|
||||
|
||||
/** @} */ // base64
|
||||
|
||||
namespace fmt {
|
||||
|
||||
/** @addtogroup doc_format_specifiers
|
||||
* @{ */
|
||||
|
||||
/** @defgroup doc_base64_fmt Base64
|
||||
* @{ */
|
||||
|
||||
template<typename CharOrConstChar>
|
||||
struct base64_wrapper_
|
||||
{
|
||||
blob_<CharOrConstChar> data;
|
||||
base64_wrapper_() : data() {}
|
||||
base64_wrapper_(blob_<CharOrConstChar> blob) : data(blob) {}
|
||||
};
|
||||
/** a tag type to mark a payload as base64-encoded */
|
||||
using const_base64_wrapper = base64_wrapper_<cbyte>;
|
||||
/** a tag type to mark a payload to be encoded as base64 */
|
||||
using base64_wrapper = base64_wrapper_<byte>;
|
||||
|
||||
|
||||
/** mark a variable to be written in base64 format */
|
||||
template<class ...Args>
|
||||
C4_ALWAYS_INLINE const_base64_wrapper cbase64(Args const& C4_RESTRICT ...args)
|
||||
{
|
||||
return const_base64_wrapper(cblob(args...));
|
||||
}
|
||||
/** mark a csubstr to be written in base64 format */
|
||||
C4_ALWAYS_INLINE const_base64_wrapper cbase64(csubstr s)
|
||||
{
|
||||
return const_base64_wrapper(cblob(s.str, s.len));
|
||||
}
|
||||
/** mark a variable to be written in base64 format */
|
||||
template<class ...Args>
|
||||
C4_ALWAYS_INLINE const_base64_wrapper base64(Args const& C4_RESTRICT ...args)
|
||||
{
|
||||
return const_base64_wrapper(cblob(args...));
|
||||
}
|
||||
/** mark a csubstr to be written in base64 format */
|
||||
C4_ALWAYS_INLINE const_base64_wrapper base64(csubstr s)
|
||||
{
|
||||
return const_base64_wrapper(cblob(s.str, s.len));
|
||||
}
|
||||
|
||||
/** mark a variable to be read in base64 format */
|
||||
template<class ...Args>
|
||||
C4_ALWAYS_INLINE base64_wrapper base64(Args &... args)
|
||||
{
|
||||
return base64_wrapper(blob(args...));
|
||||
}
|
||||
/** mark a variable to be read in base64 format */
|
||||
C4_ALWAYS_INLINE base64_wrapper base64(substr s)
|
||||
{
|
||||
return base64_wrapper(blob(s.str, s.len));
|
||||
}
|
||||
|
||||
/** @} */ // base64_fmt
|
||||
|
||||
/** @} */ // format_specifiers
|
||||
|
||||
} // namespace fmt
|
||||
|
||||
|
||||
/** write a variable in base64 format
|
||||
* @ingroup doc_to_chars */
|
||||
inline size_t to_chars(substr buf, fmt::const_base64_wrapper b)
|
||||
{
|
||||
return base64_encode(buf, b.data);
|
||||
}
|
||||
|
||||
/** read a variable in base64 format
|
||||
* @ingroup doc_from_chars */
|
||||
inline size_t from_chars(csubstr buf, fmt::base64_wrapper *b)
|
||||
{
|
||||
return base64_decode(buf, b->data);
|
||||
}
|
||||
|
||||
} // namespace c4
|
||||
|
||||
#endif /* _C4_BASE64_HPP_ */
|
||||
71
ext/c4core.src/c4/blob.hpp
Normal file
71
ext/c4core.src/c4/blob.hpp
Normal file
@@ -0,0 +1,71 @@
|
||||
#ifndef _C4_BLOB_HPP_
|
||||
#define _C4_BLOB_HPP_
|
||||
|
||||
#include "c4/error.hpp"
|
||||
#include "c4/types.hpp"
|
||||
|
||||
/** @file blob.hpp Mutable and immutable binary data blobs.
|
||||
*/
|
||||
|
||||
namespace c4 {
|
||||
|
||||
template<class T>
|
||||
struct blob_;
|
||||
|
||||
namespace detail {
|
||||
template<class T> struct is_blob_type : std::integral_constant<bool, false> {};
|
||||
template<class T> struct is_blob_type<blob_<T>> : std::integral_constant<bool, true> {};
|
||||
template<class T> struct is_blob_value_type : std::integral_constant<bool, (std::is_fundamental<T>::value || std::is_trivially_copyable<T>::value)> {};
|
||||
} // namespace
|
||||
|
||||
// NOLINTBEGIN(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)
|
||||
|
||||
template<class T>
|
||||
struct blob_
|
||||
{
|
||||
static_assert(std::is_same<T, byte>::value || std::is_same<T, cbyte>::value, "must be either byte or cbyte");
|
||||
static_assert(sizeof(T) == 1u, "must be either byte or cbyte");
|
||||
|
||||
public:
|
||||
|
||||
T * buf;
|
||||
size_t len;
|
||||
|
||||
public:
|
||||
|
||||
C4_ALWAYS_INLINE blob_() noexcept = default;
|
||||
C4_ALWAYS_INLINE blob_(blob_ const& that) noexcept = default;
|
||||
C4_ALWAYS_INLINE blob_(blob_ && that) noexcept = default;
|
||||
C4_ALWAYS_INLINE blob_& operator=(blob_ && that) noexcept = default;
|
||||
C4_ALWAYS_INLINE blob_& operator=(blob_ const& that) noexcept = default;
|
||||
|
||||
template<class U, class=typename std::enable_if<std::is_const<T>::value && std::is_same<typename std::add_const<U>::type, T>::value, U>::type> C4_ALWAYS_INLINE blob_(blob_<U> const& that) noexcept : buf(that.buf), len(that.len) {} // NOLINT
|
||||
template<class U, class=typename std::enable_if<std::is_const<T>::value && std::is_same<typename std::add_const<U>::type, T>::value, U>::type> C4_ALWAYS_INLINE blob_(blob_<U> && that) noexcept : buf(that.buf), len(that.len) {} // NOLINT
|
||||
template<class U, class=typename std::enable_if<std::is_const<T>::value && std::is_same<typename std::add_const<U>::type, T>::value, U>::type> C4_ALWAYS_INLINE blob_& operator=(blob_<U> && that) noexcept { buf = that.buf; len = that.len; } // NOLINT
|
||||
template<class U, class=typename std::enable_if<std::is_const<T>::value && std::is_same<typename std::add_const<U>::type, T>::value, U>::type> C4_ALWAYS_INLINE blob_& operator=(blob_<U> const& that) noexcept { buf = that.buf; len = that.len; } // NOLINT
|
||||
|
||||
C4_ALWAYS_INLINE blob_(void *ptr, size_t n) noexcept : buf(reinterpret_cast<T*>(ptr)), len(n) {} // NOLINT
|
||||
C4_ALWAYS_INLINE blob_(void const *ptr, size_t n) noexcept : buf(reinterpret_cast<T*>(ptr)), len(n) {} // NOLINT
|
||||
|
||||
#define _C4_REQUIRE_BLOBTYPE(ty) class=typename std::enable_if<((!detail::is_blob_type<ty>::value) && (detail::is_blob_value_type<ty>::value)), T>::type
|
||||
template<class U, _C4_REQUIRE_BLOBTYPE(U)> C4_ALWAYS_INLINE blob_(U &var) noexcept : buf(reinterpret_cast<T*>(&var)), len(sizeof(U)) {} // NOLINT
|
||||
template<class U, _C4_REQUIRE_BLOBTYPE(U)> C4_ALWAYS_INLINE blob_(U *ptr, size_t n) noexcept : buf(reinterpret_cast<T*>(ptr)), len(sizeof(U) * n) { C4_ASSERT(is_aligned(ptr)); } // NOLINT
|
||||
template<class U, _C4_REQUIRE_BLOBTYPE(U)> C4_ALWAYS_INLINE blob_& operator= (U &var) noexcept { buf = reinterpret_cast<T*>(&var); len = sizeof(U); return *this; } // NOLINT
|
||||
template<class U, size_t N, _C4_REQUIRE_BLOBTYPE(U)> C4_ALWAYS_INLINE blob_(U (&arr)[N]) noexcept : buf(reinterpret_cast<T*>(arr)), len(sizeof(U) * N) {} // NOLINT
|
||||
template<class U, size_t N, _C4_REQUIRE_BLOBTYPE(U)> C4_ALWAYS_INLINE blob_& operator= (U (&arr)[N]) noexcept { buf = reinterpret_cast<T*>(arr); len = sizeof(U) * N; return *this; } // NOLINT
|
||||
#undef _C4_REQUIRE_BLOBTYPE
|
||||
};
|
||||
|
||||
// NOLINTEND(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)
|
||||
|
||||
/** an immutable binary blob */
|
||||
using cblob = blob_<cbyte>;
|
||||
/** a mutable binary blob */
|
||||
using blob = blob_< byte>;
|
||||
|
||||
C4_MUST_BE_TRIVIAL_COPY(blob);
|
||||
C4_MUST_BE_TRIVIAL_COPY(cblob);
|
||||
|
||||
} // namespace c4
|
||||
|
||||
#endif // _C4_BLOB_HPP_
|
||||
168
ext/c4core.src/c4/c4core.natvis
Normal file
168
ext/c4core.src/c4/c4core.natvis
Normal file
@@ -0,0 +1,168 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!--
|
||||
Very good intro:
|
||||
@see https://code.msdn.microsoft.com/windowsdesktop/Writing-type-visualizers-2eae77a2
|
||||
See also:
|
||||
@see http://blogs.msdn.com/b/vcblog/archive/2013/06/28/using-visual-studio-2013-to-write-maintainable-native-visualizations-natvis.aspx?PageIndex=2
|
||||
@see http://blogs.msdn.com/b/vcblog/archive/2015/09/28/debug-visualizers-in-visual-c-2015.aspx
|
||||
@see http://stackoverflow.com/questions/36883414/limit-display-of-char-in-natvis-file-to-specific-length
|
||||
-->
|
||||
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
|
||||
<Type Name="c4::basic_substring<*>">
|
||||
<DisplayString>{str,[len]} (sz={len})</DisplayString>
|
||||
<StringView>str,[len]</StringView>
|
||||
<Expand>
|
||||
<Item Name="[size]">len</Item>
|
||||
<ArrayItems>
|
||||
<Size>len</Size>
|
||||
<ValuePointer>str</ValuePointer>
|
||||
</ArrayItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="c4::span<*>">
|
||||
<DisplayString>{m_ptr,[m_size]} (sz={m_size})</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[size]">m_size</Item>
|
||||
<ArrayItems>
|
||||
<Size>m_size</Size>
|
||||
<ValuePointer>m_ptr</ValuePointer>
|
||||
</ArrayItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="c4::spanrs<*>">
|
||||
<DisplayString>{m_ptr,[m_size]} (sz={m_size}, cap={m_capacity})</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[size]">m_size</Item>
|
||||
<Item Name="[capacity]">m_capacity</Item>
|
||||
<ArrayItems>
|
||||
<Size>m_size</Size>
|
||||
<ValuePointer>m_ptr</ValuePointer>
|
||||
</ArrayItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
<!-- display span<char>/span<const char> as a string too -->
|
||||
<Type Name="c4::span<char,*>">
|
||||
<DisplayString>{m_ptr,[m_size]} (sz={m_size})</DisplayString>
|
||||
<StringView>m_ptr,[m_size]</StringView>
|
||||
<Expand>
|
||||
<Item Name="[size]">m_size</Item>
|
||||
<ArrayItems>
|
||||
<Size>m_size</Size>
|
||||
<ValuePointer>m_ptr</ValuePointer>
|
||||
</ArrayItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="c4::span<const char,*>">
|
||||
<DisplayString>{m_ptr,[m_size]} (sz={m_size})</DisplayString>
|
||||
<StringView>m_ptr,[m_size]</StringView>
|
||||
<Expand>
|
||||
<Item Name="[size]">m_size</Item>
|
||||
<ArrayItems>
|
||||
<Size>m_size</Size>
|
||||
<ValuePointer>m_ptr</ValuePointer>
|
||||
</ArrayItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
<!-- display spanrs<char>/spanrs<const char> as a string too -->
|
||||
<Type Name="c4::spanrs<char,*>">
|
||||
<DisplayString>{m_ptr,[m_size]} (sz={m_size}, cap={m_capacity})</DisplayString>
|
||||
<StringView>m_ptr,[m_size]</StringView>
|
||||
<Expand>
|
||||
<Item Name="[size]">m_size</Item>
|
||||
<Item Name="[capacity]">m_capacity</Item>
|
||||
<ArrayItems>
|
||||
<Size>m_size</Size>
|
||||
<ValuePointer>m_ptr</ValuePointer>
|
||||
</ArrayItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="c4::spanrs<const char,*>">
|
||||
<DisplayString>{m_ptr,[m_size]} (sz={m_size}, cap={m_capacity})</DisplayString>
|
||||
<StringView>m_ptr,[m_size]</StringView>
|
||||
<Expand>
|
||||
<Item Name="[size]">m_size</Item>
|
||||
<Item Name="[capacity]">m_capacity</Item>
|
||||
<ArrayItems>
|
||||
<Size>m_size</Size>
|
||||
<ValuePointer>m_ptr</ValuePointer>
|
||||
</ArrayItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<!-- =========================================================================================== -->
|
||||
<Type Name="c4::string_impl<*,*,*,*>">
|
||||
<DisplayString>{(($T3*)this)->m_str,[(($T3*)this)->m_size]} (sz={(($T3*)this)->m_size})</DisplayString>
|
||||
<StringView>(($T3*)this)->m_str,[(($T3*)this)->m_size]</StringView>
|
||||
<Expand>
|
||||
<Synthetic Name="m_str">
|
||||
<DisplayString>{(($T3*)this)->m_str,[(($T3*)this)->m_size]}</DisplayString>
|
||||
<StringView>(($T3*)this)->m_str,[(($T3*)this)->m_size]</StringView>
|
||||
</Synthetic>
|
||||
<Synthetic Name="m_size">
|
||||
<DisplayString>{(($T3*)this)->m_size}</DisplayString>
|
||||
</Synthetic>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="c4::basic_substring<*,*>">
|
||||
<DisplayString>{m_str,[m_size]} (sz={m_size})</DisplayString>
|
||||
<StringView>m_str,[m_size]</StringView>
|
||||
<Expand>
|
||||
<Synthetic Name="[size]">
|
||||
<DisplayString>{m_size}</DisplayString>
|
||||
</Synthetic>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="c4::basic_substringrs<*,*>">
|
||||
<DisplayString>{m_str,[m_size]} (sz={m_size},cap={m_capacity})</DisplayString>
|
||||
<StringView>m_str,[m_size]</StringView>
|
||||
<Expand>
|
||||
<Synthetic Name="[size]">
|
||||
<DisplayString>{m_size}</DisplayString>
|
||||
</Synthetic>
|
||||
<Synthetic Name="[capacity]">
|
||||
<DisplayString>{m_capacity}</DisplayString>
|
||||
</Synthetic>
|
||||
<Synthetic Name="[full]">
|
||||
<DisplayString>{m_str,[m_capacity]}</DisplayString>
|
||||
<StringView>m_str,[m_capacity]</StringView>
|
||||
</Synthetic>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="c4::basic_string<*,*,*>">
|
||||
<DisplayString>{m_str,[m_size]} (sz={m_size},cap={m_capacity})</DisplayString>
|
||||
<StringView>m_str,[m_size]</StringView>
|
||||
<Expand>
|
||||
<Synthetic Name="[size]">
|
||||
<DisplayString>{m_size}</DisplayString>
|
||||
</Synthetic>
|
||||
<Synthetic Name="[full]">
|
||||
<DisplayString>{m_str,[m_capacity]}</DisplayString>
|
||||
<StringView>m_str,[m_capacity]</StringView>
|
||||
</Synthetic>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<!-- enum symbols -->
|
||||
<Type Name="c4::EnumSymbols<*>::Sym">
|
||||
<DisplayString>{value} - {name}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[value]">value</Item>
|
||||
<Item Name="[name]">name</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="c4::EnumSymbols<*>">
|
||||
<DisplayString>{m_symbols,[m_num]} (sz={m_num})</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[size]">m_num</Item>
|
||||
<ArrayItems>
|
||||
<Size>m_num</Size>
|
||||
<ValuePointer>m_symbols</ValuePointer>
|
||||
</ArrayItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
</AutoVisualizer>
|
||||
2669
ext/c4core.src/c4/charconv.hpp
Normal file
2669
ext/c4core.src/c4/charconv.hpp
Normal file
File diff suppressed because it is too large
Load Diff
120
ext/c4core.src/c4/compiler.hpp
Normal file
120
ext/c4core.src/c4/compiler.hpp
Normal file
@@ -0,0 +1,120 @@
|
||||
#ifndef _C4_COMPILER_HPP_
|
||||
#define _C4_COMPILER_HPP_
|
||||
|
||||
/** @file compiler.hpp Provides compiler information macros
|
||||
* @ingroup basic_headers */
|
||||
|
||||
#include "c4/platform.hpp"
|
||||
|
||||
// Compilers:
|
||||
// C4_MSVC
|
||||
// Visual Studio 2022: MSVC++ 17, 1930
|
||||
// Visual Studio 2019: MSVC++ 16, 1920
|
||||
// Visual Studio 2017: MSVC++ 15
|
||||
// Visual Studio 2015: MSVC++ 14
|
||||
// Visual Studio 2013: MSVC++ 13
|
||||
// Visual Studio 2013: MSVC++ 12
|
||||
// Visual Studio 2012: MSVC++ 11
|
||||
// Visual Studio 2010: MSVC++ 10
|
||||
// Visual Studio 2008: MSVC++ 09
|
||||
// Visual Studio 2005: MSVC++ 08
|
||||
// C4_CLANG
|
||||
// C4_GCC
|
||||
// C4_ICC (intel compiler)
|
||||
/** @see http://sourceforge.net/p/predef/wiki/Compilers/ for a list of compiler identifier macros */
|
||||
/** @see https://msdn.microsoft.com/en-us/library/b0084kay.aspx for VS2013 predefined macros */
|
||||
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
# define C4_MSVC
|
||||
# define C4_MSVC_VERSION_2022 17
|
||||
# define C4_MSVC_VERSION_2019 16
|
||||
# define C4_MSVC_VERSION_2017 15
|
||||
# define C4_MSVC_VERSION_2015 14
|
||||
# define C4_MSVC_VERSION_2013 12
|
||||
# define C4_MSVC_VERSION_2012 11
|
||||
# if _MSC_VER >= 1930
|
||||
# define C4_MSVC_VERSION C4_MSVC_VERSION_2022 // visual studio 2022
|
||||
# define C4_MSVC_2022
|
||||
# elif _MSC_VER >= 1920
|
||||
# define C4_MSVC_VERSION C4_MSVC_VERSION_2019 // visual studio 2019
|
||||
# define C4_MSVC_2019
|
||||
# elif _MSC_VER >= 1910
|
||||
# define C4_MSVC_VERSION C4_MSVC_VERSION_2017 // visual studio 2017
|
||||
# define C4_MSVC_2017
|
||||
# elif _MSC_VER == 1900
|
||||
# define C4_MSVC_VERSION C4_MSVC_VERSION_2015 // visual studio 2015
|
||||
# define C4_MSVC_2015
|
||||
# elif _MSC_VER == 1800
|
||||
# error "MSVC version not supported"
|
||||
# define C4_MSVC_VERSION C4_MSVC_VERSION_2013 // visual studio 2013
|
||||
# define C4_MSVC_2013
|
||||
# elif _MSC_VER == 1700
|
||||
# error "MSVC version not supported"
|
||||
# define C4_MSVC_VERSION C4_MSVC_VERSION_2012 // visual studio 2012
|
||||
# define C4_MSVC_2012
|
||||
# elif _MSC_VER == 1600
|
||||
# error "MSVC version not supported"
|
||||
# define C4_MSVC_VERSION 10 // visual studio 2010
|
||||
# define C4_MSVC_2010
|
||||
# elif _MSC_VER == 1500
|
||||
# error "MSVC version not supported"
|
||||
# define C4_MSVC_VERSION 09 // visual studio 2008
|
||||
# define C4_MSVC_2008
|
||||
# elif _MSC_VER == 1400
|
||||
# error "MSVC version not supported"
|
||||
# define C4_MSVC_VERSION 08 // visual studio 2005
|
||||
# define C4_MSVC_2005
|
||||
# else
|
||||
# error "MSVC version not supported"
|
||||
# endif // _MSC_VER
|
||||
#else
|
||||
# define C4_MSVC_VERSION 0 // visual studio not present
|
||||
# define C4_GCC_LIKE
|
||||
# ifdef __INTEL_COMPILER // check ICC before checking GCC, as ICC defines __GNUC__ too
|
||||
# define C4_ICC
|
||||
# define C4_ICC_VERSION __INTEL_COMPILER
|
||||
# elif defined(__APPLE_CC__)
|
||||
# define C4_XCODE
|
||||
# if defined(__clang__)
|
||||
# define C4_CLANG
|
||||
# ifndef __apple_build_version__
|
||||
# define C4_CLANG_VERSION C4_VERSION_ENCODED(__clang_major__, __clang_minor__, __clang_patchlevel__)
|
||||
# else
|
||||
# define C4_CLANG_VERSION __apple_build_version__
|
||||
# endif
|
||||
# else
|
||||
# define C4_XCODE_VERSION __APPLE_CC__
|
||||
# endif
|
||||
# elif defined(__clang__)
|
||||
# define C4_CLANG
|
||||
# ifndef __apple_build_version__
|
||||
# define C4_CLANG_VERSION C4_VERSION_ENCODED(__clang_major__, __clang_minor__, __clang_patchlevel__)
|
||||
# else
|
||||
# define C4_CLANG_VERSION __apple_build_version__
|
||||
# endif
|
||||
# elif defined(__GNUC__)
|
||||
# ifdef __MINGW32__
|
||||
# define C4_MINGW
|
||||
# endif
|
||||
# define C4_GCC
|
||||
# if defined(__GNUC_PATCHLEVEL__)
|
||||
# define C4_GCC_VERSION C4_VERSION_ENCODED(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
|
||||
# else
|
||||
# define C4_GCC_VERSION C4_VERSION_ENCODED(__GNUC__, __GNUC_MINOR__, 0)
|
||||
# endif
|
||||
# if __GNUC__ < 5
|
||||
# if __GNUC__ == 4 && __GNUC_MINOR__ >= 8
|
||||
// provided by cmake sub-project
|
||||
# include "c4/gcc-4.8.hpp"
|
||||
# else
|
||||
// we do not support GCC < 4.8:
|
||||
// * misses std::is_trivially_copyable
|
||||
// * misses std::align
|
||||
// * -Wshadow has false positives when a local function parameter has the same name as a method
|
||||
# error "GCC < 4.8 is not supported"
|
||||
# endif
|
||||
# endif
|
||||
# endif
|
||||
#endif // defined(C4_WIN) && defined(_MSC_VER)
|
||||
|
||||
#endif /* _C4_COMPILER_HPP_ */
|
||||
38
ext/c4core.src/c4/config.hpp
Normal file
38
ext/c4core.src/c4/config.hpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#ifndef _C4_CONFIG_HPP_
|
||||
#define _C4_CONFIG_HPP_
|
||||
|
||||
/** @defgroup basic_headers Basic headers
|
||||
* @brief Headers providing basic macros, platform+cpu+compiler information,
|
||||
* C++ facilities and basic typedefs. */
|
||||
|
||||
/** @file config.hpp Contains configuration defines and includes the basic_headers.
|
||||
* @ingroup basic_headers */
|
||||
|
||||
//#define C4_DEBUG
|
||||
|
||||
#define C4_ERROR_SHOWS_FILELINE
|
||||
//#define C4_ERROR_SHOWS_FUNC
|
||||
//#define C4_ERROR_THROWS_EXCEPTION
|
||||
//#define C4_NO_ALLOC_DEFAULTS
|
||||
//#define C4_REDEFINE_CPPNEW
|
||||
|
||||
#ifndef C4_SIZE_TYPE
|
||||
# define C4_SIZE_TYPE size_t
|
||||
#endif
|
||||
|
||||
#ifndef C4_STR_SIZE_TYPE
|
||||
# define C4_STR_SIZE_TYPE C4_SIZE_TYPE
|
||||
#endif
|
||||
|
||||
#ifndef C4_TIME_TYPE
|
||||
# define C4_TIME_TYPE double
|
||||
#endif
|
||||
|
||||
#include "c4/export.hpp"
|
||||
#include "c4/preprocessor.hpp"
|
||||
#include "c4/platform.hpp"
|
||||
#include "c4/cpu.hpp"
|
||||
#include "c4/compiler.hpp"
|
||||
#include "c4/language.hpp"
|
||||
|
||||
#endif // _C4_CONFIG_HPP_
|
||||
205
ext/c4core.src/c4/cpu.hpp
Normal file
205
ext/c4core.src/c4/cpu.hpp
Normal file
@@ -0,0 +1,205 @@
|
||||
#ifndef _C4_CPU_HPP_
|
||||
#define _C4_CPU_HPP_
|
||||
|
||||
/** @file cpu.hpp Provides processor information macros
|
||||
* @ingroup basic_headers */
|
||||
|
||||
// see also https://sourceforge.net/p/predef/wiki/Architectures/
|
||||
// see also https://sourceforge.net/p/predef/wiki/Endianness/
|
||||
// see also https://github.com/googlesamples/android-ndk/blob/android-mk/hello-jni/jni/hello-jni.c
|
||||
// see also http://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/global/qprocessordetection.h
|
||||
|
||||
#ifdef __ORDER_LITTLE_ENDIAN__
|
||||
# define _C4EL __ORDER_LITTLE_ENDIAN__
|
||||
#else
|
||||
# define _C4EL 1234
|
||||
#endif
|
||||
|
||||
#ifdef __ORDER_BIG_ENDIAN__
|
||||
# define _C4EB __ORDER_BIG_ENDIAN__
|
||||
#else
|
||||
# define _C4EB 4321
|
||||
#endif
|
||||
|
||||
// mixed byte order (eg, PowerPC or ia64)
|
||||
#define _C4EM 1111 // NOLINT
|
||||
|
||||
|
||||
// NOTE: to find defined macros in a platform,
|
||||
// g++ <flags> -dM -E - </dev/null | sort
|
||||
|
||||
#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(_M_X64)
|
||||
# define C4_CPU_X86_64
|
||||
# define C4_WORDSIZE 8
|
||||
# define C4_BYTE_ORDER _C4EL
|
||||
|
||||
#elif defined(__i386) || defined(__i386__) || defined(_M_IX86)
|
||||
# define C4_CPU_X86
|
||||
# define C4_WORDSIZE 4
|
||||
# define C4_BYTE_ORDER _C4EL
|
||||
|
||||
#elif defined(__arm__) || defined(_M_ARM) \
|
||||
|| defined(__TARGET_ARCH_ARM) || defined(__aarch64__) || defined(_M_ARM64)
|
||||
# if defined(__aarch64__) || defined(_M_ARM64)
|
||||
# define C4_CPU_ARM64
|
||||
# define C4_CPU_ARMV8
|
||||
# define C4_WORDSIZE 8
|
||||
# else
|
||||
# define C4_CPU_ARM
|
||||
# define C4_WORDSIZE 4
|
||||
# if defined(__ARM_ARCH_8__) || defined(__ARM_ARCH_8A__) \
|
||||
|| (defined(__ARCH_ARM) && __ARCH_ARM >= 8) \
|
||||
|| (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 8)
|
||||
# define C4_CPU_ARMV8
|
||||
# elif defined(__ARM_ARCH_7__) || defined(_ARM_ARCH_7) \
|
||||
|| defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) \
|
||||
|| defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__) \
|
||||
|| defined(__ARM_ARCH_7EM__) \
|
||||
|| (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 7) \
|
||||
|| (defined(_M_ARM) && _M_ARM >= 7)
|
||||
# define C4_CPU_ARMV7
|
||||
# elif defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) \
|
||||
|| defined(__ARM_ARCH_6T2__) || defined(__ARM_ARCH_6Z__) \
|
||||
|| defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6ZK__) \
|
||||
|| defined(__ARM_ARCH_6M__) || defined(__ARM_ARCH_6KZ__) \
|
||||
|| (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 6)
|
||||
# define C4_CPU_ARMV6
|
||||
# elif (defined(__ARM_ARCH) && __ARM_ARCH == 5) \
|
||||
|| defined(__ARM_ARCH_5TEJ__) \
|
||||
|| defined(__ARM_ARCH_5TE__) \
|
||||
|| defined(__ARM_ARCH_5T__) \
|
||||
|| (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 5)
|
||||
# define C4_CPU_ARMV5
|
||||
# elif (defined(__ARM_ARCH) && __ARM_ARCH == 4) \
|
||||
|| defined(__ARM_ARCH_4T__) \
|
||||
|| defined(__ARM_ARCH_4__) \
|
||||
|| (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 4)
|
||||
# define C4_CPU_ARMV4
|
||||
# else
|
||||
# error "unknown CPU architecture: ARM"
|
||||
# endif
|
||||
# endif
|
||||
# if defined(__ARMEL__) || defined(__LITTLE_ENDIAN__) || defined(__AARCH64EL__) \
|
||||
|| (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) \
|
||||
|| defined(_MSC_VER) // winarm64 does not provide any of the above macros,
|
||||
// but advises little-endianess:
|
||||
// https://docs.microsoft.com/en-us/cpp/build/overview-of-arm-abi-conventions?view=msvc-170
|
||||
// So if it is visual studio compiling, we'll assume little endian.
|
||||
# define C4_BYTE_ORDER _C4EL
|
||||
# elif defined(__ARMEB__) || defined(__BIG_ENDIAN__) || defined(__AARCH64EB__) \
|
||||
|| (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
|
||||
# define C4_BYTE_ORDER _C4EB
|
||||
# elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_PDP_ENDIAN__)
|
||||
# define C4_BYTE_ORDER _C4EM
|
||||
# else
|
||||
# error "unknown endianness"
|
||||
# endif
|
||||
|
||||
#elif defined(__ia64) || defined(__ia64__) || defined(_M_IA64)
|
||||
# define C4_CPU_IA64
|
||||
# define C4_WORDSIZE 8
|
||||
# define C4_BYTE_ORDER _C4EM
|
||||
// itanium is bi-endian - check byte order below
|
||||
|
||||
#elif defined(__ppc__) || defined(__ppc) || defined(__powerpc__) \
|
||||
|| defined(_ARCH_COM) || defined(_ARCH_PWR) || defined(_ARCH_PPC) \
|
||||
|| defined(_M_MPPC) || defined(_M_PPC)
|
||||
# if defined(__ppc64__) || defined(__powerpc64__) || defined(__64BIT__)
|
||||
# define C4_CPU_PPC64
|
||||
# define C4_WORDSIZE 8
|
||||
# else
|
||||
# define C4_CPU_PPC
|
||||
# define C4_WORDSIZE 4
|
||||
# endif
|
||||
# define C4_BYTE_ORDER _C4EM
|
||||
// ppc is bi-endian - check byte order below
|
||||
|
||||
#elif defined(__s390x__) || defined(__zarch__) || defined(__SYSC_ZARCH_)
|
||||
# define C4_CPU_S390_X
|
||||
# define C4_WORDSIZE 8
|
||||
# define C4_BYTE_ORDER _C4EB
|
||||
|
||||
#elif defined(__xtensa__) || defined(__XTENSA__)
|
||||
# define C4_CPU_XTENSA
|
||||
# define C4_WORDSIZE 4
|
||||
// not sure about this...
|
||||
# if defined(__XTENSA_EL__) || defined(__xtensa_el__)
|
||||
# define C4_BYTE_ORDER _C4EL
|
||||
# else
|
||||
# define C4_BYTE_ORDER _C4EB
|
||||
# endif
|
||||
|
||||
#elif defined(__riscv)
|
||||
# if __riscv_xlen == 64
|
||||
# define C4_CPU_RISCV64
|
||||
# define C4_WORDSIZE 8
|
||||
# else
|
||||
# define C4_CPU_RISCV32
|
||||
# define C4_WORDSIZE 4
|
||||
# endif
|
||||
# define C4_BYTE_ORDER _C4EL
|
||||
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
# define C4_BYTE_ORDER _C4EL
|
||||
# define C4_WORDSIZE 4
|
||||
|
||||
#elif defined(__loongarch__)
|
||||
# if defined(__loongarch64)
|
||||
# define C4_CPU_LOONGARCH64
|
||||
# define C4_WORDSIZE 8
|
||||
# else
|
||||
# define C4_CPU_LOONGARCH
|
||||
# define C4_WORDSIZE 4
|
||||
# endif
|
||||
# define C4_BYTE_ORDER _C4EL
|
||||
|
||||
#elif defined(__mips__) || defined(_mips) || defined(mips)
|
||||
# if defined(__mips)
|
||||
# if __mips == 64
|
||||
# define C4_CPU_MIPS64
|
||||
# define C4_WORDSIZE 8
|
||||
# elif __mips == 32
|
||||
# define C4_CPU_MIPS32
|
||||
# define C4_WORDSIZE 4
|
||||
# endif
|
||||
# elif defined(__arch64__) || (defined(__SIZE_WIDTH__) && __SIZE_WIDTH__ == 64) || (defined(__LP64__) && __LP64__)
|
||||
# define C4_CPU_MIPS64
|
||||
# define C4_WORDSIZE 8
|
||||
# elif defined(__arch32__) || (defined(__SIZE_WIDTH__) && __SIZE_WIDTH__ == 32) || (defined(__LP32__) && __LP32__)
|
||||
# define C4_CPU_MIPS32
|
||||
# define C4_WORDSIZE 4
|
||||
# else
|
||||
# error "unknown mips architecture"
|
||||
# endif
|
||||
# if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
# define C4_BYTE_ORDER _C4EB
|
||||
# elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
# define C4_BYTE_ORDER _C4EL
|
||||
# else
|
||||
# error "unknown mips endianness"
|
||||
# endif
|
||||
|
||||
#elif defined(__sparc__) || defined(__sparc) || defined(sparc)
|
||||
# if defined(__arch64__) || (defined(__SIZE_WIDTH__) && __SIZE_WIDTH__ == 64) || (defined(__LP64__) && __LP64__)
|
||||
# define C4_CPU_SPARC64
|
||||
# define C4_WORDSIZE 8
|
||||
# elif defined(__arch32__) || (defined(__SIZE_WIDTH__) && __SIZE_WIDTH__ == 32) || (defined(__LP32__) && __LP32__)
|
||||
# define C4_CPU_SPARC32
|
||||
# define C4_WORDSIZE 4
|
||||
# else
|
||||
# error "unknown sparc architecture"
|
||||
# endif
|
||||
# define C4_BYTE_ORDER _C4EB
|
||||
|
||||
#elif defined(SWIG)
|
||||
# error "please define CPU architecture macros when compiling with swig"
|
||||
|
||||
#else
|
||||
# error "unknown CPU architecture"
|
||||
#endif
|
||||
|
||||
#define C4_LITTLE_ENDIAN (C4_BYTE_ORDER == _C4EL)
|
||||
#define C4_BIG_ENDIAN (C4_BYTE_ORDER == _C4EB)
|
||||
#define C4_MIXED_ENDIAN (C4_BYTE_ORDER == _C4EM)
|
||||
|
||||
#endif /* _C4_CPU_HPP_ */
|
||||
237
ext/c4core.src/c4/error.cpp
Normal file
237
ext/c4core.src/c4/error.cpp
Normal file
@@ -0,0 +1,237 @@
|
||||
#include "c4/error.hpp"
|
||||
#include "c4/language.hpp"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#define C4_LOGF_ERR(...) (void)fprintf(stderr, __VA_ARGS__); (void)fflush(stderr)
|
||||
#define C4_LOGF_WARN(...) (void)fprintf(stderr, __VA_ARGS__); (void)fflush(stderr)
|
||||
#define C4_LOGP(msg, ...) (void)printf(msg)
|
||||
|
||||
#if defined(C4_XBOX) || (defined(C4_WIN) && defined(C4_MSVC))
|
||||
# include "c4/windows.hpp"
|
||||
#elif defined(C4_PS4)
|
||||
# include <libdbg.h>
|
||||
#elif defined(C4_UNIX) || defined(C4_LINUX)
|
||||
# include <sys/stat.h>
|
||||
# include <cstring>
|
||||
# include <fcntl.h>
|
||||
#elif defined(C4_MACOS) || defined(C4_IOS)
|
||||
# include <assert.h>
|
||||
# include <stdbool.h>
|
||||
# include <sys/types.h>
|
||||
# include <sys/sysctl.h>
|
||||
#endif
|
||||
// the amalgamation tool is dumb and was omitting this include under MACOS.
|
||||
// So do it only once:
|
||||
#if defined(C4_UNIX) || defined(C4_LINUX) || defined(C4_MACOS) || defined(C4_IOS)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if defined(C4_EXCEPTIONS_ENABLED) && defined(C4_ERROR_THROWS_EXCEPTION)
|
||||
# include <exception>
|
||||
#endif
|
||||
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wformat-nonliteral"
|
||||
# pragma clang diagnostic ignored "-Wold-style-cast"
|
||||
#elif defined(__GNUC__)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wformat-nonliteral"
|
||||
# pragma GCC diagnostic ignored "-Wold-style-cast"
|
||||
#endif
|
||||
// NOLINTBEGIN(*use-anonymous-namespace*,cert-dcl50-cpp)
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
namespace c4 {
|
||||
|
||||
static error_flags s_error_flags = ON_ERROR_DEFAULTS;
|
||||
static error_callback_type s_error_callback = nullptr;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
error_flags get_error_flags()
|
||||
{
|
||||
return s_error_flags;
|
||||
}
|
||||
void set_error_flags(error_flags flags)
|
||||
{
|
||||
s_error_flags = flags;
|
||||
}
|
||||
|
||||
error_callback_type get_error_callback()
|
||||
{
|
||||
return s_error_callback;
|
||||
}
|
||||
/** Set the function which is called when an error occurs. */
|
||||
void set_error_callback(error_callback_type cb)
|
||||
{
|
||||
s_error_callback = cb;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void handle_error(srcloc where, const char *fmt, ...)
|
||||
{
|
||||
char buf[1024];
|
||||
size_t msglen = 0;
|
||||
if(s_error_flags & (ON_ERROR_LOG|ON_ERROR_CALLBACK))
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int ilen = vsnprintf(buf, sizeof(buf), fmt, args); // NOLINT(clang-analyzer-valist.Uninitialized)
|
||||
va_end(args);
|
||||
msglen = ilen >= 0 && ilen < (int)sizeof(buf) ? static_cast<size_t>(ilen) : sizeof(buf)-1;
|
||||
}
|
||||
|
||||
if(s_error_flags & ON_ERROR_LOG)
|
||||
{
|
||||
C4_LOGF_ERR("\n");
|
||||
#if defined(C4_ERROR_SHOWS_FILELINE) && defined(C4_ERROR_SHOWS_FUNC)
|
||||
C4_LOGF_ERR("%s:%d: ERROR: %s\n", where.file, where.line, buf);
|
||||
C4_LOGF_ERR("%s:%d: ERROR here: %s\n", where.file, where.line, where.func);
|
||||
#elif defined(C4_ERROR_SHOWS_FILELINE)
|
||||
C4_LOGF_ERR("%s:%d: ERROR: %s\n", where.file, where.line, buf);
|
||||
#elif ! defined(C4_ERROR_SHOWS_FUNC)
|
||||
(void)where;
|
||||
C4_LOGF_ERR("ERROR: %s\n", buf);
|
||||
#endif
|
||||
}
|
||||
|
||||
if(s_error_flags & ON_ERROR_CALLBACK)
|
||||
{
|
||||
if(s_error_callback)
|
||||
{
|
||||
s_error_callback(buf, msglen);
|
||||
}
|
||||
}
|
||||
|
||||
if(s_error_flags & ON_ERROR_THROW)
|
||||
{
|
||||
#if defined(C4_EXCEPTIONS_ENABLED) && defined(C4_ERROR_THROWS_EXCEPTION)
|
||||
throw std::runtime_error(buf);
|
||||
#endif
|
||||
}
|
||||
|
||||
if(s_error_flags & ON_ERROR_ABORT)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
abort(); // abort anyway, in case nothing was set
|
||||
C4_UNREACHABLE_AFTER_ERR();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void handle_warning(srcloc where, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
char buf[1024];
|
||||
va_start(args, fmt);
|
||||
int ret = vsnprintf(buf, sizeof(buf), fmt, args); // NOLINT(clang-analyzer-valist.Uninitialized)
|
||||
if(ret+1 > (int)sizeof(buf))
|
||||
buf[sizeof(buf) - 1] = '\0'; // truncate
|
||||
else if(ret < 0)
|
||||
buf[0] = '\0'; // output/format error
|
||||
va_end(args);
|
||||
C4_LOGF_WARN("\n");
|
||||
#if defined(C4_ERROR_SHOWS_FILELINE) && defined(C4_ERROR_SHOWS_FUNC)
|
||||
C4_LOGF_WARN("%s:%d: WARNING: %s\n", where.file, where.line, buf);
|
||||
C4_LOGF_WARN("%s:%d: WARNING: here: %s\n", where.file, where.line, where.func);
|
||||
#elif defined(C4_ERROR_SHOWS_FILELINE)
|
||||
C4_LOGF_WARN("%s:%d: WARNING: %s\n", where.file, where.line, buf);
|
||||
#elif ! defined(C4_ERROR_SHOWS_FUNC)
|
||||
(void)where;
|
||||
C4_LOGF_WARN("WARNING: %s\n", buf);
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool is_debugger_attached()
|
||||
{
|
||||
#if defined(C4_UNIX) || defined(C4_LINUX)
|
||||
static bool first_call = true;
|
||||
static bool first_call_result = false;
|
||||
if(first_call)
|
||||
{
|
||||
first_call = false;
|
||||
C4_SUPPRESS_WARNING_GCC_PUSH
|
||||
#if defined(__GNUC__) && __GNUC__ > 9
|
||||
C4_SUPPRESS_WARNING_GCC("-Wanalyzer-fd-leak")
|
||||
#endif
|
||||
//! @see http://stackoverflow.com/questions/3596781/how-to-detect-if-the-current-process-is-being-run-by-gdb
|
||||
//! (this answer: http://stackoverflow.com/a/24969863/3968589 )
|
||||
char buf[1024] = "";
|
||||
int status_fd = open("/proc/self/status", O_RDONLY); // NOLINT
|
||||
if (status_fd == -1)
|
||||
return false;
|
||||
ssize_t num_read = ::read(status_fd, buf, sizeof(buf));
|
||||
if (num_read > 0)
|
||||
{
|
||||
static const char TracerPid[] = "TracerPid:";
|
||||
char *tracer_pid;
|
||||
if(num_read < 1024)
|
||||
buf[num_read] = 0;
|
||||
tracer_pid = strstr(buf, TracerPid);
|
||||
if(tracer_pid)
|
||||
first_call_result = !!::atoi(tracer_pid + sizeof(TracerPid) - 1); // NOLINT
|
||||
}
|
||||
close(status_fd);
|
||||
C4_SUPPRESS_WARNING_GCC_POP
|
||||
}
|
||||
return first_call_result;
|
||||
#elif defined(C4_PS4)
|
||||
return (sceDbgIsDebuggerAttached() != 0);
|
||||
#elif defined(C4_XBOX) || (defined(C4_WIN) && defined(C4_MSVC))
|
||||
return IsDebuggerPresent() != 0;
|
||||
#elif defined(C4_MACOS) || defined(C4_IOS)
|
||||
// https://stackoverflow.com/questions/2200277/detecting-debugger-on-mac-os-x
|
||||
// Returns true if the current process is being debugged (either
|
||||
// running under the debugger or has a debugger attached post facto).
|
||||
int junk;
|
||||
int mib[4];
|
||||
struct kinfo_proc info;
|
||||
size_t size;
|
||||
|
||||
// Initialize the flags so that, if sysctl fails for some bizarre
|
||||
// reason, we get a predictable result.
|
||||
|
||||
info.kp_proc.p_flag = 0;
|
||||
|
||||
// Initialize mib, which tells sysctl the info we want, in this case
|
||||
// we're looking for information about a specific process ID.
|
||||
|
||||
mib[0] = CTL_KERN;
|
||||
mib[1] = KERN_PROC;
|
||||
mib[2] = KERN_PROC_PID;
|
||||
mib[3] = getpid();
|
||||
|
||||
// Call sysctl.
|
||||
|
||||
size = sizeof(info);
|
||||
junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
|
||||
assert(junk == 0);
|
||||
(void)junk;
|
||||
|
||||
// We're being debugged if the P_TRACED flag is set.
|
||||
return ((info.kp_proc.p_flag & P_TRACED) != 0);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
} // is_debugger_attached()
|
||||
|
||||
} // namespace c4
|
||||
|
||||
// NOLINTEND(*use-anonymous-namespace*,cert-dcl50-cpp)
|
||||
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
#elif defined(__GNUC__)
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
444
ext/c4core.src/c4/error.hpp
Normal file
444
ext/c4core.src/c4/error.hpp
Normal file
@@ -0,0 +1,444 @@
|
||||
#ifndef _C4_ERROR_HPP_
|
||||
#define _C4_ERROR_HPP_
|
||||
|
||||
/** @file error.hpp Facilities for error reporting and runtime assertions. */
|
||||
|
||||
/** @defgroup error_checking Error checking */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "c4/export.hpp"
|
||||
#include "c4/compiler.hpp"
|
||||
|
||||
#ifdef _DOXYGEN_
|
||||
/** if this is defined and exceptions are enabled, then calls to C4_ERROR()
|
||||
* will throw an exception
|
||||
* @ingroup error_checking */
|
||||
# define C4_EXCEPTIONS_ENABLED
|
||||
/** if this is defined and exceptions are enabled, then calls to C4_ERROR()
|
||||
* will throw an exception
|
||||
* @see C4_EXCEPTIONS_ENABLED
|
||||
* @ingroup error_checking */
|
||||
# define C4_ERROR_THROWS_EXCEPTION
|
||||
/** evaluates to noexcept when C4_ERROR might be called and
|
||||
* exceptions are disabled. Otherwise, defaults to nothing.
|
||||
* @ingroup error_checking */
|
||||
# define C4_NOEXCEPT
|
||||
#endif // _DOXYGEN_
|
||||
|
||||
#if defined(C4_EXCEPTIONS_ENABLED) && defined(C4_ERROR_THROWS_EXCEPTION)
|
||||
# define C4_NOEXCEPT
|
||||
#else
|
||||
# define C4_NOEXCEPT noexcept
|
||||
#endif
|
||||
|
||||
|
||||
namespace c4 {
|
||||
namespace detail {
|
||||
struct fail_type__ {};
|
||||
} // detail
|
||||
} // c4
|
||||
#define C4_STATIC_ERROR(dummy_type, errmsg) \
|
||||
static_assert(std::is_same<dummy_type, c4::detail::fail_type__>::value, errmsg)
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#define C4_ASSERT_SAME_TYPE(ty1, ty2) \
|
||||
C4_STATIC_ASSERT(std::is_same<ty1 C4_COMMA_X ty2>::value)
|
||||
|
||||
#define C4_ASSERT_DIFF_TYPE(ty1, ty2) \
|
||||
C4_STATIC_ASSERT( ! std::is_same<ty1 C4_COMMA_X ty2>::value)
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifdef _DOXYGEN_
|
||||
/** utility macro that triggers a breakpoint when
|
||||
* the debugger is attached and NDEBUG is not defined.
|
||||
* @ingroup error_checking */
|
||||
# define C4_DEBUG_BREAK()
|
||||
#endif // _DOXYGEN_
|
||||
|
||||
|
||||
#if defined(NDEBUG) || defined(C4_NO_DEBUG_BREAK)
|
||||
# define C4_DEBUG_BREAK()
|
||||
#else
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wundef"
|
||||
# if !defined(__APPLE_CC__)
|
||||
# if __clang_major__ >= 10
|
||||
# pragma clang diagnostic ignored "-Wgnu-inline-cpp-without-extern" // debugbreak/debugbreak.h:50:16: error: 'gnu_inline' attribute without 'extern' in C++ treated as externally available, this changed in Clang 10 [-Werror,-Wgnu-inline-cpp-without-extern]
|
||||
# endif
|
||||
# else
|
||||
# if __clang_major__ >= 13
|
||||
# pragma clang diagnostic ignored "-Wgnu-inline-cpp-without-extern" // debugbreak/debugbreak.h:50:16: error: 'gnu_inline' attribute without 'extern' in C++ treated as externally available, this changed in Clang 10 [-Werror,-Wgnu-inline-cpp-without-extern]
|
||||
# endif
|
||||
# endif
|
||||
# elif defined(__GNUC__)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wundef"
|
||||
# endif
|
||||
# include <c4/ext/debugbreak/debugbreak.h>
|
||||
# define C4_DEBUG_BREAK() if(c4::is_debugger_attached()) { ::debug_break(); }
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# elif defined(__GNUC__)
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
namespace c4 {
|
||||
C4CORE_EXPORT bool is_debugger_attached();
|
||||
} // namespace c4
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifdef __clang__
|
||||
/* NOTE: using , ## __VA_ARGS__ to deal with zero-args calls to
|
||||
* variadic macros is not portable, but works in clang, gcc, msvc, icc.
|
||||
* clang requires switching off compiler warnings for pedantic mode.
|
||||
* @see http://stackoverflow.com/questions/32047685/variadic-macro-without-arguments */
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" // warning: token pasting of ',' and __VA_ARGS__ is a GNU extension
|
||||
#elif defined(__GNUC__)
|
||||
/* GCC also issues a warning for zero-args calls to variadic macros.
|
||||
* This warning is switched on with -pedantic and apparently there is no
|
||||
* easy way to turn it off as with clang. But marking this as a system
|
||||
* header works.
|
||||
* @see https://gcc.gnu.org/onlinedocs/cpp/System-Headers.html
|
||||
* @see http://stackoverflow.com/questions/35587137/ */
|
||||
# pragma GCC system_header
|
||||
#endif
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
namespace c4 {
|
||||
|
||||
typedef enum : uint32_t {
|
||||
/** when an error happens and the debugger is attached, call C4_DEBUG_BREAK().
|
||||
* Without effect otherwise. */
|
||||
ON_ERROR_DEBUGBREAK = 0x01u << 0u,
|
||||
/** when an error happens log a message. */
|
||||
ON_ERROR_LOG = 0x01u << 1u,
|
||||
/** when an error happens invoke a callback if it was set with
|
||||
* set_error_callback(). */
|
||||
ON_ERROR_CALLBACK = 0x01u << 2u,
|
||||
/** when an error happens call std::terminate(). */
|
||||
ON_ERROR_ABORT = 0x01u << 3u,
|
||||
/** when an error happens and exceptions are enabled throw an exception.
|
||||
* Without effect otherwise. */
|
||||
ON_ERROR_THROW = 0x01u << 4u,
|
||||
/** the default flags. */
|
||||
ON_ERROR_DEFAULTS = ON_ERROR_DEBUGBREAK|ON_ERROR_LOG|ON_ERROR_CALLBACK|ON_ERROR_ABORT
|
||||
} ErrorFlags_e;
|
||||
using error_flags = uint32_t;
|
||||
C4CORE_EXPORT void set_error_flags(error_flags f);
|
||||
C4CORE_EXPORT error_flags get_error_flags();
|
||||
|
||||
|
||||
using error_callback_type = void (*)(const char* msg, size_t msg_size);
|
||||
C4CORE_EXPORT void set_error_callback(error_callback_type cb);
|
||||
C4CORE_EXPORT error_callback_type get_error_callback();
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** RAII class controling the error settings inside a scope. */
|
||||
struct ScopedErrorSettings // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)
|
||||
{
|
||||
error_flags m_flags;
|
||||
error_callback_type m_callback;
|
||||
|
||||
explicit ScopedErrorSettings(error_callback_type cb)
|
||||
: m_flags(get_error_flags()),
|
||||
m_callback(get_error_callback())
|
||||
{
|
||||
set_error_callback(cb);
|
||||
}
|
||||
explicit ScopedErrorSettings(error_flags flags)
|
||||
: m_flags(get_error_flags()),
|
||||
m_callback(get_error_callback())
|
||||
{
|
||||
set_error_flags(flags);
|
||||
}
|
||||
explicit ScopedErrorSettings(error_flags flags, error_callback_type cb)
|
||||
: m_flags(get_error_flags()),
|
||||
m_callback(get_error_callback())
|
||||
{
|
||||
set_error_flags(flags);
|
||||
set_error_callback(cb);
|
||||
}
|
||||
~ScopedErrorSettings()
|
||||
{
|
||||
set_error_flags(m_flags);
|
||||
set_error_callback(m_callback);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/** source location */
|
||||
struct srcloc;
|
||||
|
||||
// watchout: for VS the [[noreturn]] needs to come before other annotations like C4CORE_EXPORT
|
||||
[[noreturn]] C4CORE_EXPORT void handle_error(srcloc s, const char *fmt, ...);
|
||||
C4CORE_EXPORT void handle_warning(srcloc s, const char *fmt, ...);
|
||||
|
||||
|
||||
# define C4_ERROR(msg, ...) \
|
||||
do { \
|
||||
if(c4::get_error_flags() & c4::ON_ERROR_DEBUGBREAK) \
|
||||
{ \
|
||||
C4_DEBUG_BREAK() \
|
||||
} \
|
||||
c4::handle_error(C4_SRCLOC(), msg, ## __VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
|
||||
# define C4_WARNING(msg, ...) \
|
||||
c4::handle_warning(C4_SRCLOC(), msg, ## __VA_ARGS__)
|
||||
|
||||
|
||||
#if defined(C4_ERROR_SHOWS_FILELINE) && defined(C4_ERROR_SHOWS_FUNC)
|
||||
|
||||
struct srcloc
|
||||
{
|
||||
const char *file = "";
|
||||
const char *func = "";
|
||||
int line = 0;
|
||||
};
|
||||
#define C4_SRCLOC() c4::srcloc{__FILE__, C4_PRETTY_FUNC, __LINE__}
|
||||
|
||||
#elif defined(C4_ERROR_SHOWS_FILELINE)
|
||||
|
||||
struct srcloc
|
||||
{
|
||||
const char *file;
|
||||
int line;
|
||||
};
|
||||
#define C4_SRCLOC() c4::srcloc{__FILE__, __LINE__}
|
||||
|
||||
#elif ! defined(C4_ERROR_SHOWS_FUNC)
|
||||
|
||||
struct srcloc
|
||||
{
|
||||
};
|
||||
#define C4_SRCLOC() c4::srcloc()
|
||||
|
||||
#else
|
||||
# error not implemented
|
||||
#endif
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// assertions
|
||||
|
||||
// Doxygen needs this so that only one definition counts
|
||||
#ifdef _DOXYGEN_
|
||||
/** Explicitly enables assertions, independently of NDEBUG status.
|
||||
* This is meant to allow enabling assertions even when NDEBUG is defined.
|
||||
* Defaults to undefined.
|
||||
* @ingroup error_checking */
|
||||
# define C4_USE_ASSERT
|
||||
/** assert that a condition is true; this is turned off when NDEBUG
|
||||
* is defined and C4_USE_ASSERT is not true.
|
||||
* @ingroup error_checking */
|
||||
# define C4_ASSERT
|
||||
/** same as C4_ASSERT(), additionally prints a printf-formatted message
|
||||
* @ingroup error_checking */
|
||||
# define C4_ASSERT_MSG
|
||||
/** evaluates to C4_NOEXCEPT when C4_XASSERT is disabled; otherwise, defaults
|
||||
* to noexcept
|
||||
* @ingroup error_checking */
|
||||
# define C4_NOEXCEPT_A
|
||||
#endif // _DOXYGEN_
|
||||
|
||||
#ifndef C4_USE_ASSERT
|
||||
# ifdef NDEBUG
|
||||
# define C4_USE_ASSERT 0
|
||||
# else
|
||||
# define C4_USE_ASSERT 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if C4_USE_ASSERT
|
||||
# define C4_ASSERT(cond) C4_CHECK(cond)
|
||||
# define C4_ASSERT_MSG(cond, /*fmt, */...) C4_CHECK_MSG(cond, ## __VA_ARGS__)
|
||||
# define C4_ASSERT_IF(predicate, cond) if(predicate) { C4_ASSERT(cond); }
|
||||
# define C4_NOEXCEPT_A C4_NOEXCEPT
|
||||
#else
|
||||
# define C4_ASSERT(cond)
|
||||
# define C4_ASSERT_MSG(cond, /*fmt, */...)
|
||||
# define C4_ASSERT_IF(predicate, cond)
|
||||
# define C4_NOEXCEPT_A noexcept
|
||||
#endif
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// extreme assertions
|
||||
|
||||
// Doxygen needs this so that only one definition counts
|
||||
#ifdef _DOXYGEN_
|
||||
/** Explicitly enables extreme assertions; this is meant to allow enabling
|
||||
* assertions even when NDEBUG is defined. Defaults to undefined.
|
||||
* @ingroup error_checking */
|
||||
# define C4_USE_XASSERT
|
||||
/** extreme assertion: can be switched off independently of
|
||||
* the regular assertion; use for example for bounds checking in hot code.
|
||||
* Turned on only when C4_USE_XASSERT is defined
|
||||
* @ingroup error_checking */
|
||||
# define C4_XASSERT
|
||||
/** same as C4_XASSERT(), and additionally prints a printf-formatted message
|
||||
* @ingroup error_checking */
|
||||
# define C4_XASSERT_MSG
|
||||
/** evaluates to C4_NOEXCEPT when C4_XASSERT is disabled; otherwise, defaults to noexcept
|
||||
* @ingroup error_checking */
|
||||
# define C4_NOEXCEPT_X
|
||||
#endif // _DOXYGEN_
|
||||
|
||||
#ifndef C4_USE_XASSERT
|
||||
# define C4_USE_XASSERT C4_USE_ASSERT
|
||||
#endif
|
||||
|
||||
#if C4_USE_XASSERT
|
||||
# define C4_XASSERT(cond) C4_CHECK(cond)
|
||||
# define C4_XASSERT_MSG(cond, /*fmt, */...) C4_CHECK_MSG(cond, ## __VA_ARGS__)
|
||||
# define C4_XASSERT_IF(predicate, cond) if(predicate) { C4_XASSERT(cond); }
|
||||
# define C4_NOEXCEPT_X C4_NOEXCEPT
|
||||
#else
|
||||
# define C4_XASSERT(cond)
|
||||
# define C4_XASSERT_MSG(cond, /*fmt, */...)
|
||||
# define C4_XASSERT_IF(predicate, cond)
|
||||
# define C4_NOEXCEPT_X noexcept
|
||||
#endif
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// checks: never switched-off
|
||||
|
||||
/** Check that a condition is true, or raise an error when not
|
||||
* true. Unlike C4_ASSERT(), this check is not disabled in non-debug
|
||||
* builds.
|
||||
* @see C4_ASSERT
|
||||
* @ingroup error_checking
|
||||
*
|
||||
* @todo add constexpr-compatible compile-time assert:
|
||||
* https://akrzemi1.wordpress.com/2017/05/18/asserts-in-constexpr-functions/
|
||||
*/
|
||||
#define C4_CHECK(cond) \
|
||||
do { \
|
||||
if(C4_UNLIKELY(!(cond))) \
|
||||
{ \
|
||||
C4_ERROR("check failed: %s", #cond); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
||||
/** like C4_CHECK(), and additionally log a printf-style message.
|
||||
* @see C4_CHECK
|
||||
* @ingroup error_checking */
|
||||
#define C4_CHECK_MSG(cond, fmt, ...) \
|
||||
do { \
|
||||
if(C4_UNLIKELY(!(cond))) \
|
||||
{ \
|
||||
C4_ERROR("check failed: " #cond "\n" fmt, ## __VA_ARGS__); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Common error conditions
|
||||
|
||||
#define C4_NOT_IMPLEMENTED() C4_ERROR("NOT IMPLEMENTED")
|
||||
#define C4_NOT_IMPLEMENTED_MSG(/*msg, */...) C4_ERROR("NOT IMPLEMENTED: " __VA_ARGS__)
|
||||
#define C4_NOT_IMPLEMENTED_IF(condition) do { if(C4_UNLIKELY(condition)) { C4_ERROR("NOT IMPLEMENTED"); } } while(0)
|
||||
#define C4_NOT_IMPLEMENTED_IF_MSG(condition, /*msg, */...) do { if(C4_UNLIKELY(condition)) { C4_ERROR("NOT IMPLEMENTED: " __VA_ARGS__); } } while(0)
|
||||
|
||||
#define C4_NEVER_REACH() do { C4_ERROR("never reach this point"); C4_UNREACHABLE(); } while(0)
|
||||
#define C4_NEVER_REACH_MSG(/*msg, */...) do { C4_ERROR("never reach this point: " __VA_ARGS__); C4_UNREACHABLE(); } while(0)
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// helpers for warning suppression
|
||||
// idea adapted from https://github.com/onqtam/doctest/
|
||||
|
||||
// TODO: add C4_MESSAGE() https://stackoverflow.com/questions/18252351/custom-preprocessor-macro-for-a-conditional-pragma-message-xxx?rq=1
|
||||
|
||||
|
||||
#ifdef C4_MSVC
|
||||
#define C4_SUPPRESS_WARNING_MSVC_PUSH __pragma(warning(push))
|
||||
#define C4_SUPPRESS_WARNING_MSVC(w) __pragma(warning(disable : w))
|
||||
#define C4_SUPPRESS_WARNING_MSVC_POP __pragma(warning(pop))
|
||||
#else // C4_MSVC
|
||||
#define C4_SUPPRESS_WARNING_MSVC_PUSH
|
||||
#define C4_SUPPRESS_WARNING_MSVC(w)
|
||||
#define C4_SUPPRESS_WARNING_MSVC_POP
|
||||
#endif // C4_MSVC
|
||||
|
||||
|
||||
#ifdef C4_CLANG
|
||||
#define C4_PRAGMA_TO_STR(x) _Pragma(#x)
|
||||
#define C4_SUPPRESS_WARNING_CLANG_PUSH _Pragma("clang diagnostic push")
|
||||
#define C4_SUPPRESS_WARNING_CLANG(w) C4_PRAGMA_TO_STR(clang diagnostic ignored w)
|
||||
#define C4_SUPPRESS_WARNING_CLANG_POP _Pragma("clang diagnostic pop")
|
||||
#else // C4_CLANG
|
||||
#define C4_SUPPRESS_WARNING_CLANG_PUSH
|
||||
#define C4_SUPPRESS_WARNING_CLANG(w)
|
||||
#define C4_SUPPRESS_WARNING_CLANG_POP
|
||||
#endif // C4_CLANG
|
||||
|
||||
|
||||
#ifdef C4_GCC
|
||||
#define C4_PRAGMA_TO_STR(x) _Pragma(#x)
|
||||
#define C4_SUPPRESS_WARNING_GCC_PUSH _Pragma("GCC diagnostic push")
|
||||
#define C4_SUPPRESS_WARNING_GCC(w) C4_PRAGMA_TO_STR(GCC diagnostic ignored w)
|
||||
#define C4_SUPPRESS_WARNING_GCC_POP _Pragma("GCC diagnostic pop")
|
||||
#else // C4_GCC
|
||||
#define C4_SUPPRESS_WARNING_GCC_PUSH
|
||||
#define C4_SUPPRESS_WARNING_GCC(w)
|
||||
#define C4_SUPPRESS_WARNING_GCC_POP
|
||||
#endif // C4_GCC
|
||||
|
||||
|
||||
#define C4_SUPPRESS_WARNING_MSVC_WITH_PUSH(w) \
|
||||
C4_SUPPRESS_WARNING_MSVC_PUSH \
|
||||
C4_SUPPRESS_WARNING_MSVC(w)
|
||||
|
||||
#define C4_SUPPRESS_WARNING_CLANG_WITH_PUSH(w) \
|
||||
C4_SUPPRESS_WARNING_CLANG_PUSH \
|
||||
C4_SUPPRESS_WARNING_CLANG(w)
|
||||
|
||||
#define C4_SUPPRESS_WARNING_GCC_WITH_PUSH(w) \
|
||||
C4_SUPPRESS_WARNING_GCC_PUSH \
|
||||
C4_SUPPRESS_WARNING_GCC(w)
|
||||
|
||||
|
||||
#define C4_SUPPRESS_WARNING_GCC_CLANG_PUSH \
|
||||
C4_SUPPRESS_WARNING_GCC_PUSH \
|
||||
C4_SUPPRESS_WARNING_CLANG_PUSH
|
||||
|
||||
#define C4_SUPPRESS_WARNING_GCC_CLANG(w) \
|
||||
C4_SUPPRESS_WARNING_GCC(w) \
|
||||
C4_SUPPRESS_WARNING_CLANG(w)
|
||||
|
||||
#define C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH(w) \
|
||||
C4_SUPPRESS_WARNING_GCC_WITH_PUSH(w) \
|
||||
C4_SUPPRESS_WARNING_CLANG_WITH_PUSH(w)
|
||||
|
||||
#define C4_SUPPRESS_WARNING_GCC_CLANG_POP \
|
||||
C4_SUPPRESS_WARNING_GCC_POP \
|
||||
C4_SUPPRESS_WARNING_CLANG_POP
|
||||
|
||||
} // namespace c4
|
||||
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif /* _C4_ERROR_HPP_ */
|
||||
18
ext/c4core.src/c4/export.hpp
Normal file
18
ext/c4core.src/c4/export.hpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef C4_EXPORT_HPP_
|
||||
#define C4_EXPORT_HPP_
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef C4CORE_SHARED
|
||||
#ifdef C4CORE_EXPORTS
|
||||
#define C4CORE_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define C4CORE_EXPORT __declspec(dllimport)
|
||||
#endif
|
||||
#else
|
||||
#define C4CORE_EXPORT
|
||||
#endif
|
||||
#else
|
||||
#define C4CORE_EXPORT
|
||||
#endif
|
||||
|
||||
#endif /* C4CORE_EXPORT_HPP_ */
|
||||
175
ext/c4core.src/c4/ext/debugbreak/debugbreak.h
Normal file
175
ext/c4core.src/c4/ext/debugbreak/debugbreak.h
Normal file
@@ -0,0 +1,175 @@
|
||||
/* Copyright (c) 2011-2021, Scott Tsai
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef DEBUG_BREAK_H
|
||||
#define DEBUG_BREAK_H
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
||||
#define debug_break __debugbreak
|
||||
|
||||
#else
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define DEBUG_BREAK_USE_TRAP_INSTRUCTION 1
|
||||
#define DEBUG_BREAK_USE_BUILTIN_TRAP 2
|
||||
#define DEBUG_BREAK_USE_SIGTRAP 3
|
||||
#define DEBUG_BREAK_USE_BUILTIN_DEBUGTRAP 4
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
#define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION
|
||||
__inline__ static void trap_instruction(void)
|
||||
{
|
||||
__asm__ volatile("int $0x03");
|
||||
}
|
||||
#elif defined(__thumb__)
|
||||
#define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION
|
||||
/* FIXME: handle __THUMB_INTERWORK__ */
|
||||
__attribute__((always_inline))
|
||||
__inline__ static void trap_instruction(void)
|
||||
{
|
||||
/* See 'arm-linux-tdep.c' in GDB source.
|
||||
* Both instruction sequences below work. */
|
||||
#if 1
|
||||
/* 'eabi_linux_thumb_le_breakpoint' */
|
||||
__asm__ volatile(".inst 0xde01");
|
||||
#else
|
||||
/* 'eabi_linux_thumb2_le_breakpoint' */
|
||||
__asm__ volatile(".inst.w 0xf7f0a000");
|
||||
#endif
|
||||
|
||||
/* Known problem:
|
||||
* After a breakpoint hit, can't 'stepi', 'step', or 'continue' in GDB.
|
||||
* 'step' would keep getting stuck on the same instruction.
|
||||
*
|
||||
* Workaround: use the new GDB commands 'debugbreak-step' and
|
||||
* 'debugbreak-continue' that become available
|
||||
* after you source the script from GDB:
|
||||
*
|
||||
* $ gdb -x debugbreak-gdb.py <... USUAL ARGUMENTS ...>
|
||||
*
|
||||
* 'debugbreak-step' would jump over the breakpoint instruction with
|
||||
* roughly equivalent of:
|
||||
* (gdb) set $instruction_len = 2
|
||||
* (gdb) tbreak *($pc + $instruction_len)
|
||||
* (gdb) jump *($pc + $instruction_len)
|
||||
*/
|
||||
}
|
||||
#elif defined(__arm__) && !defined(__thumb__)
|
||||
#define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION
|
||||
__attribute__((always_inline))
|
||||
__inline__ static void trap_instruction(void)
|
||||
{
|
||||
/* See 'arm-linux-tdep.c' in GDB source,
|
||||
* 'eabi_linux_arm_le_breakpoint' */
|
||||
__asm__ volatile(".inst 0xe7f001f0");
|
||||
/* Known problem:
|
||||
* Same problem and workaround as Thumb mode */
|
||||
}
|
||||
#elif defined(__aarch64__) && defined(__APPLE__)
|
||||
#define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_BUILTIN_DEBUGTRAP
|
||||
#elif defined(__aarch64__)
|
||||
#define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION
|
||||
__attribute__((always_inline))
|
||||
__inline__ static void trap_instruction(void)
|
||||
{
|
||||
/* See 'aarch64-tdep.c' in GDB source,
|
||||
* 'aarch64_default_breakpoint' */
|
||||
__asm__ volatile(".inst 0xd4200000");
|
||||
}
|
||||
#elif defined(__powerpc__)
|
||||
/* PPC 32 or 64-bit, big or little endian */
|
||||
#define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION
|
||||
__attribute__((always_inline))
|
||||
__inline__ static void trap_instruction(void)
|
||||
{
|
||||
/* See 'rs6000-tdep.c' in GDB source,
|
||||
* 'rs6000_breakpoint' */
|
||||
__asm__ volatile(".4byte 0x7d821008");
|
||||
|
||||
/* Known problem:
|
||||
* After a breakpoint hit, can't 'stepi', 'step', or 'continue' in GDB.
|
||||
* 'step' stuck on the same instruction ("twge r2,r2").
|
||||
*
|
||||
* The workaround is the same as ARM Thumb mode: use debugbreak-gdb.py
|
||||
* or manually jump over the instruction. */
|
||||
}
|
||||
#elif defined(__riscv)
|
||||
/* RISC-V 32 or 64-bit, whether the "C" extension
|
||||
* for compressed, 16-bit instructions are supported or not */
|
||||
#define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION
|
||||
__attribute__((always_inline))
|
||||
__inline__ static void trap_instruction(void)
|
||||
{
|
||||
/* See 'riscv-tdep.c' in GDB source,
|
||||
* 'riscv_sw_breakpoint_from_kind' */
|
||||
__asm__ volatile(".4byte 0x00100073");
|
||||
}
|
||||
#else
|
||||
#define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_SIGTRAP
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef DEBUG_BREAK_IMPL
|
||||
#error "debugbreak.h is not supported on this target"
|
||||
#elif DEBUG_BREAK_IMPL == DEBUG_BREAK_USE_TRAP_INSTRUCTION
|
||||
__attribute__((always_inline))
|
||||
__inline__ static void debug_break(void)
|
||||
{
|
||||
trap_instruction();
|
||||
}
|
||||
#elif DEBUG_BREAK_IMPL == DEBUG_BREAK_USE_BUILTIN_DEBUGTRAP
|
||||
__attribute__((always_inline))
|
||||
__inline__ static void debug_break(void)
|
||||
{
|
||||
__builtin_debugtrap();
|
||||
}
|
||||
#elif DEBUG_BREAK_IMPL == DEBUG_BREAK_USE_BUILTIN_TRAP
|
||||
__attribute__((always_inline))
|
||||
__inline__ static void debug_break(void)
|
||||
{
|
||||
__builtin_trap();
|
||||
}
|
||||
#elif DEBUG_BREAK_IMPL == DEBUG_BREAK_USE_SIGTRAP
|
||||
#include <signal.h>
|
||||
__attribute__((always_inline))
|
||||
__inline__ static void debug_break(void)
|
||||
{
|
||||
raise(SIGTRAP);
|
||||
}
|
||||
#else
|
||||
#error "invalid DEBUG_BREAK_IMPL value"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ifdef _MSC_VER */
|
||||
|
||||
#endif /* ifndef DEBUG_BREAK_H */
|
||||
38
ext/c4core.src/c4/ext/fast_float.hpp
Normal file
38
ext/c4core.src/c4/ext/fast_float.hpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#ifndef _C4_EXT_FAST_FLOAT_HPP_
|
||||
#define _C4_EXT_FAST_FLOAT_HPP_
|
||||
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4365) // '=': conversion from 'const _Ty' to 'fast_float::limb', signed/unsigned mismatch
|
||||
# pragma warning(disable: 4996) // snprintf/scanf: this function or variable may be unsafe
|
||||
#elif defined(__clang__) || defined(__APPLE_CC__) || defined(_LIBCPP_VERSION)
|
||||
# pragma clang diagnostic push
|
||||
# if (defined(__clang_major__) && (__clang_major__ >= 9)) || defined(__APPLE_CC__)
|
||||
# pragma clang diagnostic ignored "-Wfortify-source"
|
||||
# endif
|
||||
# pragma clang diagnostic ignored "-Wshift-count-overflow"
|
||||
# pragma clang diagnostic ignored "-Wold-style-cast"
|
||||
#elif defined(__GNUC__)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wnarrowing"
|
||||
# pragma GCC diagnostic ignored "-Wconversion"
|
||||
# pragma GCC diagnostic ignored "-Wsign-conversion"
|
||||
# pragma GCC diagnostic ignored "-Wuseless-cast"
|
||||
# pragma GCC diagnostic ignored "-Wold-style-cast"
|
||||
# pragma GCC diagnostic ignored "-Warray-bounds"
|
||||
# if __GNUC__ >= 5
|
||||
# pragma GCC diagnostic ignored "-Wshift-count-overflow"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include "c4/ext/fast_float_all.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning(pop)
|
||||
#elif defined(__clang__) || defined(__APPLE_CC__)
|
||||
# pragma clang diagnostic pop
|
||||
#elif defined(__GNUC__)
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif // _C4_EXT_FAST_FLOAT_HPP_
|
||||
4390
ext/c4core.src/c4/ext/fast_float_all.h
Normal file
4390
ext/c4core.src/c4/ext/fast_float_all.h
Normal file
File diff suppressed because it is too large
Load Diff
64
ext/c4core.src/c4/format.cpp
Normal file
64
ext/c4core.src/c4/format.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#include "c4/format.hpp"
|
||||
|
||||
#include <memory> // for std::align
|
||||
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wformat-nonliteral"
|
||||
# pragma clang diagnostic ignored "-Wold-style-cast"
|
||||
#elif defined(__GNUC__)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wformat-nonliteral"
|
||||
# pragma GCC diagnostic ignored "-Wold-style-cast"
|
||||
#endif
|
||||
|
||||
namespace c4 {
|
||||
|
||||
|
||||
size_t to_chars(substr buf, fmt::const_raw_wrapper r)
|
||||
{
|
||||
void * vptr = buf.str;
|
||||
size_t space = buf.len;
|
||||
char * ptr = (char*) std::align(r.alignment, r.len, vptr, space);
|
||||
if(ptr == nullptr)
|
||||
{
|
||||
// if it was not possible to align, return a conservative estimate
|
||||
// of the required space
|
||||
return r.alignment + r.len;
|
||||
}
|
||||
C4_CHECK(ptr >= buf.begin() && ptr <= buf.end());
|
||||
size_t sz = static_cast<size_t>(ptr - buf.str) + r.len;
|
||||
if(sz <= buf.len)
|
||||
{
|
||||
memcpy(ptr, r.buf, r.len);
|
||||
}
|
||||
return sz;
|
||||
}
|
||||
|
||||
|
||||
bool from_chars(csubstr buf, fmt::raw_wrapper *r)
|
||||
{
|
||||
C4_SUPPRESS_WARNING_GCC_WITH_PUSH("-Wcast-qual")
|
||||
void * vptr = (void*)buf.str;
|
||||
C4_SUPPRESS_WARNING_GCC_POP
|
||||
size_t space = buf.len;
|
||||
char * ptr = (char*) std::align(r->alignment, r->len, vptr, space);
|
||||
C4_CHECK(ptr != nullptr);
|
||||
C4_CHECK(ptr >= buf.begin() && ptr <= buf.end());
|
||||
C4_SUPPRESS_WARNING_GCC_PUSH
|
||||
#if defined(__GNUC__) && __GNUC__ > 9
|
||||
C4_SUPPRESS_WARNING_GCC("-Wanalyzer-null-argument")
|
||||
#endif
|
||||
memcpy(r->buf, ptr, r->len);
|
||||
C4_SUPPRESS_WARNING_GCC_POP
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
} // namespace c4
|
||||
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
#elif defined(__GNUC__)
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
1058
ext/c4core.src/c4/format.hpp
Normal file
1058
ext/c4core.src/c4/format.hpp
Normal file
File diff suppressed because it is too large
Load Diff
72
ext/c4core.src/c4/gcc-4.8.hpp
Normal file
72
ext/c4core.src/c4/gcc-4.8.hpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#ifndef _C4_GCC_4_8_HPP_
|
||||
#define _C4_GCC_4_8_HPP_
|
||||
|
||||
#if __GNUC__ == 4 && __GNUC_MINOR__ >= 8
|
||||
/* STL polyfills for old GNU compilers */
|
||||
|
||||
_Pragma("GCC diagnostic ignored \"-Wshadow\"")
|
||||
_Pragma("GCC diagnostic ignored \"-Wmissing-field-initializers\"")
|
||||
|
||||
#if __cplusplus
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
namespace std {
|
||||
|
||||
template<typename _Tp>
|
||||
struct is_trivially_copyable : public integral_constant<bool,
|
||||
is_destructible<_Tp>::value && __has_trivial_destructor(_Tp) &&
|
||||
(__has_trivial_constructor(_Tp) || __has_trivial_copy(_Tp) || __has_trivial_assign(_Tp))>
|
||||
{ };
|
||||
|
||||
template<typename _Tp>
|
||||
using is_trivially_copy_constructible = has_trivial_copy_constructor<_Tp>;
|
||||
|
||||
template<typename _Tp>
|
||||
using is_trivially_default_constructible = has_trivial_default_constructor<_Tp>;
|
||||
|
||||
template<typename _Tp>
|
||||
using is_trivially_copy_assignable = has_trivial_copy_assign<_Tp>;
|
||||
|
||||
/* not supported */
|
||||
template<typename _Tp>
|
||||
struct is_trivially_move_constructible : false_type
|
||||
{ };
|
||||
|
||||
/* not supported */
|
||||
template<typename _Tp>
|
||||
struct is_trivially_move_assignable : false_type
|
||||
{ };
|
||||
|
||||
inline void *align(size_t __align, size_t __size, void*& __ptr, size_t& __space) noexcept
|
||||
{
|
||||
if (__space < __size)
|
||||
return nullptr;
|
||||
const auto __intptr = reinterpret_cast<uintptr_t>(__ptr);
|
||||
const auto __aligned = (__intptr - 1u + __align) & -__align;
|
||||
const auto __diff = __aligned - __intptr;
|
||||
if (__diff > (__space - __size))
|
||||
return nullptr;
|
||||
else
|
||||
{
|
||||
__space -= __diff;
|
||||
return __ptr = reinterpret_cast<void*>(__aligned);
|
||||
}
|
||||
}
|
||||
|
||||
#if __GNUC__ == 4 && __GNUC_MINOR__ == 8
|
||||
typedef long double max_align_t ;
|
||||
#endif
|
||||
|
||||
}
|
||||
#else // __cplusplus
|
||||
|
||||
#include <string.h>
|
||||
// see https://sourceware.org/bugzilla/show_bug.cgi?id=25399 (ubuntu gcc-4.8)
|
||||
#define memset(s, c, count) __builtin_memset(s, c, count)
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // __GNUC__ == 4 && __GNUC_MINOR__ >= 8
|
||||
|
||||
#endif // _C4_GCC_4_8_HPP_
|
||||
16
ext/c4core.src/c4/language.cpp
Normal file
16
ext/c4core.src/c4/language.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#include "c4/language.hpp"
|
||||
|
||||
namespace c4 {
|
||||
namespace detail {
|
||||
|
||||
#ifndef __GNUC__
|
||||
void use_char_pointer(char const volatile* v)
|
||||
{
|
||||
C4_UNUSED(v);
|
||||
}
|
||||
#else
|
||||
void foo() {} // to avoid empty file warning from the linker
|
||||
#endif
|
||||
|
||||
} // namespace detail
|
||||
} // namespace c4
|
||||
358
ext/c4core.src/c4/language.hpp
Normal file
358
ext/c4core.src/c4/language.hpp
Normal file
@@ -0,0 +1,358 @@
|
||||
#ifndef _C4_LANGUAGE_HPP_
|
||||
#define _C4_LANGUAGE_HPP_
|
||||
|
||||
/** @file language.hpp Provides language standard information macros and
|
||||
* compiler agnostic utility macros: namespace facilities, function attributes,
|
||||
* variable attributes, etc.
|
||||
* @ingroup basic_headers */
|
||||
|
||||
#include "c4/preprocessor.hpp"
|
||||
#include "c4/compiler.hpp"
|
||||
|
||||
/* Detect C++ standard.
|
||||
* @see http://stackoverflow.com/a/7132549/5875572 */
|
||||
#ifndef C4_CPP
|
||||
# if defined(_MSC_VER) && !defined(__clang__)
|
||||
# if _MSC_VER >= 1910 // >VS2015: VS2017, VS2019, VS2022
|
||||
# if (!defined(_MSVC_LANG))
|
||||
# error _MSVC not defined
|
||||
# endif
|
||||
# if _MSVC_LANG >= 201705L
|
||||
# define C4_CPP 20
|
||||
# define C4_CPP20
|
||||
# elif _MSVC_LANG == 201703L
|
||||
# define C4_CPP 17
|
||||
# define C4_CPP17
|
||||
# elif _MSVC_LANG >= 201402L
|
||||
# define C4_CPP 14
|
||||
# define C4_CPP14
|
||||
# elif _MSVC_LANG >= 201103L
|
||||
# define C4_CPP 11
|
||||
# define C4_CPP11
|
||||
# else
|
||||
# error C++ lesser than C++11 not supported
|
||||
# endif
|
||||
# else
|
||||
# if _MSC_VER == 1900
|
||||
# define C4_CPP 14 // VS2015 is c++14 https://devblogs.microsoft.com/cppblog/c111417-features-in-vs-2015-rtm/
|
||||
# define C4_CPP14
|
||||
# elif _MSC_VER == 1800 // VS2013
|
||||
# define C4_CPP 11
|
||||
# define C4_CPP11
|
||||
# else
|
||||
# error C++ lesser than C++11 not supported
|
||||
# endif
|
||||
# endif
|
||||
# elif defined(__INTEL_COMPILER) // https://software.intel.com/en-us/node/524490
|
||||
# ifdef __INTEL_CXX20_MODE__ // not sure about this
|
||||
# define C4_CPP 20
|
||||
# define C4_CPP20
|
||||
# elif defined __INTEL_CXX17_MODE__ // not sure about this
|
||||
# define C4_CPP 17
|
||||
# define C4_CPP17
|
||||
# elif defined __INTEL_CXX14_MODE__ // not sure about this
|
||||
# define C4_CPP 14
|
||||
# define C4_CPP14
|
||||
# elif defined __INTEL_CXX11_MODE__
|
||||
# define C4_CPP 11
|
||||
# define C4_CPP11
|
||||
# else
|
||||
# error C++ lesser than C++11 not supported
|
||||
# endif
|
||||
# else
|
||||
# ifndef __cplusplus
|
||||
# error __cplusplus is not defined?
|
||||
# endif
|
||||
# if __cplusplus == 1
|
||||
# error cannot handle __cplusplus==1
|
||||
# elif __cplusplus >= 201709L
|
||||
# define C4_CPP 20
|
||||
# define C4_CPP20
|
||||
# elif __cplusplus >= 201703L
|
||||
# define C4_CPP 17
|
||||
# define C4_CPP17
|
||||
# elif __cplusplus >= 201402L
|
||||
# define C4_CPP 14
|
||||
# define C4_CPP14
|
||||
# elif __cplusplus >= 201103L
|
||||
# define C4_CPP 11
|
||||
# define C4_CPP11
|
||||
# elif __cplusplus >= 199711L
|
||||
# error C++ lesser than C++11 not supported
|
||||
# endif
|
||||
# endif
|
||||
#else
|
||||
# ifdef C4_CPP == 20
|
||||
# define C4_CPP20
|
||||
# elif C4_CPP == 17
|
||||
# define C4_CPP17
|
||||
# elif C4_CPP == 14
|
||||
# define C4_CPP14
|
||||
# elif C4_CPP == 11
|
||||
# define C4_CPP11
|
||||
# elif C4_CPP == 98
|
||||
# define C4_CPP98
|
||||
# error C++ lesser than C++11 not supported
|
||||
# else
|
||||
# error C4_CPP must be one of 20, 17, 14, 11, 98
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef C4_CPP20
|
||||
# define C4_CPP17
|
||||
# define C4_CPP14
|
||||
# define C4_CPP11
|
||||
#elif defined(C4_CPP17)
|
||||
# define C4_CPP14
|
||||
# define C4_CPP11
|
||||
#elif defined(C4_CPP14)
|
||||
# define C4_CPP11
|
||||
#endif
|
||||
|
||||
/** lifted from this answer: http://stackoverflow.com/a/20170989/5875572 */
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
# if _MSC_VER < 1900
|
||||
# define C4_CONSTEXPR11
|
||||
# define C4_CONSTEXPR14
|
||||
# elif _MSC_VER < 2000
|
||||
# define C4_CONSTEXPR11 constexpr
|
||||
# define C4_CONSTEXPR14
|
||||
# else
|
||||
# define C4_CONSTEXPR11 constexpr
|
||||
# define C4_CONSTEXPR14 constexpr
|
||||
# endif
|
||||
#else
|
||||
# if __cplusplus < 201103
|
||||
# define C4_CONSTEXPR11
|
||||
# define C4_CONSTEXPR14
|
||||
# elif __cplusplus == 201103
|
||||
# define C4_CONSTEXPR11 constexpr
|
||||
# define C4_CONSTEXPR14
|
||||
# else
|
||||
# define C4_CONSTEXPR11 constexpr
|
||||
# define C4_CONSTEXPR14 constexpr
|
||||
# endif
|
||||
#endif // _MSC_VER
|
||||
|
||||
|
||||
#if C4_CPP < 17
|
||||
#define C4_IF_CONSTEXPR
|
||||
#define C4_INLINE_CONSTEXPR constexpr
|
||||
#else
|
||||
#define C4_IF_CONSTEXPR constexpr
|
||||
#define C4_INLINE_CONSTEXPR inline constexpr
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
# if (defined(_CPPUNWIND) && (_CPPUNWIND == 1))
|
||||
# define C4_EXCEPTIONS
|
||||
# endif
|
||||
#else
|
||||
# if defined(__EXCEPTIONS) || defined(__cpp_exceptions)
|
||||
# define C4_EXCEPTIONS
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef C4_EXCEPTIONS
|
||||
# define C4_IF_EXCEPTIONS_(exc_code, setjmp_code) exc_code
|
||||
# define C4_IF_EXCEPTIONS(exc_code, setjmp_code) do { exc_code } while(0)
|
||||
#else
|
||||
# define C4_IF_EXCEPTIONS_(exc_code, setjmp_code) setjmp_code
|
||||
# define C4_IF_EXCEPTIONS(exc_code, setjmp_code) do { setjmp_code } while(0)
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
# if defined(_CPPRTTI)
|
||||
# define C4_RTTI
|
||||
# endif
|
||||
#else
|
||||
# if defined(__GXX_RTTI)
|
||||
# define C4_RTTI
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef C4_RTTI
|
||||
# define C4_IF_RTTI_(code_rtti, code_no_rtti) code_rtti
|
||||
# define C4_IF_RTTI(code_rtti, code_no_rtti) do { code_rtti } while(0)
|
||||
#else
|
||||
# define C4_IF_RTTI_(code_rtti, code_no_rtti) code_no_rtti
|
||||
# define C4_IF_RTTI(code_rtti, code_no_rtti) do { code_no_rtti } while(0)
|
||||
#endif
|
||||
|
||||
|
||||
//------------------------------------------------------------
|
||||
|
||||
#define _C4_BEGIN_NAMESPACE(ns) namespace ns {
|
||||
#define _C4_END_NAMESPACE(ns) }
|
||||
|
||||
// MSVC cant handle the C4_FOR_EACH macro... need to fix this
|
||||
//#define C4_BEGIN_NAMESPACE(...) C4_FOR_EACH_SEP(_C4_BEGIN_NAMESPACE, , __VA_ARGS__)
|
||||
//#define C4_END_NAMESPACE(...) C4_FOR_EACH_SEP(_C4_END_NAMESPACE, , __VA_ARGS__)
|
||||
#define C4_BEGIN_NAMESPACE(ns) namespace ns {
|
||||
#define C4_END_NAMESPACE(ns) }
|
||||
|
||||
#define C4_BEGIN_HIDDEN_NAMESPACE namespace /*hidden*/ {
|
||||
#define C4_END_HIDDEN_NAMESPACE } /* namespace hidden */
|
||||
|
||||
//------------------------------------------------------------
|
||||
|
||||
#ifndef C4_API
|
||||
# if defined(_MSC_VER) && !defined(__clang__)
|
||||
# if defined(C4_EXPORT)
|
||||
# define C4_API __declspec(dllexport)
|
||||
# elif defined(C4_IMPORT)
|
||||
# define C4_API __declspec(dllimport)
|
||||
# else
|
||||
# define C4_API
|
||||
# endif
|
||||
# else
|
||||
# define C4_API
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
# define C4_RESTRICT __restrict
|
||||
# define C4_RESTRICT_FN __declspec(restrict)
|
||||
# define C4_NO_INLINE __declspec(noinline)
|
||||
# define C4_ALWAYS_INLINE inline __forceinline
|
||||
/** these are not available in VS AFAIK */
|
||||
# define C4_CONST
|
||||
# define C4_PURE
|
||||
# define C4_FLATTEN
|
||||
# define C4_HOT /** @todo */
|
||||
# define C4_COLD /** @todo */
|
||||
# define C4_ASSUME(...) __assume(__VA_ARGS__)
|
||||
# define C4_EXPECT(x, y) x /** @todo */
|
||||
# define C4_LIKELY(x) x
|
||||
# define C4_UNLIKELY(x) x
|
||||
# define C4_UNREACHABLE() _c4_msvc_unreachable()
|
||||
# define C4_ATTR_FORMAT(...) /** */
|
||||
# define C4_NORETURN [[noreturn]]
|
||||
# if _MSC_VER >= 1700 // VS2012
|
||||
# define C4_NODISCARD _Check_return_
|
||||
# else
|
||||
# define C4_NODISCARD
|
||||
# endif
|
||||
[[noreturn]] __forceinline void _c4_msvc_unreachable() { __assume(false); } ///< https://stackoverflow.com/questions/60802864/emulating-gccs-builtin-unreachable-in-visual-studio
|
||||
# define C4_UNREACHABLE_AFTER_ERR() /* */
|
||||
#else
|
||||
///< @todo assuming gcc-like compiler. check it is actually so.
|
||||
/** for function attributes in GCC,
|
||||
* @see https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes */
|
||||
/** for __builtin functions in GCC,
|
||||
* @see https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html */
|
||||
# define C4_RESTRICT __restrict__
|
||||
# define C4_RESTRICT_FN __attribute__((restrict))
|
||||
# define C4_NO_INLINE __attribute__((noinline))
|
||||
# define C4_ALWAYS_INLINE inline __attribute__((always_inline))
|
||||
# define C4_CONST __attribute__((const))
|
||||
# define C4_PURE __attribute__((pure))
|
||||
/** force inlining of every callee function */
|
||||
# define C4_FLATTEN __atribute__((flatten))
|
||||
/** mark a function as hot, ie as having a visible impact in CPU time
|
||||
* thus making it more likely to inline, etc
|
||||
* @see http://stackoverflow.com/questions/15028990/semantics-of-gcc-hot-attribute */
|
||||
# define C4_HOT __attribute__((hot))
|
||||
/** mark a function as cold, ie as NOT having a visible impact in CPU time
|
||||
* @see http://stackoverflow.com/questions/15028990/semantics-of-gcc-hot-attribute */
|
||||
# define C4_COLD __attribute__((cold))
|
||||
# define C4_EXPECT(x, y) __builtin_expect(x, y) ///< @see https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html
|
||||
# define C4_LIKELY(x) __builtin_expect(x, 1)
|
||||
# define C4_UNLIKELY(x) __builtin_expect(x, 0)
|
||||
# define C4_UNREACHABLE() __builtin_unreachable()
|
||||
# define C4_ATTR_FORMAT(...) //__attribute__((format (__VA_ARGS__))) ///< @see https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes
|
||||
# define C4_NORETURN __attribute__((noreturn))
|
||||
# define C4_NODISCARD __attribute__((warn_unused_result))
|
||||
# define C4_UNREACHABLE_AFTER_ERR() C4_UNREACHABLE()
|
||||
// C4_ASSUME
|
||||
// see https://stackoverflow.com/questions/63493968/reproducing-clangs-builtin-assume-for-gcc
|
||||
// preferred option: C++ standard attribute
|
||||
# ifdef __has_cpp_attribute
|
||||
# if __has_cpp_attribute(assume) >= 202207L
|
||||
# define C4_ASSUME(...) [[assume(__VA_ARGS__)]]
|
||||
# endif
|
||||
# endif
|
||||
// first fallback: compiler intrinsics/attributes for assumptions
|
||||
# ifndef C4_ASSUME
|
||||
# if defined(__clang__)
|
||||
# define C4_ASSUME(...) __builtin_assume(__VA_ARGS__)
|
||||
# elif defined(__GNUC__)
|
||||
# if __GNUC__ >= 13
|
||||
# define C4_ASSUME(...) __attribute__((__assume__(__VA_ARGS__)))
|
||||
# endif
|
||||
# endif
|
||||
# endif
|
||||
// second fallback: possibly evaluating uses of unreachable()
|
||||
// Set this to 1 if you want to allow assumptions to possibly evaluate.
|
||||
# ifndef C4_ASSUME_ALLOW_EVAL
|
||||
# define C4_ASSUME_ALLOW_EVAL 0
|
||||
# endif
|
||||
# if !defined(C4_ASSUME) && (C4_ASSUME_ALLOW_EVAL)
|
||||
# define C4_ASSUME(...) do { if (!bool(__VA_ARGS__)) C4_UNREACHABLE(); ) while(0)
|
||||
# endif
|
||||
// last fallback: define macro as doing nothing
|
||||
# ifndef C4_ASSUME
|
||||
# define C4_ASSUME(...)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
#if C4_CPP >= 14
|
||||
# define C4_DEPRECATED(msg) [[deprecated(msg)]]
|
||||
#else
|
||||
# if defined(_MSC_VER)
|
||||
# define C4_DEPRECATED(msg) __declspec(deprecated(msg))
|
||||
# else // defined(__GNUC__) || defined(__clang__)
|
||||
# define C4_DEPRECATED(msg) __attribute__((deprecated(msg)))
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# define C4_FUNC __FUNCTION__
|
||||
# define C4_PRETTY_FUNC __FUNCSIG__
|
||||
#else /// @todo assuming gcc-like compiler. check it is actually so.
|
||||
# define C4_FUNC __FUNCTION__
|
||||
# define C4_PRETTY_FUNC __PRETTY_FUNCTION__
|
||||
#endif
|
||||
|
||||
/** prevent compiler warnings about a specific var being unused */
|
||||
#define C4_UNUSED(var) (void)var
|
||||
|
||||
#if C4_CPP >= 17
|
||||
#define C4_STATIC_ASSERT(cond) static_assert(cond)
|
||||
#else
|
||||
#define C4_STATIC_ASSERT(cond) static_assert((cond), #cond)
|
||||
#endif
|
||||
#define C4_STATIC_ASSERT_MSG(cond, msg) static_assert((cond), #cond ": " msg)
|
||||
|
||||
/** @def C4_DONT_OPTIMIZE idea lifted from GoogleBenchmark.
|
||||
* @see https://github.com/google/benchmark/blob/master/include/benchmark/benchmark_api.h */
|
||||
namespace c4 {
|
||||
namespace detail {
|
||||
#ifdef __GNUC__
|
||||
# define C4_DONT_OPTIMIZE(var) c4::detail::dont_optimize(var)
|
||||
template< class T >
|
||||
C4_ALWAYS_INLINE void dont_optimize(T const& value) { asm volatile("" : : "g"(value) : "memory"); } // NOLINT
|
||||
#else
|
||||
# define C4_DONT_OPTIMIZE(var) c4::detail::use_char_pointer(reinterpret_cast< const char* >(&var))
|
||||
void use_char_pointer(char const volatile*);
|
||||
#endif
|
||||
} // namespace detail
|
||||
} // namespace c4
|
||||
|
||||
/** @def C4_KEEP_EMPTY_LOOP prevent an empty loop from being optimized out.
|
||||
* @see http://stackoverflow.com/a/7084193/5875572 */
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
# define C4_KEEP_EMPTY_LOOP { char c; C4_DONT_OPTIMIZE(c); }
|
||||
#else
|
||||
# define C4_KEEP_EMPTY_LOOP { asm(""); }
|
||||
#endif
|
||||
|
||||
/** @def C4_VA_LIST_REUSE_MUST_COPY
|
||||
* @todo <jpmag> I strongly suspect that this is actually only in UNIX platforms. revisit this. */
|
||||
#ifdef __GNUC__
|
||||
# define C4_VA_LIST_REUSE_MUST_COPY
|
||||
#endif
|
||||
|
||||
#endif /* _C4_LANGUAGE_HPP_ */
|
||||
32
ext/c4core.src/c4/memory_util.cpp
Normal file
32
ext/c4core.src/c4/memory_util.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#include "c4/memory_util.hpp"
|
||||
#include "c4/error.hpp"
|
||||
|
||||
namespace c4 {
|
||||
|
||||
|
||||
/** Fills 'dest' with the first 'pattern_size' bytes at 'pattern', 'num_times'. */
|
||||
void mem_repeat(void* dest, void const* pattern, size_t pattern_size, size_t num_times)
|
||||
{
|
||||
if(C4_UNLIKELY(num_times == 0))
|
||||
return;
|
||||
C4_ASSERT( ! mem_overlaps(dest, pattern, num_times*pattern_size, pattern_size));
|
||||
char *begin = static_cast<char*>(dest);
|
||||
char *end = begin + num_times * pattern_size;
|
||||
// copy the pattern once
|
||||
::memcpy(begin, pattern, pattern_size);
|
||||
// now copy from dest to itself, doubling up every time
|
||||
size_t n = pattern_size;
|
||||
while(begin + 2*n < end)
|
||||
{
|
||||
::memcpy(begin + n, begin, n);
|
||||
n <<= 1u; // double n
|
||||
}
|
||||
// copy the missing part
|
||||
if(begin + n < end)
|
||||
{
|
||||
::memcpy(begin + n, begin, static_cast<size_t>(end - (begin + n)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace c4
|
||||
783
ext/c4core.src/c4/memory_util.hpp
Normal file
783
ext/c4core.src/c4/memory_util.hpp
Normal file
@@ -0,0 +1,783 @@
|
||||
#ifndef _C4_MEMORY_UTIL_HPP_
|
||||
#define _C4_MEMORY_UTIL_HPP_
|
||||
|
||||
#include "c4/config.hpp"
|
||||
#include "c4/error.hpp"
|
||||
#include "c4/compiler.hpp"
|
||||
#include "c4/cpu.hpp"
|
||||
#include <type_traits>
|
||||
#ifdef C4_MSVC
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
|
||||
#if (defined(__GNUC__) && __GNUC__ >= 10) || defined(__has_builtin)
|
||||
#define _C4_USE_LSB_INTRINSIC(which) __has_builtin(which)
|
||||
#define _C4_USE_MSB_INTRINSIC(which) __has_builtin(which)
|
||||
#elif defined(C4_MSVC)
|
||||
#define _C4_USE_LSB_INTRINSIC(which) true
|
||||
#define _C4_USE_MSB_INTRINSIC(which) true
|
||||
#else
|
||||
// let's try our luck
|
||||
#define _C4_USE_LSB_INTRINSIC(which) true
|
||||
#define _C4_USE_MSB_INTRINSIC(which) true
|
||||
#endif
|
||||
|
||||
|
||||
/** @file memory_util.hpp Some memory utilities. */
|
||||
|
||||
// NOLINTBEGIN(google-runtime-int)
|
||||
|
||||
namespace c4 {
|
||||
|
||||
C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
|
||||
|
||||
/** set the given memory to zero */
|
||||
C4_ALWAYS_INLINE void mem_zero(void* mem, size_t num_bytes)
|
||||
{
|
||||
memset(mem, 0, num_bytes);
|
||||
}
|
||||
/** set the given memory to zero */
|
||||
template<class T>
|
||||
C4_ALWAYS_INLINE void mem_zero(T* mem, size_t num_elms)
|
||||
{
|
||||
memset(mem, 0, sizeof(T) * num_elms);
|
||||
}
|
||||
/** set the given memory to zero */
|
||||
template<class T>
|
||||
C4_ALWAYS_INLINE void mem_zero(T* mem)
|
||||
{
|
||||
memset(mem, 0, sizeof(T));
|
||||
}
|
||||
|
||||
C4_ALWAYS_INLINE C4_CONST bool mem_overlaps(void const* a, void const* b, size_t sza, size_t szb)
|
||||
{
|
||||
// thanks @timwynants
|
||||
return (((const char*)b + szb) > a && b < ((const char*)a+sza));
|
||||
}
|
||||
|
||||
void mem_repeat(void* dest, void const* pattern, size_t pattern_size, size_t num_times);
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template<class T>
|
||||
C4_ALWAYS_INLINE C4_CONST bool is_aligned(T *ptr, uintptr_t alignment=alignof(T))
|
||||
{
|
||||
return (uintptr_t(ptr) & (alignment - uintptr_t(1))) == uintptr_t(0);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
// least significant bit
|
||||
|
||||
/** @name msb Compute the least significant bit
|
||||
* @note the input value must be nonzero
|
||||
* @note the input type must be unsigned
|
||||
*/
|
||||
/** @{ */
|
||||
|
||||
// https://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightLinear
|
||||
#define _c4_lsb_fallback \
|
||||
unsigned c = 0; \
|
||||
v = (v ^ (v - 1)) >> 1; /* Set v's trailing 0s to 1s and zero rest */ \
|
||||
for(; v; ++c) \
|
||||
v >>= 1; \
|
||||
return (unsigned) c
|
||||
|
||||
// u8
|
||||
template<class I>
|
||||
C4_CONSTEXPR14
|
||||
auto lsb(I v) noexcept
|
||||
-> typename std::enable_if<sizeof(I) == 1u, unsigned>::type
|
||||
{
|
||||
C4_STATIC_ASSERT(std::is_unsigned<I>::value);
|
||||
C4_ASSERT(v != 0);
|
||||
#if _C4_USE_LSB_INTRINSIC(__builtin_ctz)
|
||||
// upcast to use the intrinsic, it's cheaper.
|
||||
#ifdef C4_MSVC
|
||||
#if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)
|
||||
unsigned long bit;
|
||||
_BitScanForward(&bit, (unsigned long)v);
|
||||
return bit;
|
||||
#else
|
||||
_c4_lsb_fallback;
|
||||
#endif
|
||||
#else
|
||||
return (unsigned)__builtin_ctz((unsigned)v);
|
||||
#endif
|
||||
#else
|
||||
_c4_lsb_fallback;
|
||||
#endif
|
||||
}
|
||||
|
||||
// u16
|
||||
template<class I>
|
||||
C4_CONSTEXPR14
|
||||
auto lsb(I v) noexcept
|
||||
-> typename std::enable_if<sizeof(I) == 2u, unsigned>::type
|
||||
{
|
||||
C4_STATIC_ASSERT(std::is_unsigned<I>::value);
|
||||
C4_ASSERT(v != 0);
|
||||
#if _C4_USE_LSB_INTRINSIC(__builtin_ctz)
|
||||
// upcast to use the intrinsic, it's cheaper.
|
||||
// Then remember that the upcast makes it to 31bits
|
||||
#ifdef C4_MSVC
|
||||
#if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)
|
||||
unsigned long bit;
|
||||
_BitScanForward(&bit, (unsigned long)v);
|
||||
return bit;
|
||||
#else
|
||||
_c4_lsb_fallback;
|
||||
#endif
|
||||
#else
|
||||
return (unsigned)__builtin_ctz((unsigned)v);
|
||||
#endif
|
||||
#else
|
||||
_c4_lsb_fallback;
|
||||
#endif
|
||||
}
|
||||
|
||||
// u32
|
||||
template<class I>
|
||||
C4_CONSTEXPR14
|
||||
auto lsb(I v) noexcept
|
||||
-> typename std::enable_if<sizeof(I) == 4u, unsigned>::type
|
||||
{
|
||||
C4_STATIC_ASSERT(std::is_unsigned<I>::value);
|
||||
C4_ASSERT(v != 0);
|
||||
#if _C4_USE_LSB_INTRINSIC(__builtin_ctz)
|
||||
#ifdef C4_MSVC
|
||||
#if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)
|
||||
unsigned long bit;
|
||||
_BitScanForward(&bit, v);
|
||||
return bit;
|
||||
#else
|
||||
_c4_lsb_fallback;
|
||||
#endif
|
||||
#else
|
||||
return (unsigned)__builtin_ctz((unsigned)v);
|
||||
#endif
|
||||
#else
|
||||
_c4_lsb_fallback;
|
||||
#endif
|
||||
}
|
||||
|
||||
// u64 in 64bits
|
||||
template<class I>
|
||||
C4_CONSTEXPR14
|
||||
auto lsb(I v) noexcept
|
||||
-> typename std::enable_if<sizeof(I) == 8u && sizeof(unsigned long) == 8u, unsigned>::type
|
||||
{
|
||||
C4_STATIC_ASSERT(std::is_unsigned<I>::value);
|
||||
C4_ASSERT(v != 0);
|
||||
#if _C4_USE_LSB_INTRINSIC(__builtin_ctzl)
|
||||
#if defined(C4_MSVC)
|
||||
#if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)
|
||||
unsigned long bit;
|
||||
_BitScanForward64(&bit, v);
|
||||
return bit;
|
||||
#else
|
||||
_c4_lsb_fallback;
|
||||
#endif
|
||||
#else
|
||||
return (unsigned)__builtin_ctzl((unsigned long)v);
|
||||
#endif
|
||||
#else
|
||||
_c4_lsb_fallback;
|
||||
#endif
|
||||
}
|
||||
|
||||
// u64 in 32bits
|
||||
template<class I>
|
||||
C4_CONSTEXPR14
|
||||
auto lsb(I v) noexcept
|
||||
-> typename std::enable_if<sizeof(I) == 8u && sizeof(unsigned long long) == 8u && sizeof(unsigned long) != sizeof(unsigned long long), unsigned>::type
|
||||
{
|
||||
C4_STATIC_ASSERT(std::is_unsigned<I>::value);
|
||||
C4_ASSERT(v != 0);
|
||||
#if _C4_USE_LSB_INTRINSIC(__builtin_ctzll)
|
||||
#if defined(C4_MSVC)
|
||||
#if !defined(C4_CPU_X86) && !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)
|
||||
unsigned long bit;
|
||||
_BitScanForward64(&bit, v);
|
||||
return bit;
|
||||
#else
|
||||
_c4_lsb_fallback;
|
||||
#endif
|
||||
#else
|
||||
return (unsigned)__builtin_ctzll((unsigned long long)v);
|
||||
#endif
|
||||
#else
|
||||
_c4_lsb_fallback;
|
||||
#endif
|
||||
}
|
||||
|
||||
#undef _c4_lsb_fallback
|
||||
|
||||
/** @} */
|
||||
|
||||
|
||||
namespace detail {
|
||||
template<class I, I val, unsigned num_bits, bool finished> struct _lsb11;
|
||||
template<class I, I val, unsigned num_bits>
|
||||
struct _lsb11<I, val, num_bits, false>
|
||||
{
|
||||
enum : unsigned { num = _lsb11<I, (val>>1), num_bits+I(1), (((val>>1)&I(1))!=I(0))>::num };
|
||||
};
|
||||
template<class I, I val, unsigned num_bits>
|
||||
struct _lsb11<I, val, num_bits, true>
|
||||
{
|
||||
enum : unsigned { num = num_bits };
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
|
||||
/** TMP version of lsb(); this needs to be implemented with template
|
||||
* meta-programming because C++11 cannot use a constexpr function with
|
||||
* local variables
|
||||
* @see lsb */
|
||||
template<class I, I number>
|
||||
struct lsb11
|
||||
{
|
||||
static_assert(number != 0, "lsb: number must be nonzero");
|
||||
enum : unsigned { value = detail::_lsb11<I, number, 0, ((number&I(1))!=I(0))>::num};
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
// most significant bit
|
||||
|
||||
|
||||
/** @name msb Compute the most significant bit
|
||||
* @note the input value must be nonzero
|
||||
* @note the input type must be unsigned
|
||||
*/
|
||||
/** @{ */
|
||||
|
||||
|
||||
#define _c4_msb8_fallback \
|
||||
unsigned n = 0; \
|
||||
if(v & I(0xf0)) v >>= 4, n |= I(4); \
|
||||
if(v & I(0x0c)) v >>= 2, n |= I(2); \
|
||||
if(v & I(0x02)) v >>= 1, n |= I(1); \
|
||||
return n
|
||||
|
||||
#define _c4_msb16_fallback \
|
||||
unsigned n = 0; \
|
||||
if(v & I(0xff00)) v >>= 8, n |= I(8); \
|
||||
if(v & I(0x00f0)) v >>= 4, n |= I(4); \
|
||||
if(v & I(0x000c)) v >>= 2, n |= I(2); \
|
||||
if(v & I(0x0002)) v >>= 1, n |= I(1); \
|
||||
return n
|
||||
|
||||
#define _c4_msb32_fallback \
|
||||
unsigned n = 0; \
|
||||
if(v & I(0xffff0000)) v >>= 16, n |= 16; \
|
||||
if(v & I(0x0000ff00)) v >>= 8, n |= 8; \
|
||||
if(v & I(0x000000f0)) v >>= 4, n |= 4; \
|
||||
if(v & I(0x0000000c)) v >>= 2, n |= 2; \
|
||||
if(v & I(0x00000002)) v >>= 1, n |= 1; \
|
||||
return n
|
||||
|
||||
#define _c4_msb64_fallback \
|
||||
unsigned n = 0; \
|
||||
if(v & I(0xffffffff00000000)) v >>= 32, n |= I(32); \
|
||||
if(v & I(0x00000000ffff0000)) v >>= 16, n |= I(16); \
|
||||
if(v & I(0x000000000000ff00)) v >>= 8, n |= I(8); \
|
||||
if(v & I(0x00000000000000f0)) v >>= 4, n |= I(4); \
|
||||
if(v & I(0x000000000000000c)) v >>= 2, n |= I(2); \
|
||||
if(v & I(0x0000000000000002)) v >>= 1, n |= I(1); \
|
||||
return n
|
||||
|
||||
|
||||
// u8
|
||||
template<class I>
|
||||
C4_CONSTEXPR14
|
||||
auto msb(I v) noexcept
|
||||
-> typename std::enable_if<sizeof(I) == 1u, unsigned>::type
|
||||
{
|
||||
C4_STATIC_ASSERT(std::is_unsigned<I>::value);
|
||||
C4_ASSERT(v != 0);
|
||||
#if _C4_USE_MSB_INTRINSIC(__builtin_clz)
|
||||
// upcast to use the intrinsic, it's cheaper.
|
||||
// Then remember that the upcast makes it to 31bits
|
||||
#ifdef C4_MSVC
|
||||
#if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)
|
||||
unsigned long bit;
|
||||
_BitScanReverse(&bit, (unsigned long)v);
|
||||
return bit;
|
||||
#else
|
||||
_c4_msb8_fallback;
|
||||
#endif
|
||||
#else
|
||||
return 31u - (unsigned)__builtin_clz((unsigned)v);
|
||||
#endif
|
||||
#else
|
||||
_c4_msb8_fallback;
|
||||
#endif
|
||||
}
|
||||
|
||||
// u16
|
||||
template<class I>
|
||||
C4_CONSTEXPR14
|
||||
auto msb(I v) noexcept
|
||||
-> typename std::enable_if<sizeof(I) == 2u, unsigned>::type
|
||||
{
|
||||
C4_STATIC_ASSERT(std::is_unsigned<I>::value);
|
||||
C4_ASSERT(v != 0);
|
||||
#if _C4_USE_MSB_INTRINSIC(__builtin_clz)
|
||||
// upcast to use the intrinsic, it's cheaper.
|
||||
// Then remember that the upcast makes it to 31bits
|
||||
#ifdef C4_MSVC
|
||||
#if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)
|
||||
unsigned long bit;
|
||||
_BitScanReverse(&bit, (unsigned long)v);
|
||||
return bit;
|
||||
#else
|
||||
_c4_msb16_fallback;
|
||||
#endif
|
||||
#else
|
||||
return 31u - (unsigned)__builtin_clz((unsigned)v);
|
||||
#endif
|
||||
#else
|
||||
_c4_msb16_fallback;
|
||||
#endif
|
||||
}
|
||||
|
||||
// u32
|
||||
template<class I>
|
||||
C4_CONSTEXPR14
|
||||
auto msb(I v) noexcept
|
||||
-> typename std::enable_if<sizeof(I) == 4u, unsigned>::type
|
||||
{
|
||||
C4_STATIC_ASSERT(std::is_unsigned<I>::value);
|
||||
C4_ASSERT(v != 0);
|
||||
#if _C4_USE_MSB_INTRINSIC(__builtin_clz)
|
||||
#ifdef C4_MSVC
|
||||
#if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)
|
||||
unsigned long bit;
|
||||
_BitScanReverse(&bit, v);
|
||||
return bit;
|
||||
#else
|
||||
_c4_msb32_fallback;
|
||||
#endif
|
||||
#else
|
||||
return 31u - (unsigned)__builtin_clz((unsigned)v);
|
||||
#endif
|
||||
#else
|
||||
_c4_msb32_fallback;
|
||||
#endif
|
||||
}
|
||||
|
||||
// u64 in 64bits
|
||||
template<class I>
|
||||
C4_CONSTEXPR14
|
||||
auto msb(I v) noexcept
|
||||
-> typename std::enable_if<sizeof(I) == 8u && sizeof(unsigned long) == 8u, unsigned>::type
|
||||
{
|
||||
C4_STATIC_ASSERT(std::is_unsigned<I>::value);
|
||||
C4_ASSERT(v != 0);
|
||||
#if _C4_USE_MSB_INTRINSIC(__builtin_clzl)
|
||||
#ifdef C4_MSVC
|
||||
#if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)
|
||||
unsigned long bit;
|
||||
_BitScanReverse64(&bit, v);
|
||||
return bit;
|
||||
#else
|
||||
_c4_msb64_fallback;
|
||||
#endif
|
||||
#else
|
||||
return 63u - (unsigned)__builtin_clzl((unsigned long)v);
|
||||
#endif
|
||||
#else
|
||||
_c4_msb64_fallback;
|
||||
#endif
|
||||
}
|
||||
|
||||
// u64 in 32bits
|
||||
template<class I>
|
||||
C4_CONSTEXPR14
|
||||
auto msb(I v) noexcept
|
||||
-> typename std::enable_if<sizeof(I) == 8u && sizeof(unsigned long long) == 8u && sizeof(unsigned long) != sizeof(unsigned long long), unsigned>::type
|
||||
{
|
||||
C4_STATIC_ASSERT(std::is_unsigned<I>::value);
|
||||
C4_ASSERT(v != 0);
|
||||
#if _C4_USE_MSB_INTRINSIC(__builtin_clzll)
|
||||
#ifdef C4_MSVC
|
||||
#if !defined(C4_CPU_X86) && !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM)
|
||||
unsigned long bit;
|
||||
_BitScanReverse64(&bit, v);
|
||||
return bit;
|
||||
#else
|
||||
_c4_msb64_fallback;
|
||||
#endif
|
||||
#else
|
||||
return 63u - (unsigned)__builtin_clzll((unsigned long long)v);
|
||||
#endif
|
||||
#else
|
||||
_c4_msb64_fallback;
|
||||
#endif
|
||||
}
|
||||
|
||||
#undef _c4_msb8_fallback
|
||||
#undef _c4_msb16_fallback
|
||||
#undef _c4_msb32_fallback
|
||||
#undef _c4_msb64_fallback
|
||||
|
||||
/** @} */
|
||||
|
||||
|
||||
namespace detail {
|
||||
template<class I, I val, I num_bits, bool finished> struct _msb11;
|
||||
template<class I, I val, I num_bits>
|
||||
struct _msb11< I, val, num_bits, false>
|
||||
{
|
||||
enum : unsigned { num = _msb11<I, (val>>1), num_bits+I(1), ((val>>1)==I(0))>::num };
|
||||
};
|
||||
template<class I, I val, I num_bits>
|
||||
struct _msb11<I, val, num_bits, true>
|
||||
{
|
||||
static_assert(val == 0, "bad implementation");
|
||||
enum : unsigned { num = (unsigned)(num_bits-1) };
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
|
||||
/** TMP version of msb(); this needs to be implemented with template
|
||||
* meta-programming because C++11 cannot use a constexpr function with
|
||||
* local variables
|
||||
* @see msb */
|
||||
template<class I, I number>
|
||||
struct msb11
|
||||
{
|
||||
enum : unsigned { value = detail::_msb11<I, number, 0, (number==I(0))>::num };
|
||||
};
|
||||
|
||||
|
||||
|
||||
#undef _C4_USE_LSB_INTRINSIC
|
||||
#undef _C4_USE_MSB_INTRINSIC
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// there is an implicit conversion below; it happens when E or B are
|
||||
// narrower than int, and thus any operation will upcast the result to
|
||||
// int, and then downcast to assign
|
||||
C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wconversion")
|
||||
|
||||
/** integer power; this function is constexpr-14 because of the local
|
||||
* variables */
|
||||
template<class B, class E>
|
||||
C4_CONSTEXPR14 C4_CONST auto ipow(B base, E exponent) noexcept -> typename std::enable_if<std::is_signed<E>::value, B>::type
|
||||
{
|
||||
C4_STATIC_ASSERT(std::is_integral<E>::value);
|
||||
B r = B(1);
|
||||
if(exponent >= 0)
|
||||
{
|
||||
for(E e = 0; e < exponent; ++e)
|
||||
r *= base;
|
||||
}
|
||||
else
|
||||
{
|
||||
exponent *= E(-1);
|
||||
for(E e = 0; e < exponent; ++e)
|
||||
r /= base;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/** integer power; this function is constexpr-14 because of the local
|
||||
* variables */
|
||||
template<class B, B base, class E>
|
||||
C4_CONSTEXPR14 C4_CONST auto ipow(E exponent) noexcept -> typename std::enable_if<std::is_signed<E>::value, B>::type
|
||||
{
|
||||
C4_STATIC_ASSERT(std::is_integral<E>::value);
|
||||
B r = B(1);
|
||||
if(exponent >= 0)
|
||||
{
|
||||
for(E e = 0; e < exponent; ++e)
|
||||
r *= base;
|
||||
}
|
||||
else
|
||||
{
|
||||
exponent *= E(-1);
|
||||
for(E e = 0; e < exponent; ++e)
|
||||
r /= base;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/** integer power; this function is constexpr-14 because of the local
|
||||
* variables */
|
||||
template<class B, class Base, Base base, class E>
|
||||
C4_CONSTEXPR14 C4_CONST auto ipow(E exponent) noexcept -> typename std::enable_if<std::is_signed<E>::value, B>::type
|
||||
{
|
||||
C4_STATIC_ASSERT(std::is_integral<E>::value);
|
||||
B r = B(1);
|
||||
B bbase = B(base);
|
||||
if(exponent >= 0)
|
||||
{
|
||||
for(E e = 0; e < exponent; ++e)
|
||||
r *= bbase;
|
||||
}
|
||||
else
|
||||
{
|
||||
exponent *= E(-1);
|
||||
for(E e = 0; e < exponent; ++e)
|
||||
r /= bbase;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/** integer power; this function is constexpr-14 because of the local
|
||||
* variables */
|
||||
template<class B, class E>
|
||||
C4_CONSTEXPR14 C4_CONST auto ipow(B base, E exponent) noexcept -> typename std::enable_if<!std::is_signed<E>::value, B>::type
|
||||
{
|
||||
C4_STATIC_ASSERT(std::is_integral<E>::value);
|
||||
B r = B(1);
|
||||
for(E e = 0; e < exponent; ++e)
|
||||
r *= base;
|
||||
return r;
|
||||
}
|
||||
|
||||
/** integer power; this function is constexpr-14 because of the local
|
||||
* variables */
|
||||
template<class B, B base, class E>
|
||||
C4_CONSTEXPR14 C4_CONST auto ipow(E exponent) noexcept -> typename std::enable_if<!std::is_signed<E>::value, B>::type
|
||||
{
|
||||
C4_STATIC_ASSERT(std::is_integral<E>::value);
|
||||
B r = B(1);
|
||||
for(E e = 0; e < exponent; ++e)
|
||||
r *= base;
|
||||
return r;
|
||||
}
|
||||
/** integer power; this function is constexpr-14 because of the local
|
||||
* variables */
|
||||
template<class B, class Base, Base base, class E>
|
||||
C4_CONSTEXPR14 C4_CONST auto ipow(E exponent) noexcept -> typename std::enable_if<!std::is_signed<E>::value, B>::type
|
||||
{
|
||||
C4_STATIC_ASSERT(std::is_integral<E>::value);
|
||||
B r = B(1);
|
||||
B bbase = B(base);
|
||||
for(E e = 0; e < exponent; ++e)
|
||||
r *= bbase;
|
||||
return r;
|
||||
}
|
||||
|
||||
C4_SUPPRESS_WARNING_GCC_CLANG_POP
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/** return a mask with all bits set [first_bit,last_bit[; this function
|
||||
* is constexpr-14 because of the local variables */
|
||||
template<class I>
|
||||
C4_CONSTEXPR14 I contiguous_mask(I first_bit, I last_bit)
|
||||
{
|
||||
I r = 0;
|
||||
for(I i = first_bit; i < last_bit; ++i)
|
||||
{
|
||||
r |= (I(1) << i);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class I, I val, I first, I last, bool finished>
|
||||
struct _ctgmsk11;
|
||||
|
||||
template<class I, I val, I first, I last>
|
||||
struct _ctgmsk11< I, val, first, last, true>
|
||||
{
|
||||
enum : I { value = _ctgmsk11<I, val|(I(1)<<first), first+I(1), last, (first+1!=last)>::value };
|
||||
};
|
||||
|
||||
template<class I, I val, I first, I last>
|
||||
struct _ctgmsk11< I, val, first, last, false>
|
||||
{
|
||||
enum : I { value = val };
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
|
||||
/** TMP version of contiguous_mask(); this needs to be implemented with template
|
||||
* meta-programming because C++11 cannot use a constexpr function with
|
||||
* local variables
|
||||
* @see contiguous_mask */
|
||||
template<class I, I first_bit, I last_bit>
|
||||
struct contiguous_mask11
|
||||
{
|
||||
enum : I { value = detail::_ctgmsk11<I, I(0), first_bit, last_bit, (first_bit!=last_bit)>::value };
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
/** use Empty Base Class Optimization to reduce the size of a pair of
|
||||
* potentially empty types*/
|
||||
|
||||
namespace detail {
|
||||
typedef enum {
|
||||
tpc_same,
|
||||
tpc_same_empty,
|
||||
tpc_both_empty,
|
||||
tpc_first_empty,
|
||||
tpc_second_empty,
|
||||
tpc_general
|
||||
} TightPairCase_e;
|
||||
|
||||
template<class First, class Second>
|
||||
constexpr TightPairCase_e tpc_which_case()
|
||||
{
|
||||
return std::is_same<First, Second>::value ?
|
||||
std::is_empty<First>::value ?
|
||||
tpc_same_empty
|
||||
:
|
||||
tpc_same
|
||||
:
|
||||
std::is_empty<First>::value && std::is_empty<Second>::value ?
|
||||
tpc_both_empty
|
||||
:
|
||||
std::is_empty<First>::value ?
|
||||
tpc_first_empty
|
||||
:
|
||||
std::is_empty<Second>::value ?
|
||||
tpc_second_empty
|
||||
:
|
||||
tpc_general
|
||||
;
|
||||
}
|
||||
|
||||
template<class First, class Second, TightPairCase_e Case>
|
||||
struct tight_pair
|
||||
{
|
||||
private:
|
||||
|
||||
First m_first;
|
||||
Second m_second;
|
||||
|
||||
public:
|
||||
|
||||
using first_type = First;
|
||||
using second_type = Second;
|
||||
|
||||
tight_pair() : m_first(), m_second() {}
|
||||
tight_pair(First const& f, Second const& s) : m_first(f), m_second(s) {}
|
||||
|
||||
C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return m_first; }
|
||||
C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return m_first; }
|
||||
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return m_second; }
|
||||
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return m_second; }
|
||||
};
|
||||
|
||||
template<class First, class Second>
|
||||
struct tight_pair<First, Second, tpc_same_empty> : public First
|
||||
{
|
||||
static_assert(std::is_same<First, Second>::value, "bad implementation");
|
||||
|
||||
using first_type = First;
|
||||
using second_type = Second;
|
||||
|
||||
tight_pair() : First() {}
|
||||
tight_pair(First const& f, Second const& /*s*/) : First(f) {}
|
||||
|
||||
C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return static_cast<First &>(*this); }
|
||||
C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return static_cast<First const&>(*this); }
|
||||
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return reinterpret_cast<Second &>(*this); } // NOLINT
|
||||
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return reinterpret_cast<Second const&>(*this); } // NOLINT
|
||||
};
|
||||
|
||||
template<class First, class Second>
|
||||
struct tight_pair<First, Second, tpc_both_empty> : public First, public Second
|
||||
{
|
||||
using first_type = First;
|
||||
using second_type = Second;
|
||||
|
||||
tight_pair() : First(), Second() {}
|
||||
tight_pair(First const& f, Second const& s) : First(f), Second(s) {}
|
||||
|
||||
C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return static_cast<First &>(*this); }
|
||||
C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return static_cast<First const&>(*this); }
|
||||
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return static_cast<Second &>(*this); }
|
||||
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return static_cast<Second const&>(*this); }
|
||||
};
|
||||
|
||||
template<class First, class Second>
|
||||
struct tight_pair<First, Second, tpc_same> : public First
|
||||
{
|
||||
Second m_second;
|
||||
|
||||
using first_type = First;
|
||||
using second_type = Second;
|
||||
|
||||
tight_pair() : First() {}
|
||||
tight_pair(First const& f, Second const& s) : First(f), m_second(s) {}
|
||||
|
||||
C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return static_cast<First &>(*this); }
|
||||
C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return static_cast<First const&>(*this); }
|
||||
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return m_second; }
|
||||
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return m_second; }
|
||||
};
|
||||
|
||||
template<class First, class Second>
|
||||
struct tight_pair<First, Second, tpc_first_empty> : public First
|
||||
{
|
||||
Second m_second;
|
||||
|
||||
using first_type = First;
|
||||
using second_type = Second;
|
||||
|
||||
tight_pair() : First(), m_second() {}
|
||||
tight_pair(First const& f, Second const& s) : First(f), m_second(s) {}
|
||||
|
||||
C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return static_cast<First &>(*this); }
|
||||
C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return static_cast<First const&>(*this); }
|
||||
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return m_second; }
|
||||
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return m_second; }
|
||||
};
|
||||
|
||||
template<class First, class Second>
|
||||
struct tight_pair<First, Second, tpc_second_empty> : public Second
|
||||
{
|
||||
First m_first;
|
||||
|
||||
using first_type = First;
|
||||
using second_type = Second;
|
||||
|
||||
tight_pair() : Second(), m_first() {}
|
||||
tight_pair(First const& f, Second const& s) : Second(s), m_first(f) {}
|
||||
|
||||
C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return m_first; }
|
||||
C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return m_first; }
|
||||
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return static_cast<Second &>(*this); }
|
||||
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return static_cast<Second const&>(*this); }
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<class First, class Second>
|
||||
using tight_pair = detail::tight_pair<First, Second, detail::tpc_which_case<First,Second>()>;
|
||||
|
||||
C4_SUPPRESS_WARNING_GCC_CLANG_POP
|
||||
|
||||
} // namespace c4
|
||||
|
||||
// NOLINTEND(google-runtime-int)
|
||||
|
||||
#endif /* _C4_MEMORY_UTIL_HPP_ */
|
||||
46
ext/c4core.src/c4/platform.hpp
Normal file
46
ext/c4core.src/c4/platform.hpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#ifndef _C4_PLATFORM_HPP_
|
||||
#define _C4_PLATFORM_HPP_
|
||||
|
||||
/** @file platform.hpp Provides platform information macros
|
||||
* @ingroup basic_headers */
|
||||
|
||||
// see also https://sourceforge.net/p/predef/wiki/OperatingSystems/
|
||||
|
||||
#if defined(_WIN64)
|
||||
# define C4_WIN
|
||||
# define C4_WIN64
|
||||
#elif defined(_WIN32)
|
||||
# define C4_WIN
|
||||
# define C4_WIN32
|
||||
#elif defined(__ANDROID__)
|
||||
# define C4_ANDROID
|
||||
#elif defined(__APPLE__)
|
||||
# include "TargetConditionals.h"
|
||||
# if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
|
||||
# define C4_IOS
|
||||
# elif TARGET_OS_MAC || TARGET_OS_OSX
|
||||
# define C4_MACOS
|
||||
# else
|
||||
# error "Unknown Apple platform"
|
||||
# endif
|
||||
#elif defined(__linux__) || defined(__linux)
|
||||
# define C4_UNIX
|
||||
# define C4_LINUX
|
||||
#elif defined(__unix__) || defined(__unix)
|
||||
# define C4_UNIX
|
||||
#elif defined(__arm__) || defined(__aarch64__)
|
||||
# define C4_ARM
|
||||
#elif defined(__xtensa__) || defined(__XTENSA__)
|
||||
# define C4_XTENSA
|
||||
#elif defined(SWIG)
|
||||
# define C4_SWIG
|
||||
#else
|
||||
# error "unknown platform"
|
||||
#endif
|
||||
|
||||
#if defined(__posix) || defined(C4_UNIX) || defined(C4_LINUX)
|
||||
# define C4_POSIX
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* _C4_PLATFORM_HPP_ */
|
||||
123
ext/c4core.src/c4/preprocessor.hpp
Normal file
123
ext/c4core.src/c4/preprocessor.hpp
Normal file
@@ -0,0 +1,123 @@
|
||||
#ifndef _C4_PREPROCESSOR_HPP_
|
||||
#define _C4_PREPROCESSOR_HPP_
|
||||
|
||||
/** @file preprocessor.hpp Contains basic macros and preprocessor utilities.
|
||||
* @ingroup basic_headers */
|
||||
|
||||
#ifdef __clang__
|
||||
/* NOTE: using , ## __VA_ARGS__ to deal with zero-args calls to
|
||||
* variadic macros is not portable, but works in clang, gcc, msvc, icc.
|
||||
* clang requires switching off compiler warnings for pedantic mode.
|
||||
* @see http://stackoverflow.com/questions/32047685/variadic-macro-without-arguments */
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" // warning: token pasting of ',' and __VA_ARGS__ is a GNU extension
|
||||
#elif defined(__GNUC__)
|
||||
/* GCC also issues a warning for zero-args calls to variadic macros.
|
||||
* This warning is switched on with -pedantic and apparently there is no
|
||||
* easy way to turn it off as with clang. But marking this as a system
|
||||
* header works.
|
||||
* @see https://gcc.gnu.org/onlinedocs/cpp/System-Headers.html
|
||||
* @see http://stackoverflow.com/questions/35587137/ */
|
||||
# pragma GCC system_header
|
||||
#endif
|
||||
|
||||
#define C4_WIDEN(str) L"" str
|
||||
|
||||
#define C4_COUNTOF(arr) (sizeof(arr)/sizeof((arr)[0]))
|
||||
|
||||
#define C4_EXPAND(arg) arg
|
||||
|
||||
/** useful in some macro calls with template arguments */
|
||||
#define C4_COMMA ,
|
||||
/** useful in some macro calls with template arguments
|
||||
* @see C4_COMMA */
|
||||
#define C4_COMMA_X C4_COMMA
|
||||
|
||||
/** expand and quote */
|
||||
#define C4_XQUOTE(arg) _C4_XQUOTE(arg)
|
||||
#define _C4_XQUOTE(arg) C4_QUOTE(arg)
|
||||
#define C4_QUOTE(arg) #arg
|
||||
|
||||
/** expand and concatenate */
|
||||
#define C4_XCAT(arg1, arg2) _C4_XCAT(arg1, arg2)
|
||||
#define _C4_XCAT(arg1, arg2) C4_CAT(arg1, arg2)
|
||||
#define C4_CAT(arg1, arg2) arg1##arg2
|
||||
|
||||
#define C4_VERSION_CAT(major, minor, patch) ((major)*10000 + (minor)*100 + (patch))
|
||||
|
||||
/** A preprocessor foreach. Spectacular trick taken from:
|
||||
* http://stackoverflow.com/a/1872506/5875572
|
||||
* The first argument is for a macro receiving a single argument,
|
||||
* which will be called with every subsequent argument. There is
|
||||
* currently a limit of 32 arguments, and at least 1 must be provided.
|
||||
*
|
||||
Example:
|
||||
@code{.cpp}
|
||||
struct Example {
|
||||
int a;
|
||||
int b;
|
||||
int c;
|
||||
};
|
||||
// define a one-arg macro to be called
|
||||
#define PRN_STRUCT_OFFSETS(field) PRN_STRUCT_OFFSETS_(Example, field)
|
||||
#define PRN_STRUCT_OFFSETS_(structure, field) printf(C4_XQUOTE(structure) ":" C4_XQUOTE(field)" - offset=%zu\n", offsetof(structure, field));
|
||||
|
||||
// now call the macro for a, b and c
|
||||
C4_FOR_EACH(PRN_STRUCT_OFFSETS, a, b, c);
|
||||
@endcode */
|
||||
#define C4_FOR_EACH(what, ...) C4_FOR_EACH_SEP(what, ;, __VA_ARGS__)
|
||||
|
||||
/** same as C4_FOR_EACH(), but use a custom separator between statements.
|
||||
* If a comma is needed as the separator, use the C4_COMMA macro.
|
||||
* @see C4_FOR_EACH
|
||||
* @see C4_COMMA
|
||||
*/
|
||||
#define C4_FOR_EACH_SEP(what, sep, ...) _C4_FOR_EACH_(_C4_FOR_EACH_NARG(__VA_ARGS__), what, sep, __VA_ARGS__)
|
||||
|
||||
/// @cond dev
|
||||
|
||||
#define _C4_FOR_EACH_01(what, sep, x) what(x) sep
|
||||
#define _C4_FOR_EACH_02(what, sep, x, ...) what(x) sep _C4_FOR_EACH_01(what, sep, __VA_ARGS__)
|
||||
#define _C4_FOR_EACH_03(what, sep, x, ...) what(x) sep _C4_FOR_EACH_02(what, sep, __VA_ARGS__)
|
||||
#define _C4_FOR_EACH_04(what, sep, x, ...) what(x) sep _C4_FOR_EACH_03(what, sep, __VA_ARGS__)
|
||||
#define _C4_FOR_EACH_05(what, sep, x, ...) what(x) sep _C4_FOR_EACH_04(what, sep, __VA_ARGS__)
|
||||
#define _C4_FOR_EACH_06(what, sep, x, ...) what(x) sep _C4_FOR_EACH_05(what, sep, __VA_ARGS__)
|
||||
#define _C4_FOR_EACH_07(what, sep, x, ...) what(x) sep _C4_FOR_EACH_06(what, sep, __VA_ARGS__)
|
||||
#define _C4_FOR_EACH_08(what, sep, x, ...) what(x) sep _C4_FOR_EACH_07(what, sep, __VA_ARGS__)
|
||||
#define _C4_FOR_EACH_09(what, sep, x, ...) what(x) sep _C4_FOR_EACH_08(what, sep, __VA_ARGS__)
|
||||
#define _C4_FOR_EACH_10(what, sep, x, ...) what(x) sep _C4_FOR_EACH_09(what, sep, __VA_ARGS__)
|
||||
#define _C4_FOR_EACH_11(what, sep, x, ...) what(x) sep _C4_FOR_EACH_10(what, sep, __VA_ARGS__)
|
||||
#define _C4_FOR_EACH_12(what, sep, x, ...) what(x) sep _C4_FOR_EACH_11(what, sep, __VA_ARGS__)
|
||||
#define _C4_FOR_EACH_13(what, sep, x, ...) what(x) sep _C4_FOR_EACH_12(what, sep, __VA_ARGS__)
|
||||
#define _C4_FOR_EACH_14(what, sep, x, ...) what(x) sep _C4_FOR_EACH_13(what, sep, __VA_ARGS__)
|
||||
#define _C4_FOR_EACH_15(what, sep, x, ...) what(x) sep _C4_FOR_EACH_14(what, sep, __VA_ARGS__)
|
||||
#define _C4_FOR_EACH_16(what, sep, x, ...) what(x) sep _C4_FOR_EACH_15(what, sep, __VA_ARGS__)
|
||||
#define _C4_FOR_EACH_17(what, sep, x, ...) what(x) sep _C4_FOR_EACH_16(what, sep, __VA_ARGS__)
|
||||
#define _C4_FOR_EACH_18(what, sep, x, ...) what(x) sep _C4_FOR_EACH_17(what, sep, __VA_ARGS__)
|
||||
#define _C4_FOR_EACH_19(what, sep, x, ...) what(x) sep _C4_FOR_EACH_18(what, sep, __VA_ARGS__)
|
||||
#define _C4_FOR_EACH_20(what, sep, x, ...) what(x) sep _C4_FOR_EACH_19(what, sep, __VA_ARGS__)
|
||||
#define _C4_FOR_EACH_21(what, sep, x, ...) what(x) sep _C4_FOR_EACH_20(what, sep, __VA_ARGS__)
|
||||
#define _C4_FOR_EACH_22(what, sep, x, ...) what(x) sep _C4_FOR_EACH_21(what, sep, __VA_ARGS__)
|
||||
#define _C4_FOR_EACH_23(what, sep, x, ...) what(x) sep _C4_FOR_EACH_22(what, sep, __VA_ARGS__)
|
||||
#define _C4_FOR_EACH_24(what, sep, x, ...) what(x) sep _C4_FOR_EACH_23(what, sep, __VA_ARGS__)
|
||||
#define _C4_FOR_EACH_25(what, sep, x, ...) what(x) sep _C4_FOR_EACH_24(what, sep, __VA_ARGS__)
|
||||
#define _C4_FOR_EACH_26(what, sep, x, ...) what(x) sep _C4_FOR_EACH_25(what, sep, __VA_ARGS__)
|
||||
#define _C4_FOR_EACH_27(what, sep, x, ...) what(x) sep _C4_FOR_EACH_26(what, sep, __VA_ARGS__)
|
||||
#define _C4_FOR_EACH_28(what, sep, x, ...) what(x) sep _C4_FOR_EACH_27(what, sep, __VA_ARGS__)
|
||||
#define _C4_FOR_EACH_29(what, sep, x, ...) what(x) sep _C4_FOR_EACH_28(what, sep, __VA_ARGS__)
|
||||
#define _C4_FOR_EACH_30(what, sep, x, ...) what(x) sep _C4_FOR_EACH_29(what, sep, __VA_ARGS__)
|
||||
#define _C4_FOR_EACH_31(what, sep, x, ...) what(x) sep _C4_FOR_EACH_30(what, sep, __VA_ARGS__)
|
||||
#define _C4_FOR_EACH_32(what, sep, x, ...) what(x) sep _C4_FOR_EACH_31(what, sep, __VA_ARGS__)
|
||||
#define _C4_FOR_EACH_NARG(...) _C4_FOR_EACH_NARG_(__VA_ARGS__, _C4_FOR_EACH_RSEQ_N())
|
||||
#define _C4_FOR_EACH_NARG_(...) _C4_FOR_EACH_ARG_N(__VA_ARGS__)
|
||||
#define _C4_FOR_EACH_ARG_N(_01, _02, _03, _04, _05, _06, _07, _08, _09, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, N, ...) N
|
||||
#define _C4_FOR_EACH_RSEQ_N() 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 09, 08, 07, 06, 05, 04, 03, 02, 01
|
||||
#define _C4_FOR_EACH_(N, what, sep, ...) C4_XCAT(_C4_FOR_EACH_, N)(what, sep, __VA_ARGS__)
|
||||
|
||||
/// @endcond
|
||||
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif /* _C4_PREPROCESSOR_HPP_ */
|
||||
11
ext/c4core.src/c4/std/std.hpp
Normal file
11
ext/c4core.src/c4/std/std.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef _C4_STD_STD_HPP_
|
||||
#define _C4_STD_STD_HPP_
|
||||
|
||||
/** @file std.hpp includes all c4-std interop files */
|
||||
|
||||
#include "c4/std/vector.hpp"
|
||||
#include "c4/std/string.hpp"
|
||||
#include "c4/std/string_view.hpp"
|
||||
#include "c4/std/tuple.hpp"
|
||||
|
||||
#endif // _C4_STD_STD_HPP_
|
||||
10
ext/c4core.src/c4/std/std_fwd.hpp
Normal file
10
ext/c4core.src/c4/std/std_fwd.hpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef _C4_STD_STD_FWD_HPP_
|
||||
#define _C4_STD_STD_FWD_HPP_
|
||||
|
||||
/** @file std_fwd.hpp includes all c4-std interop fwd files */
|
||||
|
||||
#include "c4/std/vector_fwd.hpp"
|
||||
#include "c4/std/string_fwd.hpp"
|
||||
//#include "c4/std/tuple_fwd.hpp"
|
||||
|
||||
#endif // _C4_STD_STD_FWD_HPP_
|
||||
97
ext/c4core.src/c4/std/string.hpp
Normal file
97
ext/c4core.src/c4/std/string.hpp
Normal file
@@ -0,0 +1,97 @@
|
||||
#ifndef _C4_STD_STRING_HPP_
|
||||
#define _C4_STD_STRING_HPP_
|
||||
|
||||
/** @file string.hpp */
|
||||
|
||||
#ifndef C4CORE_SINGLE_HEADER
|
||||
#include "c4/substr.hpp"
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace c4 {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/** get a writeable view to an existing std::string.
|
||||
* When the string is empty, the returned view will be pointing
|
||||
* at the character with value '\0', but the size will be zero.
|
||||
* @see https://en.cppreference.com/w/cpp/string/basic_string/operator_at
|
||||
*/
|
||||
C4_ALWAYS_INLINE c4::substr to_substr(std::string &s) noexcept
|
||||
{
|
||||
#if C4_CPP < 11
|
||||
#error this function will have undefined behavior
|
||||
#endif
|
||||
// since c++11 it is legal to call s[s.size()].
|
||||
return c4::substr(&s[0], s.size()); // NOLINT(readability-container-data-pointer)
|
||||
}
|
||||
|
||||
/** get a readonly view to an existing std::string.
|
||||
* When the string is empty, the returned view will be pointing
|
||||
* at the character with value '\0', but the size will be zero.
|
||||
* @see https://en.cppreference.com/w/cpp/string/basic_string/operator_at
|
||||
*/
|
||||
C4_ALWAYS_INLINE c4::csubstr to_csubstr(std::string const& s) noexcept
|
||||
{
|
||||
#if C4_CPP < 11
|
||||
#error this function will have undefined behavior
|
||||
#endif
|
||||
// since c++11 it is legal to call s[s.size()].
|
||||
return c4::csubstr(&s[0], s.size()); // NOLINT(readability-container-data-pointer)
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
C4_ALWAYS_INLINE bool operator== (c4::csubstr ss, std::string const& s) { return ss.compare(to_csubstr(s)) == 0; }
|
||||
C4_ALWAYS_INLINE bool operator!= (c4::csubstr ss, std::string const& s) { return ss.compare(to_csubstr(s)) != 0; }
|
||||
C4_ALWAYS_INLINE bool operator>= (c4::csubstr ss, std::string const& s) { return ss.compare(to_csubstr(s)) >= 0; }
|
||||
C4_ALWAYS_INLINE bool operator> (c4::csubstr ss, std::string const& s) { return ss.compare(to_csubstr(s)) > 0; }
|
||||
C4_ALWAYS_INLINE bool operator<= (c4::csubstr ss, std::string const& s) { return ss.compare(to_csubstr(s)) <= 0; }
|
||||
C4_ALWAYS_INLINE bool operator< (c4::csubstr ss, std::string const& s) { return ss.compare(to_csubstr(s)) < 0; }
|
||||
|
||||
C4_ALWAYS_INLINE bool operator== (std::string const& s, c4::csubstr ss) { return ss.compare(to_csubstr(s)) == 0; }
|
||||
C4_ALWAYS_INLINE bool operator!= (std::string const& s, c4::csubstr ss) { return ss.compare(to_csubstr(s)) != 0; }
|
||||
C4_ALWAYS_INLINE bool operator>= (std::string const& s, c4::csubstr ss) { return ss.compare(to_csubstr(s)) <= 0; }
|
||||
C4_ALWAYS_INLINE bool operator> (std::string const& s, c4::csubstr ss) { return ss.compare(to_csubstr(s)) < 0; }
|
||||
C4_ALWAYS_INLINE bool operator<= (std::string const& s, c4::csubstr ss) { return ss.compare(to_csubstr(s)) >= 0; }
|
||||
C4_ALWAYS_INLINE bool operator< (std::string const& s, c4::csubstr ss) { return ss.compare(to_csubstr(s)) > 0; }
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/** copy an std::string to a writeable string view */
|
||||
inline size_t to_chars(c4::substr buf, std::string const& s)
|
||||
{
|
||||
C4_ASSERT(!buf.overlaps(to_csubstr(s)));
|
||||
size_t len = buf.len < s.size() ? buf.len : s.size();
|
||||
// calling memcpy with null strings is undefined behavior
|
||||
// and will wreak havoc in calling code's branches.
|
||||
// see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637
|
||||
if(len)
|
||||
{
|
||||
C4_ASSERT(s.data() != nullptr);
|
||||
C4_ASSERT(buf.str != nullptr);
|
||||
memcpy(buf.str, s.data(), len);
|
||||
}
|
||||
return s.size(); // return the number of needed chars
|
||||
}
|
||||
|
||||
/** copy a string view to an existing std::string */
|
||||
inline bool from_chars(c4::csubstr buf, std::string * s)
|
||||
{
|
||||
s->resize(buf.len);
|
||||
C4_ASSERT(!buf.overlaps(to_csubstr(*s)));
|
||||
// calling memcpy with null strings is undefined behavior
|
||||
// and will wreak havoc in calling code's branches.
|
||||
// see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637
|
||||
if(buf.len)
|
||||
{
|
||||
C4_ASSERT(buf.str != nullptr);
|
||||
memcpy(&(*s)[0], buf.str, buf.len); // NOLINT(readability-container-data-pointer)
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace c4
|
||||
|
||||
#endif // _C4_STD_STRING_HPP_
|
||||
59
ext/c4core.src/c4/std/string_fwd.hpp
Normal file
59
ext/c4core.src/c4/std/string_fwd.hpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#ifndef _C4_STD_STRING_FWD_HPP_
|
||||
#define _C4_STD_STRING_FWD_HPP_
|
||||
|
||||
/** @file string_fwd.hpp */
|
||||
|
||||
#ifndef DOXYGEN
|
||||
|
||||
#ifndef C4CORE_SINGLE_HEADER
|
||||
#include "c4/substr_fwd.hpp"
|
||||
#endif
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
// forward declarations for std::string
|
||||
#if defined(__GLIBCXX__) || defined(__GLIBCPP__)
|
||||
#include <bits/stringfwd.h> // use the fwd header in glibcxx
|
||||
#elif defined(_LIBCPP_VERSION) || defined(__APPLE_CC__)
|
||||
#include <iosfwd> // use the fwd header in stdlibc++
|
||||
#elif defined(_MSC_VER)
|
||||
#include "c4/error.hpp"
|
||||
//! @todo is there a fwd header in msvc?
|
||||
namespace std {
|
||||
C4_SUPPRESS_WARNING_MSVC_WITH_PUSH(4643) // Forward declaring 'char_traits' in namespace std is not permitted by the C++ Standard.
|
||||
template<typename> struct char_traits;
|
||||
template<typename> class allocator;
|
||||
template<typename _CharT, typename _Traits, typename _Alloc> class basic_string;
|
||||
using string = basic_string<char, char_traits<char>, allocator<char>>;
|
||||
C4_SUPPRESS_WARNING_MSVC_POP
|
||||
} /* namespace std */
|
||||
#else
|
||||
#error "unknown standard library"
|
||||
#endif
|
||||
|
||||
namespace c4 {
|
||||
|
||||
c4::substr to_substr(std::string &s) noexcept;
|
||||
c4::csubstr to_csubstr(std::string const& s) noexcept;
|
||||
|
||||
bool operator== (c4::csubstr ss, std::string const& s);
|
||||
bool operator!= (c4::csubstr ss, std::string const& s);
|
||||
bool operator>= (c4::csubstr ss, std::string const& s);
|
||||
bool operator> (c4::csubstr ss, std::string const& s);
|
||||
bool operator<= (c4::csubstr ss, std::string const& s);
|
||||
bool operator< (c4::csubstr ss, std::string const& s);
|
||||
|
||||
bool operator== (std::string const& s, c4::csubstr ss);
|
||||
bool operator!= (std::string const& s, c4::csubstr ss);
|
||||
bool operator>= (std::string const& s, c4::csubstr ss);
|
||||
bool operator> (std::string const& s, c4::csubstr ss);
|
||||
bool operator<= (std::string const& s, c4::csubstr ss);
|
||||
bool operator< (std::string const& s, c4::csubstr ss);
|
||||
|
||||
size_t to_chars(c4::substr buf, std::string const& s);
|
||||
bool from_chars(c4::csubstr buf, std::string * s);
|
||||
|
||||
} // namespace c4
|
||||
|
||||
#endif // DOXYGEN
|
||||
#endif // _C4_STD_STRING_FWD_HPP_
|
||||
71
ext/c4core.src/c4/std/string_view.hpp
Normal file
71
ext/c4core.src/c4/std/string_view.hpp
Normal file
@@ -0,0 +1,71 @@
|
||||
#ifndef _C4_STD_STRING_VIEW_HPP_
|
||||
#define _C4_STD_STRING_VIEW_HPP_
|
||||
|
||||
/** @file string_view.hpp */
|
||||
|
||||
#ifndef C4CORE_SINGLE_HEADER
|
||||
#include "c4/language.hpp"
|
||||
#endif
|
||||
|
||||
#if (C4_CPP >= 17 && defined(__cpp_lib_string_view)) || defined(__DOXYGEN__)
|
||||
|
||||
#ifndef C4CORE_SINGLE_HEADER
|
||||
#include "c4/substr.hpp"
|
||||
#endif
|
||||
|
||||
#include <string_view>
|
||||
|
||||
|
||||
namespace c4 {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/** create a csubstr from an existing std::string_view. */
|
||||
C4_ALWAYS_INLINE c4::csubstr to_csubstr(std::string_view s) noexcept
|
||||
{
|
||||
return c4::csubstr(s.data(), s.size());
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
C4_ALWAYS_INLINE bool operator== (c4::csubstr ss, std::string_view s) { return ss.compare(s.data(), s.size()) == 0; }
|
||||
C4_ALWAYS_INLINE bool operator!= (c4::csubstr ss, std::string_view s) { return ss.compare(s.data(), s.size()) != 0; }
|
||||
C4_ALWAYS_INLINE bool operator>= (c4::csubstr ss, std::string_view s) { return ss.compare(s.data(), s.size()) >= 0; }
|
||||
C4_ALWAYS_INLINE bool operator> (c4::csubstr ss, std::string_view s) { return ss.compare(s.data(), s.size()) > 0; }
|
||||
C4_ALWAYS_INLINE bool operator<= (c4::csubstr ss, std::string_view s) { return ss.compare(s.data(), s.size()) <= 0; }
|
||||
C4_ALWAYS_INLINE bool operator< (c4::csubstr ss, std::string_view s) { return ss.compare(s.data(), s.size()) < 0; }
|
||||
|
||||
C4_ALWAYS_INLINE bool operator== (std::string_view s, c4::csubstr ss) { return ss.compare(s.data(), s.size()) == 0; }
|
||||
C4_ALWAYS_INLINE bool operator!= (std::string_view s, c4::csubstr ss) { return ss.compare(s.data(), s.size()) != 0; }
|
||||
C4_ALWAYS_INLINE bool operator<= (std::string_view s, c4::csubstr ss) { return ss.compare(s.data(), s.size()) >= 0; }
|
||||
C4_ALWAYS_INLINE bool operator< (std::string_view s, c4::csubstr ss) { return ss.compare(s.data(), s.size()) > 0; }
|
||||
C4_ALWAYS_INLINE bool operator>= (std::string_view s, c4::csubstr ss) { return ss.compare(s.data(), s.size()) <= 0; }
|
||||
C4_ALWAYS_INLINE bool operator> (std::string_view s, c4::csubstr ss) { return ss.compare(s.data(), s.size()) < 0; }
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/** copy an std::string_view to a writeable substr */
|
||||
inline size_t to_chars(c4::substr buf, std::string_view s)
|
||||
{
|
||||
C4_ASSERT(!buf.overlaps(to_csubstr(s)));
|
||||
size_t sz = s.size();
|
||||
size_t len = buf.len < sz ? buf.len : sz;
|
||||
// calling memcpy with null strings is undefined behavior
|
||||
// and will wreak havoc in calling code's branches.
|
||||
// see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637
|
||||
if(len)
|
||||
{
|
||||
C4_ASSERT(s.data() != nullptr);
|
||||
C4_ASSERT(buf.str != nullptr);
|
||||
memcpy(buf.str, s.data(), len);
|
||||
}
|
||||
return sz; // return the number of needed chars
|
||||
}
|
||||
|
||||
} // namespace c4
|
||||
|
||||
#endif // C4_STRING_VIEW_AVAILABLE
|
||||
|
||||
#endif // _C4_STD_STRING_VIEW_HPP_
|
||||
88
ext/c4core.src/c4/std/vector.hpp
Normal file
88
ext/c4core.src/c4/std/vector.hpp
Normal file
@@ -0,0 +1,88 @@
|
||||
#ifndef _C4_STD_VECTOR_HPP_
|
||||
#define _C4_STD_VECTOR_HPP_
|
||||
|
||||
/** @file vector.hpp provides conversion and comparison facilities
|
||||
* from/between std::vector<char> to c4::substr and c4::csubstr.
|
||||
* @todo add to_span() and friends
|
||||
*/
|
||||
|
||||
#ifndef C4CORE_SINGLE_HEADER
|
||||
#include "c4/substr.hpp"
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace c4 {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/** get a substr (writeable string view) of an existing std::vector<char> */
|
||||
template<class Alloc>
|
||||
c4::substr to_substr(std::vector<char, Alloc> &vec)
|
||||
{
|
||||
char *data = vec.empty() ? nullptr : vec.data(); // data() may or may not return a null pointer.
|
||||
return c4::substr(data, vec.size());
|
||||
}
|
||||
|
||||
/** get a csubstr (read-only string) view of an existing std::vector<char> */
|
||||
template<class Alloc>
|
||||
c4::csubstr to_csubstr(std::vector<char, Alloc> const& vec)
|
||||
{
|
||||
const char *data = vec.empty() ? nullptr : vec.data(); // data() may or may not return a null pointer.
|
||||
return c4::csubstr(data, vec.size());
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// comparisons between substrings and std::vector<char>
|
||||
|
||||
template<class Alloc> C4_ALWAYS_INLINE bool operator!= (c4::csubstr ss, std::vector<char, Alloc> const& s) { return ss != to_csubstr(s); }
|
||||
template<class Alloc> C4_ALWAYS_INLINE bool operator== (c4::csubstr ss, std::vector<char, Alloc> const& s) { return ss == to_csubstr(s); }
|
||||
template<class Alloc> C4_ALWAYS_INLINE bool operator>= (c4::csubstr ss, std::vector<char, Alloc> const& s) { return ss >= to_csubstr(s); }
|
||||
template<class Alloc> C4_ALWAYS_INLINE bool operator> (c4::csubstr ss, std::vector<char, Alloc> const& s) { return ss > to_csubstr(s); }
|
||||
template<class Alloc> C4_ALWAYS_INLINE bool operator<= (c4::csubstr ss, std::vector<char, Alloc> const& s) { return ss <= to_csubstr(s); }
|
||||
template<class Alloc> C4_ALWAYS_INLINE bool operator< (c4::csubstr ss, std::vector<char, Alloc> const& s) { return ss < to_csubstr(s); }
|
||||
|
||||
template<class Alloc> C4_ALWAYS_INLINE bool operator!= (std::vector<char, Alloc> const& s, c4::csubstr ss) { return ss != to_csubstr(s); }
|
||||
template<class Alloc> C4_ALWAYS_INLINE bool operator== (std::vector<char, Alloc> const& s, c4::csubstr ss) { return ss == to_csubstr(s); }
|
||||
template<class Alloc> C4_ALWAYS_INLINE bool operator>= (std::vector<char, Alloc> const& s, c4::csubstr ss) { return ss <= to_csubstr(s); }
|
||||
template<class Alloc> C4_ALWAYS_INLINE bool operator> (std::vector<char, Alloc> const& s, c4::csubstr ss) { return ss < to_csubstr(s); }
|
||||
template<class Alloc> C4_ALWAYS_INLINE bool operator<= (std::vector<char, Alloc> const& s, c4::csubstr ss) { return ss >= to_csubstr(s); }
|
||||
template<class Alloc> C4_ALWAYS_INLINE bool operator< (std::vector<char, Alloc> const& s, c4::csubstr ss) { return ss > to_csubstr(s); }
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/** copy a std::vector<char> to a writeable string view */
|
||||
template<class Alloc>
|
||||
inline size_t to_chars(c4::substr buf, std::vector<char, Alloc> const& s)
|
||||
{
|
||||
C4_ASSERT(!buf.overlaps(to_csubstr(s)));
|
||||
size_t len = buf.len < s.size() ? buf.len : s.size();
|
||||
// calling memcpy with null strings is undefined behavior
|
||||
// and will wreak havoc in calling code's branches.
|
||||
// see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637
|
||||
if(len > 0)
|
||||
{
|
||||
memcpy(buf.str, s.data(), len);
|
||||
}
|
||||
return s.size(); // return the number of needed chars
|
||||
}
|
||||
|
||||
/** copy a string view to an existing std::vector<char> */
|
||||
template<class Alloc>
|
||||
inline bool from_chars(c4::csubstr buf, std::vector<char, Alloc> * s)
|
||||
{
|
||||
s->resize(buf.len);
|
||||
C4_ASSERT(!buf.overlaps(to_csubstr(*s)));
|
||||
// calling memcpy with null strings is undefined behavior
|
||||
// and will wreak havoc in calling code's branches.
|
||||
// see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637
|
||||
if(buf.len > 0)
|
||||
{
|
||||
memcpy(&(*s)[0], buf.str, buf.len); // NOLINT(readability-container-data-pointer)
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace c4
|
||||
|
||||
#endif // _C4_STD_VECTOR_HPP_
|
||||
70
ext/c4core.src/c4/std/vector_fwd.hpp
Normal file
70
ext/c4core.src/c4/std/vector_fwd.hpp
Normal file
@@ -0,0 +1,70 @@
|
||||
#ifndef _C4_STD_VECTOR_FWD_HPP_
|
||||
#define _C4_STD_VECTOR_FWD_HPP_
|
||||
|
||||
/** @file vector_fwd.hpp */
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
// NOLINTBEGIN(cert-dcl58-cpp)
|
||||
|
||||
// forward declarations for std::vector
|
||||
#if defined(__GLIBCXX__) || defined(__GLIBCPP__) || defined(_MSC_VER)
|
||||
#if defined(_MSC_VER)
|
||||
__pragma(warning(push))
|
||||
__pragma(warning(disable : 4643))
|
||||
#endif
|
||||
namespace std {
|
||||
template<typename> class allocator;
|
||||
#ifdef _GLIBCXX_DEBUG
|
||||
inline namespace __debug {
|
||||
template<typename T, typename Alloc> class vector;
|
||||
}
|
||||
#else
|
||||
template<typename T, typename Alloc> class vector;
|
||||
#endif
|
||||
} // namespace std
|
||||
#if defined(_MSC_VER)
|
||||
__pragma(warning(pop))
|
||||
#endif
|
||||
#elif defined(_LIBCPP_ABI_NAMESPACE)
|
||||
namespace std {
|
||||
inline namespace _LIBCPP_ABI_NAMESPACE {
|
||||
template<typename> class allocator;
|
||||
template<typename T, typename Alloc> class vector;
|
||||
} // namespace _LIBCPP_ABI_NAMESPACE
|
||||
} // namespace std
|
||||
#else
|
||||
#error "unknown standard library"
|
||||
#endif
|
||||
|
||||
#ifndef C4CORE_SINGLE_HEADER
|
||||
#include "c4/substr_fwd.hpp"
|
||||
#endif
|
||||
|
||||
namespace c4 {
|
||||
|
||||
template<class Alloc> c4::substr to_substr(std::vector<char, Alloc> &vec);
|
||||
template<class Alloc> c4::csubstr to_csubstr(std::vector<char, Alloc> const& vec);
|
||||
|
||||
template<class Alloc> bool operator!= (c4::csubstr ss, std::vector<char, Alloc> const& s);
|
||||
template<class Alloc> bool operator== (c4::csubstr ss, std::vector<char, Alloc> const& s);
|
||||
template<class Alloc> bool operator>= (c4::csubstr ss, std::vector<char, Alloc> const& s);
|
||||
template<class Alloc> bool operator> (c4::csubstr ss, std::vector<char, Alloc> const& s);
|
||||
template<class Alloc> bool operator<= (c4::csubstr ss, std::vector<char, Alloc> const& s);
|
||||
template<class Alloc> bool operator< (c4::csubstr ss, std::vector<char, Alloc> const& s);
|
||||
|
||||
template<class Alloc> bool operator!= (std::vector<char, Alloc> const& s, c4::csubstr ss);
|
||||
template<class Alloc> bool operator== (std::vector<char, Alloc> const& s, c4::csubstr ss);
|
||||
template<class Alloc> bool operator>= (std::vector<char, Alloc> const& s, c4::csubstr ss);
|
||||
template<class Alloc> bool operator> (std::vector<char, Alloc> const& s, c4::csubstr ss);
|
||||
template<class Alloc> bool operator<= (std::vector<char, Alloc> const& s, c4::csubstr ss);
|
||||
template<class Alloc> bool operator< (std::vector<char, Alloc> const& s, c4::csubstr ss);
|
||||
|
||||
template<class Alloc> size_t to_chars(c4::substr buf, std::vector<char, Alloc> const& s);
|
||||
template<class Alloc> bool from_chars(c4::csubstr buf, std::vector<char, Alloc> * s);
|
||||
|
||||
} // namespace c4
|
||||
|
||||
// NOLINTEND(cert-dcl58-cpp)
|
||||
|
||||
#endif // _C4_STD_VECTOR_FWD_HPP_
|
||||
2291
ext/c4core.src/c4/substr.hpp
Normal file
2291
ext/c4core.src/c4/substr.hpp
Normal file
File diff suppressed because it is too large
Load Diff
16
ext/c4core.src/c4/substr_fwd.hpp
Normal file
16
ext/c4core.src/c4/substr_fwd.hpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef _C4_SUBSTR_FWD_HPP_
|
||||
#define _C4_SUBSTR_FWD_HPP_
|
||||
|
||||
#include "c4/export.hpp"
|
||||
|
||||
namespace c4 {
|
||||
|
||||
#ifndef DOXYGEN
|
||||
template<class C> struct basic_substring;
|
||||
using csubstr = C4CORE_EXPORT basic_substring<const char>;
|
||||
using substr = C4CORE_EXPORT basic_substring<char>;
|
||||
#endif // !DOXYGEN
|
||||
|
||||
} // namespace c4
|
||||
|
||||
#endif /* _C4_SUBSTR_FWD_HPP_ */
|
||||
507
ext/c4core.src/c4/types.hpp
Normal file
507
ext/c4core.src/c4/types.hpp
Normal file
@@ -0,0 +1,507 @@
|
||||
#ifndef _C4_TYPES_HPP_
|
||||
#define _C4_TYPES_HPP_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <type_traits>
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
#include <utility> // for integer_sequence and friends
|
||||
#endif
|
||||
|
||||
#include "c4/preprocessor.hpp"
|
||||
#include "c4/language.hpp"
|
||||
|
||||
/** @file types.hpp basic types, and utility macros and traits for types.
|
||||
* @ingroup basic_headers */
|
||||
|
||||
/** @defgroup types Type utilities */
|
||||
|
||||
// NOLINTBEGIN(bugprone-macro-parentheses)
|
||||
|
||||
namespace c4 {
|
||||
|
||||
/** @defgroup intrinsic_types Intrinsic types
|
||||
* @ingroup types
|
||||
* @{ */
|
||||
|
||||
using cbyte = const char; /**< a constant byte */
|
||||
using byte = char; /**< a mutable byte */
|
||||
|
||||
using i8 = int8_t;
|
||||
using i16 = int16_t;
|
||||
using i32 = int32_t;
|
||||
using i64 = int64_t;
|
||||
using u8 = uint8_t;
|
||||
using u16 = uint16_t;
|
||||
using u32 = uint32_t;
|
||||
using u64 = uint64_t;
|
||||
|
||||
using f32 = float;
|
||||
using f64 = double;
|
||||
|
||||
using ssize_t = typename std::make_signed<size_t>::type;
|
||||
|
||||
/** @} */
|
||||
|
||||
//--------------------------------------------------
|
||||
|
||||
/** @defgroup utility_types Utility types
|
||||
* @ingroup types
|
||||
* @{ */
|
||||
|
||||
// some tag types
|
||||
|
||||
#if !defined(__clang__) && defined(__GNUC__)
|
||||
#pragma GCC diagnostic push
|
||||
#if __GNUC__ >= 6
|
||||
#pragma GCC diagnostic ignored "-Wunused-const-variable"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/** a tag type for initializing the containers with variadic arguments a la
|
||||
* initializer_list, minus the initializer_list overload problems.
|
||||
*/
|
||||
struct aggregate_t {};
|
||||
/** @see aggregate_t */
|
||||
constexpr const aggregate_t aggregate{};
|
||||
|
||||
/** a tag type for specifying the initial capacity of allocatable contiguous storage */
|
||||
struct with_capacity_t {};
|
||||
/** @see with_capacity_t */
|
||||
constexpr const with_capacity_t with_capacity{};
|
||||
|
||||
/** a tag type for disambiguating template parameter packs in variadic template overloads */
|
||||
struct varargs_t {};
|
||||
/** @see with_capacity_t */
|
||||
constexpr const varargs_t varargs{};
|
||||
|
||||
#if !defined(__clang__) && defined(__GNUC__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
|
||||
/** whether a value should be used in place of a const-reference in argument passing. */
|
||||
template<class T>
|
||||
struct cref_uses_val
|
||||
{
|
||||
enum { value = (
|
||||
std::is_scalar<T>::value
|
||||
||
|
||||
(
|
||||
#if C4_CPP >= 20
|
||||
(std::is_trivially_copyable<T>::value && std::is_standard_layout<T>::value)
|
||||
#else
|
||||
std::is_pod<T>::value
|
||||
#endif
|
||||
&&
|
||||
sizeof(T) <= sizeof(size_t))) };
|
||||
};
|
||||
/** utility macro to override the default behaviour for c4::fastcref<T>
|
||||
@see fastcref */
|
||||
#define C4_CREF_USES_VAL(T) \
|
||||
template<> \
|
||||
struct cref_uses_val<T> \
|
||||
{ \
|
||||
enum { value = true }; \
|
||||
};
|
||||
|
||||
/** Whether to use pass-by-value or pass-by-const-reference in a function argument
|
||||
* or return type. */
|
||||
template<class T>
|
||||
using fastcref = typename std::conditional<c4::cref_uses_val<T>::value, T, T const&>::type;
|
||||
|
||||
//--------------------------------------------------
|
||||
|
||||
/** Just what its name says. Useful sometimes as a default empty policy class. */
|
||||
struct EmptyStruct // NOLINT
|
||||
{
|
||||
template<class... T> EmptyStruct(T && ...){} // NOLINT
|
||||
};
|
||||
|
||||
/** Just what its name says. Useful sometimes as a default policy class to
|
||||
* be inherited from. */
|
||||
struct EmptyStructVirtual // NOLINT
|
||||
{
|
||||
virtual ~EmptyStructVirtual() = default;
|
||||
template<class... T> EmptyStructVirtual(T && ...){} // NOLINT
|
||||
};
|
||||
|
||||
|
||||
/** */
|
||||
template<class T>
|
||||
struct inheritfrom : public T {};
|
||||
|
||||
//--------------------------------------------------
|
||||
// Utilities to make a class obey size restrictions (eg, min size or size multiple of).
|
||||
// DirectX usually makes this restriction with uniform buffers.
|
||||
// This is also useful for padding to prevent false-sharing.
|
||||
|
||||
/** how many bytes must be added to size such that the result is at least minsize? */
|
||||
C4_ALWAYS_INLINE constexpr size_t min_remainder(size_t size, size_t minsize) noexcept
|
||||
{
|
||||
return size < minsize ? minsize-size : 0;
|
||||
}
|
||||
|
||||
/** how many bytes must be added to size such that the result is a multiple of multipleof? */
|
||||
C4_ALWAYS_INLINE constexpr size_t mult_remainder(size_t size, size_t multipleof) noexcept
|
||||
{
|
||||
return (((size % multipleof) != 0) ? (multipleof-(size % multipleof)) : 0);
|
||||
}
|
||||
|
||||
/* force the following class to be tightly packed. */
|
||||
#pragma pack(push, 1)
|
||||
/** pad a class with more bytes at the end.
|
||||
* @see http://stackoverflow.com/questions/21092415/force-c-structure-to-pack-tightly */
|
||||
template<class T, size_t BytesToPadAtEnd>
|
||||
struct Padded : public T
|
||||
{
|
||||
using T::T;
|
||||
using T::operator=;
|
||||
Padded(T const& val) : T(val) {}
|
||||
Padded(T && val) : T(std::forward<T>(val)) {} // NOLINT
|
||||
char ___c4padspace___[BytesToPadAtEnd];
|
||||
};
|
||||
#pragma pack(pop)
|
||||
/** When the padding argument is 0, we cannot declare the char[] array. */
|
||||
template<class T>
|
||||
struct Padded<T, 0> : public T
|
||||
{
|
||||
using T::T;
|
||||
using T::operator=;
|
||||
Padded(T const& val) : T(val) {}
|
||||
Padded(T && val) : T(std::forward<T>(val)) {} // NOLINT
|
||||
};
|
||||
|
||||
/** make T have a size which is at least Min bytes */
|
||||
template<class T, size_t Min>
|
||||
using MinSized = Padded<T, min_remainder(sizeof(T), Min)>;
|
||||
|
||||
/** make T have a size which is a multiple of Mult bytes */
|
||||
template<class T, size_t Mult>
|
||||
using MultSized = Padded<T, mult_remainder(sizeof(T), Mult)>;
|
||||
|
||||
/** make T have a size which is simultaneously:
|
||||
* -bigger or equal than Min
|
||||
* -a multiple of Mult */
|
||||
template<class T, size_t Min, size_t Mult>
|
||||
using MinMultSized = MultSized<MinSized<T, Min>, Mult>;
|
||||
|
||||
/** make T be suitable for use as a uniform buffer. (at least with DirectX). */
|
||||
template<class T>
|
||||
using UbufSized = MinMultSized<T, 64, 16>;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#define C4_NO_COPY_CTOR(ty) ty(ty const&) = delete
|
||||
#define C4_NO_MOVE_CTOR(ty) ty(ty &&) = delete
|
||||
#define C4_NO_COPY_ASSIGN(ty) ty& operator=(ty const&) = delete
|
||||
#define C4_NO_MOVE_ASSIGN(ty) ty& operator=(ty &&) = delete
|
||||
#define C4_DEFAULT_COPY_CTOR(ty) ty(ty const&) noexcept = default
|
||||
#define C4_DEFAULT_MOVE_CTOR(ty) ty(ty &&) noexcept = default
|
||||
#define C4_DEFAULT_COPY_ASSIGN(ty) ty& operator=(ty const&) noexcept = default
|
||||
#define C4_DEFAULT_MOVE_ASSIGN(ty) ty& operator=(ty &&) noexcept = default
|
||||
|
||||
#define C4_NO_COPY_OR_MOVE_CTOR(ty) \
|
||||
C4_NO_COPY_CTOR(ty); \
|
||||
C4_NO_MOVE_CTOR(ty)
|
||||
|
||||
#define C4_NO_COPY_OR_MOVE_ASSIGN(ty) \
|
||||
C4_NO_COPY_ASSIGN(ty); \
|
||||
C4_NO_MOVE_ASSIGN(ty)
|
||||
|
||||
#define C4_NO_COPY_OR_MOVE(ty) \
|
||||
C4_NO_COPY_OR_MOVE_CTOR(ty); \
|
||||
C4_NO_COPY_OR_MOVE_ASSIGN(ty)
|
||||
|
||||
#define C4_DEFAULT_COPY_AND_MOVE_CTOR(ty) \
|
||||
C4_DEFAULT_COPY_CTOR(ty); \
|
||||
C4_DEFAULT_MOVE_CTOR(ty)
|
||||
|
||||
#define C4_DEFAULT_COPY_AND_MOVE_ASSIGN(ty) \
|
||||
C4_DEFAULT_COPY_ASSIGN(ty); \
|
||||
C4_DEFAULT_MOVE_ASSIGN(ty)
|
||||
|
||||
#define C4_DEFAULT_COPY_AND_MOVE(ty) \
|
||||
C4_DEFAULT_COPY_AND_MOVE_CTOR(ty); \
|
||||
C4_DEFAULT_COPY_AND_MOVE_ASSIGN(ty)
|
||||
|
||||
/** @see https://en.cppreference.com/w/cpp/named_req/TriviallyCopyable */
|
||||
#define C4_MUST_BE_TRIVIAL_COPY(ty) \
|
||||
static_assert(std::is_trivially_copyable<ty>::value, #ty " must be trivially copyable")
|
||||
|
||||
/** @} */
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/** @defgroup traits_types Type traits utilities
|
||||
* @ingroup types
|
||||
* @{ */
|
||||
|
||||
// http://stackoverflow.com/questions/10821380/is-t-an-instance-of-a-template-in-c
|
||||
template<template<typename...> class X, typename T> struct is_instance_of_tpl : std::false_type {};
|
||||
template<template<typename...> class X, typename... Y> struct is_instance_of_tpl<X, X<Y...>> : std::true_type {};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/** SFINAE. use this macro to enable a template function overload
|
||||
based on a compile-time condition.
|
||||
@code
|
||||
// define an overload for a non-pod type
|
||||
template<class T, C4_REQUIRE_T(std::is_pod<T>::value)>
|
||||
void foo() { std::cout << "pod type\n"; }
|
||||
|
||||
// define an overload for a non-pod type
|
||||
template<class T, C4_REQUIRE_T(!std::is_pod<T>::value)>
|
||||
void foo() { std::cout << "nonpod type\n"; }
|
||||
|
||||
struct non_pod
|
||||
{
|
||||
non_pod() : name("asdfkjhasdkjh") {}
|
||||
const char *name;
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
foo<float>(); // prints "pod type"
|
||||
foo<non_pod>(); // prints "nonpod type"
|
||||
}
|
||||
@endcode */
|
||||
#define C4_REQUIRE_T(cond) typename std::enable_if<cond, bool>::type* = nullptr
|
||||
|
||||
/** enable_if for a return type
|
||||
* @see C4_REQUIRE_T */
|
||||
#define C4_REQUIRE_R(cond, type_) typename std::enable_if<cond, type_>::type
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** define a traits class reporting whether a type provides a member typedef */
|
||||
#define C4_DEFINE_HAS_TYPEDEF(member_typedef) \
|
||||
template<typename T> \
|
||||
struct has_##stype \
|
||||
{ \
|
||||
private: \
|
||||
\
|
||||
typedef char yes; \
|
||||
typedef struct { char array[2]; } no; \
|
||||
\
|
||||
template<typename C> \
|
||||
static yes _test(typename C::member_typedef*); \
|
||||
\
|
||||
template<typename C> \
|
||||
static no _test(...); \
|
||||
\
|
||||
public: \
|
||||
\
|
||||
enum { value = (sizeof(_test<T>(0)) == sizeof(yes)) }; \
|
||||
\
|
||||
}
|
||||
|
||||
|
||||
/** @} */
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
/** @defgroup type_declarations Type declaration utilities
|
||||
* @ingroup types
|
||||
* @{ */
|
||||
|
||||
#define _c4_DEFINE_ARRAY_TYPES_WITHOUT_ITERATOR(T, I) \
|
||||
\
|
||||
using size_type = I; \
|
||||
using ssize_type = typename std::make_signed<I>::type; \
|
||||
using difference_type = typename std::make_signed<I>::type; \
|
||||
\
|
||||
using value_type = T; \
|
||||
using pointer = T*; \
|
||||
using const_pointer = T const*; \
|
||||
using reference = T&; \
|
||||
using const_reference = T const&
|
||||
|
||||
#define _c4_DEFINE_TUPLE_ARRAY_TYPES_WITHOUT_ITERATOR(interior_types, I) \
|
||||
\
|
||||
using size_type = I; \
|
||||
using ssize_type = typename std::make_signed<I>::type; \
|
||||
using difference_type = typename std::make_signed<I>::type; \
|
||||
\
|
||||
template<I n> using value_type = typename std::tuple_element< n, std::tuple<interior_types...>>::type; \
|
||||
template<I n> using pointer = value_type<n>*; \
|
||||
template<I n> using const_pointer = value_type<n> const*; \
|
||||
template<I n> using reference = value_type<n>&; \
|
||||
template<I n> using const_reference = value_type<n> const&
|
||||
|
||||
|
||||
#define _c4_DEFINE_ARRAY_TYPES(T, I) \
|
||||
\
|
||||
_c4_DEFINE_ARRAY_TYPES_WITHOUT_ITERATOR(T, I); \
|
||||
\
|
||||
using iterator = T*; \
|
||||
using const_iterator = T const*; \
|
||||
using reverse_iterator = std::reverse_iterator<T*>; \
|
||||
using const_reverse_iterator = std::reverse_iterator<T const*>
|
||||
|
||||
|
||||
#define _c4_DEFINE_TUPLE_ARRAY_TYPES(interior_types, I) \
|
||||
\
|
||||
_c4_DEFINE_TUPLE_ARRAY_TYPES_WITHOUT_ITERATOR(interior_types, I); \
|
||||
\
|
||||
template<I n> using iterator = value_type<n>*; \
|
||||
template<I n> using const_iterator = value_type<n> const*; \
|
||||
template<I n> using reverse_iterator = std::reverse_iterator< value_type<n>*>; \
|
||||
template<I n> using const_reverse_iterator = std::reverse_iterator< value_type<n> const*>
|
||||
|
||||
|
||||
|
||||
/** @} */
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
/** @defgroup compatility_utilities Backport implementation of some Modern C++ utilities
|
||||
* @ingroup types
|
||||
* @{ */
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// index_sequence and friends are available only for C++14 and later.
|
||||
// A C++11 implementation is provided here.
|
||||
// This implementation was copied over from clang.
|
||||
// see http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/utility?revision=211563&view=markup#l687
|
||||
|
||||
#if __cplusplus > 201103L
|
||||
|
||||
using std::integer_sequence;
|
||||
using std::index_sequence;
|
||||
using std::make_integer_sequence;
|
||||
using std::make_index_sequence;
|
||||
using std::index_sequence_for;
|
||||
|
||||
#else
|
||||
|
||||
/** C++11 implementation of integer sequence
|
||||
* @see https://en.cppreference.com/w/cpp/utility/integer_sequence
|
||||
* @see taken from clang: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/utility?revision=211563&view=markup#l687 */
|
||||
template<class _Tp, _Tp... _Ip>
|
||||
struct integer_sequence
|
||||
{
|
||||
static_assert(std::is_integral<_Tp>::value,
|
||||
"std::integer_sequence can only be instantiated with an integral type" );
|
||||
using value_type = _Tp;
|
||||
static constexpr size_t size() noexcept { return sizeof...(_Ip); }
|
||||
};
|
||||
|
||||
/** C++11 implementation of index sequence
|
||||
* @see https://en.cppreference.com/w/cpp/utility/integer_sequence
|
||||
* @see taken from clang: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/utility?revision=211563&view=markup#l687 */
|
||||
template<size_t... _Ip>
|
||||
using index_sequence = integer_sequence<size_t, _Ip...>;
|
||||
|
||||
/** @cond DONT_DOCUMENT_THIS */
|
||||
namespace __detail {
|
||||
|
||||
template<typename _Tp, size_t ..._Extra>
|
||||
struct __repeat;
|
||||
|
||||
template<typename _Tp, _Tp ..._Np, size_t ..._Extra>
|
||||
struct __repeat<integer_sequence<_Tp, _Np...>, _Extra...>
|
||||
{
|
||||
using type = integer_sequence<_Tp,
|
||||
_Np...,
|
||||
sizeof...(_Np) + _Np...,
|
||||
2 * sizeof...(_Np) + _Np...,
|
||||
3 * sizeof...(_Np) + _Np...,
|
||||
4 * sizeof...(_Np) + _Np...,
|
||||
5 * sizeof...(_Np) + _Np...,
|
||||
6 * sizeof...(_Np) + _Np...,
|
||||
7 * sizeof...(_Np) + _Np...,
|
||||
_Extra...>;
|
||||
};
|
||||
|
||||
template<size_t _Np> struct __parity;
|
||||
template<size_t _Np> struct __make : __parity<_Np % 8>::template __pmake<_Np> {};
|
||||
|
||||
template<> struct __make<0> { using type = integer_sequence<size_t>; };
|
||||
template<> struct __make<1> { using type = integer_sequence<size_t, 0>; };
|
||||
template<> struct __make<2> { using type = integer_sequence<size_t, 0, 1>; };
|
||||
template<> struct __make<3> { using type = integer_sequence<size_t, 0, 1, 2>; };
|
||||
template<> struct __make<4> { using type = integer_sequence<size_t, 0, 1, 2, 3>; };
|
||||
template<> struct __make<5> { using type = integer_sequence<size_t, 0, 1, 2, 3, 4>; };
|
||||
template<> struct __make<6> { using type = integer_sequence<size_t, 0, 1, 2, 3, 4, 5>; };
|
||||
template<> struct __make<7> { using type = integer_sequence<size_t, 0, 1, 2, 3, 4, 5, 6>; };
|
||||
|
||||
template<> struct __parity<0> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type> {}; };
|
||||
template<> struct __parity<1> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 1> {}; };
|
||||
template<> struct __parity<2> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 2, _Np - 1> {}; };
|
||||
template<> struct __parity<3> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 3, _Np - 2, _Np - 1> {}; };
|
||||
template<> struct __parity<4> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 4, _Np - 3, _Np - 2, _Np - 1> {}; };
|
||||
template<> struct __parity<5> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 5, _Np - 4, _Np - 3, _Np - 2, _Np - 1> {}; };
|
||||
template<> struct __parity<6> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 6, _Np - 5, _Np - 4, _Np - 3, _Np - 2, _Np - 1> {}; };
|
||||
template<> struct __parity<7> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 7, _Np - 6, _Np - 5, _Np - 4, _Np - 3, _Np - 2, _Np - 1> {}; };
|
||||
|
||||
template<typename _Tp, typename _Up>
|
||||
struct __convert
|
||||
{
|
||||
template<typename> struct __result;
|
||||
template<_Tp ..._Np> struct __result<integer_sequence<_Tp, _Np...>>
|
||||
{
|
||||
using type = integer_sequence<_Up, _Np...>;
|
||||
};
|
||||
};
|
||||
|
||||
template<typename _Tp>
|
||||
struct __convert<_Tp, _Tp>
|
||||
{
|
||||
template<typename _Up> struct __result
|
||||
{
|
||||
using type = _Up;
|
||||
};
|
||||
};
|
||||
|
||||
template<typename _Tp, _Tp _Np>
|
||||
using __make_integer_sequence_unchecked = typename __detail::__convert<size_t, _Tp>::template __result<typename __detail::__make<_Np>::type>::type;
|
||||
|
||||
template<class _Tp, _Tp _Ep>
|
||||
struct __make_integer_sequence
|
||||
{
|
||||
static_assert(std::is_integral<_Tp>::value,
|
||||
"std::make_integer_sequence can only be instantiated with an integral type" );
|
||||
static_assert(0 <= _Ep, "std::make_integer_sequence input shall not be negative");
|
||||
typedef __make_integer_sequence_unchecked<_Tp, _Ep> type;
|
||||
};
|
||||
|
||||
} // namespace __detail
|
||||
/** @endcond */
|
||||
|
||||
|
||||
/** C++11 implementation of index sequence
|
||||
* @see https://en.cppreference.com/w/cpp/utility/integer_sequence
|
||||
* @see taken from clang: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/utility?revision=211563&view=markup#l687 */
|
||||
template<class _Tp, _Tp _Np>
|
||||
using make_integer_sequence = typename __detail::__make_integer_sequence<_Tp, _Np>::type;
|
||||
|
||||
/** C++11 implementation of index sequence
|
||||
* @see https://en.cppreference.com/w/cpp/utility/integer_sequence
|
||||
* @see taken from clang: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/utility?revision=211563&view=markup#l687 */
|
||||
template<size_t _Np>
|
||||
using make_index_sequence = make_integer_sequence<size_t, _Np>;
|
||||
|
||||
/** C++11 implementation of index sequence
|
||||
* @see https://en.cppreference.com/w/cpp/utility/integer_sequence
|
||||
* @see taken from clang: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/utility?revision=211563&view=markup#l687 */
|
||||
template<class... _Tp>
|
||||
using index_sequence_for = make_index_sequence<sizeof...(_Tp)>;
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
|
||||
|
||||
} // namespace c4
|
||||
|
||||
// NOLINTEND(bugprone-macro-parentheses)
|
||||
|
||||
#endif /* _C4_TYPES_HPP_ */
|
||||
114
ext/c4core.src/c4/utf.cpp
Normal file
114
ext/c4core.src/c4/utf.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
#include "c4/utf.hpp"
|
||||
#include "c4/charconv.hpp"
|
||||
|
||||
namespace c4 {
|
||||
|
||||
C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
|
||||
|
||||
size_t decode_code_point(uint8_t *C4_RESTRICT buf, size_t buflen, const uint32_t code)
|
||||
{
|
||||
C4_ASSERT(buf);
|
||||
C4_ASSERT(buflen >= 4);
|
||||
C4_UNUSED(buflen);
|
||||
if (code <= UINT32_C(0x7f))
|
||||
{
|
||||
buf[0] = (uint8_t)code;
|
||||
return 1u;
|
||||
}
|
||||
else if(code <= UINT32_C(0x7ff))
|
||||
{
|
||||
buf[0] = (uint8_t)(UINT32_C(0xc0) | (code >> 6u)); /* 110xxxxx */
|
||||
buf[1] = (uint8_t)(UINT32_C(0x80) | (code & UINT32_C(0x3f))); /* 10xxxxxx */
|
||||
return 2u;
|
||||
}
|
||||
else if(code <= UINT32_C(0xffff))
|
||||
{
|
||||
buf[0] = (uint8_t)(UINT32_C(0xe0) | ((code >> 12u))); /* 1110xxxx */
|
||||
buf[1] = (uint8_t)(UINT32_C(0x80) | ((code >> 6u) & UINT32_C(0x3f))); /* 10xxxxxx */
|
||||
buf[2] = (uint8_t)(UINT32_C(0x80) | ((code ) & UINT32_C(0x3f))); /* 10xxxxxx */
|
||||
return 3u;
|
||||
}
|
||||
else if(code <= UINT32_C(0x10ffff))
|
||||
{
|
||||
buf[0] = (uint8_t)(UINT32_C(0xf0) | ((code >> 18u))); /* 11110xxx */
|
||||
buf[1] = (uint8_t)(UINT32_C(0x80) | ((code >> 12u) & UINT32_C(0x3f))); /* 10xxxxxx */
|
||||
buf[2] = (uint8_t)(UINT32_C(0x80) | ((code >> 6u) & UINT32_C(0x3f))); /* 10xxxxxx */
|
||||
buf[3] = (uint8_t)(UINT32_C(0x80) | ((code ) & UINT32_C(0x3f))); /* 10xxxxxx */
|
||||
return 4u;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
substr decode_code_point(substr out, csubstr code_point)
|
||||
{
|
||||
C4_ASSERT(out.len >= 4);
|
||||
C4_ASSERT(!code_point.begins_with("U+"));
|
||||
C4_ASSERT(!code_point.begins_with("\\x"));
|
||||
C4_ASSERT(!code_point.begins_with("\\u"));
|
||||
C4_ASSERT(!code_point.begins_with("\\U"));
|
||||
C4_ASSERT(!code_point.begins_with('0'));
|
||||
C4_ASSERT(code_point.len <= 8);
|
||||
C4_ASSERT(code_point.len > 0);
|
||||
uint32_t code_point_val;
|
||||
C4_CHECK(read_hex(code_point, &code_point_val));
|
||||
size_t ret = decode_code_point((uint8_t*)out.str, out.len, code_point_val);
|
||||
C4_ASSERT(ret <= 4);
|
||||
return out.first(ret);
|
||||
}
|
||||
|
||||
size_t first_non_bom(csubstr s)
|
||||
{
|
||||
#define c4check2_(s, c0, c1) ((s).len >= 2) && (((s).str[0] == (c0)) && ((s).str[1] == (c1)))
|
||||
#define c4check3_(s, c0, c1, c2) ((s).len >= 3) && (((s).str[0] == (c0)) && ((s).str[1] == (c1)) && ((s).str[2] == (c2)))
|
||||
#define c4check4_(s, c0, c1, c2, c3) ((s).len >= 4) && (((s).str[0] == (c0)) && ((s).str[1] == (c1)) && ((s).str[2] == (c2)) && ((s).str[3] == (c3)))
|
||||
// see https://en.wikipedia.org/wiki/Byte_order_mark#Byte-order_marks_by_encoding
|
||||
if(s.len < 2u)
|
||||
return false;
|
||||
else if(c4check3_(s, '\xef', '\xbb', '\xbf')) // UTF-8
|
||||
return 3u;
|
||||
else if(c4check4_(s, '\x00', '\x00', '\xfe', '\xff')) // UTF-32BE
|
||||
return 4u;
|
||||
else if(c4check4_(s, '\xff', '\xfe', '\x00', '\x00')) // UTF-32LE
|
||||
return 4u;
|
||||
else if(c4check2_(s, '\xfe', '\xff')) // UTF-16BE
|
||||
return 2u;
|
||||
else if(c4check2_(s, '\xff', '\xfe')) // UTF-16BE
|
||||
return 2u;
|
||||
else if(c4check3_(s, '\x2b', '\x2f', '\x76')) // UTF-7
|
||||
return 3u;
|
||||
else if(c4check3_(s, '\xf7', '\x64', '\x4c')) // UTF-1
|
||||
return 3u;
|
||||
else if(c4check4_(s, '\xdd', '\x73', '\x66', '\x73')) // UTF-EBCDIC
|
||||
return 4u;
|
||||
else if(c4check3_(s, '\x0e', '\xfe', '\xff')) // SCSU
|
||||
return 3u;
|
||||
else if(c4check3_(s, '\xfb', '\xee', '\x28')) // BOCU-1
|
||||
return 3u;
|
||||
else if(c4check4_(s, '\x84', '\x31', '\x95', '\x33')) // GB18030
|
||||
return 4u;
|
||||
return 0u;
|
||||
#undef c4check2_
|
||||
#undef c4check3_
|
||||
#undef c4check4_
|
||||
}
|
||||
|
||||
substr get_bom(substr s)
|
||||
{
|
||||
return s.first(first_non_bom(s));
|
||||
}
|
||||
csubstr get_bom(csubstr s)
|
||||
{
|
||||
return s.first(first_non_bom(s));
|
||||
}
|
||||
substr skip_bom(substr s)
|
||||
{
|
||||
return s.sub(first_non_bom(s));
|
||||
}
|
||||
csubstr skip_bom(csubstr s)
|
||||
{
|
||||
return s.sub(first_non_bom(s));
|
||||
}
|
||||
|
||||
C4_SUPPRESS_WARNING_GCC_CLANG_POP
|
||||
|
||||
} // namespace c4
|
||||
73
ext/c4core.src/c4/utf.hpp
Normal file
73
ext/c4core.src/c4/utf.hpp
Normal file
@@ -0,0 +1,73 @@
|
||||
#ifndef C4_UTF_HPP_
|
||||
#define C4_UTF_HPP_
|
||||
|
||||
#include "c4/language.hpp"
|
||||
#include "c4/substr_fwd.hpp"
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/** @file utf.hpp utilities for UTF and Byte Order Mark */
|
||||
|
||||
namespace c4 {
|
||||
|
||||
/** @defgroup doc_utf UTF utilities
|
||||
* @{ */
|
||||
|
||||
|
||||
/** skip the Byte Order Mark, or get the full string if there is Byte Order Mark.
|
||||
* @see Implements the Byte Order Marks as described in https://en.wikipedia.org/wiki/Byte_order_mark#Byte-order_marks_by_encoding */
|
||||
C4CORE_EXPORT substr skip_bom(substr s);
|
||||
/** skip the Byte Order Mark, or get the full string if there is Byte Order Mark
|
||||
* @see Implements the Byte Order Marks as described in https://en.wikipedia.org/wiki/Byte_order_mark#Byte-order_marks_by_encoding */
|
||||
C4CORE_EXPORT csubstr skip_bom(csubstr s);
|
||||
|
||||
|
||||
/** get the Byte Order Mark, or an empty string if there is no Byte Order Mark
|
||||
* @see Implements the Byte Order Marks as described in https://en.wikipedia.org/wiki/Byte_order_mark#Byte-order_marks_by_encoding */
|
||||
C4CORE_EXPORT substr get_bom(substr s);
|
||||
/** get the Byte Order Mark, or an empty string if there is no Byte Order Mark
|
||||
* @see Implements the Byte Order Marks as described in https://en.wikipedia.org/wiki/Byte_order_mark#Byte-order_marks_by_encoding */
|
||||
C4CORE_EXPORT csubstr get_bom(csubstr s);
|
||||
|
||||
|
||||
/** return the position of the first character not belonging to the
|
||||
* Byte Order Mark, or 0 if there is no Byte Order Mark.
|
||||
* @see Implements the Byte Order Marks as described in https://en.wikipedia.org/wiki/Byte_order_mark#Byte-order_marks_by_encoding */
|
||||
C4CORE_EXPORT size_t first_non_bom(csubstr s);
|
||||
|
||||
|
||||
/** decode the given @p code_point, writing into the output string in
|
||||
* @p out.
|
||||
*
|
||||
* @param out the output string. must have at least 4 bytes (this is
|
||||
* asserted), and must not have a null string.
|
||||
*
|
||||
* @param code_point: must have length in ]0,8], and must not begin
|
||||
* with any of `U+`,`\\x`,`\\u,`\\U`,`0` (asserted)
|
||||
*
|
||||
* @return the part of @p out that was written, which will always be
|
||||
* at most 4 bytes.
|
||||
*/
|
||||
C4CORE_EXPORT substr decode_code_point(substr out, csubstr code_point);
|
||||
|
||||
/** decode the given @p code point, writing into the output string @p
|
||||
* buf, of size @p buflen
|
||||
*
|
||||
* @param buf the output string. must have at least 4 bytes (this is
|
||||
* asserted), and must not be null
|
||||
*
|
||||
* @param buflen the length of the output string. must be at least 4
|
||||
*
|
||||
* @param code: the code point must have length in ]0,8], and must not begin
|
||||
* with any of `U+`,`\\x`,`\\u,`\\U`,`0` (asserted)
|
||||
*
|
||||
* @return the number of written characters, which will always be
|
||||
* at most 4 bytes.
|
||||
*/
|
||||
C4CORE_EXPORT size_t decode_code_point(uint8_t *C4_RESTRICT buf, size_t buflen, uint32_t code);
|
||||
|
||||
/** @} */
|
||||
|
||||
} // namespace c4
|
||||
|
||||
#endif // C4_UTF_HPP_
|
||||
25
ext/c4core.src/c4/version.cpp
Normal file
25
ext/c4core.src/c4/version.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#include "c4/version.hpp"
|
||||
|
||||
namespace c4 {
|
||||
|
||||
const char* version()
|
||||
{
|
||||
return C4CORE_VERSION;
|
||||
}
|
||||
|
||||
int version_major()
|
||||
{
|
||||
return C4CORE_VERSION_MAJOR;
|
||||
}
|
||||
|
||||
int version_minor()
|
||||
{
|
||||
return C4CORE_VERSION_MINOR;
|
||||
}
|
||||
|
||||
int version_patch()
|
||||
{
|
||||
return C4CORE_VERSION_PATCH;
|
||||
}
|
||||
|
||||
} // namespace c4
|
||||
22
ext/c4core.src/c4/version.hpp
Normal file
22
ext/c4core.src/c4/version.hpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef _C4_VERSION_HPP_
|
||||
#define _C4_VERSION_HPP_
|
||||
|
||||
/** @file version.hpp */
|
||||
|
||||
#define C4CORE_VERSION "0.2.7"
|
||||
#define C4CORE_VERSION_MAJOR 0
|
||||
#define C4CORE_VERSION_MINOR 2
|
||||
#define C4CORE_VERSION_PATCH 7
|
||||
|
||||
#include <c4/export.hpp>
|
||||
|
||||
namespace c4 {
|
||||
|
||||
C4CORE_EXPORT const char* version();
|
||||
C4CORE_EXPORT int version_major();
|
||||
C4CORE_EXPORT int version_minor();
|
||||
C4CORE_EXPORT int version_patch();
|
||||
|
||||
} // namespace c4
|
||||
|
||||
#endif /* _C4_VERSION_HPP_ */
|
||||
@@ -1,4 +1,32 @@
|
||||
# these are used both for testing and benchmarking
|
||||
c4_require_subproject(c4fs REMOTE
|
||||
set(C4FS_DIR ${CMAKE_CURRENT_BINARY_DIR}/subprojects/c4fs)
|
||||
c4_download_remote_proj(c4fs C4FS_DIR
|
||||
GIT_REPOSITORY https://github.com/biojppm/c4fs
|
||||
GIT_TAG master)
|
||||
c4_add_library(c4fs
|
||||
SOURCES
|
||||
${C4FS_DIR}/src/c4/fs/fs.hpp
|
||||
${C4FS_DIR}/src/c4/fs/fs.cpp
|
||||
INC_DIRS
|
||||
${C4FS_DIR}/src
|
||||
${CMAKE_CURRENT_LIST_DIR}/c4core.src
|
||||
${CMAKE_CURRENT_LIST_DIR}/c4core.dev
|
||||
LIBS
|
||||
ryml
|
||||
)
|
||||
|
||||
set(C4LOG_DIR ${CMAKE_CURRENT_BINARY_DIR}/subprojects/c4log)
|
||||
c4_download_remote_proj(c4log C4LOG_DIR
|
||||
GIT_REPOSITORY https://github.com/biojppm/c4log
|
||||
GIT_TAG master)
|
||||
c4_add_library(c4log
|
||||
SOURCES
|
||||
${C4LOG_DIR}/src/c4/log/log.hpp
|
||||
${C4LOG_DIR}/src/c4/log/log.cpp
|
||||
INC_DIRS
|
||||
${C4LOG_DIR}/src
|
||||
${CMAKE_CURRENT_LIST_DIR}/c4core.src
|
||||
${CMAKE_CURRENT_LIST_DIR}/c4core.dev
|
||||
LIBS
|
||||
ryml
|
||||
)
|
||||
|
||||
32
proj/Makefile
Normal file
32
proj/Makefile
Normal file
@@ -0,0 +1,32 @@
|
||||
# this makefile contains development-related targets
|
||||
|
||||
include ./common.mk
|
||||
include ./ys.mk
|
||||
|
||||
|
||||
check: c4core-check workflows-check presets-check
|
||||
|
||||
|
||||
c4core-check:
|
||||
$(MAKE) -C ../ext c4core-check
|
||||
c4core-import:
|
||||
$(MAKE) -C ../ext c4core-import
|
||||
c4core-export:
|
||||
$(MAKE) -C ../ext c4core-export
|
||||
|
||||
|
||||
workflows:
|
||||
$(MAKE) -C ../.github/workflows-in
|
||||
workflows-check:
|
||||
$(MAKE) -C ../.github/workflows-in test
|
||||
|
||||
|
||||
presets: ../CMakePresets.json
|
||||
presets-check: presets-force presets
|
||||
$(GIT) diff --exit-code --color=auto ../CMakePresets.json
|
||||
presets-force:
|
||||
touch ../CMakePresets.ys
|
||||
../CMakePresets.json: ../CMakePresets.ys $(YS)
|
||||
@( chmod a+w $@ || echo >/dev/null )
|
||||
$(YS) --json $< > $@
|
||||
@chmod a-w $@
|
||||
120
proj/c4proj/ConfigurationTypes.cmake
Normal file
120
proj/c4proj/ConfigurationTypes.cmake
Normal file
@@ -0,0 +1,120 @@
|
||||
|
||||
|
||||
# this function works both with multiconfig and single-config generators.
|
||||
function(set_default_build_type which)
|
||||
# CMAKE_CONFIGURATION_TYPES is available only for multiconfig generators.
|
||||
# so set the build type only if CMAKE_CONFIGURATION_TYPES does not exist.
|
||||
if(NOT CMAKE_CONFIGURATION_TYPES) # not a multiconfig generator?
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
if(NOT which)
|
||||
set(which RelWithDebInfo)
|
||||
endif()
|
||||
message("Defaulting to ${which} build.")
|
||||
set(CMAKE_BUILD_TYPE ${which} CACHE STRING "")
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
|
||||
# https://stackoverflow.com/questions/31546278/where-to-set-cmake-configuration-types-in-a-project-with-subprojects
|
||||
function(setup_configuration_types)
|
||||
set(options0arg
|
||||
)
|
||||
set(options1arg
|
||||
DEFAULT
|
||||
)
|
||||
set(optionsnarg
|
||||
TYPES
|
||||
)
|
||||
cmake_parse_arguments("" "${options0arg}" "${options1arg}" "${optionsnarg}" ${ARGN})
|
||||
|
||||
if(NOT TYPES)
|
||||
set(TYPES Release Debug RelWithDebInfo MinSizeRel)
|
||||
endif()
|
||||
|
||||
# make it safe to call repeatedly
|
||||
if(NOT _setup_configuration_types_done)
|
||||
set(_setup_configuration_types_done 1 CACHE INTERNAL "")
|
||||
|
||||
# No reason to set CMAKE_CONFIGURATION_TYPES if it's not a multiconfig generator
|
||||
# Also no reason mess with CMAKE_BUILD_TYPE if it's a multiconfig generator.
|
||||
|
||||
if(CMAKE_CONFIGURATION_TYPES) # multiconfig generator?
|
||||
set(CMAKE_CONFIGURATION_TYPES "${TYPES}" CACHE STRING "")
|
||||
else() # single-config generator
|
||||
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY HELPSTRING "Choose the type of build")
|
||||
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "${TYPES}")
|
||||
# set the valid options for cmake-gui drop-down list
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
|
||||
# https://stackoverflow.com/questions/31546278/where-to-set-cmake-configuration-types-in-a-project-with-subprojects
|
||||
function(add_configuration_type name)
|
||||
set(flag_vars
|
||||
C_FLAGS
|
||||
CXX_FLAGS
|
||||
SHARED_LINKER_FLAGS
|
||||
STATIC_LINKER_FLAGS
|
||||
MODULE_LINKER_FLAGS
|
||||
EXE_LINKER_FLAGS
|
||||
RC_FLAGS
|
||||
)
|
||||
|
||||
set(options0arg
|
||||
PREPEND # when defaulting to a config, prepend to it instead of appending to it
|
||||
SET_MAIN_FLAGS # eg, set CMAKE_CXX_FLAGS from CMAKE_CXX_FLAGS_${name}
|
||||
)
|
||||
set(options1arg
|
||||
DEFAULT_FROM # take the initial value of the flags from this config
|
||||
)
|
||||
set(optionsnarg
|
||||
C_FLAGS
|
||||
CXX_FLAGS
|
||||
SHARED_LINKER_FLAGS
|
||||
STATIC_LINKER_FLAGS
|
||||
MODULE_LINKER_FLAGS
|
||||
EXE_LINKER_FLAGS
|
||||
RC_FLAGS
|
||||
)
|
||||
cmake_parse_arguments(_act "${options0arg}" "${options1arg}" "${optionsnarg}" ${ARGN})
|
||||
|
||||
string(TOUPPER ${name} UNAME)
|
||||
|
||||
# make it safe to call repeatedly
|
||||
if(NOT _add_configuration_type_${name})
|
||||
set(_add_configuration_type_${name} 1 CACHE INTERNAL "")
|
||||
|
||||
setup_configuration_types()
|
||||
|
||||
if(CMAKE_CONFIGURATION_TYPES) # multiconfig generator?
|
||||
set(CMAKE_CONFIGURATION_TYPES "${CMAKE_CONFIGURATION_TYPES};${name}" CACHE STRING "" FORCE)
|
||||
else() # single-config generator
|
||||
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY HELPSTRING "Choose the type of build" FORCE)
|
||||
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "${CMAKE_BUILD_TYPES};${name}" FORCE)
|
||||
# set the valid options for cmake-gui drop-down list
|
||||
endif()
|
||||
|
||||
# now set up the configuration
|
||||
message(STATUS "config: CMAKE_${f}_${UNAME} --- ${val}")
|
||||
foreach(f ${flag_vars})
|
||||
set(val ${_act_${f}})
|
||||
message(STATUS "config: ${name}: ${f} --- ${val}")
|
||||
if(_act_DEFAULT_FROM)
|
||||
if(_act_PREPEND)
|
||||
set(val "${val} ${CMAKE_${f}_${_act_DEFAULT_FROM}}")
|
||||
else()
|
||||
set(val "${CMAKE_${f}_${_act_DEFAULT_FROM}} ${val}")
|
||||
endif()
|
||||
endif()
|
||||
message(STATUS "config: CMAKE_${f}_${UNAME} --- ${val}")
|
||||
set(CMAKE_${f}_${UNAME} "${val}" CACHE STRING "" FORCE)
|
||||
mark_as_advanced(CMAKE_${f}_${UNAME})
|
||||
if(_act_SET_MAIN_FLAGS)
|
||||
set(CMAKE_${f} "${CMAKE_${f}_${UNAME}}" CACHE STRING "" FORCE)
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
endfunction()
|
||||
31
proj/c4proj/CreateSourceGroup.cmake
Normal file
31
proj/c4proj/CreateSourceGroup.cmake
Normal file
@@ -0,0 +1,31 @@
|
||||
# create hierarchical source groups based on a dir tree
|
||||
#
|
||||
# EXAMPLE USAGE:
|
||||
#
|
||||
# create_source_group("src" "${SRC_ROOT}" "${SRC_LIST}")
|
||||
#
|
||||
# Visual Studio usually has the equivalent to this:
|
||||
#
|
||||
# create_source_group("Header Files" ${PROJ_SRC_DIR} "${PROJ_HEADERS}")
|
||||
# create_source_group("Source Files" ${PROJ_SRC_DIR} "${PROJ_SOURCES}")
|
||||
#
|
||||
# TODO: <jpmag> this was taken from a stack overflow answer. Need to find it
|
||||
# and add a link here.
|
||||
|
||||
macro(create_source_group GroupPrefix RootDir ProjectSources)
|
||||
set(DirSources ${ProjectSources})
|
||||
foreach(Source ${DirSources})
|
||||
#message(STATUS "s=${Source}")
|
||||
string(REGEX REPLACE "([\\^\\$*+?|])" "\\\\\\1" RootDirRegex "${RootDir}")
|
||||
string(REGEX REPLACE "${RootDirRegex}" "" RelativePath "${Source}")
|
||||
#message(STATUS " ${RelativePath}")
|
||||
string(REGEX REPLACE "[\\\\/][^\\\\/]*$" "" RelativePath "${RelativePath}")
|
||||
#message(STATUS " ${RelativePath}")
|
||||
string(REGEX REPLACE "^[\\\\/]" "" RelativePath "${RelativePath}")
|
||||
#message(STATUS " ${RelativePath}")
|
||||
string(REGEX REPLACE "/" "\\\\\\\\" RelativePath "${RelativePath}")
|
||||
#message(STATUS " ${RelativePath}")
|
||||
source_group("${GroupPrefix}\\${RelativePath}" FILES ${Source})
|
||||
#message(STATUS " ${Source}")
|
||||
endforeach(Source)
|
||||
endmacro(create_source_group)
|
||||
53
proj/c4proj/GetFlags.cmake
Normal file
53
proj/c4proj/GetFlags.cmake
Normal file
@@ -0,0 +1,53 @@
|
||||
|
||||
function(_c4_intersperse_with_flag outvar flag)
|
||||
if(MSVC AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # it may be clang as well
|
||||
set(f "/${flag}")
|
||||
else()
|
||||
set(f "-${flag}")
|
||||
endif()
|
||||
set(out)
|
||||
foreach(i ${ARGN})
|
||||
if(NOT "${i}" STREQUAL "")
|
||||
set(out "${out} ${f} '${i}'")
|
||||
|
||||
# ... Following this are several unsuccessful attempts to make
|
||||
# sure that an empty generator expression passed as part of the
|
||||
# arguments won't be expanded to nothing between successive
|
||||
# flags. For example, -I /some/include -I -I /other/include,
|
||||
# which is wrong as it misses an empty quote. This causes
|
||||
# clang-tidy in particular to fail. Maybe this is happening
|
||||
# because the result is passed to separate_arguments() which
|
||||
# prevents the lists from being evaluated correctly. Also, note
|
||||
# that add_custom_target() has the following options which may
|
||||
# help: COMMAND_EXPAND_LISTS and VERBATIM.
|
||||
|
||||
# Anyway -- for now it is working, but maybe the generator
|
||||
# expression approach turns out to work while being much cleaner
|
||||
# than the current approach.
|
||||
|
||||
#set(c $<GENEX_EVAL,$<BOOL:${i}>>)
|
||||
#set(c $<BOOL:${i}>) # i may be a generator expression the evaluates to empty
|
||||
#set(s "${f} ${i}")
|
||||
#set(e "${f} aaaaaaWTF")
|
||||
#list(APPEND out $<IF:${c},${s},${e}>)
|
||||
#list(APPEND out $<${c},${s}>)
|
||||
#list(APPEND out $<GENEX_EVAL:${c},${s}>)
|
||||
#list(APPEND out $<TARGET_GENEX_EVAL:${tgt},${c},${s}>)
|
||||
endif()
|
||||
endforeach()
|
||||
## https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html#string-valued-generator-expressions
|
||||
#if(ARGN)
|
||||
# set(out "${f}$<JOIN:${ARGN},;${f}>")
|
||||
#endif()
|
||||
set(${outvar} ${out} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(c4_get_define_flags outvar)
|
||||
_c4_intersperse_with_flag(out D ${ARGN})
|
||||
set(${outvar} ${out} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(c4_get_include_flags outvar)
|
||||
_c4_intersperse_with_flag(out I ${ARGN})
|
||||
set(${outvar} ${out} PARENT_SCOPE)
|
||||
endfunction()
|
||||
275
proj/c4proj/PVS-Studio.cmake
Normal file
275
proj/c4proj/PVS-Studio.cmake
Normal file
@@ -0,0 +1,275 @@
|
||||
# 2006-2008 (c) Viva64.com Team
|
||||
# 2008-2016 (c) OOO "Program Verification Systems"
|
||||
#
|
||||
# Version 2
|
||||
|
||||
function (pvs_studio_relative_path VAR ROOT FILEPATH)
|
||||
set("${VAR}" "${FILEPATH}" PARENT_SCOPE)
|
||||
if ("${FILEPATH}" MATCHES "^/.*$")
|
||||
file(RELATIVE_PATH RPATH "${ROOT}" "${FILEPATH}")
|
||||
if (NOT "${RPATH}" MATCHES "^\\.\\..*$")
|
||||
set("${VAR}" "${RPATH}" PARENT_SCOPE)
|
||||
endif ()
|
||||
endif ()
|
||||
endfunction ()
|
||||
|
||||
function (pvs_studio_join_path VAR DIR1 DIR2)
|
||||
if ("${DIR2}" MATCHES "^(/|~).*$" OR "${DIR1}" STREQUAL "")
|
||||
set("${VAR}" "${DIR2}" PARENT_SCOPE)
|
||||
else ()
|
||||
set("${VAR}" "${DIR1}/${DIR2}" PARENT_SCOPE)
|
||||
endif ()
|
||||
endfunction ()
|
||||
|
||||
macro (pvs_studio_append_flags_from_property CXX C DIR PREFIX)
|
||||
if (NOT "${PROPERTY}" STREQUAL "NOTFOUND" AND NOT "${PROPERTY}" STREQUAL "PROPERTY-NOTFOUND")
|
||||
foreach (PROP ${PROPERTY})
|
||||
pvs_studio_join_path(PROP "${DIR}" "${PROP}")
|
||||
list(APPEND "${CXX}" "${PREFIX}${PROP}")
|
||||
list(APPEND "${C}" "${PREFIX}${PROP}")
|
||||
endforeach ()
|
||||
endif ()
|
||||
endmacro ()
|
||||
|
||||
macro (pvs_studio_append_standard_flag FLAGS STANDARD)
|
||||
if ("${STANDARD}" MATCHES "^(99|11|14|17)$")
|
||||
if ("${PVS_STUDIO_PREPROCESSOR}" MATCHES "gcc|clang")
|
||||
list(APPEND "${FLAGS}" "-std=c++${STANDARD}")
|
||||
endif ()
|
||||
endif ()
|
||||
endmacro ()
|
||||
|
||||
function (pvs_studio_set_directory_flags DIRECTORY CXX C)
|
||||
set(CXX_FLAGS "${${CXX}}")
|
||||
set(C_FLAGS "${${C}}")
|
||||
|
||||
get_directory_property(PROPERTY DIRECTORY "${DIRECTORY}" INCLUDE_DIRECTORIES)
|
||||
pvs_studio_append_flags_from_property(CXX_FLAGS C_FLAGS "${DIRECTORY}" "-I")
|
||||
|
||||
get_directory_property(PROPERTY DIRECTORY "${DIRECTORY}" COMPILE_DEFINITIONS)
|
||||
pvs_studio_append_flags_from_property(CXX_FLAGS C_FLAGS "" "-D")
|
||||
|
||||
set("${CXX}" "${CXX_FLAGS}" PARENT_SCOPE)
|
||||
set("${C}" "${C_FLAGS}" PARENT_SCOPE)
|
||||
endfunction ()
|
||||
|
||||
function (pvs_studio_set_target_flags TARGET CXX C)
|
||||
set(CXX_FLAGS "${${CXX}}")
|
||||
set(C_FLAGS "${${C}}")
|
||||
|
||||
get_target_property(PROPERTY "${TARGET}" INCLUDE_DIRECTORIES)
|
||||
pvs_studio_append_flags_from_property(CXX_FLAGS C_FLAGS "${DIRECTORY}" "-I")
|
||||
|
||||
get_target_property(PROPERTY "${TARGET}" COMPILE_DEFINITIONS)
|
||||
pvs_studio_append_flags_from_property(CXX_FLAGS C_FLAGS "" "-D")
|
||||
|
||||
get_target_property(PROPERTY "${TARGET}" CXX_STANDARD)
|
||||
pvs_studio_append_standard_flag(CXX_FLAGS "${PROPERTY}")
|
||||
|
||||
set("${CXX}" "${CXX_FLAGS}" PARENT_SCOPE)
|
||||
set("${C}" "${C_FLAGS}" PARENT_SCOPE)
|
||||
endfunction ()
|
||||
|
||||
function (pvs_studio_set_source_file_flags SOURCE)
|
||||
set(LANGUAGE "")
|
||||
|
||||
string(TOLOWER "${SOURCE}" SOURCE_LOWER)
|
||||
if ("${LANGUAGE}" STREQUAL "" AND "${SOURCE_LOWER}" MATCHES "^.*\\.(c|cpp|cc|cx|cxx|cp|c\\+\\+)$")
|
||||
if ("${SOURCE}" MATCHES "^.*\\.c$")
|
||||
set(LANGUAGE C)
|
||||
else ()
|
||||
set(LANGUAGE CXX)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if ("${LANGUAGE}" STREQUAL "C")
|
||||
set(CL_PARAMS ${PVS_STUDIO_C_FLAGS} ${PVS_STUDIO_TARGET_C_FLAGS} -DPVS_STUDIO)
|
||||
elseif ("${LANGUAGE}" STREQUAL "CXX")
|
||||
set(CL_PARAMS ${PVS_STUDIO_CXX_FLAGS} ${PVS_STUDIO_TARGET_CXX_FLAGS} -DPVS_STUDIO)
|
||||
endif ()
|
||||
|
||||
set(PVS_STUDIO_LANGUAGE "${LANGUAGE}" PARENT_SCOPE)
|
||||
set(PVS_STUDIO_CL_PARAMS "${CL_PARAMS}" PARENT_SCOPE)
|
||||
endfunction ()
|
||||
|
||||
function (pvs_studio_analyze_file SOURCE SOURCE_DIR BINARY_DIR)
|
||||
set(PLOGS ${PVS_STUDIO_PLOGS})
|
||||
pvs_studio_set_source_file_flags("${SOURCE}")
|
||||
|
||||
get_filename_component(SOURCE "${SOURCE}" REALPATH)
|
||||
pvs_studio_relative_path(SOURCE_RELATIVE "${SOURCE_DIR}" "${SOURCE}")
|
||||
pvs_studio_join_path(SOURCE "${SOURCE_DIR}" "${SOURCE}")
|
||||
|
||||
set(LOG "${BINARY_DIR}/PVS-Studio/${SOURCE_RELATIVE}.plog")
|
||||
get_filename_component(LOG "${LOG}" REALPATH)
|
||||
get_filename_component(PARENT_DIR "${LOG}" DIRECTORY)
|
||||
|
||||
if (EXISTS "${SOURCE}" AND NOT TARGET "${LOG}" AND NOT "${PVS_STUDIO_LANGUAGE}" STREQUAL "")
|
||||
add_custom_command(OUTPUT "${LOG}"
|
||||
COMMAND mkdir -p "${PARENT_DIR}"
|
||||
COMMAND rm -f "${LOG}"
|
||||
COMMAND "${PVS_STUDIO_BIN}" analyze
|
||||
--output-file "${LOG}"
|
||||
--source-file "${SOURCE}"
|
||||
${PVS_STUDIO_ARGS}
|
||||
--cl-params ${PVS_STUDIO_CL_PARAMS} "${SOURCE}"
|
||||
WORKING_DIRECTORY "${BINARY_DIR}"
|
||||
DEPENDS "${SOURCE}" "${PVS_STUDIO_CONFIG}"
|
||||
VERBATIM
|
||||
COMMENT "Analyzing ${PVS_STUDIO_LANGUAGE} file ${SOURCE_RELATIVE}")
|
||||
list(APPEND PLOGS "${LOG}")
|
||||
endif ()
|
||||
set(PVS_STUDIO_PLOGS "${PLOGS}" PARENT_SCOPE)
|
||||
endfunction ()
|
||||
|
||||
function (pvs_studio_analyze_target TARGET DIR)
|
||||
set(PVS_STUDIO_PLOGS "${PVS_STUDIO_PLOGS}")
|
||||
set(PVS_STUDIO_TARGET_CXX_FLAGS "")
|
||||
set(PVS_STUDIO_TARGET_C_FLAGS "")
|
||||
|
||||
get_target_property(PROPERTY "${TARGET}" SOURCES)
|
||||
pvs_studio_relative_path(BINARY_DIR "${CMAKE_SOURCE_DIR}" "${DIR}")
|
||||
if ("${BINARY_DIR}" MATCHES "^/.*$")
|
||||
pvs_studio_join_path(BINARY_DIR "${CMAKE_BINARY_DIR}" "PVS-Studio/__${BINARY_DIR}")
|
||||
else ()
|
||||
pvs_studio_join_path(BINARY_DIR "${CMAKE_BINARY_DIR}" "${BINARY_DIR}")
|
||||
endif ()
|
||||
|
||||
file(MAKE_DIRECTORY "${BINARY_DIR}")
|
||||
|
||||
pvs_studio_set_directory_flags("${DIR}" PVS_STUDIO_TARGET_CXX_FLAGS PVS_STUDIO_TARGET_C_FLAGS)
|
||||
pvs_studio_set_target_flags("${TARGET}" PVS_STUDIO_TARGET_CXX_FLAGS PVS_STUDIO_TARGET_C_FLAGS)
|
||||
|
||||
if (NOT "${PROPERTY}" STREQUAL "NOTFOUND" AND NOT "${PROPERTY}" STREQUAL "PROPERTY-NOTFOUND")
|
||||
foreach (SOURCE ${PROPERTY})
|
||||
pvs_studio_join_path(SOURCE "${DIR}" "${SOURCE}")
|
||||
pvs_studio_analyze_file("${SOURCE}" "${DIR}" "${BINARY_DIR}")
|
||||
endforeach ()
|
||||
endif ()
|
||||
|
||||
set(PVS_STUDIO_PLOGS "${PVS_STUDIO_PLOGS}" PARENT_SCOPE)
|
||||
endfunction ()
|
||||
|
||||
function (pvs_studio_add_target)
|
||||
macro (default VAR VALUE)
|
||||
if ("${${VAR}}" STREQUAL "")
|
||||
set("${VAR}" "${VALUE}")
|
||||
endif ()
|
||||
endmacro ()
|
||||
|
||||
set(PVS_STUDIO_SUPPORTED_PREPROCESSORS "gcc|clang")
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||
set(DEFAULT_PREPROCESSOR "clang")
|
||||
else ()
|
||||
set(DEFAULT_PREPROCESSOR "gcc")
|
||||
endif ()
|
||||
|
||||
set(OPTIONAL OUTPUT ALL)
|
||||
set(SINGLE LICENSE CONFIG TARGET LOG FORMAT BIN CONVERTER PLATFORM PREPROCESSOR CFG_TEXT)
|
||||
set(MULTI SOURCES C_FLAGS CXX_FLAGS ARGS DEPENDS ANALYZE)
|
||||
cmake_parse_arguments(PVS_STUDIO "${OPTIONAL}" "${SINGLE}" "${MULTI}" ${ARGN})
|
||||
|
||||
if ("${PVS_STUDIO_CFG}" STREQUAL "" OR NOT "${PVS_STUDIO_CFG_TEXT}" STREQUAL "")
|
||||
set(PVS_STUDIO_EMPTY_CONFIG ON)
|
||||
else ()
|
||||
set(PVS_STUDIO_EMPTY_CONFIG OFF)
|
||||
endif ()
|
||||
|
||||
default(PVS_STUDIO_CFG_TEXT "analysis-mode=4")
|
||||
default(PVS_STUDIO_CONFIG "${CMAKE_BINARY_DIR}/PVS-Studio.cfg")
|
||||
default(PVS_STUDIO_C_FLAGS "")
|
||||
default(PVS_STUDIO_CXX_FLAGS "")
|
||||
default(PVS_STUDIO_TARGET "pvs")
|
||||
default(PVS_STUDIO_LOG "PVS-Studio.log")
|
||||
default(PVS_STUDIO_BIN "pvs-studio-analyzer")
|
||||
default(PVS_STUDIO_CONVERTER "plog-converter")
|
||||
default(PVS_STUDIO_PREPROCESSOR "${DEFAULT_PREPROCESSOR}")
|
||||
default(PVS_STUDIO_PLATFORM "linux64")
|
||||
|
||||
if (PVS_STUDIO_EMPTY_CONFIG)
|
||||
set(PVS_STUDIO_CONFIG_COMMAND echo "${PVS_STUDIO_CFG_TEXT}" > "${PVS_STUDIO_CONFIG}")
|
||||
else ()
|
||||
set(PVS_STUDIO_CONFIG_COMMAND touch "${PVS_STUDIO_CONFIG}")
|
||||
endif ()
|
||||
|
||||
add_custom_command(OUTPUT "${PVS_STUDIO_CONFIG}"
|
||||
COMMAND ${PVS_STUDIO_CONFIG_COMMAND}
|
||||
WORKING_DIRECTORY "${BINARY_DIR}"
|
||||
COMMENT "Generating PVS-Studio.cfg")
|
||||
|
||||
|
||||
if (NOT "${PVS_STUDIO_PREPROCESSOR}" MATCHES "^${PVS_STUDIO_SUPPORTED_PREPROCESSORS}$")
|
||||
message(FATAL_ERROR "Preprocessor ${PVS_STUDIO_PREPROCESSOR} isn't supported. Available options: ${PVS_STUDIO_SUPPORTED_PREPROCESSORS}.")
|
||||
endif ()
|
||||
|
||||
pvs_studio_append_standard_flag(PVS_STUDIO_CXX_FLAGS "${CMAKE_CXX_STANDARD}")
|
||||
pvs_studio_set_directory_flags("${CMAKE_CURRENT_SOURCE_DIR}" PVS_STUDIO_CXX_FLAGS PVS_STUDIO_C_FLAGS)
|
||||
|
||||
if (NOT "${PVS_STUDIO_LICENSE}" STREQUAL "")
|
||||
pvs_studio_join_path(PVS_STUDIO_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}" "${PVS_STUDIO_LICENSE}")
|
||||
list(APPEND PVS_STUDIO_ARGS --lic-file "${PVS_STUDIO_LICENSE}")
|
||||
endif ()
|
||||
list(APPEND PVS_STUDIO_ARGS --cfg "${PVS_STUDIO_CONFIG}"
|
||||
--platform "${PVS_STUDIO_PLATFORM}"
|
||||
--preprocessor "${PVS_STUDIO_PREPROCESSOR}")
|
||||
|
||||
set(PVS_STUDIO_PLOGS "")
|
||||
|
||||
foreach (TARGET ${PVS_STUDIO_ANALYZE})
|
||||
set(DIR "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
string(FIND "${TARGET}" ":" DELIM)
|
||||
if ("${DELIM}" GREATER "-1")
|
||||
math(EXPR DELIMI "${DELIM}+1")
|
||||
string(SUBSTRING "${TARGET}" "${DELIMI}" "-1" DIR)
|
||||
string(SUBSTRING "${TARGET}" "0" "${DELIM}" TARGET)
|
||||
pvs_studio_join_path(DIR "${CMAKE_CURRENT_SOURCE_DIR}" "${DIR}")
|
||||
endif ()
|
||||
pvs_studio_analyze_target("${TARGET}" "${DIR}")
|
||||
list(APPEND PVS_STUDIO_DEPENDS "${TARGET}")
|
||||
endforeach ()
|
||||
|
||||
set(PVS_STUDIO_TARGET_CXX_FLAGS "")
|
||||
set(PVS_STUDIO_TARGET_C_FLAGS "")
|
||||
foreach (SOURCE ${PVS_STUDIO_SOURCES})
|
||||
pvs_studio_analyze_file("${SOURCE}" "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
endforeach ()
|
||||
|
||||
pvs_studio_relative_path(LOG_RELATIVE "${CMAKE_BINARY_DIR}" "${PVS_STUDIO_LOG}")
|
||||
if (PVS_STUDIO_PLOGS)
|
||||
set(COMMANDS COMMAND cat ${PVS_STUDIO_PLOGS} > "${PVS_STUDIO_LOG}")
|
||||
set(COMMENT "Generating ${LOG_RELATIVE}")
|
||||
if (NOT "${PVS_STUDIO_FORMAT}" STREQUAL "" OR PVS_STUDIO_OUTPUT)
|
||||
if ("${PVS_STUDIO_FORMAT}" STREQUAL "")
|
||||
set(PVS_STUDIO_FORMAT "errorfile")
|
||||
endif ()
|
||||
list(APPEND COMMANDS
|
||||
COMMAND mv "${PVS_STUDIO_LOG}" "${PVS_STUDIO_LOG}.pvs.raw"
|
||||
COMMAND "${PVS_STUDIO_CONVERTER}" -t "${PVS_STUDIO_FORMAT}" "${PVS_STUDIO_LOG}.pvs.raw" -o "${PVS_STUDIO_LOG}"
|
||||
COMMAND rm -f "${PVS_STUDIO_LOG}.pvs.raw")
|
||||
endif ()
|
||||
else ()
|
||||
set(COMMANDS COMMAND touch "${PVS_STUDIO_LOG}")
|
||||
set(COMMENT "Generating ${LOG_RELATIVE}: no sources found")
|
||||
endif ()
|
||||
|
||||
add_custom_command(OUTPUT "${PVS_STUDIO_LOG}"
|
||||
${COMMANDS}
|
||||
COMMENT "${COMMENT}"
|
||||
DEPENDS ${PVS_STUDIO_PLOGS}
|
||||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}")
|
||||
|
||||
if (PVS_STUDIO_ALL)
|
||||
set(ALL "ALL")
|
||||
else ()
|
||||
set(ALL "")
|
||||
endif ()
|
||||
|
||||
if (PVS_STUDIO_OUTPUT)
|
||||
set(COMMANDS COMMAND cat "${PVS_STUDIO_LOG}" 1>&2)
|
||||
else ()
|
||||
set(COMMANDS "")
|
||||
endif ()
|
||||
|
||||
add_custom_target("${PVS_STUDIO_TARGET}" ${ALL} ${COMMANDS} WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" DEPENDS ${PVS_STUDIO_DEPENDS} "${PVS_STUDIO_LOG}")
|
||||
endfunction ()
|
||||
|
||||
25
proj/c4proj/PatchUtils.cmake
Normal file
25
proj/c4proj/PatchUtils.cmake
Normal file
@@ -0,0 +1,25 @@
|
||||
# create a script that applies a patch (it's different in windows)
|
||||
|
||||
# to generate a patch:
|
||||
# subversion: svn diff --patch-compatible > path/to/the/patch.diff
|
||||
|
||||
|
||||
function(apply_patch patch where mark)
|
||||
if(NOT EXISTS "${mark}")
|
||||
if(NOT Patch_EXECUTABLE)
|
||||
find_package(Patch REQUIRED)
|
||||
endif()
|
||||
file(TO_NATIVE_PATH ${patch} patch_native)
|
||||
get_filename_component(patch_name "${patch}" NAME)
|
||||
message(STATUS "Applying patch: ${patch_name}")
|
||||
execute_process(
|
||||
COMMAND "${Patch_EXECUTABLE}" "-p0" "--input=${patch_native}"
|
||||
WORKING_DIRECTORY "${where}"
|
||||
RESULT_VARIABLE status)
|
||||
if(NOT status STREQUAL "0")
|
||||
message(FATAL_ERROR "could not apply patch: ${patch} ---> ${where}")
|
||||
else()
|
||||
file(TOUCH "${mark}")
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
27
proj/c4proj/PrintVar.cmake
Normal file
27
proj/c4proj/PrintVar.cmake
Normal file
@@ -0,0 +1,27 @@
|
||||
function(status)
|
||||
message(STATUS "${ARGV}")
|
||||
endfunction()
|
||||
|
||||
function(print_var var)
|
||||
message(STATUS "${var}=${${var}} ${ARGN}")
|
||||
endfunction()
|
||||
|
||||
function(print_vars)
|
||||
foreach(a ${ARGN})
|
||||
message(STATUS "${a}=${${a}}")
|
||||
endforeach(a)
|
||||
endfunction()
|
||||
|
||||
function(debug_var debug var)
|
||||
if(${debug})
|
||||
message(STATUS "${var}=${${var}} ${ARGN}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(debug_vars debug)
|
||||
if(${debug})
|
||||
foreach(a ${ARGN})
|
||||
message(STATUS "${a}=${${a}}")
|
||||
endforeach(a)
|
||||
endif()
|
||||
endfunction()
|
||||
188
proj/c4proj/TargetArchitecture.cmake
Normal file
188
proj/c4proj/TargetArchitecture.cmake
Normal file
@@ -0,0 +1,188 @@
|
||||
|
||||
|
||||
function(c4_get_architecture_defines output_var)
|
||||
c4_get_target_cpu_architecture(arch)
|
||||
if("${arch}" STREQUAL "x86_64")
|
||||
set(defines __x86_64__)
|
||||
elseif("${arch}" STREQUAL "i386")
|
||||
set(defines __i386__)
|
||||
elseif("${arch}" STREQUAL "armv8_64")
|
||||
set(defines __arm__ __aarch64__)
|
||||
elseif("${arch}" STREQUAL "armv8")
|
||||
set(defines __arm__ __ARM_ARCH_8__)
|
||||
elseif("${arch}" STREQUAL "armv7")
|
||||
set(defines __arm__ __ARM_ARCH_7__)
|
||||
elseif("${arch}" STREQUAL "armv6")
|
||||
set(defines __arm__ __ARM_ARCH_6__)
|
||||
elseif("${arch}" STREQUAL "armv5")
|
||||
set(defines __arm__ __ARM_ARCH_5__)
|
||||
elseif("${arch}" STREQUAL "armv4")
|
||||
set(defines __arm__ __ARM_ARCH_4T__)
|
||||
elseif("${arch}" STREQUAL "ia64")
|
||||
set(defines __ia64__)
|
||||
elseif("${arch}" STREQUAL "ppc64")
|
||||
set(defines __ppc64__)
|
||||
elseif("${arch}" STREQUAL "ia64")
|
||||
set(defines __ia64__)
|
||||
elseif("${arch}" STREQUAL "riscv64")
|
||||
set(defines __riscv64__)
|
||||
elseif("${arch}" STREQUAL "riscv32")
|
||||
set(defines __riscv32__)
|
||||
elseif("${arch}" STREQUAL "s390x")
|
||||
set(defines __s390x__)
|
||||
elseif("${arch}" STREQUAL "loongarch64")
|
||||
set(defines __loongarch64__)
|
||||
elseif("${arch}" STREQUAL "loongarch32")
|
||||
set(defines __loongarch32__)
|
||||
else()
|
||||
message(FATAL_ERROR "unknown target architecture: ${arch}")
|
||||
endif()
|
||||
set(${output_var} ${defines} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
|
||||
# adapted from https://github.com/axr/solar-cmake/blob/master/TargetArch.cmake
|
||||
# Set ppc_support to TRUE before including this file or ppc and ppc64
|
||||
# will be treated as invalid architectures since they are no longer supported by Apple
|
||||
function(c4_get_target_cpu_architecture output_var)
|
||||
# this should be more or less in line with c4core/cpu.hpp
|
||||
set(archdetect_c_code "
|
||||
#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(_M_X64)
|
||||
#error cmake_ARCH x86_64
|
||||
#elif defined(__i386) || defined(__i386__) || defined(_M_IX86)
|
||||
#error cmake_ARCH i386
|
||||
#elif defined(__arm__) || defined(_M_ARM) \
|
||||
|| defined(__TARGET_ARCH_ARM) || defined(__aarch64__) || defined(_M_ARM64)
|
||||
#if defined(__aarch64__) || defined(_M_ARM64)
|
||||
#error cmake_ARCH armv8_64
|
||||
#else
|
||||
#if defined(__ARM_ARCH_8__) || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 8)
|
||||
#error cmake_ARCH armv8
|
||||
#elif defined(__ARM_ARCH_7__) || defined(_ARM_ARCH_7) \
|
||||
|| defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) \
|
||||
|| defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__) \
|
||||
|| (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 7) \
|
||||
|| (defined(_M_ARM) && _M_ARM >= 7)
|
||||
#error cmake_ARCH armv7
|
||||
#elif defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) \
|
||||
|| defined(__ARM_ARCH_6T2__) || defined(__ARM_ARCH_6Z__) \
|
||||
|| defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6ZK__) \
|
||||
|| defined(__ARM_ARCH_6M__) \
|
||||
|| (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 6)
|
||||
#error cmake_ARCH armv6
|
||||
#elif defined(__ARM_ARCH_5TEJ__) \
|
||||
|| (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 5)
|
||||
#error cmake_ARCH armv5
|
||||
#elif defined(__ARM_ARCH_4T__) \
|
||||
|| (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 4)
|
||||
#error cmake_ARCH armv4
|
||||
#else
|
||||
#error cmake_ARCH arm
|
||||
#endif
|
||||
#endif
|
||||
#elif defined(__ia64) || defined(__ia64__) || defined(_M_IA64)
|
||||
#error cmake_ARCH ia64
|
||||
#elif defined(__ppc__) || defined(__ppc) || defined(__powerpc__) \
|
||||
|| defined(_ARCH_COM) || defined(_ARCH_PWR) || defined(_ARCH_PPC) \
|
||||
|| defined(_M_MPPC) || defined(_M_PPC)
|
||||
#if defined(__ppc64__) || defined(__powerpc64__) || defined(__64BIT__)
|
||||
#error cmake_ARCH ppc64
|
||||
#else
|
||||
#error cmake_ARCH ppc32
|
||||
#endif
|
||||
#elif defined(__riscv)
|
||||
#if __riscv_xlen == 64
|
||||
#error cmake_ARCH riscv64
|
||||
#else
|
||||
#error cmake_ARCH riscv32
|
||||
#endif
|
||||
#elif defined(__s390x__) || defined(__zarch__)
|
||||
#error cmake_ARCH s390x
|
||||
#elif defined(__loongarch64)
|
||||
#error cmake_ARCH loongarch64
|
||||
#elif defined(__loongarch32)
|
||||
#error cmake_ARCH loongarch32
|
||||
#endif
|
||||
#error cmake_ARCH unknown
|
||||
")
|
||||
if(APPLE AND CMAKE_OSX_ARCHITECTURES)
|
||||
# On OS X we use CMAKE_OSX_ARCHITECTURES *if* it was set
|
||||
# First let's normalize the order of the values
|
||||
|
||||
# Note that it's not possible to compile PowerPC applications if you are using
|
||||
# the OS X SDK version 10.6 or later - you'll need 10.4/10.5 for that, so we
|
||||
# disable it by default
|
||||
# See this page for more information:
|
||||
# http://stackoverflow.com/questions/5333490/how-can-we-restore-ppc-ppc64-as-well-as-full-10-4-10-5-sdk-support-to-xcode-4
|
||||
|
||||
# Architecture defaults to i386 or ppc on OS X 10.5 and earlier, depending on the CPU type detected at runtime.
|
||||
# On OS X 10.6+ the default is x86_64 if the CPU supports it, i386 otherwise.
|
||||
|
||||
foreach(osx_arch ${CMAKE_OSX_ARCHITECTURES})
|
||||
if("${osx_arch}" STREQUAL "ppc" AND ppc_support)
|
||||
set(osx_arch_ppc TRUE)
|
||||
elseif("${osx_arch}" STREQUAL "i386")
|
||||
set(osx_arch_i386 TRUE)
|
||||
elseif("${osx_arch}" STREQUAL "x86_64")
|
||||
set(osx_arch_x86_64 TRUE)
|
||||
elseif("${osx_arch}" STREQUAL "ppc64" AND ppc_support)
|
||||
set(osx_arch_ppc64 TRUE)
|
||||
else()
|
||||
message(FATAL_ERROR "Invalid OS X arch name: ${osx_arch}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Now add all the architectures in our normalized order
|
||||
if(osx_arch_ppc)
|
||||
list(APPEND ARCH ppc)
|
||||
endif()
|
||||
|
||||
if(osx_arch_i386)
|
||||
list(APPEND ARCH i386)
|
||||
endif()
|
||||
|
||||
if(osx_arch_x86_64)
|
||||
list(APPEND ARCH x86_64)
|
||||
endif()
|
||||
|
||||
if(osx_arch_ppc64)
|
||||
list(APPEND ARCH ppc64)
|
||||
endif()
|
||||
else()
|
||||
file(WRITE "${CMAKE_BINARY_DIR}/detect_cpu_arch.c" "${archdetect_c_code}")
|
||||
|
||||
enable_language(C)
|
||||
|
||||
# Detect the architecture in a rather creative way...
|
||||
# This compiles a small C program which is a series of ifdefs that selects a
|
||||
# particular #error preprocessor directive whose message string contains the
|
||||
# target architecture. The program will always fail to compile (both because
|
||||
# file is not a valid C program, and obviously because of the presence of the
|
||||
# #error preprocessor directives... but by exploiting the preprocessor in this
|
||||
# way, we can detect the correct target architecture even when cross-compiling,
|
||||
# since the program itself never needs to be run (only the compiler/preprocessor)
|
||||
try_run(
|
||||
run_result_unused
|
||||
compile_result_unused
|
||||
"${CMAKE_BINARY_DIR}"
|
||||
"${CMAKE_BINARY_DIR}/detect_cpu_arch.c"
|
||||
COMPILE_OUTPUT_VARIABLE ARCH
|
||||
CMAKE_FLAGS CMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES}
|
||||
)
|
||||
|
||||
# Parse the architecture name from the compiler output
|
||||
string(REGEX MATCH "cmake_ARCH ([a-zA-Z0-9_]+)" ARCH "${ARCH}")
|
||||
|
||||
# Get rid of the value marker leaving just the architecture name
|
||||
string(REPLACE "cmake_ARCH " "" ARCH "${ARCH}")
|
||||
|
||||
# If we are compiling with an unknown architecture this variable should
|
||||
# already be set to "unknown" but in the case that it's empty (i.e. due
|
||||
# to a typo in the code), then set it to unknown
|
||||
if (NOT ARCH)
|
||||
set(ARCH unknown)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(${output_var} "${ARCH}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
219
proj/c4proj/amalgamate_utils.py
Normal file
219
proj/c4proj/amalgamate_utils.py
Normal file
@@ -0,0 +1,219 @@
|
||||
import re
|
||||
import os
|
||||
|
||||
|
||||
class cmtfile:
|
||||
"""commented file"""
|
||||
def __init__(self, filename):
|
||||
self.filename = filename
|
||||
def __str__(self):
|
||||
return self.filename
|
||||
|
||||
|
||||
class cmttext:
|
||||
"""commented text"""
|
||||
def __init__(self, text):
|
||||
self.text = text
|
||||
def __str__(self):
|
||||
return self.text
|
||||
|
||||
|
||||
class ignfile:
|
||||
"""ignore file"""
|
||||
def __init__(self, filename):
|
||||
self.filename = filename
|
||||
def __str__(self):
|
||||
return self.filename
|
||||
|
||||
|
||||
class hdrfile:
|
||||
"""header file, with custom include guard"""
|
||||
def __init__(self, filename, incpattern, include_guard=None):
|
||||
self.filename = filename
|
||||
self.incpattern = incpattern
|
||||
self.include_guard = include_guard
|
||||
def __str__(self):
|
||||
return self.filename
|
||||
|
||||
|
||||
class injfile:
|
||||
"""header file, to be injected at the first include point"""
|
||||
def __init__(self, filename, incpattern):
|
||||
self.filename = filename
|
||||
self.incpattern = incpattern
|
||||
def __str__(self):
|
||||
return self.filename
|
||||
|
||||
|
||||
class injcode:
|
||||
"""direct code to inject"""
|
||||
def __init__(self, code):
|
||||
self.code = code
|
||||
def __str__(self):
|
||||
return self.code
|
||||
|
||||
|
||||
class onlyif:
|
||||
def __init__(self, condition, obj):
|
||||
self.condition = condition
|
||||
self.obj = obj
|
||||
|
||||
|
||||
def catfiles(filenames, rootdir,
|
||||
include_regexes,
|
||||
definition_macro,
|
||||
repo,
|
||||
result_incguard):
|
||||
file_re = re.compile('[-./]')
|
||||
sepb = "//" + ("**" * 40)
|
||||
sepf = "//" + ("--" * 40)
|
||||
to_inject = {}
|
||||
custom_include_guards = {}
|
||||
def banner(s):
|
||||
return f"\n\n\n{sepb}\n{sepf}\n// {s}\n// {repo}/{s}\n{sepf}\n{sepb}\n\n"
|
||||
def footer(s):
|
||||
return f"\n\n// (end {repo}/{s})\n"
|
||||
def incguard(filename):
|
||||
return custom_include_guards.get(filename,
|
||||
f"{file_re.sub('_', filename).upper()}_")
|
||||
def replace_include(rx, match, line, guard):
|
||||
line = line.rstrip()
|
||||
incl = match.group(1)
|
||||
if to_inject.get(incl) is None:
|
||||
if guard is None:
|
||||
guard = incguard(incl)
|
||||
return f"""// amalgamate: removed include of
|
||||
// {repo}/src/{incl}
|
||||
//{line}
|
||||
#if !defined({guard}) && !defined(_{guard})
|
||||
#error "amalgamate: file {incl} must have been included at this point"
|
||||
#endif /* {guard} */\n
|
||||
"""
|
||||
else:
|
||||
entry = to_inject[incl]
|
||||
del to_inject[incl]
|
||||
return append_file(entry.filename)
|
||||
def append_file(filename, guard=None):
|
||||
s = ""
|
||||
with open(filename, encoding="utf8") as f:
|
||||
for line in f.readlines():
|
||||
for rx in include_regexes:
|
||||
match = rx.match(line)
|
||||
if match:
|
||||
line = replace_include(rx, match, line, guard)
|
||||
s += line
|
||||
return s
|
||||
def append_cpp(filename):
|
||||
return f"""#ifdef {definition_macro}
|
||||
{append_file(filename)}
|
||||
#endif /* {definition_macro} */
|
||||
"""
|
||||
def is_src(filename):
|
||||
return filename.endswith(".cpp") or filename.endswith(".c")
|
||||
def cmtline(line, more=""):
|
||||
if len(line.strip()) > 0:
|
||||
return f"// {line}{more}"
|
||||
else:
|
||||
return "//\n"
|
||||
out = ""
|
||||
for entry in filenames:
|
||||
if isinstance(entry, onlyif):
|
||||
if entry.condition:
|
||||
entry = entry.obj
|
||||
else:
|
||||
continue
|
||||
if isinstance(entry, ignfile):
|
||||
pass
|
||||
elif isinstance(entry, cmttext):
|
||||
for line in entry.text.split("\n"):
|
||||
out += cmtline(line, "\n")
|
||||
elif isinstance(entry, cmtfile):
|
||||
filename = f"{rootdir}/{entry.filename}"
|
||||
out += banner(entry.filename)
|
||||
with open(filename, encoding="utf8") as file:
|
||||
for line in file.readlines():
|
||||
out += cmtline(line)
|
||||
elif isinstance(entry, injcode):
|
||||
out += f"\n{entry.code}\n"
|
||||
elif isinstance(entry, injfile):
|
||||
entry.filename = f"{rootdir}/{entry.filename}"
|
||||
to_inject[entry.incpattern] = entry
|
||||
else:
|
||||
filename = f"{rootdir}/{entry}"
|
||||
out += banner(entry)
|
||||
if isinstance(entry, hdrfile):
|
||||
if entry.include_guard is not None:
|
||||
custom_include_guards[entry.incpattern] = entry.include_guard
|
||||
out += append_file(filename, entry.include_guard)
|
||||
else:
|
||||
assert isinstance(entry, str)
|
||||
if is_src(filename):
|
||||
out += append_cpp(filename)
|
||||
else:
|
||||
out += append_file(filename)
|
||||
out += footer(entry)
|
||||
return f"""#ifndef {result_incguard}
|
||||
#define {result_incguard}
|
||||
|
||||
{out}
|
||||
#endif /* {result_incguard} */
|
||||
"""
|
||||
|
||||
def include_only_first(file_contents: str):
|
||||
rx = [
|
||||
re.compile(r'^\s*#\s*include "(.*?)".*'),
|
||||
re.compile(r'^\s*#\s*include <(.*?)>.*'),
|
||||
]
|
||||
already_included = {}
|
||||
out = ""
|
||||
for line in file_contents.split("\n"):
|
||||
for expr in rx:
|
||||
match = expr.match(line)
|
||||
if match:
|
||||
incl = match.group(1)
|
||||
if already_included.get(incl) is None:
|
||||
already_included[incl] = line
|
||||
if incl.endswith(".h"):
|
||||
cpp_version = f"c{incl[:-2]}"
|
||||
already_included[cpp_version] = line
|
||||
elif incl.startswith("c") and not (incl.endswith(".h") or incl.endswith(".hpp")):
|
||||
c_version = f"{incl[1:]}.h"
|
||||
already_included[c_version] = line
|
||||
else:
|
||||
line = f"//included above:\n//{line}"
|
||||
break
|
||||
out += line
|
||||
out += "\n"
|
||||
return out
|
||||
|
||||
|
||||
def mkparser(**bool_args):
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("output", default=None, nargs='?', help="output file. defaults to stdout")
|
||||
for k, (default, help) in bool_args.items():
|
||||
# https://stackoverflow.com/questions/15008758/parsing-boolean-values-with-argparse
|
||||
feature = parser.add_mutually_exclusive_group(required=False)
|
||||
yes = '--' + k
|
||||
no = '--no-' + k
|
||||
if default:
|
||||
yes_default = "this is the default"
|
||||
no_default = f"the default is {yes}"
|
||||
else:
|
||||
yes_default = f"the default is {no}"
|
||||
no_default = "this is the default"
|
||||
feature.add_argument(yes, dest=k, action='store_true', help=f"{help}. {yes_default}.")
|
||||
feature.add_argument(no, dest=k, action='store_false', help=f"{help}. {no_default}.")
|
||||
parser.set_defaults(**{k: default})
|
||||
return parser
|
||||
|
||||
|
||||
def file_put_contents(filename: str, contents: str):
|
||||
if filename is None:
|
||||
print(contents)
|
||||
else:
|
||||
dirname = os.path.dirname(filename)
|
||||
if dirname:
|
||||
os.makedirs(dirname, exist_ok=True)
|
||||
with open(filename, "w", encoding="utf8") as output:
|
||||
output.write(contents)
|
||||
1
proj/c4proj/bm-xp/.gitignore
vendored
Normal file
1
proj/c4proj/bm-xp/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
static/*
|
||||
7
proj/c4proj/bm-xp/README.md
Normal file
7
proj/c4proj/bm-xp/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Benchmark explorer
|
||||
|
||||
You need to start a http server on this folder:
|
||||
|
||||
```shellsession
|
||||
$ python bm.py serve .
|
||||
```
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user