23 Commits
Author SHA1 Message Date
waiwaylee 3a9b1c4924 Merge branch 'feature' 2026-10-20 11:10:56 +08:00
waiwaylee 3088ce4910 更新:1、更新版本号至V1.5.8
Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-10-20 11:10:45 +08:00
waiwaylee bd011d2434 Merge branch 'feature' 2026-10-20 11:08:41 +08:00
waiwaylee 782bff5e8e 更新:1、调整航迹质量下降逻辑;
2、普通航迹消亡条件改为由航迹质量确定;

Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-10-20 10:47:05 +08:00
waiwaylee 4b70b24515 更新:1、修改航迹质量下降策略,改为按点迹未更新时间计算;
Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-10-20 09:55:17 +08:00
waiwaylee d9a7bd0ee1 更新:1、优化调整临时航迹质量评估逻辑;
2、航迹起始时增加航迹质量门限;

Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-10-19 16:09:37 +08:00
waiwaylee 1ae0cf2512 更新:1、增加航迹质量信息输出;
2、航迹起始时计算初始航迹质量;
3、航迹关联时更新航迹质量;

Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-10-19 15:03:00 +08:00
waiwaylee 1b6cf62115 Merge branch 'master' into feature 2026-10-16 09:54:41 +08:00
waiwaylee 5f54d74ac3 Merge branch 'feature' 2026-10-14 09:25:02 +08:00
waiwaylee 3e6763c615 更新:1、更新扫描一圈判断条件,区分机扫和相扫不同的情况;
Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-10-14 09:24:49 +08:00
waiwaylee c01185bf9c Merge branch 'feature' 2026-10-13 14:50:10 +08:00
waiwaylee b4aed8a37b 更新:1、回调TWS外推时间差计算方法,避免TAS时同步出现TWS航迹的问题;
Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-10-13 14:49:48 +08:00
waiwaylee e98280409e 更新:1、调整临时航迹记录时机,在转为可靠航迹前记录,避免转为正式航迹后丢失当前帧的临时航迹记录;
Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-10-10 17:44:19 +08:00
waiwaylee 56b5449b9f 更新:1、修改TWS外推时,时间差计算方法,不再使用固定数据率计算时间差,而是使用最新时间戳来计算;
Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-10-09 14:04:22 +08:00
waiwaylee 5cb67ccef8 更新:1、增加TAS航迹信息、TAS关联信息记录到数据库;
Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-10-09 10:58:53 +08:00
waiwaylee 9f37fba3e7 Merge branch 'update' 2026-10-08 16:37:19 +08:00
waiwaylee 3cab2339d2 Merge branch 'feature' into update 2026-10-08 16:36:47 +08:00
waiwaylee a6a7877a86 更新:1、更新文档;
Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-10-08 16:13:15 +08:00
waiwaylee b227ae1957 更新:1、数据处理增加版本号输出;
2、增加Debug模式下可靠航迹、关联信息等数据写入数据库;

Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-10-08 16:01:53 +08:00
waiwaylee c3c7e668d2 更新:1、增加临时航迹录入数据库;
2、增加点迹信息的圈ID记录;

Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-09-30 16:33:18 +08:00
waiwaylee a4f32e4036 更新:1、新增数据记录功能,将过程数据录入数据库;
2、增加输入点迹数据、凝聚后点迹数据记录;

Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-09-30 09:02:28 +08:00
waiwaylee ff9b111d34 更新:1、升级编译器版本至MSVC2022,升级C++至C++17;
Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-09-29 11:00:43 +08:00
waiwaylee 2934e3b59b 更新:1、更新文档;
Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-09-29 10:25:56 +08:00
126 changed files with 322543 additions and 622 deletions
+1
View File
@@ -4,5 +4,6 @@ debug/
release/
build/
rdp_logs/
rdp_records/
design_docs/
.mnemon/
+10 -10
View File
@@ -2,10 +2,10 @@
"version": 4,
"configurations": [
{
"compilerPath": "D:\\APP\\VisualStudio2013\\VC\\bin\\cl.exe",
"cStandard": "c11",
"cppStandard": "c++11",
"intelliSenseMode": "windows-msvc-x86",
"compilerPath": "D:\\APP\\VisualStudio2022\\VC\\Tools\\MSVC\\14.44.35207\\bin\\Hostx64\\x64\\cl.exe",
"cStandard": "c17",
"cppStandard": "c++17",
"intelliSenseMode": "windows-msvc-x64",
"defines": [
"WIN32",
"_WINDOWS",
@@ -20,14 +20,14 @@
"${workspaceFolder}/data_process_class_dll/Eigen",
"${workspaceFolder}/**"
],
"name": "X256_PS (MSVC2013 x86)",
"name": "X256_PS (MSVC2022 x64)",
"compileCommands": "${workspaceFolder}/build/X256_PS/compile_commands.json"
},
{
"compilerPath": "D:\\APP\\VisualStudio2013\\VC\\bin\\cl.exe",
"cStandard": "c11",
"cppStandard": "c++11",
"intelliSenseMode": "windows-msvc-x86",
"compilerPath": "D:\\APP\\VisualStudio2022\\VC\\Tools\\MSVC\\14.44.35207\\bin\\Hostx64\\x64\\cl.exe",
"cStandard": "c17",
"cppStandard": "c++17",
"intelliSenseMode": "windows-msvc-x64",
"defines": [
"WIN32",
"_WINDOWS",
@@ -42,7 +42,7 @@
"${workspaceFolder}/data_process_class_dll/Eigen",
"${workspaceFolder}/**"
],
"name": "X256_MS (MSVC2013 x86)",
"name": "X256_MS (MSVC2022 x64)",
"compileCommands": "${workspaceFolder}/build/X256_MS/compile_commands.json"
}
]
+2 -2
View File
@@ -3,11 +3,11 @@ buildType:
choices:
release:
short: Release
long: Release (MSVC2013 x86)
long: Release (MSVC2022 x64, C++17)
buildType: Release
debug:
short: Debug
long: Debug (MSVC2013 x86)
long: Debug (MSVC2022 x64, C++17)
buildType: Debug
scanMode:
+12 -1
View File
@@ -1,11 +1,22 @@
@echo off
rem ===========================================================================
rem CMake build script (MSVC 2022 / v143, C++17, x64)
rem
rem Usage: build.bat <build-dir>
rem e.g. build.bat build\X256_PS
rem
rem Encoding: keep this file ASCII-only (see msvc_env.bat).
rem ===========================================================================
setlocal
set "BUILD_DIR=%~1"
if "%BUILD_DIR%"=="" (
echo Usage: build.bat ^<build-dir^>
exit /b 1
)
call D:\APP\VisualStudio2013\VC\vcvarsall.bat x86 >nul
rem Load the MSVC 2022 environment (vcvarsall.bat + cl.exe existence check).
call "%~dp0msvc_env.bat"
if errorlevel 1 exit /b %errorlevel%
cmake --build "%BUILD_DIR%"
exit /b %errorlevel%
+12 -1
View File
@@ -1,11 +1,22 @@
@echo off
rem ===========================================================================
rem CMake clean script (MSVC 2022 / v143, C++17, x64)
rem
rem Usage: clean.bat <build-dir>
rem e.g. clean.bat build\X256_PS
rem
rem Encoding: keep this file ASCII-only (see msvc_env.bat).
rem ===========================================================================
setlocal
set "BUILD_DIR=%~1"
if "%BUILD_DIR%"=="" (
echo Usage: clean.bat ^<build-dir^>
exit /b 1
)
call D:\APP\VisualStudio2013\VC\vcvarsall.bat x86 >nul
rem Load the MSVC 2022 environment (vcvarsall.bat + cl.exe existence check).
call "%~dp0msvc_env.bat"
if errorlevel 1 exit /b %errorlevel%
cmake --build "%BUILD_DIR%" --target clean
exit /b %errorlevel%
+57 -5
View File
@@ -1,21 +1,73 @@
@echo off
rem ===========================================================================
rem CMake configure script (MSVC 2022 / v143, C++17, x64)
rem
rem Usage: configure.bat <build-dir> <PHASE_SCANNING^|MECHANICAL_SCANNING> [Debug^|Release] [AUTO^|ON^|OFF]
rem e.g. configure.bat build\X256_PS PHASE_SCANNING Release
rem configure.bat build\X256_PS Debug ON (Debug + data recording enabled)
rem
rem The 4th argument selects the optional data-recording debug feature
rem (CMake cache variable RDP_DATA_RECORD). When it is omitted, CMake's AUTO
rem default applies: Debug = enabled, Release = disabled. See
rem docs/data_record_debug.md.
rem
rem The generator stays "NMake Makefiles" (the choice made during the VS Code
rem migration); cl.exe is pinned to the MSVC 2022 x64 toolset.
rem
rem A build directory that was configured by an older compiler (for example the
rem MSVC 2013 cache) is removed first: neither its CMake cache nor its object
rem files can be reused across a compiler upgrade.
rem
rem Encoding: keep this file ASCII-only (see msvc_env.bat).
rem ===========================================================================
setlocal
set "BUILD_DIR=%~1"
set "SCAN_MODE=%~2"
set "BUILD_TYPE=%~3"
set "RECORD_MODE=%~4"
if "%BUILD_DIR%"=="" (
echo Usage: configure.bat ^<build-dir^> ^<PHASE_SCANNING^|MECHANICAL_SCANNING^> [Debug^|Release]
echo Usage: configure.bat ^<build-dir^> ^<PHASE_SCANNING^|MECHANICAL_SCANNING^> [Debug^|Release] [AUTO^|ON^|OFF]
exit /b 1
)
if "%SCAN_MODE%"=="" (
echo Usage: configure.bat ^<build-dir^> ^<PHASE_SCANNING^|MECHANICAL_SCANNING^> [Debug^|Release]
echo Usage: configure.bat ^<build-dir^> ^<PHASE_SCANNING^|MECHANICAL_SCANNING^> [Debug^|Release] [AUTO^|ON^|OFF]
exit /b 1
)
if "%BUILD_TYPE%"=="" set "BUILD_TYPE=Release"
call D:\APP\VisualStudio2013\VC\vcvarsall.bat x86 >nul
if "%RECORD_MODE%"=="" set "RECORD_MODE=AUTO"
rem Load the MSVC 2022 environment (vcvarsall.bat + cl.exe existence check).
call "%~dp0msvc_env.bat"
if errorlevel 1 exit /b %errorlevel%
rem Drop a build tree left behind by another toolset.
if not exist "%BUILD_DIR%\CMakeCache.txt" goto :configure
set "OLD_TOOLSET="
if exist "%BUILD_DIR%\.msvc_toolset" set /p OLD_TOOLSET=<"%BUILD_DIR%\.msvc_toolset"
if /i "%OLD_TOOLSET%"=="%MSVC_TOOLSET_VERSION%" goto :configure
echo [configure] "%BUILD_DIR%" was configured with MSVC toolset "%OLD_TOOLSET%" - rebuilding from scratch
rmdir /s /q "%BUILD_DIR%"
if exist "%BUILD_DIR%" (
echo [ERROR] cannot remove "%BUILD_DIR%", please delete it manually and retry.
exit /b 1
)
:configure
echo [configure] compiler : %MSVC_CXX_COMPILER%
echo [configure] build dir: %BUILD_DIR%
echo [configure] mode : %SCAN_MODE% / %BUILD_TYPE%
echo [configure] record : %RECORD_MODE% (RDP_DATA_RECORD: AUTO/ON/OFF)
pushd "%~dp0..\.."
cmake -S . -B "%BUILD_DIR%" -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DRADAR_SCAN_MODE=%SCAN_MODE%
cmake -S . -B "%BUILD_DIR%" -G "NMake Makefiles" ^
-DCMAKE_BUILD_TYPE=%BUILD_TYPE% ^
-DRADAR_SCAN_MODE=%SCAN_MODE% ^
-DRDP_DATA_RECORD=%RECORD_MODE% ^
-DCMAKE_CXX_COMPILER="%MSVC_CXX_COMPILER%"
set "CMAKE_EXIT=%errorlevel%"
popd
exit /b %CMAKE_EXIT%
if not "%CMAKE_EXIT%"=="0" exit /b %CMAKE_EXIT%
rem Remember which toolset produced this build tree (checked on the next run).
> "%BUILD_DIR%\.msvc_toolset" echo %MSVC_TOOLSET_VERSION%
exit /b 0
+50
View File
@@ -0,0 +1,50 @@
@echo off
rem ===========================================================================
rem Shared MSVC 2022 (v143) environment setup.
rem
rem Called by configure.bat / build.bat / clean.bat. It:
rem 1) locates the MSVC 2022 toolset and cl.exe (default Hostx64\x64);
rem 2) calls vcvarsall.bat to set PATH / INCLUDE / LIB (nmake comes with it).
rem
rem NOTE: this script does NOT use setlocal, so the variables it defines stay
rem in the caller's environment (all callers run under setlocal).
rem
rem Override the defaults with these environment variables:
rem VS2022_DIR Visual Studio 2022 install root
rem MSVC_TOOLSET_VERSION MSVC toolset version
rem MSVC_HOST_ARCH compiler host architecture (x64 / x86)
rem MSVC_TARGET_ARCH target architecture (x64 / x86)
rem
rem Exit code: 0 = ok, non-zero = setup failed (caller must abort).
rem
rem Encoding: keep this file ASCII-only (cmd.exe parses .bat in the OEM code
rem page, UTF-8 comments would break parsing on non-UTF-8 consoles).
rem ===========================================================================
if not defined VS2022_DIR set "VS2022_DIR=D:\APP\VisualStudio2022"
if not defined MSVC_TOOLSET_VERSION set "MSVC_TOOLSET_VERSION=14.44.35207"
if not defined MSVC_HOST_ARCH set "MSVC_HOST_ARCH=x64"
if not defined MSVC_TARGET_ARCH set "MSVC_TARGET_ARCH=x64"
set "MSVC_TOOLSET_DIR=%VS2022_DIR%\VC\Tools\MSVC\%MSVC_TOOLSET_VERSION%"
set "MSVC_CXX_COMPILER=%MSVC_TOOLSET_DIR%\bin\Host%MSVC_HOST_ARCH%\%MSVC_TARGET_ARCH%\cl.exe"
set "VCVARS_BAT=%VS2022_DIR%\VC\Auxiliary\Build\vcvarsall.bat"
if not exist "%VCVARS_BAT%" (
echo [ERROR] vcvarsall.bat not found: "%VCVARS_BAT%"
echo Please set VS2022_DIR to the Visual Studio 2022 install root.
exit /b 1
)
if not exist "%MSVC_CXX_COMPILER%" (
echo [ERROR] cl.exe not found: "%MSVC_CXX_COMPILER%"
echo Please check MSVC_TOOLSET_VERSION / MSVC_HOST_ARCH / MSVC_TARGET_ARCH.
exit /b 1
)
call "%VCVARS_BAT%" %MSVC_TARGET_ARCH% >nul
if errorlevel 1 (
echo [ERROR] "vcvarsall.bat %MSVC_TARGET_ARCH%" failed.
exit /b 1
)
exit /b 0
+8 -4
View File
@@ -2,11 +2,15 @@
"cmake.configureOnOpen": false,
"cmake.generator": "NMake Makefiles",
"cmake.buildDirectory": "${workspaceFolder}/build/X256_PS",
"cmake.configureArgs": [
"-DCMAKE_CXX_COMPILER=D:/APP/VisualStudio2022/VC/Tools/MSVC/14.44.35207/bin/Hostx64/x64/cl.exe"
],
"cmake.environment": {
"PATH": "D:\\APP\\VisualStudio2013\\VC\\BIN;D:\\APP\\VisualStudio2013\\Common7\\IDE;C:\\Program Files (x86)\\Windows Kits\\8.1\\bin\\x86;${env:PATH}",
"INCLUDE": "D:\\APP\\VisualStudio2013\\VC\\INCLUDE;D:\\APP\\VisualStudio2013\\VC\\ATLMFC\\INCLUDE;C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\shared;C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\um;C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\winrt;${env:INCLUDE}",
"LIB": "D:\\APP\\VisualStudio2013\\VC\\LIB;D:\\APP\\VisualStudio2013\\VC\\ATLMFC\\LIB;C:\\Program Files (x86)\\Windows Kits\\8.1\\lib\\winv6.3\\um\\x86;${env:LIB}"
"PATH": "D:\\APP\\VisualStudio2022\\VC\\Tools\\MSVC\\14.44.35207\\bin\\Hostx64\\x64;${env:PATH}",
"INCLUDE": "D:\\APP\\VisualStudio2022\\VC\\Tools\\MSVC\\14.44.35207\\include;D:\\APP\\VisualStudio2022\\VC\\Tools\\MSVC\\14.44.35207\\ATLMFC\\include;D:\\APP\\VisualStudio2022\\VC\\Auxiliary\\VS\\include;C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.26100.0\\ucrt;C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.26100.0\\um;C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.26100.0\\shared;C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.26100.0\\winrt;C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.26100.0\\cppwinrt;${env:INCLUDE}",
"LIB": "D:\\APP\\VisualStudio2022\\VC\\Tools\\MSVC\\14.44.35207\\ATLMFC\\lib\\x64;D:\\APP\\VisualStudio2022\\VC\\Tools\\MSVC\\14.44.35207\\lib\\x64;C:\\Program Files (x86)\\Windows Kits\\10\\lib\\10.0.26100.0\\ucrt\\x64;C:\\Program Files (x86)\\Windows Kits\\10\\lib\\10.0.26100.0\\um\\x64;${env:LIB}"
},
"C_Cpp.default.cppStandard": "c++11",
"C_Cpp.default.cppStandard": "c++17",
"C_Cpp.default.intelliSenseMode": "windows-msvc-x64",
"C_Cpp.default.compileCommands": "${workspaceFolder}/build/X256_PS/compile_commands.json"
}
+8 -8
View File
@@ -5,7 +5,7 @@
},
"tasks": [
{
"label": "CMake: 配置 X256_PS (Release, MSVC2013 x86)",
"label": "CMake: 配置 X256_PS (Release, MSVC2022 x64)",
"type": "shell",
"command": "${workspaceFolder}\\.vscode\\scripts\\configure.bat",
"args": [
@@ -16,7 +16,7 @@
"problemMatcher": []
},
{
"label": "CMake: 配置 X256_PS (Debug, MSVC2013 x86)",
"label": "CMake: 配置 X256_PS (Debug, MSVC2022 x64)",
"type": "shell",
"command": "${workspaceFolder}\\.vscode\\scripts\\configure.bat",
"args": [
@@ -60,7 +60,7 @@
"problemMatcher": []
},
{
"label": "CMake: 配置 X256_MS (Release, MSVC2013 x86)",
"label": "CMake: 配置 X256_MS (Release, MSVC2022 x64)",
"type": "shell",
"command": "${workspaceFolder}\\.vscode\\scripts\\configure.bat",
"args": [
@@ -71,7 +71,7 @@
"problemMatcher": []
},
{
"label": "CMake: 配置 X256_MS (Debug, MSVC2013 x86)",
"label": "CMake: 配置 X256_MS (Debug, MSVC2022 x64)",
"type": "shell",
"command": "${workspaceFolder}\\.vscode\\scripts\\configure.bat",
"args": [
@@ -118,7 +118,7 @@
"label": "CMake: 配置并构建 X256_PS (Release)",
"dependsOrder": "sequence",
"dependsOn": [
"CMake: 配置 X256_PS (Release, MSVC2013 x86)",
"CMake: 配置 X256_PS (Release, MSVC2022 x64)",
"CMake: 构建 X256_PS (Release)"
],
"group": {
@@ -131,7 +131,7 @@
"label": "CMake: 配置并构建 X256_PS (Debug)",
"dependsOrder": "sequence",
"dependsOn": [
"CMake: 配置 X256_PS (Debug, MSVC2013 x86)",
"CMake: 配置 X256_PS (Debug, MSVC2022 x64)",
"CMake: 构建 X256_PS (Debug)"
],
"group": "build",
@@ -141,7 +141,7 @@
"label": "CMake: 配置并构建 X256_MS (Release)",
"dependsOrder": "sequence",
"dependsOn": [
"CMake: 配置 X256_MS (Release, MSVC2013 x86)",
"CMake: 配置 X256_MS (Release, MSVC2022 x64)",
"CMake: 构建 X256_MS (Release)"
],
"group": "build",
@@ -151,7 +151,7 @@
"label": "CMake: 配置并构建 X256_MS (Debug)",
"dependsOrder": "sequence",
"dependsOn": [
"CMake: 配置 X256_MS (Debug, MSVC2013 x86)",
"CMake: 配置 X256_MS (Debug, MSVC2022 x64)",
"CMake: 构建 X256_MS (Debug)"
],
"group": "build",
+57
View File
@@ -9,6 +9,24 @@
### Added
### Changed
### Fixed
### Removed
## [1.5.6] - 2026-10-08
> 本次发布把上一轮「工作区(未提交)」条目整体归档,并补充 a4f32e4 / c3c7e66 / b227ae1 三次提交的改动。
> 版本号宏位于 `parameters.h`(`MAJOR_VERSION` / `MINOR_VERSION` / `PATCH_VERSION`),当前为 **1.5.6**。
### Added
- **新增版本号输出(b227ae1)**:`parameters.h` 新增版本宏组——`MAJOR_VERSION` / `MINOR_VERSION` / `PATCH_VERSION`(当前 1 / 5 / 6)、扫描体制后缀 `VERSION_INFO`(相扫 `PS` / 机扫 `MS`)以及拼装宏 `VERSION_STRING`(`STRINGIFY` 两级宏展开,输出形如 `V1.5.6.PS`)。`Data_process_class_dll` 基类新增公开成员 `const char* version_info`,`Data_Process_Factory::GetB()` 创建实例后赋值为 `VERSION_STRING`;宿主可在拿到实例指针后直接读取该字段上报/核对 DLL 版本。
- **数据记录表扩充为 6 张在用表(7 个实体定义)(a4f32e4 / c3c7e66 / b227ae1)**:记录实体由「2 个示例」扩展为覆盖主要中间数据的 6 张在用表——`RdpRecPoint`(TWS/TAS 输入点迹,用 `point_type` 区分,逐点写入)、`RdpRecPointCohesionTWS` / `RdpRecPointCohesionTAS`(凝聚后点迹,分别写入)、`RdpRecTempTrack`(临时航迹,`track_init_process_logic()` 末尾在消亡临时航迹之后逐点写入)、`RdpRecTrustTrack`(可靠航迹,TWS 输出循环内按本圈更新航迹逐条写入)、`RdpRecAssoInfo`(点航关联信息,TWS 关联的候选/取中/删表三处写入)。原示例实体 `RdpRecTrackOut`(本圈输出航迹)与其在 `track_process()` 中的批量记录点已整体注释停用(实体定义保留注释,故 `rdp_record_entity.h` 中共 7 个实体定义),改由 `RdpRecTrustTrack` 记录内部真实航迹。
- **点迹/航迹记录新增 ID 与圈次维度(c3c7e66、b227ae1)**:`Data_Process` 新增自增成员 `point_id` / `frame_id` / `circle_id`(`data_process.h`)——每接收一帧 `frame_id` 自增;每个点迹 `point_id` 自增并随 `PointRecv` 落库;`circle_id` 在 TWS 满一圈(`Beam_index_aiz == 0`)、TAS 每帧处理结束时自增,写入该圈/帧的所有点迹。内部结构同步新增标识字段:`PointRecv` 增 `circle_id`;`Temp_track` 增 `temp_track_id` / `point_id` / `circle_id`(`Track_Init` 新增自增成员 `temp_track_id`);`Trust_Track` 增 `track_id` / `point_id` / `circle_id`(起批时取自临时航迹末点,关联成功时刷新为被关联点迹的 ID)。据此可把点迹、凝聚点迹、临时航迹、可靠航迹、关联信息按 `point_id` / `track_id` / `circle_id` 串成一条完整链路。
- **新增 `Asso_Info` 关联信息结构体(b227ae1)**:原 `Track_Asso::model_filter()` 内定义的局部结构体 `asso_info` 提升为 `struct.h` 中的全局 `Asso_Info`,在原有 `d1/d2/d3/d_min/point_index/track_index` 基础上补充 `point_id` / `track_id` / `circle_id`、门限相关量(`delta_T` / `delta_h` / `R` / `VT`、三个布尔量 `thd_d` / `thd_h` / `thd_r`)以及「本候选是否被最近邻取中」标志 `min_d_flag`,使关联信息可被完整记录与分析。
- **可靠航迹补充极坐标量快照(b227ae1)**:`Trust_Track` 新增 `Range` / `Azimuth` / `Elevation` / `Velocity` 四个字段,在 TWS 输出环节由本圈写入输出结构的距离/方位/俯仰/合速度回填(`Velocity` 取 `Range_V`,即 IMM 融合速度的模),供记录表落库;注意它们只在航迹当圈**有输出**时被刷新。
- 新增 CHANGELOG.md,采用 Keep a Changelog 格式记录项目变更历史。
- `RadarPara` 结构体新增 `DATA_RATE_TAS`(TAS 模式数据率)字段,由 DLL 调用者通过 `track_process_parameters_initial()` 输入(b3e2f52)。
- `Tracking_Target` 新增 `last_track_time` 字段,记录各 TAS 目标最近一次波束输出时间(f438ac5)。
@@ -24,10 +42,28 @@
- 输出航迹补充「靠近/远离」方向标志 `Flag_Direction`(f249fdc):TWS 关联输出(`track_asso.cpp`)与 TAS 关联输出(`track_asso_tas.cpp`)在写入 `vr_point` 后追加 `Flag_Direction = vr_point > 0 ? 1 : 0`,即径向速度为正判为靠近(1)、为零或负判为远离(0)。该字段此前已在 `data_process_class_dll.h` 的 `Track` 结构体中声明(注释「方向标志 1 靠近/0 远离」)但从未被赋值,宿主读到的是未初始化/旧值。
- 新增三份流程与专项文档(f249fdc):`docs/data_process_flow_report.md`(数据处理流程与关键逻辑说明:总体数据流、前处理与判圈、凝聚、TWS/TAS 关联与滤波、航迹起始、外推消亡与航迹号管理、TAS 波束控制、参数脉络与关键约定)、`docs/data_process_flow_report.html`(同内容的 HTML 渲染版)、`docs/track_init_readme.md`(航迹起始 Track Init 详细逻辑说明:分阶段流程、起始条件汇总、参数调参、输出字段映射、已知风险与代码索引)。
- 更新 README.md(7d5a56f、f249fdc):目录结构新增 `docs/` 目录;2、7.2、8.1 节满圈判据改为「方位波位号 == 0」并说明 `last_beam_num` 不再参与判断;8.3、9.3 节改写点-航迹头关联判据与起批输出历史点数;9.6 节补充方向标志输出与点迹消费说明;2、10.1 节 `RDP_LOG_ENABLE` 说明改为当前实际状态(注释停用、默认不重定向);11.4 节新增航迹起始未修复观察;版本历史表补充 7d5a56f、f249fdc,参考文档表补充 `docs/` 三份文档。
- 新增手动重点目标接口(50e0b5c):`data_process_class_dll.h` 增加 `primary_target_add(int track_index)`(将指定批号转为重点目标)与 `primary_target_cancel(int track_index)`(取消重点目标)两个虚接口(基类为空实现、返回 0),`Data_Process` 中按批号把 `Trust_Track::primary_target_flag` 置 1 / 清 0;重点航迹的消亡改用独立阈值 `TRACK_DIE_ROUND_PRIMARY`(= 10,两体制相同),普通航迹仍用 `TRACK_DIE_ROUND`。
- 新增未关联航迹的外推圈数上限宏(50e0b5c,`parameters.h`):`MAX_EXTRAPOLATE_NUM = 3`(TWS 通道,两体制相同)、`MAX_EXTRAPOLATE_NUM_TAS`(TAS 通道,相扫 5 / 机扫 48)。
- 输入/输出结构新增「归一化幅度」与「速度维 FFT 点数」相关字段(e8c56f9):`DataRev` 增加 `amp_asv`(目标归一化幅度 dB)与 `prn_fftn`(速度维 FFT 点数);`Track` 增加 `amp_asv_point` / `prn_fftn_point`;内部 `PointRecv`、`Trust_Track` 同步增加对应字段,`track_asso.cpp` 在关联实点时写入航迹、TWS 输出航迹时写回输出结构。⚠ 当时输入侧未打通(该描述后于本次发布更正为「已打通」,见 Changed 末条):`data_preprocess()` 未把 `DataRev.amp_asv` / `DataRev.prn_fftn` 拷入内部 `PointRecv`;**TAS 通道至今未涉及这两个字段**,TAS 输出中它们仍无有效数据来源(详见 README 11.2)。
- 更新 README.md(本次文档更新):2 节补充重点目标/外推上限/输出门控与结构体字段扩展;3 节把 `requirements.md` 移回仓库根目录;5.2、6.1、6.2、7.1、8.1、8.2、9.6、9.9、10.1、11.1、11.2、12、13 节按源码同步 `Flag_Direction` 新判据、外推圈数上限、消亡圈数(普通 6 / 重点 10 / TAS 相扫 10、机扫 96)、`Track_Update_Flag` 输出门控、新增字段与结构体布局变更提示;12 节版本历史补充 50e0b5c、fc49707、42b598e、e8c56f9 四条记录。
- 同步 `docs/data_process_flow_report.md` 与 `docs/track_init_readme.md`(本次文档更新):前者更新总体数据流表(⑥ 外推/消亡)、4.7 外推、6.1 消亡条件、7.2 TAS 外推门控、9.1 参数表与第 10 节观察;后者更新第 14 节 `Flag_Direction` 判据并补充 `amp_asv_point` / `prn_fftn_point` 两个未赋值字段。
- **新增数据记录调试功能(可选编译)**:新增 `rdp_record.h`(`rdp_record::Init()/Shutdown()/db_path()` 与 `RDP_RECORD`/`RDP_RECORD_BATCH` 记录点宏;功能关闭时宏展开为 `((void)0)`、实参不求值,头文件不含任何第三方依赖)、`rdp_record_entity.h`(记录实体=数据表定义区,含 2 个示例实体与 `RDP_RECORD_DECLARE` 实例化声明宏)、`rdp_record.cpp`(实体注册区:`YLT_REFL` + `RDP_RECORD_DEFINE`;pimpl 隔离 ormpp/SQLite;首次写入自动 `CREATE TABLE IF NOT EXISTS`;单条写入与 `std::vector` 批量事务写入;建表/插入失败每个实体只提示一次并降级为空操作;内部互斥锁串行化单连接)。`data_process_class_dll.cpp` 的 `Data_Process_Factory::GetB()/Destroy()` 分别调用 `rdp_record::Init()/Shutdown()`(关闭时为空实现),`parameters.h` 新增记录目录宏 `RDP_RECORD_DB_DIR`(`.\rdp_records\`),`.gitignore` 增加 `rdp_records/`。(实体定义区当时的「2 个示例实体」已扩展为 7 个,见本版 Added 首条。)
- 新增 2 个示例记录点(`data_process.cpp`,字段为示例可按需增删):① `data_preprocess()` 的 TWS 分支逐点写入表 **`RdpRecPointTws`**(输入点迹字段:CPI/GNSS 时间 ms、方位/俯仰波位、PRF/频点、点迹号/总数、距离/方位/俯仰/速度/幅度/SNR/RCS 等,当前实体为 22 个字段);② `track_process()` 的 TWS/TAS 处理帧结束后批量写入表 **`RdpRecTrackOut`**(输出航迹 20 个字段,两种模式共用一张表并用 `track_mode` 区分;每行取输出数组 `[i][0]`,行数超过 `MAX_TRACK_NUM` 时钳位)。⚠ 这两个实体名已随 a4f32e4 / b227ae1 演进:输入点迹表更名并扩展为 `RdpRecPoint`(TWS/TAS 共用、增 `point_id`/`frame_id`/`circle_id`),输出航迹示例整体停用、改由 `RdpRecTrustTrack` 记录内部航迹(详见本版 Added 首条的 6 张在用表清单)。
- 内置第三方源码 `third_party/ormpp/`(按上游原样保存,含 `UPSTREAM.md` 记录来源与升级方式):ormpp 0.2.0(commit `acf23989…`,Apache-2.0)+ 随其内置的 iguana 1.0.5、frozen 与 SQLite 3.49.1 amalgamation(公有领域),并保留两份 LICENSE 文件(与上游 git blob 逐字节一致)。
- 新增 `docs/data_record_debug.md`(数据记录调试功能专项说明):开关与构建(`RDP_DATA_RECORD` 取值、`configure.bat` 第 4 参数、记录模块以 C++20 独立编译与 C 编译器要求、关闭时零代码保证)、运行期行为(记录库目录与文件名、`synchronous=OFF` 取舍、`Init`/`Shutdown` 时机、失败降级、单连接 + 互斥锁与线程模型)、记录实体(表)约定(全局命名空间、结构体名即表名、首列 `int64_t id` 自增主键、类型映射表、**禁止数组字段**及原因)、**新增记录点四步配方**(含 `RDP_RECORD_DECLARE` → `YLT_REFL` + `RDP_RECORD_DEFINE` → 记录点写法 → 自动建表,以及「忘记注册报 LNK2019」自检点)、当时的 2 个示例记录点(现已扩展为 6 张在用表 + 1 个停用实体,见本版 Added 首条)、Python/DB Browser 查询示例与已知限制。
- 更新 README.md(本次文档更新):2 节新增「数据记录调试功能(可选编译)」特性条目;3 节目录结构补充 `third_party/ormpp/`、`rdp_record.h/.cpp`、`rdp_record_entity.h`、`docs/data_record_debug.md`、`rdp_records/` 并更新 `requirements.md` 说明;4.1 节环境要求补充内置依赖与 C 编译器;4.3 节补充 `RDP_DATA_RECORD` 与 `configure.bat` 第 4 参数用法;4.4 节产物补充 `rdp_record.lib`;4.5 节新增「第三方库告警例外(仅记录功能启用时)」;5.2 节模块职责新增 `rdp_record` 行;10.1 节编译期宏新增 `RDP_RECORD_ENABLE` / `RDP_RECORD_DB_DIR`;11.3 节补充「记录功能默认只在 Debug 启用」;12 节版本历史新增本次工作区条目;13 节参考文档新增 `docs/data_record_debug.md` 与 `third_party/ormpp/UPSTREAM.md`、更新 `requirements.md` 行。
### Changed
> 以下条目为上一轮「工作区(未提交)」状态的改动,随本次 1.5.6 发布一并归档(内容未改写)。
- TAS 波束控制改为按固定数据率调度:`Beam_Ctrl()` 仅当目标满足 `1 / DATA_RATE_TAS` 秒的数据率间隔(相对航迹时间 `T_track` 与上次波束输出 `last_track_time`)时才输出 TAS 跟踪波束,使 TAS 目标数据率不再随波束时间(Beam_Ctrl 调用频率)变化。
- `CMakeLists.txt` 新增缓存变量 `RDP_DATA_RECORD`(`AUTO`/`ON`/`OFF`,AUTO = Debug 启用 / Release 关闭;多配置生成器下 AUTO 直接 `FATAL_ERROR` 要求显式取值)与可选静态库 `rdp_record`:该目标以 `CXX_STANDARD 20` 独立编译(ormpp 要求 `CMAKE_CXX_STANDARD 20`,而内置 Eigen 3.3.7 在 `/std:c++20` 下会因 `std::result_of` 被 MSVC 移除而编译失败,故**雷达处理代码保持 C++17**)、经 `enable_language(C)` 编译内置 sqlite3 amalgamation(`sqlite3.c` 以源文件级 `/w` 单独降级第三方告警)、`RDP_RECORD_ENABLE` 与 `ORMPP_ENABLE_SQLITE3` 经 `PUBLIC` 传递给 DLL 并将静态库链接进 DLL;关闭时不添加任何目标、`SOURCES` 列表保持不变(Release 零痕迹的关键)。配置阶段打印启用/关闭状态。
- `parameters.h` 新增记录库目录宏 `RDP_RECORD_DB_DIR`(`.\rdp_records\`,相对宿主进程当前工作目录,目录不存在时自动创建)与开关说明注释:记录开关由构建系统定义 `RDP_RECORD_ENABLE`,**不提供手工开关宏**(避免 Release 误开)。
- `configure.bat` 新增可选第 4 参数 `AUTO|ON|OFF`(缺省 `AUTO`,即 CMake 侧默认值)并作为 `-DRDP_DATA_RECORD` 传入,回显新增 `record` 行;脚本仍保持纯 ASCII 编码。
- `.gitignore` 增加 `rdp_records/`(数据记录库输出目录)。
- `requirements.md` 内容更新为数据记录调试功能需求(结构体经 ormpp 映射写入 SQLite、Release 版可屏蔽、数据分类入不同表、宏记录点、便于快速新增结构体、不使用异步写入),本功能框架已按该说明实现。
- 本次改动的编译与运行验证:X256_PS/X256_MS × Release(记录关闭)/Debug(记录启用)四种配置均 **0 warning、0 error**;Release 产物的导出表与依赖表与改动前**逐行一致**、DLL 体积不变(PS 178,688 / MS 178,176 字节)、构建树中无记录模块目标文件、二进制内无 `sqlite` 字样;Debug(AUTO→启用)下 DLL 产物为 3,611,648 字节并额外生成 `rdp_record.lib`;运行时用临时校验程序(不提交)配合 Python `sqlite3` 验证:建库成功、一实体一表、单条与批量(事务)写入行数与取值正确、`id` 自增、`PRAGMA integrity_check` = ok、无 `-journal`/`-wal` 残留;记录目录无法创建时 DLL 接口照常返回、仅打印一次错误、后续写入静默忽略。
- TAS 波束输出目标改为队列内调度:不再固定取队首,而是遍历 TAS 队列,选择满足数据率门控且 CPI 时间最早的目标输出;原每次 Beam_Ctrl 末尾的整队循环移位(队列轮转)已停用(代码注释保留)。
- 数据率门控统一使用运行时参数 `Work_Parameter.DATA_RATE_TAS`(由 DLL 调用者经 `track_process_parameters_initial()` 输入,需为 >0 的有效值);`parameters.h` 中 `DATA_RATE_TAS` 编译期宏(相扫 0.3 / 机扫 0.0625)已注释停用,引导跟踪外推同步改用运行时参数。
- 编译时以 `#pragma message` 提示当前启用的扫描体制宏(MECHANICAL_SCANNING / PHASE_SCANNING)。
@@ -44,17 +80,38 @@
- 关联波门 `ASSO_THORD` 由 3 调大至 4(e0bf227,`parameters.h` 相扫/机扫两组宏同步):判据写作 `d*d < ASSO_THORD*ASSO_THORD`,而 `d` 本身已是马氏距离平方,等效门限由 `d < 3` 放宽为 `d < 4`(TWS 分距离段的比例门限随之由 9/1000、9/100 变为 16/1000、16/100),配合高度门限判定提高机动目标的关联概率;调参时仍需注意这一层「二次方」语义(BUG-20 有意保留)。
- TWS 与 TAS 关联的高度门限统一为同一组按航迹距离分段的取值(e0bf227):`r_track ≤ 1000 m` 时 `|h_track − h_point| ≤ 150 m`、`1000~2000 m` 时 ≤ 200 m、`2000~3000 m` 时 ≤ 250 m、`3000~5000 m` 时 ≤ 300 m、`> 5000 m` 不约束。其中 TWS 侧为该门限首次生效(原代码块被注释),TAS 侧 `2000~3000 m` 段由 200 m 放宽到 250 m。
- TAS 波束输出的数据率门控条件修正(e0bf227,`tas_ctrl.cpp::tas_beam_output()`):原判据同时要求「距上次波束输出超过 DATA_RATE_TAS」与「航迹时间 T_track 与最新时间戳之差超过 DATA_RATE_TAS」,后者与「选取 CPI 时间最早目标」的语义冲突,会额外抑制本可输出的目标;现仅保留 `delta_T_last_track = (latest_timestamp − last_track_time)/1000 > DATA_RATE_TAS` 一条门控,`delta_T` 改为在选中该目标后按 `(latest_timestamp − CPI_time)/1000` 计算并用于外推,使 TAS 目标输出间隔严格跟随数据率。
- 输出航迹改为「只输出本圈有更新的航迹」(50e0b5c):TWS `track_asso_process()` 与 TAS `track_asso_process_tas()` 的输出循环新增 `Track_Update_Flag == 1` 门控,输出后立即清零;该标志在关联到实点或外推成功时置 1。此前每个 TWS 周期都会把航迹表中的全部可靠航迹各输出一行(`Point_Sum = 1`),宿主不能再据输出行数推断航迹总数(当圈新起批的航迹仍由 `track_init` 的输出路径直接写入)。
- TWS 未关联航迹外推增加圈数上限(50e0b5c,`track_asso.cpp::model_filter()`):仅当 `Extrapolate_round <= MAX_EXTRAPOLATE_NUM`(=3)时按 `DATA_RATE_SHORT/MIDDLE/FAR` 预测一帧、推进航迹时间并置 `Track_Update_Flag = 1`;超过上限后不再推进位置与时间,只累加 `Extrapolate_round`、置 `point_flag = 0` 与 `Track_Update_Flag = 0`(该航迹此后不再输出,位置冻结在最后一次预测处),直到重新关联到实点才恢复。避免长时间未关联的航迹被反复外推到远处。
- TAS 未关联航迹外推同样增加上限(50e0b5c,`track_asso_tas.cpp`),但判定方向与 TWS 相反:`Extrapolate_round >= MAX_EXTRAPOLATE_NUM_TAS`(相扫 5 / 机扫 48)时才做预测、推进航迹时间并输出,未达该轮数时只累加 `Extrapolate_round` 且不输出(位置与航迹时间停在最后一次关联处),达到上限后才恢复逐帧外推输出(见 README 11.2 的观察)。
- 航迹消亡圈数与判据调整(50e0b5c,`parameters.h`、`track_die.cpp`、`track_die_tas.cpp`):`TRACK_DIE_ROUND` 由 4 调整为 6(两体制),新增重点航迹专用 `TRACK_DIE_ROUND_PRIMARY = 10`,`TRACK_DIE_ROUND_TAS` 相扫由 5 调整为 10(机扫保持 96);消亡判据由 `Extrapolate_round >= 阈值` 改为 `> 阈值`,并把 `manual_delete_flag == 1` 与轮数条件合并为统一的 `die_flag` 判定,TWS 侧按 `primary_target_flag` 在 6 / 10 之间二选一。因每圈未关联只自增 1,普通 TWS 航迹实际连续 7 圈无实点关联才消亡(重点航迹 11 圈,TAS 相扫 11 圈 / 机扫 97 圈)。
- 「靠近/远离」方向标志 `Flag_Direction` 改为按滤波速度矢量判定(fc49707 引入、42b598e 调整,`track_asso.cpp` 与 `track_asso_tas.cpp` 同步):不再使用受多普勒速度模糊影响、符号可能取反的实测径向速度(原 `Flag_Direction = vr_point > 0 ? 1 : 0`),改为用航迹 IMM 融合状态的速度方向角 `Direction_Angle = atan2(X[4], X[1])` 与位置矢量判定:`acos((-x·cos(Direction_Angle) − y·sin(Direction_Angle)) / r) < π/2` 时置 1(靠近),否则 0(远离),其中 `x`/`y` 为经 `Sys_delay` 补偿后的位置、`r` 为其模。该判据等价于「滤波速度矢量的径向分量指向雷达(径向接近)即判为靠近」;中间版本曾按合速度归一化并把门限取为 π/4(45° 锥角),42b598e 去掉归一化并把门限放宽到 π/2(90°)。
- 手动操作接口遍历命中目标后立即 `break`(fc49707,`data_process.cpp`):`primary_target_add` / `primary_target_cancel` / `tracking_start` / `tracking_stop` 在写入标志后退出循环,不再继续扫描整张航迹表(批号唯一,结果等价)。
- `requirements.md` 由 `archived_docs/` 移回仓库根目录(工作区改动,文件内容未变):README 目录结构(第 3 节)与参考文档表(第 13 节)中的引用路径同步改回 `requirements.md`。
- **构建工具链升级:编译器由 Microsoft Visual C++ 2013(Visual Studio 12,`_MSC_VER` 1800,x86)升级为 Microsoft Visual C++ 2022(v143 工具集,`_MSC_VER` 1944,cl 19.44.35228,x64),C++ 标准由 C++11 提升为 C++17**。`CMakeLists.txt` 的 `cmake_minimum_required` 由 3.10 提升到 3.21(VS2022 生成器自 3.21 起支持),新增 `CMAKE_CXX_STANDARD 17`(含 `CMAKE_CXX_STANDARD_REQUIRED ON`)与「必须使用 MSVC 2022(`_MSC_VER >= 1930`)」的配置期强校验(不满足直接 `FATAL_ERROR`),并在配置阶段打印实际编译器路径、`_MSC_VER` 与所选 C++ 标准。
- `CMakeLists.txt` 编译选项调整:`/utf-8` 由「MSVC >= 1900 条件追加」改为对 MSVC 无条件启用;新增 `/Zc:__cplusplus`(MSVC 默认把 `__cplusplus` 报为 `199711L`,加该选项后正确报 `201703L`,使依赖该宏判断的第三方库——内置 Eigen 3.3.7——走标准分支);显式追加 `/W3`(CMake 3.15 起策略 CMP0092 不再把 `/W3` 放入 `CMAKE_CXX_FLAGS`,显式恢复以保持原有警告级别);`/Zc:wchar_t`、`/FS` 与 Release 专用的 `/Zc:strictStrings` 保持不变。
- **全部构建脚本改为 MSVC 2022**:新增 `.vscode/scripts/msvc_env.bat`(定位 VS2022 工具集与 `cl.exe`、校验路径存在后调用 `vcvarsall.bat x64`,可用 `VS2022_DIR` / `MSVC_TOOLSET_VERSION` / `MSVC_HOST_ARCH` / `MSVC_TARGET_ARCH` 环境变量覆盖默认值),`configure.bat` / `build.bat` / `clean.bat` 统一改为调用该脚本并显式传入 `-DCMAKE_CXX_COMPILER`;四个脚本一律保持**纯 ASCII 编码**(cmd.exe 按 OEM 代码页解析 .bat,中文注释会破坏其解析)。
- `configure.bat` 新增「旧工具集构建树自动清理」:构建目录存在 `CMakeCache.txt` 但缺少 `.msvc_toolset` 标记(或标记与当前工具集版本不一致)时,先整体删除该目录再重新配置。原因是 MSVC 2013 的 CMake 缓存与目标文件不能跨编译器复用——nmake 仅按时间戳判断,会把旧目标文件当作最新而跳过重编译,导致新旧工具集目标文件混链。
- VS Code 工程配置同步为 MSVC 2022 x64 + C++17:`settings.json` 的 `cmake.environment` 改为 VS2022 v143 x64 与 Windows SDK 10.0.26100.0 的 `PATH`/`INCLUDE`/`LIB`,新增 `cmake.configureArgs` 固定 `cl.exe` 路径,`C_Cpp.default.cppStandard` 改为 `c++17` 并新增 `C_Cpp.default.intelliSenseMode`;`c_cpp_properties.json` 的 `compilerPath` 指向新 `cl.exe`、`intelliSenseMode` 改为 `windows-msvc-x64`、配置名改为「X256_PS/X256_MS (MSVC2022 x64)」;`cmake-variants.yaml` 与 `tasks.json` 的变体/任务描述同步为 MSVC2022 x64。
- 产物架构由 x86 变为 **x64(AMD64)**:宿主程序必须为 64 位、并使用同一版本头文件重新编译后才能加载新 DLL(`rdp_playback.exe` 回放工具同样需升级为 x64 版本)。
- TWS 关联中 ΔT 判定的分支结构理顺(b227ae1,`track_asso.cpp::model_filter()`):原写法为 `if (delta_T <= 0) { 空分支 } else { IMM/EKF 更新 }`,现改为 `if (delta_T > 0) { 置 min_d_flag、IMM/EKF 更新 }`,行为与原来等价(ΔT > 0 才做滤波更新,ΔT ≤ 0 仍只消费点迹),但空分支被移除、可读性提高;原 ΔT≤0 的告警日志注释块一并删除。
- 记录点由「输出结构」改为「内部结构」(b227ae1):`data_process.cpp` 中按输出数组批量记录航迹的辅助函数 `rdp_record_output_tracks()` 与 `RDP_RECORD_BATCH` 调用整体移除,改在 `track_asso.cpp` 的输出循环内对内部 `Trust_Track` 逐条 `RDP_RECORD(RdpRecTrustTrack(...))`——记录的是航迹的完整内部状态(含 IMM 的 `X`/`P`/`X1`~`X3`/`P2`/`P3`/`u`),不再受输出数组容量与列映射的限制;范围仍限于本圈有输出(`Track_Update_Flag==1`)的航迹,且仅 TWS 通道。
- `Temp_track` 字段命名规范化(c3c7e66,`struct.h`):`snr` → `SNR`、`Amp` → `amplitude`、`T` → `CPI_time`、`Temp_track_section_idx` → `temp_track_section_idx`,并新增 `temp_track_id` / `point_id` / `circle_id` / `pitch_num`;`track_init.cpp`、`track_init_direct_tracking.cpp` 中的引用同步更名(含注释停用代码)。
- 输入点迹的两个「新字段」已打通(实际始于 a4f32e4 的 `data_process.cpp` 改写,本次发布确认并更正文档):`data_preprocess()` 现已把 `DataRev.amp_asv` / `DataRev.prn_fftn` 拷入内部 `PointRecv`(TWS 与 TAS 两条分支均赋值),并随 `RdpRecPoint` 落库;此前 README/CHANGELOG 记录的「输入侧未打通、输出字段无有效数据来源」已不成立(TAS 关联/输出路径仍未写回 `Track` 的这两个字段,见 11.2)。
### Fixed
- 修正文档中「`data_preprocess()` 未搬入 `amp_asv` / `prn_fftn`」的过期描述(本次文档更新):README 11.2、`docs/data_process_flow_report.md`、`docs/track_init_readme.md` 三处同步更正为「已搬入」,并保留「TAS 输出路径未写回」这一仍然成立的部分。
- 修复 MSVC `/W3` 下全部 C4819 / C4018 / C4244 编译警告:源码文件改为带 BOM 的 UTF-8 编码;signed/unsigned 不匹配处改用 `size_t` 循环变量或显式 `static_cast<int>` 比较;double/float、__int64/double 等窄化转换改为 `static_cast` 显式转换,保持原有计算逻辑不变(涉及 `data_process.cpp`、`dot_coh.cpp`、`dot_coh_tas.cpp`、`tas_ctrl.cpp`、`track_asso.cpp`、`track_asso_tas.cpp`、`track_init.cpp`、`track_init_direct_tracking.cpp`、`track_index_mangement.cpp` 等)。
- 修复 `TAS_Ctrl::tas_beam_output()` 把航迹向量下标 `earliest_track_idx` 误赋给 `Tracking_beam->TAS_track_index` 的问题(1521592):现输出实际航迹号 `(*trust_track)[earliest_track_idx].Track_Index`,避免跟踪波束批号与目标不符。
- 修正 README.md(第 2、5.2、8.1、8.3、9.3、13 节)与 `docs/data_process_flow_report.md`(第 0、1、6.1~6.4 节、附录)中航迹起始的步骤顺序描述(纯文档勘误,无代码改动):两份文档原写作「航迹头(1 点)→ 点-航迹头关联(2 点)→ 点-临时航迹关联(≥3 点)→ 起批」,与 `track_init.cpp`(L49-126)每圈的实际执行顺序相反;现更正为「① 点迹先与 ≥2 点临时航迹关联(延长已有临时航迹)→ ② 再与 1 点航迹头关联(两点卡尔曼初始化出 2 点临时航迹)→ ③ 两个阶段都结束后统一删除被消费(`Use_Flag==1`)的点迹、剩余点迹逐条转为新航迹头 → ④ 长度达标的临时航迹经三点卡尔曼初始化转为可靠航迹」,并同步修正流程图与状态机;另补充两点此前的描述缺失:两个关联阶段互不排斥(航迹头阶段不检查临时航迹阶段留下的 `Use_Flag`,同一点迹可同时延长一条临时航迹并开启一条 2 点临时航迹)、两类关联都不做一对一分配(同一条临时航迹可被同圈多个点延长)。
- 修复速度模糊时「向站/背站」判断有误的问题(fc49707、42b598e):`Flag_Direction` 不再由受多普勒模糊影响、符号可能取反的实测径向速度 `vr_point` 决定,改为按航迹滤波速度矢量与雷达视线的几何关系判定(判据见 Changed),TWS、TAS 两通道行为一致。
- 修复升级到 MSVC 2022 后在 `/W3` 下新增报出的 **17 处 C4267**(`size_t` → `int` 窄化,可能丢失数据)告警:`track_asso.cpp`(7 处)、`track_init.cpp`(5 处)、`track_init_direct_tracking.cpp`(4 处)、`track_asso_tas.cpp`(1 处)、`tas_ctrl.cpp`(1 处)按项目既有约定改为 `static_cast<int>(...)` 显式转换,不改变任何计算逻辑、对外接口与结构体布局;被编辑的源文件均已恢复「带 BOM 的 UTF-8」编码,与仓库其余文件保持一致。修复后 X256_PS/X256_MS × Release/Debug 四种配置均为 **0 warning、0 error**。
### Removed
- 删除已注释停用的无用代码残留(d6cdbc0):`data_process.cpp` 中 TWS/TAS 分支的 `std::cout` 调试输出注释块,以及 `dot_coh.cpp` 中早期「1 km 以内 SNR < 20 dB 点迹过滤」的注释代码块。仅清理注释,不改变任何运行逻辑。
- 删除 `docs/data_process_flow_report.html`(e0bf227):该 HTML 渲染版与 `docs/data_process_flow_report.md` 内容重复且已明显滞后,保留价值不足;同内容的 HTML 已在 git 忽略的 `design_docs/` 内另行留档。README 目录结构、参考文档表与版本历史表中指向该文件的条目同步移除。
- 删除 `track_asso.cpp` TWS 输出循环中已注释停用的 `manual_tracking_flag == 0` 过滤代码块(fc49707):该过滤从未生效,删除后 TWS 输出仍会把手动转 TAS(`Track_Mode==1`)的航迹一并输出(BUG-25 未修复,见 README 11.2)。
## [1.5.5] - 2026-08-27
+131 -15
View File
@@ -1,8 +1,13 @@
cmake_minimum_required(VERSION 3.10)
cmake_minimum_required(VERSION 3.21)
# 雷达数据处理动态链接库
# 开发环境已由 Qt/qmake 迁移到 VS Code + CMake。
# 编译器保持为 Microsoft Visual C++ Compiler 12.0 (x86)。
# 编译器已由 Microsoft Visual C++ Compiler 12.0 (VS2013) 升级为
# Microsoft Visual C++ Compiler 2022(v143 工具集,_MSC_VER 19.4x,x64)。
# 语言标准同步提升为 C++17。
#
# 注意:VS2022 生成器("Visual Studio 17 2022")需要 CMake >= 3.21,
# 因此本工程的最低 CMake 版本随之提高;NMake/Ninja 生成器同样满足。
project(data_process_class_dll LANGUAGES CXX)
# 扫描模式:
@@ -16,6 +21,68 @@ if(NOT RADAR_SCAN_MODE STREQUAL "PHASE_SCANNING" AND NOT RADAR_SCAN_MODE STREQUA
message(FATAL_ERROR "RADAR_SCAN_MODE 必须为 PHASE_SCANNING 或 MECHANICAL_SCANNING,当前值:${RADAR_SCAN_MODE}")
endif()
# ---------------------------------------------------------------------------
# 数据记录调试功能开关(可选编译,详见 docs/data_record_debug.md)
# AUTO : Debug 构建启用、Release 构建关闭(默认;交付构建不做任何记录)
# ON : 强制启用(可用于 Release 排查,需自行承担性能开销)
# OFF : 强制关闭
# 启用时新增独立静态库 rdp_record:以 C++20 编译(ormpp 要求)、编译内置的 sqlite3
# amalgamation,并链接进 DLL;源码中的 RDP_RECORD 宏由 RDP_RECORD_ENABLE 控制,关闭时
# 展开为空操作(实参不求值),记录模块完全不参与编译。
# 依赖来源与升级方式见 third_party/ormpp/UPSTREAM.md。
# ---------------------------------------------------------------------------
set(RDP_DATA_RECORD "AUTO" CACHE STRING "数据记录调试功能:AUTO(Debug=ON/Release=OFF) / ON / OFF")
set_property(CACHE RDP_DATA_RECORD PROPERTY STRINGS AUTO ON OFF)
if(RDP_DATA_RECORD STREQUAL "AUTO")
if(CMAKE_CONFIGURATION_TYPES)
message(FATAL_ERROR "多配置生成器下 RDP_DATA_RECORD=AUTO 无法判定,请显式传入 ON 或 OFF")
elseif(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(RDP_RECORD_ENABLED TRUE)
else()
set(RDP_RECORD_ENABLED FALSE)
endif()
elseif(RDP_DATA_RECORD STREQUAL "ON")
set(RDP_RECORD_ENABLED TRUE)
elseif(RDP_DATA_RECORD STREQUAL "OFF")
set(RDP_RECORD_ENABLED FALSE)
else()
message(FATAL_ERROR "RDP_DATA_RECORD 必须为 AUTO / ON / OFF,当前值:${RDP_DATA_RECORD}")
endif()
if(RDP_RECORD_ENABLED)
message(STATUS "数据记录调试功能:启用(C++20 静态库 rdp_record + 内置 SQLite)")
else()
message(STATUS "数据记录调试功能:关闭(不编译记录模块,RDP_RECORD 为空操作)")
endif()
# ---------------------------------------------------------------------------
# 语言标准:C++17
# ---------------------------------------------------------------------------
# CMAKE_CXX_STANDARD 由 CMake 转换为 MSVC 的 /std:c++17(CMake >= 3.10 起支持)。
# CMAKE_CXX_EXTENSIONS OFF 在 MSVC 下没有对应开关(只影响 GCC/Clang 的 -std=gnu++xx),
# 保留它是为了在换用其它编译器时同样不启用语言扩展。
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# ---------------------------------------------------------------------------
# 编译器约束:必须使用 MSVC 2022(v143,_MSC_VER >= 1930)
# ---------------------------------------------------------------------------
if(NOT MSVC)
message(FATAL_ERROR "本工程仅支持 MSVC (cl.exe) 编译器,当前编译器:${CMAKE_CXX_COMPILER_ID}")
endif()
if(MSVC_VERSION LESS 1930)
message(FATAL_ERROR
"本工程要求 MSVC 2022 (v143) 及以上版本(_MSC_VER >= 1930),"
"当前 _MSC_VER=${MSVC_VERSION}(编译器:${CMAKE_CXX_COMPILER})。\n"
"请先运行 VS2022 的 vcvarsall.bat x64(见 .vscode/scripts/msvc_env.bat)后再配置。")
endif()
message(STATUS "编译器:${CMAKE_CXX_COMPILER}(MSVC _MSC_VER=${MSVC_VERSION})")
message(STATUS "C++ 标准:C++${CMAKE_CXX_STANDARD}")
# 导出编译命令,便于 VS Code C/C++ 插件 IntelliSense 使用。
# Visual Studio 生成器不支持该选项,因此仅对 Makefile/Ninja 生成器开启。
if(NOT CMAKE_GENERATOR MATCHES "Visual Studio")
@@ -47,6 +114,49 @@ set(PUBLIC_HEADERS
add_library(data_process_class_dll SHARED ${SOURCES})
# ---------------------------------------------------------------------------
# 数据记录模块(RDP_DATA_RECORD 启用时构建):独立静态库、C++20(ormpp 要求),
# 与雷达处理代码(C++17)通过 rdp_record.h 的「仅声明 + 显式实例化」跨标准边界。
# ---------------------------------------------------------------------------
if(RDP_RECORD_ENABLED)
enable_language(C) # 用于编译 sqlite3 amalgamation
add_library(rdp_record STATIC
data_process_class_dll/rdp_record.cpp
third_party/ormpp/thirdparty/sqlite3/sqlite3.c
)
set_target_properties(rdp_record PROPERTIES
CXX_STANDARD 20
CXX_STANDARD_REQUIRED ON
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
)
# RDP_RECORD_ENABLE 经 PUBLIC 传递给 DLL:data_process.cpp 等源码中的 RDP_RECORD 宏随之生效
target_compile_definitions(rdp_record PUBLIC
RDP_RECORD_ENABLE
ORMPP_ENABLE_SQLITE3
)
target_include_directories(rdp_record
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/data_process_class_dll
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/third_party/ormpp
${CMAKE_CURRENT_SOURCE_DIR}/third_party/ormpp/iguana
${CMAKE_CURRENT_SOURCE_DIR}/third_party/ormpp/ormpp
${CMAKE_CURRENT_SOURCE_DIR}/third_party/ormpp/thirdparty/sqlite3
)
target_compile_options(rdp_record PRIVATE
/W3
/utf-8
/Zc:__cplusplus
/FS
)
# 第三方 amalgamation 单独降到 /w(源文件级选项在编译命令行中位于目标级之后,优先生效)
set_source_files_properties(third_party/ormpp/thirdparty/sqlite3/sqlite3.c
PROPERTIES COMPILE_OPTIONS "/w")
target_link_libraries(data_process_class_dll PRIVATE rdp_record)
endif()
# 编译 DLL 时定义导出宏;引用 DLL 的工程不应定义该宏。
target_compile_definitions(data_process_class_dll PRIVATE
DATA_PROCESS_CLASS_DLL_LIBRARY
@@ -69,25 +179,31 @@ target_include_directories(data_process_class_dll PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/data_process_class_dll
)
# 原工程使用 MSVC 2013(Visual Studio 12)编译器;保持兼容,不强制更高 C++ 标准。
# 源码使用 C++11 特性(nullptr、类内成员初始化等),MSVC 2013 默认即可支持。
if(MSVC)
# 与原 qmake 生成的编译选项保持一致。
# 注意:MSVC 2013 的 Debug 运行库与 /Zc:strictStrings 同时使用会触发 STL 编译错误,
# 原 qmake 工程也仅在 Release 配置中使用该选项。
target_compile_options(data_process_class_dll PRIVATE
# 显式恢复 /W3 警告级别。
# CMake 3.15(策略 CMP0092)起不再把 /W3 放进 CMAKE_CXX_FLAGS,
# 而本工程此前(cmake_minimum_required 3.10)一直以 /W3 构建,
# README 4.5 记录的全部 C4819/C4018/C4244 告警清理也基于该级别,
# 因此这里显式指定,保持警告级别不变。
/W3
# MSVC 2022 完整支持 /utf-8(VS2015 Update 2 起),
# 源码本身为 UTF-8 with BOM,显式指定后中文字符串/注释不再受系统代码页影响(消除 C4819)。
/utf-8
# 让 __cplusplus 正确报告 201703L(MSVC 默认始终为 199711L)。
# C++17 下依赖 __cplusplus 判断的第三方库(Eigen 3.3.7 等)据此走标准分支。
/Zc:__cplusplus
/Zc:wchar_t
# NMake 生成器下并行/增量编译时统一 PDB 写入。
/FS
$<$<CONFIG:Release>:/Zc:strictStrings>
)
# 修复 C4819(源文件含当前代码页无法表示的字符)。
# /utf-8 从 VS2015 Update 2 开始支持;本工程使用 MSVC 2013 时无该选项,
# 通过为源码文件添加 UTF-8 BOM 解决 C4819,这里仅对支持 /utf-8 的新版 MSVC 开启。
if(MSVC_VERSION GREATER_EQUAL 1900)
target_compile_options(data_process_class_dll PRIVATE /utf-8)
endif()
# 与原 qmake 生成的编译选项保持一致:Release 配置额外开启严格字符串字面量检查。
# (原 MSVC 2013 因 Debug 运行库与 /Zc:strictStrings 冲突而仅限 Release,
# 升级到 MSVC 2022 后该限制已不存在,此处仍沿用“仅 Release”以保持行为不变。)
target_compile_options(data_process_class_dll PRIVATE
$<$<CONFIG:Release>:/Zc:strictStrings>
)
endif()
# 输出目录:build/bin 下为 DLL/PDB,build/lib 下为导入库。
+165 -49
View File
@@ -1,7 +1,9 @@
# 雷达数据处理 DLL(data_process_class_dll)
> X256 雷达数据处理动态链接库:将雷达输出的原始点迹(TWS/TAS)处理为稳定可靠的目标航迹,并给出 TAS 跟踪波束控制指令。
> 仓库 simianzhen256-v1.5.5-ms-vscode(上游 simianzhen256,v1.5.5;已由 Qt/qmake 工程迁移为 VS Code + CMake 工程)。
> 仓库 simianzhen256-v1.5.5-ms-vscode(目录名沿用旧版本,当前代码版本为 **V1.5.6**;已由 Qt/qmake 工程迁移为 VS Code + CMake 工程,编译器为 MSVC 2022 / v143,C++17,x64 目标)。
>
> 版本号由 `parameters.h` 的 `MAJOR_VERSION` / `MINOR_VERSION` / `PATCH_VERSION`(当前 1.5.6)与体制后缀拼装为 `VERSION_STRING`(相扫 `V1.5.6.PS` / 机扫 `V1.5.6.MS`),实例创建后可从 `Data_Process_Factory::GetB()` 返回对象的公开成员 `version_info`(`const char*`)读取。
---
@@ -35,7 +37,7 @@
- 航迹外推、消亡与航迹号管理
- TAS 手动转跟踪与跟踪波束调度输出
**技术栈**:C++(MSVC 2013 兼容,C++11 特性)、Eigen 3(随仓库内置,仅用 Eigen/Dense)、CMake ≥ 3.10。
**技术栈**:C++(MSVC 2022 / v143 工具集,C++17)、Eigen 3(随仓库内置,仅用 Eigen/Dense)、CMake ≥ 3.21。
**处理约定**:
@@ -55,12 +57,16 @@
- **两帧滑窗点迹凝聚**:TWS 点迹先与上一帧缓存合并凝聚,再进入关联,兼顾跨波位(CPI)相邻点迹的分裂问题;近程模式增加速度维约束。
- **逻辑法航迹起始**:每圈按固定顺序推进——① 未关联点迹先与 ≥2 点临时航迹做 EKF 波门 + 折角约束关联(延长临时航迹),② 再与 1 点航迹头做速度法关联(两点卡尔曼初始化出 2 点临时航迹),③ 两步都做完后删除被消费的点迹、剩余点迹转成新航迹头,④ 长度达标的临时航迹经三点卡尔曼初始化转可靠航迹;带起批屏蔽区与重复航迹去重。
- **3 模型 IMM 滤波**:三个 Singer 模型(低/中/高过程噪声),量测为「距离-方位-径向速度」三维的 EKF,多普勒残差按盲速折叠,似然用 3×3 新息协方差。
- **最近邻数据关联**:三模型统计距离波门 `ASSO_THORD = 4`(等效 d < 4,TWS 另按航迹距离分段收紧模型 3 波门),再叠加高度门限(≤1000 m 150 m / 2000 m 200 m / 3000 m 250 m / 5000 m 300 m,更远不约束)与 TWS 弦距-位移距离门限。ΔT ≤ 0(点迹时间戳不晚于航迹时间)的候选不更新航迹状态、仅消费该点迹(标记已使用),避免其继续起批而生成虚假 TWS 航迹。关联输出的航迹同时给出**靠近/远离方向标志**:`Flag_Direction = vr_point > 0 ? 1 : 0`(1 靠近 / 0 远离),TWS、TAS 两通道一致。
- **最近邻数据关联**:三模型统计距离波门 `ASSO_THORD = 4`(等效 d < 4,TWS 另按航迹距离分段收紧模型 3 波门),再叠加高度门限(≤1000 m 150 m / 2000 m 200 m / 3000 m 250 m / 5000 m 300 m,更远不约束)与 TWS 弦距-位移距离门限。ΔT ≤ 0(点迹时间戳不晚于航迹时间)的候选不更新航迹状态、仅消费该点迹(标记已使用),避免其继续起批而生成虚假 TWS 航迹。关联输出的航迹同时给出**靠近/远离方向标志**:按航迹 IMM 融合速度矢量与「雷达→目标」位置矢量的夹角判定——`acos((-x·cos(Direction_Angle) − y·sin(Direction_Angle)) / r) < π/2` 置 1(靠近),否则 0(远离),等价于「滤波速度的径向分量指向雷达」,不再使用受多普勒模糊影响的实测径向速度 `vr_point`;TWS、TAS 两通道一致(见 9.6)。
- **重点目标与外推上限**:`primary_target_add` / `primary_target_cancel` 可按批号手动标记/取消重点目标,重点航迹的消亡轮数放宽到 `TRACK_DIE_ROUND_PRIMARY`(10,普通航迹 6);未关联航迹的外推圈数受限(TWS ≤ `MAX_EXTRAPOLATE_NUM`=3,TAS 相扫 5 / 机扫 48),超限后停止外推。**输出航迹只包含本圈有更新的航迹**(`Track_Update_Flag` 门控),宿主不能再用输出行数推断航迹总数(见 9.9)。
- **点迹附加信息字段扩展(需同步重编译宿主)**:`DataRev` 新增 `amp_asv` / `prn_fftn`,`Track` 新增 `amp_asv_point` / `prn_fftn_point`,结构体布局随之变化,宿主必须使用同一版本头文件重新编译对接;输入侧已打通(TWS 输出有效,TAS 输出仍未写回),详见 11.2。
- **高度维独立平滑**:按距离分段选择滑动平均窗长(5/6/7/9 点),抑制俯仰测量跳变。
- **TAS 波束控制**:手动转 TAS 目标入队(队列长度与容量随扫描体制切换),按最新时间戳外推输出跟踪波束(距离/方位/俯仰/批号)。
- **航迹生命周期管理**:外推轮数计数、超时消亡、手动删除、全清空(含各模块 reset,防重连后幽灵航迹)。
- **防御性加固**:见 archived_docs/BUG_FIX_REPORT.md——越界防护、时间戳正序校验、方位角 ±π 环绕、asin 定义域钳位、IMM 概率奇异保护等已修复项。
- **日志文件输出**:`RDP_LOG_ENABLE` 定义时,首次 `Data_Process_Factory::GetB()` 自动用 `rdbuf()` 将 `std::cout` 重定向到 `RDP_LOG_DIR`(`.\rdp_logs\`)下按日期+时间命名的日志文件,`Destroy()` 时恢复控制台并关闭文件;`RDP_LOG` 日志宏在每条日志前附加 `[时间戳][文件:行号]`。该宏在 `parameters.h` 中**当前为注释状态(默认不启用)**,即默认不做重定向、日志输出到控制台(见 10.1)。
- **版本号输出**:`parameters.h` 的 `MAJOR_VERSION`/`MINOR_VERSION`/`PATCH_VERSION`(当前 1.5.6)经两级 `STRINGIFY` 宏拼成 `VERSION_STRING`(`V1.5.6.PS` / `V1.5.6.MS`);`Data_process_class_dll` 基类公开成员 `const char* version_info` 由 `Data_Process_Factory::GetB()` 在创建实例时赋值,宿主可直接读取以核对/上报 DLL 版本(b227ae1)。
- **数据记录调试功能(可选编译)**:`rdp_record` 模块把结构体按「一个记录实体一张表」写入 SQLite(一次运行一个 db 文件、多张表),结构体到表的映射由内置的 ormpp(`third_party/ormpp`,含 iguana、frozen 与 SQLite 3.49.1 amalgamation)完成;由 CMake 选项 `RDP_DATA_RECORD`(`AUTO`/`ON`/`OFF`)控制,**AUTO 时 Debug 构建启用、Release(交付)构建完全不编译**——记录模块源码不参与编译、`RDP_RECORD`/`RDP_RECORD_BATCH` 宏展开为 `((void)0)`(实参不求值),DLL 不新增依赖与导出符号、无性能损失。启用时 `GetB()` 建目录并开库(`.\rdp_records\rdp_record_YYYYMMDD_HHMMSS.db`),首次写入某实体时自动 `CREATE TABLE IF NOT EXISTS`,同步写入、失败降级(不抛出、不影响雷达处理)。当前内置 **6 张在用记录表**(输入点迹、TWS/TAS 凝聚后点迹、临时航迹、可靠航迹、点航关联信息,共 7 个实体定义、其中 1 个已停用),并新增 `point_id`/`frame_id`/`circle_id`/`temp_track_id`/`track_id` 等关联标识,可把一次处理的点迹→凝聚→临时航迹→可靠航迹→关联信息串成完整链路;新增记录数据只需「加结构体 + 注册 + 一行记录点」,详见 docs/data_record_debug.md(见 4.3、10.1、11.3)。
---
@@ -71,30 +77,39 @@
├── CMakeLists.txt # CMake 构建脚本(扫描模式切换、导出宏、输出目录)
├── CHANGELOG.md # 变更日志(Keep a Changelog 格式,按 git 提交记录维护)
├── README.md # 本文件:项目说明(架构、接口、流程、算法、参数)
├── requirements.md # 需求说明:数据记录调试功能修改要求(结构体经 ormpp 写入 SQLite、Release 可屏蔽、数据分类入不同表、宏记录点;原日志文件输出需求已实现)
├── archived_docs/ # 过期文档留档(仅作历史参考;接口与流程以本 README 与 docs/ 专项说明为准)
│ ├── BUG_REPORT.md # 全量逻辑 BUG 分析报告(P0~P3,37 项)
│ ├── BUG_FIX_REPORT.md # BUG 修复报告(修复明细 + 编译验证结果)
│ └── requirements.md # 需求说明:日志文件输出功能修改要求(含 rdbuf() 重定向参考示例)
│ └── BUG_FIX_REPORT.md # BUG 修复报告(修复明细 + 编译验证结果)
├── docs/ # 专项说明文档(按当前源码整理,与 README 互补)
│ ├── data_process_flow_report.md # 数据处理流程与关键逻辑说明(前处理/判圈、凝聚、关联与滤波、起始、外推消亡、TAS 波束、参数脉络)
│ └── track_init_readme.md # 航迹起始(Track Init)详细逻辑说明(阶段流程、条件汇总、输出字段映射、风险点、代码索引)
│ ├── track_init_readme.md # 航迹起始(Track Init)详细逻辑说明(阶段流程、条件汇总、输出字段映射、风险点、代码索引)
│ └── data_record_debug.md # 数据记录调试功能说明(开关与构建、运行期行为、表结构约定、新增记录点四步配方、6 张在用记录表清单与字段、查询示例)
├── design_docs/ # 设计与讨论资料留档(被 .gitignore 忽略、不入库;含设计讨论、产品介绍与 README/流程报告的导出、渲染版本)
├── .vscode/ # VS Code 工程配置
│ ├── settings.json # cmake.generator=NMake、MSVC2013 x86 环境变量、compile_commands
│ ├── settings.json # cmake.generator=NMake、MSVC2022 x64 环境变量、compile_commands
│ ├── cmake-variants.yaml # X256_PS / X256_MS × Release / Debug 变体
│ ├── tasks.json # 配置/构建/清理任务
│ └── scripts/{configure,build,clean}.bat
│ ├── tasks.json # 配置/构建/清理任务(MSVC2022 x64)
│ └── scripts/ # 构建脚本(纯 ASCII,避免 cmd 代码页问题)
│ ├── msvc_env.bat # 共享环境设置:定位 VS2022/cl.exe 并调用 vcvarsall.bat(x64)
│ ├── configure.bat # CMake 配置(含旧工具集构建树自动清理)
│ ├── build.bat # CMake 构建
│ └── clean.bat # CMake 清理
├── backup/
│ └── data_process_class_dll/
│ ├── data_process_class_dll.pro # 迁移前的 Qt/qmake 工程文件(备份)
│ └── data_process_class_dll.pro.user
├── third_party/ # 内置第三方源码(按上游原样保存,来源/版本/升级方式见其 UPSTREAM.md)
│ └── ormpp/ # ormpp 0.2.0 + iguana 1.0.5 + frozen + SQLite 3.49.1 amalgamation(Apache-2.0 / 公有领域)
├── data_process_class_dll/ # 源码目录
│ ├── data_process_class_dll.h # 对外接口:DataRev/Track/TrackingBeam/RadarPara 结构体、抽象基类、工厂类
│ ├── data_process_class_dll.h # 对外接口:DataRev/Track/TrackingBeam/RadarPara 结构体、抽象基类(公开成员 version_info)、工厂类
│ ├── data_process_class_dll_global.h # DLL 导入导出宏
│ ├── data_process_class_dll.cpp # 工厂实现(GetB/Destroy 单例)
│ ├── rdp_log.h / .cpp # 日志输出模块:rdbuf() 重定向 std::cout、RDP_LOG 宏(开关宏在 parameters.h)
│ ├── data_process.h / .cpp # Data_Process 实现:接口分发、TWS/TAS 主流程、参数管理、清空/删除/转跟踪
│ ├── struct.h # 内部结构体:PointRecv / Trust_Track / Temp_track / Tracking_Target
│ ├── rdp_record.h / .cpp # 数据记录调试模块:结构体→SQLite 表(内置 ormpp)、RDP_RECORD/RDP_RECORD_BATCH 宏、按实体自动建表、失败降级(开关由 CMake 选项 RDP_DATA_RECORD 控制;实现以 C++20 独立编译)
│ ├── rdp_record_entity.h # 记录实体(=数据表)定义区:6 张在用表 + 1 个已停用实体(输入点迹、TWS/TAS 凝聚后点迹、临时航迹、可靠航迹、关联信息;输出航迹示例已停用)+ RDP_RECORD_DECLARE 声明宏;新增需要记录的数据在此加结构体
│ ├── data_process.h / .cpp # Data_Process 实现:接口分发、TWS/TAS 主流程、参数管理、清空/删除/转跟踪;含 point_id/frame_id/circle_id 自增计数(记录用)
│ ├── struct.h # 内部结构体:PointRecv / Trust_Track / Temp_track / Asso_Info / Tracking_Target
│ ├── parameters.h # 编译期宏参数(按 MECHANICAL_SCANNING / PHASE_SCANNING 分两组)
│ ├── dot_coh.h / .cpp # TWS 点迹凝聚(含两帧滑窗缓冲)
│ ├── dot_coh_tas.h / .cpp # TAS 点迹凝聚
@@ -115,6 +130,7 @@
│ ├── X256_MS/ # 机扫构建目录
│ └── output/ # 早期打包导出(其中 data_process_class_dll.h 为旧版 32 位时间戳接口,勿作为当前 API 依据)
├── rdp_logs/ # 日志输出目录(运行时自动创建于宿主进程当前工作目录下,被 .gitignore 忽略)
├── rdp_records/ # 数据记录库输出目录(运行时自动创建于宿主进程当前工作目录下,被 .gitignore 忽略;仅记录功能启用时产生)
└── .gitignore
```
@@ -126,11 +142,13 @@
| 组件 | 要求 |
|---|---|
| 操作系统 | Windows(构建脚本按 D:\APP\VisualStudio2013 与 Windows Kits 8.1 路径配置) |
| 编译器 | Microsoft Visual C++ 2013(Visual Studio 12)**x86**(与原 Qt 工程保持一致) |
| CMake | ≥ 3.10 |
| 操作系统 | Windows(构建脚本默认按 D:\APP\VisualStudio2022 与 Windows SDK 10.0.26100.0 配置,可用环境变量覆盖) |
| 编译器 | Microsoft Visual C++ 2022(v143 工具集,`_MSC_VER` 19.44)**x64**(`D:\APP\VisualStudio2022\VC\Tools\MSVC\14.44.35207\bin\Hostx64\x64\cl.exe`) |
| C++ 标准 | C++17(`/std:c++17`;由 `CMAKE_CXX_STANDARD 17` + `CMAKE_CXX_STANDARD_REQUIRED ON` 强制) |
| CMake | ≥ 3.21(VS2022 生成器自 3.21 起支持;本工程实际在 CMake 4.x 上验证) |
| 生成器 | NMake Makefiles(VS Code 配置与脚本均使用该生成器) |
| 线性代数 | 无需安装——Eigen 头文件已内置在源码目录 |
| 数据记录(可选调试功能) | 无需安装——ormpp / iguana / frozen / SQLite amalgamation 已内置在 `third_party/ormpp`(版本与升级方式见该目录 UPSTREAM.md);启用时记录模块以 C++20 独立编译,所需的 C 编译器由同一 VS 2022 工具集提供 |
### 4.2 扫描体制
@@ -144,12 +162,13 @@
### 4.3 命令行构建
```bat
:: 需在 MSVC 2013 x86 环境中执行(先运行 vcvarsall.bat x86)
cmake -S . -B build/X256_PS -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -DRADAR_SCAN_MODE=PHASE_SCANNING
:: 需在 MSVC 2022 x64 环境中执行(先运行 vcvarsall.bat x64)
cmake -S . -B build/X256_PS -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -DRADAR_SCAN_MODE=PHASE_SCANNING ^
-DCMAKE_CXX_COMPILER="D:/APP/VisualStudio2022/VC/Tools/MSVC/14.44.35207/bin/Hostx64/x64/cl.exe"
cmake --build build/X256_PS
```
或直接使用仓库提供的脚本(内部已调用 vcvarsall.bat):
或直接使用仓库提供的脚本(内部已通过 msvc_env.bat 调用 vcvarsall.bat x64 并校验 cl.exe):
```bat
.vscode\scripts\configure.bat build/X256_PS PHASE_SCANNING Release
@@ -157,6 +176,20 @@ cmake --build build/X256_PS
.vscode\scripts\clean.bat build/X256_PS
```
configure.bat 会在构建目录中写入 `.msvc_toolset` 标记;若该目录是用其它工具集(例如旧的 MSVC 2013 缓存)配置的,脚本会先整体删除该目录再重新配置,避免新旧目标文件混链。用 `set VS2022_DIR=...`、`set MSVC_TOOLSET_VERSION=...`、`set MSVC_TARGET_ARCH=x86` 可覆盖默认的 VS 安装路径、工具集版本与目标架构。
数据记录调试功能由缓存变量 `RDP_DATA_RECORD` 控制,`configure.bat` 的第 4 个参数即该变量(取值 `AUTO`/`ON`/`OFF`,缺省 `AUTO`):
```bat
:: AUTO(默认):Debug 构建启用、Release 构建关闭——交付构建不做任何记录
.vscode\scripts\configure.bat build/X256_PS PHASE_SCANNING Debug AUTO
:: 显式关闭(Release 交付推荐)或显式开启(临时在 Release 中排查)
.vscode\scripts\configure.bat build/X256_PS PHASE_SCANNING Release OFF
.vscode\scripts\configure.bat build/X256_PS PHASE_SCANNING Debug ON
```
启用时配置阶段打印 `数据记录调试功能:启用(C++20 静态库 rdp_record + 内置 SQLite)`,并额外构建静态库 `rdp_record`(记录模块 + SQLite,链接进 DLL);关闭时打印 `...:关闭(不编译记录模块,RDP_RECORD 为空操作)`,记录模块源码完全不参与编译。开关语义、表结构约定与新增记录点配方见 docs/data_record_debug.md。
在 VS Code 中可直接运行任务(tasks.json):「CMake: 配置并构建 X256_PS (Release)」等,默认任务为 X256_PS Release。
### 4.4 产物
@@ -167,14 +200,37 @@ cmake --build build/X256_PS
| 导入库 data_process_class_dll.lib | build/<X256_XX>/lib/ |
| 安装(cmake --install) | DLL/导入库 → bin、lib,公开头文件 → include |
| compile_commands.json | 构建根目录(供 VS Code IntelliSense 使用) |
| rdp_record.lib(仅记录功能启用时) | build/<X256_XX>/lib/ —— 记录模块静态库(含 SQLite),已链接进 DLL,**无需随 DLL 分发** |
仓库 build/X256_PS/bin/ 内还带有 **rdp_playback.exe**(回放测试工具,依赖 Qt5Core.dll、sqlite3.dll,配置样例 RadarConfigParam.ini,可回放 .db 记录的点迹数据验证 DLL 输出),不在本仓库源码范围内。
产物为 **x64(AMD64)** 目标。宿主程序必须是 64 位、且使用与本 DLL 同一版本头文件重新编译——MSVC 2013 → MSVC 2022 同时伴随 x86 → x64 的 ABI 变化,旧的 32 位宿主无法加载该 DLL。
仓库 build/X256_PS/bin/ 内原附带 **rdp_playback.exe**(回放测试工具,依赖 Qt5Core.dll、sqlite3.dll,配置样例 RadarConfigParam.ini,可回放 .db 记录的点迹数据验证 DLL 输出),该工具不在本仓库源码范围内、当前构建目录中亦不存在,需自行放置;由于 DLL 已改为 x64,配套的 rdp_playback.exe 必须是 **x64 版本**(同时其自带 Qt5Core.dll / sqlite3.dll 也需为 x64),32 位版本无法加载新版 DLL。
### 4.5 编译警告处理
- 源码文件统一使用 **带 BOM 的 UTF-8** 编码保存,兼容 MSVC 2013(该版本不支持 `/utf-8`),消除 C4819。
- `CMakeLists.txt` 对 MSVC 19.0 及以上版本自动追加 `/utf-8` 编译选项;使用 MSVC 2013 构建时请保持源码为 UTF-8 with BOM。
- 修复了 `/W3` 下全部 C4018(signed/unsigned 不匹配)与 C4244(类型转换可能丢失数据)警告:循环变量按需改为 `size_t`,窄化转换改为 `static_cast` 显式转换,不改变原有计算逻辑。
- 源码文件统一使用 **带 BOM 的 UTF-8** 编码保存;`CMakeLists.txt` 对 MSVC 统一追加 `/utf-8`,中文字符串与注释不再受系统代码页影响(消除 C4819),BOM 仅作为编辑器/旧工具的兼容保留。
- `CMakeLists.txt` 显式指定 `/W3`:CMake 3.15(策略 CMP0092)起不再把 `/W3` 放进 `CMAKE_CXX_FLAGS`,本工程此前一直以 `/W3` 构建,故显式恢复以保持警告级别不变。
- 修复了 `/W3` 下全部 C4819(源文件字符集)、C4018(signed/unsigned 不匹配)、C4244(类型转换可能丢失数据)警告:循环变量按需改为 `size_t`,窄化转换改为 `static_cast` 显式转换,不改变原有计算逻辑。
- MSVC 2022 在 `/W3` 下新增报出的 C4267(`size_t` → `int` 窄化)共 17 处已按同一约定修复(`track_asso.cpp`、`track_asso_tas.cpp`、`track_init.cpp`、`track_init_direct_tracking.cpp`、`tas_ctrl.cpp` 中改为 `static_cast<int>(...)`),同样不改变计算逻辑。
- 当前四种配置(X256_PS / X256_MS × Release / Debug)均以 **0 warning、0 error** 通过编译。
- cl.exe 的诊断信息为中文(跟随 VS 安装语言,`VSLANG=1033` 对 VS2022 的 cl.exe 无效),构建日志中偶见 `注意: 包含文件:` 行泄漏,属 CMake 依赖扫描的显示噪声,头文件依赖(`CMakeFiles/*.dir/**/*.obj.d`)记录完整,不影响增量编译。
- **第三方库告警例外(仅记录功能启用时)**:数据记录模块的第三方依赖按上游原样内置在 `third_party/ormpp`(保留上游字节,含换行符,不转 BOM/编码),其中 `third_party/ormpp/thirdparty/sqlite3/sqlite3.c` 以源文件级 `/w` 单独编译,`data_process_class_dll/rdp_record.cpp` 用 `#pragma warning(push, 0)` / `(pop)` 包裹第三方头;除此之外本工程源码仍按上一条的 `/W3` 零告警标准编译(X256_PS/X256_MS × Release/Debug 四种配置实测 0 warning、0 error)。
### 4.6 编译器升级说明(MSVC 2013 → MSVC 2022)
| 项目 | 升级前 | 升级后 |
|---|---|---|
| 编译器 | Microsoft Visual C++ 2013(VS 12.0,`_MSC_VER` 1800) | Microsoft Visual C++ 2022(v143,`_MSC_VER` 1944,cl 19.44.35228) |
| 目标架构 | x86(32 位) | x64(AMD64) |
| C++ 标准 | C++11(编译器默认) | C++17(`/std:c++17`) |
| CMake 最低版本 | 3.10 | 3.21 |
| 新增/调整编译选项 | — | `/utf-8`(改为无条件)、`/Zc:__cplusplus`、`/W3`(显式恢复) |
- **`/Zc:__cplusplus`**:MSVC 默认把 `__cplusplus` 报为 `199711L`,加该选项后正确报 `201703L`,使依赖 `__cplusplus` 判断的第三方库(内置 Eigen 3.3.7)走标准分支。已验证 Eigen 3.3.7 在此组合下编译通过。
- **`/Zc:strictStrings` 仍仅在 Release 启用**:原限制(MSVC 2013 Debug 运行库与该选项冲突)已不存在,此处保持与原 qmake 工程一致的行为。
- **旧构建目录需清理**:MSVC 2013 的 CMake 缓存与目标文件不能跨编译器复用(nmake 会因目标文件时间戳较新而跳过重编译,导致新旧目标文件混链)。`configure.bat` 已内置自动清理:构建目录中无 `.msvc_toolset` 标记或标记与当前工具集不一致时,先删除整个构建目录再配置。
- **源码改动**:仅补齐 17 处 `static_cast<int>(...)` 显式窄化转换以消除 MSVC 2022 新增的 C4267 告警,未改动任何计算逻辑、接口与结构体布局;DLL 导出符号与升级前一致(差异仅为 x86 → x64 的名称修饰,如 `QAE` → `QEAA`、`PAV` → `PEAV`)。
- **兼容性风险点已核查**:C++17 移除的 `std::auto_ptr`、`std::random_shuffle`、`std::unary_function`/`binary_function`、`std::binder1st/2nd` 等在本工程源码与内置 Eigen 3.3.7 中均未被使用(Eigen 的相应特化本身带有 `__cplusplus`/`EIGEN_COMP_MSVC` 版本保护)。
---
@@ -189,6 +245,7 @@ cmake --build build/X256_PS
classDiagram
class Data_process_class_dll {
<<abstract, exported API>>
+const char* version_info
+data_preprocess(DataRev[150]) int
+track_process(Track[][10], int*, int[], int*, int) int
+Beam_Ctrl(TrackingBeam*, Track[][10], int*)
@@ -248,18 +305,19 @@ classDiagram
| 类 / 文件 | 职责 |
|---|---|
| Data_Process(data_process.cpp) | 对外接口实现与流程编排:输入缓存、按 model 分发 TWS/TAS 流水线、参数管理、航迹清空/删除、手动转/停 TAS、波束控制转发 |
| Data_Process(data_process.cpp) | 对外接口实现与流程编排:输入缓存、按 model 分发 TWS/TAS 流水线、参数管理、航迹清空/删除、手动转/停 TAS、手动标记/取消重点目标(primary_target_add/cancel)、波束控制转发 |
| Dot_Coh | TWS 点迹凝聚:两帧滑窗合并凝聚(dot_coh_process_buff) |
| Dot_Coh_TAS | TAS 点迹凝聚(单帧,距离/方位/速度三维门限) |
| Track_Asso | TWS 点航关联 + IMM 滤波:模型交互 → 门限筛选(NN)→ 各模型 EKF → 概率更新 → 模型输出;未关联航迹外推;高度平滑;ΔT≤0(乱序/重复时间戳)的候选取出后不更新滤波、不刷新航迹,仅消费该点迹 |
| Track_Asso_Tas | TAS 航迹关联(只处理指定批号 tas_track_idx,门限与 TWS 略有差异) |
| Track_Asso | TWS 点航关联 + IMM 滤波:模型交互 → 门限筛选(NN)→ 各模型 EKF → 概率更新 → 模型输出(只输出 `Track_Update_Flag==1` 的航迹);未关联航迹按上限外推;高度平滑;按滤波速度矢量写 `Flag_Direction`;ΔT≤0(乱序/重复时间戳)的候选取出后不更新滤波、不刷新航迹,仅消费该点迹 |
| Track_Asso_Tas | TAS 航迹关联(只处理指定批号 tas_track_idx,门限与 TWS 略有差异;输出同样受 `Track_Update_Flag` 门控,外推上限 `MAX_EXTRAPOLATE_NUM_TAS` 的判定方向与 TWS 相反) |
| Track_Init | 逻辑法航迹起始,每圈顺序固定:点-临时航迹关联(延长 ≥2 点临时航迹)→ 点-航迹头关联(两点起始)→ 剩余点转新航迹头 → 长度达标者起批(三点卡尔曼初始化);另含起批屏蔽区判断与临时航迹消亡 |
| Track_Die / Track_Die_Tas | 可靠航迹消亡:外推轮数超限或手动删除标志置位时输出消亡批号并删除 |
| Track_Die / Track_Die_Tas | 可靠航迹消亡:未关联轮数超限(普通 TWS 6 圈 / 重点航迹 10 圈 / TAS 相扫 10、机扫 96)或手动删除标志置位时输出消亡批号并删除 |
| TAS_Ctrl | TAS 波束控制:手动目标入队(tas_target_add)、失效目标出队(tas_target_del)、跟踪波束预测输出(tas_beam_output:数据率门控 + 队内选择最早 CPI 目标)、调度时刻记录(last_track_time) |
| Track_Ind_Mangement | 航迹号 1~500 的顺序分配与回绕复用 |
| kalman | 两点/三点滤波初始化、线性卡尔曼预测/滤波、EKF(3 维量测含多普勒)、马氏统计距离 d、盲速 Bind_speed |
| coor_trans | 极坐标 ↔ 直角坐标转换 |
| rdp_log(rdp_log.h/.cpp) | 日志输出:按 parameters.h 的 `RDP_LOG_ENABLE` 开关,在 `Init()` 中用 `rdbuf()` 将 `std::cout` 重定向到日期+时间命名的日志文件、`Shutdown()` 恢复控制台并关闭;提供 `RDP_LOG` 宏自动附加 `[时间戳][文件:行号]` 前缀 |
| rdp_record(rdp_record.h/.cpp、rdp_record_entity.h) | 数据记录调试(可选编译):由 CMake 选项 `RDP_DATA_RECORD` 决定是否参与编译;开启时把「记录实体」(结构体)经内置 ormpp 映射写入 SQLite 表(一实体一表,首次写入自动 `CREATE TABLE IF NOT EXISTS`,一次运行一个 db 文件),`RDP_RECORD`/`RDP_RECORD_BATCH` 记录点宏在关闭时展开为空操作;`Init()`/`Shutdown()` 由工厂的 `GetB()`/`Destroy()` 调用;建表/插入失败一律降级(每个实体只提示一次),不影响雷达处理。**当前 7 个实体定义、6 张在用表**:`RdpRecPoint`(输入点迹,TWS/TAS 共用)、`RdpRecPointCohesionTWS`、`RdpRecPointCohesionTAS`、`RdpRecTempTrack`、`RdpRecTrustTrack`、`RdpRecAssoInfo`(另有已停用的 `RdpRecTrackOut`),并有数组转字符串辅助 `arrayToString()`(用于把 `X`/`P` 等状态数组存为文本列) |
| Track_Init_Direct_Tracking / Track_Asso_Direct_Tracking | 引导跟踪(direct tracking)专用起批/关联。**当前入口为空实现、未启用**;track_asso_direct_tracking.cpp 未加入 CMake 源列表 |
---
@@ -270,8 +328,8 @@ classDiagram
| 结构体 | 用途 | 要点 |
|---|---|---|
| DataRev | 输入点迹 | 距离(m)、方位/俯仰(°)、速度(m/s)、幅度/门限/SNR/RCS、码盘值、CPI/GNSS 时间(ms, long long)、方位波位(0-24)/俯仰波位(0-13)、PRI(µs,**不允许为 0**)、频点号、点数、点迹号、使用标记、TAS 批号、点迹类型、十字星数据 speed_dim[129]/range_dim[8] |
| Track | 输出航迹 | 位置/速度、极坐标量、航迹号、更新点数、方向标志 Flag_Direction(关联输出赋值:1 靠近 / 0 远离,见 9.6)、实虚点标志 Flag_Point、一步预测、跟踪模式、航向角、目标类型、关联点信息、航迹时间(s)/GNSS、SNR/RCS、十字星数据透传 |
| DataRev | 输入点迹 | 距离(m)、方位/俯仰(°)、速度(m/s)、幅度/门限/SNR/RCS、码盘值、CPI/GNSS 时间(ms, long long)、方位波位(0-24)/俯仰波位(0-13)、PRI(µs,**不允许为 0**)、频点号、点数、点迹号、使用标记、TAS 批号、点迹类型、归一化幅度 `amp_asv`(dB)、速度维 FFT 点数 `prn_fftn`、十字星数据 speed_dim[129]/range_dim[8] |
| Track | 输出航迹 | 位置/速度、极坐标量、航迹号、更新点数、方向标志 Flag_Direction(仅关联/外推输出路径赋值:按滤波速度矢量判定,1 靠近 / 0 远离,见 9.6;起批输出不写该字段)、实虚点标志 Flag_Point、一步预测、跟踪模式、航向角 Direction_Angle、目标类型、关联点信息、归一化幅度 `amp_asv_point` / 速度维 FFT 点数 `prn_fftn_point`、航迹时间(s)/GNSS、SNR/RCS、十字星数据透传 |
| TrackingBeam | TAS 跟踪波束指令 | open_flag、type、Range、Azi、Elev、TAS_track_index |
| Target_direct_tracking | 引导跟踪信息 | 批号、距离/方位/俯仰、开关(接口未启用) |
| RadarPara | 工作参数 | 见 [参数说明](#10-参数说明) |
@@ -280,13 +338,16 @@ classDiagram
| 结构体 | 用途 | 关键字段 |
|---|---|---|
| PointRecv | 内部点迹 | 距离/方位(弧度)/速度/幅度/高度/SNR/RCS/CPI/GNSS 时间、点迹号、使用标志、波位/PRF/频点、跟踪模式、点迹区号、十字星数据 |
| Trust_Track | 可靠航迹(IMM 全状态) | 航迹号、高度、航迹时间、更新标志、外推轮数 Extrapolate_round、实虚点标志、手动删除/手动 TAS 标志、关联点数、高度平滑缓冲 Hight_smooth、关联点信息、融合状态 X[6]/P[6][6]、三个子模型 X1/X2/X3、P1/P2/P3、模型概率 u[3] |
| Temp_track | 临时航迹(航迹起始阶段) | 4 维状态 X[4]/P[4][4]、极坐标点信息、时间戳、buff_round(已缓存圈数)、区号、关联标志 |
| Tracking_Target | TAS 队列元素 | 批号 Index、空标志 empty_flag |
| PointRecv | 内部点迹 | 距离/方位(弧度)/速度/幅度/高度/SNR/RCS/CPI/GNSS 时间、点迹号 `point_id` / 帧号 `frame_id` / 圈号 `circle_id`(记录链路用,c3c7e66 新增)、使用标志、波位/PRF/频点、跟踪模式、点迹区号、归一化幅度 `amp_asv_point` / 速度维 FFT 点数 `prn_fftn_point`、十字星数据 |
| Trust_Track | 可靠航迹(IMM 全状态) | 航迹号、标识字段 `track_id` / `point_id` / `circle_id`(b227ae1 新增,起批时取临时航迹末点的 `temp_track_id`/`point_id`/`circle_id`,关联成功时刷新为被关联点迹的 ID)、高度、极坐标量快照 `Range` / `Azimuth` / `Elevation` / `Velocity`(b227ae1 新增,仅在本圈有输出时回填)、航迹时间、更新标志 Track_Update_Flag(**输出门控**:仅置 1 的航迹当圈被输出,输出后清零)、外推轮数 Extrapolate_round、实虚点标志、手动删除/手动 TAS/重点目标(primary_target_flag,决定消亡用 6 还是 10 圈)标志、关联点数、高度平滑缓冲 Hight_smooth、关联点信息、归一化幅度 `amp_asv_point` / 速度维 FFT 点数 `prn_fftn_point`、融合状态 X[6]/P[6][6]、三个子模型 X1/X2/X3、P1/P2/P3、模型概率 u[3] |
| Temp_track | 临时航迹(航迹起始阶段) | 4 维状态 X[4]/P[4][4]、极坐标点信息(`r`/`azi`/`vr`/`height`/`SNR`/`RCS`/`amplitude`)、时间戳 `CPI_time` / `GNSS_time`、`buff_round`(已缓存圈数)、标识字段 `temp_track_id` / `point_id` / `circle_id`、区号 `temp_track_section_idx`、俯仰波位号 `pitch_num`、关联标志(c3c7e66 起字段统一更名,如原 `snr`/`Amp`/`T`) |
| Asso_Info | 点航关联候选(track_asso.cpp 使用) | `point_id` / `track_id` / `circle_id`、三模型统计距离 `d1`/`d2`/`d3` 与最小值 `d_min`、点迹/航迹下标、`delta_T`/`delta_h`/`R`/`VT`、门限判定结果 `thd_d`/`thd_h`/`thd_r`、是否被最近邻取中 `min_d_flag`(b227ae1 由局部结构体提升到 struct.h,用于记录 `RdpRecAssoInfo`) |
| Tracking_Target | TAS 队列元素 | 批号 Index、空标志 empty_flag、最近波束输出时间 last_track_time |
| Direct_Tracking_Target | 引导跟踪目标 | ID、方位/俯仰/距离、空标志 |
> 说明:Trust_Track 的状态布局为 [x, vx, ax, y, vy, ay],x/y 两轴解耦,每轴为「位置-速度-加速度」。
>
> ⚠ **结构体布局变更**:`DataRev` 新增 `amp_asv` / `prn_fftn`,`Track` 新增 `amp_asv_point` / `prn_fftn_point`,内部 `PointRecv` / `Trust_Track` / `Temp_track` 亦有新增与更名字段。宿主与 DLL 必须使用同一版本头文件并**同步重新编译**,否则字段错位。对外结构体(`data_process_class_dll.h`)在本版中仅新增了基类成员 `version_info`,`DataRev` / `Track` / `RadarPara` 布局自 1.5.5 起未再变化。
---
@@ -296,8 +357,9 @@ classDiagram
| 接口 | 返回值 | 说明 |
|---|---|---|
| Data_Process_Factory::GetB() | 实例指针 | 获取单例(首次调用时创建) |
| Data_Process_Factory::GetB() | 实例指针 | 获取单例(首次调用时创建,并写入 `version_info`) |
| Data_Process_Factory::Destroy() | — | 销毁实例并将指针置空 |
| version_info(基类公开成员) | `const char*` | DLL 版本字符串,由 `GetB()` 赋值为 `VERSION_STRING`(相扫 `V1.5.6.PS` / 机扫 `V1.5.6.MS`);只读使用,不要释放(b227ae1) |
| track_process_parameters_initial(RadarPara) | 0 成功 / -1 失败 | 初始化工作参数;校验 3 ≤ track_start_point_num ≤ 9 |
| track_process_parameters_modify(RadarPara) | 0 / -1 | 运行时修改参数(校验同上) |
| data_preprocess(DataRev[150]) | 0/1/2/3 | 输入一帧点迹并缓存,返回处理模式(见下表) |
@@ -307,11 +369,17 @@ classDiagram
| track_delete(n, idx[]) | 0 | 按批号标记手动删除(下一轮消亡输出) |
| tracking_start(track_index) | 0 | 手动转 TAS 跟踪(置 manual_tracking_flag,由 Beam_Ctrl 入队) |
| tracking_stop(track_index) | 0 | 手动取消 TAS(清标志并 Track_Mode=0;队列项延至下一次 Beam_Ctrl 移除) |
| primary_target_add(track_index) | 0 | 手动将指定批号标为**重点目标**(置 `primary_target_flag=1`,消亡轮数放宽到 10 圈;批号不存在时不报错、无副作用) |
| primary_target_cancel(track_index) | 0 | 取消指定批号的重点目标标记(置 `primary_target_flag=0`,恢复普通消亡轮数) |
| tracking_point(azimuth) | 0 | 手动打跟踪波束——**未实现(保留空实现)** |
| direct_tracking_process(...) | 0 | 引导跟踪——**未实现(保留空实现)** |
> **输出语义(50e0b5c 起)**:`track_process(model=1/3)` 只把本圈**有更新**的航迹写入 `Trust_Track_Output`(关联到实点、外推成功,或 TAS 指定批号被更新),行数由 `Trust_track_num_Output` 给出;未更新(含外推超限)的航迹当圈不出现在输出中,宿主不能据输出行数推断航迹总数。输出数组内容由 DLL 覆盖写入指定行,其余行保持宿主原值(DLL 不清零整块数组)。
> 日志输出为 DLL 内部功能(不对外导出,接口见 10.1 的宏开关):首次 `GetB()` 创建实例时自动初始化(`RDP_LOG_ENABLE` 定义则将 `std::cout` 重定向到 `.\rdp_logs\` 下按日期+时间命名的日志文件,该宏当前为注释状态,见 10.1),`Destroy()` 销毁实例时恢复 `std::cout` 并关闭日志文件。
> 数据记录同样为 DLL 内部功能(不对外导出):记录功能启用时,`data_preprocess` 逐点写输入点迹、`dot_coh`/`dot_coh_tas` 写凝聚后点迹、`track_init` 写临时航迹、`track_asso` 写可靠航迹与关联信息,全部落到 `.\rdp_records\rdp_record_YYYYMMDD_HHMMSS.db` 的 6 张在用表中(表清单与字段见 docs/data_record_debug.md)。
### 7.2 data_preprocess 返回值的含义
| 返回值 | 含义 | 触发条件 |
@@ -357,7 +425,7 @@ flowchart TD
B --> C["track_process(1)"]
C --> D["dot_coh 两帧滑窗凝聚 → point_recv"]
D --> E["track_asso: IMM 交互 → 点航波门筛选(NN)→ EKF 滤波 → 模型输出"]
E --> F["track_die: 外推超限/手动删除 → 输出消亡批号"]
E --> F["track_die: 连续未关联超限 / 手动删除 → 输出消亡批号"]
F --> G["track_init: 点-临时航迹关联 → 点-航迹头关联 → 剩余点转新航迹头 → 起批转可靠航迹"]
G --> H["输出 Trust_Track_Output(含起批历史点)"]
H --> I["Beam_Ctrl: 手动 TAS 目标入队 / 波束输出"]
@@ -365,6 +433,8 @@ flowchart TD
其中 model=2(未满一圈)只执行滑窗凝聚 dot_coh_process_buff,把上一帧凝聚完成的点输出到 point_recv 供下一圈的关联/起批使用,当前帧未凝聚点作为下一次的缓存——即「凝聚在前、关联在后、缓存一帧」的两帧滑窗结构。
输出环节(`track_asso_process()` 末尾)只写 `Track_Update_Flag==1` 的航迹:关联到实点、或外推成功(`Extrapolate_round <= MAX_EXTRAPOLATE_NUM` = 3)时置 1;外推超限的航迹不再推进位置与航迹时间,当圈也不输出,直到重新关联上实点才恢复(详见 9.9)。
### 8.2 TAS 流程(model=3)
```mermaid
@@ -373,11 +443,13 @@ flowchart TD
B --> C["dot_coh_tas 单帧凝聚 → point_recv_tas"]
C --> D["track_asso_tas: 仅对 tas_track_idx 批号做 IMM+EKF 关联(含高度门限)"]
D --> E["未关联 → 按 latest_timestamp 外推(ΔT≤0 跳过)"]
E --> F["track_die_tas: TAS 超时/手动删除 → 消亡输出"]
E --> F["track_die_tas: TAS 连续未关联超上限 / 手动删除 → 消亡输出"]
F --> G["输出该批号航迹(point_type=1)"]
G --> H["Beam_Ctrl: 数据率门控 + 队内最早 CPI 目标 + 预测波束输出"]
```
TAS 输出同样受 `Track_Update_Flag` 门控:只有 `Track_Index == tas_track_idx` 且本圈有更新(关联到实点、或外推成功)时才向 `Trust_Track_Output` 写一行(`point_type=1`)。TAS 外推上限为 `MAX_EXTRAPOLATE_NUM_TAS`(相扫 5 / 机扫 48),判定方向与 TWS 相反(`Extrapolate_round >= 上限` 时才外推并输出),详见 9.9 与 11.2。
### 8.3 航迹起始流程(逻辑法)
```mermaid
@@ -713,7 +785,16 @@ VT<100\ \text{且}\ R>300\ \Rightarrow 拒绝;\qquad
VT>300\ \text{且}\ R>1200\ \Rightarrow 拒绝
$$
候选对按 d_min = min(d1,d2,d3) 全局排序,**最近邻**原则逐个取点:每条航迹每圈最多关联一个点、每个点只属于一条航迹。关联成功后:三模型分别 EKF 更新 → 模型概率更新 → 更新航迹时间/幅度/SNR/RCS/十字星数据 → 高度平滑。关联输出的航迹同时写入**方向标志**(f249fdc):`Flag_Direction = (vr_point > 0) ? 1 : 0`,即径向速度为正判为靠近(1)、为零或负判为远离(0),取值来自航迹记录的径向速度(最近一次关联点迹的速度)。
候选对按 d_min = min(d1,d2,d3) 全局排序,**最近邻**原则逐个取点:每条航迹每圈最多关联一个点、每个点只属于一条航迹。关联成功后:三模型分别 EKF 更新 → 模型概率更新 → 更新航迹时间/幅度/SNR/RCS/十字星数据 → 高度平滑。
**靠近/远离方向标志 Flag_Direction(fc49707 起、42b598e 调整,TWS/TAS 同步)**:不再使用实测径向速度(原 `Flag_Direction = vr_point > 0 ? 1 : 0`,实测多普勒在速度模糊时符号可能取反),改为按航迹 IMM 融合状态的速度矢量与位置矢量判定:
$$
D=\operatorname{atan2}(X[4],X[1])\ \text{(速度方向角)},\qquad
Flag\_Direction=\begin{cases}1,& \arccos\!\left(\dfrac{-x\cos D-y\sin D}{r}\right)<\dfrac{\pi}{2}\\[2mm]0,&\text{否则}\end{cases}
$$
其中 x、y 为经 `Sys_delay` 补偿后的航迹位置,r = √(x²+y²),`Direction_Angle` 输出字段即 D 的度数形式。该判据等价于「目标速度方向与『雷达→目标』位置方向的夹角大于 90°」,即**滤波速度矢量的径向分量指向雷达(径向接近)时判为靠近(1)**,否则远离(0);中间版本曾按合速度归一化并把门限取为 π/4(45° 锥角),42b598e 去掉归一化并放宽到 π/2。外推(虚点)输出同样按该判据写 `Flag_Direction`。
**ΔT ≤ 0 的候选(604f158 起的行为)**:ΔT = (点迹 CPI 时间 − 航迹 T_track)/1000,ΔT ≤ 0 表示该点迹时间戳不晚于航迹时间(典型场景:航迹已按 TAS 点迹更新过,本圈 TWS 点迹的时间戳更早,或存在重复时间戳)。这种候选**不再被剔除**,仍参与最近邻最小 d 的比较与选取,但选中后:
@@ -723,7 +804,7 @@ $$
- 该候选对连同该航迹本圈的其余候选对被一并移出候选表,因此该航迹本圈不会再关联其他点迹;由于时间戳更早的候选仍参与最小 d 比较,它可能先于有效候选(ΔT>0)被取中;
- 该点迹的 `Use_Flag` 被置 1 消费掉:`track_asso_process()` 在关联结束后会把 `Use_Flag==1` 的点迹从 `point_recv` 中删除(track_init 入口也会再剔除一次 `Use_Flag==1` 的点),因此该点迹不会进入航迹起始、不会再参与起批,避免生成虚假 TWS 航迹。
> 历史行为:604f158 之前,ΔT ≤ 0 的候选在选取时被直接剔除并 `continue`,点迹保持未被使用,可能被后续用于航迹起始。该分支原有的 `RDP_LOG` 告警日志现已注释停用;TAS 关联(track_asso_tas.cpp,见下)中同类告警日志仍启用。
> 历史行为:604f158 之前,ΔT ≤ 0 的候选在选取时被直接剔除并 `continue`,点迹保持未被使用,可能被后续用于航迹起始。该分支原有的 `RDP_LOG` 告警日志现已注释停用;TAS 关联(track_asso_tas.cpp,见下)中同类告警日志仍启用。**b227ae1 起该分支的写法由 `if (delta_T <= 0) {空} else {...}` 调整为 `if (delta_T > 0) {...}`**,判定结果与滤波行为不变;同一提交为每个候选补充了 `min_d_flag`(被最近邻取中时置 1)并配合 `RdpRecAssoInfo` 记录全部候选与取中结果(见 5.2 的 rdp_record 行)。
#### TAS(track_asso_tas.cpp)
@@ -735,7 +816,7 @@ $$
高度门限按航迹距离分段:≤1000 m:|h_t−h_p| ≤ 150 m;1000~2000 m:≤ 200 m;2000~3000 m:≤ 250 m;3000~5000 m:≤ 300 m;>5000 m:不约束(与 TWS 通道 9.6 的取值相同)。
输出航迹同样写入**方向标志** `Flag_Direction`(由 `vr_point` 符号决定,f249fdc),规则与 TWS 通道一致。
输出航迹同样写入**方向标志** `Flag_Direction`,判据与 TWS 通道完全一致(fc49707/42b598e:按 IMM 融合速度矢量与位置矢量的夹角判定,见上文 TWS 小节)。
> ⚠ **波门语义提示**:代码中 d 本身已是马氏距离平方,但门限写作 d*d < ASSO_THORD*ASSO_THORD,等效于 d < 4(而非 √d < 4)。该行为在 BUG-20 中被识别,**有意保留未改**;ASSO_THORD/1000、/100 已改为浮点除法(BUG-21 修复)。TAS 不区分距离段,`ASSO_THORD` 原值直接作为门限。
@@ -757,16 +838,27 @@ $$
### 9.9 航迹外推与消亡
**外推**:未关联到实点的 TWS 航迹(manual_tracking_flag==0)按工作模式数据率 DATA_RATE_SHORT/MIDDLE/FAR 预测一帧(ΔT≤0 的候选虽被消费但未刷新航迹状态,其 `point_flag` 仍为 0,故同样在本环节外推,见 9.6);TAS 通道按 latest_timestamp − T_track 预测(ΔT ≤ 0 跳过);预测用各子模型 Singer F/Qj 推进,Extrapolate_round+1,point_flag=0(虚点/补点),航迹时间同步推进。
#### 外推
**消亡**:
- **TWS**:未关联到实点的航迹(`manual_tracking_flag==0`)按工作模式数据率 DATA_RATE_SHORT/MIDDLE/FAR 预测一帧(ΔT≤0 的候选虽被消费但未刷新航迹状态,其 `point_flag` 仍为 0,故同样在本环节外推,见 9.6)。预测用各子模型 Singer F/Qj 推进,`Extrapolate_round+1`,`point_flag=0`(虚点/补点),航迹时间同步推进,并置 `Track_Update_Flag=1`(本圈输出)。
- **TWS 外推上限(50e0b5c)**:仅当 `Extrapolate_round <= MAX_EXTRAPOLATE_NUM`(=3)时才做上述预测;超过上限后不再推进位置与航迹时间,只执行 `Extrapolate_round+1`、`point_flag=0`、`Track_Update_Flag=0`——该航迹当圈不输出、位置冻结在最后一次预测处,直到重新关联到实点(`Extrapolate_round` 清零)才恢复。目的是避免长时间未关联的航迹被反复外推到远处。
- **TAS**:按 `latest_timestamp − T_track` 预测(ΔT ≤ 0 跳过),航迹时间推进到最新时间戳。上限判定为 `Extrapolate_round >= MAX_EXTRAPOLATE_NUM_TAS`(相扫 5 / 机扫 48)时才预测并输出,未达该轮数时只累加 `Extrapolate_round` 且不输出(位置与航迹时间停在最后一次关联处)。⚠ 判定方向与 TWS 的 `<=` 相反,见 11.2。
- **输出门控**:`track_asso_process()` / `track_asso_process_tas()` 的输出循环只写 `Track_Update_Flag==1` 的航迹,写完即清零;因此「本圈未更新」的航迹(外推超限等)不会出现在 `Trust_Track_Output` 中。
#### 消亡(track_die.cpp / track_die_tas.cpp)
判据为 `Extrapolate_round > 阈值`(50e0b5c 起由 `>=` 改为 `>`),满足其一即消亡:
| 通道 | 条件 |
|---|---|
| TWS | Track_Mode==0 且 Extrapolate_round ≥ TRACK_DIE_ROUND(=4),或 manual_delete_flag==1 |
| TAS | Track_Mode==1 且 Extrapolate_round ≥ TRACK_DIE_ROUND_TAS(相扫 5 / 机扫 96),或 manual_delete_flag==1 |
| TWS 普通航迹 | Track_Mode==0 且 primary_target_flag==0 且 Extrapolate_round > TRACK_DIE_ROUND(=6) |
| TWS 重点航迹 | Track_Mode==0 且 primary_target_flag==1 且 Extrapolate_round > TRACK_DIE_ROUND_PRIMARY(=10) |
| TAS 航迹 | Track_Mode==1 且 Extrapolate_round > TRACK_DIE_ROUND_TAS(相扫 10 / 机扫 96) |
| 任意航迹 | manual_delete_flag==1(手动删除标记,不区分通道) |
消亡批号写入 Track_die_Index_Output 并删除本地航迹。
消亡批号写入 Track_die_Index_Output 并删除本地航迹。由于 `Extrapolate_round` 每圈未关联只自增 1,普通 TWS 航迹实际连续 7 圈无实点关联才消亡(重点航迹 11 圈;TAS 相扫 11 圈 / 机扫 97 圈)。`track_process()` 中的调用顺序为 `track_asso → track_die → track_init`(data_process.cpp),即消亡先于航迹起始执行。
> 重点目标标记(`primary_target_add` / `primary_target_cancel`)只影响消亡轮数,不改变关联、滤波、波束控制等其他环节,也不体现在输出航迹字段中。
### 9.10 TAS 波束控制(tas_ctrl.cpp)
@@ -796,6 +888,8 @@ $$
|---|---|---|---|
| RDP_LOG_ENABLE | 注释停用(默认不启用) | 同左 | 日志文件输出开关:定义时将 `std::cout` 重定向到日志文件;**当前源码中该宏为注释状态**(parameters.h 的注释文字仍写「定义(默认)」,以代码实际状态为准),不重定向、日志输出到标准输出/控制台 |
| RDP_LOG_DIR | `.\rdp_logs\` | 同左 | 日志文件输出目录(相对宿主进程当前工作目录,目录不存在时自动创建) |
| RDP_RECORD_ENABLE | 由 CMake 选项 `RDP_DATA_RECORD` 定义(AUTO 时 Debug 定义) | 同左 | 数据记录调试开关:**不要在 parameters.h 中手工定义**(由构建系统传入);定义时记录模块参与编译,未定义时 `RDP_RECORD`/`RDP_RECORD_BATCH` 展开为空操作,见 4.3 与 11.3 |
| RDP_RECORD_DB_DIR | `.\rdp_records\` | 同左 | 数据记录库输出目录(相对宿主进程当前工作目录,目录不存在时自动创建;仅记录功能启用时产生) |
| DATA_RATE_TAS | 已停用(原 0.3) | 已停用(原 0.0625) | 编译期宏已注释停用;TAS 数据率统一由运行时参数 RadarPara.DATA_RATE_TAS 提供(见 10.2) |
| SIGMA_R / SIGMA_A / SIGMA_E / SIGMA_V | 10.0 / 0.02 / 0.2 / 2.0(两体制相同) | 同左 | 量测误差:距离 m / 方位 rad / 俯仰 rad / 速度 m/s |
| DOT_COH_RANGE / DOT_COH_V / DOT_COH_AZI | 80 / 2 / 6 | 同左 | 凝聚门限:距离 m / 速度 m/s / 方位 ° |
@@ -804,8 +898,11 @@ $$
| R_MIN / R_MAX | 100 / 100000 | 同左 | 有效量程(m) |
| TRACK_START_THRESHOLD | 8 | 同左 | 起批波门(d² 门限 64;越大越容易起批) |
| ALPHA_START | 60 | 同左 | 起批折角(°) |
| TRACK_DIE_ROUND | 4 | 4 | TWS 航迹消亡外推轮数 |
| TRACK_DIE_ROUND_TAS | 5 | 96 | TAS 航迹消亡外推轮数 |
| TRACK_DIE_ROUND | 6 | 6 | TWS **普通**航迹消亡圈数(判据 Extrapolate_round > 6;50e0b5c 起由 4 调整为 6,判据由 ≥ 改为 >) |
| TRACK_DIE_ROUND_PRIMARY | 10 | 10 | TWS **重点航迹**消亡圈数(primary_target_flag==1 时套用;50e0b5c 新增) |
| TRACK_DIE_ROUND_TAS | 10 | 96 | TAS 航迹消亡圈数(判据 Extrapolate_round > 阈值;相扫 50e0b5c 起由 5 调整为 10,机扫保持 96) |
| MAX_EXTRAPOLATE_NUM | 3 | 3 | TWS 未关联航迹最大外推圈数(Extrapolate_round ≤ 3 才继续外推;50e0b5c 新增) |
| MAX_EXTRAPOLATE_NUM_TAS | 5 | 48 | TAS 最大外推圈数(判据为 Extrapolate_round ≥ 该值时才外推,方向与 TWS 相反,见 9.9 与 11.2;50e0b5c 新增) |
| ASSO_THORD | 4 | 同左 | 关联波门(判据 d*d < ASSO_THORD²,d 已是马氏距离平方,等效 d < 4;e0bf227 起由 3 调大至 4) |
| H_F_WIN_LEN | 3 | 同左 | 高度平滑窗长基数(实际 +2/+3/+4/+6) |
| TAS_QUEUE_LENGTH / MAX_TAS_NUM | 4 / 4 | 1 / 1 | TAS 队列长度 / 最大 TAS 目标数 |
@@ -843,8 +940,9 @@ $$
- **时间戳/时序**:TWS 关联 ΔT≤0 的候选不参与滤波更新、不刷新航迹状态并消费该点迹(604f158 起由「剔除候选、点迹保持未使用」改为当前行为,仍属 BUG-15 处置范围);TAS 关联 ΔT≤0 拒绝(BUG-16);TAS 外推负时间差跳过(BUG-17);Bind_speed 除零保护(BUG-19)。
- **数值稳定性**:方位残差 ±π 环绕(BUG-22);IMM 概率零分母/奇异保护(BUG-23);EKF 似然 3×3 新息协方差(BUG-24);航向角 atan2(BUG-26);asin 定义域钳位(BUG-27);近程模型 3 门限浮点除法(BUG-21);track_clear_all 全模块 reset(BUG-28);未初始化变量修复(BUG-11/12/14)。
- **后修订(e0bf227,BUG_FIX_REPORT.md 成文之后)**:TWS 关联高度门限由注释状态改为实际生效(`track_asso.cpp`),统计距离/高度/距离三类判据拆为独立布尔量,`ASSO_THORD` 由 3 调大至 4;TAS 关联高度门限 2000~3000 m 段放宽至 250 m;修复 `tas_beam_output()` 数据率门控误判(详见 CHANGELOG 的 Unreleased 与 9.6、9.10)。
- **后修订(50e0b5c、fc49707、42b598e、e8c56f9,BUG_FIX_REPORT.md 成文之后)**:输出航迹增加 `Track_Update_Flag` 门控(只输出本圈有更新的航迹);未关联航迹外推增加圈数上限;消亡判据改为 `>`,正常/重点航迹分别定为 6 / 10 圈(TAS 相扫 10、机扫 96);`Flag_Direction` 改用滤波速度矢量判定,修复速度模糊时的向站/背站误判;手动操作接口补 `break`;`DataRev`/`Track` 新增归一化幅度与速度维 FFT 点数字段(输入侧其后已打通,TAS 输出仍未写回,见 11.2)。
### 11.2 有意保留 / 未修复(标注于 archived_docs/BUG_REPORT.md)
### 11.2 有意保留 / 未修复(archived_docs/BUG_REPORT.md 标注项 + 后续源码观察)
- **BUG-01**:基类无虚析构,Destroy() 按基类指针 delete。
- **BUG-02**:Hight_smooth 只增不减,长时运行内存持续增长。
@@ -853,11 +951,14 @@ $$
- **BUG-08**:消亡航迹输出数组容量未传入,仅约定 ≤500。
- **BUG-13**:Work_Parameter 等成员构造后未初始化,必须先调 track_process_parameters_initial 再处理。
- **BUG-18**:卡尔曼两点/三点初始化对零/负时间差无保护(起批时间戳必须严格递增)。
- **BUG-20**:门限对马氏距离平方再次平方(等效 d<3),有意保留。
- **BUG-25**:TWS 关联/输出未排除 Track_Mode==1 的 TAS 航迹(原过滤逻辑被注释)。
- **BUG-20**:门限对马氏距离平方再次平方(e0bf227 把 `ASSO_THORD` 调为 4 后等效 d < 4),有意保留。
- **BUG-25**:TWS 关联/输出未排除 Track_Mode==1 的 TAS 航迹(原 `manual_tracking_flag==0` 过滤代码块已于 fc49707 删除,未恢复过滤)。
- **BUG-29**:tracking_stop 后 TAS 队列项延迟到下一次 Beam_Ctrl 才移除。
- **BUG-33**:起批关联未显式校验 ΔT>0。
- **BUG-36**:TAS 波束关闭时只清 open_flag,其余字段保留旧值。
- **归一化幅度 / 速度维 FFT 点数(e8c56f9 新增,输入侧已打通)**:`data_preprocess()` 现已在 TWS、TAS 两条分支都把 `DataRev.amp_asv` / `DataRev.prn_fftn` 拷入内部 `PointRecv`(`data_process.cpp`,并随 `RdpRecPoint` 落库),因此 TWS 关联路径(`track_asso.cpp` 写航迹、输出时写回 `Track`)取到的是有效数据。**TAS 通道仍未打通**:`track_asso_tas.cpp` 的关联与输出路径没有涉及这两个字段,TAS 输出航迹中 `amp_asv_point` / `prn_fftn_point` 仍保持宿主原值,宿主暂不应依赖 TAS 侧的这两个字段。
- **TAS 外推上限判据方向与 TWS 相反(50e0b5c)**:TWS 用 `Extrapolate_round <= MAX_EXTRAPOLATE_NUM` 外推,TAS 用 `Extrapolate_round >= MAX_EXTRAPOLATE_NUM_TAS`,实际效果是 TAS 航迹在未达上限的若干圈内既不外推也不输出(位置与航迹时间停在最后一次关联处),达到上限后才恢复逐帧外推输出;如属笔误,宜与 TWS 侧统一。
- **Flag_Direction 判据的 acos 无定义域钳位(fc49707/42b598e)**:对 `(-x·cos D − y·sin D)/r` 直接取 `acos`(与 BUG-27 同类问题),浮点舍入使参数略微越过 ±1 时返回 NaN,比较为假、按「远离 0」输出;`r` 为 0 时还存在除零。
### 11.3 未启用功能
@@ -866,6 +967,8 @@ $$
- TAS **自动**开启/退出逻辑(tas_auto_start/tas_auto_end)注释停用,仅支持手动 tracking_start/tracking_stop。
- RadarPara 中的起批/关联波门字段未被实现读取(见 10.2)。
- build/output/ 下为早期打包导出物,其 data_process_class_dll.h 时间戳仍为 32 位 int,与当前 64 位接口不一致,勿作为对接依据。
- **数据记录调试功能默认只在 Debug 启用**:CMake 选项 `RDP_DATA_RECORD` 的 AUTO 默认值为「Debug 启用、Release 关闭」,交付/Release 构建下记录模块(`rdp_record.cpp`、`sqlite3.c`)不参与编译、`RDP_RECORD` 为空操作(导出符号与依赖均无变化);如需在 Release 构建中临时排查可显式 `-DRDP_DATA_RECORD=ON`,但正式交付应保持 AUTO/OFF(见 docs/data_record_debug.md)。记录点中的字段错误只在启用记录功能的构建中才会被检查(关闭时宏丢弃实参)。
- **记录表的两个观察**:① 可靠航迹表 `RdpRecTrustTrack` 在输出循环内写库,而该循环入口已把 `Track_Update_Flag` 清零(`track_asso.cpp` 先置 0 再输出),因此表中的 `Track_Update_Flag` 恒为 0,判断「本圈是否更新」应改用该表是否出现对应 `track_id`/`circle_id` 的记录;② `Trust_Track` 的极坐标量快照 `Range`/`Azimuth`/`Elevation`/`Velocity` 只在航迹当圈有输出时回填,长期未输出(外推超限)的航迹在表中该项为 0 或旧值。
### 11.4 航迹起始相关的未修复观察(详见 docs/track_init_readme.md 第 15 节)
@@ -881,10 +984,21 @@ $$
## 12. 版本历史
当前代码版本:**V1.5.6**(`parameters.h` 的 `PATCH_VERSION`;对应最近一次提交 b227ae1,2026-10-08)。版本号自本版起可运行时读取(`version_info`,见 7.1)。
近期提交(git log --oneline,新→旧):
| 提交 | 内容 |
|---|---|
| b227ae1(**V1.5.6** 版本号来源) | 数据处理增加版本号输出:`parameters.h` 新增版本宏(`MAJOR_VERSION`/`MINOR_VERSION`/`PATCH_VERSION` = 1.5.6、体制后缀 `VERSION_INFO`、拼装宏 `VERSION_STRING`),基类新增公开成员 `const char* version_info` 并由 `GetB()` 赋值;Debug 下记录可靠航迹与关联信息:`struct.h` 新增全局 `Asso_Info`(含 `min_d_flag` 与门限量)、`Trust_Track` 新增 `track_id`/`point_id`/`circle_id` 与极坐标量快照 `Range`/`Azimuth`/`Elevation`/`Velocity`,新增 `RdpRecTrustTrack`/`RdpRecAssoInfo` 两张表并在 `track_asso.cpp` 输出循环与关联三处埋点,移除 `data_process.cpp` 中按输出数组批量记录的旧示例(`RdpRecTrackOut` 实体注释停用);`track_asso.cpp` 的 ΔT 分支改写为 `if (delta_T > 0)`(行为等价) |
| c3c7e66 | 增加临时航迹录入数据库(新增 `RdpRecTempTrack` 表与 `track_init.cpp` 记录点)、输入点迹增加圈 ID(`PointRecv::circle_id`、`Data_Process::circle_id` 在满圈/TAS 帧末自增);`Temp_track` 字段更名(`snr`→`SNR`、`Amp`→`amplitude`、`T`→`CPI_time`、`Temp_track_section_idx`→`temp_track_section_idx`)并新增 `temp_track_id`/`point_id`/`circle_id`/`pitch_num`;临时航迹起批时把 ID 透传给 `Trust_Track` |
| a4f32e4 | 新增数据记录功能:把过程数据录入数据库(凝聚后点迹 `RdpRecPointCohesionTWS/TAS`,输入点迹改为 `RdpRecPoint`)、`data_process.h` 新增 `point_id`/`frame_id`/`circle_id` 计数、`dot_coh`/`dot_coh_tas` 埋记录点;<br>(该提交同时把上一轮的 CMake/工具链与记录框架改动一并提交,内容见下方「工作区(未提交…)」两条) |
| 工作区(已随本次发布归档,数据记录调试功能) | 新增可选编译的数据记录调试功能:CMake 缓存变量 `RDP_DATA_RECORD`(AUTO/ON/OFF,AUTO = Debug 启用 / Release 关闭)与独立静态库 `rdp_record`(**以 C++20 编译**,雷达代码保持 C++17);内置第三方源码 `third_party/ormpp`(ormpp 0.2.0 + iguana 1.0.5 + frozen + SQLite 3.49.1 amalgamation,附 UPSTREAM.md);新增 `rdp_record.h`(`Init`/`Shutdown`/`db_path` + `RDP_RECORD`/`RDP_RECORD_BATCH` 宏,关闭时展开为空操作)、`rdp_record_entity.h`(记录实体定义区)与 `rdp_record.cpp`(实体注册区、按实体自动建表、单条/批量写入、失败降级);`Data_Process_Factory::GetB()/Destroy()` 调用 `rdp_record::Init()/Shutdown()`;`parameters.h` 新增 `RDP_RECORD_DB_DIR`(`.\rdp_records\`);`configure.bat` 新增第 4 个参数(AUTO/ON/OFF);`.gitignore` 增加 `rdp_records/`;新增 `docs/data_record_debug.md`。验证:Release(PS/MS)导出表与依赖表与改动前逐行一致、DLL 体积不变(178,688 / 178,176 字节)、构建树无记录模块目标文件;Debug(PS/MS,AUTO→启用)0 warning 0 error;运行时校验(临时程序 + Python sqlite3)确认建库、一实体一表、批量事务写入、`id` 自增、取值一致、`integrity_check` = ok,且记录目录不可创建时接口照常返回、仅提示一次 |
| 工作区(已随本次发布归档) | 编译器由 MSVC 2013(Visual Studio 12,x86)升级为 **MSVC 2022(v143,x64)**、C++ 标准提升至 **C++17**:`CMakeLists.txt` 重写(`CMAKE_CXX_STANDARD 17`、`_MSC_VER >= 1930` 强校验、`/utf-8` `/Zc:__cplusplus` `/W3`)、`.vscode/scripts/` 全部构建脚本改为 MSVC 2022(新增共享的 `msvc_env.bat`,`configure.bat` 自动清理旧工具集构建树)、VS Code 配置同步(settings / c_cpp_properties / cmake-variants / tasks 改为 MSVC2022 x64 + C++17)、修复 MSVC 2022 新增的 17 处 C4267 窄化告警;X256_PS/X256_MS × Release/Debug 四种配置均 0 warning 0 error 通过,产物为 x64 DLL(导出符号与升级前一致,仅名称修饰随架构变化) |
| e8c56f9 | 输入/输出结构适配「归一化幅度」与「速度维 FFT 点数」:`DataRev` 新增 `amp_asv`/`prn_fftn`,`Track` 新增 `amp_asv_point`/`prn_fftn_point`,内部 `PointRecv`/`Trust_Track` 同步新增字段;`track_asso.cpp` 关联实点时写入航迹、TWS 输出时写回输出结构(TAS 侧未打通,见 11.2) |
| 42b598e | 调整「向站/背站」判断条件:判据由 `acos((-x·vx − y·vy)/r/Range_V) < π/4` 改为 `acos((-x·cos(Direction_Angle) − y·sin(Direction_Angle))/r) < π/2`(去掉按合速度归一化,锥角门限由 45° 放宽到 90°),TWS/TAS 同步 |
| fc49707 | 「向站/背站」判断改用滤波速度矢量:`Flag_Direction` 不再取 `vr_point > 0`(实测径向速度受多普勒模糊影响、符号可能取反),改按航迹 IMM 速度方向与位置矢量的夹角判定(TWS/TAS 同步);`primary_target_add/cancel`、`tracking_start/stop` 命中目标后 `break`;删除 TWS 输出循环中的注释残留代码块 |
| 50e0b5c | 新增手动重点目标接口(`primary_target_add`/`primary_target_cancel` + `Trust_Track::primary_target_flag`)与重点航迹消亡圈数 `TRACK_DIE_ROUND_PRIMARY`(10);输出航迹改为只输出 `Track_Update_Flag==1` 的本圈更新航迹;未关联航迹外推限制在 `MAX_EXTRAPOLATE_NUM`(3)圈内,TAS 侧新增 `MAX_EXTRAPOLATE_NUM_TAS`(相扫 5 / 机扫 48);`TRACK_DIE_ROUND` 由 4 调整为 6、`TRACK_DIE_ROUND_TAS` 相扫由 5 调整为 10,消亡判据改为 `>`;同步更新 README/CHANGELOG/docs |
| e0bf227 | 关联判定重构并加强:TWS `track_asso.cpp` 新增按航迹距离分段的高度门限(原高度门限代码块为注释状态)、统计距离/高度/距离三类判据拆为 `thd_d`/`thd_h`/`thd_r` 布尔量;`parameters.h` 的 `ASSO_THORD` 由 3 调大至 4(等效门限 d < 4,提高机动目标关联概率);TAS `track_asso_tas.cpp` 高度门限 2000~3000 m 段由 200 m 放宽至 250 m;修复 `tas_beam_output()` 的数据率门控条件(去掉与「取最早 CPI 目标」冲突的 T_track 判据);删除 `docs/data_process_flow_report.html`;`.gitignore` 增加 `design_docs/`;同步更新 README/CHANGELOG/docs |
| f249fdc | 输出航迹新增「靠近/远离」方向标志:`track_asso.cpp`(TWS)与 `track_asso_tas.cpp`(TAS)在写入 `vr_point` 后补 `Flag_Direction = vr_point > 0 ? 1 : 0`;新增 `docs/data_process_flow_report.md`、`docs/data_process_flow_report.html`、`docs/track_init_readme.md` 三份流程与专项文档(其中 HTML 版已于 e0bf227 删除) |
| 7d5a56f | 满圈判据由「波位号变小」改为「方位波位号 == 0」(避免机扫转台反转误判,`data_process.cpp`);点-航迹头关联改为统一速度区间 `V_MIN·ΔT ≤ dis ≤ V_MAX·ΔT` 并新增 `dis ≥ |vr_point|·ΔT`、`dis ≥ |v_track_head|·ΔT` 两条多普勒-几何一致性约束,限制切向高速短航迹(`track_init.cpp`);同步更新 README/CHANGELOG |
@@ -921,9 +1035,11 @@ $$
| CHANGELOG.md | 变更日志(Keep a Changelog 格式,按 git 提交记录维护) |
| docs/data_process_flow_report.md | 数据处理流程与关键逻辑说明(按当前源码整理:前处理与判圈、凝聚、TWS/TAS 关联与滤波、航迹起始、外推消亡、TAS 波束控制、参数脉络;含与本文档的历史差异提示)——原同内容 HTML 渲染版(`data_process_flow_report.html`)已于 e0bf227 删除,HTML 版本在 git 忽略的 `design_docs/` 内留档 |
| docs/track_init_readme.md | 航迹起始(Track Init)专项说明:分阶段流程、起始条件汇总、调参直觉、输出字段映射(含未赋值字段)、已知风险与代码索引 |
| docs/data_record_debug.md | 数据记录调试功能说明:开关与构建(`RDP_DATA_RECORD`、C++20 隔离与 C 编译器要求)、运行期行为(目录/文件名、PRAGMA、失败降级、线程模型)、表结构约定、**新增记录点四步配方**、当前 6 张在用记录表与字段、查询示例与已知限制 |
| third_party/ormpp/UPSTREAM.md | 内置第三方源码的来源说明:ormpp / iguana / frozen / SQLite 版本、许可证、复现与升级步骤(含 blob 校验值) |
| archived_docs/BUG_REPORT.md | 全量逻辑 BUG 分析(P0~P3 共 37 项,含位置、说明、建议、处置结论)——过期留档,仅作历史参考 |
| archived_docs/BUG_FIX_REPORT.md | 修复明细、修复状态汇总表与 MSVC 2013 x86 编译验证记录——过期留档(对应 01d28e0 时期) |
| archived_docs/requirements.md | 修改需求说明:日志文件输出功能(rdbuf() 重定向、parameters.h 日志开关宏、日期+时间命名日志、前缀日志宏,附参考示例)——已实现,留档 |
| requirements.md | 修改需求说明:数据记录调试功能(结构体经 ormpp 映射写入 SQLite、Release 版可屏蔽、数据分类入不同表、宏记录点、便于快速新增结构体、不使用异步写入)——框架已按该说明实现,记录表已从 2 个示例扩展为 6 张在用表(7 个实体定义),详见 docs/data_record_debug.md(此前同名的日志文件输出需求亦已实现) |
| backup/data_process_class_dll/data_process_class_dll.pro | 迁移前的 Qt/qmake 工程文件(源文件清单参考) |
| .vscode/tasks.json、.vscode/cmake-variants.yaml | VS Code 构建任务与变体定义 |
| build/X256_PS/bin/RadarConfigParam.ini | 回放测试工具(rdp_playback.exe)配置样例 |
-42
View File
@@ -1,42 +0,0 @@
# 雷达数据处理项目修改说明
## 概述
增加日志文件输出功能,具体需求如下:
- 将标准输出std::cout重定向到日志文件,考虑使用 rdbuf() 方法,见[参考1](#参考1)
- 在parameters.h中添加一个日志输出开关的宏定义,开关关闭时,不重定向输出到文件
- 日志文件输出路径为“.\\rdp_logs\\”,日志文件名按当前日期+时间命名
- 增加宏定义,在输出日志时添加时间戳、代码文件、代码行数等关键信息再输出到标准输出
## 参考
### 参考1
使用 rdbuf() 方法重定向标准输出,更符合C++的流式风格,通过替换 std::cout 的底层流缓冲区(streambuf),将其指向一个文件输出流(std::ofstream)。
```cpp
#include <iostream>
#include <fstream>
int main() {
std::ofstream file("output.txt");
if (!file.is_open()) {
return 1;
}
// 保存原始的 cout 缓冲区,以便之后恢复
auto original_buf = std::cout.rdbuf();
// 将 cout 的缓冲区替换为 file 的缓冲区
std::cout.rdbuf(file.rdbuf());
// 此后的输出都会写入文件
std::cout << "这条信息会写入文件。" << std::endl;
// 恢复 cout 的原始缓冲区,让输出回到控制台
std::cout.rdbuf(original_buf);
// file 对象会在析构时自动关闭
std::cout << "这条信息会回到控制台。" << std::endl;
return 0;
}
```
+71 -38
View File
@@ -1,5 +1,7 @@
#include "data_process.h"
#include "rdp_log.h"
#include "rdp_record.h"
#include "rdp_record_entity.h"
#include <cstring>
#include <vector>
#include <cmath>
@@ -17,6 +19,7 @@ int Data_Process::data_preprocess(struct DataRev Data_Input[150])
{
// 更新当前系统时间
latest_timestamp = Data_Input[0].CPI_time;
frame_id++;
//数据存入缓存区域 Data_buffer
if(Data_Input[0].point_type == 0) //tws数据
@@ -26,32 +29,47 @@ int Data_Process::data_preprocess(struct DataRev Data_Input[150])
for (int i=0; i<data_num;i++)
{
PointRecv Data_buffer_temp;
Data_buffer_temp.Amplitude=Data_Input[i].Amplitude;
Data_buffer_temp.Azimuth=Data_Input[i].Azimuth/180*PI;
Data_buffer_temp.snr = Data_Input[i].Snr;
Data_buffer_temp.point_id = point_id++;
Data_buffer_temp.frame_id = frame_id;
Data_buffer_temp.circle_id = circle_id;
Data_buffer_temp.amplitude=Data_Input[i].Amplitude;
Data_buffer_temp.azimuth=Data_Input[i].Azimuth/180*PI;
Data_buffer_temp.SNR = Data_Input[i].Snr;
Data_buffer_temp.RCS = Data_Input[i].RCS;
Data_buffer_temp.CPI_Time = Data_Input[i].CPI_time;
Data_buffer_temp.CPI_time = Data_Input[i].CPI_time;
Data_buffer_temp.GNSS_time = Data_Input[i].GNSS_time;
Data_buffer_temp.Freq_index = Data_Input[i].Freq_index;
Data_buffer_temp.freq_index = Data_Input[i].Freq_index;
Data_buffer_temp.PRF_index = Data_Input[i].PRI;
Data_buffer_temp.Range = Data_Input[i].Range;
Data_buffer_temp.Height= Data_Input[i].Range*sin(Data_Input[i].Elevation/180*PI);
Data_buffer_temp.Use_Flag = 0;
Data_buffer_temp.Velocity = Data_Input[i].Velocity;
Data_buffer_temp.pitch_num = Data_Input[i].pitch_num;
Data_buffer_temp.range = Data_Input[i].Range;
Data_buffer_temp.height= Data_Input[i].Range*sin(Data_Input[i].Elevation/180*PI);
Data_buffer_temp.use_flag = 0;
Data_buffer_temp.velocity = Data_Input[i].Velocity;
Data_buffer_temp.encoder_value = Data_Input[i].Encoder_value;
Data_buffer_temp.pitch_beam_index = Data_Input[i].pitch_num;
Data_buffer_temp.azimuth_beam_index = Data_Input[i].Beam_index_aiz;
Data_buffer_temp.point_type = Data_Input[i].point_type;
Data_buffer_temp.amp_asv_point = Data_Input[i].amp_asv;
Data_buffer_temp.prn_fftn_point = Data_Input[i].prn_fftn;
std::memcpy(Data_buffer_temp.speed_dim, Data_Input[i].speed_dim, sizeof(Data_buffer_temp.speed_dim));
std::memcpy(Data_buffer_temp.range_dim, Data_Input[i].range_dim, sizeof(Data_buffer_temp.range_dim));
//数据记录示例①:把本次接收的输入点迹写入表 RdpRecPointTws(字段为示例,可按需增删)。
//记录功能关闭时该语句展开为空操作(实参不求值),交付构建不产生任何代码,
//用法与新增记录点配方见 rdp_record.h 与 docs/data_record_debug.md。
RDP_RECORD(RdpRecPoint(Data_buffer_temp));
Data_buffer.push_back(Data_buffer_temp);
}
// 修改一周扫描完成的判断条件,原来采用判断波位号变小为一周扫描完成,但机扫模式下,可能存在转台反转的情况,导致波位号变小,但并未扫描一周,因此改为判断波位号为0时,表示扫描一周完成
if(Data_Input[0].Beam_index_aiz == 0) //TWS
#ifdef PHASE_SCANNING
// 相扫模式下,判断前后两帧波位号的大小,来判断是否扫描一周完成
if(Data_Input[0].Beam_index_aiz <= last_beam_num) //TWS
{
last_beam_num = Data_Input[0].Beam_index_aiz;
circle_id++;
// std::cout << Data_Input[0].CPI_time << std::endl;
// RDP_LOG<<"CPI_time: "<<Data_Input[0].CPI_time<<std::endl;
return 1;
}
else
@@ -59,19 +77,23 @@ int Data_Process::data_preprocess(struct DataRev Data_Input[150])
last_beam_num = Data_Input[0].Beam_index_aiz;
return 2;
}
// if(Data_Input[0].Beam_index_aiz < last_beam_num) //TWS
// {
// last_beam_num = Data_Input[0].Beam_index_aiz;
// return 1;
// }
// else
// {
// last_beam_num = Data_Input[0].Beam_index_aiz;
// return 2;
// }
#else
// 机扫模式下,可能存在转台反转的情况,导致波位号变小,但并未扫描一周,因此改为判断波位号为0时,表示扫描一周完成
if(Data_Input[0].Beam_index_aiz == 0) //TWS
{
last_beam_num = Data_Input[0].Beam_index_aiz;
circle_id++;
// std::cout << Data_Input[0].CPI_time << std::endl;
// RDP_LOG<<"CPI_time: "<<Data_Input[0].CPI_time<<std::endl;
return 1;
}
else
{
last_beam_num = Data_Input[0].Beam_index_aiz;
return 2;
}
#endif
}
else if(Data_Input[0].point_type == 1) //tas数据
{
// std::cout << "TAS TARGET :" <<Data_Input[0].TAS_track_index;
@@ -82,22 +104,33 @@ int Data_Process::data_preprocess(struct DataRev Data_Input[150])
for (int i=0; i<data_num;i++)
{
PointRecv Data_buffer_temp;
Data_buffer_temp.Amplitude=Data_Input[i].Amplitude;
Data_buffer_temp.Azimuth=Data_Input[i].Azimuth/180*PI;
Data_buffer_temp.snr = Data_Input[i].Snr;
Data_buffer_temp.point_id = point_id++;
Data_buffer_temp.frame_id = frame_id;
Data_buffer_temp.circle_id = circle_id;
Data_buffer_temp.amplitude=Data_Input[i].Amplitude;
Data_buffer_temp.azimuth=Data_Input[i].Azimuth/180*PI;
Data_buffer_temp.SNR = Data_Input[i].Snr;
Data_buffer_temp.RCS = Data_Input[i].RCS;
Data_buffer_temp.CPI_Time = Data_Input[i].CPI_time;
Data_buffer_temp.CPI_time = Data_Input[i].CPI_time;
Data_buffer_temp.GNSS_time = Data_Input[i].GNSS_time;
Data_buffer_temp.Freq_index = Data_Input[i].Freq_index;
Data_buffer_temp.freq_index = Data_Input[i].Freq_index;
Data_buffer_temp.PRF_index = Data_Input[i].PRI;
Data_buffer_temp.Range = Data_Input[i].Range;
Data_buffer_temp.Height= Data_Input[i].Range*sin(Data_Input[i].Elevation/180*PI);
Data_buffer_temp.Use_Flag = 0;
Data_buffer_temp.Velocity = Data_Input[i].Velocity;
Data_buffer_temp.pitch_num = Data_Input[i].pitch_num;
Data_buffer_temp.range = Data_Input[i].Range;
Data_buffer_temp.height= Data_Input[i].Range*sin(Data_Input[i].Elevation/180*PI);
Data_buffer_temp.use_flag = 0;
Data_buffer_temp.velocity = Data_Input[i].Velocity;
Data_buffer_temp.pitch_beam_index = Data_Input[i].pitch_num;
Data_buffer_temp.encoder_value = Data_Input[i].Encoder_value;
Data_buffer_temp.azimuth_beam_index = Data_Input[i].Beam_index_aiz;
Data_buffer_temp.point_type = Data_Input[i].point_type;
Data_buffer_temp.amp_asv_point = Data_Input[i].amp_asv;
Data_buffer_temp.prn_fftn_point = Data_Input[i].prn_fftn;
std::memcpy(Data_buffer_temp.speed_dim, Data_Input[i].speed_dim, sizeof(Data_buffer_temp.speed_dim));
std::memcpy(Data_buffer_temp.range_dim, Data_Input[i].range_dim, sizeof(Data_buffer_temp.range_dim));
RDP_RECORD(RdpRecPoint(Data_buffer_temp));
Data_buffer_tas.push_back(Data_buffer_temp);
// std::cout << "Azimuth:" << Data_Input[i].Azimuth
@@ -107,7 +140,7 @@ int Data_Process::data_preprocess(struct DataRev Data_Input[150])
// << "data_num:" << data_num;
}
circle_id++;
return 3;
}
@@ -131,7 +164,6 @@ int model)
{
dot_coh.dot_coh_process_buff(&Data_buffer,&point_recv,Work_Parameter);
//std::cout << "point_recv :" <<point_recv.size();
std::vector<PointRecv>().swap(Data_buffer);
}
@@ -144,6 +176,7 @@ int model)
&trust_track,
Trust_Track_Output,
Trust_track_num_Output,
latest_timestamp,
Work_Parameter
);
+6
View File
@@ -98,6 +98,12 @@ private:
long long latest_timestamp = 0; //最新时间戳
long long point_id = 0; //点迹id
long long frame_id = 0; //帧id
long long circle_id = 0; //圈id
struct RadarPara Work_Parameter; //工作参数 参数设置函数外部输入
Dot_Coh dot_coh; //点迹凝聚
@@ -4,6 +4,8 @@
#include "data_process_class_dll.h"
#include "data_process.h"
#include "rdp_log.h"
#include "rdp_record.h"
#include "parameters.h"
/*
Data_process_class_dll::Data_process_class_dll()
@@ -19,7 +21,13 @@ Data_process_class_dll *Data_Process_Factory::GetB()
{
//初始化日志输出:RDP_LOG_ENABLE 开启时重定向 std::cout 到日志文件
//rdp_log::Init();
//初始化数据记录:RDP_DATA_RECORD 开启时创建记录目录、打开本次运行的记录库;
//关闭时 rdp_record::Init() 为空实现,不产生任何开销
rdp_record::Init();
p=new Data_Process();
p->version_info = VERSION_STRING;
}
return p;
@@ -36,5 +44,8 @@ void Data_Process_Factory::Destroy()
//恢复 std::cout 到控制台并关闭日志文件
//rdp_log::Shutdown();
//关闭记录库(RDP_DATA_RECORD 关闭时为空实现)
rdp_record::Shutdown();
}
}
@@ -3,6 +3,7 @@
#define DATA_PROCESS_CLASS_DLL_H
#include "data_process_class_dll_global.h"
#include <string>
/***************************************************** 结构体 ********************************************************/
// 输入点迹的数据结构 用户可见
@@ -50,6 +51,7 @@ struct DATA_PROCESS_CLASS_DLLSHARED_EXPORT Track
float Elevation; //俯仰
float Range_V; //速度
float Amplitude; //幅度
float Quality; //航迹质量
int Track_Index; //航迹号
int Point_Sum; //本条航迹更新点的数目
int Flag_Direction; //方向标志1靠近/0远离
@@ -176,6 +178,7 @@ class DATA_PROCESS_CLASS_DLLSHARED_EXPORT Data_process_class_dll
public:
// Data_process_class_dll();
const char* version_info;
//数据预处理函数
virtual int data_preprocess(struct DataRev Data_Input[150]) //雷达的输入点迹 主程序创建全局变量 输入数据
+50 -46
View File
@@ -2,6 +2,9 @@
#include <cmath>
#include <cstring>
#include <vector>
#include "rdp_record.h"
#include "rdp_record_entity.h"
using namespace std;
int Dot_Coh::dot_coh_process(std::vector <PointRecv> *data_input,
@@ -14,20 +17,20 @@ int Dot_Coh::dot_coh_process(std::vector <PointRecv> *data_input,
for (unsigned int loop_of_point=0; loop_of_point<data_input->size()-1;loop_of_point++ )
{
if( (*data_input)[loop_of_point].Use_Flag!=1)
if( (*data_input)[loop_of_point].use_flag!=1)
{
float point_0_R=static_cast<float>((*data_input)[loop_of_point].Range);
float point_0_V=static_cast<float>((*data_input)[loop_of_point].Velocity);
float point_0_F=static_cast<float>((*data_input)[loop_of_point].Azimuth);
float point_0_A=static_cast<float>((*data_input)[loop_of_point].Amplitude);
float point_0_R=static_cast<float>((*data_input)[loop_of_point].range);
float point_0_V=static_cast<float>((*data_input)[loop_of_point].velocity);
float point_0_F=static_cast<float>((*data_input)[loop_of_point].azimuth);
float point_0_A=static_cast<float>((*data_input)[loop_of_point].amplitude);
for (unsigned int i=loop_of_point+1;i<data_input->size();i++)
{
if((*data_input)[loop_of_point].Use_Flag!=1 && (*data_input)[i].Use_Flag!=1)
if((*data_input)[loop_of_point].use_flag!=1 && (*data_input)[i].use_flag!=1)
{
float point_1_R=static_cast<float>((*data_input)[i].Range);
float point_1_V=static_cast<float>((*data_input)[i].Velocity);
float point_1_F=static_cast<float>((*data_input)[i].Azimuth);
float point_1_A=static_cast<float>((*data_input)[i].Amplitude);
float point_1_R=static_cast<float>((*data_input)[i].range);
float point_1_V=static_cast<float>((*data_input)[i].velocity);
float point_1_F=static_cast<float>((*data_input)[i].azimuth);
float point_1_A=static_cast<float>((*data_input)[i].amplitude);
//凝聚条件: 距离、方位接近
if(Work_Parameter.work_mode == 0)
@@ -36,16 +39,16 @@ int Dot_Coh::dot_coh_process(std::vector <PointRecv> *data_input,
if( (fabs(point_0_R-point_1_R)<=DOT_COH_RANGE && delta_F <=DOT_COH_AZI/180.0*PI && fabs(point_0_V-point_1_V)<=DOT_COH_V)//
&& point_0_A <point_1_A )//
{
point_0_R=static_cast<float>((*data_input)[i].Range);
point_0_V=static_cast<float>((*data_input)[i].Velocity);
point_0_A=static_cast<float>((*data_input)[i].Amplitude);
point_0_F=static_cast<float>((*data_input)[i].Azimuth);
(*data_input)[loop_of_point].Use_Flag=1;
point_0_R=static_cast<float>((*data_input)[i].range);
point_0_V=static_cast<float>((*data_input)[i].velocity);
point_0_A=static_cast<float>((*data_input)[i].amplitude);
point_0_F=static_cast<float>((*data_input)[i].azimuth);
(*data_input)[loop_of_point].use_flag=1;
}
else if( (fabs(point_0_R-point_1_R)<=DOT_COH_RANGE && delta_F <=DOT_COH_AZI/180.0*PI && fabs(point_0_V-point_1_V)<=DOT_COH_V)//
&& point_0_A >= point_1_A)//
{
(*data_input)[i].Use_Flag=1;
(*data_input)[i].use_flag=1;
}
}
else
@@ -54,16 +57,16 @@ int Dot_Coh::dot_coh_process(std::vector <PointRecv> *data_input,
if( (fabs(point_0_R-point_1_R)<=DOT_COH_RANGE && delta_F <=DOT_COH_AZI/180.0*PI )//
&& point_0_A <point_1_A )//
{
point_0_R=static_cast<float>((*data_input)[i].Range);
point_0_V=static_cast<float>((*data_input)[i].Velocity);
point_0_A=static_cast<float>((*data_input)[i].Amplitude);
point_0_F=static_cast<float>((*data_input)[i].Azimuth);
(*data_input)[loop_of_point].Use_Flag=1;
point_0_R=static_cast<float>((*data_input)[i].range);
point_0_V=static_cast<float>((*data_input)[i].velocity);
point_0_A=static_cast<float>((*data_input)[i].amplitude);
point_0_F=static_cast<float>((*data_input)[i].azimuth);
(*data_input)[loop_of_point].use_flag=1;
}
else if( (fabs(point_0_R-point_1_R)<=DOT_COH_RANGE && delta_F <=DOT_COH_AZI/180.0*PI )//
&& point_0_A >= point_1_A)//
{
(*data_input)[i].Use_Flag=1;
(*data_input)[i].use_flag=1;
}
}
@@ -77,7 +80,7 @@ int Dot_Coh::dot_coh_process(std::vector <PointRecv> *data_input,
std::vector <PointRecv>::iterator Iter;
for (Iter=data_input->begin(); Iter!=data_input->end();)
{
if((*Iter).Use_Flag==1 || (*Iter).Range <R_MIN || (*Iter).Range > R_MAX )
if((*Iter).use_flag==1 || (*Iter).range <R_MIN || (*Iter).range > R_MAX )
{
data_input->erase(Iter);
Iter=data_input->begin();
@@ -130,20 +133,20 @@ int Dot_Coh::dot_coh_process_buff( std::vector <PointRecv> *data_input,
for (unsigned int loop_of_point=0; loop_of_point<data_tmp.size()-1;loop_of_point++ )
{
if( data_tmp[loop_of_point].Use_Flag!=1)
if( data_tmp[loop_of_point].use_flag!=1)
{
float point_0_R=static_cast<float>(data_tmp[loop_of_point].Range);
float point_0_V=static_cast<float>(data_tmp[loop_of_point].Velocity);
float point_0_F=static_cast<float>(data_tmp[loop_of_point].Azimuth);
float point_0_A=static_cast<float>(data_tmp[loop_of_point].Amplitude);
float point_0_R=static_cast<float>(data_tmp[loop_of_point].range);
float point_0_V=static_cast<float>(data_tmp[loop_of_point].velocity);
float point_0_F=static_cast<float>(data_tmp[loop_of_point].azimuth);
float point_0_A=static_cast<float>(data_tmp[loop_of_point].amplitude);
for (unsigned int i=loop_of_point+1;i<data_tmp.size();i++)
{
if(data_tmp[loop_of_point].Use_Flag!=1 && data_tmp[i].Use_Flag!=1)
if(data_tmp[loop_of_point].use_flag!=1 && data_tmp[i].use_flag!=1)
{
float point_1_R=static_cast<float>(data_tmp[i].Range);
float point_1_V=static_cast<float>(data_tmp[i].Velocity);
float point_1_F=static_cast<float>(data_tmp[i].Azimuth);
float point_1_A=static_cast<float>(data_tmp[i].Amplitude);
float point_1_R=static_cast<float>(data_tmp[i].range);
float point_1_V=static_cast<float>(data_tmp[i].velocity);
float point_1_F=static_cast<float>(data_tmp[i].azimuth);
float point_1_A=static_cast<float>(data_tmp[i].amplitude);
//凝聚条件: 距离、方位接近
if(Work_Parameter.work_mode == 0)
@@ -152,16 +155,16 @@ int Dot_Coh::dot_coh_process_buff( std::vector <PointRecv> *data_input,
if( (fabs(point_0_R-point_1_R)<=DOT_COH_RANGE && delta_F <=DOT_COH_AZI/180.0*PI && fabs(point_0_V-point_1_V)<=DOT_COH_V)//
&& point_0_A <point_1_A )//
{
point_0_R=static_cast<float>(data_tmp[i].Range);
point_0_V=static_cast<float>(data_tmp[i].Velocity);
point_0_A=static_cast<float>(data_tmp[i].Amplitude);
point_0_F=static_cast<float>(data_tmp[i].Azimuth);
data_tmp[loop_of_point].Use_Flag=1;
point_0_R=static_cast<float>(data_tmp[i].range);
point_0_V=static_cast<float>(data_tmp[i].velocity);
point_0_A=static_cast<float>(data_tmp[i].amplitude);
point_0_F=static_cast<float>(data_tmp[i].azimuth);
data_tmp[loop_of_point].use_flag=1;
}
else if( (fabs(point_0_R-point_1_R)<=DOT_COH_RANGE && delta_F <=DOT_COH_AZI/180.0*PI && fabs(point_0_V-point_1_V)<=DOT_COH_V)//
&& point_0_A >= point_1_A)//
{
data_tmp[i].Use_Flag=1;
data_tmp[i].use_flag=1;
}
}
else
@@ -170,16 +173,16 @@ int Dot_Coh::dot_coh_process_buff( std::vector <PointRecv> *data_input,
if( (fabs(point_0_R-point_1_R)<=DOT_COH_RANGE && delta_F <=DOT_COH_AZI/180.0*PI )//
&& point_0_A <point_1_A )//
{
point_0_R=static_cast<float>(data_tmp[i].Range);
point_0_V=static_cast<float>(data_tmp[i].Velocity);
point_0_A=static_cast<float>(data_tmp[i].Amplitude);
point_0_F=static_cast<float>(data_tmp[i].Azimuth);
data_tmp[loop_of_point].Use_Flag=1;
point_0_R=static_cast<float>(data_tmp[i].range);
point_0_V=static_cast<float>(data_tmp[i].velocity);
point_0_A=static_cast<float>(data_tmp[i].amplitude);
point_0_F=static_cast<float>(data_tmp[i].azimuth);
data_tmp[loop_of_point].use_flag=1;
}
else if( (fabs(point_0_R-point_1_R)<=DOT_COH_RANGE && delta_F <=DOT_COH_AZI/180.0*PI )//
&& point_0_A >= point_1_A)//
{
data_tmp[i].Use_Flag=1;
data_tmp[i].use_flag=1;
}
}
@@ -193,7 +196,7 @@ int Dot_Coh::dot_coh_process_buff( std::vector <PointRecv> *data_input,
std::vector <PointRecv>::iterator Iter;
for (Iter=data_tmp.begin(); Iter!=data_tmp.end();)
{
if((*Iter).Use_Flag==1 || (*Iter).Range <R_MIN || (*Iter).Range > R_MAX )
if((*Iter).use_flag==1 || (*Iter).range <R_MIN || (*Iter).range > R_MAX )
{
data_tmp.erase(Iter);
Iter=data_tmp.begin();
@@ -224,6 +227,7 @@ int Dot_Coh::dot_coh_process_buff( std::vector <PointRecv> *data_input,
for (size_t i=0;i<data_input_buff.size();i++)
{
(*point_recv).push_back(data_input_buff[i]);
RDP_RECORD(RdpRecPointCohesionTWS(data_input_buff[i]));
}
std::vector<PointRecv>().swap(data_input_buff);
+20 -17
View File
@@ -2,6 +2,8 @@
#include <cmath>
#include <cstring>
#include <vector>
#include "rdp_record.h"
#include "rdp_record_entity.h"
using namespace std;
int Dot_Coh_TAS::dot_coh_tas_process(std::vector <PointRecv> *data_input, //输入点迹
@@ -14,36 +16,36 @@ int Dot_Coh_TAS::dot_coh_tas_process(std::vector <PointRecv> *dat
for (size_t loop_of_point=0; loop_of_point<data_input->size()-1;loop_of_point++ )
{
if( (*data_input)[loop_of_point].Use_Flag!=1)
if( (*data_input)[loop_of_point].use_flag!=1)
{
float point_0_R=static_cast<float>((*data_input)[loop_of_point].Range);
float point_0_V=static_cast<float>((*data_input)[loop_of_point].Velocity);
float point_0_F=static_cast<float>((*data_input)[loop_of_point].Azimuth);
float point_0_A=static_cast<float>((*data_input)[loop_of_point].Amplitude);
float point_0_R=static_cast<float>((*data_input)[loop_of_point].range);
float point_0_V=static_cast<float>((*data_input)[loop_of_point].velocity);
float point_0_F=static_cast<float>((*data_input)[loop_of_point].azimuth);
float point_0_A=static_cast<float>((*data_input)[loop_of_point].amplitude);
for ( size_t i=loop_of_point+1;i<(*data_input).size();i++)
{
if((*data_input)[loop_of_point].Use_Flag!=1 && (*data_input)[i].Use_Flag!=1)
if((*data_input)[loop_of_point].use_flag!=1 && (*data_input)[i].use_flag!=1)
{
float point_1_R=static_cast<float>((*data_input)[i].Range);
float point_1_V=static_cast<float>((*data_input)[i].Velocity);
float point_1_F=static_cast<float>((*data_input)[i].Azimuth);
float point_1_A=static_cast<float>((*data_input)[i].Amplitude);
float point_1_R=static_cast<float>((*data_input)[i].range);
float point_1_V=static_cast<float>((*data_input)[i].velocity);
float point_1_F=static_cast<float>((*data_input)[i].azimuth);
float point_1_A=static_cast<float>((*data_input)[i].amplitude);
//凝聚条件: 距离、方位接近
float delta_F = fabs(point_0_F-point_1_F)<2*PI-fabs(point_0_F-point_1_F) ? fabs(point_0_F-point_1_F) :2*PI-fabs(point_0_F-point_1_F);
if( (fabs(point_0_R-point_1_R)<=DOT_COH_RANGE && delta_F <=DOT_COH_AZI/180.0*PI && fabs(point_0_V-point_1_V)<=DOT_COH_V)
&& point_0_A <point_1_A )
{
point_0_R=static_cast<float>((*data_input)[i].Range);
point_0_V=static_cast<float>((*data_input)[i].Velocity);
point_0_A=static_cast<float>((*data_input)[i].Amplitude);
point_0_F=static_cast<float>((*data_input)[i].Azimuth);
(*data_input)[loop_of_point].Use_Flag=1;
point_0_R=static_cast<float>((*data_input)[i].range);
point_0_V=static_cast<float>((*data_input)[i].velocity);
point_0_A=static_cast<float>((*data_input)[i].amplitude);
point_0_F=static_cast<float>((*data_input)[i].azimuth);
(*data_input)[loop_of_point].use_flag=1;
}
else if( (fabs(point_0_R-point_1_R)<=DOT_COH_RANGE && delta_F <=DOT_COH_AZI/180.0*PI && fabs(point_0_V-point_1_V)<=DOT_COH_V)
&& point_0_A >= point_1_A)
{
(*data_input)[i].Use_Flag=1;
(*data_input)[i].use_flag=1;
}
}
}
@@ -54,7 +56,7 @@ int Dot_Coh_TAS::dot_coh_tas_process(std::vector <PointRecv> *dat
std::vector <PointRecv>::iterator Iter;
for (Iter=data_input->begin(); Iter!=data_input->end();)
{
if((*Iter).Use_Flag==1 || (*Iter).Range <R_MIN || (*Iter).Range > R_MAX )
if((*Iter).use_flag==1 || (*Iter).range <R_MIN || (*Iter).range > R_MAX )
{
data_input->erase(Iter);
Iter=data_input->begin();
@@ -68,6 +70,7 @@ int Dot_Coh_TAS::dot_coh_tas_process(std::vector <PointRecv> *dat
for (size_t i=0;i<data_input->size();i++)
{
(*point_recv_tas).push_back((*data_input)[i]);
RDP_RECORD(RdpRecPointCohesionTAS((*data_input)[i]));
}
return 1;
+52 -2
View File
@@ -7,6 +7,25 @@
//频点(GHz)
#define FREQ0 9.2
/**********************************版本信息*************************************************/
#define MAJOR_VERSION 1
#define MINOR_VERSION 5
#define PATCH_VERSION 8
#ifdef PHASE_SCANNING
#define VERSION_INFO "PS"
#else
#define VERSION_INFO "MS"
#endif
#define STRINGIFY_IMPL(x) #x
#define STRINGIFY(x) STRINGIFY_IMPL(x)
#define VERSION_STRING \
"V" STRINGIFY(MAJOR_VERSION) "." \
STRINGIFY(MINOR_VERSION) "." \
STRINGIFY(PATCH_VERSION) "." \
VERSION_INFO
/**********************************日志输出开关**********************************************/
// 日志文件输出功能开关:
// 1) 定义(默认)RDP_LOG_ENABLE:启用日志文件输出 —— 调用 Data_Process_Factory::GetB()
@@ -18,11 +37,42 @@
//日志文件输出目录(相对当前工作目录的路径,目录不存在时自动创建)
#define RDP_LOG_DIR ".\\rdp_logs\\"
/**********************************数据记录调试开关*******************************************/
// 数据记录调试功能(结构体 -> SQLite 数据表,ORM 由内置 ormpp 完成)由 CMake 选项
// RDP_DATA_RECORD(AUTO/ON/OFF)控制:AUTO 时 Debug 构建启用、Release 构建关闭(交付默认)。
// 启用时由构建系统定义 RDP_RECORD_ENABLE,记录模块(rdp_record.cpp + sqlite3)才参与编译;
// 关闭时 RDP_RECORD 宏展开为 ((void)0),实参不求值,不做任何记录、不留任何运行开销。
// 注意:此处不提供手工开关宏,请在构建配置中选择(详见 docs/data_record_debug.md)。
//记录数据库存放目录(相对宿主进程当前工作目录,不存在时自动创建)
#define RDP_RECORD_DB_DIR ".\\rdp_records\\"
//#define MECHANICAL_SCANNING
//#define PHASE_SCANNING
/******************************通用数据处理参数**************************************/
//航迹质量管理参数
#define TRACK_QUALITY_MAX 100
#define TRACK_QUALITY_MIN 0
#define TRACK_QUALITY_D_WEIGHT 0.5
#define TRACK_QUALITY_SNR_WEIGHT 0.3
#define TRACK_QUALITY_LEN_WEIGHT 0.2
#define TRACK_QUALITY_ITER_COEFF 0.8
#define TRACK_QUALITY_DECREASE_COEFF_O2 -0.0008
#define TRACK_QUALITY_DECREASE_COEFF_O1 -0.0025
//航迹起批质量条件
#define TRACK_START_QUALITY_L3 0.7
#define TRACK_START_QUALITY_L4 0.6
#define TRACK_START_QUALITY_L5 0.5
#define TRACK_START_QUALITY_L6 0.4
#define TRACK_START_QUALITY_L7 0.3
//航迹消批质量条件
#define TRACK_DIE_QUALITY 0.3
#define TRACK_DIE_QUALITY_PRAMARY 0.3
#ifdef MECHANICAL_SCANNING
/******************************数据处理参数**************************************/
/******************************机扫数据处理参数**************************************/
// #define DATA_RATE_TAS 0.0625
#define SIGMA_R 10.0 //测量误差
@@ -73,7 +123,7 @@
#ifdef PHASE_SCANNING
/******************************数据处理参数**************************************/
/******************************相扫数据处理参数**************************************/
// #define DATA_RATE_TAS 0.3
#define SIGMA_R 10.0 //测量误差
+273
View File
@@ -0,0 +1,273 @@
/***********************************数据记录调试模块实现****************************************/
// 本文件仅在构建系统定义 RDP_RECORD_ENABLE(CMake 选项 RDP_DATA_RECORD 开启)时参与编译,
// 且以 C++20 编译(ormpp 要求);雷达处理代码仍为 C++17,二者通过 rdp_record.h 的
// 「仅声明 + 显式实例化」跨标准边界,详见 docs/data_record_debug.md。
#ifdef RDP_RECORD_ENABLE
#include "rdp_record.h"
#include "rdp_record_entity.h"
#include "parameters.h"
#include "rdp_log.h"
#include <climits>
#include <direct.h>
#include <memory>
#include <mutex>
#include <string>
#include <time.h>
#include <vector>
//第三方库(ormpp / iguana)模板代码在 /W3 下可能产生告警,按 third_party/ormpp/UPSTREAM.md
//的约定在此隔离;本文件自身的逻辑仍按工程 /W3 标准编译
#pragma warning(push, 0)
#include "dbng.hpp"
#include "sqlite.hpp"
#pragma warning(pop)
//记录实体写入函数的显式实例化宏(与 rdp_record_entity.h 的 RDP_RECORD_DECLARE 配套使用)
#define RDP_RECORD_DEFINE(Entity) \
template void rdp_record::Write<Entity>(const Entity&); \
template void rdp_record::Write<Entity>(const std::vector<Entity>&)
namespace
{
//记录模块内部状态(Pimpl:ormpp / SQLite 类型不出现在 rdp_record.h 中)
struct Record_State
{
ormpp::dbng<ormpp::sqlite> db; //SQLite 连接(单连接,写入时串行使用)
std::string path; //当前 db 文件全路径
bool ready; //是否已成功打开并完成初始化
bool failed; //初始化失败:后续写入全部忽略,不再反复报错
std::mutex mtx; //写入串行化(宿主多线程调用时保护单连接)
Record_State() : ready(false), failed(false) {}
};
std::unique_ptr<Record_State>& State()
{
//函数内静态:宿主未调用 Data_Process_Factory::Destroy() 时由析构兜底关库
static std::unique_ptr<Record_State> s;
return s;
}
//当前本地时间字符串,格式 "YYYYMMDD_HHMMSS"(与 rdp_log 的日志文件命名风格一致)
bool TimeStamp(char* buf, size_t size)
{
time_t now = time(NULL);
struct tm local;
if (localtime_s(&local, &now) != 0)
{
return false;
}
return strftime(buf, size, "%Y%m%d_%H%M%S", &local) != 0;
}
//写入失败提示(「每个实体只提示一次」由调用方的静态标志控制)
void ReportError(Record_State& st, const char* what)
{
RDP_LOG << "[rdp_record] " << what << " failed: " << st.db.get_last_error() << std::endl;
}
//确保实体的数据表存在:每个实体类型只尝试建表一次(失败后该实体永久跳过)
//注意:ormpp 的 ormpp_auto_key 定义在全局命名空间(third_party/ormpp/ormpp/entity.hpp),
// 传该参数可在建表时登记自增主键,使后续插入自动跳过 id 列
template <typename T>
bool EnsureTable(Record_State& st)
{
static const bool created = st.db.create_datatable<T>(::ormpp_auto_key{"id"});
return created;
}
//写入前置处理:自动建表;返回 true 表示可以向该表写入
template <typename T>
bool PrepareWrite(Record_State& st)
{
static bool reported = false;
if (EnsureTable<T>(st))
{
return true;
}
if (!reported)
{
reported = true;
ReportError(st, "create table");
}
return false;
}
}
namespace rdp_record
{
bool Init()
{
std::unique_ptr<Record_State>& sp = State();
if (sp.get() != 0)
{
return sp->ready; //已经初始化过(含失败的幂等返回)
}
sp.reset(new Record_State());
Record_State& st = *sp;
//创建记录目录(目录已存在时 _mkdir 返回 EEXIST,忽略;逻辑与 rdp_log.cpp 一致)
std::string dir = RDP_RECORD_DB_DIR;
while (dir.size() > 0 && (dir[dir.size() - 1] == '\\' || dir[dir.size() - 1] == '/'))
{
dir.erase(dir.size() - 1);
}
if (dir.size() > 0)
{
_mkdir(dir.c_str());
}
//按当前日期+时间生成记录库文件名:rdp_record_YYYYMMDD_HHMMSS.db
char stamp[32] = {0};
if (!TimeStamp(stamp, sizeof(stamp)))
{
RDP_LOG << "[rdp_record] cannot get local time, recording disabled" << std::endl;
st.failed = true;
return false;
}
st.path = RDP_RECORD_DB_DIR;
st.path += "rdp_record_";
st.path += stamp;
st.path += ".db";
if (!st.db.connect(st.path))
{
RDP_LOG << "[rdp_record] cannot open database: " << st.path << std::endl;
st.failed = true;
return false;
}
//调试用途的性能取舍:关闭同步刷盘(提交时不再 fsync),崩溃时可能丢失尾部数据;
//如需崩溃安全可改为 PRAGMA synchronous=NORMAL(或在 sqlite3 层自行开启 WAL)。
//注意:ormpp 的 execute() 只接受「不返回结果行」的语句,故不在此设置 journal_mode。
st.db.execute("PRAGMA synchronous=OFF;");
st.ready = true;
RDP_LOG << "[rdp_record] enabled, database: " << st.path << std::endl;
return true;
}
void Shutdown()
{
std::unique_ptr<Record_State>& sp = State();
if (sp.get() == 0 || !sp->ready)
{
return;
}
std::lock_guard<std::mutex> lock(sp->mtx);
RDP_LOG << "[rdp_record] shutdown, database: " << sp->path << std::endl;
sp->db.disconnect();
sp->ready = false; //path 保留,便于宿主在关库后仍能取到本次记录库路径
}
std::string db_path()
{
std::unique_ptr<Record_State>& sp = State();
return (sp.get() == 0) ? std::string() : sp->path;
}
template <typename T>
void Write(const T& v)
{
std::unique_ptr<Record_State>& sp = State();
if (sp.get() == 0 || !sp->ready || sp->failed)
{
return; //未初始化 / 初始化失败 / 已关库:静默忽略
}
std::lock_guard<std::mutex> lock(sp->mtx);
if (!PrepareWrite<T>(*sp))
{
return;
}
//写入失败时每个实体只提示一次:不重试、不抛出、不影响雷达数据处理
static bool reported = false;
if (sp->db.insert(v) == INT_MIN && !reported)
{
reported = true;
ReportError(*sp, "insert");
}
}
template <typename T>
void Write(const std::vector<T>& v)
{
if (v.empty())
{
return;
}
std::unique_ptr<Record_State>& sp = State();
if (sp.get() == 0 || !sp->ready || sp->failed)
{
return;
}
std::lock_guard<std::mutex> lock(sp->mtx);
if (!PrepareWrite<T>(*sp))
{
return;
}
//写入失败时每个实体只提示一次:不重试、不抛出、不影响雷达数据处理
static bool reported = false;
if (sp->db.insert(v) == INT_MIN && !reported)
{
reported = true;
ReportError(*sp, "insert");
}
}
}
/***********************************记录实体注册区*********************************************/
// 新增记录实体时在此追加两行(字段顺序必须与 rdp_record_entity.h 中的结构体声明顺序一致):
// YLT_REFL(结构体名, 字段1, 字段2, ...);
// RDP_RECORD_DEFINE(结构体名);
//输入点迹
YLT_REFL(RdpRecPoint, id,point_id,frame_id,circle_id,range,azimuth, velocity,amplitude ,height,SNR,RCS,
encoder_value,amp_asv_point,prn_fftn_point,CPI_time,GNSS_time,use_flag,azimuth_beam_index,pitch_beam_index,PRF_index,freq_index,point_type,point_section_asso);
RDP_RECORD_DEFINE(RdpRecPoint);
//TWS凝聚后点迹
YLT_REFL(RdpRecPointCohesionTWS, id,point_id,frame_id,circle_id,range,azimuth, velocity,amplitude ,height,SNR,RCS,
encoder_value,amp_asv_point,prn_fftn_point,CPI_time,GNSS_time,use_flag,azimuth_beam_index,pitch_beam_index,PRF_index,freq_index,point_type,point_section_asso);
RDP_RECORD_DEFINE(RdpRecPointCohesionTWS);
//TAS凝聚后点迹
YLT_REFL(RdpRecPointCohesionTAS, id,point_id,frame_id,circle_id,range,azimuth, velocity,amplitude ,height,SNR,RCS,
encoder_value,amp_asv_point,prn_fftn_point,CPI_time,GNSS_time,use_flag,azimuth_beam_index,pitch_beam_index,PRF_index,freq_index,point_type,point_section_asso);
RDP_RECORD_DEFINE(RdpRecPointCohesionTAS);
//临时航迹
YLT_REFL(RdpRecTempTrack, id, temp_track_id,point_id,circle_id,X,P,r,azi,vr,height,SNR,RCS,amplitude, CPI_time,GNSS_time,d,buff_round,temp_track_section_idx,asso_flag,pitch_num);
RDP_RECORD_DEFINE(RdpRecTempTrack);
//可靠航迹
YLT_REFL(RdpRecTrustTrack, id,track_id,point_id,circle_id,Track_Index,Range,Azimuth,Elevation,Velocity,Amplitude,Height,T_track,GNSS_time,Track_Sum,Point_Index,Track_Mode,
Track_Update_Flag,Extrapolate_round,point_flag,approach_flag,manual_delete_flag,manual_tracking_flag,
associate_point_number,primary_target_flag,Target_Type,Track_section_idx,
range_point,azi_point,elev_point,vr_point,prf_point,point_type,snr_point,
amp_asv_point,prn_fftn_point,RCS,pitch_num,X,P,X1,X2,X3,P2,P3,u);
RDP_RECORD_DEFINE(RdpRecTrustTrack);
//关联信息
YLT_REFL(RdpRecAssoInfo, id,point_id,track_id,circle_id,d1,d2,d3,d_min,point_index,track_index,
delta_T,delta_h,R,VT,
tas_flag,thd_d,thd_h,thd_r,
min_d_flag);
RDP_RECORD_DEFINE(RdpRecAssoInfo);
// // 示例②:本圈输出的航迹
// YLT_REFL(RdpRecTrackOut, id, track_index, track_mode, point_sum, flag_point, flag_direction,
// gnss_time_ms, track_time_s, x, y, z, v_x, v_y, v_z, range_m, azi_deg, elev_deg,
// range_v_mps, snr, rcs);
// RDP_RECORD_DEFINE(RdpRecTrackOut);
#endif // RDP_RECORD_ENABLE
+56
View File
@@ -0,0 +1,56 @@
#ifndef RDP_RECORD_H
#define RDP_RECORD_H
#pragma once
/***********************************数据记录调试模块********************************************/
// 功能(完整说明见 docs/data_record_debug.md):
// 1) 把结构体数据按「一个记录实体一张表」的方式写入 SQLite 数据库,结构体到数据表的映射由
// 内置的 ormpp 完成(third_party/ormpp,见该目录 UPSTREAM.md);
// 2) 整个功能由构建开关控制:CMake 选项 RDP_DATA_RECORD(AUTO/ON/OFF)开启时由构建系统定义
// RDP_RECORD_ENABLE,rdp_record.cpp 与 sqlite3 amalgamation 才参与编译;关闭(交付/Release
// 构建默认)时本模块完全不编译,RDP_RECORD / RDP_RECORD_BATCH 展开为 ((void)0)、实参不求值,
// 不留任何代码与运行开销;
// 3) 生命周期:Data_Process_Factory::GetB() 调用 rdp_record::Init()(建目录、开库),
// Data_Process_Factory::Destroy() 调用 rdp_record::Shutdown()(关库);Init 之前、初始化
// 失败之后或 Shutdown 之后的写入一律静默忽略,不影响雷达数据处理;
// 4) 记录点用法(新增数据 = 加结构体 + 注册 + 一行记录点,配方见 docs/data_record_debug.md):
// 单条:RDP_RECORD(RdpRecPointTws{0, 值1, 值2, ...});
// 批量:RDP_RECORD_BATCH(recs); // recs 为 std::vector<某记录实体>
// 需要语句/局部变量的记录点请整体用 #ifdef RDP_RECORD_ENABLE ... #endif 包裹,
// 保证关闭记录功能时既无代码也无「变量未使用」告警;
// 5) 记录实体类型必须先在 rdp_record_entity.h 中声明并注册,否则链接期报 LNK2019
// (extern template 已阻止 C++17 调用方隐式实例化)。
#include <string>
#include <vector>
namespace rdp_record
{
bool Init(); //初始化记录库(由 Data_Process_Factory::GetB() 调用)
void Shutdown(); //关闭记录库(由 Data_Process_Factory::Destroy() 调用)
std::string db_path(); //当前记录库文件全路径(未启用或未成功打开时返回空串)
//写入单条记录实体(T 为记录实体类型,须在 rdp_record.cpp 中显式实例化)
template <typename T> void Write(const T& v);
//批量写入同一记录实体的多条记录(内部使用事务)
template <typename T> void Write(const std::vector<T>& v);
}
#ifdef RDP_RECORD_ENABLE
//记录宏:把指定记录实体写入数据库(首次写入该实体时自动建表)
#define RDP_RECORD(...) ::rdp_record::Write(__VA_ARGS__)
//批量记录宏:把 std::vector<记录实体> 一次性写入(每批一个事务)
#define RDP_RECORD_BATCH(...) ::rdp_record::Write(__VA_ARGS__)
#else
//记录功能关闭:记录模块不参与编译,宏展开为空(实参不求值)
#define RDP_RECORD(...) ((void)0)
#define RDP_RECORD_BATCH(...) ((void)0)
namespace rdp_record
{
inline bool Init() { return false; }
inline void Shutdown() {}
inline std::string db_path() { return std::string(); }
template <typename T> inline void Write(const T&) {}
template <typename T> inline void Write(const std::vector<T>&) {}
}
#endif
#endif // RDP_RECORD_H
+486
View File
@@ -0,0 +1,486 @@
#ifndef RDP_RECORD_ENTITY_H
#define RDP_RECORD_ENTITY_H
#pragma once
/*******************************数据记录实体(=数据库表)定义区*********************************/
// 使用说明(完整配方见 docs/data_record_debug.md):
// 1) 新增一张记录表:在本文件「记录实体定义区」添加结构体,并在其后紧跟一行
// RDP_RECORD_DECLARE(结构体名);
// 再到 rdp_record.cpp 的「记录实体注册区」添加两行
// YLT_REFL(结构体名, 字段1, 字段2, ...); // 顺序必须与结构体声明顺序一致
// RDP_RECORD_DEFINE(结构体名);
// 最后在需要记录的位置写 RDP_RECORD(结构体名{0, 值1, 值2, ...}); 或 RDP_RECORD_BATCH(向量);
// 建表无需任何代码:首次写入该实体时自动 CREATE TABLE IF NOT EXISTS;
// 2) 命名与建表约定:结构体定义在全局命名空间(勿放入 namespace),结构体名即数据库表名
// (ormpp 取「短结构体名」),建议以 RdpRec 开头;首个字段固定为 int64_t id(自增主键,
// 写入时固定填 0,由 SQLite 分配);
// 3) 字段类型仅限标量:int / int64_t / float / double / bool(SQLite 侧映射 INTEGER / INTEGER /
// FLOAT / DOUBLE / INTEGER)。禁止使用数组(含 float speed_dim[129]、char[N] 等)——ormpp 对
// C 数组按 C 字符串语义(strlen)绑定,会读越界或写入错误数据;大批量数组请另建「元素展开表」
// 或改用 blob 字段;
// 4) 表结构变更(增删字段)不会迁移已存在的旧表:本模块每次运行生成新的 db 文件,天然规避。
#include "rdp_record.h"
#include <cstdint>
#include <vector>
#include <iostream>
#include <sstream>
#include <string>
#include <type_traits>
#include <cstddef>
#include "struct.h"
//记录实体的写入函数实例化声明宏:声明「该实体的 Write 在 rdp_record.cpp 中显式实例化」,
//阻止 C++17 调用方隐式实例化(本模块的 ormpp 实现以 C++20 编译,见 docs/data_record_debug.md)
#define RDP_RECORD_DECLARE(Entity) \
extern template void rdp_record::Write<Entity>(const Entity&); \
extern template void rdp_record::Write<Entity>(const std::vector<Entity>&)
// 基本类型元素转字符串
template <typename T>
std::string elementToString(const T& value);
// 数组版本
template <typename T, std::size_t N>
std::string arrayToString(const T (&arr)[N]);
// 非数组元素:直接转字符串
template <typename T>
std::string arrayToString(const T& value);
// ============================ 记录实体定义区(在此新增结构体) ============================
// 输入点迹 —— 输入的原始点迹数据
struct RdpRecPoint
{
int64_t id; //自增主键(写入时固定填 0)
long long point_id; //点迹ID
long long frame_id; //帧ID
long long circle_id; //圈ID
double range; //径向距离
double azimuth; //方位 (弧度)
double velocity ; //速度
double amplitude ; //幅度
double height; //高度
double SNR; //信噪比
double RCS; //目标RCS
float encoder_value; //码盘值
float amp_asv_point; //归一化幅度dB
float prn_fftn_point; //点迹速度维FFT点数
long long CPI_time; //CPI时间
long long GNSS_time; //GNSS时间
int use_flag; //点迹使用标志 1使用 0未使用
int azimuth_beam_index; //方位波位号
int pitch_beam_index; //俯仰波位号
int PRF_index; //PRF号
int freq_index; //频点
int point_type; //TAS 1 TWS0
int point_section_asso; //点迹区
RdpRecPoint(){
id = 0;
}
RdpRecPoint(PointRecv& p){
id = 0;
point_id = p.point_id;
frame_id = p.frame_id;
circle_id = p.circle_id;
range = p.range;
azimuth = p.azimuth;
velocity = p.velocity;
amplitude = p.amplitude;
height = p.height;
SNR = p.SNR;
RCS = p.RCS;
encoder_value = p.encoder_value;
amp_asv_point = p.amp_asv_point;
prn_fftn_point = p.prn_fftn_point;
CPI_time = p.CPI_time;
GNSS_time = p.GNSS_time;
use_flag = p.use_flag;
azimuth_beam_index = p.azimuth_beam_index;
pitch_beam_index = p.pitch_beam_index;
PRF_index = p.PRF_index;
freq_index = p.freq_index;
point_type = p.point_type;
point_section_asso = p.point_section_asso;
}
};
RDP_RECORD_DECLARE(RdpRecPoint);
// TWS凝聚后点迹 —— 凝聚后的点迹数据
struct RdpRecPointCohesionTWS
{
int64_t id; //自增主键(写入时固定填 0)
long long point_id; //点迹ID
long long frame_id; //帧ID
long long circle_id; //圈ID
double range; //径向距离
double azimuth; //方位 (弧度)
double velocity ; //速度
double amplitude ; //幅度
double height; //高度
double SNR; //信噪比
double RCS; //目标RCS
float encoder_value; //码盘值
float amp_asv_point; //归一化幅度dB
float prn_fftn_point; //点迹速度维FFT点数
long long CPI_time; //CPI时间
long long GNSS_time; //GNSS时间
int use_flag; //点迹使用标志 1使用 0未使用
int azimuth_beam_index; //方位波位号
int pitch_beam_index; //俯仰波位号
int PRF_index; //PRF号
int freq_index; //频点
int point_type; //TAS 1 TWS0
int point_section_asso; //点迹区
RdpRecPointCohesionTWS(){
id = 0;
}
RdpRecPointCohesionTWS(PointRecv& p){
id = 0;
point_id = p.point_id;
frame_id = p.frame_id;
circle_id = p.circle_id;
range = p.range;
azimuth = p.azimuth;
velocity = p.velocity;
amplitude = p.amplitude;
height = p.height;
SNR = p.SNR;
RCS = p.RCS;
encoder_value = p.encoder_value;
amp_asv_point = p.amp_asv_point;
prn_fftn_point = p.prn_fftn_point;
CPI_time = p.CPI_time;
GNSS_time = p.GNSS_time;
use_flag = p.use_flag;
azimuth_beam_index = p.azimuth_beam_index;
pitch_beam_index = p.pitch_beam_index;
PRF_index = p.PRF_index;
freq_index = p.freq_index;
point_type = p.point_type;
point_section_asso = p.point_section_asso;
}
};
RDP_RECORD_DECLARE(RdpRecPointCohesionTWS);
// TAS凝聚后点迹 —— 凝聚后的点迹数据
struct RdpRecPointCohesionTAS
{
int64_t id; //自增主键(写入时固定填 0)
long long point_id; //点迹ID
long long frame_id; //帧ID
long long circle_id; //圈ID
double range; //径向距离
double azimuth; //方位 (弧度)
double velocity ; //速度
double amplitude ; //幅度
double height; //高度
double SNR; //信噪比
double RCS; //目标RCS
float encoder_value; //码盘值
float amp_asv_point; //归一化幅度dB
float prn_fftn_point; //点迹速度维FFT点数
long long CPI_time; //CPI时间
long long GNSS_time; //GNSS时间
int use_flag; //点迹使用标志 1使用 0未使用
int azimuth_beam_index; //方位波位号
int pitch_beam_index; //俯仰波位号
int PRF_index; //PRF号
int freq_index; //频点
int point_type; //TAS 1 TWS0
int point_section_asso; //点迹区
RdpRecPointCohesionTAS(){
id = 0;
}
RdpRecPointCohesionTAS(PointRecv& p){
id = 0;
point_id = p.point_id;
frame_id = p.frame_id;
circle_id = p.circle_id;
range = p.range;
azimuth = p.azimuth;
velocity = p.velocity;
amplitude = p.amplitude;
height = p.height;
SNR = p.SNR;
RCS = p.RCS;
encoder_value = p.encoder_value;
amp_asv_point = p.amp_asv_point;
prn_fftn_point = p.prn_fftn_point;
CPI_time = p.CPI_time;
GNSS_time = p.GNSS_time;
use_flag = p.use_flag;
azimuth_beam_index = p.azimuth_beam_index;
pitch_beam_index = p.pitch_beam_index;
PRF_index = p.PRF_index;
freq_index = p.freq_index;
point_type = p.point_type;
point_section_asso = p.point_section_asso;
}
};
RDP_RECORD_DECLARE(RdpRecPointCohesionTAS);
// 零时航迹
struct RdpRecTempTrack
{
int64_t id; //自增主键(写入时固定填 0)
long long temp_track_id; //临时航迹ID
long long point_id; //关联上的点迹ID
long long circle_id; //处理圈数ID
std::string X; //状态
std::string P; //协方差
double r; //距离
double azi; //方位
double vr; //径向速度
double height; //高度
double SNR; //信噪比
double RCS; //RCS
double amplitude; //幅度
long long CPI_time; //时间戳
long long GNSS_time; //GNSS时间戳
double d; //关联上的点的d(马氏距离)
int buff_round; //已缓存圈数
int temp_track_section_idx; //临时航迹区号 与点迹区的划分相同
int asso_flag; //关联标志 已关联:1 未关联:0
int pitch_num; // 俯仰波位号
RdpRecTempTrack(){
id = 0;
}
RdpRecTempTrack(Temp_track& p){
id = 0;
temp_track_id = p.temp_track_id;
point_id = p.point_id;
circle_id = p.circle_id;
X = arrayToString(p.X);
P = arrayToString(p.P);
r = p.r;
azi = p.azi;
vr = p.vr;
height = p.height;
RCS = p.RCS;
SNR = p.SNR;
amplitude = p.amplitude;
CPI_time = p.CPI_time;
GNSS_time = p.GNSS_time;
d = p.d;
buff_round = p.buff_round;
temp_track_section_idx = p.temp_track_section_idx;
asso_flag = p.asso_flag;
pitch_num = p.pitch_num;
}
};
RDP_RECORD_DECLARE(RdpRecTempTrack);
// 可靠航迹
struct RdpRecTrustTrack
{
int64_t id; //自增主键(写入时固定填 0)
long long track_id; //航迹ID
long long point_id; //关联上的点迹ID
long long circle_id; //处理圈数ID
int Track_Index; //航迹号
double Range; //距离
double Azimuth; //方位
double Elevation; //俯仰
double Velocity; //航向速度
double Amplitude; //幅度
double Height; //高度
long long T_track; //航迹时间
long long GNSS_time; //GNSS时间
int Track_Sum; //航迹总数
int Point_Index; //该航迹上的第几个点
int Track_Mode; //跟踪模式
int Track_Update_Flag; //航迹更新标志 1已更新/0未更新
int Extrapolate_round; //航迹持续外推时间
int point_flag; //是否实点 1实点 0外推
int approach_flag; //接近标志 1接近 0远离
int manual_delete_flag; //手动航迹删除
int manual_tracking_flag; //手动TAS
int associate_point_number; //关联的点数
int primary_target_flag; //重点目标标记
int Target_Type; //目标类型 搜索目标 低空目标 地面目标
int Track_section_idx; //航迹区号
double range_point; //关联上的点信息(距离、方位、俯仰)
double azi_point;
double elev_point;
double vr_point;
double prf_point;
int point_type; //TWS TAS
double snr_point; //信噪比
float amp_asv_point; //关联上的点迹信息(归一化幅度)
float prn_fftn_point; //点迹速度维FFT点数
double RCS;
int pitch_num; // 俯仰波位号
std::string X; //IMM
std::string P;
std::string X1;
std::string X2;
std::string X3;
std::string P2;
std::string P3;
std::string u;
RdpRecTrustTrack(){
id = 0;
}
RdpRecTrustTrack(Trust_Track& p){
id = 0;
track_id = p.track_id;
point_id = p.point_id;
circle_id = p.circle_id;
Track_Index = p.Track_Index;
Range = p.Range;
Azimuth = p.Azimuth;
Elevation = p.Elevation;
Velocity = p.Velocity;
Amplitude = p.Amplitude;
Height = p.Height;
T_track = p.T_track;
GNSS_time = p.GNSS_time;
Track_Sum = p.Track_Sum;
Point_Index = p.Point_Index;
Track_Mode = p.Track_Mode;
Track_Update_Flag = p.Track_Update_Flag;
Extrapolate_round = p.Extrapolate_round;
point_flag = p.point_flag;
approach_flag = p.approach_flag;
manual_delete_flag = p.manual_delete_flag;
manual_tracking_flag = p.manual_tracking_flag;
associate_point_number = p.associate_point_number;
primary_target_flag = p.primary_target_flag;
Target_Type = p.Target_Type; //目标类型 搜索目标 低空目标 地面目标
Track_section_idx = p.Track_section_idx; //航迹区号
range_point = p.range_point; //关联上的点信息(距离、方位、俯仰)
azi_point = p.azi_point;
elev_point = p.elev_point;
vr_point = p.vr_point;
prf_point = p.prf_point;
point_type = p.point_type; //TWS TAS
snr_point = p.snr_point; //信噪比
amp_asv_point = p.amp_asv_point; //关联上的点迹信息(归一化幅度)
prn_fftn_point = p.prn_fftn_point; //点迹速度维FFT点数
RCS = p.RCS;
pitch_num = p.pitch_num; // 俯仰波位号
X = arrayToString(p.X); //IMM
P = arrayToString(p.P);
X1 = arrayToString(p.X1);
X2 = arrayToString(p.X2);
X3 = arrayToString(p.X3);
P2 = arrayToString(p.P2);
P3 = arrayToString(p.P3);
u = arrayToString(p.u);
}
};
RDP_RECORD_DECLARE(RdpRecTrustTrack);
// 关联信息
struct RdpRecAssoInfo
{
int64_t id; //自增主键(写入时固定填 0)
long long point_id;
long long track_id;
long long circle_id;
double d1;
double d2;
double d3;
double d_min;
int point_index;
int track_index;
double delta_T;
double delta_h;
double R;
double VT;
bool tas_flag; //TAS关联标志 1是 0否
bool thd_d;
bool thd_h;
bool thd_r;
bool min_d_flag;
RdpRecAssoInfo(){
id = 0;
}
RdpRecAssoInfo(Asso_Info& p){
id = 0;
point_id = p.point_id;
track_id = p.track_id;
circle_id = p.circle_id;
d1 = p.d1;
d2 = p.d2;
d3 = p.d3;
d_min = p.d_min;
point_index = p.point_index;
track_index = p.track_index;
delta_T = p.delta_T;
delta_h = p.delta_h;
R = p.R;
VT = p.VT;
tas_flag = p.tas_flag;
thd_d = p.thd_d;
thd_h = p.thd_h;
thd_r = p.thd_r;
min_d_flag = p.min_d_flag;
}
};
RDP_RECORD_DECLARE(RdpRecAssoInfo);
// ============================ 数组转换函数 ============================
// 基本类型元素转字符串
template <typename T>
std::string elementToString(const T& value) {
std::ostringstream oss;
oss << value;
return oss.str();
}
// 前置声明数组版本
template <typename T, std::size_t N>
std::string arrayToString(const T (&arr)[N]);
// 非数组元素:直接转字符串
template <typename T>
std::string arrayToString(const T& value) {
return elementToString(value);
}
// 数组版本
template <typename T, std::size_t N>
std::string arrayToString(const T (&arr)[N]) {
std::string result;
// 如果 T 仍然是数组,说明当前不是最内层,用分号;
// 否则当前就是最内层,用逗号。
const char sep = std::is_array<T>::value ? ';' : ',';
for (std::size_t i = 0; i < N; ++i) {
if (i > 0) {
result += sep;
}
result += arrayToString(arr[i]);
}
return result;
}
#endif // RDP_RECORD_ENTITY_H
+68 -25
View File
@@ -9,27 +9,29 @@ using namespace std;
// 点迹数据结构
struct PointRecv
{
double Range; //径向距离
double Azimuth; //方位 (弧度)
double Velocity ; //速度
double Amplitude ; //幅度
double Height; //高度
double snr; //信噪比
long long point_id; //点迹ID
long long frame_id; //帧ID
long long circle_id; //圈ID
double range; //径向距离
double azimuth; //方位 (弧度)
double velocity ; //速度
double amplitude ; //幅度
double height; //高度
double SNR; //信噪比
double RCS; //目标RCS
float encoder_value; //码盘值
float amp_asv_point; //归一化幅度dB
float prn_fftn_point; //点迹速度维FFT点数
long long CPI_Time; //CPI时间
long long CPI_time; //CPI时间
long long GNSS_time; //GNSS时间
int Point_index; //点迹号 1~50
int Point_Sum; //点迹总数
int Use_Flag; //点迹使用标志 1使用 0未使用
int beam_index; //波位号
int use_flag; //点迹使用标志 1使用 0未使用
int azimuth_beam_index; //方位波位号
int pitch_beam_index; //俯仰波位号
int PRF_index; //PRF号
int Freq_index; //频点
int track_mode; //TAS 1 TWS0
int freq_index; //频点
int point_type; //TAS 1 TWS0
int point_section_asso; //点迹区
int pitch_num; // 俯仰波位号
float speed_dim[129]; // 目标的十字星数据,目标点速度维129个点
float range_dim[8]; // 目标的十字星数据,目标点距离维8个点
};
@@ -37,16 +39,30 @@ struct PointRecv
// 可靠航迹数据结构
struct Trust_Track
{
long long track_id; //航迹ID
long long point_id; //关联上的点迹ID
long long circle_id; //处理圈数ID
int Track_Index; //航迹号
double Range; //距离
double Azimuth; //方位
double Elevation; //俯仰
double Velocity; //航向速度
double Amplitude; //幅度
double Height; //高度
double RCS; //RCS
double Quality; //航迹质量
long long T_track; //航迹时间
long long T_track; //航迹时间(CPI时间)
long long GNSS_time; //GNSS时间
long long Unupdated_time; //航迹未更新时间
int Track_Sum; //航迹总数
int Point_Index; //该航迹上的第几个点
int Point_sum; //本条航迹更新点的数目
int Valid_point_sum; //本条航迹有效点的数目
int associate_point_number; //关联的点数
int Track_Mode; //跟踪模式
int Track_Update_Flag; //航迹更新标志 1已更新/0未更新
@@ -57,8 +73,6 @@ struct Trust_Track
int manual_delete_flag; //手动航迹删除
int manual_tracking_flag; //手动TAS
int associate_point_number; //关联的点数
int primary_target_flag; //重点目标标记
int Target_Type; //目标类型 搜索目标 低空目标 地面目标
@@ -77,8 +91,6 @@ struct Trust_Track
float amp_asv_point; //关联上的点迹信息(归一化幅度)
float prn_fftn_point; //点迹速度维FFT点数
double RCS;
int pitch_num; // 俯仰波位号
float speed_dim[129]; // 目标的十字星数据,目标点速度维129个点
float range_dim[8]; // 目标的十字星数据,目标点距离维8个点
@@ -93,12 +105,14 @@ struct Trust_Track
double P2[6][6];
double P3[6][6];
double u[3];
};
//临时航迹结构体
struct Temp_track
{
long long temp_track_id; //临时航迹ID
long long point_id; //关联上的点迹ID
long long circle_id; //处理圈数ID
double X[4]; //状态
double P[4][4]; //协方差
@@ -107,17 +121,18 @@ struct Temp_track
double azi; //方位
double vr; //径向速度
double height; //高度
double snr; //信噪比
double SNR; //信噪比
double RCS; //RCS
double Amp; //幅度
long long T; //时间戳
double amplitude; //幅度
double quality; //航迹质量
long long CPI_time; //时间戳
long long GNSS_time; //GNSS时间戳
double d; //关联上的点的d
double d; //关联上的点的d(马氏距离)
int buff_round; //已缓存圈数
int Temp_track_section_idx; //临时航迹区号 与点迹区的划分相同
int temp_track_section_idx; //临时航迹区号 与点迹区的划分相同
int asso_flag; //关联标志 已关联:1 未关联:0
@@ -126,6 +141,34 @@ struct Temp_track
float range_dim[8]; // 目标的十字星数据,目标点距离维8个点
};
//存储关联信息
struct Asso_Info
{
long long point_id;
long long track_id;
long long circle_id;
double d1;
double d2;
double d3;
double d_min;
int point_index;
int track_index;
double delta_T;
double delta_h;
double R;
double VT;
bool tas_flag; //TAS关联标志 1是 0否
bool thd_d;
bool thd_h;
bool thd_r;
bool min_d_flag;
};
// TAS跟踪目标结构体
struct Tracking_Target
{
+1 -1
View File
@@ -291,7 +291,7 @@ void TAS_Ctrl::tas_beam_output(std::vector<Trust_Track> *trust_track
H_track = (*trust_track)[track_idx].Height;
delta_T = (latest_timestamp - CPI_time) / 1000.0f;
earliest_track_idx = track_idx;
earliest_track_idx = static_cast<int>(track_idx);
earliest_tas_queue_idx = tas_idx;
for(int ii=0;ii<6;ii++){
+104 -62
View File
@@ -6,6 +6,10 @@
#include <cstring>
#include <vector>
#include <Eigen/Dense>
#include "rdp_record.h"
#include "rdp_record_entity.h"
using namespace Eigen;
using namespace std;
@@ -16,6 +20,7 @@ int Track_Asso:: track_asso_process(std::vector<PointRecv> *point_
std::vector <Trust_Track> *trust_track, //航迹文件
struct Track Trust_Track_Output[MAX_TRACK_NUM][10], //更新航迹信息
int *Trust_track_num_Output, //更新航迹数
long long latest_timestamp, //最新时间戳
struct RadarPara Work_Parameter //雷达参数
)
{
@@ -24,14 +29,14 @@ int Track_Asso:: track_asso_process(std::vector<PointRecv> *point_
for (size_t i=0;i< point_recv->size();i++)
{
point_process.push_back((*point_recv)[i]);
int n=point_process.size();
int n=static_cast<int>(point_process.size());
point_process[n-1].point_section_asso = 1;
}
//IMM算法
model_interaction(trust_track);
model_filter(trust_track, Work_Parameter);
model_filter(trust_track, Work_Parameter, latest_timestamp);
model_output(trust_track);
@@ -39,7 +44,7 @@ int Track_Asso:: track_asso_process(std::vector<PointRecv> *point_
std::vector <PointRecv>::iterator Iter;
for (Iter= point_process.begin(); Iter!=point_process.end();)
{
if((*Iter).Use_Flag==1 )
if((*Iter).use_flag==1 )
{
point_process.erase(Iter);
Iter=point_process.begin();
@@ -95,12 +100,18 @@ int Track_Asso:: track_asso_process(std::vector<PointRecv> *point_
Trust_Track_Output[*Trust_track_num_Output-1][0].GNSS_time=(*trust_track)[i].GNSS_time;
Trust_Track_Output[*Trust_track_num_Output-1][0].Flag_Point = (*trust_track)[i].point_flag;
Trust_Track_Output[*Trust_track_num_Output-1][0].z=static_cast<float>((*trust_track)[i].Height+Work_Parameter.Height);
Trust_Track_Output[*Trust_track_num_Output-1][0].Quality = static_cast<float>((*trust_track)[i].Quality * TRACK_QUALITY_MAX);
double Direction_Angle;
Direction_Angle=atan2((*trust_track)[i].X[4], (*trust_track)[i].X[1]);
if(Direction_Angle<0){Direction_Angle=Direction_Angle+2*PI;}
Trust_Track_Output[*Trust_track_num_Output-1][0].Direction_Angle=static_cast<float>(Direction_Angle/PI*180);
(*trust_track)[i].Range = Trust_Track_Output[*Trust_track_num_Output-1][0].Range;
(*trust_track)[i].Azimuth = Trust_Track_Output[*Trust_track_num_Output-1][0].Azimuth;
(*trust_track)[i].Elevation = Trust_Track_Output[*Trust_track_num_Output-1][0].Elevation;
(*trust_track)[i].Velocity = Trust_Track_Output[*Trust_track_num_Output-1][0].Range_V;
//关联点信息
Trust_Track_Output[*Trust_track_num_Output-1][0].range_point=static_cast<float>((*trust_track)[i].range_point);
Trust_Track_Output[*Trust_track_num_Output-1][0].azi_point=static_cast<float>((*trust_track)[i].azi_point);
@@ -143,12 +154,13 @@ int Track_Asso:: track_asso_process(std::vector<PointRecv> *point_
Trust_Track_Output[*Trust_track_num_Output-1][0].Flag_Direction=0;
}
Trust_Track_Output[*Trust_track_num_Output-1][0].track_rcs = static_cast<float>((*trust_track)[i].RCS);
Trust_Track_Output[*Trust_track_num_Output-1][0].pitch_num = (*trust_track)[i].pitch_num;
std::memcpy(Trust_Track_Output[*Trust_track_num_Output-1][0].speed_dim, (*trust_track)[i].speed_dim, sizeof(Trust_Track_Output[*Trust_track_num_Output-1][0].speed_dim));
std::memcpy(Trust_Track_Output[*Trust_track_num_Output-1][0].range_dim, (*trust_track)[i].range_dim, sizeof(Trust_Track_Output[*Trust_track_num_Output-1][0].range_dim));
RDP_RECORD(RdpRecTrustTrack((*trust_track)[i]));
}
}
@@ -419,20 +431,11 @@ void Track_Asso::IMM_d_cal(double v_track,
}
void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct RadarPara Work_Parameter)
void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct RadarPara Work_Parameter, long long latest_timestamp)
{
//存储关联信息
struct asso_info
{
double d1;
double d2;
double d3;
double d_min;
int point_index;
int track_index;
};
std::vector <asso_info> associated_info;
std::vector <Asso_Info> associated_info;
//遍历所有点迹航迹 计算点迹和航迹的统计距离
for (size_t loop_of_track = 0; loop_of_track<trust_track->size();loop_of_track++)
@@ -443,7 +446,7 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
{
//时间差
double delta_T = (point_process[loop_of_point].CPI_Time - (*trust_track)[loop_of_track].T_track)/1000.0;
double delta_T = (point_process[loop_of_point].CPI_time - (*trust_track)[loop_of_track].T_track)/1000.0;
//航迹X P
double X1[6],X2[6],X3[6],P1[6][6],P2[6][6],P3[6][6];
memcpy(X1,(*trust_track)[loop_of_track].X1,6*sizeof(double));
@@ -457,12 +460,12 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
double r_track = sqrt(pow((*trust_track)[loop_of_track].X[0],2)+pow((*trust_track)[loop_of_track].X[3],2));
//量测信息
double Z[3] = {point_process[loop_of_point].Range,
point_process[loop_of_point].Azimuth,
point_process[loop_of_point].Velocity};
double Z[3] = {point_process[loop_of_point].range,
point_process[loop_of_point].azimuth,
point_process[loop_of_point].velocity};
double prt = point_process[loop_of_point].PRF_index;
double freq_ind = point_process[loop_of_point].Freq_index;
double h_point = point_process[loop_of_point].Height;
double freq_ind = point_process[loop_of_point].freq_index;
double h_point = point_process[loop_of_point].height;
//计算统计距离d
double d1,d2,d3;
@@ -498,8 +501,8 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
double& t_x0 = (*trust_track)[loop_of_track].X[0];
double& t_y0 = (*trust_track)[loop_of_track].X[3];
double a_track = atan2(t_y0, t_x0); //航迹方位角
double& r_point = point_process[loop_of_point].Range; //点迹距离
double a_delta = abs(a_track - point_process[loop_of_point].Azimuth); //方位差
double& r_point = point_process[loop_of_point].range; //点迹距离
double a_delta = abs(a_track - point_process[loop_of_point].azimuth); //方位差
if(a_delta > PI){
a_delta = 2*PI - a_delta;
}
@@ -518,12 +521,7 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
thd_r = false;
}
//小于关联门限 保存关联信息
if(thd_d && thd_h && thd_r)
{
//std::cout << "asooooooooooo";
struct asso_info associated_info_tmp;
struct Asso_Info associated_info_tmp;
associated_info_tmp.d1=d1;
associated_info_tmp.d2=d2;
associated_info_tmp.d3=d3;
@@ -535,15 +533,31 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
associated_info_tmp.d_min=d2;
else
associated_info_tmp.d_min=d3;
associated_info_tmp.point_id=point_process[loop_of_point].point_id;
associated_info_tmp.track_id=(*trust_track)[loop_of_track].track_id;
associated_info_tmp.circle_id=point_process[loop_of_point].circle_id;
associated_info_tmp.delta_T=delta_T;
associated_info_tmp.delta_h=delta_h;
associated_info_tmp.R=R;
associated_info_tmp.VT=VT;
associated_info_tmp.tas_flag=point_process[loop_of_point].point_type;
associated_info_tmp.thd_d=thd_d;
associated_info_tmp.thd_h=thd_h;
associated_info_tmp.thd_r=thd_r;
associated_info_tmp.min_d_flag=false;
//小于关联门限 保存关联信息
if(thd_d && thd_h && thd_r)
{
associated_info.push_back(associated_info_tmp);
}else{
RDP_RECORD(RdpRecAssoInfo(associated_info_tmp));
}
}
// }
}
//最近邻法关联
int associated_num=associated_info.size();
int associated_num=static_cast<int>(associated_info.size());
for (size_t i=0;i<trust_track->size();i++)
{
//寻找最小d
@@ -567,9 +581,9 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
double d3=associated_info[min_index-1].d3;
//卡尔曼滤波
double Z[3]={point_process[point_index-1].Range,point_process[point_index-1].Azimuth,point_process[point_index-1].Velocity};
double Z[3]={point_process[point_index-1].range,point_process[point_index-1].azimuth,point_process[point_index-1].velocity};
double prt = point_process[point_index-1].PRF_index;
double freq_ind = point_process[point_index-1].Freq_index;
double freq_ind = point_process[point_index-1].freq_index;
double X1[6],X2[6],X3[6],P1[6][6],P2[6][6],P3[6][6];
memcpy(X1,(*trust_track)[track_index-1].X1,6*sizeof(double));
memcpy(X2,(*trust_track)[track_index-1].X2,6*sizeof(double));
@@ -578,15 +592,14 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
memcpy(P2,(*trust_track)[track_index-1].P2,6*6*sizeof(double));
memcpy(P3,(*trust_track)[track_index-1].P3,6*6*sizeof(double));
double v_track = sqrt(pow((*trust_track)[track_index-1].X[1],2)+pow((*trust_track)[track_index-1].X[4],2));
double delta_T = (point_process[point_index-1].CPI_Time - (*trust_track)[track_index-1].T_track)/1000.0;
double delta_T = (point_process[point_index-1].CPI_time - (*trust_track)[track_index-1].T_track)/1000.0;
double F[6][6], Q1[6][6], Q2[6][6], Q3[6][6];
IMM_F_Q_gen( v_track, delta_T,F, Q1, Q2, Q3,Work_Parameter);
if (delta_T <= 0)
if (delta_T > 0)
{
// RDP_LOG<<"delta_T <= 0, track_index: "<<track_index<<", point_index: "<<point_index<<", delta_T: "<<delta_T<<std::endl;
// 乱序/重复时间戳:不作为有效关联,不标记点迹已使用、不刷新航迹新鲜度
}else{
associated_info[min_index-1].min_d_flag=true;
double X1_filter[6],X2_filter[6],X3_filter[6],P1_filter[6][6],P2_filter[6][6],P3_filter[6][6];
double S1[3][3] = {{0}},S2[3][3] = {{0}},S3[3][3] = {{0}};
kalman Kalman;
@@ -644,25 +657,36 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
(*trust_track)[track_index-1].P3[ii][jj]=P3_filter[ii][jj];
}
//计算航迹质量
double d_tmp = exp(-associated_info[min_index-1].d_min);
double snr_tmp = 1/(1+exp(-(point_process[point_index-1].SNR - 16) / 1.2));
double len_tmp = 1/(1+exp(-((*trust_track)[track_index-1].Valid_point_sum - 4) / 1.2)) * 0.5 + (1 - (*trust_track)[track_index-1].Valid_point_sum / (*trust_track)[track_index-1].Point_sum) * 0.5;
double tq_tmp = TRACK_QUALITY_D_WEIGHT * d_tmp + TRACK_QUALITY_SNR_WEIGHT * snr_tmp + TRACK_QUALITY_LEN_WEIGHT * len_tmp;
(*trust_track)[track_index-1].Quality = (*trust_track)[track_index-1].Quality * TRACK_QUALITY_ITER_COEFF + tq_tmp * (1 - TRACK_QUALITY_ITER_COEFF);
//更新航迹时间
(*trust_track)[track_index-1].T_track = point_process[point_index-1].CPI_Time;
(*trust_track)[track_index-1].T_track = point_process[point_index-1].CPI_time;
(*trust_track)[track_index-1].GNSS_time = point_process[point_index-1].GNSS_time;
(*trust_track)[track_index-1].Unupdated_time = 0;
//更新关联上的点迹信息
(*trust_track)[track_index-1].range_point=point_process[point_index-1].Range;
(*trust_track)[track_index-1].azi_point=point_process[point_index-1].Azimuth/PI*180;
(*trust_track)[track_index-1].elev_point= asin(point_process[point_index-1].Height/point_process[point_index-1].Range)/PI*180;
(*trust_track)[track_index-1].vr_point=point_process[point_index-1].Velocity;
(*trust_track)[track_index-1].point_id = point_process[point_index-1].point_id;
(*trust_track)[track_index-1].circle_id = point_process[point_index-1].circle_id;
(*trust_track)[track_index-1].range_point=point_process[point_index-1].range;
(*trust_track)[track_index-1].azi_point=point_process[point_index-1].azimuth/PI*180;
(*trust_track)[track_index-1].elev_point= asin(point_process[point_index-1].height/point_process[point_index-1].range)/PI*180;
(*trust_track)[track_index-1].vr_point=point_process[point_index-1].velocity;
(*trust_track)[track_index-1].point_type = 0;
(*trust_track)[track_index-1].prf_point = point_process[point_index-1].PRF_index;
(*trust_track)[track_index-1].snr_point = point_process[point_index-1].snr;
(*trust_track)[track_index-1].Amplitude= point_process[point_index-1].Amplitude; //幅度
(*trust_track)[track_index-1].snr_point = point_process[point_index-1].SNR;
(*trust_track)[track_index-1].Amplitude= point_process[point_index-1].amplitude; //幅度
(*trust_track)[track_index-1].RCS = point_process[point_index-1].RCS;
(*trust_track)[track_index-1].amp_asv_point = point_process[point_index-1].amp_asv_point;
(*trust_track)[track_index-1].prn_fftn_point = point_process[point_index-1].prn_fftn_point;
(*trust_track)[track_index-1].pitch_num = point_process[point_index-1].pitch_num;
(*trust_track)[track_index-1].pitch_num = point_process[point_index-1].pitch_beam_index;
std::memcpy((*trust_track)[track_index-1].speed_dim, point_process[point_index-1].speed_dim, sizeof((*trust_track)[track_index-1].speed_dim));
std::memcpy((*trust_track)[track_index-1].range_dim, point_process[point_index-1].range_dim, sizeof((*trust_track)[track_index-1].range_dim));
@@ -674,6 +698,8 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
(*trust_track)[track_index-1].point_flag=1; //实点
(*trust_track)[track_index-1].associate_point_number = (*trust_track)[track_index-1].associate_point_number+1; //关联点数+1
(*trust_track)[track_index-1].Track_Update_Flag = 1; //航迹更新标志
(*trust_track)[track_index-1].Point_sum = (*trust_track)[track_index-1].Point_sum + 1;
(*trust_track)[track_index-1].Valid_point_sum = (*trust_track)[track_index-1].Valid_point_sum + 1;
}
//删除associated_info中关联上的航迹 点迹信息
@@ -681,27 +707,29 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
{
if(associated_info[ii].track_index==track_index)
{
RDP_RECORD(RdpRecAssoInfo(associated_info[ii]));
associated_info.erase(associated_info.begin()+ii);
associated_num=associated_info.size();
associated_num=static_cast<int>(associated_info.size());
}
else
ii++;
}
associated_num=associated_info.size();
associated_num=static_cast<int>(associated_info.size());
for (int ii=0;ii<associated_num;)
{
if(associated_info[ii].point_index==point_index)
{
RDP_RECORD(RdpRecAssoInfo(associated_info[ii]));
associated_info.erase(associated_info.begin()+ii);
associated_num=associated_info.size();
associated_num=static_cast<int>(associated_info.size());
}
else
ii++;
}
associated_num=associated_info.size();
associated_num=static_cast<int>(associated_info.size());
//在接收点迹中 将关联上的点迹的使用标记位置为1 等待删除
point_process[point_index-1].Use_Flag = 1;
point_process[point_index-1].use_flag = 1;
}
}
@@ -710,11 +738,11 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
{
if((*trust_track)[i].point_flag == 0 && (*trust_track)[i].manual_tracking_flag == 0) //未关联上点迹的航迹进行外推
{
//外推点数未到上限,则继续外推,否则不外推
if((*trust_track)[i].Extrapolate_round <= MAX_EXTRAPOLATE_NUM)
{
double v_track = sqrt(pow((*trust_track)[i].X[1],2)+pow((*trust_track)[i].X[4],2));
double delta_T;
double delta_T = 0;
// double delta_T = (latest_timestamp - (*trust_track)[i].T_track)/1000.0;
// if (delta_T <= 0)
// continue;
if(Work_Parameter.work_mode == 0)
{
delta_T = Work_Parameter.DATA_RATE_SHORT;
@@ -732,6 +760,11 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
delta_T = Work_Parameter.DATA_RATE_SHORT;
}
//外推点数未到上限,则继续外推,否则不外推
if((*trust_track)[i].Extrapolate_round <= MAX_EXTRAPOLATE_NUM)
{
double v_track = sqrt(pow((*trust_track)[i].X[1],2)+pow((*trust_track)[i].X[4],2));
double F[6][6], Q1[6][6], Q2[6][6], Q3[6][6];
IMM_F_Q_gen( v_track, delta_T, F, Q1, Q2, Q3,Work_Parameter);
@@ -745,7 +778,6 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
memcpy(P3,(*trust_track)[i].P3,6*6*sizeof(double));
//外推
double X1_pred[6],X2_pred[6],X3_pred[6],P1_pred[6][6],P2_pred[6][6],P3_pred[6][6];
kalman Kalman;
Kalman.kalman_pred(F, Q1, X1, P1, X1_pred, P1_pred);
@@ -758,6 +790,7 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
memcpy((*trust_track)[i].P2,P2_pred,6*6*sizeof(double));
memcpy((*trust_track)[i].P3,P3_pred,6*6*sizeof(double));
(*trust_track)[i].circle_id++;
(*trust_track)[i].T_track = static_cast<long long>((*trust_track)[i].T_track+delta_T*1000.0); //更新航迹时间
(*trust_track)[i].GNSS_time = static_cast<long long>((*trust_track)[i].GNSS_time+delta_T*1000.0); //更新航迹时间
(*trust_track)[i].Extrapolate_round =(*trust_track)[i].Extrapolate_round+1; //连续未用实点更新时间
@@ -768,9 +801,18 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
(*trust_track)[i].point_flag=0; //虚点
(*trust_track)[i].Track_Update_Flag = 0; //航迹更新标志
}
}
}
//航迹质量递减
double t = (*trust_track)[i].Unupdated_time / 1000.0;
(*trust_track)[i].Quality -= t * t * TRACK_QUALITY_DECREASE_COEFF_O2 + t * TRACK_QUALITY_DECREASE_COEFF_O1;
(*trust_track)[i].Unupdated_time += static_cast<long long>(delta_T*1000.0);
t = (*trust_track)[i].Unupdated_time / 1000.0;
(*trust_track)[i].Quality += t * t * TRACK_QUALITY_DECREASE_COEFF_O2 + t * TRACK_QUALITY_DECREASE_COEFF_O1;
(*trust_track)[i].Quality = (*trust_track)[i].Quality < 0.0 ? 0.0 : (*trust_track)[i].Quality;
(*trust_track)[i].Point_sum = (*trust_track)[i].Point_sum + 1;
}
}
}
void Track_Asso:: model_output(std::vector <Trust_Track> *trust_track)
@@ -882,7 +924,7 @@ void Track_Asso:: track_hight_update(int updata_
if(updata_track_index<=static_cast<int>(trust_track->size()) && asso_point_index<=static_cast<int>(point_process.size()))
{
(*trust_track)[updata_track_index-1].Hight_smooth.push_back(point_process[asso_point_index-1].Height);
(*trust_track)[updata_track_index-1].Hight_smooth.push_back(point_process[asso_point_index-1].height);
int height_win_length;
@@ -919,7 +961,7 @@ void Track_Asso:: track_hight_update(int updata_
else if((*trust_track)[updata_track_index-1].Hight_smooth.size()>=static_cast<size_t>(height_win_length))
{
int N=(*trust_track)[updata_track_index-1].Hight_smooth.size();
int N=static_cast<int>((*trust_track)[updata_track_index-1].Hight_smooth.size());
for (int i=0;i<height_win_length;i++)
{
sum=sum+(*trust_track)[updata_track_index-1].Hight_smooth[N-1-i];
+3 -1
View File
@@ -16,6 +16,7 @@ public:
std::vector <Trust_Track> *trust_track, //航迹文件
struct Track Trust_Track_Output[MAX_TRACK_NUM][10], //更新航迹信息
int *Trust_track_num_Output, //更新航迹数
long long latest_timestamp, //最新时间戳
struct RadarPara Work_Parameter //工作参数
);
@@ -30,7 +31,8 @@ private:
void model_interaction(std::vector <Trust_Track> *trust_track); //模型交互
void model_filter(std::vector <Trust_Track> *trust_track, //滤波
struct RadarPara Work_Parameter
struct RadarPara Work_Parameter,
long long latest_timestamp
);
void model_output(std::vector <Trust_Track> *trust_track ); //模型输出
@@ -223,14 +223,14 @@ void Track_Asso_Direct_Tracking::model_filter(Trust_Track *trust_track,
//量测信息
// double Z[2]={point_process[loop_of_point].Range*cos(point_process[loop_of_point].Azimuth),
// point_process[loop_of_point].Range*sin(point_process[loop_of_point].Azimuth)};
double Z[3] = {point_process[loop_of_point].Range,
point_process[loop_of_point].Azimuth,
point_process[loop_of_point].Velocity};
double Z[3] = {point_process[loop_of_point].range,
point_process[loop_of_point].azimuth,
point_process[loop_of_point].velocity};
// double v_point = point_process[loop_of_point].Velocity;
double prt = point_process[loop_of_point].PRF_index;
double freq_ind = point_process[loop_of_point].Freq_index;
double delta_T = (point_process[loop_of_point].CPI_Time - T_track)/1000.0;
double h_point = point_process[loop_of_point].Height;
double freq_ind = point_process[loop_of_point].freq_index;
double delta_T = (point_process[loop_of_point].CPI_time - T_track)/1000.0;
double h_point = point_process[loop_of_point].height;
//计算统计距离d
double d1,d2,d3;
@@ -329,13 +329,13 @@ void Track_Asso_Direct_Tracking::model_filter(Trust_Track *trust_track,
double d3=associated_info[min_index-1].d3;
//卡尔曼滤波
double Z[3]={point_process[point_index-1].Range,point_process[point_index-1].Azimuth,point_process[point_index-1].Velocity};
double Z[3]={point_process[point_index-1].range,point_process[point_index-1].azimuth,point_process[point_index-1].velocity};
double prt = point_process[point_index-1].PRF_index;
double freq_ind = point_process[point_index-1].Freq_index;
double freq_ind = point_process[point_index-1].freq_index;
// double Z[2];
// Z[0]=point_process[point_index-1].Range*cos(point_process[point_index-1].Azimuth);
// Z[1]=point_process[point_index-1].Range*sin(point_process[point_index-1].Azimuth);
double delta_T = (point_process[point_index-1].CPI_Time - T_track)/1000.0;
double delta_T = (point_process[point_index-1].CPI_time - T_track)/1000.0;
double F[6][6], Q1[6][6], Q2[6][6], Q3[6][6];
IMM_F_Q_gen( v_track, delta_T,F, Q1, Q2, Q3,Work_Parameter);
@@ -401,19 +401,19 @@ void Track_Asso_Direct_Tracking::model_filter(Trust_Track *trust_track,
}
//更新航迹时间
(*trust_track).T_track = point_process[point_index-1].CPI_Time;
(*trust_track).T_track = point_process[point_index-1].CPI_time;
//更新关联上的点迹信息
(*trust_track).range_point=point_process[point_index-1].Range;
(*trust_track).azi_point=point_process[point_index-1].Azimuth/PI*180;
(*trust_track).elev_point= asin(point_process[point_index-1].Height/point_process[point_index-1].Range)/PI*180;
(*trust_track).vr_point=point_process[point_index-1].Velocity;
(*trust_track).range_point=point_process[point_index-1].range;
(*trust_track).azi_point=point_process[point_index-1].azimuth/PI*180;
(*trust_track).elev_point= asin(point_process[point_index-1].height/point_process[point_index-1].range)/PI*180;
(*trust_track).vr_point=point_process[point_index-1].velocity;
(*trust_track).point_type = 1;
(*trust_track).prf_point = point_process[point_index-1].PRF_index;
(*trust_track).snr_point = point_process[point_index-1].snr;
(*trust_track).snr_point = point_process[point_index-1].SNR;
(*trust_track).Extrapolate_round = 0; //连续未用实点更新时间
(*trust_track).point_flag=1; //实点
(*trust_track).Amplitude= point_process[point_index-1].Amplitude; //幅度
(*trust_track).Amplitude= point_process[point_index-1].amplitude; //幅度
(*trust_track).associate_point_number = (*trust_track).associate_point_number+1; //关联点数+1
(*trust_track).RCS = point_process[point_index-1].RCS;
@@ -648,7 +648,7 @@ void Track_Asso_Direct_Tracking::track_hight_update(int asso_point
)
{
(*trust_track).Hight_smooth.push_back(point_process[asso_point_index-1].Height);
(*trust_track).Hight_smooth.push_back(point_process[asso_point_index-1].height);
int height_win_length;
if((*trust_track).range_point <= 1000)
+62 -37
View File
@@ -6,6 +6,10 @@
#include <cstring>
#include <vector>
#include <Eigen/Dense>
#include "rdp_record.h"
#include "rdp_record_entity.h"
using namespace Eigen;
using namespace std;
@@ -109,6 +113,13 @@ long long latest_timestamp //最新时间戳
std::memcpy(Trust_Track_Output[*Trust_track_num_Output-1][0].speed_dim, (*trust_track)[i].speed_dim, sizeof(Trust_Track_Output[*Trust_track_num_Output-1][0].speed_dim));
std::memcpy(Trust_Track_Output[*Trust_track_num_Output-1][0].range_dim, (*trust_track)[i].range_dim, sizeof(Trust_Track_Output[*Trust_track_num_Output-1][0].range_dim));
(*trust_track)[i].Range = Trust_Track_Output[*Trust_track_num_Output-1][0].Range;
(*trust_track)[i].Azimuth = Trust_Track_Output[*Trust_track_num_Output-1][0].Azimuth;
(*trust_track)[i].Elevation = Trust_Track_Output[*Trust_track_num_Output-1][0].Elevation;
(*trust_track)[i].Velocity = Trust_Track_Output[*Trust_track_num_Output-1][0].Range_V;
RDP_RECORD(RdpRecTrustTrack((*trust_track)[i]));
}
}
@@ -240,16 +251,7 @@ void Track_Asso_Tas::model_filter( std::vector<Trust_Track> *t
long long latest_timestamp //最新时间戳
)
{
//存储关联信息
struct asso_info
{
double d1;
double d2;
double d3;
double d_min;
int point_index;
};
std::vector <asso_info> associated_info;
std::vector <Asso_Info> associated_info;
//航迹信息
double X1[6] = {0},X2[6] = {0},X3[6] = {0},P1[6][6] = {{0}},P2[6][6] = {{0}},P3[6][6] = {{0}};
@@ -289,19 +291,19 @@ void Track_Asso_Tas::model_filter( std::vector<Trust_Track> *t
//量测信息
// double Z[2]={point_process[loop_of_point].Range*cos(point_process[loop_of_point].Azimuth),
// point_process[loop_of_point].Range*sin(point_process[loop_of_point].Azimuth)};
double Z[3] = {point_process[loop_of_point].Range,
point_process[loop_of_point].Azimuth,
point_process[loop_of_point].Velocity};
double Z[3] = {point_process[loop_of_point].range,
point_process[loop_of_point].azimuth,
point_process[loop_of_point].velocity};
// double v_point = point_process[loop_of_point].Velocity;
double prt = point_process[loop_of_point].PRF_index;
double freq_ind = point_process[loop_of_point].Freq_index;
double delta_T = (point_process[loop_of_point].CPI_Time - T_track)/1000.0;
double freq_ind = point_process[loop_of_point].freq_index;
double delta_T = (point_process[loop_of_point].CPI_time - T_track)/1000.0;
if (delta_T <= 0){
RDP_LOG << "Warning: delta_T <= 0, skipping point association for point index " << point_process[loop_of_point].CPI_Time << " " << T_track << std::endl;
RDP_LOG << "Warning: delta_T <= 0, skipping point association for point index " << point_process[loop_of_point].CPI_time << " " << T_track << std::endl;
continue;
}
double h_point = point_process[loop_of_point].Height;
double h_point = point_process[loop_of_point].height;
//计算统计距离d
double d1,d2,d3;
@@ -327,24 +329,38 @@ void Track_Asso_Tas::model_filter( std::vector<Trust_Track> *t
thd_h = true;
}
//小于关联门限 保存关联信息
if(thd_d && thd_h)
{
// std::cout << "TAS asoooooo111" ;
struct asso_info associated_info_tmp;
struct Asso_Info associated_info_tmp;
associated_info_tmp.d1=d1;
associated_info_tmp.d2=d2;
associated_info_tmp.d3=d3;
associated_info_tmp.point_index=static_cast<int>(loop_of_point)+1;
associated_info_tmp.track_index=tas_track_idx;
if(d1<=d2&&d1<=d3)
associated_info_tmp.d_min=d1;
else if(d2<=d1&& d2<=d3)
associated_info_tmp.d_min=d2;
else
associated_info_tmp.d_min=d3;
associated_info_tmp.point_id=point_process[loop_of_point].point_id;
associated_info_tmp.track_id=(*trust_track)[tas_track_idx].track_id;
associated_info_tmp.circle_id=point_process[loop_of_point].circle_id;
associated_info_tmp.delta_T=delta_T;
associated_info_tmp.delta_h=delta_h;
associated_info_tmp.R=0;
associated_info_tmp.VT=0;
associated_info_tmp.tas_flag=point_process[loop_of_point].point_type;
associated_info_tmp.thd_d=thd_d;
associated_info_tmp.thd_h=thd_h;
associated_info_tmp.thd_r=true;
associated_info_tmp.min_d_flag=false;
//小于关联门限 保存关联信息
if(thd_d && thd_h)
{
associated_info.push_back(associated_info_tmp);
}else{
RDP_RECORD(RdpRecAssoInfo(associated_info_tmp));
}
}
@@ -370,14 +386,16 @@ void Track_Asso_Tas::model_filter( std::vector<Trust_Track> *t
double d2=associated_info[min_index-1].d2;
double d3=associated_info[min_index-1].d3;
associated_info[min_index-1].min_d_flag=true;
//卡尔曼滤波
double Z[3]={point_process[point_index-1].Range,point_process[point_index-1].Azimuth,point_process[point_index-1].Velocity};
double Z[3]={point_process[point_index-1].range,point_process[point_index-1].azimuth,point_process[point_index-1].velocity};
double prt = point_process[point_index-1].PRF_index;
double freq_ind = point_process[point_index-1].Freq_index;
double freq_ind = point_process[point_index-1].freq_index;
// double Z[2];
// Z[0]=point_process[point_index-1].Range*cos(point_process[point_index-1].Azimuth);
// Z[1]=point_process[point_index-1].Range*sin(point_process[point_index-1].Azimuth);
double delta_T = (point_process[point_index-1].CPI_Time - T_track)/1000.0;
double delta_T = (point_process[point_index-1].CPI_time - T_track)/1000.0;
double F[6][6], Q1[6][6], Q2[6][6], Q3[6][6];
IMM_F_Q_gen( v_track, delta_T,F, Q1, Q2, Q3,Work_Parameter);
@@ -406,7 +424,7 @@ void Track_Asso_Tas::model_filter( std::vector<Trust_Track> *t
double Possibility3=(det_S3>1e-12)?(1.0/sqrt(pow(2*PI,3)*det_S3)*exp(-0.5*d3)):0.0;
//更新航迹
for(size_t i=0;i<trust_track->size();i++)
for(size_t i=0;i<trust_track->size();i++){
if((*trust_track)[i].Track_Index == tas_track_idx)
{
@@ -446,25 +464,27 @@ void Track_Asso_Tas::model_filter( std::vector<Trust_Track> *t
}
//更新航迹时间
(*trust_track)[i].T_track = point_process[point_index-1].CPI_Time;
(*trust_track)[i].T_track = point_process[point_index-1].CPI_time;
(*trust_track)[i].GNSS_time = point_process[point_index-1].GNSS_time;
//更新关联上的点迹信息
(*trust_track)[i].range_point=point_process[point_index-1].Range;
(*trust_track)[i].azi_point=point_process[point_index-1].Azimuth/PI*180;
(*trust_track)[i].elev_point= asin(point_process[point_index-1].Height/point_process[point_index-1].Range)/PI*180;
(*trust_track)[i].vr_point=point_process[point_index-1].Velocity;
(*trust_track)[i].point_id=point_process[point_index-1].point_id;
(*trust_track)[i].circle_id=point_process[point_index-1].circle_id;
(*trust_track)[i].range_point=point_process[point_index-1].range;
(*trust_track)[i].azi_point=point_process[point_index-1].azimuth/PI*180;
(*trust_track)[i].elev_point= asin(point_process[point_index-1].height/point_process[point_index-1].range)/PI*180;
(*trust_track)[i].vr_point=point_process[point_index-1].velocity;
(*trust_track)[i].point_type = 1;
(*trust_track)[i].prf_point = point_process[point_index-1].PRF_index;
(*trust_track)[i].snr_point = point_process[point_index-1].snr;
(*trust_track)[i].snr_point = point_process[point_index-1].SNR;
(*trust_track)[i].Extrapolate_round = 0; //连续未用实点更新时间
(*trust_track)[i].point_flag=1; //实点
(*trust_track)[i].Amplitude= point_process[point_index-1].Amplitude; //幅度
(*trust_track)[i].Amplitude= point_process[point_index-1].amplitude; //幅度
(*trust_track)[i].associate_point_number = (*trust_track)[i].associate_point_number+1; //关联点数+1
(*trust_track)[i].RCS = point_process[point_index-1].RCS;
(*trust_track)[i].Track_Update_Flag = 1; //航迹更新标志
(*trust_track)[i].pitch_num = point_process[point_index-1].pitch_num;
(*trust_track)[i].pitch_num = point_process[point_index-1].pitch_beam_index;
std::memcpy((*trust_track)[i].speed_dim, point_process[point_index-1].speed_dim, sizeof((*trust_track)[i].speed_dim));
std::memcpy((*trust_track)[i].range_dim, point_process[point_index-1].range_dim, sizeof((*trust_track)[i].range_dim));
@@ -472,7 +492,12 @@ void Track_Asso_Tas::model_filter( std::vector<Trust_Track> *t
//更新高度
track_hight_update(tas_track_idx,point_index,trust_track);
}
}
for(auto& info : associated_info)
{
RDP_RECORD(RdpRecAssoInfo(info));
}
}
//未关联上,航迹外推
else
@@ -730,7 +755,7 @@ void Track_Asso_Tas::track_hight_update(int tas_
for(size_t i=0; i<trust_track->size(); i++)
if((*trust_track)[i].Track_Index==tas_track_idx)
{
(*trust_track)[i].Hight_smooth.push_back(point_process[asso_point_index-1].Height);
(*trust_track)[i].Hight_smooth.push_back(point_process[asso_point_index-1].height);
int height_win_length;
if((*trust_track)[i].range_point <= 1000)
@@ -766,7 +791,7 @@ void Track_Asso_Tas::track_hight_update(int tas_
else if((*trust_track)[i].Hight_smooth.size()>=static_cast<size_t>(height_win_length))
{
int N=(*trust_track)[i].Hight_smooth.size();
int N=static_cast<int>((*trust_track)[i].Hight_smooth.size());
for (int ii=0;ii<height_win_length;ii++)
{
sum=sum+(*trust_track)[i].Hight_smooth[N-1-ii];
+4 -1
View File
@@ -16,16 +16,19 @@ void Track_Die::track_die_process( std::vector <Trust_Track> *trust_track,
{
bool die_flag = false;
//手动删除
if((*Iter).manual_delete_flag == 1)
{
die_flag = true;
}
if((*Iter).Track_Mode == 0 && (*Iter).primary_target_flag == 0 && (*Iter).Extrapolate_round > TRACK_DIE_ROUND)
//非重点航迹自动消亡
if((*Iter).Track_Mode == 0 && (*Iter).primary_target_flag == 0 && (*Iter).Quality < TRACK_DIE_QUALITY)
{
die_flag = true;
}
//重点航迹自动消亡
if((*Iter).Track_Mode == 0 && (*Iter).primary_target_flag == 1 && (*Iter).Extrapolate_round > TRACK_DIE_ROUND_PRIMARY)
{
die_flag = true;
+203 -117
View File
@@ -6,6 +6,8 @@
#include <vector>
#include <iostream>
#include<iomanip>
#include "rdp_record.h"
#include "rdp_record_entity.h"
using namespace std;
int Track_Init::track_init_process_logic( std::vector <PointRecv> *point_recv, //输入点迹
@@ -15,27 +17,12 @@ int Track_Init::track_init_process_logic( std::vector <PointRecv>
int *Trust_track_num_Output,
struct RadarPara Work_Parameter
)
{
// for(int i=0;i<(*point_recv).size();i++)
// {
// if((*point_recv)[i].CPI_Time == 30675948 || (*point_recv)[i].CPI_Time == 30682320
// || (*point_recv)[i].CPI_Time == 30682132 || (*point_recv)[i].CPI_Time == 30688692
// || (*point_recv)[i].CPI_Time == 30691692 || (*point_recv)[i].CPI_Time == 30691880)
// {
// std::cout << "azi=" << (*point_recv)[i].Azimuth
// << "dis=" << (*point_recv)[i].Range
// << "h=" << (*point_recv)[i].Height
// << "v=" << (*point_recv)[i].Velocity
// << "wave=" << (*point_recv)[i].beam_index
// << "time=" << (*point_recv)[i].CPI_Time;
// }
// }
//取出待起航的点迹数据
for (size_t i=0;i<(*point_recv).size();i++)
for (size_t i=0;i<(*point_recv).size();i++){
point_process.push_back((*point_recv)[i]);
}
//临时航迹的buff_round+1
for (size_t i=0;i<temp_track->size();i++)
@@ -58,7 +45,7 @@ int Track_Init::track_init_process_logic( std::vector <PointRecv>
std::vector <PointRecv>::iterator Iter;
for (Iter=point_process.begin(); Iter!=point_process.end();)
{
if((*Iter).Use_Flag==1)
if((*Iter).use_flag==1)
{
point_process.erase(Iter);
Iter=point_process.begin();
@@ -78,27 +65,30 @@ int Track_Init::track_init_process_logic( std::vector <PointRecv>
for (unsigned int i=0;i<point_process.size();i++)
{
Temp_track temp_track_tmp = {};
temp_track_tmp.r = point_process[i].Range;
temp_track_tmp.azi = point_process[i].Azimuth;
temp_track_tmp.height = point_process[i].Height;
temp_track_tmp.Amp = point_process[i].Amplitude;
temp_track_tmp.vr = point_process[i].Velocity;
temp_track_tmp.snr = point_process[i].snr;
temp_track_tmp.temp_track_id = temp_track_id++;
temp_track_tmp.point_id = point_process[i].point_id;
temp_track_tmp.circle_id = point_process[i].circle_id;
temp_track_tmp.r = point_process[i].range;
temp_track_tmp.azi = point_process[i].azimuth;
temp_track_tmp.height = point_process[i].height;
temp_track_tmp.amplitude = point_process[i].amplitude;
temp_track_tmp.vr = point_process[i].velocity;
temp_track_tmp.SNR = point_process[i].SNR;
temp_track_tmp.RCS = point_process[i].RCS;
temp_track_tmp.pitch_num = point_process[i].pitch_num;
temp_track_tmp.pitch_num = point_process[i].pitch_beam_index;
std::memcpy(temp_track_tmp.speed_dim, point_process[i].speed_dim, sizeof(temp_track_tmp.speed_dim));
std::memcpy(temp_track_tmp.range_dim, point_process[i].range_dim, sizeof(temp_track_tmp.range_dim));
temp_track_tmp.asso_flag = 0;
temp_track_tmp.X[0]=point_process[i].Range*cos(point_process[i].Azimuth);
temp_track_tmp.X[0]=point_process[i].range*cos(point_process[i].azimuth);
temp_track_tmp.X[1]=0;
temp_track_tmp.X[2]=point_process[i].Range*sin(point_process[i].Azimuth);
temp_track_tmp.X[2]=point_process[i].range*sin(point_process[i].azimuth);
temp_track_tmp.X[3]=0;
temp_track_tmp.T = point_process[i].CPI_Time;
temp_track_tmp.CPI_time = point_process[i].CPI_time;
temp_track_tmp.GNSS_time = point_process[i].GNSS_time;
temp_track_tmp.buff_round = 1;
// temp_track_tmp.Temp_track_section_idx = floor(point_process[i].beam_index/BEAM_NUM_DOT_SECTION)+1;
temp_track->push_back( std::vector <Temp_track> ());
int n=temp_track->size();
int n=static_cast<int>(temp_track->size());
(*temp_track)[n-1].push_back(temp_track_tmp);
// if(point_process[i].CPI_Time == 30675948 || point_process[i].CPI_Time == 30682320)
@@ -117,6 +107,15 @@ int Track_Init::track_init_process_logic( std::vector <PointRecv>
std::vector<PointRecv>().swap(point_process);
std::vector<PointRecv>().swap((*point_recv));
#ifdef RDP_RECORD_ENABLE
//记录临时航迹
for(auto& vec_t : *temp_track){
for(auto& t : vec_t){
RDP_RECORD(RdpRecTempTrack(t));
}
}
#endif
//临时航迹满足起始长度 转为可靠航迹
tmp_track_to_trust_track(trust_track,temp_track,Trust_Track_Output, Trust_track_num_Output,Work_Parameter);
@@ -125,19 +124,6 @@ int Track_Init::track_init_process_logic( std::vector <PointRecv>
//消亡临时航迹
tmp_track_die(temp_track);
// std::cout << "-----------------------------------------------";
// for(int i=0;i<temp_track->size();i++)
// {
// for(int j=0;j<(*temp_track)[i].size();j++)
// {
// std::cout << "temp_track_azi=" << (*temp_track)[i][j].azi
// << "temp_track_dis=" << (*temp_track)[i][j].r
// << "temp_track_h=" << (*temp_track)[i][j].height
// << "temp_track_v=" << (*temp_track)[i][j].vr
// << "temp_track_time=" << (*temp_track)[i][j].T;
// }
// }
return 0;
}
@@ -159,22 +145,22 @@ void Track_Init::point_temp_track_asso(std::vector <std::vector<Temp_track>>
{
for ( size_t j=0;j<(*temp_track).size();j++)
{
int L = (*temp_track)[j].size();
int L = static_cast<int>((*temp_track)[j].size());
if( (*temp_track)[j].size() > 1 && (*temp_track)[j][L-1].buff_round >= 2)
{
//点迹信息
// double Z[2]={ point_process[i].Range*cos(point_process[i].Azimuth),point_process[i].Range*sin(point_process[i].Azimuth)} ;
double Z[3]={point_process[i].Range,
point_process[i].Azimuth,
point_process[i].Velocity};
double vr_point=point_process[i].Velocity;
double r_point=point_process[i].Range;
double Z[3]={point_process[i].range,
point_process[i].azimuth,
point_process[i].velocity};
double vr_point=point_process[i].velocity;
double r_point=point_process[i].range;
double prt = point_process[i].PRF_index;
double freq_ind = point_process[i].Freq_index;
double h_point = point_process[i].Height;
double T_point = static_cast<double>(point_process[i].CPI_Time);
double freq_ind = point_process[i].freq_index;
double h_point = point_process[i].height;
double T_point = static_cast<double>(point_process[i].CPI_time);
//航迹信息
double X[4];
@@ -184,7 +170,7 @@ void Track_Init::point_temp_track_asso(std::vector <std::vector<Temp_track>>
double v_temp_track=(*temp_track)[j][L-1].vr;
double r_temp_track=(*temp_track)[j][L-1].r;
double h_temp_track=(*temp_track)[j][L-1].height;
double T_track_head = static_cast<double>((*temp_track)[j][L-1].T);
double T_track_head = static_cast<double>((*temp_track)[j][L-1].CPI_time);
double delta_T = (T_point - T_track_head)/1000.0;
@@ -204,20 +190,6 @@ void Track_Init::point_temp_track_asso(std::vector <std::vector<Temp_track>>
double y2=Z[0]*sin(Z[1]);
double alpha = alpha_cal_track_init(x0,y0,x1,y1,x2,y2);
// if(T_track_head == 33855816 && T_point == 33859004 )
// {
// std::cout << "r_point=" << r_point
// << "h_point=" << h_point
// << "T_point=" << T_point
// << "r_temp_track=" << r_temp_track
// << "v_temp_track=" << v_temp_track
// << "h_temp_track=" << h_temp_track
// << "T_track_head=" << T_track_head
// << "d=" << d
// << "alpha=" << alpha;
// }
if(d*d<=TRACK_START_THRESHOLD*TRACK_START_THRESHOLD && alpha<ALPHA_START && vr_point*v_temp_track>0)// && fabs(h_point-h_temp_track )<= r_point*SIGMA_E&& abs(vr_point-v_temp_track)/abs(v_temp_track)<0.8
{
struct Asso_info asso_info_tmp;
@@ -226,7 +198,7 @@ void Track_Init::point_temp_track_asso(std::vector <std::vector<Temp_track>>
asso_info_tmp.d = d;
asso_info.push_back(asso_info_tmp);
(*temp_track)[j][L-1].asso_flag = 1;
point_process[i].Use_Flag = 1;
point_process[i].use_flag = 1;
//std::cout << "v_temp_track : " << v_temp_track << "vr_point : "<< vr_point;
}
}
@@ -240,44 +212,82 @@ void Track_Init::point_temp_track_asso(std::vector <std::vector<Temp_track>>
(*temp_track).push_back(std::vector <Temp_track> ());
//前L个点
int L = (*temp_track)[asso_info[i].track_idx-1].size();
int L = static_cast<int>((*temp_track)[asso_info[i].track_idx-1].size());
for (int j=0;j < L;j++ )
{
(*temp_track)[(*temp_track).size()-1].push_back((*temp_track)[asso_info[i].track_idx-1][j]);
(*temp_track)[(*temp_track).size()-1][j].asso_flag = 0;
(*temp_track)[(*temp_track).size()-1][j].temp_track_id = temp_track_id;
}
//关联上的点
Temp_track asso_track_info_tmp = {};
asso_track_info_tmp.r = point_process[asso_info[i].point_idx-1].Range;
asso_track_info_tmp.azi = point_process[asso_info[i].point_idx-1].Azimuth;
asso_track_info_tmp.height = point_process[asso_info[i].point_idx-1].Height;
asso_track_info_tmp.Amp = point_process[asso_info[i].point_idx-1].Amplitude;
asso_track_info_tmp.T = point_process[asso_info[i].point_idx-1].CPI_Time;
asso_track_info_tmp.temp_track_id = temp_track_id++;
asso_track_info_tmp.point_id = point_process[asso_info[i].point_idx-1].point_id;
asso_track_info_tmp.circle_id = point_process[asso_info[i].point_idx-1].circle_id;
asso_track_info_tmp.r = point_process[asso_info[i].point_idx-1].range;
asso_track_info_tmp.azi = point_process[asso_info[i].point_idx-1].azimuth;
asso_track_info_tmp.height = point_process[asso_info[i].point_idx-1].height;
asso_track_info_tmp.amplitude = point_process[asso_info[i].point_idx-1].amplitude;
asso_track_info_tmp.CPI_time = point_process[asso_info[i].point_idx-1].CPI_time;
asso_track_info_tmp.GNSS_time = point_process[asso_info[i].point_idx-1].GNSS_time;
asso_track_info_tmp.vr = point_process[asso_info[i].point_idx-1].Velocity;
asso_track_info_tmp.snr = point_process[asso_info[i].point_idx-1].snr;
asso_track_info_tmp.vr = point_process[asso_info[i].point_idx-1].velocity;
asso_track_info_tmp.SNR = point_process[asso_info[i].point_idx-1].SNR;
asso_track_info_tmp.RCS = point_process[asso_info[i].point_idx-1].RCS;
asso_track_info_tmp.asso_flag=0;
asso_track_info_tmp.buff_round = 1;
asso_track_info_tmp.pitch_num = point_process[asso_info[i].point_idx-1].pitch_num;
asso_track_info_tmp.pitch_num = point_process[asso_info[i].point_idx-1].pitch_beam_index;
std::memcpy(asso_track_info_tmp.speed_dim, point_process[asso_info[i].point_idx-1].speed_dim, sizeof(asso_track_info_tmp.speed_dim));
std::memcpy(asso_track_info_tmp.range_dim, point_process[asso_info[i].point_idx-1].range_dim, sizeof(asso_track_info_tmp.range_dim));
asso_track_info_tmp.d = asso_info[i].d;
// asso_track_info_tmp.Temp_track_section_idx = floor(point_process[asso_info[i].point_idx-1].beam_index/BEAM_NUM_DOT_SECTION)+1;
double Z0[2]={(*temp_track)[asso_info[i].track_idx-1][L-1].r*cos((*temp_track)[asso_info[i].track_idx-1][L-1].azi),
(*temp_track)[asso_info[i].track_idx-1][L-1].r*sin((*temp_track)[asso_info[i].track_idx-1][L-1].azi)};
double Z1[2]={point_process[asso_info[i].point_idx-1].Range*cos(point_process[asso_info[i].point_idx-1].Azimuth),
point_process[asso_info[i].point_idx-1].Range*sin(point_process[asso_info[i].point_idx-1].Azimuth)};
double Z1[2]={point_process[asso_info[i].point_idx-1].range*cos(point_process[asso_info[i].point_idx-1].azimuth),
point_process[asso_info[i].point_idx-1].range*sin(point_process[asso_info[i].point_idx-1].azimuth)};
double X[4];
double P[4][4];
kalman Kalman;
double T = ( point_process[asso_info[i].point_idx-1].CPI_Time - (*temp_track)[asso_info[i].track_idx-1][L-1].T)/1000.0;
double T = ( point_process[asso_info[i].point_idx-1].CPI_time - (*temp_track)[asso_info[i].track_idx-1][L-1].CPI_time)/1000.0;
Kalman.kalman_filter_init_2dots(Z0,Z1,T,X,P);
memcpy(asso_track_info_tmp.X, X, 4*sizeof(double));
memcpy(asso_track_info_tmp.P, P, 4*4*sizeof(double));
(*temp_track)[(*temp_track).size()-1].push_back(asso_track_info_tmp);
//计算航迹质量
auto& temp_track_ref = (*temp_track)[(*temp_track).size()-1];
L = static_cast<int>(temp_track_ref.size());
double snr_tmp = 0;
double d_tmp = 0;
double quality_tmp = 0;
d_tmp = exp(-temp_track_ref[L-1].d);
if(L == 3){
double average_snr = 0;
for(int i=0;i<L;i++)
{
average_snr += temp_track_ref[i].SNR;
}
average_snr /= L;
snr_tmp = 1/(1+exp(-(average_snr - 16) / 1.2));
}else{
snr_tmp = 1/(1+exp(-(temp_track_ref.back().SNR - 16) / 1.2));
}
quality_tmp = d_tmp * TRACK_QUALITY_D_WEIGHT + snr_tmp * TRACK_QUALITY_SNR_WEIGHT;
quality_tmp /= (TRACK_QUALITY_D_WEIGHT + TRACK_QUALITY_SNR_WEIGHT);
if(L == 3){
for(int i=0;i<L;i++)
{
temp_track_ref[i].quality = quality_tmp;
}
}else{
temp_track_ref[L-1].quality = temp_track_ref[L-2].quality * TRACK_QUALITY_ITER_COEFF + quality_tmp * (1 - TRACK_QUALITY_ITER_COEFF);
}
}
//temp_track中删除关联上的临时航迹
@@ -328,11 +338,11 @@ void Track_Init::point_track_head_asso( std::vector <std::vector<Temp_track>>
//点迹信息
double x_point, y_point, vr_point;
coor_trans Coor_trans;
Coor_trans.polar2cart(&x_point,&y_point,point_process[i].Range,point_process[i].Azimuth);
vr_point=point_process[i].Velocity;
double r_point=point_process[i].Range;
double h_point = point_process[i].Height;
double T_point = static_cast<double>(point_process[i].CPI_Time);
Coor_trans.polar2cart(&x_point,&y_point,point_process[i].range,point_process[i].azimuth);
vr_point=point_process[i].velocity;
double r_point=point_process[i].range;
double h_point = point_process[i].height;
double T_point = static_cast<double>(point_process[i].CPI_time);
//航迹信息
double x_track_head, y_track_head;
@@ -340,7 +350,7 @@ void Track_Init::point_track_head_asso( std::vector <std::vector<Temp_track>>
y_track_head=(*temp_track)[j][0].X[2];
double v_track_head=(*temp_track)[j][0].vr;
double h_track_head = (*temp_track)[j][0].height;
double T_track_head = static_cast<double>((*temp_track)[j][0].T);
double T_track_head = static_cast<double>((*temp_track)[j][0].CPI_time);
//距离差
double dis = sqrt(pow(x_point-x_track_head,2)+pow(y_point-y_track_head,2));
@@ -376,7 +386,7 @@ void Track_Init::point_track_head_asso( std::vector <std::vector<Temp_track>>
asso_info_tmp.track_idx = static_cast<int>(j)+1;
asso_info.push_back(asso_info_tmp);
(*temp_track)[j][0].asso_flag = 1;
point_process[i].Use_Flag = 1;
point_process[i].use_flag = 1;
//std::cout << "v_track_head: " << v_track_head << "vr_point: " << vr_point;
}
@@ -392,19 +402,23 @@ void Track_Init::point_track_head_asso( std::vector <std::vector<Temp_track>>
//第一个点
(*temp_track)[(*temp_track).size()-1].push_back((*temp_track)[asso_info[i].track_idx-1][0]);
(*temp_track)[(*temp_track).size()-1][0].asso_flag = 0;
(*temp_track)[(*temp_track).size()-1][0].temp_track_id = temp_track_id;
//第二个点
struct Temp_track asso_track_info_tmp = {};
asso_track_info_tmp.r = point_process[asso_info[i].point_idx-1].Range;
asso_track_info_tmp.azi = point_process[asso_info[i].point_idx-1].Azimuth;
asso_track_info_tmp.height = point_process[asso_info[i].point_idx-1].Height;
asso_track_info_tmp.Amp = point_process[asso_info[i].point_idx-1].Amplitude;
asso_track_info_tmp.vr = point_process[asso_info[i].point_idx-1].Velocity;
asso_track_info_tmp.snr = point_process[asso_info[i].point_idx-1].snr;
asso_track_info_tmp.temp_track_id = temp_track_id++;
asso_track_info_tmp.point_id = point_process[asso_info[i].point_idx-1].point_id;
asso_track_info_tmp.circle_id = point_process[asso_info[i].point_idx-1].circle_id;
asso_track_info_tmp.r = point_process[asso_info[i].point_idx-1].range;
asso_track_info_tmp.azi = point_process[asso_info[i].point_idx-1].azimuth;
asso_track_info_tmp.height = point_process[asso_info[i].point_idx-1].height;
asso_track_info_tmp.amplitude = point_process[asso_info[i].point_idx-1].amplitude;
asso_track_info_tmp.vr = point_process[asso_info[i].point_idx-1].velocity;
asso_track_info_tmp.SNR = point_process[asso_info[i].point_idx-1].SNR;
asso_track_info_tmp.RCS = point_process[asso_info[i].point_idx-1].RCS;
asso_track_info_tmp.T = point_process[asso_info[i].point_idx-1].CPI_Time;
asso_track_info_tmp.CPI_time = point_process[asso_info[i].point_idx-1].CPI_time;
asso_track_info_tmp.GNSS_time = point_process[asso_info[i].point_idx-1].GNSS_time;
asso_track_info_tmp.pitch_num = point_process[asso_info[i].point_idx-1].pitch_num;
asso_track_info_tmp.pitch_num = point_process[asso_info[i].point_idx-1].pitch_beam_index;
std::memcpy(asso_track_info_tmp.speed_dim, point_process[asso_info[i].point_idx-1].speed_dim, sizeof(asso_track_info_tmp.speed_dim));
std::memcpy(asso_track_info_tmp.range_dim, point_process[asso_info[i].point_idx-1].range_dim, sizeof(asso_track_info_tmp.range_dim));
asso_track_info_tmp.asso_flag = 0;
@@ -412,12 +426,12 @@ void Track_Init::point_track_head_asso( std::vector <std::vector<Temp_track>>
// asso_track_info_tmp.Temp_track_section_idx = floor(point_process[asso_info[i].point_idx-1].beam_index/BEAM_NUM_DOT_SECTION)+1;;
double Z0[2]={(*temp_track)[asso_info[i].track_idx-1][0].r*cos((*temp_track)[asso_info[i].track_idx-1][0].azi),
(*temp_track)[asso_info[i].track_idx-1][0].r*sin((*temp_track)[asso_info[i].track_idx-1][0].azi)};
double Z1[2]={point_process[asso_info[i].point_idx-1].Range*cos(point_process[asso_info[i].point_idx-1].Azimuth),
point_process[asso_info[i].point_idx-1].Range*sin(point_process[asso_info[i].point_idx-1].Azimuth)};
double Z1[2]={point_process[asso_info[i].point_idx-1].range*cos(point_process[asso_info[i].point_idx-1].azimuth),
point_process[asso_info[i].point_idx-1].range*sin(point_process[asso_info[i].point_idx-1].azimuth)};
double X[4];
double P[4][4];
kalman Kalman;
double T = (point_process[asso_info[i].point_idx-1].CPI_Time - (*temp_track)[asso_info[i].track_idx-1][0].T)/1000.0;
double T = (point_process[asso_info[i].point_idx-1].CPI_time - (*temp_track)[asso_info[i].track_idx-1][0].CPI_time)/1000.0;
Kalman.kalman_filter_init_2dots(Z0,Z1,T,X,P);
memcpy(asso_track_info_tmp.X, X, 4*sizeof(double));
@@ -468,20 +482,84 @@ void Track_Init::tmp_track_to_trust_track(std::vector<Trust_Track> *trust_tra
std::vector <std::vector<Temp_track>>::iterator Iter;
for (Iter=temp_track->begin(); Iter!=temp_track->end();)
{
int L=(*Iter).size();
int L=static_cast<int>((*Iter).size());
double range=(*Iter)[L-1].r;
double azi=(*Iter)[L-1].azi/PI*180;
double quality = (*Iter)[L-1].quality;
//(已停用)1km以内,SNR小于20dB的临时航迹不转为可靠航迹
// bool snr_flag = true;
// for(int p_idx = 0; p_idx < L; p_idx++){
// if((*Iter)[p_idx].r < 1000 && (*Iter)[p_idx].snr < 20){
// snr_flag = false;
// break;
// //计算航迹初始质量
// double average_snr = 0;
// for(int i=0;i<L;i++)
// {
// average_snr += (*Iter)[i].SNR;
// }
// average_snr /= L;
// double snr_tmp = 1/(1+exp(-(average_snr - 16) / 1.2));
// double &d = (*Iter)[L-1].d;
// double d_tmp = exp(-d);
// double quality_tmp = d_tmp * TRACK_QUALITY_D_WEIGHT + snr_tmp * TRACK_QUALITY_SNR_WEIGHT;
// quality_tmp /= (TRACK_QUALITY_D_WEIGHT + TRACK_QUALITY_SNR_WEIGHT);
// for(int i=0;i<L;i++)
// {
// (*Iter)[i].quality = quality_tmp;
// }
if( (L <=10 && L>=Work_Parameter.track_start_point_num)&& track_init_prohibit(range,azi,Work_Parameter)==0) //按长度查找TRUST_TRACK_POINT
//航迹起批条件判断
bool track_init_flag = true;
//航迹长度大于10点,不起批
if(L > 10){
track_init_flag = false;
}
//航迹长度小于起始点数,不起批
if(L < Work_Parameter.track_start_point_num){
track_init_flag = false;
}
//航迹起批禁止区域判断
if(track_init_prohibit(range,azi,Work_Parameter)!=0)
{
track_init_flag = false;
}
//航迹质量起批条件判断
switch(L){
case 3:
if(quality < TRACK_START_QUALITY_L3){
track_init_flag = false;
}
break;
case 4:
if(quality < TRACK_START_QUALITY_L4){
track_init_flag = false;
}
break;
case 5:
if(quality < TRACK_START_QUALITY_L5){
track_init_flag = false;
}
break;
case 6:
if(quality < TRACK_START_QUALITY_L6){
track_init_flag = false;
}
break;
case 7:
if(quality < TRACK_START_QUALITY_L7){
track_init_flag = false;
}
break;
default:
if(quality < TRACK_START_QUALITY_L7){
track_init_flag = false;
}
break;
}
//TODO: 杂波密度判断
if(track_init_flag)
{
//加入Track_to_start中
Track_to_start.push_back(std::vector <Temp_track> ());
@@ -514,9 +592,9 @@ void Track_Init::tmp_track_to_trust_track(std::vector<Trust_Track> *trust_tra
if (Track_to_start[i].size() < 3 || Track_to_start[j].size() < 3)
continue;
if( (Track_to_start[i][0].T == Track_to_start[j][0].T && Track_to_start[i][0].r == Track_to_start[j][0].r)
|| (Track_to_start[i][1].T == Track_to_start[j][1].T && Track_to_start[i][1].r == Track_to_start[j][1].r)
|| (Track_to_start[i][2].T == Track_to_start[j][2].T && Track_to_start[i][2].r == Track_to_start[j][2].r)
if( (Track_to_start[i][0].CPI_time == Track_to_start[j][0].CPI_time && Track_to_start[i][0].r == Track_to_start[j][0].r)
|| (Track_to_start[i][1].CPI_time == Track_to_start[j][1].CPI_time && Track_to_start[i][1].r == Track_to_start[j][1].r)
|| (Track_to_start[i][2].CPI_time == Track_to_start[j][2].CPI_time && Track_to_start[i][2].r == Track_to_start[j][2].r)
)
{
if(Track_to_start[i][L-1].d<Track_to_start[j][L-1].d)
@@ -565,8 +643,8 @@ void Track_Init::tmp_track_to_trust_track(std::vector<Trust_Track> *trust_tra
double Z2[2]={(Track_to_start[i][L-1].r)*cos(Track_to_start[i][L-1].azi),(Track_to_start[i][L-1].r)*sin(Track_to_start[i][L-1].azi) };
double X[6];
double P[6][6];
double T1=(Track_to_start[i][L-2].T-Track_to_start[i][L-3].T)/1000.0;
double T2=(Track_to_start[i][L-1].T-Track_to_start[i][L-2].T)/1000.0;
double T1=(Track_to_start[i][L-2].CPI_time-Track_to_start[i][L-3].CPI_time)/1000.0;
double T2=(Track_to_start[i][L-1].CPI_time-Track_to_start[i][L-2].CPI_time)/1000.0;
kalman Kalman;
Kalman.kalman_filter_init_3dots(Z0, Z1, Z2, T1,T2, X ,P);
@@ -574,14 +652,18 @@ void Track_Init::tmp_track_to_trust_track(std::vector<Trust_Track> *trust_tra
memcpy(trust_track_tmp.X, X, 6*sizeof(double));
memcpy(trust_track_tmp.P, P, 6*6*sizeof(double));
trust_track_tmp.Amplitude=Track_to_start[i][L-1].Amp;
trust_track_tmp.snr_point = Track_to_start[i][L-1].snr;
trust_track_tmp.track_id = Track_to_start[i][L-1].temp_track_id;
trust_track_tmp.point_id = Track_to_start[i][L-1].point_id;
trust_track_tmp.circle_id = Track_to_start[i][L-1].circle_id;
trust_track_tmp.Amplitude=Track_to_start[i][L-1].amplitude;
trust_track_tmp.snr_point = Track_to_start[i][L-1].SNR;
trust_track_tmp.RCS = Track_to_start[i][L-1].RCS;
trust_track_tmp.pitch_num = Track_to_start[i][L-1].pitch_num;
std::memcpy(trust_track_tmp.speed_dim, Track_to_start[i][L-1].speed_dim, sizeof(trust_track_tmp.speed_dim));
std::memcpy(trust_track_tmp.range_dim, Track_to_start[i][L-1].range_dim, sizeof(trust_track_tmp.range_dim));
trust_track_tmp.T_track = Track_to_start[i][L-1].T;
trust_track_tmp.T_track = Track_to_start[i][L-1].CPI_time;
trust_track_tmp.GNSS_time = Track_to_start[i][L-1].GNSS_time;
trust_track_tmp.Unupdated_time = 0;
trust_track_tmp.point_flag=1; //实点
trust_track_tmp.Track_Mode=0; //跟踪模式 TWS 0
trust_track_tmp.Target_Type=UNCONF_TARGET;
@@ -593,6 +675,9 @@ void Track_Init::tmp_track_to_trust_track(std::vector<Trust_Track> *trust_tra
trust_track_tmp.associate_point_number = 0;
trust_track_tmp.manual_tracking_flag = 0;
trust_track_tmp.point_type = 0;
trust_track_tmp.Quality = Track_to_start[i][L-1].quality;
trust_track_tmp.Point_sum = L;
trust_track_tmp.Valid_point_sum = L;
trust_track_tmp.u[0]=0.3333;
trust_track_tmp.u[1]=0.3333;
trust_track_tmp.u[2]=0.3333;
@@ -626,9 +711,10 @@ void Track_Init::tmp_track_to_trust_track(std::vector<Trust_Track> *trust_tra
}
Trust_Track_Output[*Trust_track_num_Output-1][j].Range_V=static_cast<float>(sqrt(pow(Track_to_start[i][j].X[1],2)+pow(Track_to_start[i][j].X[3],2)));
Trust_Track_Output[*Trust_track_num_Output-1][j].z=static_cast<float>(Track_to_start[i][j].height);
Trust_Track_Output[*Trust_track_num_Output-1][j].Amplitude=static_cast<float>(Track_to_start[i][j].Amp);
Trust_Track_Output[*Trust_track_num_Output-1][j].track_snr = static_cast<float>(Track_to_start[i][j].snr);
Trust_Track_Output[*Trust_track_num_Output-1][j].Amplitude=static_cast<float>(Track_to_start[i][j].amplitude);
Trust_Track_Output[*Trust_track_num_Output-1][j].track_snr = static_cast<float>(Track_to_start[i][j].SNR);
Trust_Track_Output[*Trust_track_num_Output-1][j].track_rcs = static_cast<float>(Track_to_start[i][j].RCS);
Trust_Track_Output[*Trust_track_num_Output-1][j].Quality = static_cast<float>(Track_to_start[i][j].quality * TRACK_QUALITY_MAX);
Trust_Track_Output[*Trust_track_num_Output-1][j].pitch_num = Track_to_start[i][j].pitch_num;
@@ -640,7 +726,7 @@ void Track_Init::tmp_track_to_trust_track(std::vector<Trust_Track> *trust_tra
Direction_Angle=Direction_Angle+2*PI;
Trust_Track_Output[*Trust_track_num_Output-1][j].Direction_Angle=static_cast<float>(Direction_Angle/PI*180);
Trust_Track_Output[*Trust_track_num_Output-1][j].track_time=static_cast<float>((Track_to_start[i][j].T)/1000.0);
Trust_Track_Output[*Trust_track_num_Output-1][j].track_time=static_cast<float>((Track_to_start[i][j].CPI_time)/1000.0);
Trust_Track_Output[*Trust_track_num_Output-1][j].GNSS_time=Track_to_start[i][j].GNSS_time;
Trust_Track_Output[*Trust_track_num_Output-1][j].x=static_cast<float>(Track_to_start[i][j].X[0]);
@@ -676,7 +762,7 @@ void Track_Init::tmp_track_die(std::vector <std::vector<Temp_track>> *temp_t
std::vector <std::vector<Temp_track>>::iterator Iter;
for (Iter=temp_track->begin(); Iter!=temp_track->end();)
{
int n=(*Iter).size();
int n=static_cast<int>((*Iter).size());
if (n <= 0)
{
temp_track->erase(Iter);
+2
View File
@@ -27,6 +27,8 @@ public:
private:
long long temp_track_id = 0; //临时航迹id
std::vector<PointRecv> point_process; //要处理的点迹
void point_temp_track_asso(std::vector <std::vector<Temp_track>> *temp_track,
@@ -37,22 +37,22 @@ void Track_Init_Direct_Tracking::point_temp_track_asso(std::vector <std::vector<
{
for ( size_t j=0;j<(*temp_track).size();j++)
{
int L = (*temp_track)[j].size();
int L = static_cast<int>((*temp_track)[j].size());
if( (*temp_track)[j].size() > 1 && (*temp_track)[j][L-1].buff_round >= 2)
{
//点迹信息
// double Z[2]={ point_process[i].Range*cos(point_process[i].Azimuth),point_process[i].Range*sin(point_process[i].Azimuth)} ;
double Z[3]={point_process[i].Range,
point_process[i].Azimuth,
point_process[i].Velocity};
double vr_point=point_process[i].Velocity;
double r_point=point_process[i].Range;
double Z[3]={point_process[i].range,
point_process[i].azimuth,
point_process[i].velocity};
double vr_point=point_process[i].velocity;
double r_point=point_process[i].range;
double prt = point_process[i].PRF_index;
double freq_ind = point_process[i].Freq_index;
double h_point = point_process[i].Height;
double T_point = static_cast<double>(point_process[i].CPI_Time);
double freq_ind = point_process[i].freq_index;
double h_point = point_process[i].height;
double T_point = static_cast<double>(point_process[i].CPI_time);
//航迹信息
double X[4];
@@ -62,7 +62,7 @@ void Track_Init_Direct_Tracking::point_temp_track_asso(std::vector <std::vector<
double v_temp_track=(*temp_track)[j][L-1].vr;
double r_temp_track=(*temp_track)[j][L-1].r;
double h_temp_track=(*temp_track)[j][L-1].height;
double T_track_head = static_cast<double>((*temp_track)[j][L-1].T);
double T_track_head = static_cast<double>((*temp_track)[j][L-1].CPI_time);
double delta_T = (T_point - T_track_head)/1000.0;
@@ -89,7 +89,7 @@ void Track_Init_Direct_Tracking::point_temp_track_asso(std::vector <std::vector<
asso_info_tmp.track_idx = static_cast<int>(j)+1;
asso_info.push_back(asso_info_tmp);
(*temp_track)[j][L-1].asso_flag = 1;
point_process[i].Use_Flag = 1;
point_process[i].use_flag = 1;
}
}
}
@@ -102,7 +102,7 @@ void Track_Init_Direct_Tracking::point_temp_track_asso(std::vector <std::vector<
(*temp_track).push_back(std::vector <Temp_track> ());
//前L个点
int L = (*temp_track)[asso_info[i].track_idx-1].size();
int L = static_cast<int>((*temp_track)[asso_info[i].track_idx-1].size());
for (int j=0;j < L;j++ )
{
(*temp_track)[(*temp_track).size()-1].push_back((*temp_track)[asso_info[i].track_idx-1][j]);
@@ -111,15 +111,15 @@ void Track_Init_Direct_Tracking::point_temp_track_asso(std::vector <std::vector<
//关联上的点
Temp_track asso_track_info_tmp = {};
asso_track_info_tmp.r = point_process[asso_info[i].point_idx-1].Range;
asso_track_info_tmp.azi = point_process[asso_info[i].point_idx-1].Azimuth;
asso_track_info_tmp.height = point_process[asso_info[i].point_idx-1].Height;
asso_track_info_tmp.Amp = point_process[asso_info[i].point_idx-1].Amplitude;
asso_track_info_tmp.T = point_process[asso_info[i].point_idx-1].CPI_Time;
asso_track_info_tmp.vr = point_process[asso_info[i].point_idx-1].Velocity;
asso_track_info_tmp.snr = point_process[asso_info[i].point_idx-1].snr;
asso_track_info_tmp.r = point_process[asso_info[i].point_idx-1].range;
asso_track_info_tmp.azi = point_process[asso_info[i].point_idx-1].azimuth;
asso_track_info_tmp.height = point_process[asso_info[i].point_idx-1].height;
asso_track_info_tmp.amplitude = point_process[asso_info[i].point_idx-1].amplitude;
asso_track_info_tmp.CPI_time = point_process[asso_info[i].point_idx-1].CPI_time;
asso_track_info_tmp.vr = point_process[asso_info[i].point_idx-1].velocity;
asso_track_info_tmp.SNR = point_process[asso_info[i].point_idx-1].SNR;
asso_track_info_tmp.RCS = point_process[asso_info[i].point_idx-1].RCS;
asso_track_info_tmp.pitch_num = point_process[asso_info[i].point_idx-1].pitch_num;
asso_track_info_tmp.pitch_num = point_process[asso_info[i].point_idx-1].pitch_beam_index;
std::memcpy(asso_track_info_tmp.speed_dim, point_process[asso_info[i].point_idx-1].speed_dim, sizeof(asso_track_info_tmp.speed_dim));
std::memcpy(asso_track_info_tmp.range_dim, point_process[asso_info[i].point_idx-1].range_dim, sizeof(asso_track_info_tmp.range_dim));
asso_track_info_tmp.asso_flag=0;
@@ -127,12 +127,12 @@ void Track_Init_Direct_Tracking::point_temp_track_asso(std::vector <std::vector<
// asso_track_info_tmp.Temp_track_section_idx = floor(point_process[asso_info[i].point_idx-1].beam_index/BEAM_NUM_DOT_SECTION)+1;
double Z0[2]={(*temp_track)[asso_info[i].track_idx-1][L-1].r*cos((*temp_track)[asso_info[i].track_idx-1][L-1].azi),
(*temp_track)[asso_info[i].track_idx-1][L-1].r*sin((*temp_track)[asso_info[i].track_idx-1][L-1].azi)};
double Z1[2]={point_process[asso_info[i].point_idx-1].Range*cos(point_process[asso_info[i].point_idx-1].Azimuth),
point_process[asso_info[i].point_idx-1].Range*sin(point_process[asso_info[i].point_idx-1].Azimuth)};
double Z1[2]={point_process[asso_info[i].point_idx-1].range*cos(point_process[asso_info[i].point_idx-1].azimuth),
point_process[asso_info[i].point_idx-1].range*sin(point_process[asso_info[i].point_idx-1].azimuth)};
double X[4];
double P[4][4];
kalman Kalman;
double T = ( point_process[asso_info[i].point_idx-1].CPI_Time - (*temp_track)[asso_info[i].track_idx-1][L-1].T)/1000.0;
double T = ( point_process[asso_info[i].point_idx-1].CPI_time - (*temp_track)[asso_info[i].track_idx-1][L-1].CPI_time)/1000.0;
Kalman.kalman_filter_init_2dots(Z0,Z1,T,X,P);
memcpy(asso_track_info_tmp.X, X, 4*sizeof(double));
@@ -187,11 +187,11 @@ void Track_Init_Direct_Tracking::point_track_head_asso( std::vector <std::vector
//点迹信息
double x_point, y_point, vr_point;
coor_trans Coor_trans;
Coor_trans.polar2cart(&x_point,&y_point,point_process[i].Range,point_process[i].Azimuth);
vr_point=point_process[i].Velocity;
double r_point=point_process[i].Range;
double h_point = point_process[i].Height;
double T_point = static_cast<double>(point_process[i].CPI_Time);
Coor_trans.polar2cart(&x_point,&y_point,point_process[i].range,point_process[i].azimuth);
vr_point=point_process[i].velocity;
double r_point=point_process[i].range;
double h_point = point_process[i].height;
double T_point = static_cast<double>(point_process[i].CPI_time);
//航迹信息
double x_track_head, y_track_head;
@@ -199,7 +199,7 @@ void Track_Init_Direct_Tracking::point_track_head_asso( std::vector <std::vector
y_track_head=(*temp_track)[j][0].X[2];
double v_track_head=(*temp_track)[j][0].vr;
double h_track_head = (*temp_track)[j][0].height;
double T_track_head = static_cast<double>((*temp_track)[j][0].T);
double T_track_head = static_cast<double>((*temp_track)[j][0].CPI_time);
//距离差
double dis = sqrt(pow(x_point-x_track_head,2)+pow(y_point-y_track_head,2));
@@ -229,7 +229,7 @@ void Track_Init_Direct_Tracking::point_track_head_asso( std::vector <std::vector
asso_info_tmp.track_idx = static_cast<int>(j)+1;
asso_info.push_back(asso_info_tmp);
(*temp_track)[j][0].asso_flag = 1;
point_process[i].Use_Flag = 1;
point_process[i].use_flag = 1;
}
}
@@ -247,15 +247,15 @@ void Track_Init_Direct_Tracking::point_track_head_asso( std::vector <std::vector
//第二个点
struct Temp_track asso_track_info_tmp = {};
asso_track_info_tmp.r = point_process[asso_info[i].point_idx-1].Range;
asso_track_info_tmp.azi = point_process[asso_info[i].point_idx-1].Azimuth;
asso_track_info_tmp.height = point_process[asso_info[i].point_idx-1].Height;
asso_track_info_tmp.Amp = point_process[asso_info[i].point_idx-1].Amplitude;
asso_track_info_tmp.vr = point_process[asso_info[i].point_idx-1].Velocity;
asso_track_info_tmp.snr = point_process[asso_info[i].point_idx-1].snr;
asso_track_info_tmp.r = point_process[asso_info[i].point_idx-1].range;
asso_track_info_tmp.azi = point_process[asso_info[i].point_idx-1].azimuth;
asso_track_info_tmp.height = point_process[asso_info[i].point_idx-1].height;
asso_track_info_tmp.amplitude = point_process[asso_info[i].point_idx-1].amplitude;
asso_track_info_tmp.vr = point_process[asso_info[i].point_idx-1].velocity;
asso_track_info_tmp.SNR = point_process[asso_info[i].point_idx-1].SNR;
asso_track_info_tmp.RCS = point_process[asso_info[i].point_idx-1].RCS;
asso_track_info_tmp.T = point_process[asso_info[i].point_idx-1].CPI_Time;
asso_track_info_tmp.pitch_num = point_process[asso_info[i].point_idx-1].pitch_num;
asso_track_info_tmp.CPI_time = point_process[asso_info[i].point_idx-1].CPI_time;
asso_track_info_tmp.pitch_num = point_process[asso_info[i].point_idx-1].pitch_beam_index;
std::memcpy(asso_track_info_tmp.speed_dim, point_process[asso_info[i].point_idx-1].speed_dim, sizeof(asso_track_info_tmp.speed_dim));
std::memcpy(asso_track_info_tmp.range_dim, point_process[asso_info[i].point_idx-1].range_dim, sizeof(asso_track_info_tmp.range_dim));
asso_track_info_tmp.asso_flag = 0;
@@ -263,12 +263,12 @@ void Track_Init_Direct_Tracking::point_track_head_asso( std::vector <std::vector
// asso_track_info_tmp.Temp_track_section_idx = floor(point_process[asso_info[i].point_idx-1].beam_index/BEAM_NUM_DOT_SECTION)+1;;
double Z0[2]={(*temp_track)[asso_info[i].track_idx-1][0].r*cos((*temp_track)[asso_info[i].track_idx-1][0].azi),
(*temp_track)[asso_info[i].track_idx-1][0].r*sin((*temp_track)[asso_info[i].track_idx-1][0].azi)};
double Z1[2]={point_process[asso_info[i].point_idx-1].Range*cos(point_process[asso_info[i].point_idx-1].Azimuth),
point_process[asso_info[i].point_idx-1].Range*sin(point_process[asso_info[i].point_idx-1].Azimuth)};
double Z1[2]={point_process[asso_info[i].point_idx-1].range*cos(point_process[asso_info[i].point_idx-1].azimuth),
point_process[asso_info[i].point_idx-1].range*sin(point_process[asso_info[i].point_idx-1].azimuth)};
double X[4];
double P[4][4];
kalman Kalman;
double T = (point_process[asso_info[i].point_idx-1].CPI_Time - (*temp_track)[asso_info[i].track_idx-1][0].T)/1000.0;
double T = (point_process[asso_info[i].point_idx-1].CPI_time - (*temp_track)[asso_info[i].track_idx-1][0].CPI_time)/1000.0;
Kalman.kalman_filter_init_2dots(Z0,Z1,T,X,P);
memcpy(asso_track_info_tmp.X, X, 4*sizeof(double));
@@ -306,7 +306,7 @@ void Track_Init_Direct_Tracking::tmp_track_to_trust_track( std::vector<Trust_Tr
std::vector <std::vector<Temp_track>>::iterator Iter;
for (Iter=temp_track->begin(); Iter!=temp_track->end();)
{
int L=(*Iter).size();
int L=static_cast<int>((*Iter).size());
if (L < 3)
{
++Iter;
@@ -323,8 +323,8 @@ void Track_Init_Direct_Tracking::tmp_track_to_trust_track( std::vector<Trust_Tr
double Z2[2]={((*Iter)[L-1].r)*cos((*Iter)[L-1].azi),((*Iter)[L-1].r)*sin((*Iter)[L-1].azi) };
double X[6];
double P[6][6];
double T1=((*Iter)[L-2].T-(*Iter)[L-3].T)/1000.0;
double T2=((*Iter)[L-1].T-(*Iter)[L-2].T)/1000.0;
double T1=((*Iter)[L-2].CPI_time-(*Iter)[L-3].CPI_time)/1000.0;
double T2=((*Iter)[L-1].CPI_time-(*Iter)[L-2].CPI_time)/1000.0;
kalman Kalman;
Kalman.kalman_filter_init_3dots(Z0, Z1, Z2, T1,T2, X ,P);
Trust_Track trust_track_tmp = {};
@@ -332,10 +332,10 @@ void Track_Init_Direct_Tracking::tmp_track_to_trust_track( std::vector<Trust_Tr
memcpy(trust_track_tmp.P, P, 6*6*sizeof(double));
trust_track_tmp.Track_Index = track_ID;
trust_track_tmp.Amplitude=(*Iter)[L-1].Amp;
trust_track_tmp.snr_point = (*Iter)[L-1].snr;
trust_track_tmp.Amplitude=(*Iter)[L-1].amplitude;
trust_track_tmp.snr_point = (*Iter)[L-1].SNR;
trust_track_tmp.RCS = (*Iter)[L-1].RCS;
trust_track_tmp.T_track = (*Iter)[L-1].T;
trust_track_tmp.T_track = (*Iter)[L-1].CPI_time;
trust_track_tmp.point_flag=1; //实点
trust_track_tmp.Track_Mode=1; //跟踪模式 TWS 0
trust_track_tmp.Target_Type=UNCONF_TARGET;
@@ -375,8 +375,8 @@ void Track_Init_Direct_Tracking::tmp_track_to_trust_track( std::vector<Trust_Tr
Trust_Track_Output[*Trust_track_num_Output-1][j].Elevation=static_cast<float>(asin((*Iter)[j].height/(*Iter)[j].r)/PI*180);
Trust_Track_Output[*Trust_track_num_Output-1][j].Range_V=static_cast<float>(sqrt(pow((*Iter)[j].X[1],2)+pow((*Iter)[j].X[3],2)));
Trust_Track_Output[*Trust_track_num_Output-1][j].z=static_cast<float>((*Iter)[j].height);
Trust_Track_Output[*Trust_track_num_Output-1][j].Amplitude=static_cast<float>((*Iter)[j].Amp);
Trust_Track_Output[*Trust_track_num_Output-1][j].track_snr = static_cast<float>((*Iter)[j].snr);
Trust_Track_Output[*Trust_track_num_Output-1][j].Amplitude=static_cast<float>((*Iter)[j].amplitude);
Trust_Track_Output[*Trust_track_num_Output-1][j].track_snr = static_cast<float>((*Iter)[j].SNR);
Trust_Track_Output[*Trust_track_num_Output-1][j].track_rcs = static_cast<float>((*Iter)[j].RCS);
double Direction_Angle;
Direction_Angle=atan2((*Iter)[j].X[3],(*Iter)[j].X[1]);
@@ -384,7 +384,7 @@ void Track_Init_Direct_Tracking::tmp_track_to_trust_track( std::vector<Trust_Tr
Direction_Angle=Direction_Angle+2*PI;
Trust_Track_Output[*Trust_track_num_Output-1][j].Direction_Angle=static_cast<float>(Direction_Angle/PI*180);
Trust_Track_Output[*Trust_track_num_Output-1][j].track_time=static_cast<float>(((*Iter)[j].T)/1000.0);
Trust_Track_Output[*Trust_track_num_Output-1][j].track_time=static_cast<float>(((*Iter)[j].CPI_time)/1000.0);
Trust_Track_Output[*Trust_track_num_Output-1][j].x=static_cast<float>((*Iter)[j].X[0]);
Trust_Track_Output[*Trust_track_num_Output-1][j].v_x=static_cast<float>((*Iter)[j].X[1]);
Trust_Track_Output[*Trust_track_num_Output-1][j].y=static_cast<float>((*Iter)[j].X[2]);
@@ -419,7 +419,7 @@ void Track_Init_Direct_Tracking::tmp_track_die(std::vector <std::vector<Temp_tra
std::vector <std::vector<Temp_track>>::iterator Iter;
for (Iter=temp_track->begin(); Iter!=temp_track->end();)
{
int n=(*Iter).size();
int n=static_cast<int>((*Iter).size());
if (n <= 0)
{
temp_track->erase(Iter);
+56 -18
View File
@@ -13,14 +13,14 @@
| ③ 凝聚 | 单 CPI 或相邻两 CPI 点迹 | 同类点合并(保幅度大者) | 凝聚点迹表 |
| ④ 关联 | 凝聚点迹 + 已有航迹 | IMM 交互 → 波门筛选 → 最近邻一对一 → EKF 更新 | 更新航迹 + 剩余点迹 |
| ⑤ 起始 | 剩余(未关联)点迹 | 逻辑法(每圈顺序固定):点 × ≥2 点临时航迹关联 → 点 × 1 点航迹头关联 → 剩余点转新航迹头 → 达标临时航迹起批 | 新可靠航迹 + 更新后的临时航迹表 |
| ⑥ 外推/消亡 | 未关联航迹 | 按数据率预测一帧;超限则消亡 | 补点航迹 / 消亡批号 |
| ⑥ 外推/消亡 | 未关联航迹 | 未到外推上限则按数据率预测一帧并输出,超限只累加轮数、不再预测也不再输出(TWS 上限 3 轮;TAS 判据方向相反,相扫 5 / 机扫 48 轮起才预测) | 本圈有更新的航迹(含补点行)/ 消亡批号 |
| ⑦ 波束控制 | TAS 队列 + 航迹 | 数据率门控 + 队列调度 → 位置外推 | 跟踪波束指令 |
**贯穿全流程的三条主线**
1. **时间主线**:所有时间戳为 64 位毫秒(CPI/GNSS),参与运算时除 1000 转秒;ΔT = 点迹时间 − 航迹时间,ΔT 的正负决定"有效更新 / 只消费不更新 / 跳过"。
2. **状态主线**:每条可靠航迹维护一套 IMM(3 个 Singer 模型)状态 [x, vx, ax, y, vy, ay],x/y 两轴解耦,量测为「距离-方位-径向速度」三维 EKF。
3. **生命周期主线**:可靠航迹靠"外推轮数"消亡;临时航迹靠"缓存圈数 buff_round"消亡。
3. **生命周期主线**:可靠航迹靠"外推轮数"消亡(判据为 `>`:TWS 普通航迹 6 圈、重点目标 10 圈,TAS 相扫 10 / 机扫 96 圈);临时航迹靠"缓存圈数 buff_round"消亡。
---
@@ -176,8 +176,11 @@ flowchart TD
J --> L{"还有候选?"}
K --> L
L -- 有 --> G
L -- 无 --> M["未关联航迹(point_flag==0 且非手动TAS)按数据率外推"]
M --> N["输出全部航迹(更新行 + 补点行)"]
L -- 无 --> M{"未关联航迹(point_flag==0 且非手动TAS):<br/>Extrapolate_round ≤ MAX_EXTRAPOLATE_NUM ?"}
M -- 是 --> M1["按数据率 Singer 预测一帧<br/>推进航迹时间,Track_Update_Flag=1"]
M -- 否 --> M2["不预测、不推进时间<br/>仅 Extrapolate_round+1,Track_Update_Flag=0"]
M1 --> N["只输出 Track_Update_Flag==1 的航迹行并清零该标志<br/>(其后由 track_init 追加起批历史点)"]
M2 --> N
```
### 4.2 波门(统计距离、高度、距离三类都通过才算候选)
@@ -232,7 +235,7 @@ VT > 300 且 R > 1200 → 拒绝(高速目标给固定 1200 m 上限)
| IMM/EKF 更新 | **不执行**,X1/X2/X3、P1/P2/P3、模型概率均保持原值 |
| 航迹时间/点信息 | **不刷新**(T_track、GNSS_time、关联点信息、幅度/SNR/RCS 都不变) |
| 新鲜度标志 | `point_flag` 保持 0、`Extrapolate_round` 不归零、`associate_point_number` 不递增 |
| 本圈后续 | 该航迹本圈不再关联其它点迹;由于 point_flag 仍为 0,**在随后的外推环节被推进一帧** |
| 本圈后续 | 该航迹本圈不再关联其它点迹;由于 point_flag 仍为 0,**在随后的外推环节被推进一帧**(未超外推上限时;超限后只累加轮数、不再预测也不输出该行) |
| 点迹归属 | 该点迹被标记"已使用"消费掉 → **不会再进入航迹起始**,避免生成虚假 TWS 航迹 |
### 4.5 关联成功后的更新内容
@@ -240,9 +243,9 @@ VT > 300 且 R > 1200 → 拒绝(高速目标给固定 1200 m 上限)
1. 三个模型的 EKF 状态/协方差更新;
2. 模型概率按新息协方差似然更新(见第 5 节);
3. 航迹时间 T_track / GNSS 时间跟进到该点迹;
4. 关联点信息(距离/方位/俯仰/径向速度/PRI)、幅度、SNR、RCS、俯仰波位、十字星数据刷新;
4. 关联点信息(距离/方位/俯仰/径向速度/PRI)、幅度、SNR、RCS、归一化幅度 `amp_asv_point`、速度维 FFT 点数 `prn_fftn_point`、俯仰波位、十字星数据刷新;
5. **高度维平滑**(见 4.6);
6. `Extrapolate_round = 0`、`point_flag = 1`、`associate_point_number += 1`。
6. `Extrapolate_round = 0`、`point_flag = 1`、`associate_point_number += 1`、`Track_Update_Flag = 1`(输出门控,见 4.7 末)。
### 4.6 高度平滑
@@ -257,14 +260,26 @@ VT > 300 且 R > 1200 → 拒绝(高速目标给固定 1200 m 上限)
> 距离越远俯仰测量跳变越大,窗口随之放长;仅 TWS/TAS 关联到实点时才刷新,外推补点不改变高度。
### 4.7 未关联航迹的外推
### 4.7 未关联航迹的外推(含外推圈数上限)
对 `point_flag == 0` 且 `manual_tracking_flag == 0` 的航迹(含 ΔT≤0 被消费的情形):
对 `point_flag == 0` 且 `manual_tracking_flag == 0` 的航迹(含 ΔT≤0 被消费的情形),按 `Extrapolate_round` 分两支(50e0b5c):
**(1)未到上限**:`Extrapolate_round <= MAX_EXTRAPOLATE_NUM`(机扫/相扫均为 3)时照常外推:
- 推算步长取运行时数据率:`work_mode` 0/1/2 → `DATA_RATE_SHORT / MIDDLE / FAR`(秒);
- 用各子模型的 Singer F、Q 推进一帧(**不做量测更新**);
- `T_track`、`GNSS_time` 各推进 ΔT×1000 毫秒;
- `Extrapolate_round += 1`,`point_flag = 0`(输出为补点/虚点)。
- `Extrapolate_round += 1`,`point_flag = 0`(补点/虚点),`Track_Update_Flag = 1` → 本圈作为补点行输出。
**(2)已超上限**:判据不再成立时**不再做 Singer 预测、不推进航迹时间**,只执行:
- `Extrapolate_round += 1`、`point_flag = 0`、`Track_Update_Flag = 0` → 本圈**不输出**该航迹(航迹仍留在航迹表中,等消亡判据命中)。
**输出门控(50e0b5c)**:TWS 输出航迹的循环改为「`Track_Update_Flag == 1` 才输出,输出后清零」,即每圈只输出**本圈关联到实点或成功外推**的航迹;此前是每圈把航迹表中全部可靠航迹各输出一行。门控只判更新标志、不判 `manual_tracking_flag`,因此手动转 TAS 的航迹若本圈在 TWS 侧关联到实点,仍会经 TWS 通道输出。
> 由于外推上限(3)小于 TWS 消亡轮数(6),普通航迹在停止预测后还会**连续 3 圈不向宿主输出**任何航迹行(`Extrapolate_round` 依次走到 5、6、7,第 7 圈在 `track_die` 中消亡),只在最后输出一次消亡批号。
>
> **TAS 通道的判据方向相反**(`track_asso_tas.cpp` L484):`Extrapolate_round >= MAX_EXTRAPOLATE_NUM_TAS` 时才做预测、推进航迹时间并置 `Track_Update_Flag = 1`;未达该轮数时只累加圈数(不预测、不输出)。即 TWS 是"先外推、到上限后停",TAS 是"先不预测、到轮数后才开始外推",详见 7.2 与第 10 节。
---
@@ -375,9 +390,14 @@ dis ≥ |v_r,head| · ΔT 同上,用航迹头的多普勒约
| 通道 | 条件(满足其一即消亡) |
|---|---|
| TWS 航迹 | `Track_Mode==0` 且 `Extrapolate_round ≥ 4` |
| TAS 航迹 | `Track_Mode==1` 且 `Extrapolate_round ≥ TRACK_DIE_ROUND_TAS`(相扫 5 / 机扫 96) |
| 手动删除 | `manual_delete_flag == 1`(两种通道通用,由外部删除接口置位) |
| TWS 普通航迹 | `Track_Mode==0` 且 `primary_target_flag==0` 且 `Extrapolate_round > TRACK_DIE_ROUND`(相扫/机扫均为 6) |
| TWS 重点航迹 | `Track_Mode==0` 且 `primary_target_flag==1` 且 `Extrapolate_round > TRACK_DIE_ROUND_PRIMARY`(10) |
| TAS 航迹 | `Track_Mode==1` 且 `Extrapolate_round > TRACK_DIE_ROUND_TAS`(相扫 10 / 机扫 96) |
| 手动删除 | `manual_delete_flag == 1`(各通道单独判定,置位即消亡,由外部删除接口 `track_delete` 置位) |
三个判据都改为 `die_flag` 组合("或"关系),且比较符由 `>=` 改为 `>`(50e0b5c):即允许的外推圈数比旧版各多 1 圈,且**重点航迹与普通航迹走不同门限**。由于每圈未关联只自增 1,判据为 `>` 时实际是"连续 N+1 圈没有实点关联"才消亡:普通 TWS 航迹 7 圈、重点航迹 11 圈、TAS 相扫 11 圈 / 机扫 97 圈。
> `primary_target_flag`(重点目标标记)由外部接口 `Data_Process::primary_target_add / primary_target_cancel` 按批号置 1 / 清 0(`data_process.cpp` L268-297);新起批航迹在 `track_init.cpp` 中以 `Trust_Track trust_track_tmp = {}` 零初始化,故恒为 0。该标记目前只影响上表的 TWS 消亡轮数。
消亡时把批号写入消亡输出数组并从航迹表删除;**临时航迹消亡不对外输出**(起批失败的中间态对宿主不可见)。
@@ -412,7 +432,8 @@ flowchart TD
| 俯仰/高度门限 | 俯仰门限已注释停用;**高度门限生效**(按距离分段,e0bf227 起) | **高度门限生效**(按距离分段,取值与 TWS 相同) |
| ΔT ≤ 0 | 参与选取但只消费点迹、不更新 | **直接跳过该点**(并记告警日志) |
| 外推时间 | 按 work_mode 数据率固定步长 | 按 `最新时间戳 − 航迹时间` |
| 输出 | 全部航迹 | 仅该批号航迹 |
| 外推触发轮数 | `Extrapolate_round <= MAX_EXTRAPOLATE_NUM`(3)才预测 | `Extrapolate_round >= MAX_EXTRAPOLATE_NUM_TAS`(相扫 5 / 机扫 48)才预测(方向与 TWS 相反) |
| 输出 | 本圈有更新的航迹(关联到实点或成功外推,输出后清 `Track_Update_Flag`) | 该批号航迹且本圈有更新 |
### 7.2 TAS 关联与更新
@@ -428,10 +449,13 @@ flowchart TD
F -- 否 --> H["丢弃"]
G --> I["取 d_min 最小的一点"]
I --> J["三模型 EKF 更新 → 概率更新 → 融合<br/>刷新时间/点信息/高度平滑"]
J --> K["输出该批号航迹"]
J --> K["输出该批号航迹(Track_Update_Flag==1 才输出,输出后清零)"]
E --> L{"候选表为空?"}
L -- 是 --> M["按 ΔT = 最新时间戳 − 航迹时间 外推一帧<br/>(ΔT ≤ 0 则跳过)"]
L -- 是 --> L1{"Extrapolate_round ≥ MAX_EXTRAPOLATE_NUM_TAS ?"}
L1 -- 是 --> M["按 ΔT = 最新时间戳 − 航迹时间 外推一帧<br/>(ΔT ≤ 0 则跳过),Track_Update_Flag=1"]
L1 -- 否 --> M2["不预测、不推进时间<br/>仅 Extrapolate_round+1,Track_Update_Flag=0"]
M --> K
M2 --> K
```
**高度门限**(|h_航迹 − h_点迹| 上限,按航迹距离分段):
@@ -446,6 +470,8 @@ flowchart TD
**TAS 后续处理**:外推不受 work_mode 数据率约束,而是直接推进到"本帧最新时间戳",保证 TAS 航迹时间与波束调度时刻同步。
**TAS 外推轮数门控(50e0b5c)**:与 TWS **方向相反**——只有 `Extrapolate_round >= MAX_EXTRAPOLATE_NUM_TAS`(相扫 5 / 机扫 48)时才做预测、推进航迹时间并置 `Track_Update_Flag = 1`;未达该轮数时只 `Extrapolate_round += 1`、`point_flag = 0`、`Track_Update_Flag = 0`,既不预测也不输出。因此 TAS 航迹在刚丢失点迹时并不会立刻按"最新时间戳"外推:相扫体制下前 5 次未关联不产生任何输出行,第 6 次起才开始外推输出;机扫体制下前 48 次不输出。该不对称仅如实记录(成因不明,不作缺陷判定),连同 TWS/TAS 输出门控一并见第 10 节。
---
## 8. TAS 波束控制(外部接口 Beam_Ctrl)
@@ -510,8 +536,11 @@ flowchart TD
| `TRACK_START_THRESHOLD` | 8 | 同左 | 起始统计距离波门(d ≤ 8) |
| `ALPHA_START` | 60 | 同左 | 起始折角上限(°) |
| `ASSO_THORD` | 4 | 同左 | 关联波门(判据 d*d < ASSO_THORD²,d 已是马氏距离平方,等效 d < 4) |
| `TRACK_DIE_ROUND` | 4 | 4 | TWS 消亡外推轮数 |
| `TRACK_DIE_ROUND_TAS` | 5 | 96 | TAS 消亡外推轮数 |
| `MAX_EXTRAPOLATE_NUM` | 3 | 同左 | TWS 未关联航迹最大外推轮数(`Extrapolate_round <= 该值` 才预测并输出) |
| `MAX_EXTRAPOLATE_NUM_TAS` | 5 | 48 | TAS 未关联航迹开始外推的轮数(判据 `>=`,方向与 TWS 相反) |
| `TRACK_DIE_ROUND` | 6 | 6 | TWS 普通航迹消亡轮数(判据 `Extrapolate_round > 该值`) |
| `TRACK_DIE_ROUND_PRIMARY` | 10 | 10 | TWS 重点航迹(`primary_target_flag==1`)消亡轮数 |
| `TRACK_DIE_ROUND_TAS` | 10 | 96 | TAS 航迹消亡轮数(判据 `Extrapolate_round > 该值`) |
| `MAX_TRACK_NUM / MAX_TRACK_INDEX` | 500 / 500 | 同左 | 航迹数上限 / 航迹号上限 |
| `TAS_QUEUE_LENGTH / MAX_TAS_NUM` | 4 / 4 | 1 / 1 | TAS 队列长度 / 最大 TAS 目标数 |
| `H_F_WIN_LEN` | 3 | 同左 | 高度平滑窗长基数(实际 +2/+3/+4/+6) |
@@ -564,6 +593,15 @@ flowchart TD
- 屏蔽区/禁止区的组数按 30 组数组定义,配置数量未做上限校验;
- 高度平滑缓存只增不减,长时运行下每条航迹的内存占用会持续增长。
**外推、消亡与输出门控(50e0b5c / fc49707 / 42b598e / e8c56f9)**
- **外推圈数上限**:TWS 只在 `Extrapolate_round <= MAX_EXTRAPOLATE_NUM`(3)时预测并输出;TAS 相反,`Extrapolate_round >= MAX_EXTRAPOLATE_NUM_TAS`(相扫 5 / 机扫 48)时才预测并输出。两处比较方向不一致,此处仅如实记录,**不判定为缺陷**;
- **消亡判据**由 `>=` 改为 `>`,并区分重点目标:普通 TWS 航迹 `> TRACK_DIE_ROUND`(6)、重点航迹(`primary_target_flag==1`)`> TRACK_DIE_ROUND_PRIMARY`(10)、TAS 航迹 `> TRACK_DIE_ROUND_TAS`(相扫 10 / 机扫 96);`manual_delete_flag == 1` 单独判为消亡;因外推上限(3)小于消亡轮数(6),普通 TWS 航迹停止预测后还会连续 3 圈不向宿主输出;
- **输出门控**:TWS 与 TAS 的输出航迹循环都新增 `Track_Update_Flag == 1` 判定并在输出后清零,只输出**本圈关联到实点或成功外推**的航迹(此前 TWS 每圈把航迹表中全部可靠航迹各输出一行);
- **接近/远离判据**(fc49707 + 42b598e):`Flag_Direction` 不再用实测径向速度 `vr_point > 0`(受多普勒速度模糊影响符号可能取反),改为用 IMM 融合速度方向角判定——`Direction_Angle = atan2(X[4], X[1])`(弧度转度后输出到 `Track.Direction_Angle`),`x`、`y` 为经 `Work_Parameter.Sys_delay` 补偿后的位置、`r` 为其模,`acos((-x·cosθ - y·sinθ)/r) < π/2` 判为靠近(1)、否则远离(0),等价于"速度方向与雷达→目标位置方向夹角 > 90°"。中间版本 fc49707 曾写为 `acos((-x·vx - y·vy)/r/Range_V) < π/4`(按合速度归一化、门限 45°),42b598e 改为上式;
- **归一化幅度 / 速度维 FFT 点数(e8c56f9 新增,输入侧已打通)**:`DataRev` 新增 `amp_asv`(归一化幅度 dB)、`prn_fftn`(速度维 FFT 点数),`Track`/内部 `PointRecv`/`Trust_Track` 也新增同名成对字段;`data_preprocess()` 现已在 TWS 与 TAS 两条分支都把这两个字段搬入 `PointRecv`(并随 `RdpRecPoint` 落库),因此 `track_asso.cpp` 写入航迹并在 TWS 输出行写回的 `amp_asv_point` / `prn_fftn_point` 有有效数据来源;**TAS 的关联与输出路径(`track_asso_tas.cpp`)仍未涉及这两个字段**,TAS 输出行中它们保持宿主原值。
- **版本号与记录标识(b227ae1 / c3c7e66)**:`parameters.h` 新增版本宏并拼装为 `VERSION_STRING`(当前 `V1.5.6.PS` / `V1.5.6.MS`),实例创建后可由基类公开成员 `version_info` 读取;`Data_Process` 新增 `point_id` / `frame_id` / `circle_id` 自增计数(`circle_id` 在 TWS 满一圈、TAS 每帧末自增),`PointRecv`/`Temp_track`/`Trust_Track` 新增对应的标识字段,记录功能据此把「输入点迹 → 凝聚点迹 → 临时航迹 → 可靠航迹 → 关联信息」串成同一条链路。
**未启用**
- 引导跟踪(direct tracking)、手动打跟踪波束接口为空实现;
+195
View File
@@ -0,0 +1,195 @@
# 数据记录调试功能(rdp_record)
> 面向调试的数据记录框架:把内部结构体按「一个记录实体一张表」写入 SQLite,供事后查询分析。
> 本功能**默认只在 Debug 构建中启用**,Release/交付构建完全不编译(无依赖、无代码、无性能损失)。
> 相关代码:`data_process_class_dll/rdp_record.h`、`rdp_record_entity.h`、`rdp_record.cpp`;
> 第三方依赖来源见 `third_party/ormpp/UPSTREAM.md`。
---
## 1. 目的与范围
- **目的**:调试期把关键中间数据落库(点迹、航迹、关联结果……),用于事后 SQL 查询与对比分析;
- **框架性设计**:本功能**不预设记录哪些数据**。新增一类记录数据的成本固定为三步:
加记录实体结构体 → 注册 → 在需要的位置写一行记录点宏(配方见第 5 节);
- **非目标**:不做异步写入、不做查询/回放工具、不做数据裁剪与轮转、不追求多线程写入性能。
## 2. 开关与构建
| 项目 | 说明 |
|---|---|
| 开关 | CMake 缓存变量 `RDP_DATA_RECORD`,取值 `AUTO`(默认)/ `ON` / `OFF` |
| AUTO 语义 | `Debug` 构建启用、`Release`(及其它)构建关闭 |
| 生效方式 | 启用时构建系统定义 `RDP_RECORD_ENABLE` 并额外编译静态库 `rdp_record`(`rdp_record.cpp` + 内置 `sqlite3.c`)并链接进 DLL |
| 关闭时的保证 | 记录模块源码**不参与编译**;`RDP_RECORD(...)` / `RDP_RECORD_BATCH(...)` 展开为 `((void)0)`(实参不求值,也不会产生「变量已初始化但未使用」告警);DLL 不新增依赖与导出符号 |
```bat
:: 交付/Release:关闭(AUTO 的默认结果,也可显式 OFF)
.vscode\scripts\configure.bat build/X256_PS PHASE_SCANNING Release OFF
.vscode\scripts\build.bat build/X256_PS
:: Debug:启用(AUTO 的默认结果,也可显式 ON)
.vscode\scripts\configure.bat build/X256_PS PHASE_SCANNING Debug ON
.vscode\scripts\build.bat build/X256_PS
:: 直接用 cmake(需先进入 MSVC 2022 x64 环境)
cmake -S . -B build/debug_rec -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Debug ^
-DRADAR_SCAN_MODE=PHASE_SCANNING -DRDP_DATA_RECORD=ON
cmake --build build/debug_rec
```
配置阶段会打印开关状态:`数据记录调试功能:启用(C++20 静态库 rdp_record + 内置 SQLite)` 或 `...:关闭(不编译记录模块,RDP_RECORD 为空操作)`。
**语言标准与依赖**:记录模块以 **C++20** 独立编译(ormpp 要求 `CMAKE_CXX_STANDARD 20`),并编译内置的 SQLite amalgamation(因此启用时需要 C 编译器,随 VS 2022 工具集提供);**雷达处理代码与内置 Eigen 仍为 C++17**,二者通过 `rdp_record.h` 的「仅声明 + 显式实例化」跨标准边界:调用方(C++17)只把记录实体按 `const` 引用递给 `rdp_record::Write<T>()`,实现在 `rdp_record.cpp`(C++20)中显式实例化。
## 3. 运行期行为
| 项目 | 说明 |
|---|---|
| 存放目录 | `RDP_RECORD_DB_DIR`(`parameters.h`,默认 `.\rdp_records\`,相对宿主进程当前工作目录,不存在时自动创建) |
| 文件名 | `rdp_record_YYYYMMDD_HHMMSS.db`(**每次运行一个新文件**,与 `rdp_logs` 的日志命名风格一致) |
| 初始化 | `Data_Process_Factory::GetB()` 内调用 `rdp_record::Init()`(幂等):建目录 → 开库 → 打印记录库路径 |
| 关闭 | `Data_Process_Factory::Destroy()` 内调用 `rdp_record::Shutdown()`;宿主未调用时由静态析构兜底关库 |
| 建表 | 首次写入某记录实体时自动执行 `CREATE TABLE IF NOT EXISTS`(每实体只尝试一次) |
| PRAGMA | `synchronous=OFF`(调试取舍:提交时不再 fsync,崩溃可能丢失尾部数据;如需崩溃安全可改为 `NORMAL` 或自行在 sqlite3 层开启 WAL)。未设置 `journal_mode`——ormpp 的 `execute()` 只接受不返回结果行的语句 |
| 线程模型 | 单连接 + 内部互斥锁串行化写入;宿主多线程调用不会破坏数据,但**不保证高并发写入性能**(调试功能,按要求不做异步) |
| 失败降级 | 目录不可建/库打不开:`Init()` 返回 `false`,只打印一次日志,之后所有写入静默忽略;建表或插入失败:**每个实体只提示一次**,不重试、不抛出、不影响雷达数据处理 |
| 自增主键 | 每个记录实体的首字段固定 `int64_t id`:建表时登记自增主键,插入时自动跳过该列(写入时固定填 `0`,由 SQLite 分配) |
## 4. 记录实体(表)约定
1. 结构体定义在**全局命名空间**(勿放进 `namespace`),推荐以 `RdpRec` 开头;
2. **结构体名即表名**(ormpp 取「短结构体名」),无需任何建表代码;
3. 首字段固定为 `int64_t id`;
4. 字段类型仅限标量,映射关系:
| C++ 类型 | SQLite 类型 | 备注 |
|---|---|---|
| `int` | `INTEGER` | 枚举同理(按整数存) |
| `int64_t` / `long long` | `INTEGER` | 毫秒时间戳用它 |
| `float` | `FLOAT` | |
| `double` | `DOUBLE` | |
| `bool` | `INTEGER` | |
| `std::string` | `TEXT` | 用于把数组类状态(如 `X[6]`、`P[6][6]`、`u[3]`)经 `rdp_record_entity.h` 的 `arrayToString()` 先转成文本再入库——这是「禁止数组字段」的替代写法,见第 5 条 |
5. **禁止数组字段**(如 `float speed_dim[129]`、`char[N]`):ormpp 对 C 数组按 C 字符串语义(`strlen`)绑定,会读越界或写入错误数据。需要记录大批量数组时,按下列三种方式之一处理:① 用一个 `std::string` 字段存 `arrayToString(数组)` 的结果(`RdpRecTempTrack`/`RdpRecTrustTrack` 的 `X`/`P`/`X1`~`X3`/`P2`/`P3`/`u` 即此法,多维数组以 `;` 分隔内层、`,` 分隔元素);② 另建「元素展开表」(一行一个元素);③ 改用 blob 字段;
6. 命名约定:字段名带单位后缀(`_ms` 毫秒、`_m` 米、`_deg` 度、`_mps` 米/秒、`_s` 秒),避免单位歧义;
7. **表结构变更不迁移旧表**:增删字段后不会修改已存在的表结构;由于每次运行生成新的 db 文件,正常调试流程天然规避该问题(若手工复用旧库,需自行建新表或删库)。
## 5. 新增记录点:四步配方
以「记录航迹号 + 高度」为例:
**第 1 步 — 声明实体**(`rdp_record_entity.h`「记录实体定义区」,紧随其后写一行 `RDP_RECORD_DECLARE`):
```cpp
struct RdpRecTrackHeight
{
int64_t id; //自增主键(写入时固定填 0)
int track_index; //航迹号
double height_m; //高度(m)
};
RDP_RECORD_DECLARE(RdpRecTrackHeight);
```
**第 2 步 — 注册实体**(`rdp_record.cpp`「记录实体注册区」,字段顺序必须与结构体声明顺序一致):
```cpp
YLT_REFL(RdpRecTrackHeight, id, track_index, height_m);
RDP_RECORD_DEFINE(RdpRecTrackHeight);
```
**第 3 步 — 埋记录点**(在需要记录的位置):
```cpp
//单条(就地构造;宏为可变参数,允许花括号内有逗号)
RDP_RECORD(RdpRecTrackHeight{0, trust_track[i].Track_Index, trust_track[i].Height});
//批量(需要语句/局部变量时,整体用 #ifdef 包裹,保证交付构建零代码)
#ifdef RDP_RECORD_ENABLE
std::vector<RdpRecTrackHeight> recs;
for (size_t i = 0; i < trust_track.size(); ++i)
{
recs.push_back(RdpRecTrackHeight{0, trust_track[i].Track_Index, trust_track[i].Height});
}
RDP_RECORD_BATCH(recs);
#endif
```
**第 4 步 — 无需建表**:首次写入该实体时自动 `CREATE TABLE IF NOT EXISTS`;不同实体自动落到不同表。
> 检查点:若忘记第 2 步(注册/实例化),链接期会报 **LNK2019**(`extern template` 已阻止隐式实例化),这正是配方的自检点。
## 6. 现有记录表与记录点
当前共 **7 个记录实体定义**,其中 **6 张表在用**(第 ⑦ 项已停用)。下表逐项给出记录点、埋点位置、表名与用途;完整字段清单见本节末的「字段速查」。
| # | 记录点 | 位置 | 表 | 说明 |
|---|---|---|---|---|
| ① | 输入点迹(TWS + TAS) | `data_process.cpp::data_preprocess()`,TWS 分支(`point_type == 0`)与 TAS 分支(`point_type == 1`)的逐点循环内 | `RdpRecPoint` | TWS/TAS **共用一张表**,用 `point_type` 区分;每个输入点迹一行,含 `point_id` / `frame_id` / `circle_id` 三个链路标识 |
| ② | TWS 凝聚后点迹 | `dot_coh.cpp::dot_coh_process_buff()`,输出到 `point_recv` 的循环内 | `RdpRecPointCohesionTWS` | 两帧滑窗凝聚后进入关联的点迹 |
| ③ | TAS 凝聚后点迹 | `dot_coh_tas.cpp::dot_coh_tas_process()`,输出到 `point_recv_tas` 的循环内 | `RdpRecPointCohesionTAS` | 单帧凝聚后的 TAS 点迹 |
| ④ | 临时航迹 | `track_init.cpp::track_init_process_logic()` 末尾(**消亡临时航迹之后**),双层遍历 `temp_track` | `RdpRecTempTrack` | 每条临时航迹的每个点一行,含 `temp_track_id` / `point_id` / `circle_id`;因在消亡之后记录,表中是「本圈存活下来 + 本圈新建」的临时航迹 |
| ⑤ | 可靠航迹 | `track_asso.cpp::track_asso_process()` 的**输出循环内**(`Track_Update_Flag==1` 的分支末尾) | `RdpRecTrustTrack` | 内部 `Trust_Track` 全量落库(含 IMM 的 `X`/`P`/`X1`~`X3`/`P2`/`P3`/`u`,数组经 `arrayToString()` 存为文本列)。**只记录本圈有输出的航迹,且仅 TWS 通道**——`track_asso_tas.cpp`(TAS)与 `track_init.cpp`(起批)均未埋此点 |
| ⑥ | 点航关联信息 | `track_asso.cpp::model_filter()` 三处:未通过门限的候选、被最近邻取中的候选、因航迹/点迹已被消费而删表的候选 | `RdpRecAssoInfo` | 每个候选一行(`d1`/`d2`/`d3`/`d_min`、`delta_T`/`delta_h`/`R`/`VT`、`thd_d`/`thd_h`/`thd_r`),并以 `min_d_flag` 标记是否被最近邻取中,可用于复现关联决策 |
| ⑦ | (已停用)本圈输出航迹 | 原 `data_process.cpp::track_process()` 的 `model == 1/3` 分支 | ~~`RdpRecTrackOut`~~ | 该实体与记录点已在 b227ae1 中整体注释停用,改由 ⑤ 记录内部航迹;`rdp_record.cpp` 与 `rdp_record_entity.h` 中保留注释代码以备参考 |
> 表清单即上表 ①②③④⑤⑥ 六张在用表(`rdp_record_entity.h` 中共 7 个实体定义,其中第 ⑦ 项已停用)。
**链路串联**:①→②/③(点迹 → 凝聚后点迹)按 `point_id` 关联;②/③→④/⑤ 按 `point_id` 与 `circle_id` 关联;④→⑤ 通过起批时透传的 `track_id`(取自临时航迹末点的 `temp_track_id`)关联;⑤→⑥ 按 `track_id` 与 `point_id` 关联。`circle_id` 在 TWS 满一圈(`Beam_index_aiz == 0`)、TAS 每帧处理结束时自增,可用来划分「第几圈/第几帧」。
**字段速查**(字段顺序即建表列顺序;首列均为 `int64_t id` 自增主键):
- `RdpRecPoint` / `RdpRecPointCohesionTWS` / `RdpRecPointCohesionTAS`(三者字段一致):`point_id`、`frame_id`、`circle_id`、`range`、`azimuth`(弧度)、`velocity`、`amplitude`、`height`、`SNR`、`RCS`、`encoder_value`、`amp_asv_point`、`prn_fftn_point`、`CPI_time`、`GNSS_time`、`use_flag`、`azimuth_beam_index`、`pitch_beam_index`、`PRF_index`、`freq_index`、`point_type`、`point_section_asso`;
- `RdpRecTempTrack`:`temp_track_id`、`point_id`、`circle_id`、`X`、`P`(文本)、`r`、`azi`、`vr`、`height`、`SNR`、`RCS`、`amplitude`、`CPI_time`、`GNSS_time`、`d`、`buff_round`、`temp_track_section_idx`、`asso_flag`、`pitch_num`;
- `RdpRecTrustTrack`:`track_id`、`point_id`、`circle_id`、`Track_Index`、`Range`、`Azimuth`、`Elevation`、`Velocity`、`Amplitude`、`Height`、`T_track`、`GNSS_time`、`Track_Sum`、`Point_Index`、`Track_Mode`、`Track_Update_Flag`、`Extrapolate_round`、`point_flag`、`approach_flag`、`manual_delete_flag`、`manual_tracking_flag`、`associate_point_number`、`primary_target_flag`、`Target_Type`、`Track_section_idx`、`range_point`、`azi_point`、`elev_point`、`vr_point`、`prf_point`、`point_type`、`snr_point`、`amp_asv_point`、`prn_fftn_point`、`RCS`、`pitch_num`、`X`、`P`、`X1`、`X2`、`X3`、`P2`、`P3`、`u`(后 8 项为文本);
- `RdpRecAssoInfo`:`point_id`、`track_id`、`circle_id`、`d1`、`d2`、`d3`、`d_min`、`point_index`、`track_index`、`delta_T`、`delta_h`、`R`、`VT`、`thd_d`、`thd_h`、`thd_r`、`min_d_flag`。
各表的字段为**调试用途**,按第 5 节增删即可,不影响框架。注意记录点中的字段错误只在启用记录功能的构建中才会被检查(关闭时宏丢弃实参)。
**两点使用提醒**:
- `RdpRecTrustTrack::Track_Update_Flag` **恒为 0**:写库发生在输出循环内,而该循环入口已先把标志清零(`track_asso.cpp` 先置 0、再输出、最后记录)。判断「某航迹本圈是否更新」应改用该表是否出现对应 `track_id` / `circle_id` 的记录,或改用 `RdpRecAssoInfo::min_d_flag`。
- `Range` / `Azimuth` / `Elevation` / `Velocity` 四个内部快照**只在航迹当圈有输出时被回填**,长期未输出(外推超限)的航迹在表中为 0 或旧值。
## 7. 查询与分析
```python
# 取最近一次运行的记录库并查看表结构、行数与抽样数据(Python 自带 sqlite3 即可)
import glob, os, sqlite3
path = max(glob.glob(r'rdp_records\rdp_record_*.db'), key=os.path.getmtime)
con = sqlite3.connect(path)
print(path)
print(con.execute("select name from sqlite_master where type='table'").fetchall())
print(con.execute("pragma table_info(RdpRecPoint)").fetchall())
print(con.execute("select count(*) from RdpRecPoint").fetchone())
print(con.execute("select * from RdpRecPoint order by id desc limit 3").fetchall())
print(con.execute("pragma integrity_check").fetchone())
# 按点迹链路串联:输入点迹 -> 凝聚后点迹 -> 临时航迹
print(con.execute("""
select p.circle_id, p.point_id, p.range, c.range, t.temp_track_id, t.buff_round
from RdpRecPoint p
left join RdpRecPointCohesionTWS c on c.point_id = p.point_id
left join RdpRecTempTrack t on t.point_id = p.point_id
order by p.id desc limit 10
""").fetchall())
# 查看某圈的关联决策(min_d_flag=1 为最终取中的候选)
print(con.execute("""
select track_id, point_id, d_min, delta_T, thd_d, thd_h, thd_r, min_d_flag
from RdpRecAssoInfo where circle_id = (select max(circle_id) from RdpRecAssoInfo)
order by min_d_flag desc, d_min asc limit 20
""").fetchall())
```
也可用 DB Browser for SQLite 直接打开 db 文件;同一 db 内所有表一一对应各记录实体。
## 8. 注意事项与已知限制
- **交付构建不要开启**:`Release` + `RDP_DATA_RECORD=ON` 仅用于临时排查,会带来 sqlite3 编译与运行时写入开销;正式交付保持 `AUTO`/`OFF`;
- **Release 下记录点不做类型检查**:关闭时宏丢弃实参,因此记录点里的字段拼写/类型错误只在启用记录功能的构建中才会暴露——新增记录点后请至少构建一次开启记录功能的配置;
- `NaN`/`Inf` 会由 SQLite 存为 `NULL`(`sqlite3_bind_double` 行为),分析时需注意;
- 记录库是调试产物:进程异常退出(未调用 `Destroy()`)时,`synchronous=OFF` 下可能丢失最后若干条记录;
- IntelliSense 可能对 `rdp_record.cpp` 按 `c_cpp_properties.json` 的 C++17 提示(该文件实际按 C++20 编译,`build/<...>/compile_commands.json` 中带有 `/std:c++20`),以实际构建结果为准;
- `third_party/ormpp/` 为上游原样内置(ormpp 0.2.0 / iguana 1.0.5 / SQLite 3.49.1,Apache-2.0 与公有领域),升级方式与校验值见该目录 `UPSTREAM.md`。
+17 -12
View File
@@ -77,20 +77,23 @@ track_init.track_init_process_logic(&point_recv, &trust_track, &temp_track,
## 2. 数据结构与状态容器
### 2.1 Temp_track(临时航迹点,`struct.h` L94-121)
### 2.1 Temp_track(临时航迹点,`struct.h`)
| 字段 | 含义 | 起始流程中的作用 |
|---|---|---|
| `X[4]` | 4 维状态 **[x, vx, y, vy]** | EKF 一步预测的状态;由两点卡尔曼初始化填充 |
| `P[4][4]` | 状态协方差 | 同上的协方差 |
| `r / azi / vr / height / snr / RCS / Amp` | 该点的极坐标测量 | 初始化、输出、条件判据(`azi` 为**弧度**) |
| `T` / `GNSS_time` | CPI 时间 / GNSS 时间(ms) | ΔT 计算(除 1000 转秒) |
| `r / azi / vr / height / SNR / RCS / amplitude` | 该点的极坐标测量 | 初始化、输出、条件判据(`azi` 为**弧度**) |
| `CPI_time` / `GNSS_time` | CPI 时间 / GNSS 时间(ms) | ΔT 计算(除 1000 转秒) |
| `d` | 该点关联时的马氏统计距离 | 去重阶段用于「保留更优者」 |
| `buff_round` | **已缓存圈数** | 生命周期与关联时机控制(见 4.2) |
| `asso_flag` | 0 未关联 / 1 已关联(待删除)/ 2 去重淘汰 | 临时状态标记 |
| `pitch_num`、`speed_dim[129]`、`range_dim[8]` | 俯仰波位、十字星数据 | 纯透传 |
| `Temp_track_section_idx` | 临时航迹区号 | 相关赋值已被注释停用,当前不参与逻辑 |
| `temp_track_id` / `point_id` / `circle_id` | 临时航迹 ID / 末关联点迹 ID / 处理圈 ID(c3c7e66 新增) | 数据记录链路标识;起批时透传给 `Trust_Track` |
| `temp_track_section_idx` | 临时航迹区号 | 相关赋值已被注释停用,当前不参与逻辑 |
> 字段更名提示(c3c7e66):`snr`→`SNR`、`Amp`→`amplitude`、`T`→`CPI_time`、`Temp_track_section_idx`→`temp_track_section_idx`。阅读早期提交或旧文档时注意对应关系。
>
> ⚠ **下标陷阱**:`Temp_track::X` 是 4 维 `[x, vx, y, vy]`(y 在 index 2),而 `Trust_Track::X` 是 6 维 `[x, vx, ax, y, vy, ay]`(y 在 index 3)。两套结构混用下标必错。
### 2.2 容器语义
@@ -112,7 +115,7 @@ track_init.track_init_process_logic(&point_recv, &trust_track, &temp_track,
| `Trust_track_num_Output` | 输出 | 累计行数(track_asso 输出 N 行后,track_init 在其后继续追加) |
| `Work_Parameter` | 输入 | 运行时雷达参数(`RadarPara`) |
> **输出行数核算**:`track_asso` 先为当圈全部可靠航迹写 N 行;`track_die` 随后删除 D 条(已写出的行与计数不变);起始流程每次起批前判 `trust_track->size() < MAX_TRACK_INDEX(500)`,故最多再追加 `499 − N + D` 行,**合计最多 499 + D 行**。正常情况下 D 很小,远小于 500;但理论上当 D ≥ 2 且当圈起批数量恰好占满配额(需数百条同时起批)时,行号会超过第一维容量 500,存在越界写入风险(极端场景,见 15.14)。第二维 `[j]`(j < L ≤ 10)始终安全。代价是可靠航迹数达 500 时不再允许新起批。
> **输出行数核算**:`track_asso` 先为当圈**有更新**的可靠航迹写 N 行(输出循环判 `Track_Update_Flag==1` 并清零,即只含关联到实点或成功外推的航迹);`track_die` 随后删除 D 条(已写出的行与计数不变);起始流程每次起批前判 `trust_track->size() < MAX_TRACK_INDEX(500)`,故最多再追加 `499 − N + D` 行,**合计最多 499 + D 行**。正常情况下 D 很小,远小于 500;但理论上当 D ≥ 2 且当圈起批数量恰好占满配额(需数百条同时起批)时,行号会超过第一维容量 500,存在越界写入风险(极端场景,见 15.14)。第二维 `[j]`(j < L ≤ 10)始终安全。代价是可靠航迹数达 500 时不再允许新起批。
---
@@ -356,10 +359,11 @@ for (...) (*point_recv).push_back(point_process[i]); // 回写未被使用的
| 字段 | 取值 |
|---|---|
| `r / azi / height / Amp / vr / snr / RCS` | 点迹对应量(`azi` 为弧度) |
| `r / azi / height / amplitude / vr / SNR / RCS` | 点迹对应量(`azi` 为弧度) |
| `pitch_num / speed_dim / range_dim` | 透传 |
| `X[0] = Range·cos(Azimuth)`、`X[1]=0`、`X[2] = Range·sin(Azimuth)`、`X[3]=0` | 位置赋值,速度置 0(**注意 4 维布局**) |
| `T`、`GNSS_time` | 点迹时间 |
| `CPI_time`、`GNSS_time` | 点迹时间 |
| `temp_track_id = temp_track_id++`、`point_id`、`circle_id` | 取自该点迹(记录链路标识,c3c7e66 新增) |
| `asso_flag = 0`、`buff_round = 1` | 生命周期起点 |
| `d` | 未赋值 → 0 |
@@ -440,14 +444,15 @@ Kalman.kalman_filter_init_3dots(Z0, Z1, Z2, T1, T2, X, P); // 6 维 [x,vx,ax,y
| `X1/X2/X3 = X`、`P1/P2/P3 = P` | 三个 IMM 子模型同状态、同协方差初始化 |
| `u[0..2] = 0.3333` | 模型概率均分 |
| `Track_Index` | `track_ind_get()` 分配的航迹号 |
| `T_track` / `GNSS_time` / `Height` | 取**末点**的对应值 |
| `T_track` / `GNSS_time` / `Height` | 取**末点**的对应值(`CPI_time` 字段) |
| `Amplitude / snr_point / RCS / pitch_num / speed_dim / range_dim` | 末点透传 |
| `track_id / point_id / circle_id` | 取末点的 `temp_track_id / point_id / circle_id`(记录链路标识,b227ae1 新增) |
| `point_flag = 1` | 实点 |
| `Track_Mode = 0` | TWS |
| `Target_Type = UNCONF_TARGET(0)` | 未确认目标 |
| `Extrapolate_round = 0` | 外推计数清零 |
| `approach_flag = -1` | 接近标志复位 |
| `manual_delete_flag / manual_tracking_flag / associate_point_number / point_type = 0` | 各类标志复位 |
| `manual_delete_flag / manual_tracking_flag / associate_point_number / point_type / primary_target_flag = 0` | 各类标志复位(`Trust_Track trust_track_tmp = {}` 零初始化,故新航迹的重点目标标记为 0) |
> L606-608 有一处**死代码**:`coor_trans::cart2polar(X[0], X[3], &r, &azi)`(把 `X[3]=vy` 当 y 用),计算结果 `r/azi` 从未被使用。
@@ -664,13 +669,13 @@ $$
| `Direction_Angle` | `atan2(vy, vx)`(→度, `[0,360)`) | 航向角 |
| `x / v_x / y / v_y` | 点的 `X[0..3]` | 注意 4 维布局 |
| `range_point / azi_point / elev_point / vr_point` | 点的原始测量 | `point_type=0` |
| `Amplitude / track_snr / track_rcs` | 点的 `Amp / snr / RCS` | |
| `Amplitude / track_snr / track_rcs` | 点的 `amplitude / SNR / RCS` | |
| `pitch_num / speed_dim / range_dim` | 点透传 | |
| `track_time` | `T/1000`(秒) | |
| `track_time` | `CPI_time/1000`(秒) | |
| `GNSS_time` | 点透传 | |
| `Flag_Point` | 恒为 **1**(实点) | |
| `Track_Mode` | 恒为 **0**(TWS) | |
| **未赋值** | `Flag_Direction`、`x_Predict/y_Predict/z_Predict`、`Range_Predict/Azimuth_Predict/Elevation_Predict`、`Target_Type`、`prf_point`、`v_z` | DLL 不清零该数组,宿主读到的是其自身缓存的旧值/未初始化值。注意 `Flag_Direction` 只在关联输出路径赋值(`track_asso.cpp` / `track_asso_tas.cpp`,f249fdc:`vr_point > 0 ? 1 : 0`),起批输出不写该字段 |
| **未赋值** | `Flag_Direction`、`x_Predict/y_Predict/z_Predict`、`Range_Predict/Azimuth_Predict/Elevation_Predict`、`Target_Type`、`prf_point`、`v_z`、`amp_asv_point`、`prn_fftn_point` | DLL 不清零该数组,宿主读到的是其自身缓存的旧值/未初始化值。注意 `Flag_Direction` 只在关联输出路径赋值(`track_asso.cpp` L129 / `track_asso_tas.cpp` L93),起批输出不写该字段。该字段的判据已由 f249fdc 的 `vr_point > 0 ? 1 : 0`(实测径向速度,受多普勒速度模糊影响符号可能取反)改为 fc49707 + 42b598e 的**航向-位置夹角法**:`Direction_Angle = atan2(X[4], X[1])`(弧度转度后同时输出到 `Direction_Angle`),`x`、`y` 为经 `Work_Parameter.Sys_delay` 补偿后的位置、`r` 为其模,`acos((-x·cos(Direction_Angle) - y·sin(Direction_Angle))/r) < π/2` 判为靠近(1)、否则远离(0),即滤波速度方向与"雷达→目标"位置方向夹角超 90° 判为接近。另 `amp_asv_point` / `prn_fftn_point` 属 e8c56f9 新增,`data_preprocess()` **现已**把 `DataRev.amp_asv / prn_fftn` 搬入 `PointRecv`(TWS 与 TAS 分支均赋值,并随 `RdpRecPoint` 落库),因此 TWS 关联/输出路径的该字段已有有效来源;TAS 关联与输出路径(`track_asso_tas.cpp`)仍未涉及这两个字段,TAS 输出行中它们保持宿主原值 |
---
+19
View File
@@ -0,0 +1,19 @@
# 雷达数据处理项目修改说明
## 概述
新增一个数据记录调试功能,将各类结构体数据写入到数据库中,供后续查询分析。
## 具体需求
* 本次只设计功能框架,具体存储哪些数据不在此次考虑范围内;
* 数据库使用SQLite;
* 使用ormpp库实现结构体到数据表的隐射,项目地址:https://github.com/qicosmos/ormpp;
* 数据记录功能在release版可以屏蔽,不做任何记录,避免交付版本的性能损失;
* 数据分类记录,所有数据存储到一个db文件中,不同的数据存储到不同的表中;
* 使用宏或其他方式,在代码中添加记录点,将指定数据写入数据库;
* 能够快速添加新的结构体数据,便于后续增加需要记录的数据;
* 作为一个调试功能,不必使用异步写入;
## 其他
* 如果有任何不明确或者有问题的地方,可以直接向我提问;
+201
View File
@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+35
View File
@@ -0,0 +1,35 @@
# 第三方库来源说明(ormpp / iguana / SQLite)
本目录为**按上游原样内置**的第三方源码(保留上游字节,含换行符,不做编码/格式转换),与源码目录下内置的 Eigen 3.3.7 同类处理:仓库自带依赖,离线可配置、可交付。
| 内容 | 本目录位置 | 上游 | 版本 / 标识 |
|---|---|---|---|
| ormpp(Modern C++ ORM 头文件库) | `ormpp/*.hpp` | <https://github.com/qicosmos/ormpp> | commit `acf23989f8e40f2c8671eaf209d014164f2d6319`(master,2026-08-29);`ormpp/version.hpp` = 0.2.0 |
| iguana(编译期反射,随 ormpp 内置) | `iguana/**` | 随 ormpp 内置;独立仓库 <https://github.com/qicosmos/iguana> | `iguana/version.hpp` = 1.0.5 |
| SQLite amalgamation | `thirdparty/sqlite3/{sqlite3.c,sqlite3.h,sqlite3ext.h}` | 随 ormpp 内置;<https://sqlite.org> | SQLite 3.49.1(`sqlite3.h` 的 `SQLITE_VERSION`),公共领域(Public Domain) |
| frozen(iguana 的 constexpr 容器依赖,随 ormpp 内置) | `frozen/**` | 随 ormpp 内置;独立仓库 <https://github.com/serge-sans-paille/frozen> | 18 个文件 / 86,136 字节(`iguana/field_reflection.hpp` 需要 `frozen/string.h`、`frozen/unordered_map.h`) |
| 许可证 | `LICENSE`、`iguana/LICENSE` | — | 均为 Apache License 2.0;与上游 git blob 逐字节一致(`261eeb9e…` / `8dada3ed…`) |
## 复现步骤(重新获取/升级时执行)
```bat
git -c core.autocrlf=false clone --depth 1 https://github.com/qicosmos/ormpp.git <tmp-ormpp>
git -c core.autocrlf=false clone --depth 1 https://github.com/qicosmos/iguana.git <tmp-iguana>
:: 仅拷贝以下内容到本目录(其余 tests/example/cfg/docs/lang/shell.c 等均不需要):
:: <tmp-ormpp>\ormpp\*.hpp + ormpp\pg_types.h -> third_party\ormpp\ormpp\
:: <tmp-ormpp>\iguana\ -> third_party\ormpp\iguana\
:: <tmp-ormpp>\frozen\ -> third_party\ormpp\frozen\ (iguana 依赖)
:: <tmp-ormpp>\thirdparty\sqlite3\sqlite3.c|sqlite3.h|sqlite3ext.h -> third_party\ormpp\thirdparty\sqlite3\
:: <tmp-ormpp>\LICENSE -> third_party\ormpp\LICENSE
:: <tmp-iguana>\LICENSE -> third_party\ormpp\iguana\LICENSE
```
注意:本机全局 `core.autocrlf=true` 会把源码转成 CRLF(`sqlite3.c` 由 9,461,193 字节变为 9,728,476 字节),因此克隆时必须显式 `-c core.autocrlf=false`。校验方式:与上游 blob 大小比对(`sqlite3.c` = 9,461,193、`dbng.hpp` = 11,681、`iguana/common.hpp` = 45,850)。
## 使用方式与约束
- **只被**可选目标 `rdp_record` 使用(见根目录 `CMakeLists.txt` 的 `RDP_DATA_RECORD` 选项);该目标以 **C++20** 编译(ormpp 的 `cmake/build.cmake` 强制 `CMAKE_CXX_STANDARD 20`),**雷达处理代码仍为 C++17**。
- 包含路径按上游约定给全(4 个):`third_party/ormpp`(`iguana/...`)、`third_party/ormpp/iguana`、`third_party/ormpp/ormpp`(`dbng.hpp`、`sqlite.hpp`)、`third_party/ormpp/thirdparty/sqlite3`(`sqlite3.h`)。
- 告警处理:`sqlite3.c` 以 `/w` 单独编译;首个包含第三方头的本项目文件(`data_process_class_dll/rdp_record.cpp`)用 `#pragma warning(push, 0)` / `(pop)` 隔离模板告警——这两处是 README 4.5「0 告警」的唯一例外,范围仅限本目录代码。
- **升级方式**:整目录替换为新的上游快照,同步更新本文件的 commit / 版本号 / blob 校验值,然后必须重新验证:`-DRDP_DATA_RECORD=ON` 构建通过(0 告警)+ 记录功能写入校验(见 `docs/data_record_debug.md`)。
+12
View File
@@ -0,0 +1,12 @@
target_sources(frozen-headers INTERFACE
"${prefix}/frozen/algorithm.h"
"${prefix}/frozen/map.h"
"${prefix}/frozen/random.h"
"${prefix}/frozen/set.h"
"${prefix}/frozen/string.h"
"${prefix}/frozen/unordered_map.h"
"${prefix}/frozen/unordered_set.h"
"${prefix}/frozen/bits/algorithms.h"
"${prefix}/frozen/bits/basic_types.h"
"${prefix}/frozen/bits/elsa.h"
"${prefix}/frozen/bits/pmh.h")
+203
View File
@@ -0,0 +1,203 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef FROZEN_LETITGO_ALGORITHM_H
#define FROZEN_LETITGO_ALGORITHM_H
#include "frozen/bits/basic_types.h"
#include "frozen/bits/version.h"
#include "frozen/string.h"
namespace frozen {
// 'search' implementation if C++17 is not available
// https://en.cppreference.com/w/cpp/algorithm/search
template <class ForwardIterator, class Searcher>
ForwardIterator search(ForwardIterator first, ForwardIterator last,
const Searcher &searcher) {
return searcher(first, last).first;
}
// text book implementation from
// https://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm
template <std::size_t size>
class knuth_morris_pratt_searcher {
bits::carray<std::ptrdiff_t, size> step_;
bits::carray<char, size> needle_;
static constexpr bits::carray<std::ptrdiff_t, size> build_kmp_cache(
char const (&needle)[size + 1]) {
std::ptrdiff_t cnd = 0;
bits::carray<std::ptrdiff_t, size> cache;
cache.fill(-1);
for (std::size_t pos = 1; pos < size; ++pos) {
if (needle[pos] == needle[cnd]) {
cache[pos] = cache[cnd];
cnd += 1;
}
else {
cache[pos] = cnd;
cnd = cache[cnd];
while (cnd >= 0 && needle[pos] != needle[cnd]) cnd = cache[cnd];
cnd += 1;
}
}
return cache;
}
public:
constexpr knuth_morris_pratt_searcher(char const (&needle)[size + 1])
: step_{build_kmp_cache(needle)}, needle_(needle) {}
template <class ForwardIterator>
constexpr std::pair<ForwardIterator, ForwardIterator> operator()(
ForwardIterator first, ForwardIterator last) const {
std::size_t i = 0;
ForwardIterator iter = first;
while (iter != last) {
if (needle_[i] == *iter) {
if (i == (size - 1))
return {iter - i, iter - i + size};
++i;
++iter;
}
else {
if (step_[i] > -1) {
i = step_[i];
}
else {
++iter;
i = 0;
}
}
}
return {last, last};
}
};
template <std::size_t N>
constexpr knuth_morris_pratt_searcher<N - 1> make_knuth_morris_pratt_searcher(
char const (&needle)[N]) {
return {needle};
}
// text book implementation from
// https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore%E2%80%93Horspool_algorithm
template <std::size_t size>
class boyer_moore_searcher {
using skip_table_type = bits::carray<std::ptrdiff_t, sizeof(char) << 8>;
using suffix_table_type = bits::carray<std::ptrdiff_t, size>;
skip_table_type skip_table_;
suffix_table_type suffix_table_;
bits::carray<char, size> needle_;
constexpr auto build_skip_table(char const (&needle)[size + 1]) {
skip_table_type skip_table;
skip_table.fill(size);
for (std::size_t i = 0; i < size - 1; ++i) skip_table[needle[i]] -= i + 1;
return skip_table;
}
constexpr bool is_prefix(char const (&needle)[size + 1], std::size_t pos) {
std::size_t suffixlen = size - pos;
for (std::size_t i = 0; i < suffixlen; i++) {
if (needle[i] != needle[pos + i])
return false;
}
return true;
}
constexpr std::size_t suffix_length(char const (&needle)[size + 1],
std::size_t pos) {
// increment suffix length slen to the first mismatch or beginning
// of the word
for (std::size_t slen = 0; slen < pos; slen++)
if (needle[pos - slen] != needle[size - 1 - slen])
return slen;
return pos;
}
constexpr auto build_suffix_table(char const (&needle)[size + 1]) {
suffix_table_type suffix;
std::ptrdiff_t last_prefix_index = size - 1;
// first loop
for (std::ptrdiff_t p = size - 1; p >= 0; p--) {
if (is_prefix(needle, p + 1))
last_prefix_index = p + 1;
suffix[p] = last_prefix_index + (size - 1 - p);
}
// second loop
for (std::size_t p = 0; p < size - 1; p++) {
auto slen = suffix_length(needle, p);
if (needle[p - slen] != needle[size - 1 - slen])
suffix[size - 1 - slen] = size - 1 - p + slen;
}
return suffix;
}
public:
constexpr boyer_moore_searcher(char const (&needle)[size + 1])
: skip_table_{build_skip_table(needle)},
suffix_table_{build_suffix_table(needle)},
needle_(needle) {}
template <class ForwardIterator>
constexpr std::pair<ForwardIterator, ForwardIterator> operator()(
ForwardIterator first, ForwardIterator last) const {
if (size == 0)
return {first, first + size};
ForwardIterator iter = first + size - 1;
while (iter < last) {
std::ptrdiff_t j = size - 1;
while (j > 0 && (*iter == needle_[j])) {
--iter;
--j;
}
if (*iter == needle_[0])
return {iter, iter + size};
iter += std::max(skip_table_[*iter], suffix_table_[j]);
}
return {last, last + size};
}
};
template <std::size_t N>
constexpr boyer_moore_searcher<N - 1> make_boyer_moore_searcher(
char const (&needle)[N]) {
return {needle};
}
} // namespace frozen
#endif
+240
View File
@@ -0,0 +1,240 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef FROZEN_LETITGO_BITS_ALGORITHMS_H
#define FROZEN_LETITGO_BITS_ALGORITHMS_H
#include <limits>
#include <tuple>
#include "frozen/bits/basic_types.h"
namespace frozen {
namespace bits {
auto constexpr next_highest_power_of_two(std::size_t v) {
// https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
constexpr auto trip_count = std::numeric_limits<decltype(v)>::digits;
v--;
for (std::size_t i = 1; i < trip_count; i <<= 1) v |= v >> i;
v++;
return v;
}
template <class T>
auto constexpr log(T v) {
std::size_t n = 0;
while (v > 1) {
n += 1;
v >>= 1;
}
return n;
}
constexpr std::size_t bit_weight(std::size_t n) {
return (n <= 8 * sizeof(unsigned int)) + (n <= 8 * sizeof(unsigned long)) +
(n <= 8 * sizeof(unsigned long long)) + (n <= 128);
}
unsigned int select_uint_least(std::integral_constant<std::size_t, 4>);
unsigned long select_uint_least(std::integral_constant<std::size_t, 3>);
unsigned long long select_uint_least(std::integral_constant<std::size_t, 2>);
template <std::size_t N>
unsigned long long select_uint_least(std::integral_constant<std::size_t, N>) {
static_assert(N < 2, "unsupported type size");
return {};
}
template <std::size_t N>
using select_uint_least_t = decltype(select_uint_least(
std::integral_constant<std::size_t, bit_weight(N)>()));
template <typename Iter, typename Compare>
constexpr auto min_element(Iter begin, const Iter end, Compare const &compare) {
auto result = begin;
while (begin != end) {
if (compare(*begin, *result)) {
result = begin;
}
++begin;
}
return result;
}
template <class T>
constexpr void cswap(T &a, T &b) {
auto tmp = a;
a = b;
b = tmp;
}
template <class T, class U>
constexpr void cswap(std::pair<T, U> &a, std::pair<T, U> &b) {
cswap(a.first, b.first);
cswap(a.second, b.second);
}
template <class... Tys, std::size_t... Is>
constexpr void cswap(std::tuple<Tys...> &a, std::tuple<Tys...> &b,
std::index_sequence<Is...>) {
using swallow = int[];
(void)swallow{(cswap(std::get<Is>(a), std::get<Is>(b)), 0)...};
}
template <class... Tys>
constexpr void cswap(std::tuple<Tys...> &a, std::tuple<Tys...> &b) {
cswap(a, b, std::make_index_sequence<sizeof...(Tys)>());
}
template <typename Iterator, class Compare>
constexpr Iterator partition(Iterator left, Iterator right,
Compare const &compare) {
auto pivot = left + (right - left) / 2;
auto value = *pivot;
cswap(*right, *pivot);
for (auto it = left; 0 < right - it; ++it) {
if (compare(*it, value)) {
cswap(*it, *left);
left++;
}
}
cswap(*right, *left);
return left;
}
template <typename Iterator, class Compare>
constexpr void quicksort(Iterator left, Iterator right,
Compare const &compare) {
while (0 < right - left) {
auto new_pivot = bits::partition(left, right, compare);
quicksort(left, new_pivot, compare);
left = new_pivot + 1;
}
}
template <typename T, std::size_t N, class Compare>
constexpr bits::carray<T, N> quicksort(bits::carray<T, N> const &array,
Compare const &compare) {
bits::carray<T, N> res = array;
quicksort(res.begin(), res.end() - 1, compare);
return res;
}
template <class T, class Compare>
struct LowerBound {
T const &value_;
Compare const &compare_;
constexpr LowerBound(T const &value, Compare const &compare)
: value_(value), compare_(compare) {}
template <class ForwardIt>
inline constexpr ForwardIt doit_fast(ForwardIt first,
std::integral_constant<std::size_t, 0>) {
return first;
}
template <class ForwardIt, std::size_t N>
inline constexpr ForwardIt doit_fast(ForwardIt first,
std::integral_constant<std::size_t, N>) {
auto constexpr step = N / 2;
static_assert(N / 2 == N - N / 2 - 1, "power of two minus 1");
auto it = first + step;
auto next_it = compare_(*it, value_) ? it + 1 : first;
return doit_fast(next_it, std::integral_constant<std::size_t, N / 2>{});
}
template <class ForwardIt, std::size_t N>
inline constexpr ForwardIt doitfirst(ForwardIt first,
std::integral_constant<std::size_t, N>,
std::integral_constant<bool, true>) {
return doit_fast(first, std::integral_constant<std::size_t, N>{});
}
template <class ForwardIt, std::size_t N>
inline constexpr ForwardIt doitfirst(ForwardIt first,
std::integral_constant<std::size_t, N>,
std::integral_constant<bool, false>) {
auto constexpr next_power = next_highest_power_of_two(N);
auto constexpr next_start = next_power / 2 - 1;
auto it = first + next_start;
if (compare_(*it, value_)) {
auto constexpr next = N - next_start - 1;
return doitfirst(
it + 1, std::integral_constant<std::size_t, next>{},
std::integral_constant<bool, next_highest_power_of_two(next) - 1 ==
next>{});
}
else
return doit_fast(first,
std::integral_constant<std::size_t, next_start>{});
}
template <class ForwardIt>
inline constexpr ForwardIt doitfirst(ForwardIt first,
std::integral_constant<std::size_t, 1>,
std::integral_constant<bool, false>) {
return doit_fast(first, std::integral_constant<std::size_t, 1>{});
}
};
template <std::size_t N, class ForwardIt, class T, class Compare>
constexpr ForwardIt lower_bound(ForwardIt first, const T &value,
Compare const &compare) {
return LowerBound<T, Compare>{value, compare}.doitfirst(
first, std::integral_constant<std::size_t, N>{},
std::integral_constant<bool, next_highest_power_of_two(N) - 1 == N>{});
}
template <std::size_t N, class Compare, class ForwardIt, class T>
constexpr bool binary_search(ForwardIt first, const T &value,
Compare const &compare) {
ForwardIt where = lower_bound<N>(first, value, compare);
return (!(where == first + N) && !(compare(value, *where)));
}
template <class InputIt1, class InputIt2>
constexpr bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2) {
for (; first1 != last1; ++first1, ++first2) {
if (!(*first1 == *first2)) {
return false;
}
}
return true;
}
template <class InputIt1, class InputIt2>
constexpr bool lexicographical_compare(InputIt1 first1, InputIt1 last1,
InputIt2 first2, InputIt2 last2) {
for (; (first1 != last1) && (first2 != last2); ++first1, ++first2) {
if (*first1 < *first2)
return true;
if (*first2 < *first1)
return false;
}
return (first1 == last1) && (first2 != last2);
}
} // namespace bits
} // namespace frozen
#endif
+224
View File
@@ -0,0 +1,224 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef FROZEN_LETITGO_BASIC_TYPES_H
#define FROZEN_LETITGO_BASIC_TYPES_H
#include <array>
#include <iterator>
#include <string>
#include <utility>
#include "frozen/bits/exceptions.h"
namespace frozen {
namespace bits {
// used as a fake argument for frozen::make_set and frozen::make_map in the case
// of N=0
struct ignored_arg {};
template <class T, std::size_t N>
class cvector {
T data[N] = {}; // zero-initialization for scalar type T, default-initialized
// otherwise
std::size_t dsize = 0;
public:
// Container typdefs
using value_type = T;
using reference = value_type &;
using const_reference = const value_type &;
using pointer = value_type *;
using const_pointer = const value_type *;
using iterator = pointer;
using const_iterator = const_pointer;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
// Constructors
constexpr cvector(void) = default;
constexpr cvector(size_type count, const T &value) : dsize(count) {
for (std::size_t i = 0; i < N; ++i) data[i] = value;
}
// Iterators
constexpr iterator begin() noexcept { return data; }
constexpr iterator end() noexcept { return data + dsize; }
// Capacity
constexpr size_type size() const { return dsize; }
// Element access
constexpr reference operator[](std::size_t index) { return data[index]; }
constexpr const_reference operator[](std::size_t index) const {
return data[index];
}
constexpr reference back() { return data[dsize - 1]; }
constexpr const_reference back() const { return data[dsize - 1]; }
// Modifiers
constexpr void push_back(const T &a) { data[dsize++] = a; }
constexpr void push_back(T &&a) { data[dsize++] = std::move(a); }
constexpr void pop_back() { --dsize; }
constexpr void clear() { dsize = 0; }
};
template <class T, std::size_t N>
class carray {
T data_[N] = {}; // zero-initialization for scalar type T,
// default-initialized otherwise
template <std::size_t M, std::size_t... I>
constexpr carray(T const (&init)[M], std::index_sequence<I...>)
: data_{init[I]...} {}
template <class Iter, std::size_t... I>
constexpr carray(Iter iter, std::index_sequence<I...>)
: data_{((void)I, *iter++)...} {}
public:
// Container typdefs
using value_type = T;
using reference = value_type &;
using const_reference = const value_type &;
using pointer = value_type *;
using const_pointer = const value_type *;
using iterator = pointer;
using const_iterator = const_pointer;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
// Constructors
constexpr carray(void) = default;
template <std::size_t M>
constexpr carray(T const (&init)[M])
: carray(init, std::make_index_sequence<N>()) {
static_assert(M >= N, "Cannot initialize a carray with an smaller array");
}
template <std::size_t M>
constexpr carray(std::array<T, M> const &init)
: carray(&init[0], std::make_index_sequence<N>()) {
static_assert(M >= N, "Cannot initialize a carray with an smaller array");
}
constexpr carray(std::initializer_list<T> init)
: carray(init.begin(), std::make_index_sequence<N>()) {
// clang & gcc doesn't recognize init.size() as a constexpr
// static_assert(init.size() >= N, "Cannot initialize a carray with an
// smaller initializer list");
}
// Iterators
constexpr iterator begin() noexcept { return data_; }
constexpr const_iterator begin() const noexcept { return data_; }
constexpr const_iterator cbegin() const noexcept { return data_; }
constexpr iterator end() noexcept { return data_ + N; }
constexpr const_iterator end() const noexcept { return data_ + N; }
constexpr const_iterator cend() const noexcept { return data_ + N; }
constexpr reverse_iterator rbegin() noexcept {
return reverse_iterator(end());
}
constexpr const_reverse_iterator rbegin() const noexcept {
return const_reverse_iterator(end());
}
constexpr const_reverse_iterator crbegin() const noexcept {
return const_reverse_iterator(end());
}
constexpr reverse_iterator rend() noexcept {
return reverse_iterator(begin());
}
constexpr const_reverse_iterator rend() const noexcept {
return const_reverse_iterator(begin());
}
constexpr const_reverse_iterator crend() const noexcept {
return const_reverse_iterator(begin());
}
// Capacity
constexpr size_type size() const { return N; }
constexpr size_type max_size() const { return N; }
// Element access
constexpr reference operator[](std::size_t index) { return data_[index]; }
constexpr const_reference operator[](std::size_t index) const {
return data_[index];
}
constexpr reference at(std::size_t index) {
if (index > N)
FROZEN_THROW_OR_ABORT(
std::out_of_range("Index (" + std::to_string(index) +
") out of bound (" + std::to_string(N) + ')'));
return data_[index];
}
constexpr const_reference at(std::size_t index) const {
if (index > N)
FROZEN_THROW_OR_ABORT(
std::out_of_range("Index (" + std::to_string(index) +
") out of bound (" + std::to_string(N) + ')'));
return data_[index];
}
constexpr reference front() { return data_[0]; }
constexpr const_reference front() const { return data_[0]; }
constexpr reference back() { return data_[N - 1]; }
constexpr const_reference back() const { return data_[N - 1]; }
constexpr value_type *data() noexcept { return data_; }
constexpr const value_type *data() const noexcept { return data_; }
// Modifiers
constexpr void fill(const value_type &val) {
for (std::size_t i = 0; i < N; ++i) data_[i] = val;
}
};
template <class T>
class carray<T, 0> {
public:
// Container typdefs
using value_type = T;
using reference = value_type &;
using const_reference = const value_type &;
using pointer = value_type *;
using const_pointer = const value_type *;
using iterator = pointer;
using const_iterator = const_pointer;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
// Constructors
constexpr carray(void) = default;
};
} // namespace bits
} // namespace frozen
#endif
+38
View File
@@ -0,0 +1,38 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef FROZEN_LETITGO_CONSTEXPR_ASSERT_H
#define FROZEN_LETITGO_CONSTEXPR_ASSERT_H
#include <cassert>
#ifdef _MSC_VER
// FIXME: find a way to implement that correctly for msvc
#define constexpr_assert(cond, msg)
#else
#define constexpr_assert(cond, msg) assert(cond &&msg);
#endif
#endif
+69
View File
@@ -0,0 +1,69 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef FROZEN_LETITGO_DEFINES_H
#define FROZEN_LETITGO_DEFINES_H
#if defined(_MSVC_LANG) && \
!(defined(__EDG__) && defined(__clang__)) // TRANSITION, VSO#273681
#define FROZEN_LETITGO_IS_MSVC
#endif
// Code taken from
// https://stackoverflow.com/questions/43639122/which-values-can-msvc-lang-have
#if defined(FROZEN_LETITGO_IS_MSVC)
#if _MSVC_LANG > 201402
#define FROZEN_LETITGO_HAS_CXX17 1
#else /* _MSVC_LANG > 201402 */
#define FROZEN_LETITGO_HAS_CXX17 0
#endif /* _MSVC_LANG > 201402 */
#else /* _MSVC_LANG etc. */
#if __cplusplus > 201402
#define FROZEN_LETITGO_HAS_CXX17 1
#else /* __cplusplus > 201402 */
#define FROZEN_LETITGO_HAS_CXX17 0
#endif /* __cplusplus > 201402 */
#endif /* _MSVC_LANG etc. */
// End if taken code
#if FROZEN_LETITGO_HAS_CXX17 == 1 && defined(FROZEN_LETITGO_IS_MSVC)
#define FROZEN_LETITGO_HAS_STRING_VIEW // We assume Visual Studio always has
// string_view in C++17
#else
#if FROZEN_LETITGO_HAS_CXX17 == 1 && __has_include(<string_view>)
#define FROZEN_LETITGO_HAS_STRING_VIEW
#endif
#endif
#ifdef __cpp_char8_t
#define FROZEN_LETITGO_HAS_CHAR8T
#endif
#if __cpp_deduction_guides >= 201703L
#define FROZEN_LETITGO_HAS_DEDUCTION_GUIDES
#endif
#if __cpp_lib_constexpr_string >= 201907L
#define FROZEN_LETITGO_HAS_CONSTEXPR_STRING
#endif
#endif // FROZEN_LETITGO_DEFINES_H
+60
View File
@@ -0,0 +1,60 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef FROZEN_LETITGO_ELSA_H
#define FROZEN_LETITGO_ELSA_H
#include <type_traits>
namespace frozen {
template <class T = void>
struct elsa {
static_assert(std::is_integral<T>::value || std::is_enum<T>::value,
"only supports integral types, specialize for other types");
constexpr std::size_t operator()(T const &value, std::size_t seed) const {
std::size_t key = seed ^ static_cast<std::size_t>(value);
key = (~key) + (key << 21); // key = (key << 21) - key - 1;
key = key ^ (key >> 24);
key = (key + (key << 3)) + (key << 8); // key * 265
key = key ^ (key >> 14);
key = (key + (key << 2)) + (key << 4); // key * 21
key = key ^ (key >> 28);
key = key + (key << 31);
return key;
}
};
template <>
struct elsa<void> {
template <class T>
constexpr std::size_t operator()(T const &value, std::size_t seed) const {
return elsa<T>{}(value, seed);
}
};
template <class T>
using anna = elsa<T>;
} // namespace frozen
#endif
+44
View File
@@ -0,0 +1,44 @@
#ifndef FROZEN_LETITGO_BITS_ELSA_STD_H
#define FROZEN_LETITGO_BITS_ELSA_STD_H
#include "elsa.h"
#include "hash_string.h"
#ifdef FROZEN_LETITGO_HAS_STRING_VIEW
#include <string_view>
#endif
#include <string>
namespace frozen {
#ifdef FROZEN_LETITGO_HAS_STRING_VIEW
template <typename CharT>
struct elsa<std::basic_string_view<CharT>> {
constexpr std::size_t operator()(
const std::basic_string_view<CharT> &value) const {
return hash_string(value);
}
constexpr std::size_t operator()(const std::basic_string_view<CharT> &value,
std::size_t seed) const {
return hash_string(value, seed);
}
};
#endif
template <typename CharT>
struct elsa<std::basic_string<CharT>> {
constexpr std::size_t operator()(
const std::basic_string<CharT> &value) const {
return hash_string(value);
}
constexpr std::size_t operator()(const std::basic_string<CharT> &value,
std::size_t seed) const {
return hash_string(value, seed);
}
};
} // namespace frozen
#endif // FROZEN_LETITGO_BITS_ELSA_STD_H
+40
View File
@@ -0,0 +1,40 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef FROZEN_LETITGO_EXCEPTIONS_H
#define FROZEN_LETITGO_EXCEPTIONS_H
#if defined(FROZEN_NO_EXCEPTIONS) || \
(defined(_MSC_VER) && !defined(_CPPUNWIND)) || \
(!defined(_MSC_VER) && !defined(__cpp_exceptions))
#include <cstdlib>
#define FROZEN_THROW_OR_ABORT(_) std::abort()
#else
#include <stdexcept>
#define FROZEN_THROW_OR_ABORT(err) throw err
#endif
#endif
+27
View File
@@ -0,0 +1,27 @@
#ifndef FROZEN_LETITGO_BITS_HASH_STRING_H
#define FROZEN_LETITGO_BITS_HASH_STRING_H
#include <cstddef>
namespace frozen {
template <typename String>
constexpr std::size_t hash_string(const String &value) {
std::size_t d = 5381;
for (const auto &c : value) d = d * 33 + static_cast<size_t>(c);
return d;
}
// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
// With the lowest bits removed, based on experimental setup.
template <typename String>
constexpr std::size_t hash_string(const String &value, std::size_t seed) {
std::size_t d = (0x811c9dc5 ^ seed) * static_cast<size_t>(0x01000193);
for (const auto &c : value)
d = (d ^ static_cast<size_t>(c)) * static_cast<size_t>(0x01000193);
return d >> 8;
}
} // namespace frozen
#endif // FROZEN_LETITGO_BITS_HASH_STRING_H
+259
View File
@@ -0,0 +1,259 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
// inspired from http://stevehanov.ca/blog/index.php?id=119
#ifndef FROZEN_LETITGO_PMH_H
#define FROZEN_LETITGO_PMH_H
#include <array>
#include <limits>
#include "frozen/bits/algorithms.h"
#include "frozen/bits/basic_types.h"
namespace frozen {
namespace bits {
// Function object for sorting buckets in decreasing order of size
struct bucket_size_compare {
template <typename B>
bool constexpr operator()(B const &b0, B const &b1) const {
return b0.size() > b1.size();
}
};
// Step One in pmh routine is to take all items and hash them into buckets,
// with some collisions. Then process those buckets further to build a perfect
// hash function.
// pmh_buckets represents the initial placement into buckets.
template <size_t M>
struct pmh_buckets {
// Step 0: Bucket max is 2 * sqrt M
// TODO: Come up with justification for this, should it not be O(log M)?
static constexpr auto bucket_max = 2 * (1u << (log(M) / 2));
using bucket_t = cvector<std::size_t, bucket_max>;
carray<bucket_t, M> buckets;
uint64_t seed;
// Represents a reference to a bucket. This is used because the buckets
// have to be sorted, but buckets are big, making it slower than sorting refs
struct bucket_ref {
unsigned hash;
const bucket_t *ptr;
// Forward some interface of bucket
using value_type = typename bucket_t::value_type;
using const_iterator = typename bucket_t::const_iterator;
constexpr auto size() const { return ptr->size(); }
constexpr const auto &operator[](std::size_t idx) const {
return (*ptr)[idx];
}
constexpr auto begin() const { return ptr->begin(); }
constexpr auto end() const { return ptr->end(); }
};
// Make a bucket_ref for each bucket
template <std::size_t... Is>
carray<bucket_ref, M> constexpr make_bucket_refs(
std::index_sequence<Is...>) const {
return {{bucket_ref{Is, &buckets[Is]}...}};
}
// Makes a bucket_ref for each bucket and sorts them by size
carray<bucket_ref, M> constexpr get_sorted_buckets() const {
carray<bucket_ref, M> result{
this->make_bucket_refs(std::make_index_sequence<M>())};
bits::quicksort(result.begin(), result.end() - 1, bucket_size_compare{});
return result;
}
};
template <size_t M, class Item, size_t N, class Hash, class Key, class PRG>
pmh_buckets<M> constexpr make_pmh_buckets(const carray<Item, N> &items,
Hash const &hash, Key const &key,
PRG &prg) {
using result_t = pmh_buckets<M>;
result_t result{};
bool rejected = false;
// Continue until all items are placed without exceeding bucket_max
while (1) {
for (auto &b : result.buckets) {
b.clear();
}
result.seed = prg();
rejected = false;
for (std::size_t i = 0; i < N; ++i) {
auto &bucket =
result.buckets[hash(key(items[i]), static_cast<size_t>(result.seed)) %
M];
if (bucket.size() >= result_t::bucket_max) {
rejected = true;
break;
}
bucket.push_back(i);
}
if (!rejected) {
return result;
}
}
}
// Check if an item appears in a cvector
template <class T, size_t N>
constexpr bool all_different_from(cvector<T, N> &data, T &a) {
for (std::size_t i = 0; i < data.size(); ++i)
if (data[i] == a)
return false;
return true;
}
// Represents either an index to a data item array, or a seed to be used with
// a hasher. Seed must have high bit of 1, value has high bit of zero.
struct seed_or_index {
using value_type = uint64_t;
private:
static constexpr value_type MINUS_ONE =
(std::numeric_limits<value_type>::max)();
static constexpr value_type HIGH_BIT = ~(MINUS_ONE >> 1);
value_type value_ = 0;
public:
constexpr value_type value() const { return value_; }
constexpr bool is_seed() const { return value_ & HIGH_BIT; }
constexpr seed_or_index(bool is_seed, value_type value)
: value_(is_seed ? (value | HIGH_BIT) : (value & ~HIGH_BIT)) {}
constexpr seed_or_index() = default;
constexpr seed_or_index(const seed_or_index &) = default;
constexpr seed_or_index &operator=(const seed_or_index &) = default;
};
// Represents the perfect hash function created by pmh algorithm
template <std::size_t M, class Hasher>
struct pmh_tables {
uint64_t first_seed_;
carray<seed_or_index, M> first_table_;
carray<std::size_t, M> second_table_;
Hasher hash_;
template <typename KeyType>
constexpr std::size_t lookup(const KeyType &key) const {
return lookup(key, hash_);
}
// Looks up a given key, to find its expected index in carray<Item, N>
// Always returns a valid index, must use KeyEqual test after to confirm.
template <typename KeyType, typename HasherType>
constexpr std::size_t lookup(const KeyType &key,
const HasherType &hasher) const {
auto const d =
first_table_[hasher(key, static_cast<size_t>(first_seed_)) % M];
if (!d.is_seed()) {
return static_cast<std::size_t>(d.value());
} // this is narrowing uint64 -> size_t but should be fine
else {
return second_table_[hasher(key, static_cast<std::size_t>(d.value())) %
M];
}
}
};
// Make pmh tables for given items, hash function, prg, etc.
template <std::size_t M, class Item, std::size_t N, class Hash, class Key,
class PRG>
pmh_tables<M, Hash> constexpr make_pmh_tables(const carray<Item, N> &items,
Hash const &hash, Key const &key,
PRG prg) {
// Step 1: Place all of the keys into buckets
auto step_one = make_pmh_buckets<M>(items, hash, key, prg);
// Step 2: Sort the buckets to process the ones with the most items first.
auto buckets = step_one.get_sorted_buckets();
// G becomes the first hash table in the resulting pmh function
carray<seed_or_index, M> G; // Default constructed to "index 0"
// H becomes the second hash table in the resulting pmh function
constexpr std::size_t UNUSED = (std::numeric_limits<std::size_t>::max)();
carray<std::size_t, M> H;
H.fill(UNUSED);
// Step 3: Map the items in buckets into hash tables.
for (const auto &bucket : buckets) {
auto const bsize = bucket.size();
if (bsize == 1) {
// Store index to the (single) item in G
// assert(bucket.hash == hash(key(items[bucket[0]]), step_one.seed) % M);
G[bucket.hash] = {false, static_cast<uint64_t>(bucket[0])};
}
else if (bsize > 1) {
// Repeatedly try different H of d until we find a hash function
// that places all items in the bucket into free slots
seed_or_index d{true, prg()};
cvector<std::size_t, decltype(step_one)::bucket_max> bucket_slots;
while (bucket_slots.size() < bsize) {
auto slot = hash(key(items[bucket[bucket_slots.size()]]),
static_cast<size_t>(d.value())) %
M;
if (H[slot] != UNUSED || !all_different_from(bucket_slots, slot)) {
bucket_slots.clear();
d = {true, prg()};
continue;
}
bucket_slots.push_back(slot);
}
// Put successful seed in G, and put indices to items in their slots
// assert(bucket.hash == hash(key(items[bucket[0]]), step_one.seed) % M);
G[bucket.hash] = d;
for (std::size_t i = 0; i < bsize; ++i) H[bucket_slots[i]] = bucket[i];
}
}
// Any unused entries in the H table have to get changed to zero.
// This is because hashing should not fail or return an out-of-bounds entry.
// A lookup fails after we apply user-supplied KeyEqual to the query and the
// key found by hashing. Sending such queries to zero cannot hurt.
for (std::size_t i = 0; i < M; ++i)
if (H[i] == UNUSED)
H[i] = 0;
return {step_one.seed, G, H, hash};
}
} // namespace bits
} // namespace frozen
#endif
+30
View File
@@ -0,0 +1,30 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef FROZEN_LETITGO_VERSION_H
#define FROZEN_LETITGO_VERSION_H
#define FROZEN_MAJOR_VERSION 1
#define FROZEN_MINOR_VERSION 1
#define FROZEN_PATCH_VERSION 1
#endif
+378
View File
@@ -0,0 +1,378 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef FROZEN_LETITGO_MAP_H
#define FROZEN_LETITGO_MAP_H
#include <utility>
#include "frozen/bits/algorithms.h"
#include "frozen/bits/basic_types.h"
#include "frozen/bits/constexpr_assert.h"
#include "frozen/bits/exceptions.h"
#include "frozen/bits/version.h"
namespace frozen {
namespace impl {
template <class Comparator>
class CompareKey {
Comparator const comparator_;
public:
constexpr CompareKey(Comparator const &comparator)
: comparator_(comparator) {}
template <class Key1, class Key2, class Value>
constexpr int operator()(std::pair<Key1, Value> const &self,
std::pair<Key2, Value> const &other) const {
return comparator_(std::get<0>(self), std::get<0>(other));
}
template <class Key1, class Key2, class Value>
constexpr int operator()(Key1 const &self_key,
std::pair<Key2, Value> const &other) const {
return comparator_(self_key, std::get<0>(other));
}
template <class Key1, class Key2, class Value>
constexpr int operator()(std::pair<Key1, Value> const &self,
Key2 const &other_key) const {
return comparator_(std::get<0>(self), other_key);
}
template <class Key1, class Key2>
constexpr int operator()(Key1 const &self_key, Key2 const &other_key) const {
return comparator_(self_key, other_key);
}
};
} // namespace impl
template <class Key, class Value, std::size_t N, class Compare = std::less<Key>>
class map {
using container_type = bits::carray<std::pair<Key, Value>, N>;
impl::CompareKey<Compare> less_than_;
container_type items_;
public:
using key_type = Key;
using mapped_type = Value;
using value_type = typename container_type::value_type;
using size_type = typename container_type::size_type;
using difference_type = typename container_type::difference_type;
using key_compare = decltype(less_than_);
using reference = typename container_type::reference;
using const_reference = typename container_type::const_reference;
using pointer = typename container_type::pointer;
using const_pointer = typename container_type::const_pointer;
using iterator = typename container_type::iterator;
using const_iterator = typename container_type::const_iterator;
using reverse_iterator = typename container_type::reverse_iterator;
using const_reverse_iterator =
typename container_type::const_reverse_iterator;
public:
/* constructors */
constexpr map(container_type items, Compare const &compare)
: less_than_{compare}, items_{bits::quicksort(items, less_than_)} {}
explicit constexpr map(container_type items) : map{items, Compare{}} {}
constexpr map(std::initializer_list<value_type> items, Compare const &compare)
: map{container_type{items}, compare} {
constexpr_assert(
items.size() == N,
"Inconsistent initializer_list size and type size argument");
}
constexpr map(std::initializer_list<value_type> items)
: map{items, Compare{}} {}
/* element access */
constexpr Value const &at(Key const &key) const {
return at_impl(*this, key);
}
constexpr Value &at(Key const &key) { return at_impl(*this, key); }
/* iterators */
constexpr iterator begin() { return items_.begin(); }
constexpr const_iterator begin() const { return items_.begin(); }
constexpr const_iterator cbegin() const { return items_.cbegin(); }
constexpr iterator end() { return items_.end(); }
constexpr const_iterator end() const { return items_.end(); }
constexpr const_iterator cend() const { return items_.cend(); }
constexpr reverse_iterator rbegin() { return items_.rbegin(); }
constexpr const_reverse_iterator rbegin() const { return items_.rbegin(); }
constexpr const_reverse_iterator crbegin() const { return items_.crbegin(); }
constexpr reverse_iterator rend() { return items_.rend(); }
constexpr const_reverse_iterator rend() const { return items_.rend(); }
constexpr const_reverse_iterator crend() const { return items_.crend(); }
/* capacity */
constexpr bool empty() const { return !N; }
constexpr size_type size() const { return N; }
constexpr size_type max_size() const { return N; }
/* lookup */
template <class KeyType>
constexpr std::size_t count(KeyType const &key) const {
return bits::binary_search<N>(items_.begin(), key, less_than_);
}
template <class KeyType>
constexpr const_iterator find(KeyType const &key) const {
return map::find_impl(*this, key);
}
template <class KeyType>
constexpr iterator find(KeyType const &key) {
return map::find_impl(*this, key);
}
template <class KeyType>
constexpr std::pair<const_iterator, const_iterator> equal_range(
KeyType const &key) const {
return equal_range_impl(*this, key);
}
template <class KeyType>
constexpr std::pair<iterator, iterator> equal_range(KeyType const &key) {
return equal_range_impl(*this, key);
}
template <class KeyType>
constexpr const_iterator lower_bound(KeyType const &key) const {
return lower_bound_impl(*this, key);
}
template <class KeyType>
constexpr iterator lower_bound(KeyType const &key) {
return lower_bound_impl(*this, key);
}
template <class KeyType>
constexpr const_iterator upper_bound(KeyType const &key) const {
return upper_bound_impl(*this, key);
}
template <class KeyType>
constexpr iterator upper_bound(KeyType const &key) {
return upper_bound_impl(*this, key);
}
/* observers */
constexpr const key_compare &key_comp() const { return less_than_; }
constexpr const key_compare &value_comp() const { return less_than_; }
private:
template <class This, class KeyType>
static inline constexpr auto &at_impl(This &&self, KeyType const &key) {
auto where = self.lower_bound(key);
if (where != self.end())
return where->second;
else
FROZEN_THROW_OR_ABORT(std::out_of_range("unknown key"));
}
template <class This, class KeyType>
static inline constexpr auto find_impl(This &&self, KeyType const &key) {
auto where = self.lower_bound(key);
if ((where != self.end()) && !self.less_than_(key, *where))
return where;
else
return self.end();
}
template <class This, class KeyType>
static inline constexpr auto equal_range_impl(This &&self,
KeyType const &key) {
auto lower = self.lower_bound(key);
using lower_t = decltype(lower);
if (lower == self.end())
return std::pair<lower_t, lower_t>{lower, lower};
else
return std::pair<lower_t, lower_t>{lower, lower + 1};
}
template <class This, class KeyType>
static inline constexpr auto lower_bound_impl(This &&self, KeyType const &key)
-> decltype(self.end()) {
auto where =
bits::lower_bound<N>(self.items_.begin(), key, self.less_than_);
if ((where != self.end()) && !self.less_than_(key, *where))
return where;
else
return self.end();
}
template <class This, class KeyType>
static inline constexpr auto upper_bound_impl(This &&self, KeyType const &key)
-> decltype(self.end()) {
auto where =
bits::lower_bound<N>(self.items_.begin(), key, self.less_than_);
if ((where != self.end()) && !self.less_than_(key, *where))
return where + 1;
else
return self.end();
}
};
template <class Key, class Value, class Compare>
class map<Key, Value, 0, Compare> {
using container_type = bits::carray<std::pair<Key, Value>, 0>;
impl::CompareKey<Compare> less_than_;
public:
using key_type = Key;
using mapped_type = Value;
using value_type = typename container_type::value_type;
using size_type = typename container_type::size_type;
using difference_type = typename container_type::difference_type;
using key_compare = decltype(less_than_);
using reference = typename container_type::reference;
using const_reference = typename container_type::const_reference;
using pointer = typename container_type::pointer;
using const_pointer = typename container_type::const_pointer;
using iterator = pointer;
using const_iterator = const_pointer;
using reverse_iterator = pointer;
using const_reverse_iterator = const_pointer;
public:
/* constructors */
constexpr map(const map &other) = default;
constexpr map(std::initializer_list<value_type>, Compare const &compare)
: less_than_{compare} {}
constexpr map(std::initializer_list<value_type> items)
: map{items, Compare{}} {}
/* element access */
template <class KeyType>
constexpr mapped_type at(KeyType const &) const {
FROZEN_THROW_OR_ABORT(std::out_of_range("invalid key"));
}
template <class KeyType>
constexpr mapped_type at(KeyType const &) {
FROZEN_THROW_OR_ABORT(std::out_of_range("invalid key"));
}
/* iterators */
constexpr iterator begin() { return nullptr; }
constexpr const_iterator begin() const { return nullptr; }
constexpr const_iterator cbegin() const { return nullptr; }
constexpr iterator end() { return nullptr; }
constexpr const_iterator end() const { return nullptr; }
constexpr const_iterator cend() const { return nullptr; }
constexpr reverse_iterator rbegin() { return nullptr; }
constexpr const_reverse_iterator rbegin() const { return nullptr; }
constexpr const_reverse_iterator crbegin() const { return nullptr; }
constexpr reverse_iterator rend() { return nullptr; }
constexpr const_reverse_iterator rend() const { return nullptr; }
constexpr const_reverse_iterator crend() const { return nullptr; }
/* capacity */
constexpr bool empty() const { return true; }
constexpr size_type size() const { return 0; }
constexpr size_type max_size() const { return 0; }
/* lookup */
template <class KeyType>
constexpr std::size_t count(KeyType const &) const {
return 0;
}
template <class KeyType>
constexpr const_iterator find(KeyType const &) const {
return end();
}
template <class KeyType>
constexpr iterator find(KeyType const &) {
return end();
}
template <class KeyType>
constexpr std::pair<const_iterator, const_iterator> equal_range(
KeyType const &) const {
return {end(), end()};
}
template <class KeyType>
constexpr std::pair<iterator, iterator> equal_range(KeyType const &) {
return {end(), end()};
}
template <class KeyType>
constexpr const_iterator lower_bound(KeyType const &) const {
return end();
}
template <class KeyType>
constexpr iterator lower_bound(KeyType const &) {
return end();
}
template <class KeyType>
constexpr const_iterator upper_bound(KeyType const &) const {
return end();
}
template <class KeyType>
constexpr iterator upper_bound(KeyType const &) {
return end();
}
/* observers */
constexpr key_compare const &key_comp() const { return less_than_; }
constexpr key_compare const &value_comp() const { return less_than_; }
};
template <typename T, typename U, typename Compare = std::less<T>>
constexpr auto make_map(
bits::ignored_arg =
{} /* for consistency with the initializer below for N = 0*/) {
return map<T, U, 0, Compare>{};
}
template <typename T, typename U, std::size_t N>
constexpr auto make_map(std::pair<T, U> const (&items)[N]) {
return map<T, U, N>{items};
}
template <typename T, typename U, std::size_t N>
constexpr auto make_map(std::array<std::pair<T, U>, N> const &items) {
return map<T, U, N>{items};
}
template <typename T, typename U, typename Compare, std::size_t N>
constexpr auto make_map(std::pair<T, U> const (&items)[N],
Compare const &compare = Compare{}) {
return map<T, U, N, Compare>{items, compare};
}
template <typename T, typename U, typename Compare, std::size_t N>
constexpr auto make_map(std::array<std::pair<T, U>, N> const &items,
Compare const &compare = Compare{}) {
return map<T, U, N, Compare>{items, compare};
}
} // namespace frozen
#endif
+97
View File
@@ -0,0 +1,97 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef FROZEN_LETITGO_RANDOM_H
#define FROZEN_LETITGO_RANDOM_H
#include <cstdint>
#include <type_traits>
#include "frozen/bits/algorithms.h"
#include "frozen/bits/version.h"
namespace frozen {
template <class UIntType, UIntType a, UIntType c, UIntType m>
class linear_congruential_engine {
static_assert(std::is_unsigned<UIntType>::value,
"UIntType must be an unsigned integral type");
template <class T>
static constexpr UIntType modulo(T val, std::integral_constant<UIntType, 0>) {
return static_cast<UIntType>(val);
}
template <class T, UIntType M>
static constexpr UIntType modulo(T val, std::integral_constant<UIntType, M>) {
// the static cast below may end up doing a truncation
return static_cast<UIntType>(val % M);
}
public:
using result_type = UIntType;
static constexpr result_type multiplier = a;
static constexpr result_type increment = c;
static constexpr result_type modulus = m;
static constexpr result_type default_seed = 1u;
linear_congruential_engine() = default;
constexpr linear_congruential_engine(result_type s) { seed(s); }
void seed(result_type s = default_seed) { state_ = s; }
constexpr result_type operator()() {
using uint_least_t =
bits::select_uint_least_t<bits::log(a) + bits::log(m) + 4>;
uint_least_t tmp =
static_cast<uint_least_t>(multiplier) * state_ + increment;
state_ = modulo(tmp, std::integral_constant<UIntType, modulus>());
return state_;
}
// constexpr void discard(unsigned long long n) {
// while (n--) operator()();
// }
// static constexpr result_type min() { return increment == 0u ? 1u : 0u; }
// static constexpr result_type max() { return modulus - 1u; }
friend constexpr bool operator==(linear_congruential_engine const &self,
linear_congruential_engine const &other) {
return self.state_ == other.state_;
}
friend constexpr bool operator!=(linear_congruential_engine const &self,
linear_congruential_engine const &other) {
return !(self == other);
}
private:
result_type state_ = default_seed;
};
using minstd_rand0 =
linear_congruential_engine<std::uint_fast32_t, 16807, 0, 2147483647>;
using minstd_rand =
linear_congruential_engine<std::uint_fast32_t, 48271, 0, 2147483647>;
// This generator is used by default in unordered frozen containers
using default_prg_t = minstd_rand;
} // namespace frozen
#endif
+283
View File
@@ -0,0 +1,283 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef FROZEN_SET_H
#define FROZEN_SET_H
#include <utility>
#include "frozen/bits/algorithms.h"
#include "frozen/bits/basic_types.h"
#include "frozen/bits/constexpr_assert.h"
#include "frozen/bits/defines.h"
#include "frozen/bits/version.h"
namespace frozen {
template <class Key, std::size_t N, class Compare = std::less<Key>>
class set {
using container_type = bits::carray<Key, N>;
Compare less_than_;
container_type keys_;
public:
/* container typedefs*/
using key_type = Key;
using value_type = Key;
using size_type = typename container_type::size_type;
using difference_type = typename container_type::size_type;
using key_compare = Compare;
using value_compare = Compare;
using reference = typename container_type::const_reference;
using const_reference = reference;
using pointer = typename container_type::const_pointer;
using const_pointer = pointer;
using iterator = typename container_type::const_iterator;
using reverse_iterator = typename container_type::const_reverse_iterator;
using const_iterator = iterator;
using const_reverse_iterator = reverse_iterator;
public:
/* constructors */
constexpr set(const set &other) = default;
constexpr set(container_type keys, Compare const &comp)
: less_than_{comp}, keys_(bits::quicksort(keys, less_than_)) {}
explicit constexpr set(container_type keys) : set{keys, Compare{}} {}
constexpr set(std::initializer_list<Key> keys, Compare const &comp)
: set{container_type{keys}, comp} {
constexpr_assert(
keys.size() == N,
"Inconsistent initializer_list size and type size argument");
}
constexpr set(std::initializer_list<Key> keys) : set{keys, Compare{}} {}
constexpr set &operator=(const set &other) = default;
/* capacity */
constexpr bool empty() const { return !N; }
constexpr size_type size() const { return N; }
constexpr size_type max_size() const { return N; }
/* lookup */
template <class KeyType>
constexpr std::size_t count(KeyType const &key) const {
return bits::binary_search<N>(keys_.begin(), key, less_than_);
}
template <class KeyType>
constexpr const_iterator find(KeyType const &key) const {
const_iterator where = lower_bound(key);
if ((where != end()) && !less_than_(key, *where))
return where;
else
return end();
}
template <class KeyType>
constexpr std::pair<const_iterator, const_iterator> equal_range(
KeyType const &key) const {
auto const lower = lower_bound(key);
if (lower == end())
return {lower, lower};
else
return {lower, lower + 1};
}
template <class KeyType>
constexpr const_iterator lower_bound(KeyType const &key) const {
auto const where = bits::lower_bound<N>(keys_.begin(), key, less_than_);
if ((where != end()) && !less_than_(key, *where))
return where;
else
return end();
}
template <class KeyType>
constexpr const_iterator upper_bound(KeyType const &key) const {
auto const where = bits::lower_bound<N>(keys_.begin(), key, less_than_);
if ((where != end()) && !less_than_(key, *where))
return where + 1;
else
return end();
}
/* observers */
constexpr const key_compare &key_comp() const { return less_than_; }
constexpr const key_compare &value_comp() const { return less_than_; }
/* iterators */
constexpr const_iterator begin() const { return keys_.begin(); }
constexpr const_iterator cbegin() const { return keys_.cbegin(); }
constexpr const_iterator end() const { return keys_.end(); }
constexpr const_iterator cend() const { return keys_.cend(); }
constexpr const_reverse_iterator rbegin() const { return keys_.rbegin(); }
constexpr const_reverse_iterator crbegin() const { return keys_.crbegin(); }
constexpr const_reverse_iterator rend() const { return keys_.rend(); }
constexpr const_reverse_iterator crend() const { return keys_.crend(); }
/* comparison */
constexpr bool operator==(set const &rhs) const {
return bits::equal(begin(), end(), rhs.begin());
}
constexpr bool operator!=(set const &rhs) const { return !(*this == rhs); }
constexpr bool operator<(set const &rhs) const {
return bits::lexicographical_compare(begin(), end(), rhs.begin(),
rhs.end());
}
constexpr bool operator<=(set const &rhs) const {
return (*this < rhs) || (*this == rhs);
}
constexpr bool operator>(set const &rhs) const {
return bits::lexicographical_compare(rhs.begin(), rhs.end(), begin(),
end());
}
constexpr bool operator>=(set const &rhs) const {
return (*this > rhs) || (*this == rhs);
}
};
template <class Key, class Compare>
class set<Key, 0, Compare> {
using container_type = bits::carray<Key, 0>; // just for the type definitions
Compare less_than_;
public:
/* container typedefs*/
using key_type = Key;
using value_type = Key;
using size_type = typename container_type::size_type;
using difference_type = typename container_type::size_type;
using key_compare = Compare;
using value_compare = Compare;
using reference = typename container_type::const_reference;
using const_reference = reference;
using pointer = typename container_type::const_pointer;
using const_pointer = pointer;
using iterator = pointer;
using reverse_iterator = pointer;
using const_iterator = const_pointer;
using const_reverse_iterator = const_pointer;
public:
/* constructors */
constexpr set(const set &other) = default;
constexpr set(bits::carray<Key, 0>, Compare const &) {}
explicit constexpr set(bits::carray<Key, 0>) {}
constexpr set(std::initializer_list<Key>, Compare const &comp)
: less_than_{comp} {}
constexpr set(std::initializer_list<Key> keys) : set{keys, Compare{}} {}
constexpr set &operator=(const set &other) = default;
/* capacity */
constexpr bool empty() const { return true; }
constexpr size_type size() const { return 0; }
constexpr size_type max_size() const { return 0; }
/* lookup */
template <class KeyType>
constexpr std::size_t count(KeyType const &) const {
return 0;
}
template <class KeyType>
constexpr const_iterator find(KeyType const &) const {
return end();
}
template <class KeyType>
constexpr std::pair<const_iterator, const_iterator> equal_range(
KeyType const &) const {
return {end(), end()};
}
template <class KeyType>
constexpr const_iterator lower_bound(KeyType const &) const {
return end();
}
template <class KeyType>
constexpr const_iterator upper_bound(KeyType const &) const {
return end();
}
/* observers */
constexpr key_compare key_comp() const { return less_than_; }
constexpr key_compare value_comp() const { return less_than_; }
/* iterators */
constexpr const_iterator begin() const { return nullptr; }
constexpr const_iterator cbegin() const { return nullptr; }
constexpr const_iterator end() const { return nullptr; }
constexpr const_iterator cend() const { return nullptr; }
constexpr const_reverse_iterator rbegin() const { return nullptr; }
constexpr const_reverse_iterator crbegin() const { return nullptr; }
constexpr const_reverse_iterator rend() const { return nullptr; }
constexpr const_reverse_iterator crend() const { return nullptr; }
};
template <typename T>
constexpr auto make_set(
bits::ignored_arg =
{} /* for consistency with the initializer below for N = 0*/) {
return set<T, 0>{};
}
template <typename T, std::size_t N>
constexpr auto make_set(const T (&args)[N]) {
return set<T, N>(args);
}
template <typename T, std::size_t N>
constexpr auto make_set(std::array<T, N> const &args) {
return set<T, N>(args);
}
template <typename T, typename Compare, std::size_t N>
constexpr auto make_set(const T (&args)[N],
Compare const &compare = Compare{}) {
return set<T, N, Compare>(args, compare);
}
template <typename T, typename Compare, std::size_t N>
constexpr auto make_set(std::array<T, N> const &args,
Compare const &compare = Compare{}) {
return set<T, N, Compare>(args, compare);
}
#ifdef FROZEN_LETITGO_HAS_DEDUCTION_GUIDES
template <class T, class... Args>
set(T, Args...) -> set<T, sizeof...(Args) + 1>;
#endif // FROZEN_LETITGO_HAS_DEDUCTION_GUIDES
} // namespace frozen
#endif
+150
View File
@@ -0,0 +1,150 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef FROZEN_LETITGO_STRING_H
#define FROZEN_LETITGO_STRING_H
#include <cstring>
#include <functional>
#include "frozen/bits/defines.h"
#include "frozen/bits/elsa.h"
#include "frozen/bits/hash_string.h"
#include "frozen/bits/version.h"
#ifdef FROZEN_LETITGO_HAS_STRING_VIEW
#include <string_view>
#endif
namespace frozen {
template <typename _CharT>
class basic_string {
using chr_t = _CharT;
chr_t const *data_;
std::size_t size_;
public:
template <std::size_t N>
constexpr basic_string(chr_t const (&data)[N]) : data_(data), size_(N - 1) {}
constexpr basic_string(chr_t const *data, std::size_t size)
: data_(data), size_(size) {}
#ifdef FROZEN_LETITGO_HAS_STRING_VIEW
constexpr basic_string(std::basic_string_view<chr_t> data)
: data_(data.data()), size_(data.size()) {}
#endif
constexpr basic_string(const basic_string &) noexcept = default;
constexpr basic_string &operator=(const basic_string &) noexcept = default;
constexpr std::size_t size() const { return size_; }
constexpr chr_t operator[](std::size_t i) const { return data_[i]; }
constexpr bool operator==(basic_string other) const {
if (size_ != other.size_)
return false;
for (std::size_t i = 0; i < size_; ++i)
if (data_[i] != other.data_[i])
return false;
return true;
}
constexpr bool operator<(const basic_string &other) const {
unsigned i = 0;
while (i < size() && i < other.size()) {
if ((*this)[i] < other[i]) {
return true;
}
if ((*this)[i] > other[i]) {
return false;
}
++i;
}
return size() < other.size();
}
constexpr const chr_t *data() const { return data_; }
constexpr const chr_t *begin() const { return data(); }
constexpr const chr_t *end() const { return data() + size(); }
};
template <typename _CharT>
struct elsa<basic_string<_CharT>> {
constexpr std::size_t operator()(basic_string<_CharT> value) const {
return hash_string(value);
}
constexpr std::size_t operator()(basic_string<_CharT> value,
std::size_t seed) const {
return hash_string(value, seed);
}
};
using string = basic_string<char>;
using wstring = basic_string<wchar_t>;
using u16string = basic_string<char16_t>;
using u32string = basic_string<char32_t>;
#ifdef FROZEN_LETITGO_HAS_CHAR8T
using u8string = basic_string<char8_t>;
#endif
namespace string_literals {
constexpr string operator"" _s(const char *data, std::size_t size) {
return {data, size};
}
constexpr wstring operator"" _s(const wchar_t *data, std::size_t size) {
return {data, size};
}
constexpr u16string operator"" _s(const char16_t *data, std::size_t size) {
return {data, size};
}
constexpr u32string operator"" _s(const char32_t *data, std::size_t size) {
return {data, size};
}
#ifdef FROZEN_LETITGO_HAS_CHAR8T
constexpr u8string operator"" _s(const char8_t *data, std::size_t size) {
return {data, size};
}
#endif
} // namespace string_literals
} // namespace frozen
namespace std {
template <typename _CharT>
struct hash<frozen::basic_string<_CharT>> {
size_t operator()(frozen::basic_string<_CharT> s) const {
return frozen::elsa<frozen::basic_string<_CharT>>{}(s);
}
};
} // namespace std
#endif
+275
View File
@@ -0,0 +1,275 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef FROZEN_LETITGO_UNORDERED_MAP_H
#define FROZEN_LETITGO_UNORDERED_MAP_H
#include <functional>
#include <tuple>
#include "frozen/bits/basic_types.h"
#include "frozen/bits/constexpr_assert.h"
#include "frozen/bits/elsa.h"
#include "frozen/bits/exceptions.h"
#include "frozen/bits/pmh.h"
#include "frozen/bits/version.h"
#include "frozen/random.h"
namespace frozen {
namespace bits {
struct GetKey {
template <class KV>
constexpr auto const &operator()(KV const &kv) const {
return kv.first;
}
};
} // namespace bits
template <class Key, class Value, std::size_t N, typename Hash = anna<Key>,
class KeyEqual = std::equal_to<Key>>
class unordered_map {
static constexpr std::size_t storage_size =
bits::next_highest_power_of_two(N) *
(N < 32 ? 2 : 1); // size adjustment to prevent high collision rate for
// small sets
using container_type = bits::carray<std::pair<Key, Value>, N>;
using tables_type = bits::pmh_tables<storage_size, Hash>;
KeyEqual const equal_;
container_type items_;
tables_type tables_;
public:
/* typedefs */
using Self = unordered_map<Key, Value, N, Hash, KeyEqual>;
using key_type = Key;
using mapped_type = Value;
using value_type = typename container_type::value_type;
using size_type = typename container_type::size_type;
using difference_type = typename container_type::difference_type;
using hasher = Hash;
using key_equal = KeyEqual;
using reference = typename container_type::reference;
using const_reference = typename container_type::const_reference;
using pointer = typename container_type::pointer;
using const_pointer = typename container_type::const_pointer;
using iterator = typename container_type::iterator;
using const_iterator = typename container_type::const_iterator;
public:
/* constructors */
unordered_map(unordered_map const &) = default;
constexpr unordered_map(container_type items, Hash const &hash,
KeyEqual const &equal)
: equal_{equal},
items_{items},
tables_{bits::make_pmh_tables<storage_size>(
items_, hash, bits::GetKey{}, default_prg_t{})} {}
explicit constexpr unordered_map(container_type items)
: unordered_map{items, Hash{}, KeyEqual{}} {}
constexpr unordered_map(std::initializer_list<value_type> items,
Hash const &hash, KeyEqual const &equal)
: unordered_map{container_type{items}, hash, equal} {
constexpr_assert(
items.size() == N,
"Inconsistent initializer_list size and type size argument");
}
constexpr unordered_map(std::initializer_list<value_type> items)
: unordered_map{items, Hash{}, KeyEqual{}} {}
/* iterators */
constexpr iterator begin() { return items_.begin(); }
constexpr iterator end() { return items_.end(); }
constexpr const_iterator begin() const { return items_.begin(); }
constexpr const_iterator end() const { return items_.end(); }
constexpr const_iterator cbegin() const { return items_.cbegin(); }
constexpr const_iterator cend() const { return items_.cend(); }
/* capacity */
constexpr bool empty() const { return !N; }
constexpr size_type size() const { return N; }
constexpr size_type max_size() const { return N; }
/* lookup */
template <class KeyType, class Hasher, class Equal>
constexpr std::size_t count(KeyType const &key, Hasher const &hash,
Equal const &equal) const {
auto const &kv = lookup(key, hash);
return equal(kv.first, key);
}
template <class KeyType>
constexpr std::size_t count(KeyType const &key) const {
return count(key, hash_function(), key_eq());
}
template <class KeyType, class Hasher, class Equal>
constexpr Value const &at(KeyType const &key, Hasher const &hash,
Equal const &equal) const {
return at_impl(*this, key, hash, equal);
}
template <class KeyType, class Hasher, class Equal>
constexpr Value &at(KeyType const &key, Hasher const &hash,
Equal const &equal) {
return at_impl(*this, key, hash, equal);
}
template <class KeyType>
constexpr Value const &at(KeyType const &key) const {
return at(key, hash_function(), key_eq());
}
template <class KeyType>
constexpr Value &at(KeyType const &key) {
return at(key, hash_function(), key_eq());
}
template <class KeyType, class Hasher, class Equal>
constexpr const_iterator find(KeyType const &key, Hasher const &hash,
Equal const &equal) const {
return find_impl(*this, key, hash, equal);
}
template <class KeyType, class Hasher, class Equal>
constexpr iterator find(KeyType const &key, Hasher const &hash,
Equal const &equal) {
return find_impl(*this, key, hash, equal);
}
template <class KeyType>
constexpr const_iterator find(KeyType const &key) const {
return find(key, hash_function(), key_eq());
}
template <class KeyType>
constexpr iterator find(KeyType const &key) {
return find(key, hash_function(), key_eq());
}
template <class KeyType, class Hasher, class Equal>
constexpr std::pair<const_iterator, const_iterator> equal_range(
KeyType const &key, Hasher const &hash, Equal const &equal) const {
return equal_range_impl(*this, key, hash, equal);
}
template <class KeyType, class Hasher, class Equal>
constexpr std::pair<iterator, iterator> equal_range(KeyType const &key,
Hasher const &hash,
Equal const &equal) {
return equal_range_impl(*this, key, hash, equal);
}
template <class KeyType>
constexpr std::pair<const_iterator, const_iterator> equal_range(
KeyType const &key) const {
return equal_range(key, hash_function(), key_eq());
}
template <class KeyType>
constexpr std::pair<iterator, iterator> equal_range(KeyType const &key) {
return equal_range(key, hash_function(), key_eq());
}
/* bucket interface */
constexpr std::size_t bucket_count() const { return storage_size; }
constexpr std::size_t max_bucket_count() const { return storage_size; }
/* observers*/
constexpr const hasher &hash_function() const { return tables_.hash_; }
constexpr const key_equal &key_eq() const { return equal_; }
private:
template <class This, class KeyType, class Hasher, class Equal>
static inline constexpr auto &at_impl(This &&self, KeyType const &key,
Hasher const &hash,
Equal const &equal) {
auto &kv = self.lookup(key, hash);
if (equal(kv.first, key))
return kv.second;
else
FROZEN_THROW_OR_ABORT(std::out_of_range("unknown key"));
}
template <class This, class KeyType, class Hasher, class Equal>
static inline constexpr auto find_impl(This &&self, KeyType const &key,
Hasher const &hash,
Equal const &equal) {
auto &kv = self.lookup(key, hash);
if (equal(kv.first, key))
return &kv;
else
return self.items_.end();
}
template <class This, class KeyType, class Hasher, class Equal>
static inline constexpr auto equal_range_impl(This &&self, KeyType const &key,
Hasher const &hash,
Equal const &equal) {
auto &kv = self.lookup(key, hash);
using kv_ptr = decltype(&kv);
if (equal(kv.first, key))
return std::pair<kv_ptr, kv_ptr>{&kv, &kv + 1};
else
return std::pair<kv_ptr, kv_ptr>{self.items_.end(), self.items_.end()};
}
template <class This, class KeyType, class Hasher>
static inline constexpr auto &lookup_impl(This &&self, KeyType const &key,
Hasher const &hash) {
return self.items_[self.tables_.lookup(key, hash)];
}
template <class KeyType, class Hasher>
constexpr auto const &lookup(KeyType const &key, Hasher const &hash) const {
return lookup_impl(*this, key, hash);
}
template <class KeyType, class Hasher>
constexpr auto &lookup(KeyType const &key, Hasher const &hash) {
return lookup_impl(*this, key, hash);
}
};
template <typename T, typename U, std::size_t N>
constexpr auto make_unordered_map(std::pair<T, U> const (&items)[N]) {
return unordered_map<T, U, N>{items};
}
template <typename T, typename U, std::size_t N, typename Hasher,
typename Equal>
constexpr auto make_unordered_map(std::pair<T, U> const (&items)[N],
Hasher const &hash = elsa<T>{},
Equal const &equal = std::equal_to<T>{}) {
return unordered_map<T, U, N, Hasher, Equal>{items, hash, equal};
}
template <typename T, typename U, std::size_t N>
constexpr auto make_unordered_map(std::array<std::pair<T, U>, N> const &items) {
return unordered_map<T, U, N>{items};
}
template <typename T, typename U, std::size_t N, typename Hasher,
typename Equal>
constexpr auto make_unordered_map(std::array<std::pair<T, U>, N> const &items,
Hasher const &hash = elsa<T>{},
Equal const &equal = std::equal_to<T>{}) {
return unordered_map<T, U, N, Hasher, Equal>{items, hash, equal};
}
} // namespace frozen
#endif
+198
View File
@@ -0,0 +1,198 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef FROZEN_LETITGO_UNORDERED_SET_H
#define FROZEN_LETITGO_UNORDERED_SET_H
#include <utility>
#include "frozen/bits/basic_types.h"
#include "frozen/bits/constexpr_assert.h"
#include "frozen/bits/elsa.h"
#include "frozen/bits/pmh.h"
#include "frozen/bits/version.h"
#include "frozen/random.h"
namespace frozen {
namespace bits {
struct Get {
template <class T>
constexpr T const &operator()(T const &key) const {
return key;
}
};
} // namespace bits
template <class Key, std::size_t N, typename Hash = elsa<Key>,
class KeyEqual = std::equal_to<Key>>
class unordered_set {
static constexpr std::size_t storage_size =
bits::next_highest_power_of_two(N) *
(N < 32 ? 2 : 1); // size adjustment to prevent high collision rate for
// small sets
using container_type = bits::carray<Key, N>;
using tables_type = bits::pmh_tables<storage_size, Hash>;
KeyEqual const equal_;
container_type keys_;
tables_type tables_;
public:
/* typedefs */
using key_type = Key;
using value_type = Key;
using size_type = typename container_type::size_type;
using difference_type = typename container_type::difference_type;
using hasher = Hash;
using key_equal = KeyEqual;
using const_reference = typename container_type::const_reference;
using reference = const_reference;
using const_pointer = typename container_type::const_pointer;
using pointer = const_pointer;
using const_iterator = const_pointer;
using iterator = const_iterator;
public:
/* constructors */
unordered_set(unordered_set const &) = default;
constexpr unordered_set(container_type keys, Hash const &hash,
KeyEqual const &equal)
: equal_{equal},
keys_{keys},
tables_{bits::make_pmh_tables<storage_size>(keys_, hash, bits::Get{},
default_prg_t{})} {}
explicit constexpr unordered_set(container_type keys)
: unordered_set{keys, Hash{}, KeyEqual{}} {}
constexpr unordered_set(std::initializer_list<Key> keys)
: unordered_set{keys, Hash{}, KeyEqual{}} {}
constexpr unordered_set(std::initializer_list<Key> keys, Hash const &hash,
KeyEqual const &equal)
: unordered_set{container_type{keys}, hash, equal} {
constexpr_assert(
keys.size() == N,
"Inconsistent initializer_list size and type size argument");
}
/* iterators */
constexpr const_iterator begin() const { return keys_.begin(); }
constexpr const_iterator end() const { return keys_.end(); }
constexpr const_iterator cbegin() const { return keys_.cbegin(); }
constexpr const_iterator cend() const { return keys_.cend(); }
/* capacity */
constexpr bool empty() const { return !N; }
constexpr size_type size() const { return N; }
constexpr size_type max_size() const { return N; }
/* lookup */
template <class KeyType, class Hasher, class Equal>
constexpr std::size_t count(KeyType const &key, Hasher const &hash,
Equal const &equal) const {
auto const k = lookup(key, hash);
return equal(k, key);
}
template <class KeyType>
constexpr std::size_t count(KeyType const &key) const {
return count(key, hash_function(), key_eq());
}
template <class KeyType, class Hasher, class Equal>
constexpr const_iterator find(KeyType const &key, Hasher const &hash,
Equal const &equal) const {
auto const &k = lookup(key, hash);
if (equal(k, key))
return &k;
else
return keys_.end();
}
template <class KeyType>
constexpr const_iterator find(KeyType const &key) const {
return find(key, hash_function(), key_eq());
}
template <class KeyType, class Hasher, class Equal>
constexpr std::pair<const_iterator, const_iterator> equal_range(
KeyType const &key, Hasher const &hash, Equal const &equal) const {
auto const &k = lookup(key, hash);
if (equal(k, key))
return {&k, &k + 1};
else
return {keys_.end(), keys_.end()};
}
template <class KeyType>
constexpr std::pair<const_iterator, const_iterator> equal_range(
KeyType const &key) const {
return equal_range(key, hash_function(), key_eq());
}
/* bucket interface */
constexpr std::size_t bucket_count() const { return storage_size; }
constexpr std::size_t max_bucket_count() const { return storage_size; }
/* observers*/
constexpr const hasher &hash_function() const { return tables_.hash_; }
constexpr const key_equal &key_eq() const { return equal_; }
private:
template <class KeyType, class Hasher>
constexpr auto const &lookup(KeyType const &key, Hasher const &hash) const {
return keys_[tables_.lookup(key, hash)];
}
};
template <typename T, std::size_t N>
constexpr auto make_unordered_set(T const (&keys)[N]) {
return unordered_set<T, N>{keys};
}
template <typename T, std::size_t N, typename Hasher, typename Equal>
constexpr auto make_unordered_set(T const (&keys)[N], Hasher const &hash,
Equal const &equal) {
return unordered_set<T, N, Hasher, Equal>{keys, hash, equal};
}
template <typename T, std::size_t N>
constexpr auto make_unordered_set(std::array<T, N> const &keys) {
return unordered_set<T, N>{keys};
}
template <typename T, std::size_t N, typename Hasher, typename Equal>
constexpr auto make_unordered_set(std::array<T, N> const &keys,
Hasher const &hash, Equal const &equal) {
return unordered_set<T, N, Hasher, Equal>{keys, hash, equal};
}
#ifdef FROZEN_LETITGO_HAS_DEDUCTION_GUIDES
template <class T, class... Args>
unordered_set(T, Args...) -> unordered_set<T, sizeof...(Args) + 1>;
#endif
} // namespace frozen
#endif
+201
View File
@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
File diff suppressed because it is too large Load Diff
+117
View File
@@ -0,0 +1,117 @@
#pragma once
#if (defined(_MSC_VER) && _MSC_VER >= 1920 && _MSVC_LANG >= 202002L) || \
(!defined(_MSC_VER) && defined(__cplusplus) && __cplusplus >= 202002L)
#include <bit>
#endif
#include <array>
#include <string>
#include <string_view>
#include <type_traits>
#include <vector>
#if defined __clang__
#define IGUANA_INLINE __attribute__((always_inline)) inline
#define IGUANA__INLINE_LAMBDA __attribute__((always_inline)) constexpr
#elif defined _MSC_VER
#define IGUANA_INLINE __forceinline
#define IGUANA__INLINE_LAMBDA constexpr
#else
#define IGUANA_INLINE __attribute__((always_inline)) inline
#define IGUANA__INLINE_LAMBDA constexpr __attribute__((always_inline))
#endif
#if __has_cpp_attribute(likely) && __has_cpp_attribute(unlikely) && \
__cplusplus >= 202002L
#define IGUANA_LIKELY [[likely]]
#define IGUANA_UNLIKELY [[unlikely]]
#else
#define IGUANA_LIKELY
#define IGUANA_UNLIKELY
#endif
#ifdef _MSC_VER
#if _MSC_VER >= 1920 && _MSVC_LANG >= 202002L
IGUANA_INLINE int countr_zero(unsigned long long x) {
return std::countr_zero(x);
}
template <typename T>
constexpr inline bool contiguous_iterator = std::contiguous_iterator<T>;
#else
IGUANA_INLINE int countr_zero(unsigned long long n) {
// x will never be zero in iguana
#if defined(_MSC_VER) && defined(_M_X64)
unsigned long c;
_BitScanForward64(&c, n);
return static_cast<int>(c);
#elif defined(_MSC_VER) && defined(_M_IX86)
unsigned long c;
if (static_cast<uint32_t>(n) != 0) {
_BitScanForward(&c, static_cast<uint32_t>(n));
return static_cast<int>(c);
}
else {
_BitScanForward(&c, static_cast<uint32_t>(n >> 32));
return static_cast<int>(c) + 32;
}
#endif
}
namespace std {
template <typename T>
using remove_cvref_t =
typename remove_cv<typename remove_reference<T>::type>::type;
}
template <typename T>
constexpr inline bool contiguous_iterator =
std::is_same_v<std::remove_cvref_t<T>,
typename std::vector<char>::iterator> ||
std::is_same_v<std::remove_cvref_t<T>,
typename std::vector<char>::const_iterator> ||
std::is_same_v<std::remove_cvref_t<T>, typename std::string::iterator> ||
std::is_same_v<std::remove_cvref_t<T>,
typename std::string::const_iterator> ||
std::is_same_v<std::remove_cvref_t<T>, char *> ||
std::is_same_v<std::remove_cvref_t<T>,
typename std::string_view::iterator> ||
std::is_same_v<std::remove_cvref_t<T>,
typename std::string_view::const_iterator>;
#endif
#else
#if __cplusplus >= 202002L
IGUANA_INLINE int countr_zero(unsigned long long x) {
return std::countr_zero(x);
}
template <typename T>
constexpr inline bool contiguous_iterator = std::contiguous_iterator<T>;
#else
IGUANA_INLINE int countr_zero(unsigned long long x) {
// x will never be zero in iguana
return __builtin_ctzll(x);
}
namespace std {
template <typename T>
using remove_cvref_t =
typename remove_cv<typename remove_reference<T>::type>::type;
}
template <typename T>
constexpr inline bool contiguous_iterator =
std::is_same_v<std::remove_cvref_t<T>,
typename std::vector<char>::iterator> ||
std::is_same_v<std::remove_cvref_t<T>,
typename std::vector<char>::const_iterator> ||
std::is_same_v<std::remove_cvref_t<T>, typename std::string::iterator> ||
std::is_same_v<std::remove_cvref_t<T>,
typename std::string::const_iterator> ||
std::is_same_v<std::remove_cvref_t<T>, char *> ||
std::is_same_v<std::remove_cvref_t<T>,
typename std::string_view::iterator> ||
std::is_same_v<std::remove_cvref_t<T>,
typename std::string_view::const_iterator>;
#endif
#endif
+83
View File
@@ -0,0 +1,83 @@
#pragma once
#include <charconv>
#include "dragonbox_to_chars.h"
#include "fast_float.h"
#include "iguana/define.h"
#include "itoa.hpp"
namespace iguana {
template <typename T>
struct is_char_type
: std::disjunction<std::is_same<T, char>, std::is_same<T, wchar_t>,
std::is_same<T, char16_t>, std::is_same<T, char32_t>> {};
inline void *to_chars_float(...) {
throw std::runtime_error("not allowed to invoke");
return {};
}
template <typename T, typename Ret = decltype(to_chars_float(
std::declval<T>(), std::declval<char *>()))>
using return_of_tochars = std::conditional_t<std::is_same_v<Ret, char *>,
std::true_type, std::false_type>;
// here std::true_type is used as a type , any other type is also ok.
using has_to_chars_float = iguana::return_of_tochars<std::true_type>;
namespace detail {
// check_number==true: check if the string [first, last) is a legal number
template <bool check_number = true, typename U>
std::pair<const char *, std::errc> from_chars(const char *first,
const char *last, U &value) {
using T = std::decay_t<U>;
if constexpr (std::is_floating_point_v<T>) {
auto [p, ec] = fast_float::from_chars(first, last, value);
if constexpr (check_number) {
if (p != last || ec != std::errc{})
IGUANA_UNLIKELY { throw std::runtime_error("Failed to parse number"); }
}
return {p, ec};
}
else {
auto [p, ec] = std::from_chars(first, last, value);
if constexpr (check_number) {
if (p != last || ec != std::errc{})
IGUANA_UNLIKELY { throw std::runtime_error("Failed to parse number"); }
}
return {p, ec};
}
}
// not support uint8 for now
template <typename T>
char *to_chars(char *buffer, T value) noexcept {
using U = std::decay_t<T>;
if constexpr (std::is_floating_point_v<U>) {
if constexpr (has_to_chars_float::value) {
return static_cast<char *>(to_chars_float(value, buffer));
}
else {
return jkj::dragonbox::to_chars(value, buffer);
}
}
else if constexpr (std::is_signed_v<U> && (sizeof(U) >= 8)) {
return xtoa(value, buffer, 10, 1); // int64_t
}
else if constexpr (std::is_unsigned_v<U> && (sizeof(U) >= 8)) {
return xtoa(value, buffer, 10, 0); // uint64_t
}
else if constexpr (std::is_integral_v<U> && (sizeof(U) > 1)) {
return itoa_fwd(value, buffer); // only support more than 2 bytes intergal
}
else if constexpr (!is_char_type<U>::value) {
return itoa_fwd(static_cast<int>(value),
buffer); // only support more than 2 bytes intergal
}
else {
static_assert(!sizeof(U), "only support arithmetic type except char type");
}
}
} // namespace detail
} // namespace iguana
File diff suppressed because it is too large Load Diff
+629
View File
@@ -0,0 +1,629 @@
// Copyright 2020-2022 Junekey Jeon
//
// The contents of this file may be used under the terms of
// the Apache License v2.0 with LLVM Exceptions.
//
// (See accompanying file LICENSE-Apache or copy at
// https://llvm.org/foundation/relicensing/LICENSE.txt)
//
// Alternatively, the contents of this file may be used under the terms of
// the Boost Software License, Version 1.0.
// (See accompanying file LICENSE-Boost or copy at
// https://www.boost.org/LICENSE_1_0.txt)
//
// Unless required by applicable law or agreed to in writing, this software
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.
#ifndef JKJ_HEADER_DRAGONBOX_TO_CHARS
#define JKJ_HEADER_DRAGONBOX_TO_CHARS
#include "dragonbox.h"
#if defined(__GNUC__) || defined(__clang__)
#define JKJ_FORCEINLINE inline __attribute__((always_inline))
#elif defined(_MSC_VER)
#define JKJ_FORCEINLINE __forceinline
#else
#define JKJ_FORCEINLINE inline
#endif
namespace jkj::dragonbox {
namespace to_chars_detail {
template <class Float, class FloatTraits>
extern char *to_chars(typename FloatTraits::carrier_uint significand,
int exponent, char *buffer) noexcept;
// These "//"'s are to prevent clang-format to ruin this nice alignment.
// Thanks to reddit user u/mcmcc:
// https://www.reddit.com/r/cpp/comments/so3wx9/dragonbox_110_is_released_a_fast_floattostring/hw8z26r/?context=3
static constexpr char radix_100_table[] = {
'0', '0', '0', '1', '0', '2', '0', '3', '0', '4', //
'0', '5', '0', '6', '0', '7', '0', '8', '0', '9', //
'1', '0', '1', '1', '1', '2', '1', '3', '1', '4', //
'1', '5', '1', '6', '1', '7', '1', '8', '1', '9', //
'2', '0', '2', '1', '2', '2', '2', '3', '2', '4', //
'2', '5', '2', '6', '2', '7', '2', '8', '2', '9', //
'3', '0', '3', '1', '3', '2', '3', '3', '3', '4', //
'3', '5', '3', '6', '3', '7', '3', '8', '3', '9', //
'4', '0', '4', '1', '4', '2', '4', '3', '4', '4', //
'4', '5', '4', '6', '4', '7', '4', '8', '4', '9', //
'5', '0', '5', '1', '5', '2', '5', '3', '5', '4', //
'5', '5', '5', '6', '5', '7', '5', '8', '5', '9', //
'6', '0', '6', '1', '6', '2', '6', '3', '6', '4', //
'6', '5', '6', '6', '6', '7', '6', '8', '6', '9', //
'7', '0', '7', '1', '7', '2', '7', '3', '7', '4', //
'7', '5', '7', '6', '7', '7', '7', '8', '7', '9', //
'8', '0', '8', '1', '8', '2', '8', '3', '8', '4', //
'8', '5', '8', '6', '8', '7', '8', '8', '8', '9', //
'9', '0', '9', '1', '9', '2', '9', '3', '9', '4', //
'9', '5', '9', '6', '9', '7', '9', '8', '9', '9' //
};
static constexpr char radix_100_head_table[] = {
'0', '.', '1', '.', '2', '.', '3', '.', '4', '.', //
'5', '.', '6', '.', '7', '.', '8', '.', '9', '.', //
'1', '.', '1', '.', '1', '.', '1', '.', '1', '.', //
'1', '.', '1', '.', '1', '.', '1', '.', '1', '.', //
'2', '.', '2', '.', '2', '.', '2', '.', '2', '.', //
'2', '.', '2', '.', '2', '.', '2', '.', '2', '.', //
'3', '.', '3', '.', '3', '.', '3', '.', '3', '.', //
'3', '.', '3', '.', '3', '.', '3', '.', '3', '.', //
'4', '.', '4', '.', '4', '.', '4', '.', '4', '.', //
'4', '.', '4', '.', '4', '.', '4', '.', '4', '.', //
'5', '.', '5', '.', '5', '.', '5', '.', '5', '.', //
'5', '.', '5', '.', '5', '.', '5', '.', '5', '.', //
'6', '.', '6', '.', '6', '.', '6', '.', '6', '.', //
'6', '.', '6', '.', '6', '.', '6', '.', '6', '.', //
'7', '.', '7', '.', '7', '.', '7', '.', '7', '.', //
'7', '.', '7', '.', '7', '.', '7', '.', '7', '.', //
'8', '.', '8', '.', '8', '.', '8', '.', '8', '.', //
'8', '.', '8', '.', '8', '.', '8', '.', '8', '.', //
'9', '.', '9', '.', '9', '.', '9', '.', '9', '.', //
'9', '.', '9', '.', '9', '.', '9', '.', '9', '.' //
};
// These digit generation routines are inspired by James Anhalt's itoa
// algorithm: https://github.com/jeaiii/itoa The main idea is for given n, find
// y such that floor(10^k * y / 2^32) = n holds, where k is an appropriate
// integer depending on the length of n. For example, if n = 1234567, we set k
// = 6. In this case, we have floor(y / 2^32) = 1, floor(10^2 * ((10^0 * y) mod
// 2^32) / 2^32) = 23, floor(10^2 * ((10^2 * y) mod 2^32) / 2^32) = 45, and
// floor(10^2 * ((10^4 * y) mod 2^32) / 2^32) = 67.
// See https://jk-jeon.github.io/posts/2022/02/jeaiii-algorithm/ for more
// explanation.
JKJ_FORCEINLINE static void print_9_digits(std::uint32_t s32, int &exponent,
char *&buffer) noexcept {
// -- IEEE-754 binary32
// Since we do not cut trailing zeros in advance, s32 must be of 6~9 digits
// unless the original input was subnormal.
// In particular, when it is of 9 digits it shouldn't have any trailing zeros.
// -- IEEE-754 binary64
// In this case, s32 must be of 7~9 digits unless the input is subnormal,
// and it shouldn't have any trailing zeros if it is of 9 digits.
if (s32 >= 1'0000'0000) {
// 9 digits.
// 1441151882 = ceil(2^57 / 1'0000'0000) + 1
auto prod = s32 * std::uint64_t(1441151882);
prod >>= 25;
std::memcpy(buffer, radix_100_head_table + std::uint32_t(prod >> 32) * 2,
2);
prod = std::uint32_t(prod) * std::uint64_t(100);
std::memcpy(buffer + 2, radix_100_table + std::uint32_t(prod >> 32) * 2, 2);
prod = std::uint32_t(prod) * std::uint64_t(100);
std::memcpy(buffer + 4, radix_100_table + std::uint32_t(prod >> 32) * 2, 2);
prod = std::uint32_t(prod) * std::uint64_t(100);
std::memcpy(buffer + 6, radix_100_table + std::uint32_t(prod >> 32) * 2, 2);
prod = std::uint32_t(prod) * std::uint64_t(100);
std::memcpy(buffer + 8, radix_100_table + std::uint32_t(prod >> 32) * 2, 2);
exponent += 8;
buffer += 10;
}
else if (s32 >= 100'0000) {
// 7 or 8 digits.
// 281474978 = ceil(2^48 / 100'0000) + 1
auto prod = s32 * std::uint64_t(281474978);
prod >>= 16;
auto two_digits = std::uint32_t(prod >> 32);
// If s32 is of 8 digits, increase the exponent by 7.
// Otherwise, increase it by 6.
exponent += (6 + unsigned(two_digits >= 10));
// Write the first digit and the decimal point.
std::memcpy(buffer, radix_100_head_table + two_digits * 2, 2);
// This third character may be overwritten later but we don't care.
buffer[2] = radix_100_table[two_digits * 2 + 1];
// Remaining 6 digits are all zero?
if (std::uint32_t(prod) <=
std::uint32_t((std::uint64_t(1) << 32) / 100'0000)) {
// The number of characters actually written is:
// 1, if only the first digit is nonzero, which means that either s32 is
// of 7 digits or it is of 8 digits but the second digit is zero, or 3,
// otherwise.
// Note that buffer[2] is never zero if s32 is of 7 digits, because the
// input is never zero.
buffer +=
(1 + (unsigned(two_digits >= 10) & unsigned(buffer[2] > '0')) * 2);
}
else {
// At least one of the remaining 6 digits are nonzero.
// After this adjustment, now the first destination becomes buffer + 2.
buffer += unsigned(two_digits >= 10);
// Obtain the next two digits.
prod = std::uint32_t(prod) * std::uint64_t(100);
two_digits = std::uint32_t(prod >> 32);
std::memcpy(buffer + 2, radix_100_table + two_digits * 2, 2);
// Remaining 4 digits are all zero?
if (std::uint32_t(prod) <=
std::uint32_t((std::uint64_t(1) << 32) / 1'0000)) {
buffer += (3 + unsigned(buffer[3] > '0'));
}
else {
// At least one of the remaining 4 digits are nonzero.
// Obtain the next two digits.
prod = std::uint32_t(prod) * std::uint64_t(100);
two_digits = std::uint32_t(prod >> 32);
std::memcpy(buffer + 4, radix_100_table + two_digits * 2, 2);
// Remaining 2 digits are all zero?
if (std::uint32_t(prod) <=
std::uint32_t((std::uint64_t(1) << 32) / 100)) {
buffer += (5 + unsigned(buffer[5] > '0'));
}
else {
// Obtain the last two digits.
prod = std::uint32_t(prod) * std::uint64_t(100);
two_digits = std::uint32_t(prod >> 32);
std::memcpy(buffer + 6, radix_100_table + two_digits * 2, 2);
buffer += (7 + unsigned(buffer[7] > '0'));
}
}
}
}
else if (s32 >= 1'0000) {
// 5 or 6 digits.
// 429497 = ceil(2^32 / 1'0000)
auto prod = s32 * std::uint64_t(429497);
auto two_digits = std::uint32_t(prod >> 32);
// If s32 is of 6 digits, increase the exponent by 5.
// Otherwise, increase it by 4.
exponent += (4 + unsigned(two_digits >= 10));
// Write the first digit and the decimal point.
std::memcpy(buffer, radix_100_head_table + two_digits * 2, 2);
// This third character may be overwritten later but we don't care.
buffer[2] = radix_100_table[two_digits * 2 + 1];
// Remaining 4 digits are all zero?
if (std::uint32_t(prod) <=
std::uint32_t((std::uint64_t(1) << 32) / 1'0000)) {
// The number of characters actually written is 1 or 3, similarly to the
// case of 7 or 8 digits.
buffer +=
(1 + (unsigned(two_digits >= 10) & unsigned(buffer[2] > '0')) * 2);
}
else {
// At least one of the remaining 4 digits are nonzero.
// After this adjustment, now the first destination becomes buffer + 2.
buffer += unsigned(two_digits >= 10);
// Obtain the next two digits.
prod = std::uint32_t(prod) * std::uint64_t(100);
two_digits = std::uint32_t(prod >> 32);
std::memcpy(buffer + 2, radix_100_table + two_digits * 2, 2);
// Remaining 2 digits are all zero?
if (std::uint32_t(prod) <=
std::uint32_t((std::uint64_t(1) << 32) / 100)) {
buffer += (3 + unsigned(buffer[3] > '0'));
}
else {
// Obtain the last two digits.
prod = std::uint32_t(prod) * std::uint64_t(100);
two_digits = std::uint32_t(prod >> 32);
std::memcpy(buffer + 4, radix_100_table + two_digits * 2, 2);
buffer += (5 + unsigned(buffer[5] > '0'));
}
}
}
else if (s32 >= 100) {
// 3 or 4 digits.
// 42949673 = ceil(2^32 / 100)
auto prod = s32 * std::uint64_t(42949673);
auto two_digits = std::uint32_t(prod >> 32);
// If s32 is of 4 digits, increase the exponent by 3.
// Otherwise, increase it by 2.
exponent += (2 + int(two_digits >= 10));
// Write the first digit and the decimal point.
std::memcpy(buffer, radix_100_head_table + two_digits * 2, 2);
// This third character may be overwritten later but we don't care.
buffer[2] = radix_100_table[two_digits * 2 + 1];
// Remaining 2 digits are all zero?
if (std::uint32_t(prod) <= std::uint32_t((std::uint64_t(1) << 32) / 100)) {
// The number of characters actually written is 1 or 3, similarly to the
// case of 7 or 8 digits.
buffer +=
(1 + (unsigned(two_digits >= 10) & unsigned(buffer[2] > '0')) * 2);
}
else {
// At least one of the remaining 2 digits are nonzero.
// After this adjustment, now the first destination becomes buffer + 2.
buffer += unsigned(two_digits >= 10);
// Obtain the last two digits.
prod = std::uint32_t(prod) * std::uint64_t(100);
two_digits = std::uint32_t(prod >> 32);
std::memcpy(buffer + 2, radix_100_table + two_digits * 2, 2);
buffer += (3 + unsigned(buffer[3] > '0'));
}
}
else {
// 1 or 2 digits.
// If s32 is of 2 digits, increase the exponent by 1.
exponent += int(s32 >= 10);
// Write the first digit and the decimal point.
std::memcpy(buffer, radix_100_head_table + s32 * 2, 2);
// This third character may be overwritten later but we don't care.
buffer[2] = radix_100_table[s32 * 2 + 1];
// The number of characters actually written is 1 or 3, similarly to the
// case of 7 or 8 digits.
buffer += (1 + (unsigned(s32 >= 10) & unsigned(buffer[2] > '0')) * 2);
}
}
template <>
inline char *to_chars<float, default_float_traits<float>>(
std::uint32_t s32, int exponent, char *buffer) noexcept {
// Print significand.
print_9_digits(s32, exponent, buffer);
// Print exponent and return
if (exponent < 0) {
std::memcpy(buffer, "E-", 2);
buffer += 2;
exponent = -exponent;
}
else {
buffer[0] = 'E';
buffer += 1;
}
if (exponent >= 10) {
std::memcpy(buffer, &radix_100_table[exponent * 2], 2);
buffer += 2;
}
else {
buffer[0] = char('0' + exponent);
buffer += 1;
}
return buffer;
}
template <>
inline char *to_chars<double, default_float_traits<double>>(
std::uint64_t const significand, int exponent, char *buffer) noexcept {
// Print significand by decomposing it into a 9-digit block and a 8-digit
// block.
std::uint32_t first_block, second_block;
bool no_second_block;
if (significand >= 1'0000'0000) {
first_block = std::uint32_t(significand / 1'0000'0000);
second_block = std::uint32_t(significand) - first_block * 1'0000'0000;
exponent += 8;
no_second_block = (second_block == 0);
}
else {
first_block = std::uint32_t(significand);
no_second_block = true;
}
if (no_second_block) {
print_9_digits(first_block, exponent, buffer);
}
else {
// We proceed similarly to print_9_digits(), but since we do not need to
// remove trailing zeros, the procedure is a bit simpler.
if (first_block >= 1'0000'0000) {
// The input is of 17 digits, thus there should be no trailing zero at
// all. The first block is of 9 digits. 1441151882 = ceil(2^57 /
// 1'0000'0000) + 1
auto prod = first_block * std::uint64_t(1441151882);
prod >>= 25;
std::memcpy(buffer, radix_100_head_table + std::uint32_t(prod >> 32) * 2,
2);
prod = std::uint32_t(prod) * std::uint64_t(100);
std::memcpy(buffer + 2, radix_100_table + std::uint32_t(prod >> 32) * 2,
2);
prod = std::uint32_t(prod) * std::uint64_t(100);
std::memcpy(buffer + 4, radix_100_table + std::uint32_t(prod >> 32) * 2,
2);
prod = std::uint32_t(prod) * std::uint64_t(100);
std::memcpy(buffer + 6, radix_100_table + std::uint32_t(prod >> 32) * 2,
2);
prod = std::uint32_t(prod) * std::uint64_t(100);
std::memcpy(buffer + 8, radix_100_table + std::uint32_t(prod >> 32) * 2,
2);
// The second block is of 8 digits.
// 281474978 = ceil(2^48 / 100'0000) + 1
prod = second_block * std::uint64_t(281474978);
prod >>= 16;
prod += 1;
std::memcpy(buffer + 10, radix_100_table + std::uint32_t(prod >> 32) * 2,
2);
prod = std::uint32_t(prod) * std::uint64_t(100);
std::memcpy(buffer + 12, radix_100_table + std::uint32_t(prod >> 32) * 2,
2);
prod = std::uint32_t(prod) * std::uint64_t(100);
std::memcpy(buffer + 14, radix_100_table + std::uint32_t(prod >> 32) * 2,
2);
prod = std::uint32_t(prod) * std::uint64_t(100);
std::memcpy(buffer + 16, radix_100_table + std::uint32_t(prod >> 32) * 2,
2);
exponent += 8;
buffer += 18;
}
else {
if (first_block >= 100'0000) {
// 7 or 8 digits.
// 281474978 = ceil(2^48 / 100'0000) + 1
auto prod = first_block * std::uint64_t(281474978);
prod >>= 16;
auto two_digits = std::uint32_t(prod >> 32);
std::memcpy(buffer, radix_100_head_table + two_digits * 2, 2);
buffer[2] = radix_100_table[two_digits * 2 + 1];
exponent += (6 + unsigned(two_digits >= 10));
buffer += unsigned(two_digits >= 10);
// Print remaining 6 digits.
prod = std::uint32_t(prod) * std::uint64_t(100);
std::memcpy(buffer + 2, radix_100_table + std::uint32_t(prod >> 32) * 2,
2);
prod = std::uint32_t(prod) * std::uint64_t(100);
std::memcpy(buffer + 4, radix_100_table + std::uint32_t(prod >> 32) * 2,
2);
prod = std::uint32_t(prod) * std::uint64_t(100);
std::memcpy(buffer + 6, radix_100_table + std::uint32_t(prod >> 32) * 2,
2);
buffer += 8;
}
else if (first_block >= 1'0000) {
// 5 or 6 digits.
// 429497 = ceil(2^32 / 1'0000)
auto prod = first_block * std::uint64_t(429497);
auto two_digits = std::uint32_t(prod >> 32);
std::memcpy(buffer, radix_100_head_table + two_digits * 2, 2);
buffer[2] = radix_100_table[two_digits * 2 + 1];
exponent += (4 + unsigned(two_digits >= 10));
buffer += unsigned(two_digits >= 10);
// Print remaining 4 digits.
prod = std::uint32_t(prod) * std::uint64_t(100);
std::memcpy(buffer + 2, radix_100_table + std::uint32_t(prod >> 32) * 2,
2);
prod = std::uint32_t(prod) * std::uint64_t(100);
std::memcpy(buffer + 4, radix_100_table + std::uint32_t(prod >> 32) * 2,
2);
buffer += 6;
}
else if (first_block >= 100) {
// 3 or 4 digits.
// 42949673 = ceil(2^32 / 100)
auto prod = first_block * std::uint64_t(42949673);
auto two_digits = std::uint32_t(prod >> 32);
std::memcpy(buffer, radix_100_head_table + two_digits * 2, 2);
buffer[2] = radix_100_table[two_digits * 2 + 1];
exponent += (2 + unsigned(two_digits >= 10));
buffer += unsigned(two_digits >= 10);
// Print remaining 2 digits.
prod = std::uint32_t(prod) * std::uint64_t(100);
std::memcpy(buffer + 2, radix_100_table + std::uint32_t(prod >> 32) * 2,
2);
buffer += 4;
}
else {
// 1 or 2 digits.
std::memcpy(buffer, radix_100_head_table + first_block * 2, 2);
buffer[2] = radix_100_table[first_block * 2 + 1];
exponent += unsigned(first_block >= 10);
buffer += (2 + unsigned(first_block >= 10));
}
// Next, print the second block.
// The second block is of 8 digits, but we may have trailing zeros.
// 281474978 = ceil(2^48 / 100'0000) + 1
auto prod = second_block * std::uint64_t(281474978);
prod >>= 16;
prod += 1;
auto two_digits = std::uint32_t(prod >> 32);
std::memcpy(buffer, radix_100_table + two_digits * 2, 2);
// Remaining 6 digits are all zero?
if (std::uint32_t(prod) <=
std::uint32_t((std::uint64_t(1) << 32) / 100'0000)) {
buffer += (1 + unsigned(buffer[1] > '0'));
}
else {
// Obtain the next two digits.
prod = std::uint32_t(prod) * std::uint64_t(100);
two_digits = std::uint32_t(prod >> 32);
std::memcpy(buffer + 2, radix_100_table + two_digits * 2, 2);
// Remaining 4 digits are all zero?
if (std::uint32_t(prod) <=
std::uint32_t((std::uint64_t(1) << 32) / 1'0000)) {
buffer += (3 + unsigned(buffer[3] > '0'));
}
else {
// Obtain the next two digits.
prod = std::uint32_t(prod) * std::uint64_t(100);
two_digits = std::uint32_t(prod >> 32);
std::memcpy(buffer + 4, radix_100_table + two_digits * 2, 2);
// Remaining 2 digits are all zero?
if (std::uint32_t(prod) <=
std::uint32_t((std::uint64_t(1) << 32) / 100)) {
buffer += (5 + unsigned(buffer[5] > '0'));
}
else {
// Obtain the last two digits.
prod = std::uint32_t(prod) * std::uint64_t(100);
two_digits = std::uint32_t(prod >> 32);
std::memcpy(buffer + 6, radix_100_table + two_digits * 2, 2);
buffer += (7 + unsigned(buffer[7] > '0'));
}
}
}
}
}
// Print exponent and return
if (exponent < 0) {
std::memcpy(buffer, "E-", 2);
buffer += 2;
exponent = -exponent;
}
else {
buffer[0] = 'E';
buffer += 1;
}
if (exponent >= 100) {
// d1 = exponent / 10; d2 = exponent % 10;
// 6554 = ceil(2^16 / 10)
auto prod = std::uint32_t(exponent) * std::uint32_t(6554);
auto d1 = prod >> 16;
prod = std::uint16_t(prod) * std::uint32_t(5); // * 10
auto d2 = prod >> 15; // >> 16
std::memcpy(buffer, &radix_100_table[d1 * 2], 2);
buffer[2] = char('0' + d2);
buffer += 3;
}
else if (exponent >= 10) {
std::memcpy(buffer, &radix_100_table[exponent * 2], 2);
buffer += 2;
}
else {
buffer[0] = char('0' + exponent);
buffer += 1;
}
return buffer;
}
// Avoid needless ABI overhead incurred by tag dispatch.
template <class PolicyHolder, class Float, class FloatTraits>
char *to_chars_n_impl(float_bits<Float, FloatTraits> br,
char *buffer) noexcept {
auto const exponent_bits = br.extract_exponent_bits();
auto const s = br.remove_exponent_bits(exponent_bits);
if (br.is_finite(exponent_bits)) {
if (s.is_negative()) {
*buffer = '-';
++buffer;
}
if (br.is_nonzero()) {
auto result = to_decimal<Float, FloatTraits>(
s, exponent_bits, policy::sign::ignore, policy::trailing_zero::ignore,
typename PolicyHolder::decimal_to_binary_rounding_policy{},
typename PolicyHolder::binary_to_decimal_rounding_policy{},
typename PolicyHolder::cache_policy{});
return to_chars_detail::to_chars<Float, FloatTraits>(
result.significand, result.exponent, buffer);
}
else {
std::memcpy(buffer, "0E0", 3);
return buffer + 3;
}
}
else {
if (s.has_all_zero_significand_bits()) {
if (s.is_negative()) {
*buffer = '-';
++buffer;
}
std::memcpy(buffer, "Infinity", 8);
return buffer + 8;
}
else {
std::memcpy(buffer, "NaN", 3);
return buffer + 3;
}
}
}
} // namespace to_chars_detail
// Returns the next-to-end position
template <class Float, class FloatTraits = default_float_traits<Float>,
class... Policies>
char *to_chars_n(Float x, char *buffer, Policies... policies) noexcept {
using namespace jkj::dragonbox::detail::policy_impl;
using policy_holder = decltype(make_policy_holder(
base_default_pair_list<
base_default_pair<decimal_to_binary_rounding::base,
decimal_to_binary_rounding::nearest_to_even>,
base_default_pair<binary_to_decimal_rounding::base,
binary_to_decimal_rounding::to_even>,
base_default_pair<cache::base, cache::full>>{},
policies...));
return to_chars_detail::to_chars_n_impl<policy_holder>(
float_bits<Float, FloatTraits>(x), buffer);
}
// Null-terminate and bypass the return value of fp_to_chars_n
template <class Float, class FloatTraits = default_float_traits<Float>,
class... Policies>
char *to_chars(Float x, char *buffer, Policies... policies) noexcept {
auto ptr = to_chars_n<Float, FloatTraits>(x, buffer, policies...);
*ptr = '\0';
return ptr;
}
// Maximum required buffer size (excluding null-terminator)
template <class FloatFormat>
inline constexpr std::size_t max_output_string_length =
std::is_same_v<FloatFormat, ieee754_binary32>
?
// sign(1) + significand(9) + decimal_point(1) + exp_marker(1) +
// exp_sign(1) + exp(2)
(1 + 9 + 1 + 1 + 1 + 2)
:
// format == ieee754_format::binary64
// sign(1) + significand(17) + decimal_point(1) + exp_marker(1) +
// exp_sign(1) + exp(3)
(1 + 17 + 1 + 1 + 1 + 3);
} // namespace jkj::dragonbox
#endif
File diff suppressed because it is too large Load Diff
+321
View File
@@ -0,0 +1,321 @@
//=== itoa.h - Fast integer to ascii conversion --*- C++ -*-//
//
// The MIT License (MIT)
// Copyright (c) 2016 Arturo Martin-de-Nicolas
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//===----------------------------------------------------------------------===//
#ifndef DEC_ITOA_IMPL_H
#define DEC_ITOA_IMPL_H
#include <array>
#include <cstddef>
#include <cstdint>
#include <cstring> // memcpy
#include <type_traits>
namespace dec_ {
// Using a lookup table to convert binary numbers from 0 to 99
// into ascii characters as described by Andrei Alexandrescu in
// https://www.facebook.com/notes/facebook-engineering/
// three-optimization-tips-for-c/10151361643253920/
template <typename T, size_t N, typename Gen, size_t... Is>
constexpr auto generate_array(Gen&& item, std::index_sequence<Is...>) {
return std::array<T, N>{{item(Is)...}};
}
const std::array<char, 200> digits = generate_array<char, 200>(
[](size_t i) {
return char('0' + ((i % 2) ? ((i / 2) % 10) : ((i / 2) / 10)));
},
std::make_index_sequence<200>{});
// extern const std::array<char,200> digits;
static inline uint16_t const& dd(uint8_t u) {
return reinterpret_cast<uint16_t const*>(digits.data())[u];
}
template <typename T>
static constexpr T pow10(size_t x) {
return x ? 10 * pow10<T>(x - 1) : 1;
}
// Division by a power of 10 is implemented using a multiplicative inverse.
// This strength reduction is also done by optimizing compilers, but
// presently the fastest results are produced by using the values
// for the multiplication and the shift as given by the algorithm
// described by Agner Fog in "Optimizing Subroutines in Assembly Language"
//
// http://www.agner.org/optimize/optimizing_assembly.pdf
//
// "Integer division by a constant (all processors)
// A floating point number can be divided by a constant by multiplying
// with the reciprocal. If we want to do the same with integers, we have
// to scale the reciprocal by 2n and then shift the product to the right
// by n. There are various algorithms for finding a suitable value of n
// and compensating for rounding errors. The algorithm described below
// was invented by Terje Mathisen, Norway, and not published elsewhere."
// using uint128_t = unsigned __int128;
template <typename UInt, bool A, UInt M, unsigned S>
struct MulInv {
using type = UInt;
static constexpr bool a{A};
static constexpr UInt m{M};
static constexpr unsigned s{S};
};
template <int, int, class...>
struct UT;
template <int N, class T, class... Ts>
struct UT<N, N, T, Ts...> {
using U = T;
};
template <int N, int M, class T, class... Ts>
struct UT<N, M, T, Ts...> {
using U = typename UT<N, 2 * M, Ts...>::U;
};
template <int N>
using MI = typename UT<N, 1, MulInv<uint8_t, 0, 205U, 11>,
MulInv<uint16_t, 1, 41943U, 22>,
MulInv<uint32_t, 0, 3518437209U, 45>,
MulInv<uint64_t, 0, 12379400392853802749U, 90>>::U;
template <int N>
using U = typename MI<N>::type;
// struct QR holds the result of dividing an unsigned N-byte variable
// by 10^N resulting in
template <size_t N>
struct QR {
U<N> q; // quotient with fewer than 2*N decimal digits
U<N / 2> r; // remainder with at most N decimal digits
};
template <size_t N>
QR<N> static inline split(U<N> u) {
constexpr MI<N> mi{};
U<N> q = (mi.m * (U<2 * N>(u) + mi.a)) >> mi.s;
return {q, U<N / 2>(u - q * pow10<U<N / 2>>(N))};
}
enum Direction { Fwd, Rev };
template <Direction D>
struct convert {
//===----------------------------------------------------------===//
// output the digits in either a forward or reverse direction
// use memcpy so compiler handles alignment on target architecture.
// Typically generates one store to memory with an optimizing
// compiler for target architecture that supports unaligned access.
//===----------------------------------------------------------===//
template <typename T>
static inline char* out(char* p, T&& obj) {
if (D == Rev)
p -= sizeof(T);
memcpy(p, reinterpret_cast<const void*>(&obj), sizeof(T));
if (D == Fwd)
p += sizeof(T);
return p;
}
//===----------------------------------------------------------===//
// head: find most significant digit, skip leading zeros
//===----------------------------------------------------------===//
// "x" contains quotient and remainder after division by 10^N
// quotient is less than 10^N
template <size_t N>
static inline char* head(char* p, QR<N> x) {
return (D == Fwd ? (tail(head(p, U<N / 2>(x.q)), x.r))
: (head(tail(p, x.r), U<N / 2>(x.q))));
}
// "u" is less than 10^2*N
template <typename UInt, size_t N = sizeof(UInt)>
static inline char* head(char* p, UInt u) {
return (u < pow10<U<N>>(N) ? (head(p, U<N / 2>(u)))
: (head<N>(p, split<N>(u))));
}
// recursion base case, selected when "u" is one byte
static inline char* head(char* p, U<1> u) {
return (u < 10 ? (out<char>(p, '0' + u)) : (out(p, dd(u))));
}
//===----------------------------------------------------------===//
// tail: produce all digits including leading zeros
//===----------------------------------------------------------===//
// recursive step, "u" is less than 10^2*N
template <typename UInt, size_t N = sizeof(UInt)>
static inline char* tail(char* p, UInt u) {
QR<N> x = split<N>(u);
return (D == Fwd ? (tail(tail(p, U<N / 2>(x.q)), x.r))
: (tail(tail(p, x.r), U<N / 2>(x.q))));
}
// recursion base case, selected when "u" is one byte
static inline char* tail(char* p, U<1> u) { return out(p, dd(u)); }
//===----------------------------------------------------------===//
// large values are >= 10^2*N
// where x contains quotient and remainder after division by 10^N
//===----------------------------------------------------------===//
template <size_t N>
static inline char* large(char* p, QR<N> x) {
QR<N> y = split<N>(x.q);
return (D == Fwd ? (tail(tail(head(p, U<N / 2>(y.q)), y.r), x.r))
: (head(tail(tail(p, x.r), y.r), U<N / 2>(y.q))));
}
//===----------------------------------------------------------===//
// handle values of "u" that might be >= 10^2*N
// where N is the size of "u" in bytes
//===----------------------------------------------------------===//
template <typename UInt, size_t N = sizeof(UInt)>
static inline char* itoa(char* p, UInt u) {
if (u < pow10<U<N>>(N))
return head(p, U<N / 2>(u));
QR<N> x = split<N>(u);
return (u < pow10<U<N>>(2 * N) ? (head<N>(p, x)) : (large<N>(p, x)));
}
// selected when "u" is one byte
static inline char* itoa(char* p, U<1> u) {
if (u < 10)
return out<char>(p, '0' + u);
if (u < 100)
return out(p, dd(u));
return (D == Fwd ? (out(out<char>(p, '0' + u / 100), dd(u % 100)))
: (out<char>(out(p, dd(u % 100)), '0' + u / 100)));
}
//===----------------------------------------------------------===//
// handle unsigned and signed integral operands
//===----------------------------------------------------------===//
// itoa: handle unsigned integral operands (selected by SFINAE)
template <typename U, std::enable_if_t<!std::is_signed<U>::value &&
std::is_integral<U>::value>* = nullptr>
static inline char* itoa(U u, char* p) {
return convert<D>::template itoa<>(p, u);
}
// itoa: handle signed integral operands (selected by SFINAE)
template <typename I, size_t N = sizeof(I),
std::enable_if_t<std::is_signed<I>::value &&
std::is_integral<I>::value>* = nullptr>
static inline char* itoa(I i, char* p) {
// Need "mask" to be filled with a copy of the sign bit.
// If "i" is a negative value, then the result of "operator >>"
// is implementation-defined, though usually it is an arithmetic
// right shift that replicates the sign bit.
// Use a conditional expression to be portable,
// a good optimizing compiler generates an arithmetic right shift
// and avoids the conditional branch.
U<N> mask = i < 0 ? ~U<N>(0) : 0;
// Now get the absolute value of "i" and cast to unsigned type U<N>.
// Cannot use std::abs() because the result is undefined
// in 2's complement systems for the most-negative value.
// Want to avoid conditional branch for performance reasons since
// CPU branch prediction will be ineffective when negative values
// occur randomly.
// Let "u" be "i" cast to unsigned type U<N>.
// Subtract "u" from 2*u if "i" is positive or 0 if "i" is negative.
// This yields the absolute value with the desired type without
// using a conditional branch and without invoking undefined or
// implementation defined behavior:
U<N> u = ((2 * U<N>(i)) & ~mask) - U<N>(i);
// Unconditionally store a minus sign when producing digits
// in a forward direction and increment the pointer only if
// the value is in fact negative.
// This avoids a conditional branch and is safe because we will
// always produce at least one digit and it will overwrite the
// minus sign when the value is not negative.
if (D == Fwd) {
*p = '-';
p += (mask & 1);
}
p = convert<D>::template itoa<>(p, u);
if (D == Rev && mask)
*--p = '-';
return p;
}
};
} // namespace dec_
// Programming interface: itoa_fwd, itoa_rev
template <typename I>
char* itoa_fwd(I i, char* p) {
return dec_::convert<dec_::Fwd>::itoa(i, p);
}
inline char* xtoa(long long sval, char* str, int radix, int signedp) {
unsigned long long uval;
unsigned int uradix = radix;
char* sp = str;
char* sp2;
char* sp3;
/* If signed, store sign at start of buffer for negative base-10 values */
if (signedp && (10 == uradix) && (0 > sval)) {
*sp++ = '-';
uval = -sval;
}
else {
uval = sval;
}
sp2 = sp;
do {
unsigned int rem = uval % uradix;
uval /= uradix;
if (10 > rem) {
*sp++ = '0' + (char)rem;
}
else {
*sp++ = 'A' + (char)rem - 10;
}
} while (0 < uval);
/* Mark end of string */
sp3 = sp;
*sp-- = 0;
/* Reverse string contents (excluding sign) in place */
while (sp2 < sp) {
char tmp = *sp2;
*sp2++ = *sp;
*sp-- = tmp;
}
return sp3;
}
template <typename I>
char* itoa_rev(I i, char* p) {
return dec_::convert<dec_::Rev>::itoa(i, p);
}
#endif // DEC_ITOA_IMPL_H
+143
View File
@@ -0,0 +1,143 @@
#pragma once
#include <cstdint>
#include <type_traits>
namespace iguana {
struct sint32_t {
using value_type = int32_t;
int32_t val;
bool operator==(const sint32_t& other) const { return val == other.val; }
};
inline bool operator==(sint32_t value1, int32_t value2) {
return value1.val == value2;
}
// for key in std::map
inline bool operator<(const sint32_t& lhs, const sint32_t& rhs) {
return lhs.val < rhs.val;
}
struct sint64_t {
using value_type = int64_t;
int64_t val;
bool operator==(const sint64_t& other) const { return val == other.val; }
};
inline bool operator==(sint64_t value1, int64_t value2) {
return value1.val == value2;
}
inline bool operator<(const sint64_t& lhs, const sint64_t& rhs) {
return lhs.val < rhs.val;
}
struct fixed32_t {
using value_type = uint32_t;
uint32_t val;
bool operator==(const fixed32_t& other) const { return val == other.val; }
};
inline bool operator==(fixed32_t value1, uint32_t value2) {
return value1.val == value2;
}
inline bool operator<(const fixed32_t& lhs, const fixed32_t& rhs) {
return lhs.val < rhs.val;
}
struct fixed64_t {
using value_type = uint64_t;
uint64_t val;
bool operator==(const fixed64_t& other) const { return val == other.val; }
};
inline bool operator==(fixed64_t value1, uint64_t value2) {
return value1.val == value2;
}
inline bool operator<(const fixed64_t& lhs, const fixed64_t& rhs) {
return lhs.val < rhs.val;
}
struct sfixed32_t {
using value_type = int32_t;
int32_t val;
bool operator==(const sfixed32_t& other) const { return val == other.val; }
};
inline bool operator==(sfixed32_t value1, int32_t value2) {
return value1.val == value2;
}
inline bool operator<(const sfixed32_t& lhs, const sfixed32_t& rhs) {
return lhs.val < rhs.val;
}
struct sfixed64_t {
using value_type = int64_t;
int64_t val;
bool operator==(const sfixed64_t& other) const { return val == other.val; }
};
inline bool operator==(sfixed64_t value1, int64_t value2) {
return value1.val == value2;
}
inline bool operator<(const sfixed64_t& lhs, const sfixed64_t& rhs) {
return lhs.val < rhs.val;
}
template <typename Type, typename T = std::decay_t<Type>>
constexpr bool is_pb_type_v =
std::is_same_v<sint32_t, T> || std::is_same_v<sint64_t, T> ||
std::is_same_v<fixed32_t, T> || std::is_same_v<fixed64_t, T> ||
std::is_same_v<sfixed32_t, T> || std::is_same_v<sfixed64_t, T>;
} // namespace iguana
// for key in std::unordered_map
namespace std {
template <>
struct hash<iguana::sint32_t> {
size_t operator()(const iguana::sint32_t& x) const noexcept {
return std::hash<int32_t>()(x.val);
}
};
template <>
struct hash<iguana::sint64_t> {
size_t operator()(const iguana::sint64_t& x) const noexcept {
return std::hash<int64_t>()(x.val);
}
};
template <>
struct hash<iguana::fixed32_t> {
size_t operator()(const iguana::fixed32_t& x) const noexcept {
return std::hash<uint32_t>()(x.val);
}
};
template <>
struct hash<iguana::fixed64_t> {
size_t operator()(const iguana::fixed64_t& x) const noexcept {
return std::hash<uint64_t>()(x.val);
}
};
template <>
struct hash<iguana::sfixed32_t> {
size_t operator()(const iguana::sfixed32_t& x) const noexcept {
return std::hash<int32_t>()(x.val);
}
};
template <>
struct hash<iguana::sfixed64_t> {
size_t operator()(const iguana::sfixed64_t& x) const noexcept {
return std::hash<int64_t>()(x.val);
}
};
} // namespace std
+131
View File
@@ -0,0 +1,131 @@
#pragma once
#include <cstddef>
#include <string>
#include <utility>
#include <vector>
namespace iguana::detail {
#if __cpp_lib_string_resize_and_overwrite >= 202110L
template <typename ch>
inline void resize(std::basic_string<ch> &str, std::size_t sz) {
str.resize_and_overwrite(sz, [sz](ch *, std::size_t) {
return sz;
});
}
#elif (defined(_MSC_VER) && _MSC_VER <= 1920)
// old msvc don't support visit private, discard it.
#else
template <typename Function, Function func_ptr>
class string_thief {
public:
friend void string_set_length_hacker(std::string &self, std::size_t sz) {
#if defined(_MSVC_STL_VERSION)
(self.*func_ptr)._Myval2._Mysize = sz;
#else
#if defined(_LIBCPP_VERSION)
(self.*func_ptr)(sz);
#else
#if (_GLIBCXX_USE_CXX11_ABI == 0) && defined(__GLIBCXX__)
(self.*func_ptr)()->_M_set_length_and_sharable(sz);
#else
#if defined(__GLIBCXX__)
(self.*func_ptr)(sz);
#endif
#endif
#endif
#endif
}
};
#if defined(__GLIBCXX__) // libstdc++
#if (_GLIBCXX_USE_CXX11_ABI == 0)
template class string_thief<decltype(&std::string::_M_rep),
&std::string::_M_rep>;
#else
template class string_thief<decltype(&std::string::_M_set_length),
&std::string::_M_set_length>;
#endif
#elif defined(_LIBCPP_VERSION)
template class string_thief<decltype(&std::string::__set_size),
&std::string::__set_size>;
#elif defined(_MSVC_STL_VERSION)
template class string_thief<decltype(&std::string::_Mypair),
&std::string::_Mypair>;
#endif
void string_set_length_hacker(std::string &, std::size_t);
template <typename ch>
inline void resize(std::basic_string<ch> &raw_str, std::size_t sz) {
std::string &str = *reinterpret_cast<std::string *>(&raw_str);
#if defined(__GLIBCXX__) || defined(_LIBCPP_VERSION) || \
defined(_MSVC_STL_VERSION)
if (sz > str.capacity()) {
str.reserve(sz);
}
string_set_length_hacker(str, sz);
str[sz] = '\0';
#else
raw_str.resize(sz);
#endif
}
#endif
#if (defined(_MSC_VER) && _MSC_VER <= 1920)
#else
void vector_set_length_hacker(std::vector<char> &self, std::size_t sz);
template <typename Function, Function func_ptr>
class vector_thief {
public:
friend void vector_set_length_hacker(std::vector<char> &self,
std::size_t sz) {
#if defined(_MSVC_STL_VERSION)
(self.*func_ptr)._Myval2._Mylast = self.data() + sz;
#else
#if defined(_LIBCPP_VERSION)
#if _LIBCPP_VERSION < 14000
((*(std::__vector_base<char, std::allocator<char> > *)(&self)).*func_ptr) =
self.data() + sz;
#else
(self.*func_ptr) = self.data() + sz;
#endif
#else
#if defined(__GLIBCXX__)
((*(std::_Vector_base<char, std::allocator<char> > *)(&self)).*func_ptr)
._M_finish = self.data() + sz;
#endif
#endif
#endif
}
};
#if defined(__GLIBCXX__) // libstdc++
template class vector_thief<decltype(&std::vector<char>::_M_impl),
&std::vector<char>::_M_impl>;
#elif defined(_LIBCPP_VERSION)
template class vector_thief<decltype(&std::vector<char>::__end_),
&std::vector<char>::__end_>;
#elif defined(_MSVC_STL_VERSION)
template class vector_thief<decltype(&std::vector<char>::_Mypair),
&std::vector<char>::_Mypair>;
#endif
template <typename ch>
inline void resize(std::vector<ch> &raw_vec, std::size_t sz) {
#if defined(__GLIBCXX__) || \
(defined(_LIBCPP_VERSION) && defined(_LIBCPP_HAS_NO_ASAN)) || \
defined(_MSVC_STL_VERSION)
std::vector<char> &vec = *reinterpret_cast<std::vector<char> *>(&raw_vec);
vec.reserve(sz);
vector_set_length_hacker(vec, sz);
#else
raw_vec.resize(sz);
#endif
}
#endif
}; // namespace iguana::detail
+17
View File
@@ -0,0 +1,17 @@
#pragma once
#include <string>
#if __has_include(<memory_resource>)
#include <memory_resource>
#endif
namespace iguana {
#ifdef IGUANA_ENABLE_PMR
#if __has_include(<memory_resource>)
inline char iguana_buf[2048];
inline std::pmr::monotonic_buffer_resource iguana_resource(iguana_buf, 2048);
using string_stream = std::pmr::string;
#endif
#else
using string_stream = std::string;
#endif
} // namespace iguana
+229
View File
@@ -0,0 +1,229 @@
//
// Created by QY on 2017-01-05.
//
#ifndef SERIALIZE_TRAITS_HPP
#define SERIALIZE_TRAITS_HPP
#include <deque>
#include <list>
#include <map>
#include <queue>
#include <type_traits>
#include <unordered_map>
#include <variant>
#include <vector>
#if __cplusplus > 201703L
#if __has_include(<span>)
#include <span>
#endif
#endif
#include "iguana/define.h"
namespace iguana {
template <class T>
struct is_signed_intergral_like
: std::integral_constant<bool, (std::is_integral<T>::value) &&
std::is_signed<T>::value> {};
template <class T>
struct is_unsigned_intergral_like
: std::integral_constant<bool, (std::is_integral<T>::value) &&
std::is_unsigned<T>::value> {};
template <template <typename...> class U, typename T>
struct is_template_instant_of : std::false_type {};
template <template <typename...> class U, typename... args>
struct is_template_instant_of<U, U<args...>> : std::true_type {};
template <typename T>
struct is_stdstring : is_template_instant_of<std::basic_string, T> {};
template <typename T>
struct is_tuple : is_template_instant_of<std::tuple, T> {};
#if __cplusplus > 201703L
#if __has_include(<span>)
template <typename>
struct is_span : std::false_type {};
template <typename T, size_t Extent>
struct is_span<std::span<T, Extent>> : std::true_type {};
#endif
#endif
template <class T>
struct is_sequence_container
: std::integral_constant<
bool, is_template_instant_of<std::deque, T>::value ||
is_template_instant_of<std::list, T>::value ||
is_template_instant_of<std::vector, T>::value ||
is_template_instant_of<std::queue, T>::value> {};
template <class T>
struct is_associat_container
: std::integral_constant<
bool, is_template_instant_of<std::map, T>::value ||
is_template_instant_of<std::unordered_map, T>::value> {};
template <class T>
struct is_emplace_back_able
: std::integral_constant<
bool, is_template_instant_of<std::deque, T>::value ||
is_template_instant_of<std::list, T>::value ||
is_template_instant_of<std::vector, T>::value> {};
template <typename T, typename Tuple>
struct has_type;
template <typename T, typename... Us>
struct has_type<T, std::tuple<Us...>>
: std::disjunction<std::is_same<T, Us>...> {};
template <class T>
struct member_tratis {};
template <class T, class Owner>
struct member_tratis<T Owner::*> {
using owner_type = Owner;
using value_type = T;
};
template <typename T>
inline constexpr bool is_int64_v =
std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>;
template <typename T>
struct is_variant : std::false_type {};
template <typename... T>
struct is_variant<std::variant<T...>> : std::true_type {};
template <class T>
struct member_traits {
using value_type = T;
};
template <class T, class Owner>
struct member_traits<T Owner::*> {
using owner_type = Owner;
using value_type = T;
};
template <class T>
using member_value_type_t = typename member_traits<T>::value_type;
template <typename T, std::size_t I, typename = void>
struct variant_type_at {
using type = T;
};
template <typename T, std::size_t I>
struct variant_type_at<T, I, std::enable_if_t<is_variant<T>::value>> {
using type = std::variant_alternative_t<I, T>;
};
template <typename T, std::size_t I>
using variant_type_at_t =
typename variant_type_at<typename member_traits<T>::value_type, I>::type;
/// concepts
template <typename Type>
constexpr bool is_char_v =
std::is_same_v<Type, signed char> || std::is_same_v<Type, char> ||
std::is_same_v<Type, unsigned char> || std::is_same_v<Type, wchar_t> ||
std::is_same_v<Type, char16_t> || std::is_same_v<Type, char32_t>
#ifdef __cpp_lib_char8_t
|| std::is_same_v<Type, char8_t>
#endif
;
#if __cplusplus >= 202002L
template <typename Type>
concept is_container_v = requires(Type container) {
typename std::remove_cvref_t<Type>::value_type;
container.size();
container.begin();
container.end();
};
template <typename Type>
concept is_resizable_char_container_v = requires(Type container) {
typename std::remove_cvref_t<Type>::value_type;
container.size();
container.begin();
container.end();
requires is_char_v<typename std::remove_cvref_t<Type>::value_type>;
container.resize(std::size_t{});
};
template <typename T>
concept char_writer = requires(T t) {
t.write((const char *)nullptr, std::size_t{});
};
template <typename T>
concept char_reader = requires(T t) {
t.read((char *)nullptr, std::size_t{});
};
#else
template <typename T, typename = void>
struct container_impl : std::false_type {};
template <typename T>
struct container_impl<T,
std::void_t<typename std::remove_cvref_t<T>::value_type,
decltype(std::declval<T>().size()),
decltype(std::declval<T>().begin()),
decltype(std::declval<T>().end())>>
: std::true_type {};
template <typename T>
constexpr bool is_container_v = container_impl<T>::value;
template <typename T, typename = void>
struct resizable_char_container_impl : std::false_type {};
template <typename T>
struct resizable_char_container_impl<
T, std::void_t<typename std::remove_cvref_t<T>::value_type,
decltype(std::declval<T>().size()),
decltype(std::declval<T>().begin()),
decltype(std::declval<T>().end()),
std::enable_if_t<
is_char_v<typename std::remove_cvref_t<T>::value_type>>,
decltype(std::declval<T>().resize(std::size_t{}))>>
: std::true_type {};
template <typename T>
constexpr bool is_resizable_char_container_v =
resizable_char_container_impl<T>::value;
template <typename T, typename = void>
struct char_writer_impl : std::false_type {};
template <typename T>
struct char_writer_impl<T, std::void_t<decltype(std::declval<T>().write(
(const char *)nullptr, std::size_t{}))>>
: std::true_type {};
template <typename T>
constexpr bool char_writer = char_writer_impl<T>::value;
template <typename T, typename = void>
struct char_reader_impl : std::false_type {};
template <typename T>
struct char_reader_impl<T, std::void_t<decltype(std::declval<T>().read(
(char *)nullptr, std::size_t{}))>>
: std::true_type {};
template <typename T>
constexpr bool char_reader = char_reader_impl<T>::value;
#endif
} // namespace iguana
#endif // SERIALIZE_TRAITS_HPP
+154
View File
@@ -0,0 +1,154 @@
#pragma once
#include <cassert>
#include <stdexcept>
#include "iguana/define.h"
namespace iguana {
// https://github.com/Tencent/rapidjson/blob/master/include/rapidjson/reader.h
template <typename Ch = char, typename It>
inline unsigned parse_unicode_hex4(It &&it) {
unsigned codepoint = 0;
for (int i = 0; i < 4; i++) {
Ch c = *it;
codepoint <<= 4;
codepoint += static_cast<unsigned>(c);
if (c >= '0' && c <= '9')
codepoint -= '0';
else if (c >= 'A' && c <= 'F')
codepoint -= 'A' - 10;
else if (c >= 'a' && c <= 'f')
codepoint -= 'a' - 10;
else {
throw std::runtime_error("Invalid Unicode Escape Hex");
}
++it;
}
return codepoint;
}
// https://github.com/Tencent/rapidjson/blob/master/include/rapidjson/encodings.h
template <typename Ch = char, typename OutputStream>
inline void encode_utf8(OutputStream &os, unsigned codepoint) {
if (codepoint > 0x10FFFF)
IGUANA_UNLIKELY {
throw std::runtime_error("Invalid unicode codepoint:" +
std::to_string(codepoint));
}
if (codepoint <= 0x7F)
os.push_back(static_cast<Ch>(codepoint & 0xFF));
else if (codepoint <= 0x7FF) {
os.push_back(static_cast<Ch>(0xC0 | ((codepoint >> 6) & 0xFF)));
os.push_back(static_cast<Ch>(0x80 | ((codepoint & 0x3F))));
}
else if (codepoint <= 0xFFFF) {
os.push_back(static_cast<Ch>(0xE0 | ((codepoint >> 12) & 0xFF)));
os.push_back(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
os.push_back(static_cast<Ch>(0x80 | (codepoint & 0x3F)));
}
else {
os.push_back(static_cast<Ch>(0xF0 | ((codepoint >> 18) & 0xFF)));
os.push_back(static_cast<Ch>(0x80 | ((codepoint >> 12) & 0x3F)));
os.push_back(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
os.push_back(static_cast<Ch>(0x80 | (codepoint & 0x3F)));
}
}
// https://github.com/Tencent/rapidjson/blob/master/include/rapidjson/encodings.h
static inline unsigned char GetRange(unsigned char c) {
static const unsigned char type[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0x10, 0x10, 0x10, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
0x40, 0x40, 0x40, 0x40, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 10, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3,
11, 6, 6, 6, 5, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8,
};
return type[c];
}
// https://github.com/Tencent/rapidjson/blob/master/include/rapidjson/encodings.h
template <typename Ch = char, typename It>
inline bool decode_utf8(It &&it, unsigned &codepoint) {
auto c = *(it++);
bool result = true;
auto copy = [&]() IGUANA__INLINE_LAMBDA {
c = *(it++);
codepoint = (codepoint << 6) | (static_cast<unsigned char>(c) & 0x3Fu);
};
auto trans = [&](unsigned mask) IGUANA__INLINE_LAMBDA {
result &= ((GetRange(static_cast<unsigned char>(c)) & mask) != 0);
};
auto tail = [&]() IGUANA__INLINE_LAMBDA {
copy();
trans(0x70);
};
if (!(c & 0x80)) {
codepoint = static_cast<unsigned char>(c);
return true;
}
unsigned char type = GetRange(static_cast<unsigned char>(c));
if (type >= 32) {
codepoint = 0;
}
else {
codepoint = (0xFFu >> type) & static_cast<unsigned char>(c);
}
switch (type) {
case 2:
tail();
return result;
case 3:
tail();
tail();
return result;
case 4:
copy();
trans(0x50);
tail();
return result;
case 5:
copy();
trans(0x10);
tail();
tail();
return result;
case 6:
tail();
tail();
tail();
return result;
case 10:
copy();
trans(0x20);
tail();
return result;
case 11:
copy();
trans(0x60);
tail();
tail();
return result;
default:
return false;
}
}
} // namespace iguana
+197
View File
@@ -0,0 +1,197 @@
#pragma once
#include "common.hpp"
namespace iguana {
using base = detail::base;
constexpr inline uint8_t ENABLE_JSON = 0x01;
constexpr inline uint8_t ENABLE_YAML = 0x02;
constexpr inline uint8_t ENABLE_XML = 0x04;
constexpr inline uint8_t ENABLE_PB = 0x08;
constexpr inline uint8_t ENABLE_ALL = 0x0F;
template <typename T, uint8_t ENABLE_FLAG = ENABLE_PB>
struct base_impl : public base {
base_impl() { [[maybe_unused]] static bool r = register_type<T>(); }
void to_pb(std::string& str) const override {
if constexpr ((ENABLE_FLAG & ENABLE_PB) != 0) {
to_pb_adl((iguana_adl_t*)nullptr, *(static_cast<T const*>(this)), str);
}
else {
throw std::runtime_error("Protobuf Disabled");
}
}
void from_pb(std::string_view str) override {
if constexpr ((ENABLE_FLAG & ENABLE_PB) != 0) {
from_pb_adl((iguana_adl_t*)nullptr, *(static_cast<T*>(this)), str);
}
else {
throw std::runtime_error("Protobuf Disabled");
}
}
void to_json(std::string& str) const override {
if constexpr ((ENABLE_FLAG & ENABLE_JSON) != 0) {
to_json_adl((iguana_adl_t*)nullptr, *(static_cast<T const*>(this)), str);
}
else {
throw std::runtime_error("Json Disabled");
}
}
void from_json(std::string_view str) override {
if constexpr ((ENABLE_FLAG & ENABLE_JSON) != 0) {
from_json_adl((iguana_adl_t*)nullptr, *(static_cast<T*>(this)), str);
}
else {
throw std::runtime_error("Json Disabled");
}
}
void to_xml(std::string& str) const override {
if constexpr ((ENABLE_FLAG & ENABLE_XML) != 0) {
to_xml_adl((iguana_adl_t*)nullptr, *(static_cast<T const*>(this)), str);
}
else {
throw std::runtime_error("Xml Disabled");
}
}
void from_xml(std::string_view str) override {
if constexpr ((ENABLE_FLAG & ENABLE_XML) != 0) {
from_xml_adl((iguana_adl_t*)nullptr, *(static_cast<T*>(this)), str);
}
else {
throw std::runtime_error("Xml Disabled");
}
}
void to_yaml(std::string& str) const override {
if constexpr ((ENABLE_FLAG & ENABLE_YAML) != 0) {
to_yaml_adl((iguana_adl_t*)nullptr, *(static_cast<T const*>(this)), str);
}
else {
throw std::runtime_error("Yaml Disabled");
}
}
void from_yaml(std::string_view str) override {
if constexpr ((ENABLE_FLAG & ENABLE_YAML) != 0) {
from_yaml_adl((iguana_adl_t*)nullptr, *(static_cast<T*>(this)), str);
}
else {
throw std::runtime_error("Yaml Disabled");
}
}
iguana::detail::field_info get_field_info(
std::string_view name) const override {
static auto map =
detail::get_members(ylt::reflection::internal::wrapper<T>::value);
iguana::detail::field_info info{};
for (auto const& [no, field] : map) {
if (info.offset > 0) {
break;
}
std::visit(
[&](auto const& val) {
if (val.field_name == name) {
info.offset = val.offset;
using value_type =
typename std::remove_reference_t<decltype(val)>::value_type;
#if defined(__clang__) || defined(_MSC_VER) || \
(defined(__GNUC__) && __GNUC__ > 8)
info.type_name = type_string<value_type>();
#endif
}
},
field);
}
return info;
}
std::vector<std::string_view> get_fields_name() const override {
static auto map =
detail::get_members(ylt::reflection::internal::wrapper<T>::value);
std::vector<std::string_view> vec;
for (auto const& [no, val] : map) {
std::visit(
[&](auto const& field) {
std::string_view const current_name{field.field_name.data(),
field.field_name.size()};
if (vec.empty() || (vec.back() != current_name)) {
vec.push_back(current_name);
}
},
val);
}
return vec;
}
std::any get_field_any(std::string_view name) const override {
static auto map =
detail::get_members(ylt::reflection::internal::wrapper<T>::value);
std::any result;
for (auto const& [no, field] : map) {
if (result.has_value()) {
break;
}
std::visit(
[&](auto const& val) {
if (val.field_name == name) {
using value_type =
typename std::remove_reference_t<decltype(val)>::value_type;
auto ptr =
reinterpret_cast<const char*>(static_cast<const T*>(this)) +
val.offset;
result = *((value_type*)ptr);
}
},
field);
}
return result;
}
virtual ~base_impl() {}
mutable size_t cache_size = 0;
protected:
void* object_ptr() override { return static_cast<T*>(this); }
const void* object_ptr() const override {
return static_cast<const T*>(this);
}
private:
virtual void dummy() {
// make sure init T before main, and can register_type before main.
[[maybe_unused]] static auto t =
ylt::reflection::internal::wrapper<T>::value;
}
};
IGUANA_INLINE std::shared_ptr<base> create_instance(std::string_view name) {
auto it = iguana::detail::g_pb_map.find(name);
if (it == iguana::detail::g_pb_map.end()) {
throw std::invalid_argument(std::string(name) +
"not inheried from iguana::base_impl");
}
return it->second();
}
} // namespace iguana
#ifdef YLT_USE_CXX26_REFLECTION
namespace ylt::reflection::reflect26 {
template <>
constexpr inline bool skip_base_v<iguana::detail::base> = true;
template <typename T, uint8_t ENABLE_FLAG>
constexpr inline bool skip_base_v<iguana::base_impl<T, ENABLE_FLAG>> = true;
} // namespace ylt::reflection::reflect26
#endif
+100
View File
@@ -0,0 +1,100 @@
#pragma once
#include <cassert>
#include <map>
#include <system_error>
namespace iguana {
class iguana_category : public std::error_category {
public:
virtual const char *name() const noexcept override {
return "iguana::category";
}
virtual std::string message(int err_val) const override {
for (auto &pair : err_map_) {
if (pair.second == err_val) {
return pair.first;
}
}
return "unrecognized error";
}
int add_message(const std::string &msg) {
if (auto it = err_map_.find(msg); it != err_map_.end()) {
return it->second;
}
else {
err_++;
err_map_.emplace(msg, err_);
return err_;
}
}
int err_ = 0;
std::map<std::string, int> err_map_;
};
inline iguana::iguana_category &category() {
static iguana::iguana_category instance;
return instance;
}
inline std::error_code make_error_code(const std::string data = "") {
assert(!data.empty());
auto err = iguana::category().add_message(data);
return std::error_code(err, iguana::category());
}
// dom parse error code
enum class dom_errc {
ok = 0,
wrong_type,
};
class iguana_dom_category : public std::error_category {
public:
virtual const char *name() const noexcept override {
return "iguana::dom_category";
}
virtual std::string message(int err_val) const override {
switch (static_cast<dom_errc>(err_val)) {
case dom_errc::ok:
return "ok";
case dom_errc::wrong_type: {
auto it = detail_msg_map_.find(dom_errc::wrong_type);
if (it != detail_msg_map_.end()) {
return std::string("wrong type, ")
.append("real type is ")
.append(it->second);
}
else {
return "wrong type";
}
}
default:
return "(unrecognized error)";
}
}
// private:
std::map<iguana::dom_errc, std::string> detail_msg_map_;
};
inline iguana::iguana_dom_category &dom_category() {
static iguana::iguana_dom_category instance;
return instance;
}
inline std::error_code make_error_code(iguana::dom_errc err,
const std::string &error_msg) {
auto &instance = iguana::dom_category();
if (!error_msg.empty()) {
instance.detail_msg_map_.emplace(err, error_msg);
}
return std::error_code((int)err, instance);
}
} // namespace iguana
+192
View File
@@ -0,0 +1,192 @@
#pragma once
#include <array>
#include <string_view>
#include "frozen/string.h"
#include "frozen/unordered_map.h"
namespace iguana {
#if defined(__clang__) || defined(_MSC_VER) || \
(defined(__GNUC__) && __GNUC__ > 8)
template <typename T>
constexpr std::string_view get_raw_name() {
#ifdef _MSC_VER
return __FUNCSIG__;
#else
return __PRETTY_FUNCTION__;
#endif
}
template <auto T>
constexpr std::string_view get_raw_name() {
#ifdef _MSC_VER
return __FUNCSIG__;
#else
return __PRETTY_FUNCTION__;
#endif
}
template <typename T>
inline constexpr std::string_view type_string() {
constexpr std::string_view sample = get_raw_name<int>();
constexpr size_t pos = sample.find("int");
constexpr std::string_view str = get_raw_name<T>();
constexpr auto next1 = str.rfind(sample[pos + 3]);
#if defined(_MSC_VER)
constexpr std::size_t npos = str.find_first_of(" ", pos);
if (npos != std::string_view::npos)
return str.substr(npos + 1, next1 - npos - 1);
else
return str.substr(pos, next1 - pos);
#else
return str.substr(pos, next1 - pos);
#endif
}
template <auto T>
inline constexpr std::string_view enum_string() {
constexpr std::string_view sample = get_raw_name<int>();
constexpr size_t pos = sample.find("int");
constexpr std::string_view str = get_raw_name<T>();
constexpr auto next1 = str.rfind(sample[pos + 3]);
#if defined(__clang__) || defined(_MSC_VER)
constexpr auto s1 = str.substr(pos, next1 - pos);
#else
constexpr auto s1 = str.substr(pos + 5, next1 - pos - 5);
#endif
return s1;
}
template <auto field>
inline constexpr std::string_view field_string() {
constexpr std::string_view raw_name = enum_string<field>();
return raw_name.substr(raw_name.rfind(":") + 1);
}
#if defined(__clang__) && (__clang_major__ >= 17)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wenum-constexpr-conversion"
#endif
template <typename E, E V>
constexpr std::string_view get_raw_name_with_v() {
#ifdef _MSC_VER
return __FUNCSIG__;
#else
return __PRETTY_FUNCTION__;
#endif
}
// True means the V is a valid enum value, and the second of the result is the
// name
template <typename E, E V>
constexpr std::pair<bool, std::string_view> try_get_enum_name() {
constexpr std::string_view sample_raw_name = get_raw_name_with_v<int, 5>();
constexpr size_t pos = sample_raw_name.find("5");
constexpr std::string_view enum_raw_name = get_raw_name_with_v<E, V>();
constexpr auto enum_end = enum_raw_name.rfind(&sample_raw_name[pos + 1]);
#ifdef _MSC_VER
constexpr auto enum_begin = enum_raw_name.rfind(',', enum_end) + 1;
#else
constexpr auto enum_begin = enum_raw_name.rfind(' ', enum_end) + 1;
#endif
constexpr std::string_view res =
enum_raw_name.substr(enum_begin, enum_end - enum_begin);
constexpr size_t pos_brackets = res.find(')');
size_t pos_colon = res.find("::");
return {pos_brackets == std::string_view::npos,
res.substr(pos_colon == std::string_view::npos ? 0 : pos_colon + 2)};
}
// Enumerate the numbers in a integer sequence to see if they are legal enum
// value
template <typename E, std::int64_t... Is>
constexpr inline auto get_enum_arr(
const std::integer_sequence<std::int64_t, Is...> &) {
constexpr std::size_t N = sizeof...(Is);
std::array<std::string_view, N> enum_names = {};
std::array<E, N> enum_values = {};
std::size_t num = 0;
(([&]() {
constexpr auto res = try_get_enum_name<E, static_cast<E>(Is)>();
if constexpr (res.first) {
// the Is is a valid enum value
enum_names[num] = res.second;
enum_values[num] = static_cast<E>(Is);
++num;
}
})(),
...);
return std::make_tuple(num, enum_values, enum_names);
}
template <std::size_t N, const std::array<int, N> &arr, size_t... Is>
constexpr auto array_to_seq(const std::index_sequence<Is...> &) {
return std::integer_sequence<std::int64_t, arr[Is]...>();
}
// convert array to map
template <typename E, size_t N, size_t... Is>
constexpr inline auto get_enum_to_str_map(
const std::array<std::string_view, N> &enum_names,
const std::array<E, N> &enum_values, const std::index_sequence<Is...> &) {
return frozen::unordered_map<E, frozen::string, sizeof...(Is)>{
{enum_values[Is], enum_names[Is]}...};
}
template <typename E, size_t N, size_t... Is>
constexpr inline auto get_str_to_enum_map(
const std::array<std::string_view, N> &enum_names,
const std::array<E, N> &enum_values, const std::index_sequence<Is...> &) {
return frozen::unordered_map<frozen::string, E, sizeof...(Is)>{
{enum_names[Is], enum_values[Is]}...};
}
#endif
// the default generic enum_value
// if the user has not defined a specialization template, this will be called
template <typename T>
struct enum_value {
constexpr static std::array<int, 0> value = {};
};
template <bool str_to_enum, typename E>
constexpr inline auto get_enum_map() {
#if defined(__clang__) || defined(_MSC_VER) || \
(defined(__GNUC__) && __GNUC__ > 8)
constexpr auto &arr = enum_value<E>::value;
constexpr auto arr_size = arr.size();
if constexpr (arr_size > 0) {
// the user has defined a specialization template
constexpr auto arr_seq =
array_to_seq<arr_size, arr>(std::make_index_sequence<arr_size>());
constexpr auto t = get_enum_arr<E>(arr_seq);
if constexpr (str_to_enum) {
return get_str_to_enum_map<E, arr_size>(
std::get<2>(t), std::get<1>(t),
std::make_index_sequence<std::get<0>(t)>{});
}
else {
return get_enum_to_str_map<E, arr_size>(
std::get<2>(t), std::get<1>(t),
std::make_index_sequence<std::get<0>(t)>{});
}
}
else {
return false;
}
#else
return false;
#endif
}
#if defined(__clang__) && (__clang_major__ >= 17)
#pragma clang diagnostic pop
#endif
} // namespace iguana
+10
View File
@@ -0,0 +1,10 @@
#pragma once
#include "iguana/dynamic.hpp"
#include "iguana/json_reader.hpp"
#include "iguana/json_writer.hpp"
#include "iguana/pb_reader.hpp"
#include "iguana/pb_writer.hpp"
#include "iguana/xml_reader.hpp"
#include "iguana/xml_writer.hpp"
#include "iguana/yaml_reader.hpp"
#include "iguana/yaml_writer.hpp"
+989
View File
@@ -0,0 +1,989 @@
#pragma once
#include "detail/utf.hpp"
#include "error_code.h"
#include "json_util.hpp"
#ifdef YLT_USE_CXX26_REFLECTION
#include "ylt/reflection/reflect26_dispatch.hpp"
#endif
namespace iguana {
template <typename T, typename It,
std::enable_if_t<ylt_refletable_v<T>, int> = 0>
IGUANA_INLINE void from_json(T &value, It &&it, It &&end);
template <typename T, typename View,
std::enable_if_t<ylt_refletable_v<T>, int> = 0>
IGUANA_INLINE void from_json(T &value, const View &view);
template <typename T, typename View,
std::enable_if_t<non_ylt_refletable_v<T>, int> = 0>
IGUANA_INLINE void from_json(T &value, const View &view);
namespace detail {
template <typename U, typename It, std::enable_if_t<optional_v<U>, int> = 0>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end);
template <typename U, typename It, std::enable_if_t<tuple_v<U>, int> = 0>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end);
template <typename U, typename It,
std::enable_if_t<sequence_container_v<U>, int> = 0>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end);
template <typename U, typename It, std::enable_if_t<smart_ptr_v<U>, int> = 0>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end);
template <typename U, typename It, std::enable_if_t<variant_v<U>, int> = 0>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end);
template <typename U, typename It,
std::enable_if_t<ylt_refletable_v<U>, int> = 0>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
from_json(value, it, end);
}
template <typename U, typename It, std::enable_if_t<string_v<U>, int> = 0>
IGUANA_INLINE void parse_escape(U &value, It &&it, It &&end) {
if (it == end)
IGUANA_UNLIKELY { throw std::runtime_error(R"(Expected ")"); }
if (*it == 'u') {
++it;
if (std::distance(it, end) <= 4)
IGUANA_UNLIKELY {
throw std::runtime_error(R"(Expected 4 hexadecimal digits)");
}
auto code_point = parse_unicode_hex4(it);
encode_utf8(value, code_point);
}
else if (*it == 'n') {
++it;
value.push_back('\n');
}
else if (*it == 't') {
++it;
value.push_back('\t');
}
else if (*it == 'r') {
++it;
value.push_back('\r');
}
else if (*it == 'b') {
++it;
value.push_back('\b');
}
else if (*it == 'f') {
++it;
value.push_back('\f');
}
else {
value.push_back(*it); // add the escaped character
++it;
}
}
template <typename U, typename It, std::enable_if_t<num_v<U>, int> = 0>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
skip_ws(it, end);
if (it != end && *it == 'n') {
++it;
match<'u', 'l', 'l'>(it, end);
return;
}
if constexpr (contiguous_iterator<std::decay_t<It>>) {
const auto size = std::distance(it, end);
if (size == 0)
IGUANA_UNLIKELY { throw std::runtime_error("Failed to parse number"); }
const auto start = &*it;
auto [p, ec] = detail::from_chars<false>(start, start + size, value);
if (ec != std::errc{} || !can_follow_number(*p))
IGUANA_UNLIKELY { throw std::runtime_error("Failed to parse number"); }
it += (p - &*it);
}
else {
char buffer[256];
size_t i{};
while (it != end && is_numeric(*it)) {
if (i > 254)
IGUANA_UNLIKELY { throw std::runtime_error("Number is too long"); }
buffer[i] = *it++;
++i;
}
detail::from_chars(buffer, buffer + i, value);
}
}
// template <typename U, typename It, std::enable_if_t<is_pb_type_v<U>, int> =
// 0> IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
// from_json_impl(value.val, it, end);
// }
template <typename U, typename It, std::enable_if_t<numeric_str_v<U>, int> = 0>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
skip_ws(it, end);
auto start = it;
while (it != end && is_numeric(*it)) {
++it;
}
value.value() =
std::string_view(&*start, static_cast<size_t>(std::distance(start, it)));
}
template <bool skip = false, typename U, typename It,
std::enable_if_t<char_v<U>, int> = 0>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
if constexpr (!skip) {
skip_ws(it, end);
match<'"'>(it, end);
}
if (it == end)
IGUANA_UNLIKELY { throw std::runtime_error("Unxpected end of buffer"); }
if (*it == '\\')
IGUANA_UNLIKELY {
if (++it == end)
IGUANA_UNLIKELY { throw std::runtime_error("Unxpected end of buffer"); }
else if (*it == 'n') {
value = '\n';
}
else if (*it == 't') {
value = '\t';
}
else if (*it == 'r') {
value = '\r';
}
else if (*it == 'b') {
value = '\b';
}
else if (*it == 'f') {
value = '\f';
}
else
IGUANA_UNLIKELY { value = *it; }
}
else {
value = *it;
}
++it;
if constexpr (!skip) {
match<'"'>(it, end);
}
}
template <typename U, typename It, std::enable_if_t<bool_v<U>, int> = 0>
IGUANA_INLINE void from_json_impl(U &&value, It &&it, It &&end) {
skip_ws(it, end);
if (it < end)
IGUANA_LIKELY {
switch (*it) {
case 't':
++it;
match<'r', 'u', 'e'>(it, end);
value = true;
break;
case 'f':
++it;
match<'a', 'l', 's', 'e'>(it, end);
value = false;
break;
case 'n':
++it;
match<'u', 'l', 'l'>(it, end);
break;
IGUANA_UNLIKELY default
: throw std::runtime_error("Expected true or false");
}
}
else
IGUANA_UNLIKELY { throw std::runtime_error("Expected true or false"); }
}
template <bool skip = false, typename U, typename It,
std::enable_if_t<string_v<U>, int> = 0>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
if constexpr (!skip) {
skip_ws(it, end);
// handle JSON null: leave value empty and skip "null"
if (it != end && *it == 'n') {
++it;
match<'u', 'l', 'l'>(it, end);
value.clear();
return;
}
match<'"'>(it, end);
}
value.clear();
if constexpr (contiguous_iterator<std::decay_t<It>>) {
auto start = it;
while (it < end) {
skip_till_escape_or_qoute(it, end);
if (*it == '"') {
value.append(&*start, static_cast<size_t>(std::distance(start, it)));
++it;
return;
}
else {
// Must be an escape
value.append(&*start, static_cast<size_t>(std::distance(start, it)));
++it; // skip first escape
parse_escape(value, it, end);
start = it;
}
}
}
else {
while (it != end) {
switch (*it) {
IGUANA_UNLIKELY case '\\':
++it;
parse_escape(value, it, end);
break;
// IGUANA_UNLIKELY case ']' : return;
IGUANA_UNLIKELY case '"':
++it;
return;
IGUANA_LIKELY default : value.push_back(*it);
++it;
}
}
}
}
template <bool skip = false, typename U, typename It,
std::enable_if_t<string_view_v<U>, int> = 0>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
static_assert(contiguous_iterator<std::decay_t<It>>, "must be contiguous");
if constexpr (!skip) {
skip_ws(it, end);
match<'"'>(it, end);
}
using T = std::decay_t<U>;
auto start = it;
while (it != end) {
skip_till_qoute(it, end);
if (*(it - 1) != '\\') {
value = T(&*start, static_cast<size_t>(std::distance(start, it)));
++it;
return;
}
++it;
}
throw std::runtime_error("Expected \"");
}
template <typename U, typename It, std::enable_if_t<enum_v<U>, int> = 0>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
static constexpr auto str_to_enum = get_enum_map<true, std::decay_t<U>>();
if constexpr (bool_v<decltype(str_to_enum)>) {
// not defined a specialization template
using T = std::underlying_type_t<std::decay_t<U>>;
from_json_impl(reinterpret_cast<T &>(value), it, end);
}
else {
std::string_view enum_names;
from_json_impl(enum_names, it, end);
auto it = str_to_enum.find(enum_names);
if (it != str_to_enum.end())
IGUANA_LIKELY { value = it->second; }
else {
throw std::runtime_error(std::string(enum_names) +
" missing corresponding value in enum_value");
}
}
}
template <typename T>
constexpr size_t get_array_size(const T &t) {
#if __cplusplus > 201703L
#if __has_include(<span>)
if constexpr (is_span<T>::value)
return t.size();
else
#endif
#endif
return sizeof(T) / sizeof(decltype(std::declval<T>()[0]));
}
template <typename U, typename It, std::enable_if_t<fixed_array_v<U>, int> = 0>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
using T = std::remove_reference_t<U>;
size_t n = get_array_size(value);
skip_ws(it, end);
if constexpr (std::is_same_v<char, std::remove_reference_t<
decltype(std::declval<T>()[0])>>) {
if (*it == '"') {
match<'"'>(it, end);
auto value_it = std::begin(value);
for (size_t i = 0; i < n; ++i) {
if (*it != '"')
IGUANA_LIKELY { from_json_impl<true>(*value_it++, it, end); }
}
match<'"'>(it, end);
return;
}
}
match<'['>(it, end);
skip_ws(it, end);
if (it == end)
IGUANA_UNLIKELY { throw std::runtime_error("Unexpected end"); }
if (*it == ']')
IGUANA_UNLIKELY {
++it;
return;
}
auto value_it = std::begin(value);
for (size_t i = 0; i < n; ++i) {
from_json_impl(*value_it++, it, end);
skip_ws(it, end);
if (it == end) {
throw std::runtime_error("Unexpected end");
}
if (*it == ',')
IGUANA_LIKELY {
++it;
skip_ws(it, end);
}
else if (*it == ']') {
++it;
return;
}
else
IGUANA_UNLIKELY { throw std::runtime_error("Expected ]"); }
}
}
template <typename U, typename It,
std::enable_if_t<sequence_container_v<U>, int>>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
value.clear();
skip_ws(it, end);
match<'['>(it, end);
skip_ws(it, end);
for (size_t i = 0; it != end; ++i) {
if (*it == ']')
IGUANA_UNLIKELY {
++it;
return;
}
if (i > 0)
IGUANA_LIKELY { match<','>(it, end); }
from_json_impl(value.emplace_back(), it, end);
skip_ws(it, end);
}
throw std::runtime_error("Expected ]");
}
template <typename It>
IGUANA_INLINE auto get_key(It &&it, It &&end) {
if constexpr (contiguous_iterator<std::decay_t<It>>) {
// skip white space and escape characters and find the string
skip_ws(it, end);
match<'"'>(it, end);
auto start = it;
skip_till_escape_or_qoute(it, end);
if (*it == '\\')
IGUANA_UNLIKELY {
// we dont' optimize this currently because it would increase binary
// size significantly with the complexity of generating escaped
// compile time versions of keys
it = start;
static thread_local std::string static_key{};
detail::from_json_impl<true>(static_key, it, end);
return std::string_view(static_key);
}
else
IGUANA_LIKELY {
auto key = std::string_view{
&*start, static_cast<size_t>(std::distance(start, it))};
++it;
if (key[0] == '@')
IGUANA_UNLIKELY { return key.substr(1); }
return key;
}
}
else {
static thread_local std::string static_key{};
detail::from_json_impl(static_key, it, end);
return std::string_view(static_key);
}
}
template <typename U, typename It,
std::enable_if_t<map_container_v<U>, int> = 0>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
using T = std::remove_reference_t<U>;
using key_type = typename T::key_type;
skip_ws(it, end);
match<'{'>(it, end);
skip_ws(it, end);
bool first = true;
while (it != end) {
if (*it == '}')
IGUANA_UNLIKELY {
++it;
return;
}
else if (first)
IGUANA_UNLIKELY { first = false; }
else
IGUANA_LIKELY { match<','>(it, end); }
std::string_view key = get_key(it, end);
skip_ws(it, end);
match<':'>(it, end);
if constexpr (string_v<key_type> || string_view_v<key_type>) {
from_json_impl(value[key_type(key)], it, end);
}
else {
static thread_local key_type key_value{};
from_json_impl(key_value, key.begin(), key.end());
from_json_impl(value[key_value], it, end);
}
skip_ws(it, end);
}
}
template <typename U, typename It, std::enable_if_t<tuple_v<U>, int>>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
skip_ws(it, end);
match<'['>(it, end);
skip_ws(it, end);
foreach_tuple(
[&](auto &v, auto i) IGUANA__INLINE_LAMBDA {
constexpr auto I = decltype(i)::value;
if (it == end || *it == ']') {
return;
}
if constexpr (I != 0) {
match<','>(it, end);
skip_ws(it, end);
}
from_json_impl(v, it, end);
skip_ws(it, end);
},
value);
match<']'>(it, end);
}
template <typename U, typename It, std::enable_if_t<optional_v<U>, int>>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
skip_ws(it, end);
using T = std::remove_reference_t<U>;
if (it == end)
IGUANA_UNLIKELY { throw std::runtime_error("Unexexpected eof"); }
if (*it == 'n') {
++it;
match<'u', 'l', 'l'>(it, end);
if constexpr (!std::is_pointer_v<T>) {
value.reset();
if (it < end && *it == '"') {
++it;
}
}
}
else {
using value_type = typename T::value_type;
value_type t{};
if constexpr (string_v<value_type> || string_view_v<value_type>) {
if (it < end && *it == '"')
IGUANA_LIKELY { ++it; }
from_json_impl<true>(t, it, end);
}
else {
from_json_impl(t, it, end);
}
value = std::move(t);
}
}
template <typename U, typename It, std::enable_if_t<smart_ptr_v<U>, int>>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
skip_ws(it, end);
if (it == end)
IGUANA_UNLIKELY { throw std::runtime_error("Unexexpected eof"); }
if (*it == 'n') {
++it;
match<'u', 'l', 'l'>(it, end);
}
else {
using value_type = typename std::remove_reference_t<U>::element_type;
if constexpr (unique_ptr_v<U>) {
value = std::make_unique<value_type>();
}
else {
value = std::make_shared<value_type>();
}
from_json_impl(*value, it, end);
}
}
template <typename It>
IGUANA_INLINE void skip_object_value(It &&it, It &&end) {
skip_ws(it, end);
while (it != end) {
switch (*it) {
case '{':
skip_until_closed<'{', '}'>(it, end);
break;
case '[':
skip_until_closed<'[', ']'>(it, end);
break;
case '"':
skip_string(it, end);
break;
case '/':
skip_comment(it, end);
continue;
case ',':
case '}':
case ']':
break;
default: {
++it;
continue;
}
}
break;
}
}
template <typename value_type, typename U, typename It>
IGUANA_INLINE bool from_json_variant_impl(U &value, It it, It end, It &temp_it,
It &temp_end) {
try {
value_type val{};
from_json_impl(val, it, end);
value = val;
temp_it = it;
temp_end = end;
return true;
} catch (std::exception &ex) {
return false;
}
}
template <typename U, typename It, size_t... Idx>
IGUANA_INLINE void from_json_variant(U &value, It &it, It &end,
std::index_sequence<Idx...>) {
static_assert(!has_duplicate_type_v<std::remove_reference_t<U>>,
"don't allow same type in std::variant");
bool r = false;
It temp_it = it;
It temp_end = end;
((void)(!r && (r = from_json_variant_impl<
variant_element_t<Idx, std::remove_reference_t<U>>>(
value, it, end, temp_it, temp_end),
true)),
...);
it = temp_it;
end = temp_end;
}
template <typename U, typename It, std::enable_if_t<variant_v<U>, int>>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
from_json_variant(value, it, end,
std::make_index_sequence<
std::variant_size_v<std::remove_reference_t<U>>>{});
}
} // namespace detail
#ifdef YLT_USE_CXX26_REFLECTION
template <typename T, typename It, std::enable_if_t<ylt_refletable_v<T>, int>>
IGUANA_INLINE void from_json(T &value, It &&it, It &&end) {
skip_ws(it, end);
match<'{'>(it, end);
skip_ws(it, end);
if (*it == '}')
IGUANA_UNLIKELY {
++it;
return;
}
while (it != end) {
std::string_view key = detail::get_key(it, end);
skip_ws(it, end);
match<':'>(it, end);
bool found = ylt::reflection::reflect26::dispatch_by_name(
value, key, [&](auto &field) IGUANA__INLINE_LAMBDA {
using namespace detail;
from_json_impl(field, it, end);
});
if (!found)
IGUANA_UNLIKELY {
#ifdef THROW_UNKNOWN_KEY
throw std::runtime_error("Unknown key: " + std::string(key));
#else
detail::skip_object_value(it, end);
#endif
}
skip_ws(it, end);
if (*it == '}')
IGUANA_UNLIKELY {
++it;
return;
}
else
IGUANA_LIKELY { match<','>(it, end); }
}
}
#endif
#ifndef YLT_USE_CXX26_REFLECTION
template <typename T, typename It, std::enable_if_t<ylt_refletable_v<T>, int>>
IGUANA_INLINE void from_json(T &value, It &&it, It &&end) {
skip_ws(it, end);
match<'{'>(it, end);
skip_ws(it, end);
if (*it == '}')
IGUANA_UNLIKELY {
++it;
return;
}
using U = ylt::reflection::remove_cvref_t<T>;
constexpr auto Count = ylt::reflection::members_count_v<U>;
if constexpr (Count > 0) {
std::string_view key = detail::get_key(it, end);
#ifdef SEQUENTIAL_PARSE
bool parse_done = false;
ylt::reflection::for_each(
value, [&](auto &field, auto name, auto index) IGUANA__INLINE_LAMBDA {
if (parse_done || (key != name))
IGUANA_UNLIKELY { return; }
skip_ws(it, end);
match<':'>(it, end);
{
using namespace detail;
from_json_impl(field, it, end);
}
skip_ws(it, end);
if (*it == '}')
IGUANA_UNLIKELY {
++it;
parse_done = true;
return;
}
else
IGUANA_LIKELY { match<','>(it, end); }
key = detail::get_key(it, end);
});
if (parse_done) [[unlikely]] {
return;
}
#endif
while (it != end) {
static auto frozen_map = ylt::reflection::get_variant_map<U>();
if (frozen_map.size() > 0) {
const auto &member_it = frozen_map.find(key);
skip_ws(it, end);
match<':'>(it, end);
if (member_it != frozen_map.end())
IGUANA_LIKELY {
std::visit(
[&](auto offset) IGUANA__INLINE_LAMBDA {
using namespace detail;
using value_type = typename decltype(offset)::type;
auto member_ptr =
(value_type *)((char *)(&value) + offset.value);
from_json_impl(*member_ptr, it, end);
},
member_it->second);
}
else
IGUANA_UNLIKELY {
#ifdef THROW_UNKNOWN_KEY
throw std::runtime_error("Unknown key: " + std::string(key));
#else
detail::skip_object_value(it, end);
#endif
}
}
skip_ws(it, end);
if (*it == '}')
IGUANA_UNLIKELY {
++it;
return;
}
else
IGUANA_LIKELY { match<','>(it, end); }
key = detail::get_key(it, end);
}
}
}
#endif // !YLT_USE_CXX26_REFLECTION
template <typename T, typename It,
std::enable_if_t<non_ylt_refletable_v<T>, int> = 0>
IGUANA_INLINE void from_json(T &value, It &&it, It &&end) {
using namespace detail;
from_json_impl(value, it, end);
}
template <typename T, typename It>
IGUANA_INLINE void from_json(T &value, It &&it, It &&end,
std::error_code &ec) noexcept {
try {
from_json(value, it, end);
ec = {};
} catch (std::runtime_error &e) {
ec = iguana::make_error_code(e.what());
}
}
template <typename T, typename View, std::enable_if_t<ylt_refletable_v<T>, int>>
IGUANA_INLINE void from_json(T &value, const View &view) {
from_json(value, std::begin(view), std::end(view));
}
template <typename T, typename View,
std::enable_if_t<non_ylt_refletable_v<T>, int>>
IGUANA_INLINE void from_json(T &value, const View &view) {
from_json(value, std::begin(view), std::end(view));
}
template <typename T, typename View,
std::enable_if_t<json_view_v<View>, int> = 0>
IGUANA_INLINE void from_json(T &value, const View &view,
std::error_code &ec) noexcept {
try {
from_json(value, view);
ec = {};
} catch (std::runtime_error &e) {
ec = iguana::make_error_code(e.what());
}
}
template <typename T, typename Byte,
std::enable_if_t<json_byte_v<Byte>, int> = 0>
IGUANA_INLINE void from_json(T &value, const Byte *data, size_t size) {
std::string_view buffer(data, size);
from_json(value, buffer);
}
template <typename T, typename Byte,
std::enable_if_t<json_byte_v<Byte>, int> = 0>
IGUANA_INLINE void from_json(T &value, const Byte *data, size_t size,
std::error_code &ec) noexcept {
try {
from_json(value, data, size);
ec = {};
} catch (std::runtime_error &e) {
ec = iguana::make_error_code(e.what());
}
}
template <bool Is_view = false, typename It>
void parse(jvalue &result, It &&it, It &&end, bool parse_as_double = false);
template <bool Is_view = false, typename It>
inline void parse(jarray &result, It &&it, It &&end,
bool parse_as_double = false) {
skip_ws(it, end);
match<'['>(it, end);
if (*it == ']')
IGUANA_UNLIKELY {
++it;
return;
}
while (true) {
if (it == end) {
break;
}
result.emplace_back();
parse<Is_view>(result.back(), it, end, parse_as_double);
if (*it == ']')
IGUANA_UNLIKELY {
++it;
return;
}
match<','>(it, end);
}
throw std::runtime_error("Expected ]");
}
template <bool Is_view = false, typename It>
inline void parse(jobject &result, It &&it, It &&end,
bool parse_as_double = false) {
skip_ws(it, end);
match<'{'>(it, end);
if (*it == '}')
IGUANA_UNLIKELY {
++it;
return;
}
skip_ws(it, end);
while (true) {
if (it == end)
IGUANA_UNLIKELY { throw std::runtime_error("Expected }"); }
std::string key;
using namespace detail;
from_json_impl(key, it, end);
auto emplaced = result.try_emplace(key);
if (!emplaced.second)
throw std::runtime_error("duplicated key " + key);
match<':'>(it, end);
parse<Is_view>(emplaced.first->second, it, end, parse_as_double);
if (*it == '}')
IGUANA_UNLIKELY {
++it;
return;
}
match<','>(it, end);
}
}
template <bool Is_view, typename It>
inline void parse(jvalue &result, It &&it, It &&end, bool parse_as_double) {
using namespace detail;
skip_ws(it, end);
switch (*it) {
case 'n':
++it;
match<'u', 'l', 'l'>(it, end);
result.template emplace<std::nullptr_t>();
break;
case 'f':
case 't':
from_json_impl(result.template emplace<bool>(), it, end);
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '-': {
double d{};
from_json_impl(d, it, end);
if (!parse_as_double && (static_cast<int>(d) == d))
result.emplace<int>(d);
else
result.emplace<double>(d);
break;
}
case '"':
if constexpr (Is_view) {
result.template emplace<std::string_view>();
from_json_impl(std::get<std::string_view>(result), it, end);
}
else {
result.template emplace<std::string>();
from_json_impl(std::get<std::string>(result), it, end);
}
break;
case '[':
result.template emplace<jarray>();
parse<Is_view>(std::get<jarray>(result), it, end, parse_as_double);
break;
case '{': {
result.template emplace<jobject>();
parse<Is_view>(std::get<jobject>(result), it, end, parse_as_double);
break;
}
default:
throw std::runtime_error("parse failed");
}
skip_ws(it, end);
}
// set Is_view == true, parse str as std::string_view
// set parse_as_double == true, parse the number as double in any case
template <bool Is_view = false, typename It>
inline void parse(jvalue &result, It &&it, It &&end, std::error_code &ec,
bool parse_as_double = false) {
try {
parse<Is_view>(result, it, end, parse_as_double);
ec = {};
} catch (const std::runtime_error &e) {
result.template emplace<std::nullptr_t>();
ec = iguana::make_error_code(e.what());
}
}
template <bool Is_view = false, typename T, typename View,
std::enable_if_t<json_view_v<View>, int> = 0>
inline void parse(T &result, const View &view, bool parse_as_double = false) {
parse<Is_view>(result, std::begin(view), std::end(view), parse_as_double);
}
template <bool Is_view = false, typename T, typename View,
std::enable_if_t<json_view_v<View>, int> = 0>
inline void parse(T &result, const View &view, std::error_code &ec,
bool parse_as_double = false) noexcept {
try {
parse<Is_view>(result, view, parse_as_double);
ec = {};
} catch (std::runtime_error &e) {
ec = iguana::make_error_code(e.what());
}
}
IGUANA_INLINE std::string json_file_content(const std::string &filename) {
std::error_code ec;
uint64_t size = std::filesystem::file_size(filename, ec);
if (ec) {
throw std::runtime_error("file size error " + ec.message());
}
if (size == 0) {
throw std::runtime_error("empty file");
}
std::string content;
content.resize(size);
std::ifstream file(filename, std::ios::binary);
file.read(content.data(), content.size());
return content;
}
template <typename T>
IGUANA_INLINE void from_json_file(T &value, const std::string &filename) {
std::string content = json_file_content(filename);
from_json(value, content.begin(), content.end());
}
template <typename T>
IGUANA_INLINE void from_json_file(T &value, const std::string &filename,
std::error_code &ec) noexcept {
try {
from_json_file(value, filename);
ec = {};
} catch (std::runtime_error &e) {
ec = iguana::make_error_code(e.what());
}
}
template <typename T>
IGUANA_INLINE void from_json_adl(iguana_adl_t *p, T &t,
std::string_view pb_str) {
iguana::from_json(t, pb_str);
}
} // namespace iguana
+243
View File
@@ -0,0 +1,243 @@
// Glaze Library
// For the license information refer to glaze.hpp
#pragma once
#include "common.hpp"
#include "detail/string_stream.hpp"
#include "util.hpp"
#include "value.hpp"
namespace iguana {
class numeric_str {
public:
std::string_view &value() { return val_; }
std::string_view value() const { return val_; }
template <typename T>
T convert() {
static_assert(num_v<T>, "T must be numeric type");
if (val_.empty())
IGUANA_UNLIKELY { throw std::runtime_error("Failed to parse number"); }
T res;
detail::from_chars(val_.data(), val_.data() + val_.size(), res);
return res;
}
private:
std::string_view val_;
};
template <typename T>
constexpr inline bool numeric_str_v =
std::is_same_v<numeric_str, std::remove_cvref_t<T>>;
template <>
constexpr inline bool reflect26_excluded_v<numeric_str> = true;
template <typename It>
IGUANA_INLINE void skip_comment(It &&it, It &&end) {
++it;
if (it == end)
IGUANA_UNLIKELY {
throw std::runtime_error("Unexpected end, expected comment");
}
else if (*it == '/') {
while (++it != end && *it != '\n')
;
}
else if (*it == '*') {
while (++it != end) {
if (*it == '*')
IGUANA_UNLIKELY {
if (++it == end)
IGUANA_UNLIKELY { break; }
else if (*it == '/')
IGUANA_LIKELY {
++it;
break;
}
}
}
}
else
IGUANA_UNLIKELY throw std::runtime_error("Expected / or * after /");
}
template <typename It>
IGUANA_INLINE void skip_ws(It &&it, It &&end) {
while (it != end) {
// assuming ascii
if (static_cast<uint8_t>(*it) < 33) {
++it;
}
else if (*it == '/') {
skip_comment(it, end);
}
else {
break;
}
}
}
template <typename It>
IGUANA_INLINE void skip_ws_no_comments(It &&it, It &&end) {
while (it != end) {
// assuming ascii
if (static_cast<uint8_t>(*it) < 33)
IGUANA_LIKELY { ++it; }
else {
break;
}
}
}
inline constexpr auto has_escape = [](uint64_t chunk) IGUANA__INLINE_LAMBDA {
return has_zero(
chunk ^
0b0101110001011100010111000101110001011100010111000101110001011100);
};
template <typename It>
IGUANA_INLINE void skip_till_escape_or_qoute(It &&it, It &&end) {
static_assert(contiguous_iterator<std::decay_t<decltype(it)>>);
if (std::distance(it, end) >= 7)
IGUANA_LIKELY {
const auto end_m7 = end - 7;
for (; it < end_m7; it += 8) {
const auto chunk = *reinterpret_cast<const uint64_t *>(&*it);
uint64_t test = has_qoute(chunk) | has_escape(chunk);
if (test != 0) {
it += (countr_zero(test) >> 3);
return;
}
}
}
// Tail end of buffer. Should be rare we even get here
while (it < end) {
switch (*it) {
case '\\':
case '"':
return;
}
++it;
}
throw std::runtime_error("Expected \"");
}
template <typename It>
IGUANA_INLINE void skip_till_qoute(It &&it, It &&end) {
static_assert(contiguous_iterator<std::decay_t<decltype(it)>>);
if (std::distance(it, end) >= 7)
IGUANA_LIKELY {
const auto end_m7 = end - 7;
for (; it < end_m7; it += 8) {
const auto chunk = *reinterpret_cast<const uint64_t *>(&*it);
uint64_t test = has_qoute(chunk);
if (test != 0) {
it += (countr_zero(test) >> 3);
return;
}
}
}
// Tail end of buffer. Should be rare we even get here
while (it < end) {
if (*it == '"')
return;
++it;
}
throw std::runtime_error("Expected \"");
}
template <typename It>
IGUANA_INLINE void skip_string(It &&it, It &&end) noexcept {
++it;
while (it < end) {
if (*it == '"') {
++it;
break;
}
else if (*it == '\\' && ++it == end)
IGUANA_UNLIKELY
break;
++it;
}
}
template <char open, char close, typename It>
IGUANA_INLINE void skip_until_closed(It &&it, It &&end) {
++it;
size_t open_count = 1;
size_t close_count = 0;
while (it < end && open_count > close_count) {
switch (*it) {
case '/':
skip_comment(it, end);
break;
case '"':
skip_string(it, end);
break;
case open:
++open_count;
++it;
break;
case close:
++close_count;
++it;
break;
default:
++it;
}
}
}
IGUANA_INLINE bool is_numeric(char c) noexcept {
static constexpr int is_num[256] = {
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, // 2
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 3
0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5
0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F
};
return static_cast<bool>(is_num[static_cast<unsigned char>(c)]);
}
// '\t' '\r' '\n' '"' '}' ']' ',' ' ' '\0'
IGUANA_INLINE bool can_follow_number(char c) noexcept {
static constexpr int can_follow_num[256] = {
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, // 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, // 2
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, // 7
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F
};
return static_cast<bool>(can_follow_num[static_cast<unsigned char>(c)]);
}
} // namespace iguana
+320
View File
@@ -0,0 +1,320 @@
//
// Created by Qiyu on 17-6-5.
//
#ifndef SERIALIZE_JSON_HPP
#define SERIALIZE_JSON_HPP
#include "json_util.hpp"
namespace iguana {
template <bool Is_writing_escape = true, typename Stream, typename T,
std::enable_if_t<ylt_refletable_v<T>, int> = 0>
IGUANA_INLINE void to_json(T &&t, Stream &s);
namespace detail {
template <bool Is_writing_escape = true, typename Stream, typename T,
std::enable_if_t<optional_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &ss, const T &val);
template <bool Is_writing_escape = true, typename Stream, typename T,
std::enable_if_t<fixed_array_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &ss, const T &t);
template <bool Is_writing_escape = true, typename Stream, typename T,
std::enable_if_t<sequence_container_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &ss, const T &v);
template <bool Is_writing_escape = true, typename Stream, typename T,
std::enable_if_t<pair_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &ss, const T &v);
template <bool Is_writing_escape = true, typename Stream, typename T,
std::enable_if_t<smart_ptr_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &ss, const T &v);
template <bool Is_writing_escape = true, typename Stream, typename T,
std::enable_if_t<map_container_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &ss, const T &o);
template <bool Is_writing_escape = true, typename Stream, typename T,
std::enable_if_t<tuple_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &s, T &&t);
template <bool Is_writing_escape = true, typename Stream, typename T,
std::enable_if_t<variant_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &s, T &&t);
template <typename Stream, typename InputIt, typename T, typename F>
IGUANA_INLINE void join(Stream &ss, InputIt first, InputIt last, const T &delim,
const F &f) {
if (first == last)
return;
f(*first++);
while (first != last) {
ss.push_back(delim);
f(*first++);
}
}
template <bool Is_writing_escape, typename Stream>
IGUANA_INLINE void to_json_impl(Stream &ss, std::nullptr_t) {
ss.append("null");
}
template <bool Is_writing_escape, typename Stream>
IGUANA_INLINE void to_json_impl(Stream &ss, bool b) {
ss.append(b ? "true" : "false");
};
template <bool Is_writing_escape, typename Stream>
IGUANA_INLINE void to_json_impl(Stream &ss, char value) {
ss.append("\"");
ss.push_back(value);
ss.append("\"");
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<num_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &ss, T value) {
char temp[65];
auto p = detail::to_chars(temp, value);
ss.append(temp, p - temp);
}
// template <bool Is_writing_escape, typename Stream, typename T,
// std::enable_if_t<is_pb_type_v<T>, int> = 0>
// IGUANA_INLINE void to_json_impl(Stream &ss, T value) {
// to_json_impl<Is_writing_escape>(ss, value.val);
// }
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<numeric_str_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &ss, T v) {
ss.append(v.value().data(), v.value().size());
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<string_container_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &ss, T &&t) {
ss.push_back('"');
if constexpr (Is_writing_escape) {
write_string_with_escape(t.data(), t.size(), ss);
}
else {
ss.append(t.data(), t.size());
}
ss.push_back('"');
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<num_v<T>, int> = 0>
IGUANA_INLINE void render_key(Stream &ss, const T &t) {
ss.push_back('"');
to_json_impl<Is_writing_escape>(ss, t);
ss.push_back('"');
}
// template <bool Is_writing_escape, typename Stream, typename T,
// std::enable_if_t<is_pb_type_v<T>, int> = 0>
// IGUANA_INLINE void render_key(Stream &ss, const T &t) {
// render_key<Is_writing_escape>(ss, t.val);
// }
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<string_container_v<T>, int> = 0>
IGUANA_INLINE void render_key(Stream &ss, T &&t) {
to_json_impl<Is_writing_escape>(ss, std::forward<T>(t));
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<ylt_refletable_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &ss, T &&t) {
to_json(std::forward<T>(t), ss);
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<enum_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &ss, T val) {
static constexpr auto enum_to_str = get_enum_map<false, std::decay_t<T>>();
if constexpr (bool_v<decltype(enum_to_str)>) {
to_json_impl<Is_writing_escape>(
ss, static_cast<std::underlying_type_t<T>>(val));
}
else {
auto it = enum_to_str.find(val);
if (it != enum_to_str.end())
IGUANA_LIKELY {
auto str = it->second;
to_json_impl<Is_writing_escape>(
ss, std::string_view(str.data(), str.size()));
}
else {
throw std::runtime_error(
std::to_string(static_cast<std::underlying_type_t<T>>(val)) +
" is a missing value in enum_value");
}
}
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<optional_v<T>, int>>
IGUANA_INLINE void to_json_impl(Stream &ss, const T &val) {
if (!val) {
ss.append("null");
}
else {
to_json_impl<Is_writing_escape>(ss, *val);
}
}
template <bool Is_writing_escape, typename Stream, typename T>
IGUANA_INLINE void render_array(Stream &ss, const T &v) {
ss.push_back('[');
join(ss, std::begin(v), std::end(v), ',',
[&ss](const auto &jsv) IGUANA__INLINE_LAMBDA {
to_json_impl<Is_writing_escape>(ss, jsv);
});
ss.push_back(']');
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<fixed_array_v<T>, int>>
IGUANA_INLINE void to_json_impl(Stream &ss, const T &t) {
if constexpr (std::is_same_v<char, std::remove_reference_t<
decltype(std::declval<T>()[0])>>) {
constexpr size_t n = sizeof(T) / sizeof(decltype(std::declval<T>()[0]));
ss.push_back('"');
auto get_length = [&t](int n) constexpr {
for (int i = 0; i < n; ++i) {
if (t[i] == '\0')
return i;
}
return n;
};
size_t len = get_length(n);
ss.append(std::data(t), len);
ss.push_back('"');
}
else {
render_array<Is_writing_escape>(ss, t);
}
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<map_container_v<T>, int>>
IGUANA_INLINE void to_json_impl(Stream &ss, const T &o) {
ss.push_back('{');
join(ss, o.cbegin(), o.cend(), ',',
[&ss](const auto &jsv) IGUANA__INLINE_LAMBDA {
render_key<Is_writing_escape>(ss, jsv.first);
ss.push_back(':');
to_json_impl<Is_writing_escape>(ss, jsv.second);
});
ss.push_back('}');
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<sequence_container_v<T>, int>>
IGUANA_INLINE void to_json_impl(Stream &ss, const T &v) {
constexpr char pre = pair_v<typename T::value_type> ? '{' : '[';
ss.push_back(pre);
join(ss, v.cbegin(), v.cend(), ',',
[&ss](const auto &jsv) IGUANA__INLINE_LAMBDA {
to_json_impl<Is_writing_escape>(ss, jsv);
});
constexpr char post = pair_v<typename T::value_type> ? '}' : ']';
ss.push_back(post);
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<pair_v<T>, int>>
IGUANA_INLINE void to_json_impl(Stream &ss, const T &v) {
render_key<Is_writing_escape>(ss, v.first);
ss.push_back(':');
to_json_impl<Is_writing_escape>(ss, v.second);
}
constexpr auto write_json_key = [](auto &s, auto name) IGUANA__INLINE_LAMBDA {
s.push_back('"');
// will be replaced by string_view later
s.append(name.data(), name.size());
s.push_back('"');
};
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<smart_ptr_v<T>, int>>
IGUANA_INLINE void to_json_impl(Stream &ss, const T &v) {
if (v) {
to_json_impl<Is_writing_escape>(ss, *v);
}
else {
ss.append("null");
}
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<tuple_v<T>, int>>
IGUANA_INLINE void to_json_impl(Stream &s, T &&t) {
using U = typename std::decay_t<T>;
s.push_back('[');
constexpr size_t size = std::tuple_size_v<U>;
foreach_tuple(
[&s, size](auto &v, auto i) IGUANA__INLINE_LAMBDA {
to_json_impl<Is_writing_escape>(s, v);
if (i != size - 1)
IGUANA_LIKELY { s.push_back(','); }
},
std::forward<T>(t));
s.push_back(']');
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<variant_v<T>, int>>
IGUANA_INLINE void to_json_impl(Stream &s, T &&t) {
static_assert(!has_duplicate_type_v<std::remove_reference_t<T>>,
"don't allow same type in std::variant");
std::visit(
[&s](auto value) {
to_json_impl<Is_writing_escape>(s, value);
},
t);
}
} // namespace detail
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<ylt_refletable_v<T>, int>>
IGUANA_INLINE void to_json(T &&t, Stream &s) {
using namespace detail;
s.push_back('{');
using U = ylt::reflection::remove_cvref_t<T>;
constexpr auto Count = ylt::reflection::members_count_v<U>;
if constexpr (Count > 0) {
ylt::reflection::for_each(t, [&](auto &field, auto name, auto index) {
static_assert(
!std::is_pointer_v<std::remove_reference_t<decltype(field)>>,
"don't use raw pointer, please use smart pointer");
write_json_key(s, name);
s.push_back(':');
to_json_impl<Is_writing_escape>(s, field);
if (index < Count - 1)
IGUANA_LIKELY { s.push_back(','); }
});
}
s.push_back('}');
}
template <bool Is_writing_escape = true, typename Stream, typename T,
std::enable_if_t<non_ylt_refletable_v<T>, int> = 0>
IGUANA_INLINE void to_json(T &&t, Stream &s) {
using namespace detail;
to_json_impl<Is_writing_escape>(s, t);
}
template <typename T>
IGUANA_INLINE void to_json_adl(iguana_adl_t *p, T &t, std::string &pb_str) {
to_json(t, pb_str);
}
} // namespace iguana
#endif // SERIALIZE_JSON_HPP
+555
View File
@@ -0,0 +1,555 @@
#pragma once
#include "detail/string_resize.hpp"
#include "pb_util.hpp"
namespace iguana {
template <typename T>
IGUANA_INLINE void from_pb(T& t, std::string_view pb_str);
namespace detail {
template <typename T>
IGUANA_INLINE void from_pb_impl(T& val, std::string_view& pb_str,
uint32_t field_no = 0);
inline void skip_unknown_field(std::string_view& pb_str, WireType wire_type,
uint32_t field_number = 0);
template <typename T>
IGUANA_INLINE void decode_pair_value(T& val, std::string_view& pb_str) {
auto key = detail::decode_key(pb_str);
pb_str = pb_str.substr(key.tag_size);
WireType wire_type = key.wire_type;
if (wire_type != detail::get_wire_type<std::remove_reference_t<T>>()) {
return;
}
from_pb_impl(val, pb_str);
}
template <typename T>
IGUANA_INLINE void from_pb_impl(T& val, std::string_view& pb_str,
uint32_t field_no) {
size_t pos = 0;
if constexpr (ylt_refletable_v<T>) {
const size_t size = detail::decode_length_delimited(
pb_str, "Invalid message value: too few bytes.");
if (size == 0) {
return;
}
from_pb(val, pb_str.substr(0, size));
pb_str = pb_str.substr(size);
}
else if constexpr (is_sequence_container<T>::value) {
using item_type = typename T::value_type;
if constexpr (is_lenprefix_v<item_type>) {
// item_type non-packed
while (!pb_str.empty()) {
item_type item{};
from_pb_impl(item, pb_str);
val.push_back(std::move(item));
if (pb_str.empty()) {
break;
}
auto key = detail::decode_key(pb_str);
if (key.field_number != field_no ||
key.wire_type != detail::get_wire_type<item_type>()) {
break;
}
else {
pb_str = pb_str.substr(key.tag_size);
}
}
}
else {
// item_type packed
const size_t size = detail::decode_length_delimited(
pb_str, "Invalid packed repeated value: too few bytes.");
std::string_view packed = pb_str.substr(0, size);
pb_str = pb_str.substr(size);
if constexpr (is_fixed_v<item_type>) {
if (size % sizeof(item_type) != 0)
IGUANA_UNLIKELY {
throw std::invalid_argument(
"Invalid packed fixed repeated value: bad length.");
}
size_t num = size / sizeof(item_type);
size_t old_size = val.size();
detail::resize(val, old_size + num);
std::memcpy(val.data() + old_size, packed.data(), size);
}
else {
while (!packed.empty()) {
item_type item;
from_pb_impl(item, packed);
val.push_back(std::move(item));
}
}
}
}
else if constexpr (is_map_container<T>::value) {
using key_type = typename T::key_type;
using mapped_type = typename T::mapped_type;
while (!pb_str.empty()) {
const size_t size = detail::decode_length_delimited(
pb_str, "Invalid map entry value: too few bytes.");
std::string_view entry = pb_str.substr(0, size);
pb_str = pb_str.substr(size);
key_type map_key{};
mapped_type mapped{};
while (!entry.empty()) {
auto entry_key = detail::decode_key(entry);
WireType entry_wire_type = entry_key.wire_type;
uint32_t entry_field_number = entry_key.field_number;
entry = entry.substr(entry_key.tag_size);
if (entry_field_number == 1) {
if (entry_wire_type == detail::get_wire_type<key_type>()) {
from_pb_impl(map_key, entry, 1);
}
else {
skip_unknown_field(entry, entry_wire_type, entry_field_number);
}
}
else if (entry_field_number == 2) {
if (entry_wire_type == detail::get_wire_type<mapped_type>()) {
from_pb_impl(mapped, entry, 2);
}
else {
skip_unknown_field(entry, entry_wire_type, entry_field_number);
}
}
else {
skip_unknown_field(entry, entry_wire_type, entry_field_number);
}
}
val.insert_or_assign(std::move(map_key), std::move(mapped));
if (pb_str.empty()) {
break;
}
auto key = detail::decode_key(pb_str);
if (key.field_number != field_no ||
key.wire_type != WireType::LengthDelimeted) {
break;
}
pb_str = pb_str.substr(key.tag_size);
}
}
else if constexpr (std::is_integral_v<T>) {
val = static_cast<T>(detail::decode_varint(pb_str, pos));
pb_str = pb_str.substr(pos);
}
else if constexpr (detail::is_signed_varint_v<T>) {
constexpr size_t len = sizeof(typename T::value_type);
uint64_t temp = detail::decode_varint(pb_str, pos);
if constexpr (len == 8) {
val.val = detail::decode_zigzag(temp);
}
else {
val.val = detail::decode_zigzag(static_cast<uint32_t>(temp));
}
pb_str = pb_str.substr(pos);
}
else if constexpr (detail::is_fixed_v<T>) {
constexpr size_t size = sizeof(typename T::value_type);
if (pb_str.size() < size)
IGUANA_UNLIKELY {
throw std::invalid_argument("Invalid fixed int value: too few bytes.");
}
memcpy(&(val.val), pb_str.data(), size);
pb_str = pb_str.substr(size);
}
else if constexpr (std::is_same_v<T, double> || std::is_same_v<T, float>) {
constexpr size_t size = sizeof(T);
if (pb_str.size() < size)
IGUANA_UNLIKELY {
throw std::invalid_argument("Invalid fixed int value: too few bytes.");
}
memcpy(&(val), pb_str.data(), size);
pb_str = pb_str.substr(size);
}
else if constexpr (std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view>) {
const size_t size = detail::decode_length_delimited(
pb_str, "Invalid string value: too few bytes.");
if constexpr (std::is_same_v<T, std::string_view>) {
val = std::string_view(pb_str.data(), size);
}
else {
detail::resize(val, size);
memcpy(val.data(), pb_str.data(), size);
}
pb_str = pb_str.substr(size);
}
else if constexpr (std::is_enum_v<T>) {
using U = std::underlying_type_t<T>;
U value{};
from_pb_impl(value, pb_str);
val = static_cast<T>(value);
}
else if constexpr (optional_v<T>) {
if (!val.has_value()) {
val.emplace();
}
from_pb_impl(*val, pb_str, field_no);
}
else {
static_assert(!sizeof(T), "err");
}
}
template <typename T, typename Field>
IGUANA_INLINE void parse_oneof(T& t, const Field& f, std::string_view& pb_str) {
using item_type = typename std::decay_t<Field>::sub_type;
from_pb_impl(t.template emplace<item_type>(), pb_str, f.field_no);
}
template <typename T>
constexpr bool is_unpacked_repeated_type() {
using value_type = std::remove_const_t<std::remove_reference_t<T>>;
if constexpr (is_sequence_container<value_type>::value) {
using item_type = typename value_type::value_type;
return !is_lenprefix_v<item_type>;
}
else {
return false;
}
}
template <typename T>
constexpr bool is_unpacked_repeated_wire_type(WireType wire_type) {
using value_type = std::remove_const_t<std::remove_reference_t<T>>;
if constexpr (is_unpacked_repeated_type<value_type>()) {
using item_type = typename value_type::value_type;
return wire_type == detail::get_wire_type<item_type>();
}
else {
return false;
}
}
template <typename T>
IGUANA_INLINE void from_pb_unpacked_repeated_item(T& val,
std::string_view& pb_str) {
using value_type = std::remove_const_t<std::remove_reference_t<T>>;
using item_type = typename value_type::value_type;
item_type item{};
from_pb_impl(item, pb_str);
val.push_back(std::move(item));
}
template <bool TimestampSchema, typename T>
IGUANA_INLINE auto from_pb_well_known_value(const T* current,
std::string_view& pb_str,
uint32_t field_no) {
using value_type = std::remove_const_t<std::remove_reference_t<T>>;
if constexpr (TimestampSchema) {
iguana::pb_timestamp wire_value{};
if (current != nullptr) {
wire_value = iguana::pb_timestamp{*current};
}
from_pb_impl(wire_value, pb_str, field_no);
return static_cast<value_type>(wire_value);
}
else {
iguana::pb_duration wire_value{};
if (current != nullptr) {
wire_value = iguana::pb_duration{*current};
}
from_pb_impl(wire_value, pb_str, field_no);
return static_cast<value_type>(wire_value);
}
}
template <bool TimestampSchema, typename T>
IGUANA_INLINE void from_pb_well_known_impl(T& val, std::string_view& pb_str,
uint32_t field_no) {
using value_type = std::remove_const_t<std::remove_reference_t<T>>;
if constexpr (optional_v<value_type>) {
using item_type = typename value_type::value_type;
const item_type* current = val.has_value() ? &*val : nullptr;
val = from_pb_well_known_value<TimestampSchema, item_type>(current, pb_str,
field_no);
}
else if constexpr (is_sequence_container<value_type>::value) {
using item_type = typename value_type::value_type;
val.push_back(from_pb_well_known_value<TimestampSchema, item_type>(
nullptr, pb_str, field_no));
}
else {
val = from_pb_well_known_value<TimestampSchema, value_type>(&val, pb_str,
field_no);
}
}
template <typename Wire, typename T>
IGUANA_INLINE void from_pb_schema_impl(T& val, std::string_view& pb_str,
uint32_t field_no) {
using value_type = ylt::reflection::remove_cvref_t<T>;
using wire_type = ylt::reflection::remove_cvref_t<Wire>;
if constexpr (std::is_same_v<value_type, wire_type>) {
from_pb_impl(val, pb_str, field_no);
}
else if constexpr (is_sequence_container<value_type>::value) {
using wire_item_type = typename wire_type::value_type;
if constexpr (is_lenprefix_v<wire_item_type>) {
wire_type wire_value{};
from_pb_impl(wire_value, pb_str, field_no);
assign_pb_wire_value(val, std::move(wire_value));
}
else {
const size_t size = decode_length_delimited(
pb_str, "Invalid packed repeated value: too few bytes.");
std::string_view packed = pb_str.substr(0, size);
pb_str = pb_str.substr(size);
while (!packed.empty()) {
wire_item_type wire_item{};
from_pb_impl(wire_item, packed);
typename value_type::value_type item{};
assign_pb_wire_value(item, std::move(wire_item));
val.push_back(std::move(item));
}
}
}
else {
wire_type wire_value{};
from_pb_impl(wire_value, pb_str, field_no);
assign_pb_wire_value(val, std::move(wire_value));
}
}
// pb_str must already point past the field tag varint.
inline void skip_unknown_field(std::string_view& pb_str, WireType wire_type,
uint32_t field_number) {
switch (wire_type) {
case WireType::Varint: {
if (pb_str.empty())
throw std::invalid_argument("unknown Varint field: too few bytes");
size_t skip_pos = 0;
decode_varint(pb_str, skip_pos);
pb_str = pb_str.substr(skip_pos);
break;
}
case WireType::Fixed64:
if (pb_str.size() < 8)
throw std::invalid_argument("unknown Fixed64 field: too few bytes");
pb_str = pb_str.substr(8);
break;
case WireType::LengthDelimeted: {
const size_t len = decode_length_delimited(
pb_str, "unknown LengthDelimited field: too few bytes");
pb_str = pb_str.substr(len);
break;
}
case WireType::Fixed32:
if (pb_str.size() < 4)
throw std::invalid_argument("unknown Fixed32 field: too few bytes");
pb_str = pb_str.substr(4);
break;
case WireType::StartGroup: {
pb_recursion_guard recursion_guard;
while (true) {
if (pb_str.empty())
throw std::invalid_argument("unknown group field: missing end tag");
auto key = detail::decode_key(pb_str, true);
pb_str = pb_str.substr(key.tag_size);
WireType child_wire_type = key.wire_type;
uint32_t child_field_number = key.field_number;
if (child_wire_type == WireType::EndGroup) {
if (child_field_number != field_number) {
throw std::invalid_argument(
"unknown group field: mismatched end tag");
}
return;
}
skip_unknown_field(pb_str, child_wire_type, child_field_number);
}
}
case WireType::EndGroup:
throw std::runtime_error("unexpected end group in unknown field");
default:
throw std::runtime_error("unknown wire type in unknown field");
}
}
} // namespace detail
template <typename T>
IGUANA_INLINE void from_pb(T& t, std::string_view pb_str) {
if (pb_str.empty())
IGUANA_UNLIKELY { return; }
detail::pb_recursion_guard recursion_guard;
auto key = detail::decode_key(pb_str);
size_t pos = key.tag_size;
WireType wire_type = key.wire_type;
uint32_t field_number = key.field_number;
#ifdef SEQUENTIAL_PARSE
static auto tp = detail::get_pb_members_tuple(t);
constexpr size_t SIZE = std::tuple_size_v<std::decay_t<decltype(tp)>>;
bool parse_done = false;
auto ptr = &t;
detail::for_each_n(
[&, ptr](auto i) IGUANA__INLINE_LAMBDA {
auto val = std::get<decltype(i)::value>(tp);
using sub_type = typename std::decay_t<decltype(val)>::sub_type;
using value_type = typename std::decay_t<decltype(val)>::value_type;
using wire_value_type =
typename std::decay_t<decltype(val)>::wire_value_type;
using wire_sub_type =
typename std::decay_t<decltype(val)>::wire_sub_type;
constexpr bool is_variant_v = variant_v<value_type>;
// sub_type is the element type when value_type is the variant type;
// otherwise, they are the same.
if (parse_done || field_number != val.field_no) {
return;
}
pb_str = pb_str.substr(pos);
if constexpr (std::decay_t<decltype(val)>::timestamp_schema ||
std::decay_t<decltype(val)>::duration_schema) {
if (wire_type != WireType::LengthDelimeted) {
throw std::runtime_error("unmatched wire_type");
}
}
else if constexpr (std::is_same_v<sub_type, std::monostate>) {
throw std::runtime_error("oneof monostate has no wire type");
}
else if constexpr (detail::is_unpacked_repeated_type<
wire_value_type>()) {
if (wire_type != WireType::LengthDelimeted &&
!detail::is_unpacked_repeated_wire_type<wire_value_type>(
wire_type)) {
throw std::runtime_error("unmatched wire_type");
}
}
else if (wire_type != detail::get_wire_type<wire_sub_type>())
IGUANA_UNLIKELY { throw std::runtime_error("unmatched wire_type"); }
auto member_ptr = (value_type*)((char*)(ptr) + val.offset);
if constexpr (std::decay_t<decltype(val)>::timestamp_schema ||
std::decay_t<decltype(val)>::duration_schema) {
detail::from_pb_well_known_impl<
std::decay_t<decltype(val)>::timestamp_schema>(
*member_ptr, pb_str, val.field_no);
}
else if constexpr (detail::is_unpacked_repeated_type<
wire_value_type>()) {
if (wire_type == WireType::LengthDelimeted) {
detail::from_pb_schema_impl<wire_value_type>(*member_ptr, pb_str,
val.field_no);
}
else {
typename wire_value_type::value_type wire_item{};
detail::from_pb_impl(wire_item, pb_str);
typename value_type::value_type item{};
detail::assign_pb_wire_value(item, std::move(wire_item));
member_ptr->push_back(std::move(item));
}
}
else if constexpr (is_variant_v) {
detail::parse_oneof(*member_ptr, val, pb_str);
}
else {
detail::from_pb_schema_impl<wire_value_type>(*member_ptr, pb_str,
val.field_no);
}
if (pb_str.empty()) {
parse_done = true;
return;
}
key = detail::decode_key(pb_str);
pos = key.tag_size;
wire_type = key.wire_type;
field_number = key.field_number;
},
std::make_index_sequence<SIZE>{});
if (parse_done)
IGUANA_LIKELY { return; }
#endif
static auto map = detail::get_members(t);
while (true) {
const char* field_start = pb_str.data();
pb_str = pb_str.substr(pos);
auto it = map.find(field_number);
if (it == map.end()) {
// Unknown field: skip according to wire type (proto3 forward compat)
detail::skip_unknown_field(pb_str, wire_type, field_number);
detail::append_pb_unknown_field(
t, field_start, static_cast<size_t>(pb_str.data() - field_start));
}
else {
auto& member = it->second;
std::visit(
[&t, &pb_str, wire_type](auto& val) {
using sub_type = typename std::decay_t<decltype(val)>::sub_type;
using value_type = typename std::decay_t<decltype(val)>::value_type;
using wire_value_type =
typename std::decay_t<decltype(val)>::wire_value_type;
using wire_sub_type =
typename std::decay_t<decltype(val)>::wire_sub_type;
if constexpr (std::decay_t<decltype(val)>::timestamp_schema ||
std::decay_t<decltype(val)>::duration_schema) {
if (wire_type != WireType::LengthDelimeted) {
throw std::runtime_error("unmatched wire_type");
}
}
else if constexpr (std::is_same_v<sub_type, std::monostate>) {
throw std::runtime_error("oneof monostate has no wire type");
}
else if constexpr (detail::is_unpacked_repeated_type<
wire_value_type>()) {
if (wire_type != WireType::LengthDelimeted &&
!detail::is_unpacked_repeated_wire_type<wire_value_type>(
wire_type)) {
throw std::runtime_error("unmatched wire_type");
}
}
else if (wire_type != detail::get_wire_type<wire_sub_type>()) {
throw std::runtime_error("unmatched wire_type");
}
if constexpr (std::decay_t<decltype(val)>::timestamp_schema ||
std::decay_t<decltype(val)>::duration_schema) {
detail::from_pb_well_known_impl<
std::decay_t<decltype(val)>::timestamp_schema>(
val.value(t), pb_str, val.field_no);
}
else if constexpr (detail::is_unpacked_repeated_type<
wire_value_type>()) {
if (wire_type == WireType::LengthDelimeted) {
detail::from_pb_schema_impl<wire_value_type>(
val.value(t), pb_str, val.field_no);
}
else {
typename wire_value_type::value_type wire_item{};
detail::from_pb_impl(wire_item, pb_str);
typename value_type::value_type item{};
detail::assign_pb_wire_value(item, std::move(wire_item));
val.value(t).push_back(std::move(item));
}
}
else if constexpr (variant_v<value_type>) {
detail::parse_oneof(val.value(t), val, pb_str);
}
else {
detail::from_pb_schema_impl<wire_value_type>(val.value(t), pb_str,
val.field_no);
}
},
member);
}
if (!pb_str.empty())
IGUANA_LIKELY {
key = detail::decode_key(pb_str);
pos = key.tag_size;
wire_type = key.wire_type;
field_number = key.field_number;
}
else {
return;
}
}
}
template <typename T>
IGUANA_INLINE void from_pb_adl(iguana_adl_t* p, T& t, std::string_view pb_str) {
iguana::from_pb(t, pb_str);
}
} // namespace iguana
+826
View File
@@ -0,0 +1,826 @@
#pragma once
#include <cassert>
#include <cstddef>
#include <cstring>
#include <limits>
#include <map>
#include <stdexcept>
#include <string>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <variant>
#include <vector>
#include "common.hpp"
#include "detail/pb_type.hpp"
#include "util.hpp"
namespace iguana {
enum class WireType : uint32_t {
Varint = 0,
Fixed64 = 1,
LengthDelimeted = 2,
StartGroup = 3,
EndGroup = 4,
Fixed32 = 5,
Unknown
};
template <typename T>
constexpr bool inherits_from_base_v = std::is_base_of_v<detail::base, T>;
namespace detail {
template <typename T>
constexpr bool is_fixed_v =
std::is_same_v<T, fixed32_t> || std::is_same_v<T, fixed64_t> ||
std::is_same_v<T, sfixed32_t> || std::is_same_v<T, sfixed64_t>;
template <typename T>
constexpr bool is_signed_varint_v =
std::is_same_v<T, sint32_t> || std::is_same_v<T, sint64_t>;
template <typename T>
constexpr inline WireType get_wire_type() {
if constexpr (std::is_integral_v<T> || is_signed_varint_v<T> ||
std::is_enum_v<T> || std::is_same_v<T, bool>) {
return WireType::Varint;
}
else if constexpr (std::is_same_v<T, fixed32_t> ||
std::is_same_v<T, sfixed32_t> ||
std::is_same_v<T, float>) {
return WireType::Fixed32;
}
else if constexpr (std::is_same_v<T, fixed64_t> ||
std::is_same_v<T, sfixed64_t> ||
std::is_same_v<T, double>) {
return WireType::Fixed64;
}
else if constexpr (std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view> ||
ylt_refletable_v<T> || is_sequence_container<T>::value ||
is_map_container<T>::value) {
return WireType::LengthDelimeted;
}
else if constexpr (optional_v<T>) {
return get_wire_type<typename T::value_type>();
}
else {
throw std::runtime_error("unknown type");
}
}
template <typename T>
constexpr bool is_lenprefix_v = (get_wire_type<T>() ==
WireType::LengthDelimeted);
template <typename T>
inline constexpr bool pb_well_known_chrono_v =
std::is_same_v<std::remove_const_t<std::remove_reference_t<T>>,
std::chrono::system_clock::time_point> ||
std::is_same_v<std::remove_const_t<std::remove_reference_t<T>>,
std::chrono::nanoseconds>;
template <bool TimestampSchema, typename T>
IGUANA_INLINE auto make_pb_well_known_value(const T& value) {
if constexpr (TimestampSchema) {
return iguana::pb_timestamp{value};
}
else {
return iguana::pb_duration{value};
}
}
[[nodiscard]] IGUANA_INLINE uint32_t encode_zigzag(int32_t v) {
return (static_cast<uint32_t>(v) << 1U) ^
static_cast<uint32_t>(
-static_cast<int32_t>(static_cast<uint32_t>(v) >> 31U));
}
[[nodiscard]] IGUANA_INLINE uint64_t encode_zigzag(int64_t v) {
return (static_cast<uint64_t>(v) << 1U) ^
static_cast<uint64_t>(
-static_cast<int64_t>(static_cast<uint64_t>(v) >> 63U));
}
[[nodiscard]] IGUANA_INLINE int64_t decode_zigzag(uint64_t u) {
return static_cast<int64_t>((u >> 1U)) ^
static_cast<uint64_t>(-static_cast<int64_t>(u & 1U));
}
[[nodiscard]] IGUANA_INLINE int64_t decode_zigzag(uint32_t u) {
return static_cast<int64_t>((u >> 1U)) ^
static_cast<uint64_t>(-static_cast<int64_t>(u & 1U));
}
template <class T>
IGUANA_INLINE uint64_t decode_varint(T& data, size_t& pos) {
if (data.empty())
IGUANA_UNLIKELY {
throw std::invalid_argument("Invalid varint value: too few bytes.");
}
const auto* begin = reinterpret_cast<const unsigned char*>(data.data());
uint64_t val = 0;
const size_t size = data.size();
const size_t limit = size < 10 ? size : 10;
for (size_t i = 0; i < limit; ++i) {
const uint64_t byte = begin[i];
if (i == 9 && (byte & 0xfeU) != 0)
IGUANA_UNLIKELY {
throw std::invalid_argument("Invalid varint value: too many bytes.");
}
val |= (byte & 0x7fU) << (7 * i);
if ((byte & 0x80U) == 0) {
pos = i + 1;
return val;
}
}
if (size >= 10)
IGUANA_UNLIKELY {
throw std::invalid_argument("Invalid varint value: too many bytes.");
}
throw std::invalid_argument("Invalid varint value: too few bytes.");
}
struct pb_key {
uint32_t field_number;
WireType wire_type;
size_t tag_size;
};
IGUANA_INLINE pb_key decode_key(std::string_view data,
bool allow_end_group = false) {
size_t pos = 0;
const uint64_t raw_key = decode_varint(data, pos);
if (raw_key > std::numeric_limits<uint32_t>::max())
IGUANA_UNLIKELY {
throw std::invalid_argument("Invalid protobuf tag: too large.");
}
const uint32_t key = static_cast<uint32_t>(raw_key);
const uint32_t wire = key & 0b0111U;
const uint32_t field_number = key >> 3U;
if (field_number == 0)
IGUANA_UNLIKELY {
throw std::invalid_argument(
"Invalid protobuf tag: field number must be positive.");
}
if (wire > static_cast<uint32_t>(WireType::Fixed32))
IGUANA_UNLIKELY {
throw std::invalid_argument("Invalid protobuf tag: unknown wire type.");
}
auto wire_type = static_cast<WireType>(wire);
if (wire_type == WireType::EndGroup && !allow_end_group)
IGUANA_UNLIKELY {
throw std::invalid_argument("unexpected end group in protobuf stream");
}
return {field_number, wire_type, pos};
}
IGUANA_INLINE size_t decode_length_delimited(std::string_view& pb_str,
const char* error) {
size_t pos = 0;
const uint64_t size = decode_varint(pb_str, pos);
pb_str = pb_str.substr(pos);
if (size > pb_str.size())
IGUANA_UNLIKELY { throw std::invalid_argument(error); }
if (size > std::numeric_limits<size_t>::max())
IGUANA_UNLIKELY {
throw std::invalid_argument("Invalid length-delimited value: too large.");
}
return static_cast<size_t>(size);
}
inline constexpr size_t max_pb_recursion_depth = 100;
inline thread_local size_t pb_recursion_depth = 0;
struct pb_recursion_guard {
pb_recursion_guard() {
if (pb_recursion_depth >= max_pb_recursion_depth)
IGUANA_UNLIKELY {
throw std::runtime_error("protobuf message recursion limit exceeded");
}
++pb_recursion_depth;
}
~pb_recursion_guard() { --pb_recursion_depth; }
};
// value == 0 ? 1 : floor(log2(value)) / 7 + 1
constexpr size_t variant_uint32_size_constexpr(uint32_t value) {
if (value == 0) {
return 1;
}
int log = 0;
while (value >>= 1) ++log;
return log / 7 + 1;
}
template <uint64_t v, typename Writer, size_t... I>
IGUANA_INLINE void append_varint_u32(Writer& writer,
std::index_sequence<I...>) {
uint8_t temp = 0;
((temp = static_cast<uint8_t>((v >> (7 * I)) | 0x80),
writer.write((const char*)&temp, 1)),
...);
}
template <uint32_t v, typename Writer>
IGUANA_INLINE void serialize_varint_u32(Writer& writer) {
constexpr auto size = variant_uint32_size_constexpr(v);
append_varint_u32<v>(writer, std::make_index_sequence<size - 1>{});
uint8_t temp = static_cast<uint8_t>(v >> (7 * (size - 1)));
writer.write((const char*)&temp, 1);
}
template <typename Writer>
IGUANA_INLINE void serialize_varint(uint64_t v, Writer& writer) {
uint8_t temp = static_cast<uint8_t>(v);
if (v < 0x80) {
writer.write((const char*)&temp, 1);
return;
}
temp = static_cast<uint8_t>(v | 0x80);
writer.write((const char*)&temp, 1);
v >>= 7;
if (v < 0x80) {
temp = static_cast<uint8_t>(v);
writer.write((const char*)&temp, 1);
return;
}
do {
temp = static_cast<uint8_t>(v | 0x80);
writer.write((const char*)&temp, 1);
v >>= 7;
} while (v >= 0x80);
temp = static_cast<uint8_t>(v);
writer.write((const char*)&temp, 1);
}
IGUANA_INLINE uint32_t log2_floor_uint32(uint32_t n) {
#if defined(__GNUC__)
return 31 ^ static_cast<uint32_t>(__builtin_clz(n));
#else
unsigned long where;
_BitScanReverse(&where, n);
return where;
#endif
}
IGUANA_INLINE size_t variant_uint32_size(uint32_t value) {
// This computes value == 0 ? 1 : floor(log2(value)) / 7 + 1
// Use an explicit multiplication to implement the divide of
// a number in the 1..31 range.
// Explicit OR 0x1 to avoid calling Bits::Log2FloorNonZero(0), which is
// undefined.
uint32_t log2value = log2_floor_uint32(value | 0x1);
return static_cast<size_t>((log2value * 9 + 73) / 64);
}
IGUANA_INLINE int Log2FloorNonZero_Portable(uint32_t n) {
if (n == 0)
return -1;
int log = 0;
uint32_t value = n;
for (int i = 4; i >= 0; --i) {
int shift = (1 << i);
uint32_t x = value >> shift;
if (x != 0) {
value = x;
log += shift;
}
}
assert(value == 1);
return log;
}
IGUANA_INLINE uint32_t Log2FloorNonZero(uint32_t n) {
#if defined(__GNUC__)
return 31 ^ static_cast<uint32_t>(__builtin_clz(n));
#elif defined(_MSC_VER)
unsigned long where;
_BitScanReverse(&where, n);
return where;
#else
return Log2FloorNonZero_Portable(n);
#endif
}
IGUANA_INLINE int Log2FloorNonZero64_Portable(uint64_t n) {
const uint32_t topbits = static_cast<uint32_t>(n >> 32);
if (topbits == 0) {
// Top bits are zero, so scan in bottom bits
return static_cast<int>(Log2FloorNonZero(static_cast<uint32_t>(n)));
}
else {
return 32 + static_cast<int>(Log2FloorNonZero(topbits));
}
}
IGUANA_INLINE uint32_t log2_floor_uint64(uint64_t n) {
#if defined(__GNUC__)
return 63 ^ static_cast<uint32_t>(__builtin_clzll(n));
#elif defined(_MSC_VER) && defined(_M_X64)
unsigned long where;
_BitScanReverse64(&where, n);
return where;
#else
return Log2FloorNonZero64_Portable(n);
#endif
}
IGUANA_INLINE size_t variant_uint64_size(uint64_t value) {
// This computes value == 0 ? 1 : floor(log2(value)) / 7 + 1
// Use an explicit multiplication to implement the divide of
// a number in the 1..63 range.
// Explicit OR 0x1 to avoid calling Bits::Log2FloorNonZero(0), which is
// undefined.
uint32_t log2value = log2_floor_uint64(value | 0x1);
return static_cast<size_t>((log2value * 9 + 73) / 64);
}
template <typename U>
constexpr IGUANA_INLINE size_t variant_intergal_size(U value) {
using T = std::remove_reference_t<U>;
if constexpr (sizeof(T) == 8) {
return variant_uint64_size(static_cast<uint64_t>(value));
}
else if constexpr (sizeof(T) <= 4) {
if constexpr (std::is_signed_v<T>) {
if (value < 0) {
return 10;
}
}
return variant_uint32_size(static_cast<uint32_t>(value));
}
else {
static_assert(!sizeof(T), "intergal in not supported");
}
}
template <typename F, size_t... I>
IGUANA_INLINE constexpr void for_each_n(F&& f, std::index_sequence<I...>) {
(std::forward<F>(f)(std::integral_constant<size_t, I>{}), ...);
}
template <size_t key_size, bool omit_default_val, typename Type>
IGUANA_INLINE size_t str_numeric_size(Type&& t) {
using T = std::remove_const_t<std::remove_reference_t<Type>>;
if constexpr (std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view>) {
// string
if constexpr (omit_default_val) {
if (t.size() == 0)
IGUANA_UNLIKELY { return 0; }
}
if constexpr (key_size == 0) {
return t.size();
}
else {
return key_size + variant_uint32_size(static_cast<uint32_t>(t.size())) +
t.size();
}
}
else {
// numeric
if constexpr (omit_default_val) {
if constexpr (is_fixed_v<T> || is_signed_varint_v<T>) {
if (t.val == 0)
IGUANA_UNLIKELY { return 0; }
}
else {
if (t == static_cast<T>(0))
IGUANA_UNLIKELY { return 0; }
}
}
if constexpr (std::is_integral_v<T>) {
if constexpr (std::is_same_v<bool, T>) {
return 1 + key_size;
}
else {
return key_size + variant_intergal_size(t);
}
}
else if constexpr (detail::is_signed_varint_v<T>) {
return key_size + variant_intergal_size(encode_zigzag(t.val));
}
else if constexpr (detail::is_fixed_v<T>) {
return key_size + sizeof(typename T::value_type);
}
else if constexpr (std::is_same_v<T, double> || std::is_same_v<T, float>) {
return key_size + sizeof(T);
}
else if constexpr (std::is_enum_v<T>) {
using U = std::underlying_type_t<T>;
return key_size + variant_intergal_size(static_cast<U>(t));
}
else {
static_assert(!sizeof(T), "err");
}
}
}
template <size_t key_size, bool omit_default_val = true, typename Type,
typename Arr>
IGUANA_INLINE size_t pb_key_value_size(Type&& t, Arr& size_arr);
template <bool TimestampSchema, size_t key_size, bool omit_default_val = true,
typename Type, typename Arr>
IGUANA_INLINE size_t pb_well_known_key_value_size(Type&& t, Arr& size_arr) {
using T = std::remove_const_t<std::remove_reference_t<Type>>;
if constexpr (optional_v<T>) {
if (!t.has_value()) {
return 0;
}
return pb_well_known_key_value_size<TimestampSchema, key_size,
omit_default_val>(*t, size_arr);
}
else if constexpr (is_sequence_container<T>::value) {
size_t len = 0;
for (auto& item : t) {
len += pb_well_known_key_value_size<TimestampSchema, key_size, false>(
item, size_arr);
}
return len;
}
else {
auto wire_value = make_pb_well_known_value<TimestampSchema>(t);
return pb_key_value_size<key_size, omit_default_val>(wire_value, size_arr);
}
}
template <typename Wire, typename T>
IGUANA_INLINE auto make_pb_wire_scalar(const T& value) {
using W = ylt::reflection::remove_cvref_t<Wire>;
using U = ylt::reflection::remove_cvref_t<T>;
if constexpr (std::is_same_v<W, U>) {
return value;
}
else if constexpr (is_pb_type_v<W>) {
return W{static_cast<typename W::value_type>(value)};
}
else {
return W{value};
}
}
template <typename T, typename Wire>
IGUANA_INLINE void assign_pb_wire_value(T& value, Wire&& wire) {
using U = ylt::reflection::remove_cvref_t<T>;
using W = ylt::reflection::remove_cvref_t<Wire>;
if constexpr (std::is_same_v<U, W>) {
value = std::forward<Wire>(wire);
}
else if constexpr (optional_v<U>) {
if (wire.has_value()) {
typename U::value_type item{};
assign_pb_wire_value(item, *wire);
value = std::move(item);
}
else {
value.reset();
}
}
else if constexpr (is_sequence_container<U>::value) {
value.clear();
for (auto& item : wire) {
typename U::value_type converted{};
assign_pb_wire_value(converted, item);
value.push_back(std::move(converted));
}
}
else if constexpr (is_pb_type_v<W>) {
value = wire.val;
}
else {
value = static_cast<U>(wire);
}
}
template <typename Wire, size_t key_size, bool omit_default_val = true,
typename Type, typename Arr>
IGUANA_INLINE size_t pb_schema_key_value_size(Type&& t, Arr& size_arr);
template <typename Variant, typename T, size_t I>
constexpr inline size_t get_variant_index() {
if constexpr (I == 0) {
static_assert(std::is_same_v<std::variant_alternative_t<0, Variant>, T>,
"Type T is not found in Variant");
return 0;
}
else if constexpr (std::is_same_v<std::variant_alternative_t<I, Variant>,
T>) {
return I;
}
else {
return get_variant_index<Variant, T, I - 1>();
}
}
template <size_t field_no, typename Type, typename Arr>
IGUANA_INLINE size_t pb_oneof_size(Type&& t, Arr& size_arr) {
using T = std::decay_t<Type>;
int len = 0;
std::visit(
[&len, &size_arr](auto&& value) IGUANA__INLINE_LAMBDA {
using raw_value_type = decltype(value);
using value_type =
std::remove_const_t<std::remove_reference_t<decltype(value)>>;
constexpr auto offset =
get_variant_index<T, value_type, std::variant_size_v<T> - 1>();
constexpr uint32_t key =
((field_no + offset) << 3) |
static_cast<uint32_t>(get_wire_type<value_type>());
len = pb_key_value_size<variant_uint32_size_constexpr(key), false>(
std::forward<raw_value_type>(value), size_arr);
},
std::forward<Type>(t));
return len;
}
// returns size = key_size + optional(len_size) + len
// when key_size == 0, return len
template <size_t key_size, bool omit_default_val, typename Type, typename Arr>
IGUANA_INLINE size_t pb_key_value_size(Type&& t, Arr& size_arr) {
using T = std::remove_const_t<std::remove_reference_t<Type>>;
if constexpr (ylt_refletable_v<T> || is_custom_reflection_v<T>) {
size_t len = 0;
static auto tuple = get_pb_members_tuple(std::forward<Type>(t));
constexpr size_t SIZE = std::tuple_size_v<std::decay_t<decltype(tuple)>>;
size_t pre_index = -1;
if constexpr (!inherits_from_base_v<T> && key_size != 0) {
pre_index = size_arr.size();
size_arr.push_back(0); // placeholder
}
for_each_n(
[&len, &t, &size_arr](auto i) IGUANA__INLINE_LAMBDA {
using field_type =
std::tuple_element_t<decltype(i)::value,
std::decay_t<decltype(tuple)>>;
auto value = std::get<decltype(i)::value>(tuple);
using U = typename field_type::value_type;
using wire_type = typename field_type::wire_value_type;
using sub_type = typename field_type::sub_type;
auto& val = value.value(t);
if constexpr (field_type::timestamp_schema ||
field_type::duration_schema) {
constexpr uint32_t sub_key =
(value.field_no << 3) |
static_cast<uint32_t>(WireType::LengthDelimeted);
constexpr auto sub_keysize = variant_uint32_size_constexpr(sub_key);
len += pb_well_known_key_value_size<field_type::timestamp_schema,
sub_keysize>(val, size_arr);
}
else if constexpr (field_type::optional_schema) {
constexpr uint32_t sub_key =
(value.field_no << 3) |
static_cast<uint32_t>(get_wire_type<wire_type>());
constexpr auto sub_keysize = variant_uint32_size_constexpr(sub_key);
len += pb_schema_key_value_size<wire_type, sub_keysize, false>(
val, size_arr);
}
else if constexpr (variant_v<U>) {
constexpr auto offset =
get_variant_index<U, sub_type, std::variant_size_v<U> - 1>();
if (val.index() == offset) {
if constexpr (!std::is_same_v<sub_type, std::monostate>) {
constexpr uint32_t sub_key =
(value.field_no << 3) |
static_cast<uint32_t>(get_wire_type<sub_type>());
constexpr auto sub_keysize =
variant_uint32_size_constexpr(sub_key);
len += pb_key_value_size<sub_keysize, false>(
std::get<offset>(val), size_arr);
}
}
}
else {
constexpr uint32_t sub_key =
(value.field_no << 3) |
static_cast<uint32_t>(get_wire_type<wire_type>());
constexpr auto sub_keysize = variant_uint32_size_constexpr(sub_key);
len +=
pb_schema_key_value_size<wire_type, sub_keysize>(val, size_arr);
}
},
std::make_index_sequence<SIZE>{});
len += pb_unknown_fields_size(t);
if constexpr (inherits_from_base_v<T>) {
t.cache_size = len;
}
else if constexpr (key_size != 0) {
size_arr[pre_index] = len;
}
if constexpr (key_size == 0) {
// for top level
return len;
}
else {
if (len == 0) {
// equals key_size + variant_uint32_size(len)
return key_size + 1;
}
else {
return key_size + variant_uint32_size(static_cast<uint32_t>(len)) + len;
}
}
}
else if constexpr (is_sequence_container<T>::value) {
using item_type = typename T::value_type;
size_t len = 0;
if constexpr (is_lenprefix_v<item_type>) {
for (auto& item : t) {
len += pb_key_value_size<key_size, false>(item, size_arr);
}
return len;
}
else {
for (auto& item : t) {
// here 0 to get pakced size, and item must be numeric
len += str_numeric_size<0, false>(item);
}
if (len == 0) {
return 0;
}
else {
return key_size + variant_uint32_size(static_cast<uint32_t>(len)) + len;
}
}
}
else if constexpr (is_map_container<T>::value) {
size_t len = 0;
for (auto& [k, v] : t) {
// the key_size of k and v is constant 1
auto kv_len = pb_key_value_size<1, false>(k, size_arr) +
pb_key_value_size<1, false>(v, size_arr);
len += key_size + variant_uint32_size(static_cast<uint32_t>(kv_len)) +
kv_len;
}
return len;
}
else if constexpr (optional_v<T>) {
if (!t.has_value()) {
return 0;
}
return pb_key_value_size<key_size, omit_default_val>(*t, size_arr);
}
else {
return str_numeric_size<key_size, omit_default_val>(t);
}
}
template <typename Wire, size_t key_size, bool omit_default_val, typename Type,
typename Arr>
IGUANA_INLINE size_t pb_schema_key_value_size(Type&& t, Arr& size_arr) {
using T = ylt::reflection::remove_cvref_t<Type>;
using W = ylt::reflection::remove_cvref_t<Wire>;
if constexpr (std::is_same_v<T, W>) {
return pb_key_value_size<key_size, omit_default_val>(std::forward<Type>(t),
size_arr);
}
else if constexpr (optional_v<T>) {
if (!t.has_value()) {
return 0;
}
return pb_schema_key_value_size<typename W::value_type, key_size,
omit_default_val>(*t, size_arr);
}
else if constexpr (is_sequence_container<T>::value) {
using wire_item_type = typename W::value_type;
size_t len = 0;
if constexpr (is_lenprefix_v<wire_item_type>) {
for (auto& item : t) {
len += pb_schema_key_value_size<wire_item_type, key_size, false>(
item, size_arr);
}
return len;
}
else {
for (auto& item : t) {
auto wire_item = make_pb_wire_scalar<wire_item_type>(item);
len += str_numeric_size<0, false>(wire_item);
}
if (len == 0) {
return 0;
}
return key_size + variant_uint32_size(static_cast<uint32_t>(len)) + len;
}
}
else {
auto wire_value = make_pb_wire_scalar<W>(t);
return pb_key_value_size<key_size, omit_default_val>(wire_value, size_arr);
}
}
// return the payload size
template <bool skip_next = true, typename Type>
IGUANA_INLINE size_t pb_value_size(Type&& t, uint32_t*& sz_ptr) {
using T = std::remove_const_t<std::remove_reference_t<Type>>;
if constexpr (ylt_refletable_v<T> || is_custom_reflection_v<T>) {
if constexpr (inherits_from_base_v<T>) {
return t.cache_size;
}
else {
// *sz_ptr is secure and logically guaranteed
if constexpr (skip_next) {
return *(sz_ptr++);
}
else {
return *sz_ptr;
}
}
}
else if constexpr (is_sequence_container<T>::value) {
using item_type = typename T::value_type;
size_t len = 0;
if constexpr (!is_lenprefix_v<item_type>) {
for (auto& item : t) {
len += str_numeric_size<0, false>(item);
}
return len;
}
else {
static_assert(!sizeof(item_type), "the size of this type is meaningless");
}
}
else if constexpr (is_map_container<T>::value) {
static_assert(!sizeof(T), "the size of this type is meaningless");
}
else if constexpr (optional_v<T>) {
if (!t.has_value()) {
return 0;
}
return pb_value_size(*t, sz_ptr);
}
else {
return str_numeric_size<0, false>(t);
}
}
template <typename T, size_t... I>
inline auto build_pb_members_impl(
const std::array<size_t, sizeof...(I)>& offset_arr,
std::index_sequence<I...>) {
constexpr auto names = ylt::reflection::get_member_names<T>();
constexpr auto numbers = get_pb_field_numbers((T*)nullptr);
#ifdef YLT_USE_CXX26_REFLECTION
static constexpr auto members =
ylt::reflection::reflect26::data_members_array<T>();
return std::tuple_cat(
build_pb_fields_impl<T, numbers[I] - 1,
ylt::reflection::reflect26::meta_type_t<members[I]>>(
offset_arr[I], names[I])...);
#else
using Tuple = decltype(ylt::reflection::object_to_tuple(std::declval<T>()));
return std::tuple_cat(
build_pb_fields_impl<T, numbers[I] - 1, std::tuple_element_t<I, Tuple>>(
offset_arr[I], names[I])...);
#endif
}
template <typename T>
inline auto build_pb_members() {
#ifdef YLT_USE_CXX26_REFLECTION
constexpr size_t N = ylt::reflection::members_count_v<T>;
static const auto& offset_arr =
ylt::reflection::internal::get_member_offset_arr<T>();
#else
using Tuple = decltype(ylt::reflection::object_to_tuple(std::declval<T>()));
constexpr size_t N = std::tuple_size_v<Tuple>;
static auto& offset_arr = ylt::reflection::internal::get_member_offset_arr(
ylt::reflection::internal::wrapper<T>::value);
#endif
auto res =
build_pb_members_impl<T>(offset_arr, std::make_index_sequence<N>{});
using ResultTuple = decltype(res);
validate_pb_members_tuple<ResultTuple>();
return res;
}
#define YLT_REFL_PB(STRUCT, ...) \
IGUANA_PB_YLT_REFL_FWD( \
STRUCT, WRAP_ARGS(IGUANA_PB_FIELD_NAME, unused, ##__VA_ARGS__)) \
inline constexpr auto get_pb_field_numbers(STRUCT*) { \
return std::array<size_t, YLT_ARG_COUNT(__VA_ARGS__)>{ \
WRAP_ARGS(IGUANA_PB_FIELD_NO, unused, ##__VA_ARGS__)}; \
} \
inline auto get_members_impl(STRUCT*) { \
return iguana::detail::build_pb_members<STRUCT>(); \
}
#define IGUANA_PB_GET_FIELD_(field, no) field
#define IGUANA_PB_GET_FIELD(pair) IGUANA_PB_GET_FIELD_ pair
#define IGUANA_PB_GET_NO_(field, no) no
#define IGUANA_PB_GET_NO(pair) IGUANA_PB_GET_NO_ pair
#define IGUANA_PB_FIELD_NAME(unused, pair) IGUANA_PB_GET_FIELD(pair)
#define IGUANA_PB_FIELD_NO(unused, pair) (size_t) IGUANA_PB_GET_NO(pair)
#define IGUANA_PB_YLT_REFL_FWD(STRUCT, ...) YLT_REFL(STRUCT, __VA_ARGS__)
} // namespace detail
} // namespace iguana
+814
View File
@@ -0,0 +1,814 @@
#pragma once
#include "detail/string_resize.hpp"
#include "pb_util.hpp"
namespace iguana {
namespace detail {
template <uint32_t key, typename V, typename Writer>
IGUANA_INLINE void encode_varint_field(V val, Writer& writer) {
static_assert(std::is_integral_v<V>, "must be integral");
if constexpr (key != 0) {
serialize_varint_u32<key>(writer);
}
serialize_varint(val, writer);
}
template <uint32_t key, typename V, typename Writer>
IGUANA_INLINE void encode_fixed_field(V val, Writer& writer) {
if constexpr (key != 0) {
serialize_varint_u32<key>(writer);
}
constexpr size_t size = sizeof(V);
// TODO: check Stream continuous
writer.write((const char*)&val, size);
}
template <uint32_t key, bool omit_default_val = true, typename Type,
typename Writer>
IGUANA_INLINE void to_pb_impl(Type&& t, uint32_t*& sz_ptr, Writer& writer);
template <uint32_t key, bool omit_default_val, typename T, typename Writer>
IGUANA_INLINE void encode_numeric_field(T t, Writer& writer);
template <bool TimestampSchema, uint32_t key, bool omit_default_val = true,
typename Type, typename Writer>
IGUANA_INLINE void to_pb_well_known_impl(Type&& t, uint32_t*& sz_ptr,
Writer& writer) {
using T = std::remove_const_t<std::remove_reference_t<Type>>;
if constexpr (optional_v<T>) {
if (!t.has_value()) {
return;
}
to_pb_well_known_impl<TimestampSchema, key, omit_default_val>(*t, sz_ptr,
writer);
}
else if constexpr (is_sequence_container<T>::value) {
for (auto& item : t) {
to_pb_well_known_impl<TimestampSchema, key, false>(item, sz_ptr, writer);
}
}
else {
auto wire_value = make_pb_well_known_value<TimestampSchema>(t);
to_pb_impl<key, omit_default_val>(wire_value, sz_ptr, writer);
}
}
template <typename Wire, uint32_t key, bool omit_default_val = true,
typename Type, typename Writer>
IGUANA_INLINE void to_pb_schema_impl(Type&& t, uint32_t*& sz_ptr,
Writer& writer) {
using T = ylt::reflection::remove_cvref_t<Type>;
using W = ylt::reflection::remove_cvref_t<Wire>;
if constexpr (std::is_same_v<T, W>) {
to_pb_impl<key, omit_default_val>(std::forward<Type>(t), sz_ptr, writer);
}
else if constexpr (optional_v<T>) {
if (!t.has_value()) {
return;
}
to_pb_schema_impl<typename W::value_type, key, omit_default_val>(*t, sz_ptr,
writer);
}
else if constexpr (is_sequence_container<T>::value) {
using wire_item_type = typename W::value_type;
if constexpr (is_lenprefix_v<wire_item_type>) {
for (auto& item : t) {
to_pb_schema_impl<wire_item_type, key, false>(item, sz_ptr, writer);
}
}
else {
if (t.empty())
IGUANA_UNLIKELY { return; }
serialize_varint_u32<key>(writer);
size_t len = 0;
for (auto& item : t) {
auto wire_item = make_pb_wire_scalar<wire_item_type>(item);
len += str_numeric_size<0, false>(wire_item);
}
serialize_varint(len, writer);
for (auto& item : t) {
auto wire_item = make_pb_wire_scalar<wire_item_type>(item);
encode_numeric_field<0, false>(wire_item, writer);
}
}
}
else {
auto wire_value = make_pb_wire_scalar<W>(t);
to_pb_impl<key, omit_default_val>(wire_value, sz_ptr, writer);
}
}
template <uint32_t key, typename V, typename Writer>
IGUANA_INLINE void encode_pair_value(V&& val, size_t size, uint32_t*& sz_ptr,
Writer& writer) {
if (size == 0)
IGUANA_UNLIKELY {
// map keys can't be omitted even if values are empty
// TODO: repeated ?
serialize_varint_u32<key>(writer);
serialize_varint(0, writer);
}
else {
to_pb_impl<key, false>(val, sz_ptr, writer);
}
}
template <uint32_t key, bool omit_default_val, typename T, typename Writer>
IGUANA_INLINE void encode_numeric_field(T t, Writer& writer) {
if constexpr (omit_default_val) {
if constexpr (is_fixed_v<T> || is_signed_varint_v<T>) {
if (t.val == 0) {
return;
}
}
else {
if (t == static_cast<T>(0))
IGUANA_UNLIKELY { return; }
}
}
if constexpr (std::is_integral_v<T>) {
detail::encode_varint_field<key>(t, writer);
}
else if constexpr (detail::is_signed_varint_v<T>) {
detail::encode_varint_field<key>(encode_zigzag(t.val), writer);
}
else if constexpr (detail::is_fixed_v<T>) {
detail::encode_fixed_field<key>(t.val, writer);
}
else if constexpr (std::is_same_v<T, double> || std::is_same_v<T, float>) {
detail::encode_fixed_field<key>(t, writer);
}
else if constexpr (std::is_enum_v<T>) {
using U = std::underlying_type_t<T>;
detail::encode_varint_field<key>(static_cast<U>(t), writer);
}
else {
static_assert(!sizeof(T), "unsupported type");
}
}
template <uint32_t field_no, typename Type, typename Writer>
IGUANA_INLINE void to_pb_oneof(Type&& t, uint32_t*& sz_ptr, Writer& writer) {
using T = std::decay_t<Type>;
std::visit(
[&sz_ptr, &writer](auto&& value) IGUANA__INLINE_LAMBDA {
using raw_value_type = decltype(value);
using value_type =
std::remove_const_t<std::remove_reference_t<decltype(value)>>;
constexpr auto offset =
get_variant_index<T, value_type, std::variant_size_v<T> - 1>();
constexpr uint32_t key =
((field_no + offset) << 3) |
static_cast<uint32_t>(get_wire_type<value_type>());
to_pb_impl<key, false>(std::forward<raw_value_type>(value), sz_ptr,
writer);
},
std::forward<Type>(t));
}
// omit_default_val = true indicates to omit the default value in searlization
template <uint32_t key, bool omit_default_val, typename Type, typename Writer>
IGUANA_INLINE void to_pb_impl(Type&& t, uint32_t*& sz_ptr, Writer& writer) {
using T = std::remove_const_t<std::remove_reference_t<Type>>;
if constexpr (ylt_refletable_v<T> || is_custom_reflection_v<T>) {
// can't be omitted even if values are empty
if constexpr (key != 0) {
auto len = pb_value_size(t, sz_ptr);
serialize_varint_u32<key>(writer);
serialize_varint(len, writer);
if (len == 0)
IGUANA_UNLIKELY { return; }
}
static auto tuple = get_pb_members_tuple(std::forward<Type>(t));
constexpr size_t SIZE = std::tuple_size_v<std::decay_t<decltype(tuple)>>;
for_each_n(
[&t, &sz_ptr, &writer](auto i) IGUANA__INLINE_LAMBDA {
using field_type =
std::tuple_element_t<decltype(i)::value,
std::decay_t<decltype(tuple)>>;
auto value = std::get<decltype(i)::value>(tuple);
auto& val = value.value(t);
using U = typename field_type::value_type;
using wire_type = typename field_type::wire_value_type;
using sub_type = typename field_type::sub_type;
if constexpr (field_type::timestamp_schema ||
field_type::duration_schema) {
constexpr uint32_t sub_key =
(value.field_no << 3) |
static_cast<uint32_t>(WireType::LengthDelimeted);
to_pb_well_known_impl<field_type::timestamp_schema, sub_key>(
val, sz_ptr, writer);
}
else if constexpr (field_type::optional_schema) {
constexpr uint32_t sub_key =
(value.field_no << 3) |
static_cast<uint32_t>(get_wire_type<wire_type>());
to_pb_schema_impl<wire_type, sub_key, false>(val, sz_ptr, writer);
}
else if constexpr (variant_v<U>) {
constexpr auto offset =
get_variant_index<U, sub_type, std::variant_size_v<U> - 1>();
if (val.index() == offset) {
if constexpr (!std::is_same_v<sub_type, std::monostate>) {
constexpr uint32_t sub_key =
(value.field_no << 3) |
static_cast<uint32_t>(get_wire_type<sub_type>());
to_pb_impl<sub_key, false>(std::get<offset>(val), sz_ptr,
writer);
}
}
}
else {
constexpr uint32_t sub_key =
(value.field_no << 3) |
static_cast<uint32_t>(get_wire_type<wire_type>());
to_pb_schema_impl<wire_type, sub_key>(val, sz_ptr, writer);
}
},
std::make_index_sequence<SIZE>{});
write_pb_unknown_fields(t, writer);
}
else if constexpr (is_sequence_container<T>::value) {
// TODO support std::array
// repeated values can't be omitted even if values are empty
using item_type = typename T::value_type;
if constexpr (is_lenprefix_v<item_type>) {
// non-packed
for (auto& item : t) {
to_pb_impl<key, false>(item, sz_ptr, writer);
}
}
else {
if (t.empty())
IGUANA_UNLIKELY { return; }
serialize_varint_u32<key>(writer);
serialize_varint(pb_value_size(t, sz_ptr), writer);
for (auto& item : t) {
encode_numeric_field<false, 0>(item, writer);
}
}
}
else if constexpr (is_map_container<T>::value) {
using first_type = typename T::key_type;
using second_type = typename T::mapped_type;
constexpr uint32_t key1 =
(1 << 3) | static_cast<uint32_t>(get_wire_type<first_type>());
constexpr auto key1_size = variant_uint32_size_constexpr(key1);
constexpr uint32_t key2 =
(2 << 3) | static_cast<uint32_t>(get_wire_type<second_type>());
constexpr auto key2_size = variant_uint32_size_constexpr(key2);
for (auto& [k, v] : t) {
serialize_varint_u32<key>(writer);
// k must be string or numeric
auto k_val_len = str_numeric_size<0, false>(k);
auto v_val_len = pb_value_size<false>(v, sz_ptr);
auto pair_len = key1_size + key2_size + k_val_len + v_val_len;
if constexpr (is_lenprefix_v<first_type>) {
pair_len += variant_uint32_size(k_val_len);
}
if constexpr (is_lenprefix_v<second_type>) {
pair_len += variant_uint32_size(v_val_len);
}
serialize_varint(pair_len, writer);
// map k and v can't be omitted even if values are empty
encode_pair_value<key1>(k, k_val_len, sz_ptr, writer);
encode_pair_value<key2>(v, v_val_len, sz_ptr, writer);
}
}
else if constexpr (optional_v<T>) {
if (!t.has_value()) {
return;
}
to_pb_impl<key, omit_default_val>(*t, sz_ptr, writer);
}
else if constexpr (std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view>) {
if constexpr (omit_default_val) {
if (t.size() == 0)
IGUANA_UNLIKELY { return; }
}
serialize_varint_u32<key>(writer);
serialize_varint(t.size(), writer);
writer.write(t.data(), t.size());
}
else {
encode_numeric_field<key, omit_default_val>(t, writer);
}
}
#if defined(__clang__) || defined(_MSC_VER) || \
(defined(__GNUC__) && __GNUC__ > 8)
template <typename T>
IGUANA_INLINE constexpr std::string_view get_type_string() {
if constexpr (std::is_integral_v<T>) {
if constexpr (std::is_same_v<T, bool>) {
return "bool";
}
else if constexpr (sizeof(T) <= 4) {
if constexpr (std::is_unsigned_v<T>) {
return "uint32";
}
else {
return "int32";
}
}
else {
if constexpr (std::is_unsigned_v<T>) {
return "uint64";
}
else {
return "int64";
}
}
}
else if constexpr (std::is_same_v<T, iguana::sint32_t>) {
return "sint32";
}
else if constexpr (std::is_same_v<T, iguana::sint64_t>) {
return "sint64";
}
else if constexpr (std::is_same_v<T, iguana::fixed32_t>) {
return "fixed32";
}
else if constexpr (std::is_same_v<T, iguana::fixed64_t>) {
return "fixed64";
}
else if constexpr (std::is_same_v<T, iguana::sfixed32_t>) {
return "sfixed32";
}
else if constexpr (std::is_same_v<T, iguana::sfixed64_t>) {
return "sfixed64";
}
else if constexpr (std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view>) {
return "string";
}
else if constexpr (std::is_floating_point_v<T>) {
return type_string<T>();
}
else {
constexpr auto str_type_name = type_string<T>();
constexpr size_t pos = str_type_name.rfind("::");
if constexpr (pos != std::string_view::npos) {
constexpr size_t pos = str_type_name.rfind("::") + 2;
if constexpr (detail::is_signed_varint_v<T> || detail::is_fixed_v<T>) {
return str_type_name.substr(pos, str_type_name.size() - pos - 2);
}
else {
return str_type_name.substr(pos);
}
}
else {
return str_type_name;
}
}
}
template <typename T, typename Stream>
IGUANA_INLINE void numeric_to_proto(Stream& out, std::string_view field_name,
uint32_t field_no) {
constexpr auto name = get_type_string<T>();
out.append(name).append(" ");
out.append(field_name)
.append(" = ")
.append(std::to_string(field_no))
.append(";\n");
}
template <size_t space_count = 2, typename Stream>
IGUANA_INLINE void build_proto_field(Stream& out, std::string_view str_type,
std::string_view field_name,
uint32_t field_no) {
for (size_t i = 0; i < space_count; i++) {
out.append(" ");
}
if (!str_type.empty()) {
out.append(str_type);
}
out.append(" ")
.append(field_name)
.append(" = ")
.append(std::to_string(field_no))
.append(";\n");
}
template <typename T, typename Map>
IGUANA_INLINE void build_sub_proto(Map& map, std::string_view str_type,
std::string& sub_str);
template <typename Type, typename Stream>
IGUANA_INLINE void to_proto_impl(
Stream& out, std::unordered_map<std::string_view, std::string>& map,
std::string_view field_name = "", uint32_t field_no = 0) {
std::string sub_str;
using T = std::remove_const_t<std::remove_reference_t<Type>>;
if constexpr (ylt_refletable_v<T> || is_custom_reflection_v<T>) {
constexpr auto name = type_string<T>();
out.append("message ").append(name).append(" {\n");
static T t;
static auto tuple = get_pb_members_tuple(t);
constexpr size_t SIZE = std::tuple_size_v<std::decay_t<decltype(tuple)>>;
for_each_n(
[&out, &sub_str, &map](auto i) mutable {
using field_type =
std::tuple_element_t<decltype(i)::value,
std::decay_t<decltype(tuple)>>;
auto value = std::get<decltype(i)::value>(tuple);
using U = typename field_type::value_type;
using WireU = typename field_type::wire_value_type;
using sub_type = typename field_type::sub_type;
if constexpr (field_type::bytes_schema) {
if constexpr (is_sequence_container<U>::value) {
build_proto_field(
out, "repeated bytes",
{value.field_name.data(), value.field_name.size()},
value.field_no);
}
else if constexpr (optional_v<U>) {
out.append(" optional");
build_proto_field(
out, "bytes",
{value.field_name.data(), value.field_name.size()},
value.field_no);
}
else {
build_proto_field(
out, "bytes ",
{value.field_name.data(), value.field_name.size()},
value.field_no);
}
}
else if constexpr (field_type::timestamp_schema) {
if constexpr (is_sequence_container<U>::value) {
build_proto_field(
out, "repeated google.protobuf.Timestamp",
{value.field_name.data(), value.field_name.size()},
value.field_no);
}
else {
build_proto_field(
out, "google.protobuf.Timestamp",
{value.field_name.data(), value.field_name.size()},
value.field_no);
}
}
else if constexpr (field_type::duration_schema) {
if constexpr (is_sequence_container<U>::value) {
build_proto_field(
out, "repeated google.protobuf.Duration",
{value.field_name.data(), value.field_name.size()},
value.field_no);
}
else {
build_proto_field(
out, "google.protobuf.Duration",
{value.field_name.data(), value.field_name.size()},
value.field_no);
}
}
else if constexpr (field_type::optional_schema) {
static_assert(optional_v<U>,
"pb_optional member must be std::optional<T>");
out.append(" optional");
to_proto_impl<typename WireU::value_type>(
out, map, {value.field_name.data(), value.field_name.size()},
value.field_no);
}
else if constexpr (ylt_refletable_v<U>) {
constexpr auto str_type = get_type_string<U>();
build_proto_field(
out, str_type,
{value.field_name.data(), value.field_name.size()},
value.field_no);
build_sub_proto<U>(map, str_type, sub_str);
}
else if constexpr (variant_v<U>) {
constexpr size_t var_size = std::variant_size_v<U>;
constexpr size_t first_case = pb_variant_first_case_index<U>();
constexpr auto offset =
get_variant_index<U, sub_type, var_size - 1>();
if (offset == first_case) {
out.append(" oneof ");
out.append(value.field_name.data(), value.field_name.size())
.append(" {\n");
}
if constexpr (!std::is_same_v<sub_type, std::monostate>) {
constexpr auto str_type = get_type_string<sub_type>();
std::string field_name = " one_of_";
field_name.append(str_type);
out.append(" ");
build_proto_field(out, str_type, field_name, value.field_no);
if constexpr (ylt_refletable_v<sub_type>) {
build_sub_proto<sub_type>(map, str_type, sub_str);
}
}
if (offset == var_size - 1) {
out.append(" }\n");
}
}
else {
to_proto_impl<WireU>(
out, map, {value.field_name.data(), value.field_name.size()},
value.field_no);
}
},
std::make_index_sequence<SIZE>{});
out.append("}\r\n\r\n");
}
else if constexpr (is_sequence_container<T>::value) {
out.append(" repeated");
using item_type = typename T::value_type;
if constexpr (is_lenprefix_v<item_type>) {
// non-packed
if constexpr (ylt_refletable_v<item_type>) {
constexpr auto str_type = get_type_string<item_type>();
build_proto_field(out, str_type, field_name, field_no);
build_sub_proto<item_type>(map, str_type, sub_str);
}
else {
to_proto_impl<item_type>(out, map, field_name, field_no);
}
}
else {
out.append(" ");
numeric_to_proto<item_type>(out, field_name, field_no);
}
}
else if constexpr (is_map_container<T>::value) {
out.append(" map<");
using first_type = typename T::key_type;
using second_type = typename T::mapped_type;
constexpr auto str_first = get_type_string<first_type>();
constexpr auto str_second = get_type_string<second_type>();
out.append(str_first).append(", ").append(str_second).append(">");
build_proto_field<1>(out, "", field_name, field_no);
if constexpr (ylt_refletable_v<second_type>) {
constexpr auto str_type = get_type_string<second_type>();
build_sub_proto<second_type>(map, str_type, sub_str);
}
}
else if constexpr (optional_v<T>) {
to_proto_impl<typename T::value_type>(
out, map, {field_name.data(), field_name.size()}, field_no);
}
else if constexpr (std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view>) {
build_proto_field(out, "string ", field_name, field_no);
}
else if constexpr (enum_v<T>) {
constexpr auto str_type = get_type_string<T>();
static constexpr auto enum_to_str = get_enum_map<false, std::decay_t<T>>();
if constexpr (bool_v<decltype(enum_to_str)>) {
build_proto_field(out, "int32", field_name, field_no);
}
else {
static_assert(enum_to_str.size() > 0, "empty enum not allowed");
static_assert((int)(enum_to_str.begin()->first) == 0,
"the first enum value must be zero in proto3");
build_proto_field(out, str_type, field_name, field_no);
if (map.find(str_type) == map.end()) {
sub_str.append("enum ").append(str_type).append(" {\n");
for (auto& [k, field_name] : enum_to_str) {
std::string_view name{field_name.data(), field_name.size()};
size_t pos = name.rfind("::");
if (pos != std::string_view::npos) {
name = name.substr(pos + 2);
}
sub_str.append(" ")
.append(name)
.append(" = ")
.append(std::to_string(static_cast<std::underlying_type_t<T>>(k)))
.append(";\n");
}
sub_str.append("}\r\n\r\n");
map.emplace(str_type, std::move(sub_str));
}
}
}
else {
out.append(" ");
numeric_to_proto<Type>(out, field_name, field_no);
}
}
template <typename T, typename Map>
IGUANA_INLINE void build_sub_proto(Map& map, std::string_view str_type,
std::string& sub_str) {
if (map.find(str_type) == map.end()) {
to_proto_impl<T>(sub_str, map);
map.emplace(str_type, std::move(sub_str));
}
}
template <typename T>
constexpr bool proto_needs_timestamp_import();
template <typename T>
constexpr bool proto_needs_duration_import();
template <typename T>
constexpr bool proto_value_needs_timestamp_import();
template <typename T>
constexpr bool proto_value_needs_duration_import();
template <typename Variant, size_t... I>
constexpr bool proto_variant_needs_timestamp_import(std::index_sequence<I...>) {
return (proto_value_needs_timestamp_import<
std::variant_alternative_t<I, Variant>>() ||
...);
}
template <typename Variant, size_t... I>
constexpr bool proto_variant_needs_duration_import(std::index_sequence<I...>) {
return (proto_value_needs_duration_import<
std::variant_alternative_t<I, Variant>>() ||
...);
}
template <typename T>
constexpr bool proto_value_needs_timestamp_import() {
using U = std::remove_cvref_t<T>;
if constexpr (std::is_same_v<U, std::monostate> ||
pb_well_known_chrono_v<U>) {
return false;
}
else if constexpr (optional_v<U>) {
return proto_value_needs_timestamp_import<typename U::value_type>();
}
else if constexpr (is_sequence_container<U>::value) {
return proto_value_needs_timestamp_import<typename U::value_type>();
}
else if constexpr (is_map_container<U>::value) {
return proto_value_needs_timestamp_import<typename U::mapped_type>();
}
else if constexpr (variant_v<U>) {
return proto_variant_needs_timestamp_import<U>(
std::make_index_sequence<std::variant_size_v<U>>{});
}
else if constexpr (ylt_refletable_v<U> || is_custom_reflection_v<U>) {
return proto_needs_timestamp_import<U>();
}
else {
return false;
}
}
template <typename T>
constexpr bool proto_value_needs_duration_import() {
using U = std::remove_cvref_t<T>;
if constexpr (std::is_same_v<U, std::monostate> ||
pb_well_known_chrono_v<U>) {
return false;
}
else if constexpr (optional_v<U>) {
return proto_value_needs_duration_import<typename U::value_type>();
}
else if constexpr (is_sequence_container<U>::value) {
return proto_value_needs_duration_import<typename U::value_type>();
}
else if constexpr (is_map_container<U>::value) {
return proto_value_needs_duration_import<typename U::mapped_type>();
}
else if constexpr (variant_v<U>) {
return proto_variant_needs_duration_import<U>(
std::make_index_sequence<std::variant_size_v<U>>{});
}
else if constexpr (ylt_refletable_v<U> || is_custom_reflection_v<U>) {
return proto_needs_duration_import<U>();
}
else {
return false;
}
}
template <typename Tuple, size_t... I>
constexpr bool proto_tuple_needs_timestamp_import(std::index_sequence<I...>) {
return ((std::tuple_element_t<I, Tuple>::timestamp_schema ||
proto_value_needs_timestamp_import<
typename std::tuple_element_t<I, Tuple>::value_type>()) ||
...);
}
template <typename Tuple, size_t... I>
constexpr bool proto_tuple_needs_duration_import(std::index_sequence<I...>) {
return ((std::tuple_element_t<I, Tuple>::duration_schema ||
proto_value_needs_duration_import<
typename std::tuple_element_t<I, Tuple>::value_type>()) ||
...);
}
template <typename T>
constexpr bool proto_needs_timestamp_import() {
if constexpr (ylt_refletable_v<T> || is_custom_reflection_v<T>) {
using Tuple =
std::decay_t<decltype(get_pb_members_tuple(std::declval<T&>()))>;
return proto_tuple_needs_timestamp_import<Tuple>(
std::make_index_sequence<std::tuple_size_v<Tuple>>{});
}
else {
return false;
}
}
template <typename T>
constexpr bool proto_needs_duration_import() {
if constexpr (ylt_refletable_v<T> || is_custom_reflection_v<T>) {
using Tuple =
std::decay_t<decltype(get_pb_members_tuple(std::declval<T&>()))>;
return proto_tuple_needs_duration_import<Tuple>(
std::make_index_sequence<std::tuple_size_v<Tuple>>{});
}
else {
return false;
}
}
#endif
} // namespace detail
template <
typename T, typename Stream,
std::enable_if_t<ylt_refletable_v<T> || detail::is_custom_reflection_v<T>,
int> = 0>
IGUANA_INLINE void to_pb(T const& t, Stream& out) {
std::vector<uint32_t> size_arr;
auto byte_len = detail::pb_key_value_size<0>(t, size_arr);
auto sz_ptr = size_arr.empty() ? nullptr : &size_arr[0];
if constexpr (is_resizable_char_container_v<Stream>) {
detail::resize(out, byte_len);
memory_writer writer{out.data()};
detail::to_pb_impl<0>(t, sz_ptr, writer);
}
else if constexpr (char_writer<Stream>) {
detail::to_pb_impl<0>(t, sz_ptr, out);
}
else {
static_assert(!sizeof(Stream), "Invalid stream type");
}
}
#if defined(__clang__) || defined(_MSC_VER) || \
(defined(__GNUC__) && __GNUC__ > 8)
template <typename T, bool gen_header = true, typename Stream>
IGUANA_INLINE void to_proto(Stream& out, std::string_view ns = "") {
if (gen_header) {
constexpr std::string_view crlf = "\r\n\r\n";
out.append(R"(syntax = "proto3";)").append(crlf);
if constexpr (detail::proto_needs_timestamp_import<T>()) {
out.append(R"(import "google/protobuf/timestamp.proto";)").append(crlf);
}
if constexpr (detail::proto_needs_duration_import<T>()) {
out.append(R"(import "google/protobuf/duration.proto";)").append(crlf);
}
if (!ns.empty()) {
out.append("package ").append(ns).append(";").append(crlf);
}
out.append(R"(option optimize_for = SPEED;)").append(crlf);
out.append(R"(option cc_enable_arenas = true;)").append(crlf);
}
std::unordered_map<std::string_view, std::string> map;
detail::to_proto_impl<T>(out, map);
for (auto& [k, s] : map) {
out.append(s);
}
}
template <typename T, bool gen_header = true, typename Stream>
IGUANA_INLINE void to_proto_file(Stream& stream, std::string_view ns = "") {
if (!stream.is_open()) {
return;
}
std::string out;
to_proto<T, gen_header>(out, ns);
stream.write(out.data(), out.size());
}
#endif
template <typename T, typename Stream>
IGUANA_INLINE void to_pb_adl(iguana_adl_t* p, T const& t, Stream& out) {
to_pb(t, out);
}
} // namespace iguana
+144
View File
@@ -0,0 +1,144 @@
#pragma once
// https://github.com/stephenberry/glaze/blob/main/include/glaze/json/prettify.hpp
namespace iguana {
namespace detail {
enum class general_state : uint32_t {
NORMAL,
ESCAPED,
STRING,
BEFORE_ASTERISK,
COMMENT,
BEFORE_FSLASH
};
template <typename Out, typename NL>
inline void prettify_normal_state(const char c, Out &out, uint32_t &indent,
NL nl, general_state &state) noexcept {
switch (c) {
case ',':
out += c;
nl();
break;
case '[':
out += c;
++indent;
nl();
break;
case ']':
--indent;
nl();
out += c;
break;
case '{':
out += c;
++indent;
nl();
break;
case '}':
--indent;
nl();
out += c;
break;
case '\\':
out += c;
state = general_state::ESCAPED;
break;
case '\"':
out += c;
state = general_state::STRING;
break;
case '/':
out += " /";
state = general_state::BEFORE_ASTERISK;
break;
case ':':
out += ": ";
break;
case ' ':
case '\n':
case '\r':
case '\t':
break;
default:
out += c;
break;
}
}
inline void prettify_other_states(const char c, general_state &state) noexcept {
switch (state) {
case general_state::ESCAPED:
state = general_state::NORMAL;
break;
case general_state::STRING:
if (c == '"') {
state = general_state::NORMAL;
}
break;
case general_state::BEFORE_ASTERISK:
state = general_state::COMMENT;
break;
case general_state::COMMENT:
if (c == '*') {
state = general_state::BEFORE_FSLASH;
}
break;
case general_state::BEFORE_FSLASH:
if (c == '/') {
state = general_state::NORMAL;
}
else {
state = general_state::COMMENT;
}
break;
default:
break;
}
}
} // namespace detail
/// <summary>
/// pretty print a JSON string
/// </summary>
template <typename In, typename Out>
inline void prettify(const In &in, Out &out, const bool tabs = false,
const uint32_t indent_size = 3) noexcept {
out.reserve(in.size());
uint32_t indent{};
auto nl = [&]() {
out += "\n";
for (uint32_t i = 0; i < indent * (tabs ? 1 : indent_size); i++) {
out += tabs ? "\t" : " ";
}
};
using namespace detail;
general_state state{general_state::NORMAL};
for (auto c : in) {
if (state == general_state::NORMAL) {
prettify_normal_state(c, out, indent, nl, state);
continue;
}
else {
out += c;
prettify_other_states(c, state);
}
}
}
/// <summary>
/// allocating version of prettify
/// </summary>
template <typename In>
inline std::string prettify(const In &in, const bool tabs = false,
const uint32_t indent_size = 3) noexcept {
std::string out{};
prettify(in, out, tabs, indent_size);
return out;
}
} // namespace iguana
+389
View File
@@ -0,0 +1,389 @@
#pragma once
#include <math.h>
#include <filesystem>
#include <forward_list>
#include <fstream>
#include <memory>
#include <optional>
#include <stdexcept>
#include <string_view>
#include <type_traits>
#include "define.h"
#include "detail/charconv.h"
#include "detail/pb_type.hpp"
#include "detail/traits.hpp"
#include "detail/utf.hpp"
#include "error_code.h"
#include "field_reflection.hpp"
#include "ylt/reflection/member_value.hpp"
#include "ylt/reflection/user_reflect_macro.hpp"
namespace iguana {
template <typename T>
inline constexpr bool char_v = std::is_same_v<std::decay_t<T>, char> ||
std::is_same_v<std::decay_t<T>, char16_t> ||
std::is_same_v<std::decay_t<T>, char32_t> ||
std::is_same_v<std::decay_t<T>, wchar_t>;
template <typename T>
inline constexpr bool bool_v =
std::is_same_v<std::decay_t<T>, bool> ||
std::is_same_v<std::decay_t<T>, std::vector<bool>::reference>;
template <typename T>
inline constexpr bool int_v = std::is_integral_v<std::decay_t<T>> &&
!char_v<std::decay_t<T>> && !bool_v<T>;
template <typename T>
inline constexpr bool float_v = std::is_floating_point_v<std::decay_t<T>>;
template <typename T>
inline constexpr bool num_v = float_v<T> || int_v<T>;
template <typename T>
inline constexpr bool enum_v = std::is_enum_v<std::decay_t<T>>;
template <typename T>
constexpr inline bool optional_v = ylt::reflection::optional<T>;
template <typename T>
constexpr inline bool pair_v = ylt::reflection::pair<T>;
template <class, class = void>
struct is_container : std::false_type {};
template <class T>
struct is_container<
T, std::void_t<decltype(std::declval<T>().size(), std::declval<T>().begin(),
std::declval<T>().end())>> : std::true_type {};
template <class, class = void>
struct is_map_container : std::false_type {};
template <class T>
struct is_map_container<
T, std::void_t<decltype(std::declval<typename T::mapped_type>())>>
: is_container<T> {};
template <typename T>
constexpr inline bool container_v = is_container<std::remove_cvref_t<T>>::value;
template <typename T>
constexpr inline bool map_container_v =
is_map_container<std::remove_cvref_t<T>>::value;
template <class T>
constexpr inline bool c_array_v = std::is_array_v<std::remove_cvref_t<T>>&&
std::extent_v<std::remove_cvref_t<T>> > 0;
template <typename Type, typename = void>
struct is_array : std::false_type {};
template <typename T>
struct is_array<
T, std::void_t<decltype(std::declval<T>().size()),
typename std::enable_if_t<(std::tuple_size<T>::value != 0)>>>
: std::true_type {};
template <typename T>
constexpr inline bool array_v = is_array<std::remove_cvref_t<T>>::value;
template <typename Type>
constexpr inline bool fixed_array_v = c_array_v<Type> ||
#if __cplusplus > 201703L
#if __has_include(<span>)
is_span<Type>::value ||
#endif
#endif
array_v<Type>;
template <typename T>
constexpr inline bool string_view_v =
is_template_instant_of<std::basic_string_view,
std::remove_cvref_t<T>>::value;
template <typename T>
constexpr inline bool string_v =
is_template_instant_of<std::basic_string, std::remove_cvref_t<T>>::value;
// TODO: type must be char
template <typename T>
constexpr inline bool json_view_v = container_v<T>;
template <typename T>
constexpr inline bool json_byte_v =
std::is_same_v<char, std::remove_cvref_t<T>> ||
std::is_same_v<unsigned char, std::remove_cvref_t<T>> ||
std::is_same_v<std::byte, std::remove_cvref_t<T>>;
template <typename T>
constexpr inline bool sequence_container_v =
is_sequence_container<std::remove_cvref_t<T>>::value;
template <typename T>
constexpr inline bool tuple_v = is_tuple<std::remove_cvref_t<T>>::value;
template <typename T>
constexpr inline bool string_container_v = string_v<T> || string_view_v<T>;
template <typename T>
constexpr inline bool unique_ptr_v =
is_template_instant_of<std::unique_ptr, std::remove_cvref_t<T>>::value;
template <typename T>
constexpr inline bool shared_ptr_v =
is_template_instant_of<std::shared_ptr, std::remove_cvref_t<T>>::value;
template <typename T>
constexpr inline bool smart_ptr_v = shared_ptr_v<T> || unique_ptr_v<T>;
template <typename T>
constexpr inline bool variant_v = is_variant<std::remove_cvref_t<T>>::value;
template <size_t Idx, typename T>
using variant_element_t = std::remove_reference_t<decltype(std::get<Idx>(
std::declval<std::remove_reference_t<T>>()))>;
template <typename F, typename Tuple, size_t... Is>
constexpr void foreach_tuple(F&& f, Tuple& tp, std::index_sequence<Is...>) {
(void(f(std::get<Is>(tp), std::integral_constant<size_t, Is>{})), ...);
}
template <typename F, typename Tuple>
constexpr void foreach_tuple(F&& f, Tuple& tp) {
foreach_tuple(std::forward<F>(f), tp,
std::make_index_sequence<std::tuple_size_v<Tuple>>{});
}
template <typename F, typename T>
inline constexpr auto for_each_tuple(F&& f, T&& tup) {
std::apply(
[&f](auto&&... args) {
(f(args), ...);
},
std::forward<T>(tup));
}
template <class T>
constexpr inline bool reflect26_excluded_v = false;
template <class T>
constexpr inline bool ylt_refletable_v =
#ifdef YLT_USE_CXX26_REFLECTION
(std::is_class_v<ylt::reflection::remove_cvref_t<T>> ||
std::is_aggregate_v<ylt::reflection::remove_cvref_t<T>>) &&
!string_container_v<T> && !container_v<T> && !fixed_array_v<T> &&
!tuple_v<T> && !optional_v<T> && !variant_v<T> && !smart_ptr_v<T> &&
!reflect26_excluded_v<ylt::reflection::remove_cvref_t<T>> &&
!ylt::reflection::is_custom_refl_v<T> && !is_pb_type_v<T>;
#else
(ylt::reflection::is_ylt_refl_v<T> ||
std::is_aggregate_v<ylt::reflection::remove_cvref_t<T>>) &&
!fixed_array_v<T> && !ylt::reflection::is_custom_refl_v<T> &&
!is_pb_type_v<T>;
#endif
template <class T>
constexpr inline bool non_ylt_refletable_v = !ylt_refletable_v<T>;
template <typename T>
constexpr inline bool plain_v =
string_container_v<T> || num_v<T> || char_v<T> || bool_v<T> || enum_v<T>;
template <typename T>
struct underline_type {
using type = T;
};
template <typename T>
struct underline_type<std::unique_ptr<T>> {
using type = typename underline_type<T>::type;
};
template <typename T>
struct underline_type<std::shared_ptr<T>> {
using type = typename underline_type<T>::type;
};
template <typename T>
struct underline_type<std::optional<T>> {
using type = typename underline_type<T>::type;
};
template <typename T>
using underline_type_t = typename underline_type<std::remove_cvref_t<T>>::type;
struct memory_writer {
char* buffer;
void write(const char* data, std::size_t len) {
memcpy(buffer, data, len);
buffer += len;
}
};
template <char... C, typename It>
IGUANA_INLINE void match(It&& it, It&& end) {
const auto n = static_cast<size_t>(std::distance(it, end));
if (n < sizeof...(C))
IGUANA_UNLIKELY {
// TODO: compile time generate this message, currently borken with
// MSVC
static constexpr auto error = "Unexpected end of buffer. Expected:";
throw std::runtime_error(error);
}
if (((... || (*it++ != C))))
IGUANA_UNLIKELY {
// TODO: compile time generate this message, currently borken with
// MSVC
static constexpr char b[] = {C..., '\0'};
throw std::runtime_error(std::string("Expected these: ").append(b));
}
}
inline constexpr auto has_zero = [](uint64_t chunk) IGUANA__INLINE_LAMBDA {
return (((chunk - 0x0101010101010101) & ~chunk) & 0x8080808080808080);
};
inline constexpr auto has_qoute = [](uint64_t chunk) IGUANA__INLINE_LAMBDA {
return has_zero(
chunk ^
0b0010001000100010001000100010001000100010001000100010001000100010);
};
template <bool is_xml_serialization = false, typename Stream, typename Ch>
IGUANA_INLINE void write_unicode_to_string(Ch& it, Stream& ss) {
static const char hexDigits[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
unsigned codepoint = 0;
if (!decode_utf8(it, codepoint))
IGUANA_UNLIKELY { throw std::runtime_error("illegal unicode character"); }
if constexpr (is_xml_serialization) {
ss.append("&#x");
}
else {
ss.push_back('\\');
ss.push_back('u');
}
if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) {
ss.push_back(hexDigits[(codepoint >> 12) & 15]);
ss.push_back(hexDigits[(codepoint >> 8) & 15]);
ss.push_back(hexDigits[(codepoint >> 4) & 15]);
ss.push_back(hexDigits[(codepoint)&15]);
}
else {
if (codepoint < 0x010000 || codepoint > 0x10FFFF)
IGUANA_UNLIKELY { throw std::runtime_error("illegal codepoint"); }
// Surrogate pair
unsigned s = codepoint - 0x010000;
unsigned lead = (s >> 10) + 0xD800;
unsigned trail = (s & 0x3FF) + 0xDC00;
ss.push_back(hexDigits[(lead >> 12) & 15]);
ss.push_back(hexDigits[(lead >> 8) & 15]);
ss.push_back(hexDigits[(lead >> 4) & 15]);
ss.push_back(hexDigits[(lead)&15]);
if constexpr (is_xml_serialization) {
ss.append(";&#x");
}
else {
ss.push_back('\\');
ss.push_back('u');
}
ss.push_back(hexDigits[(trail >> 12) & 15]);
ss.push_back(hexDigits[(trail >> 8) & 15]);
ss.push_back(hexDigits[(trail >> 4) & 15]);
ss.push_back(hexDigits[(trail)&15]);
}
if constexpr (is_xml_serialization) {
ss.push_back(';');
}
}
// https://github.com/Tencent/rapidjson/blob/master/include/rapidjson/writer.h
template <typename Ch, typename SizeType, typename Stream>
IGUANA_INLINE void write_string_with_escape(const Ch* it, SizeType length,
Stream& ss) {
static const char hexDigits[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
static const char escape[256] = {
#define Z16 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
// 0 1 2 3 4 5 6 7 8 9 A B C D E
// F
'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'b', 't',
'n', 'u', 'f', 'r', 'u', 'u', // 00
'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u',
'u', 'u', 'u', 'u', 'u', 'u', // 10
0, 0, '"', 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, // 20
Z16, Z16, // 30~4F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, '\\', 0, 0, 0, // 50
Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 // 60~FF
#undef Z16
};
auto end = it;
std::advance(end, length);
while (it < end) {
if (static_cast<unsigned>(*it) >= 0x80)
IGUANA_UNLIKELY { write_unicode_to_string(it, ss); }
else if (escape[static_cast<unsigned char>(*it)])
IGUANA_UNLIKELY {
ss.push_back('\\');
ss.push_back(escape[static_cast<unsigned char>(*it)]);
if (escape[static_cast<unsigned char>(*it)] == 'u') {
// escape other control characters
ss.push_back('0');
ss.push_back('0');
ss.push_back(hexDigits[static_cast<unsigned char>(*it) >> 4]);
ss.push_back(hexDigits[static_cast<unsigned char>(*it) & 0xF]);
}
++it;
}
else {
ss.push_back(*(it++));
}
}
}
template <typename T, size_t N>
IGUANA_INLINE constexpr bool has_duplicate(const std::array<T, N>& arr) {
for (size_t i = 0; i < arr.size(); i++) {
for (size_t j = i + 1; j < arr.size(); j++) {
if (arr[i] == arr[j]) {
return true;
}
}
}
return false;
}
#if defined(__clang__) || defined(_MSC_VER) || \
(defined(__GNUC__) && __GNUC__ > 8)
template <typename... Types>
IGUANA_INLINE constexpr bool has_duplicate_type() {
std::array<std::string_view, sizeof...(Types)> arr{
iguana::type_string<Types>()...};
return has_duplicate(arr);
}
template <typename T>
struct has_duplicate_type_in_variant : std::false_type {};
template <typename... Us>
struct has_duplicate_type_in_variant<std::variant<Us...>> {
inline constexpr static bool value = has_duplicate_type<Us...>();
};
template <typename T>
constexpr inline bool has_duplicate_type_v =
has_duplicate_type_in_variant<T>::value;
#else
template <typename T>
constexpr inline bool has_duplicate_type_v = false;
#endif
} // namespace iguana
+203
View File
@@ -0,0 +1,203 @@
#pragma once
#include <string>
#include <string_view>
#include <unordered_map>
#include <utility>
#include <variant>
#include <vector>
#include "error_code.h"
namespace iguana {
#define GCC_COMPILER (defined(__GNUC__) && !defined(__clang__))
#ifdef GCC_COMPILER
#include <map>
template <class Key, class T, typename... Args>
using json_map = std::map<Key, T, Args...>;
#else
template <class Key, class T, typename... Args>
using json_map = std::unordered_map<Key, T, Args...>;
#endif
enum dom_parse_error { ok, wrong_type };
template <typename CharT>
struct basic_json_value
: std::variant<std::monostate, std::nullptr_t, bool, double, int,
std::basic_string<CharT>,
std::vector<basic_json_value<CharT>>,
json_map<std::basic_string<CharT>, basic_json_value<CharT>>,
std::basic_string_view<CharT>> {
using string_type = std::basic_string<CharT>;
using string_view_type = std::basic_string_view<CharT>;
using array_type = std::vector<basic_json_value<CharT>>;
using object_type = json_map<string_type, basic_json_value<CharT>>;
using base_type =
std::variant<std::monostate, std::nullptr_t, bool, double, int,
string_type, array_type, object_type, string_view_type>;
using base_type::base_type;
inline const static std::unordered_map<size_t, std::string> type_map_ = {
{0, "undefined type"}, {1, "null type"}, {2, "bool type"},
{3, "double type"}, {4, "int type"}, {5, "string type"},
{6, "array type"}, {7, "object type"}, {8, "string_view type"}};
basic_json_value() : base_type(std::in_place_type<std::monostate>) {}
basic_json_value(CharT const *value)
: base_type(std::in_place_type<string_type>, value) {}
base_type &base() { return *this; }
base_type const &base() const { return *this; }
bool is_undefined() const {
return std::holds_alternative<std::monostate>(*this);
}
bool is_null() const { return std::holds_alternative<std::nullptr_t>(*this); }
bool is_bool() const { return std::holds_alternative<bool>(*this); }
bool is_double() const { return std::holds_alternative<double>(*this); }
bool is_int() const { return std::holds_alternative<int>(*this); }
bool is_number() const { return is_double() || is_int(); }
bool is_string() const { return std::holds_alternative<string_type>(*this); }
bool is_array() const { return std::holds_alternative<array_type>(*this); }
bool is_object() const { return std::holds_alternative<object_type>(*this); }
bool is_string_view() const {
return std::holds_alternative<string_view_type>(*this);
}
// if type is not match, will throw exception, if pass std::error_code, won't
// throw exception
template <typename T>
T get() const {
try {
return std::get<T>(*this);
} catch (std::exception &e) {
auto it = type_map_.find(this->index());
if (it == type_map_.end()) {
throw std::invalid_argument("undefined type");
}
else {
throw std::invalid_argument(it->second);
}
} catch (...) {
throw std::invalid_argument("unknown exception");
}
}
template <typename T>
T get(std::error_code &ec) const {
try {
return get<T>();
} catch (std::exception &e) {
ec = iguana::make_error_code(iguana::dom_errc::wrong_type, e.what());
return T{};
}
}
template <typename T>
std::error_code get_to(T &v) const {
std::error_code ec;
v = get<T>(ec);
return ec;
}
template <typename T>
T at(const std::string &key) {
const auto &map = get<object_type>();
auto it = map.find(key);
if (it == map.end()) {
throw std::invalid_argument("the key is unknown");
}
return it->second.template get<T>();
}
template <typename T>
T at(const std::string &key, std::error_code &ec) {
const auto &map = get<object_type>(ec);
if (ec) {
return T{};
}
auto it = map.find(key);
if (it == map.end()) {
ec = std::make_error_code(std::errc::invalid_argument);
return T{};
}
return it->second.template get<T>(ec);
}
template <typename T>
T at(size_t idx) {
const auto &arr = get<array_type>();
if (idx >= arr.size()) {
throw std::out_of_range("idx is out of range");
}
return arr[idx].template get<T>();
}
template <typename T>
T at(size_t idx, std::error_code &ec) {
const auto &arr = get<array_type>(ec);
if (ec) {
return T{};
}
if (idx >= arr.size()) {
ec = std::make_error_code(std::errc::result_out_of_range);
return T{};
}
return arr[idx].template get<T>(ec);
}
object_type to_object() const { return get<object_type>(); }
object_type to_object(std::error_code &ec) const {
return get<object_type>(ec);
}
array_type to_array() const { return get<array_type>(); }
array_type to_array(std::error_code &ec) const { return get<array_type>(ec); }
double to_double() const { return get<double>(); }
double to_double(std::error_code &ec) const { return get<double>(ec); }
int to_int() const { return get<int>(); }
int to_int(std::error_code &ec) const { return get<int>(ec); }
bool to_bool() const { return get<bool>(); }
bool to_bool(std::error_code &ec) const { return get<bool>(ec); }
string_type to_string() const { return get<string_type>(); }
string_type to_string(std::error_code &ec) const {
return get<string_type>(ec);
}
string_view_type to_string_view() const { return get<string_view_type>(); }
string_view_type to_string_view(std::error_code &ec) const {
return get<string_view_type>(ec);
}
};
template <typename CharT>
using basic_jarray = typename basic_json_value<CharT>::array_type;
template <typename CharT>
using basic_jobject = typename basic_json_value<CharT>::object_type;
template <typename CharT>
using basic_jpair = typename basic_jobject<CharT>::value_type;
using jvalue = basic_json_value<char>;
using jarray = basic_jarray<char>;
using jobject = basic_jobject<char>;
using jpair = basic_jpair<char>;
template <typename CharT>
void swap(basic_json_value<CharT> &lhs, basic_json_value<CharT> &rhs) noexcept {
lhs.swap(rhs);
}
} // namespace iguana
+8
View File
@@ -0,0 +1,8 @@
#pragma once
// Note: Update the version when release a new version.
// IGUANA_VERSION % 100 is the sub-minor version
// IGUANA_VERSION / 100 % 1000 is the minor version
// IGUANA_VERSION / 100000 is the major version
#define IGUANA_VERSION 100005 // 1.0.5
+577
View File
@@ -0,0 +1,577 @@
#pragma once
#include <charconv>
#include <vector>
#include "detail/charconv.h"
#include "detail/utf.hpp"
#include "xml_util.hpp"
#ifdef YLT_USE_CXX26_REFLECTION
#include "ylt/reflection/reflect26_dispatch.hpp"
#endif
namespace iguana {
namespace detail {
template <typename U, typename It, std::enable_if_t<optional_v<U>, int> = 0>
IGUANA_INLINE void xml_parse_item(U &value, It &&it, It &&end,
std::string_view name);
template <typename U, typename It, std::enable_if_t<smart_ptr_v<U>, int> = 0>
IGUANA_INLINE void xml_parse_item(U &value, It &&it, It &&end,
std::string_view name);
template <typename U, typename It,
std::enable_if_t<ylt_refletable_v<U>, int> = 0>
IGUANA_INLINE void xml_parse_item(U &value, It &&it, It &&end,
std::string_view name);
template <typename U, typename It, std::enable_if_t<attr_v<U>, int> = 0>
IGUANA_INLINE void xml_parse_item(U &value, It &&it, It &&end,
std::string_view name);
template <typename U, typename It, std::enable_if_t<plain_v<U>, int> = 0>
IGUANA_INLINE void xml_parse_value(U &&value, It &&begin, It &&end) {
using T = std::decay_t<U>;
if constexpr (string_container_v<T>) {
if constexpr (string_view_v<T>) {
value = T(&*begin, static_cast<size_t>(std::distance(begin, end)));
}
else {
// TODO: When not parsing the value in the attribute, it is not necessary
// to unescape'and "
value.clear();
auto pre = begin;
while (advance_until_character<'&'>(begin, end)) {
value.append(T(&*pre, static_cast<size_t>(std::distance(pre, begin))));
parse_escape_xml(value, begin, end);
pre = begin;
}
value.append(T(&*pre, static_cast<size_t>(std::distance(pre, begin))));
}
}
else if constexpr (num_v<T>) {
auto size = std::distance(begin, end);
const auto start = &*begin;
detail::from_chars(start, start + size, value);
}
else if constexpr (char_v<T>) {
if (static_cast<size_t>(std::distance(begin, end)) != 1)
IGUANA_UNLIKELY { throw std::runtime_error("Expected one character"); }
value = *begin;
}
else if constexpr (bool_v<T>) {
auto bool_v = std::string_view(
&*begin, static_cast<size_t>(std::distance(begin, end)));
if (bool_v == "true") {
value = true;
}
else if (bool_v == "false") {
value = false;
}
else
IGUANA_UNLIKELY { throw std::runtime_error("Expected true or false"); }
}
else if constexpr (enum_v<T>) {
static constexpr auto str_to_enum = get_enum_map<true, T>();
if constexpr (bool_v<decltype(str_to_enum)>) {
// not defined a specialization template
xml_parse_value(reinterpret_cast<std::underlying_type_t<T> &>(value),
begin, end);
}
else {
auto enum_names = std::string_view(
&*begin, static_cast<size_t>(std::distance(begin, end)));
auto it = str_to_enum.find(enum_names);
if (it != str_to_enum.end())
IGUANA_LIKELY { value = it->second; }
else {
throw std::runtime_error(std::string(enum_names) +
" missing corresponding value in enum_value");
}
}
}
}
template <typename U, typename It, std::enable_if_t<is_pb_type_v<U>, int> = 0>
IGUANA_INLINE void xml_parse_value(U &&value, It &&begin, It &&end) {
xml_parse_value(value.val, begin, end);
}
template <typename U, typename It,
std::enable_if_t<map_container_v<U>, int> = 0>
IGUANA_INLINE void parse_attr(U &&value, It &&it, It &&end) {
using key_type = typename std::remove_cvref_t<U>::key_type;
using value_type = typename std::remove_cvref_t<U>::mapped_type;
while (it != end) {
skip_sapces_and_newline(it, end);
if (*it == '>' || *it == '/')
IGUANA_UNLIKELY { return; }
auto key_begin = it;
auto key_end = skip_pass<'='>(it, end);
key_type key;
xml_parse_value(key, key_begin, key_end);
skip_sapces_and_newline(it, end);
auto value_begin = it + 1;
auto value_end = value_begin;
if (*it == '"')
IGUANA_LIKELY {
++it;
value_end = skip_pass<'"'>(it, end);
}
else if (*it == '\'') {
++it;
value_end = skip_pass<'\''>(it, end);
}
else
IGUANA_UNLIKELY { throw std::runtime_error("expected quote or apos"); }
value_type v;
xml_parse_value(v, value_begin, value_end);
value.emplace(std::move(key), std::move(v));
}
}
template <typename U, typename It, std::enable_if_t<plain_v<U>, int> = 0>
IGUANA_INLINE void xml_parse_item(U &value, It &&it, It &&end,
std::string_view name) {
skip_till<'>'>(it, end);
++it;
skip_sapces_and_newline(it, end);
auto value_begin = it;
auto value_end = skip_pass<'<'>(it, end);
xml_parse_value(value, value_begin, value_end);
match_close_tag(it, end, name);
}
template <typename U, typename It, std::enable_if_t<is_pb_type_v<U>, int> = 0>
IGUANA_INLINE void xml_parse_item(U &value, It &&it, It &&end,
std::string_view name) {
xml_parse_item(value.val, it, end, name);
}
template <typename U, typename It,
std::enable_if_t<sequence_container_v<U>, int> = 0>
IGUANA_INLINE void xml_parse_item(U &value, It &&it, It &&end,
std::string_view name) {
xml_parse_item(value.emplace_back(), it, end, name);
skip_sapces_and_newline(it, end);
while (it != end) {
match<'<'>(it, end);
if (*it == '?' || *it == '!')
IGUANA_UNLIKELY {
--it;
return;
}
auto start = it;
skip_till_greater_or_space(it, end);
std::string_view key = std::string_view{
&*start, static_cast<size_t>(std::distance(start, it))};
if (key != name)
IGUANA_UNLIKELY {
it = start - 1;
return;
}
xml_parse_item(value.emplace_back(), it, end, name);
skip_sapces_and_newline(it, end);
}
}
template <typename U, typename It,
std::enable_if_t<map_container_v<U>, int> = 0>
IGUANA_INLINE void xml_parse_item(U &value, It &&it, It &&end,
std::string_view name) {
throw std::bad_function_call();
}
template <typename U, typename It, std::enable_if_t<variant_v<U>, int> = 0>
IGUANA_INLINE void xml_parse_item(U &value, It &&it, It &&end,
std::string_view name) {
throw std::bad_function_call();
}
template <typename U, typename It, std::enable_if_t<optional_v<U>, int>>
IGUANA_INLINE void xml_parse_item(U &value, It &&it, It &&end,
std::string_view name) {
using value_type = typename std::remove_reference_t<U>::value_type;
skip_till<'>'>(it, end);
if (*(it - 1) == '/')
IGUANA_LIKELY {
++it;
return;
}
if constexpr (plain_v<value_type>) {
// The following code is for option not to be emplaced
// when parse "...> <..."
skip_till<'>'>(it, end);
++it;
skip_sapces_and_newline(it, end);
auto value_begin = it;
auto value_end = skip_pass<'<'>(it, end);
if (value_begin == value_end) {
match_close_tag(it, end, name);
return;
}
xml_parse_value(value.emplace(), value_begin, value_end);
match_close_tag(it, end, name);
}
else {
xml_parse_item(value.emplace(), it, end, name);
}
}
template <typename U, typename It, std::enable_if_t<smart_ptr_v<U>, int>>
IGUANA_INLINE void xml_parse_item(U &value, It &&it, It &&end,
std::string_view name) {
if constexpr (unique_ptr_v<U>) {
value = std::make_unique<typename std::remove_cvref_t<U>::element_type>();
}
else {
value = std::make_shared<typename std::remove_cvref_t<U>::element_type>();
}
xml_parse_item(*value, it, end, name);
}
template <typename U, typename It, std::enable_if_t<attr_v<U>, int>>
IGUANA_INLINE void xml_parse_item(U &value, It &&it, It &&end,
std::string_view name) {
parse_attr(value.attr(), it, end);
xml_parse_item(value.value(), it, end, name);
}
// /> or skip <?>、 <!> and <tag></tag> until the </name>
// loose inspection here
template <typename It>
IGUANA_INLINE void skip_object_value(It &&it, It &&end, std::string_view name) {
skip_till<'>'>(it, end);
++it;
if (*(it - 2) == '/') {
// .../>
return;
}
// </name>
size_t size = name.size();
while (it != end) {
skip_till<'>'>(it, end);
if (*(it - size - 2) == '<' && *(it - size - 1) == '/' &&
(std::string_view{&*(it - size), size} == name)) {
++it;
return;
}
++it;
skip_sapces_and_newline(it, end);
}
throw std::runtime_error("unclosed tag: " + std::string(name));
}
// skip <?...?>
template <typename It>
IGUANA_INLINE void skip_instructions(It &&it, It &&end) {
while (*(it - 1) != '?') {
++it;
skip_till<'>'>(it, end);
}
++it;
}
template <typename It>
IGUANA_INLINE void skip_cdata(It &&it, It &&end) {
++it;
skip_till<']'>(it, end);
++it;
match<']', '>'>(it, end);
}
template <typename It>
IGUANA_INLINE void skip_comment(It &&it, It &&end) {
while (*(it - 1) != '-' || *(it - 2) != '-') {
++it;
skip_till<'>'>(it, end);
}
++it;
}
// return true means reach the close tag
template <size_t cdata_idx, typename T, typename It,
std::enable_if_t<ylt_refletable_v<T>, int> = 0>
IGUANA_INLINE auto skip_till_close_tag(T &value, It &&it, It &&end) {
while (true) {
skip_sapces_and_newline(it, end);
match<'<'>(it, end);
if (*it == '/')
IGUANA_UNLIKELY {
// reach the close tag
return true;
}
else if (*it == '?')
IGUANA_UNLIKELY {
skip_instructions(it, end);
continue;
}
else if (*it == '!')
IGUANA_UNLIKELY {
++it;
if (*it == '[') {
// <![
if constexpr (cdata_idx == ylt::reflection::members_count_v<T>) {
skip_cdata(it, end);
}
else {
// if parse cdata
++it;
match<'C', 'D', 'A', 'T', 'A', '['>(it, end);
skip_sapces_and_newline(it, end);
auto &cdata_value = ylt::reflection::get<cdata_idx>(value);
using VT = typename std::decay_t<decltype(cdata_value)>::value_type;
auto vb = it;
auto ve = skip_pass<']'>(it, end);
if constexpr (string_view_v<VT>) {
cdata_value.value() =
VT(&*vb, static_cast<size_t>(std::distance(vb, ve)));
}
else {
cdata_value.value().append(
&*vb, static_cast<size_t>(std::distance(vb, ve)));
}
match<']', '>'>(it, end);
}
}
else if (*it == '-') {
// <!-- -->
skip_comment(it, end);
}
else {
// <!D... >
skip_till<'>'>(it, end);
++it;
}
continue;
}
return false;
}
}
template <typename It>
IGUANA_INLINE void skip_till_first_key(It &&it, It &&end) {
while (it != end) {
skip_sapces_and_newline(it, end);
match<'<'>(it, end);
if (*it == '?')
IGUANA_UNLIKELY {
skip_instructions(it, end);
continue;
}
else if (*it == '!')
IGUANA_UNLIKELY {
++it;
if (*it == '-') {
// <!-- -->
skip_comment(it, end);
}
else {
// <!D... >
skip_till<'>'>(it, end);
++it;
}
continue;
}
else {
break;
}
}
}
IGUANA_INLINE bool contains_xml_key(const std::vector<std::string_view> &keys,
std::string_view key) {
for (auto item : keys) {
if (item == key) {
return true;
}
}
return false;
}
template <typename T>
IGUANA_INLINE void check_required(
const std::vector<std::string_view> &parsed_keys) {
if constexpr (iguana::has_iguana_required_arr_v<T>) {
constexpr auto required_arr =
iguana::iguana_required_struct<T>::requied_arr();
for (auto &item : required_arr) {
if (!contains_xml_key(parsed_keys, item)) {
std::string err = "required filed ";
err.append(item).append(" not found!");
throw std::invalid_argument(err);
}
}
}
#ifdef YLT_USE_CXX26_REFLECTION
if constexpr (iguana::detail::xml_required_count<T>() > 0) {
constexpr auto required_arr = iguana::detail::xml_required_names<T>();
for (auto &item : required_arr) {
if (!contains_xml_key(parsed_keys, item)) {
std::string err = "required filed ";
err.append(item).append(" not found!");
throw std::invalid_argument(err);
}
}
}
#endif
}
template <typename T, typename It, std::enable_if_t<ylt_refletable_v<T>, int>>
IGUANA_INLINE void xml_parse_item(T &value, It &&it, It &&end,
std::string_view name) {
using U = std::decay_t<T>;
constexpr auto cdata_idx = get_type_index<is_cdata_t, U>();
skip_till<'>'>(it, end);
++it;
if (skip_till_close_tag<cdata_idx>(value, it, end)) {
match_close_tag(it, end, name);
return;
}
auto start = it;
skip_till_greater_or_space(it, end);
std::string_view key =
std::string_view{&*start, static_cast<size_t>(std::distance(start, it))};
[[maybe_unused]] std::vector<std::string_view> parsed_keys;
bool parse_done = false;
// sequential parse
ylt::reflection::for_each(value, [&](auto &field, auto st_key, auto index) {
#if defined(_MSC_VER) && _MSVC_LANG < 202002L
constexpr auto cdata_idx = get_type_index<is_cdata_t, U>();
#endif
using item_type = std::remove_reference_t<decltype(field)>;
if constexpr (cdata_v<item_type>) {
return;
}
if (parse_done || key != st_key)
IGUANA_UNLIKELY { return; }
if constexpr (!cdata_v<item_type>) {
xml_parse_item(field, it, end, key);
if constexpr (iguana::has_xml_required_fields_v<U>) {
parsed_keys.push_back(key);
}
}
if (skip_till_close_tag<cdata_idx>(value, it, end))
IGUANA_UNLIKELY {
match_close_tag(it, end, name);
parse_done = true;
return;
}
start = it;
skip_till_greater_or_space(it, end);
key = std::string_view{&*start,
static_cast<size_t>(std::distance(start, it))};
});
if (parse_done)
IGUANA_UNLIKELY {
check_required<U>(parsed_keys);
return;
}
// map parse
while (true) {
#ifdef YLT_USE_CXX26_REFLECTION
bool found = ylt::reflection::reflect26::dispatch_by_name(
value, key, [&](auto &field) IGUANA__INLINE_LAMBDA {
if constexpr (!cdata_v<std::remove_reference_t<decltype(field)>>) {
xml_parse_item(field, it, end, key);
if constexpr (iguana::has_xml_required_fields_v<U>) {
parsed_keys.push_back(key);
}
}
});
if (!found)
IGUANA_UNLIKELY {
#ifdef THROW_UNKNOWN_KEY
throw std::runtime_error("Unknown key: " + std::string(key));
#else
skip_object_value(it, end, key);
#endif
}
#else
static auto frozen_map = ylt::reflection::get_variant_map<U>();
const auto &member_it = frozen_map.find(key);
if (member_it != frozen_map.end())
IGUANA_LIKELY {
std::visit(
[&](auto offset) IGUANA__INLINE_LAMBDA {
using value_type = typename decltype(offset)::type;
if constexpr (!cdata_v<value_type>) {
auto member_ptr =
(value_type *)((char *)(&value) + offset.value);
xml_parse_item(*member_ptr, it, end, key);
if constexpr (iguana::has_xml_required_fields_v<U>) {
parsed_keys.push_back(key);
}
}
},
member_it->second);
}
else
IGUANA_UNLIKELY {
#ifdef THROW_UNKNOWN_KEY
throw std::runtime_error("Unknown key: " + std::string(key));
#else
skip_object_value(it, end, key);
#endif
}
#endif
if (skip_till_close_tag<cdata_idx>(value, it, end)) {
match_close_tag(it, end, name);
check_required<U>(parsed_keys);
return;
}
start = it;
skip_till_greater_or_space(it, end);
key = std::string_view{&*start,
static_cast<size_t>(std::distance(start, it))};
}
}
} // namespace detail
template <typename It, typename U, std::enable_if_t<attr_v<U>, int> = 0>
IGUANA_INLINE void from_xml(U &value, It &&it, It &&end) {
detail::skip_till_first_key(it, end);
auto start = it;
skip_till_greater_or_space(it, end);
std::string_view key =
std::string_view{&*start, static_cast<size_t>(std::distance(start, it))};
detail::parse_attr(value.attr(), it, end);
detail::xml_parse_item(value.value(), it, end, key);
}
template <typename It, typename U,
std::enable_if_t<ylt_refletable_v<U>, int> = 0>
IGUANA_INLINE void from_xml(U &value, It &&it, It &&end) {
detail::skip_till_first_key(it, end);
auto start = it;
skip_till_greater_or_space(it, end);
std::string_view key =
std::string_view{&*start, static_cast<size_t>(std::distance(start, it))};
detail::xml_parse_item(value, it, end, key);
}
template <typename U, typename View,
std::enable_if_t<string_container_v<View>, int> = 0>
IGUANA_INLINE void from_xml(U &value, const View &view) {
from_xml(value, std::begin(view), std::end(view));
}
template <typename Num, std::enable_if_t<num_v<Num>, int> = 0>
IGUANA_INLINE Num get_number(std::string_view str) {
Num num;
detail::xml_parse_value(num, str.begin(), str.end());
return num;
}
template <typename T>
IGUANA_INLINE void from_xml_adl(iguana_adl_t *p, T &t,
std::string_view pb_str) {
iguana::from_xml(t, pb_str);
}
} // namespace iguana
+465
View File
@@ -0,0 +1,465 @@
#pragma once
#include "common.hpp"
#include "detail/pb_type.hpp"
#include "util.hpp"
#ifdef YLT_USE_CXX26_REFLECTION
#include <meta>
#include "ylt/reflection/reflect26_core.hpp"
#endif
namespace iguana {
struct xml_required {};
template <typename T>
struct iguana_required_struct;
#define REQUIRED_IMPL(STRUCT_NAME, N, ...) \
template <> \
struct iguana::iguana_required_struct<STRUCT_NAME> { \
inline static constexpr auto requied_arr() { \
std::array<std::string_view, N> arr_required = { \
WRAP_ARGS(CONCAT_NAME, 0, ##__VA_ARGS__)}; \
return arr_required; \
} \
};
#define REQUIRED(STRUCT_NAME, ...) \
REQUIRED_IMPL(STRUCT_NAME, YLT_ARG_COUNT(__VA_ARGS__), __VA_ARGS__)
template <class T, class = void>
struct has_iguana_required_arr : std::false_type {};
template <class T>
struct has_iguana_required_arr<
T, std::void_t<decltype(iguana_required_struct<T>::requied_arr())>>
: std::true_type {};
template <class T>
constexpr bool has_iguana_required_arr_v = has_iguana_required_arr<T>::value;
#ifdef YLT_USE_CXX26_REFLECTION
namespace detail {
template <typename T>
struct is_xml_required_annotation : std::false_type {};
template <>
struct is_xml_required_annotation<iguana::xml_required> : std::true_type {};
template <std::meta::info Member>
consteval bool has_xml_required() {
return ylt::reflection::reflect26::has_annotation<
Member, is_xml_required_annotation>();
}
template <typename T>
consteval size_t xml_required_count() {
using U = ylt::reflection::remove_cvref_t<T>;
static constexpr auto members =
ylt::reflection::reflect26::data_members_array<U>();
size_t count = 0;
template for (constexpr auto member : members) {
if constexpr (has_xml_required<member>()) {
++count;
}
}
return count;
}
template <typename T>
consteval auto xml_required_names() {
using U = ylt::reflection::remove_cvref_t<T>;
static constexpr auto members =
ylt::reflection::reflect26::data_members_array<U>();
constexpr auto member_names = ylt::reflection::get_member_names<U>();
std::array<std::string_view, xml_required_count<U>()> names{};
size_t index = 0;
size_t member_index = 0;
template for (constexpr auto member : members) {
if constexpr (has_xml_required<member>()) {
names[index++] = member_names[member_index];
}
++member_index;
}
return names;
}
} // namespace detail
#endif
template <class T>
constexpr bool has_xml_required_fields_v =
has_iguana_required_arr_v<T>
#ifdef YLT_USE_CXX26_REFLECTION
|| detail::xml_required_count<T>() > 0
#endif
;
template <typename T,
typename map_type = std::unordered_map<std::string, std::string>>
class xml_attr_t {
public:
T &value() { return val_; }
map_type &attr() { return attr_; }
const T &value() const { return val_; }
const map_type &attr() const { return attr_; }
using value_type = std::remove_cvref_t<T>;
private:
T val_;
map_type attr_;
};
template <typename T>
using xml_attr_view_t =
xml_attr_t<T, std::map<std::string_view, std::string_view>>;
template <typename T = std::string_view,
std::enable_if_t<string_container_v<T>, int> = 0>
class xml_cdata_t {
public:
T &value() { return val_; }
const T &value() const { return val_; }
using value_type = std::remove_cvref_t<T>;
private:
T val_;
};
template <typename T>
constexpr inline bool is_attr_t_v = false;
template <typename T, typename map_type>
constexpr inline bool is_attr_t_v<xml_attr_t<T, map_type>> = true;
template <typename T>
constexpr inline bool attr_v = is_attr_t_v<std::remove_cvref_t<T>>;
template <typename>
struct is_cdata_t : std::false_type {};
template <typename T>
struct is_cdata_t<xml_cdata_t<T>> : std::true_type {};
template <typename T, typename map_type>
constexpr inline bool reflect26_excluded_v<xml_attr_t<T, map_type>> = true;
template <typename T>
constexpr inline bool reflect26_excluded_v<xml_cdata_t<T>> = true;
template <std::size_t index, template <typename...> typename Condition,
typename Tuple>
constexpr int element_index_helper() {
if constexpr (index == std::tuple_size_v<Tuple>) {
return index;
}
else {
using item_type =
ylt::reflection::remove_cvref_t<std::tuple_element_t<index, Tuple>>;
return Condition<item_type>::value
? index
: element_index_helper<index + 1, Condition, Tuple>();
}
}
template <template <typename...> typename Condition, typename T>
constexpr int tuple_element_index() {
#ifdef YLT_USE_CXX26_REFLECTION
return static_cast<int>(
ylt::reflection::reflect26::member_index_if<Condition, T>());
#else
using Tuple = decltype(ylt::reflection::object_to_tuple(std::declval<T>()));
return element_index_helper<0, Condition, Tuple>();
#endif
}
template <template <typename...> typename Condition, typename T>
constexpr size_t get_type_index() {
return tuple_element_index<Condition, T>();
}
template <typename T>
constexpr inline bool cdata_v = is_cdata_t<std::remove_cvref_t<T>>::value;
inline constexpr auto has_greater = [](uint64_t chunk) IGUANA__INLINE_LAMBDA {
return has_zero(
chunk ^
0b0011111000111110001111100011111000111110001111100011111000111110);
};
inline constexpr auto has_space = [](uint64_t chunk) IGUANA__INLINE_LAMBDA {
return has_zero(
chunk ^
0b0010000000100000001000000010000000100000001000000010000000100000);
};
inline constexpr auto has_smaller = [](uint64_t chunk) IGUANA__INLINE_LAMBDA {
return has_zero(
chunk ^
0b0011110000111100001111000011110000111100001111000011110000111100);
};
inline constexpr auto has_square_bracket =
[](uint64_t chunk) IGUANA__INLINE_LAMBDA {
return has_zero(
chunk ^
0b0101110101011101010111010101110101011101010111010101110101011101);
};
inline constexpr auto has_and = [](uint64_t chunk) IGUANA__INLINE_LAMBDA {
return has_zero(
chunk ^
0b0010011000100110001001100010011000100110001001100010011000100110);
};
inline constexpr auto has_equal = [](uint64_t chunk) IGUANA__INLINE_LAMBDA {
return has_zero(
chunk ^
0b0011110100111101001111010011110100111101001111010011110100111101);
};
inline constexpr auto has_apos = [](uint64_t chunk) IGUANA__INLINE_LAMBDA {
return has_zero(
chunk ^
0b0010011100100111001001110010011100100111001001110010011100100111);
};
template <typename It>
IGUANA_INLINE void skip_sapces_and_newline(It &&it, It &&end) {
while (it != end && (static_cast<uint8_t>(*it) < 33)) {
++it;
}
}
template <typename It>
IGUANA_INLINE void match_close_tag(It &&it, It &&end, std::string_view key) {
if (it == end || (*it++) != '/')
IGUANA_UNLIKELY {
throw std::runtime_error("unclosed tag: " + std::string(key));
}
size_t size = key.size();
if (static_cast<size_t>(std::distance(it, end)) <= size ||
std::string_view{&*it, size} != key)
IGUANA_UNLIKELY {
throw std::runtime_error("unclosed tag: " + std::string(key));
}
it += size;
match<'>'>(it, end);
// skip_till<'>'>(it, end); // not check
// ++it;
}
// returns true if the specified character 'c' is found, false otherwise.
template <char c, typename It>
IGUANA_INLINE bool advance_until_character(It &&it, It &&end) {
static_assert(contiguous_iterator<std::decay_t<It>>);
if (std::distance(it, end) >= 7)
IGUANA_LIKELY {
const auto end_m7 = end - 7;
for (; it < end_m7; it += 8) {
const auto chunk = *reinterpret_cast<const uint64_t *>(&*it);
uint64_t test;
if constexpr (c == '&')
test = has_and(chunk);
else
static_assert(!c, "not support this character");
if (test != 0) {
it += (countr_zero(test) >> 3);
return true;
}
}
}
// Tail end of buffer. Should be rare we even get here
while (it < end) {
if (*it == c)
return true;
++it;
}
return false;
}
template <char c, typename It>
IGUANA_INLINE void skip_till(It &&it, It &&end) {
static_assert(contiguous_iterator<std::decay_t<It>>);
if (std::distance(it, end) >= 7)
IGUANA_LIKELY {
const auto end_m7 = end - 7;
for (; it < end_m7; it += 8) {
const auto chunk = *reinterpret_cast<const uint64_t *>(&*it);
uint64_t test;
if constexpr (c == '>')
test = has_greater(chunk);
else if constexpr (c == '<')
test = has_smaller(chunk);
else if constexpr (c == '"')
test = has_qoute(chunk);
else if constexpr (c == ' ')
test = has_space(chunk);
else if constexpr (c == ']')
test = has_square_bracket(chunk);
else if constexpr (c == '=')
test = has_equal(chunk);
else if constexpr (c == '\'')
test = has_apos(chunk);
else
static_assert(!c, "not support this character");
if (test != 0) {
it += (countr_zero(test) >> 3);
return;
}
}
}
// Tail end of buffer. Should be rare we even get here
while (it < end) {
if (*it == c)
return;
++it;
}
static constexpr char b[] = {c, '\0'};
std::string error = std::string("Expected: ").append(b);
throw std::runtime_error(error);
}
// skip_till<'>', '<'>(it, end);
template <typename It>
IGUANA_INLINE void skip_till_greater_or_space(It &&it, It &&end) {
static_assert(contiguous_iterator<std::decay_t<It>>);
if (std::distance(it, end) >= 7)
IGUANA_LIKELY {
const auto end_m7 = end - 7;
for (; it < end_m7; it += 8) {
const auto chunk = *reinterpret_cast<const uint64_t *>(&*it);
uint64_t test = has_greater(chunk) | has_space(chunk);
if (test != 0) {
it += (countr_zero(test) >> 3);
return;
}
}
}
// Tail end of buffer. Should be rare we even get here
while (it < end) {
switch (*it) {
case '>':
case ' ':
return;
}
++it;
}
throw std::runtime_error("Expected > or space");
}
template <char c, typename It>
IGUANA_INLINE auto skip_pass(It &&it, It &&end) {
skip_till<c>(it, end);
auto res = it++ - 1;
while (*res == ' ') {
--res;
}
return res + 1;
}
template <char... C, typename It>
IGUANA_INLINE bool is_match(It &&it, const It &end) {
const auto n = static_cast<size_t>(std::distance(it, end));
if ((n < sizeof...(C)) || (... || (*it++ != C))) {
return false;
}
return true;
}
template <typename U, typename It, std::enable_if_t<string_v<U>, int> = 0>
IGUANA_INLINE void parse_escape_xml(U &value, It &&it, It &&end) {
static const unsigned char lookup_digits[256] = {
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255,
255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255};
switch (*(it + 1)) {
// &amp; &apos;
case 'a':
if (is_match<'m', 'p', ';'>(it + 2, end)) {
value.push_back('&');
it += 5;
return;
}
if (is_match<'p', 'o', 's', ';'>(it + 2, end)) {
value.push_back('\'');
it += 6;
}
break;
// &quot;
case 'q':
if (is_match<'u', 'o', 't', ';'>(it + 2, end)) {
value.push_back('\"');
it += 6;
}
break;
// &gt;
case 'g':
if (is_match<'t', ';'>(it + 2, end)) {
value.push_back('>');
it += 4;
}
break;
// &lt;
case 'l':
if (is_match<'t', ';'>(it + 2, end)) {
value.push_back('<');
it += 4;
}
break;
case '#':
if (*(it + 2) == 'x') {
// &#x
unsigned long codepoint = 0;
it += 3;
while (true) {
auto digit = lookup_digits[static_cast<unsigned char>(*it)];
if (digit == 0xFF)
break;
codepoint = codepoint * 16 + digit;
++it;
}
encode_utf8(value, codepoint);
}
else {
unsigned long codepoint = 0;
it += 2;
while (true) {
auto digit = lookup_digits[static_cast<unsigned char>(*it)];
if (digit == 0xFF)
break;
codepoint = codepoint * 10 + digit;
++it;
}
encode_utf8(value, codepoint);
}
match<';'>(it, end);
break;
default:
// skip '&'
// loose policy: allow '&'
value.push_back(*(it++));
break;
}
}
} // namespace iguana
+307
View File
@@ -0,0 +1,307 @@
#pragma once
#include "detail/charconv.h"
#include "xml_util.hpp"
namespace iguana {
#ifdef XML_ATTR_USE_APOS
#define XML_ATTR_DELIMITER '\''
#else
#define XML_ATTR_DELIMITER '\"'
#endif
// TODO: improve by precaculate size
template <bool escape_quote_apos, typename Ch, typename SizeType,
typename Stream>
IGUANA_INLINE void render_string_with_escape_xml(const Ch *it, SizeType length,
Stream &ss) {
auto end = it;
std::advance(end, length);
while (it < end) {
#ifdef XML_ESCAPE_UNICODE
if (static_cast<unsigned>(*it) >= 0x80)
IGUANA_UNLIKELY {
write_unicode_to_string<true>(it, ss);
continue;
}
#endif
if constexpr (escape_quote_apos) {
if constexpr (XML_ATTR_DELIMITER == '\"') {
if (*it == '"')
IGUANA_UNLIKELY {
ss.append("&quot;");
++it;
continue;
}
}
else {
if (*it == '\'')
IGUANA_UNLIKELY {
ss.append("&apos;");
++it;
continue;
}
}
}
if (*it == '&')
IGUANA_UNLIKELY { ss.append("&amp;"); }
else if (*it == '>')
IGUANA_UNLIKELY { ss.append("&gt;"); }
else if (*it == '<')
IGUANA_UNLIKELY { ss.append("&lt;"); }
else {
ss.push_back(*it);
}
++it;
}
}
template <bool pretty, size_t spaces, typename Stream, typename T,
std::enable_if_t<sequence_container_v<T>, int> = 0>
IGUANA_INLINE void render_xml_value(Stream &ss, const T &value,
std::string_view name);
template <bool pretty, size_t spaces, typename Stream, typename T,
std::enable_if_t<map_container_v<T>, int> = 0>
IGUANA_INLINE void render_xml_value(Stream &ss, const T &value,
std::string_view name);
template <bool pretty, size_t spaces, typename Stream, typename T,
std::enable_if_t<ylt_refletable_v<T>, int> = 0>
IGUANA_INLINE void render_xml_value(Stream &ss, T &&t, std::string_view name);
template <bool pretty, size_t spaces, typename Stream>
IGUANA_INLINE void render_tail(Stream &ss, std::string_view str) {
if constexpr (pretty) {
ss.append(spaces, '\t');
}
ss.push_back('<');
ss.push_back('/');
ss.append(str.data(), str.size());
ss.push_back('>');
if constexpr (pretty) {
ss.push_back('\n');
}
}
template <bool pretty, size_t spaces, typename Stream>
IGUANA_INLINE void render_head(Stream &ss, std::string_view str) {
if constexpr (pretty) {
ss.append(spaces, '\t');
}
ss.push_back('<');
ss.append(str.data(), str.size());
ss.push_back('>');
}
template <bool escape_quote_apos = false, typename Stream, typename T,
std::enable_if_t<plain_v<T>, int> = 0>
IGUANA_INLINE void render_value(Stream &ss, const T &value) {
if constexpr (string_container_v<T>) {
render_string_with_escape_xml<escape_quote_apos>(value.data(), value.size(),
ss);
}
else if constexpr (num_v<T>) {
char temp[65];
auto p = detail::to_chars(temp, value);
ss.append(temp, p - temp);
}
else if constexpr (char_v<T>) {
ss.push_back(value);
}
else if constexpr (bool_v<T>) {
ss.append(value ? "true" : "false");
}
else if constexpr (enum_v<T>) {
static constexpr auto enum_to_str = get_enum_map<false, std::decay_t<T>>();
if constexpr (bool_v<decltype(enum_to_str)>) {
render_value(ss, static_cast<std::underlying_type_t<T>>(value));
}
else {
auto it = enum_to_str.find(value);
if (it != enum_to_str.end())
IGUANA_LIKELY {
auto str = it->second;
render_value(ss, std::string_view(str.data(), str.size()));
}
else {
throw std::runtime_error(
std::to_string(static_cast<std::underlying_type_t<T>>(value)) +
" is a missing value in enum_value");
}
}
}
else {
static_assert(!sizeof(T), "type is not supported");
}
}
template <bool escape_quote_apos = false, typename Stream, typename T,
std::enable_if_t<is_pb_type_v<T>, int> = 0>
IGUANA_INLINE void render_value(Stream &ss, const T &value) {
render_value<escape_quote_apos>(ss, value);
}
template <bool pretty, size_t spaces, typename Stream, typename T,
std::enable_if_t<map_container_v<T>, int> = 0>
inline void render_xml_attr(Stream &ss, const T &value, std::string_view name) {
if constexpr (pretty) {
ss.append(spaces, '\t');
}
ss.push_back('<');
ss.append(name.data(), name.size());
for (auto [k, v] : value) {
ss.push_back(' ');
render_value(ss, k);
ss.push_back('=');
ss.push_back(XML_ATTR_DELIMITER);
render_value<true>(ss, v);
ss.push_back(XML_ATTR_DELIMITER);
}
ss.push_back('>');
}
template <bool pretty, size_t spaces, typename Stream, typename T,
std::enable_if_t<plain_v<T>, int> = 0>
IGUANA_INLINE void render_xml_value(Stream &ss, const T &value,
std::string_view name) {
render_value(ss, value);
render_tail<pretty, 0>(ss, name);
}
template <bool pretty, size_t spaces, typename Stream, typename T,
std::enable_if_t<is_pb_type_v<std::decay_t<T>>, int> = 0>
IGUANA_INLINE void render_xml_value(Stream &ss, const T &value,
std::string_view name) {
render_xml_value<pretty, spaces>(ss, value.val, name);
}
template <bool pretty, size_t spaces, typename Stream, typename T,
std::enable_if_t<variant_v<std::decay_t<T>>, int> = 0>
IGUANA_INLINE void render_xml_value(Stream &ss, const T &value,
std::string_view name) {
throw std::bad_function_call();
}
template <bool pretty, size_t spaces, typename Stream, typename T,
std::enable_if_t<optional_v<T>, int> = 0>
IGUANA_INLINE void render_xml_value(Stream &ss, const T &value,
std::string_view name) {
if (value) {
render_xml_value<pretty, spaces>(ss, *value, name);
}
else {
render_tail<pretty, 0>(ss, name);
}
}
template <bool pretty, size_t spaces, typename Stream, typename T,
std::enable_if_t<smart_ptr_v<T>, int> = 0>
IGUANA_INLINE void render_xml_value(Stream &ss, const T &value,
std::string_view name) {
if (value) {
render_xml_value<pretty, spaces>(ss, *value, name);
}
else {
render_tail<pretty, 0>(ss, name);
}
}
template <bool pretty, size_t spaces, typename Stream, typename T,
std::enable_if_t<attr_v<T>, int> = 0>
IGUANA_INLINE void render_xml_value(Stream &ss, const T &value,
std::string_view name) {
render_xml_attr<pretty, spaces>(ss, value.attr(), name);
render_xml_value<pretty, spaces>(ss, value.value(), name);
}
template <bool pretty, size_t spaces, typename Stream, typename T,
std::enable_if_t<sequence_container_v<T>, int>>
IGUANA_INLINE void render_xml_value(Stream &ss, const T &value,
std::string_view name) {
using value_type =
underline_type_t<typename std::remove_cvref_t<T>::value_type>;
for (const auto &v : value) {
if constexpr (attr_v<value_type>) {
render_xml_value<pretty, spaces>(ss, v, name);
}
else {
render_head<pretty, spaces>(ss, name);
render_xml_value<pretty, spaces>(ss, v, name);
}
}
}
template <bool pretty, size_t spaces, typename Stream, typename T,
std::enable_if_t<map_container_v<T>, int>>
IGUANA_INLINE void render_xml_value(Stream &ss, const T &value,
std::string_view name) {
throw std::bad_function_call();
}
template <bool pretty, size_t spaces, typename Stream, typename T,
std::enable_if_t<variant_v<T>, int>>
IGUANA_INLINE void render_xml_value(Stream &ss, const T &value,
std::string_view name) {
throw std::bad_function_call();
}
template <bool pretty, size_t spaces, typename Stream, typename T,
std::enable_if_t<ylt_refletable_v<T>, int>>
IGUANA_INLINE void render_xml_value(Stream &ss, T &&t, std::string_view name) {
if constexpr (pretty) {
ss.push_back('\n');
}
ylt::reflection::for_each(t, [&](auto &field, auto tag_name, auto index) {
using value_type = underline_type_t<decltype(field)>;
static_assert(!std::is_pointer_v<value_type>,
"don't use raw pointer, please use smart pointer");
if constexpr (sequence_container_v<value_type>) {
render_xml_value<pretty, spaces + 1>(ss, field, tag_name);
}
else if constexpr (attr_v<value_type>) {
render_xml_value<pretty, spaces + 1>(ss, field, tag_name);
}
else if constexpr (cdata_v<value_type>) {
if constexpr (pretty) {
ss.append(spaces + 1, '\t');
ss.append("<![CDATA[").append(field.value()).append("]]>\n");
}
else {
ss.append("<![CDATA[").append(field.value()).append("]]>");
}
}
else {
render_head<pretty, spaces + 1>(ss, tag_name);
render_xml_value<pretty, spaces + 1>(ss, field, tag_name);
}
});
render_tail<pretty, spaces>(ss, name);
}
template <bool pretty = false, typename Stream, typename T,
std::enable_if_t<attr_v<T>, int> = 0>
IGUANA_INLINE void to_xml(T &&t, Stream &s) {
using value_type = typename std::decay_t<T>::value_type;
static_assert(ylt_refletable_v<value_type>, "value_type must be refletable");
constexpr std::string_view root_name =
ylt::reflection::get_struct_name<value_type>();
render_xml_value<pretty, 0>(s, std::forward<T>(t), root_name);
}
template <bool pretty = false, typename Stream, typename T,
std::enable_if_t<ylt_refletable_v<T>, int> = 0>
IGUANA_INLINE void to_xml(T &&t, Stream &s) {
constexpr std::string_view root_name =
ylt::reflection::get_struct_name<ylt::reflection::remove_cvref_t<T>>();
render_head<pretty, 0>(s, root_name);
render_xml_value<pretty, 0>(s, std::forward<T>(t), root_name);
}
template <typename T>
IGUANA_INLINE void to_xml_adl(iguana_adl_t *p, T &t, std::string &pb_str) {
to_xml(t, pb_str);
}
} // namespace iguana
+679
View File
@@ -0,0 +1,679 @@
#pragma once
#include <charconv>
#include "detail/charconv.h"
#include "detail/utf.hpp"
#include "yaml_util.hpp"
#ifdef YLT_USE_CXX26_REFLECTION
#include "ylt/reflection/reflect26_dispatch.hpp"
#endif
namespace iguana {
template <typename T, typename It,
std::enable_if_t<ylt_refletable_v<T>, int> = 0>
IGUANA_INLINE void from_yaml(T &value, It &&it, It &&end,
size_t min_spaces = 0);
namespace detail {
template <typename U, typename It,
std::enable_if_t<yaml_not_support_v<U>, int> = 0>
IGUANA_INLINE void yaml_parse_item(U &value, It &&it, It &&end,
size_t min_spaces) {
throw std::bad_function_call();
}
template <typename U, class It,
std::enable_if_t<yaml_not_support_v<U>, int> = 0>
IGUANA_INLINE void yaml_parse_value(U &value, It &&value_begin,
It &&value_end) {
throw std::bad_function_call();
}
template <typename U, typename It, std::enable_if_t<string_v<U>, int> = 0>
IGUANA_INLINE void parse_escape_str(U &value, It &&it, It &&end) {
auto start = it;
value.clear();
while (it != end) {
if (*(it++) == '\\' && it != end)
IGUANA_UNLIKELY {
if (*it == 'u') {
value.append(&*start,
static_cast<size_t>(std::distance(start, it) - 1));
++it;
if (std::distance(it, end) < 4)
IGUANA_UNLIKELY
throw std::runtime_error(R"(Expected 4 hexadecimal digits)");
auto code_point = parse_unicode_hex4(it);
encode_utf8(value, code_point);
start = it;
}
else if (*it == 'n') {
value.append(&*start,
static_cast<size_t>(std::distance(start, it) - 1));
start = ++it;
value.push_back('\n');
}
else if (*it == 't') {
value.append(&*start,
static_cast<size_t>(std::distance(start, it) - 1));
start = ++it;
value.push_back('\t');
}
else if (*it == 'r') {
value.append(&*start,
static_cast<size_t>(std::distance(start, it) - 1));
start = ++it;
value.push_back('\r');
}
else if (*it == 'b') {
value.append(&*start,
static_cast<size_t>(std::distance(start, it) - 1));
start = ++it;
value.push_back('\b');
}
else if (*it == 'f') {
value.append(&*start,
static_cast<size_t>(std::distance(start, it) - 1));
start = ++it;
value.push_back('\f');
}
}
}
value.append(&*start, static_cast<size_t>(std::distance(start, end)));
}
// use '-' here to simply represent '>-'
template <char block_type, typename U, typename It,
std::enable_if_t<string_v<U>, int> = 0>
IGUANA_INLINE void parse_block_str(U &value, It &&it, It &&end,
size_t min_spaces) {
auto spaces = skip_space_and_lines<false>(it, end, min_spaces);
if (spaces < min_spaces)
IGUANA_UNLIKELY {
// back to the end of the previous line
if constexpr (block_type == '|' || block_type == '>') {
value.push_back('\n');
}
it -= spaces + 1;
return;
}
while (it != end) {
auto start = it;
auto value_end = skip_till_newline(it, end);
value.append(&*start, static_cast<size_t>(std::distance(start, value_end)));
spaces = skip_space_and_lines<false>(it, end, min_spaces);
if ((spaces < min_spaces) || (it == end))
IGUANA_UNLIKELY {
if constexpr (block_type == '|' || block_type == '>') {
value.push_back('\n');
}
it -= spaces + 1;
return;
}
if constexpr (block_type == '|') {
value.push_back('\n');
}
else if constexpr (block_type == '-' || block_type == '>') {
value.push_back(' ');
}
}
}
template <typename U, typename It,
std::enable_if_t<map_container_v<U>, int> = 0>
IGUANA_INLINE void yaml_parse_item(U &value, It &&it, It &&end,
size_t min_spaces);
template <typename U, class It, std::enable_if_t<num_v<U>, int> = 0>
IGUANA_INLINE void yaml_parse_value(U &value, It &&value_begin,
It &&value_end) {
if (value_begin == value_end)
IGUANA_UNLIKELY { return; }
auto size = std::distance(value_begin, value_end);
const auto start = &*value_begin;
detail::from_chars(start, start + size, value);
}
template <typename U, typename It, std::enable_if_t<is_pb_type_v<U>, int> = 0>
IGUANA_INLINE void yaml_parse_value(U &value, It &&value_begin,
It &&value_end) {
yaml_parse_value(value.val, value_begin, value_end);
}
// string_view should be used for string with ' " ?
template <typename U, typename It,
std::enable_if_t<string_container_v<U>, int> = 0>
IGUANA_INLINE void yaml_parse_value(U &value, It value_begin, It value_end) {
using T = std::decay_t<U>;
auto end = value_end;
auto handle_quoted = [&](char quote_char, const char *error_msg) {
// Check minimum length: at least 2 characters (opening and closing quote)
if (std::distance(value_begin, value_end) < 2)
IGUANA_UNLIKELY { throw std::runtime_error(error_msg); }
++value_begin;
--value_end;
if (*value_end != quote_char)
IGUANA_UNLIKELY {
// Search for closing quote (may be followed by comment)
auto it = value_begin;
while (it != value_end && *it != quote_char) {
++it;
}
if (it == value_end)
IGUANA_UNLIKELY { throw std::runtime_error(error_msg); }
end = it++;
// skip spaces
if (!skip_space_till_end(it, value_end)) {
// Should be comment
if (*it != '#')
IGUANA_UNLIKELY { throw std::runtime_error(error_msg); }
}
}
else {
end = value_end;
}
};
if (*value_begin == '"') {
handle_quoted('"', R"(Expected closing ")");
if constexpr (string_v<T>) {
parse_escape_str(value, value_begin, end);
return;
}
}
else if (*value_begin == '\'') {
handle_quoted('\'', R"(Expected closing ')");
}
value =
T(&*value_begin, static_cast<size_t>(std::distance(value_begin, end)));
if ((value == "~") || (value == "null")) {
value = T{};
}
}
template <typename U, typename It, std::enable_if_t<char_v<U>, int> = 0>
IGUANA_INLINE void yaml_parse_value(U &value, It &&value_begin,
It &&value_end) {
if (static_cast<size_t>(std::distance(value_begin, value_end)) != 1)
IGUANA_UNLIKELY { throw std::runtime_error("Expected one character"); }
value = *value_begin;
}
template <typename U, typename It, std::enable_if_t<enum_v<U>, int> = 0>
IGUANA_INLINE void yaml_parse_value(U &value, It &&value_begin,
It &&value_end) {
static constexpr auto str_to_enum = get_enum_map<true, std::decay_t<U>>();
if constexpr (bool_v<decltype(str_to_enum)>) {
// not defined a specialization template
using T = std::underlying_type_t<std::decay_t<U>>;
yaml_parse_value(reinterpret_cast<T &>(value), value_begin, value_end);
}
else {
auto enum_names = std::string_view(
&*value_begin,
static_cast<size_t>(std::distance(value_begin, value_end)));
auto it = str_to_enum.find(enum_names);
if (it != str_to_enum.end())
IGUANA_LIKELY { value = it->second; }
else {
throw std::runtime_error(std::string(enum_names) +
" missing corresponding value in enum_value");
}
}
}
template <typename U, typename It, std::enable_if_t<bool_v<U>, int> = 0>
IGUANA_INLINE void yaml_parse_value(U &&value, It &&value_begin,
It &&value_end) {
auto bool_v = std::string_view(
&*value_begin,
static_cast<size_t>(std::distance(value_begin, value_end)));
if (bool_v == "true") {
value = true;
}
else if (bool_v == "false") {
value = false;
}
else
IGUANA_UNLIKELY { throw std::runtime_error("Expected true or false"); }
}
template <typename U, typename It,
std::enable_if_t<ylt_refletable_v<U>, int> = 0>
IGUANA_INLINE void yaml_parse_item(U &value, It &&it, It &&end,
size_t min_spaces) {
from_yaml(value, it, end, min_spaces);
}
template <typename U, typename It, std::enable_if_t<plain_v<U>, int> = 0>
IGUANA_INLINE void yaml_parse_item(U &value, It &&it, It &&end,
size_t min_spaces) {
using T = std::decay_t<U>;
if constexpr (string_v<T>) {
if (skip_space_till_end(it, end))
IGUANA_UNLIKELY { return; }
if (*it == '|') {
++it;
parse_block_str<'|'>(value, it, end, min_spaces);
}
else if (*it == '>') {
++it;
if (*it == '-') {
++it;
parse_block_str<'-'>(value, it, end, min_spaces);
}
else {
parse_block_str<'>'>(value, it, end, min_spaces);
}
}
else {
auto indent = skip_space_and_lines<false>(it, end, min_spaces);
// in case of a string field it is valid for the field to have no content
if (indent < min_spaces) {
value = "";
}
else {
auto start = it;
auto value_end = skip_till_newline(it, end);
yaml_parse_value(value, start, value_end);
}
}
}
else {
skip_space_and_lines(it, end, min_spaces);
auto start = it;
auto value_end = skip_till_newline(it, end);
yaml_parse_value(value, start, value_end);
}
}
template <typename U, typename It, std::enable_if_t<is_pb_type_v<U>, int> = 0>
IGUANA_INLINE void yaml_parse_item(U &value, It &&it, It &&end,
size_t min_spaces) {
yaml_parse_item(value.val, it, end, min_spaces);
}
template <typename U, typename It, std::enable_if_t<optional_v<U>, int> = 0>
IGUANA_INLINE void yaml_parse_item(U &value, It &&it, It &&end,
size_t min_spaces);
template <typename U, typename It, std::enable_if_t<smart_ptr_v<U>, int> = 0>
IGUANA_INLINE void yaml_parse_item(U &value, It &&it, It &&end,
size_t min_spaces);
// minspaces : The minimum indentation
template <typename U, typename It,
std::enable_if_t<sequence_container_v<U>, int> = 0>
IGUANA_INLINE void yaml_parse_item(U &value, It &&it, It &&end,
size_t min_spaces) {
value.clear();
auto spaces = skip_space_and_lines(it, end, min_spaces);
using value_type = typename std::remove_cv_t<U>::value_type;
if (*it == '[') {
++it;
skip_space_and_lines(it, end, min_spaces);
while (it != end) {
if (*it == ']')
IGUANA_UNLIKELY {
++it;
return;
}
if constexpr (plain_v<value_type>) {
auto start = it;
auto value_end = yaml_skip_till<',', ']'>(it, end);
yaml_parse_value(value.emplace_back(), start, value_end);
if (*(it - 1) == ']')
IGUANA_UNLIKELY { return; }
}
else {
yaml_parse_item(value.emplace_back(), it, end, spaces + 1);
skip_space_and_lines(it, end, min_spaces);
if (*it == ',')
IGUANA_LIKELY { ++it; }
}
skip_space_and_lines(it, end, min_spaces);
}
}
else if (*it == '-') {
while (it != end) {
auto subspaces = skip_space_and_lines<false>(it, end, spaces);
if (subspaces < spaces)
IGUANA_UNLIKELY {
it -= subspaces + 1;
return;
}
if (it == end)
IGUANA_UNLIKELY
return;
match<'-'>(it, end);
// space + 1 because of the skipped '-'
subspaces = skip_space_and_lines(it, end, spaces + 1);
if constexpr (string_v<value_type>) {
// except string_v because of scalar blocks
yaml_parse_item(value.emplace_back(), it, end, spaces + 1);
}
else if constexpr (plain_v<value_type>) {
auto start = it;
auto value_end = skip_till_newline(it, end);
yaml_parse_value(value.emplace_back(), start, value_end);
}
else {
yaml_parse_item(value.emplace_back(), it, end, subspaces);
}
}
}
else
IGUANA_UNLIKELY { throw std::runtime_error("Expected ']' or '-'"); }
}
template <typename U, typename It, std::enable_if_t<tuple_v<U>, int> = 0>
IGUANA_INLINE void yaml_parse_item(U &value, It &&it, It &&end,
size_t min_spaces) {
auto spaces = skip_space_and_lines(it, end, min_spaces);
if (*it == '[') {
++it;
skip_space_and_lines(it, end, min_spaces);
for_each_tuple(
[&](auto &v) IGUANA__INLINE_LAMBDA {
using value_type = std::decay_t<decltype(v)>;
skip_space_and_lines(it, end, min_spaces);
if constexpr (plain_v<value_type>) {
auto start = it;
auto value_end = yaml_skip_till<',', ']'>(it, end);
yaml_parse_value(v, start, value_end);
}
else {
yaml_parse_item(v, it, end, spaces + 1);
skip_space_and_lines(it, end, min_spaces);
++it; // skip ,
}
},
value);
}
else if (*it == '-') {
for_each_tuple(
[&](auto &v) IGUANA__INLINE_LAMBDA {
// we don't need to determine when it will end
// because the user decides how many items there are
// the following is similar to sequence_t
using value_type = std::decay_t<decltype(v)>;
skip_space_and_lines(it, end, spaces);
match<'-'>(it, end);
[[maybe_unused]] auto subspaces =
skip_space_and_lines(it, end, spaces + 1);
if constexpr (string_v<value_type>) {
yaml_parse_item(v, it, end, spaces + 1);
}
else if constexpr (plain_v<value_type>) {
auto start = it;
auto value_end = skip_till_newline(it, end);
yaml_parse_value(v, start, value_end);
}
else {
yaml_parse_item(v, it, end, subspaces);
}
},
value);
}
else {
throw std::runtime_error("Expected ']' or '-'");
}
}
template <typename U, typename It, std::enable_if_t<map_container_v<U>, int>>
IGUANA_INLINE void yaml_parse_item(U &value, It &&it, It &&end,
size_t min_spaces) {
using T = std::remove_reference_t<U>;
using key_type = typename T::key_type;
using value_type = typename T::mapped_type;
[[maybe_unused]] auto spaces = skip_space_and_lines(it, end, min_spaces);
if (*it == '{') {
++it;
[[maybe_unused]] auto subspaces = skip_space_and_lines(it, end, min_spaces);
while (it != end) {
if (*it == '}')
IGUANA_UNLIKELY {
++it;
return;
}
auto start = it;
auto value_end = yaml_skip_till<':'>(it, end);
key_type key;
yaml_parse_value(key, start, value_end);
subspaces = skip_space_and_lines(it, end, min_spaces);
if constexpr (plain_v<value_type>) {
start = it;
value_end = yaml_skip_till<',', '}'>(it, end);
yaml_parse_value(value[key], start, value_end);
if (*(it - 1) == '}')
IGUANA_UNLIKELY { return; }
}
else {
yaml_parse_item(value[key], it, end, min_spaces);
subspaces = skip_space_and_lines(it, end, min_spaces);
if (*it == ',')
IGUANA_LIKELY { ++it; }
}
subspaces = skip_space_and_lines(it, end, min_spaces);
}
}
else {
auto subspaces = skip_space_and_lines<false>(it, end, min_spaces);
while (it != end) {
if (subspaces < min_spaces)
IGUANA_UNLIKELY {
it -= subspaces + 1;
return;
}
auto start = it;
auto value_end = yaml_skip_till<':'>(it, end);
key_type key;
yaml_parse_value(key, start, value_end);
subspaces = skip_space_and_lines(it, end, min_spaces);
if constexpr (plain_v<value_type> && !string_v<value_type>) {
start = it;
value_end = skip_till_newline(it, end);
yaml_parse_value(value[key], start, value_end);
}
else {
yaml_parse_item(value[key], it, end, spaces + 1);
}
subspaces = skip_space_and_lines<false>(it, end, min_spaces);
}
}
}
template <typename U, typename It, std::enable_if_t<smart_ptr_v<U>, int>>
IGUANA_INLINE void yaml_parse_item(U &value, It &&it, It &&end,
size_t min_spaces) {
using T = std::remove_reference_t<U>;
if constexpr (unique_ptr_v<T>) {
value = std::make_unique<typename T::element_type>();
}
else {
value = std::make_shared<typename T::element_type>();
}
static_assert(!string_v<typename T::element_type>,
"smart_ptr<string> is not allowed");
yaml_parse_item(*value, it, end, min_spaces);
}
template <typename U, typename It, std::enable_if_t<optional_v<U>, int>>
IGUANA_INLINE void yaml_parse_item(U &value, It &&it, It &&end,
size_t min_spaces) {
using T = std::remove_reference_t<U>;
using value_type = typename T::value_type;
auto spaces = skip_space_and_lines<false>(it, end, min_spaces);
if (it == end)
IGUANA_UNLIKELY { return; }
if (spaces < min_spaces)
IGUANA_UNLIKELY {
it -= spaces + 1;
return;
}
auto start = it;
std::decay_t<It> value_end = end;
if (*it == 'n') {
value_end = skip_till_newline(it, end);
auto opt_v = std::string_view(
&*start, static_cast<size_t>(std::distance(start, value_end)));
if (opt_v == "null") {
return;
}
}
if constexpr (string_v<value_type> || !plain_v<value_type>) {
it = start;
yaml_parse_item(value.emplace(), it, end, min_spaces);
}
else {
if (value_end == end)
IGUANA_LIKELY {
// if value_end isn't touched
value_end = skip_till_newline(it, end);
}
yaml_parse_value(value.emplace(), start, value_end);
}
}
template <typename It>
IGUANA_INLINE void skip_object_value(It &&it, It &&end, size_t min_spaces) {
size_t subspace = min_spaces;
while (it != end) {
while (it != end && *it != '\n') {
++it;
}
// here : it == '\n'
subspace = 0;
if (it != end)
IGUANA_LIKELY { ++it; }
while (it != end && *(it++) == ' ') {
++subspace;
}
if (subspace < min_spaces) {
it -= subspace + 1;
return;
}
}
}
} // namespace detail
#ifdef YLT_USE_CXX26_REFLECTION
template <typename T, typename It, std::enable_if_t<ylt_refletable_v<T>, int>>
IGUANA_INLINE void from_yaml(T &value, It &&it, It &&end, size_t min_spaces) {
auto spaces = skip_space_and_lines(it, end, min_spaces);
while (it != end) {
auto start = it;
auto keyend = yaml_skip_till<':'>(it, end);
std::string_view key = std::string_view{
&*start, static_cast<size_t>(std::distance(start, keyend))};
bool found = ylt::reflection::reflect26::dispatch_by_name(
value, key, [&](auto &field) IGUANA__INLINE_LAMBDA {
detail::yaml_parse_item(field, it, end, spaces + 1);
});
if (!found)
IGUANA_UNLIKELY {
#ifdef THROW_UNKNOWN_KEY
throw std::runtime_error("Unknown key: " + std::string(key));
#else
detail::skip_object_value(it, end, spaces + 1);
#endif
}
auto subspaces = skip_space_and_lines<false>(it, end, min_spaces);
if (subspaces < min_spaces)
IGUANA_UNLIKELY {
it -= subspaces + 1;
return;
}
}
}
#else
template <typename T, typename It, std::enable_if_t<ylt_refletable_v<T>, int>>
IGUANA_INLINE void from_yaml(T &value, It &&it, It &&end, size_t min_spaces) {
auto spaces = skip_space_and_lines(it, end, min_spaces);
while (it != end) {
auto start = it;
auto keyend = yaml_skip_till<':'>(it, end);
std::string_view key = std::string_view{
&*start, static_cast<size_t>(std::distance(start, keyend))};
static auto frozen_map = ylt::reflection::get_variant_map<T>();
if constexpr (frozen_map.size() > 0) {
const auto &member_it = frozen_map.find(key);
if (member_it != frozen_map.end())
IGUANA_LIKELY {
std::visit(
[&](auto offset) IGUANA__INLINE_LAMBDA {
using value_type = typename decltype(offset)::type;
auto member_ptr =
(value_type *)((char *)(&value) + offset.value);
detail::yaml_parse_item(*member_ptr, it, end, spaces + 1);
},
member_it->second);
}
else
IGUANA_UNLIKELY {
#ifdef THROW_UNKNOWN_KEY
throw std::runtime_error("Unknown key: " + std::string(key));
#else
detail::skip_object_value(it, end, spaces + 1);
#endif
}
}
auto subspaces = skip_space_and_lines<false>(it, end, min_spaces);
if (subspaces < min_spaces)
IGUANA_UNLIKELY {
it -= subspaces + 1;
return;
}
}
}
#endif // !YLT_USE_CXX26_REFLECTION
template <typename T, typename It,
std::enable_if_t<non_ylt_refletable_v<T>, int> = 0>
IGUANA_INLINE void from_yaml(T &value, It &&it, It &&end) {
detail::yaml_parse_item(value, it, end, 0);
}
template <typename T, typename It>
IGUANA_INLINE void from_yaml(T &value, It &&it, It &&end,
std::error_code &ec) noexcept {
try {
from_yaml(value, it, end);
ec = {};
} catch (std::runtime_error &e) {
ec = iguana::make_error_code(e.what());
}
}
template <typename T, typename View,
std::enable_if_t<string_container_v<View>, int> = 0>
IGUANA_INLINE void from_yaml(T &value, const View &view) {
from_yaml(value, std::begin(view), std::end(view));
}
template <typename T, typename View,
std::enable_if_t<string_container_v<View>, int> = 0>
IGUANA_INLINE void from_yaml(T &value, const View &view,
std::error_code &ec) noexcept {
try {
from_yaml(value, std::begin(view), std::end(view));
ec = {};
} catch (std::runtime_error &e) {
ec = iguana::make_error_code(e.what());
}
}
template <typename T>
IGUANA_INLINE void from_yaml_adl(iguana_adl_t *p, T &t,
std::string_view pb_str) {
iguana::from_yaml(t, pb_str);
}
} // namespace iguana
+132
View File
@@ -0,0 +1,132 @@
#pragma once
#include "common.hpp"
#include "detail/pb_type.hpp"
#include "util.hpp"
namespace iguana {
template <typename T>
constexpr inline bool yaml_not_support_v = variant_v<T>;
// skip non-linebreak whitespaces return true when it==end
template <typename It>
IGUANA_INLINE bool skip_space_till_end(It &&it, It &&end) {
while (it != end && (*it == ' ' || *it == '\t')) ++it;
return it == end;
}
// will not skip '\n'
template <typename It>
IGUANA_INLINE auto skip_till_newline(It &&it, It &&end) {
if (it == end)
IGUANA_UNLIKELY { return it; }
std::decay_t<decltype(it)> res = it;
while ((it != end) && (*it != '\n')) {
if (*it == ' ')
IGUANA_UNLIKELY {
res = it;
while (it != end && *it == ' ') ++it;
}
else if (*it == '#')
IGUANA_UNLIKELY {
if (*(it - 1) == ' ') {
// it - 1 should be legal because this func is for parse value
while ((it != end) && *it != '\n') {
++it;
}
return res;
}
else {
++it;
}
}
else
IGUANA_LIKELY { ++it; }
}
return (*(it - 1) == ' ') ? res : it;
}
template <char... C, typename It>
IGUANA_INLINE auto yaml_skip_till(It &&it, It &&end) {
if (it == end)
IGUANA_UNLIKELY { return it; }
std::decay_t<decltype(it)> start = it;
std::decay_t<decltype(it)> res = it;
while ((it != end) && (!((... || (*it == C))))) {
if (*it == '\n')
IGUANA_UNLIKELY { throw std::runtime_error("\\n is not expected"); }
else if (*it == ' ' || *it == '\t')
IGUANA_UNLIKELY {
res = it;
while (it != end && (*it == ' ' || *it == '\t')) ++it;
continue;
}
else if (*it == '#')
IGUANA_UNLIKELY {
// Check if it - 1 is valid before accessing
if (it > start && (*(it - 1) == ' ' || *(it - 1) == '\t'))
IGUANA_UNLIKELY {
while ((it != end) && *it != '\n') {
++it;
}
return res;
}
}
res = ++it;
}
if (it == end)
IGUANA_UNLIKELY {
static constexpr char b[] = {C..., '\0'};
std::string error = std::string("Expected one of these: ").append(b);
throw std::runtime_error(error);
}
++it; // skip
return res;
}
// If there are '\n' , return indentation
// If not, return minspaces + space
// If Throw == true, check res < minspaces
template <bool Throw = true, typename It>
IGUANA_INLINE size_t skip_space_and_lines(It &&it, It &&end, size_t minspaces) {
size_t res = minspaces;
while (it != end) {
if (*it == '\n') {
++it;
res = 0;
auto start = it;
// skip the --- line
if ((it != end) && (*it == '-'))
IGUANA_UNLIKELY {
auto line_end = yaml_skip_till<false, '\n'>(it, end);
auto line = std::string_view(
&*start, static_cast<size_t>(std::distance(start, line_end)));
if (line != "---") {
it = start;
}
}
}
else if (*it == ' ' || *it == '\t') {
++it;
++res;
}
else if (*it == '#') {
while (it != end && *it != '\n') {
++it;
}
res = 0;
}
else {
if constexpr (Throw) {
if (res < minspaces)
IGUANA_UNLIKELY { throw std::runtime_error("Indentation problem"); }
}
return res;
}
}
return res; // throw in certain situations ?
}
} // namespace iguana
+213
View File
@@ -0,0 +1,213 @@
#pragma once
#include "detail/charconv.h"
#include "yaml_util.hpp"
namespace iguana {
template <bool Is_writing_escape = false, typename Stream, typename T,
std::enable_if_t<yaml_not_support_v<T>, int> = 0>
IGUANA_INLINE void render_yaml_value(Stream &ss, T &&t, size_t min_spaces = 0) {
throw std::bad_function_call();
}
template <bool Is_writing_escape = false, typename Stream, typename T,
std::enable_if_t<ylt_refletable_v<T>, int> = 0>
IGUANA_INLINE void to_yaml(T &&t, Stream &s, size_t min_spaces = 0);
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<ylt_refletable_v<T>, int> = 0>
IGUANA_INLINE void render_yaml_value(Stream &ss, T &&t, size_t min_spaces) {
ss.push_back('\n');
to_yaml<Is_writing_escape>(std::forward<T>(t), ss, min_spaces);
}
template <bool Is_writing_escape, bool appendLf = true, typename Stream,
typename T, std::enable_if_t<string_container_v<T>, int> = 0>
IGUANA_INLINE void render_yaml_value(Stream &ss, T &&t, size_t min_spaces) {
if constexpr (Is_writing_escape) {
ss.push_back('"');
write_string_with_escape(t.data(), t.size(), ss);
ss.push_back('"');
}
else if (t.size() == 0) {
ss.push_back('~');
}
else {
ss.append(t.data(), t.size());
}
if constexpr (appendLf)
ss.push_back('\n');
}
template <bool Is_writing_escape, bool appendLf = true, typename Stream,
typename T, std::enable_if_t<num_v<T>, int> = 0>
IGUANA_INLINE void render_yaml_value(Stream &ss, T value, size_t min_spaces) {
char temp[65];
auto p = detail::to_chars(temp, value);
ss.append(temp, p - temp);
if constexpr (appendLf)
ss.push_back('\n');
}
template <bool Is_writing_escape, bool appendLf = true, typename Stream,
typename T, std::enable_if_t<is_pb_type_v<T>, int> = 0>
IGUANA_INLINE void render_yaml_value(Stream &ss, T value, size_t min_spaces) {
render_yaml_value<Is_writing_escape>(ss, value.val, min_spaces);
}
template <bool Is_writing_escape, bool appendLf = true, typename Stream>
IGUANA_INLINE void render_yaml_value(Stream &ss, char value,
size_t min_spaces) {
ss.push_back(value);
if constexpr (appendLf)
ss.push_back('\n');
}
template <bool Is_writing_escape, bool appendLf = true, typename Stream>
IGUANA_INLINE void render_yaml_value(Stream &ss, bool value,
size_t min_spaces) {
ss.append(value ? "true" : "false");
if constexpr (appendLf)
ss.push_back('\n');
}
template <bool Is_writing_escape, bool appendLf = true, typename Stream,
typename T, std::enable_if_t<enum_v<T>, int> = 0>
IGUANA_INLINE void render_yaml_value(Stream &ss, T value, size_t min_spaces) {
static constexpr auto enum_to_str = get_enum_map<false, std::decay_t<T>>();
if constexpr (bool_v<decltype(enum_to_str)>) {
render_yaml_value<Is_writing_escape>(
ss, static_cast<std::underlying_type_t<T>>(value), min_spaces);
}
else {
auto it = enum_to_str.find(value);
if (it != enum_to_str.end())
IGUANA_LIKELY {
auto str = it->second;
render_yaml_value<Is_writing_escape>(
ss, std::string_view(str.data(), str.size()), min_spaces);
}
else {
throw std::runtime_error(
std::to_string(static_cast<std::underlying_type_t<T>>(value)) +
" is a missing value in enum_value");
}
}
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<optional_v<T>, int> = 0>
IGUANA_INLINE void render_yaml_value(Stream &ss, const T &val,
size_t min_spaces);
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<smart_ptr_v<T>, int> = 0>
IGUANA_INLINE void render_yaml_value(Stream &ss, const T &val,
size_t min_spaces);
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<sequence_container_v<T>, int> = 0>
IGUANA_INLINE void render_yaml_value(Stream &ss, const T &t,
size_t min_spaces) {
if (t.empty()) {
ss.append("[]");
ss.push_back('\n');
}
else {
ss.push_back('\n');
for (const auto &v : t) {
ss.append(min_spaces, ' ');
ss.append("- ");
render_yaml_value<Is_writing_escape>(ss, v, min_spaces + 1);
}
}
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<tuple_v<T>, int> = 0>
IGUANA_INLINE void render_yaml_value(Stream &ss, T &&t, size_t min_spaces) {
ss.push_back('\n');
for_each_tuple(
[&ss, min_spaces](auto &v) IGUANA__INLINE_LAMBDA {
ss.append(min_spaces, ' ');
ss.append("- ");
render_yaml_value<Is_writing_escape>(ss, v, min_spaces + 1);
},
std::forward<T>(t));
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<map_container_v<T>, int> = 0>
IGUANA_INLINE void render_yaml_value(Stream &ss, const T &t,
size_t min_spaces) {
ss.push_back('\n');
for (const auto &[k, v] : t) {
ss.append(min_spaces, ' ');
render_yaml_value<false, false>(ss, k, 0); // key must be plaint type
ss.append(": ");
render_yaml_value<Is_writing_escape>(ss, v, min_spaces + 1);
}
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<optional_v<T>, int>>
IGUANA_INLINE void render_yaml_value(Stream &ss, const T &val,
size_t min_spaces) {
if (!val) {
ss.append("null");
ss.push_back('\n');
}
else {
render_yaml_value<Is_writing_escape>(ss, *val, min_spaces);
}
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<smart_ptr_v<T>, int>>
IGUANA_INLINE void render_yaml_value(Stream &ss, const T &val,
size_t min_spaces) {
if (!val) {
ss.push_back('\n');
}
else {
render_yaml_value<Is_writing_escape>(ss, *val, min_spaces);
}
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<ylt_refletable_v<T>, int>>
IGUANA_INLINE void to_yaml(T &&t, Stream &s, size_t min_spaces) {
ylt::reflection::for_each(t, [&](auto &field, auto field_name, auto index) {
using field_type = std::remove_reference_t<decltype(field)>;
static_assert(!std::is_pointer_v<field_type>,
"don't use raw pointer, please use smart pointer");
s.append(min_spaces, ' ');
s.append(field_name);
s.append(": ");
if constexpr (!ylt_refletable_v<field_type>) {
render_yaml_value<Is_writing_escape>(s, field, min_spaces + 1);
}
else {
s.push_back('\n');
to_yaml<Is_writing_escape>(field, s, min_spaces + 1);
}
});
}
template <bool Is_writing_escape = false, typename Stream, typename T,
std::enable_if_t<non_ylt_refletable_v<T>, int> = 0>
IGUANA_INLINE void to_yaml(T &&t, Stream &s) {
if constexpr (tuple_v<T> || map_container_v<T> || sequence_container_v<T> ||
optional_v<T> || smart_ptr_v<T>)
render_yaml_value<Is_writing_escape>(s, std::forward<T>(t), 0);
else
static_assert(!sizeof(T), "don't suppport this type");
}
template <typename T>
IGUANA_INLINE void to_yaml_adl(iguana_adl_t *p, const T &t,
std::string &pb_str) {
to_yaml(t, pb_str);
}
} // namespace iguana
@@ -0,0 +1,13 @@
#pragma once
#include "common_macro.hpp"
#define YLT_MACRO_EXPAND(...) __VA_ARGS__
#define WRAP_ARGS0(w, o)
#define WRAP_ARGS1(w, o, _1) w(o, _1)
#include "generate/arg_list_macro_gen.hpp"
#define WRAP_ARGS_(w, object, ...) \
YLT_CONCAT(WRAP_ARGS, YLT_ARG_COUNT(__VA_ARGS__)) \
(w, object, ##__VA_ARGS__)
#define WRAP_ARGS(w, object, ...) WRAP_ARGS_(w, object, ##__VA_ARGS__)
@@ -0,0 +1,15 @@
#pragma once
#define YLT_CONCAT_(l, r) l##r
#define YLT_CONCAT(l, r) YLT_CONCAT_(l, r)
#define CONCAT_MEMBER(t, x) t.x
#define CONCAT_ADDR(T, x) &T::x
#define CONCAT_NAME(t, x) #x
namespace ylt::reflection {
template <typename T>
struct identity {};
} // namespace ylt::reflection
@@ -0,0 +1,342 @@
// clang-format off
/*The following boilerplate code was generated using a Python script:
max_fields = 124
macro = "#define WRAP_ARGS"
with open("generated_args.txt", "w", encoding="utf-8") as codefile:
codefile.write("#ifdef _MSC_VER // Microsoft compilers\n\n")
codefile.write("#define __NARGS(")
codefile.write(
",".join(['_{}'.format(num) for num in range(1, max_fields+2)])
)
codefile.write(", N, ...) N\n")
codefile.write("#define NARGS_1(...) YLT_MACRO_EXPAND(__NARGS(__VA_ARGS__,")
codefile.write(
",".join(['{}'.format(num) for num in range(max_fields,-1,-1)])
)
codefile.write("))\n")
codefile.write("#define AUGMENTER(...) unused, __VA_ARGS__\n")
codefile.write("#define YLT_ARG_COUNT(...) NARGS_1(AUGMENTER(__VA_ARGS__))\n\n")
codefile.write("#else // Others\n\n")
codefile.write("#define YLT_ARG_COUNT(...) __NARGS(0, ##__VA_ARGS__,")
codefile.write(
",".join(['{}'.format(num) for num in range(max_fields,-1,-1)])
)
codefile.write(")\n")
codefile.write("#define __NARGS(")
codefile.write(
",".join(['_{}'.format(num) for num in range(0,max_fields+1)])
)
codefile.write(", N, ...) N\n")
codefile.write("#endif\n\n")
codefile.write(
"\n".join(
[
f"{macro}{i}(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS{i-1}(w,o,__VA_ARGS__))" for i in range(2, 126)
]
)
)
with open("generated_args.txt", "w", encoding="utf-8") as codefile:
codefile.write("#ifdef _MSC_VER // Microsoft compilers\n\n")
codefile.write("#define __NARGS(\n")
codefile.write(
",".join(['_{}'.format(num) for num in range(1, max_fields+2)])
)
codefile.write(", N, ...) N\n")
codefile.write("#define NARGS_1(...) YLT_MACRO_EXPAND(__NARGS(__VA_ARGS__,")
codefile.write(
",".join(['{}'.format(num) for num in range(max_fields,-1,-1)])
)
codefile.write("))\n")
codefile.write("#define AUGMENTER(...) unused, __VA_ARGS__\n")
codefile.write("#define YLT_ARG_COUNT(...) NARGS_1(AUGMENTER(__VA_ARGS__))\n\n")
codefile.write("#else // Others\n\n")
codefile.write("#define YLT_ARG_COUNT(...) __NARGS(0, ##__VA_ARGS__,")
codefile.write(
",".join(['{}'.format(num) for num in range(max_fields,-1,-1)])
)
codefile.write(")\n")
codefile.write("#define __NARGS(")
codefile.write(
",".join(['_{}'.format(num) for num in range(0,max_fields+1)])
)
codefile.write(", N, ...) N\n")
codefile.write("#endif\n\n")
codefile.write(
"\n".join(
[
f"{macro}{i}(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS{i-1}(w,o,__VA_ARGS__))" for i in range(2, 126)
]
)
)
*/
#ifdef _MSC_VER // Microsoft compilers
#define __NARGS(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,_41,_42,_43,_44,_45,_46,_47,_48,_49,_50,_51,_52,_53,_54,_55,_56,_57,_58,_59,_60,_61,_62,_63,_64,_65,_66,_67,_68,_69,_70,_71,_72,_73,_74,_75,_76,_77,_78,_79,_80,_81,_82,_83,_84,_85,_86,_87,_88,_89,_90,_91,_92,_93,_94,_95,_96,_97,_98,_99,_100,_101,_102,_103,_104,_105,_106,_107,_108,_109,_110,_111,_112,_113,_114,_115,_116,_117,_118,_119,_120,_121,_122,_123,_124,_125, N, ...) N
#define NARGS_1(...) YLT_MACRO_EXPAND(__NARGS(__VA_ARGS__,124,123,122,121,120,119,118,117,116,115,114,113,112,111,110,109,108,107,106,105,104,103,102,101,100,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0))
#define AUGMENTER(...) unused, __VA_ARGS__
#define YLT_ARG_COUNT(...) NARGS_1(AUGMENTER(__VA_ARGS__))
#else // Others
#define YLT_ARG_COUNT(...) __NARGS(0, ##__VA_ARGS__,256,255,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,239,238,237,236,235,234,233,232,231,230,229,228,227,226,225,224,223,222,221,220,219,218,217,216,215,214,213,212,211,210,209,208,207,206,205,204,203,202,201,200,199,198,197,196,195,194,193,192,191,190,189,188,187,186,185,184,183,182,181,180,179,178,177,176,175,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,159,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128,127,126,125,124,123,122,121,120,119,118,117,116,115,114,113,112,111,110,109,108,107,106,105,104,103,102,101,100,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)
#define __NARGS(_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,_41,_42,_43,_44,_45,_46,_47,_48,_49,_50,_51,_52,_53,_54,_55,_56,_57,_58,_59,_60,_61,_62,_63,_64,_65,_66,_67,_68,_69,_70,_71,_72,_73,_74,_75,_76,_77,_78,_79,_80,_81,_82,_83,_84,_85,_86,_87,_88,_89,_90,_91,_92,_93,_94,_95,_96,_97,_98,_99,_100,_101,_102,_103,_104,_105,_106,_107,_108,_109,_110,_111,_112,_113,_114,_115,_116,_117,_118,_119,_120,_121,_122,_123,_124,_125,_126,_127,_128,_129,_130,_131,_132,_133,_134,_135,_136,_137,_138,_139,_140,_141,_142,_143,_144,_145,_146,_147,_148,_149,_150,_151,_152,_153,_154,_155,_156,_157,_158,_159,_160,_161,_162,_163,_164,_165,_166,_167,_168,_169,_170,_171,_172,_173,_174,_175,_176,_177,_178,_179,_180,_181,_182,_183,_184,_185,_186,_187,_188,_189,_190,_191,_192,_193,_194,_195,_196,_197,_198,_199,_200,_201,_202,_203,_204,_205,_206,_207,_208,_209,_210,_211,_212,_213,_214,_215,_216,_217,_218,_219,_220,_221,_222,_223,_224,_225,_226,_227,_228,_229,_230,_231,_232,_233,_234,_235,_236,_237,_238,_239,_240,_241,_242,_243,_244,_245,_246,_247,_248,_249,_250,_251,_252,_253,_254,_255,_256, N, ...) N
#endif
#define WRAP_ARGS2(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS1(w,o,__VA_ARGS__))
#define WRAP_ARGS3(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS2(w,o,__VA_ARGS__))
#define WRAP_ARGS4(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS3(w,o,__VA_ARGS__))
#define WRAP_ARGS5(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS4(w,o,__VA_ARGS__))
#define WRAP_ARGS6(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS5(w,o,__VA_ARGS__))
#define WRAP_ARGS7(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS6(w,o,__VA_ARGS__))
#define WRAP_ARGS8(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS7(w,o,__VA_ARGS__))
#define WRAP_ARGS9(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS8(w,o,__VA_ARGS__))
#define WRAP_ARGS10(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS9(w,o,__VA_ARGS__))
#define WRAP_ARGS11(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS10(w,o,__VA_ARGS__))
#define WRAP_ARGS12(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS11(w,o,__VA_ARGS__))
#define WRAP_ARGS13(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS12(w,o,__VA_ARGS__))
#define WRAP_ARGS14(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS13(w,o,__VA_ARGS__))
#define WRAP_ARGS15(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS14(w,o,__VA_ARGS__))
#define WRAP_ARGS16(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS15(w,o,__VA_ARGS__))
#define WRAP_ARGS17(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS16(w,o,__VA_ARGS__))
#define WRAP_ARGS18(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS17(w,o,__VA_ARGS__))
#define WRAP_ARGS19(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS18(w,o,__VA_ARGS__))
#define WRAP_ARGS20(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS19(w,o,__VA_ARGS__))
#define WRAP_ARGS21(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS20(w,o,__VA_ARGS__))
#define WRAP_ARGS22(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS21(w,o,__VA_ARGS__))
#define WRAP_ARGS23(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS22(w,o,__VA_ARGS__))
#define WRAP_ARGS24(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS23(w,o,__VA_ARGS__))
#define WRAP_ARGS25(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS24(w,o,__VA_ARGS__))
#define WRAP_ARGS26(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS25(w,o,__VA_ARGS__))
#define WRAP_ARGS27(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS26(w,o,__VA_ARGS__))
#define WRAP_ARGS28(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS27(w,o,__VA_ARGS__))
#define WRAP_ARGS29(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS28(w,o,__VA_ARGS__))
#define WRAP_ARGS30(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS29(w,o,__VA_ARGS__))
#define WRAP_ARGS31(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS30(w,o,__VA_ARGS__))
#define WRAP_ARGS32(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS31(w,o,__VA_ARGS__))
#define WRAP_ARGS33(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS32(w,o,__VA_ARGS__))
#define WRAP_ARGS34(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS33(w,o,__VA_ARGS__))
#define WRAP_ARGS35(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS34(w,o,__VA_ARGS__))
#define WRAP_ARGS36(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS35(w,o,__VA_ARGS__))
#define WRAP_ARGS37(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS36(w,o,__VA_ARGS__))
#define WRAP_ARGS38(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS37(w,o,__VA_ARGS__))
#define WRAP_ARGS39(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS38(w,o,__VA_ARGS__))
#define WRAP_ARGS40(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS39(w,o,__VA_ARGS__))
#define WRAP_ARGS41(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS40(w,o,__VA_ARGS__))
#define WRAP_ARGS42(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS41(w,o,__VA_ARGS__))
#define WRAP_ARGS43(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS42(w,o,__VA_ARGS__))
#define WRAP_ARGS44(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS43(w,o,__VA_ARGS__))
#define WRAP_ARGS45(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS44(w,o,__VA_ARGS__))
#define WRAP_ARGS46(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS45(w,o,__VA_ARGS__))
#define WRAP_ARGS47(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS46(w,o,__VA_ARGS__))
#define WRAP_ARGS48(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS47(w,o,__VA_ARGS__))
#define WRAP_ARGS49(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS48(w,o,__VA_ARGS__))
#define WRAP_ARGS50(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS49(w,o,__VA_ARGS__))
#define WRAP_ARGS51(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS50(w,o,__VA_ARGS__))
#define WRAP_ARGS52(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS51(w,o,__VA_ARGS__))
#define WRAP_ARGS53(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS52(w,o,__VA_ARGS__))
#define WRAP_ARGS54(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS53(w,o,__VA_ARGS__))
#define WRAP_ARGS55(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS54(w,o,__VA_ARGS__))
#define WRAP_ARGS56(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS55(w,o,__VA_ARGS__))
#define WRAP_ARGS57(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS56(w,o,__VA_ARGS__))
#define WRAP_ARGS58(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS57(w,o,__VA_ARGS__))
#define WRAP_ARGS59(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS58(w,o,__VA_ARGS__))
#define WRAP_ARGS60(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS59(w,o,__VA_ARGS__))
#define WRAP_ARGS61(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS60(w,o,__VA_ARGS__))
#define WRAP_ARGS62(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS61(w,o,__VA_ARGS__))
#define WRAP_ARGS63(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS62(w,o,__VA_ARGS__))
#define WRAP_ARGS64(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS63(w,o,__VA_ARGS__))
#define WRAP_ARGS65(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS64(w,o,__VA_ARGS__))
#define WRAP_ARGS66(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS65(w,o,__VA_ARGS__))
#define WRAP_ARGS67(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS66(w,o,__VA_ARGS__))
#define WRAP_ARGS68(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS67(w,o,__VA_ARGS__))
#define WRAP_ARGS69(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS68(w,o,__VA_ARGS__))
#define WRAP_ARGS70(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS69(w,o,__VA_ARGS__))
#define WRAP_ARGS71(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS70(w,o,__VA_ARGS__))
#define WRAP_ARGS72(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS71(w,o,__VA_ARGS__))
#define WRAP_ARGS73(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS72(w,o,__VA_ARGS__))
#define WRAP_ARGS74(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS73(w,o,__VA_ARGS__))
#define WRAP_ARGS75(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS74(w,o,__VA_ARGS__))
#define WRAP_ARGS76(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS75(w,o,__VA_ARGS__))
#define WRAP_ARGS77(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS76(w,o,__VA_ARGS__))
#define WRAP_ARGS78(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS77(w,o,__VA_ARGS__))
#define WRAP_ARGS79(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS78(w,o,__VA_ARGS__))
#define WRAP_ARGS80(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS79(w,o,__VA_ARGS__))
#define WRAP_ARGS81(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS80(w,o,__VA_ARGS__))
#define WRAP_ARGS82(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS81(w,o,__VA_ARGS__))
#define WRAP_ARGS83(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS82(w,o,__VA_ARGS__))
#define WRAP_ARGS84(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS83(w,o,__VA_ARGS__))
#define WRAP_ARGS85(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS84(w,o,__VA_ARGS__))
#define WRAP_ARGS86(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS85(w,o,__VA_ARGS__))
#define WRAP_ARGS87(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS86(w,o,__VA_ARGS__))
#define WRAP_ARGS88(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS87(w,o,__VA_ARGS__))
#define WRAP_ARGS89(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS88(w,o,__VA_ARGS__))
#define WRAP_ARGS90(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS89(w,o,__VA_ARGS__))
#define WRAP_ARGS91(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS90(w,o,__VA_ARGS__))
#define WRAP_ARGS92(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS91(w,o,__VA_ARGS__))
#define WRAP_ARGS93(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS92(w,o,__VA_ARGS__))
#define WRAP_ARGS94(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS93(w,o,__VA_ARGS__))
#define WRAP_ARGS95(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS94(w,o,__VA_ARGS__))
#define WRAP_ARGS96(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS95(w,o,__VA_ARGS__))
#define WRAP_ARGS97(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS96(w,o,__VA_ARGS__))
#define WRAP_ARGS98(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS97(w,o,__VA_ARGS__))
#define WRAP_ARGS99(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS98(w,o,__VA_ARGS__))
#define WRAP_ARGS100(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS99(w,o,__VA_ARGS__))
#define WRAP_ARGS101(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS100(w,o,__VA_ARGS__))
#define WRAP_ARGS102(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS101(w,o,__VA_ARGS__))
#define WRAP_ARGS103(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS102(w,o,__VA_ARGS__))
#define WRAP_ARGS104(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS103(w,o,__VA_ARGS__))
#define WRAP_ARGS105(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS104(w,o,__VA_ARGS__))
#define WRAP_ARGS106(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS105(w,o,__VA_ARGS__))
#define WRAP_ARGS107(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS106(w,o,__VA_ARGS__))
#define WRAP_ARGS108(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS107(w,o,__VA_ARGS__))
#define WRAP_ARGS109(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS108(w,o,__VA_ARGS__))
#define WRAP_ARGS110(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS109(w,o,__VA_ARGS__))
#define WRAP_ARGS111(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS110(w,o,__VA_ARGS__))
#define WRAP_ARGS112(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS111(w,o,__VA_ARGS__))
#define WRAP_ARGS113(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS112(w,o,__VA_ARGS__))
#define WRAP_ARGS114(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS113(w,o,__VA_ARGS__))
#define WRAP_ARGS115(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS114(w,o,__VA_ARGS__))
#define WRAP_ARGS116(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS115(w,o,__VA_ARGS__))
#define WRAP_ARGS117(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS116(w,o,__VA_ARGS__))
#define WRAP_ARGS118(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS117(w,o,__VA_ARGS__))
#define WRAP_ARGS119(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS118(w,o,__VA_ARGS__))
#define WRAP_ARGS120(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS119(w,o,__VA_ARGS__))
#define WRAP_ARGS121(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS120(w,o,__VA_ARGS__))
#define WRAP_ARGS122(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS121(w,o,__VA_ARGS__))
#define WRAP_ARGS123(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS122(w,o,__VA_ARGS__))
#define WRAP_ARGS124(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS123(w,o,__VA_ARGS__))
#define WRAP_ARGS125(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS124(w,o,__VA_ARGS__))
#define WRAP_ARGS126(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS125(w,o,__VA_ARGS__))
#define WRAP_ARGS127(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS126(w,o,__VA_ARGS__))
#define WRAP_ARGS128(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS127(w,o,__VA_ARGS__))
#define WRAP_ARGS129(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS128(w,o,__VA_ARGS__))
#define WRAP_ARGS130(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS129(w,o,__VA_ARGS__))
#define WRAP_ARGS131(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS130(w,o,__VA_ARGS__))
#define WRAP_ARGS132(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS131(w,o,__VA_ARGS__))
#define WRAP_ARGS133(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS132(w,o,__VA_ARGS__))
#define WRAP_ARGS134(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS133(w,o,__VA_ARGS__))
#define WRAP_ARGS135(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS134(w,o,__VA_ARGS__))
#define WRAP_ARGS136(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS135(w,o,__VA_ARGS__))
#define WRAP_ARGS137(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS136(w,o,__VA_ARGS__))
#define WRAP_ARGS138(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS137(w,o,__VA_ARGS__))
#define WRAP_ARGS139(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS138(w,o,__VA_ARGS__))
#define WRAP_ARGS140(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS139(w,o,__VA_ARGS__))
#define WRAP_ARGS141(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS140(w,o,__VA_ARGS__))
#define WRAP_ARGS142(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS141(w,o,__VA_ARGS__))
#define WRAP_ARGS143(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS142(w,o,__VA_ARGS__))
#define WRAP_ARGS144(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS143(w,o,__VA_ARGS__))
#define WRAP_ARGS145(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS144(w,o,__VA_ARGS__))
#define WRAP_ARGS146(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS145(w,o,__VA_ARGS__))
#define WRAP_ARGS147(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS146(w,o,__VA_ARGS__))
#define WRAP_ARGS148(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS147(w,o,__VA_ARGS__))
#define WRAP_ARGS149(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS148(w,o,__VA_ARGS__))
#define WRAP_ARGS150(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS149(w,o,__VA_ARGS__))
#define WRAP_ARGS151(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS150(w,o,__VA_ARGS__))
#define WRAP_ARGS152(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS151(w,o,__VA_ARGS__))
#define WRAP_ARGS153(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS152(w,o,__VA_ARGS__))
#define WRAP_ARGS154(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS153(w,o,__VA_ARGS__))
#define WRAP_ARGS155(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS154(w,o,__VA_ARGS__))
#define WRAP_ARGS156(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS155(w,o,__VA_ARGS__))
#define WRAP_ARGS157(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS156(w,o,__VA_ARGS__))
#define WRAP_ARGS158(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS157(w,o,__VA_ARGS__))
#define WRAP_ARGS159(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS158(w,o,__VA_ARGS__))
#define WRAP_ARGS160(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS159(w,o,__VA_ARGS__))
#define WRAP_ARGS161(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS160(w,o,__VA_ARGS__))
#define WRAP_ARGS162(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS161(w,o,__VA_ARGS__))
#define WRAP_ARGS163(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS162(w,o,__VA_ARGS__))
#define WRAP_ARGS164(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS163(w,o,__VA_ARGS__))
#define WRAP_ARGS165(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS164(w,o,__VA_ARGS__))
#define WRAP_ARGS166(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS165(w,o,__VA_ARGS__))
#define WRAP_ARGS167(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS166(w,o,__VA_ARGS__))
#define WRAP_ARGS168(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS167(w,o,__VA_ARGS__))
#define WRAP_ARGS169(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS168(w,o,__VA_ARGS__))
#define WRAP_ARGS170(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS169(w,o,__VA_ARGS__))
#define WRAP_ARGS171(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS170(w,o,__VA_ARGS__))
#define WRAP_ARGS172(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS171(w,o,__VA_ARGS__))
#define WRAP_ARGS173(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS172(w,o,__VA_ARGS__))
#define WRAP_ARGS174(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS173(w,o,__VA_ARGS__))
#define WRAP_ARGS175(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS174(w,o,__VA_ARGS__))
#define WRAP_ARGS176(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS175(w,o,__VA_ARGS__))
#define WRAP_ARGS177(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS176(w,o,__VA_ARGS__))
#define WRAP_ARGS178(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS177(w,o,__VA_ARGS__))
#define WRAP_ARGS179(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS178(w,o,__VA_ARGS__))
#define WRAP_ARGS180(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS179(w,o,__VA_ARGS__))
#define WRAP_ARGS181(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS180(w,o,__VA_ARGS__))
#define WRAP_ARGS182(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS181(w,o,__VA_ARGS__))
#define WRAP_ARGS183(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS182(w,o,__VA_ARGS__))
#define WRAP_ARGS184(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS183(w,o,__VA_ARGS__))
#define WRAP_ARGS185(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS184(w,o,__VA_ARGS__))
#define WRAP_ARGS186(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS185(w,o,__VA_ARGS__))
#define WRAP_ARGS187(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS186(w,o,__VA_ARGS__))
#define WRAP_ARGS188(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS187(w,o,__VA_ARGS__))
#define WRAP_ARGS189(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS188(w,o,__VA_ARGS__))
#define WRAP_ARGS190(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS189(w,o,__VA_ARGS__))
#define WRAP_ARGS191(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS190(w,o,__VA_ARGS__))
#define WRAP_ARGS192(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS191(w,o,__VA_ARGS__))
#define WRAP_ARGS193(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS192(w,o,__VA_ARGS__))
#define WRAP_ARGS194(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS193(w,o,__VA_ARGS__))
#define WRAP_ARGS195(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS194(w,o,__VA_ARGS__))
#define WRAP_ARGS196(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS195(w,o,__VA_ARGS__))
#define WRAP_ARGS197(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS196(w,o,__VA_ARGS__))
#define WRAP_ARGS198(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS197(w,o,__VA_ARGS__))
#define WRAP_ARGS199(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS198(w,o,__VA_ARGS__))
#define WRAP_ARGS200(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS199(w,o,__VA_ARGS__))
#define WRAP_ARGS201(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS200(w,o,__VA_ARGS__))
#define WRAP_ARGS202(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS201(w,o,__VA_ARGS__))
#define WRAP_ARGS203(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS202(w,o,__VA_ARGS__))
#define WRAP_ARGS204(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS203(w,o,__VA_ARGS__))
#define WRAP_ARGS205(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS204(w,o,__VA_ARGS__))
#define WRAP_ARGS206(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS205(w,o,__VA_ARGS__))
#define WRAP_ARGS207(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS206(w,o,__VA_ARGS__))
#define WRAP_ARGS208(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS207(w,o,__VA_ARGS__))
#define WRAP_ARGS209(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS208(w,o,__VA_ARGS__))
#define WRAP_ARGS210(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS209(w,o,__VA_ARGS__))
#define WRAP_ARGS211(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS210(w,o,__VA_ARGS__))
#define WRAP_ARGS212(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS211(w,o,__VA_ARGS__))
#define WRAP_ARGS213(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS212(w,o,__VA_ARGS__))
#define WRAP_ARGS214(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS213(w,o,__VA_ARGS__))
#define WRAP_ARGS215(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS214(w,o,__VA_ARGS__))
#define WRAP_ARGS216(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS215(w,o,__VA_ARGS__))
#define WRAP_ARGS217(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS216(w,o,__VA_ARGS__))
#define WRAP_ARGS218(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS217(w,o,__VA_ARGS__))
#define WRAP_ARGS219(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS218(w,o,__VA_ARGS__))
#define WRAP_ARGS220(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS219(w,o,__VA_ARGS__))
#define WRAP_ARGS221(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS220(w,o,__VA_ARGS__))
#define WRAP_ARGS222(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS221(w,o,__VA_ARGS__))
#define WRAP_ARGS223(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS222(w,o,__VA_ARGS__))
#define WRAP_ARGS224(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS223(w,o,__VA_ARGS__))
#define WRAP_ARGS225(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS224(w,o,__VA_ARGS__))
#define WRAP_ARGS226(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS225(w,o,__VA_ARGS__))
#define WRAP_ARGS227(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS226(w,o,__VA_ARGS__))
#define WRAP_ARGS228(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS227(w,o,__VA_ARGS__))
#define WRAP_ARGS229(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS228(w,o,__VA_ARGS__))
#define WRAP_ARGS230(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS229(w,o,__VA_ARGS__))
#define WRAP_ARGS231(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS230(w,o,__VA_ARGS__))
#define WRAP_ARGS232(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS231(w,o,__VA_ARGS__))
#define WRAP_ARGS233(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS232(w,o,__VA_ARGS__))
#define WRAP_ARGS234(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS233(w,o,__VA_ARGS__))
#define WRAP_ARGS235(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS234(w,o,__VA_ARGS__))
#define WRAP_ARGS236(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS235(w,o,__VA_ARGS__))
#define WRAP_ARGS237(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS236(w,o,__VA_ARGS__))
#define WRAP_ARGS238(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS237(w,o,__VA_ARGS__))
#define WRAP_ARGS239(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS238(w,o,__VA_ARGS__))
#define WRAP_ARGS240(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS239(w,o,__VA_ARGS__))
#define WRAP_ARGS241(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS240(w,o,__VA_ARGS__))
#define WRAP_ARGS242(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS241(w,o,__VA_ARGS__))
#define WRAP_ARGS243(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS242(w,o,__VA_ARGS__))
#define WRAP_ARGS244(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS243(w,o,__VA_ARGS__))
#define WRAP_ARGS245(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS244(w,o,__VA_ARGS__))
#define WRAP_ARGS246(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS245(w,o,__VA_ARGS__))
#define WRAP_ARGS247(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS246(w,o,__VA_ARGS__))
#define WRAP_ARGS248(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS247(w,o,__VA_ARGS__))
#define WRAP_ARGS249(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS248(w,o,__VA_ARGS__))
#define WRAP_ARGS250(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS249(w,o,__VA_ARGS__))
#define WRAP_ARGS251(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS250(w,o,__VA_ARGS__))
#define WRAP_ARGS252(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS251(w,o,__VA_ARGS__))
#define WRAP_ARGS253(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS252(w,o,__VA_ARGS__))
#define WRAP_ARGS254(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS253(w,o,__VA_ARGS__))
#define WRAP_ARGS255(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS254(w,o,__VA_ARGS__))
#define WRAP_ARGS256(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS255(w,o,__VA_ARGS__))
#define WRAP_ARGS257(w,o,_1, ...) w(o, _1),YLT_MACRO_EXPAND(WRAP_ARGS256(w,o,__VA_ARGS__))
File diff suppressed because it is too large Load Diff
+224
View File
@@ -0,0 +1,224 @@
#pragma once
#include <cstdint>
#include <optional>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
#if __has_include(<ylt/util/expected.hpp>)
#include <ylt/util/expected.hpp>
#else
#include "iguana/ylt/util/expected.hpp"
#endif
#include "reflect26_compat.hpp"
#include "user_reflect_macro.hpp"
#ifdef YLT_USE_CXX26_REFLECTION
#include "reflect26_core.hpp"
#endif
namespace struct_pack {
template <typename T, uint64_t version>
struct compatible;
}
namespace ylt::reflection {
template <typename T>
using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
#if __cpp_concepts >= 201907L
template <typename Type>
concept expected = requires(Type e) {
typename remove_cvref_t<Type>::value_type;
typename remove_cvref_t<Type>::error_type;
typename remove_cvref_t<Type>::unexpected_type;
e.has_value();
e.error();
requires std::is_same_v<void, typename remove_cvref_t<Type>::value_type> ||
requires(Type e) {
e.value();
};
};
#else
template <typename T, typename = void>
struct expected_impl : std::false_type {};
template <typename T>
struct expected_impl<T, std::void_t<typename remove_cvref_t<T>::value_type,
typename remove_cvref_t<T>::error_type,
typename remove_cvref_t<T>::unexpected_type,
decltype(std::declval<T>().has_value()),
decltype(std::declval<T>().error())>>
: std::true_type {};
// TODO: check e.value()
template <typename T>
constexpr bool expected = expected_impl<T>::value;
#endif
#if __cpp_concepts >= 201907L
template <typename Type>
concept optional = !expected<Type> && requires(Type optional) {
optional.value();
optional.has_value();
optional.operator*();
typename remove_cvref_t<Type>::value_type;
};
#else
template <typename T, typename = void>
struct optional_impl : std::false_type {};
template <typename T>
struct optional_impl<T, std::void_t<decltype(std::declval<T>().value()),
decltype(std::declval<T>().has_value()),
decltype(std::declval<T>().operator*()),
typename remove_cvref_t<T>::value_type>>
: std::true_type {};
template <typename T>
constexpr bool optional = !expected<T> && optional_impl<T>::value;
#endif
template <typename T>
struct pair_impl : std::false_type {};
template <typename F, typename S>
struct pair_impl<std::pair<F, S>> : std::true_type {};
#if __cpp_concepts >= 201907L
template <typename T>
concept pair = pair_impl<remove_cvref_t<T>>::value;
#else
template <typename T>
constexpr bool pair = pair_impl<remove_cvref_t<T>>::value;
#endif
namespace internal {
#if __cpp_concepts >= 201907L
template <typename Type>
concept tuple_size = requires(Type tuple) {
std::tuple_size<remove_cvref_t<Type>>::value;
};
#else
template <typename T, typename = void>
struct tuple_size_impl : std::false_type {};
template <typename T>
struct tuple_size_impl<
T, std::void_t<decltype(std::tuple_size<remove_cvref_t<T>>::value)>>
: std::true_type {};
template <typename T>
constexpr bool tuple_size = tuple_size_impl<T>::value;
#endif
template <typename Type>
constexpr inline bool is_compatible_v = false;
template <typename Type, uint64_t version>
constexpr inline bool is_compatible_v<struct_pack::compatible<Type, version>> =
true;
struct UniversalVectorType {
template <typename T>
operator std::vector<T>();
};
struct UniversalType {
template <typename T>
operator T();
};
struct UniversalIntegralType {
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
operator T();
};
struct UniversalNullptrType {
operator std::nullptr_t();
};
struct UniversalOptionalType {
template <typename U, typename = std::enable_if_t<optional<U>>>
operator U();
};
struct UniversalCompatibleType {
template <typename U, typename = std::enable_if_t<is_compatible_v<U>>>
operator U();
};
template <typename T, typename construct_param_t, typename = void,
typename... Args>
struct is_constructable_impl : std::false_type {};
template <typename T, typename construct_param_t, typename... Args>
struct is_constructable_impl<
T, construct_param_t,
std::void_t<decltype(T{{Args{}}..., {construct_param_t{}}})>, Args...>
: std::true_type {};
template <typename T, typename construct_param_t, typename... Args>
constexpr bool is_constructable =
is_constructable_impl<T, construct_param_t, void, Args...>::value;
template <typename T, typename... Args>
inline constexpr std::size_t members_count_impl() {
if constexpr (is_constructable<T, UniversalVectorType, Args...>) {
return members_count_impl<T, Args..., UniversalVectorType>();
}
else if constexpr (is_constructable<T, UniversalType, Args...>) {
return members_count_impl<T, Args..., UniversalType>();
}
else if constexpr (is_constructable<T, UniversalOptionalType, Args...>) {
return members_count_impl<T, Args..., UniversalOptionalType>();
}
else if constexpr (is_constructable<T, UniversalIntegralType, Args...>) {
return members_count_impl<T, Args..., UniversalIntegralType>();
}
else if constexpr (is_constructable<T, UniversalNullptrType, Args...>) {
return members_count_impl<T, Args..., UniversalNullptrType>();
}
else if constexpr (is_constructable<T, UniversalCompatibleType, Args...>) {
return members_count_impl<T, Args..., UniversalCompatibleType>();
}
else {
return sizeof...(Args);
}
}
} // namespace internal
template <typename T>
inline constexpr std::size_t members_count() {
using type = remove_cvref_t<T>;
#ifdef YLT_USE_CXX26_REFLECTION
if constexpr (internal::tuple_size<type>) {
return std::tuple_size<type>::value;
}
else {
return reflect26::members_count<type>();
}
#else
if constexpr (is_out_ylt_refl_v<type>) {
return refl_member_count(ylt::reflection::identity<type>{});
}
else if constexpr (is_inner_ylt_refl_v<type>) {
return type::refl_member_count(ylt::reflection::identity<type>{});
}
else if constexpr (internal::tuple_size<type>) {
return std::tuple_size<type>::value;
}
else if constexpr (std::is_aggregate_v<type>) {
if constexpr (std::is_empty_v<type>) {
return 0;
}
else {
return internal::members_count_impl<type>();
}
}
else {
static_assert(!sizeof(T), "not supported type!");
}
#endif
}
template <typename T>
constexpr std::size_t members_count_v = members_count<T>();
} // namespace ylt::reflection
+461
View File
@@ -0,0 +1,461 @@
#pragma once
#include <array>
#include <stdexcept>
#include <string_view>
#include <variant>
#include "member_ptr.hpp"
#include "reflect26_compat.hpp"
#include "template_string.hpp"
#ifdef YLT_USE_CXX26_REFLECTION
#include "reflect26_core.hpp"
#endif
namespace ylt::reflection {
template <typename T>
inline constexpr auto get_alias_field_names();
namespace internal {
template <class T>
struct Wrapper {
using Type = T;
T v;
};
template <class T>
Wrapper(T) -> Wrapper<T>;
// This workaround is necessary for clang.
template <class T>
inline constexpr auto wrap(const T& arg) noexcept {
return Wrapper{arg};
}
template <auto ptr>
inline constexpr std::string_view get_member_name() {
#if defined(_MSC_VER)
constexpr std::string_view func_name = __FUNCSIG__;
#else
constexpr std::string_view func_name = __PRETTY_FUNCTION__;
#endif
#if defined(__clang__)
auto split = func_name.substr(0, func_name.size() - 2);
return split.substr(split.find_last_of(":.") + 1);
#elif defined(__GNUC__)
auto split = func_name.substr(0, func_name.rfind(")}"));
return split.substr(split.find_last_of(":") + 1);
#elif defined(_MSC_VER)
auto split = func_name.substr(0, func_name.rfind("}>"));
return split.substr(split.rfind("->") + 2);
#else
static_assert(false,
"You are using an unsupported compiler. Please use GCC, Clang "
"or MSVC or switch to the rfl::Field-syntax.");
#endif
}
template <class T>
struct member_tratis {};
template <class T, class Owner>
struct member_tratis<T Owner::*> {
using owner_type = Owner;
using value_type = T;
};
template <typename T, typename = void>
struct has_alias_struct_name_t : std::false_type {};
template <typename T>
struct has_alias_struct_name_t<
T, std::void_t<decltype(get_alias_struct_name((T*)nullptr))>>
: std::true_type {};
template <typename T, typename = void>
struct has_inner_alias_struct_name_t : std::false_type {};
template <typename T>
struct has_inner_alias_struct_name_t<
T, std::void_t<decltype(T::get_alias_struct_name((T*)nullptr))>>
: std::true_type {};
template <typename T>
constexpr bool has_alias_struct_name_v = has_alias_struct_name_t<T>::value;
template <typename T>
constexpr bool has_inner_alias_struct_name_v =
has_inner_alias_struct_name_t<T>::value;
template <typename T, typename = void>
struct has_alias_field_names_t : std::false_type {};
template <typename T>
struct has_alias_field_names_t<
T, std::void_t<decltype(get_alias_field_names((T*)nullptr))>>
: std::true_type {};
template <typename T, typename = void>
struct has_inner_alias_field_names_t : std::false_type {};
template <typename T>
struct has_inner_alias_field_names_t<
T, std::void_t<decltype(T::get_alias_field_names((T*)nullptr))>>
: std::true_type {};
template <typename T>
constexpr bool has_alias_field_names_v = has_alias_field_names_t<T>::value;
template <typename T>
constexpr bool has_inner_alias_field_names_v =
has_inner_alias_field_names_t<T>::value;
template <typename T, typename U, size_t... Is>
inline constexpr void init_arr_with_tuple(U& arr, std::index_sequence<Is...>) {
constexpr auto tp = struct_to_tuple<T>();
((arr[Is] = internal::get_member_name<internal::wrap(std::get<Is>(tp))>()),
...);
}
template <typename T>
inline constexpr std::array<std::string_view, members_count_v<T>>
get_member_names() {
using type = remove_cvref_t<T>;
#ifdef YLT_USE_CXX26_REFLECTION
return reflect26::member_names_array<type>();
#else
if constexpr (is_out_ylt_refl_v<type>) {
return refl_member_names(ylt::reflection::identity<type>{});
}
else if constexpr (is_inner_ylt_refl_v<type>) {
return type::refl_member_names(ylt::reflection::identity<type>{});
}
else {
constexpr size_t Count = members_count_v<T>;
std::array<std::string_view, Count> arr;
#if __cplusplus >= 202002L && (!defined(_MSC_VER) || _MSC_VER >= 1930)
constexpr auto tp = struct_to_tuple<T>();
[&]<size_t... Is>(std::index_sequence<Is...>) mutable {
((arr[Is] =
internal::get_member_name<internal::wrap(std::get<Is>(tp))>()),
...);
}(std::make_index_sequence<Count>{});
#else
init_arr_with_tuple<T>(arr, std::make_index_sequence<Count>{});
#endif
return arr;
}
#endif
}
#ifdef YLT_USE_CXX26_REFLECTION
template <std::size_t N>
struct member_names_linear_map {
std::array<std::string_view, N> names;
constexpr std::size_t size() const noexcept { return N; }
constexpr auto begin() const noexcept { return names.begin(); }
constexpr auto end() const noexcept { return names.end(); }
static constexpr std::string_view normalized_name(std::string_view name) {
return reflect26::normalized_member_name(name);
}
constexpr std::size_t at(std::string_view name) const {
for (std::size_t i = 0; i < N; ++i) {
if (names[i] == name || normalized_name(names[i]) == name) {
return i;
}
}
throw std::out_of_range("unknown member name");
}
};
#else
template <typename T, size_t... Is>
inline constexpr auto get_member_names_map_impl(T& name_arr,
std::index_sequence<Is...>) {
return frozen::unordered_map<frozen::string, size_t, sizeof...(Is)>{
{name_arr[Is], Is}...};
}
#endif
template <typename T>
inline constexpr auto get_member_names_map() {
constexpr auto name_arr = get_member_names<T>();
#ifdef YLT_USE_CXX26_REFLECTION
return member_names_linear_map<name_arr.size()>{name_arr};
#elif __cplusplus >= 202002L
return [&]<size_t... Is>(std::index_sequence<Is...>) mutable {
return frozen::unordered_map<frozen::string, size_t, name_arr.size()>{
{name_arr[Is], Is}...};
}(std::make_index_sequence<name_arr.size()>{});
#else
return get_member_names_map_impl(name_arr,
std::make_index_sequence<name_arr.size()>{});
#endif
}
template <typename T, typename Tuple, size_t... Is>
inline auto get_member_offset_arr_impl(T& t, Tuple& tp,
std::index_sequence<Is...>) {
std::array<size_t, sizeof...(Is)> arr;
((arr[Is] = size_t((const char*)&std::get<Is>(tp) - (char*)(&t))), ...);
return arr;
}
template <typename T>
inline const auto& get_member_offset_arr(T&& t) {
#ifdef YLT_USE_CXX26_REFLECTION
[[maybe_unused]] static constexpr auto arr =
reflect26::member_offsets<remove_cvref_t<T>>();
return arr;
#else
constexpr size_t Count = members_count_v<T>;
auto tp = ylt::reflection::object_to_tuple(std::forward<T>(t));
#if __cplusplus >= 202002L
[[maybe_unused]] static std::array<size_t, Count> arr = {
[&]<size_t... Is>(std::index_sequence<Is...>) mutable {
std::array<size_t, Count> arr;
((arr[Is] = size_t((const char*)&std::get<Is>(tp) - (char*)(&t))), ...);
return arr;
}(std::make_index_sequence<Count>{})}; // namespace internal
return arr;
#else
[[maybe_unused]] static std::array<size_t, Count> arr =
get_member_offset_arr_impl(t, tp, std::make_index_sequence<Count>{});
return arr;
#endif
#endif
} // namespace ylt::reflection
template <typename T>
inline const auto& get_member_offset_arr() {
#ifdef YLT_USE_CXX26_REFLECTION
[[maybe_unused]] static constexpr auto arr =
reflect26::member_offsets<remove_cvref_t<T>>();
return arr;
#else
return get_member_offset_arr(internal::wrapper<T>::value);
#endif
} // namespace ylt::reflection
} // namespace internal
template <typename... Args>
inline constexpr auto tuple_to_variant(std::tuple<Args...>) {
return std::variant<std::add_pointer_t<Args>...>{};
}
#ifdef YLT_USE_CXX26_REFLECTION
namespace internal {
template <typename T, typename Member>
using copy_const_t =
std::conditional_t<std::is_const_v<std::remove_reference_t<T>>,
std::add_const_t<Member>, Member>;
template <typename T, std::size_t... Is>
inline constexpr auto struct_variant_reflect(std::index_sequence<Is...>) {
static constexpr auto members = reflect26::data_members_array<T>();
return std::variant<std::add_pointer_t<
copy_const_t<T, reflect26::meta_type_t<members[Is]>>>...>{};
}
} // namespace internal
template <typename T>
using struct_variant_t = decltype(internal::struct_variant_reflect<T>(
std::make_index_sequence<members_count_v<remove_cvref_t<T>>>{}));
#else
template <typename T>
using struct_variant_t = decltype(tuple_to_variant(
std::declval<decltype(struct_to_tuple<remove_cvref_t<T>>())>));
#endif
template <typename T>
constexpr auto member_names_map = internal::get_member_names_map<T>();
template <typename T>
inline auto member_offsets = internal::get_member_offset_arr<T>();
template <auto member>
inline constexpr size_t index_of() {
using T = typename internal::member_tratis<
std::remove_const_t<decltype(member)>>::owner_type;
constexpr auto name = field_string<member>();
constexpr auto names = internal::get_member_names<T>();
for (size_t i = 0; i < names.size(); i++) {
if (name == names[i]) {
return i;
}
}
return names.size();
}
struct field_alias_t {
std::string_view alias_name;
size_t index;
};
template <typename T>
inline constexpr auto get_alias_field_names() {
if constexpr (internal::has_alias_field_names_v<T>) {
return get_alias_field_names((T*)nullptr);
}
else if constexpr (internal::has_inner_alias_field_names_v<T>) {
return T::get_alias_field_names((T*)nullptr);
}
else {
return std::array<std::string_view, 0>{};
}
}
template <typename T>
constexpr std::string_view get_struct_name() {
using U = ylt::reflection::remove_cvref_t<T>;
#ifdef YLT_USE_CXX26_REFLECTION
constexpr auto annotation_name = reflect26::type_name<U>();
if constexpr (!annotation_name.empty()) {
return annotation_name;
}
else {
#endif
if constexpr (internal::has_alias_struct_name_v<U>) {
return get_alias_struct_name((U*)nullptr);
}
else if constexpr (internal::has_inner_alias_struct_name_v<U>) {
return U::get_alias_struct_name((U*)nullptr);
}
else {
return type_string<U>();
}
#ifdef YLT_USE_CXX26_REFLECTION
}
#endif
}
template <typename T>
inline constexpr std::array<std::string_view, members_count_v<T>>
get_member_names() {
auto arr = internal::get_member_names<T>();
using U = ylt::reflection::remove_cvref_t<T>;
if constexpr (internal::has_alias_field_names_v<U> ||
internal::has_inner_alias_field_names_v<U>) {
constexpr auto alias_arr = get_alias_field_names<U>();
for (size_t i = 0; i < alias_arr.size(); i++) {
arr[alias_arr[i].index] = alias_arr[i].alias_name;
}
}
return arr;
}
template <std::size_t N>
struct FixedString {
char data[N];
template <std::size_t... I>
constexpr FixedString(const char (&s)[N], std::index_sequence<I...>)
: data{s[I]...} {}
constexpr FixedString(const char (&s)[N])
: FixedString(s, std::make_index_sequence<N>()) {}
constexpr std::string_view str() const {
return std::string_view{data, N - 1};
}
};
template <typename T>
inline constexpr size_t index_of(std::string_view name) {
constexpr auto arr = get_member_names<T>();
for (size_t i = 0; i < arr.size(); i++) {
if (arr[i] == name) {
return i;
}
}
return arr.size();
}
#if __cplusplus >= 202002L
template <typename T, FixedString name>
inline constexpr size_t index_of() {
return index_of<T>(name.str());
}
#endif
template <typename T, size_t index>
inline constexpr std::string_view name_of() {
static_assert(index < members_count_v<T>, "index out of range");
constexpr auto arr = get_member_names<T>();
return arr[index];
}
template <typename T>
inline constexpr std::string_view name_of(size_t index) {
constexpr auto arr = get_member_names<T>();
if (index >= arr.size()) {
return "";
}
return arr[index];
}
template <typename Visit, typename U, size_t... Is>
inline constexpr void for_each_impl(Visit&& func, U& arr,
std::index_sequence<Is...>) {
if constexpr (std::is_invocable_v<Visit, std::string_view, size_t>) {
(func(arr[Is], Is), ...);
}
else if constexpr (std::is_invocable_v<Visit, std::string_view>) {
(func(arr[Is]), ...);
}
else {
static_assert(sizeof(Visit) < 0,
"invalid arguments, full arguments: [std::string_view, "
"size_t], at least has std::string_view and make sure keep "
"the order of arguments");
}
}
template <typename T, typename Visit>
inline constexpr void for_each(Visit&& func) {
#ifdef YLT_USE_CXX26_REFLECTION
static constexpr auto arr = get_member_names<T>();
[[maybe_unused]] size_t index = 0;
template for (constexpr auto name : arr) {
if constexpr (std::is_invocable_v<Visit, std::string_view, size_t>) {
func(name, index++);
}
else if constexpr (std::is_invocable_v<Visit, std::string_view>) {
func(name);
}
else {
static_assert(sizeof(Visit) < 0,
"invalid arguments, full arguments: [std::string_view, "
"size_t], at least has std::string_view and make sure keep "
"the order of arguments");
}
}
#elif __cplusplus >= 202002L
constexpr auto arr = get_member_names<T>();
[&]<size_t... Is>(std::index_sequence<Is...>) mutable {
if constexpr (std::is_invocable_v<Visit, std::string_view, size_t>) {
(func(arr[Is], Is), ...);
}
else if constexpr (std::is_invocable_v<Visit, std::string_view>) {
(func(arr[Is]), ...);
}
else {
static_assert(sizeof(Visit) < 0,
"invalid arguments, full arguments: [std::string_view, "
"size_t], at least has std::string_view and make sure keep "
"the order of arguments");
}
}(std::make_index_sequence<arr.size()>{});
#else
constexpr auto arr = get_member_names<T>();
for_each_impl(std::forward<Visit>(func), arr,
std::make_index_sequence<arr.size()>{});
#endif
}
} // namespace ylt::reflection
+157
View File
@@ -0,0 +1,157 @@
#pragma once
#include "member_count.hpp"
#include "reflect26_compat.hpp"
#ifdef YLT_USE_CXX26_REFLECTION
#include "reflect26_core.hpp"
#endif
// modified based on:
// https://github.com/getml/reflect-cpp/blob/main/include/rfl/internal/bind_fake_object_to_tuple.hpp
// thanks for alxn4's great idea!
namespace ylt::reflection {
namespace internal {
template <class T, std::size_t n>
struct object_tuple_view_helper {
static constexpr auto tuple_view() {
static_assert(
sizeof(T) < 0,
"\n\nThis error occurs for one of two reasons:\n\n"
"1) You have created a struct with more than 256 fields, which is "
"unsupported. \n\n"
"2) Your struct is not an aggregate type. You can make it aggregated, "
"or defined a YLT_REFL macro. \n\n");
}
static constexpr auto tuple_view(T&) {
static_assert(
sizeof(T) < 0,
"\n\nThis error occurs for one of two reasons:\n\n"
"1) You have created a struct with more than 256 fields, which is "
"unsupported. \n\n"
"2) Your struct is not an aggregate type. You can make it aggregated, "
"or defined a YLT_REFL macro. \n\n");
}
template <typename Visitor>
static constexpr decltype(auto) tuple_view(T&&, Visitor&&) {
static_assert(
sizeof(T) < 0,
"\n\nThis error occurs for one of two reasons:\n\n"
"1) You have created a struct with more than 256 fields, which is "
"unsupported. \n\n"
"2) Your struct is not an aggregate type. You can make it aggregated, "
"or defined a YLT_REFL macro. \n\n");
}
};
template <class T>
struct object_tuple_view_helper<T, 0> {
static constexpr auto tuple_view() { return std::tie(); }
static constexpr auto tuple_view(T&) { return std::tie(); }
template <typename Visitor>
static constexpr decltype(auto) tuple_view(T&&, Visitor&&) {}
};
template <class T>
struct wrapper {
inline static remove_cvref_t<T> value;
};
template <class T>
inline constexpr remove_cvref_t<T>& get_fake_object() noexcept {
return wrapper<remove_cvref_t<T>>::value;
}
#define RFL_INTERNAL_OBJECT_IF_YOU_SEE_AN_ERROR_REFER_TO_DOCUMENTATION_ON_C_ARRAYS( \
n, ...) \
template <class T> \
struct object_tuple_view_helper<T, n> { \
static constexpr auto tuple_view() { \
auto& [__VA_ARGS__] = get_fake_object<remove_cvref_t<T>>(); \
auto ref_tup = std::tie(__VA_ARGS__); \
auto get_ptrs = [](auto&... _refs) { \
return std::make_tuple(&_refs...); \
}; \
return std::apply(get_ptrs, ref_tup); \
} \
static constexpr auto tuple_view(T& t) { \
auto& [__VA_ARGS__] = t; \
return std::tie(__VA_ARGS__); \
} \
template <typename Visitor> \
static constexpr decltype(auto) tuple_view(T&& t, Visitor&& visitor) { \
auto&& [__VA_ARGS__] = t; \
return visitor(__VA_ARGS__); \
} \
}
// such file is generate macro file
#ifndef YLT_USE_CXX26_REFLECTION
#include "internal/generate/member_macro.hpp"
#endif
template <class T>
inline constexpr auto tuple_view(T&& t) {
return internal::object_tuple_view_helper<T, members_count_v<T>>::tuple_view(
t);
}
template <size_t Count, class T, typename Visitor>
inline constexpr decltype(auto) tuple_view(T&& t, Visitor&& visitor) {
return internal::object_tuple_view_helper<T, Count>::tuple_view(
std::forward<T>(t), std::forward<Visitor>(visitor));
}
} // namespace internal
template <class T>
inline constexpr auto struct_to_tuple() {
#ifdef YLT_USE_CXX26_REFLECTION
return reflect26::data_members<remove_cvref_t<T>>();
#else
return internal::object_tuple_view_helper<T,
members_count_v<T>>::tuple_view();
#endif
}
template <class T>
inline constexpr auto object_to_tuple(T&& t) {
#ifdef YLT_USE_CXX26_REFLECTION
return reflect26::data_members<remove_cvref_t<T>>();
#else
using type = remove_cvref_t<T>;
if constexpr (is_out_ylt_refl_v<type>) {
return refl_object_to_tuple(std::forward<T>(t));
}
else if constexpr (is_inner_ylt_refl_v<type>) {
return type::refl_object_to_tuple(std::forward<T>(t));
}
else {
return internal::tuple_view(std::forward<T>(t));
}
#endif
}
template <class T, typename Visitor, size_t Count = members_count_v<T>>
inline constexpr decltype(auto) visit_members(T&& t, Visitor&& visitor) {
#ifdef YLT_USE_CXX26_REFLECTION
return reflect26::for_each_data_member(std::forward<T>(t),
std::forward<Visitor>(visitor));
#else
using type = remove_cvref_t<T>;
if constexpr (is_out_ylt_refl_v<type>) {
return refl_visit_members(std::forward<T>(t),
std::forward<Visitor>(visitor));
}
else if constexpr (is_inner_ylt_refl_v<type>) {
return t.refl_visit_members(std::forward<T>(t),
std::forward<Visitor>(visitor));
}
else {
return internal::tuple_view<Count>(std::forward<T>(t),
std::forward<Visitor>(visitor));
}
#endif
}
} // namespace ylt::reflection
+430
View File
@@ -0,0 +1,430 @@
#pragma once
#include <algorithm>
#include <cstddef>
#include <iterator>
#include <memory>
#include <stdexcept>
#include <variant>
#include "member_names.hpp"
#ifdef YLT_USE_CXX26_REFLECTION
#include "reflect26_dispatch.hpp"
#endif
#include "template_switch.hpp"
namespace ylt::reflection {
namespace internal {
template <typename Member, typename T>
inline void set_member_ptr(Member& member, T t) {
if constexpr (std::is_constructible_v<Member, T>) {
member = t;
}
else {
std::string str = "given type: ";
str.append(type_string<std::remove_pointer_t<Member>>());
str.append(" is not equal with real type: ")
.append(type_string<std::remove_pointer_t<T>>());
throw std::invalid_argument(str);
}
}
template <typename T>
struct is_variant : std::false_type {};
template <typename... T>
struct is_variant<std::variant<T...>> : std::true_type {};
struct switch_helper {
template <size_t index, typename Member, class Tuple>
static constexpr size_t run(Member& member, Tuple& t) {
if constexpr (index >= std::tuple_size_v<Tuple>) {
return index;
}
else {
if constexpr (is_variant<Member>::value) {
member = Member{std::in_place_index<index>, &std::get<index>(t)};
}
else {
set_member_ptr(member, &std::get<index>(t));
}
return index;
}
}
};
#ifndef YLT_USE_CXX26_REFLECTION
inline constexpr frozen::string filter_str(const frozen::string& str) {
if (str.size() > 3 && str[0] == '_' && str[1] == '_' && str[2] == '_') {
auto ptr = str.data() + 3;
return frozen::string(ptr, str.size() - 3);
}
return str;
}
template <typename value_type>
struct offset_t {
using type = value_type;
size_t value;
};
template <typename T, typename Tuple, size_t... Is>
inline auto get_variant_type() {
return std::variant<offset_t<
ylt::reflection::remove_cvref_t<std::tuple_element_t<Is, Tuple>>>...>{};
}
template <typename T, size_t... Is>
inline constexpr auto get_variant_map_impl(std::index_sequence<Is...>) {
using U = ylt::reflection::remove_cvref_t<T>;
constexpr auto arr = ylt::reflection::get_member_names<U>();
auto& offset_arr = get_member_offset_arr(wrapper<U>::value);
using Tuple = decltype(ylt::reflection::object_to_tuple(std::declval<U>()));
using ValueType = decltype(get_variant_type<U, Tuple, Is...>());
return frozen::unordered_map<frozen::string, ValueType, sizeof...(Is)>{
{filter_str(arr[Is]),
ValueType{std::in_place_index<Is>,
offset_t<ylt::reflection::remove_cvref_t<
std::tuple_element_t<Is, Tuple>>>{offset_arr[Is]}}}...};
}
#endif
#ifdef YLT_USE_CXX26_REFLECTION
template <typename T, size_t... Is>
inline auto get_variant_by_index_reflect(T& t, size_t index,
std::index_sequence<Is...>) {
using U = ylt::reflection::remove_cvref_t<T>;
using variant = struct_variant_t<T>;
static constexpr auto members = reflect26::data_members_array<U>();
variant member_ptr;
bool found = false;
(
[&] {
if (!found && index == Is) {
member_ptr.template emplace<Is>(std::addressof(t.[:members[Is]:]));
found = true;
}
}(),
...);
if (!found) {
std::string str = "index out of range, ";
str.append("index: ")
.append(std::to_string(index))
.append(" is greater equal than member count ")
.append(std::to_string(members_count_v<U>));
throw std::out_of_range(str);
}
return member_ptr;
}
#endif
} // namespace internal
template <typename T>
inline constexpr auto get_variant_map() {
#ifdef YLT_USE_CXX26_REFLECTION
static_assert(sizeof(T) < 0,
"get_variant_map is not available with C++26 reflection; use "
"reflect26::dispatch_by_name instead");
#else
return internal::get_variant_map_impl<T>(
std::make_index_sequence<members_count_v<T>>{});
#endif
}
template <typename Member, typename T>
inline Member& get(T& t, size_t index) {
#ifdef YLT_USE_CXX26_REFLECTION
Member* member_ptr = nullptr;
bool found = reflect26::dispatch_by_index(t, index, [&](auto& field) {
internal::set_member_ptr(member_ptr, std::addressof(field));
});
if (!found) {
std::string str = "index out of range, ";
str.append("index: ")
.append(std::to_string(index))
.append(" is greater equal than member count ")
.append(std::to_string(members_count_v<remove_cvref_t<T>>));
throw std::out_of_range(str);
}
return *member_ptr;
#else
auto ref_tp = object_to_tuple(t);
constexpr size_t tuple_size = std::tuple_size_v<decltype(ref_tp)>;
if (index >= tuple_size) {
std::string str = "index out of range, ";
str.append("index: ")
.append(std::to_string(index))
.append(" is greater equal than member count ")
.append(std::to_string(tuple_size));
throw std::out_of_range(str);
}
Member* member_ptr = nullptr;
template_switch<internal::switch_helper>(index, member_ptr, ref_tp);
return *member_ptr;
#endif
}
template <typename Member, typename T>
inline Member& get(T& t, std::string_view name) {
#ifdef YLT_USE_CXX26_REFLECTION
Member* member_ptr = nullptr;
bool found = reflect26::dispatch_by_name(t, name, [&](auto& field) {
internal::set_member_ptr(member_ptr, std::addressof(field));
});
if (!found) {
throw std::out_of_range("unknown member name");
}
return *member_ptr;
#else
static constexpr auto map = member_names_map<T>;
size_t index = map.at(name); // may throw out_of_range: unknown key.
auto ref_tp = object_to_tuple(t);
Member* member_ptr = nullptr;
template_switch<internal::switch_helper>(index, member_ptr, ref_tp);
return *member_ptr;
#endif
}
template <typename T>
inline auto get(T& t, size_t index) {
#ifdef YLT_USE_CXX26_REFLECTION
using U = remove_cvref_t<T>;
if constexpr (members_count_v<U> == 0) {
throw std::out_of_range("index out of range, empty object");
}
else {
return internal::get_variant_by_index_reflect(
t, index, std::make_index_sequence<members_count_v<U>>{});
}
#else
auto ref_tp = object_to_tuple(t);
constexpr size_t tuple_size = std::tuple_size_v<decltype(ref_tp)>;
if (index >= tuple_size) {
std::string str = "index out of range, ";
str.append("index: ")
.append(std::to_string(index))
.append(" is greater equal than member count ")
.append(std::to_string(tuple_size));
throw std::out_of_range(str);
}
using variant = decltype(tuple_to_variant(ref_tp));
variant member_ptr;
template_switch<internal::switch_helper>(index, member_ptr, ref_tp);
return member_ptr;
#endif
}
template <typename T>
inline constexpr auto get(T& t, std::string_view name) {
#ifdef YLT_USE_CXX26_REFLECTION
constexpr auto& map = member_names_map<T>;
size_t index = map.at(name); // may throw out_of_range: unknown key.
return get(t, index);
#else
constexpr auto& map = member_names_map<T>;
size_t index = map.at(name); // may throw out_of_range: unknown key.
return get(t, index);
#endif
}
template <size_t index, typename T>
inline constexpr auto& get(T& t) {
#ifdef YLT_USE_CXX26_REFLECTION
using U = remove_cvref_t<T>;
static_assert(index < members_count_v<U>, "index out of range");
decltype(auto) result = [&]() -> decltype(auto) {
static constexpr auto members = reflect26::data_members_array<U>();
return (t.[:members[index]:]);
}();
return result;
#else
auto ref_tp = object_to_tuple(t);
static_assert(index < std::tuple_size_v<decltype(ref_tp)>,
"index out of range");
return std::get<index>(ref_tp);
#endif
}
#if __cplusplus >= 202002L
template <FixedString name, typename T>
inline constexpr auto& get(T& t) {
constexpr size_t index = index_of<T, name>();
return get<index>(t);
}
#endif
template <typename T, typename Field>
inline size_t index_of(T& t, Field& value) {
#ifdef YLT_USE_CXX26_REFLECTION
using U = remove_cvref_t<T>;
size_t index = 0;
size_t result = members_count_v<U>;
const auto value_addr =
static_cast<const volatile void*>(std::addressof(value));
reflect26::for_each_data_member(t, [&](auto& field, std::string_view, auto) {
const auto field_addr =
static_cast<const volatile void*>(std::addressof(field));
if (result == members_count_v<U> && field_addr == value_addr) {
result = index;
}
++index;
});
return result;
#else
const auto& offset_arr = member_offsets<T>;
size_t cur_offset = (const char*)(&value) - (const char*)(&t);
auto it = std::lower_bound(offset_arr.begin(), offset_arr.end(), cur_offset);
if (it == offset_arr.end()) {
return offset_arr.size();
}
return std::distance(offset_arr.begin(), it);
#endif
}
template <typename Member>
inline size_t index_of(Member member) {
using T = typename member_traits<Member>::owner_type;
static auto& t = internal::get_fake_object<T>();
return index_of(t, t.*member);
}
template <typename T, typename Field>
inline constexpr std::string_view name_of(T& t, Field& value) {
size_t index = index_of(t, value);
constexpr auto arr = get_member_names<T>();
if (index == arr.size()) {
return "";
}
return arr[index];
}
template <typename T, typename Visit, size_t... Is, typename... Args>
inline constexpr void visit_members_impl0(Visit&& func,
std::index_sequence<Is...>,
Args&... args) {
constexpr auto arr = get_member_names<T>();
(func(args, arr[Is]), ...);
}
template <typename T, typename Visit, size_t... Is, typename... Args>
inline constexpr void visit_members_impl(Visit&& func,
std::index_sequence<Is...>,
Args&... args) {
constexpr auto arr = get_member_names<T>();
(func(args, arr[Is], Is), ...);
}
namespace internal {
template <typename Visit, typename Field>
inline constexpr void invoke_for_each_field(Visit& func, Field& field,
std::string_view name,
std::size_t index) {
if constexpr (std::is_invocable_v<Visit&, Field&>) {
func(field);
}
else if constexpr (std::is_invocable_v<Visit&, Field&, std::string_view>) {
func(field, name);
}
else if constexpr (std::is_invocable_v<Visit&, Field&, std::string_view,
std::size_t>) {
func(field, name, index);
}
else {
static_assert(sizeof(Visit) < 0,
"invalid arguments, full arguments: [field_value&, "
"std::string_view, size_t], at least has field_value and "
"make sure keep the order of arguments");
}
}
} // namespace internal
template <typename T, typename Visit>
inline constexpr void for_each(T&& t, Visit&& func) {
#ifdef YLT_USE_CXX26_REFLECTION
using U = remove_cvref_t<T>;
constexpr auto Count = members_count_v<U>;
if constexpr (Count == 0) {
return;
}
else {
constexpr auto names = get_member_names<U>();
reflect26::for_each_data_member(
std::forward<T>(t),
[&](auto& field, std::string_view name, auto index) {
(void)name;
internal::invoke_for_each_field(func, field, names[index], index);
});
}
#else
using Tuple = decltype(object_to_tuple(t));
constexpr auto Count = std::tuple_size_v<Tuple>;
if constexpr (Count == 0) {
return;
}
else {
using first_t = std::tuple_element_t<0, Tuple>;
if constexpr (std::is_invocable_v<Visit, first_t>) {
visit_members(t, [&func](auto&... args) {
(func(args), ...);
});
}
else {
if constexpr (std::is_invocable_v<Visit, first_t, std::string_view>) {
visit_members(t, [&](auto&... args) {
#if __cplusplus >= 202002L && (!defined(_MSC_VER) || _MSC_VER >= 1930)
[&]<size_t... Is>(std::index_sequence<Is...>) mutable {
constexpr auto arr = get_member_names<T>();
(func(args, arr[Is]), ...);
}(std::make_index_sequence<sizeof...(args)>{});
#else
visit_members_impl0<T>(std::forward<Visit>(func),
std::make_index_sequence<sizeof...(args)>{},
args...);
#endif
});
}
else if constexpr (std::is_invocable_v<Visit, first_t, std::string_view,
size_t>) {
visit_members(t, [&](auto&... args) {
#if __cplusplus >= 202002L && (!defined(_MSC_VER) || _MSC_VER >= 1930)
[&]<size_t... Is>(std::index_sequence<Is...>) mutable {
constexpr auto arr = get_member_names<T>();
(func(args, arr[Is], Is), ...);
}(std::make_index_sequence<sizeof...(args)>{});
#else
visit_members_impl<T>(std::forward<Visit>(func),
std::make_index_sequence<sizeof...(args)>{},
args...);
#endif
});
}
else {
static_assert(sizeof(Visit) < 0,
"invalid arguments, full arguments: [field_value&, "
"std::string_view, size_t], at least has field_value and "
"make sure keep the order of arguments");
}
}
}
#endif
}
} // namespace ylt::reflection
#if (defined(__GNUC__) && __GNUC__ > 10) || \
((defined(__clang__) || defined(_MSC_VER)) && __has_include(<concepts>))
#if __cplusplus >= 202002L
template <ylt::reflection::FixedString s>
inline constexpr auto operator""_ylts() {
return s;
}
#endif
#endif
@@ -0,0 +1,15 @@
#pragma once
#include <tuple>
#include "internal/common_macro.hpp"
namespace ylt::reflection {
template <typename T, auto... field>
struct private_visitor {
friend inline constexpr auto get_private_ptrs(
const ylt::reflection::identity<T>&) {
constexpr auto tp = std::make_tuple(field...);
return tp;
}
};
} // namespace ylt::reflection
@@ -0,0 +1,6 @@
#pragma once
// GCC 16 uses __cpp_impl_reflection; future compilers may use __cpp_reflection
#if (defined(__cpp_impl_reflection) && __cpp_impl_reflection >= 202406L) || \
(defined(__cpp_reflection) && __cpp_reflection >= 202406L)
#define YLT_USE_CXX26_REFLECTION 1
#endif

Some files were not shown because too many files have changed in this diff Show More