wip [ci skip]

This commit is contained in:
Joao Paulo Magalhaes
2025-03-30 02:38:51 +01:00
parent 806b8d8878
commit 072822377c
152 changed files with 28914 additions and 190 deletions

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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')

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -1,4 +1,4 @@
# DO NOT EDIT - GENERATED FROM .github/workflows-in/clang.ys
# DO NOT EDIT - GENERATED FROM clang.ys
name: clang
defaults:

View File

@@ -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:

View File

@@ -1,4 +1,4 @@
# DO NOT EDIT - GENERATED FROM .github/workflows-in/codeql.ys
# DO NOT EDIT - GENERATED FROM codeql.ys
name: CodeQL
'on':

View File

@@ -1,4 +1,4 @@
# DO NOT EDIT - GENERATED FROM .github/workflows-in/coverage.ys
# DO NOT EDIT - GENERATED FROM coverage.ys
name: coverage
defaults:

View File

@@ -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}}" \

View File

@@ -1,4 +1,4 @@
# DO NOT EDIT - GENERATED FROM .github/workflows-in/emscripten.ys
# DO NOT EDIT - GENERATED FROM emscripten.ys
name: emscripten
defaults:

View File

@@ -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}}

View File

@@ -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

View File

@@ -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

View File

@@ -1,4 +1,4 @@
# DO NOT EDIT - GENERATED FROM .github/workflows-in/macosx.ys
# DO NOT EDIT - GENERATED FROM macosx.ys
name: macosx
defaults:

View File

@@ -1,4 +1,4 @@
# DO NOT EDIT - GENERATED FROM .github/workflows-in/release.ys
# DO NOT EDIT - GENERATED FROM release.ys
name: release
defaults:

View File

@@ -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

View File

@@ -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
View File

@@ -1,3 +1,6 @@
# external files
ext/c4core.git/
# text editor files
*.bck
\#*

3
.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[submodule "extern/c4core"]
path = ext/c4core
url = https://github.com/biojppm/c4core

View File

@@ -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
View 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
View 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

View File

@@ -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

View File

@@ -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).
------

View File

@@ -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}")

View File

@@ -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()

View File

@@ -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 rymls 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 youre 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
View 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
View 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
View 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

View 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_

View 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_ */

View 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
View 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
View 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_

View 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

View 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_ */

View 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
View 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_ */

View 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_ */

View 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_ */

View 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_ */

View 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_ */

View 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
View 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
View 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

View 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

View 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_ */

View 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_

View 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&lt;*&gt;">
<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&lt;*&gt;">
<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&lt;*&gt;">
<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&lt;char,*&gt;">
<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&lt;const char,*&gt;">
<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&lt;char,*&gt;">
<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&lt;const char,*&gt;">
<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&lt;*,*,*,*&gt;">
<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&lt;*,*&gt;">
<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&lt;*,*&gt;">
<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&lt;*,*,*&gt;">
<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&lt;*&gt;::Sym">
<DisplayString>{value} - {name}</DisplayString>
<Expand>
<Item Name="[value]">value</Item>
<Item Name="[name]">name</Item>
</Expand>
</Type>
<Type Name="c4::EnumSymbols&lt;*&gt;">
<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>

File diff suppressed because it is too large Load Diff

View 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_ */

View 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
View 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
View 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
View 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_ */

View 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_ */

View 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 */

View 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_

File diff suppressed because it is too large Load Diff

View 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

File diff suppressed because it is too large Load Diff

View 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_

View 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

View 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_ */

View 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

View 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_ */

View 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_ */

View 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_ */

View 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_

View 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_

View 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_

View 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_

View 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_

View 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_

View 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

File diff suppressed because it is too large Load Diff

View 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
View 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
View 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
View 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_

View 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

View 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_ */

View File

@@ -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
View 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 $@

View 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()

View 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)

View 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()

View 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 ()

View 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()

View 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()

View 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()

View 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
View File

@@ -0,0 +1 @@
static/*

View 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