From 9bcdc61c5a6c5f3ea998dfa559b97b8896ca2f23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=B0=E4=BB=94?= Date: Wed, 18 Feb 2026 16:00:16 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E6=8F=90=E4=BA=A4?= =?UTF-8?q?=EF=BC=9AUtils=20=E5=B7=A5=E5=85=B7=E5=BA=93=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 包含以下子模块: - VrCommon: 核心接口和数据结构 - VrUtils: 工具类库(JSON、log4cpp、tinyxml2、INI、MD5、CRC) - CloudUtils: 点云工具 - DataUtils: 数据处理工具(CloudMathClac、CoordinateTransform) - CloudView: 点云查看工具 功能说明: - 提供项目通用的基础工具类和实用功能 - 支持 Windows (MSVC/MinGW) 和 Linux (ARM/x86_64) 平台 - 使用 Qt qmake 构建系统 - 所有模块编译为静态库 --- .gitignore | 53 + CloudUtils/CloudUtils.pro | 35 + CloudUtils/Inc/LaserDataLoader.h | 74 + CloudUtils/Inc/PointCloudImageUtils.h | 127 + CloudUtils/Src/LaserDataLoader.cpp | 554 +++ CloudUtils/Src/PointCloudImageUtils.cpp | 1333 ++++++ CloudView/CloudView.pro | 67 + CloudView/Inc/CloudViewMainWindow.h | 312 ++ CloudView/Inc/PointCloudConverter.h | 149 + CloudView/Inc/PointCloudGLWidget.h | 393 ++ CloudView/Src/CloudViewMainWindow.cpp | 1670 ++++++++ CloudView/Src/PointCloudConverter.cpp | 561 +++ CloudView/Src/PointCloudGLWidget.cpp | 1276 ++++++ CloudView/main.cpp | 35 + CloudView/poses.txt | 17 + CloudView/resource/logo.ico | Bin 0 -> 38078 bytes CloudView/resource/logo.png | Bin 0 -> 6687 bytes CloudView/resource/resource.qrc | 6 + CloudView/segments.txt | 23 + DataUtils/CloudMathClac/CloudMathClac.pro | 27 + DataUtils/CloudMathClac/Inc/CurveFitting.h | 50 + DataUtils/CloudMathClac/Src/CurveFitting.cpp | 202 + .../CoordinateTransform.pro | 27 + .../Inc/CoordinateTransform.h | 169 + .../CoordinateTransform/Inc/CoordinateTypes.h | 362 ++ .../Src/CoordinateTransform.cpp | 616 +++ DataUtils/DataUtils.pro | 8 + MIGRATION.md | 197 + QUICKSTART.md | 201 + README.md | 141 + Utils.pro | 22 + VrCommon/Inc/VrCommon.h | 59 + VrCommon/Inc/VrConfigCmd.h | 205 + VrCommon/Inc/VrError.h | 100 + VrCommon/Src/VrCommon.cpp | 1 + VrCommon/VrCommon.pro | 27 + VrUtils/CMakeLists.txt | 31 + VrUtils/Inc/IVrUtils.h | 63 + VrUtils/Inc/VrAngleUtils.h | 14 + VrUtils/Inc/VrCodeFormatUtils.h | 25 + VrUtils/Inc/VrDateUtils.h | 22 + VrUtils/Inc/VrDebugTime.h | 50 + VrUtils/Inc/VrFileUtils.h | 44 + VrUtils/Inc/VrLog.h | 87 + VrUtils/Inc/VrMD5Utils.h | 7 + VrUtils/Inc/VrNTPUtils.h | 50 + VrUtils/Inc/VrNetUtils.h | 68 + VrUtils/Inc/VrNumUtils.h | 10 + VrUtils/Inc/VrShellUtils.h | 6 + VrUtils/Inc/VrStringUtils.h | 23 + VrUtils/Inc/VrTimeUtils.h | 32 + VrUtils/Inc/getopt.h | 133 + VrUtils/MD5/md5.cpp | 287 ++ VrUtils/MD5/md5.h | 138 + VrUtils/Src/VrAngleUtils.cpp | 16 + VrUtils/Src/VrCodeFormatUtils.cpp | 161 + VrUtils/Src/VrDateUtils.cpp | 72 + VrUtils/Src/VrFileUtils.cpp | 275 ++ VrUtils/Src/VrLog.cpp | 379 ++ VrUtils/Src/VrMD5Utils.cpp | 10 + VrUtils/Src/VrNTPUtils.cpp | 216 + VrUtils/Src/VrNetUtils.cpp | 812 ++++ VrUtils/Src/VrNumUtils.cpp | 20 + VrUtils/Src/VrShellUtils.cpp | 43 + VrUtils/Src/VrStringUtils.cpp | 104 + VrUtils/Src/VrTimeUtils.cpp | 31 + VrUtils/Src/getopt.c | 764 ++++ VrUtils/VrUtils.pro | 169 + VrUtils/crc/checksum.h | 116 + VrUtils/crc/src/crc16.c | 149 + VrUtils/crc/src/crc32.c | 81 + VrUtils/crc/src/crc64.c | 107 + VrUtils/crc/src/crc8.c | 103 + VrUtils/crc/src/crcccitt.c | 162 + VrUtils/crc/src/crcdnp.c | 128 + VrUtils/crc/src/crckrmit.c | 126 + VrUtils/crc/src/crcsick.c | 102 + VrUtils/crc/src/nmea-chk.c | 76 + VrUtils/ini/ConvertUTF.c | 539 +++ VrUtils/ini/ConvertUTF.h | 149 + VrUtils/ini/SimpleIni.h | 3625 +++++++++++++++++ VrUtils/jsoncpp/json/assertions.h | 41 + VrUtils/jsoncpp/json/autolink.h | 25 + VrUtils/jsoncpp/json/config.h | 114 + VrUtils/jsoncpp/json/features.h | 59 + VrUtils/jsoncpp/json/forwards.h | 45 + VrUtils/jsoncpp/json/json.h | 15 + VrUtils/jsoncpp/json/reader.h | 277 ++ VrUtils/jsoncpp/json/value.h | 1091 +++++ VrUtils/jsoncpp/json/version.h | 14 + VrUtils/jsoncpp/json/writer.h | 214 + VrUtils/jsoncpp/json_batchallocator.h | 123 + VrUtils/jsoncpp/json_internalarray.inl | 360 ++ VrUtils/jsoncpp/json_internalmap.inl | 473 +++ VrUtils/jsoncpp/json_reader.cpp | 887 ++++ VrUtils/jsoncpp/json_tool.h | 89 + VrUtils/jsoncpp/json_value.cpp | 1480 +++++++ VrUtils/jsoncpp/json_valueiterator.inl | 241 ++ VrUtils/jsoncpp/json_writer.cpp | 691 ++++ .../log4cpp/include/log4cpp/AbortAppender.hh | 46 + VrUtils/log4cpp/include/log4cpp/Appender.hh | 169 + .../include/log4cpp/AppenderSkeleton.hh | 111 + .../include/log4cpp/AppendersFactory.hh | 42 + .../include/log4cpp/BasicConfigurator.hh | 31 + .../log4cpp/include/log4cpp/BasicLayout.hh | 34 + .../include/log4cpp/BufferingAppender.hh | 45 + VrUtils/log4cpp/include/log4cpp/Category.hh | 675 +++ .../log4cpp/include/log4cpp/CategoryStream.hh | 146 + .../log4cpp/include/log4cpp/Configurator.hh | 31 + .../log4cpp/DailyRollingFileAppender.hh | 45 + VrUtils/log4cpp/include/log4cpp/Export.hh | 30 + .../log4cpp/include/log4cpp/FactoryParams.hh | 158 + .../log4cpp/include/log4cpp/FileAppender.hh | 92 + VrUtils/log4cpp/include/log4cpp/Filter.hh | 119 + .../include/log4cpp/FixedContextCategory.hh | 174 + .../include/log4cpp/HierarchyMaintainer.hh | 59 + .../log4cpp/include/log4cpp/IdsaAppender.hh | 76 + VrUtils/log4cpp/include/log4cpp/Layout.hh | 39 + .../log4cpp/include/log4cpp/LayoutAppender.hh | 55 + .../log4cpp/include/log4cpp/LayoutsFactory.hh | 42 + .../log4cpp/include/log4cpp/LevelEvaluator.hh | 26 + .../log4cpp/include/log4cpp/LoggingEvent.hh | 73 + .../log4cpp/include/log4cpp/Manipulator.hh | 30 + VrUtils/log4cpp/include/log4cpp/NDC.hh | 181 + .../include/log4cpp/NTEventLogAppender.hh | 105 + .../include/log4cpp/OstreamAppender.hh | 38 + .../include/log4cpp/PassThroughLayout.hh | 22 + .../log4cpp/include/log4cpp/PatternLayout.hh | 104 + .../log4cpp/include/log4cpp/Portability.hh | 69 + VrUtils/log4cpp/include/log4cpp/Priority.hh | 110 + .../include/log4cpp/PropertyConfigurator.hh | 58 + .../include/log4cpp/RemoteSyslogAppender.hh | 139 + .../include/log4cpp/RollingFileAppender.hh | 48 + .../include/log4cpp/SimpleConfigurator.hh | 52 + .../log4cpp/include/log4cpp/SimpleLayout.hh | 34 + .../log4cpp/include/log4cpp/SmtpAppender.hh | 40 + .../include/log4cpp/StringQueueAppender.hh | 72 + .../log4cpp/include/log4cpp/SyslogAppender.hh | 76 + VrUtils/log4cpp/include/log4cpp/TimeStamp.hh | 74 + .../log4cpp/TriggeringEventEvaluator.hh | 23 + .../TriggeringEventEvaluatorFactory.hh | 41 + .../include/log4cpp/Win32DebugAppender.hh | 57 + .../log4cpp/include/log4cpp/config-MinGW32.h | 105 + .../log4cpp/include/log4cpp/config-openvms.h | 74 + .../log4cpp/config-win32-stlport-boost.h | 157 + .../log4cpp/include/log4cpp/config-win32.h | 174 + VrUtils/log4cpp/include/log4cpp/convenience.h | 104 + .../include/log4cpp/threading/BoostThreads.hh | 55 + .../include/log4cpp/threading/DummyThreads.hh | 67 + .../include/log4cpp/threading/MSThreads.hh | 161 + .../include/log4cpp/threading/OmniThreads.hh | 137 + .../include/log4cpp/threading/PThreads.hh | 125 + .../include/log4cpp/threading/Threading.hh | 37 + VrUtils/log4cpp/src/AbortAppender.cpp | 52 + VrUtils/log4cpp/src/Appender.cpp | 123 + VrUtils/log4cpp/src/AppenderSkeleton.cpp | 60 + VrUtils/log4cpp/src/AppendersFactory.cpp | 90 + VrUtils/log4cpp/src/BasicConfigurator.cpp | 35 + VrUtils/log4cpp/src/BasicLayout.cpp | 43 + VrUtils/log4cpp/src/BufferingAppender.cpp | 53 + VrUtils/log4cpp/src/Category.cpp | 430 ++ VrUtils/log4cpp/src/CategoryStream.cpp | 110 + VrUtils/log4cpp/src/Configurator.cpp | 20 + .../log4cpp/src/DailyRollingFileAppender.cpp | 186 + VrUtils/log4cpp/src/DllMain.cpp | 31 + VrUtils/log4cpp/src/DummyThreads.cpp | 22 + VrUtils/log4cpp/src/FactoryParams.cpp | 26 + VrUtils/log4cpp/src/FileAppender.cpp | 113 + VrUtils/log4cpp/src/Filter.cpp | 60 + VrUtils/log4cpp/src/FixedContextCategory.cpp | 100 + VrUtils/log4cpp/src/HierarchyMaintainer.cpp | 132 + VrUtils/log4cpp/src/IdsaAppender.cpp | 89 + VrUtils/log4cpp/src/LayoutAppender.cpp | 39 + VrUtils/log4cpp/src/LayoutsFactory.cpp | 57 + VrUtils/log4cpp/src/LevelEvaluator.cpp | 21 + VrUtils/log4cpp/src/Localtime.cpp | 37 + VrUtils/log4cpp/src/Localtime.hh | 17 + VrUtils/log4cpp/src/LoggingEvent.cpp | 26 + VrUtils/log4cpp/src/MSThreads.cpp | 23 + VrUtils/log4cpp/src/Manipulator.cpp | 22 + VrUtils/log4cpp/src/NDC.cpp | 128 + VrUtils/log4cpp/src/NTEventLogAppender.cpp | 165 + VrUtils/log4cpp/src/OmniThreads.cpp | 22 + VrUtils/log4cpp/src/OstreamAppender.cpp | 44 + VrUtils/log4cpp/src/PThreads.cpp | 36 + VrUtils/log4cpp/src/PassThroughLayout.cpp | 17 + VrUtils/log4cpp/src/PatternLayout.cpp | 438 ++ VrUtils/log4cpp/src/PortabilityImpl.cpp | 27 + VrUtils/log4cpp/src/PortabilityImpl.hh | 70 + VrUtils/log4cpp/src/Priority.cpp | 70 + VrUtils/log4cpp/src/Properties.cpp | 158 + VrUtils/log4cpp/src/Properties.hh | 38 + VrUtils/log4cpp/src/PropertyConfigurator.cpp | 20 + .../log4cpp/src/PropertyConfiguratorImpl.cpp | 370 ++ .../log4cpp/src/PropertyConfiguratorImpl.hh | 96 + VrUtils/log4cpp/src/RemoteSyslogAppender.cpp | 186 + VrUtils/log4cpp/src/RollingFileAppender.cpp | 111 + VrUtils/log4cpp/src/SimpleConfigurator.cpp | 232 ++ VrUtils/log4cpp/src/SimpleLayout.cpp | 42 + VrUtils/log4cpp/src/SmtpAppender.cpp | 186 + VrUtils/log4cpp/src/StringQueueAppender.cpp | 57 + VrUtils/log4cpp/src/StringUtil.cpp | 98 + VrUtils/log4cpp/src/StringUtil.hh | 84 + VrUtils/log4cpp/src/SyslogAppender.cpp | 88 + VrUtils/log4cpp/src/TimeStamp.cpp | 53 + .../src/TriggeringEventEvaluatorFactory.cpp | 50 + VrUtils/log4cpp/src/Win32DebugAppender.cpp | 56 + VrUtils/log4cpp/src/snprintf.c | 1025 +++++ VrUtils/resource.h | 14 + VrUtils/tinyxml2/tinyxml2.cpp | 2987 ++++++++++++++ VrUtils/tinyxml2/tinyxml2.h | 2380 +++++++++++ 211 files changed, 41215 insertions(+) create mode 100644 .gitignore create mode 100644 CloudUtils/CloudUtils.pro create mode 100644 CloudUtils/Inc/LaserDataLoader.h create mode 100644 CloudUtils/Inc/PointCloudImageUtils.h create mode 100644 CloudUtils/Src/LaserDataLoader.cpp create mode 100644 CloudUtils/Src/PointCloudImageUtils.cpp create mode 100644 CloudView/CloudView.pro create mode 100644 CloudView/Inc/CloudViewMainWindow.h create mode 100644 CloudView/Inc/PointCloudConverter.h create mode 100644 CloudView/Inc/PointCloudGLWidget.h create mode 100644 CloudView/Src/CloudViewMainWindow.cpp create mode 100644 CloudView/Src/PointCloudConverter.cpp create mode 100644 CloudView/Src/PointCloudGLWidget.cpp create mode 100644 CloudView/main.cpp create mode 100644 CloudView/poses.txt create mode 100644 CloudView/resource/logo.ico create mode 100644 CloudView/resource/logo.png create mode 100644 CloudView/resource/resource.qrc create mode 100644 CloudView/segments.txt create mode 100644 DataUtils/CloudMathClac/CloudMathClac.pro create mode 100644 DataUtils/CloudMathClac/Inc/CurveFitting.h create mode 100644 DataUtils/CloudMathClac/Src/CurveFitting.cpp create mode 100644 DataUtils/CoordinateTransform/CoordinateTransform.pro create mode 100644 DataUtils/CoordinateTransform/Inc/CoordinateTransform.h create mode 100644 DataUtils/CoordinateTransform/Inc/CoordinateTypes.h create mode 100644 DataUtils/CoordinateTransform/Src/CoordinateTransform.cpp create mode 100644 DataUtils/DataUtils.pro create mode 100644 MIGRATION.md create mode 100644 QUICKSTART.md create mode 100644 README.md create mode 100644 Utils.pro create mode 100644 VrCommon/Inc/VrCommon.h create mode 100644 VrCommon/Inc/VrConfigCmd.h create mode 100644 VrCommon/Inc/VrError.h create mode 100644 VrCommon/Src/VrCommon.cpp create mode 100644 VrCommon/VrCommon.pro create mode 100644 VrUtils/CMakeLists.txt create mode 100644 VrUtils/Inc/IVrUtils.h create mode 100644 VrUtils/Inc/VrAngleUtils.h create mode 100644 VrUtils/Inc/VrCodeFormatUtils.h create mode 100644 VrUtils/Inc/VrDateUtils.h create mode 100644 VrUtils/Inc/VrDebugTime.h create mode 100644 VrUtils/Inc/VrFileUtils.h create mode 100644 VrUtils/Inc/VrLog.h create mode 100644 VrUtils/Inc/VrMD5Utils.h create mode 100644 VrUtils/Inc/VrNTPUtils.h create mode 100644 VrUtils/Inc/VrNetUtils.h create mode 100644 VrUtils/Inc/VrNumUtils.h create mode 100644 VrUtils/Inc/VrShellUtils.h create mode 100644 VrUtils/Inc/VrStringUtils.h create mode 100644 VrUtils/Inc/VrTimeUtils.h create mode 100644 VrUtils/Inc/getopt.h create mode 100644 VrUtils/MD5/md5.cpp create mode 100644 VrUtils/MD5/md5.h create mode 100644 VrUtils/Src/VrAngleUtils.cpp create mode 100644 VrUtils/Src/VrCodeFormatUtils.cpp create mode 100644 VrUtils/Src/VrDateUtils.cpp create mode 100644 VrUtils/Src/VrFileUtils.cpp create mode 100644 VrUtils/Src/VrLog.cpp create mode 100644 VrUtils/Src/VrMD5Utils.cpp create mode 100644 VrUtils/Src/VrNTPUtils.cpp create mode 100644 VrUtils/Src/VrNetUtils.cpp create mode 100644 VrUtils/Src/VrNumUtils.cpp create mode 100644 VrUtils/Src/VrShellUtils.cpp create mode 100644 VrUtils/Src/VrStringUtils.cpp create mode 100644 VrUtils/Src/VrTimeUtils.cpp create mode 100644 VrUtils/Src/getopt.c create mode 100644 VrUtils/VrUtils.pro create mode 100644 VrUtils/crc/checksum.h create mode 100644 VrUtils/crc/src/crc16.c create mode 100644 VrUtils/crc/src/crc32.c create mode 100644 VrUtils/crc/src/crc64.c create mode 100644 VrUtils/crc/src/crc8.c create mode 100644 VrUtils/crc/src/crcccitt.c create mode 100644 VrUtils/crc/src/crcdnp.c create mode 100644 VrUtils/crc/src/crckrmit.c create mode 100644 VrUtils/crc/src/crcsick.c create mode 100644 VrUtils/crc/src/nmea-chk.c create mode 100644 VrUtils/ini/ConvertUTF.c create mode 100644 VrUtils/ini/ConvertUTF.h create mode 100644 VrUtils/ini/SimpleIni.h create mode 100644 VrUtils/jsoncpp/json/assertions.h create mode 100644 VrUtils/jsoncpp/json/autolink.h create mode 100644 VrUtils/jsoncpp/json/config.h create mode 100644 VrUtils/jsoncpp/json/features.h create mode 100644 VrUtils/jsoncpp/json/forwards.h create mode 100644 VrUtils/jsoncpp/json/json.h create mode 100644 VrUtils/jsoncpp/json/reader.h create mode 100644 VrUtils/jsoncpp/json/value.h create mode 100644 VrUtils/jsoncpp/json/version.h create mode 100644 VrUtils/jsoncpp/json/writer.h create mode 100644 VrUtils/jsoncpp/json_batchallocator.h create mode 100644 VrUtils/jsoncpp/json_internalarray.inl create mode 100644 VrUtils/jsoncpp/json_internalmap.inl create mode 100644 VrUtils/jsoncpp/json_reader.cpp create mode 100644 VrUtils/jsoncpp/json_tool.h create mode 100644 VrUtils/jsoncpp/json_value.cpp create mode 100644 VrUtils/jsoncpp/json_valueiterator.inl create mode 100644 VrUtils/jsoncpp/json_writer.cpp create mode 100644 VrUtils/log4cpp/include/log4cpp/AbortAppender.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/Appender.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/AppenderSkeleton.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/AppendersFactory.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/BasicConfigurator.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/BasicLayout.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/BufferingAppender.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/Category.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/CategoryStream.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/Configurator.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/DailyRollingFileAppender.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/Export.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/FactoryParams.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/FileAppender.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/Filter.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/FixedContextCategory.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/HierarchyMaintainer.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/IdsaAppender.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/Layout.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/LayoutAppender.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/LayoutsFactory.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/LevelEvaluator.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/LoggingEvent.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/Manipulator.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/NDC.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/NTEventLogAppender.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/OstreamAppender.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/PassThroughLayout.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/PatternLayout.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/Portability.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/Priority.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/PropertyConfigurator.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/RemoteSyslogAppender.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/RollingFileAppender.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/SimpleConfigurator.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/SimpleLayout.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/SmtpAppender.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/StringQueueAppender.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/SyslogAppender.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/TimeStamp.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/TriggeringEventEvaluator.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/TriggeringEventEvaluatorFactory.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/Win32DebugAppender.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/config-MinGW32.h create mode 100644 VrUtils/log4cpp/include/log4cpp/config-openvms.h create mode 100644 VrUtils/log4cpp/include/log4cpp/config-win32-stlport-boost.h create mode 100644 VrUtils/log4cpp/include/log4cpp/config-win32.h create mode 100644 VrUtils/log4cpp/include/log4cpp/convenience.h create mode 100644 VrUtils/log4cpp/include/log4cpp/threading/BoostThreads.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/threading/DummyThreads.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/threading/MSThreads.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/threading/OmniThreads.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/threading/PThreads.hh create mode 100644 VrUtils/log4cpp/include/log4cpp/threading/Threading.hh create mode 100644 VrUtils/log4cpp/src/AbortAppender.cpp create mode 100644 VrUtils/log4cpp/src/Appender.cpp create mode 100644 VrUtils/log4cpp/src/AppenderSkeleton.cpp create mode 100644 VrUtils/log4cpp/src/AppendersFactory.cpp create mode 100644 VrUtils/log4cpp/src/BasicConfigurator.cpp create mode 100644 VrUtils/log4cpp/src/BasicLayout.cpp create mode 100644 VrUtils/log4cpp/src/BufferingAppender.cpp create mode 100644 VrUtils/log4cpp/src/Category.cpp create mode 100644 VrUtils/log4cpp/src/CategoryStream.cpp create mode 100644 VrUtils/log4cpp/src/Configurator.cpp create mode 100644 VrUtils/log4cpp/src/DailyRollingFileAppender.cpp create mode 100644 VrUtils/log4cpp/src/DllMain.cpp create mode 100644 VrUtils/log4cpp/src/DummyThreads.cpp create mode 100644 VrUtils/log4cpp/src/FactoryParams.cpp create mode 100644 VrUtils/log4cpp/src/FileAppender.cpp create mode 100644 VrUtils/log4cpp/src/Filter.cpp create mode 100644 VrUtils/log4cpp/src/FixedContextCategory.cpp create mode 100644 VrUtils/log4cpp/src/HierarchyMaintainer.cpp create mode 100644 VrUtils/log4cpp/src/IdsaAppender.cpp create mode 100644 VrUtils/log4cpp/src/LayoutAppender.cpp create mode 100644 VrUtils/log4cpp/src/LayoutsFactory.cpp create mode 100644 VrUtils/log4cpp/src/LevelEvaluator.cpp create mode 100644 VrUtils/log4cpp/src/Localtime.cpp create mode 100644 VrUtils/log4cpp/src/Localtime.hh create mode 100644 VrUtils/log4cpp/src/LoggingEvent.cpp create mode 100644 VrUtils/log4cpp/src/MSThreads.cpp create mode 100644 VrUtils/log4cpp/src/Manipulator.cpp create mode 100644 VrUtils/log4cpp/src/NDC.cpp create mode 100644 VrUtils/log4cpp/src/NTEventLogAppender.cpp create mode 100644 VrUtils/log4cpp/src/OmniThreads.cpp create mode 100644 VrUtils/log4cpp/src/OstreamAppender.cpp create mode 100644 VrUtils/log4cpp/src/PThreads.cpp create mode 100644 VrUtils/log4cpp/src/PassThroughLayout.cpp create mode 100644 VrUtils/log4cpp/src/PatternLayout.cpp create mode 100644 VrUtils/log4cpp/src/PortabilityImpl.cpp create mode 100644 VrUtils/log4cpp/src/PortabilityImpl.hh create mode 100644 VrUtils/log4cpp/src/Priority.cpp create mode 100644 VrUtils/log4cpp/src/Properties.cpp create mode 100644 VrUtils/log4cpp/src/Properties.hh create mode 100644 VrUtils/log4cpp/src/PropertyConfigurator.cpp create mode 100644 VrUtils/log4cpp/src/PropertyConfiguratorImpl.cpp create mode 100644 VrUtils/log4cpp/src/PropertyConfiguratorImpl.hh create mode 100644 VrUtils/log4cpp/src/RemoteSyslogAppender.cpp create mode 100644 VrUtils/log4cpp/src/RollingFileAppender.cpp create mode 100644 VrUtils/log4cpp/src/SimpleConfigurator.cpp create mode 100644 VrUtils/log4cpp/src/SimpleLayout.cpp create mode 100644 VrUtils/log4cpp/src/SmtpAppender.cpp create mode 100644 VrUtils/log4cpp/src/StringQueueAppender.cpp create mode 100644 VrUtils/log4cpp/src/StringUtil.cpp create mode 100644 VrUtils/log4cpp/src/StringUtil.hh create mode 100644 VrUtils/log4cpp/src/SyslogAppender.cpp create mode 100644 VrUtils/log4cpp/src/TimeStamp.cpp create mode 100644 VrUtils/log4cpp/src/TriggeringEventEvaluatorFactory.cpp create mode 100644 VrUtils/log4cpp/src/Win32DebugAppender.cpp create mode 100644 VrUtils/log4cpp/src/snprintf.c create mode 100644 VrUtils/resource.h create mode 100644 VrUtils/tinyxml2/tinyxml2.cpp create mode 100644 VrUtils/tinyxml2/tinyxml2.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..24a2059 --- /dev/null +++ b/.gitignore @@ -0,0 +1,53 @@ +# Qt 编译产物 +*.o +*.obj +*.a +*.lib +*.so +*.dll +*.dylib +*.exe +*.app +*.user +*.pro.user +*.pro.user.* +*.qmake.stash +Makefile* +moc_*.cpp +moc_*.h +qrc_*.cpp +ui_*.h +*.autosave + +# 构建目录 +debug/ +release/ +Debug/ +Release/ +build/ +build-*/ +*-build-*/ + +# IDE 配置文件 +.vscode/ +.idea/ +*.creator +*.creator.user +*.files +*.includes +*.config +*.cflags +*.cxxflags + +# 临时文件 +*~ +*.swp +*.swo +*.tmp +*.bak +*.log + +# 系统文件 +.DS_Store +Thumbs.db +desktop.ini diff --git a/CloudUtils/CloudUtils.pro b/CloudUtils/CloudUtils.pro new file mode 100644 index 0000000..2fa47ab --- /dev/null +++ b/CloudUtils/CloudUtils.pro @@ -0,0 +1,35 @@ +# CloudUtils库项目配置文件 + +# 指定Qt模块 +QT += core + +# 指定项目模板为静态库 +TEMPLATE = lib +CONFIG += staticlib + +win32-msvc { + QMAKE_CXXFLAGS += /utf-8 +} + + +# 指定C++标准 +CONFIG += c++11 + +INCLUDEPATH += $$PWD/../../SDK/Device/VzNLSDK/_Inc +INCLUDEPATH += $$PWD/../../SDK/Device/VzNLSDK/Inc + +INCLUDEPATH += $$PWD/../VrCommon/Inc +INCLUDEPATH += $$PWD/../VrUtils/Inc + +# 指定包含路径 +INCLUDEPATH += ./Inc \ + ./Src + +# 指定头文件 +HEADERS += Inc/LaserDataLoader.h \ + Inc/PointCloudImageUtils.h + +# 指定源文件 +SOURCES += Src/LaserDataLoader.cpp \ + Src/PointCloudImageUtils.cpp + diff --git a/CloudUtils/Inc/LaserDataLoader.h b/CloudUtils/Inc/LaserDataLoader.h new file mode 100644 index 0000000..1430b9a --- /dev/null +++ b/CloudUtils/Inc/LaserDataLoader.h @@ -0,0 +1,74 @@ +#ifndef LASER_DATA_LOADER_H +#define LASER_DATA_LOADER_H + +#include +#include +#include "VZNL_Types.h" +#include "VrError.h" + +// 激光数据加载器类 +class LaserDataLoader +{ +public: + LaserDataLoader(); + ~LaserDataLoader(); + + // 从文件加载激光扫描数据 - 统一接口,根据文件格式自动判断数据类型 + int LoadLaserScanData(const std::string& fileName, + std::vector>& laserLines, + int& lineNum, + float& scanSpeed, + int& maxTimeStamp, + int& clockPerSecond); + + // 保存激光扫描数据到文件 - 统一接口,支持两种类型的数据 + int SaveLaserScanData(const std::string& fileName, + const std::vector>& laserLines, + int lineNum, + float scanSpeed, + int maxTimeStamp, + int clockPerSecond); + + int DebugSaveLaser(std::string fileName, std::vector> xyzData); + + // 释放统一格式数据内存 + void FreeLaserScanData(std::vector>& laserLines); + + // 释放转换后的SVzNL3DLaserLine数据内存 + void FreeConvertedData(std::vector& xyzData); + + // 释放转换后的SVzNLXYZRGBDLaserLine数据内存 + void FreeConvertedData(std::vector& rgbdData); + + // 转换统一格式数据为SVzNL3DLaserLine格式 + int ConvertToSVzNL3DLaserLine(const std::vector>& unifiedData, + std::vector& xyzData); + + // 转换统一格式数据为SVzNLXYZRGBDLaserLine格式 + int ConvertToSVzNLXYZRGBDLaserLine(const std::vector>& unifiedData, + std::vector& rgbdData); + + // 转换统一格式数据为std::vector>格式 + // validPointCount: 可选输出参数,返回有效点个数(xyz全非0的点) + int ConvertToSVzNL3DPosition(const std::vector>& unifiedData, + std::vector>& scanLines, + size_t* validPointCount = nullptr); + + // 获取最后的错误信息 + std::string GetLastError() const { return m_lastError; } + +private: + // 读取XYZ格式的激光数据 + int _ParseLaserScanPoint(const std::string& data, SVzNL3DPosition& sData, SVzNL2DPosition& s2DData); + + // 读取RGBD格式的激光数据 - 新增RGBD支持 + int _ParseLaserScanPoint(const std::string& data, SVzNLPointXYZRGBA& sData, SVzNL2DLRPoint& s2DData); + + // 获取激光数据类型 + int _GetLaserType(const std::string& fileName, EVzResultDataType& eDataType); + + std::string m_lastError; + static const int VZ_LASER_LINE_PT_MAX_NUM = 4096; +}; + +#endif // LASER_DATA_LOADER_H diff --git a/CloudUtils/Inc/PointCloudImageUtils.h b/CloudUtils/Inc/PointCloudImageUtils.h new file mode 100644 index 0000000..ed51139 --- /dev/null +++ b/CloudUtils/Inc/PointCloudImageUtils.h @@ -0,0 +1,127 @@ +#ifndef POINTCLOUDIMAGEUTILS_H +#define POINTCLOUDIMAGEUTILS_H + +#include +#include +#include +#include // for std::pair + +#include "VZNL_Types.h" + +// 简单的颗粒信息结构 - 避免依赖OpenCV +struct SimpleParticleInfo { + SVzNL3DPoint vertix[8]; // 8个顶点 + struct { + double width; + double height; + } size; +}; + +// 通用检测目标结构体 - 替代算法SDK的 SSG_peakRgnInfo +struct DetectionTargetInfo { + struct { + double x; + double y; + double z_yaw; // 偏航角(度) + } centerPos; + + struct { + double dWidth; + double dHeight; + } objSize; + + int orienFlag = 0; // 朝向标记: 0=无, 1=正面, 2=反面 +}; + +class PointCloudImageUtils +{ +public: + // 点云转图像 - 使用通用检测目标结构体 + static QImage GeneratePointCloudImage(SVzNL3DLaserLine* scanData, + int lineNum, + const std::vector& objOps); + + static QImage GeneratePointCloudImage(SVzNLXYZRGBDLaserLine* scanData, + int lineNum, + const std::vector& objOps); + + // 新的点云图像生成函数 - 基于X、Y范围创建图像 + static QImage GeneratePointCloudImage(SVzNLXYZRGBDLaserLine* scanData, + int lineNum); + + // LapWeld点云和检测结果转图像 - 基于scan lines格式 + static QImage GeneratePointCloudImage(const std::vector>& scanLines, + const std::vector>& weldResults, + int imageWidth = 800, int imageHeight = 600); + + // Workpiece点云和角点检测结果转图像 - 将角点画成圆点 + static QImage GeneratePointCloudRetPointImage(const std::vector>& scanLines, + const std::vector>& cornerPoints, + double margin = 0.0); + + // ParticleSize点云和颗粒检测结果转图像 - 生成带颗粒标记的点云图像 (直接返回QImage) + static QImage GeneratePointCloudImageWithParticles( + const std::vector>& scanLines, + const std::vector& particles, + int cameraIndex); + + // WheelMeasure点云和轮眉检测结果转图像 - 生成带轮眉标记的点云图像 + static QImage GenerateWheelArchImage( + const std::vector>& scanLines, + const SVzNL3DPoint& wheelArchPos, + const SVzNL3DPoint& wheelUpPos, + const SVzNL3DPoint& wheelDownPos, + double archToCenterHeight, + bool hasResult = true); + + // 从检测数据缓存生成深度图像 + static int GenerateDepthImage( + const std::vector>& detectionDataCache, + QImage& outImage); + + // 通道间距测量图像生成 - 根据算法标记的nPointIdx着色 + // 参考 HC_chanelSpaceMeasure_test.cpp 的 _outputRGBDScan_RGBD 函数 + // nPointIdx > 0: 使用预定义的8种颜色, 否则使用灰色 + static QImage GenerateChannelSpaceImage( + const std::vector>& scanLines, + int imageWidth = 1056, int imageHeight = 992); + +private: + // 定义线特征颜色和大小获取函数 + static void GetLineFeatureStyle(int vType, int hType, int objId, + QColor& pointColor, int& pointSize); + + // 获取对象颜色 + static QColor GetObjectColor(int index); + + // 计算点云范围 + static void CalculatePointCloudRange(SVzNL3DLaserLine* scanData, int lineNum, + double& xMin, double& xMax, + double& yMin, double& yMax); + + // 计算scan lines格式点云的范围 + static void CalculateScanLinesRange(const std::vector>& scanLines, + double& xMin, double& xMax, + double& yMin, double& yMax); + + // 绘制LapWeld检测结果 + static void DrawLapWeldResults(QPainter& painter, + const std::vector>& weldResults, + double xMin, double xMax, double yMin, double yMax, + int imageWidth, int imageHeight); + + // 绘制scan lines点云数据 + static void DrawScanLinesPointCloud(QPainter& painter, + const std::vector>& scanLines, + double xMin, double xMax, double yMin, double yMax, + int imageWidth, int imageHeight); + + // 绘制检测目标(使用通用结构体) + static void DrawDetectionTargets(QPainter& painter, + const std::vector& objOps, + double xMin, double xScale, int xSkip, + double yMin, double yScale, int ySkip, + int imgCols, int imgRows); +}; + +#endif // POINTCLOUDIMAGEUTILS_H diff --git a/CloudUtils/Src/LaserDataLoader.cpp b/CloudUtils/Src/LaserDataLoader.cpp new file mode 100644 index 0000000..d0fed29 --- /dev/null +++ b/CloudUtils/Src/LaserDataLoader.cpp @@ -0,0 +1,554 @@ +#include "LaserDataLoader.h" +#include +#include +#include +#include +#include +#include +#include +#include "VrLog.h" +#include + +// 辅助函数:去除字符串末尾的 \r 字符(处理 Windows 格式的 CR LF 换行符) +static inline void TrimCarriageReturn(std::string& str) +{ + if (!str.empty() && str.back() == '\r') { + str.pop_back(); + } +} + +LaserDataLoader::LaserDataLoader() +{ + m_lastError.clear(); +} + +LaserDataLoader::~LaserDataLoader() +{ +} + +int LaserDataLoader::LoadLaserScanData(const std::string& fileName, + std::vector>& laserLines, + int& lineNum, + float& scanSpeed, + int& maxTimeStamp, + int& clockPerSecond) +{ + LOG_INFO("Loading laser scan data from file: %s\n", fileName.c_str()); + + // 清空输出参数 + laserLines.clear(); + lineNum = 0; + scanSpeed = 0.0f; + maxTimeStamp = 0; + clockPerSecond = 0; + + // 判断文件类型 + std::ifstream inputFile(fileName); + if (!inputFile.is_open()) { + m_lastError = "Cannot open file: " + fileName; + LOG_ERROR("Cannot open file: %s\n", fileName.c_str()); + return ERR_CODE(FILE_ERR_NOEXIST); + } + + std::string line; + int result = SUCCESS; + + // 包含DataType字段,检查数据类型 + EVzResultDataType eDataType = keResultDataType_Invalid; + result = _GetLaserType(fileName, eDataType); + ERR_CODE_RETURN(result); + + LOG_INFO("Laser data type: %d \n", eDataType); + + SVzLaserLineData sLaserData; + memset(&sLaserData, 0, sizeof(SVzLaserLineData)); + + bool bFindLineNum = true; + int nLaserPointIdx = 0; + + while (std::getline(inputFile, line)) { + // 去除行末的 \r 字符(处理 Windows 格式的 CR LF 换行符) + TrimCarriageReturn(line); + + if (line.find("LineNum:") == 0) { + sscanf(line.c_str(), "LineNum:%d", &lineNum); + } else if (line.find("DataType:") == 0) { + + } else if (line.find("Line_") == 0) { + + if(false == bFindLineNum) { + laserLines.push_back(std::make_pair(eDataType, sLaserData)); + } + + int lineIndex; + unsigned int timeStamp; + int ptNum = 0; + sscanf(line.c_str(), "Line_%d_%u_%d", &lineIndex, &timeStamp, &ptNum); + + sLaserData.llFrameIdx = lineIndex; + sLaserData.llTimeStamp = timeStamp; + sLaserData.nPointCount = ptNum; + if (eDataType == keResultDataType_PointXYZRGBA) { + sLaserData.p3DPoint = new SVzNLPointXYZRGBA[ptNum]; + sLaserData.p2DPoint = new SVzNL2DLRPoint[ptNum]; + memset(sLaserData.p3DPoint, 0, sizeof(SVzNLPointXYZRGBA) * ptNum); + memset(sLaserData.p2DPoint, 0, sizeof(SVzNL2DLRPoint) * ptNum); + } else if(eDataType == keResultDataType_Position) { + sLaserData.p3DPoint = new SVzNL3DPosition[ptNum]; + sLaserData.p2DPoint = new SVzNL2DPosition[ptNum]; + memset(sLaserData.p3DPoint, 0, sizeof(SVzNL3DPosition) * ptNum); + memset(sLaserData.p2DPoint, 0, sizeof(SVzNL2DPosition) * ptNum); + } + nLaserPointIdx = 0; + bFindLineNum = false; + + } else if (line.find("{") == 0) { + // 使用正则表达式判断是XYZ还是RGBD格式 + // XYZ格式: {x,y,z}-{leftX,leftY}-{rightX,rightY} + // RGBD格式: {x,y,z,r,g,b}-{leftX,leftY}-{rightX,rightY} + + // 更精确的正则表达式,匹配完整的行格式 + if(sLaserData.p3DPoint == nullptr || sLaserData.p2DPoint == nullptr) { + LOG_ERROR("sLaserData.p3DPoint == nullptr || sLaserData.p2DPoint == nullptr \n"); + return ERR_CODE(DATA_ERR_INVALID); + } + + if (eDataType == keResultDataType_PointXYZRGBA) { + SVzNLPointXYZRGBA* pRGBAPoints = static_cast(sLaserData.p3DPoint); + SVzNL2DLRPoint* p2DPoints = static_cast(sLaserData.p2DPoint); + _ParseLaserScanPoint(line, pRGBAPoints[nLaserPointIdx], p2DPoints[nLaserPointIdx]); + nLaserPointIdx++; + } else { + SVzNL3DPosition* p3DPoints = static_cast(sLaserData.p3DPoint); + SVzNL2DPosition* p2DPoints = static_cast(sLaserData.p2DPoint); + _ParseLaserScanPoint(line, p3DPoints[nLaserPointIdx], p2DPoints[nLaserPointIdx]); + p3DPoints[nLaserPointIdx].nPointIdx = nLaserPointIdx; + nLaserPointIdx++; + } + } + } + + // 添加最后一条扫描线数据 + if (!bFindLineNum) { + laserLines.push_back(std::make_pair(eDataType, sLaserData)); + } + + inputFile.close(); + LOG_INFO("Successfully loaded %d laser scan lines from file: %s\n", lineNum, fileName.c_str()); + return SUCCESS; +} + +// 保存激光扫描数据到文件 - 统一接口,支持两种类型的数据 +int LaserDataLoader::SaveLaserScanData(const std::string& fileName, + const std::vector>& laserLines, + int lineNum, + float scanSpeed, + int maxTimeStamp, + int clockPerSecond) +{ + LOG_INFO("Saving unified laser scan data to file: %s\n", fileName.c_str()); + + if (laserLines.empty() || lineNum <= 0) { + m_lastError = "Invalid input parameters for saving unified data"; + LOG_ERROR("Invalid parameters for saving unified laser data\n"); + return ERR_CODE(DEV_ARG_INVAILD); + } + + try { + std::ofstream sw(fileName); + if (!sw.is_open()) { + m_lastError = "Cannot open file for writing: " + fileName; + LOG_ERROR("Cannot open file for writing: %s\n", fileName.c_str()); + return ERR_CODE(FILE_ERR_WRITE); + } + + // 写入文件头 + sw << "LineNum:" << lineNum << std::endl; + sw << "DataType: 0" << std::endl; + sw << "ScanSpeed:" << scanSpeed << std::endl; + sw << "PointAdjust: 1" << std::endl; + sw << "MaxTimeStamp:" << maxTimeStamp << "_" << clockPerSecond << std::endl; + + // 写入每条扫描线数据 + for (const auto& linePair : laserLines) { + + EVzResultDataType dataType = linePair.first; + const SVzLaserLineData& lineData = linePair.second; + + sw << "Line_" << lineData.llFrameIdx << "_" << lineData.llTimeStamp << "_" << lineData.nPointCount << std::endl; + + // 根据数据类型写入点云数据 + if (dataType == keResultDataType_Position && lineData.p3DPoint) { + // 写入XYZ格式数据 + const SVzNL3DPosition* points = static_cast(lineData.p3DPoint); + const SVzNL2DPosition* points2D = static_cast(lineData.p2DPoint); + for (int i = 0; i < lineData.nPointCount; i++) { + float x = static_cast(points[i].pt3D.x); + float y = static_cast(points[i].pt3D.y); + float z = static_cast(points[i].pt3D.z); + sw << "{ " << std::fixed << std::setprecision(6) << x << "," << y << "," << z << " }-"; + sw << "{ " << points2D[i].ptLeft2D.x << "," << points2D[i].ptLeft2D.y << " }-"; + sw << "{ " << points2D[i].ptRight2D.x << "," << points2D[i].ptRight2D.y << " }" << std::endl; + } + } else if (dataType == keResultDataType_PointXYZRGBA && lineData.p3DPoint) { + // 写入RGBD格式数据 + const SVzNLPointXYZRGBA* points = static_cast(lineData.p3DPoint); + const SVzNL2DLRPoint* points2D = static_cast(lineData.p2DPoint); + for (int i = 0; i < lineData.nPointCount; i++) { + float x = static_cast(points[i].x); + float y = static_cast(points[i].y); + float z = static_cast(points[i].z); + int r = (points[i].nRGB >> 16) & 0xFF; + int g = (points[i].nRGB >> 8) & 0xFF; + int b = points[i].nRGB & 0xFF; + + sw << "{" << std::fixed << std::setprecision(6) << x << "," + << std::fixed << std::setprecision(6) << y << "," + << std::fixed << std::setprecision(6) << z << "," + << std::fixed << std::setprecision(6) << b * 1.0f / 255 << "," + << std::fixed << std::setprecision(6) << g * 1.0f / 255 << "," + << std::fixed << std::setprecision(6) << r * 1.0f / 255 << "}-"; + sw << "{ " << points2D[i].sLeft.x << "," << points2D[i].sLeft.y << " }-"; + sw << "{ " << points2D[i].sRight.x << "," << points2D[i].sRight.y << " }" << std::endl; + } + } + } + + sw.close(); + return SUCCESS; + + } catch (const std::exception& e) { + m_lastError = "Error saving unified file: " + std::string(e.what()); + LOG_ERROR("Error saving unified laser data to file: %s\n", e.what()); + return ERR_CODE(FILE_ERR_WRITE); + } +} + +int LaserDataLoader::DebugSaveLaser(std::string fileName, std::vector > xyzData) +{ + LOG_INFO("Saving unified laser scan data to file: %s\n", fileName.c_str()); + + if (xyzData.empty()) { + m_lastError = "Invalid input parameters for saving unified data"; + LOG_ERROR("Invalid parameters for saving unified laser data\n"); + return ERR_CODE(DEV_ARG_INVAILD); + } + + try { + std::ofstream sw(fileName); + if (!sw.is_open()) { + m_lastError = "Cannot open file for writing: " + fileName; + LOG_ERROR("Cannot open file for writing: %s\n", fileName.c_str()); + return ERR_CODE(FILE_ERR_WRITE); + } + + // 写入文件头 + sw << "LineNum:" << xyzData.size() << std::endl; + sw << "DataType: 0" << std::endl; + sw << "ScanSpeed:" << 0 << std::endl; + sw << "PointAdjust: 1" << std::endl; + sw << "MaxTimeStamp:" << 0 << "_" << 0 << std::endl; + + int index = 0; + // 写入每条扫描线数据 + for (const auto& linePair : xyzData) { + sw << "Line_" << index++ << "_" << 0 << "_" << linePair.size() << std::endl; + // 根据数据类型写入点云数据 + for(const auto& point : linePair){ + + // 写入XYZ格式数据 + float x = static_cast(point.pt3D.x); + float y = static_cast(point.pt3D.y); + float z = static_cast(point.pt3D.z); + sw << "{ " + << std::fixed << std::setprecision(6) << x << ", " + << std::fixed << std::setprecision(6) << y << ", " + << std::fixed << std::setprecision(6) << z << " } - "; + sw << "{ 0, 0 } - { 0, 0 }" << std::endl; + } + } + + sw.close(); + return SUCCESS; + + } catch (const std::exception& e) { + m_lastError = "Error saving unified file: " + std::string(e.what()); + LOG_ERROR("Error saving unified laser data to file: %s\n", e.what()); + return ERR_CODE(FILE_ERR_WRITE); + } +} + +void LaserDataLoader::FreeLaserScanData(std::vector>& laserLines) +{ + LOG_DEBUG("Freeing unified laser scan data, line count: %zu\n", laserLines.size()); + + if (!laserLines.empty()) { + for (auto& linePair : laserLines) { + EVzResultDataType dataType = linePair.first; + SVzLaserLineData& lineData = linePair.second; + + if (lineData.p3DPoint) { + delete[] lineData.p3DPoint; + lineData.p3DPoint = nullptr; + } + + if (lineData.p2DPoint) { + delete[] lineData.p2DPoint; + lineData.p2DPoint = nullptr; + } + } + laserLines.clear(); + } + + LOG_DEBUG("Unified laser scan data freed successfully\n"); +} + +// 转换统一格式数据为std::vector>格式 +int LaserDataLoader::ConvertToSVzNL3DPosition(const std::vector>& unifiedData, + std::vector>& scanLines, + size_t* validPointCount) +{ + LOG_DEBUG("Converting unified data to scan lines format\n"); + + scanLines.clear(); + + // 初始化有效点计数 + size_t validCount = 0; + + // 浮点数零值判断阈值 + const double EPSILON = 1e-6; + + for (const auto& linePair : unifiedData) { + EVzResultDataType dataType = linePair.first; + const SVzLaserLineData& lineData = linePair.second; + + // 只处理Position类型的数据 + if (dataType == keResultDataType_Position && lineData.p3DPoint) { + std::vector scanLine; + + // 为当前扫描线分配空间 + scanLine.reserve(lineData.nPointCount); + + // 复制点数据并统计有效点 + const SVzNL3DPosition* points = static_cast(lineData.p3DPoint); + for (int i = 0; i < lineData.nPointCount; i++) { + scanLine.push_back(points[i]); + + // 统计有效点(xyz全非0,使用epsilon阈值判断) + if (std::fabs(points[i].pt3D.x) > EPSILON && + std::fabs(points[i].pt3D.y) > EPSILON && + std::fabs(points[i].pt3D.z) > EPSILON) { + validCount++; + } + } + + scanLines.push_back(scanLine); + } + } + + // 返回有效点计数(如果调用者需要) + if (validPointCount) { + *validPointCount = validCount; + } + + LOG_DEBUG("Converted %zu lines to scan lines format, valid points: %zu\n", scanLines.size(), validCount); + return SUCCESS; +} + +// 转换统一格式数据为SVzNL3DLaserLine格式 +int LaserDataLoader::ConvertToSVzNL3DLaserLine(const std::vector>& unifiedData, + std::vector& xyzData) +{ + xyzData.clear(); + + for (const auto& linePair : unifiedData) { + EVzResultDataType dataType = linePair.first; + const SVzLaserLineData& lineData = linePair.second; + + // 只处理Position类型的数据 + if (dataType == keResultDataType_Position && lineData.p3DPoint) { + SVzNL3DLaserLine xyzLine; + xyzLine.nTimeStamp = lineData.llTimeStamp; + xyzLine.nPositionCnt = lineData.nPointCount; + + if (lineData.nPointCount > 0) { + xyzLine.p3DPosition = new SVzNL3DPosition[lineData.nPointCount]; + if (xyzLine.p3DPosition) { + memcpy(xyzLine.p3DPosition, lineData.p3DPoint, sizeof(SVzNL3DPosition) * lineData.nPointCount); + } else { + m_lastError = "Memory allocation failed for SVzNL3DPosition"; + LOG_ERROR("Memory allocation failed for SVzNL3DPosition\n"); + return ERR_CODE(DATA_ERR_INVALID); + } + } else { + xyzLine.p3DPosition = nullptr; + } + + xyzData.push_back(xyzLine); + } + } + + LOG_DEBUG("Converted %zu lines to SVzNL3DLaserLine format\n", xyzData.size()); + return SUCCESS; +} + +// 转换统一格式数据为SVzNLXYZRGBDLaserLine格式 +int LaserDataLoader::ConvertToSVzNLXYZRGBDLaserLine(const std::vector>& unifiedData, + std::vector& rgbdData) +{ + LOG_DEBUG("Converting unified data to SVzNLXYZRGBDLaserLine format\n"); + + rgbdData.clear(); + + for (const auto& linePair : unifiedData) { + EVzResultDataType dataType = linePair.first; + const SVzLaserLineData& lineData = linePair.second; + + // 只处理PointXYZRGBA类型的数据 + if (dataType == keResultDataType_PointXYZRGBA && lineData.p3DPoint) { + SVzNLXYZRGBDLaserLine rgbdLine; + rgbdLine.nTimeStamp = lineData.llTimeStamp; + rgbdLine.nPointCnt = lineData.nPointCount; + + if (lineData.nPointCount > 0) { + rgbdLine.p3DPoint = new SVzNLPointXYZRGBA[lineData.nPointCount]; + if (rgbdLine.p3DPoint) { + memcpy(rgbdLine.p3DPoint, lineData.p3DPoint, sizeof(SVzNLPointXYZRGBA) * lineData.nPointCount); + } else { + m_lastError = "Memory allocation failed for SVzNLPointXYZRGBA"; + LOG_ERROR("Memory allocation failed for SVzNLPointXYZRGBA\n"); + return ERR_CODE(DATA_ERR_INVALID); + } + } else { + rgbdLine.p3DPoint = nullptr; + } + + rgbdData.push_back(rgbdLine); + } + } + + LOG_DEBUG("Converted %zu lines to SVzNLXYZRGBDLaserLine format\n", rgbdData.size()); + return SUCCESS; +} + +// 释放转换后的SVzNL3DLaserLine数据内存 +void LaserDataLoader::FreeConvertedData(std::vector& xyzData) +{ + LOG_DEBUG("Freeing converted SVzNL3DLaserLine data, line count: %zu\n", xyzData.size()); + + if (!xyzData.empty()) { + for (auto& scanLine : xyzData) { + if (scanLine.p3DPosition) { + delete[] scanLine.p3DPosition; + scanLine.p3DPosition = nullptr; + } + } + xyzData.clear(); + } + + LOG_DEBUG("Converted SVzNL3DLaserLine data freed successfully\n"); +} + +// 释放转换后的SVzNLXYZRGBDLaserLine数据内存 +void LaserDataLoader::FreeConvertedData(std::vector& rgbdData) +{ + LOG_DEBUG("Freeing converted SVzNLXYZRGBDLaserLine data, line count: %zu\n", rgbdData.size()); + + if (!rgbdData.empty()) { + for (auto& scanLine : rgbdData) { + if (scanLine.p3DPoint) { + delete[] scanLine.p3DPoint; + scanLine.p3DPoint = nullptr; + } + } + rgbdData.clear(); + } + + LOG_DEBUG("Converted SVzNLXYZRGBDLaserLine data freed successfully\n"); +} + + +int LaserDataLoader::_ParseLaserScanPoint(const std::string& data, SVzNL3DPosition& sData, SVzNL2DPosition& s2DData) +{ + float X, Y, Z; + float leftX, leftY; + float rightX, rightY; + sscanf(data.c_str(), "{%f,%f,%f}-{%f,%f}-{%f,%f}", &X, &Y, &Z, &leftX, &leftY, &rightX, &rightY); + sData.pt3D.x = X; + sData.pt3D.y = Y; + sData.pt3D.z = Z; + s2DData.ptLeft2D.x = leftX; + s2DData.ptLeft2D.y = leftY; + s2DData.ptRight2D.x = rightX; + s2DData.ptRight2D.y = rightY; + return SUCCESS; +} + +int LaserDataLoader::_ParseLaserScanPoint(const std::string& data, SVzNLPointXYZRGBA& sData, SVzNL2DLRPoint& s2DData) +{ + float X, Y, Z; + float r, g, b; + float leftX, leftY; + float rightX, rightY; + sscanf(data.c_str(), "{%f,%f,%f,%f,%f,%f}-{%f,%f}-{%f,%f}", &X, &Y, &Z, &r, &g, &b, &leftX, &leftY, &rightX, &rightY); + sData.x = X; + sData.y = Y; + sData.z = Z; + + int nr = (int)(r * 255); + int ng = (int)(g * 255); + int nb = (int)(b * 255); + nb <<= 8; + nb += ng; + nb <<= 8; + nb += nr; + sData.nRGB = nb; + + s2DData.sLeft.x = leftX; + s2DData.sLeft.y = leftY; + s2DData.sRight.x = rightX; + s2DData.sRight.y = rightY; + return SUCCESS; +} + +// 获取激光数据类型 +int LaserDataLoader::_GetLaserType(const std::string& fileName, EVzResultDataType& eDataType) +{ + std::ifstream inputFile(fileName); + std::string linedata; + + if (!inputFile.is_open()) { + m_lastError = "Cannot open file: " + fileName; + return ERR_CODE(FILE_ERR_NOEXIST); + } + + bool bFind = false; + while (std::getline(inputFile, linedata)) { + // 去除行末的 \r 字符(处理 Windows 格式的 CR LF 换行符) + TrimCarriageReturn(linedata); + + if (linedata.find("{") == 0) { + // 修复正则表达式以匹配实际数据格式 + // XYZ格式: {x,y,z}-{leftX,leftY}-{rightX,rightY} + // RGBD格式: {x,y,z,r,g,b}-{leftX,leftY}-{rightX,rightY} + // 更宽松的正则表达式,允许更多的空格变化 + std::regex xyzPattern(R"(\{\s*[+-]?(?:\d+\.?\d*|\.\d+)\s*,\s*[+-]?(?:\d+\.?\d*|\.\d+)\s*,\s*[+-]?(?:\d+\.?\d*|\.\d+)\s*\}\s*-\s*\{\s*[+-]?(?:\d+\.?\d*|\.\d+)\s*,\s*[+-]?(?:\d+\.?\d*|\.\d+)\s*\}\s*-\s*\{\s*[+-]?(?:\d+\.?\d*|\.\d+)\s*,\s*[+-]?(?:\d+\.?\d*|\.\d+)\s*\})"); + std::regex rgbdPattern(R"(\{\s*[+-]?(?:\d+\.?\d*|\.\d+)\s*,\s*[+-]?(?:\d+\.?\d*|\.\d+)\s*,\s*[+-]?(?:\d+\.?\d*|\.\d+)\s*,\s*[+-]?(?:\d+\.?\d*|\.\d+)\s*,\s*[+-]?(?:\d+\.?\d*|\.\d+)\s*,\s*[+-]?(?:\d+\.?\d*|\.\d+)\s*\}\s*-\s*\{\s*[+-]?(?:\d+\.?\d*|\.\d+)\s*,\s*[+-]?(?:\d+\.?\d*|\.\d+)\s*\}\s*-\s*\{\s*[+-]?(?:\d+\.?\d*|\.\d+)\s*,\s*[+-]?(?:\d+\.?\d*|\.\d+)\s*\})"); + + // 先尝试匹配RGBD格式(6个数字) + if (std::regex_match(linedata, rgbdPattern)) { + eDataType = keResultDataType_PointXYZRGBA; + bFind = true; + } + // 再尝试匹配XYZ格式(3个数字) + else if (std::regex_match(linedata, xyzPattern)) { + eDataType = keResultDataType_Position; + bFind = true; + } + break; + } + } + + inputFile.close(); + return bFind ? SUCCESS : ERR_CODE(FILE_ERR_FORMAT); +} diff --git a/CloudUtils/Src/PointCloudImageUtils.cpp b/CloudUtils/Src/PointCloudImageUtils.cpp new file mode 100644 index 0000000..c2dbb1e --- /dev/null +++ b/CloudUtils/Src/PointCloudImageUtils.cpp @@ -0,0 +1,1333 @@ +#include "PointCloudImageUtils.h" +#include +#include +#include +#include +#include "VrLog.h" + +#ifndef PI +#define PI 3.14159265358979323846 +#endif + +// 线特征类型定义 - 用于点云可视化颜色区分 +#ifndef LINE_FEATURE_UNDEF +#define LINE_FEATURE_UNDEF 0 +#define LINE_FEATURE_L_JUMP_H2L 1 +#define LINE_FEATURE_L_JUMP_L2H 2 +#define LINE_FEATURE_L_SLOPE_H2L 3 +#define LINE_FEATURE_L_SLOPE_L2H 4 +#define LINE_FEATURE_V_SLOPE 5 +#define LINE_FEATURE_LINE_ENDING_0 6 +#define LINE_FEATURE_LINE_ENDING_1 7 +#endif + +QImage PointCloudImageUtils::GeneratePointCloudImage(SVzNL3DLaserLine* scanData, + int lineNum, + const std::vector& objOps) +{ + if (!scanData || lineNum <= 0) { + return QImage(); // 返回空图像 + } + + // 统计X和Y的范围 + double xMin = 0.0, xMax = -1.0; + double yMin = 0.0, yMax = -1.0; + CalculatePointCloudRange(scanData, lineNum, xMin, xMax, yMin, yMax); + + // 检查范围是否有效 + if (xMax < xMin || yMax < yMin) { + return QImage(); // 返回空图像 + } + + // 创建图像 + int imgRows = 992; + int imgCols = 1056; + int x_skip = 16; + int y_skip = 16; + + // 计算投影比例 + double y_rows = (double)(imgRows - y_skip * 2); + double x_cols = (double)(imgCols - x_skip * 2); + double x_scale = (xMax - xMin) / x_cols; + double y_scale = (yMax - yMin) / y_rows; + if (x_scale < y_scale) + x_scale = y_scale; + else + y_scale = x_scale; + + QImage image(imgCols, imgRows, QImage::Format_RGB888); + image.fill(Qt::black); + QPainter painter(&image); + + // 绘制点云 + for (int line = 0; line < lineNum; line++) { + for (int i = 0; i < scanData[line].nPositionCnt; i++) { + SVzNL3DPosition* pt3D = &scanData[line].p3DPosition[i]; + if (pt3D->pt3D.z < 1e-4) continue; + + // 解析点索引信息 + int vType = pt3D->nPointIdx & 0xff; + int hType = vType >> 4; + int objId = (pt3D->nPointIdx >> 16) & 0xff; + vType = vType & 0x0f; + + // 根据线特征类型确定颜色和大小 + QColor pointColor; + int pointSize = 1; + GetLineFeatureStyle(vType, hType, objId, pointColor, pointSize); + + int px = (int)((pt3D->pt3D.x - xMin) / x_scale + x_skip); + int py = (int)((pt3D->pt3D.y - yMin) / y_scale + y_skip); + + if (px >= 0 && px < imgCols && py >= 0 && py < imgRows) { + painter.setPen(QPen(pointColor, 1)); + painter.drawPoint(px, py); + } + } + } + + // 绘制检测目标和方向线 + DrawDetectionTargets(painter, objOps, xMin, x_scale, x_skip, yMin, y_scale, y_skip, imgCols, imgRows); + + return image; +} + +QImage PointCloudImageUtils::GeneratePointCloudImage(SVzNLXYZRGBDLaserLine* scanData, + int lineNum, + const std::vector& objOps) +{ + int imgRows = 992; + int imgCols = 1056; + QImage image(imgCols, imgRows, QImage::Format_RGB888); + image.fill(Qt::black); + + if (!scanData || lineNum <= 0) { + return image; // 返回空图像 + } + + // 统计X和Y的范围 + double xMin = 0.0, xMax = -1.0; + double yMin = 0.0, yMax = -1.0; + + for (int line = 0; line < lineNum; line++) { + for (int i = 0; i < scanData[line].nPointCnt; i++) { + SVzNLPointXYZRGBA* pt3D = &scanData[line].p3DPoint[i]; + if (pt3D->z < 1e-4) continue; + + // 更新X范围 + if (xMax < xMin) { + xMin = xMax = pt3D->x; + } else { + if (xMin > pt3D->x) xMin = pt3D->x; + if (xMax < pt3D->x) xMax = pt3D->x; + } + + // 更新Y范围 + if (yMax < yMin) { + yMin = yMax = pt3D->y; + } else { + if (yMin > pt3D->y) yMin = pt3D->y; + if (yMax < pt3D->y) yMax = pt3D->y; + } + } + } + + // 检查范围是否有效 + if (xMax < xMin || yMax < yMin) { + return image; // 返回空图像 + } + + // 创建图像 + int x_skip = 16; + int y_skip = 16; + double y_rows = (double)(imgRows - y_skip * 2); + double x_cols = (double)(imgCols - x_skip * 2); + + // 计算投影比例 + double x_scale = (xMax - xMin) / x_cols; + double y_scale = (yMax - yMin) / y_rows; + if (x_scale < y_scale) + x_scale = y_scale; + else + y_scale = x_scale; + + QPainter painter(&image); + + // 绘制点云 + for (int line = 0; line < lineNum; line++) { + for (int i = 0; i < scanData[line].nPointCnt; i++) { + SVzNLPointXYZRGBA* pt3D = &scanData[line].p3DPoint[i]; + if (pt3D->z < 1e-4) continue; + + // 解析RGB颜色 + int nRGB = pt3D->nRGB; + int r = nRGB & 0xff; + nRGB >>= 8; + int g = nRGB & 0xff; + nRGB >>= 8; + int b = nRGB & 0xff; + + QColor pointColor(r, g, b); + + int px = (int)((pt3D->x - xMin) / x_scale + x_skip); + int py = (int)((pt3D->y - yMin) / y_scale + y_skip); + + if (px >= 0 && px < imgCols && py >= 0 && py < imgRows) { + painter.setPen(QPen(pointColor, 1)); + painter.drawPoint(px, py); + } + } + } + + // 绘制检测目标和方向线 + DrawDetectionTargets(painter, objOps, xMin, x_scale, x_skip, yMin, y_scale, y_skip, imgCols, imgRows); + + return image; +} + +// 新的点云图像生成函数 - 基于X、Y范围创建图像 +QImage PointCloudImageUtils::GeneratePointCloudImage(SVzNLXYZRGBDLaserLine* scanData, int lineNum) +{ + if (!scanData || lineNum <= 0) { + return QImage(); + } + + // 1. 遍历X, Y的范围,计算最小和最大值 + double xMin = std::numeric_limits::max(); + double xMax = std::numeric_limits::lowest(); + double yMin = std::numeric_limits::max(); + double yMax = std::numeric_limits::lowest(); + + for (int i = 0; i < lineNum; i++) { + if (scanData[i].p3DPoint && scanData[i].nPointCnt > 0) { + for (int j = 0; j < scanData[i].nPointCnt; j++) { + const SVzNLPointXYZRGBA& point = scanData[i].p3DPoint[j]; + + // 更新X范围 + if (point.x < xMin) xMin = point.x; + if (point.x > xMax) xMax = point.x; + + // 更新Y范围 + if (point.y < yMin) yMin = point.y; + if (point.y > yMax) yMax = point.y; + } + } + } + + // 检查范围是否有效 + if (xMin >= xMax || yMin >= yMax) { + return QImage(); + } + + // 2. 根据范围创建图像大小 + // 设置图像分辨率,可以根据需要调整 + const int imageWidth = xMax - xMin; + const int imageHeight = yMax - yMin; + + + // 3. 创建默认黑底图像 + QImage image(imageWidth, imageHeight, QImage::Format_RGB888); + image.fill(Qt::black); + + // 4. 遍历点云数据,将3D X, Y对应的颜色值赋值给图像 + for (int i = 0; i < lineNum; i++) { + if (scanData[i].p3DPoint && scanData[i].nPointCnt > 0) { + for (int j = 0; j < scanData[i].nPointCnt; j++) { + const SVzNLPointXYZRGBA& point = scanData[i].p3DPoint[j]; + + // 将3D坐标转换为图像坐标 + int imageX = static_cast((point.x - xMin)); + int imageY = static_cast((point.y - yMin)); + + // 确保坐标在图像范围内 + if (imageX >= 0 && imageX < imageWidth && + imageY >= 0 && imageY < imageHeight) { + + // 解析RGB颜色 - 参考_RGBDto2DImage函数的解析方式 + int nRGB = point.nRGB; + int r = nRGB & 0xff; + nRGB >>= 8; + int g = nRGB & 0xff; + nRGB >>= 8; + int b = nRGB & 0xff; + + QColor pointColor(r, g, b); + // 设置图像像素颜色 + image.setPixel(imageX, imageY, pointColor.rgb()); + } + } + } + } + + return image; +} + +void PointCloudImageUtils::GetLineFeatureStyle(int vType, int hType, int objId, + QColor& pointColor, int& pointSize) +{ + pointSize = 1; + + // 优先根据垂直方向特征设置颜色 + if (LINE_FEATURE_L_JUMP_H2L == vType) { + pointColor = QColor(255, 97, 0); // 橙色 + pointSize = 2; + } + else if (LINE_FEATURE_L_JUMP_L2H == vType) { + pointColor = QColor(255, 255, 0); // 黄色 + pointSize = 2; + } + else if (LINE_FEATURE_V_SLOPE == vType) { + pointColor = QColor(255, 0, 255); // 紫色 + pointSize = 2; + } + else if (LINE_FEATURE_L_SLOPE_H2L == vType) { + pointColor = QColor(160, 82, 45); // 褐色 + pointSize = 2; + } + else if ((LINE_FEATURE_LINE_ENDING_0 == vType) || (LINE_FEATURE_LINE_ENDING_1 == vType)) { + pointColor = QColor(255, 0, 0); // 红色 + pointSize = 2; + } + else if (LINE_FEATURE_L_SLOPE_L2H == vType) { + pointColor = QColor(233, 150, 122); // 浅褐色 + pointSize = 2; + } + // 检查水平方向特征 + else if (LINE_FEATURE_L_JUMP_H2L == hType) { + pointColor = QColor(0, 0, 255); // 蓝色 + pointSize = 2; + } + else if (LINE_FEATURE_L_JUMP_L2H == hType) { + pointColor = QColor(0, 255, 255); // 青色 + pointSize = 2; + } + else if (LINE_FEATURE_V_SLOPE == hType) { + pointColor = QColor(0, 255, 0); // 绿色 + pointSize = 2; + } + else if (LINE_FEATURE_L_SLOPE_H2L == hType) { + pointColor = QColor(85, 107, 47); // 橄榄绿 + pointSize = 2; + } + else if (LINE_FEATURE_L_SLOPE_L2H == hType) { + pointColor = QColor(0, 255, 154); // 浅绿色 + pointSize = 2; + } + else if ((LINE_FEATURE_LINE_ENDING_0 == hType) || (LINE_FEATURE_LINE_ENDING_1 == hType)) { + pointColor = QColor(255, 0, 0); // 红色 + pointSize = 3; + } + // 检查是否为目标对象 + else if (objId > 0) { + pointColor = GetObjectColor(objId); + pointSize = 1; + } + // 默认颜色 + else { + pointColor = QColor(150, 150, 150); // 深灰色 + pointSize = 1; + } +} + +QColor PointCloudImageUtils::GetObjectColor(int index) +{ + QColor objColors[8] = { + QColor(245,222,179), QColor(210,105,30), QColor(240,230,140), QColor(135,206,235), + QColor(250,235,215), QColor(189,252,201), QColor(221,160,221), QColor(188,143,143) + }; + + return objColors[index % 8]; +} + +void PointCloudImageUtils::CalculatePointCloudRange(SVzNL3DLaserLine* scanData, int lineNum, + double& xMin, double& xMax, + double& yMin, double& yMax) +{ + xMin = 0.0; xMax = -1.0; + yMin = 0.0; yMax = -1.0; + + for (int line = 0; line < lineNum; line++) { + for (int i = 0; i < scanData[line].nPositionCnt; i++) { + SVzNL3DPosition* pt3D = &scanData[line].p3DPosition[i]; + if (pt3D->pt3D.z < 1e-4) continue; + + // 更新X范围 + if (xMax < xMin) { + xMin = xMax = pt3D->pt3D.x; + } else { + if (xMin > pt3D->pt3D.x) xMin = pt3D->pt3D.x; + if (xMax < pt3D->pt3D.x) xMax = pt3D->pt3D.x; + } + + // 更新Y范围 + if (yMax < yMin) { + yMin = yMax = pt3D->pt3D.y; + } else { + if (yMin > pt3D->pt3D.y) yMin = pt3D->pt3D.y; + if (yMax < pt3D->pt3D.y) yMax = pt3D->pt3D.y; + } + } + } +} + +void PointCloudImageUtils::DrawDetectionTargets(QPainter& painter, + const std::vector& objOps, + double xMin, double xScale, int xSkip, + double yMin, double yScale, int ySkip, + int imgCols, int imgRows) +{ + // 绘制检测目标和方向线 + for (size_t i = 0; i < objOps.size(); i++) { + QColor objColor = (i == 0) ? QColor(255, 0, 0) : QColor(255, 255, 0); + int size = (i == 0) ? 20 : 10; + + int px = (int)((objOps[i].centerPos.x - xMin) / xScale + xSkip); + int py = (int)((objOps[i].centerPos.y - yMin) / yScale + ySkip); + + if (px >= 0 && px < imgCols && py >= 0 && py < imgRows) { + // 绘制抓取点圆圈 + painter.setPen(QPen(objColor, 2)); + painter.setBrush(QBrush(objColor)); + painter.drawEllipse(px - size/2, py - size/2, size, size); + + // 绘制方向线 + const double deg2rad = PI / 180.0; + + // 使用检测目标实际2D尺寸的较大值的一半作为方向线长度 + double maxSize = std::max(objOps[i].objSize.dHeight, objOps[i].objSize.dWidth); + double R = std::max(20.0, maxSize / 6.0); + + const double yaw = objOps[i].centerPos.z_yaw * deg2rad; + double cy = cos(yaw); + double sy = sin(yaw); + + // 计算方向线的端点 + double x1 = objOps[i].centerPos.x + R * cy; + double y1 = objOps[i].centerPos.y - R * sy; + double x2 = objOps[i].centerPos.x - R * cy; + double y2 = objOps[i].centerPos.y + R * sy; + + int px1 = (int)((x1 - xMin) / xScale + xSkip); + int py1 = (int)((y1 - yMin) / yScale + ySkip); + int px2 = (int)((x2 - xMin) / xScale + xSkip); + int py2 = (int)((y2 - yMin) / yScale + ySkip); + + // 绘制方向线 + painter.setPen(QPen(objColor, 2)); + painter.drawLine(px1, py1, px2, py2); + + // 根据orienFlag绘制箭头 + if (objOps[i].orienFlag == 1) { + // 绿色箭头 - 正面 + painter.setPen(QPen(QColor(0, 255, 0), 2)); + + // 计算箭头端点 + double arrowLen = R / 3; + double arrowAngle = 30 * deg2rad; + double ca = cos(arrowAngle); + double sa = sin(arrowAngle); + + double x3 = x1 - arrowLen * ca * cy + arrowLen * sa * sy; + double y3 = y1 + arrowLen * ca * sy + arrowLen * sa * cy; + double x4 = x1 - arrowLen * ca * cy - arrowLen * sa * sy; + double y4 = y1 + arrowLen * ca * sy - arrowLen * sa * cy; + + int px3 = (int)((x3 - xMin) / xScale + xSkip); + int py3 = (int)((y3 - yMin) / yScale + ySkip); + int px4 = (int)((x4 - xMin) / xScale + xSkip); + int py4 = (int)((y4 - yMin) / yScale + ySkip); + + painter.drawLine(px1, py1, px3, py3); + painter.drawLine(px1, py1, px4, py4); + } + else if (objOps[i].orienFlag == 2) { + // 蓝色箭头 - 反面 + painter.setPen(QPen(QColor(0, 0, 255), 2)); + + // 计算箭头端点 + double arrowLen = R / 3; + double arrowAngle = 30 * deg2rad; + double ca = cos(arrowAngle); + double sa = sin(arrowAngle); + + double x3 = x1 - arrowLen * ca * cy + arrowLen * sa * sy; + double y3 = y1 + arrowLen * ca * sy + arrowLen * sa * cy; + double x4 = x1 - arrowLen * ca * cy - arrowLen * sa * sy; + double y4 = y1 + arrowLen * ca * sy - arrowLen * sa * cy; + + int px3 = (int)((x3 - xMin) / xScale + xSkip); + int py3 = (int)((y3 - yMin) / yScale + ySkip); + int px4 = (int)((x4 - xMin) / xScale + xSkip); + int py4 = (int)((y4 - yMin) / yScale + ySkip); + + painter.drawLine(px1, py1, px3, py3); + painter.drawLine(px1, py1, px4, py4); + } + + // 绘制目标编号 + painter.setPen(QPen(Qt::white, 1)); + painter.setFont(QFont("Arial", 15, QFont::Bold)); + painter.drawText(px + 15, py - 10, QString("%1").arg(i + 1)); + } + } +} + +QImage PointCloudImageUtils::GeneratePointCloudImage(const std::vector>& scanLines, + const std::vector>& weldResults, + int imageWidth, int imageHeight) +{ + if (scanLines.empty()) { + return QImage(); + } + + // 计算点云范围 + double xMin, xMax, yMin, yMax; + CalculateScanLinesRange(scanLines, xMin, xMax, yMin, yMax); + + // 检查范围是否有效 + if (xMax <= xMin || yMax <= yMin) { + return QImage(); + } + + // 创建图像 + QImage image(imageWidth, imageHeight, QImage::Format_RGB888); + image.fill(Qt::black); + + QPainter painter(&image); + + // 绘制点云数据 + DrawScanLinesPointCloud(painter, scanLines, xMin, xMax, yMin, yMax, imageWidth, imageHeight); + + // 绘制检测结果 + DrawLapWeldResults(painter, weldResults, xMin, xMax, yMin, yMax, imageWidth, imageHeight); + + return image; +} + +void PointCloudImageUtils::CalculateScanLinesRange(const std::vector>& scanLines, + double& xMin, double& xMax, + double& yMin, double& yMax) +{ + xMin = std::numeric_limits::max(); + xMax = std::numeric_limits::lowest(); + yMin = std::numeric_limits::max(); + yMax = std::numeric_limits::lowest(); + + for (const auto& scanLine : scanLines) { + for (const auto& point : scanLine) { + if (point.pt3D.z < 1e-4) continue; + + if (point.pt3D.x < xMin) xMin = point.pt3D.x; + if (point.pt3D.x > xMax) xMax = point.pt3D.x; + if (point.pt3D.y < yMin) yMin = point.pt3D.y; + if (point.pt3D.y > yMax) yMax = point.pt3D.y; + } + } +} + +void PointCloudImageUtils::DrawScanLinesPointCloud(QPainter& painter, + const std::vector>& scanLines, + double xMin, double xMax, double yMin, double yMax, + int imageWidth, int imageHeight) +{ + double xScale = (xMax - xMin) / imageWidth; + double yScale = (yMax - yMin) / imageHeight; + + for (const auto& scanLine : scanLines) { + for (const auto& point : scanLine) { + if (point.pt3D.z < 1e-4) continue; + + // 解析点索引信息 + int vType = point.nPointIdx & 0xff; + int hType = vType >> 4; + int objId = (point.nPointIdx >> 16) & 0xff; + vType = vType & 0x0f; + + // 根据线特征类型确定颜色和大小 + QColor pointColor; + int pointSize = 1; + GetLineFeatureStyle(vType, hType, objId, pointColor, pointSize); + + // 计算图像坐标 + int px = (int)((point.pt3D.x - xMin) / xScale); + int py = (int)((point.pt3D.y - yMin) / yScale); + + if (px >= 0 && px < imageWidth && py >= 0 && py < imageHeight) { + painter.setPen(QPen(pointColor, pointSize)); + painter.drawPoint(px, py); + } + } + } +} + +void PointCloudImageUtils::DrawLapWeldResults(QPainter& painter, + const std::vector>& weldResults, + double xMin, double xMax, double yMin, double yMax, + int imageWidth, int imageHeight) +{ + if (weldResults.empty()) return; + + double xScale = (xMax - xMin) / imageWidth; + double yScale = (yMax - yMin) / imageHeight; + + // 使用不同颜色绘制每条焊缝 + QColor weldColors[] = { + QColor(255, 0, 0), // 红色 + QColor(0, 255, 0), // 绿色 + QColor(0, 0, 255), // 蓝色 + QColor(255, 255, 0), // 黄色 + QColor(255, 0, 255), // 紫色 + QColor(0, 255, 255), // 青色 + QColor(255, 128, 0), // 橙色 + QColor(128, 255, 0) // 浅绿色 + }; + int numColors = sizeof(weldColors) / sizeof(weldColors[0]); + + for (size_t i = 0; i < weldResults.size(); i++) { + const auto& weldLine = weldResults[i]; + if (weldLine.empty()) continue; + + QColor weldColor = weldColors[i % numColors]; + painter.setPen(QPen(weldColor, 3)); + + // 绘制焊缝线段 + for (size_t j = 1; j < weldLine.size(); j++) { + int px1 = (int)((weldLine[j-1].x - xMin) / xScale); + int py1 = (int)((weldLine[j-1].y - yMin) / yScale); + int px2 = (int)((weldLine[j].x - xMin) / xScale); + int py2 = (int)((weldLine[j].y - yMin) / yScale); + + if (px1 >= 0 && px1 < imageWidth && py1 >= 0 && py1 < imageHeight && + px2 >= 0 && px2 < imageWidth && py2 >= 0 && py2 < imageHeight) { + painter.drawLine(px1, py1, px2, py2); + } + } + + // 在起点和终点绘制标记 + if (!weldLine.empty()) { + // 起点标记 - 圆形 + int startX = (int)((weldLine[0].x - xMin) / xScale); + int startY = (int)((weldLine[0].y - yMin) / yScale); + if (startX >= 0 && startX < imageWidth && startY >= 0 && startY < imageHeight) { + painter.setPen(QPen(weldColor, 2)); + painter.setBrush(QBrush(weldColor)); + painter.drawEllipse(startX - 5, startY - 5, 10, 10); + } + + // 终点标记 - 方形 + int endX = (int)((weldLine.back().x - xMin) / xScale); + int endY = (int)((weldLine.back().y - yMin) / yScale); + if (endX >= 0 && endX < imageWidth && endY >= 0 && endY < imageHeight) { + painter.setPen(QPen(weldColor, 2)); + painter.setBrush(QBrush(weldColor)); + painter.drawRect(endX - 4, endY - 4, 8, 8); + } + } + } +} + +// Workpiece点云和角点检测结果转图像 - 将角点画成圆点 +QImage PointCloudImageUtils::GeneratePointCloudRetPointImage(const std::vector>& scanLines, + const std::vector>& cornerPoints, + double margin) +{ + if (scanLines.empty()) { + return QImage(); + } + + // 固定图像尺寸,与其他函数保持一致 + int imgRows = 992; + int imgCols = 1056; + int x_skip = 50; + int y_skip = 50; + + // 计算点云范围 + double xMin, xMax, yMin, yMax; + CalculateScanLinesRange(scanLines, xMin, xMax, yMin, yMax); + + // 检查范围是否有效 + if (xMax <= xMin || yMax <= yMin) { + return QImage(); + } + + // 在3D范围上扩展边界(如果指定) + if (margin > 0.0) { + xMin -= margin; + xMax += margin; + yMin -= margin; + yMax += margin; + } + + // 创建图像 + QImage image(imgCols, imgRows, QImage::Format_RGB888); + image.fill(Qt::black); + + QPainter painter(&image); + + // 计算投影比例 + double y_rows = (double)(imgRows - y_skip * 2); + double x_cols = (double)(imgCols - x_skip * 2); + double x_scale = (xMax - xMin) / x_cols; + double y_scale = (yMax - yMin) / y_rows; + + // 使用统一的比例尺,并计算居中偏移 + double scale = std::max(x_scale, y_scale); + x_scale = scale; + y_scale = scale; + + // 计算点云在图像中居中的偏移量 + double cloudWidth = (xMax - xMin) / scale; // 点云在图像中的像素宽度 + double cloudHeight = (yMax - yMin) / scale; // 点云在图像中的像素高度 + int x_offset = x_skip + (int)((x_cols - cloudWidth) / 2); // X方向居中偏移 + int y_offset = y_skip + (int)((y_rows - cloudHeight) / 2); // Y方向居中偏移 + + // 绘制点云数据 + for (const auto& scanLine : scanLines) { + for (const auto& point : scanLine) { + if (point.pt3D.z < 1e-4) continue; + + // 解析点索引信息 + int vType = point.nPointIdx & 0xff; + int hType = vType >> 4; + int objId = (point.nPointIdx >> 16) & 0xff; + vType = vType & 0x0f; + + // 根据线特征类型确定颜色和大小 + QColor pointColor; + int pointSize = 1; + GetLineFeatureStyle(vType, hType, objId, pointColor, pointSize); + + // 计算图像坐标(使用居中偏移) + int px = (int)((point.pt3D.x - xMin) / x_scale + x_offset); + int py = (int)((point.pt3D.y - yMin) / y_scale + y_offset); + + if (px >= 0 && px < imgCols && py >= 0 && py < imgRows) { + painter.setPen(QPen(pointColor, pointSize)); + painter.drawPoint(px, py); + } + } + } + + // 绘制角点作为圆点 + if (!cornerPoints.empty()) { + // 定义不同组角点的颜色 + QColor cornerColors[] = { + QColor(255, 0, 0), // 红色 + QColor(0, 255, 0), // 绿色 + QColor(0, 0, 255), // 蓝色 + QColor(255, 255, 0), // 黄色 + QColor(255, 0, 255), // 紫色 + QColor(0, 255, 255), // 青色 + QColor(255, 128, 0), // 橙色 + QColor(128, 255, 0) // 浅绿色 + }; + int numColors = sizeof(cornerColors) / sizeof(cornerColors[0]); + + for (size_t i = 0; i < cornerPoints.size(); i++) { + const auto& cornerGroup = cornerPoints[i]; + if (cornerGroup.empty()) continue; + + QColor cornerColor = cornerColors[i % numColors]; + + // 绘制每个角点 + for (size_t j = 0; j < cornerGroup.size(); j++) { + const SVzNL3DPoint& corner = cornerGroup[j]; + + // 跳过全0的点 + if (fabs(corner.x) < 0.0001 && fabs(corner.y) < 0.0001 && fabs(corner.z) < 0.0001) { + continue; + } + + // 计算图像坐标(使用居中偏移) + int px = (int)((corner.x - xMin) / x_scale + x_offset); + int py = (int)((corner.y - yMin) / y_scale + y_offset); + + if (px >= 0 && px < imgCols && py >= 0 && py < imgRows) { + // 绘制圆点标记 + int circleSize = 10; // 圆点直径 + painter.setPen(QPen(cornerColor, 2)); + painter.setBrush(QBrush(cornerColor)); + painter.drawEllipse(px - circleSize/2, py - circleSize/2, circleSize, circleSize); + + // 绘制角点编号,确保不超出图像边界 + painter.setPen(QPen(Qt::white, 1)); + QFont font("Arial", 16, QFont::Bold); + painter.setFont(font); + + QString numberText = QString("%1").arg(j + 1); + painter.drawText(px + 8, py + 8, numberText); + } + } + } + } + + return image; +} + +QImage PointCloudImageUtils::GeneratePointCloudImageWithParticles( + const std::vector>& scanLines, + const std::vector& particles, + int cameraIndex) +{ + // 创建空图像 + int imageWidth = 800; + int imageHeight = 600; + QImage image(imageWidth, imageHeight, QImage::Format_RGB888); + image.fill(Qt::black); + + if (scanLines.empty()) { + return image; + } + + // 计算点云范围 + double xMin = std::numeric_limits::max(); + double xMax = std::numeric_limits::lowest(); + double yMin = std::numeric_limits::max(); + double yMax = std::numeric_limits::lowest(); + + for (const auto& line : scanLines) { + for (const auto& pt : line) { + if (pt.pt3D.z < 1e-4) continue; // 跳过无效点 + xMin = std::min(xMin, static_cast(pt.pt3D.x)); + xMax = std::max(xMax, static_cast(pt.pt3D.x)); + yMin = std::min(yMin, static_cast(pt.pt3D.y)); + yMax = std::max(yMax, static_cast(pt.pt3D.y)); + } + } + + // 检查范围是否有效 + if (xMax <= xMin || yMax <= yMin) { + return image; + } + + // 计算缩放比例 + double xScale = (xMax - xMin) / imageWidth; + double yScale = (yMax - yMin) / imageHeight; + double scale = std::max(xScale, yScale); + + QPainter painter(&image); + painter.setRenderHint(QPainter::Antialiasing, true); + + // 绘制点云数据(灰色) + painter.setPen(QColor(128, 128, 128)); + for (const auto& line : scanLines) { + for (const auto& pt : line) { + if (pt.pt3D.z < 1e-4) continue; // 跳过无效点 + + int px = static_cast((pt.pt3D.x - xMin) / scale); + int py = static_cast((pt.pt3D.y - yMin) / scale); + + if (px >= 0 && px < imageWidth && py >= 0 && py < imageHeight) { + painter.drawPoint(px, py); + } + } + } + + // 绘制颗粒(用不同颜色标记) + QColor colors[] = { + QColor(255, 0, 0), // 红色 + QColor(0, 255, 0), // 绿色 + QColor(0, 0, 255), // 蓝色 + QColor(255, 255, 0), // 黄色 + QColor(255, 0, 255), // 紫色 + QColor(0, 255, 255), // 青色 + QColor(255, 128, 0), // 橙色 + QColor(128, 255, 0) // 浅绿色 + }; + int numColors = sizeof(colors) / sizeof(colors[0]); + + for (size_t i = 0; i < particles.size(); i++) { + const auto& particle = particles[i]; + QColor color = colors[i % numColors]; + + // 绘制颗粒的8个顶点和边界框 + QVector points2D; + for (int j = 0; j < 8; j++) { + int px = static_cast((particle.vertix[j].x - xMin) / scale); + int py = static_cast((particle.vertix[j].y - yMin) / scale); + + if (px >= 0 && px < imageWidth && py >= 0 && py < imageHeight) { + points2D.append(QPoint(px, py)); + // 绘制顶点 + painter.setPen(QPen(color, 3)); + painter.setBrush(color); + painter.drawEllipse(QPoint(px, py), 3, 3); + } + } + + // 绘制边界框(连接相邻顶点) + if (points2D.size() >= 4) { + painter.setPen(QPen(color, 1)); + painter.setBrush(Qt::NoBrush); + + // 假设8个顶点按照立方体的顺序排列 + // 底面4个顶点:0,1,2,3 顶面4个顶点:4,5,6,7 + int edges[12][2] = { + {0,1}, {1,2}, {2,3}, {3,0}, // 底面 + {4,5}, {5,6}, {6,7}, {7,4}, // 顶面 + {0,4}, {1,5}, {2,6}, {3,7} // 竖边 + }; + + for (int j = 0; j < 12; j++) { + int idx1 = edges[j][0]; + int idx2 = edges[j][1]; + if (idx1 < points2D.size() && idx2 < points2D.size()) { + painter.drawLine(points2D[idx1], points2D[idx2]); + } + } + } + + // 计算颗粒中心位置并标注序号 + if (!points2D.empty()) { + QPoint center(0, 0); + for (const auto& pt : points2D) { + center.setX(center.x() + pt.x()); + center.setY(center.y() + pt.y()); + } + center.setX(center.x() / points2D.size()); + center.setY(center.y() / points2D.size()); + + // 绘制序号 + painter.setPen(color); + painter.setFont(QFont("Arial", 10, QFont::Bold)); + painter.drawText(center, QString::number(i + 1)); + } + } + + return image; +} + +QImage PointCloudImageUtils::GenerateWheelArchImage( + const std::vector>& scanLines, + const SVzNL3DPoint& wheelArchPos, + const SVzNL3DPoint& wheelUpPos, + const SVzNL3DPoint& wheelDownPos, + double archToCenterHeight, + bool hasResult) +{ + // 固定图像尺寸 + int imgRows = 992; + int imgCols = 1056; + int x_skip = 50; + int y_skip = 50; + + // 创建图像 + QImage image(imgCols, imgRows, QImage::Format_RGB888); + image.fill(Qt::black); + + if (scanLines.empty()) { + return image; + } + + // 计算点云范围 + double xMin, xMax, yMin, yMax; + CalculateScanLinesRange(scanLines, xMin, xMax, yMin, yMax); + + // 检查范围是否有效 + if (xMax <= xMin || yMax <= yMin) { + return image; + } + + QPainter painter(&image); + painter.setRenderHint(QPainter::Antialiasing, true); + + // 计算投影比例 + double y_rows = (double)(imgRows - y_skip * 2); + double x_cols = (double)(imgCols - x_skip * 2); + double x_scale = (xMax - xMin) / x_cols; + double y_scale = (yMax - yMin) / y_rows; + + // 使用统一的比例尺 + if (x_scale < y_scale) + x_scale = y_scale; + else + y_scale = x_scale; + + // 绘制点云数据(灰色) + for (const auto& scanLine : scanLines) { + for (const auto& point : scanLine) { + if (point.pt3D.z < 1e-4) continue; + + // 计算图像坐标 + int px = (int)((point.pt3D.x - xMin) / x_scale + x_skip); + int py = (int)((point.pt3D.y - yMin) / y_scale + y_skip); + + if (px >= 0 && px < imgCols && py >= 0 && py < imgRows) { + painter.setPen(QPen(QColor(128, 128, 128), 1)); + painter.drawPoint(px, py); + } + } + } + + // 如果有检测结果,绘制轮眉检测结果 + if (hasResult) { + // 绘制轮眉位置(红色大圆点) + if (fabs(wheelArchPos.x) > 0.0001 || fabs(wheelArchPos.y) > 0.0001) { + int px = (int)((wheelArchPos.x - xMin) / x_scale + x_skip); + int py = (int)((wheelArchPos.y - yMin) / y_scale + y_skip); + + if (px >= 0 && px < imgCols && py >= 0 && py < imgRows) { + painter.setPen(QPen(QColor(255, 0, 0), 2)); + painter.setBrush(QBrush(QColor(255, 0, 0))); + painter.drawEllipse(px - 8, py - 8, 16, 16); + + // 标注"轮眉" + painter.setPen(QPen(Qt::white, 1)); + painter.setFont(QFont("Arial", 24, QFont::Bold)); + painter.drawText(px + 12, py + 5, QString::fromUtf8("轮眉")); + } + } + + // 绘制上点位置(绿色圆点) + if (fabs(wheelUpPos.x) > 0.0001 || fabs(wheelUpPos.y) > 0.0001) { + int px = (int)((wheelUpPos.x - xMin) / x_scale + x_skip); + int py = (int)((wheelUpPos.y - yMin) / y_scale + y_skip); + + if (px >= 0 && px < imgCols && py >= 0 && py < imgRows) { + painter.setPen(QPen(QColor(0, 255, 0), 2)); + painter.setBrush(QBrush(QColor(0, 255, 0))); + painter.drawEllipse(px - 6, py - 6, 12, 12); + + // 标注"上点" + painter.setPen(QPen(Qt::white, 1)); + painter.setFont(QFont("Arial", 24, QFont::Bold)); + painter.drawText(px + 10, py + 5, QString::fromUtf8("上点")); + } + } + + // 绘制下点位置(蓝色圆点) + if (fabs(wheelDownPos.x) > 0.0001 || fabs(wheelDownPos.y) > 0.0001) { + int px = (int)((wheelDownPos.x - xMin) / x_scale + x_skip); + int py = (int)((wheelDownPos.y - yMin) / y_scale + y_skip); + + if (px >= 0 && px < imgCols && py >= 0 && py < imgRows) { + painter.setPen(QPen(QColor(0, 0, 255), 2)); + painter.setBrush(QBrush(QColor(0, 0, 255))); + painter.drawEllipse(px - 6, py - 6, 12, 12); + + // 标注"下点" + painter.setPen(QPen(Qt::white, 1)); + painter.setFont(QFont("Arial", 24, QFont::Bold)); + painter.drawText(px + 10, py + 5, QString::fromUtf8("下点")); + } + } + + // 绘制连线(从上点到轮眉到下点) + if ((fabs(wheelUpPos.x) > 0.0001 || fabs(wheelUpPos.y) > 0.0001) && + (fabs(wheelArchPos.x) > 0.0001 || fabs(wheelArchPos.y) > 0.0001)) { + int px1 = (int)((wheelUpPos.x - xMin) / x_scale + x_skip); + int py1 = (int)((wheelUpPos.y - yMin) / y_scale + y_skip); + int px2 = (int)((wheelArchPos.x - xMin) / x_scale + x_skip); + int py2 = (int)((wheelArchPos.y - yMin) / y_scale + y_skip); + + painter.setPen(QPen(QColor(255, 255, 0), 2, Qt::DashLine)); + painter.drawLine(px1, py1, px2, py2); + } + + if ((fabs(wheelArchPos.x) > 0.0001 || fabs(wheelArchPos.y) > 0.0001) && + (fabs(wheelDownPos.x) > 0.0001 || fabs(wheelDownPos.y) > 0.0001)) { + int px1 = (int)((wheelArchPos.x - xMin) / x_scale + x_skip); + int py1 = (int)((wheelArchPos.y - yMin) / y_scale + y_skip); + int px2 = (int)((wheelDownPos.x - xMin) / x_scale + x_skip); + int py2 = (int)((wheelDownPos.y - yMin) / y_scale + y_skip); + + painter.setPen(QPen(QColor(255, 255, 0), 2, Qt::DashLine)); + painter.drawLine(px1, py1, px2, py2); + } + + // 在图像右上角显示拱高信息 + // painter.setPen(QPen(Qt::white, 1)); + // painter.setFont(QFont("Arial", 14, QFont::Bold)); + // QString heightText = QString::fromUtf8("拱高: %1 mm").arg(archToCenterHeight, 0, 'f', 2); + // painter.drawText(imgCols - 200, 40, heightText); + } + + return image; +} + +QImage PointCloudImageUtils::GenerateChannelSpaceImage( + const std::vector>& scanLines, + int imageWidth, int imageHeight) +{ + // 创建图像 + QImage image(imageWidth, imageHeight, QImage::Format_RGB888); + image.fill(Qt::black); + + if (scanLines.empty()) { + return image; + } + + int x_skip = 30; + int y_skip = 30; + + // 计算点云范围 + double xMin, xMax, yMin, yMax; + CalculateScanLinesRange(scanLines, xMin, xMax, yMin, yMax); + + // 检查范围是否有效 + if (xMax <= xMin || yMax <= yMin) { + return image; + } + + // 计算投影比例 + double y_rows = (double)(imageHeight - y_skip * 2); + double x_cols = (double)(imageWidth - x_skip * 2); + double x_scale = (xMax - xMin) / x_cols; + double y_scale = (yMax - yMin) / y_rows; + + // 使用统一的比例尺 + double scale = std::max(x_scale, y_scale); + + // 计算点云总数和密度,动态调整点的绘制大小 + int totalPoints = 0; + for (const auto& scanLine : scanLines) { + for (const auto& point : scanLine) { + if (point.pt3D.z > 1e-4) totalPoints++; + } + } + + // 根据点云密度计算基础点大小(点少则画大,点多则画小) + int basePointSize = 2; + if (totalPoints < 10000) { + basePointSize = 4; + } else if (totalPoints < 50000) { + basePointSize = 3; + } else { + basePointSize = 2; + } + + QPainter painter(&image); + painter.setRenderHint(QPainter::Antialiasing, false); + + // 预定义8种颜色(参考 HC_chanelSpaceMeasure_test.cpp 的 objColor) + QColor objColors[8] = { + QColor(245, 222, 179), // 淡黄色 + QColor(210, 105, 30), // 巧克力色 + QColor(240, 230, 140), // 黄褐色 + QColor(135, 206, 235), // 天蓝色 + QColor(250, 235, 215), // 古董白 + QColor(189, 252, 201), // 薄荷色 + QColor(221, 160, 221), // 梅红色 + QColor(188, 143, 143) // 玫瑰红色 + }; + + // 绘制点云数据 - 根据算法标记的nPointIdx着色 + for (const auto& scanLine : scanLines) { + for (const auto& point : scanLine) { + if (point.pt3D.z < 1e-4) continue; + + // 根据nPointIdx确定颜色和大小 + // 参考 _outputRGBDScan_RGBD: nPointIdx > 0 使用objColor, 否则灰色 + QColor pointColor; + int pointSize = basePointSize; + + if (point.nPointIdx > 0 && point.nPointIdx <= 8) { + pointColor = objColors[point.nPointIdx - 1]; + pointSize = basePointSize + 2; // 特征点更大 + } else if (point.nPointIdx > 8) { + // nPointIdx > 8 使用取模方式循环颜色 + pointColor = objColors[(point.nPointIdx - 1) % 8]; + pointSize = basePointSize + 2; + } else { + // nPointIdx == 0 或负数,使用灰色 + pointColor = QColor(180, 180, 180); + pointSize = basePointSize; + } + + // 计算图像坐标 + int px = (int)((point.pt3D.x - xMin) / scale + x_skip); + int py = (int)((point.pt3D.y - yMin) / scale + y_skip); + + if (px >= 0 && px < imageWidth && py >= 0 && py < imageHeight) { + // 使用填充矩形绘制点,避免稀疏 + painter.fillRect(px - pointSize/2, py - pointSize/2, + pointSize, pointSize, pointColor); + } + } + } + + return image; +} + +int PointCloudImageUtils::GenerateDepthImage( + const std::vector>& detectionDataCache, + QImage& outImage) +{ + if (detectionDataCache.empty()) { + return -1; + } + + // 固定图像尺寸 + int imgRows = 992; + int imgCols = 1056; + int x_skip = 50; + int y_skip = 50; + + // 计算点云范围 + double xMin = std::numeric_limits::max(); + double xMax = std::numeric_limits::lowest(); + double yMin = std::numeric_limits::max(); + double yMax = std::numeric_limits::lowest(); + double zMin = std::numeric_limits::max(); + double zMax = std::numeric_limits::lowest(); + + for (const auto& dataPair : detectionDataCache) { + EVzResultDataType dataType = dataPair.first; + const SVzLaserLineData& lineData = dataPair.second; + + if (!lineData.p3DPoint || lineData.nPointCount <= 0) continue; + + // 根据数据类型处理点云数据 + if (dataType == keResultDataType_Position) { + SVzNL3DPosition* positions = static_cast(lineData.p3DPoint); + for (int i = 0; i < lineData.nPointCount; i++) { + const SVzNL3DPosition& point = positions[i]; + if (point.pt3D.z < 1e-4) continue; + + if (point.pt3D.x < xMin) xMin = point.pt3D.x; + if (point.pt3D.x > xMax) xMax = point.pt3D.x; + if (point.pt3D.y < yMin) yMin = point.pt3D.y; + if (point.pt3D.y > yMax) yMax = point.pt3D.y; + if (point.pt3D.z < zMin) zMin = point.pt3D.z; + if (point.pt3D.z > zMax) zMax = point.pt3D.z; + } + } else if (dataType == keResultDataType_PointXYZRGBA) { + SVzNLPointXYZRGBA* points = static_cast(lineData.p3DPoint); + for (int i = 0; i < lineData.nPointCount; i++) { + const SVzNLPointXYZRGBA& point = points[i]; + if (point.z < 1e-4) continue; + + if (point.x < xMin) xMin = point.x; + if (point.x > xMax) xMax = point.x; + if (point.y < yMin) yMin = point.y; + if (point.y > yMax) yMax = point.y; + if (point.z < zMin) zMin = point.z; + if (point.z > zMax) zMax = point.z; + } + } + } + + // 检查范围是否有效 + if (xMax <= xMin || yMax <= yMin) { + return -2; + } + + // 创建图像 + outImage = QImage(imgCols, imgRows, QImage::Format_RGB888); + outImage.fill(Qt::black); + + QPainter painter(&outImage); + + // 计算投影比例 + double y_rows = (double)(imgRows - y_skip * 2); + double x_cols = (double)(imgCols - x_skip * 2); + double x_scale = (xMax - xMin) / x_cols; + double y_scale = (yMax - yMin) / y_rows; + + // 使用统一的比例尺 + if (x_scale < y_scale) + x_scale = y_scale; + else + y_scale = x_scale; + + double zRange = zMax - zMin; + if (zRange < 1e-4) zRange = 1.0; // 防止除0 + + // 绘制点云数据 - 使用Z值映射颜色(深度图) + for (const auto& dataPair : detectionDataCache) { + EVzResultDataType dataType = dataPair.first; + const SVzLaserLineData& lineData = dataPair.second; + + if (!lineData.p3DPoint || lineData.nPointCount <= 0) continue; + + if (dataType == keResultDataType_Position) { + SVzNL3DPosition* positions = static_cast(lineData.p3DPoint); + for (int i = 0; i < lineData.nPointCount; i++) { + const SVzNL3DPosition& point = positions[i]; + if (point.pt3D.z < 1e-4) continue; + + // 计算图像坐标 + int px = (int)((point.pt3D.x - xMin) / x_scale + x_skip); + int py = (int)((point.pt3D.y - yMin) / y_scale + y_skip); + + if (px >= 0 && px < imgCols && py >= 0 && py < imgRows) { + // 根据Z值计算颜色(深度图效果) + double normalizedZ = (point.pt3D.z - zMin) / zRange; + normalizedZ = std::max(0.0, std::min(1.0, normalizedZ)); + + // 使用热力图颜色映射:蓝->青->绿->黄->红 + QColor color; + if (normalizedZ < 0.25) { + int r = 0; + int g = (int)(normalizedZ * 4 * 255); + int b = 255; + color = QColor(r, g, b); + } else if (normalizedZ < 0.5) { + int r = 0; + int g = 255; + int b = (int)((0.5 - normalizedZ) * 4 * 255); + color = QColor(r, g, b); + } else if (normalizedZ < 0.75) { + int r = (int)((normalizedZ - 0.5) * 4 * 255); + int g = 255; + int b = 0; + color = QColor(r, g, b); + } else { + int r = 255; + int g = (int)((1.0 - normalizedZ) * 4 * 255); + int b = 0; + color = QColor(r, g, b); + } + + painter.setPen(QPen(color, 1)); + painter.drawPoint(px, py); + } + } + } else if (dataType == keResultDataType_PointXYZRGBA) { + SVzNLPointXYZRGBA* points = static_cast(lineData.p3DPoint); + for (int i = 0; i < lineData.nPointCount; i++) { + const SVzNLPointXYZRGBA& point = points[i]; + if (point.z < 1e-4) continue; + + // 计算图像坐标 + int px = (int)((point.x - xMin) / x_scale + x_skip); + int py = (int)((point.y - yMin) / y_scale + y_skip); + + if (px >= 0 && px < imgCols && py >= 0 && py < imgRows) { + // 根据Z值计算颜色(深度图效果) + double normalizedZ = (point.z - zMin) / zRange; + normalizedZ = std::max(0.0, std::min(1.0, normalizedZ)); + + // 使用热力图颜色映射:蓝->青->绿->黄->红 + QColor color; + if (normalizedZ < 0.25) { + int r = 0; + int g = (int)(normalizedZ * 4 * 255); + int b = 255; + color = QColor(r, g, b); + } else if (normalizedZ < 0.5) { + int r = 0; + int g = 255; + int b = (int)((0.5 - normalizedZ) * 4 * 255); + color = QColor(r, g, b); + } else if (normalizedZ < 0.75) { + int r = (int)((normalizedZ - 0.5) * 4 * 255); + int g = 255; + int b = 0; + color = QColor(r, g, b); + } else { + int r = 255; + int g = (int)((1.0 - normalizedZ) * 4 * 255); + int b = 0; + color = QColor(r, g, b); + } + + painter.setPen(QPen(color, 1)); + painter.drawPoint(px, py); + } + } + } + } + + return 0; +} diff --git a/CloudView/CloudView.pro b/CloudView/CloudView.pro new file mode 100644 index 0000000..b2774d4 --- /dev/null +++ b/CloudView/CloudView.pro @@ -0,0 +1,67 @@ +# Qt 5 兼容:QOpenGLWidget 在 widgets 模块中 +QT += core gui widgets opengl + +CONFIG += c++17 +CONFIG -= app_bundle + +# Windows平台UTF-8编码支持 +win32-msvc { + QMAKE_CXXFLAGS += /utf-8 + QMAKE_CXXFLAGS += /bigobj +} + +TARGET = CloudView +TEMPLATE = app + +# 项目目录 +INCLUDEPATH += $$PWD/Inc +INCLUDEPATH += $$PWD/../CloudUtils/Inc +INCLUDEPATH += $$PWD/../../SDK/Device/VzNLSDK/Inc +INCLUDEPATH += $$PWD/../VrUtils/Inc +INCLUDEPATH += $$PWD/../VrCommon/Inc + +# 头文件 +HEADERS += \ + Inc/CloudViewMainWindow.h \ + Inc/PointCloudGLWidget.h \ + Inc/PointCloudConverter.h + +# 源文件 +SOURCES += \ + main.cpp \ + Src/CloudViewMainWindow.cpp \ + Src/PointCloudGLWidget.cpp \ + Src/PointCloudConverter.cpp + +# 资源文件 +RESOURCES += resource/resource.qrc + +# Windows 应用图标 +RC_ICONS = resource/logo.ico + +# Windows平台库链接 +win32:CONFIG(release, debug|release): { + LIBS += -L../CloudUtils/release -lCloudUtils + LIBS += -L../VrUtils/release -lVrUtils + LIBS += -L../VrCommon/release -lVrCommon +} +else:win32:CONFIG(debug, debug|release): { + LIBS += -L../CloudUtils/debug -lCloudUtils + LIBS += -L../VrUtils/debug -lVrUtils + LIBS += -L../VrCommon/debug -lVrCommon +} + +# Windows系统库 +win32 { + LIBS += -lAdvapi32 + LIBS += -lopengl32 + LIBS += -lglu32 +} + + +DEFINES += _USE_MATH_DEFINES + +# Default rules for deployment +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target diff --git a/CloudView/Inc/CloudViewMainWindow.h b/CloudView/Inc/CloudViewMainWindow.h new file mode 100644 index 0000000..dddb2c2 --- /dev/null +++ b/CloudView/Inc/CloudViewMainWindow.h @@ -0,0 +1,312 @@ +#ifndef CLOUD_VIEW_MAIN_WINDOW_H +#define CLOUD_VIEW_MAIN_WINDOW_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "PointCloudGLWidget.h" +#include "PointCloudConverter.h" + +class QTextEdit; +class QTableWidget; + +/** + * @brief 点云查看器主窗口 + * 左侧显示点云,右侧为操作区域 + */ +class CloudViewMainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit CloudViewMainWindow(QWidget* parent = nullptr); + ~CloudViewMainWindow() override; + +private slots: + /** + * @brief 打开文件 + */ + void onOpenFile(); + + /** + * @brief 打开线段文件 + */ + void onOpenSegmentFile(); + + /** + * @brief 打开姿态点文件 + */ + void onOpenPoseFile(); + + /** + * @brief 清除所有点云 + */ + void onClearAll(); + + /** + * @brief 重置视图 + */ + void onResetView(); + + /** + * @brief 清除选中的点 + */ + void onClearSelectedPoints(); + + /** + * @brief 清除选线 + */ + void onClearLinePoints(); + + /** + * @brief 处理点选中事件 + */ + void onPointSelected(const SelectedPointInfo& point); + + /** + * @brief 处理两点选中事件(测距) + */ + void onTwoPointsSelected(const SelectedPointInfo& p1, const SelectedPointInfo& p2, float distance); + + /** + * @brief 处理线选中事件 + */ + void onLineSelected(const SelectedLineInfo& line); + + /** + * @brief 处理视角旋转角度改变事件 + */ + void onViewAnglesChanged(float rotX, float rotY, float rotZ); + + /** + * @brief 通过输入线号选择线 + */ + void onSelectLineByNumber(); + + /** + * @brief 选线模式切换(纵向/横向) + */ + void onLineSelectModeChanged(bool checked); + + /** + * @brief 显示选中线上的所有点坐标 + */ + void onShowLinePoints(); + + /** + * @brief 线上点列表项被点击 + */ + void onLinePointTableClicked(int row, int column); + + /** + * @brief 显示点1的姿态 + */ + void onShowPose1(); + + /** + * @brief 显示点2的姿态 + */ + void onShowPose2(); + + /** + * @brief 显示输入的线段 + */ + void onShowInputLine(); + + /** + * @brief 清除输入的线段 + */ + void onClearInputLine(); + + /** + * @brief 欧拉角旋转顺序改变 + */ + void onEulerOrderChanged(int index); + + /** + * @brief 从文件加载变换矩阵 + */ + void onLoadMatrix(); + + /** + * @brief 应用矩阵变换到所有点云 + */ + void onApplyMatrix(); + + /** + * @brief 重置矩阵为单位矩阵 + */ + void onResetMatrix(); + +private: + /** + * @brief 初始化界面 + */ + void setupUI(); + + /** + * @brief 创建左侧点云显示区域 + */ + QWidget* createViewerArea(); + + /** + * @brief 创建右侧操作区域 + */ + QWidget* createControlPanel(); + + /** + * @brief 创建文件操作组 + */ + QGroupBox* createFileGroup(); + + /** + * @brief 创建视图方向组 + */ + QGroupBox* createViewGroup(); + + /** + * @brief 创建选点测距组 + */ + QGroupBox* createMeasureGroup(); + + /** + * @brief 创建选点测距页面 + */ + QWidget* createMeasurePage(); + + /** + * @brief 创建选线页面 + */ + QWidget* createLinePage(); + + /** + * @brief 创建矩阵变换页面 + */ + QWidget* createTransformPage(); + + /** + * @brief 创建选线拟合组 + */ + QGroupBox* createLineGroup(); + + /** + * @brief 创建点云列表组 + */ + QGroupBox* createCloudListGroup(); + + /** + * @brief 更新选点信息显示 + */ + void updateSelectedPointsDisplay(); + + /** + * @brief 从原始数据获取线上点(包含0,0,0) + */ + QVector getOriginalLinePoints(const SelectedLineInfo& lineInfo); + + /** + * @brief 更新线上点对话框内容 + */ + void updateLinePointsDialog(); + + // 点云显示控件 + PointCloudGLWidget* m_glWidget; + + // 点云转换器 + std::unique_ptr m_converter; + + // 文件操作控件 + QPushButton* m_btnOpenFile; + QPushButton* m_btnOpenSegment; + QPushButton* m_btnOpenPose; + QPushButton* m_btnClearAll; + + // 视图角度输入控件 + QLineEdit* m_editRotX; + QLineEdit* m_editRotY; + QLineEdit* m_editRotZ; + + // 选点测距控件 + QCheckBox* m_cbMeasureDistance; + QPushButton* m_btnClearPoints; + QLabel* m_lblPoint1; + QLabel* m_lblPoint2; + QLabel* m_lblDistance; + + // 点1姿态输入控件 + QLineEdit* m_editRx1; + QLineEdit* m_editRy1; + QLineEdit* m_editRz1; + QPushButton* m_btnShowPose1; + + // 点2姿态输入控件 + QLineEdit* m_editRx2; + QLineEdit* m_editRy2; + QLineEdit* m_editRz2; + QPushButton* m_btnShowPose2; + + // 欧拉角旋转顺序选择 + QComboBox* m_comboEulerOrder; + + // 选线拟合控件 + QPushButton* m_btnClearLine; + QPushButton* m_btnShowLinePoints; + QLineEdit* m_lineNumberInput; + QPushButton* m_btnSelectByNumber; + QRadioButton* m_rbVertical; + QRadioButton* m_rbHorizontal; + QLabel* m_lblLineIndex; + QLabel* m_lblLinePointCount; + + // 输入线段控件 + QLineEdit* m_editLineX1; + QLineEdit* m_editLineY1; + QLineEdit* m_editLineZ1; + QLineEdit* m_editLineX2; + QLineEdit* m_editLineY2; + QLineEdit* m_editLineZ2; + QPushButton* m_btnShowLine; + QPushButton* m_btnClearLine2; + + // 点云列表 + QListWidget* m_cloudList; + + // 已加载的点云数量 + int m_cloudCount; + + // 当前点云的线信息(用于旋转) + int m_currentLineNum; + int m_currentLinePtNum; + + // 原始完整点云数据(包含0,0,0点,用于旋转) + PointCloudXYZ m_originalCloud; + + // 线上点对话框 + QDialog* m_linePointsDialog; + QTableWidget* m_linePointsTable; + QVector m_currentLinePoints; // 当前线的原始点坐标 + + // 矩阵变换控件 + QTextEdit* m_matrixEdit; + QPushButton* m_btnLoadMatrix; + QPushButton* m_btnApplyMatrix; + QPushButton* m_btnResetMatrix; +}; + +#endif // CLOUD_VIEW_MAIN_WINDOW_H diff --git a/CloudView/Inc/PointCloudConverter.h b/CloudView/Inc/PointCloudConverter.h new file mode 100644 index 0000000..2c5a9ec --- /dev/null +++ b/CloudView/Inc/PointCloudConverter.h @@ -0,0 +1,149 @@ +#ifndef POINT_CLOUD_CONVERTER_H +#define POINT_CLOUD_CONVERTER_H + +#include +#include +#include + +/** + * @brief 简单的 3D 点结构 + */ +struct Point3D +{ + float x, y, z; + Point3D() : x(0), y(0), z(0) {} + Point3D(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {} +}; + +/** + * @brief 带颜色的 3D 点结构 + */ +struct Point3DRGB +{ + float x, y, z; + uint8_t r, g, b; + Point3DRGB() : x(0), y(0), z(0), r(255), g(255), b(255) {} + Point3DRGB(float _x, float _y, float _z, uint8_t _r = 255, uint8_t _g = 255, uint8_t _b = 255) + : x(_x), y(_y), z(_z), r(_r), g(_g), b(_b) {} +}; + +/** + * @brief 简单的点云容器 + */ +template +class SimplePointCloud +{ +public: + std::vector points; + std::vector lineIndices; // 每个点所属的线索引 + + void clear() { points.clear(); lineIndices.clear(); } + size_t size() const { return points.size(); } + bool empty() const { return points.empty(); } + void reserve(size_t n) { points.reserve(n); lineIndices.reserve(n); } + void push_back(const PointT& pt) { points.push_back(pt); } + void push_back(const PointT& pt, int lineIdx) { + points.push_back(pt); + lineIndices.push_back(lineIdx); + } +}; + +using PointCloudXYZ = SimplePointCloud; +using PointCloudXYZRGB = SimplePointCloud; + +/** + * @brief 点云数据转换器 + * 负责加载 txt 和 pcd 格式的点云文件 + */ +class PointCloudConverter +{ +public: + PointCloudConverter(); + ~PointCloudConverter(); + + /** + * @brief 从 txt 文件加载点云(使用 CloudUtils) + */ + int loadFromTxt(const std::string& fileName, PointCloudXYZ& cloud); + int loadFromTxt(const std::string& fileName, PointCloudXYZRGB& cloud); + + /** + * @brief 从 pcd 文件加载点云(简单 ASCII/Binary 解析) + */ + int loadFromPcd(const std::string& fileName, PointCloudXYZ& cloud); + int loadFromPcd(const std::string& fileName, PointCloudXYZRGB& cloud); + + /** + * @brief 根据文件扩展名自动选择加载方式 + */ + int loadFromFile(const std::string& fileName, PointCloudXYZ& cloud); + int loadFromFile(const std::string& fileName, PointCloudXYZRGB& cloud); + + /** + * @brief 保存点云到 txt 文件(使用 CloudUtils) + */ + int saveToTxt(const std::string& fileName, const PointCloudXYZ& cloud, int lineNum, int linePtNum); + + /** + * @brief 旋转点云(行列转置 + 交换XY) + * @param cloud 输入点云 + * @param rotatedCloud 输出旋转后的点云 + * @param lineNum 原始线数 + * @param linePtNum 原始每线点数 + * @param newLineNum 输出新的线数 + * @param newLinePtNum 输出新的每线点数 + */ + int rotateCloud(const PointCloudXYZ& cloud, PointCloudXYZ& rotatedCloud, + int lineNum, int linePtNum, int& newLineNum, int& newLinePtNum); + + /** + * @brief 获取最后的错误信息 + */ + std::string getLastError() const { return m_lastError; } + + /** + * @brief 获取加载的点云数量 + */ + size_t getLoadedPointCount() const { return m_loadedPointCount; } + + /** + * @brief 获取加载的线数量 + */ + int getLoadedLineCount() const { return m_loadedLineCount; } + + /** + * @brief 上次 loadFromTxt(PointCloudXYZRGB&) 加载的文件是否包含颜色数据 + */ + bool lastLoadHadColor() const { return m_lastLoadHadColor; } + +private: + /** + * @brief 获取文件扩展名(小写) + */ + std::string getFileExtension(const std::string& fileName); + + /** + * @brief 解析 PCD 文件头 + */ + struct PcdHeader + { + int width = 0; + int height = 1; + int points = 0; + bool isBinary = false; + bool hasRgb = false; + std::vector fields; + std::vector fieldSizes; + std::vector fieldTypes; + int pointSize = 0; + }; + + bool parsePcdHeader(std::ifstream& file, PcdHeader& header); + + std::string m_lastError; + size_t m_loadedPointCount; + int m_loadedLineCount; + bool m_lastLoadHadColor; +}; + +#endif // POINT_CLOUD_CONVERTER_H diff --git a/CloudView/Inc/PointCloudGLWidget.h b/CloudView/Inc/PointCloudGLWidget.h new file mode 100644 index 0000000..0140004 --- /dev/null +++ b/CloudView/Inc/PointCloudGLWidget.h @@ -0,0 +1,393 @@ +#ifndef POINT_CLOUD_GL_WIDGET_H +#define POINT_CLOUD_GL_WIDGET_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "PointCloudConverter.h" + +/** + * @brief 点云显示颜色枚举 + */ +enum class PointCloudColor +{ + White, + Red, + Green, + Blue, + Yellow, + Cyan, + Magenta, + Original +}; + +/** + * @brief 选中的点信息 + */ +struct SelectedPointInfo +{ + bool valid; + size_t index; + float x, y, z; + int cloudIndex; + int lineIndex; // 所属线索引 + int pointIndexInLine; // 点在线中的索引 + + SelectedPointInfo() : valid(false), index(0), x(0), y(0), z(0), cloudIndex(-1), lineIndex(-1), pointIndexInLine(-1) {} +}; + +/** + * @brief 选线模式 + */ +enum class LineSelectMode +{ + Vertical, // 纵向选线(同一条扫描线) + Horizontal // 横向选线(所有线的相同索引点) +}; + +/** + * @brief 欧拉角旋转顺序 + */ +enum class EulerRotationOrder +{ + ZYX, // Yaw-Pitch-Roll(最常用) + XYZ, // Roll-Pitch-Yaw + ZXY, // Yaw-Roll-Pitch + YXZ, // Pitch-Roll-Yaw + XZY, // Roll-Yaw-Pitch + YZX // Pitch-Yaw-Roll +}; + +/** + * @brief 选中的线信息 + */ +struct SelectedLineInfo +{ + bool valid; + int cloudIndex; // 点云索引 + int lineIndex; // 线索引(纵向选线时使用) + int pointIndex; // 点索引(横向选线时使用) + int pointCount; // 该线上的点数 + LineSelectMode mode; // 选线模式 + + SelectedLineInfo() : valid(false), cloudIndex(-1), lineIndex(-1), pointIndex(-1), pointCount(0), mode(LineSelectMode::Vertical) {} +}; + +/** + * @brief 线段数据 + */ +struct LineSegment +{ + float x1, y1, z1; // 起点 + float x2, y2, z2; // 终点 + float r, g, b; // 颜色 (0-1) + + LineSegment() : x1(0), y1(0), z1(0), x2(0), y2(0), z2(0), r(1), g(1), b(1) {} + LineSegment(float _x1, float _y1, float _z1, float _x2, float _y2, float _z2, float _r = 1.0f, float _g = 1.0f, float _b = 1.0f) + : x1(_x1), y1(_y1), z1(_z1), x2(_x2), y2(_y2), z2(_z2), r(_r), g(_g), b(_b) {} +}; + +/** + * @brief 姿态点数据 + * + * 坐标系约定(右手坐标系): + * - X轴:红色,指向右 + * - Y轴:绿色,指向上 + * - Z轴:蓝色,指向观察者 + * + * 欧拉角旋转顺序:ZYX(Yaw-Pitch-Roll) + * - rz: 绕Z轴旋转(偏航角,Yaw) + * - ry: 绕Y轴旋转(俯仰角,Pitch) + * - rx: 绕X轴旋转(滚转角,Roll) + */ +struct PosePoint +{ + float x, y, z; // 位置 + float rx, ry, rz; // 欧拉角(度) + float scale; // 坐标系大小 + + PosePoint() : x(0), y(0), z(0), rx(0), ry(0), rz(0), scale(10.0f) {} + PosePoint(float _x, float _y, float _z, float _rx, float _ry, float _rz, float _scale = 10.0f) + : x(_x), y(_y), z(_z), rx(_rx), ry(_ry), rz(_rz), scale(_scale) {} +}; + +/** + * @brief 点云 OpenGL 渲染控件 + */ +class PointCloudGLWidget : public QOpenGLWidget, protected QOpenGLFunctions +{ + Q_OBJECT + +public: + explicit PointCloudGLWidget(QWidget* parent = nullptr); + ~PointCloudGLWidget() override; + + void addPointCloud(const PointCloudXYZ& cloud, const QString& name = ""); + void addPointCloud(const PointCloudXYZRGB& cloud, const QString& name = ""); + void clearPointClouds(); + void setPointCloudColor(PointCloudColor color); + void setPointSize(float size); + void resetView(); + + /** + * @brief 设置视角旋转角度(不改变缩放和平移) + * @param rotX X轴旋转角度(俯仰) + * @param rotY Y轴旋转角度(偏航) + * @param rotZ Z轴旋转角度(滚转) + */ + void setViewAngles(float rotX, float rotY, float rotZ = 0.0f); + + QVector getSelectedPoints() const { return m_selectedPoints; } + SelectedLineInfo getSelectedLine() const { return m_selectedLine; } + void clearSelectedPoints(); + void clearSelectedLine(); + float calculateDistance(const SelectedPointInfo& p1, const SelectedPointInfo& p2); + + /** + * @brief 通过线索引选择线(纵向) + */ + bool selectLineByIndex(int lineIndex); + + /** + * @brief 通过点索引选择横向线(所有线的相同索引点) + */ + bool selectHorizontalLineByIndex(int pointIndex); + + /** + * @brief 设置选线模式 + */ + void setLineSelectMode(LineSelectMode mode) { m_lineSelectMode = mode; } + + /** + * @brief 设置欧拉角旋转顺序 + */ + void setEulerRotationOrder(EulerRotationOrder order) { m_eulerRotationOrder = order; } + + /** + * @brief 获取欧拉角旋转顺序 + */ + EulerRotationOrder getEulerRotationOrder() const { return m_eulerRotationOrder; } + + /** + * @brief 设置是否启用测距功能 + */ + void setMeasureDistanceEnabled(bool enabled) { m_measureDistanceEnabled = enabled; } + + /** + * @brief 获取测距功能是否启用 + */ + bool isMeasureDistanceEnabled() const { return m_measureDistanceEnabled; } + + /** + * @brief 获取选中线上的所有点坐标 + * @return 点坐标列表,每个元素为 (x, y, z) + */ + QVector getSelectedLinePoints() const; + + /** + * @brief 设置列表高亮点(与选点功能区分) + * @param point 点坐标,如果为空则清除高亮 + */ + void setListHighlightPoint(const QVector3D& point); + + /** + * @brief 清除列表高亮点 + */ + void clearListHighlightPoint(); + + /** + * @brief 获取第一个点云的数据(用于旋转和保存) + */ + bool getFirstCloudData(PointCloudXYZ& cloud) const; + + /** + * @brief 替换第一个点云(用于显示旋转后的数据) + */ + void replaceFirstCloud(const PointCloudXYZ& cloud, const QString& name); + + /** + * @brief 获取点云数量 + */ + size_t getCloudCount() const { return m_pointClouds.size(); } + + /** + * @brief 对所有点云应用 4x4 变换矩阵 + * @param matrix 4x4 变换矩阵 + */ + void transformAllClouds(const QMatrix4x4& matrix); + + /** + * @brief 添加线段 + */ + void addLineSegments(const QVector& segments); + + /** + * @brief 清除所有线段 + */ + void clearLineSegments(); + + /** + * @brief 添加姿态点 + */ + void addPosePoints(const QVector& poses); + + /** + * @brief 清除所有姿态点 + */ + void clearPosePoints(); + +signals: + void pointSelected(const SelectedPointInfo& point); + void twoPointsSelected(const SelectedPointInfo& p1, const SelectedPointInfo& p2, float distance); + void lineSelected(const SelectedLineInfo& line); + void viewAnglesChanged(float rotX, float rotY, float rotZ); + +protected: + void initializeGL() override; + void resizeGL(int w, int h) override; + void paintGL() override; + void mousePressEvent(QMouseEvent* event) override; + void mouseMoveEvent(QMouseEvent* event) override; + void mouseReleaseEvent(QMouseEvent* event) override; + void wheelEvent(QWheelEvent* event) override; + void keyPressEvent(QKeyEvent* event) override; + +private: + struct PointCloudData + { + std::vector vertices; + std::vector colors; + std::vector lineIndices; // 每个点所属的线索引 + std::vector originalIndices; // 每个显示点在原始点云中的索引(用于计算原始index) + bool hasColor; + bool hasLineInfo; // 是否有线信息 + QString name; + int colorIndex; // 点云颜色索引 + int totalLines; // 总线数 + int pointsPerLine; // 每线点数(网格化点云) + + // VBO 缓冲区 + QOpenGLBuffer vertexBuffer; // 顶点 VBO + QOpenGLBuffer colorBuffer; // 颜色 VBO + bool vboCreated; // VBO 是否已创建 + + PointCloudData() + : hasColor(false), hasLineInfo(false), colorIndex(0) + , totalLines(0), pointsPerLine(0) + , vertexBuffer(QOpenGLBuffer::VertexBuffer) + , colorBuffer(QOpenGLBuffer::VertexBuffer) + , vboCreated(false) + {} + + // 移动构造函数(QOpenGLBuffer 不支持拷贝) + PointCloudData(PointCloudData&& other) noexcept + : vertices(std::move(other.vertices)) + , colors(std::move(other.colors)) + , lineIndices(std::move(other.lineIndices)) + , originalIndices(std::move(other.originalIndices)) + , hasColor(other.hasColor) + , hasLineInfo(other.hasLineInfo) + , name(std::move(other.name)) + , colorIndex(other.colorIndex) + , totalLines(other.totalLines) + , pointsPerLine(other.pointsPerLine) + , vertexBuffer(QOpenGLBuffer::VertexBuffer) + , colorBuffer(QOpenGLBuffer::VertexBuffer) + , vboCreated(false) + { + // VBO 需要在 GL 上下文中重建,标记为未创建 + other.vboCreated = false; + } + + PointCloudData& operator=(PointCloudData&& other) noexcept + { + if (this != &other) { + // 释放自身 VBO + if (vboCreated) { + vertexBuffer.destroy(); + colorBuffer.destroy(); + vboCreated = false; + } + vertices = std::move(other.vertices); + colors = std::move(other.colors); + lineIndices = std::move(other.lineIndices); + originalIndices = std::move(other.originalIndices); + hasColor = other.hasColor; + hasLineInfo = other.hasLineInfo; + name = std::move(other.name); + colorIndex = other.colorIndex; + totalLines = other.totalLines; + pointsPerLine = other.pointsPerLine; + // VBO 需要重建 + other.vboCreated = false; + } + return *this; + } + + // 禁止拷贝 + PointCloudData(const PointCloudData&) = delete; + PointCloudData& operator=(const PointCloudData&) = delete; + }; + + void computeBoundingBox(); + void setCurrentColor(PointCloudColor color); + void setColorByIndex(int colorIndex); // 根据索引设置颜色 + SelectedPointInfo pickPoint(int screenX, int screenY); + void drawSelectedPoints(); + void drawMeasurementLine(); + void drawAxis(); + void drawSelectedLine(); // 绘制选中的线 + void drawLineSegments(); // 绘制线段 + void drawPosePoints(); // 绘制姿态点 + void uploadToVBO(PointCloudData& data); // 上传数据到 VBO + void releaseVBO(PointCloudData& data); // 释放 VBO 资源 + + std::vector m_pointClouds; + + QMatrix4x4 m_projection; + QMatrix4x4 m_view; + QMatrix4x4 m_model; + + float m_distance; + float m_rotationX; + float m_rotationY; + float m_rotationZ; + QVector3D m_center; + QVector3D m_pan; + QVector3D m_minBound; + QVector3D m_maxBound; + + QPoint m_lastMousePos; + bool m_leftButtonPressed; + bool m_rightButtonPressed; + bool m_middleButtonPressed; + + PointCloudColor m_currentColor; + float m_pointSize; + + LineSelectMode m_lineSelectMode; + EulerRotationOrder m_eulerRotationOrder; + bool m_measureDistanceEnabled; + QVector m_selectedPoints; + SelectedLineInfo m_selectedLine; + static const int MAX_SELECTED_POINTS = 2; + + // 列表高亮点(与选点功能区分) + bool m_hasListHighlightPoint; + QVector3D m_listHighlightPoint; + + int m_colorIndex; // 颜色轮换索引 + static const int COLOR_COUNT = 7; // 可用颜色数量 + + // 线段和姿态点数据 + QVector m_lineSegments; + QVector m_posePoints; +}; + +#endif // POINT_CLOUD_GL_WIDGET_H diff --git a/CloudView/Src/CloudViewMainWindow.cpp b/CloudView/Src/CloudViewMainWindow.cpp new file mode 100644 index 0000000..2d93329 --- /dev/null +++ b/CloudView/Src/CloudViewMainWindow.cpp @@ -0,0 +1,1670 @@ +#include "CloudViewMainWindow.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "VrLog.h" + +CloudViewMainWindow::CloudViewMainWindow(QWidget* parent) + : QMainWindow(parent) + , m_glWidget(nullptr) + , m_converter(std::make_unique()) + , m_cloudCount(0) + , m_currentLineNum(0) + , m_currentLinePtNum(0) + , m_linePointsDialog(nullptr) + , m_linePointsTable(nullptr) +{ + setupUI(); + LOG_INFO("CloudViewMainWindow initialized\n"); +} + +CloudViewMainWindow::~CloudViewMainWindow() +{ +} + +void CloudViewMainWindow::setupUI() +{ + // 创建中央控件 + QWidget* centralWidget = new QWidget(this); + setCentralWidget(centralWidget); + + // 创建主布局 + QHBoxLayout* mainLayout = new QHBoxLayout(centralWidget); + mainLayout->setContentsMargins(5, 5, 5, 5); + mainLayout->setSpacing(5); + + // 创建分割器 + QSplitter* splitter = new QSplitter(Qt::Horizontal, centralWidget); + + // 左侧:点云显示区域 + QWidget* viewerArea = createViewerArea(); + splitter->addWidget(viewerArea); + + // 右侧:控制面板 + QWidget* controlPanel = createControlPanel(); + splitter->addWidget(controlPanel); + + // 设置分割器初始大小(左侧 70%,右侧 30%) + splitter->setSizes({700, 300}); + + mainLayout->addWidget(splitter); + + // 状态栏 + statusBar()->showMessage("就绪"); +} + +QWidget* CloudViewMainWindow::createViewerArea() +{ + QWidget* widget = new QWidget(this); + QVBoxLayout* layout = new QVBoxLayout(widget); + layout->setContentsMargins(0, 0, 0, 0); + + m_glWidget = new PointCloudGLWidget(widget); + m_glWidget->setMinimumSize(400, 300); // 设置最小尺寸 + m_glWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + layout->addWidget(m_glWidget); + + // 连接信号 + connect(m_glWidget, &PointCloudGLWidget::pointSelected, + this, &CloudViewMainWindow::onPointSelected); + connect(m_glWidget, &PointCloudGLWidget::twoPointsSelected, + this, &CloudViewMainWindow::onTwoPointsSelected); + connect(m_glWidget, &PointCloudGLWidget::lineSelected, + this, &CloudViewMainWindow::onLineSelected); + connect(m_glWidget, &PointCloudGLWidget::viewAnglesChanged, + this, &CloudViewMainWindow::onViewAnglesChanged); + + return widget; +} + +QWidget* CloudViewMainWindow::createControlPanel() +{ + QWidget* widget = new QWidget(this); + widget->setMaximumWidth(350); + QVBoxLayout* layout = new QVBoxLayout(widget); + layout->setContentsMargins(5, 5, 5, 5); + layout->setSpacing(5); + + // 文件操作组 + layout->addWidget(createFileGroup()); + + // 视图方向组 + layout->addWidget(createViewGroup()); + + // 创建 Tab 控件 + QTabWidget* tabWidget = new QTabWidget(widget); + tabWidget->addTab(createMeasurePage(), "选点测距"); + tabWidget->addTab(createLinePage(), "选线"); + tabWidget->addTab(createTransformPage(), "矩阵变换"); + layout->addWidget(tabWidget); + + // 点云列表组 + layout->addWidget(createCloudListGroup()); + + // 添加弹性空间 + layout->addStretch(); + + return widget; +} + +QGroupBox* CloudViewMainWindow::createFileGroup() +{ + QGroupBox* group = new QGroupBox("文件操作", this); + group->setMaximumWidth(350); + QVBoxLayout* layout = new QVBoxLayout(group); + layout->setSpacing(3); + layout->setContentsMargins(5, 5, 5, 5); + + m_btnOpenFile = new QPushButton("打开点云", group); + m_btnOpenFile->setMinimumHeight(24); + m_btnOpenFile->setMaximumHeight(24); + connect(m_btnOpenFile, &QPushButton::clicked, this, &CloudViewMainWindow::onOpenFile); + layout->addWidget(m_btnOpenFile); + + m_btnOpenSegment = new QPushButton("打开线段 {x,y,z}-{x,y,z}", group); + m_btnOpenSegment->setMinimumHeight(24); + m_btnOpenSegment->setMaximumHeight(24); + connect(m_btnOpenSegment, &QPushButton::clicked, this, &CloudViewMainWindow::onOpenSegmentFile); + layout->addWidget(m_btnOpenSegment); + + m_btnOpenPose = new QPushButton("打开姿态点 {x,y,z}-{r,p,y}", group); + m_btnOpenPose->setMinimumHeight(24); + m_btnOpenPose->setMaximumHeight(24); + connect(m_btnOpenPose, &QPushButton::clicked, this, &CloudViewMainWindow::onOpenPoseFile); + layout->addWidget(m_btnOpenPose); + + m_btnClearAll = new QPushButton("清除所有", group); + m_btnClearAll->setMinimumHeight(24); + m_btnClearAll->setMaximumHeight(24); + connect(m_btnClearAll, &QPushButton::clicked, this, &CloudViewMainWindow::onClearAll); + layout->addWidget(m_btnClearAll); + + return group; +} + +QGroupBox* CloudViewMainWindow::createViewGroup() +{ + QGroupBox* group = new QGroupBox("视图方向", this); + group->setMaximumWidth(350); + QVBoxLayout* mainLayout = new QVBoxLayout(group); + mainLayout->setSpacing(4); + mainLayout->setContentsMargins(5, 5, 5, 5); + + // 视角预设按钮,使用水平布局 + struct ViewPreset { + const char* name; + float rotX; + float rotY; + float rotZ; + }; + + ViewPreset presets[] = { + {"正视", 0.0f, 0.0f, 0.0f}, + {"后视", 0.0f, 180.0f, 0.0f}, + {"左侧", 0.0f, -90.0f, 0.0f}, + {"右侧", 0.0f, 90.0f, 0.0f}, + {"俯视", -90.0f, 0.0f, 0.0f}, + {"仰视", 90.0f, 0.0f, 0.0f}, + }; + + QHBoxLayout* btnLayout = new QHBoxLayout(); + btnLayout->setSpacing(3); + for (const auto& preset : presets) { + QPushButton* btn = new QPushButton(preset.name, group); + btn->setMinimumHeight(24); + btn->setMaximumHeight(24); + float rx = preset.rotX; + float ry = preset.rotY; + float rz = preset.rotZ; + connect(btn, &QPushButton::clicked, this, [this, rx, ry, rz]() { + m_glWidget->setViewAngles(rx, ry, rz); + // 更新显示的角度值 + m_editRotX->setText(QString::number(rx, 'f', 1)); + m_editRotY->setText(QString::number(ry, 'f', 1)); + m_editRotZ->setText(QString::number(rz, 'f', 1)); + }); + btnLayout->addWidget(btn); + } + mainLayout->addLayout(btnLayout); + + QLabel* lblTip2 = new QLabel("左键旋转XY,Alt+左键或右键旋转Z轴,中键拖动平移", group); + lblTip2->setWordWrap(true); + lblTip2->setStyleSheet("color: gray; font-size: 12px;"); + mainLayout->addWidget(lblTip2); + + // 旋转角度输入(三行布局) + QGridLayout* angleGrid = new QGridLayout(); + angleGrid->setSpacing(5); + angleGrid->setContentsMargins(0, 0, 0, 0); + + // RotX + QLabel* lblRotX = new QLabel("RotX:", group); + angleGrid->addWidget(lblRotX, 0, 0); + m_editRotX = new QLineEdit("0.0", group); + m_editRotX->setMaximumWidth(60); + m_editRotX->setMaximumHeight(24); + angleGrid->addWidget(m_editRotX, 0, 1); + + // RotY + QLabel* lblRotY = new QLabel("RotY:", group); + angleGrid->addWidget(lblRotY, 0, 2); + m_editRotY = new QLineEdit("0.0", group); + m_editRotY->setMaximumWidth(60); + m_editRotY->setMaximumHeight(24); + angleGrid->addWidget(m_editRotY, 0, 3); + + // RotZ + QLabel* lblRotZ = new QLabel("RotZ:", group); + angleGrid->addWidget(lblRotZ, 0, 4); + m_editRotZ = new QLineEdit("0.0", group); + m_editRotZ->setMaximumWidth(60); + m_editRotZ->setMaximumHeight(24); + angleGrid->addWidget(m_editRotZ, 0, 5); + + // 应用按钮 + QPushButton* btnApply = new QPushButton("应用", group); + btnApply->setMaximumWidth(50); + btnApply->setMaximumHeight(24); + connect(btnApply, &QPushButton::clicked, this, [this]() { + bool okX, okY, okZ; + float rotX = m_editRotX->text().toFloat(&okX); + float rotY = m_editRotY->text().toFloat(&okY); + float rotZ = m_editRotZ->text().toFloat(&okZ); + if (okX && okY && okZ) { + m_glWidget->setViewAngles(rotX, rotY, rotZ); + } + }); + angleGrid->addWidget(btnApply, 0, 6); + + mainLayout->addLayout(angleGrid); + + return group; +} + +QWidget* CloudViewMainWindow::createMeasurePage() +{ + QWidget* page = new QWidget(this); + QVBoxLayout* layout = new QVBoxLayout(page); + layout->setContentsMargins(0, 5, 0, 0); + layout->addWidget(createMeasureGroup()); + layout->addStretch(); + return page; +} + +QWidget* CloudViewMainWindow::createLinePage() +{ + QWidget* page = new QWidget(this); + QVBoxLayout* layout = new QVBoxLayout(page); + layout->setContentsMargins(0, 5, 0, 0); + + // 选线拟合组 + layout->addWidget(createLineGroup()); + + // 输入线段组 + QGroupBox* inputLineGroup = new QGroupBox("输入线段", page); + QVBoxLayout* inputLayout = new QVBoxLayout(inputLineGroup); + + // 提示 + QLabel* lblTip = new QLabel("输入两点坐标显示线段", inputLineGroup); + lblTip->setStyleSheet("color: gray; font-size: 10px;"); + inputLayout->addWidget(lblTip); + + // 点1坐标 + QLabel* lblPoint1 = new QLabel("点1:", inputLineGroup); + lblPoint1->setStyleSheet("font-weight: bold;"); + inputLayout->addWidget(lblPoint1); + + QHBoxLayout* p1Layout = new QHBoxLayout(); + p1Layout->addWidget(new QLabel("X:", inputLineGroup)); + m_editLineX1 = new QLineEdit("0.0", inputLineGroup); + m_editLineX1->setMaximumWidth(70); + p1Layout->addWidget(m_editLineX1); + + p1Layout->addWidget(new QLabel("Y:", inputLineGroup)); + m_editLineY1 = new QLineEdit("0.0", inputLineGroup); + m_editLineY1->setMaximumWidth(70); + p1Layout->addWidget(m_editLineY1); + + p1Layout->addWidget(new QLabel("Z:", inputLineGroup)); + m_editLineZ1 = new QLineEdit("0.0", inputLineGroup); + m_editLineZ1->setMaximumWidth(70); + p1Layout->addWidget(m_editLineZ1); + inputLayout->addLayout(p1Layout); + + // 点2坐标 + QLabel* lblPoint2 = new QLabel("点2:", inputLineGroup); + lblPoint2->setStyleSheet("font-weight: bold;"); + inputLayout->addWidget(lblPoint2); + + QHBoxLayout* p2Layout = new QHBoxLayout(); + p2Layout->addWidget(new QLabel("X:", inputLineGroup)); + m_editLineX2 = new QLineEdit("100.0", inputLineGroup); + m_editLineX2->setMaximumWidth(70); + p2Layout->addWidget(m_editLineX2); + + p2Layout->addWidget(new QLabel("Y:", inputLineGroup)); + m_editLineY2 = new QLineEdit("100.0", inputLineGroup); + m_editLineY2->setMaximumWidth(70); + p2Layout->addWidget(m_editLineY2); + + p2Layout->addWidget(new QLabel("Z:", inputLineGroup)); + m_editLineZ2 = new QLineEdit("100.0", inputLineGroup); + m_editLineZ2->setMaximumWidth(70); + p2Layout->addWidget(m_editLineZ2); + inputLayout->addLayout(p2Layout); + + // 按钮 + QHBoxLayout* btnLayout = new QHBoxLayout(); + m_btnShowLine = new QPushButton("显示线段", inputLineGroup); + connect(m_btnShowLine, &QPushButton::clicked, this, &CloudViewMainWindow::onShowInputLine); + btnLayout->addWidget(m_btnShowLine); + + m_btnClearLine2 = new QPushButton("清除线段", inputLineGroup); + connect(m_btnClearLine2, &QPushButton::clicked, this, &CloudViewMainWindow::onClearInputLine); + btnLayout->addWidget(m_btnClearLine2); + inputLayout->addLayout(btnLayout); + + layout->addWidget(inputLineGroup); + layout->addStretch(); + return page; +} + +QGroupBox* CloudViewMainWindow::createMeasureGroup() +{ + QGroupBox* group = new QGroupBox("选点测距", this); + group->setMaximumWidth(350); + QVBoxLayout* layout = new QVBoxLayout(group); + layout->setSpacing(4); + layout->setContentsMargins(5, 5, 5, 5); + + // 操作说明 + QLabel* lblTip = new QLabel("Ctrl+左键点击点云选择点", group); + lblTip->setWordWrap(true); + lblTip->setStyleSheet("color: gray; font-size: 12px;"); + layout->addWidget(lblTip); + // 测距复选框 + m_cbMeasureDistance = new QCheckBox("启用测距", group); + m_cbMeasureDistance->setChecked(false); + connect(m_cbMeasureDistance, &QCheckBox::toggled, this, [this](bool checked) { + m_glWidget->setMeasureDistanceEnabled(checked); + m_glWidget->clearSelectedPoints(); + m_lblPoint1->setText("点1: --"); + m_lblPoint2->setText("点2: --"); + m_lblDistance->setText("--"); + }); + layout->addWidget(m_cbMeasureDistance); + + // 清除选点按钮 + m_btnClearPoints = new QPushButton("清除选点", group); + m_btnClearPoints->setMinimumHeight(24); + connect(m_btnClearPoints, &QPushButton::clicked, this, &CloudViewMainWindow::onClearSelectedPoints); + layout->addWidget(m_btnClearPoints); + + // 分隔线 + QFrame* line1 = new QFrame(group); + line1->setFrameShape(QFrame::HLine); + line1->setFrameShadow(QFrame::Sunken); + layout->addWidget(line1); + + // 点1信息(坐标直接显示在标题后) + m_lblPoint1 = new QLabel("点1: --", group); + m_lblPoint1->setWordWrap(true); + m_lblPoint1->setStyleSheet("font-weight: bold; font-size: 11px;"); + layout->addWidget(m_lblPoint1); + + // 点1姿态输入(紧凑布局) + QHBoxLayout* pose1Layout = new QHBoxLayout(); + pose1Layout->setSpacing(5); + pose1Layout->addWidget(new QLabel("RX:", group)); + m_editRx1 = new QLineEdit("0.0", group); + m_editRx1->setMaximumWidth(50); + pose1Layout->addWidget(m_editRx1); + + pose1Layout->addWidget(new QLabel("RY:", group)); + m_editRy1 = new QLineEdit("0.0", group); + m_editRy1->setMaximumWidth(50); + pose1Layout->addWidget(m_editRy1); + + pose1Layout->addWidget(new QLabel("RZ:", group)); + m_editRz1 = new QLineEdit("0.0", group); + m_editRz1->setMaximumWidth(50); + pose1Layout->addWidget(m_editRz1); + pose1Layout->addStretch(); + layout->addLayout(pose1Layout); + + m_btnShowPose1 = new QPushButton("显示点1姿态", group); + m_btnShowPose1->setMinimumHeight(24); + connect(m_btnShowPose1, &QPushButton::clicked, this, &CloudViewMainWindow::onShowPose1); + layout->addWidget(m_btnShowPose1); + + // 分隔线 + QFrame* line2 = new QFrame(group); + line2->setFrameShape(QFrame::HLine); + line2->setFrameShadow(QFrame::Sunken); + layout->addWidget(line2); + + // 点2信息(坐标直接显示在标题后) + m_lblPoint2 = new QLabel("点2: --", group); + m_lblPoint2->setWordWrap(true); + m_lblPoint2->setStyleSheet("font-weight: bold; font-size: 11px;"); + layout->addWidget(m_lblPoint2); + + // 点2姿态输入(紧凑布局) + QHBoxLayout* pose2Layout = new QHBoxLayout(); + pose2Layout->setSpacing(5); + pose2Layout->addWidget(new QLabel("RX:", group)); + m_editRx2 = new QLineEdit("0.0", group); + m_editRx2->setMaximumWidth(50); + pose2Layout->addWidget(m_editRx2); + + pose2Layout->addWidget(new QLabel("RY:", group)); + m_editRy2 = new QLineEdit("0.0", group); + m_editRy2->setMaximumWidth(50); + pose2Layout->addWidget(m_editRy2); + + pose2Layout->addWidget(new QLabel("RZ:", group)); + m_editRz2 = new QLineEdit("0.0", group); + m_editRz2->setMaximumWidth(50); + pose2Layout->addWidget(m_editRz2); + pose2Layout->addStretch(); + layout->addLayout(pose2Layout); + + m_btnShowPose2 = new QPushButton("显示点2姿态", group); + m_btnShowPose2->setMinimumHeight(24); + connect(m_btnShowPose2, &QPushButton::clicked, this, &CloudViewMainWindow::onShowPose2); + layout->addWidget(m_btnShowPose2); + + // 分隔线 + QFrame* line3 = new QFrame(group); + line3->setFrameShape(QFrame::HLine); + line3->setFrameShadow(QFrame::Sunken); + layout->addWidget(line3); + + // 欧拉角旋转顺序选择 + QLabel* lblEulerOrder = new QLabel("欧拉角旋转顺序:", group); + lblEulerOrder->setStyleSheet("font-weight: bold; font-size: 10px;"); + layout->addWidget(lblEulerOrder); + + m_comboEulerOrder = new QComboBox(group); + m_comboEulerOrder->addItem("ZYX (Yaw-Pitch-Roll)", static_cast(EulerRotationOrder::ZYX)); + m_comboEulerOrder->addItem("XYZ (Roll-Pitch-Yaw)", static_cast(EulerRotationOrder::XYZ)); + m_comboEulerOrder->addItem("ZXY (Yaw-Roll-Pitch)", static_cast(EulerRotationOrder::ZXY)); + m_comboEulerOrder->addItem("YXZ (Pitch-Roll-Yaw)", static_cast(EulerRotationOrder::YXZ)); + m_comboEulerOrder->addItem("XZY (Roll-Yaw-Pitch)", static_cast(EulerRotationOrder::XZY)); + m_comboEulerOrder->addItem("YZX (Pitch-Yaw-Roll)", static_cast(EulerRotationOrder::YZX)); + m_comboEulerOrder->setCurrentIndex(0); + m_comboEulerOrder->setMaximumHeight(24); + connect(m_comboEulerOrder, QOverload::of(&QComboBox::currentIndexChanged), + this, &CloudViewMainWindow::onEulerOrderChanged); + layout->addWidget(m_comboEulerOrder); + + // 分隔线 + QFrame* line4 = new QFrame(group); + line4->setFrameShape(QFrame::HLine); + line4->setFrameShadow(QFrame::Sunken); + layout->addWidget(line4); + + // 距离信息 + QHBoxLayout* distLayout = new QHBoxLayout(); + QLabel* lblDistTitle = new QLabel("距离:", group); + lblDistTitle->setStyleSheet("font-size: 10px;"); + m_lblDistance = new QLabel("--", group); + m_lblDistance->setStyleSheet("font-weight: bold; color: green; font-size: 10px;"); + distLayout->addWidget(lblDistTitle); + distLayout->addWidget(m_lblDistance, 1); + layout->addLayout(distLayout); + + return group; +} + +QGroupBox* CloudViewMainWindow::createLineGroup() +{ + QGroupBox* group = new QGroupBox("选线", this); + group->setMaximumWidth(400); + QVBoxLayout* layout = new QVBoxLayout(group); + layout->setSpacing(3); + layout->setContentsMargins(5, 5, 5, 5); + + // 操作说明 + QLabel* lblTip = new QLabel("Shift+左键点击点云选择线", group); + lblTip->setWordWrap(true); + lblTip->setStyleSheet("color: gray; font-size: 9px;"); + layout->addWidget(lblTip); + + // 选线模式选择 + QHBoxLayout* modeLayout = new QHBoxLayout(); + modeLayout->setSpacing(5); + m_rbVertical = new QRadioButton("纵向", group); + m_rbHorizontal = new QRadioButton("横向", group); + m_rbVertical->setChecked(true); + connect(m_rbVertical, &QRadioButton::toggled, this, &CloudViewMainWindow::onLineSelectModeChanged); + modeLayout->addWidget(m_rbVertical); + modeLayout->addWidget(m_rbHorizontal); + modeLayout->addStretch(); + layout->addLayout(modeLayout); + + // 输入索引选择 + QHBoxLayout* inputLayout = new QHBoxLayout(); + inputLayout->setSpacing(5); + m_lineNumberInput = new QLineEdit(group); + m_lineNumberInput->setPlaceholderText("输入索引"); + m_lineNumberInput->setMaximumHeight(24); + m_btnSelectByNumber = new QPushButton("选择", group); + m_btnSelectByNumber->setMaximumHeight(24); + m_btnSelectByNumber->setMaximumWidth(50); + connect(m_btnSelectByNumber, &QPushButton::clicked, this, &CloudViewMainWindow::onSelectLineByNumber); + inputLayout->addWidget(m_lineNumberInput, 1); + inputLayout->addWidget(m_btnSelectByNumber); + layout->addLayout(inputLayout); + + // 清除选线按钮 + m_btnClearLine = new QPushButton("清除选线", group); + m_btnClearLine->setMinimumHeight(24); + connect(m_btnClearLine, &QPushButton::clicked, this, &CloudViewMainWindow::onClearLinePoints); + layout->addWidget(m_btnClearLine); + + // 显示线上点按钮 + m_btnShowLinePoints = new QPushButton("显示线上点", group); + m_btnShowLinePoints->setMinimumHeight(24); + connect(m_btnShowLinePoints, &QPushButton::clicked, this, &CloudViewMainWindow::onShowLinePoints); + layout->addWidget(m_btnShowLinePoints); + + // 线索引信息 + QHBoxLayout* indexLayout = new QHBoxLayout(); + QLabel* lblIndexTitle = new QLabel("索引:", group); + lblIndexTitle->setStyleSheet("font-size: 10px;"); + m_lblLineIndex = new QLabel("--", group); + m_lblLineIndex->setStyleSheet("font-size: 10px;"); + indexLayout->addWidget(lblIndexTitle); + indexLayout->addWidget(m_lblLineIndex, 1); + layout->addLayout(indexLayout); + + // 点数信息 + QHBoxLayout* countLayout = new QHBoxLayout(); + QLabel* lblCountTitle = new QLabel("点数:", group); + lblCountTitle->setStyleSheet("font-size: 10px;"); + m_lblLinePointCount = new QLabel("--", group); + m_lblLinePointCount->setStyleSheet("font-size: 10px;"); + countLayout->addWidget(lblCountTitle); + countLayout->addWidget(m_lblLinePointCount, 1); + layout->addLayout(countLayout); + + return group; +} + +QGroupBox* CloudViewMainWindow::createCloudListGroup() +{ + QGroupBox* group = new QGroupBox("点云列表", this); + group->setMaximumWidth(350); + QVBoxLayout* layout = new QVBoxLayout(group); + layout->setSpacing(3); + layout->setContentsMargins(5, 5, 5, 5); + + m_cloudList = new QListWidget(group); + m_cloudList->setMinimumHeight(70); + layout->addWidget(m_cloudList); + + return group; +} + +void CloudViewMainWindow::onOpenFile() +{ + QString fileName = QFileDialog::getOpenFileName( + this, + "打开点云文件", + QString(), + "点云文件 (*.pcd *.txt);;PCD 文件 (*.pcd);;TXT 文件 (*.txt);;所有文件 (*.*)" + ); + + if (fileName.isEmpty()) { + return; + } + + statusBar()->showMessage("正在加载点云..."); + + QFileInfo fileInfo(fileName); + QString ext = fileInfo.suffix().toLower(); + QString cloudName = QString("Cloud_%1 (%2)").arg(++m_cloudCount).arg(fileInfo.fileName()); + + // 统一使用 PointCloudXYZRGB 加载,支持带颜色和不带颜色的文件 + PointCloudXYZRGB rgbCloud; + int result = m_converter->loadFromFile(fileName.toStdString(), rgbCloud); + + if (result != 0) { + QMessageBox::critical(this, "错误", QString("加载点云失败: %1").arg(QString::fromStdString(m_converter->getLastError()))); + statusBar()->showMessage("加载失败"); + return; + } + + // 保存原始完整点云 XYZ(用于旋转/线上点等功能) + m_originalCloud.clear(); + m_originalCloud.reserve(rgbCloud.size()); + for (size_t i = 0; i < rgbCloud.points.size(); ++i) { + const auto& pt = rgbCloud.points[i]; + Point3D xyzPt(pt.x, pt.y, pt.z); + int lineIdx = (i < rgbCloud.lineIndices.size()) ? rgbCloud.lineIndices[i] : 0; + m_originalCloud.push_back(xyzPt, lineIdx); + } + + // 根据是否有颜色选择显示方式 + bool hadColor = m_converter->lastLoadHadColor(); + + // PCD 文件:检查是否有非白色的颜色数据来判断 + if (ext == "pcd") { + hadColor = false; + for (size_t i = 0; i < rgbCloud.points.size(); ++i) { + const auto& pt = rgbCloud.points[i]; + if (pt.r != 255 || pt.g != 255 || pt.b != 255) { + hadColor = true; + break; + } + } + } + + if (hadColor) { + // 有颜色数据:使用 addPointCloud(PointCloudXYZRGB) 显示原始颜色 + m_glWidget->addPointCloud(rgbCloud, cloudName); + LOG_INFO("[CloudView] Loaded with original color, points: %zu\n", rgbCloud.size()); + } else { + // 无颜色数据:使用 addPointCloud(PointCloudXYZ) 显示(颜色表轮换) + m_glWidget->addPointCloud(m_originalCloud, cloudName); + LOG_INFO("[CloudView] Loaded without color (color table), points: %zu\n", m_originalCloud.size()); + } + + // 保存线信息(用于旋转功能) + int lineCount = m_converter->getLoadedLineCount(); + if (lineCount > 0) { + m_currentLineNum = lineCount; + m_currentLinePtNum = static_cast(m_converter->getLoadedPointCount()) / lineCount; + } else { + m_currentLineNum = 0; + m_currentLinePtNum = 0; + } + + // 添加到列表 + QString itemText; + if (lineCount > 0) { + itemText = QString("%1 - %2 点, %3 线").arg(cloudName).arg(m_converter->getLoadedPointCount()).arg(lineCount); + } else { + itemText = QString("%1 - %2 点").arg(cloudName).arg(m_converter->getLoadedPointCount()); + } + if (hadColor) { + itemText += " [彩色]"; + } + m_cloudList->addItem(itemText); + + statusBar()->showMessage(QString("已加载 %1 个点%2").arg(m_converter->getLoadedPointCount()).arg(hadColor ? " (彩色)" : "")); +} + +void CloudViewMainWindow::onOpenSegmentFile() +{ + QString fileName = QFileDialog::getOpenFileName( + this, + "打开线段文件", + QString(), + "文本文件 (*.txt);;所有文件 (*.*)" + ); + + if (fileName.isEmpty()) { + return; + } + + statusBar()->showMessage("正在加载线段..."); + + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + QMessageBox::critical(this, "错误", QString("无法打开文件: %1").arg(fileName)); + statusBar()->showMessage("加载失败"); + return; + } + + QVector segments; + QTextStream in(&file); + int lineNum = 0; + int validCount = 0; + + while (!in.atEnd()) { + QString line = in.readLine().trimmed(); + lineNum++; + + // 跳过空行和注释 + if (line.isEmpty() || line.startsWith('#')) { + continue; + } + + // 解析格式:{x,y,z}-{x,y,z} + QRegExp regex("\\{([^}]+)\\}-\\{([^}]+)\\}"); + if (regex.indexIn(line) == -1) { + LOG_WARN("[CloudView] Line %d: Invalid format, expected {x,y,z}-{x,y,z}\n", lineNum); + continue; + } + + QString point1Str = regex.cap(1); + QString point2Str = regex.cap(2); + + QStringList p1 = point1Str.split(','); + QStringList p2 = point2Str.split(','); + + if (p1.size() != 3 || p2.size() != 3) { + LOG_WARN("[CloudView] Line %d: Invalid point format\n", lineNum); + continue; + } + + bool ok = true; + float x1 = p1[0].toFloat(&ok); if (!ok) continue; + float y1 = p1[1].toFloat(&ok); if (!ok) continue; + float z1 = p1[2].toFloat(&ok); if (!ok) continue; + float x2 = p2[0].toFloat(&ok); if (!ok) continue; + float y2 = p2[1].toFloat(&ok); if (!ok) continue; + float z2 = p2[2].toFloat(&ok); if (!ok) continue; + + // 默认白色 + segments.append(LineSegment(x1, y1, z1, x2, y2, z2, 1.0f, 1.0f, 1.0f)); + validCount++; + } + + file.close(); + + if (segments.isEmpty()) { + QMessageBox::warning(this, "警告", "文件中没有有效的线段数据"); + statusBar()->showMessage("加载失败"); + return; + } + + m_glWidget->addLineSegments(segments); + statusBar()->showMessage(QString("已加载 %1 条线段").arg(validCount)); + LOG_INFO("[CloudView] Loaded %d line segments from %s\n", validCount, fileName.toStdString().c_str()); +} + +void CloudViewMainWindow::onOpenPoseFile() +{ + QString fileName = QFileDialog::getOpenFileName( + this, + "打开姿态点文件", + QString(), + "文本文件 (*.txt);;所有文件 (*.*)" + ); + + if (fileName.isEmpty()) { + return; + } + + statusBar()->showMessage("正在加载姿态点..."); + + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + QMessageBox::critical(this, "错误", QString("无法打开文件: %1").arg(fileName)); + statusBar()->showMessage("加载失败"); + return; + } + + QVector poses; + QTextStream in(&file); + int lineNum = 0; + int validCount = 0; + + while (!in.atEnd()) { + QString line = in.readLine().trimmed(); + lineNum++; + + // 跳过空行和注释 + if (line.isEmpty() || line.startsWith('#')) { + continue; + } + + // 解析格式:{x,y,z}-{r,p,y} + QRegExp regex("\\{([^}]+)\\}-\\{([^}]+)\\}"); + if (regex.indexIn(line) == -1) { + LOG_WARN("[CloudView] Line %d: Invalid format, expected {x,y,z}-{r,p,y}\n", lineNum); + continue; + } + + QString posStr = regex.cap(1); + QString rotStr = regex.cap(2); + + QStringList pos = posStr.split(','); + QStringList rot = rotStr.split(','); + + if (pos.size() != 3 || rot.size() != 3) { + LOG_WARN("[CloudView] Line %d: Invalid point format\n", lineNum); + continue; + } + + bool ok = true; + float x = pos[0].toFloat(&ok); if (!ok) continue; + float y = pos[1].toFloat(&ok); if (!ok) continue; + float z = pos[2].toFloat(&ok); if (!ok) continue; + float roll = rot[0].toFloat(&ok); if (!ok) continue; + float pitch = rot[1].toFloat(&ok); if (!ok) continue; + float yaw = rot[2].toFloat(&ok); if (!ok) continue; + + // 固定大小为10 + float scale = 10.0f; + + poses.append(PosePoint(x, y, z, roll, pitch, yaw, scale)); + validCount++; + } + + file.close(); + + if (poses.isEmpty()) { + QMessageBox::warning(this, "警告", "文件中没有有效的姿态点数据"); + statusBar()->showMessage("加载失败"); + return; + } + + m_glWidget->addPosePoints(poses); + statusBar()->showMessage(QString("已加载 %1 个姿态点").arg(validCount)); + LOG_INFO("[CloudView] Loaded %d pose points from %s\n", validCount, fileName.toStdString().c_str()); +} + +void CloudViewMainWindow::onClearAll() +{ + m_glWidget->clearPointClouds(); + m_cloudList->clear(); + m_cloudCount = 0; + m_currentLineNum = 0; + m_currentLinePtNum = 0; + m_originalCloud.clear(); + + // 清除选点信息 + m_lblPoint1->setText("点1: --"); + m_lblPoint2->setText("点2: --"); + m_lblDistance->setText("--"); + + // 清除选线信息 + m_lblLineIndex->setText("--"); + m_lblLinePointCount->setText("--"); + + statusBar()->showMessage("已清除所有数据"); +} + +void CloudViewMainWindow::onResetView() +{ + m_glWidget->resetView(); + statusBar()->showMessage("视图已重置"); +} + +void CloudViewMainWindow::onClearSelectedPoints() +{ + m_glWidget->clearSelectedPoints(); + m_glWidget->clearPosePoints(); // 清除选点时也清除姿态 + m_lblPoint1->setText("点1: --"); + m_lblPoint2->setText("点2: --"); + m_lblDistance->setText("--"); + statusBar()->showMessage("已清除选中的点"); +} + +void CloudViewMainWindow::onPointSelected(const SelectedPointInfo& point) +{ + if (!point.valid) { + return; + } + + // 选择新点时清除之前的姿态显示 + m_glWidget->clearPosePoints(); + + updateSelectedPointsDisplay(); + + // 状态栏显示:坐标、线号、索引号 + QString statusMsg = QString("选中点: (%1, %2, %3)") + .arg(point.x, 0, 'f', 3) + .arg(point.y, 0, 'f', 3) + .arg(point.z, 0, 'f', 3); + if (point.lineIndex >= 0) { + statusMsg += QString(" | 线号: %1").arg(point.lineIndex); + if (point.pointIndexInLine >= 0) { + statusMsg += QString(" | 索引号: %1").arg(point.pointIndexInLine); + } + } + statusBar()->showMessage(statusMsg); +} + +void CloudViewMainWindow::onTwoPointsSelected(const SelectedPointInfo& p1, const SelectedPointInfo& p2, float distance) +{ + updateSelectedPointsDisplay(); + m_lblDistance->setText(QString("%1 mm").arg(distance, 0, 'f', 3)); + statusBar()->showMessage(QString("测量距离: %1 mm").arg(distance, 0, 'f', 3)); +} + +void CloudViewMainWindow::updateSelectedPointsDisplay() +{ + auto selectedPoints = m_glWidget->getSelectedPoints(); + + if (selectedPoints.size() >= 1 && selectedPoints[0].valid) { + QString text; + if (selectedPoints[0].lineIndex >= 0) { + text = QString("点1: 线号:%1 点序:%2 x:%3 y:%4 z:%5") + .arg(selectedPoints[0].lineIndex) + .arg(selectedPoints[0].pointIndexInLine) + .arg(selectedPoints[0].x, 0, 'f', 3) + .arg(selectedPoints[0].y, 0, 'f', 3) + .arg(selectedPoints[0].z, 0, 'f', 3); + } else { + text = QString("点1: x:%1 y:%2 z:%3") + .arg(selectedPoints[0].x, 0, 'f', 3) + .arg(selectedPoints[0].y, 0, 'f', 3) + .arg(selectedPoints[0].z, 0, 'f', 3); + } + m_lblPoint1->setText(text); + } else { + m_lblPoint1->setText("点1: --"); + } + + if (selectedPoints.size() >= 2 && selectedPoints[1].valid) { + QString text; + if (selectedPoints[1].lineIndex >= 0) { + text = QString("点2: 线号:%1 点序:%2 x:%3 y:%4 z:%5") + .arg(selectedPoints[1].lineIndex) + .arg(selectedPoints[1].pointIndexInLine) + .arg(selectedPoints[1].x, 0, 'f', 3) + .arg(selectedPoints[1].y, 0, 'f', 3) + .arg(selectedPoints[1].z, 0, 'f', 3); + } else { + text = QString("点2: x:%1 y:%2 z:%3") + .arg(selectedPoints[1].x, 0, 'f', 3) + .arg(selectedPoints[1].y, 0, 'f', 3) + .arg(selectedPoints[1].z, 0, 'f', 3); + } + m_lblPoint2->setText(text); + } else { + m_lblPoint2->setText("点2: --"); + } +} + +void CloudViewMainWindow::onLineSelectModeChanged(bool checked) +{ + if (checked) { + // 纵向模式 + m_glWidget->setLineSelectMode(LineSelectMode::Vertical); + } else { + // 横向模式 + m_glWidget->setLineSelectMode(LineSelectMode::Horizontal); + } + m_lineNumberInput->setPlaceholderText("输入索引"); +} + +void CloudViewMainWindow::onClearLinePoints() +{ + m_glWidget->clearSelectedLine(); + m_glWidget->clearListHighlightPoint(); // 清除列表选中的高亮点 + m_lblLineIndex->setText("--"); + m_lblLinePointCount->setText("--"); + statusBar()->showMessage("已清除选线"); +} + +void CloudViewMainWindow::onLineSelected(const SelectedLineInfo& line) +{ + // 重新选线时清除列表高亮点 + m_glWidget->clearListHighlightPoint(); + + if (!line.valid) { + m_lblLineIndex->setText("--"); + m_lblLinePointCount->setText("--"); + return; + } + + // 状态栏显示:线号/索引号、线点数 + if (line.mode == LineSelectMode::Vertical) { + m_lblLineIndex->setText(QString::number(line.lineIndex)); + statusBar()->showMessage(QString("选中线 | 线号: %1 | 线点数: %2") + .arg(line.lineIndex) + .arg(line.pointCount)); + } else { + // 横向选线:显示索引号 + m_lblLineIndex->setText(QString::number(line.pointIndex)); + statusBar()->showMessage(QString("选中横向线 | 索引号: %1 | 线点数: %2") + .arg(line.pointIndex) + .arg(line.pointCount)); + } + m_lblLinePointCount->setText(QString::number(line.pointCount)); + + // 如果线上点对话框已打开,刷新内容 + updateLinePointsDialog(); +} + +void CloudViewMainWindow::onSelectLineByNumber() +{ + if (m_glWidget->getCloudCount() == 0) { + QMessageBox::warning(this, "提示", "请先加载点云"); + return; + } + + QString text = m_lineNumberInput->text().trimmed(); + if (text.isEmpty()) { + QMessageBox::warning(this, "提示", "请输入索引"); + return; + } + + bool ok; + int index = text.toInt(&ok); + if (!ok || index < 0) { + QMessageBox::warning(this, "提示", "请输入有效的索引(非负整数)"); + return; + } + + bool success = false; + if (m_rbVertical->isChecked()) { + // 纵向选线:直接使用索引 + success = m_glWidget->selectLineByIndex(index); + if (!success) { + QMessageBox::warning(this, "提示", QString("索引 %1 不存在").arg(index)); + } + } else { + // 横向选线:直接使用索引 + success = m_glWidget->selectHorizontalLineByIndex(index); + if (!success) { + QMessageBox::warning(this, "提示", QString("索引 %1 不存在").arg(index)); + } + } +} + +QVector CloudViewMainWindow::getOriginalLinePoints(const SelectedLineInfo& lineInfo) +{ + QVector points; + + if (!lineInfo.valid || m_originalCloud.empty()) { + return points; + } + + if (lineInfo.mode == LineSelectMode::Vertical) { + // 纵向选线:获取同一条扫描线上的所有点 + for (size_t i = 0; i < m_originalCloud.points.size(); ++i) { + if (i < m_originalCloud.lineIndices.size() && + m_originalCloud.lineIndices[i] == lineInfo.lineIndex) { + const auto& pt = m_originalCloud.points[i]; + points.append(QVector3D(pt.x, pt.y, pt.z)); + } + } + } else { + // 横向选线:获取所有线的相同索引点 + if (m_currentLinePtNum > 0 && lineInfo.pointIndex >= 0) { + for (size_t i = 0; i < m_originalCloud.points.size(); ++i) { + int originalIdx = static_cast(i); + if (originalIdx % m_currentLinePtNum == lineInfo.pointIndex) { + const auto& pt = m_originalCloud.points[i]; + points.append(QVector3D(pt.x, pt.y, pt.z)); + } + } + } + } + + return points; +} + +void CloudViewMainWindow::updateLinePointsDialog() +{ + if (!m_linePointsDialog || !m_linePointsTable) { + return; + } + + SelectedLineInfo lineInfo = m_glWidget->getSelectedLine(); + if (!lineInfo.valid) { + m_linePointsTable->setRowCount(0); + m_linePointsDialog->setWindowTitle("线上点坐标"); + m_currentLinePoints.clear(); + return; + } + + // 从原始数据获取线上点(包含0,0,0) + m_currentLinePoints = getOriginalLinePoints(lineInfo); + + // 更新标题 + QString title; + if (lineInfo.mode == LineSelectMode::Vertical) { + title = QString("线上点坐标 - 线号: %1 (共 %2 个点)") + .arg(lineInfo.lineIndex) + .arg(m_currentLinePoints.size()); + } else { + title = QString("线上点坐标 - 索引号: %1 (共 %2 个点)") + .arg(lineInfo.pointIndex) + .arg(m_currentLinePoints.size()); + } + m_linePointsDialog->setWindowTitle(title); + + // 更新表格 + m_linePointsTable->setRowCount(m_currentLinePoints.size()); + + // 斑马线颜色 + QColor evenColor(245, 245, 245); // 浅灰色 + QColor oddColor(255, 255, 255); // 白色 + + for (int i = 0; i < m_currentLinePoints.size(); ++i) { + const QVector3D& pt = m_currentLinePoints[i]; + QColor rowColor = (i % 2 == 0) ? evenColor : oddColor; + + // 序号 + QTableWidgetItem* indexItem = new QTableWidgetItem(QString::number(i)); + indexItem->setTextAlignment(Qt::AlignCenter); + indexItem->setBackground(rowColor); + indexItem->setFlags(indexItem->flags() & ~Qt::ItemIsEditable); + m_linePointsTable->setItem(i, 0, indexItem); + + // X + QTableWidgetItem* xItem = new QTableWidgetItem(QString::number(pt.x(), 'f', 3)); + xItem->setTextAlignment(Qt::AlignCenter); + xItem->setBackground(rowColor); + xItem->setFlags(xItem->flags() & ~Qt::ItemIsEditable); + m_linePointsTable->setItem(i, 1, xItem); + + // Y + QTableWidgetItem* yItem = new QTableWidgetItem(QString::number(pt.y(), 'f', 3)); + yItem->setTextAlignment(Qt::AlignCenter); + yItem->setBackground(rowColor); + yItem->setFlags(yItem->flags() & ~Qt::ItemIsEditable); + m_linePointsTable->setItem(i, 2, yItem); + + // Z + QTableWidgetItem* zItem = new QTableWidgetItem(QString::number(pt.z(), 'f', 3)); + zItem->setTextAlignment(Qt::AlignCenter); + zItem->setBackground(rowColor); + zItem->setFlags(zItem->flags() & ~Qt::ItemIsEditable); + m_linePointsTable->setItem(i, 3, zItem); + } +} + +void CloudViewMainWindow::onShowLinePoints() +{ + SelectedLineInfo lineInfo = m_glWidget->getSelectedLine(); + if (!lineInfo.valid) { + QMessageBox::warning(this, "提示", "请先选择一条线"); + return; + } + + // 如果对话框已存在,刷新内容并显示 + if (m_linePointsDialog) { + updateLinePointsDialog(); + m_linePointsDialog->raise(); + m_linePointsDialog->activateWindow(); + return; + } + + // 创建对话框 + m_linePointsDialog = new QDialog(this); + m_linePointsDialog->resize(450, 500); + m_linePointsDialog->setAttribute(Qt::WA_DeleteOnClose); + + // 对话框关闭时清理指针 + connect(m_linePointsDialog, &QDialog::destroyed, this, [this]() { + m_linePointsDialog = nullptr; + m_linePointsTable = nullptr; + m_currentLinePoints.clear(); + m_glWidget->clearListHighlightPoint(); + }); + + QVBoxLayout* layout = new QVBoxLayout(m_linePointsDialog); + + // 提示标签 + QLabel* lblTip = new QLabel("点击行在3D视图中高亮显示", m_linePointsDialog); + lblTip->setStyleSheet("color: gray; font-size: 10px;"); + layout->addWidget(lblTip); + + // 创建表格控件 + m_linePointsTable = new QTableWidget(m_linePointsDialog); + m_linePointsTable->setColumnCount(4); + m_linePointsTable->setHorizontalHeaderLabels({"序号", "X", "Y", "Z"}); + m_linePointsTable->setFont(QFont("Consolas", 9)); + m_linePointsTable->setSelectionBehavior(QAbstractItemView::SelectRows); + m_linePointsTable->setSelectionMode(QAbstractItemView::SingleSelection); + m_linePointsTable->verticalHeader()->setVisible(false); + + // 设置列宽 + m_linePointsTable->setColumnWidth(0, 60); // 序号 + m_linePointsTable->setColumnWidth(1, 110); // X + m_linePointsTable->setColumnWidth(2, 110); // Y + m_linePointsTable->setColumnWidth(3, 110); // Z + + // 表头样式 + m_linePointsTable->horizontalHeader()->setStretchLastSection(true); + m_linePointsTable->horizontalHeader()->setDefaultAlignment(Qt::AlignCenter); + + connect(m_linePointsTable, &QTableWidget::cellClicked, + this, &CloudViewMainWindow::onLinePointTableClicked); + layout->addWidget(m_linePointsTable); + + // 关闭按钮 + QPushButton* btnClose = new QPushButton("关闭", m_linePointsDialog); + connect(btnClose, &QPushButton::clicked, m_linePointsDialog, &QDialog::close); + layout->addWidget(btnClose); + + // 填充数据 + updateLinePointsDialog(); + + m_linePointsDialog->show(); +} + +void CloudViewMainWindow::onLinePointTableClicked(int row, int column) +{ + Q_UNUSED(column); + + if (row >= 0 && row < m_currentLinePoints.size()) { + const QVector3D& pt = m_currentLinePoints[row]; + m_glWidget->setListHighlightPoint(pt); + + // 在状态栏显示选中点信息 + statusBar()->showMessage(QString("列表选中点 %1: (%2, %3, %4)") + .arg(row) + .arg(pt.x(), 0, 'f', 3) + .arg(pt.y(), 0, 'f', 3) + .arg(pt.z(), 0, 'f', 3)); + } +} + +void CloudViewMainWindow::onShowPose1() +{ + auto selectedPoints = m_glWidget->getSelectedPoints(); + if (selectedPoints.isEmpty() || !selectedPoints[0].valid) { + QMessageBox::warning(this, "提示", "请先选择点1(Ctrl+左键点击点云)"); + return; + } + + const auto& point = selectedPoints[0]; + + // 读取姿态参数 + bool ok = true; + float rx = m_editRx1->text().toFloat(&ok); + if (!ok) { + QMessageBox::warning(this, "错误", "点1 RX 值无效"); + return; + } + + float ry = m_editRy1->text().toFloat(&ok); + if (!ok) { + QMessageBox::warning(this, "错误", "点1 RY 值无效"); + return; + } + + float rz = m_editRz1->text().toFloat(&ok); + if (!ok) { + QMessageBox::warning(this, "错误", "点1 RZ 值无效"); + return; + } + + // 固定大小为10 + float scale = 10.0f; + + // 清除之前的姿态点 + m_glWidget->clearPosePoints(); + + // 创建点1的姿态点 + PosePoint pose1(point.x, point.y, point.z, rx, ry, rz, scale); + QVector poses; + poses.append(pose1); + + // 如果点2也存在,添加点2的姿态 + if (selectedPoints.size() >= 2 && selectedPoints[1].valid) { + const auto& point2 = selectedPoints[1]; + float rx2 = m_editRx2->text().toFloat(&ok); + float ry2 = m_editRy2->text().toFloat(&ok); + float rz2 = m_editRz2->text().toFloat(&ok); + if (ok) { + PosePoint pose2(point2.x, point2.y, point2.z, rx2, ry2, rz2, scale); + poses.append(pose2); + } + } + + // 添加到显示 + m_glWidget->addPosePoints(poses); + + statusBar()->showMessage(QString("已显示点1姿态 (%.3f, %.3f, %.3f) 旋转(%.1f°, %.1f°, %.1f°)") + .arg(point.x).arg(point.y).arg(point.z) + .arg(rx).arg(ry).arg(rz)); + + LOG_INFO("[CloudView] Show pose1 at (%.3f, %.3f, %.3f) with rotation (%.1f, %.1f, %.1f)\n", + point.x, point.y, point.z, rx, ry, rz); +} + +void CloudViewMainWindow::onShowPose2() +{ + auto selectedPoints = m_glWidget->getSelectedPoints(); + if (selectedPoints.size() < 2 || !selectedPoints[1].valid) { + QMessageBox::warning(this, "提示", "请先选择点2(启用测距后,Ctrl+左键点击第二个点)"); + return; + } + + const auto& point = selectedPoints[1]; + + // 读取姿态参数 + bool ok = true; + float rx = m_editRx2->text().toFloat(&ok); + if (!ok) { + QMessageBox::warning(this, "错误", "点2 RX 值无效"); + return; + } + + float ry = m_editRy2->text().toFloat(&ok); + if (!ok) { + QMessageBox::warning(this, "错误", "点2 RY 值无效"); + return; + } + + float rz = m_editRz2->text().toFloat(&ok); + if (!ok) { + QMessageBox::warning(this, "错误", "点2 RZ 值无效"); + return; + } + + // 固定大小为10 + float scale = 10.0f; + + // 清除之前的姿态点 + m_glWidget->clearPosePoints(); + + // 创建点2的姿态点 + PosePoint pose2(point.x, point.y, point.z, rx, ry, rz, scale); + QVector poses; + + // 如果点1也存在,添加点1的姿态 + if (selectedPoints[0].valid) { + const auto& point1 = selectedPoints[0]; + float rx1 = m_editRx1->text().toFloat(&ok); + float ry1 = m_editRy1->text().toFloat(&ok); + float rz1 = m_editRz1->text().toFloat(&ok); + if (ok) { + PosePoint pose1(point1.x, point1.y, point1.z, rx1, ry1, rz1, scale); + poses.append(pose1); + } + } + + poses.append(pose2); + + // 添加到显示 + m_glWidget->addPosePoints(poses); + + statusBar()->showMessage(QString("已显示点2姿态 (%.3f, %.3f, %.3f) 旋转(%.1f°, %.1f°, %.1f°)") + .arg(point.x).arg(point.y).arg(point.z) + .arg(rx).arg(ry).arg(rz)); + + LOG_INFO("[CloudView] Show pose2 at (%.3f, %.3f, %.3f) with rotation (%.1f, %.1f, %.1f)\n", + point.x, point.y, point.z, rx, ry, rz); +} + +void CloudViewMainWindow::onShowInputLine() +{ + // 读取点1坐标 + bool ok = true; + float x1 = m_editLineX1->text().toFloat(&ok); + if (!ok) { + QMessageBox::warning(this, "错误", "点1 X 值无效"); + return; + } + + float y1 = m_editLineY1->text().toFloat(&ok); + if (!ok) { + QMessageBox::warning(this, "错误", "点1 Y 值无效"); + return; + } + + float z1 = m_editLineZ1->text().toFloat(&ok); + if (!ok) { + QMessageBox::warning(this, "错误", "点1 Z 值无效"); + return; + } + + // 读取点2坐标 + float x2 = m_editLineX2->text().toFloat(&ok); + if (!ok) { + QMessageBox::warning(this, "错误", "点2 X 值无效"); + return; + } + + float y2 = m_editLineY2->text().toFloat(&ok); + if (!ok) { + QMessageBox::warning(this, "错误", "点2 Y 值无效"); + return; + } + + float z2 = m_editLineZ2->text().toFloat(&ok); + if (!ok) { + QMessageBox::warning(this, "错误", "点2 Z 值无效"); + return; + } + + // 清除之前的线段 + m_glWidget->clearLineSegments(); + + // 创建线段(红色) + LineSegment segment(x1, y1, z1, x2, y2, z2, 1.0f, 0.0f, 0.0f); + QVector segments; + segments.append(segment); + + // 添加到显示 + m_glWidget->addLineSegments(segments); + + // 计算距离 + float dx = x2 - x1; + float dy = y2 - y1; + float dz = z2 - z1; + float distance = std::sqrt(dx * dx + dy * dy + dz * dz); + + statusBar()->showMessage(QString("已显示线段 (%1,%2,%3) → (%4,%5,%6) 长度: %7") + .arg(x1).arg(y1).arg(z1) + .arg(x2).arg(y2).arg(z2) + .arg(distance)); + + LOG_INFO("[CloudView] Show input line from (%.3f, %.3f, %.3f) to (%.3f, %.3f, %.3f) length=%.3f\n", + x1, y1, z1, x2, y2, z2, distance); +} + +void CloudViewMainWindow::onClearInputLine() +{ + m_glWidget->clearLineSegments(); + statusBar()->showMessage("已清除输入的线段"); +} + +QWidget* CloudViewMainWindow::createTransformPage() +{ + QWidget* page = new QWidget(this); + QVBoxLayout* layout = new QVBoxLayout(page); + layout->setContentsMargins(0, 5, 0, 0); + + QGroupBox* group = new QGroupBox("矩阵变换", page); + group->setMaximumWidth(400); + QVBoxLayout* groupLayout = new QVBoxLayout(group); + + // 操作说明 + QLabel* lblTip = new QLabel("输入或从文件加载 4x4 变换矩阵,应用到所有点云", group); + lblTip->setWordWrap(true); + lblTip->setStyleSheet("color: gray; font-size: 10px;"); + groupLayout->addWidget(lblTip); + + // 矩阵编辑区域 + QLabel* lblMatrix = new QLabel("变换矩阵 (4x4):", group); + lblMatrix->setStyleSheet("font-weight: bold;"); + groupLayout->addWidget(lblMatrix); + + m_matrixEdit = new QTextEdit(group); + m_matrixEdit->setFont(QFont("Consolas", 10)); + m_matrixEdit->setMinimumHeight(100); + m_matrixEdit->setMaximumHeight(120); + // 初始化为单位矩阵 + m_matrixEdit->setPlainText( + "1.0 0.0 0.0 0.0\n" + "0.0 1.0 0.0 0.0\n" + "0.0 0.0 1.0 0.0\n" + "0.0 0.0 0.0 1.0" + ); + groupLayout->addWidget(m_matrixEdit); + + // 从文件加载按钮 + m_btnLoadMatrix = new QPushButton("从文件加载矩阵", group); + m_btnLoadMatrix->setMinimumHeight(30); + connect(m_btnLoadMatrix, &QPushButton::clicked, this, &CloudViewMainWindow::onLoadMatrix); + groupLayout->addWidget(m_btnLoadMatrix); + + // 按钮行 + QHBoxLayout* btnLayout = new QHBoxLayout(); + m_btnApplyMatrix = new QPushButton("应用变换", group); + m_btnApplyMatrix->setMinimumHeight(30); + m_btnApplyMatrix->setStyleSheet("QPushButton { background-color: #4CAF50; color: white; font-weight: bold; }" + "QPushButton:hover { background-color: #45a049; }"); + connect(m_btnApplyMatrix, &QPushButton::clicked, this, &CloudViewMainWindow::onApplyMatrix); + btnLayout->addWidget(m_btnApplyMatrix); + + m_btnResetMatrix = new QPushButton("重置矩阵", group); + connect(m_btnResetMatrix, &QPushButton::clicked, this, &CloudViewMainWindow::onResetMatrix); + btnLayout->addWidget(m_btnResetMatrix); + groupLayout->addLayout(btnLayout); + + // 文件格式说明 + QLabel* lblFormat = new QLabel( + "矩阵文件格式:4行,每行4个数值\n" + "分隔符:空格/Tab/逗号\n" + "#开头的行为注释", group); + lblFormat->setWordWrap(true); + lblFormat->setStyleSheet("color: gray; font-size: 9px;"); + groupLayout->addWidget(lblFormat); + + layout->addWidget(group); + layout->addStretch(); + return page; +} + +void CloudViewMainWindow::onLoadMatrix() +{ + QString fileName = QFileDialog::getOpenFileName( + this, + "打开矩阵文件", + QString(), + "文本文件 (*.txt);;所有文件 (*.*)" + ); + + if (fileName.isEmpty()) { + return; + } + + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + QMessageBox::critical(this, "错误", QString("无法打开文件: %1").arg(fileName)); + return; + } + + QTextStream in(&file); + QVector> rows; + + while (!in.atEnd()) { + QString line = in.readLine().trimmed(); + // 跳过空行和注释 + if (line.isEmpty() || line.startsWith('#')) { + continue; + } + + // 将逗号替换为空格,统一分隔符 + line.replace(',', ' '); + line.replace('\t', ' '); + QStringList parts = line.split(' ', QString::SkipEmptyParts); + + QVector row; + bool ok = true; + for (const QString& part : parts) { + float val = part.toFloat(&ok); + if (!ok) break; + row.append(val); + } + + if (!ok || row.size() != 4) { + QMessageBox::warning(this, "格式错误", + QString("第 %1 行格式无效,需要4个数值").arg(rows.size() + 1)); + file.close(); + return; + } + rows.append(row); + + if (rows.size() == 4) { + break; + } + } + file.close(); + + if (rows.size() != 4) { + QMessageBox::warning(this, "格式错误", + QString("矩阵需要4行数据,当前只有 %1 行").arg(rows.size())); + return; + } + + // 将矩阵显示到编辑区域 + QString matrixText; + for (int r = 0; r < 4; ++r) { + QStringList vals; + for (int c = 0; c < 4; ++c) { + vals.append(QString::number(static_cast(rows[r][c]), 'f', 6)); + } + matrixText += vals.join(" "); + if (r < 3) matrixText += "\n"; + } + m_matrixEdit->setPlainText(matrixText); + + statusBar()->showMessage(QString("已从 %1 加载矩阵").arg(QFileInfo(fileName).fileName())); + LOG_INFO("[CloudView] Loaded matrix from %s\n", fileName.toStdString().c_str()); +} + +void CloudViewMainWindow::onApplyMatrix() +{ + if (m_glWidget->getCloudCount() == 0) { + QMessageBox::warning(this, "提示", "请先加载点云"); + return; + } + + // 解析编辑区域中的矩阵 + QString text = m_matrixEdit->toPlainText().trimmed(); + QStringList lines = text.split('\n', QString::SkipEmptyParts); + + QVector> rows; + for (const QString& line : lines) { + QString cleaned = line.trimmed(); + if (cleaned.isEmpty() || cleaned.startsWith('#')) { + continue; + } + cleaned.replace(',', ' '); + cleaned.replace('\t', ' '); + QStringList parts = cleaned.split(' ', QString::SkipEmptyParts); + + QVector row; + bool ok = true; + for (const QString& part : parts) { + float val = part.toFloat(&ok); + if (!ok) break; + row.append(val); + } + + if (!ok || row.size() != 4) { + QMessageBox::warning(this, "格式错误", "矩阵格式无效,需要4行4列数值"); + return; + } + rows.append(row); + } + + if (rows.size() != 4) { + QMessageBox::warning(this, "格式错误", + QString("矩阵需要4行数据,当前 %1 行").arg(rows.size())); + return; + } + + // 构造 QMatrix4x4(按行优先存储) + float values[16]; + for (int r = 0; r < 4; ++r) { + for (int c = 0; c < 4; ++c) { + values[r * 4 + c] = rows[r][c]; + } + } + QMatrix4x4 matrix(values); + + // 检查是否为单位矩阵 + if (matrix.isIdentity()) { + QMessageBox::information(this, "提示", "当前矩阵为单位矩阵,无需变换"); + return; + } + + // 应用变换 + m_glWidget->transformAllClouds(matrix); + + statusBar()->showMessage("已应用矩阵变换到所有点云"); + LOG_INFO("[CloudView] Applied matrix transform to all point clouds\n"); +} + +void CloudViewMainWindow::onResetMatrix() +{ + m_matrixEdit->setPlainText( + "1.0 0.0 0.0 0.0\n" + "0.0 1.0 0.0 0.0\n" + "0.0 0.0 1.0 0.0\n" + "0.0 0.0 0.0 1.0" + ); + statusBar()->showMessage("矩阵已重置为单位矩阵"); +} + +void CloudViewMainWindow::onEulerOrderChanged(int index) +{ + if (!m_glWidget) { + return; + } + + EulerRotationOrder order = static_cast(m_comboEulerOrder->itemData(index).toInt()); + m_glWidget->setEulerRotationOrder(order); + + // 如果有姿态点,刷新显示 + m_glWidget->update(); + + QString orderName = m_comboEulerOrder->currentText(); + statusBar()->showMessage(QString("欧拉角旋转顺序已切换为: %1").arg(orderName)); + LOG_INFO("[CloudView] Euler rotation order changed to: %s\n", orderName.toStdString().c_str()); +} + +void CloudViewMainWindow::onViewAnglesChanged(float rotX, float rotY, float rotZ) +{ + // 更新显示的角度值 + m_editRotX->setText(QString::number(rotX, 'f', 1)); + m_editRotY->setText(QString::number(rotY, 'f', 1)); + m_editRotZ->setText(QString::number(rotZ, 'f', 1)); +} + diff --git a/CloudView/Src/PointCloudConverter.cpp b/CloudView/Src/PointCloudConverter.cpp new file mode 100644 index 0000000..9c14d68 --- /dev/null +++ b/CloudView/Src/PointCloudConverter.cpp @@ -0,0 +1,561 @@ +#include "PointCloudConverter.h" +#include "LaserDataLoader.h" +#include "VZNL_Types.h" +#include "VrLog.h" + +#include +#include +#include +#include +#include +#include + +PointCloudConverter::PointCloudConverter() + : m_loadedPointCount(0) + , m_loadedLineCount(0) + , m_lastLoadHadColor(false) +{ +} + +PointCloudConverter::~PointCloudConverter() +{ +} + +std::string PointCloudConverter::getFileExtension(const std::string& fileName) +{ + size_t pos = fileName.rfind('.'); + if (pos == std::string::npos) { + return ""; + } + std::string ext = fileName.substr(pos + 1); + std::transform(ext.begin(), ext.end(), ext.begin(), + [](unsigned char c) { return std::tolower(c); }); + return ext; +} + +int PointCloudConverter::loadFromTxt(const std::string& fileName, PointCloudXYZ& cloud) +{ + LaserDataLoader loader; + + // 使用 CloudUtils 加载数据 + std::vector> laserLines; + int lineNum = 0; + float scanSpeed = 0.0f; + int maxTimeStamp = 0; + int clockPerSecond = 0; + + int result = loader.LoadLaserScanData(fileName, laserLines, lineNum, scanSpeed, maxTimeStamp, clockPerSecond); + if (result != 0) { + m_lastError = "加载文件失败: " + loader.GetLastError(); + return result; + } + + LOG_INFO("[CloudView] LoadLaserScanData success, laserLines size: %zu, lineNum: %d\n", + laserLines.size(), lineNum); + + // 转换为 SVzNL3DPosition 格式 + std::vector> scanLines; + result = loader.ConvertToSVzNL3DPosition(laserLines, scanLines); + if (result != 0) { + m_lastError = "转换数据失败"; + loader.FreeLaserScanData(laserLines); + return result; + } + + LOG_INFO("[CloudView] ConvertToSVzNL3DPosition success, scanLines size: %zu\n", scanLines.size()); + + // 转换为自定义点云格式,保留线索引(保留所有点包括0,0,0用于旋转) + cloud.clear(); + size_t totalCount = 0; + int lineIndex = 0; + + for (const auto& line : scanLines) { + for (const auto& pos : line) { + totalCount++; + Point3D point; + point.x = static_cast(pos.pt3D.x); + point.y = static_cast(pos.pt3D.y); + point.z = static_cast(pos.pt3D.z); + cloud.push_back(point, lineIndex); + } + lineIndex++; + } + + LOG_INFO("[CloudView] Total points: %zu, Lines: %d\n", totalCount, lineIndex); + + loader.FreeLaserScanData(laserLines); + m_loadedPointCount = totalCount; + m_loadedLineCount = lineIndex; + return 0; +} + +int PointCloudConverter::loadFromTxt(const std::string& fileName, PointCloudXYZRGB& cloud) +{ + LaserDataLoader loader; + + // 使用 CloudUtils 加载数据 + std::vector> laserLines; + int lineNum = 0; + float scanSpeed = 0.0f; + int maxTimeStamp = 0; + int clockPerSecond = 0; + + int result = loader.LoadLaserScanData(fileName, laserLines, lineNum, scanSpeed, maxTimeStamp, clockPerSecond); + if (result != 0) { + m_lastError = "加载文件失败,loasreuslt: " + std::to_string(result); + return result; + } + + LOG_INFO("[CloudView] LoadLaserScanData(XYZRGB) success, laserLines size: %zu, lineNum: %d\n", + laserLines.size(), lineNum); + + // 检查数据类型是否包含 RGBA + bool hasRGBA = false; + for (const auto& linePair : laserLines) { + if (linePair.first == keResultDataType_PointXYZRGBA) { + hasRGBA = true; + break; + } + } + + cloud.clear(); + size_t totalCount = 0; + int lineIndex = 0; + + if (hasRGBA) { + // RGBA 路径:使用 ConvertToSVzNLXYZRGBDLaserLine 转换 + std::vector rgbdData; + result = loader.ConvertToSVzNLXYZRGBDLaserLine(laserLines, rgbdData); + if (result != 0) { + m_lastError = "转换RGBA数据失败"; + loader.FreeLaserScanData(laserLines); + return result; + } + + LOG_INFO("[CloudView] ConvertToSVzNLXYZRGBDLaserLine success, rgbdData size: %zu\n", rgbdData.size()); + + for (const auto& line : rgbdData) { + for (int i = 0; i < line.nPointCnt; ++i) { + const SVzNLPointXYZRGBA& pt = line.p3DPoint[i]; + // 解包颜色:nRGB 格式为 (B << 16) | (G << 8) | R + uint8_t r = static_cast(pt.nRGB & 0xFF); + uint8_t g = static_cast((pt.nRGB >> 8) & 0xFF); + uint8_t b = static_cast((pt.nRGB >> 16) & 0xFF); + + Point3DRGB point(pt.x, pt.y, pt.z, r, g, b); + cloud.push_back(point, lineIndex); + totalCount++; + } + lineIndex++; + } + + loader.FreeConvertedData(rgbdData); + m_lastLoadHadColor = true; + } else { + // 非 RGBA 路径:回退到 SVzNL3DPosition + 白色 + std::vector> scanLines; + result = loader.ConvertToSVzNL3DPosition(laserLines, scanLines); + if (result != 0) { + m_lastError = "转换数据失败"; + loader.FreeLaserScanData(laserLines); + return result; + } + + LOG_INFO("[CloudView] ConvertToSVzNL3DPosition success, scanLines size: %zu\n", scanLines.size()); + + for (const auto& line : scanLines) { + for (const auto& pos : line) { + Point3DRGB point( + static_cast(pos.pt3D.x), + static_cast(pos.pt3D.y), + static_cast(pos.pt3D.z), + 255, 255, 255); + cloud.push_back(point, lineIndex); + totalCount++; + } + lineIndex++; + } + + m_lastLoadHadColor = false; + } + + LOG_INFO("[CloudView] Total points(XYZRGB): %zu, Lines: %d, hasRGBA: %d\n", totalCount, lineIndex, hasRGBA); + + loader.FreeLaserScanData(laserLines); + m_loadedPointCount = totalCount; + m_loadedLineCount = lineIndex; + return 0; +} + +bool PointCloudConverter::parsePcdHeader(std::ifstream& file, PcdHeader& header) +{ + std::string line; + while (std::getline(file, line)) { + if (line.empty() || line[0] == '#') { + continue; + } + + std::istringstream iss(line); + std::string key; + iss >> key; + + if (key == "VERSION") { + // 忽略版本 + } else if (key == "FIELDS") { + std::string field; + while (iss >> field) { + header.fields.push_back(field); + if (field == "rgb" || field == "rgba") { + header.hasRgb = true; + } + } + } else if (key == "SIZE") { + int size; + while (iss >> size) { + header.fieldSizes.push_back(size); + header.pointSize += size; + } + } else if (key == "TYPE") { + char type; + while (iss >> type) { + header.fieldTypes.push_back(type); + } + } else if (key == "COUNT") { + // 忽略 COUNT + } else if (key == "WIDTH") { + iss >> header.width; + } else if (key == "HEIGHT") { + iss >> header.height; + } else if (key == "VIEWPOINT") { + // 忽略 VIEWPOINT + } else if (key == "POINTS") { + iss >> header.points; + } else if (key == "DATA") { + std::string dataType; + iss >> dataType; + header.isBinary = (dataType == "binary" || dataType == "binary_compressed"); + return true; // 头部解析完成 + } + } + return false; +} + +int PointCloudConverter::loadFromPcd(const std::string& fileName, PointCloudXYZ& cloud) +{ + std::ifstream file(fileName, std::ios::binary); + if (!file.is_open()) { + m_lastError = "无法打开文件: " + fileName; + return -1; + } + + PcdHeader header; + if (!parsePcdHeader(file, header)) { + m_lastError = "无法解析 PCD 文件头"; + return -1; + } + + int numPoints = header.points > 0 ? header.points : header.width * header.height; + LOG_INFO("[CloudView] PCD header: points=%d, width=%d, height=%d, isBinary=%d\n", + numPoints, header.width, header.height, header.isBinary); + + cloud.clear(); + cloud.reserve(numPoints); + + // 查找 x, y, z 字段的索引 + int xIdx = -1, yIdx = -1, zIdx = -1; + for (size_t i = 0; i < header.fields.size(); ++i) { + if (header.fields[i] == "x") xIdx = static_cast(i); + else if (header.fields[i] == "y") yIdx = static_cast(i); + else if (header.fields[i] == "z") zIdx = static_cast(i); + } + + if (xIdx < 0 || yIdx < 0 || zIdx < 0) { + m_lastError = "PCD 文件缺少 x, y, z 字段"; + return -1; + } + + if (header.isBinary) { + // 二进制格式 + std::vector buffer(header.pointSize); + for (int i = 0; i < numPoints; ++i) { + file.read(buffer.data(), header.pointSize); + if (!file) break; + + Point3D pt; + int offset = 0; + for (size_t j = 0; j < header.fields.size(); ++j) { + if (j == static_cast(xIdx)) { + memcpy(&pt.x, buffer.data() + offset, sizeof(float)); + } else if (j == static_cast(yIdx)) { + memcpy(&pt.y, buffer.data() + offset, sizeof(float)); + } else if (j == static_cast(zIdx)) { + memcpy(&pt.z, buffer.data() + offset, sizeof(float)); + } + offset += header.fieldSizes[j]; + } + + if (std::isfinite(pt.x) && std::isfinite(pt.y) && std::isfinite(pt.z)) { + cloud.push_back(pt); + } + } + } else { + // ASCII 格式 + std::string line; + while (std::getline(file, line) && cloud.size() < static_cast(numPoints)) { + std::istringstream iss(line); + std::vector values; + float val; + while (iss >> val) { + values.push_back(val); + } + + if (values.size() >= 3 && + static_cast(xIdx) < values.size() && + static_cast(yIdx) < values.size() && + static_cast(zIdx) < values.size()) { + Point3D pt; + pt.x = values[xIdx]; + pt.y = values[yIdx]; + pt.z = values[zIdx]; + + if (std::isfinite(pt.x) && std::isfinite(pt.y) && std::isfinite(pt.z)) { + cloud.push_back(pt); + } + } + } + } + + LOG_INFO("[CloudView] Loaded %zu points from PCD\n", cloud.size()); + m_loadedPointCount = cloud.size(); + m_loadedLineCount = 0; // PCD 文件没有线信息 + return 0; +} + +int PointCloudConverter::loadFromPcd(const std::string& fileName, PointCloudXYZRGB& cloud) +{ + std::ifstream file(fileName, std::ios::binary); + if (!file.is_open()) { + m_lastError = "无法打开文件: " + fileName; + return -1; + } + + PcdHeader header; + if (!parsePcdHeader(file, header)) { + m_lastError = "无法解析 PCD 文件头"; + return -1; + } + + int numPoints = header.points > 0 ? header.points : header.width * header.height; + cloud.clear(); + cloud.reserve(numPoints); + + // 查找字段索引 + int xIdx = -1, yIdx = -1, zIdx = -1, rgbIdx = -1; + for (size_t i = 0; i < header.fields.size(); ++i) { + if (header.fields[i] == "x") xIdx = static_cast(i); + else if (header.fields[i] == "y") yIdx = static_cast(i); + else if (header.fields[i] == "z") zIdx = static_cast(i); + else if (header.fields[i] == "rgb" || header.fields[i] == "rgba") rgbIdx = static_cast(i); + } + + if (xIdx < 0 || yIdx < 0 || zIdx < 0) { + m_lastError = "PCD 文件缺少 x, y, z 字段"; + return -1; + } + + if (header.isBinary) { + std::vector buffer(header.pointSize); + for (int i = 0; i < numPoints; ++i) { + file.read(buffer.data(), header.pointSize); + if (!file) break; + + Point3DRGB pt; + int offset = 0; + for (size_t j = 0; j < header.fields.size(); ++j) { + if (j == static_cast(xIdx)) { + memcpy(&pt.x, buffer.data() + offset, sizeof(float)); + } else if (j == static_cast(yIdx)) { + memcpy(&pt.y, buffer.data() + offset, sizeof(float)); + } else if (j == static_cast(zIdx)) { + memcpy(&pt.z, buffer.data() + offset, sizeof(float)); + } else if (j == static_cast(rgbIdx)) { + // RGB 通常存储为 packed float + float rgbFloat; + memcpy(&rgbFloat, buffer.data() + offset, sizeof(float)); + uint32_t rgb = *reinterpret_cast(&rgbFloat); + pt.r = (rgb >> 16) & 0xFF; + pt.g = (rgb >> 8) & 0xFF; + pt.b = rgb & 0xFF; + } + offset += header.fieldSizes[j]; + } + + if (std::isfinite(pt.x) && std::isfinite(pt.y) && std::isfinite(pt.z)) { + cloud.push_back(pt); + } + } + } else { + std::string line; + while (std::getline(file, line) && cloud.size() < static_cast(numPoints)) { + std::istringstream iss(line); + std::vector values; + float val; + while (iss >> val) { + values.push_back(val); + } + + if (values.size() >= 3 && + static_cast(xIdx) < values.size() && + static_cast(yIdx) < values.size() && + static_cast(zIdx) < values.size()) { + Point3DRGB pt; + pt.x = values[xIdx]; + pt.y = values[yIdx]; + pt.z = values[zIdx]; + + if (rgbIdx >= 0 && static_cast(rgbIdx) < values.size()) { + float rgbFloat = values[rgbIdx]; + uint32_t rgb = *reinterpret_cast(&rgbFloat); + pt.r = (rgb >> 16) & 0xFF; + pt.g = (rgb >> 8) & 0xFF; + pt.b = rgb & 0xFF; + } + + if (std::isfinite(pt.x) && std::isfinite(pt.y) && std::isfinite(pt.z)) { + cloud.push_back(pt); + } + } + } + } + + m_loadedPointCount = cloud.size(); + m_loadedLineCount = 0; // PCD 文件没有线信息 + return 0; +} + +int PointCloudConverter::loadFromFile(const std::string& fileName, PointCloudXYZ& cloud) +{ + std::string ext = getFileExtension(fileName); + + if (ext == "pcd") { + return loadFromPcd(fileName, cloud); + } else if (ext == "txt") { + return loadFromTxt(fileName, cloud); + } else { + m_lastError = "不支持的文件格式: " + ext; + return -1; + } +} + +int PointCloudConverter::loadFromFile(const std::string& fileName, PointCloudXYZRGB& cloud) +{ + std::string ext = getFileExtension(fileName); + + if (ext == "pcd") { + return loadFromPcd(fileName, cloud); + } else if (ext == "txt") { + return loadFromTxt(fileName, cloud); + } else { + m_lastError = "不支持的文件格式: " + ext; + return -1; + } +} + +int PointCloudConverter::saveToTxt(const std::string& fileName, const PointCloudXYZ& cloud, int lineNum, int linePtNum) +{ + if (cloud.empty()) { + m_lastError = "点云数据为空"; + return -1; + } + + // 转换为 std::vector> 格式 + std::vector> xyzData; + xyzData.resize(lineNum); + + for (int line = 0; line < lineNum; ++line) { + xyzData[line].resize(linePtNum); + } + + // 填充数据 + size_t ptIdx = 0; + for (int line = 0; line < lineNum; ++line) { + for (int j = 0; j < linePtNum; ++j) { + if (ptIdx < cloud.points.size()) { + xyzData[line][j].pt3D.x = cloud.points[ptIdx].x; + xyzData[line][j].pt3D.y = cloud.points[ptIdx].y; + xyzData[line][j].pt3D.z = cloud.points[ptIdx].z; + xyzData[line][j].nPointIdx = j; + ptIdx++; + } else { + // 填充零点 + xyzData[line][j].pt3D.x = 0; + xyzData[line][j].pt3D.y = 0; + xyzData[line][j].pt3D.z = 0; + xyzData[line][j].nPointIdx = j; + } + } + } + + // 使用 LaserDataLoader 保存 + LaserDataLoader loader; + int result = loader.DebugSaveLaser(fileName, xyzData); + if (result != 0) { + m_lastError = "保存文件失败: " + loader.GetLastError(); + return result; + } + + LOG_INFO("[CloudView] Saved %zu points to %s (lineNum=%d, linePtNum=%d)\n", + cloud.points.size(), fileName.c_str(), lineNum, linePtNum); + return 0; +} + +int PointCloudConverter::rotateCloud(const PointCloudXYZ& cloud, PointCloudXYZ& rotatedCloud, + int lineNum, int linePtNum, int& newLineNum, int& newLinePtNum) +{ + if (cloud.empty()) { + m_lastError = "无效的点云数据"; + return -1; + } + + if (lineNum <= 0 || linePtNum <= 0) { + m_lastError = "无效的线信息"; + return -1; + } + + // 检查点数是否匹配 + size_t expectedPoints = static_cast(lineNum) * static_cast(linePtNum); + if (cloud.points.size() != expectedPoints) { + m_lastError = "点云数据与线信息不匹配,无法旋转"; + return -1; + } + + // 矩阵转置:原来的行列互换 + // 原来: lineNum 条线,每条线 linePtNum 个点 + // 转置后: linePtNum 条线,每条线 lineNum 个点 + newLineNum = linePtNum; + newLinePtNum = lineNum; + + rotatedCloud.clear(); + rotatedCloud.reserve(cloud.points.size()); + + // 转置操作: + // 原来第 line 条线的第 col 个点 -> 新的第 col 条线的第 line 个点 + // 原索引: line * linePtNum + col + // 新索引: col * lineNum + line + for (int newLine = 0; newLine < newLineNum; ++newLine) { + for (int newCol = 0; newCol < newLinePtNum; ++newCol) { + // newLine 对应原来的 col(点在线内的位置) + // newCol 对应原来的 line(线号) + int oldLine = newCol; + int oldCol = newLine; + size_t oldIdx = static_cast(oldLine) * static_cast(linePtNum) + static_cast(oldCol); + + const Point3D& pt = cloud.points[oldIdx]; + rotatedCloud.push_back(pt, newLine); + } + } + + LOG_INFO("[CloudView] Rotated cloud: %zu points, %d lines -> %d lines\n", + rotatedCloud.points.size(), lineNum, newLineNum); + return 0; +} diff --git a/CloudView/Src/PointCloudGLWidget.cpp b/CloudView/Src/PointCloudGLWidget.cpp new file mode 100644 index 0000000..410ab60 --- /dev/null +++ b/CloudView/Src/PointCloudGLWidget.cpp @@ -0,0 +1,1276 @@ +#include "PointCloudGLWidget.h" +#include +#include +#include +#include "VrLog.h" + +// OpenGL/GLU 头文件 +#ifdef _WIN32 +#include +#include +#include +#else +#include +#include +#endif + +PointCloudGLWidget::PointCloudGLWidget(QWidget* parent) + : QOpenGLWidget(parent) + , m_distance(100.0f) + , m_rotationX(0.0f) + , m_rotationY(0.0f) + , m_rotationZ(0.0f) + , m_center(0, 0, 0) + , m_pan(0, 0, 0) + , m_minBound(-50, -50, -50) + , m_maxBound(50, 50, 50) + , m_leftButtonPressed(false) + , m_rightButtonPressed(false) + , m_middleButtonPressed(false) + , m_currentColor(PointCloudColor::White) + , m_pointSize(1.0f) + , m_lineSelectMode(LineSelectMode::Vertical) + , m_eulerRotationOrder(EulerRotationOrder::ZYX) + , m_measureDistanceEnabled(false) + , m_hasListHighlightPoint(false) + , m_colorIndex(0) +{ + setFocusPolicy(Qt::StrongFocus); + setMouseTracking(true); + + // 确保 m_model 是单位矩阵 + m_model.setToIdentity(); +} + +PointCloudGLWidget::~PointCloudGLWidget() +{ + // 在 GL 上下文中释放所有 VBO + makeCurrent(); + for (auto& cloudData : m_pointClouds) { + releaseVBO(cloudData); + } + doneCurrent(); +} + +void PointCloudGLWidget::uploadToVBO(PointCloudData& data) +{ + // 先释放旧的 VBO + releaseVBO(data); + + if (data.vertices.empty()) { + return; + } + + // 创建并上传顶点 VBO + data.vertexBuffer.create(); + data.vertexBuffer.bind(); + data.vertexBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw); + data.vertexBuffer.allocate(data.vertices.data(), + static_cast(data.vertices.size() * sizeof(float))); + data.vertexBuffer.release(); + + // 如果有颜色数据,创建并上传颜色 VBO + if (data.hasColor && !data.colors.empty()) { + data.colorBuffer.create(); + data.colorBuffer.bind(); + data.colorBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw); + data.colorBuffer.allocate(data.colors.data(), + static_cast(data.colors.size() * sizeof(float))); + data.colorBuffer.release(); + } + + data.vboCreated = true; +} + +void PointCloudGLWidget::releaseVBO(PointCloudData& data) +{ + if (!data.vboCreated) { + return; + } + + if (data.vertexBuffer.isCreated()) { + data.vertexBuffer.destroy(); + } + if (data.colorBuffer.isCreated()) { + data.colorBuffer.destroy(); + } + + data.vboCreated = false; +} + +void PointCloudGLWidget::initializeGL() +{ + initializeOpenGLFunctions(); + + LOG_INFO("[CloudView] OpenGL initialized, version: %s\n", reinterpret_cast(glGetString(GL_VERSION))); + + glClearColor(0.15f, 0.15f, 0.15f, 1.0f); + glEnable(GL_DEPTH_TEST); + glEnable(GL_POINT_SMOOTH); + glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); +} + +void PointCloudGLWidget::resizeGL(int w, int h) +{ + glViewport(0, 0, w, h); + + m_projection.setToIdentity(); + float aspect = static_cast(w) / static_cast(h > 0 ? h : 1); + m_projection.perspective(45.0f, aspect, 0.1f, 10000.0f); +} + +void PointCloudGLWidget::paintGL() +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + m_view.setToIdentity(); + // 平移在相机空间中进行,使鼠标拖动方向与屏幕方向一致 + m_view.translate(-m_pan.x(), -m_pan.y(), -m_distance); + + // 使用轨迹球旋转方式:先Y后X后Z,这样更直观 + m_view.rotate(m_rotationY, 0, 1, 0); + m_view.rotate(m_rotationX, 1, 0, 0); + m_view.rotate(m_rotationZ, 0, 0, 1); + + m_view.translate(-m_center); + + // 调试输出:每100帧输出一次当前旋转角度 + static int frameCount = 0; + if (++frameCount % 100 == 0) { + LOG_INFO("[CloudView] Current rotation: rotX=%.1f, rotY=%.1f, rotZ=%.1f\n", + m_rotationX, m_rotationY, m_rotationZ); + } + + glMatrixMode(GL_PROJECTION); + glLoadMatrixf(m_projection.constData()); + + glMatrixMode(GL_MODELVIEW); + QMatrix4x4 modelView = m_view * m_model; + glLoadMatrixf(modelView.constData()); + + // 确保颜色数组禁用 + glDisableClientState(GL_COLOR_ARRAY); + + glPointSize(m_pointSize); + + for (size_t cloudIdx = 0; cloudIdx < m_pointClouds.size(); ++cloudIdx) { + auto& cloudData = m_pointClouds[cloudIdx]; + if (cloudData.vertices.empty()) { + continue; + } + + // 确保 VBO 已创建(延迟上传,因为 addPointCloud 可能在 GL 上下文外调用) + if (!cloudData.vboCreated) { + uploadToVBO(cloudData); + } + + glEnableClientState(GL_VERTEX_ARRAY); + + if (cloudData.vboCreated && cloudData.vertexBuffer.isCreated()) { + // 使用 VBO 渲染:数据已在 GPU 显存中,无需每帧 CPU→GPU 拷贝 + cloudData.vertexBuffer.bind(); + glVertexPointer(3, GL_FLOAT, 0, nullptr); + cloudData.vertexBuffer.release(); + } else { + // 回退到 CPU 指针(VBO 创建失败时) + glVertexPointer(3, GL_FLOAT, 0, cloudData.vertices.data()); + } + + // 如果有原始颜色且选择显示原始颜色 + if (cloudData.hasColor && !cloudData.colors.empty()) { + glEnableClientState(GL_COLOR_ARRAY); + if (cloudData.vboCreated && cloudData.colorBuffer.isCreated()) { + cloudData.colorBuffer.bind(); + glColorPointer(3, GL_FLOAT, 0, nullptr); + cloudData.colorBuffer.release(); + } else { + glColorPointer(3, GL_FLOAT, 0, cloudData.colors.data()); + } + } else { + // 使用点云自己的颜色索引 + glDisableClientState(GL_COLOR_ARRAY); + setColorByIndex(cloudData.colorIndex); + } + + glDrawArrays(GL_POINTS, 0, static_cast(cloudData.vertices.size() / 3)); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + } + + drawSelectedPoints(); + drawMeasurementLine(); + drawSelectedLine(); + drawLineSegments(); + drawPosePoints(); + + // 最后绘制坐标系指示器(覆盖在所有内容之上) + drawAxis(); +} + +void PointCloudGLWidget::addPointCloud(const PointCloudXYZ& cloud, const QString& name) +{ + LOG_INFO("[CloudView] addPointCloud called, cloud size: %zu\n", cloud.size()); + + if (cloud.empty()) { + LOG_WARN("[CloudView] Cloud is empty, returning\n"); + return; + } + + PointCloudData data; + data.name = name; + data.hasColor = false; + data.hasLineInfo = !cloud.lineIndices.empty(); + data.colorIndex = m_colorIndex; // 分配颜色索引 + m_colorIndex = (m_colorIndex + 1) % COLOR_COUNT; // 轮换到下一个颜色 + data.vertices.reserve(cloud.size() * 3); + data.totalLines = 0; + data.pointsPerLine = 0; + + const float EPSILON = 1e-6f; + for (size_t i = 0; i < cloud.points.size(); ++i) { + const auto& pt = cloud.points[i]; + if (!std::isfinite(pt.x) || !std::isfinite(pt.y) || !std::isfinite(pt.z)) { + continue; + } + // 显示时过滤 (0,0,0) 点 + if (std::fabs(pt.x) < EPSILON && std::fabs(pt.y) < EPSILON && std::fabs(pt.z) < EPSILON) { + continue; + } + data.vertices.push_back(pt.x); + data.vertices.push_back(pt.y); + data.vertices.push_back(pt.z); + + // 保存原始索引 + data.originalIndices.push_back(static_cast(i)); + + // 保存线索引 + if (data.hasLineInfo && i < cloud.lineIndices.size()) { + int lineIdx = cloud.lineIndices[i]; + data.lineIndices.push_back(lineIdx); + if (lineIdx + 1 > data.totalLines) { + data.totalLines = lineIdx + 1; + } + } + } + + // 计算每线点数(假设网格化点云) + if (data.totalLines > 0) { + data.pointsPerLine = static_cast(cloud.size()) / data.totalLines; + } + + LOG_INFO("[CloudView] Valid vertices count: %zu, colorIndex: %d, totalLines: %d, pointsPerLine: %d\n", + data.vertices.size() / 3, data.colorIndex, data.totalLines, data.pointsPerLine); + + m_pointClouds.push_back(std::move(data)); + + // 上传 VBO(如果当前在 GL 上下文中) + makeCurrent(); + uploadToVBO(m_pointClouds.back()); + doneCurrent(); + + computeBoundingBox(); + LOG_INFO("[CloudView] BoundingBox min: (%.3f, %.3f, %.3f) max: (%.3f, %.3f, %.3f)\n", + m_minBound.x(), m_minBound.y(), m_minBound.z(), + m_maxBound.x(), m_maxBound.y(), m_maxBound.z()); + LOG_INFO("[CloudView] Center: (%.3f, %.3f, %.3f) Distance: %.3f\n", + m_center.x(), m_center.y(), m_center.z(), m_distance); + + resetView(); + update(); +} + +void PointCloudGLWidget::addPointCloud(const PointCloudXYZRGB& cloud, const QString& name) +{ + if (cloud.empty()) { + return; + } + + PointCloudData data; + data.name = name; + data.hasColor = true; + data.hasLineInfo = !cloud.lineIndices.empty(); + data.colorIndex = m_colorIndex; // 即使有颜色也分配索引(备用) + m_colorIndex = (m_colorIndex + 1) % COLOR_COUNT; + data.vertices.reserve(cloud.size() * 3); + data.colors.reserve(cloud.size() * 3); + data.totalLines = 0; + data.pointsPerLine = 0; + + for (size_t i = 0; i < cloud.points.size(); ++i) { + const auto& pt = cloud.points[i]; + if (!std::isfinite(pt.x) || !std::isfinite(pt.y) || !std::isfinite(pt.z)) { + continue; + } + data.vertices.push_back(pt.x); + data.vertices.push_back(pt.y); + data.vertices.push_back(pt.z); + + data.colors.push_back(pt.r / 255.0f); + data.colors.push_back(pt.g / 255.0f); + data.colors.push_back(pt.b / 255.0f); + + // 保存原始索引 + data.originalIndices.push_back(static_cast(i)); + + // 保存线索引 + if (data.hasLineInfo && i < cloud.lineIndices.size()) { + int lineIdx = cloud.lineIndices[i]; + data.lineIndices.push_back(lineIdx); + if (lineIdx + 1 > data.totalLines) { + data.totalLines = lineIdx + 1; + } + } + } + + // 计算每线点数 + if (data.totalLines > 0) { + data.pointsPerLine = static_cast(cloud.size()) / data.totalLines; + } + + m_pointClouds.push_back(std::move(data)); + + // 上传 VBO + makeCurrent(); + uploadToVBO(m_pointClouds.back()); + doneCurrent(); + + computeBoundingBox(); + resetView(); + update(); +} + +void PointCloudGLWidget::clearPointClouds() +{ + // 释放所有 VBO + makeCurrent(); + for (auto& cloudData : m_pointClouds) { + releaseVBO(cloudData); + } + doneCurrent(); + + m_pointClouds.clear(); + m_selectedPoints.clear(); + m_selectedLine = SelectedLineInfo(); + m_lineSegments.clear(); + m_posePoints.clear(); + m_minBound = QVector3D(-50, -50, -50); + m_maxBound = QVector3D(50, 50, 50); + m_center = QVector3D(0, 0, 0); + m_colorIndex = 0; // 重置颜色索引 + resetView(); + update(); +} + +void PointCloudGLWidget::setPointCloudColor(PointCloudColor color) +{ + m_currentColor = color; + update(); +} + +void PointCloudGLWidget::setPointSize(float size) +{ + m_pointSize = qBound(1.0f, size, 10.0f); + update(); +} + +void PointCloudGLWidget::resetView() +{ + QVector3D size = m_maxBound - m_minBound; + float maxSize = qMax(qMax(size.x(), size.y()), size.z()); + + // 确保最小视距 + m_distance = qMax(maxSize * 2.0f, 10.0f); + // 默认视角:右手坐标系,X轴朝右,Y轴朝上,Z轴朝向观察者 + m_rotationX = 0.0f; + m_rotationY = 0.0f; + m_rotationZ = 0.0f; + m_pan = QVector3D(0, 0, 0); + + LOG_INFO("[CloudView] resetView: size=(%.3f, %.3f, %.3f) maxSize=%.3f distance=%.3f\n", + size.x(), size.y(), size.z(), maxSize, m_distance); + + emit viewAnglesChanged(m_rotationX, m_rotationY, m_rotationZ); + update(); +} + +void PointCloudGLWidget::setViewAngles(float rotX, float rotY, float rotZ) +{ + m_rotationX = rotX; + m_rotationY = rotY; + m_rotationZ = rotZ; + m_pan = QVector3D(0, 0, 0); + + LOG_INFO("[CloudView] setViewAngles: rotX=%.1f, rotY=%.1f, rotZ=%.1f\n", rotX, rotY, rotZ); + + emit viewAnglesChanged(m_rotationX, m_rotationY, m_rotationZ); + update(); +} + +void PointCloudGLWidget::clearSelectedPoints() +{ + m_selectedPoints.clear(); + update(); +} + +void PointCloudGLWidget::clearSelectedLine() +{ + m_selectedLine = SelectedLineInfo(); + update(); +} + +bool PointCloudGLWidget::selectLineByIndex(int lineIndex) +{ + if (m_pointClouds.empty()) { + return false; + } + + const auto& cloudData = m_pointClouds[0]; + if (!cloudData.hasLineInfo) { + return false; + } + + // 检查线索引是否有效 + if (lineIndex < 0 || lineIndex >= cloudData.totalLines) { + return false; + } + + // 计算该线上的点数 + int pointCount = 0; + for (int idx : cloudData.lineIndices) { + if (idx == lineIndex) { + pointCount++; + } + } + + if (pointCount == 0) { + return false; + } + + // 设置选中的线 + m_selectedLine.valid = true; + m_selectedLine.cloudIndex = 0; + m_selectedLine.lineIndex = lineIndex; + m_selectedLine.pointIndex = -1; + m_selectedLine.pointCount = pointCount; + m_selectedLine.mode = LineSelectMode::Vertical; + + emit lineSelected(m_selectedLine); + update(); + return true; +} + +bool PointCloudGLWidget::selectHorizontalLineByIndex(int pointIndex) +{ + if (m_pointClouds.empty()) { + return false; + } + + const auto& cloudData = m_pointClouds[0]; + if (!cloudData.hasLineInfo || cloudData.totalLines <= 0 || cloudData.pointsPerLine <= 0) { + return false; + } + + // 检查点索引是否有效(使用原始点云的每线点数) + if (pointIndex < 0 || pointIndex >= cloudData.pointsPerLine) { + return false; + } + + // 设置选中的横向线 + m_selectedLine.valid = true; + m_selectedLine.cloudIndex = 0; + m_selectedLine.lineIndex = -1; + m_selectedLine.pointIndex = pointIndex; + m_selectedLine.pointCount = cloudData.totalLines; // 横向线的点数等于总线数 + m_selectedLine.mode = LineSelectMode::Horizontal; + + emit lineSelected(m_selectedLine); + update(); + return true; +} + +float PointCloudGLWidget::calculateDistance(const SelectedPointInfo& p1, const SelectedPointInfo& p2) +{ + if (!p1.valid || !p2.valid) { + return 0.0f; + } + + float dx = p2.x - p1.x; + float dy = p2.y - p1.y; + float dz = p2.z - p1.z; + + return std::sqrt(dx * dx + dy * dy + dz * dz); +} + +QVector PointCloudGLWidget::getSelectedLinePoints() const +{ + QVector points; + + if (!m_selectedLine.valid || m_selectedLine.cloudIndex < 0) { + return points; + } + + if (m_selectedLine.cloudIndex >= static_cast(m_pointClouds.size())) { + return points; + } + + const auto& cloudData = m_pointClouds[m_selectedLine.cloudIndex]; + if (!cloudData.hasLineInfo) { + return points; + } + + if (m_selectedLine.mode == LineSelectMode::Vertical) { + // 纵向选线:获取同一条扫描线上的所有点 + for (size_t i = 0; i < cloudData.lineIndices.size(); ++i) { + if (cloudData.lineIndices[i] == m_selectedLine.lineIndex) { + size_t vertIdx = i * 3; + if (vertIdx + 2 < cloudData.vertices.size()) { + points.append(QVector3D( + cloudData.vertices[vertIdx], + cloudData.vertices[vertIdx + 1], + cloudData.vertices[vertIdx + 2])); + } + } + } + } else { + // 横向选线:获取所有线的相同索引点 + if (cloudData.pointsPerLine > 0 && m_selectedLine.pointIndex >= 0) { + for (size_t i = 0; i < cloudData.originalIndices.size(); ++i) { + int originalIdx = cloudData.originalIndices[i]; + if (originalIdx % cloudData.pointsPerLine == m_selectedLine.pointIndex) { + size_t vertIdx = i * 3; + if (vertIdx + 2 < cloudData.vertices.size()) { + points.append(QVector3D( + cloudData.vertices[vertIdx], + cloudData.vertices[vertIdx + 1], + cloudData.vertices[vertIdx + 2])); + } + } + } + } + } + + return points; +} + +void PointCloudGLWidget::setListHighlightPoint(const QVector3D& point) +{ + m_hasListHighlightPoint = true; + m_listHighlightPoint = point; + update(); +} + +void PointCloudGLWidget::clearListHighlightPoint() +{ + m_hasListHighlightPoint = false; + update(); +} + +void PointCloudGLWidget::transformAllClouds(const QMatrix4x4& matrix) +{ + for (auto& cloudData : m_pointClouds) { + for (size_t i = 0; i + 2 < cloudData.vertices.size(); i += 3) { + QVector3D pt(cloudData.vertices[i], cloudData.vertices[i + 1], cloudData.vertices[i + 2]); + QVector3D transformed = matrix.map(pt); + cloudData.vertices[i] = transformed.x(); + cloudData.vertices[i + 1] = transformed.y(); + cloudData.vertices[i + 2] = transformed.z(); + } + } + + // 重新上传 VBO(顶点数据已变更) + makeCurrent(); + for (auto& cloudData : m_pointClouds) { + uploadToVBO(cloudData); + } + doneCurrent(); + + computeBoundingBox(); + resetView(); + update(); +} + +void PointCloudGLWidget::computeBoundingBox() +{ + if (m_pointClouds.empty()) { + m_minBound = QVector3D(-50, -50, -50); + m_maxBound = QVector3D(50, 50, 50); + m_center = QVector3D(0, 0, 0); + return; + } + + float minX = FLT_MAX, minY = FLT_MAX, minZ = FLT_MAX; + float maxX = -FLT_MAX, maxY = -FLT_MAX, maxZ = -FLT_MAX; + + for (const auto& cloudData : m_pointClouds) { + for (size_t i = 0; i < cloudData.vertices.size(); i += 3) { + float x = cloudData.vertices[i]; + float y = cloudData.vertices[i + 1]; + float z = cloudData.vertices[i + 2]; + + minX = qMin(minX, x); + minY = qMin(minY, y); + minZ = qMin(minZ, z); + maxX = qMax(maxX, x); + maxY = qMax(maxY, y); + maxZ = qMax(maxZ, z); + } + } + + m_minBound = QVector3D(minX, minY, minZ); + m_maxBound = QVector3D(maxX, maxY, maxZ); + m_center = (m_minBound + m_maxBound) / 2.0f; +} + +void PointCloudGLWidget::setCurrentColor(PointCloudColor color) +{ + switch (color) { + case PointCloudColor::White: glColor3f(1.0f, 1.0f, 1.0f); break; + case PointCloudColor::Red: glColor3f(1.0f, 0.0f, 0.0f); break; + case PointCloudColor::Green: glColor3f(0.0f, 1.0f, 0.0f); break; + case PointCloudColor::Blue: glColor3f(0.0f, 0.0f, 1.0f); break; + case PointCloudColor::Yellow: glColor3f(1.0f, 1.0f, 0.0f); break; + case PointCloudColor::Cyan: glColor3f(0.0f, 1.0f, 1.0f); break; + case PointCloudColor::Magenta: glColor3f(1.0f, 0.0f, 1.0f); break; + default: glColor3f(1.0f, 1.0f, 1.0f); break; + } +} + +void PointCloudGLWidget::setColorByIndex(int colorIndex) +{ + // 颜色表:浅灰、浅绿、浅蓝、黄、青、品红、白 + static const float colorTable[7][3] = { + {0.75f, 0.75f, 0.75f}, // 浅灰色 + {0.3f, 1.0f, 0.3f}, // 浅绿 + {0.3f, 0.3f, 1.0f}, // 浅蓝 + {1.0f, 1.0f, 0.3f}, // 黄 + {0.3f, 1.0f, 1.0f}, // 青 + {1.0f, 0.3f, 1.0f}, // 品红 + {1.0f, 1.0f, 1.0f}, // 白 + }; + + int idx = colorIndex % COLOR_COUNT; + glColor3f(colorTable[idx][0], colorTable[idx][1], colorTable[idx][2]); +} + +SelectedPointInfo PointCloudGLWidget::pickPoint(int screenX, int screenY) +{ + SelectedPointInfo result; + + if (m_pointClouds.empty()) { + return result; + } + + // 获取视口和矩阵 + GLint viewport[4]; + GLdouble modelMatrix[16], projMatrix[16]; + + glGetIntegerv(GL_VIEWPORT, viewport); + glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix); + glGetDoublev(GL_PROJECTION_MATRIX, projMatrix); + + // 转换屏幕 Y 坐标(OpenGL Y 轴向上) + int glScreenY = viewport[3] - screenY; + + float minScreenDist = FLT_MAX; + size_t bestIndex = 0; + int bestCloudIndex = -1; + int bestLineIndex = -1; + float bestX = 0, bestY = 0, bestZ = 0; + + // 遍历所有点,计算屏幕投影距离 + for (size_t cloudIdx = 0; cloudIdx < m_pointClouds.size(); ++cloudIdx) { + const auto& cloudData = m_pointClouds[cloudIdx]; + for (size_t i = 0; i < cloudData.vertices.size(); i += 3) { + float wx = cloudData.vertices[i]; + float wy = cloudData.vertices[i + 1]; + float wz = cloudData.vertices[i + 2]; + + // 将世界坐标投影到屏幕 + GLdouble sx, sy, sz; + gluProject(wx, wy, wz, modelMatrix, projMatrix, viewport, &sx, &sy, &sz); + + // 计算屏幕距离 + float dx = static_cast(sx) - screenX; + float dy = static_cast(sy) - glScreenY; + float screenDist = dx * dx + dy * dy; + + if (screenDist < minScreenDist) { + minScreenDist = screenDist; + bestIndex = i / 3; + bestCloudIndex = static_cast(cloudIdx); + bestX = wx; + bestY = wy; + bestZ = wz; + + // 获取线索引 + if (cloudData.hasLineInfo && bestIndex < cloudData.lineIndices.size()) { + bestLineIndex = cloudData.lineIndices[bestIndex]; + } else { + bestLineIndex = -1; + } + } + } + } + + // 屏幕距离阈值(像素),20像素内认为选中 + float threshold = 20.0f * 20.0f; + if (minScreenDist < threshold) { + result.valid = true; + result.index = bestIndex; + result.cloudIndex = bestCloudIndex; + result.lineIndex = bestLineIndex; + result.x = bestX; + result.y = bestY; + result.z = bestZ; + + // 计算点在线中的原始索引 + if (bestLineIndex >= 0 && bestCloudIndex >= 0) { + const auto& cloudData = m_pointClouds[bestCloudIndex]; + // 使用原始索引计算:原始索引 % 每线点数 + if (bestIndex < cloudData.originalIndices.size() && cloudData.pointsPerLine > 0) { + int originalIdx = cloudData.originalIndices[bestIndex]; + result.pointIndexInLine = originalIdx % cloudData.pointsPerLine; + } + } + + LOG_INFO("[CloudView] Point selected: (%.3f, %.3f, %.3f) lineIndex=%d pointIndexInLine=%d screenDist=%.1f\n", + bestX, bestY, bestZ, bestLineIndex, result.pointIndexInLine, std::sqrt(minScreenDist)); + } else { + LOG_INFO("[CloudView] No point selected, minScreenDist=%.1f\n", std::sqrt(minScreenDist)); + } + + return result; +} + +void PointCloudGLWidget::drawSelectedPoints() +{ + glPointSize(10.0f); + glDisable(GL_DEPTH_TEST); + + glBegin(GL_POINTS); + + // 绘制选中的点(橙色) + for (const auto& pt : m_selectedPoints) { + if (pt.valid) { + glColor3f(1.0f, 0.5f, 0.0f); // 橙色 + glVertex3f(pt.x, pt.y, pt.z); + } + } + + // 绘制列表高亮点(蓝色,与选点区分) + if (m_hasListHighlightPoint) { + glColor3f(0.0f, 0.5f, 1.0f); // 蓝色 + glVertex3f(m_listHighlightPoint.x(), m_listHighlightPoint.y(), m_listHighlightPoint.z()); + } + + glEnd(); + + glEnable(GL_DEPTH_TEST); + glPointSize(m_pointSize); +} + +void PointCloudGLWidget::drawMeasurementLine() +{ + if (m_selectedPoints.size() < 2) { + return; + } + + const auto& p1 = m_selectedPoints[0]; + const auto& p2 = m_selectedPoints[1]; + + if (!p1.valid || !p2.valid) { + return; + } + + glDisable(GL_DEPTH_TEST); + glLineWidth(2.0f); + + glBegin(GL_LINES); + glColor3f(0.0f, 1.0f, 0.0f); + glVertex3f(p1.x, p1.y, p1.z); + glVertex3f(p2.x, p2.y, p2.z); + glEnd(); + + glLineWidth(1.0f); + glEnable(GL_DEPTH_TEST); +} + +void PointCloudGLWidget::drawSelectedLine() +{ + if (!m_selectedLine.valid || m_selectedLine.cloudIndex < 0) { + return; + } + + if (m_selectedLine.cloudIndex >= static_cast(m_pointClouds.size())) { + return; + } + + const auto& cloudData = m_pointClouds[m_selectedLine.cloudIndex]; + if (!cloudData.hasLineInfo) { + return; + } + + // 高亮显示选中线上的所有点 + glPointSize(3.0f); + glDisable(GL_DEPTH_TEST); + + glBegin(GL_POINTS); + glColor3f(1.0f, 1.0f, 0.0f); // 黄色高亮 + + if (m_selectedLine.mode == LineSelectMode::Vertical) { + // 纵向选线:显示同一条扫描线上的所有点 + for (size_t i = 0; i < cloudData.lineIndices.size(); ++i) { + if (cloudData.lineIndices[i] == m_selectedLine.lineIndex) { + size_t vertIdx = i * 3; + if (vertIdx + 2 < cloudData.vertices.size()) { + glVertex3f(cloudData.vertices[vertIdx], + cloudData.vertices[vertIdx + 1], + cloudData.vertices[vertIdx + 2]); + } + } + } + } else { + // 横向选线:显示所有线的相同原始索引点 + if (cloudData.pointsPerLine > 0 && m_selectedLine.pointIndex >= 0) { + for (size_t i = 0; i < cloudData.originalIndices.size(); ++i) { + int originalIdx = cloudData.originalIndices[i]; + // 原始索引 % 每线点数 == 选中的点索引 + if (originalIdx % cloudData.pointsPerLine == m_selectedLine.pointIndex) { + size_t vertIdx = i * 3; + if (vertIdx + 2 < cloudData.vertices.size()) { + glVertex3f(cloudData.vertices[vertIdx], + cloudData.vertices[vertIdx + 1], + cloudData.vertices[vertIdx + 2]); + } + } + } + } + } + glEnd(); + + glEnable(GL_DEPTH_TEST); + glPointSize(m_pointSize); +} + +void PointCloudGLWidget::drawAxis() +{ + // 在右下角绘制坐标系指示器(右手坐标系) + // X轴:红色,指向右 + // Y轴:绿色,指向上 + // Z轴:蓝色,指向观察者(屏幕外) + GLint viewport[4]; + glGetIntegerv(GL_VIEWPORT, viewport); + + int axisSize = 60; // 坐标系区域大小(像素) + int margin = 10; // 距离边缘的边距 + int axisX = viewport[2] - axisSize - margin; // 右下角 X + int axisY = margin; // 右下角 Y(OpenGL Y 轴向上) + + // 保存当前矩阵状态 + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + // 设置正交投影,覆盖整个视口 + glOrtho(0, viewport[2], 0, viewport[3], -100, 100); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + // 移动到右下角中心位置 + glTranslatef(axisX + axisSize / 2.0f, axisY + axisSize / 2.0f, 0.0f); + + // 应用当前视图的旋转(与主视图同步) + glRotatef(m_rotationY, 0, 1, 0); + glRotatef(m_rotationX, 1, 0, 0); + glRotatef(m_rotationZ, 0, 0, 1); + + // 关闭深度测试,确保坐标系始终可见 + glDisable(GL_DEPTH_TEST); + + float axisLength = axisSize * 0.4f; // 坐标轴长度 + glLineWidth(2.0f); + glBegin(GL_LINES); + + // X 轴 - 红色(右手坐标系:向右) + glColor3f(1.0f, 0.0f, 0.0f); + glVertex3f(0.0f, 0.0f, 0.0f); + glVertex3f(axisLength, 0.0f, 0.0f); + + // Y 轴 - 绿色(右手坐标系:向上) + glColor3f(0.0f, 1.0f, 0.0f); + glVertex3f(0.0f, 0.0f, 0.0f); + glVertex3f(0.0f, axisLength, 0.0f); + + // Z 轴 - 蓝色(右手坐标系:向观察者) + glColor3f(0.0f, 0.0f, 1.0f); + glVertex3f(0.0f, 0.0f, 0.0f); + glVertex3f(0.0f, 0.0f, axisLength); + + glEnd(); + glLineWidth(1.0f); + + // 恢复深度测试 + glEnable(GL_DEPTH_TEST); + + // 恢复矩阵状态 + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); +} + +void PointCloudGLWidget::mousePressEvent(QMouseEvent* event) +{ + m_lastMousePos = event->pos(); + + if (event->button() == Qt::LeftButton) { + // Ctrl+左键:选点 + if (event->modifiers() & Qt::ControlModifier) { + makeCurrent(); + SelectedPointInfo point = pickPoint(event->pos().x(), event->pos().y()); + doneCurrent(); + + if (point.valid) { + if (m_measureDistanceEnabled) { + // 启用测距:最多保留两个点 + if (m_selectedPoints.size() >= MAX_SELECTED_POINTS) { + m_selectedPoints.clear(); + } + m_selectedPoints.append(point); + + emit pointSelected(point); + + if (m_selectedPoints.size() == 2) { + float distance = calculateDistance(m_selectedPoints[0], m_selectedPoints[1]); + emit twoPointsSelected(m_selectedPoints[0], m_selectedPoints[1], distance); + } + } else { + // 未启用测距:只保留一个点 + m_selectedPoints.clear(); + m_selectedPoints.append(point); + emit pointSelected(point); + } + + update(); + } + } else if (event->modifiers() & Qt::ShiftModifier) { + // Shift+左键:选线 + makeCurrent(); + SelectedPointInfo point = pickPoint(event->pos().x(), event->pos().y()); + doneCurrent(); + + if (point.valid && point.lineIndex >= 0) { + if (m_lineSelectMode == LineSelectMode::Vertical) { + // 纵向选线:选中该点所在的线 + m_selectedLine.valid = true; + m_selectedLine.cloudIndex = point.cloudIndex; + m_selectedLine.lineIndex = point.lineIndex; + m_selectedLine.pointIndex = -1; + m_selectedLine.mode = LineSelectMode::Vertical; + + // 计算该线上的点数 + int pointCount = 0; + if (point.cloudIndex >= 0 && point.cloudIndex < static_cast(m_pointClouds.size())) { + const auto& cloudData = m_pointClouds[point.cloudIndex]; + for (int idx : cloudData.lineIndices) { + if (idx == point.lineIndex) { + pointCount++; + } + } + } + m_selectedLine.pointCount = pointCount; + } else { + // 横向选线:选中所有线的相同索引点 + m_selectedLine.valid = true; + m_selectedLine.cloudIndex = point.cloudIndex; + m_selectedLine.lineIndex = -1; + m_selectedLine.pointIndex = point.pointIndexInLine; + m_selectedLine.mode = LineSelectMode::Horizontal; + + // 横向线的点数等于总线数 + if (point.cloudIndex >= 0 && point.cloudIndex < static_cast(m_pointClouds.size())) { + m_selectedLine.pointCount = m_pointClouds[point.cloudIndex].totalLines; + } + } + + emit lineSelected(m_selectedLine); + update(); + } + } else { + m_leftButtonPressed = true; + } + } else if (event->button() == Qt::RightButton) { + m_rightButtonPressed = true; + } else if (event->button() == Qt::MiddleButton) { + m_middleButtonPressed = true; + } +} + +void PointCloudGLWidget::mouseMoveEvent(QMouseEvent* event) +{ + QPoint delta = event->pos() - m_lastMousePos; + m_lastMousePos = event->pos(); + + if (m_leftButtonPressed) { + // Alt+左键拖动:旋转rotZ(滚转) + if (event->modifiers() & Qt::AltModifier) { + m_rotationZ += delta.x() * 0.5f; + } else { + // 普通左键拖动:旋转rotX和rotY + m_rotationY += delta.x() * 0.5f; + m_rotationX += delta.y() * 0.5f; + } + // 移除角度限制,允许无限旋转 + emit viewAnglesChanged(m_rotationX, m_rotationY, m_rotationZ); + update(); + } else if (m_middleButtonPressed) { + float factor = m_distance * 0.002f; + m_pan.setX(m_pan.x() - delta.x() * factor); + m_pan.setY(m_pan.y() + delta.y() * factor); + update(); + } else if (m_rightButtonPressed) { + m_rotationZ += delta.x() * 0.5f; + emit viewAnglesChanged(m_rotationX, m_rotationY, m_rotationZ); + update(); + } +} + +void PointCloudGLWidget::mouseReleaseEvent(QMouseEvent* event) +{ + if (event->button() == Qt::LeftButton) { + m_leftButtonPressed = false; + } else if (event->button() == Qt::RightButton) { + m_rightButtonPressed = false; + } else if (event->button() == Qt::MiddleButton) { + m_middleButtonPressed = false; + } +} + +void PointCloudGLWidget::wheelEvent(QWheelEvent* event) +{ + float delta = event->angleDelta().y() / 120.0f; + m_distance *= (1.0f - delta * 0.1f); + m_distance = qBound(0.1f, m_distance, 10000.0f); + update(); +} + +void PointCloudGLWidget::keyPressEvent(QKeyEvent* event) +{ + if (event->key() == Qt::Key_Space) { + resetView(); + } else { + QOpenGLWidget::keyPressEvent(event); + } +} + +bool PointCloudGLWidget::getFirstCloudData(PointCloudXYZ& cloud) const +{ + if (m_pointClouds.empty()) { + return false; + } + + const auto& cloudData = m_pointClouds[0]; + cloud.clear(); + cloud.reserve(cloudData.vertices.size() / 3); + + for (size_t i = 0; i < cloudData.vertices.size(); i += 3) { + Point3D pt; + pt.x = cloudData.vertices[i]; + pt.y = cloudData.vertices[i + 1]; + pt.z = cloudData.vertices[i + 2]; + + int lineIdx = 0; + if (cloudData.hasLineInfo && (i / 3) < cloudData.lineIndices.size()) { + lineIdx = cloudData.lineIndices[i / 3]; + } + cloud.push_back(pt, lineIdx); + } + + return true; +} + +void PointCloudGLWidget::replaceFirstCloud(const PointCloudXYZ& cloud, const QString& name) +{ + if (m_pointClouds.empty()) { + addPointCloud(cloud, name); + return; + } + + // 保留第一个点云的颜色索引 + int colorIndex = m_pointClouds[0].colorIndex; + + // 重新构建数据 + PointCloudData data; + data.name = name; + data.hasColor = false; + data.hasLineInfo = !cloud.lineIndices.empty(); + data.colorIndex = colorIndex; + data.vertices.reserve(cloud.size() * 3); + data.totalLines = 0; + data.pointsPerLine = 0; + + const float EPSILON = 1e-6f; + for (size_t i = 0; i < cloud.points.size(); ++i) { + const auto& pt = cloud.points[i]; + if (!std::isfinite(pt.x) || !std::isfinite(pt.y) || !std::isfinite(pt.z)) { + continue; + } + // 显示时过滤 (0,0,0) 点 + if (std::fabs(pt.x) < EPSILON && std::fabs(pt.y) < EPSILON && std::fabs(pt.z) < EPSILON) { + continue; + } + data.vertices.push_back(pt.x); + data.vertices.push_back(pt.y); + data.vertices.push_back(pt.z); + + // 保存原始索引 + data.originalIndices.push_back(static_cast(i)); + + if (data.hasLineInfo && i < cloud.lineIndices.size()) { + int lineIdx = cloud.lineIndices[i]; + data.lineIndices.push_back(lineIdx); + if (lineIdx + 1 > data.totalLines) { + data.totalLines = lineIdx + 1; + } + } + } + + // 计算每线点数 + if (data.totalLines > 0) { + data.pointsPerLine = static_cast(cloud.size()) / data.totalLines; + } + + // 释放旧的 VBO + makeCurrent(); + releaseVBO(m_pointClouds[0]); + + m_pointClouds[0] = std::move(data); + + // 上传新的 VBO + uploadToVBO(m_pointClouds[0]); + doneCurrent(); + + computeBoundingBox(); + resetView(); + update(); +} + +void PointCloudGLWidget::addLineSegments(const QVector& segments) +{ + m_lineSegments.append(segments); + update(); +} + +void PointCloudGLWidget::clearLineSegments() +{ + m_lineSegments.clear(); + update(); +} + +void PointCloudGLWidget::addPosePoints(const QVector& poses) +{ + m_posePoints.append(poses); + update(); +} + +void PointCloudGLWidget::clearPosePoints() +{ + m_posePoints.clear(); + update(); +} + +void PointCloudGLWidget::drawLineSegments() +{ + if (m_lineSegments.isEmpty()) { + return; + } + + glLineWidth(2.0f); + glBegin(GL_LINES); + + for (const auto& seg : m_lineSegments) { + glColor3f(seg.r, seg.g, seg.b); + glVertex3f(seg.x1, seg.y1, seg.z1); + glVertex3f(seg.x2, seg.y2, seg.z2); + } + + glEnd(); + glLineWidth(1.0f); +} + +void PointCloudGLWidget::drawPosePoints() +{ + if (m_posePoints.isEmpty()) { + return; + } + + glLineWidth(2.0f); + + for (const auto& pose : m_posePoints) { + glPushMatrix(); + + // 移动到姿态点位置 + glTranslatef(pose.x, pose.y, pose.z); + + // 根据选择的欧拉角旋转顺序应用旋转 + // OpenGL的glRotatef是右乘,所以需要反向写 + switch (m_eulerRotationOrder) { + case EulerRotationOrder::ZYX: // Yaw-Pitch-Roll(最常用) + glRotatef(pose.rx, 1, 0, 0); // X轴旋转(最后应用) + glRotatef(pose.ry, 0, 1, 0); // Y轴旋转(第二个应用) + glRotatef(pose.rz, 0, 0, 1); // Z轴旋转(第一个应用) + break; + case EulerRotationOrder::XYZ: // Roll-Pitch-Yaw + glRotatef(pose.rz, 0, 0, 1); + glRotatef(pose.ry, 0, 1, 0); + glRotatef(pose.rx, 1, 0, 0); + break; + case EulerRotationOrder::ZXY: // Yaw-Roll-Pitch + glRotatef(pose.ry, 0, 1, 0); + glRotatef(pose.rx, 1, 0, 0); + glRotatef(pose.rz, 0, 0, 1); + break; + case EulerRotationOrder::YXZ: // Pitch-Roll-Yaw + glRotatef(pose.rz, 0, 0, 1); + glRotatef(pose.rx, 1, 0, 0); + glRotatef(pose.ry, 0, 1, 0); + break; + case EulerRotationOrder::XZY: // Roll-Yaw-Pitch + glRotatef(pose.ry, 0, 1, 0); + glRotatef(pose.rz, 0, 0, 1); + glRotatef(pose.rx, 1, 0, 0); + break; + case EulerRotationOrder::YZX: // Pitch-Yaw-Roll + glRotatef(pose.rx, 1, 0, 0); + glRotatef(pose.rz, 0, 0, 1); + glRotatef(pose.ry, 0, 1, 0); + break; + } + + // 绘制坐标系(右手坐标系:X红右,Y绿上,Z蓝前) + glBegin(GL_LINES); + + // X轴 - 红色 + glColor3f(1.0f, 0.0f, 0.0f); + glVertex3f(0.0f, 0.0f, 0.0f); + glVertex3f(pose.scale, 0.0f, 0.0f); + + // Y轴 - 绿色 + glColor3f(0.0f, 1.0f, 0.0f); + glVertex3f(0.0f, 0.0f, 0.0f); + glVertex3f(0.0f, pose.scale, 0.0f); + + // Z轴 - 蓝色 + glColor3f(0.0f, 0.0f, 1.0f); + glVertex3f(0.0f, 0.0f, 0.0f); + glVertex3f(0.0f, 0.0f, pose.scale); + + glEnd(); + + glPopMatrix(); + } + + glLineWidth(1.0f); +} diff --git a/CloudView/main.cpp b/CloudView/main.cpp new file mode 100644 index 0000000..1ae2828 --- /dev/null +++ b/CloudView/main.cpp @@ -0,0 +1,35 @@ +#include +#include +#include +#include "CloudViewMainWindow.h" + +#define APP_VERSION "1.1.0" + +int main(int argc, char* argv[]) +{ + QApplication app(argc, argv); + + // 设置 OpenGL 格式 - 使用 2.1 版本以确保固定管线兼容 + QSurfaceFormat format; + format.setDepthBufferSize(24); + format.setStencilBufferSize(8); + format.setVersion(2, 1); // 使用 OpenGL 2.1 以确保固定管线函数可用 + format.setSamples(4); // 抗锯齿 + QSurfaceFormat::setDefaultFormat(format); + + // 设置应用信息 + app.setApplicationName("CloudView"); + app.setOrganizationName("CloudView"); + app.setApplicationVersion(APP_VERSION); + + // 设置应用程序图标 + app.setWindowIcon(QIcon(":/logo.png")); + + // 创建并显示主窗口 + CloudViewMainWindow mainWindow; + mainWindow.setWindowTitle(QString("点云查看器 v%1").arg(APP_VERSION)); + mainWindow.resize(1280, 720); + mainWindow.show(); + + return app.exec(); +} diff --git a/CloudView/poses.txt b/CloudView/poses.txt new file mode 100644 index 0000000..b22a532 --- /dev/null +++ b/CloudView/poses.txt @@ -0,0 +1,17 @@ +# 姿态点示例 +# 格式:{x,y,z}-{r,p,y} + +# 原点 +{0,0,0}-{0,0,0} + +# X轴上 +{100,0,0}-{0,0,45} + +# Y轴上 +{0,100,0}-{0,0,90} + +# Z轴上 +{0,0,100}-{0,45,0} + +# 对角线 +{50,50,50}-{30,30,30} diff --git a/CloudView/resource/logo.ico b/CloudView/resource/logo.ico new file mode 100644 index 0000000000000000000000000000000000000000..cd0e408c0acea72dee23c29d461324b342bac58f GIT binary patch literal 38078 zcmeHQd5m4vd4FSLn-G@RH8C#67@OE;@Vxi#dt*1y1tp4L!2 zRmTAd=q6PaAxJ2J(vpRcUCKfdGzub{Nd>WR3xUj}Dr!X>Y+@E;#`F67oqK+FzI)!i z^X{8_-?Lr6K_BPbbI<*j-}jyMo_AfVjp4s5udLz!n`)oGp;miwtybF(LQ!oR#B%=a z#=X(s2!Rm-BLqeWj1U+hFhXF2zzBg88G%ikHl4a*!-h3mw{Bf~veDYLYtMl1mJi#1 z+qZAu)Mzw5+-kL^+wJzlNM}H^y(o&J*%O(pea&Wb78pDV+B-2Z@y|PU?6?$u7#pN7 z)~{cG8suIJJstxc1(qG8T?C(Wm1WgC-pgp8CF*Ohfkj6IKOCyp>$jl~ICl^}7#|-$ z2eR%0W#UfEi_o#gyvo>+FXRKC&R%@+#V@PU2ivx7I|ID$fxLOjv=|n_yjLCZ>s9Y~ zx-XrT5*PUaZQ%g=q=~A00Df14y5e4VOioUAdXeh>e@v|p)FzPreDmhbXO;B<+7h5| z9H4I%`xe7qxEHx^`5lk>EE}@)q#uMo@HztA-cuHT@VEw)U{4v6ouyaVGVwlM(xUcA z-=Hnr3;b7<@WB}TxF;+2i|W;HJ=wvO&)VxM@>%+bZ`Qqj%E-s!58R$$|FKm3G3PlQ zJRTV){=MuKR(UC(xA%JStMb0&nto^w{n6`}ia*!0=zC@dfj@P%woCV6r&fn5^3-2# zfVg2@_P(X!4_*VoUpfvO>mu^X`arg+4PZ_7p)&YKi2s&f2a>03Sc;x{RyrzGybLq7 zy7bETW9x!VQKucb^>JzZ+a>YOvM&ELY5F+e>;x-UK|XR3Hn9j2<+E6vh%DfqWr zG5)P)vlRY#ewH@L2CH8Yy=;BuGchpOw%N`^w%q3@b?r zGm7PNo}xLP^#${a8`;D(&kw3&NuT|L)k;n#}rmkyG_G>ij#kM%9?u#gDUwc1L|(a(dvZv%ZE^lRuk@AY;49&I66`xdj`)g3ba(oMR?IwbRz zImR1se{XZsrWaL#+n{(LCN?AXZv_5Npk2&stWMdL(*`UC?6XPb<3FDNNWNr*{#Uu` zu`L60(vz@difidXVKxZ9*n8Op8-4_G?>uOaY?G}@)eq>J^PiA5-CVjl$5Vv2eJ zMY!h(Jg}~CXEA_P?s*I7H-o*!y`b*{-@itG6G7k1xd6}Dp#En-aT{2qFCr$w-^KqB z@SkjRuEX^d`a8_2VfURl6ZC?N*k1|U-;etbLdQ8fKivtvExCK`2z;B15btA*L7k`t zxd~wn=HelJHbb$?lterYJr3Qj&3k8vInp{j^E1v%U2J>)3o*DbFRdDX&c$R;mb?Ui zVBcsqo)~YqSW3aqvn#OgM%ZwewsT&=Hsj`~Oo4xzSQfXdWG}*D&F454QYTNCXE>h3 z1kaE{J*9t`@;Q?C!yMYfT{z$LJ1OP;1@wO%>ONfzf9S^kzhwNw-?M$;A2qIny5X6y z6|n2m(0xhgCqm44?e;EQn`67npPPh!2(QIE@Wbusk5-00xbVUYSHVwTE`~pJssR6F z{==As-J$-xj{6bbkDi1bSkk2b5I65bd20N!d^C?@YRi?6F=_*6;_iuY+Xw!6ysZrR zfVyH#_|vo%@l|tT;!TLjIkw+CJX|^NSBO7#%af;m9JbuEZr!>UTD^HT6fyHFw(l6n zxKF0t7WXhOT$J)zhkb+;x^SFvV~y+k7kI0FFaDCJ`qIt#V20zawS{@K3!X!JXfZq- zPSCN+^;e!eCkxoPe!SiOM5r%f{|aKC^}I01N`(tk%HTNb##+CwjNCYn{WDz0i_fPS^R#RvXWnU$?F+9My}{>MUlcny25&BG(nx1#tjg1`D7 z^o_3$^@Xl`-T3YMFXyiweGhRF9-903_@Bof^YxH%3gvM91m1VEovU7Azri`-l~E=i zsE)-Sy5AVu!)wIBZqm1XyT`oTY%n0S?RREp=H zaPzea@ZZ^rW-hwuqV?j<6c}Af{$=n_)*Zl&?Z{(K{NH6|DGz*a3ge%c%NN4m&3`J2 zfAX|4B#UVh|Bc2~(ore!zdtYj-Lt_v47COE7jMQB`~B6cSEtUjPDasNE5RT3(TC&( zor(Ps?2~Mh9VT4??7tV{FMXM&@E^kZOR|9-`{vXi=0CRzf7cGYAFVHmxp*jbYihr* z;o36r$MgRJ=BYgzVD@qHb^Hu(UW+s~sExQZ>DOZ1g*|xzd$xgZLzWw7tU=s|fIa_7 zp4JBG&6MRXUMiUXtN%%C0e>qim2b7~hOVi7AK3DC;%|K*UU~YHr}i||k793a4!(Sg z#e#Y8{ig7@e8YSSd*H*pCYAAd$HVIhoew}(*H}h*_3`mPhRo+AQ}){!qU>SgAIoe# zj=8~B>Zw2Op<|4ERQPM%Xz`H^HjS}~*D##lAPyb?Cj7KT^2fCv-ZA`^buB;UIVTma zOwmu_8sjwc{vMxG9qK274ex-Eti9V)&@MimJj3UGJcjtQ&M^9a$@Td2z3Eh_J8-)K z0H@1g=X^K*z#8k~Hr8m9QS&F0&E~)E zYBlbgj2cHLarOZ|b9`fr7I4S)|6sq2V{W*AyC|CB z{=UVE>v;I!cJ`~H5P$ZkvdigCA1&0sm3v>7C-SGjr$5Fs^Bkk)H>KnSEbs99$;;a9 z^{2syZ@@G6!w=84bq1wbKMFYy!I#%T#|ywWEADH+c?Ig->~!maJ#`oc{`9x?IpqS^ zd0_l^up>22!YBL<*{vYnci2+LkbsZt2R!yify;aq7QGI9?|dJ8aXt9G2L7yvvUrW> zUyu7oK+bi|c{G)CdhupbCD>F}qEtkFSxp>`D!Tb04{Ik`ii0#tL z>O>4^Kj7CGgOT4sf7G>CMO`5C zFS$nKn5(&_<&~GG49TIse%&|_{2l)S@h4raZi+*shhS^d;t$;UV|%wwHuZYWyCf$s zwYYd6a4+c!D_eQkb_22J_}-75*2b#w59dGq=p{@-{m3H&gJqEQ-;@_Mlkq#jfjq>o zpH%o8pYF%GfUNJttVA82$6Hv77nB*=S5^FD4%IdPEQ7zWAP>+d5qGI|GWHDqDaJn@ z|A?8dTWAgv`o-cR{L#j~qIkDy15-})JqHr}eV-EY%(}L696lBG|HR+<(zHj)%F>}o znZ+OZN5e6IYc_OC2SLf0Dg2#XOT;LIS(wZlKnm%L)mq}Ki(%o{AaZR=@6#W!*I-#D>_a3x?(=8@vpM5s>Cw@Y_JZ32$$VR2&xsal7!`WS) z2aeAKC;P5@^L__{^}w4E`-emPL;drvB|FinqW-69{G~tjrLK%i#QUEE|J42?zZ(MV z9}0b7+d^V1-&2ZQ{1SVfkHc>!-8n7Sm;Dlc&fE)Gvu4eikaJHG_vFz>KS!HXKhbHx z_zTCSrWSwL{u|7NTDA=^54^_V8?FPmUP#6R`oLl3`wGS^-)V{X?uNKG_`6f|=a@@9 z2v(`Nu93xW?7R!OJxA<>W50Ikxk2Dx<@hVRtiHq(^aKBU6a!7I#`7k?=Vr`Hp5Z$I zHx?0pPcims{JHig_VB^e;QOU*+uVDV6klE7{$;>ye;Mp)leNe3sxbZsfxmQCywC^0 z`D^~0rh><%ZXX!yV))=&!2L=1fbR$rA5cF_n6n)M_V+egjmr`DxqF{}Pj9buAcl&M ze$zqXpEdvKSHHaa)LZt`2k38Z0qEhoM)_kE`l;W8J|Am08{fct-96aj{|~$$c?9nd z9%wb{KfpELBmQBpy-vuv81jB7*}^YR&tBzvKH;bMFMa;1d;LXOz4YnlzSSL=@SWpr zSO=uuA#SBYg!hO|6eD{{7~;r;0xAufY3|WC=H6o~K{0a`8#{Rv7<- z>i@&|vNixCj>-J)^;C|F2N{>`z(x$`o-!_{tmN>_WuWkzwEVs zuy%MKuq_+{eG|U867!*#AnsFZg}iov_dWbh_*HoJTfpQQhl|Hta->6Ex^(TDH{`{#-2ALO{#Eb4^<$rGvVM?#^aqG-<0#6W2OR;?Kgs?X@`?W}FXsEv zqexxxm&|y6Tp|8({3WqjM5n6i$hNFh_J(Oz-Kz8n`Gm5lOI7&eyHRfcpYi7S=8Rdk z4-fSl^tH8r(6X}lTlvg8oZK1T^6`&7@k01#>2y4w!r#t=PIf<7?3s7{kB@!4BYFm9rOseu z{_}ml_h9^M3|1d!{g4;;p?)XwwY4$R4zBlv6H}aJ;&;w$O!u3|UhN04&QHDn%fdfX zAH>7SQm)@A3xDi^@O{!9z`M|1Ri0n4r?LU~&v<@|wu8m|V(*p7s~z@AC8JlptTwG@ z!!$3JSdZ_c`7V_Bd#s+u8G={l;o46*eDFc|fZx<}yk%3UpRRTPL{39nbe)?5cg}YV z{$D5?`^HBrf&X_v?o1T@Py}1*LSk$vf1;$+*^#j!v4i%2hylO(=xhQeZk`gK8{5P0 zP8s}4XE7J$cb~ojO4@n@=X_%}4bbRngun=a z5dtFwMhJ`$7$Go1V1&SNgg|E?e==~Y(>bjCS)IYn^SIAC&V;Er?~piaH_OLnX~L>$=9f=1zkJr^ zcQcQ>C|?!l54t>yYc-empWn3k!>ogHo>W$zC;0R7dX{y>ACHeIf2MR;zPq;3=(a&R zk4@M9pmy+lr$4m)3+Er`P&*7B9dG;8Ewi=DU^L6mhmX(lSgismsOHLRz%0qnE|SMR z@~PovE}+WH1J1borB2N_50wFVoZkpNz{2r?xtA#qr2j_w<+Ec@DXud%PnF|*ZK^Yy ybM)T@CL`YK?$s9o z08oIw7Ro#nxc8i@(BfUh$T>s@ZS=iH>!m1L&AYgWE_InWt&bxe%!xYs$-in%gvo5% zs_IkfhIM6Z2uiFjrZKGM;rMxtGVB!@dd3~~L?%0O^qIyl(8%IOa1>`YTQl-l}x zE_JQ4RrIqs@A~)VwUSieO(_P~%NJk)k}%j^%zB*89RU(oR|DuNiFOHq!~jRKvMs2s zPEn>>0;X3-sAK3yE$#@CWFtyIO%-LcY8sjPJE-`w21F+XAIA*euIae#k}hMN}byfoR@)xlL&RdEAWY0!lE zCKtI%)9U5aiC(a5V@*@R?hV@A{G&T8?jA+o7!Yk_QeW*$M zz}8j{4t=rHMe%zW_q(8Du3Jow7btD*ce}qnzQwsdR(*#^P0)c-$~LsOpD9rK^GdQz zm4M~7SKLRBskJWVW~;+)B3D4Ah;CFWx!^qQtwsvL&m|jY>c$|%7^aZl z?&e7f;uwm1FkwZo%hoT?k|k-`-Yni{0`C*4oJ7sl0TBLueF z7^Nc-;7=ICpZSe$7-|Sa5Y;oMkvL3SI&ZddQJ531Oc(pWkYcTpOxmXeyw^HoeCi;1 zWC+cdxQKf948m#!15-!8fB)X!0e)@>70>gu(?%SC1yE}anhzZ-hz2swQHmA}Tu!&Z z{35I5%hVzQQNPwgG{cwZ439CTGe8nI0ngm4>@hi_s}EI8!3&F491<=%UrA)8tN2q_$P&C>kmzy{=XMSRV^$m+Ei3jl#s!r(W7?j z%IoO|2L~6UrF(LSCy1!?x2}OO_$*lQxm=u`6nbOdp@rPKGgrXJ2AQ)syWOc2{;#bo$vs zh+tzWE~L~)LphbM*3dY3g}e8<#q0E%A3*uVnlKsXNRsqcO}MqJFJiMw=RpyCi1b80 zOS!dsNPD)>q{=YQbY^KunZm=f`nZmyy&g+kt7g3hl*B z9UtD}4|*$%?P{fL9hg7C+uj;aApsI=9tK9!SMs%XoyQzDZ~Hd~Xkic*BE}>K1lpew z96pA=JzdG3TM1>qY9iuXZq!p^=lNti&>tSld~F;n1Km@YzT#W^@CAp{>avwfZ;zKI z;#nH{PFDV>cPsm~xuN(~ctpe>e*cx9ew$)8H(16wQzvM!TTw<*dh3q(taX1QTieB= zy(g47Er+4euH-Zx`Y+%p#jR_?L#E2+6ecq^SEkbluCfUMLvN0zC zQ~$G)EtDRgfJ>(_zzu%B-c3qMii@*oS9|6%!7lY++%#3u#~(|ua(8yRrsv5zNUxDr zQc`j(yB%6vGfI(E0TR5M7xYYQ|NHk#Z(!EG@hXmgE%&y6ZQkN@?4~a7!nc7G;Kg>5 z{Z=#^Jk6AGgdn!1@J=os&hzwZc!K{(+3yz}YBAhJ6R=LB?^i99)4?2mM}dd~cn5`g zdQIYDVkVl{CSN|XNv)5Lj=mjO_&y9w-UHC>QP~>9(mZf|0t%V3TP`A_Wx4IjYt5>b zg;4m;;@02V-+vv${^IXCR8&`qyg}2M!|&J(aop*XT*2E*ZWy`BSHJI1M2ifT+kV2n zruYHJ#6<@10wtfS{9__aHJ;qJf;aDQx<1`YI}@?^JZr)#um8@Q32omMMoB(|Zdq%EtUZ;Y+4%akC#*&JO;M2**D$~P>0j+{ z{!%`c$48_4)P4-O0x?ykHBx91loASv0DL%}prZCzCSs ztAF0>-ai>mUa5|+i0Q$|WiPLT=)1PuDUawDoSmKCX{IA&G znR}z-8N!${r_}a6C=8;TOy_o#=%^@!>1nyCo+;Z>wC|OeeA?j64 zO%_7C_nj(f*g&x8gr7OF8BnE0lGF_n9FkD1=Nj2DT<`B!2-hX$td}M4z9cQ}F~A9U zewgg39_N)m#N$d?I-v;;(^FHcyUw}L4gKjql9OPYCVR#ew96d;r>-dY*I2rL^Y%-X zxS;zUW!?A6hX%sZ2E#Z5qtgfi%G}%Wv4LY((nzKV#WZ93RV(zRy;mlw)~<&UKMf5{ zWTK0)=9(?O0=}Lzds28S5f!;edfZ1fawS4eIEKV2?{;FcoykDom@X7$-;5Efh0b#cD|U7&Ub*JiX8j`F%Z(4u z$UX_{hm(1BLNBuHL}v6&sg7kC9x+?`5&l48)kU|@jRFXv!AKo>7+VoZs07-mtWRq%Nxd>}t_L*A76Fkp=CKwAl6{GuGV<-h6reD=5ha-0t%%h|0lk!z6uQu6WsZfA@3YlPj44hp zqqxTuIq9fBbJVPsq|rbiZL78TAn~0(QR7NeN&chW)RZ(2&?3dLs_2L^`{e2PQwRj| z1Qq+Upr9a!OPt$QNJz*bAxIeSc&xs6Begf~1S!|i+RCmRa$uJ?H)D~t*26ZO%Axel zVjYQZyt=fgvFx?g68$t&W7QD(T=M4X^>@x0Nn`N4tn>&MDTb3w;{OKO? zPLDx7DYTwsN^{V~)$B=n{nDNO!Rz!vs*h*g1(|6wY~K=LD0xy*%LAS`qN>09pFE0+ zUo}xoPu$;Cax>!jkZ8rT3{L(0+wLZew?pc1K4+alEXjP&MpFy^uqnx+IiJ1t6?6Wu zLw3kWZo4$^#ETNxw!r!igiQ`79&%$uZoTEh`B8suqp_ja%|NJsdDf&eCxRxZ-c{|W zE4?y=xJ*O7xeM!i@r;Ia3aNP9lXJysV!8g@pAm34c|&wH$AIpQA?>fl#2H~yP`n=861WbN3JB;Y>#k2aw z*7(bZeH;>SJlVKN`Ogw9xgmpO1!d=bD@I*2_{dW`t=F`oCsF1sOc5f5Pno_x!OtvQ z2c9UCig^Xlt?m}VDHNJ5KyUDUBLjzWe9R`LOr;@6xcJl01e4sRBwWAe~ z7HI4chF@IZ<8$3L9MG};URof{F*%x>$NU<+DoOk9(J~)q>Eq`Y+v}7VX{*EA^Nd}R z0g9P^N56g$T%iuqQtvmdgiW&_t#A2jb+)wOm)CHqJ~!!a-TMZ5O_M&VQAs*`T)cMh z)ny*7E$mE%ihq5{(snb5+qm*!U(^tC1srfHyPpmEdytt{!cL;Evs6VM7T4-ip1?TC zh*2K0(Cu>*02>l{Nhr3pq$Hh|pMy^VSTG@-sLA4LaHktY^ zj|F~LqaCW{a4byuCR^0D!dJQSrSmA|bkcaP1i_4Np^UAYe@%O@=!Sm1NLj|=$yzx7 zTEG$`hS}c3Pwe8Rve^8CFB13t{Ej}2-?%v2d90juXqTMkIFteyIkcCs%Re_OW+_yL%KQ zQ9AaiU%uFQs64`w*DBOmd^i6M08otn(*kZG_1xUH7KgIQ%e?j|X3ci1j0*ZzXPt-L zbiTU1*tx9ae1ZWub?D!p%AgH4G%ugNpbwc(Qi)-1+rQQc5m}rFc_@C$=culj!kFI& z0ZH`c8&gAM&RL8k6O(4<4RF0IjU9QJ{^kkIiunrx(hm0)=jmdj3k6Zhh2kPkfg3SW z%nN!U?<_H+ng+U}VUCW)OLKp(?LyzJ-Xp%ao(||ub#h_aNf~vOY#(bS-r;x?xq?OP z^f)U;5WfsV_^=E2V5jmieBR)M1Qts-!}wd;Nr7tROwvOR3OD(pj;y#bC2luR!+|+3Ryt0U@!1pz=bz2IuN7%DH8qQa;E7b2c7!qD z!)#q`t@NLjWipK36r%=43e9gT9|@m82JSp4ZvGx(Wc0CtgY`E>!ewdoNFQnGF8T}R zBsM;OBpLC>yOrlS?wb*A?cUksgDXe&oWre<^W5;+sb%ZVfDI+%WagVUvlT`PwJ40_ z&Oks4Zj8B=)it|)freD9Jb4(n-1mX!A+nSn!7cBtvc51D$@0UcjqM5M6NWjs30)1u z?W;>`J&T$u_|KZ}QudCuWmnPD;UYxlr(b3!qbKZ_tVpdWMZeaPomP(-)s)Vsi9?b@ zMTj63o1O9kRi`UefsxBAlD*pj^YW3uLv=x=p30&VQN#$3qvQvWz1`gmUg%&xkC=D9 zj}C0bx!wKZc%yo1na|dS-P&wOO9uX23#J9FefbiK>EXQ+Dn&K|_ZkAzU2JU&mYNF8 z1dM05ytqd12emws0M>Z!nipxn>4HE1ppD*DBoc7{bUUG}^trd&wh{b0m(`wBD0CJ- z!MEtilP8Nn!81A%Cl}Mfx5T4ts!dwZ8<($-u_?qI>D4=M(ko(8HMssAEj?AZUGrLJ z<+QZ4)U0sW$HQZxSiYWbiO<3P4Ijp^DouFik~!vv{`b3&PRyAiAT-b@cJkVQraiWp zKmyAVM=qiJl@SO(DDEUO&7)LBqL03i&fEAZo%B7A3lUONrAafgT>t6u=w8l9o-18$ zw#i^S3{Bku=a?)6>u*NKmurW7`VYqm40E|J`TD(Vzh$b_Qse0?W=2aCY>s5Eb zNYa5ZU%#%KB}0URxi#YHJqG(!vR?@X*#wI?Q_Isi+s!U7_b(`ak^OiQw}05dk$7E{ z1p>|`64_pTe?b(_x#^TQ)K#|b{YC}9FlW;gE)g)V{Qj^7U%e8nJek`yAHsm%1AjXf z!qxmT(QeK{On<&qj#05*UXpnPbycV{P|8wc6XY-LSEtcJFUans+oxt^rWfi9``K>QtZqp3ctAQJPH5GP=?;9@^?n#gE=DX|}1G z;-C5!2vLixphMG7RYNx{Z8C$+zegmSrE;p2+6ICK>S%sXxL$#pigb;&;2Z{Vi1I9f z;*j?zMdxNyzT6?xqy_IvA`OT=>>zM!qK@4{|?@sUUkK{_5sQcC}`&Gltui-F;K8%G##MSg%M0LTkQ{9pVm0gWXj z-?Rd$t5cAGiCSDZQDivgB5c1F;%6YrR;HeoA2z6nBCn&m=Hy)8YZppVPlo<-A~y8<4QIbJpk&|Gem=wwTYwa+HE# z<}ov&)K_M!q!1vvfC)s#i(T)(Ya5pi=fy}O0pf)jltOb2E7`nZJ{3E0G^E3N^qmN- z=YAv&jm`-&Tq84vN|2;`;l!A(MCpx(`NUfqP%ytFb@IPI8NzqmN1K*B&~lfhQ1oo0 zNm*moJDIfgU(ZKvv1Hl^P7*dmT&*8(%+U^x&=;PBVyDeQL~sf5gGl|aKK8;RoFh*IDzdq63bt+QFoinreVsfo)qi0 zAZE2A_Aj>I@;B=DV1~;DRDlunqm(^A76}Ct)h>0X&srH6B4Pz)syDzOBA5}456kpX z10)z8(ubl6DMX}u)ylLfNE2~cimGE-z(&D`hHcCWG&PA}W}VH)r+_nMi7^sw3g-V} z2J7+`;)yZstY%L%{{dcTDL>Eq-!wH6ZF2uYfN$!?_-;h2kpKYK*R?O`Zvg-RvVWZJ Z2H>@kp*f#Oyccl + + logo.png + logo.ico + + diff --git a/CloudView/segments.txt b/CloudView/segments.txt new file mode 100644 index 0000000..ccc9d5b --- /dev/null +++ b/CloudView/segments.txt @@ -0,0 +1,23 @@ +# 线段示例 +# 格式:{x,y,z}-{x,y,z} + +# 坐标轴 +{0,0,0}-{100,0,0} +{0,0,0}-{0,100,0} +{0,0,0}-{0,0,100} + +# 立方体 +{50,50,50}-{150,50,50} +{150,50,50}-{150,150,50} +{150,150,50}-{50,150,50} +{50,150,50}-{50,50,50} + +{50,50,150}-{150,50,150} +{150,50,150}-{150,150,150} +{150,150,150}-{50,150,150} +{50,150,150}-{50,50,150} + +{50,50,50}-{50,50,150} +{150,50,50}-{150,50,150} +{150,150,50}-{150,150,150} +{50,150,50}-{50,150,150} diff --git a/DataUtils/CloudMathClac/CloudMathClac.pro b/DataUtils/CloudMathClac/CloudMathClac.pro new file mode 100644 index 0000000..861f48c --- /dev/null +++ b/DataUtils/CloudMathClac/CloudMathClac.pro @@ -0,0 +1,27 @@ +TEMPLATE = lib +CONFIG += staticlib c++17 +CONFIG -= qt + +TARGET = CloudMathClac + +INCLUDEPATH += $$PWD/Inc +INCLUDEPATH += $$PWD/../../../SDK/Device/VzNLSDK/Inc +INCLUDEPATH += $$PWD/../../../SDK/eigen-3.3.9 + +HEADERS += \ + Inc/CurveFitting.h + +SOURCES += \ + Src/CurveFitting.cpp + +# 平台特定配置 +unix { + QMAKE_CXXFLAGS += -fPIC +} + +win32 { + QMAKE_CXXFLAGS += /utf-8 + CONFIG(debug, debug|release) { + TARGET = CloudMathClacd + } +} diff --git a/DataUtils/CloudMathClac/Inc/CurveFitting.h b/DataUtils/CloudMathClac/Inc/CurveFitting.h new file mode 100644 index 0000000..99cf75f --- /dev/null +++ b/DataUtils/CloudMathClac/Inc/CurveFitting.h @@ -0,0 +1,50 @@ +#ifndef CURVE_FITTING_H +#define CURVE_FITTING_H + +#include +#include "VZNL_Types.h" + +// 平面选择 +enum Plane +{ + XY = 0, + XZ = 1, + YZ = 2 +}; + +// 直线拟合结果 +struct LineFitResult +{ + SVzNL3DPoint point; // 直线上的点 + SVzNL3DPoint direction; // 方向向量 + double error; // 拟合误差 + bool success; // 是否成功 +}; + +// 抛物线拟合结果 +struct ParabolaFitResult +{ + double a, b, c; // 抛物线系数 + double error; // 拟合误差 + bool success; // 是否成功 +}; + +// 圆拟合结果 +struct CircleFitResult +{ + SVzNL3DPoint center; // 圆心 + double radius; // 半径 + double error; // 拟合误差 + bool success; // 是否成功 +}; + +// 直线拟合(3D) +LineFitResult FitLine3D(const std::vector& points); + +// 抛物线拟合 +ParabolaFitResult FitParabola(const std::vector& points, Plane plane); + +// 圆拟合 +CircleFitResult FitCircle(const std::vector& points, Plane plane); + +#endif // CURVE_FITTING_H diff --git a/DataUtils/CloudMathClac/Src/CurveFitting.cpp b/DataUtils/CloudMathClac/Src/CurveFitting.cpp new file mode 100644 index 0000000..4ec2935 --- /dev/null +++ b/DataUtils/CloudMathClac/Src/CurveFitting.cpp @@ -0,0 +1,202 @@ +#include "CurveFitting.h" +#include +#include +#include + +using namespace Eigen; + +LineFitResult FitLine3D(const std::vector& points) +{ + LineFitResult result; + result.success = false; + result.error = 0.0; + + if (points.size() < 2) + return result; + + // 计算质心 + Vector3d centroid(0, 0, 0); + for (const auto& p : points) + { + centroid.x() += p.pt3D.x; + centroid.y() += p.pt3D.y; + centroid.z() += p.pt3D.z; + } + centroid /= points.size(); + + // 构建协方差矩阵 + Matrix3d covariance = Matrix3d::Zero(); + for (const auto& p : points) + { + Vector3d pt(p.pt3D.x - centroid.x(), + p.pt3D.y - centroid.y(), + p.pt3D.z - centroid.z()); + covariance += pt * pt.transpose(); + } + + // 特征值分解,最大特征值对应的特征向量即为方向向量 + SelfAdjointEigenSolver solver(covariance); + Vector3d direction = solver.eigenvectors().col(2); + + // 计算拟合误差 + double totalError = 0.0; + for (const auto& p : points) + { + Vector3d pt(p.pt3D.x - centroid.x(), + p.pt3D.y - centroid.y(), + p.pt3D.z - centroid.z()); + double dist = (pt - pt.dot(direction) * direction).norm(); + totalError += dist * dist; + } + + result.point.x = centroid.x(); + result.point.y = centroid.y(); + result.point.z = centroid.z(); + result.direction.x = direction.x(); + result.direction.y = direction.y(); + result.direction.z = direction.z(); + result.error = std::sqrt(totalError / points.size()); + result.success = true; + + return result; +} + +ParabolaFitResult FitParabola(const std::vector& points, Plane plane) +{ + ParabolaFitResult result; + result.success = false; + result.error = 0.0; + + if (points.size() < 3) + return result; + + MatrixXd A(points.size(), 3); + VectorXd B(points.size()); + + for (size_t i = 0; i < points.size(); ++i) + { + double u, v; + if (plane == XY) { + u = points[i].pt3D.x; + v = points[i].pt3D.y; + } else if (plane == XZ) { + u = points[i].pt3D.x; + v = points[i].pt3D.z; + } else { + u = points[i].pt3D.y; + v = points[i].pt3D.z; + } + A(i, 0) = u * u; + A(i, 1) = u; + A(i, 2) = 1.0; + B(i) = v; + } + + Vector3d coeffs = A.colPivHouseholderQr().solve(B); + result.a = coeffs(0); + result.b = coeffs(1); + result.c = coeffs(2); + + double totalError = 0.0; + for (const auto& p : points) + { + double u, v; + if (plane == XY) { + u = p.pt3D.x; + v = p.pt3D.y; + } else if (plane == XZ) { + u = p.pt3D.x; + v = p.pt3D.z; + } else { + u = p.pt3D.y; + v = p.pt3D.z; + } + double v_fit = result.a * u * u + result.b * u + result.c; + double error = v_fit - v; + totalError += error * error; + } + + result.error = std::sqrt(totalError / points.size()); + result.success = true; + return result; +} + +CircleFitResult FitCircle(const std::vector& points, Plane plane) +{ + CircleFitResult result; + result.success = false; + result.error = 0.0; + + if (points.size() < 3) + return result; + + MatrixXd A(points.size(), 3); + VectorXd B(points.size()); + + for (size_t i = 0; i < points.size(); ++i) + { + double u, v; + if (plane == XY) { + u = points[i].pt3D.x; + v = points[i].pt3D.y; + } else if (plane == XZ) { + u = points[i].pt3D.x; + v = points[i].pt3D.z; + } else { + u = points[i].pt3D.y; + v = points[i].pt3D.z; + } + A(i, 0) = u; + A(i, 1) = v; + A(i, 2) = 1.0; + B(i) = -(u * u + v * v); + } + + Vector3d coeffs = A.colPivHouseholderQr().solve(B); + double D = coeffs(0); + double E = coeffs(1); + double F = coeffs(2); + + double cu = -D / 2.0; + double cv = -E / 2.0; + result.radius = std::sqrt(D * D / 4.0 + E * E / 4.0 - F); + + if (plane == XY) { + result.center.x = cu; + result.center.y = cv; + result.center.z = 0.0; + } else if (plane == XZ) { + result.center.x = cu; + result.center.y = 0.0; + result.center.z = cv; + } else { + result.center.x = 0.0; + result.center.y = cu; + result.center.z = cv; + } + + double totalError = 0.0; + for (const auto& p : points) + { + double u, v; + if (plane == XY) { + u = p.pt3D.x; + v = p.pt3D.y; + } else if (plane == XZ) { + u = p.pt3D.x; + v = p.pt3D.z; + } else { + u = p.pt3D.y; + v = p.pt3D.z; + } + double du = u - cu; + double dv = v - cv; + double dist = std::sqrt(du * du + dv * dv); + double error = dist - result.radius; + totalError += error * error; + } + + result.error = std::sqrt(totalError / points.size()); + result.success = true; + return result; +} diff --git a/DataUtils/CoordinateTransform/CoordinateTransform.pro b/DataUtils/CoordinateTransform/CoordinateTransform.pro new file mode 100644 index 0000000..5ae2dd0 --- /dev/null +++ b/DataUtils/CoordinateTransform/CoordinateTransform.pro @@ -0,0 +1,27 @@ +TEMPLATE = lib +CONFIG += staticlib c++17 +CONFIG -= qt + +TARGET = CoordinateTransform + +INCLUDEPATH += $$PWD/Inc +INCLUDEPATH += $$PWD/../../../SDK/eigen-3.3.9 + +HEADERS += \ + Inc/CoordinateTypes.h \ + Inc/CoordinateTransform.h + +SOURCES += \ + Src/CoordinateTransform.cpp + +# 平台特定配置 +unix { + QMAKE_CXXFLAGS += -fPIC +} + +win32 { + QMAKE_CXXFLAGS += /utf-8 + CONFIG(debug, debug|release) { + TARGET = CoordinateTransformd + } +} diff --git a/DataUtils/CoordinateTransform/Inc/CoordinateTransform.h b/DataUtils/CoordinateTransform/Inc/CoordinateTransform.h new file mode 100644 index 0000000..034edd7 --- /dev/null +++ b/DataUtils/CoordinateTransform/Inc/CoordinateTransform.h @@ -0,0 +1,169 @@ +#ifndef COORDINATE_TRANSFORM_H +#define COORDINATE_TRANSFORM_H + +#include "CoordinateTypes.h" + +namespace CCoordinateTransform +{ + // ==================== 欧拉角与四元数互转 ==================== + + /// 欧拉角转四元数 + /// @param euler 欧拉角 (弧度制) + /// @return 归一化的四元数 + CTQuaternionD eulerToQuaternion(const CTEulerAngles& euler); + + /// 四元数转欧拉角 + /// @param q 四元数 (会自动归一化) + /// @param order 欧拉角顺序 + /// @return 欧拉角 (弧度制) + CTEulerAngles quaternionToEuler(const CTQuaternionD& q, CTEulerOrder order = CTEulerOrder::ZYX); + + // ==================== 旋转矩阵与欧拉角互转 ==================== + + /// 欧拉角转旋转矩阵 + /// @param euler 欧拉角 (弧度制) + /// @return 3x3旋转矩阵 + CTRotationMatrix eulerToRotationMatrix(const CTEulerAngles& euler); + + /// 旋转矩阵转欧拉角 + /// @param R 3x3旋转矩阵 + /// @param order 欧拉角顺序 + /// @return 欧拉角 (弧度制) + CTEulerAngles rotationMatrixToEuler(const CTRotationMatrix& R, CTEulerOrder order = CTEulerOrder::ZYX); + + // ==================== 旋转矩阵与四元数互转 ==================== + + /// 四元数转旋转矩阵 + /// @param q 四元数 + /// @return 3x3旋转矩阵 + CTRotationMatrix quaternionToRotationMatrix(const CTQuaternionD& q); + + /// 旋转矩阵转四元数 + /// @param R 3x3旋转矩阵 + /// @return 归一化的四元数 + CTQuaternionD rotationMatrixToQuaternion(const CTRotationMatrix& R); + + // ==================== 三轴手眼转换 ==================== + + /// 三轴手眼转换:相机坐标 -> 机器人坐标 + /// 适用于三轴龙门架等只有XYZ平移和Z轴旋转的设备 + /// @param cameraPt 相机坐标系下的点 + /// @param robotPose 机器人当前位姿 (XYZ + Rz) + /// @param handEye 三轴手眼标定矩阵 + /// @return 机器人基坐标系下的点 + CTVec3D threeAxisCameraToRobot(const CTVec3D& cameraPt, + const CTRobotPose& robotPose, + const CTThreeAxisCalibResult& handEye); + + /// 三轴手眼转换:机器人坐标 -> 相机坐标 + /// @param robotPt 机器人基坐标系下的点 + /// @param robotPose 机器人当前位姿 (XYZ + Rz) + /// @param handEye 三轴手眼标定矩阵 + /// @return 相机坐标系下的点 + CTVec3D threeAxisRobotToCamera(const CTVec3D& robotPt, + const CTRobotPose& robotPose, + const CTThreeAxisCalibResult& handEye); + + /// 三轴手眼转换:计算抓取位姿 + /// @param cameraPt 相机坐标系下的目标点 + /// @param cameraAngle 相机坐标系下的目标角度 (弧度) + /// @param robotPose 机器人当前位姿 + /// @param handEye 三轴手眼标定矩阵 + /// @param[out] targetPose 输出的目标抓取位姿 + void threeAxisCalcGraspPose(const CTVec3D& cameraPt, + double cameraAngle, + const CTRobotPose& robotPose, + const CTThreeAxisCalibResult& handEye, + CTRobotPose& targetPose); + + // ==================== 六轴手眼转换 ==================== + + /// 六轴手眼转换 (Eye-in-Hand):相机坐标 -> 机器人坐标 + /// 相机安装在机器人末端执行器上 + /// @param cameraPt 相机坐标系下的点 + /// @param robotPose 机器人当前位姿 (完整6轴) + /// @param handEye 六轴手眼标定矩阵 (相机到末端执行器) + /// @return 机器人基坐标系下的点 + CTVec3D sixAxisEyeInHandCameraToRobot(const CTVec3D& cameraPt, + const CTRobotPose& robotPose, + const CTSixAxisCalibResult& handEye); + + /// 六轴手眼转换 (Eye-in-Hand):机器人坐标 -> 相机坐标 + /// @param robotPt 机器人基坐标系下的点 + /// @param robotPose 机器人当前位姿 + /// @param handEye 六轴手眼标定矩阵 + /// @return 相机坐标系下的点 + CTVec3D sixAxisEyeInHandRobotToCamera(const CTVec3D& robotPt, + const CTRobotPose& robotPose, + const CTSixAxisCalibResult& handEye); + + /// 六轴手眼转换 (Eye-to-Hand):相机坐标 -> 机器人坐标 + /// 相机固定在外部,观察机器人工作空间 + /// @param cameraPt 相机坐标系下的点 + /// @param cameraToBase 相机到机器人基座的标定矩阵 + /// @return 机器人基坐标系下的点 + CTVec3D sixAxisEyeToHandCameraToRobot(const CTVec3D& cameraPt, + const CTSixAxisCalibResult& cameraToBase); + + /// 六轴手眼转换 (Eye-to-Hand):相机坐标 -> 机器人坐标 (直接使用齐次矩阵) + /// @param cameraPt 相机坐标系下的点 + /// @param cameraToBaseMatrix 相机到机器人基座的4x4齐次变换矩阵 + /// @return 机器人基坐标系下的点 + CTVec3D sixAxisEyeToHandCameraToRobot(const CTVec3D& cameraPt, + const CTHomogeneousMatrix& cameraToBaseMatrix); + + /// 六轴手眼转换 (Eye-to-Hand):机器人坐标 -> 相机坐标 + /// @param robotPt 机器人基坐标系下的点 + /// @param cameraToBase 相机到机器人基座的标定矩阵 + /// @return 相机坐标系下的点 + CTVec3D sixAxisEyeToHandRobotToCamera(const CTVec3D& robotPt, + const CTSixAxisCalibResult& cameraToBase); + + /// 六轴手眼转换:计算抓取位姿 (Eye-in-Hand) + /// @param cameraPose 相机坐标系下的目标位姿 + /// @param robotPose 机器人当前位姿 + /// @param handEye 六轴手眼标定矩阵 + /// @param[out] targetPose 输出的目标抓取位姿 + void sixAxisEyeInHandCalcGraspPose(const CTCameraPose& cameraPose, + const CTRobotPose& robotPose, + const CTSixAxisCalibResult& handEye, + CTRobotPose& targetPose); + + /// 六轴手眼转换:计算抓取位姿 (Eye-to-Hand) + /// @param cameraPose 相机坐标系下的目标位姿 + /// @param cameraToBase 相机到机器人基座的标定矩阵 + /// @param[out] targetPose 输出的目标抓取位姿 + void sixAxisEyeToHandCalcGraspPose(const CTCameraPose& cameraPose, + const CTSixAxisCalibResult& cameraToBase, + CTRobotPose& targetPose); + + /// 六轴手眼转换:计算抓取位姿 (Eye-to-Hand) - 直接使用齐次矩阵 + /// @param cameraPose 相机坐标系下的目标位姿 + /// @param cameraToBaseMatrix 相机到机器人基座的4x4齐次变换矩阵 + /// @param eulerOrder 输出欧拉角的顺序 (默认XYZ,对应外旋ZYX即RZ-RY-RX) + /// @param[out] targetPose 输出的目标抓取位姿 + void sixAxisEyeToHandCalcGraspPose(const CTCameraPose& cameraPose, + const CTHomogeneousMatrix& cameraToBaseMatrix, + CTEulerOrder eulerOrder, + CTRobotPose& targetPose); + + // ==================== 辅助函数 ==================== + + /// 角度转弧度 + inline double degToRad(double deg) { return deg * M_PI / 180.0; } + + /// 弧度转角度 + inline double radToDeg(double rad) { return rad * 180.0 / M_PI; } + + /// 归一化角度到 [-π, π] + double normalizeAngle(double angle); + + /// 计算两个四元数之间的角度差 (弧度) + double quaternionAngleDiff(const CTQuaternionD& q1, const CTQuaternionD& q2); + + /// 四元数球面线性插值 (SLERP) + CTQuaternionD quaternionSlerp(const CTQuaternionD& q1, const CTQuaternionD& q2, double t); + +} // namespace CCoordinateTransform + +#endif // COORDINATE_TRANSFORM_H diff --git a/DataUtils/CoordinateTransform/Inc/CoordinateTypes.h b/DataUtils/CoordinateTransform/Inc/CoordinateTypes.h new file mode 100644 index 0000000..d78a740 --- /dev/null +++ b/DataUtils/CoordinateTransform/Inc/CoordinateTypes.h @@ -0,0 +1,362 @@ +#ifndef COORDINATE_TYPES_H +#define COORDINATE_TYPES_H + +// MSVC 需要在 cmath 之前定义 _USE_MATH_DEFINES 才能使用 M_PI +#define _USE_MATH_DEFINES +#include + +// 如果 M_PI 仍未定义,手动定义 +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +// 欧拉角顺序枚举 +// 内旋 (Intrinsic): 绕旋转后的轴旋转 +// 外旋 (Extrinsic): 绕固定轴旋转 +enum class CTEulerOrder +{ + // 内旋顺序 + XYZ = 0, // 内旋 XYZ: 先绕X轴(roll),再绕Y'轴(pitch),最后绕Z''轴(yaw) + ZYX = 1, // 内旋 ZYX: 先绕Z轴(yaw),再绕Y'轴(pitch),最后绕X''轴(roll) + ZXY = 2, + YXZ = 3, + YZX = 4, + XZY = 5, + + // 外旋顺序 (Extrinsic) - 绕固定轴旋转 + // 外旋 ABC = 内旋 CBA,但角度赋值不同 + sXYZ = 10, // 外旋 XYZ (RX-RY-RZ): 先绕固定X轴(rx),再绕固定Y轴(ry),最后绕固定Z轴(rz) + sZYX = 11, // 外旋 ZYX (RZ-RY-RX): 先绕固定Z轴(rz),再绕固定Y轴(ry),最后绕固定X轴(rx) + sZXY = 12, + sYXZ = 13, + sYZX = 14, + sXZY = 15 +}; + +// 三维向量 +struct CTVec3D +{ + double x = 0.0; + double y = 0.0; + double z = 0.0; + + CTVec3D() = default; + CTVec3D(double _x, double _y, double _z) : x(_x), y(_y), z(_z) {} + + // 向量运算 + CTVec3D operator+(const CTVec3D& other) const { return CTVec3D(x + other.x, y + other.y, z + other.z); } + CTVec3D operator-(const CTVec3D& other) const { return CTVec3D(x - other.x, y - other.y, z - other.z); } + CTVec3D operator*(double scalar) const { return CTVec3D(x * scalar, y * scalar, z * scalar); } + + double norm() const { return std::sqrt(x * x + y * y + z * z); } +}; + +// 欧拉角 (弧度制) +struct CTEulerAngles +{ + double roll = 0.0; // 绕X轴旋转 + double pitch = 0.0; // 绕Y轴旋转 + double yaw = 0.0; // 绕Z轴旋转 + CTEulerOrder order = CTEulerOrder::ZYX; // 默认ZYX顺序 + + CTEulerAngles() = default; + CTEulerAngles(double r, double p, double y, CTEulerOrder o = CTEulerOrder::ZYX) + : roll(r), pitch(p), yaw(y), order(o) {} + + // 从角度创建 + static CTEulerAngles fromDegrees(double roll_deg, double pitch_deg, double yaw_deg, + CTEulerOrder o = CTEulerOrder::ZYX) + { + const double deg2rad = M_PI / 180.0; + return CTEulerAngles(roll_deg * deg2rad, pitch_deg * deg2rad, yaw_deg * deg2rad, o); + } + + // 转换为角度 + void toDegrees(double& roll_deg, double& pitch_deg, double& yaw_deg) const + { + const double rad2deg = 180.0 / M_PI; + roll_deg = roll * rad2deg; + pitch_deg = pitch * rad2deg; + yaw_deg = yaw * rad2deg; + } +}; + +// 四元数 (w, x, y, z) +struct CTQuaternionD +{ + double w = 1.0; + double x = 0.0; + double y = 0.0; + double z = 0.0; + + CTQuaternionD() = default; + CTQuaternionD(double _w, double _x, double _y, double _z) : w(_w), x(_x), y(_y), z(_z) {} + + // 归一化 + CTQuaternionD normalized() const + { + double n = std::sqrt(w * w + x * x + y * y + z * z); + if (n < 1e-10) return CTQuaternionD(1.0, 0.0, 0.0, 0.0); + return CTQuaternionD(w / n, x / n, y / n, z / n); + } + + // 共轭四元数 + CTQuaternionD conjugate() const { return CTQuaternionD(w, -x, -y, -z); } + + // 四元数乘法 + CTQuaternionD operator*(const CTQuaternionD& q) const + { + return CTQuaternionD( + w * q.w - x * q.x - y * q.y - z * q.z, + w * q.x + x * q.w + y * q.z - z * q.y, + w * q.y - x * q.z + y * q.w + z * q.x, + w * q.z + x * q.y - y * q.x + z * q.w + ); + } + + // 模长 + double norm() const { return std::sqrt(w * w + x * x + y * y + z * z); } +}; + +// 3x3旋转矩阵 (行优先存储) +struct CTRotationMatrix +{ + double data[9] = {1, 0, 0, 0, 1, 0, 0, 0, 1}; // 单位矩阵 + + CTRotationMatrix() = default; + + // 元素访问 (row, col) + double& at(int row, int col) { return data[row * 3 + col]; } + double at(int row, int col) const { return data[row * 3 + col]; } + + // 矩阵乘法 + CTRotationMatrix operator*(const CTRotationMatrix& other) const + { + CTRotationMatrix result; + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + result.at(i, j) = 0; + for (int k = 0; k < 3; ++k) { + result.at(i, j) += at(i, k) * other.at(k, j); + } + } + } + return result; + } + + // 向量变换 + CTVec3D transform(const CTVec3D& v) const + { + return CTVec3D( + at(0, 0) * v.x + at(0, 1) * v.y + at(0, 2) * v.z, + at(1, 0) * v.x + at(1, 1) * v.y + at(1, 2) * v.z, + at(2, 0) * v.x + at(2, 1) * v.y + at(2, 2) * v.z + ); + } + + // 转置 + CTRotationMatrix transpose() const + { + CTRotationMatrix result; + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + result.at(i, j) = at(j, i); + } + } + return result; + } +}; + +// 4x4齐次变换矩阵 (行优先存储) +struct CTHomogeneousMatrix +{ + double data[16] = {1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1}; // 单位矩阵 + + CTHomogeneousMatrix() = default; + + // 从旋转矩阵和平移向量构造 + CTHomogeneousMatrix(const CTRotationMatrix& R, const CTVec3D& t) + { + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + at(i, j) = R.at(i, j); + } + } + at(0, 3) = t.x; + at(1, 3) = t.y; + at(2, 3) = t.z; + at(3, 0) = at(3, 1) = at(3, 2) = 0.0; + at(3, 3) = 1.0; + } + + // 元素访问 (row, col) + double& at(int row, int col) { return data[row * 4 + col]; } + double at(int row, int col) const { return data[row * 4 + col]; } + + // 获取旋转部分 + CTRotationMatrix getRotation() const + { + CTRotationMatrix R; + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + R.at(i, j) = at(i, j); + } + } + return R; + } + + // 获取平移部分 + CTVec3D getTranslation() const + { + return CTVec3D(at(0, 3), at(1, 3), at(2, 3)); + } + + // 设置旋转部分 + void setRotation(const CTRotationMatrix& R) + { + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + at(i, j) = R.at(i, j); + } + } + } + + // 设置平移部分 + void setTranslation(const CTVec3D& t) + { + at(0, 3) = t.x; + at(1, 3) = t.y; + at(2, 3) = t.z; + } + + // 矩阵乘法 + CTHomogeneousMatrix operator*(const CTHomogeneousMatrix& other) const + { + CTHomogeneousMatrix result; + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < 4; ++j) { + result.at(i, j) = 0; + for (int k = 0; k < 4; ++k) { + result.at(i, j) += at(i, k) * other.at(k, j); + } + } + } + return result; + } + + // 点变换 (齐次坐标) + CTVec3D transformPoint(const CTVec3D& p) const + { + return CTVec3D( + at(0, 0) * p.x + at(0, 1) * p.y + at(0, 2) * p.z + at(0, 3), + at(1, 0) * p.x + at(1, 1) * p.y + at(1, 2) * p.z + at(1, 3), + at(2, 0) * p.x + at(2, 1) * p.y + at(2, 2) * p.z + at(2, 3) + ); + } + + // 向量变换 (仅旋转,不平移) + CTVec3D transformVector(const CTVec3D& v) const + { + return CTVec3D( + at(0, 0) * v.x + at(0, 1) * v.y + at(0, 2) * v.z, + at(1, 0) * v.x + at(1, 1) * v.y + at(1, 2) * v.z, + at(2, 0) * v.x + at(2, 1) * v.y + at(2, 2) * v.z + ); + } + + // 求逆 + CTHomogeneousMatrix inverse() const + { + CTRotationMatrix R = getRotation(); + CTRotationMatrix R_inv = R.transpose(); + CTVec3D t = getTranslation(); + CTVec3D t_inv = R_inv.transform(t) * (-1.0); + return CTHomogeneousMatrix(R_inv, t_inv); + } +}; + +// 三轴手眼标定结果 (XYZ平移 + Rz旋转) +struct CTThreeAxisCalibResult +{ + double dx = 0.0; // X方向平移 + double dy = 0.0; // Y方向平移 + double dz = 0.0; // Z方向平移 + double rz = 0.0; // 绕Z轴旋转 (弧度) + + CTThreeAxisCalibResult() = default; + CTThreeAxisCalibResult(double _dx, double _dy, double _dz, double _rz) + : dx(_dx), dy(_dy), dz(_dz), rz(_rz) {} + + // 转换为齐次变换矩阵 + CTHomogeneousMatrix toHomogeneousMatrix() const; +}; + +// 六轴手眼标定结果 (完整位姿) +struct CTSixAxisCalibResult +{ + double dx = 0.0; // X方向平移 + double dy = 0.0; // Y方向平移 + double dz = 0.0; // Z方向平移 + double rx = 0.0; // 绕X轴旋转 (弧度) + double ry = 0.0; // 绕Y轴旋转 (弧度) + double rz = 0.0; // 绕Z轴旋转 (弧度) + + CTSixAxisCalibResult() = default; + CTSixAxisCalibResult(double _dx, double _dy, double _dz, double _rx, double _ry, double _rz) + : dx(_dx), dy(_dy), dz(_dz), rx(_rx), ry(_ry), rz(_rz) {} + + // 转换为齐次变换矩阵 + CTHomogeneousMatrix toHomogeneousMatrix() const; +}; + +// 机器人位姿 (工具坐标系相对于基坐标系) +struct CTRobotPose +{ + double x = 0.0; // X位置 (mm) + double y = 0.0; // Y位置 (mm) + double z = 0.0; // Z位置 (mm) + double rx = 0.0; // 绕X轴旋转 (弧度) + double ry = 0.0; // 绕Y轴旋转 (弧度) + double rz = 0.0; // 绕Z轴旋转 (弧度) + + CTRobotPose() = default; + CTRobotPose(double _x, double _y, double _z, double _rx, double _ry, double _rz) + : x(_x), y(_y), z(_z), rx(_rx), ry(_ry), rz(_rz) {} + + // 从角度创建 + static CTRobotPose fromDegrees(double x, double y, double z, + double rx_deg, double ry_deg, double rz_deg) + { + const double deg2rad = M_PI / 180.0; + return CTRobotPose(x, y, z, rx_deg * deg2rad, ry_deg * deg2rad, rz_deg * deg2rad); + } + + // 转换为齐次变换矩阵 + CTHomogeneousMatrix toHomogeneousMatrix() const; +}; + +// 相机位姿 (相机坐标系中的目标位姿) +struct CTCameraPose +{ + double x = 0.0; // X位置 (mm) + double y = 0.0; // Y位置 (mm) + double z = 0.0; // Z位置 (mm) + double rx = 0.0; // 绕X轴旋转 (弧度) + double ry = 0.0; // 绕Y轴旋转 (弧度) + double rz = 0.0; // 绕Z轴旋转 (弧度) + + CTCameraPose() = default; + CTCameraPose(double _x, double _y, double _z, double _rx, double _ry, double _rz) + : x(_x), y(_y), z(_z), rx(_rx), ry(_ry), rz(_rz) {} + + // 转换为齐次变换矩阵 (默认使用ZYX内旋) + CTHomogeneousMatrix toHomogeneousMatrix() const; + + // 转换为齐次变换矩阵 (指定欧拉角顺序) + CTHomogeneousMatrix toHomogeneousMatrix(CTEulerOrder order) const; +}; + +#endif // COORDINATE_TYPES_H diff --git a/DataUtils/CoordinateTransform/Src/CoordinateTransform.cpp b/DataUtils/CoordinateTransform/Src/CoordinateTransform.cpp new file mode 100644 index 0000000..9372053 --- /dev/null +++ b/DataUtils/CoordinateTransform/Src/CoordinateTransform.cpp @@ -0,0 +1,616 @@ +#include "CoordinateTransform.h" +#include +#include +#include + +namespace CCoordinateTransform +{ + +// ==================== 内部辅助函数 ==================== + +// 将自定义 CTRotationMatrix 转为 Eigen Matrix3d +static Eigen::Matrix3d toEigenMatrix(const CTRotationMatrix& R) +{ + Eigen::Matrix3d mat; + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + mat(i, j) = R.at(i, j); + } + } + return mat; +} + +// 将 Eigen Matrix3d 转为自定义 CTRotationMatrix +static CTRotationMatrix fromEigenMatrix(const Eigen::Matrix3d& mat) +{ + CTRotationMatrix R; + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + R.at(i, j) = mat(i, j); + } + } + return R; +} + +// 将自定义 CTQuaternionD 转为 Eigen Quaterniond +static Eigen::Quaterniond toEigenQuaternion(const CTQuaternionD& q) +{ + return Eigen::Quaterniond(q.w, q.x, q.y, q.z); +} + +// 将 Eigen Quaterniond 转为自定义 CTQuaternionD +static CTQuaternionD fromEigenQuaternion(const Eigen::Quaterniond& eq) +{ + return CTQuaternionD(eq.w(), eq.x(), eq.y(), eq.z()); +} + +// 将自定义 CTVec3D 转为 Eigen Vector3d +static Eigen::Vector3d toEigenVector(const CTVec3D& v) +{ + return Eigen::Vector3d(v.x, v.y, v.z); +} + +// 将 Eigen Vector3d 转为自定义 CTVec3D +static CTVec3D fromEigenVector(const Eigen::Vector3d& ev) +{ + return CTVec3D(ev.x(), ev.y(), ev.z()); +} + +// 将自定义 CTHomogeneousMatrix 转为 Eigen Matrix4d +static Eigen::Matrix4d toEigenMatrix4(const CTHomogeneousMatrix& H) +{ + Eigen::Matrix4d mat; + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < 4; ++j) { + mat(i, j) = H.at(i, j); + } + } + return mat; +} + +// 将 Eigen Matrix4d 转为自定义 CTHomogeneousMatrix +static CTHomogeneousMatrix fromEigenMatrix4(const Eigen::Matrix4d& mat) +{ + CTHomogeneousMatrix H; + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < 4; ++j) { + H.at(i, j) = mat(i, j); + } + } + return H; +} + +// ==================== 欧拉角与四元数互转 ==================== + +CTQuaternionD eulerToQuaternion(const CTEulerAngles& euler) +{ + // 使用 Eigen 的 AngleAxis 构建旋转 + Eigen::AngleAxisd rollAngle(euler.roll, Eigen::Vector3d::UnitX()); + Eigen::AngleAxisd pitchAngle(euler.pitch, Eigen::Vector3d::UnitY()); + Eigen::AngleAxisd yawAngle(euler.yaw, Eigen::Vector3d::UnitZ()); + + Eigen::Quaterniond q; + switch (euler.order) + { + // ==================== 内旋顺序 ==================== + case CTEulerOrder::XYZ: + // XYZ内旋: 先绕X轴(roll),再绕Y'轴(pitch),最后绕Z''轴(yaw) + // R = Rx * Ry * Rz, 四元数: q = qx * qy * qz + q = rollAngle * pitchAngle * yawAngle; + break; + case CTEulerOrder::ZYX: + // ZYX内旋: 先绕Z轴(yaw),再绕Y'轴(pitch),最后绕X''轴(roll) + // R = Rz * Ry * Rx, 四元数: q = qz * qy * qx + q = yawAngle * pitchAngle * rollAngle; + break; + case CTEulerOrder::ZXY: + // ZXY内旋: 先绕Z轴(yaw),再绕X'轴(roll),最后绕Y''轴(pitch) + // R = Rz * Rx * Ry, 四元数: q = qz * qx * qy + q = yawAngle * rollAngle * pitchAngle; + break; + case CTEulerOrder::YXZ: + // YXZ内旋: 先绕Y轴(pitch),再绕X'轴(roll),最后绕Z''轴(yaw) + // R = Ry * Rx * Rz, 四元数: q = qy * qx * qz + q = pitchAngle * rollAngle * yawAngle; + break; + case CTEulerOrder::YZX: + // YZX内旋: 先绕Y轴(pitch),再绕Z'轴(yaw),最后绕X''轴(roll) + // R = Ry * Rz * Rx, 四元数: q = qy * qz * qx + q = pitchAngle * yawAngle * rollAngle; + break; + case CTEulerOrder::XZY: + // XZY内旋: 先绕X轴(roll),再绕Z'轴(yaw),最后绕Y''轴(pitch) + // R = Rx * Rz * Ry, 四元数: q = qx * qz * qy + q = rollAngle * yawAngle * pitchAngle; + break; + + // ==================== 外旋顺序 ==================== + // 外旋 ABC: 先绕固定A轴,再绕固定B轴,最后绕固定C轴 + // 外旋 ABC 的旋转矩阵 = 内旋 CBA 的旋转矩阵 + // R = Rc * Rb * Ra + case CTEulerOrder::sZYX: + // 外旋 ZYX (RZ-RY-RX): 先绕固定Z轴(rz),再绕固定Y轴(ry),最后绕固定X轴(rx) + // R = Rx * Ry * Rz, 四元数: q = qx * qy * qz + // 注意:这里 roll=rx, pitch=ry, yaw=rz + q = rollAngle * pitchAngle * yawAngle; + break; + case CTEulerOrder::sXYZ: + // 外旋 XYZ (RX-RY-RZ): 先绕固定X轴(rx),再绕固定Y轴(ry),最后绕固定Z轴(rz) + // R = Rz * Ry * Rx, 四元数: q = qz * qy * qx + q = yawAngle * pitchAngle * rollAngle; + break; + case CTEulerOrder::sZXY: + // 外旋 ZXY (RZ-RX-RY) + // R = Ry * Rx * Rz, 四元数: q = qy * qx * qz + q = pitchAngle * rollAngle * yawAngle; + break; + case CTEulerOrder::sYXZ: + // 外旋 YXZ (RY-RX-RZ) + // R = Rz * Rx * Ry, 四元数: q = qz * qx * qy + q = yawAngle * rollAngle * pitchAngle; + break; + case CTEulerOrder::sYZX: + // 外旋 YZX (RY-RZ-RX) + // R = Rx * Rz * Ry, 四元数: q = qx * qz * qy + q = rollAngle * yawAngle * pitchAngle; + break; + case CTEulerOrder::sXZY: + // 外旋 XZY (RX-RZ-RY) + // R = Ry * Rz * Rx, 四元数: q = qy * qz * qx + q = pitchAngle * yawAngle * rollAngle; + break; + + default: + // 默认 ZYX内旋 + q = yawAngle * pitchAngle * rollAngle; + break; + } + + return fromEigenQuaternion(q.normalized()); +} + +CTEulerAngles quaternionToEuler(const CTQuaternionD& q, CTEulerOrder order) +{ + Eigen::Quaterniond eq = toEigenQuaternion(q).normalized(); + Eigen::Matrix3d R = eq.toRotationMatrix(); + + CTEulerAngles euler; + euler.order = order; + + // 根据不同顺序提取欧拉角 + Eigen::Vector3d angles; + + switch (order) + { + // ==================== 内旋顺序 ==================== + case CTEulerOrder::ZYX: + // ZYX内旋: R = Rz * Ry * Rx + angles = R.eulerAngles(2, 1, 0); // Eigen 返回 (z, y, x) + euler.yaw = angles(0); + euler.pitch = angles(1); + euler.roll = angles(2); + break; + + case CTEulerOrder::XYZ: + // XYZ内旋: R = Rx * Ry * Rz + angles = R.eulerAngles(0, 1, 2); // Eigen 返回 (x, y, z) + euler.roll = angles(0); + euler.pitch = angles(1); + euler.yaw = angles(2); + break; + + case CTEulerOrder::ZXY: + angles = R.eulerAngles(2, 0, 1); + euler.yaw = angles(0); + euler.roll = angles(1); + euler.pitch = angles(2); + break; + + case CTEulerOrder::YXZ: + angles = R.eulerAngles(1, 0, 2); + euler.pitch = angles(0); + euler.roll = angles(1); + euler.yaw = angles(2); + break; + + case CTEulerOrder::YZX: + angles = R.eulerAngles(1, 2, 0); + euler.pitch = angles(0); + euler.yaw = angles(1); + euler.roll = angles(2); + break; + + case CTEulerOrder::XZY: + angles = R.eulerAngles(0, 2, 1); + euler.roll = angles(0); + euler.yaw = angles(1); + euler.pitch = angles(2); + break; + + // ==================== 外旋顺序 ==================== + // 外旋 ABC 的旋转矩阵 = 内旋 CBA 的旋转矩阵 + // 所以提取欧拉角时使用相同的矩阵分解,但角度赋值不同 + case CTEulerOrder::sZYX: + // 外旋 ZYX (RZ-RY-RX): R = Rx * Ry * Rz + // 使用 XYZ 分解 + angles = R.eulerAngles(0, 1, 2); // Eigen 返回 (x, y, z) + euler.roll = angles(0); // rx + euler.pitch = angles(1); // ry + euler.yaw = angles(2); // rz + break; + + case CTEulerOrder::sXYZ: + // 外旋 XYZ (RX-RY-RZ): R = Rz * Ry * Rx + // 使用 ZYX 分解 + angles = R.eulerAngles(2, 1, 0); // Eigen 返回 (z, y, x) + euler.yaw = angles(0); // rz + euler.pitch = angles(1); // ry + euler.roll = angles(2); // rx + break; + + case CTEulerOrder::sZXY: + // 外旋 ZXY (RZ-RX-RY): R = Ry * Rx * Rz + angles = R.eulerAngles(1, 0, 2); + euler.pitch = angles(0); // ry + euler.roll = angles(1); // rx + euler.yaw = angles(2); // rz + break; + + case CTEulerOrder::sYXZ: + // 外旋 YXZ (RY-RX-RZ): R = Rz * Rx * Ry + angles = R.eulerAngles(2, 0, 1); + euler.yaw = angles(0); // rz + euler.roll = angles(1); // rx + euler.pitch = angles(2); // ry + break; + + case CTEulerOrder::sYZX: + // 外旋 YZX (RY-RZ-RX): R = Rx * Rz * Ry + angles = R.eulerAngles(0, 2, 1); + euler.roll = angles(0); // rx + euler.yaw = angles(1); // rz + euler.pitch = angles(2); // ry + break; + + case CTEulerOrder::sXZY: + // 外旋 XZY (RX-RZ-RY): R = Ry * Rz * Rx + angles = R.eulerAngles(1, 2, 0); + euler.pitch = angles(0); // ry + euler.yaw = angles(1); // rz + euler.roll = angles(2); // rx + break; + + default: + angles = R.eulerAngles(2, 1, 0); + euler.yaw = angles(0); + euler.pitch = angles(1); + euler.roll = angles(2); + break; + } + + return euler; +} + +// ==================== 旋转矩阵与欧拉角互转 ==================== + +CTRotationMatrix eulerToRotationMatrix(const CTEulerAngles& euler) +{ + // 先转四元数,再转旋转矩阵 + CTQuaternionD q = eulerToQuaternion(euler); + return quaternionToRotationMatrix(q); +} + +CTEulerAngles rotationMatrixToEuler(const CTRotationMatrix& R, CTEulerOrder order) +{ + // 先转四元数,再提取欧拉角 + CTQuaternionD q = rotationMatrixToQuaternion(R); + return quaternionToEuler(q, order); +} + +// ==================== 旋转矩阵与四元数互转 ==================== + +CTRotationMatrix quaternionToRotationMatrix(const CTQuaternionD& q) +{ + Eigen::Quaterniond eq = toEigenQuaternion(q).normalized(); + return fromEigenMatrix(eq.toRotationMatrix()); +} + +CTQuaternionD rotationMatrixToQuaternion(const CTRotationMatrix& R) +{ + Eigen::Matrix3d mat = toEigenMatrix(R); + Eigen::Quaterniond eq(mat); + return fromEigenQuaternion(eq.normalized()); +} + +} // namespace CCoordinateTransform + +// ==================== 结构体成员函数实现 (命名空间外) ==================== + +CTHomogeneousMatrix CTThreeAxisCalibResult::toHomogeneousMatrix() const +{ + // 三轴只有Z轴旋转 + CTEulerAngles euler(0.0, 0.0, rz, CTEulerOrder::ZYX); + CTRotationMatrix R = CCoordinateTransform::eulerToRotationMatrix(euler); + CTVec3D t(dx, dy, dz); + return CTHomogeneousMatrix(R, t); +} + +CTHomogeneousMatrix CTSixAxisCalibResult::toHomogeneousMatrix() const +{ + CTEulerAngles euler(rx, ry, rz, CTEulerOrder::ZYX); + CTRotationMatrix R = CCoordinateTransform::eulerToRotationMatrix(euler); + CTVec3D t(dx, dy, dz); + return CTHomogeneousMatrix(R, t); +} + +CTHomogeneousMatrix CTRobotPose::toHomogeneousMatrix() const +{ + CTEulerAngles euler(rx, ry, rz, CTEulerOrder::ZYX); + CTRotationMatrix R = CCoordinateTransform::eulerToRotationMatrix(euler); + CTVec3D t(x, y, z); + return CTHomogeneousMatrix(R, t); +} + +CTHomogeneousMatrix CTCameraPose::toHomogeneousMatrix() const +{ + CTEulerAngles euler(rx, ry, rz, CTEulerOrder::ZYX); + CTRotationMatrix R = CCoordinateTransform::eulerToRotationMatrix(euler); + CTVec3D t(x, y, z); + return CTHomogeneousMatrix(R, t); +} + +CTHomogeneousMatrix CTCameraPose::toHomogeneousMatrix(CTEulerOrder order) const +{ + CTEulerAngles euler(rx, ry, rz, order); + CTRotationMatrix R = CCoordinateTransform::eulerToRotationMatrix(euler); + CTVec3D t(x, y, z); + return CTHomogeneousMatrix(R, t); +} + +// ==================== 继续命名空间内的函数 ==================== + +namespace CCoordinateTransform +{ + +// ==================== 三轴手眼转换 ==================== + +CTVec3D threeAxisCameraToRobot(const CTVec3D& cameraPt, + const CTRobotPose& robotPose, + const CTThreeAxisCalibResult& handEye) +{ + // 三轴龙门架模型: + // P_robot = T_base_to_tool * T_tool_to_camera * P_camera + // 其中 T_tool_to_camera 就是手眼标定矩阵 + + // 手眼变换矩阵 + CTHomogeneousMatrix T_hand_eye = handEye.toHomogeneousMatrix(); + + // 机器人当前位姿变换矩阵 (只使用XYZ和Rz) + CTRobotPose poseRz(robotPose.x, robotPose.y, robotPose.z, 0.0, 0.0, robotPose.rz); + CTHomogeneousMatrix T_robot = poseRz.toHomogeneousMatrix(); + + // 完整变换: P_robot = T_robot * T_hand_eye * P_camera + CTHomogeneousMatrix T_total = T_robot * T_hand_eye; + return T_total.transformPoint(cameraPt); +} + +CTVec3D threeAxisRobotToCamera(const CTVec3D& robotPt, + const CTRobotPose& robotPose, + const CTThreeAxisCalibResult& handEye) +{ + // 逆变换 + CTHomogeneousMatrix T_hand_eye = handEye.toHomogeneousMatrix(); + CTRobotPose poseRz(robotPose.x, robotPose.y, robotPose.z, 0.0, 0.0, robotPose.rz); + CTHomogeneousMatrix T_robot = poseRz.toHomogeneousMatrix(); + + CTHomogeneousMatrix T_total = T_robot * T_hand_eye; + CTHomogeneousMatrix T_inv = T_total.inverse(); + + return T_inv.transformPoint(robotPt); +} + +void threeAxisCalcGraspPose(const CTVec3D& cameraPt, + double cameraAngle, + const CTRobotPose& robotPose, + const CTThreeAxisCalibResult& handEye, + CTRobotPose& targetPose) +{ + // 将相机坐标转换到机器人坐标 + CTVec3D robotPt = threeAxisCameraToRobot(cameraPt, robotPose, handEye); + + // 计算目标角度 + double targetAngle = normalizeAngle(robotPose.rz + handEye.rz + cameraAngle); + + targetPose.x = robotPt.x; + targetPose.y = robotPt.y; + targetPose.z = robotPt.z; + targetPose.rx = 0.0; + targetPose.ry = 0.0; + targetPose.rz = targetAngle; +} + +// ==================== 六轴手眼转换 ==================== + +CTVec3D sixAxisEyeInHandCameraToRobot(const CTVec3D& cameraPt, + const CTRobotPose& robotPose, + const CTSixAxisCalibResult& handEye) +{ + // Eye-in-Hand 模型: + // P_base = T_base_to_end * T_end_to_cam * P_cam + // T_end_to_cam 就是手眼标定矩阵 + + CTHomogeneousMatrix T_hand_eye = handEye.toHomogeneousMatrix(); + CTHomogeneousMatrix T_robot = robotPose.toHomogeneousMatrix(); + + CTHomogeneousMatrix T_total = T_robot * T_hand_eye; + return T_total.transformPoint(cameraPt); +} + +CTVec3D sixAxisEyeInHandRobotToCamera(const CTVec3D& robotPt, + const CTRobotPose& robotPose, + const CTSixAxisCalibResult& handEye) +{ + CTHomogeneousMatrix T_hand_eye = handEye.toHomogeneousMatrix(); + CTHomogeneousMatrix T_robot = robotPose.toHomogeneousMatrix(); + + CTHomogeneousMatrix T_total = T_robot * T_hand_eye; + CTHomogeneousMatrix T_inv = T_total.inverse(); + + return T_inv.transformPoint(robotPt); +} + +CTVec3D sixAxisEyeToHandCameraToRobot(const CTVec3D& cameraPt, + const CTSixAxisCalibResult& cameraToBase) +{ + // Eye-to-Hand 模型: + // P_base = T_base_to_cam * P_cam + // 其中 T_base_to_cam = T_cam_to_base 的逆 + + CTHomogeneousMatrix T_cam_to_base = cameraToBase.toHomogeneousMatrix(); + return T_cam_to_base.transformPoint(cameraPt); +} + +CTVec3D sixAxisEyeToHandCameraToRobot(const CTVec3D& cameraPt, + const CTHomogeneousMatrix& cameraToBaseMatrix) +{ + // Eye-to-Hand 模型: P_robot = T_cam_to_base * P_cam + // 直接使用传入的齐次矩阵 + return cameraToBaseMatrix.transformPoint(cameraPt); +} + +CTVec3D sixAxisEyeToHandRobotToCamera(const CTVec3D& robotPt, + const CTSixAxisCalibResult& cameraToBase) +{ + CTHomogeneousMatrix T_cam_to_base = cameraToBase.toHomogeneousMatrix(); + CTHomogeneousMatrix T_inv = T_cam_to_base.inverse(); + + return T_inv.transformPoint(robotPt); +} + +void sixAxisEyeInHandCalcGraspPose(const CTCameraPose& cameraPose, + const CTRobotPose& robotPose, + const CTSixAxisCalibResult& handEye, + CTRobotPose& targetPose) +{ + // Eye-in-Hand 六轴转换 + // T_total = T_robot * T_hand_eye (从相机坐标系到机器人基座的总变换) + CTHomogeneousMatrix T_hand_eye = handEye.toHomogeneousMatrix(); + CTHomogeneousMatrix T_robot = robotPose.toHomogeneousMatrix(); + CTHomogeneousMatrix T_total = T_robot * T_hand_eye; + + // 1. 位置转换: P_robot = T_total * P_cam + CTVec3D cameraPt(cameraPose.x, cameraPose.y, cameraPose.z); + CTVec3D robotPt = T_total.transformPoint(cameraPt); + targetPose.x = robotPt.x; + targetPose.y = robotPt.y; + targetPose.z = robotPt.z; + + // 2. 姿态转换: R_robot = R_total * R_cam + CTEulerAngles camEuler(cameraPose.rx, cameraPose.ry, cameraPose.rz, CTEulerOrder::ZYX); + CTRotationMatrix R_cam = eulerToRotationMatrix(camEuler); + CTRotationMatrix R_total = T_total.getRotation(); + CTRotationMatrix R_robot = R_total * R_cam; + + // 从旋转矩阵提取欧拉角 + CTEulerAngles robotEuler = rotationMatrixToEuler(R_robot, CTEulerOrder::ZYX); + targetPose.rx = robotEuler.roll; + targetPose.ry = robotEuler.pitch; + targetPose.rz = robotEuler.yaw; +} + +void sixAxisEyeToHandCalcGraspPose(const CTCameraPose& cameraPose, + const CTSixAxisCalibResult& cameraToBase, + CTRobotPose& targetPose) +{ + // Eye-to-Hand 六轴转换 + CTHomogeneousMatrix T_cam_to_base = cameraToBase.toHomogeneousMatrix(); + + // 1. 位置转换: P_robot = T_cam_to_base * P_cam + CTVec3D cameraPt(cameraPose.x, cameraPose.y, cameraPose.z); + CTVec3D robotPt = T_cam_to_base.transformPoint(cameraPt); + targetPose.x = robotPt.x; + targetPose.y = robotPt.y; + targetPose.z = robotPt.z; + + // 2. 姿态转换: R_robot = R_cam_to_base * R_cam + // 默认使用 ZYX 内旋顺序 + CTEulerAngles camEuler(cameraPose.rx, cameraPose.ry, cameraPose.rz, CTEulerOrder::ZYX); + CTRotationMatrix R_cam = eulerToRotationMatrix(camEuler); + CTRotationMatrix R_cam_to_base = T_cam_to_base.getRotation(); + CTRotationMatrix R_robot = R_cam_to_base * R_cam; + + // 从旋转矩阵提取欧拉角 + CTEulerAngles robotEuler = rotationMatrixToEuler(R_robot, CTEulerOrder::ZYX); + targetPose.rx = robotEuler.roll; + targetPose.ry = robotEuler.pitch; + targetPose.rz = robotEuler.yaw; +} + +void sixAxisEyeToHandCalcGraspPose(const CTCameraPose& cameraPose, + const CTHomogeneousMatrix& cameraToBaseMatrix, + CTEulerOrder eulerOrder, + CTRobotPose& targetPose) +{ + // Eye-to-Hand 六轴转换 + // 1. 位置转换: P_robot = T_cam_to_base * P_cam + CTVec3D cameraPt(cameraPose.x, cameraPose.y, cameraPose.z); + CTVec3D robotPt = cameraToBaseMatrix.transformPoint(cameraPt); + targetPose.x = robotPt.x; + targetPose.y = robotPt.y; + targetPose.z = robotPt.z; + + // 2. 姿态转换: R_robot = R_cam_to_base * R_cam + // 先将相机位姿的欧拉角转换为旋转矩阵 + CTEulerAngles camEuler(cameraPose.rx, cameraPose.ry, cameraPose.rz, eulerOrder); + CTRotationMatrix R_cam = eulerToRotationMatrix(camEuler); + + // 获取标定矩阵的旋转部分 + CTRotationMatrix R_cam_to_base = cameraToBaseMatrix.getRotation(); + + // 计算机器人坐标系下的姿态 + CTRotationMatrix R_robot = R_cam_to_base * R_cam; + + // 从旋转矩阵提取欧拉角 + CTEulerAngles robotEuler = rotationMatrixToEuler(R_robot, eulerOrder); + targetPose.rx = robotEuler.roll; + targetPose.ry = robotEuler.pitch; + targetPose.rz = robotEuler.yaw; +} + +// ==================== 辅助函数 ==================== + +double normalizeAngle(double angle) +{ + while (angle > M_PI) + angle -= 2.0 * M_PI; + while (angle < -M_PI) + angle += 2.0 * M_PI; + return angle; +} + +double quaternionAngleDiff(const CTQuaternionD& q1, const CTQuaternionD& q2) +{ + Eigen::Quaterniond eq1 = toEigenQuaternion(q1).normalized(); + Eigen::Quaterniond eq2 = toEigenQuaternion(q2).normalized(); + + // 两个四元数的夹角 + double dot = eq1.dot(eq2); + // 处理符号问题 (q 和 -q 表示相同旋转) + if (dot < 0.0) { + dot = -dot; + } + if (dot > 1.0) dot = 1.0; + + return 2.0 * std::acos(dot); +} + +CTQuaternionD quaternionSlerp(const CTQuaternionD& q1, const CTQuaternionD& q2, double t) +{ + Eigen::Quaterniond eq1 = toEigenQuaternion(q1).normalized(); + Eigen::Quaterniond eq2 = toEigenQuaternion(q2).normalized(); + + Eigen::Quaterniond result = eq1.slerp(t, eq2); + return fromEigenQuaternion(result); +} + +} // namespace CCoordinateTransform diff --git a/DataUtils/DataUtils.pro b/DataUtils/DataUtils.pro new file mode 100644 index 0000000..7ada19d --- /dev/null +++ b/DataUtils/DataUtils.pro @@ -0,0 +1,8 @@ +TEMPLATE = subdirs + +SUBDIRS += \ + CloudMathClac \ + CoordinateTransform + +CloudMathClac.file = CloudMathClac/CloudMathClac.pro +CoordinateTransform.file = CoordinateTransform/CoordinateTransform.pro diff --git a/MIGRATION.md b/MIGRATION.md new file mode 100644 index 0000000..f0cda3d --- /dev/null +++ b/MIGRATION.md @@ -0,0 +1,197 @@ +# Utils 模块重构总结 + +## 修改概述 + +已成功将 Utils 目录设置为独立的 git 仓库,并更新了主项目中所有对 VrCommon、VrUtils 和 CloudUtils 的引用。 + +## 主要修改内容 + +### 1. Utils 目录结构 + +创建了独立的 Utils 模块,包含以下子模块: + +``` +Utils/ +├── VrCommon/ # 核心接口和数据结构 +├── VrUtils/ # 工具类库(JSON、log4cpp、tinyxml2、INI、MD5、CRC) +├── CloudUtils/ # 点云工具 +├── DataUtils/ # 数据处理工具 +│ ├── CloudMathClac/ # 点云数学计算 +│ └── CoordinateTransform/ # 坐标转换 +└── CloudView/ # 点云查看工具 +``` + +### 2. 创建的新文件 + +- `Utils/Utils.pro` - Utils 模块主项目文件 +- `Utils/README.md` - Utils 模块说明文档 +- `Utils/.gitignore` - Git 忽略文件配置 + +### 3. 修改的 .pro 文件 + +#### 主项目 +- `GrabBagPrj/GrabBagPrj.pro` - 将 VrCommon 和 VrUtils 的引用改为 Utils 模块 + +#### Utils 内部模块 +- `Utils/VrCommon/VrCommon.pro` - 添加 VrConfigCmd.h 头文件 +- `Utils/VrUtils/VrUtils.pro` - 保持不变 +- `Utils/CloudUtils/CloudUtils.pro` - 更新 INCLUDEPATH 为相对路径 +- `Utils/CloudView/CloudView.pro` - 更新 INCLUDEPATH 和 LIBS 路径 +- `Utils/DataUtils/CloudMathClac/CloudMathClac.pro` - 更新 SDK 路径 +- `Utils/DataUtils/CoordinateTransform/CoordinateTransform.pro` - 更新 SDK 路径 + +#### VrNets 模块 +- `VrNets/VrTcpClient.pro` - 更新 INCLUDEPATH 和 LIBS 路径 +- `VrNets/VrTcpServer.pro` - 更新 INCLUDEPATH 和 LIBS 路径 + +#### Module 模块 +- `Module/ShareMem/ShareMem.pro` - 更新 INCLUDEPATH +- `Module/ThreadPool/ThreadPool.pro` - 更新 INCLUDEPATH(修正错误路径) +- `Module/ModbusTCPServer/ModbusTCPServer.pro` - 更新 INCLUDEPATH 和 LIBS +- `Module/BinocularMarkReceiver/BinocularMarkReceiver.pro` - 更新 INCLUDEPATH 和 LIBS +- `Module/HandEyeCalib/HandEyeCalib.pro` - 更新 INCLUDEPATH +- `Module/ChessboardDetector/ChessboardDetector.pro` - 更新 INCLUDEPATH +- `Module/AuthModule/AuthModule.pro` - 更新 INCLUDEPATH + +#### Device 模块 +- `Device/VrEyeDevice/VrEyeDevice.pro` - 更新 INCLUDEPATH 和 LIBS +- `Device/HikDevice/HikDevice.pro` - 更新 INCLUDEPATH 和 LIBS +- `Device/GalaxyDevice/GalaxyDevice.pro` - 更新 INCLUDEPATH 和 LIBS +- `Device/GlLineLaserDevice/GlLineLaserDevice.pro` - 更新 INCLUDEPATH 和 LIBS +- `Device/EpicEyeDevice/EpicEyeDevice.pro` - 更新 INCLUDEPATH + +#### AppUtils 模块 +- `AppUtils/AppCommon/AppCommon.pro` - 更新 INCLUDEPATH 和 LIBS +- `AppUtils/UICommon/UICommon.pro` - 更新 INCLUDEPATH + +#### Tools 模块 +- `Tools/AuthRegister/AuthRegister.pro` - 更新 LIBS +- `Tools/ConfigDecryptor/ConfigDecryptor.pro` - 更新 INCLUDEPATH 和 LIBS +- `Tools/CalibView/CalibView.pro` - 更新 INCLUDEPATH 和 LIBS +- `Tools/VrEyeView/VrEyeView.pro` - 更新 INCLUDEPATH 和 LIBS + +#### App 模块(批量更新) +批量更新了 App 目录下所有应用程序的 .pro 文件(约 27 个文件),包括: +- GrabBag +- BeltTearing +- LapWeld +- Workpiece +- WorkpieceProject +- BinocularMark +- BagThreadPosition +- ParticleSize +- ScrewPosition +- TunnelChannel +- WheelMeasure +- WorkpieceHole + +#### Test 模块 +- `Test/tcpclient/tcpclient_test.pro` - 更新路径 +- `Test/tcpserver/tcpserver_test.pro` - 更新路径 + +### 4. 路径替换规则 + +所有 .pro 文件中的路径按以下规则进行了替换: + +``` +旧路径 新路径 +../../VrCommon/Inc -> ../../Utils/VrCommon/Inc +../../VrUtils/Inc -> ../../Utils/VrUtils/Inc +../../VrCommon/release -> ../../Utils/VrCommon/release +../../VrCommon/debug -> ../../Utils/VrCommon/debug +../../VrUtils/release -> ../../Utils/VrUtils/release +../../VrUtils/debug -> ../../Utils/VrUtils/debug +../../AppUtils/CloudUtils -> ../../Utils/CloudUtils + +../../../VrCommon/Inc -> ../../../Utils/VrCommon/Inc +../../../VrUtils/Inc -> ../../../Utils/VrUtils/Inc +../../../VrCommon/release -> ../../../Utils/VrCommon/release +../../../VrCommon/debug -> ../../../Utils/VrCommon/debug +../../../VrUtils/release -> ../../../Utils/VrUtils/release +../../../VrUtils/debug -> ../../../Utils/VrUtils/debug +../../../AppUtils/CloudUtils -> ../../../Utils/CloudUtils +``` + +### 5. Git 仓库初始化 + +已在 Utils 目录下初始化 git 仓库: +```bash +cd Utils +git init +git add . +``` + +所有文件已添加到暂存区,等待提交。 + +## 依赖关系 + +更新后的依赖关系: + +``` +主项目 (GrabBagPrj) + └── Utils + ├── VrCommon + ├── VrUtils (依赖 VrCommon) + ├── CloudUtils (依赖 VrCommon, VrUtils) + ├── DataUtils (依赖 VrCommon) + └── CloudView (依赖 VrCommon, VrUtils, CloudUtils) + └── VrNets (依赖 Utils) + └── Module (依赖 Utils) + └── Device (依赖 Utils) + └── AppUtils (依赖 Utils) + └── App (依赖 Utils, VrNets, Module, Device, AppUtils) + └── Tools (依赖 Utils, Module, Robot) +``` + +## 下一步操作 + +1. 在 Utils 目录下提交初始版本: + ```bash + cd Utils + git commit -m "初始提交:Utils 工具库模块" + ``` + +2. 如果需要推送到远程仓库: + ```bash + git remote add origin <远程仓库地址> + git push -u origin master + ``` + +3. 在主项目中,可以将 Utils 作为 git submodule 引用: + ```bash + cd .. + git submodule add Utils + ``` + +## 验证构建 + +建议执行以下步骤验证构建: + +1. 清理旧的构建产物: + ```bash + cd GrabBagPrj + make clean + ``` + +2. 重新生成 Makefile: + ```bash + qmake GrabBagPrj.pro + ``` + +3. 编译项目: + ```bash + make + ``` + +## 注意事项 + +1. 所有路径更新已完成,但建议在实际编译前进行测试 +2. Utils 模块现在是独立的,可以被其他项目引用 +3. 如果需要修改 Utils 模块,应该在 Utils 目录下进行 git 操作 +4. 主项目的 .gitignore 应该排除 Utils 目录(如果使用 submodule) + +## 文件统计 + +- 修改的 .pro 文件:约 60+ 个 +- 新建的文件:3 个(Utils.pro, README.md, .gitignore) +- Utils 模块包含的文件:约 230+ 个 diff --git a/QUICKSTART.md b/QUICKSTART.md new file mode 100644 index 0000000..1b24496 --- /dev/null +++ b/QUICKSTART.md @@ -0,0 +1,201 @@ +# Utils 模块快速参考指南 + +## 快速开始 + +### 1. 提交 Utils 仓库 + +```bash +cd Utils +git commit -m "初始提交:Utils 工具库模块 + +包含以下子模块: +- VrCommon: 核心接口和数据结构 +- VrUtils: 工具类库(JSON、log4cpp、tinyxml2、INI、MD5、CRC) +- CloudUtils: 点云工具 +- DataUtils: 数据处理工具(CloudMathClac、CoordinateTransform) +- CloudView: 点云查看工具 +" +``` + +### 2. 添加远程仓库(可选) + +```bash +# 添加远程仓库 +git remote add origin <你的远程仓库地址> + +# 推送到远程 +git push -u origin master +``` + +### 3. 在主项目中使用 submodule(可选) + +如果你想将 Utils 作为 git submodule 使用: + +```bash +# 在主项目根目录 +cd .. + +# 删除当前的 Utils 目录(确保已备份) +# rm -rf Utils + +# 添加为 submodule +git submodule add Utils + +# 提交 submodule 配置 +git add .gitmodules Utils +git commit -m "将 Utils 添加为 git submodule" +``` + +## 在其他项目中使用 Utils + +### 方法 1: 作为 git submodule + +```bash +# 在你的项目根目录 +git submodule add Utils +git submodule update --init --recursive +``` + +### 方法 2: 直接克隆 + +```bash +# 在你的项目根目录 +git clone Utils +``` + +### 方法 3: 复制文件 + +直接将 Utils 目录复制到你的项目中。 + +## 在项目中引用 Utils + +### 在 .pro 文件中添加 + +```qmake +# 包含路径 +INCLUDEPATH += $$PWD/../Utils/VrCommon/Inc +INCLUDEPATH += $$PWD/../Utils/VrUtils/Inc + +# 链接库(Windows) +win32:CONFIG(release, debug|release): { + LIBS += -L$$PWD/../Utils/VrCommon/release -lVrCommon + LIBS += -L$$PWD/../Utils/VrUtils/release -lVrUtils +} +else:win32:CONFIG(debug, debug|release): { + LIBS += -L$$PWD/../Utils/VrCommon/debug -lVrCommon + LIBS += -L$$PWD/../Utils/VrUtils/debug -lVrUtils +} + +# 链接库(Unix/Linux) +else:unix:!macx { + LIBS += -L$$PWD/../Utils/VrCommon -lVrCommon + LIBS += -L$$PWD/../Utils/VrUtils -lVrUtils +} +``` + +### 在主项目中添加 Utils 子项目 + +```qmake +# 在主项目的 .pro 文件中 +TEMPLATE = subdirs + +Utils.file = ../Utils/Utils.pro +SUBDIRS += Utils + +# 设置依赖关系 +YourModule.depends = Utils +``` + +## 常用命令 + +### 构建 Utils 模块 + +```bash +cd Utils +qmake Utils.pro +make +``` + +### 清理构建产物 + +```bash +cd Utils +make clean +``` + +### 更新 Utils 模块(如果使用 submodule) + +```bash +# 在主项目根目录 +git submodule update --remote Utils +``` + +## 模块说明 + +### VrCommon +- **用途**: 核心接口和数据结构定义 +- **主要文件**: VrCommon.h, VrConfigCmd.h, VrError.h +- **依赖**: 无 + +### VrUtils +- **用途**: 通用工具类库 +- **主要功能**: 日志、JSON、XML、INI、MD5、CRC、时间、网络、文件、字符串 +- **依赖**: VrCommon + +### CloudUtils +- **用途**: 点云数据处理工具 +- **主要功能**: 激光数据加载、点云图像转换 +- **依赖**: VrCommon, VrUtils + +### DataUtils +- **用途**: 数据处理工具集 +- **子模块**: CloudMathClac(曲线拟合)、CoordinateTransform(坐标转换) +- **依赖**: VrCommon + +### CloudView +- **用途**: 点云可视化查看工具(独立应用) +- **依赖**: VrCommon, VrUtils, CloudUtils + +## 故障排查 + +### 问题 1: 找不到头文件 + +**症状**: 编译时提示找不到 VrCommon.h 或 VrUtils 相关头文件 + +**解决方案**: +1. 检查 INCLUDEPATH 是否正确设置 +2. 确保相对路径正确(根据你的项目结构调整 `../` 的数量) +3. 确保 Utils 目录存在且包含所有必要文件 + +### 问题 2: 链接错误 + +**症状**: 编译通过但链接时提示找不到库文件 + +**解决方案**: +1. 确保先编译 Utils 模块 +2. 检查 LIBS 路径是否正确 +3. 确认 debug/release 配置匹配 + +### 问题 3: submodule 未初始化 + +**症状**: Utils 目录为空 + +**解决方案**: +```bash +git submodule update --init --recursive +``` + +## 版本管理建议 + +### Utils 仓库 +- 使用语义化版本号(如 v1.0.0) +- 为重要版本打 tag +- 维护 CHANGELOG.md + +### 主项目 +- 如果使用 submodule,锁定 Utils 到特定版本 +- 定期更新 Utils 到最新稳定版本 + +## 联系方式 + +如有问题或建议,请联系项目维护者。 diff --git a/README.md b/README.md new file mode 100644 index 0000000..6a30b61 --- /dev/null +++ b/README.md @@ -0,0 +1,141 @@ +# Utils 工具库模块 + +这是一个独立的 Qt 工具库模块,包含了项目中常用的基础工具类和实用功能。 + +## 模块结构 + +``` +Utils/ +├── VrCommon/ # 核心接口和数据结构 +│ ├── Inc/ # 公共头文件 +│ │ ├── VrCommon.h +│ │ ├── VrConfigCmd.h +│ │ └── VrError.h +│ └── Src/ # 源文件 +│ +├── VrUtils/ # 工具类库 +│ ├── Inc/ # 工具类头文件 +│ ├── Src/ # 工具类源文件 +│ ├── jsoncpp/ # JSON 解析库 +│ ├── log4cpp/ # 日志库 +│ ├── tinyxml2/ # XML 解析库 +│ ├── ini/ # INI 配置文件解析 +│ ├── MD5/ # MD5 加密 +│ └── crc/ # CRC 校验 +│ +├── CloudUtils/ # 点云工具 +│ ├── Inc/ +│ │ ├── LaserDataLoader.h +│ │ └── PointCloudImageUtils.h +│ └── Src/ +│ +├── DataUtils/ # 数据处理工具 +│ ├── CloudMathClac/ # 点云数学计算 +│ └── CoordinateTransform/ # 坐标转换 +│ +└── CloudView/ # 点云查看工具(独立应用) + ├── Inc/ + ├── Src/ + └── resource/ +``` + +## 构建说明 + +### 作为子模块构建 + +在主项目的 .pro 文件中添加: + +```qmake +Utils.file = ../Utils/Utils.pro +SUBDIRS += Utils + +# 设置依赖关系 +YourModule.depends = Utils +``` + +### 独立构建 + +```bash +cd Utils +qmake Utils.pro +make +``` + +## 模块说明 + +### VrCommon + +核心接口和数据结构定义,包括: +- 配置接口定义 +- 错误码定义 +- 配置命令数据结构 + +### VrUtils + +通用工具类库,包括: +- **日志工具**: 基于 log4cpp 的日志系统 +- **JSON 解析**: jsoncpp 库 +- **XML 解析**: tinyxml2 库 +- **INI 配置**: SimpleIni 库 +- **加密工具**: MD5、CRC 校验 +- **时间工具**: 时间格式化、NTP 同步 +- **网络工具**: 网络相关实用函数 +- **文件工具**: 文件操作相关函数 +- **字符串工具**: 字符串处理函数 + +### CloudUtils + +点云数据处理工具,包括: +- 激光数据加载器 +- 点云图像转换工具 + +### DataUtils + +数据处理工具集,包括: +- **CloudMathClac**: 点云数学计算(曲线拟合等) +- **CoordinateTransform**: 坐标系转换工具 + +### CloudView + +点云可视化查看工具(独立应用程序) + +## 使用示例 + +### 在项目中引用 + +```qmake +# 包含路径 +INCLUDEPATH += $$PWD/../Utils/VrCommon/Inc +INCLUDEPATH += $$PWD/../Utils/VrUtils/Inc + +# 链接库 +win32:CONFIG(release, debug|release): { + LIBS += -L$$PWD/../Utils/VrCommon/release -lVrCommon + LIBS += -L$$PWD/../Utils/VrUtils/release -lVrUtils +} +else:win32:CONFIG(debug, debug|release): { + LIBS += -L$$PWD/../Utils/VrCommon/debug -lVrCommon + LIBS += -L$$PWD/../Utils/VrUtils/debug -lVrUtils +} +else:unix:!macx { + LIBS += -L$$PWD/../Utils/VrCommon -lVrCommon + LIBS += -L$$PWD/../Utils/VrUtils -lVrUtils +} +``` + +## 依赖关系 + +- **VrUtils** 依赖 **VrCommon** +- **CloudUtils** 依赖 **VrCommon** 和 **VrUtils** +- **DataUtils** 依赖 **VrCommon** +- **CloudView** 依赖 **VrCommon**、**VrUtils** 和 **CloudUtils** + +## 编译要求 + +- Qt 5.x 或更高版本 +- C++11 或更高版本 +- 支持平台:Windows (MSVC/MinGW)、Linux (ARM/x86_64) + +## 许可证 + +内部项目使用 diff --git a/Utils.pro b/Utils.pro new file mode 100644 index 0000000..bdae74f --- /dev/null +++ b/Utils.pro @@ -0,0 +1,22 @@ +TEMPLATE = subdirs + +# 定义子项目 +VrCommon.file = VrCommon/VrCommon.pro +VrUtils.file = VrUtils/VrUtils.pro +CloudUtils.file = CloudUtils/CloudUtils.pro +DataUtils.file = DataUtils/DataUtils.pro +CloudView.file = CloudView/CloudView.pro + +# 添加子项目 +SUBDIRS += \ + VrCommon \ + VrUtils \ + CloudUtils \ + DataUtils \ + CloudView + +# 设置依赖关系 +VrUtils.depends = VrCommon +CloudUtils.depends = VrCommon VrUtils +DataUtils.depends = VrCommon +CloudView.depends = VrCommon VrUtils CloudUtils diff --git a/VrCommon/Inc/VrCommon.h b/VrCommon/Inc/VrCommon.h new file mode 100644 index 0000000..b328614 --- /dev/null +++ b/VrCommon/Inc/VrCommon.h @@ -0,0 +1,59 @@ +#pragma once +//基础头文件 +#include +#include +#include +#include + +//容器 +#include +#include +#include + +//字符流 +#include + +#include "VrError.h" + +#define PI 3.14159265358979323846 + +#define VR_IP_LEN 16 +#define VR_IP_HEX_LEN 4 + +#define VR_MAC_LEN 18 +#define VR_MAC_HEX_LEN 6 + +#define VR_NAME_LEN 64 +#define VR_DESC_LEN 256 + +#define VR_MD5DATA_LEN 32 + +#define UNUSED(x) (void)x + +#define YEAR ((((__DATE__[7] - '0') * 10 + (__DATE__[8] - '0')) * 10 \ + + (__DATE__ [9] - '0')) * 10 + (__DATE__ [10] - '0')) + +#define MONTH (__DATE__ [2] == 'n' ? (__DATE__ [1] == 'a' ? 1 : 6) \ + : __DATE__ [2] == 'b' ? 2 \ + : __DATE__ [2] == 'r' ? (__DATE__ [0] == 'M' ? 3 : 4) \ + : __DATE__ [2] == 'y' ? 5 \ + : __DATE__ [2] == 'l' ? 7 \ + : __DATE__ [2] == 'g' ? 8 \ + : __DATE__ [2] == 'p' ? 9 \ + : __DATE__ [2] == 't' ? 10 \ + : __DATE__ [2] == 'v' ? 11 : 12) + +#define DAY ((__DATE__ [4] == ' ' ? 0 : __DATE__ [4] - '0') * 10 \ + + (__DATE__[5] - '0')) + + +#define HOUR ((__TIME__[0] - '0') * 10 + (__TIME__[1] - '0')) + +#define MINUTE ((__TIME__[3] - '0') * 10 + (__TIME__[4] - '0')) + +#define SECOND ((__TIME__[6] - '0') * 10 + (__TIME__[7] - '0')) + +namespace CVrCommon +{ + +}; diff --git a/VrCommon/Inc/VrConfigCmd.h b/VrCommon/Inc/VrConfigCmd.h new file mode 100644 index 0000000..568432e --- /dev/null +++ b/VrCommon/Inc/VrConfigCmd.h @@ -0,0 +1,205 @@ +#ifndef VRCONFIGCMD_H +#define VRCONFIGCMD_H + +#include +#include + +// 配置命令类型枚举 +enum ConfigCmdType +{ + CONFIG_CMD_UNKNOWN = 0, + CONFIG_CMD_CAMERA_EXPOSE, // 相机曝光设置 + CONFIG_CMD_CAMERA_GAIN, // 相机增益设置 + CONFIG_CMD_CAMERA_FRAMERATE, // 相机帧率设置 + CONFIG_CMD_CAMERA_SWING, // 相机摆动参数设置 + CONFIG_CMD_ALGO_PARAM, // 算法参数设置 + CONFIG_CMD_CALIB_PARAM, // 标定参数设置 + CONFIG_CMD_FULL_CONFIG, // 完整配置更新 + CONFIG_CMD_SWITCH_WORKPOSITION, // 切换工作点 + CONFIG_CMD_SWITCH_PACKAGETYPE, // 切换包裹类型 +}; + +// 相机参数结构 (扩展支持UI中的所有参数) +struct CameraConfigParam +{ + int cameraIndex; // 相机索引 (1或2, -1表示所有相机) + double exposeTime; // 曝光时间 (微秒) + double gain; // 增益值 + double frameRate; // 帧率 + double swingSpeed; // 摆动速度 + double swingStartAngle; // 开始角度 + double swingStopAngle; // 结束角度 + + CameraConfigParam() : cameraIndex(-1), exposeTime(0.0), gain(0.0), + frameRate(0.0), swingSpeed(0.0), swingStartAngle(0.0), swingStopAngle(0.0) {} +}; + +// ROI区域参数结构 +struct ROIConfigParam +{ + int cameraIndex; // 相机索引 (1或2, -1表示所有相机) + int x; // 起始X坐标 + int y; // 起始Y坐标 + int width; // 宽度 + int height; // 高度 + + ROIConfigParam() : cameraIndex(-1), x(0), y(0), width(0), height(0) {} +}; + +// 算法参数结构 +struct AlgoConfigParam +{ + char paramName[64]; // 参数名称 + double paramValue; // 参数值 + + AlgoConfigParam() : paramValue(0.0) + { + memset(paramName, 0, sizeof(paramName)); + } +}; + +// 标定参数结构 +struct CalibConfigParam +{ + int cameraIndex; // 相机索引 + double matrix[16]; // 4x4变换矩阵 + + CalibConfigParam() : cameraIndex(-1) + { + memset(matrix, 0, sizeof(matrix)); + } +}; + +// 摆动参数结构 +struct SwingConfigParam +{ + int cameraIndex; // 相机索引 + double swingSpeed; // 摆动速度 + double startAngle; // 开始角度 + double stopAngle; // 结束角度 + + SwingConfigParam() : cameraIndex(-1), swingSpeed(0.0), startAngle(0.0), stopAngle(0.0) {} +}; + +// 完整配置结构 (用于传输ConfigResult) +struct FullConfigParam +{ + char configJson[4096]; // JSON格式的完整配置数据 + + FullConfigParam() + { + memset(configJson, 0, sizeof(configJson)); + } +}; + +// 工作点切换参数结构 +struct SwitchWorkPositionParam +{ + char workPositionId[64]; // 工作点ID + + SwitchWorkPositionParam() + { + memset(workPositionId, 0, sizeof(workPositionId)); + } +}; + +// 包裹类型切换参数结构 +struct SwitchPackageTypeParam +{ + char packageTypeId[64]; // 包裹类型ID + + SwitchPackageTypeParam() + { + memset(packageTypeId, 0, sizeof(packageTypeId)); + } +}; + +// 配置命令数据结构 +struct ConfigCmdData +{ + ConfigCmdType cmdType; // 命令类型 + char timestamp[32]; // 时间戳 + + union { + CameraConfigParam cameraParam; + ROIConfigParam roiParam; + AlgoConfigParam algoParam; + CalibConfigParam calibParam; + SwingConfigParam swingParam; + FullConfigParam fullConfigParam; + SwitchWorkPositionParam switchWorkPositionParam; + SwitchPackageTypeParam switchPackageTypeParam; + }; + + ConfigCmdData() : cmdType(CONFIG_CMD_UNKNOWN) + { + memset(timestamp, 0, sizeof(timestamp)); + } +}; + +// 共享内存通信协议 +struct ConfigCmdHeader +{ + char magic[8]; // 魔数 "VRCFG001" + int version; // 版本号 + int dataSize; // 数据大小 + int checksum; // 校验和 + bool hasNewData; // 是否有新数据 + + ConfigCmdHeader() + { + strcpy(magic, "VRCFG001"); + version = 1; + dataSize = sizeof(ConfigCmdData); + checksum = 0; + hasNewData = false; + } +}; + +// 完整的共享内存数据结构 +struct ConfigCmdSharedData +{ + ConfigCmdHeader header; + ConfigCmdData data; + + // 计算校验和 + int CalculateChecksum() const + { + int sum = 0; + const char* ptr = reinterpret_cast(&data); + for (size_t i = 0; i < sizeof(ConfigCmdData); ++i) { + sum += static_cast(ptr[i]); + } + return sum; + } + + // 验证校验和 + bool ValidateChecksum() const + { + return header.checksum == CalculateChecksum(); + } +}; + +// 共享内存名称定义 +#define CONFIG_CMD_SHARED_MEM_NAME "GrabBagConfigCmd" +#define CONFIG_CMD_SHARED_MEM_SIZE sizeof(ConfigCmdSharedData) + +// 命令行参数解析函数声明 +class ConfigCmdParser +{ +public: + // 解析命令行参数 + static bool ParseCommandLine(int argc, char* argv[], ConfigCmdData& configData); + + // 解析具体的配置命令 + static bool ParseCameraExpose(const std::string& param, ConfigCmdData& configData); + static bool ParseCameraGain(const std::string& param, ConfigCmdData& configData); + static bool ParseROISetting(const std::string& param, ConfigCmdData& configData); + static bool ParseAlgoParam(const std::string& param, ConfigCmdData& configData); + static bool ParseCalibParam(const std::string& param, ConfigCmdData& configData); + + // 显示帮助信息 + static void ShowHelp(); +}; + +#endif // VRCONFIGCMD_H \ No newline at end of file diff --git a/VrCommon/Inc/VrError.h b/VrCommon/Inc/VrError.h new file mode 100644 index 0000000..32d15b3 --- /dev/null +++ b/VrCommon/Inc/VrError.h @@ -0,0 +1,100 @@ +#pragma once +#include +#include + +enum ErrorCode +{ + SUCCESS = 0, + CLASS_OBJ_NULL = 1000, + FUN_UNSUPPORT, + + //功能执行失败 + APP_ERR_EXEC, + APP_ERR_ACK, + APP_ERR_PARAM, + + //相关加密内容 + ENCRYPT_SUCCESS = 0, + ENCRYPT_ERROR_BASE = 1050, + ENCRYPT_LOAD, + ENCRYPT_NOT_LOAD, + ENCRYPT_RELEASE, + ENCRYPT_NO_FUN, + ENCRYPT_ARG, + + //文件相关错误 + FILE_ERR_EXIST = 1100, + FILE_ERR_NOEXIST, + FILE_ERR_READ, + FILE_ERR_WRITE, + FILE_ERR_FORMAT, + FILE_ERR_DEL, + + //共享内存错误 + SHAREMEM_ERR_BASE = 1200, + SHAREMEM_ERR_PARAM, // 参数错误 + SHAREMEM_ERR_CREATE, // 创建失败 + SHAREMEM_ERR_ALREADY_EXIST, // 已存在 + SHAREMEM_ERR_NOT_EXIST, // 不存在 + SHAREMEM_ERR_SIZE, // 设置大小失败 + SHAREMEM_ERR_INFO, // 获取信息失败 + SHAREMEM_ERR_MUTEX, // 互斥锁失败 + SHAREMEM_ERR_NOT_MAPPED, // 未映射 + SHAREMEM_ERR_TIMEOUT, // 超时 + + //数据错误 + DATA_ERR_LEN = 1300, + DATA_ERR_INVALID, + DATA_ERR_MD5, + DATA_ERR_KEY, + DATA_ERR_RANGE, + DATA_ERR_MEM, + + //网络错误 + NET_SUCCESS = 0, + NET_ERR_CREAT_INIT = 1400, + NET_ERR_CREAT_BIND, + NET_ERR_CREAT_LISTEN, + NET_ERR_CONNECT, + NET_ERR_ACCEPT, + NET_ERR_IP_INVALID, + NET_ERR_NOTINIT, + NET_ERR_SEND_DATA, + NET_ERR_RECV_CMD, + NET_ERR_RECV_DATA, + NET_ERR_RECV_DATA_LACK, //1410 + NET_ERR_ARG, + NET_ERR_CONFIG, + NET_ERR_CONFIG_GET_IP, + NET_ERR_REMOVE_FD_FAILED, + NET_ERR_GET, + NET_DEV_NOT_FIND, + NET_DEV_NOT_RIGHT, + NET_DEV_CLIENT_LINK, + NET_DEV_CLIENT_SENDLEN, + NET_DEV_CLIENT_RECV, //1420 + + // 设备类型 + DEV_TYPE_ERR = 1500, + DEV_NOT_FIND, + DEV_NO_OPEN, + DEV_ID_ERR, + DEV_OPEN_ERR, + DEV_CLOSE_ERR, //1505 + DEV_CTRL_ERR, + DEV_SEND_ERR, + DEV_RECV_ERR, + DEV_CTRL_TIMEOUT, + DEV_UNSUPPORT, //1510 + DEV_ARG_INVAILD, + DEV_BUSY, + DEV_CONFIG_ERR, + DEV_DATA_INVALID, + DEV_RESULT_EMPTY, //1515 + DEV_UNACTIVATE, //未激活 +}; + +#define ERR_CODE(nCode) -std::abs((int)nCode) +#define ERR_CODE_RETURN(nCode) do{ if(SUCCESS != (nCode)) return ERR_CODE(nCode); }while(0) + +#define ERRCODE(nCode) -std::abs((int)nCode) diff --git a/VrCommon/Src/VrCommon.cpp b/VrCommon/Src/VrCommon.cpp new file mode 100644 index 0000000..e38a0ad --- /dev/null +++ b/VrCommon/Src/VrCommon.cpp @@ -0,0 +1 @@ +#include "VrCommon.h" \ No newline at end of file diff --git a/VrCommon/VrCommon.pro b/VrCommon/VrCommon.pro new file mode 100644 index 0000000..c1fbd83 --- /dev/null +++ b/VrCommon/VrCommon.pro @@ -0,0 +1,27 @@ +#CONFIG -= qt + +TEMPLATE = lib +CONFIG += staticlib +CONFIG += c++17 + +# You can make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + + +INCLUDEPATH += ./Inc + +SOURCES += \ + Src/VrCommon.cpp + +HEADERS += \ + Inc/VrCommon.h \ + Inc/VrConfigCmd.h \ + Inc/VrError.h + + +# Default rules for deployment. +unix { + target.path = /usr/lib +} +!isEmpty(target.path): INSTALLS += target diff --git a/VrUtils/CMakeLists.txt b/VrUtils/CMakeLists.txt new file mode 100644 index 0000000..c12f7ee --- /dev/null +++ b/VrUtils/CMakeLists.txt @@ -0,0 +1,31 @@ +ADD_DEFINITIONS(-D__OPENVMS__) #log4cpp 编译选项 +ADD_DEFINITIONS(-DVR_UTILS_EXPORTS) #VrUtils 导出宏定义 +# ADD_COMPILE_OPTIONS(-Wno-deprecated-declarations) #auto_ptr警告消除 + +# 设置CMake策略,允许AUTOMOC和AUTOUIC处理.hh文件 +# cmake_policy(SET CMP0100 NEW) + +INCLUDE_DIRECTORIES( + ./_Inc + ./Inc + ./MD5 + ./tinyxml2 + ./log4cpp/include +) + +AUX_SOURCE_DIRECTORY(./Src SrcS) +AUX_SOURCE_DIRECTORY(./MD5 MD5SrcS) +AUX_SOURCE_DIRECTORY(./tinyxml2 TinyxmlSrcS) +AUX_SOURCE_DIRECTORY(./log4cpp/src LogSrcS) + +# 对特定的.hh文件设置SKIP_AUTOGEN属性 +set_property(SOURCE log4cpp/src/StringUtil.hh PROPERTY SKIP_AUTOGEN ON) +set_property(SOURCE log4cpp/src/PortabilityImpl.hh PROPERTY SKIP_AUTOGEN ON) +set_property(SOURCE log4cpp/src/Properties.hh PROPERTY SKIP_AUTOGEN ON) +set_property(SOURCE log4cpp/src/Localtime.hh PROPERTY SKIP_AUTOGEN ON) +set_property(SOURCE log4cpp/src/PropertyConfiguratorImpl.hh PROPERTY SKIP_AUTOGEN ON) + +ADD_LIBRARY(VrUtils STATIC ${SrcS} ${MD5SrcS} + ${TinyxmlSrcS} + ${LogSrcS} + ) diff --git a/VrUtils/Inc/IVrUtils.h b/VrUtils/Inc/IVrUtils.h new file mode 100644 index 0000000..5f7a3f8 --- /dev/null +++ b/VrUtils/Inc/IVrUtils.h @@ -0,0 +1,63 @@ +#ifndef __IVRUILTS__H__ +#define __IVRUILTS__H__ +#include "VrDateUtils.h" +#include "VrFileUtils.h" +#include "VrTimeUtils.h" +#include "VrCodeFormatUtils.h" +#include "VrMD5Utils.h" +#include "VrStringUtils.h" +#include "VrNumUtils.h" +#include "VrLog.h" +#include "VrShellUtils.h" +#include "VrNetUtils.h" +#include "VrDebugTime.h" +#include "VrNTPUtils.h" +#include "../crc/checksum.h" +#include "../ini/SimpleIni.h" + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +// getopt只在Windows平台下包含,其他平台使用系统自带的getopt +#ifdef _WIN32 +#include "getopt.h" +#pragma comment(lib, "VrUtils.lib") +#pragma comment(lib, "Shell32.lib") // VrLog 需要 SHGetFolderPathA +#endif // _WIN32 + + +#define YEAR ((((__DATE__[7] - '0') * 10 + (__DATE__[8] - '0')) * 10 \ + + (__DATE__ [9] - '0')) * 10 + (__DATE__ [10] - '0')) + +#define MONTH (__DATE__ [2] == 'n' ? (__DATE__ [1] == 'a' ? 1 : 6) \ + : __DATE__ [2] == 'b' ? 2 \ + : __DATE__ [2] == 'r' ? (__DATE__ [0] == 'M' ? 3 : 4) \ + : __DATE__ [2] == 'y' ? 5 \ + : __DATE__ [2] == 'l' ? 7 \ + : __DATE__ [2] == 'g' ? 8 \ + : __DATE__ [2] == 'p' ? 9 \ + : __DATE__ [2] == 't' ? 10 \ + : __DATE__ [2] == 'v' ? 11 : 12) + +#define DAY ((__DATE__ [4] == ' ' ? 0 : __DATE__ [4] - '0') * 10 \ + + (__DATE__[5] - '0')) + + +#define HOUR ((__TIME__[0] - '0') * 10 + (__TIME__[1] - '0')) + +#define MINUTE ((__TIME__[3] - '0') * 10 + (__TIME__[4] - '0')) + +#define SECOND ((__TIME__[6] - '0') * 10 + (__TIME__[7] - '0')) + +#define BUILD_TIME (std::to_string(YEAR) + \ + (MONTH < 10 ? "0" : "") + std::to_string(MONTH) + \ + (DAY < 10 ? "0" : "") + std::to_string(DAY) + \ + (HOUR < 10 ? "0" : "") + std::to_string(HOUR) + \ + (MINUTE < 10 ? "0" : "") + std::to_string(MINUTE) + \ + (SECOND < 10 ? "0" : "") + std::to_string(SECOND)) + +#endif + + + diff --git a/VrUtils/Inc/VrAngleUtils.h b/VrUtils/Inc/VrAngleUtils.h new file mode 100644 index 0000000..04b71de --- /dev/null +++ b/VrUtils/Inc/VrAngleUtils.h @@ -0,0 +1,14 @@ +#pragma once +#include + +namespace CVrAngleRadianUtils +{ + /// 从角度转换成弧度 + double AngleToRadian(double angle); + + /// 从弧度转换成角度 + double RadianToAngle(double radian); + +}; + + diff --git a/VrUtils/Inc/VrCodeFormatUtils.h b/VrUtils/Inc/VrCodeFormatUtils.h new file mode 100644 index 0000000..b82eb18 --- /dev/null +++ b/VrUtils/Inc/VrCodeFormatUtils.h @@ -0,0 +1,25 @@ +#pragma once + +namespace CVrCodeFormatUtils +{ + //GB2312到UTF-8的转换 + int GB2312ToUtf8(const char* gb2312, char* utf8); + + //判断是否是utf8 + bool IsTextUTF8(const char* str, long length); + + //UTF-8到GB2312的转换 + int Utf8ToGB2312(const char* utf8, char* gb2312); + + //GB2312到Unicode的转换 + int GB2312ToUnicode(const char* gb2312, char* unicode); + + //Unicode到GB2312的转换 + int UnicodeToGB2312(const char* unicode, int size, char*gb2312); + + //UTF-8到Unicode的转换 + int Utf8ToUnicode(const char* utf8, char*unicode); + + //Unicode到UTF-8的转换 + int UnicodeToUtf8(const char* unicode, int size, char* utf8); +} diff --git a/VrUtils/Inc/VrDateUtils.h b/VrUtils/Inc/VrDateUtils.h new file mode 100644 index 0000000..95c68a9 --- /dev/null +++ b/VrUtils/Inc/VrDateUtils.h @@ -0,0 +1,22 @@ +#pragma once +#include +#include + +namespace CVrDateUtils +{ + /** + * 获取时间年月日时分秒 + */ + std::string GetNowTime(); + + /** + * 获取时间:年-月-日 时:分:秒 + */ + std::string GetStrNowTime(bool bYear = true); + + /** + * 获取微妙时间戳 + */ + unsigned long long GetTimestamp(); + +}; \ No newline at end of file diff --git a/VrUtils/Inc/VrDebugTime.h b/VrUtils/Inc/VrDebugTime.h new file mode 100644 index 0000000..48ff3e3 --- /dev/null +++ b/VrUtils/Inc/VrDebugTime.h @@ -0,0 +1,50 @@ +#pragma once +#include +#include +#include +#include +#include +#include + + +/// \brief +/// ʱ +class CVrDebugTimer +{ +public: + CVrDebugTimer(const char* szDesc) + : m_strFunName(szDesc) + { + m_tBegin = std::chrono::high_resolution_clock::now(); + m_tRecordTime = m_tBegin; + printf("===[%s Runtime]===\n", m_strFunName.c_str()); + } + + ~CVrDebugTimer() + { + std::chrono::high_resolution_clock::time_point tCur = std::chrono::high_resolution_clock::now(); + std::chrono::duration> duration_ms(tCur - m_tBegin); + + printf("===[%s Runtime %f ms]===\n", m_strFunName.c_str(), duration_ms.count()); + } + + void RecordTime(const char* szDesc) + { + std::chrono::high_resolution_clock::time_point tCur = std::chrono::high_resolution_clock::now(); + std::chrono::duration> duration_ms(tCur - m_tRecordTime); + m_tRecordTime = tCur; + + printf("===[%s Runtime %f ms]===\n", szDesc, duration_ms.count()); + } + +private: + std::string m_strFunName; + std::chrono::high_resolution_clock::time_point m_tBegin; + std::chrono::high_resolution_clock::time_point m_tRecordTime; +}; + + + +/// \brief +/// ӡʱ +#define VRDEBUG_PRINT_TIME(_desc) CVrDebugTimer oFuncDebugTime(_desc) diff --git a/VrUtils/Inc/VrFileUtils.h b/VrUtils/Inc/VrFileUtils.h new file mode 100644 index 0000000..1c80ebf --- /dev/null +++ b/VrUtils/Inc/VrFileUtils.h @@ -0,0 +1,44 @@ +#pragma once +#include +#include + +namespace CVrFileUtils +{ + /// 文件是否存在 + bool IsFileExist(const char* pFileName); + + /// 文件大小 + size_t GetFileSize(const char* fileName); + + /// 写文件 + bool WriteFileData(const char* pFileName, const char* pData, const size_t nLen, bool isAppend = false); + + /// 读文件 + bool ReadFileData(const char* pFileName, char* pData, size_t* pLen, const int nOffset = 0); + + /// 获取目录下所有的文件 + bool GetFileList(std::string& pDirPath, std::string exd, std::vector& vetFiles); + + /// 获取目录下所有目录 + bool GetDirList(std::string& pDirPath, std::vector& vetFiles); + + /// 文件重命名 + bool ReNameFile(const char* oldName, const char* newName); + + /// 删除文件 + bool DeleteLocalFile(const char* sFileName); + + /*******目录操作*******/ + /// 目录是否存在 + bool IsDirExist(const char* szDirectory); + + /// 创建新的文件夹 + bool CreatNewDir(const char* szPath); + + /// 删除目录 + bool DeleteDir(const char* szDirPath); + + /// 去除后缀获取文件名称 + std::string GetFileName(const std::string& filePath, bool bHasSuffix = true); + +} // namespace name diff --git a/VrUtils/Inc/VrLog.h b/VrUtils/Inc/VrLog.h new file mode 100644 index 0000000..83cf20f --- /dev/null +++ b/VrUtils/Inc/VrLog.h @@ -0,0 +1,87 @@ +#ifndef __VR_LOG__H_ +#define __VR_LOG__H_ + +#include +#include + +#ifdef _WIN32 +#pragma comment(lib, "Shell32.lib") // VrLog 需要 SHGetFolderPathA +#endif // _WIN32 + +#ifdef _WIN32 + #define VR_UTILS_API +#else + // Linux/Unixƽ̨ + #ifdef VR_UTILS_EXPORTS + #define VR_UTILS_API __attribute__((visibility("default"))) + #else + #define VR_UTILS_API __attribute__((visibility("default"))) + #endif +#endif + +#ifdef _WIN32 +// windows: +#define logfilename(x) strrchr(x,'\\')?strrchr(x,'\\')+1:x +#else +// linux: +#define logfilename(x) strrchr(x,'/')?strrchr(x,'/')+1:x +#endif // _WIN32 + + +#ifdef _WIN32 +#pragma comment(lib, "advapi32.lib") +#endif + +#define LOG_VERBOSE(...) VrLogUtils::EchoLog(KELOGLEVEL_Verbose, logfilename(__FILE__), __LINE__, "APPV", ##__VA_ARGS__) +#define LOG_DEBUG(...) VrLogUtils::EchoLog(KELOGLEVEL_Debug, logfilename(__FILE__), __LINE__, "APPD", ##__VA_ARGS__) +#define LOG_INFO(...) VrLogUtils::EchoLog(KELOGLEVEL_Info, logfilename(__FILE__), __LINE__, "APPI", ##__VA_ARGS__) +#define LOG_WARNING(...) VrLogUtils::EchoLog(KELOGLEVEL_Warning, logfilename(__FILE__), __LINE__, "APPW", ##__VA_ARGS__) +#define LOG_WARN(...) VrLogUtils::EchoLog(KELOGLEVEL_Warning, logfilename(__FILE__), __LINE__, "APPW", ##__VA_ARGS__) +#define LOG_ERROR(...) VrLogUtils::EchoLog(KELOGLEVEL_Error, logfilename(__FILE__), __LINE__, "APPE", ##__VA_ARGS__) +#define LOG_ERRO(...) VrLogUtils::EchoLog(KELOGLEVEL_Error, logfilename(__FILE__), __LINE__, "APPE", ##__VA_ARGS__) +#define LOG_ERR(...) VrLogUtils::EchoLog(KELOGLEVEL_Error, logfilename(__FILE__), __LINE__, "APPE", ##__VA_ARGS__) + +/// @brief +/// 日志等级 +enum VrLogLevel +{ + KELOGLEVEL_None = 0, + KELOGLEVEL_Verbose = 1, + KELOGLEVEL_Debug, + KELOGLEVEL_Info, + KELOGLEVEL_Warning, + KELOGLEVEL_Error, +}; + +enum VrLogType +{ + KELOGTYPE_None = 0, + KELOGTYPE_Console = 1, + KELOGTYPE_Log4Cpp = 1 << 1, + KELOGTYPE_All = 0xff, +}; + +namespace VrLogUtils +{ + /// 初始化log + VR_UTILS_API void InitLog(); + + /// 关闭log + VR_UTILS_API void UninitLog(); + + /// 开启/关闭时间戳 + VR_UTILS_API void EnableTime(bool bEnable); + + /// 输出log + VR_UTILS_API void EchoLog(VrLogLevel eLogLevel, const char* sFilePath, const int nLine, const char* sLogGroup, const char* sFormat, ...); + + /// 修改log level default info + VR_UTILS_API void AlterLogLevel(VrLogLevel eLogLevel); + + /// 修改log输出形式 默认都输出 + VR_UTILS_API void AlterLogType(VrLogType eLogType); + +}; + +#endif + diff --git a/VrUtils/Inc/VrMD5Utils.h b/VrUtils/Inc/VrMD5Utils.h new file mode 100644 index 0000000..1fff6d5 --- /dev/null +++ b/VrUtils/Inc/VrMD5Utils.h @@ -0,0 +1,7 @@ +#pragma once +#include + +namespace VrMD5Utils { + std::string GetMD5(const char* pData, const int nDataLen); + +} diff --git a/VrUtils/Inc/VrNTPUtils.h b/VrUtils/Inc/VrNTPUtils.h new file mode 100644 index 0000000..23e058c --- /dev/null +++ b/VrUtils/Inc/VrNTPUtils.h @@ -0,0 +1,50 @@ +#pragma once + +#ifdef _WIN32 +#include +#include +#endif // _WIN32 + + +class CrNTPUtils +{ +public: + CrNTPUtils(); + ~CrNTPUtils(); + + +#ifndef _WIN32 +public: + struct SYSTEMTIME + { + int wYear; + int wMonth; + int wDayOfWeek; + int wDay; + int wHour; + int wMinute; + int wSecond; + int wMilliseconds; + }; +#endif + + + /// ʼNTPClient + bool InitNTPClient(char* sIP = nullptr, int nPort = 123); + + /// ˳NTPClient + bool ExitNTPClient(); + + ///ȡʱ + bool GetSystemTime(SYSTEMTIME& newtime); + +private: + bool _UpdateDate(); + +private: + char m_sIP[16]; + bool m_bValidIP; + int m_nPort; + +}; + diff --git a/VrUtils/Inc/VrNetUtils.h b/VrUtils/Inc/VrNetUtils.h new file mode 100644 index 0000000..3a23736 --- /dev/null +++ b/VrUtils/Inc/VrNetUtils.h @@ -0,0 +1,68 @@ +#pragma once +#include +#include +#include +#include + +#define IPV4_CHAR_LENGTH 22 +#define MAC_CHAR_LENGTH 18 +#define NAME_MAXPATH 256 + +/// 网络信息 +typedef struct +{ + unsigned int nAdapterIndex; + char szNetCardName[NAME_MAXPATH]; //< + unsigned char byLocalIP[4]; + unsigned char byMacAddress[6]; + unsigned char bySubMask[4]; + unsigned char byGetWay[4]; + unsigned char byBroadCastIP[4]; +} SVrCardInfo; + +std::ostream& operator<<(std::ostream& os, const SVrCardInfo& sAction); + +namespace CVrNetUtils +{ + /// 获取所有设备 + bool QueryAllNetworkInfo(std::list& lstNetCardInfo); + + /// ip是否有效 + bool IsValidIP(const char szIPv4[IPV4_CHAR_LENGTH]); + + bool SetAddrMaskGateWay(const char *ifname, const char *Ipaddr, const char *mask, const char *gateway); + + /// 设置IP + bool SetIPv4Address(const char ip[IPV4_CHAR_LENGTH], const char *devName); + + /// 获取IP + bool GetIPv4Address(char ip[IPV4_CHAR_LENGTH], const char *devName); + + /// 设置网关; + bool SetIPv4NetMask(const char ip[IPV4_CHAR_LENGTH], const char *devName); + + /// 获取子网掩码 + bool GetIPv4NetMask(char ip[IPV4_CHAR_LENGTH], const char *devName); + + /// 获取广播地址 + bool GetIPv4BroadIP(char ip[IPV4_CHAR_LENGTH], const char *devName); + + /// 获取网卡地址 + bool GetLocalMAC(char byMac[MAC_CHAR_LENGTH], const char *devName); + + /// 设置网关 + bool SetGateWay(const char ip[IPV4_CHAR_LENGTH], const char *devName); + + /// 获取网关 + bool GetGateWay(char ip[IPV4_CHAR_LENGTH], const char *devName); + + /// 将IP字符串转为Byte + bool IPString2Byte(const char* szIP, unsigned char byIP[4]); + + /// 将IP的Byte转为字符串 + void IPByte2String(const unsigned char byIP[4], char szIP[IPV4_CHAR_LENGTH]); + + /// 计算广播地址 + void CalcBroadCastIP(unsigned char byIP[4], unsigned char byMask[4], unsigned char byBroadCast[4]); + +} diff --git a/VrUtils/Inc/VrNumUtils.h b/VrUtils/Inc/VrNumUtils.h new file mode 100644 index 0000000..f5cfb96 --- /dev/null +++ b/VrUtils/Inc/VrNumUtils.h @@ -0,0 +1,10 @@ +#pragma once + +namespace CVrNumUtils +{ + // С˷ת + short NumFlip(short nData); + + // С˷ת + float NumFlipFloat(float fData); +}; \ No newline at end of file diff --git a/VrUtils/Inc/VrShellUtils.h b/VrUtils/Inc/VrShellUtils.h new file mode 100644 index 0000000..8f925da --- /dev/null +++ b/VrUtils/Inc/VrShellUtils.h @@ -0,0 +1,6 @@ +#pragma once + +namespace CVrShellUtils +{ + int ExecuCMD(const char *data); +}; \ No newline at end of file diff --git a/VrUtils/Inc/VrStringUtils.h b/VrUtils/Inc/VrStringUtils.h new file mode 100644 index 0000000..b137b5a --- /dev/null +++ b/VrUtils/Inc/VrStringUtils.h @@ -0,0 +1,23 @@ +#pragma once +#include +#include + +namespace CVrStringUtils +{ + /// תַ + std::string Int2Str(int nNum); + std::string Int2HexStr(int nNum); + std::string Int2str(long num); + std::string Int2Str(unsigned int nNum); + std::string Int2Str(unsigned long long nNum); + + /// ַָ + std::vector SplitString(std::string srcStr, std::string delimStr, bool repeatedCharIgnored); + + /// Сдת + int StringUpper(char *sData, int nLen); + int StringLower(char *sData, int nLen); + + /// 滻ַ + void ReplaceString(std::string& source, const std::string& to_replace, const std::string& replacement); +}; \ No newline at end of file diff --git a/VrUtils/Inc/VrTimeUtils.h b/VrUtils/Inc/VrTimeUtils.h new file mode 100644 index 0000000..362b0d6 --- /dev/null +++ b/VrUtils/Inc/VrTimeUtils.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +class CVrTimeUtils +{ +public: + CVrTimeUtils(); + ~CVrTimeUtils(); + + ///更新时间 + void Update(); + + /** + * 获取当前秒 + */ + double GetElapsedSecond(); + + /** + * 获取毫秒 + */ + double GetElapsedTimeInMilliSec(); + + /** + * 获取微妙 + */ + long long GetElapsedTimeInMicroSec(); + +private: + std::chrono::time_point m_begin; + +}; diff --git a/VrUtils/Inc/getopt.h b/VrUtils/Inc/getopt.h new file mode 100644 index 0000000..4cefc3f --- /dev/null +++ b/VrUtils/Inc/getopt.h @@ -0,0 +1,133 @@ +/* Declarations for getopt. + Copyright (C) 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +// ֻWindowsƽ̨ṩԶgetoptʵ֣ƽ̨ʹϵͳԴ +#ifdef _WIN32 + +#ifndef _GETOPT_H +#define _GETOPT_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ +#if __STDC__ + const char *name; +#else + char *name; +#endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +//#if __STDC__ || defined(PROTO) +//#if defined(__GNU_LIBRARY__) +/* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ +extern int getopt (int argc, char *const *argv, const char *shortopts); +//#endif /* not __GNU_LIBRARY__ */ +extern int getopt_long (int argc, char *const *argv, const char *shortopts, + const struct option *longopts, int *longind); +extern int getopt_long_only (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind); + +/* Internal only. Users should not call this directly. */ +/* extern int _getopt_internal (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind, + int long_only); */ +//#else /* not __STDC__ */ +//extern int getopt (); +//extern int getopt_long (); +//extern int getopt_long_only (); + +//extern int _getopt_internal (); +//#endif /* not __STDC__ */ + +#ifdef __cplusplus +} +#endif + +#endif /* _GETOPT_H */ + +#endif /* _WIN32 */ + diff --git a/VrUtils/MD5/md5.cpp b/VrUtils/MD5/md5.cpp new file mode 100644 index 0000000..565bd5d --- /dev/null +++ b/VrUtils/MD5/md5.cpp @@ -0,0 +1,287 @@ +/** + * @file md5.cpp + * @The implement of md5. + * @author Jiewei Wei + * @mail weijieweijerry@163.com + * @github https://github.com/JieweiWei + * @data Oct 19 2014 + * + */ + +#include "md5.h" + +/* Define the static member of MD5. */ +const byte MD5::PADDING[64] = { 0x80 }; +const char MD5::HEX_NUMBERS[16] = { + '0', '1', '2', '3', + '4', '5', '6', '7', + '8', '9', 'a', 'b', + 'c', 'd', 'e', 'f' +}; + +/** + * @Construct a MD5 object with a string. + * + * @param {message} the message will be transformed. + * + */ +MD5::MD5(const string& message) { + finished = false; + /* Reset number of bits. */ + count[0] = count[1] = 0; + /* Initialization constants. */ + state[0] = 0x67452301; + state[1] = 0xefcdab89; + state[2] = 0x98badcfe; + state[3] = 0x10325476; + + /* Initialization the object according to message. */ + init((const byte*)message.c_str(), message.length()); +} + +MD5::MD5(const byte* input, size_t len) +{ + finished = false; + /* Reset number of bits. */ + count[0] = count[1] = 0; + /* Initialization constants. */ + state[0] = 0x67452301; + state[1] = 0xefcdab89; + state[2] = 0x98badcfe; + state[3] = 0x10325476; + + /* Initialization the object according to message. */ + init(input, len); +} + +/** + * @Generate md5 digest. + * + * @return the message-digest. + * + */ +const byte* MD5::getDigest() { + if (!finished) { + finished = true; + + byte bits[8]; + bit32 oldState[4]; + bit32 oldCount[2]; + bit32 index, padLen; + + /* Save current state and count. */ + memcpy(oldState, state, 16); + memcpy(oldCount, count, 8); + + /* Save number of bits */ + encode(count, bits, 8); + + /* Pad out to 56 mod 64. */ + index = (bit32)((count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + init(PADDING, padLen); + + /* Append length (before padding) */ + init(bits, 8); + + /* Store state in digest */ + encode(state, digest, 16); + + /* Restore current state and count. */ + memcpy(state, oldState, 16); + memcpy(count, oldCount, 8); + } + return digest; +} + +/** + * @Initialization the md5 object, processing another message block, + * and updating the context. + * + * @param {input} the input message. + * + * @param {len} the number btye of message. + * + */ +void MD5::init(const byte* input, size_t len) { + + bit32 i, index, partLen; + + finished = false; + + /* Compute number of bytes mod 64 */ + index = (bit32)((count[0] >> 3) & 0x3f); + + /* update number of bits */ + if ((count[0] += ((bit32)len << 3)) < ((bit32)len << 3)) { + ++count[1]; + } + count[1] += ((bit32)len >> 29); + + partLen = 64 - index; + + /* transform as many times as possible. */ + if (len >= partLen) { + + memcpy(&buffer[index], input, partLen); + transform(buffer); + + for (i = partLen; i + 63 < len; i += 64) { + transform(&input[i]); + } + index = 0; + + } else { + i = 0; + } + + /* Buffer remaining input */ + memcpy(&buffer[index], &input[i], len - i); +} + +/** + * @MD5 basic transformation. Transforms state based on block. + * + * @param {block} the message block. + */ +void MD5::transform(const byte block[64]) { + + bit32 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + decode(block, x, 64); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], s11, 0xd76aa478); + FF (d, a, b, c, x[ 1], s12, 0xe8c7b756); + FF (c, d, a, b, x[ 2], s13, 0x242070db); + FF (b, c, d, a, x[ 3], s14, 0xc1bdceee); + FF (a, b, c, d, x[ 4], s11, 0xf57c0faf); + FF (d, a, b, c, x[ 5], s12, 0x4787c62a); + FF (c, d, a, b, x[ 6], s13, 0xa8304613); + FF (b, c, d, a, x[ 7], s14, 0xfd469501); + FF (a, b, c, d, x[ 8], s11, 0x698098d8); + FF (d, a, b, c, x[ 9], s12, 0x8b44f7af); + FF (c, d, a, b, x[10], s13, 0xffff5bb1); + FF (b, c, d, a, x[11], s14, 0x895cd7be); + FF (a, b, c, d, x[12], s11, 0x6b901122); + FF (d, a, b, c, x[13], s12, 0xfd987193); + FF (c, d, a, b, x[14], s13, 0xa679438e); + FF (b, c, d, a, x[15], s14, 0x49b40821); + + /* Round 2 */ + GG (a, b, c, d, x[ 1], s21, 0xf61e2562); + GG (d, a, b, c, x[ 6], s22, 0xc040b340); + GG (c, d, a, b, x[11], s23, 0x265e5a51); + GG (b, c, d, a, x[ 0], s24, 0xe9b6c7aa); + GG (a, b, c, d, x[ 5], s21, 0xd62f105d); + GG (d, a, b, c, x[10], s22, 0x2441453); + GG (c, d, a, b, x[15], s23, 0xd8a1e681); + GG (b, c, d, a, x[ 4], s24, 0xe7d3fbc8); + GG (a, b, c, d, x[ 9], s21, 0x21e1cde6); + GG (d, a, b, c, x[14], s22, 0xc33707d6); + GG (c, d, a, b, x[ 3], s23, 0xf4d50d87); + GG (b, c, d, a, x[ 8], s24, 0x455a14ed); + GG (a, b, c, d, x[13], s21, 0xa9e3e905); + GG (d, a, b, c, x[ 2], s22, 0xfcefa3f8); + GG (c, d, a, b, x[ 7], s23, 0x676f02d9); + GG (b, c, d, a, x[12], s24, 0x8d2a4c8a); + + /* Round 3 */ + HH (a, b, c, d, x[ 5], s31, 0xfffa3942); + HH (d, a, b, c, x[ 8], s32, 0x8771f681); + HH (c, d, a, b, x[11], s33, 0x6d9d6122); + HH (b, c, d, a, x[14], s34, 0xfde5380c); + HH (a, b, c, d, x[ 1], s31, 0xa4beea44); + HH (d, a, b, c, x[ 4], s32, 0x4bdecfa9); + HH (c, d, a, b, x[ 7], s33, 0xf6bb4b60); + HH (b, c, d, a, x[10], s34, 0xbebfbc70); + HH (a, b, c, d, x[13], s31, 0x289b7ec6); + HH (d, a, b, c, x[ 0], s32, 0xeaa127fa); + HH (c, d, a, b, x[ 3], s33, 0xd4ef3085); + HH (b, c, d, a, x[ 6], s34, 0x4881d05); + HH (a, b, c, d, x[ 9], s31, 0xd9d4d039); + HH (d, a, b, c, x[12], s32, 0xe6db99e5); + HH (c, d, a, b, x[15], s33, 0x1fa27cf8); + HH (b, c, d, a, x[ 2], s34, 0xc4ac5665); + + /* Round 4 */ + II (a, b, c, d, x[ 0], s41, 0xf4292244); + II (d, a, b, c, x[ 7], s42, 0x432aff97); + II (c, d, a, b, x[14], s43, 0xab9423a7); + II (b, c, d, a, x[ 5], s44, 0xfc93a039); + II (a, b, c, d, x[12], s41, 0x655b59c3); + II (d, a, b, c, x[ 3], s42, 0x8f0ccc92); + II (c, d, a, b, x[10], s43, 0xffeff47d); + II (b, c, d, a, x[ 1], s44, 0x85845dd1); + II (a, b, c, d, x[ 8], s41, 0x6fa87e4f); + II (d, a, b, c, x[15], s42, 0xfe2ce6e0); + II (c, d, a, b, x[ 6], s43, 0xa3014314); + II (b, c, d, a, x[13], s44, 0x4e0811a1); + II (a, b, c, d, x[ 4], s41, 0xf7537e82); + II (d, a, b, c, x[11], s42, 0xbd3af235); + II (c, d, a, b, x[ 2], s43, 0x2ad7d2bb); + II (b, c, d, a, x[ 9], s44, 0xeb86d391); + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; +} + +/** +* @Encodes input (unsigned long) into output (byte). +* +* @param {input} usigned long. +* +* @param {output} byte. +* +* @param {length} the length of input. +* +*/ +void MD5::encode(const bit32* input, byte* output, size_t length) { + + for (size_t i = 0, j = 0; j < length; ++i, j += 4) { + output[j]= (byte)(input[i] & 0xff); + output[j + 1] = (byte)((input[i] >> 8) & 0xff); + output[j + 2] = (byte)((input[i] >> 16) & 0xff); + output[j + 3] = (byte)((input[i] >> 24) & 0xff); + } +} + +/** + * @Decodes input (byte) into output (usigned long). + * + * @param {input} bytes. + * + * @param {output} unsigned long. + * + * @param {length} the length of input. + * + */ +void MD5::decode(const byte* input, bit32* output, size_t length) { + for (size_t i = 0, j = 0; j < length; ++i, j += 4) { + output[i] = ((bit32)input[j]) | (((bit32)input[j + 1]) << 8) | + (((bit32)input[j + 2]) << 16) | (((bit32)input[j + 3]) << 24); + } +} + + +/** + * @Convert digest to string value. + * + * @return the hex string of digest. + * + */ +string MD5::toStr() { + const byte* digest_ = getDigest(); + string str; + str.reserve(16 << 1); + for (size_t i = 0; i < 16; ++i) { + int t = digest_[i]; + int a = t / 16; + int b = t % 16; + str.append(1, HEX_NUMBERS[a]); + str.append(1, HEX_NUMBERS[b]); + } + return str; +} diff --git a/VrUtils/MD5/md5.h b/VrUtils/MD5/md5.h new file mode 100644 index 0000000..3b0ebb5 --- /dev/null +++ b/VrUtils/MD5/md5.h @@ -0,0 +1,138 @@ +/** + * @file md5.h + * @The header file of md5. + * @author Jiewei Wei + * @mail weijieweijerry@163.com + * @github https://github.com/JieweiWei + * @data Oct 19 2014 + * + */ + +#ifndef MD5_H +#define MD5_H + +/* Parameters of MD5. */ +#define s11 7 +#define s12 12 +#define s13 17 +#define s14 22 +#define s21 5 +#define s22 9 +#define s23 14 +#define s24 20 +#define s31 4 +#define s32 11 +#define s33 16 +#define s34 23 +#define s41 6 +#define s42 10 +#define s43 15 +#define s44 21 + +/** + * @Basic MD5 functions. + * + * @param there bit32. + * + * @return one bit32. + */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/** + * @Rotate Left. + * + * @param {num} the raw number. + * + * @param {n} rotate left n. + * + * @return the number after rotated left. + */ +#define ROTATELEFT(num, n) (((num) << (n)) | ((num) >> (32-(n)))) + +/** + * @Transformations for rounds 1, 2, 3, and 4. + */ +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + ac; \ + (a) = ROTATELEFT ((a), (s)); \ + (a) += (b); \ +} +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + ac; \ + (a) = ROTATELEFT ((a), (s)); \ + (a) += (b); \ +} +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + ac; \ + (a) = ROTATELEFT ((a), (s)); \ + (a) += (b); \ +} +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + ac; \ + (a) = ROTATELEFT ((a), (s)); \ + (a) += (b); \ +} + +#include +#include + +using std::string; + +/* Define of btye.*/ +typedef unsigned char byte; +/* Define of byte. */ +typedef unsigned int bit32; + +class MD5 { +public: + /* Construct a MD5 object with a string. */ + MD5(const string& message); + MD5(const byte* input, size_t len); + + /* Generate md5 digest. */ + const byte* getDigest(); + + /* Convert digest to string value */ + string toStr(); + +private: + /* Initialization the md5 object, processing another message block, + * and updating the context.*/ + void init(const byte* input, size_t len); + + /* MD5 basic transformation. Transforms state based on block. */ + void transform(const byte block[64]); + + /* Encodes input (usigned long) into output (byte). */ + void encode(const bit32* input, byte* output, size_t length); + + /* Decodes input (byte) into output (usigned long). */ + void decode(const byte* input, bit32* output, size_t length); + +private: + /* Flag for mark whether calculate finished. */ + bool finished; + + /* state (ABCD). */ + bit32 state[4]; + + /* number of bits, low-order word first. */ + bit32 count[2]; + + /* input buffer. */ + byte buffer[64]; + + /* message digest. */ + byte digest[16]; + + /* padding for calculate. */ + static const byte PADDING[64]; + + /* Hex numbers. */ + static const char HEX_NUMBERS[16]; +}; + +#endif // MD5_H diff --git a/VrUtils/Src/VrAngleUtils.cpp b/VrUtils/Src/VrAngleUtils.cpp new file mode 100644 index 0000000..75d8a8a --- /dev/null +++ b/VrUtils/Src/VrAngleUtils.cpp @@ -0,0 +1,16 @@ +#include "VrAngleUtils.h" +#include +#include + +#define PI 3.1415926 + +/// 从角度转换成弧度 +double CVrAngleRadianUtils::AngleToRadian(double angle){ + return angle * PI / 180.f; +} + + +/// 从弧度转换成角度 +double CVrAngleRadianUtils::RadianToAngle(double radian){ + return radian * 180.f / PI; +} \ No newline at end of file diff --git a/VrUtils/Src/VrCodeFormatUtils.cpp b/VrUtils/Src/VrCodeFormatUtils.cpp new file mode 100644 index 0000000..b64b69c --- /dev/null +++ b/VrUtils/Src/VrCodeFormatUtils.cpp @@ -0,0 +1,161 @@ +#include "VrCodeFormatUtils.h" +#ifdef _WIN32 +#include "windows.h" +#endif +//GB2312UTF-8ת +int CVrCodeFormatUtils::GB2312ToUtf8(const char* gb2312, char* utf8) +{ + int len = 0; +#ifdef _WIN32 + len = MultiByteToWideChar(CP_ACP, 0, gb2312, -1, NULL, 0); + wchar_t* wstr = new wchar_t[len + 1]; + memset(wstr, 0, len + 1); + MultiByteToWideChar(CP_ACP, 0, gb2312, -1, wstr, len); + len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL); + WideCharToMultiByte(CP_UTF8, 0, wstr, -1, utf8, len, NULL, NULL); + if (wstr) delete[] wstr; +#endif + return len; +} + +//жǷutf8 +bool CVrCodeFormatUtils::IsTextUTF8(const char* str, long length) +{ + unsigned char chr; + + int nBytes = 0; // UFT81-6ֽڱ,ASCIIһֽ + bool bAllAscii = true; // ȫASCII, ˵UTF-8 + + for (int i = 0; i < length; i++) + { + chr = *(str + i); + + if ((chr & 0x80) != 0) // жǷASCII,,˵пUTF-8, ASCII7λ,һֽڴ,λΪ0,o0xxxxxxx + { + bAllAscii = false; + } + + if (nBytes == 0) // ASCII,ӦǶֽڷ,ֽ + { + if (chr >= 0x80) + { + if (chr >= 0xFC && chr <= 0xFD) + nBytes = 6; + else if (chr >= 0xF8) + nBytes = 5; + else if (chr >= 0xF0) + nBytes = 4; + else if (chr >= 0xE0) + nBytes = 3; + else if (chr >= 0xC0) + nBytes = 2; + else + return false; + + nBytes--; + } + } + else // every char of ascii buffer looks like 10xxxxxx, except the first char + { + if ((chr & 0xC0) != 0x80) + { + return false; + } + nBytes--; + } + } + + if (nBytes > 0) // format error + { + return false; + } + + if (bAllAscii) // if all chars are ascii, the buffer is not utf-8 + { + return false; + } + + return true; +} + + +//UTF-8GB2312ת +int CVrCodeFormatUtils::Utf8ToGB2312(const char* utf8, char* gb2312) +{ + int len = 0; +#ifdef _WIN32 + len = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0); + wchar_t* wstr = new wchar_t[len + 1]; + memset(wstr, 0, len + 1); + MultiByteToWideChar(CP_UTF8, 0, utf8, -1, wstr, len); + len = WideCharToMultiByte(CP_ACP, 0, wstr, -1, NULL, 0, NULL, NULL); + WideCharToMultiByte(CP_ACP, 0, wstr, -1, gb2312, len, NULL, NULL); + if (wstr) delete[] wstr; +#endif + return len; +} + + +//GB2312Unicodeת +int CVrCodeFormatUtils::GB2312ToUnicode(const char* gb2312, char* unicode) +{ + int len = 0; +#ifdef _WIN32 + UINT nCodePage = 936; //GB2312 + len = MultiByteToWideChar(nCodePage, 0, gb2312, -1, NULL, 0); + wchar_t* wstr = new wchar_t[len + 1]; + memset(wstr, 0, len + 1); + MultiByteToWideChar(nCodePage, 0, gb2312, -1, wstr, len); + len = len * sizeof(wchar_t); + memcpy(unicode, wstr, len); + if (wstr) delete[] wstr; +#endif + return len; +} + + +//UnicodeGB2312ת +int CVrCodeFormatUtils::UnicodeToGB2312(const char* unicode, int size, char*gb2312) +{ + int len = 0; +#ifdef _WIN32 + UINT nCodePage = 936; //GB2312 + wchar_t* wstr = new wchar_t[size / 2 + 1]; + memcpy(wstr, unicode, size); + len = WideCharToMultiByte(nCodePage, 0, wstr, -1, NULL, 0, NULL, NULL); + WideCharToMultiByte(nCodePage, 0, wstr, -1, gb2312, len, NULL, NULL); + if (wstr) delete[] wstr; +#endif + return len; +} + + +//UTF-8Unicodeת +int CVrCodeFormatUtils::Utf8ToUnicode(const char* utf8, char*unicode) +{ + int len = 0; +#ifdef _WIN32 + MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0); + wchar_t* wstr = new wchar_t[len + 1]; + memset(wstr, 0, len + 1); + MultiByteToWideChar(CP_UTF8, 0, utf8, -1, wstr, len); + memcpy(unicode, wstr, len); + if (wstr) delete[] wstr; +#endif + return len; +} + + +//UnicodeUTF-8ת +int CVrCodeFormatUtils::UnicodeToUtf8(const char* unicode, int size, char* utf8) +{ + int len = 0; +#ifdef _WIN32 + wchar_t* wstr = new wchar_t[size / 2 + 1]; + memcpy(wstr, unicode, size); + len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL); + WideCharToMultiByte(CP_UTF8, 0, wstr, -1, utf8, len, NULL, NULL); + if (wstr) delete[] wstr; +#endif + return len; +} \ No newline at end of file diff --git a/VrUtils/Src/VrDateUtils.cpp b/VrUtils/Src/VrDateUtils.cpp new file mode 100644 index 0000000..cac93f8 --- /dev/null +++ b/VrUtils/Src/VrDateUtils.cpp @@ -0,0 +1,72 @@ +#include "VrDateUtils.h" + +/** +* 获取时间年月日时分秒 +*/ +std::string CVrDateUtils::GetNowTime() +{ + time_t tt = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); +#ifdef _WIN32 + struct tm ptminfo; + localtime_s(&ptminfo, &tt); +#else + struct tm ptminfo = *(localtime(&tt)); +#endif + //printf("current: %02d-%02d-%02d %02d:%02d:%02d\n", + // ptminfo.tm_year + 1900, ptminfo.tm_mon + 1, ptminfo.tm_mday, + // ptminfo.tm_hour, ptminfo.tm_min, ptminfo.tm_sec); + char date[60] = { 0 }; +#ifdef _WIN32 + sprintf_s(date, "%02d%02d%02d%02d%02d%02d", ptminfo.tm_year + 1900, \ + ptminfo.tm_mon + 1, ptminfo.tm_mday, ptminfo.tm_hour, ptminfo.tm_min, ptminfo.tm_sec); +#else + sprintf(date, "%02d%02d%02d%02d%02d%02d", ptminfo.tm_year + 1900, \ + ptminfo.tm_mon + 1, ptminfo.tm_mday, ptminfo.tm_hour, ptminfo.tm_min, ptminfo.tm_sec); +#endif + return std::string(date); +} + +/** +* 获取时间:年-月-日 时:分:秒 +*/ +std::string CVrDateUtils::GetStrNowTime(bool bYear) +{ + time_t tt = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); +#ifdef _WIN32 + struct tm ptminfo; + localtime_s(&ptminfo, &tt); +#else + struct tm ptminfo = *(localtime(&tt)); +#endif + char date[60] = { 0 }; +#ifdef _WIN32 + if (bYear) + { + sprintf_s(date, "%04d-%02d-%02d %02d:%02d:%02d", ptminfo.tm_year + 1900, \ + ptminfo.tm_mon + 1, ptminfo.tm_mday, ptminfo.tm_hour, ptminfo.tm_min, ptminfo.tm_sec); + } + else + { + sprintf_s(date, "%02d-%02d %02d:%02d:%02d", ptminfo.tm_mon + 1, ptminfo.tm_mday, ptminfo.tm_hour, ptminfo.tm_min, ptminfo.tm_sec); + } +#else + if (bYear) + { + sprintf(date, "%04d-%02d-%02d %02d:%02d:%02d", ptminfo.tm_year + 1900, \ + ptminfo.tm_mon + 1, ptminfo.tm_mday, ptminfo.tm_hour, ptminfo.tm_min, ptminfo.tm_sec); + } + else + { + sprintf(date, "%02d-%02d %02d:%02d:%02d", ptminfo.tm_mon + 1, ptminfo.tm_mday, ptminfo.tm_hour, ptminfo.tm_min, ptminfo.tm_sec); + } +#endif + return std::string(date); +} + +/** +* 获取微妙时间戳 +*/ +unsigned long long CVrDateUtils::GetTimestamp() +{ + return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); +} diff --git a/VrUtils/Src/VrFileUtils.cpp b/VrUtils/Src/VrFileUtils.cpp new file mode 100644 index 0000000..137fa49 --- /dev/null +++ b/VrUtils/Src/VrFileUtils.cpp @@ -0,0 +1,275 @@ +#include "VrFileUtils.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#include +#else +#include +#endif + +bool CVrFileUtils::IsFileExist(const char* pFileName) +{ + std::ifstream in(pFileName); + return in.good(); +} + +size_t CVrFileUtils::GetFileSize(const char* fileName) +{ + std::ifstream in(fileName); + in.seekg(0, std::ios::end); + size_t size = in.tellg(); + in.close(); + return (int)size; +} + +bool CVrFileUtils::WriteFileData(const char* pFileName, const char* pData, const size_t nLen, bool isAppend) +{ + if(!pFileName || !pData) + return false; + + bool bRet = false; + std::ofstream fStream; + std::ios_base::openmode nMode = std::ios::binary; + if(isAppend) + { + nMode = nMode | std::ios::app; + } + + fStream.open(pFileName, nMode); + + bRet = fStream.good(); + + if(bRet) + { + fStream.write(pData, nLen); + fStream.flush(); + } + fStream.close(); +#ifndef _WIN32 + sync(); +#endif + return bRet; +} + +bool CVrFileUtils::ReadFileData(const char* pFileName, char* pData, size_t* pLen, const int nOffset) +{ + if(!pFileName) + return false; + + int nFileSize = GetFileSize(pFileName); + if(nOffset >= nFileSize) + { + return false; + } + + if(nFileSize - nOffset < *pLen) + { + *pLen = nFileSize - nOffset; + } + + std::ifstream iStream(pFileName, std::ios::binary); + iStream.seekg(nOffset); + iStream.read(pData, *pLen); + iStream.close(); + return true; +} + +bool CVrFileUtils::GetFileList(std::string& pDirPath, std::string exd, std::vector& vetFiles) +{ +#ifdef _WIN32 + //ļ + intptr_t hFile = 0; + //ļϢ + struct _finddata_t fileinfo; + std::string pathName, exdName; + + if (0 != strcmp(exd.c_str(), "")) + { + exdName = "\\*." + exd; + } + else + { + exdName = "\\*"; + } + + if ((hFile = _findfirst(pathName.assign(pDirPath).append(exdName).c_str(), &fileinfo)) != -1) + { + do + { + //ļļ,֮ + //,б + if ((fileinfo.attrib & _A_SUBDIR)) + { + if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0) + { + std::string sFilePath = pathName.assign(pDirPath).append("\\").append(fileinfo.name); + GetFileList(sFilePath, exd, vetFiles); + } + + } + else + { + if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0) + vetFiles.push_back(pathName.assign(pDirPath).append("\\").append(fileinfo.name)); + } + } while (_findnext(hFile, &fileinfo) == 0); + _findclose(hFile); + } +#endif + return true; +} + +/// ȡĿ¼Ŀ¼ +bool CVrFileUtils::GetDirList(std::string& pDirPath, std::vector& vetFiles) +{ +#if 0 + // Ŀ¼ + DIR* dir = opendir(pDirPath.c_str()); + if (dir == nullptr) { + std::cerr << "Unable to open directory: " << pDirPath << std::endl; + return false; + } + + struct dirent* entry; + while ((entry = readdir(dir)) != nullptr) { + const std::string name = entry->d_name; + + // ų "." ".." + if (name != "." && name != "..") { + const std::string full_path = pDirPath + "/" + name; + struct stat info; + if (stat(full_path.c_str(), &info) == 0) { + if (S_ISDIR(info.st_mode)) { + subdirs.push_back(full_path); // Ŀ¼򱣴· + } + } + } + } + + closedir(dir); // رĿ¼ +#endif + return true; +} + +bool CVrFileUtils::ReNameFile(const char* oldName, const char* newName) +{ + if (!IsFileExist(oldName)) return false; + return 0 == rename(oldName, newName); +} + +bool CVrFileUtils::DeleteLocalFile(const char* sFileName) +{ + if (!IsFileExist(sFileName)) return false; + return 0 == remove(sFileName); +} + +/// Ŀ¼Ƿ +bool CVrFileUtils::IsDirExist(const char* szDirectory) +{ + if (nullptr == szDirectory) + { + return false; + } + + struct stat sStat; + memset(&sStat, 0, sizeof(struct stat)); + if (0 != stat(szDirectory, &sStat)) + return false; + return true; +} + + +bool _CreateDirs(const std::string& path) { + // Ŀ¼ǷѾ + struct stat info; + if (stat(path.c_str(), &info) == 0 && (info.st_mode & S_IFDIR)) + { + return true; + } + + // ݹ鴴Ŀ¼ +#ifdef _WIN32 + size_t pos = path.find_last_of('\\'); +#else + size_t pos = path.find_last_of('/'); +#endif // _WIN32 + + if (pos != std::string::npos) { + std::string parent_path = path.substr(0, pos); + if (!_CreateDirs(parent_path)) { + return false; + } + } + + // ǰĿ¼ +#ifdef _WIN32 + int nMkdirRet = mkdir(path.c_str()); +#else + int nMkdirRet = mkdir(path.c_str(), 0777); +#endif // __WIN32 + if (nMkdirRet == -1) { + perror("mkdir"); + return false; + } + return true; +} + + +bool _DeleteDirs(const std::string& path) +{ + // Ŀ¼ǷѾ + struct stat info; + if (stat(path.c_str(), &info) == 0 && (info.st_mode & S_IFDIR)) + { + std::cout << "Directory already exists: " << path << std::endl; + return true; + } + int nRet = rmdir(path.c_str()); + return 0 == nRet; +} + + +/// µļ +bool CVrFileUtils::CreatNewDir(const char* szPath) +{ + return _CreateDirs(szPath); +} + +/// ɾĿ¼ +bool CVrFileUtils::DeleteDir(const char* szDirPath) +{ + return _DeleteDirs(szDirPath); +} + +/// ȥ׺ȡļ +std::string CVrFileUtils::GetFileName(const std::string& filePath, bool bHasSuffix) +{ + // ȡһбܻбܵλ + size_t slashIndex = filePath.find_last_of("/\\"); + + // ȡļչ + std::string fileName = (slashIndex != std::string::npos) ? filePath.substr(slashIndex + 1) : filePath; + + if(!bHasSuffix) + { + // һλ + size_t dotIndex = fileName.find_last_of("."); + + // ȥչ + if (dotIndex != std::string::npos) { + fileName = fileName.substr(0, dotIndex); + } + } + + return fileName; +} + diff --git a/VrUtils/Src/VrLog.cpp b/VrUtils/Src/VrLog.cpp new file mode 100644 index 0000000..6abcfd0 --- /dev/null +++ b/VrUtils/Src/VrLog.cpp @@ -0,0 +1,379 @@ +#include "VrLog.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "VrFileUtils.h" + +#ifdef _WIN32 +#include +#include +#include +#define _WINSOCK_DEPRECATED_NO_WARNINGS +#include +#pragma comment(lib,"Ws2_32.lib") +#else +#include +#include +#include +#endif + + +////定义Log宏 +///******************************************************************************************************************/ + +#ifdef _WIN32 + #define PATH_SEP "\\" +#else + #define PATH_SEP "/" +#endif + +#define LOG_CONFIG_FILE "config.ini" +#define LOG_PRINT_FILE "AppLog.log" + +// 使用函数内静态变量实现真正的全局单例(跨静态库安全) +struct VrLogState { + log4cpp::PatternLayout* pLayout = nullptr; + log4cpp::RollingFileAppender* pRollFileAppender = nullptr; + std::atomic isInitialized{false}; + std::atomic isShutdown{false}; // 标记日志系统是否已关闭(程序退出时) + std::atomic isTimeEnabled{true}; + std::once_flag initFlag; + std::once_flag uninitFlag; +}; + +// 函数内静态变量,确保跨编译单元的唯一性 +static VrLogState& GetLogState() { + static VrLogState state; + return state; +} + +// 兼容旧代码的访问方式 +#define m_pLayout (GetLogState().pLayout) +#define m_prollfileAppender (GetLogState().pRollFileAppender) +#define g_isLogInitialized (GetLogState().isInitialized) +#define g_isLogShutdown (GetLogState().isShutdown) +#define g_isTimeEnabled (GetLogState().isTimeEnabled) + + +#ifdef STM32_UCOSIII +#define LOG_TIME do{ \ + OS_ERR err;\ + OS_TICK time = OSTimeGet(&err); \ + printf("[%10u]",time); \ + }while(0) +#else +#define LOG_TIME do { \ + std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); \ + std::time_t timestamp = std::chrono::system_clock::to_time_t(now); \ + std::tm local_time = *std::localtime(×tamp); \ + long long milliseconds = std::chrono::duration_cast(now.time_since_epoch()).count(); \ + printf("%d-%02d-%02d %02d:%02d:%02d.%03lld", local_time.tm_year + 1900 , local_time.tm_mon + 1, local_time.tm_mday, local_time.tm_hour, local_time.tm_min, local_time.tm_sec, milliseconds % 1000); \ + } while(0) +#endif + +#ifdef __ANDROID__ +#include +#define MY_LOG_VERBOSE(fmt, ...) __android_log_print(ANDROID_LOG_VERBOSE, "APPV", ##__VA_ARGS__) +#define MY_LOG_DEBUG(fmt, ...) __android_log_print(ANDROID_LOG_DEBUG, "APPD", ##__VA_ARGS__) +#define MY_LOG_INFO(fmt, ...) __android_log_print(ANDROID_LOG_INFO, "APPI", ##__VA_ARGS__) +#define MY_LOG_WARNING(fmt, ...) __android_log_print(ANDROID_LOG_WARN, "APPW", ##__VA_ARGS__) +#define MY_LOG_ERRO(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, "APPE", ##__VA_ARGS__) +#else + +// 辅助函数:截断文件路径,最多显示15个字符(12个字符 + "...") +static inline const char* TruncateFilePath(const char* filePath) { + static thread_local char buffer[20]; // 线程局部存储,避免多线程问题 + if (!filePath) return ""; + + size_t len = strlen(filePath); + if (len <= 15) { + // 如果长度不超过15,直接返回 + return filePath; + } else { + // 如果超过15个字符,取前13个字符 + "..." + strncpy(buffer, filePath, 13); + buffer[13] = '.'; + buffer[14] = '.'; + buffer[15] = '\0'; + return buffer; + } +} +#define MY_LOG_VERBOSE(filePath, nLine, fmt, ...) do { if(g_isTimeEnabled) LOG_TIME; printf(" V[%15s:%4u] " fmt"", TruncateFilePath(filePath), nLine ,##__VA_ARGS__); fflush(stdout); } while(0) +#define MY_LOG_DEBUG(filePath, nLine, fmt, ...) do { if(g_isTimeEnabled) LOG_TIME; printf(" D[%15s:%4u] " fmt"", TruncateFilePath(filePath), nLine ,##__VA_ARGS__); fflush(stdout); } while(0) +#define MY_LOG_INFO(filePath, nLine, fmt, ...) do { if(g_isTimeEnabled) LOG_TIME; printf(" I[%15s:%4u] " fmt"", TruncateFilePath(filePath), nLine ,##__VA_ARGS__); fflush(stdout); } while(0) +#define MY_LOG_WARNING(filePath, nLine, fmt, ...) do { if(g_isTimeEnabled) LOG_TIME; printf(" W[%15s:%4u] " fmt"", TruncateFilePath(filePath), nLine ,##__VA_ARGS__); fflush(stdout); } while(0) +#define MY_LOG_ERRO(filePath, nLine, fmt, ...) do { if(g_isTimeEnabled) LOG_TIME; printf(" E[%15s:%4u] " fmt"", TruncateFilePath(filePath), nLine ,##__VA_ARGS__); fflush(stdout); } while(0) +#endif // __ANROID__ +///******************************************************************************************************************/ + +// 动态获取日志路径(包含应用名称) +static std::string GetLogPath() +{ + std::string appName = "YJApp"; // 默认应用名称 + + // 尝试从进程路径获取应用名称 +#ifdef _WIN32 + // 获取用户文档目录 + + // 如果无法获取用户目录,则使用原来的方式 + char exePath[MAX_PATH]; + if (GetModuleFileNameA(NULL, exePath, MAX_PATH) != 0) { + std::string fullPath(exePath); + size_t pos = fullPath.find_last_of("\\"); + if (pos != std::string::npos) { + std::string fileName = fullPath.substr(pos + 1); + size_t dotPos = fileName.find_last_of("."); + if (dotPos != std::string::npos) { + appName = fileName.substr(0, dotPos); + } + } + } + + char userPath[MAX_PATH]; + if (SHGetFolderPathA(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, userPath) == S_OK) { + std::string userDir(userPath); + return userDir + "\\" + appName + "\\Log"; + } + + return ".\\" + appName + "\\Log"; +#else + // Linux/Android平台 + char exePath[1024]; + ssize_t len = readlink("/proc/self/exe", exePath, sizeof(exePath) - 1); + if (len != -1) { + exePath[len] = '\0'; + std::string fullPath(exePath); + size_t pos = fullPath.find_last_of("/"); + if (pos != std::string::npos) { + appName = fullPath.substr(pos + 1); + } + } + +#ifdef __ANDROID__ + return "/sdcard/" + appName + "/Log"; +#else + return appName + "/Log"; +#endif +#endif +} + + +// 自动初始化和清理类,确保程序启动时初始化日志,退出时清理日志资源 +class VrLogAutoCleaner { +public: + VrLogAutoCleaner() { + VrLogUtils::InitLog(); + } + + ~VrLogAutoCleaner() { + VrLogUtils::UninitLog(); + } +}; + +// 静态实例,程序启动时自动初始化日志,程序退出时自动清理日志 +static VrLogAutoCleaner g_logAutoCleaner; + +// 实际执行初始化的内部函数 +static void DoInitLog() +{ +#ifdef _WIN32 + // 设置控制台代码页为 UTF-8,解决中文乱码 + SetConsoleOutputCP(CP_UTF8); + SetConsoleCP(CP_UTF8); + // 设置 C 运行时的 locale 为 UTF-8 + setlocale(LC_ALL, ".UTF-8"); +#endif + + // if file exist then load file config + std::string logPath = GetLogPath(); + std::string logConfig = logPath + PATH_SEP + LOG_CONFIG_FILE; + if (CVrFileUtils::IsFileExist(logConfig.c_str())) + { + // load profile + try + { + log4cpp::PropertyConfigurator::configure(logConfig); + } + catch (log4cpp::ConfigureFailure& f) + { + std::cout << "Load Log Profile Error" << f.what() << std::endl; + } + } + else + { + if (!CVrFileUtils::IsDirExist(logPath.c_str())) + { + CVrFileUtils::CreatNewDir(logPath.c_str()); + } + + // create file appender + VrLogState& state = GetLogState(); + if (nullptr == state.pLayout) + { + state.pLayout = new log4cpp::PatternLayout; + // 设置布局格式,可以控制是否自动换行 + // %d{%Y-%m-%d %H:%M:%S.%l} [%p] %c - %m%n + // 其中 %n 表示换行符,如果想要控制换行可以去掉或修改这个 + state.pLayout->setConversionPattern("%d{%Y-%m-%d %H:%M:%S.%l} [%p] %m"); + } + if (nullptr == state.pRollFileAppender) + { + state.pRollFileAppender = new log4cpp::RollingFileAppender("AppLogAppender", logPath + PATH_SEP + LOG_PRINT_FILE, + 1024 * 1024, // 单个文件大小1M + 10); // 10个文件 + + state.pRollFileAppender->setLayout(state.pLayout); + + // add appender to category + log4cpp::Category& root = log4cpp::Category::getRoot(); + root.addAppender(state.pRollFileAppender); + + // set priority + root.setPriority(log4cpp::Priority::DEBUG); + } + } + + g_isLogInitialized = true; +} + +/// 初始化log +void VrLogUtils::InitLog() +{ + // 如果已经关闭,不再初始化 + if (g_isLogShutdown) { + return; + } + + // 使用 call_once 确保只执行一次初始化(跨静态库安全) + std::call_once(GetLogState().initFlag, DoInitLog); +} + +// 实际执行清理的内部函数 +static void DoUninitLog() +{ + // 先设置标志,防止其他线程继续调用日志 + g_isLogInitialized = false; + g_isLogShutdown = true; // 标记已关闭,防止 EchoLog 重新初始化 + + // 注意:不调用 log4cpp::Category::shutdown() + // 原因:静态对象析构顺序不确定,log4cpp 内部的静态对象可能在 + // VrLogAutoCleaner 之前就已经被析构,此时调用 shutdown() 会崩溃。 + // 程序退出时,操作系统会自动回收所有内存,不需要手动清理。 + + // 只置空指针,不删除对象(对象由 log4cpp 内部管理) + VrLogState& state = GetLogState(); + state.pLayout = nullptr; + state.pRollFileAppender = nullptr; +} + +/// 关闭log +void VrLogUtils::UninitLog() +{ + // 使用 call_once 确保只执行一次清理(跨静态库安全) + std::call_once(GetLogState().uninitFlag, DoUninitLog); +} + + +/// 开启/关闭时间戳 +void VrLogUtils::EnableTime(bool bEnable) +{ + if (nullptr != m_pLayout) + { + m_pLayout->setConversionPattern(bEnable ? "%d{%Y-%m-%d %H:%M:%S.%l} [%p] %m" : "%p %m"); + } + g_isTimeEnabled = false; +} + +/// 输出log +void VrLogUtils::EchoLog(VrLogLevel eLogLevel, const char* sFilePath, const int nLine, const char* sLogGroup, const char* sFormat, ...) +{ + // 如果日志系统已关闭(程序退出),直接返回,不要重新初始化 + if (g_isLogShutdown) { + return; + } + + // 由于VrLogAutoCleaner会在程序启动时自动初始化,这里只做保险检查 + if (!g_isLogInitialized) { + InitLog(); + } + + // load log info + va_list args; + va_start(args, sFormat); + char szLogInfo[1024] = { 0 }; +#ifdef _WIN32 + vsprintf_s(szLogInfo, sFormat, args); +#else + vsprintf(szLogInfo, sFormat, args); +#endif + va_end(args); + log4cpp::Category& root = log4cpp::Category::getRoot(); + + switch (eLogLevel) + { + case KELOGLEVEL_None: + break; + case KELOGLEVEL_Verbose: + MY_LOG_VERBOSE(sFilePath, nLine, "%s", szLogInfo); + if (nullptr != m_pLayout && nullptr != m_prollfileAppender) + { + LOG4CPP_DEBUG(root, szLogInfo); + } + break; + case KELOGLEVEL_Debug: + MY_LOG_DEBUG(sFilePath, nLine, "%s", szLogInfo); + if (nullptr != m_pLayout && nullptr != m_prollfileAppender) + { + LOG4CPP_DEBUG(root, szLogInfo); + } + break; + case KELOGLEVEL_Info: + MY_LOG_INFO(sFilePath, nLine, "%s", szLogInfo); + if (nullptr != m_pLayout && nullptr != m_prollfileAppender) + { + LOG4CPP_INFO(root, szLogInfo); + } + break; + case KELOGLEVEL_Warning: + MY_LOG_WARNING(sFilePath, nLine, "%s", szLogInfo); + if (nullptr != m_pLayout && nullptr != m_prollfileAppender) + { + LOG4CPP_WARN(root, szLogInfo); + } + break; + case KELOGLEVEL_Error: + MY_LOG_ERRO(sFilePath, nLine, "%s", szLogInfo); + if (nullptr != m_pLayout && nullptr != m_prollfileAppender) + { + LOG4CPP_ERROR(root, szLogInfo); + } + break; + default: + break; + } +} + +/// 修改log level default info +void VrLogUtils::AlterLogLevel(VrLogLevel eLogLevel) +{ + +} + +/// 修改log输出形式 默认都输出 +void VrLogUtils::AlterLogType(VrLogType eLogType) +{ + +} diff --git a/VrUtils/Src/VrMD5Utils.cpp b/VrUtils/Src/VrMD5Utils.cpp new file mode 100644 index 0000000..27eec2d --- /dev/null +++ b/VrUtils/Src/VrMD5Utils.cpp @@ -0,0 +1,10 @@ +#include "VrMD5Utils.h" +#include +#include + +std::string VrMD5Utils::GetMD5(const char* pData, const int nDataLen) +{ + std::string md5 = MD5((unsigned char*)pData, nDataLen).toStr(); + std::transform(md5.begin(), md5.end(), md5.begin(), ::toupper); + return md5; +} \ No newline at end of file diff --git a/VrUtils/Src/VrNTPUtils.cpp b/VrUtils/Src/VrNTPUtils.cpp new file mode 100644 index 0000000..1bf9bf2 --- /dev/null +++ b/VrUtils/Src/VrNTPUtils.cpp @@ -0,0 +1,216 @@ +#include "VrNTPUtils.h" + +#ifdef _WIN32 +#define _WINSOCK_DEPRECATED_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#include +#include +#pragma comment(lib,"Ws2_32.lib") +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SOCKET int +#define INVALID_SOCKET -1 +#define SOCKET_ERROR -1 +#endif +#include + +struct NTP_Packet +{ + int Control_Word; + int root_delay; + int root_dispersion; + int reference_identifier; + long long reference_timestamp; + long long originate_timestamp; + long long receive_timestamp; + int transmit_timestamp_seconds; + int transmit_timestamp_fractions; +}; + +CrNTPUtils::CrNTPUtils() + : m_bValidIP(false) + , m_nPort(123) +{ +} + +CrNTPUtils::~CrNTPUtils() +{ +} + + +/// ʼNTPClient +bool CrNTPUtils::InitNTPClient(char* sIP, int nPort) +{ + bool bRet = true; + +#ifdef _WIN32 + WORD wVersionRequested; + WSADATA wsaData; + + // ʼ汾 + wVersionRequested = MAKEWORD(1, 1); + if (0 != WSAStartup(wVersionRequested, &wsaData)) + { + WSACleanup(); + return false; + } + if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) + { + WSACleanup(); + return false; + } +#endif + if (nullptr != sIP) + { + m_bValidIP = true; +#ifdef _WIN32 + strcpy_s(m_sIP, sIP); +#else + strcpy(m_sIP, sIP); +#endif // _WIN32 + + } + return true; +} + + +/// ˳NTPClient +bool CrNTPUtils::ExitNTPClient() +{ + bool bRet = true; + + + return bRet; +} + +///ȡʱ +bool CrNTPUtils::GetSystemTime(SYSTEMTIME& newtime) +{ + // IPй½ʱַͬ޸ + SOCKET soc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + struct sockaddr_in addrSrv; + + // time.ustc.edu.cn 202.38.64.7 + // ntp.aliyun.com 203.107.6.88 +#ifdef _WIN32 + if (!m_bValidIP) + { + inet_pton(AF_INET, "203.107.6.88", &addrSrv);//time.ustc.edu.cn"); + } + else + { + inet_pton(AF_INET, m_sIP, &addrSrv); + } +#else + if (!m_bValidIP) + { + addrSrv.sin_addr.s_addr = inet_addr("203.107.6.88");//time.ustc.edu.cn"); + } + else + { + addrSrv.sin_addr.s_addr = inet_addr(m_sIP); + } +#endif // _WIN32 + + + addrSrv.sin_family = AF_INET; + addrSrv.sin_port = htons(m_nPort); + + NTP_Packet NTP_Send, NTP_Recv; + NTP_Send.Control_Word = htonl(0x1B000000); + NTP_Send.root_delay = 0; + NTP_Send.root_dispersion = 0; + NTP_Send.reference_identifier = 0; + NTP_Send.reference_timestamp = 0; + NTP_Send.originate_timestamp = 0; + NTP_Send.receive_timestamp = 0; + NTP_Send.transmit_timestamp_seconds = 0; + NTP_Send.transmit_timestamp_fractions = 0; + + if (SOCKET_ERROR == sendto(soc, (const char*)&NTP_Send, sizeof(NTP_Send), + 0, (struct sockaddr*)&addrSrv, sizeof(addrSrv))) + { +#ifdef _WIN32 + closesocket(soc); +#else + close(soc); +#endif // _WIN32 + + return false; + } + socklen_t sockaddr_Size = sizeof(addrSrv); + if (SOCKET_ERROR == recvfrom(soc, (char*)&NTP_Recv, sizeof(NTP_Recv), 0, (struct sockaddr*)&addrSrv, &sockaddr_Size)) + { +#ifdef _WIN32 + closesocket(soc); +#else + close(soc); +#endif // _WIN32 + return false; + } +#ifdef _WIN32 + closesocket(soc); + WSACleanup(); +#else + close(soc); +#endif + + float Splitseconds; + struct tm* lpLocalTime = nullptr; + time_t ntp_time; + + // ȡʱʱ + ntp_time = ntohl(NTP_Recv.transmit_timestamp_seconds) - 2208988800; +#ifdef _WIN32 + localtime_s(lpLocalTime , &ntp_time); +#else + lpLocalTime = localtime(&ntp_time); +#endif + + if (lpLocalTime == nullptr) + { + return false; + } + + // ȡµʱ + newtime.wYear = lpLocalTime->tm_year + 1900; + newtime.wMonth = lpLocalTime->tm_mon + 1; + newtime.wDayOfWeek = lpLocalTime->tm_wday; + newtime.wDay = lpLocalTime->tm_mday; + newtime.wHour = lpLocalTime->tm_hour; + newtime.wMinute = lpLocalTime->tm_min; + newtime.wSecond = lpLocalTime->tm_sec; + + // ʱ侫 + Splitseconds = (float)ntohl(NTP_Recv.transmit_timestamp_fractions); + Splitseconds = (float)0.000000000200 * Splitseconds; + Splitseconds = (float)1000.0 * Splitseconds; + newtime.wMilliseconds = (unsigned short)Splitseconds; + return true; +} + +/************************************************************************/ +/* ˵:Զʱͬ +/* ˵: +/* ֵ:ɹTRUEʧܷFALSE +/************************************************************************/ +bool CrNTPUtils::_UpdateDate() +{ + SYSTEMTIME newtime; + bool bRet = GetSystemTime(newtime); + // ޸ıϵͳʱ +#ifdef _WIN32 + SetLocalTime(&newtime); +#endif + return true; +} \ No newline at end of file diff --git a/VrUtils/Src/VrNetUtils.cpp b/VrUtils/Src/VrNetUtils.cpp new file mode 100644 index 0000000..4b95208 --- /dev/null +++ b/VrUtils/Src/VrNetUtils.cpp @@ -0,0 +1,812 @@ +#include "VrNetUtils.h" +#include "VrLog.h" + +#ifdef _WIN32 +#define _WINSOCK_DEPRECATED_NO_WARNINGS +#include +#include +#include +#include +#include +#include +#include +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "IPHlpApi.lib") +#pragma comment(lib, "Advapi32.lib") +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +//#include +#include +#include +#include +#include +#endif +#include +#ifdef _WIN32 +enum +{ + SIOCSIFADDR = 1, + SIOCGIFADDR, + SIOCSIFNETMASK, + SIOCGIFNETMASK, + SIOCGIFHWADDR, +}; +#endif + +#define PJ_MAX_HOSTNAME (128) + +#define RUN_SUCCESS 0 +#define RUN_FAIL -1 + +std::mutex csDevice; + +std::ostream& operator<<(std::ostream& os, const SVrCardInfo& sAction) +{ + os << "cardInfo"; + os << " index:" << sAction.nAdapterIndex << " name:" << sAction.szNetCardName; + char ip[IPV4_CHAR_LENGTH]; + CVrNetUtils::IPByte2String(sAction.byLocalIP, ip); + os << " ip:" << ip; +// os << "mac:" << sAction.byMacAddress[0] << ":" << sAction.byMacAddress[1] << sAction.byMacAddress[3] << ":" +// << sAction.byMacAddress[4] << ":" << sAction.byMacAddress[4] << sAction.byMacAddress[5]; + CVrNetUtils::IPByte2String(sAction.bySubMask, ip); + os << " mask:" << ip; + + CVrNetUtils::IPByte2String(sAction.byGetWay, ip); + os << " getway:" << ip; + + CVrNetUtils::IPByte2String(sAction.byBroadCastIP, ip); + os << " broadIp:" << ip; + return os; +} + +static bool _GetAddr(char *addr, int flag, const char *devName) +{ +#ifdef _WIN32 + char ip[IPV4_CHAR_LENGTH] = "192.168.1.173"; + memcpy(addr, ip, IPV4_CHAR_LENGTH); + return false; +#else + + int sockfd = 0; + struct sockaddr_in *sin; + struct ifreq ifr; + + if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) + { + LOG_ERRO("socket error! [%d]\n", sockfd); + return false; + } + + memset(&ifr, 0, sizeof(ifr)); + sprintf(ifr.ifr_name, "%s", devName); + + LOG_DEBUG("dev %s \n", ifr.ifr_name); + + if (ioctl(sockfd, flag, &ifr) < 0) + { + LOG_ERRO("ioctl error! [%d]\n", sockfd); + close(sockfd); + return false; + } + close(sockfd); + + if (SIOCGIFHWADDR == flag) + { + memcpy((void *)addr, (const void *)&ifr.ifr_ifru.ifru_hwaddr.sa_data, 6); + sprintf((char *)addr, "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); + } + else + { + sin = (struct sockaddr_in *)&ifr.ifr_addr; + // ��ӡ sin->sin_addr.s_addr �Ա���� + //sprintf((char *)addr, "%s", inet_ntoa(sin->sin_addr)); + unsigned long ipInfo = sin->sin_addr.s_addr; + sprintf((char*)addr, "%ld.%ld.%ld.%ld", ipInfo & 0xFF, (ipInfo >> 8) & 0xFF, (ipInfo >> 16) & 0xFF, (ipInfo >> 24) & 0xFF); + } + return true; + +#endif // _WIN32 + +} + +static bool _SetAddr(const char ip[IPV4_CHAR_LENGTH], int flag, const char* devName) +{ +#ifndef _WIN32 + std::lock_guard oLock(csDevice); + struct ifreq ifr; + struct sockaddr_in sin; + int sockfd; + + if (!CVrNetUtils::IsValidIP(ip)) + { + LOG_WARNING("Set %d %s invalied ", flag, ip); + return false; + } + + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd <= 0) + { + LOG_ERRO("Could not get socket %s fd = %d [%d - %s]\n", devName, sockfd, errno, strerror(errno)); + return false; + } + + snprintf(ifr.ifr_name, (sizeof(ifr.ifr_name) - 1), "%s", devName); + + /* Read interface flags */ + if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) + { + LOG_ERRO("%s ifdown: shutdown ", ifr.ifr_name); + close(sockfd); + return false; + } + + memset(&sin, 0, sizeof(struct sockaddr)); + sin.sin_family = AF_INET; + + inet_aton((const char*)ip, (in_addr*)(&sin.sin_addr.s_addr)); + memcpy(&ifr.ifr_addr, &sin, sizeof(struct sockaddr)); + if (ioctl(sockfd, flag, &ifr) < 0) + { + LOG_ERRO("Cannot set IP address. "); + perror(ifr.ifr_name); + close(sockfd); + return false; + } + close(sockfd); +#endif + return true; +} + +bool CVrNetUtils::SetAddrMaskGateWay(const char *ifname, const char *Ipaddr, const char *mask, const char *gateway) +{ +#ifndef _WIN32 + + int fd; + int rc; + struct ifreq ifr; + struct sockaddr_in *sin; + struct rtentry rt; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) + { + perror("socket error"); + return false; + } + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, ifname); + sin = (struct sockaddr_in*)&ifr.ifr_addr; + sin->sin_family = AF_INET; + //IP��ַ + if (inet_aton(Ipaddr, &(sin->sin_addr)) < 0) + { + LOG_ERRO("inet_aton error"); + return false; + } + + + if (ioctl(fd, SIOCSIFADDR, &ifr) < 0) + { + LOG_ERRO("ioctl SIOCSIFADDR error"); + return false; + } + //�������� + if (inet_aton(mask, &(sin->sin_addr)) < 0) + { + LOG_ERRO("inet_pton error"); + return false; + } + if (ioctl(fd, SIOCSIFNETMASK, &ifr) < 0) + { + LOG_ERRO("ioctl"); + return false; + } + //���� + memset(&rt, 0, sizeof(struct rtentry)); + memset(sin, 0, sizeof(struct sockaddr_in)); + sin->sin_family = AF_INET; + sin->sin_port = 0; + if (inet_aton(gateway, &sin->sin_addr) < 0) + { + LOG_WARNING("inet_aton error\n"); + } + +#if 0 + memcpy(&rt.rt_gateway, sin, sizeof(struct sockaddr_in)); + ((struct sockaddr_in *)&rt.rt_dst)->sin_family = AF_INET; + ((struct sockaddr_in *)&rt.rt_genmask)->sin_family = AF_INET; + ((struct sockaddr_in *)&rt.rt_dst)->sin_addr.s_addr = htonl(INADDR_ANY); // Ĭ��·�� + ((struct sockaddr_in *)&rt.rt_genmask)->sin_addr.s_addr = htonl(0); // ����Ϊ 0.0.0.0 + + rt.rt_flags = RTF_GATEWAY; + if (ioctl(fd, SIOCADDRT, &rt) < 0) + { + LOG_ERRO("ioctl(SIOCADDRT) error in set_default_route\n"); + close(fd); + return false; + } +#endif + + close(fd); +#endif // !_WIN32 + return true; + +} + +// @brief +// Is Virtual Net Card +static bool _IsVirtualNetDevice(char* lpszAdapterName) +{ + bool bRet = false; +#ifdef _WIN32 + HKEY hSubKey = NULL; + HKEY hLocalKey = NULL; + do + { + if (nullptr == lpszAdapterName || 0 == strlen(lpszAdapterName)) + { + break; + } + + char szKeyName[260] = "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"; + if (ERROR_SUCCESS != RegOpenKeyExA(HKEY_LOCAL_MACHINE, szKeyName, 0, KEY_READ, &hSubKey)) + { + break; + } + + char szDataBuf[260] = { 0 }; + sprintf_s(szDataBuf, "%s\\Connection", lpszAdapterName);//��ʽ���ַ��� + if (ERROR_SUCCESS != RegOpenKeyExA(hSubKey, szDataBuf, 0, KEY_READ, &hLocalKey)) + { + break; + } + + //����ע����е�ֵ + DWORD dwType = REG_SZ; char szData[250]; DWORD dwSize = 250; + if (ERROR_SUCCESS != RegQueryValueExA(hLocalKey, "PnPInstanceId", 0, &dwType, (BYTE*)(szData), &dwSize)) + { + break; + } + + bRet = (nullptr == strstr(szData, "PCI") && nullptr == strstr(szData, "USB")); + } while (false); + if (nullptr != hSubKey) + { + RegCloseKey(hSubKey); + } + if (nullptr != hLocalKey) + { + RegCloseKey(hLocalKey); + } +#else + +#endif + return bRet; +} + +std::string _GetCardBroadIP(const std::string localIP, const std::string locakMask) +{ + unsigned int arrLocalIP[4]; + unsigned int arrlocakMask[4]; + unsigned int arrlocalBroadcast[4]; + +#ifndef _WIN32 + sscanf(localIP.c_str(), "%u.%u.%u.%u", &arrLocalIP[0], &arrLocalIP[1], &arrLocalIP[2], &arrLocalIP[3]); + sscanf(locakMask.c_str(), "%u.%u.%u.%u", &arrlocakMask[0], &arrlocakMask[1], &arrlocakMask[2], &arrlocakMask[3]); +#else + sscanf_s(localIP.c_str(), "%u.%u.%u.%u", &arrLocalIP[0], &arrLocalIP[1], &arrLocalIP[2], &arrLocalIP[3]); + sscanf_s(locakMask.c_str(), "%u.%u.%u.%u", &arrlocakMask[0], &arrlocakMask[1], &arrlocakMask[2], &arrlocakMask[3]); +#endif // !_WIN32 + + arrlocalBroadcast[0] = (arrlocakMask[0] & arrLocalIP[0] & 0xFF) | (~arrlocakMask[0] & 0xFF); + arrlocalBroadcast[1] = (arrlocakMask[1] & arrLocalIP[1] & 0xFF) | (~arrlocakMask[1] & 0xFF); + arrlocalBroadcast[2] = (arrlocakMask[2] & arrLocalIP[2] & 0xFF) | (~arrlocakMask[2] & 0xFF); + arrlocalBroadcast[3] = (arrlocakMask[3] & arrLocalIP[3] & 0xFF) | (~arrlocakMask[3] & 0xFF); + + std::stringstream ss; + ss << arrlocalBroadcast[0] << "."; + ss << arrlocalBroadcast[1] << "."; + ss << arrlocalBroadcast[2] << "."; + ss << arrlocalBroadcast[3]; + return ss.str(); +} + +/// ��ȡ�����豸 +bool CVrNetUtils::QueryAllNetworkInfo(std::list& lstNetCardInfo) +{ +#ifdef _WIN32 + unsigned long lSize = 0; + GetAdaptersInfo(nullptr, &lSize); + + if (lSize > 0) + { + PIP_ADAPTER_INFO pAdapterInfo = (PIP_ADAPTER_INFO)new unsigned char[lSize]; + + GetAdaptersInfo(pAdapterInfo, &lSize); + PIP_ADAPTER_INFO pCurAdapter = pAdapterInfo; + while (nullptr != pCurAdapter) + { + IP_ADDR_STRING* pCurAddress = &pCurAdapter->IpAddressList; + IP_ADDR_STRING* pCurGetWay = &pCurAdapter->GatewayList; + while (nullptr != pCurAddress) + { + // Filter? + // is Virtual Card + if (pCurAdapter->AdapterName == nullptr || _IsVirtualNetDevice(pCurAdapter->AdapterName)) + { + pCurAddress = pCurAddress->Next; + continue; + } + + // + if (0 == strcmp("0.0.0.0", pCurAddress->IpAddress.String)) + { + pCurAddress = pCurAddress->Next; + continue; + } + + // No Support UnEqu Length 6 MAC Address + if (pCurAdapter->AddressLength != 6) + { + pCurAddress = pCurAddress->Next; + continue; + } + + // Add To list + //lstNetCardInfo.push_back(pCurAddress->IpAddress.String); + //SVrCardInfo config; + //config.ip = pCurAddress->IpAddress.String; + //config.mask = pCurAddress->IpMask.String; + //config.borad = _GetCardBroadIP(pCurAddress->IpAddress.String, pCurAddress->IpMask.String); + // + SVrCardInfo sNetCardInfo; + sNetCardInfo.nAdapterIndex = pCurAdapter->Index; + if (pCurGetWay && 0 != strlen(pCurGetWay->IpAddress.String)) + { + IPString2Byte(pCurGetWay->IpAddress.String, sNetCardInfo.byGetWay); + } + IPString2Byte(pCurAddress->IpAddress.String, sNetCardInfo.byLocalIP); + IPString2Byte(pCurAddress->IpMask.String, sNetCardInfo.bySubMask); +#ifdef _WIN32 + strcpy_s(sNetCardInfo.szNetCardName, pCurAdapter->Description); +#else + strcpy(sNetCardInfo.szNetCardName, pCurAdapter->Description); +#endif // _WIN32 + + memcpy(sNetCardInfo.byMacAddress, pCurAdapter->Address, pCurAdapter->AddressLength); + + // ����㲥��ַ + CalcBroadCastIP(sNetCardInfo.byLocalIP, sNetCardInfo.bySubMask, sNetCardInfo.byBroadCastIP); + + lstNetCardInfo.push_back(sNetCardInfo); + pCurAddress = pCurAddress->Next; + if (pCurGetWay) + { + pCurGetWay = pCurGetWay->Next; + } + } + pCurAdapter = pCurAdapter->Next; + } + + delete pAdapterInfo; + } +#else + int fd; + int interfaceNum = 0; + struct ifreq buf[16]; + struct ifconf ifc; + struct ifreq ifrcopy; + char ip[32] = { 0 }; + char broadAddr[32] = { 0 }; + char subnetMask[32] = { 0 }; + + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + { + close(fd); + return -1; + } + + ifc.ifc_len = sizeof(buf); + ifc.ifc_buf = (caddr_t)buf; + if (!ioctl(fd, SIOCGIFCONF, (char *)&ifc)) + { + interfaceNum = ifc.ifc_len / sizeof(struct ifreq); + while (interfaceNum-- > 0) + { + SVrCardInfo sNetCardInfo; + strcpy(sNetCardInfo.szNetCardName, buf[interfaceNum].ifr_name); + + //ignore the interface that not up or not runing + ifrcopy = buf[interfaceNum]; + if (ioctl(fd, SIOCGIFFLAGS, &ifrcopy)) + { + close(fd); + return false; + } + + // index + sNetCardInfo.nAdapterIndex = if_nametoindex(sNetCardInfo.szNetCardName); + + //get the mac of this interface + if (!ioctl(fd, SIOCGIFHWADDR, (char *)(&buf[interfaceNum]))) + { + memcpy(sNetCardInfo.byMacAddress, buf[interfaceNum].ifr_hwaddr.sa_data, 6); + } + else + { + close(fd); + return false; + } + + //get the IP of this interface + if (!ioctl(fd, SIOCGIFADDR, (char *)&buf[interfaceNum])) + { + snprintf(ip, sizeof(ip), "%s", (char *)inet_ntoa(((struct sockaddr_in *)&(buf[interfaceNum].ifr_addr))->sin_addr)); + CVrNetUtils::IPString2Byte(ip, sNetCardInfo.byLocalIP); + } + else + { + close(fd); + return false; + } + + //get the subnet mask of this interface + if (!ioctl(fd, SIOCGIFNETMASK, &buf[interfaceNum])) + { + snprintf(subnetMask, sizeof(subnetMask), "%s", (char *)inet_ntoa(((struct sockaddr_in *)&(buf[interfaceNum].ifr_netmask))->sin_addr)); + CVrNetUtils::IPString2Byte(subnetMask, sNetCardInfo.bySubMask); + } + else + { + close(fd); + return false; + } + + lstNetCardInfo.push_back(sNetCardInfo); + } + } + else + { + close(fd); + return false; + } + + close(fd); +#endif + return lstNetCardInfo.size() > 0; +} + +/// @brief +/// IP�Ƿ���� +/// @param szIPv4[in] IP��ַ +/// @return IP�����򷵻�true +bool CVrNetUtils::IsValidIP(const char szIPv4[IPV4_CHAR_LENGTH]) +{ + if (szIPv4 == nullptr) { + return false; + } + + char tmp[IPV4_CHAR_LENGTH]; + // 使用 strncpy 安全复制字符串,并确保 null 结尾 + strncpy(tmp, szIPv4, IPV4_CHAR_LENGTH - 1); + tmp[IPV4_CHAR_LENGTH - 1] = '\0'; + + char *buf; + + const char * delim = "."; //分隔符字符串(点号) +#ifdef _WIN32 + char* p = strtok_s((char *)tmp, delim, &buf); //第一次调用strtok +#else + char* p = strtok((char *)tmp, delim); //第一次调用strtok +#endif + int index = 0; + while (p != NULL) + { //如果返回值不为NULL时继续循环 + // 验证每个部分是否为有效数字且在0-255范围内 + int num = atoi(p); + if (num < 0 || num > 255) { + return false; + } + + index++; +#ifdef _WIN32 + p = strtok_s(NULL, delim, &buf); //后续调用strtok来分解剩下的字符串 +#else + p = strtok(NULL, delim); //后续调用strtok来分解剩下的字符串 +#endif // _WIN32 + + } + if (index == 4) + { + return true; + } + return false; +} + +bool CVrNetUtils::SetIPv4Address(const char ip[IPV4_CHAR_LENGTH], const char *devName) +{ + return _SetAddr(ip, SIOCSIFADDR, devName); +} + +/// @brief +/// ��ȡIP��ַ +bool CVrNetUtils::GetIPv4Address(char ip[IPV4_CHAR_LENGTH], const char *devName) +{ + return _GetAddr(ip, SIOCGIFADDR, devName) && IsValidIP(ip); +} + +bool CVrNetUtils::SetIPv4NetMask(const char ip[IPV4_CHAR_LENGTH], const char *devName) +{ + return _SetAddr(ip, SIOCSIFNETMASK, devName); +} + +/// @brief +/// ��ȡ�������� +bool CVrNetUtils::GetIPv4NetMask(char ip[IPV4_CHAR_LENGTH], const char *devName) +{ + return _GetAddr(ip, SIOCGIFNETMASK, devName); +} + +/// ��ȡ�㲥��ַ +bool CVrNetUtils::GetIPv4BroadIP(char ip[IPV4_CHAR_LENGTH], const char *devName) +{ + char devip[IPV4_CHAR_LENGTH]; + char devmask[IPV4_CHAR_LENGTH]; + + unsigned char deviparr[4]; + unsigned char devmaskarr[4]; + unsigned char devbroadarr[4]; + + bool bRet = GetIPv4Address(devip, devName); + if (bRet) + { + bRet = GetIPv4NetMask(devmask, devName); + } + + if (bRet) + { + IPString2Byte(devip, deviparr); + IPString2Byte(devmask, devmaskarr); + CalcBroadCastIP(deviparr, devmaskarr, devbroadarr); + IPByte2String(devbroadarr, ip); + } + return bRet; +} + +/// @brief +/// ��ȡ������ַ +bool CVrNetUtils::GetLocalMAC(char byMac[MAC_CHAR_LENGTH], const char *devName) +{ + return _GetAddr((char*)byMac, SIOCGIFHWADDR, devName); +} + +/// �������� +bool CVrNetUtils::SetGateWay(const char ip[IPV4_CHAR_LENGTH], const char *devName) +{ +#ifndef _WIN32 + std::lock_guard oLock(csDevice); + int sockFd; + struct sockaddr_in sockaddr; + struct rtentry rt; + + if (!IsValidIP(ip)) + { + LOG_WARNING("gateway invalid!\n"); + return false; + } + + sockFd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockFd < 0) + { + LOG_WARNING("Socket create error.\n"); + return false; + } + + memset(&rt, 0, sizeof(struct rtentry)); + memset(&sockaddr, 0, sizeof(struct sockaddr_in)); + sockaddr.sin_family = AF_INET; + sockaddr.sin_port = 0; + if (inet_aton((const char*)ip, &sockaddr.sin_addr) < 0) + { + perror("inet_aton error\n"); + close(sockFd); + return false; + } + + memcpy(&rt.rt_gateway, &sockaddr, sizeof(struct sockaddr_in)); + ((struct sockaddr_in *)&rt.rt_dst)->sin_family = AF_INET; + ((struct sockaddr_in *)&rt.rt_genmask)->sin_family = AF_INET; + rt.rt_flags = RTF_GATEWAY; + if (ioctl(sockFd, SIOCADDRT, &rt) < 0) + { + perror("ioctl(SIOCADDRT) error in set_default_route\n"); + close(sockFd); + return true; + } + close(sockFd); +#endif + return true; +} + +/// ��ȡ���� +bool CVrNetUtils::GetGateWay(char ip[IPV4_CHAR_LENGTH], const char *devName) +{ +#ifndef _WIN32 + + int received_bytes = 0, msg_len = 0, route_attribute_len = 0; + int sock = -1, msgseq = 0; + struct nlmsghdr *nlh, *nlmsg; + struct rtmsg *route_entry; + // This struct contain route attributes (route type) + struct rtattr *route_attribute; + int BUFFER_SIZE = 4096; + char msgbuf[BUFFER_SIZE], buffer[BUFFER_SIZE]; + char *ptr = buffer; + struct timeval tv; + + if ((sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) + { + perror("socket failed"); + return false; + } + + memset(msgbuf, 0, sizeof(msgbuf)); + memset(ip, 0, IPV4_CHAR_LENGTH); + memset(buffer, 0, sizeof(buffer)); + + /* point the header and the msg structure pointers into the buffer */ + nlmsg = (struct nlmsghdr *)msgbuf; + + /* Fill in the nlmsg header*/ + nlmsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + nlmsg->nlmsg_type = RTM_GETROUTE; // Get the routes from kernel routing table . + nlmsg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; // The message is a request for dump. + nlmsg->nlmsg_seq = msgseq++; // Sequence of the message packet. + nlmsg->nlmsg_pid = getpid(); // PID of process sending the request. + + /* 1 Sec Timeout to avoid stall */ + tv.tv_sec = 1; + setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (struct timeval *)&tv, sizeof(struct timeval)); + /* send msg */ + if (send(sock, nlmsg, nlmsg->nlmsg_len, 0) < 0) + { + perror("send failed"); + return false; + } + /* receive response */ + do + { + received_bytes = recv(sock, ptr, sizeof(buffer) - msg_len, 0); + if (received_bytes < 0) { + perror("Error in recv"); + return false; + } + + nlh = (struct nlmsghdr *) ptr; + + /* Check if the header is valid */ + if ((NLMSG_OK(nlmsg, received_bytes) == 0) || + (nlmsg->nlmsg_type == NLMSG_ERROR)) + { + perror("Error in received packet"); + return false; + } + + /* If we received all data break */ + if (nlh->nlmsg_type == NLMSG_DONE) + break; + else { + ptr += received_bytes; + msg_len += received_bytes; + } + + /* Break if its not a multi part message */ + if ((nlmsg->nlmsg_flags & NLM_F_MULTI) == 0) + break; + } while ((nlmsg->nlmsg_seq != msgseq) || (nlmsg->nlmsg_pid != getpid())); + + /* parse response */ + for (; NLMSG_OK(nlh, received_bytes); nlh = NLMSG_NEXT(nlh, received_bytes)) + { + /* Get the route data */ + route_entry = (struct rtmsg *) NLMSG_DATA(nlh); + + /* We are just interested in main routing table */ + if (route_entry->rtm_table != RT_TABLE_MAIN) + continue; + + route_attribute = (struct rtattr *) RTM_RTA(route_entry); + route_attribute_len = RTM_PAYLOAD(nlh); + + /* Loop through all attributes */ + for (; RTA_OK(route_attribute, route_attribute_len); route_attribute = RTA_NEXT(route_attribute, route_attribute_len)) + { + switch (route_attribute->rta_type) + { + case RTA_GATEWAY: + inet_ntop(AF_INET, RTA_DATA(route_attribute), ip, IPV4_CHAR_LENGTH); + break; + default: + break; + } + } + } + + close(sock); + + return IsValidIP(ip); + +#endif // !_WIN32 + return true; +} + +/// ��IP�ַ���תΪByte +bool CVrNetUtils::IPString2Byte(const char* szIP, unsigned char byIP[4]) +{ + if (nullptr == szIP) + return false; + + unsigned char byTmpIP[4] = { 0 }; + int nNum = 0; + const char* pchValue = szIP; + while ('\0' != *pchValue) + { + if (*pchValue == '.') + { + nNum++; + + if (nNum >= 4) + return false; + + pchValue++; + continue; + } + + if (*pchValue < '0' || *pchValue > '9') + return false; + + byTmpIP[nNum] = byTmpIP[nNum] * 10 + (*pchValue - '0'); + pchValue++; + } + + memcpy(byIP, byTmpIP, 4); + return true; +} + +/// ��IP��ByteתΪ�ַ��� +void CVrNetUtils::IPByte2String(const unsigned char byIP[4], char szIP[IPV4_CHAR_LENGTH]) +{ + memset(szIP, 0, IPV4_CHAR_LENGTH); +#ifdef _MSC_VER + sprintf_s(szIP, IPV4_CHAR_LENGTH, "%d.%d.%d.%d", byIP[0], byIP[1], byIP[2], byIP[3]); +#else + sprintf(szIP, "%d.%d.%d.%d", byIP[0], byIP[1], byIP[2], byIP[3]); +#endif +} + +/// ����㲥��ַ +void CVrNetUtils::CalcBroadCastIP(unsigned char byIP[4], unsigned char byMask[4], unsigned char byBroadCast[4]) +{ + byBroadCast[0] = (byMask[0] & byIP[0]) | (~byMask[0]); + byBroadCast[1] = (byMask[1] & byIP[1]) | (~byMask[1]); + byBroadCast[2] = (byMask[2] & byIP[2]) | (~byMask[2]); + byBroadCast[3] = (byMask[3] & byIP[3]) | (~byMask[3]); +} + + + diff --git a/VrUtils/Src/VrNumUtils.cpp b/VrUtils/Src/VrNumUtils.cpp new file mode 100644 index 0000000..45d7d85 --- /dev/null +++ b/VrUtils/Src/VrNumUtils.cpp @@ -0,0 +1,20 @@ +#include "VrNumUtils.h" +#include + +// С˷ת +short CVrNumUtils::NumFlip(short nData) +{ + short sNumHi = (nData >> 8 & 0xff); + short sNumLo = nData & 0xff; + return sNumLo << 8 | sNumHi; +} + +// С˷ת +float CVrNumUtils::NumFlipFloat(float fData) +{ + char* pData = (char *)&fData; + // ֶֽ + std::swap(pData[0], pData[3]); + std::swap(pData[1], pData[2]); + return *((float*)pData); +} \ No newline at end of file diff --git a/VrUtils/Src/VrShellUtils.cpp b/VrUtils/Src/VrShellUtils.cpp new file mode 100644 index 0000000..b22a4f3 --- /dev/null +++ b/VrUtils/Src/VrShellUtils.cpp @@ -0,0 +1,43 @@ +#include "VrShellUtils.h" +#include +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#include +#endif + +/// @brief +/// ִShell +/// [in]Shell +int CVrShellUtils::ExecuCMD(const char *data) +{ + int ret = 0; +#ifndef _WIN32 + while (true) + { + ret = system(data); + if (WIFSIGNALED(ret) && (WTERMSIG(ret) == SIGINT || WTERMSIG(ret) == SIGQUIT)) + { + if (ret == 512) + { + break; + } + std::this_thread::sleep_for(std::chrono::seconds(1)); + continue; + } + else + { +#ifdef SHELL_PRINT + LOG_DEBUG("execu %s successful code:%d", data, ret); +#endif + break; + } + break; + } +#endif + return ret; +} diff --git a/VrUtils/Src/VrStringUtils.cpp b/VrUtils/Src/VrStringUtils.cpp new file mode 100644 index 0000000..7d1b375 --- /dev/null +++ b/VrUtils/Src/VrStringUtils.cpp @@ -0,0 +1,104 @@ +#include "VrStringUtils.h" +#include +#include +#include + +/// \brief +/// תΪַ +/// \param nNumber[in] +/// \return ַ +std::string CVrStringUtils::Int2Str(int nNum) +{ + std::stringstream ss; + ss << nNum; + return ss.str(); +} + +std::string CVrStringUtils::Int2HexStr(int nNum) +{ + std::stringstream ss; + ss << std::hex << std::setw(2) << std::setfill('0') << std::uppercase << nNum; // ʹ std::hex ־ʮʽ + return ss.str(); // ʹ str() ȡеַʾ +} + +std::string CVrStringUtils::Int2Str(unsigned int nNum) +{ + std::stringstream ss; + ss << nNum; + return ss.str(); +} + +std::string CVrStringUtils::Int2Str(unsigned long long nNum) +{ + std::stringstream ss; + ss << nNum; + return ss.str(); +} + +std::string CVrStringUtils::Int2str(long num) +{ + std::stringstream ss; + ss << num; + return ss.str(); +} + +std::vector CVrStringUtils::SplitString(std::string srcStr, std::string delimStr, bool repeatedCharIgnored) +{ + std::vector resultStringVector; + std::replace_if(srcStr.begin(), srcStr.end(), [&](const char& c) {if (delimStr.find(c) != std::string::npos) { return true; } else { return false; }}/*pred*/, delimStr.at(0));//ֵзָ滻Ϊһַַָͬĵһ + size_t pos = srcStr.find(delimStr.at(0)); + std::string addedString = ""; + while (pos != std::string::npos) { + addedString = srcStr.substr(0, pos); + if (!addedString.empty() || !repeatedCharIgnored) { + resultStringVector.push_back(addedString); + } + srcStr.erase(srcStr.begin(), srcStr.begin() + pos + 1); + pos = srcStr.find(delimStr.at(0)); + } + addedString = srcStr; + if (!addedString.empty() || !repeatedCharIgnored) { + resultStringVector.push_back(addedString); + } + return resultStringVector; +} + + +int CVrStringUtils::StringUpper(char *sData, int nLen) +{ + if (sData != nullptr && nLen != 0) + { + for (int i = 0; i < nLen; i++) + { + sData[i] = toupper(sData[i]); + } + } + return 0; +} + +int CVrStringUtils::StringLower(char *sData, int nLen) +{ + if (sData != nullptr && nLen != 0) + { + for (int i = 0; i < nLen; i++) + { + sData[i] = tolower(sData[i]); + } + } + return 0; +} + +/// 滻ַ +void CVrStringUtils::ReplaceString(std::string& src, const std::string& repSrc, const std::string& repDest) +{ + size_t pos = 0; + // ҵַʼλ + while ((pos = src.find(repSrc, pos)) != std::string::npos) + { + // 滻ҵַ + src.replace(pos, repSrc.length(), repDest); + // ƶ滻λã滻Ѿ滻IJ + pos += repDest.length(); + } +} + diff --git a/VrUtils/Src/VrTimeUtils.cpp b/VrUtils/Src/VrTimeUtils.cpp new file mode 100644 index 0000000..cfe87df --- /dev/null +++ b/VrUtils/Src/VrTimeUtils.cpp @@ -0,0 +1,31 @@ +#include "VrTimeUtils.h" + +CVrTimeUtils::CVrTimeUtils() +{ + m_begin = std::chrono::high_resolution_clock::now(); +} + + +CVrTimeUtils::~CVrTimeUtils() +{ +} + +void CVrTimeUtils::Update() +{ + m_begin = std::chrono::high_resolution_clock::now(); +} + +double CVrTimeUtils::GetElapsedSecond() +{ + return GetElapsedTimeInMicroSec() * 0.000001; +} + +double CVrTimeUtils::GetElapsedTimeInMilliSec() +{ + return GetElapsedTimeInMicroSec() * 0.001; +} + +long long CVrTimeUtils::GetElapsedTimeInMicroSec() +{ + return std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - m_begin).count(); +} diff --git a/VrUtils/Src/getopt.c b/VrUtils/Src/getopt.c new file mode 100644 index 0000000..5a60b98 --- /dev/null +++ b/VrUtils/Src/getopt.c @@ -0,0 +1,764 @@ +// ֻWindowsƽ̨±getoptʵ֣ƽ̨ʹϵͳԴ +#ifdef _WIN32 + +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu + before changing it! + + Copyright (C) 1987, 88, 89, 90, 91, 92, 1993 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef __STDC__ +# ifndef const +# define const +# endif +#endif + +/* This tells Alpha OSF/1 not to define a getopt prototype in . */ +#ifndef _NO_PROTO +#define _NO_PROTO +#endif + +#include +#include "tailor.h" + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#if defined (_LIBC) || !defined (__GNU_LIBRARY__) + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +#include +#endif /* GNU C library. */ + +/* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a + long-named option. Because this is not POSIX.2 compliant, it is + being phased out. */ +/* #define GETOPT_COMPAT */ + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg = 0; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* XXX 1003.2 says this must be 1 before any call. */ +int optind = 0; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +#define BAD_OPTION '\0' +int optopt = BAD_OPTION; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return EOF with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +#include + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ + +#define my_index strchr +#define my_strlen strlen +#else + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +#if __STDC__ || defined(PROTO) +extern char *getenv(const char *name); +extern int strcmp (const char *s1, const char *s2); +#ifdef _WIN32 +extern int strncmp(const char *s1, const char *s2, int n); +#endif // _WIN32 + +static int my_strlen(const char *s); +static char *my_index (const char *str, int chr); +#else +extern char *getenv (); +#endif + +static int +my_strlen (str) + const char *str; +{ + int n = 0; + while (*str++) + n++; + return n; +} + +static char * +my_index (str, chr) + const char *str; + int chr; +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +#endif /* GNU C library. */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. + + To perform the swap, we first reverse the order of all elements. So + all options now come before all non options, but they are in the + wrong order. So we put back the options and non options in original + order by reversing them again. For example: + original input: a b c -x -y + reverse all: -y -x c b a + reverse options: -x -y c b a + reverse non options: -x -y a b c +*/ + +#if __STDC__ || defined(PROTO) +static void exchange (char **argv); +#endif + +static void +exchange (argv) + char **argv; +{ + char *temp, **first, **last; + + /* Reverse all the elements [first_nonopt, optind) */ + first = &argv[first_nonopt]; + last = &argv[optind-1]; + while (first < last) { + temp = *first; *first = *last; *last = temp; first++; last--; + } + /* Put back the options in order */ + first = &argv[first_nonopt]; + first_nonopt += (optind - last_nonopt); + last = &argv[first_nonopt - 1]; + while (first < last) { + temp = *first; *first = *last; *last = temp; first++; last--; + } + + /* Put back the non options in order */ + first = &argv[first_nonopt]; + last_nonopt = optind; + last = &argv[last_nonopt-1]; + while (first < last) { + temp = *first; *first = *last; *last = temp; first++; last--; + } +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns `EOF'. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return BAD_OPTION after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return BAD_OPTION. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct option *longopts; + int *longind; + int long_only; +{ + int option_index; + + optarg = 0; + + /* Initialize the internal data when the first call is made. + Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + if (optind == 0) + { + first_nonopt = last_nonopt = optind = 1; + + nextchar = NULL; + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (getenv ("POSIXLY_CORRECT") != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + } + + if (nextchar == NULL || *nextchar == '\0') + { + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Now skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc + && (argv[optind][0] != '-' || argv[optind][1] == '\0') +#ifdef GETOPT_COMPAT + && (longopts == NULL + || argv[optind][0] != '+' || argv[optind][1] == '\0') +#endif /* GETOPT_COMPAT */ + ) + optind++; + last_nonopt = optind; + } + + /* Special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return EOF; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if ((argv[optind][0] != '-' || argv[optind][1] == '\0') +#ifdef GETOPT_COMPAT + && (longopts == NULL + || argv[optind][0] != '+' || argv[optind][1] == '\0') +#endif /* GETOPT_COMPAT */ + ) + { + if (ordering == REQUIRE_ORDER) + return EOF; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Start decoding its characters. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + if (longopts != NULL + && ((argv[optind][0] == '-' + && (argv[optind][1] == '-' || long_only)) +#ifdef GETOPT_COMPAT + || argv[optind][0] == '+' +#endif /* GETOPT_COMPAT */ + )) + { + const struct option *p; + char *s = nextchar; + int exact = 0; + int ambig = 0; + const struct option *pfound = NULL; + int indfound = 0; + + while (*s && *s != '=') + s++; + + /* Test all options for either exact match or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; + p++, option_index++) + if (!strncmp (p->name, nextchar, s - nextchar)) + { + if (s - nextchar == my_strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, "%s: option `%s' is ambiguous\n", + argv[0], argv[optind]); + nextchar += my_strlen (nextchar); + optind++; + return BAD_OPTION; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*s) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = s + 1; + else + { + if (opterr) + { + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + "%s: option `--%s' doesn't allow an argument\n", + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + "%s: option `%c%s' doesn't allow an argument\n", + argv[0], argv[optind - 1][0], pfound->name); + } + nextchar += my_strlen (nextchar); + return BAD_OPTION; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, "%s: option `%s' requires an argument\n", + argv[0], argv[optind - 1]); + nextchar += my_strlen (nextchar); + return optstring[0] == ':' ? ':' : BAD_OPTION; + } + } + nextchar += my_strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' +#ifdef GETOPT_COMPAT + || argv[optind][0] == '+' +#endif /* GETOPT_COMPAT */ + || my_index (optstring, *nextchar) == NULL) + { + if (opterr) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, "%s: unrecognized option `--%s'\n", + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, "%s: unrecognized option `%c%s'\n", + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + return BAD_OPTION; + } + } + + /* Look at and handle the next option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (opterr) + { +#if 0 + if (c < 040 || c >= 0177) + fprintf (stderr, "%s: unrecognized option, character code 0%o\n", + argv[0], c); + else + fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c); +#else + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c); +#endif + } + optopt = c; + return BAD_OPTION; + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = 0; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { +#if 0 + fprintf (stderr, "%s: option `-%c' requires an argument\n", + argv[0], c); +#else + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: option requires an argument -- %c\n", + argv[0], c); +#endif + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = BAD_OPTION; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +int +getopt_long (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 0); +} + +#endif /* _LIBC or not __GNU_LIBRARY__. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == EOF) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case BAD_OPTION: + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ + +#endif /* _WIN32 */ + diff --git a/VrUtils/VrUtils.pro b/VrUtils/VrUtils.pro new file mode 100644 index 0000000..95c76e3 --- /dev/null +++ b/VrUtils/VrUtils.pro @@ -0,0 +1,169 @@ +QT -= gui + +TEMPLATE = lib +CONFIG += staticlib +DEFINES += TINYXML2_EXPORT +DEFINES += HAVE_SNPRINTF +DEFINES += LOG4CPP_HAVE_SSTREAM=1 +DEFINES += VR_UTILS_EXPORTS +CONFIG += c++11 + +# Unix平台特定配置 +unix:!macx { + DEFINES += LOG4CPP_HAVE_UNISTD_H + # 确保所有符号导出(对于静态库) + QMAKE_CXXFLAGS += -fPIC +} + +# You can make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 +# 启用 auto_ptr 支持 (for log4cpp compatibility) +win32-msvc*:QMAKE_CXXFLAGS += /D_HAS_AUTO_PTR_ETC=1 + +HEADERS += \ + Inc/IVrUtils.h \ + Inc/VrDateUtils.h \ + Inc/VrFileUtils.h \ + Inc/VrLog.h \ + Inc/VrNetUtils.h \ + Inc/VrTimeUtils.h \ + ini/ConvertUTF.h \ + ini/SimpleIni.h \ + log4cpp/include/log4cpp/AbortAppender.hh \ + log4cpp/include/log4cpp/Appender.hh \ + log4cpp/include/log4cpp/AppenderSkeleton.hh \ + log4cpp/include/log4cpp/AppendersFactory.hh \ + log4cpp/include/log4cpp/BasicConfigurator.hh \ + log4cpp/include/log4cpp/BasicLayout.hh \ + log4cpp/include/log4cpp/BufferingAppender.hh \ + log4cpp/include/log4cpp/Category.hh \ + log4cpp/include/log4cpp/CategoryStream.hh \ + log4cpp/include/log4cpp/Configurator.hh \ + log4cpp/include/log4cpp/DailyRollingFileAppender.hh \ + log4cpp/include/log4cpp/Export.hh \ + log4cpp/include/log4cpp/FactoryParams.hh \ + log4cpp/include/log4cpp/FileAppender.hh \ + log4cpp/include/log4cpp/Filter.hh \ + log4cpp/include/log4cpp/FixedContextCategory.hh \ + log4cpp/include/log4cpp/HierarchyMaintainer.hh \ + log4cpp/include/log4cpp/IdsaAppender.hh \ + log4cpp/include/log4cpp/Layout.hh \ + log4cpp/include/log4cpp/LayoutAppender.hh \ + log4cpp/include/log4cpp/LayoutsFactory.hh \ + log4cpp/include/log4cpp/LevelEvaluator.hh \ + log4cpp/include/log4cpp/LoggingEvent.hh \ + log4cpp/include/log4cpp/Manipulator.hh \ + log4cpp/include/log4cpp/NDC.hh \ + log4cpp/include/log4cpp/NTEventLogAppender.hh \ + log4cpp/include/log4cpp/OstreamAppender.hh \ + log4cpp/include/log4cpp/PassThroughLayout.hh \ + log4cpp/include/log4cpp/PatternLayout.hh \ + log4cpp/include/log4cpp/Portability.hh \ + log4cpp/include/log4cpp/Priority.hh \ + log4cpp/include/log4cpp/PropertyConfigurator.hh \ + log4cpp/include/log4cpp/RemoteSyslogAppender.hh \ + log4cpp/include/log4cpp/RollingFileAppender.hh \ + log4cpp/include/log4cpp/SimpleConfigurator.hh \ + log4cpp/include/log4cpp/SimpleLayout.hh \ + log4cpp/include/log4cpp/SmtpAppender.hh \ + log4cpp/include/log4cpp/StringQueueAppender.hh \ + log4cpp/include/log4cpp/SyslogAppender.hh \ + log4cpp/include/log4cpp/TimeStamp.hh \ + log4cpp/include/log4cpp/TriggeringEventEvaluator.hh \ + log4cpp/include/log4cpp/TriggeringEventEvaluatorFactory.hh \ + log4cpp/include/log4cpp/Win32DebugAppender.hh \ + log4cpp/include/log4cpp/config-MinGW32.h \ + log4cpp/include/log4cpp/config-openvms.h \ + log4cpp/include/log4cpp/config-win32-stlport-boost.h \ + log4cpp/include/log4cpp/config-win32.h \ + log4cpp/include/log4cpp/convenience.h \ + log4cpp/include/log4cpp/threading/BoostThreads.hh \ + log4cpp/include/log4cpp/threading/DummyThreads.hh \ + log4cpp/include/log4cpp/threading/MSThreads.hh \ + log4cpp/include/log4cpp/threading/OmniThreads.hh \ + log4cpp/include/log4cpp/threading/PThreads.hh \ + log4cpp/include/log4cpp/threading/Threading.hh \ + log4cpp/src/Localtime.hh \ + log4cpp/src/PortabilityImpl.hh \ + log4cpp/src/Properties.hh \ + log4cpp/src/PropertyConfiguratorImpl.hh \ + log4cpp/src/StringUtil.hh \ + tinyxml2/tinyxml2.h \ + jsoncpp/json/json.h \ + jsoncpp/json/reader.h \ + jsoncpp/json/value.h \ + jsoncpp/json/writer.h + +SOURCES += \ + Src/VrDateUtils.cpp \ + Src/VrFileUtils.cpp \ + Src/VrLog.cpp \ + Src/VrNetUtils.cpp \ + Src/VrTimeUtils.cpp \ + ini/ConvertUTF.c \ + log4cpp/src/AbortAppender.cpp \ + log4cpp/src/Appender.cpp \ + log4cpp/src/AppenderSkeleton.cpp \ + log4cpp/src/AppendersFactory.cpp \ + log4cpp/src/BasicConfigurator.cpp \ + log4cpp/src/BasicLayout.cpp \ + log4cpp/src/BufferingAppender.cpp \ + log4cpp/src/Category.cpp \ + log4cpp/src/CategoryStream.cpp \ + log4cpp/src/Configurator.cpp \ + log4cpp/src/DailyRollingFileAppender.cpp \ + log4cpp/src/DllMain.cpp \ + log4cpp/src/DummyThreads.cpp \ + log4cpp/src/FactoryParams.cpp \ + log4cpp/src/FileAppender.cpp \ + log4cpp/src/Filter.cpp \ + log4cpp/src/FixedContextCategory.cpp \ + log4cpp/src/HierarchyMaintainer.cpp \ + log4cpp/src/IdsaAppender.cpp \ + log4cpp/src/LayoutAppender.cpp \ + log4cpp/src/LayoutsFactory.cpp \ + log4cpp/src/LevelEvaluator.cpp \ + log4cpp/src/Localtime.cpp \ + log4cpp/src/LoggingEvent.cpp \ + log4cpp/src/MSThreads.cpp \ + log4cpp/src/Manipulator.cpp \ + log4cpp/src/NDC.cpp \ + log4cpp/src/NTEventLogAppender.cpp \ + log4cpp/src/OmniThreads.cpp \ + log4cpp/src/OstreamAppender.cpp \ + log4cpp/src/PThreads.cpp \ + log4cpp/src/PassThroughLayout.cpp \ + log4cpp/src/PatternLayout.cpp \ + log4cpp/src/PortabilityImpl.cpp \ + log4cpp/src/Priority.cpp \ + log4cpp/src/Properties.cpp \ + log4cpp/src/PropertyConfigurator.cpp \ + log4cpp/src/PropertyConfiguratorImpl.cpp \ + log4cpp/src/RemoteSyslogAppender.cpp \ + log4cpp/src/RollingFileAppender.cpp \ + log4cpp/src/SimpleConfigurator.cpp \ + log4cpp/src/SimpleLayout.cpp \ + log4cpp/src/SmtpAppender.cpp \ + log4cpp/src/StringQueueAppender.cpp \ + log4cpp/src/StringUtil.cpp \ + log4cpp/src/SyslogAppender.cpp \ + log4cpp/src/TimeStamp.cpp \ + log4cpp/src/TriggeringEventEvaluatorFactory.cpp \ + log4cpp/src/Win32DebugAppender.cpp \ + log4cpp/src/snprintf.c \ + tinyxml2/tinyxml2.cpp \ + jsoncpp/json_reader.cpp \ + jsoncpp/json_value.cpp \ + jsoncpp/json_writer.cpp + + +INCLUDEPATH += $$PWD/Inc +INCLUDEPATH += $$PWD/jsoncpp +INCLUDEPATH += $$PWD/log4cpp/include + +# Default rules for deployment. +unix { + target.path = /usr/lib +} +!isEmpty(target.path): INSTALLS += target diff --git a/VrUtils/crc/checksum.h b/VrUtils/crc/checksum.h new file mode 100644 index 0000000..e41798e --- /dev/null +++ b/VrUtils/crc/checksum.h @@ -0,0 +1,116 @@ +/* + * Library: libcrc + * File: include/checksum.h + * Author: Lammert Bies + * + * This file is licensed under the MIT License as stated below + * + * Copyright (c) 1999-2018 Lammert Bies + * + * 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. + * + * Description + * ----------- + * The headerfile include/checksum.h contains the definitions and prototypes + * for routines that can be used to calculate several kinds of checksums. + */ + +#ifndef DEF_LIBCRC_CHECKSUM_H +#define DEF_LIBCRC_CHECKSUM_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * #define CRC_POLY_xxxx + * + * The constants of the form CRC_POLY_xxxx define the polynomials for some well + * known CRC calculations. + */ + +#define CRC_POLY_16 0xA001 +#define CRC_POLY_32 0xEDB88320ul +#define CRC_POLY_64 0x42F0E1EBA9EA3693ull +#define CRC_POLY_CCITT 0x1021 +#define CRC_POLY_DNP 0xA6BC +#define CRC_POLY_KERMIT 0x8408 +#define CRC_POLY_SICK 0x8005 + +/* + * #define CRC_START_xxxx + * + * The constants of the form CRC_START_xxxx define the values that are used for + * initialization of a CRC value for common used calculation methods. + */ + +#define CRC_START_8 0x00 +#define CRC_START_16 0x0000 +#define CRC_START_MODBUS 0xFFFF +#define CRC_START_XMODEM 0x0000 +#define CRC_START_CCITT_1D0F 0x1D0F +#define CRC_START_CCITT_FFFF 0xFFFF +#define CRC_START_KERMIT 0x0000 +#define CRC_START_SICK 0x0000 +#define CRC_START_DNP 0x0000 +#define CRC_START_32 0xFFFFFFFFul +#define CRC_START_64_ECMA 0x0000000000000000ull +#define CRC_START_64_WE 0xFFFFFFFFFFFFFFFFull + +/* + * Prototype list of global functions + */ + +unsigned char * checksum_NMEA( const unsigned char *input_str, unsigned char *result ); +uint8_t crc_8( const unsigned char *input_str, size_t num_bytes ); +uint16_t crc_16( const unsigned char *input_str, size_t num_bytes ); +uint32_t crc_32( const unsigned char *input_str, size_t num_bytes ); +uint64_t crc_64_ecma( const unsigned char *input_str, size_t num_bytes ); +uint64_t crc_64_we( const unsigned char *input_str, size_t num_bytes ); +uint16_t crc_ccitt_1d0f( const unsigned char *input_str, size_t num_bytes ); +uint16_t crc_ccitt_ffff( const unsigned char *input_str, size_t num_bytes ); +uint16_t crc_dnp( const unsigned char *input_str, size_t num_bytes ); +uint16_t crc_kermit( const unsigned char *input_str, size_t num_bytes ); +uint16_t crc_modbus( const unsigned char *input_str, size_t num_bytes ); +uint16_t crc_sick( const unsigned char *input_str, size_t num_bytes ); +uint16_t crc_xmodem( const unsigned char *input_str, size_t num_bytes ); +uint8_t update_crc_8( uint8_t crc, unsigned char c ); +uint16_t update_crc_16( uint16_t crc, unsigned char c ); +uint32_t update_crc_32( uint32_t crc, unsigned char c ); +uint64_t update_crc_64_ecma( uint64_t crc, unsigned char c ); +uint16_t update_crc_ccitt( uint16_t crc, unsigned char c ); +uint16_t update_crc_dnp( uint16_t crc, unsigned char c ); +uint16_t update_crc_kermit( uint16_t crc, unsigned char c ); +uint16_t update_crc_sick( uint16_t crc, unsigned char c, unsigned char prev_byte ); + +/* + * Global CRC lookup tables + */ + +extern const uint32_t crc_tab32[]; +extern const uint64_t crc_tab64[]; + +#ifdef __cplusplus +}// Extern C +#endif + +#endif // DEF_LIBCRC_CHECKSUM_H diff --git a/VrUtils/crc/src/crc16.c b/VrUtils/crc/src/crc16.c new file mode 100644 index 0000000..d99273a --- /dev/null +++ b/VrUtils/crc/src/crc16.c @@ -0,0 +1,149 @@ +/* + * Library: libcrc + * File: src/crc16.c + * Author: Lammert Bies + * + * This file is licensed under the MIT License as stated below + * + * Copyright (c) 1999-2016 Lammert Bies + * + * 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. + * + * Description + * ----------- + * The source file src/crc16.c contains routines which calculate the common + * CRC16 cyclic redundancy check values for an incomming byte string. + */ + +#include +#include +#include "checksum.h" + +static void init_crc16_tab( void ); + +static bool crc_tab16_init = false; +static uint16_t crc_tab16[256]; + +/* + * uint16_t crc_16( const unsigned char *input_str, size_t num_bytes ); + * + * The function crc_16() calculates the 16 bits CRC16 in one pass for a byte + * string of which the beginning has been passed to the function. The number of + * bytes to check is also a parameter. The number of the bytes in the string is + * limited by the constant SIZE_MAX. + */ + +uint16_t crc_16( const unsigned char *input_str, size_t num_bytes ) { + + uint16_t crc; + const unsigned char *ptr; + size_t a; + + if ( ! crc_tab16_init ) init_crc16_tab(); + + crc = CRC_START_16; + ptr = input_str; + + if ( ptr != NULL ) for (a=0; a> 8) ^ crc_tab16[ (crc ^ (uint16_t) *ptr++) & 0x00FF ]; + } + + return crc; + +} /* crc_16 */ + +/* + * uint16_t crc_modbus( const unsigned char *input_str, size_t num_bytes ); + * + * The function crc_modbus() calculates the 16 bits Modbus CRC in one pass for + * a byte string of which the beginning has been passed to the function. The + * number of bytes to check is also a parameter. + */ + +uint16_t crc_modbus( const unsigned char *input_str, size_t num_bytes ) { + + uint16_t crc; + const unsigned char *ptr; + size_t a; + + if ( ! crc_tab16_init ) init_crc16_tab(); + + crc = CRC_START_MODBUS; + ptr = input_str; + + if ( ptr != NULL ) for (a=0; a> 8) ^ crc_tab16[ (crc ^ (uint16_t) *ptr++) & 0x00FF ]; + } + + return crc; + +} /* crc_modbus */ + +/* + * uint16_t update_crc_16( uint16_t crc, unsigned char c ); + * + * The function update_crc_16() calculates a new CRC-16 value based on the + * previous value of the CRC and the next byte of data to be checked. + */ + +uint16_t update_crc_16( uint16_t crc, unsigned char c ) { + + if ( ! crc_tab16_init ) init_crc16_tab(); + + return (crc >> 8) ^ crc_tab16[ (crc ^ (uint16_t) c) & 0x00FF ]; + +} /* update_crc_16 */ + +/* + * static void init_crc16_tab( void ); + * + * For optimal performance uses the CRC16 routine a lookup table with values + * that can be used directly in the XOR arithmetic in the algorithm. This + * lookup table is calculated by the init_crc16_tab() routine, the first time + * the CRC function is called. + */ + +static void init_crc16_tab( void ) { + + uint16_t i; + uint16_t j; + uint16_t crc; + uint16_t c; + + for (i=0; i<256; i++) { + + crc = 0; + c = i; + + for (j=0; j<8; j++) { + + if ( (crc ^ c) & 0x0001 ) crc = ( crc >> 1 ) ^ CRC_POLY_16; + else crc = crc >> 1; + + c = c >> 1; + } + + crc_tab16[i] = crc; + } + + crc_tab16_init = true; + +} /* init_crc16_tab */ diff --git a/VrUtils/crc/src/crc32.c b/VrUtils/crc/src/crc32.c new file mode 100644 index 0000000..99ae490 --- /dev/null +++ b/VrUtils/crc/src/crc32.c @@ -0,0 +1,81 @@ +/* + * Library: libcrc + * File: src/crc32.c + * Author: Lammert Bies + * + * This file is licensed under the MIT License as stated below + * + * Copyright (c) 1999-2016 Lammert Bies + * + * 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. + * + * Description + * ----------- + * The source file src/crc32.c contains the routines which are needed to + * calculate a 32 bit CRC value of a sequence of bytes. + */ + +#include +#include +#include "checksum.h" + +/* + * Include the lookup table for the CRC 32 calculation + */ + +//#include "../tab/gentab32.inc" + +/* + * uint32_t crc_32( const unsigned char *input_str, size_t num_bytes ); + * + * The function crc_32() calculates in one pass the common 32 bit CRC value for + * a byte string that is passed to the function together with a parameter + * indicating the length. + */ + +uint32_t crc_32( const unsigned char *input_str, size_t num_bytes ) { + + uint32_t crc; + const unsigned char *ptr; + size_t a; + + crc = CRC_START_32; + ptr = input_str; + + if ( ptr != NULL ) for (a=0; a> 8) ^ crc_tab32[ (crc ^ (uint32_t) *ptr++) & 0x000000FFul ]; + } + + return (crc ^ 0xFFFFFFFFul); + +} /* crc_32 */ + +/* + * uint32_t update_crc_32( uint32_t crc, unsigned char c ); + * + * The function update_crc_32() calculates a new CRC-32 value based on the + * previous value of the CRC and the next byte of the data to be checked. + */ + +uint32_t update_crc_32( uint32_t crc, unsigned char c ) { + + return (crc >> 8) ^ crc_tab32[ (crc ^ (uint32_t) c) & 0x000000FFul ]; + +} /* update_crc_32 */ diff --git a/VrUtils/crc/src/crc64.c b/VrUtils/crc/src/crc64.c new file mode 100644 index 0000000..e533062 --- /dev/null +++ b/VrUtils/crc/src/crc64.c @@ -0,0 +1,107 @@ +/* + * Library: libcrc + * File: src/crc64.c + * Author: Lammert Bies + * + * This file is licensed under the MIT License as stated below + * + * Copyright (c) 2016 Lammert Bies + * + * 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. + * + * Description + * ----------- + * The source file src/crc64.c contains the routines which are needed to + * calculate a 64 bit CRC value of a sequence of bytes. + */ + +#include +#include +#include "checksum.h" + +/* + * Include the lookup table for the CRC 64 calculation + */ + +//#include "../tab/gentab64.inc" + +/* + * uint64_t crc_64_ecma( const unsigned char *input_str, size_t num_bytes ); + * + * The function crc_64_ecma() calculates in one pass the ECMA 64 bit CRC value + * for a byte string that is passed to the function together with a parameter + * indicating the length. + */ + +uint64_t crc_64_ecma( const unsigned char *input_str, size_t num_bytes ) { + + uint64_t crc; + const unsigned char *ptr; + size_t a; + + crc = CRC_START_64_ECMA; + ptr = input_str; + + if ( ptr != NULL ) for (a=0; a> 56) ^ (uint64_t) *ptr++) & 0x00000000000000FFull ]; + } + + return crc; + +} /* crc_64_ecma */ + +/* + * uint64_t crc_64_we( const unsigned char *input_str, size_t num_bytes ); + * + * The function crc_64_we() calculates in one pass the CRC64-WE 64 bit CRC + * value for a byte string that is passed to the function together with a + * parameter indicating the length. + */ + +uint64_t crc_64_we( const unsigned char *input_str, size_t num_bytes ) { + + uint64_t crc; + const unsigned char *ptr; + size_t a; + + crc = CRC_START_64_WE; + ptr = input_str; + + if ( ptr != NULL ) for (a=0; a> 56) ^ (uint64_t) *ptr++) & 0x00000000000000FFull ]; + } + + return crc ^ 0xFFFFFFFFFFFFFFFFull; + +} /* crc_64_we */ + +/* + * uint64_t update_crc_64( uint64_t crc, unsigned char c ); + * + * The function update_crc_64() calculates a new CRC-64 value based on the + * previous value of the CRC and the next byte of the data to be checked. + */ + +uint64_t update_crc_64( uint64_t crc, unsigned char c ) { + + return (crc << 8) ^ crc_tab64[ ((crc >> 56) ^ (uint64_t) c) & 0x00000000000000FFull ]; + +} /* update_crc_64 */ diff --git a/VrUtils/crc/src/crc8.c b/VrUtils/crc/src/crc8.c new file mode 100644 index 0000000..2556cbd --- /dev/null +++ b/VrUtils/crc/src/crc8.c @@ -0,0 +1,103 @@ +/* + * Library: libcrc + * File: src/crc8.c + * Author: Lammert Bies + * + * This file is licensed under the MIT License as stated below + * + * Copyright (c) 1999-2016 Lammert Bies + * + * 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. + * + * Description + * ----------- + * The source file src/crc8.c contains routines for the calculation of 8 bit + * CRC values according to the calculation rules used in the SHT1x and SHT7x + * series of temperature and humidity sensors. + */ + +#include +#include "checksum.h" + +/* + * static uint8_t sht75_crc_table[]; + * + * The SHT75 humidity sensor is capable of calculating an 8 bit CRC checksum to + * ensure data integrity. The lookup table crc_table[] is used to recalculate + * the CRC. + */ + +static uint8_t sht75_crc_table[] = { + + 0, 49, 98, 83, 196, 245, 166, 151, 185, 136, 219, 234, 125, 76, 31, 46, + 67, 114, 33, 16, 135, 182, 229, 212, 250, 203, 152, 169, 62, 15, 92, 109, + 134, 183, 228, 213, 66, 115, 32, 17, 63, 14, 93, 108, 251, 202, 153, 168, + 197, 244, 167, 150, 1, 48, 99, 82, 124, 77, 30, 47, 184, 137, 218, 235, + 61, 12, 95, 110, 249, 200, 155, 170, 132, 181, 230, 215, 64, 113, 34, 19, + 126, 79, 28, 45, 186, 139, 216, 233, 199, 246, 165, 148, 3, 50, 97, 80, + 187, 138, 217, 232, 127, 78, 29, 44, 2, 51, 96, 81, 198, 247, 164, 149, + 248, 201, 154, 171, 60, 13, 94, 111, 65, 112, 35, 18, 133, 180, 231, 214, + 122, 75, 24, 41, 190, 143, 220, 237, 195, 242, 161, 144, 7, 54, 101, 84, + 57, 8, 91, 106, 253, 204, 159, 174, 128, 177, 226, 211, 68, 117, 38, 23, + 252, 205, 158, 175, 56, 9, 90, 107, 69, 116, 39, 22, 129, 176, 227, 210, + 191, 142, 221, 236, 123, 74, 25, 40, 6, 55, 100, 85, 194, 243, 160, 145, + 71, 118, 37, 20, 131, 178, 225, 208, 254, 207, 156, 173, 58, 11, 88, 105, + 4, 53, 102, 87, 192, 241, 162, 147, 189, 140, 223, 238, 121, 72, 27, 42, + 193, 240, 163, 146, 5, 52, 103, 86, 120, 73, 26, 43, 188, 141, 222, 239, + 130, 179, 224, 209, 70, 119, 36, 21, 59, 10, 89, 104, 255, 206, 157, 172 +}; + +/* + * uint8_t crc_8( const unsigned char *input_str, size_t num_bytes ); + * + * The function crc_8() calculates the 8 bit wide CRC of an input string of a + * given length. + */ + +uint8_t crc_8( const unsigned char *input_str, size_t num_bytes ) { + + size_t a; + uint8_t crc; + const unsigned char *ptr; + + crc = CRC_START_8; + ptr = input_str; + + if ( ptr != NULL ) for (a=0; a +#include +#include "checksum.h" + +static uint16_t crc_ccitt_generic( const unsigned char *input_str, size_t num_bytes, uint16_t start_value ); +static void init_crcccitt_tab( void ); + +static bool crc_tabccitt_init = false; +static uint16_t crc_tabccitt[256]; + +/* + * uint16_t crc_xmodem( const unsigned char *input_str, size_t num_bytes ); + * + * The function crc_xmodem() performs a one-pass calculation of an X-Modem CRC + * for a byte string that has been passed as a parameter. + */ + +uint16_t crc_xmodem( const unsigned char *input_str, size_t num_bytes ) { + + return crc_ccitt_generic( input_str, num_bytes, CRC_START_XMODEM ); + +} /* crc_xmodem */ + +/* + * uint16_t crc_ccitt_1d0f( const unsigned char *input_str, size_t num_bytes ); + * + * The function crc_ccitt_1d0f() performs a one-pass calculation of the CCITT + * CRC for a byte string that has been passed as a parameter. The initial value + * 0x1d0f is used for the CRC. + */ + +uint16_t crc_ccitt_1d0f( const unsigned char *input_str, size_t num_bytes ) { + + return crc_ccitt_generic( input_str, num_bytes, CRC_START_CCITT_1D0F ); + +} /* crc_ccitt_1d0f */ + +/* + * uint16_t crc_ccitt_ffff( const unsigned char *input_str, size_t num_bytes ); + * + * The function crc_ccitt_ffff() performs a one-pass calculation of the CCITT + * CRC for a byte string that has been passed as a parameter. The initial value + * 0xffff is used for the CRC. + */ + +uint16_t crc_ccitt_ffff( const unsigned char *input_str, size_t num_bytes ) { + + return crc_ccitt_generic( input_str, num_bytes, CRC_START_CCITT_FFFF ); + +} /* crc_ccitt_ffff */ + +/* + * static uint16_t crc_ccitt_generic( const unsigned char *input_str, size_t num_bytes, uint16_t start_value ); + * + * The function crc_ccitt_generic() is a generic implementation of the CCITT + * algorithm for a one-pass calculation of the CRC for a byte string. The + * function accepts an initial start value for the crc. + */ + +static uint16_t crc_ccitt_generic( const unsigned char *input_str, size_t num_bytes, uint16_t start_value ) { + + uint16_t crc; + const unsigned char *ptr; + size_t a; + + if ( ! crc_tabccitt_init ) init_crcccitt_tab(); + + crc = start_value; + ptr = input_str; + + if ( ptr != NULL ) for (a=0; a> 8) ^ (uint16_t) *ptr++) & 0x00FF ]; + } + + return crc; + +} /* crc_ccitt_generic */ + +/* + * uint16_t update_crc_ccitt( uint16_t crc, unsigned char c ); + * + * The function update_crc_ccitt() calculates a new CRC-CCITT value based on + * the previous value of the CRC and the next byte of the data to be checked. + */ + +uint16_t update_crc_ccitt( uint16_t crc, unsigned char c ) { + + if ( ! crc_tabccitt_init ) init_crcccitt_tab(); + + return (crc << 8) ^ crc_tabccitt[ ((crc >> 8) ^ (uint16_t) c) & 0x00FF ]; + +} /* update_crc_ccitt */ + +/* + * static void init_crcccitt_tab( void ); + * + * For optimal performance, the routine to calculate the CRC-CCITT uses a + * lookup table with pre-compiled values that can be directly applied in the + * XOR action. This table is created at the first call of the function by the + * init_crcccitt_tab() routine. + */ + +static void init_crcccitt_tab( void ) { + + uint16_t i; + uint16_t j; + uint16_t crc; + uint16_t c; + + for (i=0; i<256; i++) { + + crc = 0; + c = i << 8; + + for (j=0; j<8; j++) { + + if ( (crc ^ c) & 0x8000 ) crc = ( crc << 1 ) ^ CRC_POLY_CCITT; + else crc = crc << 1; + + c = c << 1; + } + + crc_tabccitt[i] = crc; + } + + crc_tabccitt_init = true; + +} /* init_crcccitt_tab */ diff --git a/VrUtils/crc/src/crcdnp.c b/VrUtils/crc/src/crcdnp.c new file mode 100644 index 0000000..0f6935a --- /dev/null +++ b/VrUtils/crc/src/crcdnp.c @@ -0,0 +1,128 @@ +/* + * Library: libcrc + * File: src/crcdnp.c + * Author: Lammert Bies + * + * This file is licensed under the MIT License as stated below + * + * Copyright (c) 2005-2016 Lammert Bies + * + * 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. + * + * Description + * ----------- + * The source file src/crcdnp.c contains routines which are used to calculate + * the CRC value in DNP messages. + */ + + + +#include +#include +#include "checksum.h" + +static void init_crcdnp_tab( void ); + +static bool crc_tabdnp_init = false; +static uint16_t crc_tabdnp[256]; + +/* + * uint16_t crc_dnp( const unsigned char* input_str, size_t num_bytes ); + * + * The function crc_dnp() calculates the DNP CRC checksum of a provided byte + * string in one pass. + */ + +uint16_t crc_dnp( const unsigned char *input_str, size_t num_bytes ) { + + uint16_t crc; + uint16_t low_byte; + uint16_t high_byte; + const unsigned char *ptr; + size_t a; + + if ( ! crc_tabdnp_init ) init_crcdnp_tab(); + + crc = CRC_START_DNP; + ptr = input_str; + + if ( ptr != NULL ) for (a=0; a> 8) ^ crc_tabdnp[ (crc ^ (uint16_t) *ptr++) & 0x00FF ]; + } + + crc = ~crc; + low_byte = (crc & 0xff00) >> 8; + high_byte = (crc & 0x00ff) << 8; + crc = low_byte | high_byte; + + return crc; + +} /* crc_dnp */ + +/* + * uint16_t update_crc_dnp( uint16_t crc, unsigned char c ); + * + * The function update_crc_dnp() is called for every new byte in a row that + * must be feeded tot the CRC-DNP routine to calculate the DNP CRC. + */ + +uint16_t update_crc_dnp( uint16_t crc, unsigned char c ) { + + if ( ! crc_tabdnp_init ) init_crcdnp_tab(); + + return (crc >> 8) ^ crc_tabdnp[ (crc ^ (uint16_t) c) & 0x00FF ]; + +} /* update_crc_dnp */ + +/* + * static void init_crcdnp_tab( void ); + * + * For better performance, the DNP CRC calculation uses a precompiled list with + * bit patterns that are used in the XOR operation in the main routine. This + * table is calculated once at the start of the program by the + * init_crcdnp_tab() routine. + */ + +static void init_crcdnp_tab( void ) { + + int i; + int j; + uint16_t crc; + uint16_t c; + + for (i=0; i<256; i++) { + + crc = 0; + c = (uint16_t) i; + + for (j=0; j<8; j++) { + + if ( (crc ^ c) & 0x0001 ) crc = ( crc >> 1 ) ^ CRC_POLY_DNP; + else crc = crc >> 1; + + c = c >> 1; + } + + crc_tabdnp[i] = crc; + } + + crc_tabdnp_init = true; + +} /* init_crcdnp_tab */ diff --git a/VrUtils/crc/src/crckrmit.c b/VrUtils/crc/src/crckrmit.c new file mode 100644 index 0000000..5d2a55e --- /dev/null +++ b/VrUtils/crc/src/crckrmit.c @@ -0,0 +1,126 @@ +/* + * Library: libcrc + * File: src/crckrmit.c + * Author: Lammert Bies + * + * This file is licensed under the MIT License as stated below + * + * Copyright (c) 1999-2016 Lammert Bies + * + * 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. + * + * Description + * ----------- + * The source file src/crckrmit.c contains routines which calculate the CRC + * Kermit cyclic redundancy check value for an incomming byte string. + */ + +#include +#include +#include "checksum.h" + +static void init_crc_tab( void ); + +static bool crc_tab_init = false; +static uint16_t crc_tab[256]; + +/* + * uint16_t crc_kermit( const unsigned char *input_str, size_t num_bytes ); + * + * The function crc_kermit() calculates the 16 bits Kermit CRC in one pass for + * a byte string of which the beginning has been passed to the function. The + * number of bytes to check is also a parameter. + */ + +uint16_t crc_kermit( const unsigned char *input_str, size_t num_bytes ) { + + uint16_t crc; + uint16_t low_byte; + uint16_t high_byte; + const unsigned char *ptr; + size_t a; + + if ( ! crc_tab_init ) init_crc_tab(); + + crc = CRC_START_KERMIT; + ptr = input_str; + + if ( ptr != NULL ) for (a=0; a> 8) ^ crc_tab[ (crc ^ (uint16_t) *ptr++) & 0x00FF ]; + } + + low_byte = (crc & 0xff00) >> 8; + high_byte = (crc & 0x00ff) << 8; + crc = low_byte | high_byte; + + return crc; + +} /* crc_kermit */ + +/* + * uint16_t update_crc_kermit( uint16_t crc, unsigned char c ); + * + * The function update_crc_kermit() calculates a new CRC Kermit value based on + * the previous value of the CRC and the next byte of data to be checked. + */ + +uint16_t update_crc_kermit( uint16_t crc, unsigned char c ) { + + if ( ! crc_tab_init ) init_crc_tab(); + + return (crc >> 8) ^ crc_tab[ (crc ^ (uint16_t) c) & 0x00FF ]; + +} /* update_crc_kermit */ + +/* + * static void init_crc_tab( void ); + * + * For optimal performance, the CRC Kermit routine uses a lookup table with + * values that can be used directly in the XOR arithmetic in the algorithm. + * This lookup table is calculated by the init_crc_tab() routine, the first + * time the CRC function is called. + */ + +static void init_crc_tab( void ) { + + uint16_t i; + uint16_t j; + uint16_t crc; + uint16_t c; + + for (i=0; i<256; i++) { + + crc = 0; + c = i; + + for (j=0; j<8; j++) { + + if ( (crc ^ c) & 0x0001 ) crc = ( crc >> 1 ) ^ CRC_POLY_KERMIT; + else crc = crc >> 1; + + c = c >> 1; + } + + crc_tab[i] = crc; + } + + crc_tab_init = true; + +} /* init_crc_tab */ diff --git a/VrUtils/crc/src/crcsick.c b/VrUtils/crc/src/crcsick.c new file mode 100644 index 0000000..f26139a --- /dev/null +++ b/VrUtils/crc/src/crcsick.c @@ -0,0 +1,102 @@ +/* + * Library: libcrc + * File: src/crcsick.c + * Author: Lammert Bies + * + * This file is licensed under the MIT License as stated below + * + * Copyright (c) 2007-2016 Lammert Bies + * + * 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. + * + * Description + * ----------- + * The source file src/crcsick.c contains routines that help in calculating the + * CRC value that is used as dataprotection measure in communications with Sick + * electronic devices. + */ + +#include +#include "checksum.h" + +/* + * uint16_t crc_sick( const unsigned char *input_str, size_t num_bytes ); + * + * The function crc_sick() calculates the SICK CRC value of an input string in + * one pass. + */ + +uint16_t crc_sick( const unsigned char *input_str, size_t num_bytes ) { + + uint16_t crc; + uint16_t low_byte; + uint16_t high_byte; + uint16_t short_c; + uint16_t short_p; + const unsigned char *ptr; + size_t a; + + crc = CRC_START_SICK; + ptr = input_str; + short_p = 0; + + if ( ptr != NULL ) for (a=0; a> 8; + high_byte = (crc & 0x00FF) << 8; + crc = low_byte | high_byte; + + return crc; + +} /* crc_sick */ + +/* + * uint16_t update_crc_sick( uint16_t crc, unsigned char c, unsigned char prev_byte ); + * + * The function update_crc_sick() calculates a new CRC-SICK value based on the + * previous value of the CRC and the next byte of the data to be checked. + */ + +uint16_t update_crc_sick( uint16_t crc, unsigned char c, unsigned char prev_byte ) { + + uint16_t short_c; + uint16_t short_p; + + short_c = 0x00FF & (uint16_t) c; + short_p = ( 0x00FF & (uint16_t) prev_byte ) << 8; + + if ( crc & 0x8000 ) crc = ( crc << 1 ) ^ CRC_POLY_SICK; + else crc = crc << 1; + + crc ^= ( short_c | short_p ); + + return crc; + +} /* update_crc_sick */ diff --git a/VrUtils/crc/src/nmea-chk.c b/VrUtils/crc/src/nmea-chk.c new file mode 100644 index 0000000..de0b899 --- /dev/null +++ b/VrUtils/crc/src/nmea-chk.c @@ -0,0 +1,76 @@ +/* + * Library: libcrc + * File: src/nmea-chk.c + * Author: Lammert Bies + * + * This file is licensed under the MIT License as stated below + * + * Copyright (c) 2008-2016 Lammert Bies + * + * 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. + * + * Description + * ----------- + * The source file src/nmea-chk.c contains routines to calculate the checksum + * in NMEA messages. + */ + +#include +#include +#include "checksum.h" + +/* + * unsigned char *checksum_NMEA( const unsigned char *input_str, unsigned char *result ); + * + * The function checksum_NMEA() calculates the checksum of a valid NMEA string. + * The routine does not try to validate the string itself. A leading '$' will + * be ignored, as this character is part of the NMEA sentence, but not part of + * the checksum calculation. The calculation stops, whenever a linefeed, + * carriage return, '*' or end of string is scanned. + * + * Because there is no NMEA syntax checking involved, the function always + * returns with succes, unless a NULL pointer is provided as parameter. The + * return value is a pointer to the result buffer provided by the calling + * application, or NULL in case of error. + * + * The result buffer must be at least three characters long. Two for the + * checksum value and the third to store the EOS. The result buffer is not + * filled when an error occurs. + */ + +unsigned char * checksum_NMEA( const unsigned char *input_str, unsigned char *result ) { + + const unsigned char *ptr; + unsigned char checksum; + + if ( input_str == NULL ) return NULL; + if ( result == NULL ) return NULL; + + checksum = 0; + ptr = (const unsigned char *) input_str; + + if ( *ptr == '$' ) ptr++; + + while ( *ptr && *ptr != '\r' && *ptr != '\n' && *ptr != '*' ) checksum ^= *ptr++; + + snprintf( (char *) result, 3, "%02hhX", checksum ); + + return result; + +} /* checksum_NMEA */ diff --git a/VrUtils/ini/ConvertUTF.c b/VrUtils/ini/ConvertUTF.c new file mode 100644 index 0000000..9b3deeb --- /dev/null +++ b/VrUtils/ini/ConvertUTF.c @@ -0,0 +1,539 @@ +/* + * Copyright 2001-2004 Unicode, Inc. + * + * Disclaimer + * + * This source code is provided as is by Unicode, Inc. No claims are + * made as to fitness for any particular purpose. No warranties of any + * kind are expressed or implied. The recipient agrees to determine + * applicability of information provided. If this file has been + * purchased on magnetic or optical media from Unicode, Inc., the + * sole remedy for any claim will be exchange of defective media + * within 90 days of receipt. + * + * Limitations on Rights to Redistribute This Code + * + * Unicode, Inc. hereby grants the right to freely use the information + * supplied in this file in the creation of products supporting the + * Unicode Standard, and to make copies of this file in any form + * for internal or external distribution as long as this notice + * remains attached. + */ + +/* --------------------------------------------------------------------- + + Conversions between UTF32, UTF-16, and UTF-8. Source code file. + Author: Mark E. Davis, 1994. + Rev History: Rick McGowan, fixes & updates May 2001. + Sept 2001: fixed const & error conditions per + mods suggested by S. Parent & A. Lillich. + June 2002: Tim Dodd added detection and handling of incomplete + source sequences, enhanced error detection, added casts + to eliminate compiler warnings. + July 2003: slight mods to back out aggressive FFFE detection. + Jan 2004: updated switches in from-UTF8 conversions. + Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions. + + See the header file "ConvertUTF.h" for complete documentation. + +------------------------------------------------------------------------ */ + + +#include "ConvertUTF.h" +#ifdef CVTUTF_DEBUG +#include +#endif + +static const int halfShift = 10; /* used for shifting by 10 bits */ + +static const UTF32 halfBase = 0x0010000UL; +static const UTF32 halfMask = 0x3FFUL; + +#define UNI_SUR_HIGH_START (UTF32)0xD800 +#define UNI_SUR_HIGH_END (UTF32)0xDBFF +#define UNI_SUR_LOW_START (UTF32)0xDC00 +#define UNI_SUR_LOW_END (UTF32)0xDFFF +#define false 0 +#define true 1 + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF32toUTF16 ( + const UTF32** sourceStart, const UTF32* sourceEnd, + UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF32* source = *sourceStart; + UTF16* target = *targetStart; + while (source < sourceEnd) { + UTF32 ch; + if (target >= targetEnd) { + result = targetExhausted; break; + } + ch = *source++; + if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ + /* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { + if (flags == strictConversion) { + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + *target++ = (UTF16)ch; /* normal case */ + } + } else if (ch > UNI_MAX_LEGAL_UTF32) { + if (flags == strictConversion) { + result = sourceIllegal; + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + /* target is a character in range 0xFFFF - 0x10FFFF. */ + if (target + 1 >= targetEnd) { + --source; /* Back up source pointer! */ + result = targetExhausted; break; + } + ch -= halfBase; + *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); + *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); + } + } + *sourceStart = source; + *targetStart = target; + return result; +} + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF16toUTF32 ( + const UTF16** sourceStart, const UTF16* sourceEnd, + UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF16* source = *sourceStart; + UTF32* target = *targetStart; + UTF32 ch, ch2; + while (source < sourceEnd) { + const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */ + ch = *source++; + /* If we have a surrogate pair, convert to UTF32 first. */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) { + /* If the 16 bits following the high surrogate are in the source buffer... */ + if (source < sourceEnd) { + ch2 = *source; + /* If it's a low surrogate, convert to UTF32. */ + if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) { + ch = ((ch - UNI_SUR_HIGH_START) << halfShift) + + (ch2 - UNI_SUR_LOW_START) + halfBase; + ++source; + } else if (flags == strictConversion) { /* it's an unpaired high surrogate */ + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } else { /* We don't have the 16 bits following the high surrogate. */ + --source; /* return to the high surrogate */ + result = sourceExhausted; + break; + } + } else if (flags == strictConversion) { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) { + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } + if (target >= targetEnd) { + source = oldSource; /* Back up source pointer! */ + result = targetExhausted; break; + } + *target++ = ch; + } + *sourceStart = source; + *targetStart = target; +#ifdef CVTUTF_DEBUG +if (result == sourceIllegal) { + fprintf(stderr, "ConvertUTF16toUTF32 illegal seq 0x%04x,%04x\n", ch, ch2); + fflush(stderr); +} +#endif + return result; +} + +/* --------------------------------------------------------------------- */ + +/* + * Index into the table below with the first byte of a UTF-8 sequence to + * get the number of trailing bytes that are supposed to follow it. + * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is + * left as-is for anyone who may want to do such conversion, which was + * allowed in earlier algorithms. + */ +static const char trailingBytesForUTF8[256] = { + 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, + 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, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 +}; + +/* + * Magic values subtracted from a buffer value during UTF8 conversion. + * This table contains as many values as there might be trailing bytes + * in a UTF-8 sequence. + */ +static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, + 0x03C82080UL, 0xFA082080UL, 0x82082080UL }; + +/* + * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed + * into the first byte, depending on how many bytes follow. There are + * as many entries in this table as there are UTF-8 sequence types. + * (I.e., one byte sequence, two byte... etc.). Remember that sequencs + * for *legal* UTF-8 will be 4 or fewer bytes total. + */ +static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + +/* --------------------------------------------------------------------- */ + +/* The interface converts a whole buffer to avoid function-call overhead. + * Constants have been gathered. Loops & conditionals have been removed as + * much as possible for efficiency, in favor of drop-through switches. + * (See "Note A" at the bottom of the file for equivalent code.) + * If your compiler supports it, the "isLegalUTF8" call can be turned + * into an inline function. + */ + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF16toUTF8 ( + const UTF16** sourceStart, const UTF16* sourceEnd, + UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF16* source = *sourceStart; + UTF8* target = *targetStart; + while (source < sourceEnd) { + UTF32 ch; + unsigned short bytesToWrite = 0; + const UTF32 byteMask = 0xBF; + const UTF32 byteMark = 0x80; + const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */ + ch = *source++; + /* If we have a surrogate pair, convert to UTF32 first. */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) { + /* If the 16 bits following the high surrogate are in the source buffer... */ + if (source < sourceEnd) { + UTF32 ch2 = *source; + /* If it's a low surrogate, convert to UTF32. */ + if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) { + ch = ((ch - UNI_SUR_HIGH_START) << halfShift) + + (ch2 - UNI_SUR_LOW_START) + halfBase; + ++source; + } else if (flags == strictConversion) { /* it's an unpaired high surrogate */ + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } else { /* We don't have the 16 bits following the high surrogate. */ + --source; /* return to the high surrogate */ + result = sourceExhausted; + break; + } + } else if (flags == strictConversion) { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) { + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } + /* Figure out how many bytes the result will require */ + if (ch < (UTF32)0x80) { bytesToWrite = 1; + } else if (ch < (UTF32)0x800) { bytesToWrite = 2; + } else if (ch < (UTF32)0x10000) { bytesToWrite = 3; + } else if (ch < (UTF32)0x110000) { bytesToWrite = 4; + } else { bytesToWrite = 3; + ch = UNI_REPLACEMENT_CHAR; + } + + target += bytesToWrite; + if (target > targetEnd) { + source = oldSource; /* Back up source pointer! */ + target -= bytesToWrite; result = targetExhausted; break; + } + switch (bytesToWrite) { /* note: everything falls through. */ + case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; + case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; + case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; + case 1: *--target = (UTF8)(ch | firstByteMark[bytesToWrite]); + } + target += bytesToWrite; + } + *sourceStart = source; + *targetStart = target; + return result; +} + +/* --------------------------------------------------------------------- */ + +/* + * Utility routine to tell whether a sequence of bytes is legal UTF-8. + * This must be called with the length pre-determined by the first byte. + * If not calling this from ConvertUTF8to*, then the length can be set by: + * length = trailingBytesForUTF8[*source]+1; + * and the sequence is illegal right away if there aren't that many bytes + * available. + * If presented with a length > 4, this returns false. The Unicode + * definition of UTF-8 goes up to 4-byte sequences. + */ + +static Boolean isLegalUTF8(const UTF8 *source, int length) { + UTF8 a; + const UTF8 *srcptr = source+length; + switch (length) { + default: return false; + /* Everything else falls through when "true"... */ + case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; + case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; + case 2: if ((a = (*--srcptr)) > 0xBF) return false; + + switch (*source) { + /* no fall-through in this inner switch */ + case 0xE0: if (a < 0xA0) return false; break; + case 0xED: if (a > 0x9F) return false; break; + case 0xF0: if (a < 0x90) return false; break; + case 0xF4: if (a > 0x8F) return false; break; + default: if (a < 0x80) return false; + } + + case 1: if (*source >= 0x80 && *source < 0xC2) return false; + } + if (*source > 0xF4) return false; + return true; +} + +/* --------------------------------------------------------------------- */ + +/* + * Exported function to return whether a UTF-8 sequence is legal or not. + * This is not used here; it's just exported. + */ +Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) { + int length = trailingBytesForUTF8[*source]+1; + if (source+length > sourceEnd) { + return false; + } + return isLegalUTF8(source, length); +} + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF8toUTF16 ( + const UTF8** sourceStart, const UTF8* sourceEnd, + UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF8* source = *sourceStart; + UTF16* target = *targetStart; + while (source < sourceEnd) { + UTF32 ch = 0; + unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; + if (source + extraBytesToRead >= sourceEnd) { + result = sourceExhausted; break; + } + /* Do this check whether lenient or strict */ + if (! isLegalUTF8(source, extraBytesToRead+1)) { + result = sourceIllegal; + break; + } + /* + * The cases all fall through. See "Note A" below. + */ + switch (extraBytesToRead) { + case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */ + case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */ + case 3: ch += *source++; ch <<= 6; + case 2: ch += *source++; ch <<= 6; + case 1: ch += *source++; ch <<= 6; + case 0: ch += *source++; + } + ch -= offsetsFromUTF8[extraBytesToRead]; + + if (target >= targetEnd) { + source -= (extraBytesToRead+1); /* Back up source pointer! */ + result = targetExhausted; break; + } + if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { + if (flags == strictConversion) { + source -= (extraBytesToRead+1); /* return to the illegal value itself */ + result = sourceIllegal; + break; + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + *target++ = (UTF16)ch; /* normal case */ + } + } else if (ch > UNI_MAX_UTF16) { + if (flags == strictConversion) { + result = sourceIllegal; + source -= (extraBytesToRead+1); /* return to the start */ + break; /* Bail out; shouldn't continue */ + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + /* target is a character in range 0xFFFF - 0x10FFFF. */ + if (target + 1 >= targetEnd) { + source -= (extraBytesToRead+1); /* Back up source pointer! */ + result = targetExhausted; break; + } + ch -= halfBase; + *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); + *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); + } + } + *sourceStart = source; + *targetStart = target; + return result; +} + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF32toUTF8 ( + const UTF32** sourceStart, const UTF32* sourceEnd, + UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF32* source = *sourceStart; + UTF8* target = *targetStart; + while (source < sourceEnd) { + UTF32 ch; + unsigned short bytesToWrite = 0; + const UTF32 byteMask = 0xBF; + const UTF32 byteMark = 0x80; + ch = *source++; + if (flags == strictConversion ) { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } + /* + * Figure out how many bytes the result will require. Turn any + * illegally large UTF32 things (> Plane 17) into replacement chars. + */ + if (ch < (UTF32)0x80) { bytesToWrite = 1; + } else if (ch < (UTF32)0x800) { bytesToWrite = 2; + } else if (ch < (UTF32)0x10000) { bytesToWrite = 3; + } else if (ch <= UNI_MAX_LEGAL_UTF32) { bytesToWrite = 4; + } else { bytesToWrite = 3; + ch = UNI_REPLACEMENT_CHAR; + result = sourceIllegal; + } + + target += bytesToWrite; + if (target > targetEnd) { + --source; /* Back up source pointer! */ + target -= bytesToWrite; result = targetExhausted; break; + } + switch (bytesToWrite) { /* note: everything falls through. */ + case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; + case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; + case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; + case 1: *--target = (UTF8) (ch | firstByteMark[bytesToWrite]); + } + target += bytesToWrite; + } + *sourceStart = source; + *targetStart = target; + return result; +} + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF8toUTF32 ( + const UTF8** sourceStart, const UTF8* sourceEnd, + UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF8* source = *sourceStart; + UTF32* target = *targetStart; + while (source < sourceEnd) { + UTF32 ch = 0; + unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; + if (source + extraBytesToRead >= sourceEnd) { + result = sourceExhausted; break; + } + /* Do this check whether lenient or strict */ + if (! isLegalUTF8(source, extraBytesToRead+1)) { + result = sourceIllegal; + break; + } + /* + * The cases all fall through. See "Note A" below. + */ + switch (extraBytesToRead) { + case 5: ch += *source++; ch <<= 6; + case 4: ch += *source++; ch <<= 6; + case 3: ch += *source++; ch <<= 6; + case 2: ch += *source++; ch <<= 6; + case 1: ch += *source++; ch <<= 6; + case 0: ch += *source++; + } + ch -= offsetsFromUTF8[extraBytesToRead]; + + if (target >= targetEnd) { + source -= (extraBytesToRead+1); /* Back up the source pointer! */ + result = targetExhausted; break; + } + if (ch <= UNI_MAX_LEGAL_UTF32) { + /* + * UTF-16 surrogate values are illegal in UTF-32, and anything + * over Plane 17 (> 0x10FFFF) is illegal. + */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { + if (flags == strictConversion) { + source -= (extraBytesToRead+1); /* return to the illegal value itself */ + result = sourceIllegal; + break; + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + *target++ = ch; + } + } else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */ + result = sourceIllegal; + *target++ = UNI_REPLACEMENT_CHAR; + } + } + *sourceStart = source; + *targetStart = target; + return result; +} + +/* --------------------------------------------------------------------- + + Note A. + The fall-through switches in UTF-8 reading code save a + temp variable, some decrements & conditionals. The switches + are equivalent to the following loop: + { + int tmpBytesToRead = extraBytesToRead+1; + do { + ch += *source++; + --tmpBytesToRead; + if (tmpBytesToRead) ch <<= 6; + } while (tmpBytesToRead > 0); + } + In UTF-8 writing code, the switches on "bytesToWrite" are + similarly unrolled loops. + + --------------------------------------------------------------------- */ diff --git a/VrUtils/ini/ConvertUTF.h b/VrUtils/ini/ConvertUTF.h new file mode 100644 index 0000000..14d7b70 --- /dev/null +++ b/VrUtils/ini/ConvertUTF.h @@ -0,0 +1,149 @@ +/* + * Copyright 2001-2004 Unicode, Inc. + * + * Disclaimer + * + * This source code is provided as is by Unicode, Inc. No claims are + * made as to fitness for any particular purpose. No warranties of any + * kind are expressed or implied. The recipient agrees to determine + * applicability of information provided. If this file has been + * purchased on magnetic or optical media from Unicode, Inc., the + * sole remedy for any claim will be exchange of defective media + * within 90 days of receipt. + * + * Limitations on Rights to Redistribute This Code + * + * Unicode, Inc. hereby grants the right to freely use the information + * supplied in this file in the creation of products supporting the + * Unicode Standard, and to make copies of this file in any form + * for internal or external distribution as long as this notice + * remains attached. + */ + +/* --------------------------------------------------------------------- + + Conversions between UTF32, UTF-16, and UTF-8. Header file. + + Several funtions are included here, forming a complete set of + conversions between the three formats. UTF-7 is not included + here, but is handled in a separate source file. + + Each of these routines takes pointers to input buffers and output + buffers. The input buffers are const. + + Each routine converts the text between *sourceStart and sourceEnd, + putting the result into the buffer between *targetStart and + targetEnd. Note: the end pointers are *after* the last item: e.g. + *(sourceEnd - 1) is the last item. + + The return result indicates whether the conversion was successful, + and if not, whether the problem was in the source or target buffers. + (Only the first encountered problem is indicated.) + + After the conversion, *sourceStart and *targetStart are both + updated to point to the end of last text successfully converted in + the respective buffers. + + Input parameters: + sourceStart - pointer to a pointer to the source buffer. + The contents of this are modified on return so that + it points at the next thing to be converted. + targetStart - similarly, pointer to pointer to the target buffer. + sourceEnd, targetEnd - respectively pointers to the ends of the + two buffers, for overflow checking only. + + These conversion functions take a ConversionFlags argument. When this + flag is set to strict, both irregular sequences and isolated surrogates + will cause an error. When the flag is set to lenient, both irregular + sequences and isolated surrogates are converted. + + Whether the flag is strict or lenient, all illegal sequences will cause + an error return. This includes sequences such as: , , + or in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code + must check for illegal sequences. + + When the flag is set to lenient, characters over 0x10FFFF are converted + to the replacement character; otherwise (when the flag is set to strict) + they constitute an error. + + Output parameters: + The value "sourceIllegal" is returned from some routines if the input + sequence is malformed. When "sourceIllegal" is returned, the source + value will point to the illegal value that caused the problem. E.g., + in UTF-8 when a sequence is malformed, it points to the start of the + malformed sequence. + + Author: Mark E. Davis, 1994. + Rev History: Rick McGowan, fixes & updates May 2001. + Fixes & updates, Sept 2001. + +------------------------------------------------------------------------ */ + +/* --------------------------------------------------------------------- + The following 4 definitions are compiler-specific. + The C standard does not guarantee that wchar_t has at least + 16 bits, so wchar_t is no less portable than unsigned short! + All should be unsigned values to avoid sign extension during + bit mask & shift operations. +------------------------------------------------------------------------ */ + +typedef unsigned int UTF32; /* at least 32 bits */ +typedef unsigned short UTF16; /* at least 16 bits */ +typedef unsigned char UTF8; /* typically 8 bits */ +typedef unsigned char Boolean; /* 0 or 1 */ + +/* Some fundamental constants */ +#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD +#define UNI_MAX_BMP (UTF32)0x0000FFFF +#define UNI_MAX_UTF16 (UTF32)0x0010FFFF +#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF +#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF + +typedef enum { + conversionOK, /* conversion successful */ + sourceExhausted, /* partial character in source, but hit end */ + targetExhausted, /* insuff. room in target for conversion */ + sourceIllegal /* source sequence is illegal/malformed */ +} ConversionResult; + +typedef enum { + strictConversion = 0, + lenientConversion +} ConversionFlags; + +/* This is for C++ and does no harm in C */ +#ifdef __cplusplus +extern "C" { +#endif + +ConversionResult ConvertUTF8toUTF16 ( + const UTF8** sourceStart, const UTF8* sourceEnd, + UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags); + +ConversionResult ConvertUTF16toUTF8 ( + const UTF16** sourceStart, const UTF16* sourceEnd, + UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags); + +ConversionResult ConvertUTF8toUTF32 ( + const UTF8** sourceStart, const UTF8* sourceEnd, + UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); + +ConversionResult ConvertUTF32toUTF8 ( + const UTF32** sourceStart, const UTF32* sourceEnd, + UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags); + +ConversionResult ConvertUTF16toUTF32 ( + const UTF16** sourceStart, const UTF16* sourceEnd, + UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); + +ConversionResult ConvertUTF32toUTF16 ( + const UTF32** sourceStart, const UTF32* sourceEnd, + UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags); + +Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd); + +#ifdef __cplusplus +} +#endif + +/* --------------------------------------------------------------------- */ diff --git a/VrUtils/ini/SimpleIni.h b/VrUtils/ini/SimpleIni.h new file mode 100644 index 0000000..1a9d768 --- /dev/null +++ b/VrUtils/ini/SimpleIni.h @@ -0,0 +1,3625 @@ +/** @mainpage + + +
Library SimpleIni +
File SimpleIni.h +
Author Brodie Thiesfield +
Source https://github.com/brofield/simpleini +
Version 4.20 +
+ + Jump to the @link CSimpleIniTempl CSimpleIni @endlink interface documentation. + + @section intro INTRODUCTION + + This component allows an INI-style configuration file to be used on both + Windows and Linux/Unix. It is fast, simple and source code using this + component will compile unchanged on either OS. + + + @section features FEATURES + + - MIT Licence allows free use in all software (including GPL and commercial) + - multi-platform (Windows CE/9x/NT..10/etc, Linux, MacOSX, Unix) + - loading and saving of INI-style configuration files + - configuration files can have any newline format on all platforms + - liberal acceptance of file format + - key/values with no section + - removal of whitespace around sections, keys and values + - support for multi-line values (values with embedded newline characters) + - optional support for multiple keys with the same name + - optional case-insensitive sections and keys (for ASCII characters only) + - saves files with sections and keys in the same order as they were loaded + - preserves comments on the file, section and keys where possible. + - supports both char or wchar_t programming interfaces + - supports both MBCS (system locale) and UTF-8 file encodings + - system locale does not need to be UTF-8 on Linux/Unix to load UTF-8 file + - support for non-ASCII characters in section, keys, values and comments + - support for non-standard character types or file encodings + via user-written converter classes + - support for adding/modifying values programmatically + - compiles cleanly in the following compilers: + - Windows/VC6 (warning level 3) + - Windows/VC.NET 2003 (warning level 4) + - Windows/VC 2005 (warning level 4) + - Windows/VC 2019 (warning level 4) + - Linux/gcc (-Wall) + + + @section usage USAGE SUMMARY + + -# Decide if you will be using utf8 or MBCS files, and working with the + data in utf8, wchar_t or ICU chars. + -# If you will only be using straight utf8 files and access the data via the + char interface, then you do not need any conversion library and could define + SI_NO_CONVERSION. Note that no conversion also means no validation of the data. + If no converter is specified then the default converter is SI_CONVERT_GENERIC + on Mac/Linux and SI_CONVERT_WIN32 on Windows. If you need widechar support on + Mac/Linux then use either SI_CONVERT_GENERIC or SI_CONVERT_ICU. These are also + supported on all platforms. + -# Define the appropriate symbol for the converter you wish to use and + include the SimpleIni.h header file. + -# Declare an instance of the appropriate class. Note that the following + definitions are just shortcuts for commonly used types. Other types + (PRUnichar, unsigned short, unsigned char) are also possible. + +
Interface Case-sensitive Load UTF-8 Load MBCS Typedef +
SI_NO_CONVERSION +
char No Yes No CSimpleIniA +
char Yes Yes No CSimpleIniCaseA +
SI_CONVERT_GENERIC +
char No Yes Yes #1 CSimpleIniA +
char Yes Yes Yes CSimpleIniCaseA +
wchar_t No Yes Yes CSimpleIniW +
wchar_t Yes Yes Yes CSimpleIniCaseW +
SI_CONVERT_WIN32 +
char No No #2 Yes CSimpleIniA +
char Yes Yes Yes CSimpleIniCaseA +
wchar_t No Yes Yes CSimpleIniW +
wchar_t Yes Yes Yes CSimpleIniCaseW +
SI_CONVERT_ICU +
char No Yes Yes CSimpleIniA +
char Yes Yes Yes CSimpleIniCaseA +
UChar No Yes Yes CSimpleIniW +
UChar Yes Yes Yes CSimpleIniCaseW +
+ #1 On Windows you are better to use CSimpleIniA with SI_CONVERT_WIN32.
+ #2 Only affects Windows. On Windows this uses MBCS functions and + so may fold case incorrectly leading to uncertain results. + -# Call LoadData() or LoadFile() to load and parse the INI configuration file + -# Access and modify the data of the file using the following functions + +
GetAllSections Return all section names +
GetAllKeys Return all key names within a section +
GetAllValues Return all values within a section & key +
GetSection Return all key names and values in a section +
GetSectionSize Return the number of keys in a section +
GetValue Return a value for a section & key +
SetValue Add or update a value for a section & key +
Delete Remove a section, or a key from a section +
SectionExists Does a section exist? +
KeyExists Does a key exist? +
+ -# Call Save() or SaveFile() to save the INI configuration data + + @section iostreams IO STREAMS + + SimpleIni supports reading from and writing to STL IO streams. Enable this + by defining SI_SUPPORT_IOSTREAMS before including the SimpleIni.h header + file. Ensure that if the streams are backed by a file (e.g. ifstream or + ofstream) then the flag ios_base::binary has been used when the file was + opened. + + @section multiline MULTI-LINE VALUES + + Values that span multiple lines are created using the following format. + +
+        key = <<
+
+    Note the following:
+    - The text used for ENDTAG can be anything and is used to find
+      where the multi-line text ends.
+    - The newline after ENDTAG in the start tag, and the newline
+      before ENDTAG in the end tag is not included in the data value.
+    - The ending tag must be on it's own line with no whitespace before
+      or after it.
+    - The multi-line value is modified at load so that each line in the value
+      is delimited by a single '\\n' character on all platforms. At save time
+      it will be converted into the newline format used by the current
+      platform.
+
+    @section comments COMMENTS
+
+    Comments are preserved in the file within the following restrictions:
+    - Every file may have a single "file comment". It must start with the
+      first character in the file, and will end with the first non-comment
+      line in the file.
+    - Every section may have a single "section comment". It will start
+      with the first comment line following the file comment, or the last
+      data entry. It ends at the beginning of the section.
+    - Every key may have a single "key comment". This comment will start
+      with the first comment line following the section start, or the file
+      comment if there is no section name.
+    - Comments are set at the time that the file, section or key is first
+      created. The only way to modify a comment on a section or a key is to
+      delete that entry and recreate it with the new comment. There is no
+      way to change the file comment.
+
+    @section save SAVE ORDER
+
+    The sections and keys are written out in the same order as they were
+    read in from the file. Sections and keys added to the data after the
+    file has been loaded will be added to the end of the file when it is
+    written. There is no way to specify the location of a section or key
+    other than in first-created, first-saved order.
+
+    @section notes NOTES
+
+    - To load UTF-8 data on Windows 95, you need to use Microsoft Layer for
+      Unicode, or SI_CONVERT_GENERIC, or SI_CONVERT_ICU.
+    - When using SI_CONVERT_GENERIC, ConvertUTF.c must be compiled and linked.
+    - When using SI_CONVERT_ICU, ICU header files must be on the include
+      path and icuuc.lib must be linked in.
+    - To load a UTF-8 file on Windows AND expose it with SI_CHAR == char,
+      you should use SI_CONVERT_GENERIC.
+    - The collation (sorting) order used for sections and keys returned from
+      iterators is NOT DEFINED. If collation order of the text is important
+      then it should be done yourself by either supplying a replacement
+      SI_STRLESS class, or by sorting the strings external to this library.
+    - Usage of the  header on Windows can be disabled by defining
+      SI_NO_MBCS. This is defined automatically on Windows CE platforms.
+    - Not thread-safe so manage your own locking
+
+    @section contrib CONTRIBUTIONS
+    
+    - 2010/05/03: Tobias Gehrig: added GetDoubleValue()
+
+    @section licence MIT LICENCE
+
+    The licence text below is the boilerplate "MIT Licence" used from:
+    http://www.opensource.org/licenses/mit-license.php
+
+    Copyright (c) 2006-2012, Brodie Thiesfield
+
+    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 INCLUDED_SimpleIni_h
+#define INCLUDED_SimpleIni_h
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+# pragma once
+#endif
+
+// Disable these warnings in MSVC:
+//  4127 "conditional expression is constant" as the conversion classes trigger
+//  it with the statement if (sizeof(SI_CHAR) == sizeof(char)). This test will
+//  be optimized away in a release build.
+//  4503 'insert' : decorated name length exceeded, name was truncated
+//  4702 "unreachable code" as the MS STL header causes it in release mode.
+//  Again, the code causing the warning will be cleaned up by the compiler.
+//  4786 "identifier truncated to 256 characters" as this is thrown hundreds
+//  of times VC6 as soon as STL is used.
+#ifdef _MSC_VER
+# pragma warning (push)
+# pragma warning (disable: 4127 4503 4702 4786)
+#endif
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#ifdef SI_SUPPORT_IOSTREAMS
+# include 
+#endif // SI_SUPPORT_IOSTREAMS
+
+#ifdef _DEBUG
+# ifndef assert
+#  include 
+# endif
+# define SI_ASSERT(x)   assert(x)
+#else
+# define SI_ASSERT(x)
+#endif
+
+using SI_Error = int;
+
+constexpr int SI_OK = 0;        //!< No error
+constexpr int SI_UPDATED = 1;   //!< An existing value was updated
+constexpr int SI_INSERTED = 2;  //!< A new value was inserted
+
+// note: test for any error with (retval < 0)
+constexpr int SI_FAIL = -1;     //!< Generic failure
+constexpr int SI_NOMEM = -2;    //!< Out of memory error
+constexpr int SI_FILE = -3;     //!< File error (see errno for detail error)
+
+#define SI_UTF8_SIGNATURE     "\xEF\xBB\xBF"
+
+#ifdef _WIN32
+# define SI_NEWLINE_A   "\r\n"
+# define SI_NEWLINE_W   L"\r\n"
+#else // !_WIN32
+# define SI_NEWLINE_A   "\n"
+# define SI_NEWLINE_W   L"\n"
+#endif // _WIN32
+
+#if defined(SI_CONVERT_ICU)
+# include 
+#endif
+
+#if defined(_WIN32)
+# define SI_HAS_WIDE_FILE
+# define SI_WCHAR_T     wchar_t
+#elif defined(SI_CONVERT_ICU)
+# define SI_HAS_WIDE_FILE
+# define SI_WCHAR_T     UChar
+#endif
+
+
+// ---------------------------------------------------------------------------
+//                              MAIN TEMPLATE CLASS
+// ---------------------------------------------------------------------------
+
+/** Simple INI file reader.
+
+    This can be instantiated with the choice of unicode or native characterset,
+    and case sensitive or insensitive comparisons of section and key names.
+    The supported combinations are pre-defined with the following typedefs:
+
+    
+        
Interface Case-sensitive Typedef +
char No CSimpleIniA +
char Yes CSimpleIniCaseA +
wchar_t No CSimpleIniW +
wchar_t Yes CSimpleIniCaseW +
+ + Note that using other types for the SI_CHAR is supported. For instance, + unsigned char, unsigned short, etc. Note that where the alternative type + is a different size to char/wchar_t you may need to supply new helper + classes for SI_STRLESS and SI_CONVERTER. + */ +template +class CSimpleIniTempl +{ +public: + typedef SI_CHAR SI_CHAR_T; + + /** key entry */ + struct Entry { + const SI_CHAR * pItem; + const SI_CHAR * pComment; + int nOrder; + + Entry(const SI_CHAR * a_pszItem = NULL, int a_nOrder = 0) + : pItem(a_pszItem) + , pComment(NULL) + , nOrder(a_nOrder) + { } + Entry(const SI_CHAR * a_pszItem, const SI_CHAR * a_pszComment, int a_nOrder) + : pItem(a_pszItem) + , pComment(a_pszComment) + , nOrder(a_nOrder) + { } + Entry(const Entry & rhs) { operator=(rhs); } + Entry & operator=(const Entry & rhs) { + pItem = rhs.pItem; + pComment = rhs.pComment; + nOrder = rhs.nOrder; + return *this; + } + +#if defined(_MSC_VER) && _MSC_VER <= 1200 + /** STL of VC6 doesn't allow me to specify my own comparator for list::sort() */ + bool operator<(const Entry & rhs) const { return LoadOrder()(*this, rhs); } + bool operator>(const Entry & rhs) const { return LoadOrder()(rhs, *this); } +#endif + + /** Strict less ordering by name of key only */ + struct KeyOrder { + bool operator()(const Entry & lhs, const Entry & rhs) const { + const static SI_STRLESS isLess = SI_STRLESS(); + return isLess(lhs.pItem, rhs.pItem); + } + }; + + /** Strict less ordering by order, and then name of key */ + struct LoadOrder { + bool operator()(const Entry & lhs, const Entry & rhs) const { + if (lhs.nOrder != rhs.nOrder) { + return lhs.nOrder < rhs.nOrder; + } + return KeyOrder()(lhs.pItem, rhs.pItem); + } + }; + }; + + /** map keys to values */ + typedef std::multimap TKeyVal; + + /** map sections to key/value map */ + typedef std::map TSection; + + /** set of dependent string pointers. Note that these pointers are + dependent on memory owned by CSimpleIni. + */ + typedef std::list TNamesDepend; + + /** interface definition for the OutputWriter object to pass to Save() + in order to output the INI file data. + */ + class OutputWriter { + public: + OutputWriter() { } + virtual ~OutputWriter() { } + virtual void Write(const char * a_pBuf) = 0; + private: + OutputWriter(const OutputWriter &); // disable + OutputWriter & operator=(const OutputWriter &); // disable + }; + + /** OutputWriter class to write the INI data to a file */ + class FileWriter : public OutputWriter { + FILE * m_file; + public: + FileWriter(FILE * a_file) : m_file(a_file) { } + void Write(const char * a_pBuf) { + fputs(a_pBuf, m_file); + } + private: + FileWriter(const FileWriter &); // disable + FileWriter & operator=(const FileWriter &); // disable + }; + + /** OutputWriter class to write the INI data to a string */ + class StringWriter : public OutputWriter { + std::string & m_string; + public: + StringWriter(std::string & a_string) : m_string(a_string) { } + void Write(const char * a_pBuf) { + m_string.append(a_pBuf); + } + private: + StringWriter(const StringWriter &); // disable + StringWriter & operator=(const StringWriter &); // disable + }; + +#ifdef SI_SUPPORT_IOSTREAMS + /** OutputWriter class to write the INI data to an ostream */ + class StreamWriter : public OutputWriter { + std::ostream & m_ostream; + public: + StreamWriter(std::ostream & a_ostream) : m_ostream(a_ostream) { } + void Write(const char * a_pBuf) { + m_ostream << a_pBuf; + } + private: + StreamWriter(const StreamWriter &); // disable + StreamWriter & operator=(const StreamWriter &); // disable + }; +#endif // SI_SUPPORT_IOSTREAMS + + /** Characterset conversion utility class to convert strings to the + same format as is used for the storage. + */ + class Converter : private SI_CONVERTER { + public: + Converter(bool a_bStoreIsUtf8) : SI_CONVERTER(a_bStoreIsUtf8) { + m_scratch.resize(1024); + } + Converter(const Converter & rhs) { operator=(rhs); } + Converter & operator=(const Converter & rhs) { + m_scratch = rhs.m_scratch; + return *this; + } + bool ConvertToStore(const SI_CHAR * a_pszString) { + size_t uLen = SI_CONVERTER::SizeToStore(a_pszString); + if (uLen == (size_t)(-1)) { + return false; + } + while (uLen > m_scratch.size()) { + m_scratch.resize(m_scratch.size() * 2); + } + return SI_CONVERTER::ConvertToStore( + a_pszString, + const_cast(m_scratch.data()), + m_scratch.size()); + } + const char * Data() { return m_scratch.data(); } + private: + std::string m_scratch; + }; + +public: + /*-----------------------------------------------------------------------*/ + + /** Default constructor. + + @param a_bIsUtf8 See the method SetUnicode() for details. + @param a_bMultiKey See the method SetMultiKey() for details. + @param a_bMultiLine See the method SetMultiLine() for details. + */ + CSimpleIniTempl( + bool a_bIsUtf8 = false, + bool a_bMultiKey = false, + bool a_bMultiLine = false + ); + + /** Destructor */ + ~CSimpleIniTempl(); + + /** Deallocate all memory stored by this object */ + void Reset(); + + /** Has any data been loaded */ + bool IsEmpty() const { return m_data.empty(); } + + /*-----------------------------------------------------------------------*/ + /** @{ @name Settings */ + + /** Set the storage format of the INI data. This affects both the loading + and saving of the INI data using all of the Load/Save API functions. + This value cannot be changed after any INI data has been loaded. + + If the file is not set to Unicode (UTF-8), then the data encoding is + assumed to be the OS native encoding. This encoding is the system + locale on Linux/Unix and the legacy MBCS encoding on Windows NT/2K/XP. + If the storage format is set to Unicode then the file will be loaded + as UTF-8 encoded data regardless of the native file encoding. If + SI_CHAR == char then all of the char* parameters take and return UTF-8 + encoded data regardless of the system locale. + + \param a_bIsUtf8 Assume UTF-8 encoding for the source? + */ + void SetUnicode(bool a_bIsUtf8 = true) { + if (!m_pData) m_bStoreIsUtf8 = a_bIsUtf8; + } + + /** Get the storage format of the INI data. */ + bool IsUnicode() const { return m_bStoreIsUtf8; } + + /** Should multiple identical keys be permitted in the file. If set to false + then the last value encountered will be used as the value of the key. + If set to true, then all values will be available to be queried. For + example, with the following input: + +
+        [section]
+        test=value1
+        test=value2
+        
+ + Then with SetMultiKey(true), both of the values "value1" and "value2" + will be returned for the key test. If SetMultiKey(false) is used, then + the value for "test" will only be "value2". This value may be changed + at any time. + + \param a_bAllowMultiKey Allow multi-keys in the source? + */ + void SetMultiKey(bool a_bAllowMultiKey = true) { + m_bAllowMultiKey = a_bAllowMultiKey; + } + + /** Get the storage format of the INI data. */ + bool IsMultiKey() const { return m_bAllowMultiKey; } + + /** Should data values be permitted to span multiple lines in the file. If + set to false then the multi-line construct << + SI_CHAR FORMAT + char same format as when loaded (MBCS or UTF-8) + wchar_t UTF-8 + other UTF-8 + + + Note that comments from the original data is preserved as per the + documentation on comments. The order of the sections and values + from the original file will be preserved. + + Any data prepended or appended to the output device must use the the + same format (MBCS or UTF-8). You may use the GetConverter() method to + convert text to the correct format regardless of the output format + being used by SimpleIni. + + To add a BOM to UTF-8 data, write it out manually at the very beginning + like is done in SaveFile when a_bUseBOM is true. + + @param a_oOutput Output writer to write the data to. + + @param a_bAddSignature Prepend the UTF-8 BOM if the output data is in + UTF-8 format. If it is not UTF-8 then this value is + ignored. Do not set this to true if anything has + already been written to the OutputWriter. + + @return SI_Error See error definitions + */ + SI_Error Save( + OutputWriter & a_oOutput, + bool a_bAddSignature = false + ) const; + +#ifdef SI_SUPPORT_IOSTREAMS + /** Save the INI data to an ostream. See Save() for details. + + @param a_ostream String to have the INI data appended to. + + @param a_bAddSignature Prepend the UTF-8 BOM if the output data is in + UTF-8 format. If it is not UTF-8 then this value is + ignored. Do not set this to true if anything has + already been written to the stream. + + @return SI_Error See error definitions + */ + SI_Error Save( + std::ostream & a_ostream, + bool a_bAddSignature = false + ) const + { + StreamWriter writer(a_ostream); + return Save(writer, a_bAddSignature); + } +#endif // SI_SUPPORT_IOSTREAMS + + /** Append the INI data to a string. See Save() for details. + + @param a_sBuffer String to have the INI data appended to. + + @param a_bAddSignature Prepend the UTF-8 BOM if the output data is in + UTF-8 format. If it is not UTF-8 then this value is + ignored. Do not set this to true if anything has + already been written to the string. + + @return SI_Error See error definitions + */ + SI_Error Save( + std::string & a_sBuffer, + bool a_bAddSignature = false + ) const + { + StringWriter writer(a_sBuffer); + return Save(writer, a_bAddSignature); + } + + /*-----------------------------------------------------------------------*/ + /** @} + @{ @name Accessing INI Data */ + + /** Retrieve all section names. The list is returned as an STL vector of + names and can be iterated or searched as necessary. Note that the + sort order of the returned strings is NOT DEFINED. You can sort + the names into the load order if desired. Search this file for ".sort" + for an example. + + NOTE! This structure contains only pointers to strings. The actual + string data is stored in memory owned by CSimpleIni. Ensure that the + CSimpleIni object is not destroyed or Reset() while these pointers + are in use! + + @param a_names Vector that will receive all of the section + names. See note above! + */ + void GetAllSections( + TNamesDepend & a_names + ) const; + + /** Retrieve all unique key names in a section. The sort order of the + returned strings is NOT DEFINED. You can sort the names into the load + order if desired. Search this file for ".sort" for an example. Only + unique key names are returned. + + NOTE! This structure contains only pointers to strings. The actual + string data is stored in memory owned by CSimpleIni. Ensure that the + CSimpleIni object is not destroyed or Reset() while these strings + are in use! + + @param a_pSection Section to request data for + @param a_names List that will receive all of the key + names. See note above! + + @return true Section was found. + @return false Matching section was not found. + */ + bool GetAllKeys( + const SI_CHAR * a_pSection, + TNamesDepend & a_names + ) const; + + /** Retrieve all values for a specific key. This method can be used when + multiple keys are both enabled and disabled. Note that the sort order + of the returned strings is NOT DEFINED. You can sort the names into + the load order if desired. Search this file for ".sort" for an example. + + NOTE! The returned values are pointers to string data stored in memory + owned by CSimpleIni. Ensure that the CSimpleIni object is not destroyed + or Reset while you are using this pointer! + + @param a_pSection Section to search + @param a_pKey Key to search for + @param a_values List to return if the key is not found + + @return true Key was found. + @return false Matching section/key was not found. + */ + bool GetAllValues( + const SI_CHAR * a_pSection, + const SI_CHAR * a_pKey, + TNamesDepend & a_values + ) const; + + /** Query the number of keys in a specific section. Note that if multiple + keys are enabled, then this value may be different to the number of + keys returned by GetAllKeys. + + @param a_pSection Section to request data for + + @return -1 Section does not exist in the file + @return >=0 Number of keys in the section + */ + int GetSectionSize( + const SI_CHAR * a_pSection + ) const; + + /** Retrieve all key and value pairs for a section. The data is returned + as a pointer to an STL map and can be iterated or searched as + desired. Note that multiple entries for the same key may exist when + multiple keys have been enabled. + + NOTE! This structure contains only pointers to strings. The actual + string data is stored in memory owned by CSimpleIni. Ensure that the + CSimpleIni object is not destroyed or Reset() while these strings + are in use! + + @param a_pSection Name of the section to return + @return Section data + */ + const TKeyVal * GetSection( + const SI_CHAR * a_pSection + ) const; + + /** Test if a section exists. Convenience function */ + inline bool SectionExists( + const SI_CHAR * a_pSection + ) const { + return GetSection(a_pSection) != NULL; + } + + /** Test if the key exists in a section. Convenience function. */ + inline bool KeyExists( + const SI_CHAR * a_pSection, + const SI_CHAR * a_pKey + ) const { + return GetValue(a_pSection, a_pKey) != NULL; + } + + /** Retrieve the value for a specific key. If multiple keys are enabled + (see SetMultiKey) then only the first value associated with that key + will be returned, see GetAllValues for getting all values with multikey. + + NOTE! The returned value is a pointer to string data stored in memory + owned by CSimpleIni. Ensure that the CSimpleIni object is not destroyed + or Reset while you are using this pointer! + + @param a_pSection Section to search + @param a_pKey Key to search for + @param a_pDefault Value to return if the key is not found + @param a_pHasMultiple Optionally receive notification of if there are + multiple entries for this key. + + @return a_pDefault Key was not found in the section + @return other Value of the key + */ + const SI_CHAR * GetValue( + const SI_CHAR * a_pSection, + const SI_CHAR * a_pKey, + const SI_CHAR * a_pDefault = NULL, + bool * a_pHasMultiple = NULL + ) const; + + /** Retrieve a numeric value for a specific key. If multiple keys are enabled + (see SetMultiKey) then only the first value associated with that key + will be returned, see GetAllValues for getting all values with multikey. + + @param a_pSection Section to search + @param a_pKey Key to search for + @param a_nDefault Value to return if the key is not found + @param a_pHasMultiple Optionally receive notification of if there are + multiple entries for this key. + + @return a_nDefault Key was not found in the section + @return other Value of the key + */ + long GetLongValue( + const SI_CHAR * a_pSection, + const SI_CHAR * a_pKey, + long a_nDefault = 0, + bool * a_pHasMultiple = NULL + ) const; + + /** Retrieve a numeric value for a specific key. If multiple keys are enabled + (see SetMultiKey) then only the first value associated with that key + will be returned, see GetAllValues for getting all values with multikey. + + @param a_pSection Section to search + @param a_pKey Key to search for + @param a_nDefault Value to return if the key is not found + @param a_pHasMultiple Optionally receive notification of if there are + multiple entries for this key. + + @return a_nDefault Key was not found in the section + @return other Value of the key + */ + double GetDoubleValue( + const SI_CHAR * a_pSection, + const SI_CHAR * a_pKey, + double a_nDefault = 0, + bool * a_pHasMultiple = NULL + ) const; + + /** Retrieve a boolean value for a specific key. If multiple keys are enabled + (see SetMultiKey) then only the first value associated with that key + will be returned, see GetAllValues for getting all values with multikey. + + Strings starting with "t", "y", "on" or "1" are returned as logically true. + Strings starting with "f", "n", "of" or "0" are returned as logically false. + For all other values the default is returned. Character comparisons are + case-insensitive. + + @param a_pSection Section to search + @param a_pKey Key to search for + @param a_bDefault Value to return if the key is not found + @param a_pHasMultiple Optionally receive notification of if there are + multiple entries for this key. + + @return a_nDefault Key was not found in the section + @return other Value of the key + */ + bool GetBoolValue( + const SI_CHAR * a_pSection, + const SI_CHAR * a_pKey, + bool a_bDefault = false, + bool * a_pHasMultiple = NULL + ) const; + + /** Add or update a section or value. This will always insert + when multiple keys are enabled. + + @param a_pSection Section to add or update + @param a_pKey Key to add or update. Set to NULL to + create an empty section. + @param a_pValue Value to set. Set to NULL to create an + empty section. + @param a_pComment Comment to be associated with the section or the + key. If a_pKey is NULL then it will be associated + with the section, otherwise the key. Note that a + comment may be set ONLY when the section or key is + first created (i.e. when this function returns the + value SI_INSERTED). If you wish to create a section + with a comment then you need to create the section + separately to the key. The comment string must be + in full comment form already (have a comment + character starting every line). + @param a_bForceReplace Should all existing values in a multi-key INI + file be replaced with this entry. This option has + no effect if not using multi-key files. The + difference between Delete/SetValue and SetValue + with a_bForceReplace = true, is that the load + order and comment will be preserved this way. + + @return SI_Error See error definitions + @return SI_UPDATED Value was updated + @return SI_INSERTED Value was inserted + */ + SI_Error SetValue( + const SI_CHAR * a_pSection, + const SI_CHAR * a_pKey, + const SI_CHAR * a_pValue, + const SI_CHAR * a_pComment = NULL, + bool a_bForceReplace = false + ) + { + return AddEntry(a_pSection, a_pKey, a_pValue, a_pComment, a_bForceReplace, true); + } + + /** Add or update a numeric value. This will always insert + when multiple keys are enabled. + + @param a_pSection Section to add or update + @param a_pKey Key to add or update. + @param a_nValue Value to set. + @param a_pComment Comment to be associated with the key. See the + notes on SetValue() for comments. + @param a_bUseHex By default the value will be written to the file + in decimal format. Set this to true to write it + as hexadecimal. + @param a_bForceReplace Should all existing values in a multi-key INI + file be replaced with this entry. This option has + no effect if not using multi-key files. The + difference between Delete/SetLongValue and + SetLongValue with a_bForceReplace = true, is that + the load order and comment will be preserved this + way. + + @return SI_Error See error definitions + @return SI_UPDATED Value was updated + @return SI_INSERTED Value was inserted + */ + SI_Error SetLongValue( + const SI_CHAR * a_pSection, + const SI_CHAR * a_pKey, + long a_nValue, + const SI_CHAR * a_pComment = NULL, + bool a_bUseHex = false, + bool a_bForceReplace = false + ); + + /** Add or update a double value. This will always insert + when multiple keys are enabled. + + @param a_pSection Section to add or update + @param a_pKey Key to add or update. + @param a_nValue Value to set. + @param a_pComment Comment to be associated with the key. See the + notes on SetValue() for comments. + @param a_bForceReplace Should all existing values in a multi-key INI + file be replaced with this entry. This option has + no effect if not using multi-key files. The + difference between Delete/SetDoubleValue and + SetDoubleValue with a_bForceReplace = true, is that + the load order and comment will be preserved this + way. + + @return SI_Error See error definitions + @return SI_UPDATED Value was updated + @return SI_INSERTED Value was inserted + */ + SI_Error SetDoubleValue( + const SI_CHAR * a_pSection, + const SI_CHAR * a_pKey, + double a_nValue, + const SI_CHAR * a_pComment = NULL, + bool a_bForceReplace = false + ); + + /** Add or update a boolean value. This will always insert + when multiple keys are enabled. + + @param a_pSection Section to add or update + @param a_pKey Key to add or update. + @param a_bValue Value to set. + @param a_pComment Comment to be associated with the key. See the + notes on SetValue() for comments. + @param a_bForceReplace Should all existing values in a multi-key INI + file be replaced with this entry. This option has + no effect if not using multi-key files. The + difference between Delete/SetBoolValue and + SetBoolValue with a_bForceReplace = true, is that + the load order and comment will be preserved this + way. + + @return SI_Error See error definitions + @return SI_UPDATED Value was updated + @return SI_INSERTED Value was inserted + */ + SI_Error SetBoolValue( + const SI_CHAR * a_pSection, + const SI_CHAR * a_pKey, + bool a_bValue, + const SI_CHAR * a_pComment = NULL, + bool a_bForceReplace = false + ); + + /** Delete an entire section, or a key from a section. Note that the + data returned by GetSection is invalid and must not be used after + anything has been deleted from that section using this method. + Note when multiple keys is enabled, this will delete all keys with + that name; to selectively delete individual key/values, use + DeleteValue. + + @param a_pSection Section to delete key from, or if + a_pKey is NULL, the section to remove. + @param a_pKey Key to remove from the section. Set to + NULL to remove the entire section. + @param a_bRemoveEmpty If the section is empty after this key has + been deleted, should the empty section be + removed? + + @return true Key or section was deleted. + @return false Key or section was not found. + */ + bool Delete( + const SI_CHAR * a_pSection, + const SI_CHAR * a_pKey, + bool a_bRemoveEmpty = false + ); + + /** Delete an entire section, or a key from a section. If value is + provided, only remove keys with the value. Note that the data + returned by GetSection is invalid and must not be used after + anything has been deleted from that section using this method. + Note when multiple keys is enabled, all keys with the value will + be deleted. + + @param a_pSection Section to delete key from, or if + a_pKey is NULL, the section to remove. + @param a_pKey Key to remove from the section. Set to + NULL to remove the entire section. + @param a_pValue Value of key to remove from the section. + Set to NULL to remove all keys. + @param a_bRemoveEmpty If the section is empty after this key has + been deleted, should the empty section be + removed? + + @return true Key/value or section was deleted. + @return false Key/value or section was not found. + */ + bool DeleteValue( + const SI_CHAR * a_pSection, + const SI_CHAR * a_pKey, + const SI_CHAR * a_pValue, + bool a_bRemoveEmpty = false + ); + + /*-----------------------------------------------------------------------*/ + /** @} + @{ @name Converter */ + + /** Return a conversion object to convert text to the same encoding + as is used by the Save(), SaveFile() and SaveString() functions. + Use this to prepare the strings that you wish to append or prepend + to the output INI data. + */ + Converter GetConverter() const { + return Converter(m_bStoreIsUtf8); + } + + /*-----------------------------------------------------------------------*/ + /** @} */ + +private: + // copying is not permitted + CSimpleIniTempl(const CSimpleIniTempl &); // disabled + CSimpleIniTempl & operator=(const CSimpleIniTempl &); // disabled + + /** Parse the data looking for a file comment and store it if found. + */ + SI_Error FindFileComment( + SI_CHAR *& a_pData, + bool a_bCopyStrings + ); + + /** Parse the data looking for the next valid entry. The memory pointed to + by a_pData is modified by inserting NULL characters. The pointer is + updated to the current location in the block of text. + */ + bool FindEntry( + SI_CHAR *& a_pData, + const SI_CHAR *& a_pSection, + const SI_CHAR *& a_pKey, + const SI_CHAR *& a_pVal, + const SI_CHAR *& a_pComment + ) const; + + /** Add the section/key/value to our data. + + @param a_pSection Section name. Sections will be created if they + don't already exist. + @param a_pKey Key name. May be NULL to create an empty section. + Existing entries will be updated. New entries will + be created. + @param a_pValue Value for the key. + @param a_pComment Comment to be associated with the section or the + key. If a_pKey is NULL then it will be associated + with the section, otherwise the key. This must be + a string in full comment form already (have a + comment character starting every line). + @param a_bForceReplace Should all existing values in a multi-key INI + file be replaced with this entry. This option has + no effect if not using multi-key files. The + difference between Delete/AddEntry and AddEntry + with a_bForceReplace = true, is that the load + order and comment will be preserved this way. + @param a_bCopyStrings Should copies of the strings be made or not. + If false then the pointers will be used as is. + */ + SI_Error AddEntry( + const SI_CHAR * a_pSection, + const SI_CHAR * a_pKey, + const SI_CHAR * a_pValue, + const SI_CHAR * a_pComment, + bool a_bForceReplace, + bool a_bCopyStrings + ); + + /** Is the supplied character a whitespace character? */ + inline bool IsSpace(SI_CHAR ch) const { + return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'); + } + + /** Does the supplied character start a comment line? */ + inline bool IsComment(SI_CHAR ch) const { + return (ch == ';' || ch == '#'); + } + + + /** Skip over a newline character (or characters) for either DOS or UNIX */ + inline void SkipNewLine(SI_CHAR *& a_pData) const { + a_pData += (*a_pData == '\r' && *(a_pData+1) == '\n') ? 2 : 1; + } + + /** Make a copy of the supplied string, replacing the original pointer */ + SI_Error CopyString(const SI_CHAR *& a_pString); + + /** Delete a string from the copied strings buffer if necessary */ + void DeleteString(const SI_CHAR * a_pString); + + /** Internal use of our string comparison function */ + bool IsLess(const SI_CHAR * a_pLeft, const SI_CHAR * a_pRight) const { + const static SI_STRLESS isLess = SI_STRLESS(); + return isLess(a_pLeft, a_pRight); + } + + bool IsMultiLineTag(const SI_CHAR * a_pData) const; + bool IsMultiLineData(const SI_CHAR * a_pData) const; + bool IsSingleLineQuotedValue(const SI_CHAR* a_pData) const; + bool LoadMultiLineText( + SI_CHAR *& a_pData, + const SI_CHAR *& a_pVal, + const SI_CHAR * a_pTagName, + bool a_bAllowBlankLinesInComment = false + ) const; + bool IsNewLineChar(SI_CHAR a_c) const; + + bool OutputMultiLineText( + OutputWriter & a_oOutput, + Converter & a_oConverter, + const SI_CHAR * a_pText + ) const; + +private: + /** Copy of the INI file data in our character format. This will be + modified when parsed to have NULL characters added after all + interesting string entries. All of the string pointers to sections, + keys and values point into this block of memory. + */ + SI_CHAR * m_pData; + + /** Length of the data that we have stored. Used when deleting strings + to determine if the string is stored here or in the allocated string + buffer. + */ + size_t m_uDataLen; + + /** File comment for this data, if one exists. */ + const SI_CHAR * m_pFileComment; + + /** constant empty string */ + const SI_CHAR m_cEmptyString; + + /** Parsed INI data. Section -> (Key -> Value). */ + TSection m_data; + + /** This vector stores allocated memory for copies of strings that have + been supplied after the file load. It will be empty unless SetValue() + has been called. + */ + TNamesDepend m_strings; + + /** Is the format of our datafile UTF-8 or MBCS? */ + bool m_bStoreIsUtf8; + + /** Are multiple values permitted for the same key? */ + bool m_bAllowMultiKey; + + /** Are data values permitted to span multiple lines? */ + bool m_bAllowMultiLine; + + /** Should spaces be written out surrounding the equals sign? */ + bool m_bSpaces; + + /** Should quoted data in values be recognized and parsed? */ + bool m_bParseQuotes; + + /** Do keys always need to have an equals sign when reading/writing? */ + bool m_bAllowKeyOnly; + + /** Next order value, used to ensure sections and keys are output in the + same order that they are loaded/added. + */ + int m_nOrder; +}; + +// --------------------------------------------------------------------------- +// IMPLEMENTATION +// --------------------------------------------------------------------------- + +template +CSimpleIniTempl::CSimpleIniTempl( + bool a_bIsUtf8, + bool a_bAllowMultiKey, + bool a_bAllowMultiLine + ) + : m_pData(0) + , m_uDataLen(0) + , m_pFileComment(NULL) + , m_cEmptyString(0) + , m_bStoreIsUtf8(a_bIsUtf8) + , m_bAllowMultiKey(a_bAllowMultiKey) + , m_bAllowMultiLine(a_bAllowMultiLine) + , m_bSpaces(true) + , m_bParseQuotes(false) + , m_bAllowKeyOnly(false) + , m_nOrder(0) +{ } + +template +CSimpleIniTempl::~CSimpleIniTempl() +{ + Reset(); +} + +template +void +CSimpleIniTempl::Reset() +{ + // remove all data + delete[] m_pData; + m_pData = NULL; + m_uDataLen = 0; + m_pFileComment = NULL; + if (!m_data.empty()) { + m_data.erase(m_data.begin(), m_data.end()); + } + + // remove all strings + if (!m_strings.empty()) { + typename TNamesDepend::iterator i = m_strings.begin(); + for (; i != m_strings.end(); ++i) { + delete[] const_cast(i->pItem); + } + m_strings.erase(m_strings.begin(), m_strings.end()); + } +} + +template +SI_Error +CSimpleIniTempl::LoadFile( + const char * a_pszFile + ) +{ + FILE * fp = NULL; +#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE + fopen_s(&fp, a_pszFile, "rb"); +#else // !__STDC_WANT_SECURE_LIB__ + fp = fopen(a_pszFile, "rb"); +#endif // __STDC_WANT_SECURE_LIB__ + if (!fp) { + return SI_FILE; + } + SI_Error rc = LoadFile(fp); + fclose(fp); + return rc; +} + +#ifdef SI_HAS_WIDE_FILE +template +SI_Error +CSimpleIniTempl::LoadFile( + const SI_WCHAR_T * a_pwszFile + ) +{ +#ifdef _WIN32 + FILE * fp = NULL; +#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE + _wfopen_s(&fp, a_pwszFile, L"rb"); +#else // !__STDC_WANT_SECURE_LIB__ + fp = _wfopen(a_pwszFile, L"rb"); +#endif // __STDC_WANT_SECURE_LIB__ + if (!fp) return SI_FILE; + SI_Error rc = LoadFile(fp); + fclose(fp); + return rc; +#else // !_WIN32 (therefore SI_CONVERT_ICU) + char szFile[256]; + u_austrncpy(szFile, a_pwszFile, sizeof(szFile)); + return LoadFile(szFile); +#endif // _WIN32 +} +#endif // SI_HAS_WIDE_FILE + +template +SI_Error +CSimpleIniTempl::LoadFile( + FILE * a_fpFile + ) +{ + // load the raw file data + int retval = fseek(a_fpFile, 0, SEEK_END); + if (retval != 0) { + return SI_FILE; + } + long lSize = ftell(a_fpFile); + if (lSize < 0) { + return SI_FILE; + } + if (lSize == 0) { + return SI_OK; + } + + // allocate and ensure NULL terminated + char * pData = new(std::nothrow) char[lSize+static_cast(1)]; + if (!pData) { + return SI_NOMEM; + } + pData[lSize] = 0; + + // load data into buffer + fseek(a_fpFile, 0, SEEK_SET); + size_t uRead = fread(pData, sizeof(char), lSize, a_fpFile); + if (uRead != (size_t) lSize) { + delete[] pData; + return SI_FILE; + } + + // convert the raw data to unicode + SI_Error rc = LoadData(pData, uRead); + delete[] pData; + return rc; +} + +template +SI_Error +CSimpleIniTempl::LoadData( + const char * a_pData, + size_t a_uDataLen + ) +{ + if (!a_pData) { + return SI_OK; + } + + // if the UTF-8 BOM exists, consume it and set mode to unicode, if we have + // already loaded data and try to change mode half-way through then this will + // be ignored and we will assert in debug versions + if (a_uDataLen >= 3 && memcmp(a_pData, SI_UTF8_SIGNATURE, 3) == 0) { + a_pData += 3; + a_uDataLen -= 3; + SI_ASSERT(m_bStoreIsUtf8 || !m_pData); // we don't expect mixed mode data + SetUnicode(); + } + + if (a_uDataLen == 0) { + return SI_OK; + } + + // determine the length of the converted data + SI_CONVERTER converter(m_bStoreIsUtf8); + size_t uLen = converter.SizeFromStore(a_pData, a_uDataLen); + if (uLen == (size_t)(-1)) { + return SI_FAIL; + } + + // allocate memory for the data, ensure that there is a NULL + // terminator wherever the converted data ends + SI_CHAR * pData = new(std::nothrow) SI_CHAR[uLen+1]; + if (!pData) { + return SI_NOMEM; + } + memset(pData, 0, sizeof(SI_CHAR)*(uLen+1)); + + // convert the data + if (!converter.ConvertFromStore(a_pData, a_uDataLen, pData, uLen)) { + delete[] pData; + return SI_FAIL; + } + + // parse it + const static SI_CHAR empty = 0; + SI_CHAR * pWork = pData; + const SI_CHAR * pSection = ∅ + const SI_CHAR * pItem = NULL; + const SI_CHAR * pVal = NULL; + const SI_CHAR * pComment = NULL; + + // We copy the strings if we are loading data into this class when we + // already have stored some. + bool bCopyStrings = (m_pData != NULL); + + // find a file comment if it exists, this is a comment that starts at the + // beginning of the file and continues until the first blank line. + SI_Error rc = FindFileComment(pWork, bCopyStrings); + if (rc < 0) return rc; + + // add every entry in the file to the data table + while (FindEntry(pWork, pSection, pItem, pVal, pComment)) { + rc = AddEntry(pSection, pItem, pVal, pComment, false, bCopyStrings); + if (rc < 0) return rc; + } + + // store these strings if we didn't copy them + if (bCopyStrings) { + delete[] pData; + } + else { + m_pData = pData; + m_uDataLen = uLen+1; + } + + return SI_OK; +} + +#ifdef SI_SUPPORT_IOSTREAMS +template +SI_Error +CSimpleIniTempl::LoadData( + std::istream & a_istream + ) +{ + std::string strData; + char szBuf[512]; + do { + a_istream.get(szBuf, sizeof(szBuf), '\0'); + strData.append(szBuf); + } + while (a_istream.good()); + return LoadData(strData); +} +#endif // SI_SUPPORT_IOSTREAMS + +template +SI_Error +CSimpleIniTempl::FindFileComment( + SI_CHAR *& a_pData, + bool a_bCopyStrings + ) +{ + // there can only be a single file comment + if (m_pFileComment) { + return SI_OK; + } + + // Load the file comment as multi-line text, this will modify all of + // the newline characters to be single \n chars + if (!LoadMultiLineText(a_pData, m_pFileComment, NULL, false)) { + return SI_OK; + } + + // copy the string if necessary + if (a_bCopyStrings) { + SI_Error rc = CopyString(m_pFileComment); + if (rc < 0) return rc; + } + + return SI_OK; +} + +template +bool +CSimpleIniTempl::FindEntry( + SI_CHAR *& a_pData, + const SI_CHAR *& a_pSection, + const SI_CHAR *& a_pKey, + const SI_CHAR *& a_pVal, + const SI_CHAR *& a_pComment + ) const +{ + a_pComment = NULL; + + bool bHaveValue = false; + SI_CHAR * pTrail = NULL; + while (*a_pData) { + // skip spaces and empty lines + while (*a_pData && IsSpace(*a_pData)) { + ++a_pData; + } + if (!*a_pData) { + break; + } + + // skip processing of comment lines but keep a pointer to + // the start of the comment. + if (IsComment(*a_pData)) { + LoadMultiLineText(a_pData, a_pComment, NULL, true); + continue; + } + + // process section names + if (*a_pData == '[') { + // skip leading spaces + ++a_pData; + while (*a_pData && IsSpace(*a_pData)) { + ++a_pData; + } + + // find the end of the section name (it may contain spaces) + // and convert it to lowercase as necessary + a_pSection = a_pData; + while (*a_pData && *a_pData != ']' && !IsNewLineChar(*a_pData)) { + ++a_pData; + } + + // if it's an invalid line, just skip it + if (*a_pData != ']') { + continue; + } + + // remove trailing spaces from the section + pTrail = a_pData - 1; + while (pTrail >= a_pSection && IsSpace(*pTrail)) { + --pTrail; + } + ++pTrail; + *pTrail = 0; + + // skip to the end of the line + ++a_pData; // safe as checked that it == ']' above + while (*a_pData && !IsNewLineChar(*a_pData)) { + ++a_pData; + } + + a_pKey = NULL; + a_pVal = NULL; + return true; + } + + // find the end of the key name (it may contain spaces) + a_pKey = a_pData; + while (*a_pData && *a_pData != '=' && !IsNewLineChar(*a_pData)) { + ++a_pData; + } + // *a_pData is null, equals, or newline + + // if no value and we don't allow no value, then invalid + bHaveValue = (*a_pData == '='); + if (!bHaveValue && !m_bAllowKeyOnly) { + continue; + } + + // empty keys are invalid + if (bHaveValue && a_pKey == a_pData) { + while (*a_pData && !IsNewLineChar(*a_pData)) { + ++a_pData; + } + continue; + } + + // remove trailing spaces from the key + pTrail = a_pData - 1; + while (pTrail >= a_pKey && IsSpace(*pTrail)) { + --pTrail; + } + ++pTrail; + + if (bHaveValue) { + // process the value + *pTrail = 0; + + // skip leading whitespace on the value + ++a_pData; // safe as checked that it == '=' above + while (*a_pData && !IsNewLineChar(*a_pData) && IsSpace(*a_pData)) { + ++a_pData; + } + + // find the end of the value which is the end of this line + a_pVal = a_pData; + while (*a_pData && !IsNewLineChar(*a_pData)) { + ++a_pData; + } + + // remove trailing spaces from the value + pTrail = a_pData - 1; + if (*a_pData) { // prepare for the next round + SkipNewLine(a_pData); + } + while (pTrail >= a_pVal && IsSpace(*pTrail)) { + --pTrail; + } + ++pTrail; + *pTrail = 0; + + // check for multi-line entries + if (m_bAllowMultiLine && IsMultiLineTag(a_pVal)) { + // skip the "<<<" to get the tag that will end the multiline + const SI_CHAR* pTagName = a_pVal + 3; + return LoadMultiLineText(a_pData, a_pVal, pTagName); + } + + // check for quoted values, we are not supporting escapes in quoted values (yet) + if (m_bParseQuotes) { + --pTrail; + if (pTrail > a_pVal && *a_pVal == '"' && *pTrail == '"') { + ++a_pVal; + *pTrail = 0; + } + } + } + else { + // no value to process, just prepare for the next + if (*a_pData) { + SkipNewLine(a_pData); + } + *pTrail = 0; + } + + // return the standard entry + return true; + } + + return false; +} + +template +bool +CSimpleIniTempl::IsMultiLineTag( + const SI_CHAR * a_pVal + ) const +{ + // check for the "<<<" prefix for a multi-line entry + if (*a_pVal++ != '<') return false; + if (*a_pVal++ != '<') return false; + if (*a_pVal++ != '<') return false; + return true; +} + +template +bool +CSimpleIniTempl::IsMultiLineData( + const SI_CHAR * a_pData + ) const +{ + // data is multi-line if it has any of the following features: + // * whitespace prefix + // * embedded newlines + // * whitespace suffix + + // empty string + if (!*a_pData) { + return false; + } + + // check for prefix + if (IsSpace(*a_pData)) { + return true; + } + + // embedded newlines + while (*a_pData) { + if (IsNewLineChar(*a_pData)) { + return true; + } + ++a_pData; + } + + // check for suffix + if (IsSpace(*--a_pData)) { + return true; + } + + return false; +} + +template +bool +CSimpleIniTempl::IsSingleLineQuotedValue( + const SI_CHAR* a_pData +) const +{ + // data needs quoting if it starts or ends with whitespace + // and doesn't have embedded newlines + + // empty string + if (!*a_pData) { + return false; + } + + // check for prefix + if (IsSpace(*a_pData)) { + return true; + } + + // embedded newlines + while (*a_pData) { + if (IsNewLineChar(*a_pData)) { + return false; + } + ++a_pData; + } + + // check for suffix + if (IsSpace(*--a_pData)) { + return true; + } + + return false; +} + +template +bool +CSimpleIniTempl::IsNewLineChar( + SI_CHAR a_c + ) const +{ + return (a_c == '\n' || a_c == '\r'); +} + +template +bool +CSimpleIniTempl::LoadMultiLineText( + SI_CHAR *& a_pData, + const SI_CHAR *& a_pVal, + const SI_CHAR * a_pTagName, + bool a_bAllowBlankLinesInComment + ) const +{ + // we modify this data to strip all newlines down to a single '\n' + // character. This means that on Windows we need to strip out some + // characters which will make the data shorter. + // i.e. LINE1-LINE1\r\nLINE2-LINE2\0 will become + // LINE1-LINE1\nLINE2-LINE2\0 + // The pDataLine entry is the pointer to the location in memory that + // the current line needs to start to run following the existing one. + // This may be the same as pCurrLine in which case no move is needed. + SI_CHAR * pDataLine = a_pData; + SI_CHAR * pCurrLine; + + // value starts at the current line + a_pVal = a_pData; + + // find the end tag. This tag must start in column 1 and be + // followed by a newline. We ignore any whitespace after the end + // tag but not whitespace before it. + SI_CHAR cEndOfLineChar = *a_pData; + for(;;) { + // if we are loading comments then we need a comment character as + // the first character on every line + if (!a_pTagName && !IsComment(*a_pData)) { + // if we aren't allowing blank lines then we're done + if (!a_bAllowBlankLinesInComment) { + break; + } + + // if we are allowing blank lines then we only include them + // in this comment if another comment follows, so read ahead + // to find out. + SI_CHAR * pCurr = a_pData; + int nNewLines = 0; + while (IsSpace(*pCurr)) { + if (IsNewLineChar(*pCurr)) { + ++nNewLines; + SkipNewLine(pCurr); + } + else { + ++pCurr; + } + } + + // we have a comment, add the blank lines to the output + // and continue processing from here + if (IsComment(*pCurr)) { + for (; nNewLines > 0; --nNewLines) *pDataLine++ = '\n'; + a_pData = pCurr; + continue; + } + + // the comment ends here + break; + } + + // find the end of this line + pCurrLine = a_pData; + while (*a_pData && !IsNewLineChar(*a_pData)) ++a_pData; + + // move this line down to the location that it should be if necessary + if (pDataLine < pCurrLine) { + size_t nLen = (size_t) (a_pData - pCurrLine); + memmove(pDataLine, pCurrLine, nLen * sizeof(SI_CHAR)); + pDataLine[nLen] = '\0'; + } + + // end the line with a NULL + cEndOfLineChar = *a_pData; + *a_pData = 0; + + // if are looking for a tag then do the check now. This is done before + // checking for end of the data, so that if we have the tag at the end + // of the data then the tag is removed correctly. + if (a_pTagName) { + // strip whitespace from the end of this tag + SI_CHAR* pc = a_pData - 1; + while (pc > pDataLine && IsSpace(*pc)) --pc; + SI_CHAR ch = *++pc; + *pc = 0; + + if (!IsLess(pDataLine, a_pTagName) && !IsLess(a_pTagName, pDataLine)) { + break; + } + + *pc = ch; + } + + // if we are at the end of the data then we just automatically end + // this entry and return the current data. + if (!cEndOfLineChar) { + return true; + } + + // otherwise we need to process this newline to ensure that it consists + // of just a single \n character. + pDataLine += (a_pData - pCurrLine); + *a_pData = cEndOfLineChar; + SkipNewLine(a_pData); + *pDataLine++ = '\n'; + } + + // if we didn't find a comment at all then return false + if (a_pVal == a_pData) { + a_pVal = NULL; + return false; + } + + // the data (which ends at the end of the last line) needs to be + // null-terminated BEFORE before the newline character(s). If the + // user wants a new line in the multi-line data then they need to + // add an empty line before the tag. + *--pDataLine = '\0'; + + // if looking for a tag and if we aren't at the end of the data, + // then move a_pData to the start of the next line. + if (a_pTagName && cEndOfLineChar) { + SI_ASSERT(IsNewLineChar(cEndOfLineChar)); + *a_pData = cEndOfLineChar; + SkipNewLine(a_pData); + } + + return true; +} + +template +SI_Error +CSimpleIniTempl::CopyString( + const SI_CHAR *& a_pString + ) +{ + size_t uLen = 0; + if (sizeof(SI_CHAR) == sizeof(char)) { + uLen = strlen((const char *)a_pString); + } + else if (sizeof(SI_CHAR) == sizeof(wchar_t)) { + uLen = wcslen((const wchar_t *)a_pString); + } + else { + for ( ; a_pString[uLen]; ++uLen) /*loop*/ ; + } + ++uLen; // NULL character + SI_CHAR * pCopy = new(std::nothrow) SI_CHAR[uLen]; + if (!pCopy) { + return SI_NOMEM; + } + memcpy(pCopy, a_pString, sizeof(SI_CHAR)*uLen); + m_strings.push_back(pCopy); + a_pString = pCopy; + return SI_OK; +} + +template +SI_Error +CSimpleIniTempl::AddEntry( + const SI_CHAR * a_pSection, + const SI_CHAR * a_pKey, + const SI_CHAR * a_pValue, + const SI_CHAR * a_pComment, + bool a_bForceReplace, + bool a_bCopyStrings + ) +{ + SI_Error rc; + bool bInserted = false; + + SI_ASSERT(!a_pComment || IsComment(*a_pComment)); + + // if we are copying strings then make a copy of the comment now + // because we will need it when we add the entry. + if (a_bCopyStrings && a_pComment) { + rc = CopyString(a_pComment); + if (rc < 0) return rc; + } + + // create the section entry if necessary + typename TSection::iterator iSection = m_data.find(a_pSection); + if (iSection == m_data.end()) { + // if the section doesn't exist then we need a copy as the + // string needs to last beyond the end of this function + if (a_bCopyStrings) { + rc = CopyString(a_pSection); + if (rc < 0) return rc; + } + + // only set the comment if this is a section only entry + Entry oSection(a_pSection, ++m_nOrder); + if (a_pComment && !a_pKey) { + oSection.pComment = a_pComment; + } + + typename TSection::value_type oEntry(oSection, TKeyVal()); + typedef typename TSection::iterator SectionIterator; + std::pair i = m_data.insert(oEntry); + iSection = i.first; + bInserted = true; + } + if (!a_pKey) { + // section only entries are specified with pItem as NULL + return bInserted ? SI_INSERTED : SI_UPDATED; + } + + // check for existence of the key + TKeyVal & keyval = iSection->second; + typename TKeyVal::iterator iKey = keyval.find(a_pKey); + bInserted = iKey == keyval.end(); + + // remove all existing entries but save the load order and + // comment of the first entry + int nLoadOrder = ++m_nOrder; + if (iKey != keyval.end() && m_bAllowMultiKey && a_bForceReplace) { + const SI_CHAR * pComment = NULL; + while (iKey != keyval.end() && !IsLess(a_pKey, iKey->first.pItem)) { + if (iKey->first.nOrder < nLoadOrder) { + nLoadOrder = iKey->first.nOrder; + pComment = iKey->first.pComment; + } + ++iKey; + } + if (pComment) { + DeleteString(a_pComment); + a_pComment = pComment; + CopyString(a_pComment); + } + Delete(a_pSection, a_pKey); + iKey = keyval.end(); + } + + // values need to be a valid string, even if they are an empty string + if (!a_pValue) { + a_pValue = &m_cEmptyString; + } + + // make string copies if necessary + bool bForceCreateNewKey = m_bAllowMultiKey && !a_bForceReplace; + if (a_bCopyStrings) { + if (bForceCreateNewKey || iKey == keyval.end()) { + // if the key doesn't exist then we need a copy as the + // string needs to last beyond the end of this function + // because we will be inserting the key next + rc = CopyString(a_pKey); + if (rc < 0) return rc; + } + + // we always need a copy of the value + rc = CopyString(a_pValue); + if (rc < 0) return rc; + } + + // create the key entry + if (iKey == keyval.end() || bForceCreateNewKey) { + Entry oKey(a_pKey, nLoadOrder); + if (a_pComment) { + oKey.pComment = a_pComment; + } + typename TKeyVal::value_type oEntry(oKey, static_cast(NULL)); + iKey = keyval.insert(oEntry); + } + + iKey->second = a_pValue; + return bInserted ? SI_INSERTED : SI_UPDATED; +} + +template +const SI_CHAR * +CSimpleIniTempl::GetValue( + const SI_CHAR * a_pSection, + const SI_CHAR * a_pKey, + const SI_CHAR * a_pDefault, + bool * a_pHasMultiple + ) const +{ + if (a_pHasMultiple) { + *a_pHasMultiple = false; + } + if (!a_pSection || !a_pKey) { + return a_pDefault; + } + typename TSection::const_iterator iSection = m_data.find(a_pSection); + if (iSection == m_data.end()) { + return a_pDefault; + } + typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey); + if (iKeyVal == iSection->second.end()) { + return a_pDefault; + } + + // check for multiple entries with the same key + if (m_bAllowMultiKey && a_pHasMultiple) { + typename TKeyVal::const_iterator iTemp = iKeyVal; + if (++iTemp != iSection->second.end()) { + if (!IsLess(a_pKey, iTemp->first.pItem)) { + *a_pHasMultiple = true; + } + } + } + + return iKeyVal->second; +} + +template +long +CSimpleIniTempl::GetLongValue( + const SI_CHAR * a_pSection, + const SI_CHAR * a_pKey, + long a_nDefault, + bool * a_pHasMultiple + ) const +{ + // return the default if we don't have a value + const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple); + if (!pszValue || !*pszValue) return a_nDefault; + + // convert to UTF-8/MBCS which for a numeric value will be the same as ASCII + char szValue[64] = { 0 }; + SI_CONVERTER c(m_bStoreIsUtf8); + if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue))) { + return a_nDefault; + } + + // handle the value as hex if prefaced with "0x" + long nValue = a_nDefault; + char * pszSuffix = szValue; + if (szValue[0] == '0' && (szValue[1] == 'x' || szValue[1] == 'X')) { + if (!szValue[2]) return a_nDefault; + nValue = strtol(&szValue[2], &pszSuffix, 16); + } + else { + nValue = strtol(szValue, &pszSuffix, 10); + } + + // any invalid strings will return the default value + if (*pszSuffix) { + return a_nDefault; + } + + return nValue; +} + +template +SI_Error +CSimpleIniTempl::SetLongValue( + const SI_CHAR * a_pSection, + const SI_CHAR * a_pKey, + long a_nValue, + const SI_CHAR * a_pComment, + bool a_bUseHex, + bool a_bForceReplace + ) +{ + // use SetValue to create sections + if (!a_pSection || !a_pKey) return SI_FAIL; + + // convert to an ASCII string + char szInput[64]; +#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE + sprintf_s(szInput, a_bUseHex ? "0x%lx" : "%ld", a_nValue); +#else // !__STDC_WANT_SECURE_LIB__ + snprintf(szInput, sizeof(szInput), a_bUseHex ? "0x%lx" : "%ld", a_nValue); +#endif // __STDC_WANT_SECURE_LIB__ + + // convert to output text + SI_CHAR szOutput[64]; + SI_CONVERTER c(m_bStoreIsUtf8); + c.ConvertFromStore(szInput, strlen(szInput) + 1, + szOutput, sizeof(szOutput) / sizeof(SI_CHAR)); + + // actually add it + return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true); +} + +template +double +CSimpleIniTempl::GetDoubleValue( + const SI_CHAR * a_pSection, + const SI_CHAR * a_pKey, + double a_nDefault, + bool * a_pHasMultiple + ) const +{ + // return the default if we don't have a value + const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple); + if (!pszValue || !*pszValue) return a_nDefault; + + // convert to UTF-8/MBCS which for a numeric value will be the same as ASCII + char szValue[64] = { 0 }; + SI_CONVERTER c(m_bStoreIsUtf8); + if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue))) { + return a_nDefault; + } + + char * pszSuffix = NULL; + double nValue = strtod(szValue, &pszSuffix); + + // any invalid strings will return the default value + if (!pszSuffix || *pszSuffix) { + return a_nDefault; + } + + return nValue; +} + +template +SI_Error +CSimpleIniTempl::SetDoubleValue( + const SI_CHAR * a_pSection, + const SI_CHAR * a_pKey, + double a_nValue, + const SI_CHAR * a_pComment, + bool a_bForceReplace + ) +{ + // use SetValue to create sections + if (!a_pSection || !a_pKey) return SI_FAIL; + + // convert to an ASCII string + char szInput[64]; +#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE + sprintf_s(szInput, "%f", a_nValue); +#else // !__STDC_WANT_SECURE_LIB__ + snprintf(szInput, sizeof(szInput), "%f", a_nValue); +#endif // __STDC_WANT_SECURE_LIB__ + + // convert to output text + SI_CHAR szOutput[64]; + SI_CONVERTER c(m_bStoreIsUtf8); + c.ConvertFromStore(szInput, strlen(szInput) + 1, + szOutput, sizeof(szOutput) / sizeof(SI_CHAR)); + + // actually add it + return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true); +} + +template +bool +CSimpleIniTempl::GetBoolValue( + const SI_CHAR * a_pSection, + const SI_CHAR * a_pKey, + bool a_bDefault, + bool * a_pHasMultiple + ) const +{ + // return the default if we don't have a value + const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple); + if (!pszValue || !*pszValue) return a_bDefault; + + // we only look at the minimum number of characters + switch (pszValue[0]) { + case 't': case 'T': // true + case 'y': case 'Y': // yes + case '1': // 1 (one) + return true; + + case 'f': case 'F': // false + case 'n': case 'N': // no + case '0': // 0 (zero) + return false; + + case 'o': case 'O': + if (pszValue[1] == 'n' || pszValue[1] == 'N') return true; // on + if (pszValue[1] == 'f' || pszValue[1] == 'F') return false; // off + break; + } + + // no recognized value, return the default + return a_bDefault; +} + +template +SI_Error +CSimpleIniTempl::SetBoolValue( + const SI_CHAR * a_pSection, + const SI_CHAR * a_pKey, + bool a_bValue, + const SI_CHAR * a_pComment, + bool a_bForceReplace + ) +{ + // use SetValue to create sections + if (!a_pSection || !a_pKey) return SI_FAIL; + + // convert to an ASCII string + const char * pszInput = a_bValue ? "true" : "false"; + + // convert to output text + SI_CHAR szOutput[64]; + SI_CONVERTER c(m_bStoreIsUtf8); + c.ConvertFromStore(pszInput, strlen(pszInput) + 1, + szOutput, sizeof(szOutput) / sizeof(SI_CHAR)); + + // actually add it + return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true); +} + +template +bool +CSimpleIniTempl::GetAllValues( + const SI_CHAR * a_pSection, + const SI_CHAR * a_pKey, + TNamesDepend & a_values + ) const +{ + a_values.clear(); + + if (!a_pSection || !a_pKey) { + return false; + } + typename TSection::const_iterator iSection = m_data.find(a_pSection); + if (iSection == m_data.end()) { + return false; + } + typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey); + if (iKeyVal == iSection->second.end()) { + return false; + } + + // insert all values for this key + a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.pComment, iKeyVal->first.nOrder)); + if (m_bAllowMultiKey) { + ++iKeyVal; + while (iKeyVal != iSection->second.end() && !IsLess(a_pKey, iKeyVal->first.pItem)) { + a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.pComment, iKeyVal->first.nOrder)); + ++iKeyVal; + } + } + + return true; +} + +template +int +CSimpleIniTempl::GetSectionSize( + const SI_CHAR * a_pSection + ) const +{ + if (!a_pSection) { + return -1; + } + + typename TSection::const_iterator iSection = m_data.find(a_pSection); + if (iSection == m_data.end()) { + return -1; + } + const TKeyVal & section = iSection->second; + + // if multi-key isn't permitted then the section size is + // the number of keys that we have. + if (!m_bAllowMultiKey || section.empty()) { + return (int) section.size(); + } + + // otherwise we need to count them + int nCount = 0; + const SI_CHAR * pLastKey = NULL; + typename TKeyVal::const_iterator iKeyVal = section.begin(); + for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n) { + if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) { + ++nCount; + pLastKey = iKeyVal->first.pItem; + } + } + return nCount; +} + +template +const typename CSimpleIniTempl::TKeyVal * +CSimpleIniTempl::GetSection( + const SI_CHAR * a_pSection + ) const +{ + if (a_pSection) { + typename TSection::const_iterator i = m_data.find(a_pSection); + if (i != m_data.end()) { + return &(i->second); + } + } + return 0; +} + +template +void +CSimpleIniTempl::GetAllSections( + TNamesDepend & a_names + ) const +{ + a_names.clear(); + typename TSection::const_iterator i = m_data.begin(); + for (int n = 0; i != m_data.end(); ++i, ++n ) { + a_names.push_back(i->first); + } +} + +template +bool +CSimpleIniTempl::GetAllKeys( + const SI_CHAR * a_pSection, + TNamesDepend & a_names + ) const +{ + a_names.clear(); + + if (!a_pSection) { + return false; + } + + typename TSection::const_iterator iSection = m_data.find(a_pSection); + if (iSection == m_data.end()) { + return false; + } + + const TKeyVal & section = iSection->second; + const SI_CHAR * pLastKey = NULL; + typename TKeyVal::const_iterator iKeyVal = section.begin(); + for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n ) { + if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) { + a_names.push_back(iKeyVal->first); + pLastKey = iKeyVal->first.pItem; + } + } + + return true; +} + +template +SI_Error +CSimpleIniTempl::SaveFile( + const char * a_pszFile, + bool a_bAddSignature + ) const +{ + FILE * fp = NULL; +#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE + fopen_s(&fp, a_pszFile, "wb"); +#else // !__STDC_WANT_SECURE_LIB__ + fp = fopen(a_pszFile, "wb"); +#endif // __STDC_WANT_SECURE_LIB__ + if (!fp) return SI_FILE; + SI_Error rc = SaveFile(fp, a_bAddSignature); + fclose(fp); + return rc; +} + +#ifdef SI_HAS_WIDE_FILE +template +SI_Error +CSimpleIniTempl::SaveFile( + const SI_WCHAR_T * a_pwszFile, + bool a_bAddSignature + ) const +{ +#ifdef _WIN32 + FILE * fp = NULL; +#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE + _wfopen_s(&fp, a_pwszFile, L"wb"); +#else // !__STDC_WANT_SECURE_LIB__ + fp = _wfopen(a_pwszFile, L"wb"); +#endif // __STDC_WANT_SECURE_LIB__ + if (!fp) return SI_FILE; + SI_Error rc = SaveFile(fp, a_bAddSignature); + fclose(fp); + return rc; +#else // !_WIN32 (therefore SI_CONVERT_ICU) + char szFile[256]; + u_austrncpy(szFile, a_pwszFile, sizeof(szFile)); + return SaveFile(szFile, a_bAddSignature); +#endif // _WIN32 +} +#endif // SI_HAS_WIDE_FILE + +template +SI_Error +CSimpleIniTempl::SaveFile( + FILE * a_pFile, + bool a_bAddSignature + ) const +{ + FileWriter writer(a_pFile); + return Save(writer, a_bAddSignature); +} + +template +SI_Error +CSimpleIniTempl::Save( + OutputWriter & a_oOutput, + bool a_bAddSignature + ) const +{ + Converter convert(m_bStoreIsUtf8); + + // add the UTF-8 signature if it is desired + if (m_bStoreIsUtf8 && a_bAddSignature) { + a_oOutput.Write(SI_UTF8_SIGNATURE); + } + + // get all of the sections sorted in load order + TNamesDepend oSections; + GetAllSections(oSections); +#if defined(_MSC_VER) && _MSC_VER <= 1200 + oSections.sort(); +#elif defined(__BORLANDC__) + oSections.sort(Entry::LoadOrder()); +#else + oSections.sort(typename Entry::LoadOrder()); +#endif + + // if there is an empty section name, then it must be written out first + // regardless of the load order + typename TNamesDepend::iterator is = oSections.begin(); + for (; is != oSections.end(); ++is) { + if (!*is->pItem) { + // move the empty section name to the front of the section list + if (is != oSections.begin()) { + oSections.splice(oSections.begin(), oSections, is, std::next(is)); + } + break; + } + } + + // write the file comment if we have one + bool bNeedNewLine = false; + if (m_pFileComment) { + if (!OutputMultiLineText(a_oOutput, convert, m_pFileComment)) { + return SI_FAIL; + } + bNeedNewLine = true; + } + + // iterate through our sections and output the data + typename TNamesDepend::const_iterator iSection = oSections.begin(); + for ( ; iSection != oSections.end(); ++iSection ) { + // write out the comment if there is one + if (iSection->pComment) { + if (bNeedNewLine) { + a_oOutput.Write(SI_NEWLINE_A); + a_oOutput.Write(SI_NEWLINE_A); + } + if (!OutputMultiLineText(a_oOutput, convert, iSection->pComment)) { + return SI_FAIL; + } + bNeedNewLine = false; + } + + if (bNeedNewLine) { + a_oOutput.Write(SI_NEWLINE_A); + a_oOutput.Write(SI_NEWLINE_A); + bNeedNewLine = false; + } + + // write the section (unless there is no section name) + if (*iSection->pItem) { + if (!convert.ConvertToStore(iSection->pItem)) { + return SI_FAIL; + } + a_oOutput.Write("["); + a_oOutput.Write(convert.Data()); + a_oOutput.Write("]"); + a_oOutput.Write(SI_NEWLINE_A); + } + + // get all of the keys sorted in load order + TNamesDepend oKeys; + GetAllKeys(iSection->pItem, oKeys); +#if defined(_MSC_VER) && _MSC_VER <= 1200 + oKeys.sort(); +#elif defined(__BORLANDC__) + oKeys.sort(Entry::LoadOrder()); +#else + oKeys.sort(typename Entry::LoadOrder()); +#endif + + // write all keys and values + typename TNamesDepend::const_iterator iKey = oKeys.begin(); + for ( ; iKey != oKeys.end(); ++iKey) { + // get all values for this key + TNamesDepend oValues; + GetAllValues(iSection->pItem, iKey->pItem, oValues); + + typename TNamesDepend::const_iterator iValue = oValues.begin(); + for ( ; iValue != oValues.end(); ++iValue) { + // write out the comment if there is one + if (iValue->pComment) { + a_oOutput.Write(SI_NEWLINE_A); + if (!OutputMultiLineText(a_oOutput, convert, iValue->pComment)) { + return SI_FAIL; + } + } + + // write the key + if (!convert.ConvertToStore(iKey->pItem)) { + return SI_FAIL; + } + a_oOutput.Write(convert.Data()); + + // write the value as long + if (*iValue->pItem || !m_bAllowKeyOnly) { + if (!convert.ConvertToStore(iValue->pItem)) { + return SI_FAIL; + } + a_oOutput.Write(m_bSpaces ? " = " : "="); + if (m_bParseQuotes && IsSingleLineQuotedValue(iValue->pItem)) { + // the only way to preserve external whitespace on a value (i.e. before or after) + // is to quote it. This is simple quoting, we don't escape quotes within the data. + a_oOutput.Write("\""); + a_oOutput.Write(convert.Data()); + a_oOutput.Write("\""); + } + else if (m_bAllowMultiLine && IsMultiLineData(iValue->pItem)) { + // multi-line data needs to be processed specially to ensure + // that we use the correct newline format for the current system + a_oOutput.Write("<<pItem)) { + return SI_FAIL; + } + a_oOutput.Write("END_OF_TEXT"); + } + else { + a_oOutput.Write(convert.Data()); + } + } + a_oOutput.Write(SI_NEWLINE_A); + } + } + + bNeedNewLine = true; + } + + return SI_OK; +} + +template +bool +CSimpleIniTempl::OutputMultiLineText( + OutputWriter & a_oOutput, + Converter & a_oConverter, + const SI_CHAR * a_pText + ) const +{ + const SI_CHAR * pEndOfLine; + SI_CHAR cEndOfLineChar = *a_pText; + while (cEndOfLineChar) { + // find the end of this line + pEndOfLine = a_pText; + for (; *pEndOfLine && *pEndOfLine != '\n'; ++pEndOfLine) /*loop*/ ; + cEndOfLineChar = *pEndOfLine; + + // temporarily null terminate, convert and output the line + *const_cast(pEndOfLine) = 0; + if (!a_oConverter.ConvertToStore(a_pText)) { + return false; + } + *const_cast(pEndOfLine) = cEndOfLineChar; + a_pText += (pEndOfLine - a_pText) + 1; + a_oOutput.Write(a_oConverter.Data()); + a_oOutput.Write(SI_NEWLINE_A); + } + return true; +} + +template +bool +CSimpleIniTempl::Delete( + const SI_CHAR * a_pSection, + const SI_CHAR * a_pKey, + bool a_bRemoveEmpty + ) +{ + return DeleteValue(a_pSection, a_pKey, NULL, a_bRemoveEmpty); +} + +template +bool +CSimpleIniTempl::DeleteValue( + const SI_CHAR * a_pSection, + const SI_CHAR * a_pKey, + const SI_CHAR * a_pValue, + bool a_bRemoveEmpty + ) +{ + if (!a_pSection) { + return false; + } + + typename TSection::iterator iSection = m_data.find(a_pSection); + if (iSection == m_data.end()) { + return false; + } + + // remove a single key if we have a keyname + if (a_pKey) { + typename TKeyVal::iterator iKeyVal = iSection->second.find(a_pKey); + if (iKeyVal == iSection->second.end()) { + return false; + } + + const static SI_STRLESS isLess = SI_STRLESS(); + + // remove any copied strings and then the key + typename TKeyVal::iterator iDelete; + bool bDeleted = false; + do { + iDelete = iKeyVal++; + + if(a_pValue == NULL || + (isLess(a_pValue, iDelete->second) == false && + isLess(iDelete->second, a_pValue) == false)) { + DeleteString(iDelete->first.pItem); + DeleteString(iDelete->second); + iSection->second.erase(iDelete); + bDeleted = true; + } + } + while (iKeyVal != iSection->second.end() + && !IsLess(a_pKey, iKeyVal->first.pItem)); + + if(!bDeleted) { + return false; + } + + // done now if the section is not empty or we are not pruning away + // the empty sections. Otherwise let it fall through into the section + // deletion code + if (!a_bRemoveEmpty || !iSection->second.empty()) { + return true; + } + } + else { + // delete all copied strings from this section. The actual + // entries will be removed when the section is removed. + typename TKeyVal::iterator iKeyVal = iSection->second.begin(); + for ( ; iKeyVal != iSection->second.end(); ++iKeyVal) { + DeleteString(iKeyVal->first.pItem); + DeleteString(iKeyVal->second); + } + } + + // delete the section itself + DeleteString(iSection->first.pItem); + m_data.erase(iSection); + + return true; +} + +template +void +CSimpleIniTempl::DeleteString( + const SI_CHAR * a_pString + ) +{ + // strings may exist either inside the data block, or they will be + // individually allocated and stored in m_strings. We only physically + // delete those stored in m_strings. + if (a_pString < m_pData || a_pString >= m_pData + m_uDataLen) { + typename TNamesDepend::iterator i = m_strings.begin(); + for (;i != m_strings.end(); ++i) { + if (a_pString == i->pItem) { + delete[] const_cast(i->pItem); + m_strings.erase(i); + break; + } + } + } +} + +// --------------------------------------------------------------------------- +// CONVERSION FUNCTIONS +// --------------------------------------------------------------------------- + +// Defines the conversion classes for different libraries. Before including +// SimpleIni.h, set the converter that you wish you use by defining one of the +// following symbols. +// +// SI_NO_CONVERSION Do not make the "W" wide character version of the +// library available. Only CSimpleIniA etc is defined. +// SI_CONVERT_GENERIC Use the Unicode reference conversion library in +// the accompanying files ConvertUTF.h/c +// SI_CONVERT_ICU Use the IBM ICU conversion library. Requires +// ICU headers on include path and icuuc.lib +// SI_CONVERT_WIN32 Use the Win32 API functions for conversion. + +#if !defined(SI_NO_CONVERSION) && !defined(SI_CONVERT_GENERIC) && !defined(SI_CONVERT_WIN32) && !defined(SI_CONVERT_ICU) +# ifdef _WIN32 +# define SI_CONVERT_WIN32 +# else +# define SI_CONVERT_GENERIC +# endif +#endif + +/** + * Generic case-sensitive less than comparison. This class returns numerically + * ordered ASCII case-sensitive text for all possible sizes and types of + * SI_CHAR. + */ +template +struct SI_GenericCase { + bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const { + long cmp; + for ( ;*pLeft && *pRight; ++pLeft, ++pRight) { + cmp = (long) *pLeft - (long) *pRight; + if (cmp != 0) { + return cmp < 0; + } + } + return *pRight != 0; + } +}; + +/** + * Generic ASCII case-insensitive less than comparison. This class returns + * numerically ordered ASCII case-insensitive text for all possible sizes + * and types of SI_CHAR. It is not safe for MBCS text comparison where + * ASCII A-Z characters are used in the encoding of multi-byte characters. + */ +template +struct SI_GenericNoCase { + inline SI_CHAR locase(SI_CHAR ch) const { + return (ch < 'A' || ch > 'Z') ? ch : (ch - 'A' + 'a'); + } + bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const { + long cmp; + for ( ;*pLeft && *pRight; ++pLeft, ++pRight) { + cmp = (long) locase(*pLeft) - (long) locase(*pRight); + if (cmp != 0) { + return cmp < 0; + } + } + return *pRight != 0; + } +}; + +/** + * Null conversion class for MBCS/UTF-8 to char (or equivalent). + */ +template +class SI_ConvertA { + bool m_bStoreIsUtf8; +protected: + SI_ConvertA() { } +public: + SI_ConvertA(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { } + + /* copy and assignment */ + SI_ConvertA(const SI_ConvertA & rhs) { operator=(rhs); } + SI_ConvertA & operator=(const SI_ConvertA & rhs) { + m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8; + return *this; + } + + /** Calculate the number of SI_CHAR required for converting the input + * from the storage format. The storage format is always UTF-8 or MBCS. + * + * @param a_pInputData Data in storage format to be converted to SI_CHAR. + * @param a_uInputDataLen Length of storage format data in bytes. This + * must be the actual length of the data, including + * NULL byte if NULL terminated string is required. + * @return Number of SI_CHAR required by the string when + * converted. If there are embedded NULL bytes in the + * input data, only the string up and not including + * the NULL byte will be converted. + * @return -1 cast to size_t on a conversion error. + */ + size_t SizeFromStore( + const char * a_pInputData, + size_t a_uInputDataLen) + { + (void)a_pInputData; + SI_ASSERT(a_uInputDataLen != (size_t) -1); + + // ASCII/MBCS/UTF-8 needs no conversion + return a_uInputDataLen; + } + + /** Convert the input string from the storage format to SI_CHAR. + * The storage format is always UTF-8 or MBCS. + * + * @param a_pInputData Data in storage format to be converted to SI_CHAR. + * @param a_uInputDataLen Length of storage format data in bytes. This + * must be the actual length of the data, including + * NULL byte if NULL terminated string is required. + * @param a_pOutputData Pointer to the output buffer to received the + * converted data. + * @param a_uOutputDataSize Size of the output buffer in SI_CHAR. + * @return true if all of the input data was successfully + * converted. + */ + bool ConvertFromStore( + const char * a_pInputData, + size_t a_uInputDataLen, + SI_CHAR * a_pOutputData, + size_t a_uOutputDataSize) + { + // ASCII/MBCS/UTF-8 needs no conversion + if (a_uInputDataLen > a_uOutputDataSize) { + return false; + } + memcpy(a_pOutputData, a_pInputData, a_uInputDataLen); + return true; + } + + /** Calculate the number of char required by the storage format of this + * data. The storage format is always UTF-8 or MBCS. + * + * @param a_pInputData NULL terminated string to calculate the number of + * bytes required to be converted to storage format. + * @return Number of bytes required by the string when + * converted to storage format. This size always + * includes space for the terminating NULL character. + * @return -1 cast to size_t on a conversion error. + */ + size_t SizeToStore( + const SI_CHAR * a_pInputData) + { + // ASCII/MBCS/UTF-8 needs no conversion + return strlen((const char *)a_pInputData) + 1; + } + + /** Convert the input string to the storage format of this data. + * The storage format is always UTF-8 or MBCS. + * + * @param a_pInputData NULL terminated source string to convert. All of + * the data will be converted including the + * terminating NULL character. + * @param a_pOutputData Pointer to the buffer to receive the converted + * string. + * @param a_uOutputDataSize Size of the output buffer in char. + * @return true if all of the input data, including the + * terminating NULL character was successfully + * converted. + */ + bool ConvertToStore( + const SI_CHAR * a_pInputData, + char * a_pOutputData, + size_t a_uOutputDataSize) + { + // calc input string length (SI_CHAR type and size independent) + size_t uInputLen = strlen((const char *)a_pInputData) + 1; + if (uInputLen > a_uOutputDataSize) { + return false; + } + + // ascii/UTF-8 needs no conversion + memcpy(a_pOutputData, a_pInputData, uInputLen); + return true; + } +}; + + +// --------------------------------------------------------------------------- +// SI_CONVERT_GENERIC +// --------------------------------------------------------------------------- +#ifdef SI_CONVERT_GENERIC + +#define SI_Case SI_GenericCase +#define SI_NoCase SI_GenericNoCase + +#include +#include "ConvertUTF.h" + +/** + * Converts UTF-8 to a wchar_t (or equivalent) using the Unicode reference + * library functions. This can be used on all platforms. + */ +template +class SI_ConvertW { + bool m_bStoreIsUtf8; +protected: + SI_ConvertW() { } +public: + SI_ConvertW(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { } + + /* copy and assignment */ + SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); } + SI_ConvertW & operator=(const SI_ConvertW & rhs) { + m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8; + return *this; + } + + /** Calculate the number of SI_CHAR required for converting the input + * from the storage format. The storage format is always UTF-8 or MBCS. + * + * @param a_pInputData Data in storage format to be converted to SI_CHAR. + * @param a_uInputDataLen Length of storage format data in bytes. This + * must be the actual length of the data, including + * NULL byte if NULL terminated string is required. + * @return Number of SI_CHAR required by the string when + * converted. If there are embedded NULL bytes in the + * input data, only the string up and not including + * the NULL byte will be converted. + * @return -1 cast to size_t on a conversion error. + */ + size_t SizeFromStore( + const char * a_pInputData, + size_t a_uInputDataLen) + { + SI_ASSERT(a_uInputDataLen != (size_t) -1); + + if (m_bStoreIsUtf8) { + // worst case scenario for UTF-8 to wchar_t is 1 char -> 1 wchar_t + // so we just return the same number of characters required as for + // the source text. + return a_uInputDataLen; + } + +#if defined(SI_NO_MBSTOWCS_NULL) || (!defined(_MSC_VER) && !defined(_linux)) + // fall back processing for platforms that don't support a NULL dest to mbstowcs + // worst case scenario is 1:1, this will be a sufficient buffer size + (void)a_pInputData; + return a_uInputDataLen; +#else + // get the actual required buffer size + return mbstowcs(NULL, a_pInputData, a_uInputDataLen); +#endif + } + + /** Convert the input string from the storage format to SI_CHAR. + * The storage format is always UTF-8 or MBCS. + * + * @param a_pInputData Data in storage format to be converted to SI_CHAR. + * @param a_uInputDataLen Length of storage format data in bytes. This + * must be the actual length of the data, including + * NULL byte if NULL terminated string is required. + * @param a_pOutputData Pointer to the output buffer to received the + * converted data. + * @param a_uOutputDataSize Size of the output buffer in SI_CHAR. + * @return true if all of the input data was successfully + * converted. + */ + bool ConvertFromStore( + const char * a_pInputData, + size_t a_uInputDataLen, + SI_CHAR * a_pOutputData, + size_t a_uOutputDataSize) + { + if (m_bStoreIsUtf8) { + // This uses the Unicode reference implementation to do the + // conversion from UTF-8 to wchar_t. The required files are + // ConvertUTF.h and ConvertUTF.c which should be included in + // the distribution but are publically available from unicode.org + // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/ + ConversionResult retval; + const UTF8 * pUtf8 = (const UTF8 *) a_pInputData; + if (sizeof(wchar_t) == sizeof(UTF32)) { + UTF32 * pUtf32 = (UTF32 *) a_pOutputData; + retval = ConvertUTF8toUTF32( + &pUtf8, pUtf8 + a_uInputDataLen, + &pUtf32, pUtf32 + a_uOutputDataSize, + lenientConversion); + } + else if (sizeof(wchar_t) == sizeof(UTF16)) { + UTF16 * pUtf16 = (UTF16 *) a_pOutputData; + retval = ConvertUTF8toUTF16( + &pUtf8, pUtf8 + a_uInputDataLen, + &pUtf16, pUtf16 + a_uOutputDataSize, + lenientConversion); + } + return retval == conversionOK; + } + + // convert to wchar_t + size_t retval = mbstowcs(a_pOutputData, + a_pInputData, a_uOutputDataSize); + return retval != (size_t)(-1); + } + + /** Calculate the number of char required by the storage format of this + * data. The storage format is always UTF-8 or MBCS. + * + * @param a_pInputData NULL terminated string to calculate the number of + * bytes required to be converted to storage format. + * @return Number of bytes required by the string when + * converted to storage format. This size always + * includes space for the terminating NULL character. + * @return -1 cast to size_t on a conversion error. + */ + size_t SizeToStore( + const SI_CHAR * a_pInputData) + { + if (m_bStoreIsUtf8) { + // worst case scenario for wchar_t to UTF-8 is 1 wchar_t -> 6 char + size_t uLen = 0; + while (a_pInputData[uLen]) { + ++uLen; + } + return (6 * uLen) + 1; + } + else { + size_t uLen = wcstombs(NULL, a_pInputData, 0); + if (uLen == (size_t)(-1)) { + return uLen; + } + return uLen + 1; // include NULL terminator + } + } + + /** Convert the input string to the storage format of this data. + * The storage format is always UTF-8 or MBCS. + * + * @param a_pInputData NULL terminated source string to convert. All of + * the data will be converted including the + * terminating NULL character. + * @param a_pOutputData Pointer to the buffer to receive the converted + * string. + * @param a_uOutputDataSize Size of the output buffer in char. + * @return true if all of the input data, including the + * terminating NULL character was successfully + * converted. + */ + bool ConvertToStore( + const SI_CHAR * a_pInputData, + char * a_pOutputData, + size_t a_uOutputDataSize + ) + { + if (m_bStoreIsUtf8) { + // calc input string length (SI_CHAR type and size independent) + size_t uInputLen = 0; + while (a_pInputData[uInputLen]) { + ++uInputLen; + } + ++uInputLen; // include the NULL char + + // This uses the Unicode reference implementation to do the + // conversion from wchar_t to UTF-8. The required files are + // ConvertUTF.h and ConvertUTF.c which should be included in + // the distribution but are publically available from unicode.org + // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/ + ConversionResult retval; + UTF8 * pUtf8 = (UTF8 *) a_pOutputData; + if (sizeof(wchar_t) == sizeof(UTF32)) { + const UTF32 * pUtf32 = (const UTF32 *) a_pInputData; + retval = ConvertUTF32toUTF8( + &pUtf32, pUtf32 + uInputLen, + &pUtf8, pUtf8 + a_uOutputDataSize, + lenientConversion); + } + else if (sizeof(wchar_t) == sizeof(UTF16)) { + const UTF16 * pUtf16 = (const UTF16 *) a_pInputData; + retval = ConvertUTF16toUTF8( + &pUtf16, pUtf16 + uInputLen, + &pUtf8, pUtf8 + a_uOutputDataSize, + lenientConversion); + } + return retval == conversionOK; + } + else { + size_t retval = wcstombs(a_pOutputData, + a_pInputData, a_uOutputDataSize); + return retval != (size_t) -1; + } + } +}; + +#endif // SI_CONVERT_GENERIC + + +// --------------------------------------------------------------------------- +// SI_CONVERT_ICU +// --------------------------------------------------------------------------- +#ifdef SI_CONVERT_ICU + +#define SI_Case SI_GenericCase +#define SI_NoCase SI_GenericNoCase + +#include + +/** + * Converts MBCS/UTF-8 to UChar using ICU. This can be used on all platforms. + */ +template +class SI_ConvertW { + const char * m_pEncoding; + UConverter * m_pConverter; +protected: + SI_ConvertW() : m_pEncoding(NULL), m_pConverter(NULL) { } +public: + SI_ConvertW(bool a_bStoreIsUtf8) : m_pConverter(NULL) { + m_pEncoding = a_bStoreIsUtf8 ? "UTF-8" : NULL; + } + + /* copy and assignment */ + SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); } + SI_ConvertW & operator=(const SI_ConvertW & rhs) { + m_pEncoding = rhs.m_pEncoding; + m_pConverter = NULL; + return *this; + } + ~SI_ConvertW() { if (m_pConverter) ucnv_close(m_pConverter); } + + /** Calculate the number of UChar required for converting the input + * from the storage format. The storage format is always UTF-8 or MBCS. + * + * @param a_pInputData Data in storage format to be converted to UChar. + * @param a_uInputDataLen Length of storage format data in bytes. This + * must be the actual length of the data, including + * NULL byte if NULL terminated string is required. + * @return Number of UChar required by the string when + * converted. If there are embedded NULL bytes in the + * input data, only the string up and not including + * the NULL byte will be converted. + * @return -1 cast to size_t on a conversion error. + */ + size_t SizeFromStore( + const char * a_pInputData, + size_t a_uInputDataLen) + { + SI_ASSERT(a_uInputDataLen != (size_t) -1); + + UErrorCode nError; + + if (!m_pConverter) { + nError = U_ZERO_ERROR; + m_pConverter = ucnv_open(m_pEncoding, &nError); + if (U_FAILURE(nError)) { + return (size_t) -1; + } + } + + nError = U_ZERO_ERROR; + int32_t nLen = ucnv_toUChars(m_pConverter, NULL, 0, + a_pInputData, (int32_t) a_uInputDataLen, &nError); + if (U_FAILURE(nError) && nError != U_BUFFER_OVERFLOW_ERROR) { + return (size_t) -1; + } + + return (size_t) nLen; + } + + /** Convert the input string from the storage format to UChar. + * The storage format is always UTF-8 or MBCS. + * + * @param a_pInputData Data in storage format to be converted to UChar. + * @param a_uInputDataLen Length of storage format data in bytes. This + * must be the actual length of the data, including + * NULL byte if NULL terminated string is required. + * @param a_pOutputData Pointer to the output buffer to received the + * converted data. + * @param a_uOutputDataSize Size of the output buffer in UChar. + * @return true if all of the input data was successfully + * converted. + */ + bool ConvertFromStore( + const char * a_pInputData, + size_t a_uInputDataLen, + UChar * a_pOutputData, + size_t a_uOutputDataSize) + { + UErrorCode nError; + + if (!m_pConverter) { + nError = U_ZERO_ERROR; + m_pConverter = ucnv_open(m_pEncoding, &nError); + if (U_FAILURE(nError)) { + return false; + } + } + + nError = U_ZERO_ERROR; + ucnv_toUChars(m_pConverter, + a_pOutputData, (int32_t) a_uOutputDataSize, + a_pInputData, (int32_t) a_uInputDataLen, &nError); + if (U_FAILURE(nError)) { + return false; + } + + return true; + } + + /** Calculate the number of char required by the storage format of this + * data. The storage format is always UTF-8 or MBCS. + * + * @param a_pInputData NULL terminated string to calculate the number of + * bytes required to be converted to storage format. + * @return Number of bytes required by the string when + * converted to storage format. This size always + * includes space for the terminating NULL character. + * @return -1 cast to size_t on a conversion error. + */ + size_t SizeToStore( + const UChar * a_pInputData) + { + UErrorCode nError; + + if (!m_pConverter) { + nError = U_ZERO_ERROR; + m_pConverter = ucnv_open(m_pEncoding, &nError); + if (U_FAILURE(nError)) { + return (size_t) -1; + } + } + + nError = U_ZERO_ERROR; + int32_t nLen = ucnv_fromUChars(m_pConverter, NULL, 0, + a_pInputData, -1, &nError); + if (U_FAILURE(nError) && nError != U_BUFFER_OVERFLOW_ERROR) { + return (size_t) -1; + } + + return (size_t) nLen + 1; + } + + /** Convert the input string to the storage format of this data. + * The storage format is always UTF-8 or MBCS. + * + * @param a_pInputData NULL terminated source string to convert. All of + * the data will be converted including the + * terminating NULL character. + * @param a_pOutputData Pointer to the buffer to receive the converted + * string. + * @param a_pOutputDataSize Size of the output buffer in char. + * @return true if all of the input data, including the + * terminating NULL character was successfully + * converted. + */ + bool ConvertToStore( + const UChar * a_pInputData, + char * a_pOutputData, + size_t a_uOutputDataSize) + { + UErrorCode nError; + + if (!m_pConverter) { + nError = U_ZERO_ERROR; + m_pConverter = ucnv_open(m_pEncoding, &nError); + if (U_FAILURE(nError)) { + return false; + } + } + + nError = U_ZERO_ERROR; + ucnv_fromUChars(m_pConverter, + a_pOutputData, (int32_t) a_uOutputDataSize, + a_pInputData, -1, &nError); + if (U_FAILURE(nError)) { + return false; + } + + return true; + } +}; + +#endif // SI_CONVERT_ICU + + +// --------------------------------------------------------------------------- +// SI_CONVERT_WIN32 +// --------------------------------------------------------------------------- +#ifdef SI_CONVERT_WIN32 + +#define SI_Case SI_GenericCase + +// Windows CE doesn't have errno or MBCS libraries +#ifdef _WIN32_WCE +# ifndef SI_NO_MBCS +# define SI_NO_MBCS +# endif +#endif + +#include +#ifdef SI_NO_MBCS +# define SI_NoCase SI_GenericNoCase +#else // !SI_NO_MBCS +/** + * Case-insensitive comparison class using Win32 MBCS functions. This class + * returns a case-insensitive semi-collation order for MBCS text. It may not + * be safe for UTF-8 text returned in char format as we don't know what + * characters will be folded by the function! Therefore, if you are using + * SI_CHAR == char and SetUnicode(true), then you need to use the generic + * SI_NoCase class instead. + */ +#include +template +struct SI_NoCase { + bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const { + if (sizeof(SI_CHAR) == sizeof(char)) { + return _mbsicmp((const unsigned char *)pLeft, + (const unsigned char *)pRight) < 0; + } + if (sizeof(SI_CHAR) == sizeof(wchar_t)) { + return _wcsicmp((const wchar_t *)pLeft, + (const wchar_t *)pRight) < 0; + } + return SI_GenericNoCase()(pLeft, pRight); + } +}; +#endif // SI_NO_MBCS + +/** + * Converts MBCS and UTF-8 to a wchar_t (or equivalent) on Windows. This uses + * only the Win32 functions and doesn't require the external Unicode UTF-8 + * conversion library. It will not work on Windows 95 without using Microsoft + * Layer for Unicode in your application. + */ +template +class SI_ConvertW { + UINT m_uCodePage; +protected: + SI_ConvertW() { } +public: + SI_ConvertW(bool a_bStoreIsUtf8) { + m_uCodePage = a_bStoreIsUtf8 ? CP_UTF8 : CP_ACP; + } + + /* copy and assignment */ + SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); } + SI_ConvertW & operator=(const SI_ConvertW & rhs) { + m_uCodePage = rhs.m_uCodePage; + return *this; + } + + /** Calculate the number of SI_CHAR required for converting the input + * from the storage format. The storage format is always UTF-8 or MBCS. + * + * @param a_pInputData Data in storage format to be converted to SI_CHAR. + * @param a_uInputDataLen Length of storage format data in bytes. This + * must be the actual length of the data, including + * NULL byte if NULL terminated string is required. + * @return Number of SI_CHAR required by the string when + * converted. If there are embedded NULL bytes in the + * input data, only the string up and not including + * the NULL byte will be converted. + * @return -1 cast to size_t on a conversion error. + */ + size_t SizeFromStore( + const char * a_pInputData, + size_t a_uInputDataLen) + { + SI_ASSERT(a_uInputDataLen != (size_t) -1); + + int retval = MultiByteToWideChar( + m_uCodePage, 0, + a_pInputData, (int) a_uInputDataLen, + 0, 0); + return (size_t)(retval > 0 ? retval : -1); + } + + /** Convert the input string from the storage format to SI_CHAR. + * The storage format is always UTF-8 or MBCS. + * + * @param a_pInputData Data in storage format to be converted to SI_CHAR. + * @param a_uInputDataLen Length of storage format data in bytes. This + * must be the actual length of the data, including + * NULL byte if NULL terminated string is required. + * @param a_pOutputData Pointer to the output buffer to received the + * converted data. + * @param a_uOutputDataSize Size of the output buffer in SI_CHAR. + * @return true if all of the input data was successfully + * converted. + */ + bool ConvertFromStore( + const char * a_pInputData, + size_t a_uInputDataLen, + SI_CHAR * a_pOutputData, + size_t a_uOutputDataSize) + { + int nSize = MultiByteToWideChar( + m_uCodePage, 0, + a_pInputData, (int) a_uInputDataLen, + (wchar_t *) a_pOutputData, (int) a_uOutputDataSize); + return (nSize > 0); + } + + /** Calculate the number of char required by the storage format of this + * data. The storage format is always UTF-8. + * + * @param a_pInputData NULL terminated string to calculate the number of + * bytes required to be converted to storage format. + * @return Number of bytes required by the string when + * converted to storage format. This size always + * includes space for the terminating NULL character. + * @return -1 cast to size_t on a conversion error. + */ + size_t SizeToStore( + const SI_CHAR * a_pInputData) + { + int retval = WideCharToMultiByte( + m_uCodePage, 0, + (const wchar_t *) a_pInputData, -1, + 0, 0, 0, 0); + return (size_t) (retval > 0 ? retval : -1); + } + + /** Convert the input string to the storage format of this data. + * The storage format is always UTF-8 or MBCS. + * + * @param a_pInputData NULL terminated source string to convert. All of + * the data will be converted including the + * terminating NULL character. + * @param a_pOutputData Pointer to the buffer to receive the converted + * string. + * @param a_pOutputDataSize Size of the output buffer in char. + * @return true if all of the input data, including the + * terminating NULL character was successfully + * converted. + */ + bool ConvertToStore( + const SI_CHAR * a_pInputData, + char * a_pOutputData, + size_t a_uOutputDataSize) + { + int retval = WideCharToMultiByte( + m_uCodePage, 0, + (const wchar_t *) a_pInputData, -1, + a_pOutputData, (int) a_uOutputDataSize, 0, 0); + return retval > 0; + } +}; + +#endif // SI_CONVERT_WIN32 + + + +// --------------------------------------------------------------------------- +// SI_NO_CONVERSION +// --------------------------------------------------------------------------- +#ifdef SI_NO_CONVERSION + +#define SI_Case SI_GenericCase +#define SI_NoCase SI_GenericNoCase + +#endif // SI_NO_CONVERSION + + + +// --------------------------------------------------------------------------- +// TYPE DEFINITIONS +// --------------------------------------------------------------------------- + +typedef CSimpleIniTempl,SI_ConvertA > CSimpleIniA; +typedef CSimpleIniTempl,SI_ConvertA > CSimpleIniCaseA; + +#if defined(SI_NO_CONVERSION) +// if there is no wide char conversion then we don't need to define the +// widechar "W" versions of CSimpleIni +# define CSimpleIni CSimpleIniA +# define CSimpleIniCase CSimpleIniCaseA +# define SI_NEWLINE SI_NEWLINE_A +#else +# if defined(SI_CONVERT_ICU) +typedef CSimpleIniTempl,SI_ConvertW > CSimpleIniW; +typedef CSimpleIniTempl,SI_ConvertW > CSimpleIniCaseW; +# else +typedef CSimpleIniTempl,SI_ConvertW > CSimpleIniW; +typedef CSimpleIniTempl,SI_ConvertW > CSimpleIniCaseW; +# endif + +# ifdef _UNICODE +# define CSimpleIni CSimpleIniW +# define CSimpleIniCase CSimpleIniCaseW +# define SI_NEWLINE SI_NEWLINE_W +# else // !_UNICODE +# define CSimpleIni CSimpleIniA +# define CSimpleIniCase CSimpleIniCaseA +# define SI_NEWLINE SI_NEWLINE_A +# endif // _UNICODE +#endif + +#ifdef _MSC_VER +# pragma warning (pop) +#endif + +#endif // INCLUDED_SimpleIni_h + diff --git a/VrUtils/jsoncpp/json/assertions.h b/VrUtils/jsoncpp/json/assertions.h new file mode 100644 index 0000000..5ef7e7b --- /dev/null +++ b/VrUtils/jsoncpp/json/assertions.h @@ -0,0 +1,41 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_ASSERTIONS_H_INCLUDED +#define CPPTL_JSON_ASSERTIONS_H_INCLUDED + +#include + +#if !defined(JSON_IS_AMALGAMATION) +#include "config.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +#if JSON_USE_EXCEPTION +#include +#define JSON_ASSERT(condition) \ + assert(condition); // @todo <= change this into an exception throw +#define JSON_FAIL_MESSAGE(message) throw std::runtime_error(message); +#else // JSON_USE_EXCEPTION +#define JSON_ASSERT(condition) assert(condition); + +// The call to assert() will show the failure message in debug builds. In +// release bugs we write to invalid memory in order to crash hard, so that a +// debugger or crash reporter gets the chance to take over. We still call exit() +// afterward in order to tell the compiler that this macro doesn't return. +#define JSON_FAIL_MESSAGE(message) \ + { \ + assert(false&& message); \ + strcpy(reinterpret_cast(666), message); \ + exit(123); \ + } + +#endif + +#define JSON_ASSERT_MESSAGE(condition, message) \ + if (!(condition)) { \ + JSON_FAIL_MESSAGE(message) \ + } + +#endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED diff --git a/VrUtils/jsoncpp/json/autolink.h b/VrUtils/jsoncpp/json/autolink.h new file mode 100644 index 0000000..6fcc8af --- /dev/null +++ b/VrUtils/jsoncpp/json/autolink.h @@ -0,0 +1,25 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_AUTOLINK_H_INCLUDED +#define JSON_AUTOLINK_H_INCLUDED + +#include "config.h" + +#ifdef JSON_IN_CPPTL +#include +#endif + +#if !defined(JSON_NO_AUTOLINK) && !defined(JSON_DLL_BUILD) && \ + !defined(JSON_IN_CPPTL) +#define CPPTL_AUTOLINK_NAME "json" +#undef CPPTL_AUTOLINK_DLL +#ifdef JSON_DLL +#define CPPTL_AUTOLINK_DLL +#endif +#include "autolink.h" +#endif + +#endif // JSON_AUTOLINK_H_INCLUDED diff --git a/VrUtils/jsoncpp/json/config.h b/VrUtils/jsoncpp/json/config.h new file mode 100644 index 0000000..c9a8c77 --- /dev/null +++ b/VrUtils/jsoncpp/json/config.h @@ -0,0 +1,114 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_CONFIG_H_INCLUDED +#define JSON_CONFIG_H_INCLUDED + +/// If defined, indicates that json library is embedded in CppTL library. +//# define JSON_IN_CPPTL 1 + +/// If defined, indicates that json may leverage CppTL library +//# define JSON_USE_CPPTL 1 +/// If defined, indicates that cpptl vector based map should be used instead of +/// std::map +/// as Value container. +//# define JSON_USE_CPPTL_SMALLMAP 1 +/// If defined, indicates that Json specific container should be used +/// (hash table & simple deque container with customizable allocator). +/// THIS FEATURE IS STILL EXPERIMENTAL! There is know bugs: See #3177332 +//# define JSON_VALUE_USE_INTERNAL_MAP 1 +/// Force usage of standard new/malloc based allocator instead of memory pool +/// based allocator. +/// The memory pools allocator used optimization (initializing Value and +/// ValueInternalLink +/// as if it was a POD) that may cause some validation tool to report errors. +/// Only has effects if JSON_VALUE_USE_INTERNAL_MAP is defined. +//# define JSON_USE_SIMPLE_INTERNAL_ALLOCATOR 1 + +// If non-zero, the library uses exceptions to report bad input instead of C +// assertion macros. The default is to use exceptions. +#ifndef JSON_USE_EXCEPTION +#define JSON_USE_EXCEPTION 1 +#endif + +/// If defined, indicates that the source file is amalgated +/// to prevent private header inclusion. +/// Remarks: it is automatically defined in the generated amalgated header. +// #define JSON_IS_AMALGAMATION + +#ifdef JSON_IN_CPPTL +#include +#ifndef JSON_USE_CPPTL +#define JSON_USE_CPPTL 1 +#endif +#endif + +#ifdef JSON_IN_CPPTL +#define JSON_API CPPTL_API +#elif defined(JSON_DLL_BUILD) +#if defined(_MSC_VER) +#define JSON_API __declspec(dllexport) +#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#endif // if defined(_MSC_VER) +#elif defined(JSON_DLL) +#if defined(_MSC_VER) +#define JSON_API __declspec(dllimport) +#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#endif // if defined(_MSC_VER) +#endif // ifdef JSON_IN_CPPTL +#if !defined(JSON_API) +#define JSON_API +#endif + +// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for +// integer +// Storages, and 64 bits integer support is disabled. +// #define JSON_NO_INT64 1 + +#if defined(_MSC_VER) && _MSC_VER <= 1200 // MSVC 6 +// Microsoft Visual Studio 6 only support conversion from __int64 to double +// (no conversion from unsigned __int64). +#define JSON_USE_INT64_DOUBLE_CONVERSION 1 +// Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255' +// characters in the debug information) +// All projects I've ever seen with VS6 were using this globally (not bothering +// with pragma push/pop). +#pragma warning(disable : 4786) +#endif // if defined(_MSC_VER) && _MSC_VER < 1200 // MSVC 6 + +#if defined(_MSC_VER) && _MSC_VER >= 1500 // MSVC 2008 +/// Indicates that the following function is deprecated. +#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) +#endif + +#if !defined(JSONCPP_DEPRECATED) +#define JSONCPP_DEPRECATED(message) +#endif // if !defined(JSONCPP_DEPRECATED) + +namespace VA { +namespace Json { +typedef int Int; +typedef unsigned int UInt; +#if defined(JSON_NO_INT64) +typedef int LargestInt; +typedef unsigned int LargestUInt; +#undef JSON_HAS_INT64 +#else // if defined(JSON_NO_INT64) +// For Microsoft Visual use specific types as long long is not supported +#if defined(_MSC_VER) // Microsoft Visual Studio +typedef __int64 Int64; +typedef unsigned __int64 UInt64; +#else // if defined(_MSC_VER) // Other platforms, use long long +typedef long long int Int64; +typedef unsigned long long int UInt64; +#endif // if defined(_MSC_VER) +typedef Int64 LargestInt; +typedef UInt64 LargestUInt; +#define JSON_HAS_INT64 +#endif // if defined(JSON_NO_INT64) +} // end namespace Json +} // namespace VA + +#endif // JSON_CONFIG_H_INCLUDED diff --git a/VrUtils/jsoncpp/json/features.h b/VrUtils/jsoncpp/json/features.h new file mode 100644 index 0000000..fb2562b --- /dev/null +++ b/VrUtils/jsoncpp/json/features.h @@ -0,0 +1,59 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_FEATURES_H_INCLUDED +#define CPPTL_JSON_FEATURES_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "forwards.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace VA { +namespace Json { + +/** \brief Configuration passed to reader and writer. + * This configuration object can be used to force the Reader or Writer + * to behave in a standard conforming way. + */ +class JSON_API Features { +public: + /** \brief A configuration that allows all features and assumes all strings + * are UTF-8. + * - C & C++ comments are allowed + * - Root object can be any JSON value + * - Assumes Value strings are encoded in UTF-8 + */ + static Features all(); + + /** \brief A configuration that is strictly compatible with the JSON + * specification. + * - Comments are forbidden. + * - Root object must be either an array or an object value. + * - Assumes Value strings are encoded in UTF-8 + */ + static Features strictMode(); + + /** \brief Initialize the configuration like JsonConfig::allFeatures; + */ + Features(); + + /// \c true if comments are allowed. Default: \c true. + bool allowComments_; + + /// \c true if root must be either an array or an object value. Default: \c + /// false. + bool strictRoot_; + + /// \c true if dropped null placeholders are allowed. Default: \c false. + bool allowDroppedNullPlaceholders_; + + /// \c true if numeric object key are allowed. Default: \c false. + bool allowNumericKeys_; +}; + +} // namespace Json +} // namespace VA + +#endif // CPPTL_JSON_FEATURES_H_INCLUDED diff --git a/VrUtils/jsoncpp/json/forwards.h b/VrUtils/jsoncpp/json/forwards.h new file mode 100644 index 0000000..a6b19ae --- /dev/null +++ b/VrUtils/jsoncpp/json/forwards.h @@ -0,0 +1,45 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_FORWARDS_H_INCLUDED +#define JSON_FORWARDS_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "config.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace VA { +namespace Json { + +// writer.h +class FastWriter; +class StyledWriter; + +// reader.h +class Reader; + +// features.h +class Features; + +// value.h +typedef unsigned int ArrayIndex; +class StaticString; +class Path; +class PathArgument; +class Value; +class ValueIteratorBase; +class ValueIterator; +class ValueConstIterator; +#ifdef JSON_VALUE_USE_INTERNAL_MAP +class ValueMapAllocator; +class ValueInternalLink; +class ValueInternalArray; +class ValueInternalMap; +#endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP + +} // namespace Json +} // namespace VA + +#endif // JSON_FORWARDS_H_INCLUDED diff --git a/VrUtils/jsoncpp/json/json.h b/VrUtils/jsoncpp/json/json.h new file mode 100644 index 0000000..8f10ac2 --- /dev/null +++ b/VrUtils/jsoncpp/json/json.h @@ -0,0 +1,15 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_JSON_H_INCLUDED +#define JSON_JSON_H_INCLUDED + +#include "autolink.h" +#include "value.h" +#include "reader.h" +#include "writer.h" +#include "features.h" + +#endif // JSON_JSON_H_INCLUDED diff --git a/VrUtils/jsoncpp/json/reader.h b/VrUtils/jsoncpp/json/reader.h new file mode 100644 index 0000000..daa3a70 --- /dev/null +++ b/VrUtils/jsoncpp/json/reader.h @@ -0,0 +1,277 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_READER_H_INCLUDED +#define CPPTL_JSON_READER_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "features.h" +#include "value.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include + +// Disable warning C4251: : needs to have dll-interface to +// be used by... +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(push) +#pragma warning(disable : 4251) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +namespace VA { +namespace Json { + +/** \brief Unserialize a JSON document into a + *Value. + * + */ +class JSON_API Reader { +public: + typedef char Char; + typedef const Char* Location; + + /** \brief An error tagged with where in the JSON text it was encountered. + * + * The offsets give the [start, limit) range of bytes within the text. Note + * that this is bytes, not codepoints. + * + */ + struct StructuredError { + size_t offset_start; + size_t offset_limit; + std::string message; + }; + + /** \brief Constructs a Reader allowing all features + * for parsing. + */ + Reader(); + + /** \brief Constructs a Reader allowing the specified feature set + * for parsing. + */ + Reader(const Features& features); + + /** \brief Read a Value from a JSON + * document. + * \param document UTF-8 encoded string containing the document to read. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param collectComments \c true to collect comment and allow writing them + * back during + * serialization, \c false to discard comments. + * This parameter is ignored if + * Features::allowComments_ + * is \c false. + * \return \c true if the document was successfully parsed, \c false if an + * error occurred. + */ + bool + parse(const std::string& document, Value& root, bool collectComments = true); + + /** \brief Read a Value from a JSON + document. + * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the + document to read. + * \param endDoc Pointer on the end of the UTF-8 encoded string of the + document to read. + \ Must be >= beginDoc. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param collectComments \c true to collect comment and allow writing them + back during + * serialization, \c false to discard comments. + * This parameter is ignored if + Features::allowComments_ + * is \c false. + * \return \c true if the document was successfully parsed, \c false if an + error occurred. + */ + bool parse(const char* beginDoc, + const char* endDoc, + Value& root, + bool collectComments = true); + + /// \brief Parse from input stream. + /// \see Json::operator>>(std::istream&, Json::Value&). + bool parse(std::istream& is, Value& root, bool collectComments = true); + + /** \brief Returns a user friendly string that list errors in the parsed + * document. + * \return Formatted error message with the list of errors with their location + * in + * the parsed document. An empty string is returned if no error + * occurred + * during parsing. + * \deprecated Use getFormattedErrorMessages() instead (typo fix). + */ + JSONCPP_DEPRECATED("Use getFormattedErrorMessages instead") + std::string getFormatedErrorMessages() const; + + /** \brief Returns a user friendly string that list errors in the parsed + * document. + * \return Formatted error message with the list of errors with their location + * in + * the parsed document. An empty string is returned if no error + * occurred + * during parsing. + */ + std::string getFormattedErrorMessages() const; + + /** \brief Returns a vector of structured erros encounted while parsing. + * \return A (possibly empty) vector of StructuredError objects. Currently + * only one error can be returned, but the caller should tolerate + * multiple + * errors. This can occur if the parser recovers from a non-fatal + * parse error and then encounters additional errors. + */ + std::vector getStructuredErrors() const; + + /** \brief Add a semantic error message. + * \param value JSON Value location associated with the error + * \param message The error message. + * \return \c true if the error was successfully added, \c false if the + * Value offset exceeds the document size. + */ + bool pushError(const Value& value, const std::string& message); + + /** \brief Add a semantic error message with extra context. + * \param value JSON Value location associated with the error + * \param message The error message. + * \param extra Additional JSON Value location to contextualize the error + * \return \c true if the error was successfully added, \c false if either + * Value offset exceeds the document size. + */ + bool pushError(const Value& value, const std::string& message, const Value& extra); + + /** \brief Return whether there are any errors. + * \return \c true if there are no errors to report \c false if + * errors have occurred. + */ + bool good() const; + +private: + enum TokenType { + tokenEndOfStream = 0, + tokenObjectBegin, + tokenObjectEnd, + tokenArrayBegin, + tokenArrayEnd, + tokenString, + tokenNumber, + tokenTrue, + tokenFalse, + tokenNull, + tokenArraySeparator, + tokenMemberSeparator, + tokenComment, + tokenError + }; + + class Token { + public: + TokenType type_; + Location start_; + Location end_; + }; + + class ErrorInfo { + public: + Token token_; + std::string message_; + Location extra_; + }; + + typedef std::deque Errors; + + bool expectToken(TokenType type, Token& token, const char* message); + bool readToken(Token& token); + void skipSpaces(); + bool match(Location pattern, int patternLength); + bool readComment(); + bool readCStyleComment(); + bool readCppStyleComment(); + bool readString(); + void readNumber(); + bool readValue(); + bool readObject(Token& token); + bool readArray(Token& token); + bool decodeNumber(Token& token); + bool decodeNumber(Token& token, Value& decoded); + bool decodeString(Token& token); + bool decodeString(Token& token, std::string& decoded); + bool decodeDouble(Token& token); + bool decodeDouble(Token& token, Value& decoded); + bool decodeUnicodeCodePoint(Token& token, + Location& current, + Location end, + unsigned int& unicode); + bool decodeUnicodeEscapeSequence(Token& token, + Location& current, + Location end, + unsigned int& unicode); + bool addError(const std::string& message, Token& token, Location extra = 0); + bool recoverFromError(TokenType skipUntilToken); + bool addErrorAndRecover(const std::string& message, + Token& token, + TokenType skipUntilToken); + void skipUntilSpace(); + Value& currentValue(); + Char getNextChar(); + void + getLocationLineAndColumn(Location location, int& line, int& column) const; + std::string getLocationLineAndColumn(Location location) const; + void addComment(Location begin, Location end, CommentPlacement placement); + void skipCommentTokens(Token& token); + + typedef std::stack Nodes; + Nodes nodes_; + Errors errors_; + std::string document_; + Location begin_; + Location end_; + Location current_; + Location lastValueEnd_; + Value* lastValue_; + std::string commentsBefore_; + Features features_; + bool collectComments_; +}; + +/** \brief Read from 'sin' into 'root'. + + Always keep comments from the input JSON. + + This can be used to read a file into a particular sub-object. + For example: + \code + Json::Value root; + cin >> root["dir"]["file"]; + cout << root; + \endcode + Result: + \verbatim + { + "dir": { + "file": { + // The input stream JSON would be nested here. + } + } + } + \endverbatim + \throw std::exception on parse error. + \see Json::operator<<() +*/ +JSON_API std::istream& operator>>(std::istream&, Value&); + +} // namespace Json +} // namespace VA +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // CPPTL_JSON_READER_H_INCLUDED diff --git a/VrUtils/jsoncpp/json/value.h b/VrUtils/jsoncpp/json/value.h new file mode 100644 index 0000000..0cd139f --- /dev/null +++ b/VrUtils/jsoncpp/json/value.h @@ -0,0 +1,1091 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_H_INCLUDED +#define CPPTL_JSON_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "forwards.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include + +#ifndef JSON_USE_CPPTL_SMALLMAP +#include +#else +#include +#endif +#ifdef JSON_USE_CPPTL +#include +#endif + +// Disable warning C4251: : needs to have dll-interface to +// be used by... +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(push) +#pragma warning(disable : 4251) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +/** \brief JSON (JavaScript Object Notation). + */ +namespace VA { +namespace Json { + +/** \brief Type of the value held by a Value object. + */ +enum ValueType { + nullValue = 0, ///< 'null' value + intValue, ///< signed integer value + uintValue, ///< unsigned integer value + realValue, ///< double value + stringValue, ///< UTF-8 string value + booleanValue, ///< bool value + arrayValue, ///< array value (ordered list) + objectValue ///< object value (collection of name/value pairs). +}; + +enum CommentPlacement { + commentBefore = 0, ///< a comment placed on the line before a value + commentAfterOnSameLine, ///< a comment just after a value on the same line + commentAfter, ///< a comment on the line after a value (only make sense for + /// root value) + numberOfCommentPlacement +}; + +//# ifdef JSON_USE_CPPTL +// typedef CppTL::AnyEnumerator EnumMemberNames; +// typedef CppTL::AnyEnumerator EnumValues; +//# endif + +/** \brief Lightweight wrapper to tag static string. + * + * Value constructor and objectValue member assignement takes advantage of the + * StaticString and avoid the cost of string duplication when storing the + * string or the member name. + * + * Example of usage: + * \code + * Json::Value aValue( StaticString("some text") ); + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ +class JSON_API StaticString { +public: + explicit StaticString(const char* czstring) : str_(czstring) {} + + operator const char*() const { return str_; } + + const char* c_str() const { return str_; } + +private: + const char* str_; +}; + +/** \brief Represents a JSON value. + * + * This class is a discriminated union wrapper that can represents a: + * - signed integer [range: Value::minInt - Value::maxInt] + * - unsigned integer (range: 0 - Value::maxUInt) + * - double + * - UTF-8 string + * - boolean + * - 'null' + * - an ordered list of Value + * - collection of name/value pairs (javascript object) + * + * The type of the held value is represented by a #ValueType and + * can be obtained using type(). + * + * values of an #objectValue or #arrayValue can be accessed using operator[]() + *methods. + * Non const methods will automatically create the a #nullValue element + * if it does not exist. + * The sequence of an #arrayValue will be automatically resize and initialized + * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue. + * + * The get() methods can be used to obtanis default value in the case the + *required element + * does not exist. + * + * It is possible to iterate over the list of a #objectValue values using + * the getMemberNames() method. + */ +class JSON_API Value { + friend class ValueIteratorBase; +#ifdef JSON_VALUE_USE_INTERNAL_MAP + friend class ValueInternalLink; + friend class ValueInternalMap; +#endif +public: + typedef std::vector Members; + typedef ValueIterator iterator; + typedef ValueConstIterator const_iterator; + typedef Json::UInt UInt; + typedef Json::Int Int; +#if defined(JSON_HAS_INT64) + typedef Json::UInt64 UInt64; + typedef Json::Int64 Int64; +#endif // defined(JSON_HAS_INT64) + typedef Json::LargestInt LargestInt; + typedef Json::LargestUInt LargestUInt; + typedef Json::ArrayIndex ArrayIndex; + + static const Value& null; + /// Minimum signed integer value that can be stored in a Json::Value. + static const LargestInt minLargestInt; + /// Maximum signed integer value that can be stored in a Json::Value. + static const LargestInt maxLargestInt; + /// Maximum unsigned integer value that can be stored in a Json::Value. + static const LargestUInt maxLargestUInt; + + /// Minimum signed int value that can be stored in a Json::Value. + static const Int minInt; + /// Maximum signed int value that can be stored in a Json::Value. + static const Int maxInt; + /// Maximum unsigned int value that can be stored in a Json::Value. + static const UInt maxUInt; + +#if defined(JSON_HAS_INT64) + /// Minimum signed 64 bits int value that can be stored in a Json::Value. + static const Int64 minInt64; + /// Maximum signed 64 bits int value that can be stored in a Json::Value. + static const Int64 maxInt64; + /// Maximum unsigned 64 bits int value that can be stored in a Json::Value. + static const UInt64 maxUInt64; +#endif // defined(JSON_HAS_INT64) + +private: +#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION +#ifndef JSON_VALUE_USE_INTERNAL_MAP + class CZString { + public: + enum DuplicationPolicy { + noDuplication = 0, + duplicate, + duplicateOnCopy + }; + CZString(ArrayIndex index); + CZString(const char* cstr, DuplicationPolicy allocate); + CZString(const CZString& other); + ~CZString(); + CZString& operator=(CZString other); + bool operator<(const CZString& other) const; + bool operator==(const CZString& other) const; + ArrayIndex index() const; + const char* c_str() const; + bool isStaticString() const; + + private: + void swap(CZString& other); + const char* cstr_; + ArrayIndex index_; + }; + +public: +#ifndef JSON_USE_CPPTL_SMALLMAP + typedef std::map ObjectValues; +#else + typedef CppTL::SmallMap ObjectValues; +#endif // ifndef JSON_USE_CPPTL_SMALLMAP +#endif // ifndef JSON_VALUE_USE_INTERNAL_MAP +#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + +public: + /** \brief Create a default Value of the given type. + + This is a very useful constructor. + To create an empty array, pass arrayValue. + To create an empty object, pass objectValue. + Another Value can then be set to this one by assignment. +This is useful since clear() and resize() will not alter types. + + Examples: +\code +Json::Value null_value; // null +Json::Value arr_value(Json::arrayValue); // [] +Json::Value obj_value(Json::objectValue); // {} +\endcode + */ + Value(ValueType type = nullValue); + Value(Int value); + Value(UInt value); +#if defined(JSON_HAS_INT64) + Value(Int64 value); + Value(UInt64 value); +#endif // if defined(JSON_HAS_INT64) + Value(double value); + Value(const char* value); + Value(const char* beginValue, const char* endValue); + /** \brief Constructs a value from a static string. + + * Like other value string constructor but do not duplicate the string for + * internal storage. The given string must remain alive after the call to this + * constructor. + * Example of usage: + * \code + * Json::Value aValue( StaticString("some text") ); + * \endcode + */ + Value(const StaticString& value); + Value(const std::string& value); +#ifdef JSON_USE_CPPTL + Value(const CppTL::ConstString& value); +#endif + Value(bool value); + Value(const Value& other); + ~Value(); + + Value& operator=(Value other); + /// Swap values. + /// \note Currently, comments are intentionally not swapped, for + /// both logic and efficiency. + void swap(Value& other); + + ValueType type() const; + + bool operator<(const Value& other) const; + bool operator<=(const Value& other) const; + bool operator>=(const Value& other) const; + bool operator>(const Value& other) const; + + bool operator==(const Value& other) const; + bool operator!=(const Value& other) const; + + int compare(const Value& other) const; + + const char* asCString() const; + std::string asString() const; +#ifdef JSON_USE_CPPTL + CppTL::ConstString asConstString() const; +#endif + Int asInt() const; + UInt asUInt() const; +#if defined(JSON_HAS_INT64) + Int64 asInt64() const; + UInt64 asUInt64() const; +#endif // if defined(JSON_HAS_INT64) + LargestInt asLargestInt() const; + LargestUInt asLargestUInt() const; + float asFloat() const; + double asDouble() const; + bool asBool() const; + + bool isNull() const; + bool isBool() const; + bool isInt() const; + bool isInt64() const; + bool isUInt() const; + bool isUInt64() const; + bool isIntegral() const; + bool isDouble() const; + bool isNumeric() const; + bool isString() const; + bool isArray() const; + bool isObject() const; + + bool isConvertibleTo(ValueType other) const; + + /// Number of values in array or object + ArrayIndex size() const; + + /// \brief Return true if empty array, empty object, or null; + /// otherwise, false. + bool empty() const; + + /// Return isNull() + bool operator!() const; + + /// Remove all object members and array elements. + /// \pre type() is arrayValue, objectValue, or nullValue + /// \post type() is unchanged + void clear(); + + /// Resize the array to size elements. + /// New elements are initialized to null. + /// May only be called on nullValue or arrayValue. + /// \pre type() is arrayValue or nullValue + /// \post type() is arrayValue + void resize(ArrayIndex size); + + /// Access an array element (zero based index ). + /// If the array contains less than index element, then null value are + /// inserted + /// in the array so that its size is index+1. + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + Value& operator[](ArrayIndex index); + + /// Access an array element (zero based index ). + /// If the array contains less than index element, then null value are + /// inserted + /// in the array so that its size is index+1. + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + Value& operator[](int index); + + /// Access an array element (zero based index ) + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + const Value& operator[](ArrayIndex index) const; + + /// Access an array element (zero based index ) + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + const Value& operator[](int index) const; + + /// If the array contains at least index+1 elements, returns the element + /// value, + /// otherwise returns defaultValue. + Value get(ArrayIndex index, const Value& defaultValue) const; + /// Return true if index < size(). + bool isValidIndex(ArrayIndex index) const; + /// \brief Append value to array at the end. + /// + /// Equivalent to jsonvalue[jsonvalue.size()] = value; + Value& append(const Value& value); + + /// Access an object value by name, create a null member if it does not exist. + Value& operator[](const char* key); + /// Access an object value by name, returns null if there is no member with + /// that name. + const Value& operator[](const char* key) const; + /// Access an object value by name, create a null member if it does not exist. + Value& operator[](const std::string& key); + /// Access an object value by name, returns null if there is no member with + /// that name. + const Value& operator[](const std::string& key) const; + /** \brief Access an object value by name, create a null member if it does not + exist. + + * If the object as no entry for that name, then the member name used to store + * the new entry is not duplicated. + * Example of use: + * \code + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ + Value& operator[](const StaticString& key); +#ifdef JSON_USE_CPPTL + /// Access an object value by name, create a null member if it does not exist. + Value& operator[](const CppTL::ConstString& key); + /// Access an object value by name, returns null if there is no member with + /// that name. + const Value& operator[](const CppTL::ConstString& key) const; +#endif + /// Return the member named key if it exist, defaultValue otherwise. + Value get(const char* key, const Value& defaultValue) const; + /// Return the member named key if it exist, defaultValue otherwise. + Value get(const std::string& key, const Value& defaultValue) const; +#ifdef JSON_USE_CPPTL + /// Return the member named key if it exist, defaultValue otherwise. + Value get(const CppTL::ConstString& key, const Value& defaultValue) const; +#endif + /// \brief Remove and return the named member. + /// + /// Do nothing if it did not exist. + /// \return the removed Value, or null. + /// \pre type() is objectValue or nullValue + /// \post type() is unchanged + Value removeMember(const char* key); + /// Same as removeMember(const char*) + Value removeMember(const std::string& key); + + /// Return true if the object has a member named key. + bool isMember(const char* key) const; + /// Return true if the object has a member named key. + bool isMember(const std::string& key) const; +#ifdef JSON_USE_CPPTL + /// Return true if the object has a member named key. + bool isMember(const CppTL::ConstString& key) const; +#endif + + /// \brief Return a list of the member names. + /// + /// If null, return an empty list. + /// \pre type() is objectValue or nullValue + /// \post if type() was nullValue, it remains nullValue + Members getMemberNames() const; + + //# ifdef JSON_USE_CPPTL + // EnumMemberNames enumMemberNames() const; + // EnumValues enumValues() const; + //# endif + + /// Comments must be //... or /* ... */ + void setComment(const char* comment, CommentPlacement placement); + /// Comments must be //... or /* ... */ + void setComment(const std::string& comment, CommentPlacement placement); + bool hasComment(CommentPlacement placement) const; + /// Include delimiters and embedded newlines. + std::string getComment(CommentPlacement placement) const; + + std::string toStyledString() const; + + const_iterator begin() const; + const_iterator end() const; + + iterator begin(); + iterator end(); + + // Accessors for the [start, limit) range of bytes within the JSON text from + // which this value was parsed, if any. + void setOffsetStart(size_t start); + void setOffsetLimit(size_t limit); + size_t getOffsetStart() const; + size_t getOffsetLimit() const; + +private: + void initBasic(ValueType type, bool allocated = false); + + Value& resolveReference(const char* key, bool isStatic); + +#ifdef JSON_VALUE_USE_INTERNAL_MAP + inline bool isItemAvailable() const { return itemIsUsed_ == 0; } + + inline void setItemUsed(bool isUsed = true) { itemIsUsed_ = isUsed ? 1 : 0; } + + inline bool isMemberNameStatic() const { return memberNameIsStatic_ == 0; } + + inline void setMemberNameIsStatic(bool isStatic) { + memberNameIsStatic_ = isStatic ? 1 : 0; + } +#endif // # ifdef JSON_VALUE_USE_INTERNAL_MAP + +private: + struct CommentInfo { + CommentInfo(); + ~CommentInfo(); + + void setComment(const char* text); + + char* comment_; + }; + + // struct MemberNamesTransform + //{ + // typedef const char *result_type; + // const char *operator()( const CZString &name ) const + // { + // return name.c_str(); + // } + //}; + + union ValueHolder { + LargestInt int_; + LargestUInt uint_; + double real_; + bool bool_; + char* string_; +#ifdef JSON_VALUE_USE_INTERNAL_MAP + ValueInternalArray* array_; + ValueInternalMap* map_; +#else + ObjectValues* map_; +#endif + } value_; + ValueType type_ : 8; + int allocated_ : 1; // Notes: if declared as bool, bitfield is useless. +#ifdef JSON_VALUE_USE_INTERNAL_MAP + unsigned int itemIsUsed_ : 1; // used by the ValueInternalMap container. + int memberNameIsStatic_ : 1; // used by the ValueInternalMap container. +#endif + CommentInfo* comments_; + + // [start, limit) byte offsets in the source JSON text from which this Value + // was extracted. + size_t start_; + size_t limit_; +}; + +/** \brief Experimental and untested: represents an element of the "path" to + * access a node. + */ +class JSON_API PathArgument { +public: + friend class Path; + + PathArgument(); + PathArgument(ArrayIndex index); + PathArgument(const char* key); + PathArgument(const std::string& key); + +private: + enum Kind { + kindNone = 0, + kindIndex, + kindKey + }; + std::string key_; + ArrayIndex index_; + Kind kind_; +}; + +/** \brief Experimental and untested: represents a "path" to access a node. + * + * Syntax: + * - "." => root node + * - ".[n]" => elements at index 'n' of root node (an array value) + * - ".name" => member named 'name' of root node (an object value) + * - ".name1.name2.name3" + * - ".[0][1][2].name1[3]" + * - ".%" => member name is provided as parameter + * - ".[%]" => index is provied as parameter + */ +class JSON_API Path { +public: + Path(const std::string& path, + const PathArgument& a1 = PathArgument(), + const PathArgument& a2 = PathArgument(), + const PathArgument& a3 = PathArgument(), + const PathArgument& a4 = PathArgument(), + const PathArgument& a5 = PathArgument()); + + const Value& resolve(const Value& root) const; + Value resolve(const Value& root, const Value& defaultValue) const; + /// Creates the "path" to access the specified node and returns a reference on + /// the node. + Value& make(Value& root) const; + +private: + typedef std::vector InArgs; + typedef std::vector Args; + + void makePath(const std::string& path, const InArgs& in); + void addPathInArg(const std::string& path, + const InArgs& in, + InArgs::const_iterator& itInArg, + PathArgument::Kind kind); + void invalidPath(const std::string& path, int location); + + Args args_; +}; + +#ifdef JSON_VALUE_USE_INTERNAL_MAP +/** \brief Allocator to customize Value internal map. + * Below is an example of a simple implementation (default implementation + actually + * use memory pool for speed). + * \code + class DefaultValueMapAllocator : public ValueMapAllocator + { + public: // overridden from ValueMapAllocator + virtual ValueInternalMap *newMap() + { + return new ValueInternalMap(); + } + + virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) + { + return new ValueInternalMap( other ); + } + + virtual void destructMap( ValueInternalMap *map ) + { + delete map; + } + + virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) + { + return new ValueInternalLink[size]; + } + + virtual void releaseMapBuckets( ValueInternalLink *links ) + { + delete [] links; + } + + virtual ValueInternalLink *allocateMapLink() + { + return new ValueInternalLink(); + } + + virtual void releaseMapLink( ValueInternalLink *link ) + { + delete link; + } + }; + * \endcode + */ +class JSON_API ValueMapAllocator { +public: + virtual ~ValueMapAllocator(); + virtual ValueInternalMap* newMap() = 0; + virtual ValueInternalMap* newMapCopy(const ValueInternalMap& other) = 0; + virtual void destructMap(ValueInternalMap* map) = 0; + virtual ValueInternalLink* allocateMapBuckets(unsigned int size) = 0; + virtual void releaseMapBuckets(ValueInternalLink* links) = 0; + virtual ValueInternalLink* allocateMapLink() = 0; + virtual void releaseMapLink(ValueInternalLink* link) = 0; +}; + +/** \brief ValueInternalMap hash-map bucket chain link (for internal use only). + * \internal previous_ & next_ allows for bidirectional traversal. + */ +class JSON_API ValueInternalLink { +public: + enum { + itemPerLink = 6 + }; // sizeof(ValueInternalLink) = 128 on 32 bits architecture. + enum InternalFlags { + flagAvailable = 0, + flagUsed = 1 + }; + + ValueInternalLink(); + + ~ValueInternalLink(); + + Value items_[itemPerLink]; + char* keys_[itemPerLink]; + ValueInternalLink* previous_; + ValueInternalLink* next_; +}; + +/** \brief A linked page based hash-table implementation used internally by + *Value. + * \internal ValueInternalMap is a tradional bucket based hash-table, with a + *linked + * list in each bucket to handle collision. There is an addional twist in that + * each node of the collision linked list is a page containing a fixed amount of + * value. This provides a better compromise between memory usage and speed. + * + * Each bucket is made up of a chained list of ValueInternalLink. The last + * link of a given bucket can be found in the 'previous_' field of the following + *bucket. + * The last link of the last bucket is stored in tailLink_ as it has no + *following bucket. + * Only the last link of a bucket may contains 'available' item. The last link + *always + * contains at least one element unless is it the bucket one very first link. + */ +class JSON_API ValueInternalMap { + friend class ValueIteratorBase; + friend class Value; + +public: + typedef unsigned int HashKey; + typedef unsigned int BucketIndex; + +#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + struct IteratorState { + IteratorState() : map_(0), link_(0), itemIndex_(0), bucketIndex_(0) {} + ValueInternalMap* map_; + ValueInternalLink* link_; + BucketIndex itemIndex_; + BucketIndex bucketIndex_; + }; +#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + + ValueInternalMap(); + ValueInternalMap(const ValueInternalMap& other); + ValueInternalMap& operator=(ValueInternalMap other); + ~ValueInternalMap(); + + void swap(ValueInternalMap& other); + + BucketIndex size() const; + + void clear(); + + bool reserveDelta(BucketIndex growth); + + bool reserve(BucketIndex newItemCount); + + const Value* find(const char* key) const; + + Value* find(const char* key); + + Value& resolveReference(const char* key, bool isStatic); + + void remove(const char* key); + + void doActualRemove(ValueInternalLink* link, + BucketIndex index, + BucketIndex bucketIndex); + + ValueInternalLink*& getLastLinkInBucket(BucketIndex bucketIndex); + + Value& setNewItem(const char* key, + bool isStatic, + ValueInternalLink* link, + BucketIndex index); + + Value& unsafeAdd(const char* key, bool isStatic, HashKey hashedKey); + + HashKey hash(const char* key) const; + + int compare(const ValueInternalMap& other) const; + +private: + void makeBeginIterator(IteratorState& it) const; + void makeEndIterator(IteratorState& it) const; + static bool equals(const IteratorState& x, const IteratorState& other); + static void increment(IteratorState& iterator); + static void incrementBucket(IteratorState& iterator); + static void decrement(IteratorState& iterator); + static const char* key(const IteratorState& iterator); + static const char* key(const IteratorState& iterator, bool& isStatic); + static Value& value(const IteratorState& iterator); + static int distance(const IteratorState& x, const IteratorState& y); + +private: + ValueInternalLink* buckets_; + ValueInternalLink* tailLink_; + BucketIndex bucketsSize_; + BucketIndex itemCount_; +}; + +/** \brief A simplified deque implementation used internally by Value. +* \internal +* It is based on a list of fixed "page", each page contains a fixed number of +*items. +* Instead of using a linked-list, a array of pointer is used for fast item +*look-up. +* Look-up for an element is as follow: +* - compute page index: pageIndex = itemIndex / itemsPerPage +* - look-up item in page: pages_[pageIndex][itemIndex % itemsPerPage] +* +* Insertion is amortized constant time (only the array containing the index of +*pointers +* need to be reallocated when items are appended). +*/ +class JSON_API ValueInternalArray { + friend class Value; + friend class ValueIteratorBase; + +public: + enum { + itemsPerPage = 8 + }; // should be a power of 2 for fast divide and modulo. + typedef Value::ArrayIndex ArrayIndex; + typedef unsigned int PageIndex; + +#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + struct IteratorState // Must be a POD + { + IteratorState() : array_(0), currentPageIndex_(0), currentItemIndex_(0) {} + ValueInternalArray* array_; + Value** currentPageIndex_; + unsigned int currentItemIndex_; + }; +#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + + ValueInternalArray(); + ValueInternalArray(const ValueInternalArray& other); + ValueInternalArray& operator=(ValueInternalArray other); + ~ValueInternalArray(); + void swap(ValueInternalArray& other); + + void clear(); + void resize(ArrayIndex newSize); + + Value& resolveReference(ArrayIndex index); + + Value* find(ArrayIndex index) const; + + ArrayIndex size() const; + + int compare(const ValueInternalArray& other) const; + +private: + static bool equals(const IteratorState& x, const IteratorState& other); + static void increment(IteratorState& iterator); + static void decrement(IteratorState& iterator); + static Value& dereference(const IteratorState& iterator); + static Value& unsafeDereference(const IteratorState& iterator); + static int distance(const IteratorState& x, const IteratorState& y); + static ArrayIndex indexOf(const IteratorState& iterator); + void makeBeginIterator(IteratorState& it) const; + void makeEndIterator(IteratorState& it) const; + void makeIterator(IteratorState& it, ArrayIndex index) const; + + void makeIndexValid(ArrayIndex index); + + Value** pages_; + ArrayIndex size_; + PageIndex pageCount_; +}; + +/** \brief Experimental: do not use. Allocator to customize Value internal +array. + * Below is an example of a simple implementation (actual implementation use + * memory pool). + \code +class DefaultValueArrayAllocator : public ValueArrayAllocator +{ +public: // overridden from ValueArrayAllocator +virtual ~DefaultValueArrayAllocator() +{ +} + +virtual ValueInternalArray *newArray() +{ + return new ValueInternalArray(); +} + +virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) +{ + return new ValueInternalArray( other ); +} + +virtual void destruct( ValueInternalArray *array ) +{ + delete array; +} + +virtual void reallocateArrayPageIndex( Value **&indexes, + ValueInternalArray::PageIndex +&indexCount, + ValueInternalArray::PageIndex +minNewIndexCount ) +{ + ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1; + if ( minNewIndexCount > newIndexCount ) + newIndexCount = minNewIndexCount; + void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount ); + if ( !newIndexes ) + throw std::bad_alloc(); + indexCount = newIndexCount; + indexes = static_cast( newIndexes ); +} +virtual void releaseArrayPageIndex( Value **indexes, + ValueInternalArray::PageIndex indexCount ) +{ + if ( indexes ) + free( indexes ); +} + +virtual Value *allocateArrayPage() +{ + return static_cast( malloc( sizeof(Value) * +ValueInternalArray::itemsPerPage ) ); +} + +virtual void releaseArrayPage( Value *value ) +{ + if ( value ) + free( value ); +} +}; + \endcode + */ +class JSON_API ValueArrayAllocator { +public: + virtual ~ValueArrayAllocator(); + virtual ValueInternalArray* newArray() = 0; + virtual ValueInternalArray* newArrayCopy(const ValueInternalArray& other) = 0; + virtual void destructArray(ValueInternalArray* array) = 0; + /** \brief Reallocate array page index. + * Reallocates an array of pointer on each page. + * \param indexes [input] pointer on the current index. May be \c NULL. + * [output] pointer on the new index of at least + * \a minNewIndexCount pages. + * \param indexCount [input] current number of pages in the index. + * [output] number of page the reallocated index can handle. + * \b MUST be >= \a minNewIndexCount. + * \param minNewIndexCount Minimum number of page the new index must be able + * to + * handle. + */ + virtual void + reallocateArrayPageIndex(Value**& indexes, + ValueInternalArray::PageIndex& indexCount, + ValueInternalArray::PageIndex minNewIndexCount) = 0; + virtual void + releaseArrayPageIndex(Value** indexes, + ValueInternalArray::PageIndex indexCount) = 0; + virtual Value* allocateArrayPage() = 0; + virtual void releaseArrayPage(Value* value) = 0; +}; +#endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP + +/** \brief base class for Value iterators. + * + */ +class JSON_API ValueIteratorBase { +public: + typedef std::bidirectional_iterator_tag iterator_category; + typedef unsigned int size_t; + typedef int difference_type; + typedef ValueIteratorBase SelfType; + + ValueIteratorBase(); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + explicit ValueIteratorBase(const Value::ObjectValues::iterator& current); +#else + ValueIteratorBase(const ValueInternalArray::IteratorState& state); + ValueIteratorBase(const ValueInternalMap::IteratorState& state); +#endif + + bool operator==(const SelfType& other) const { return isEqual(other); } + + bool operator!=(const SelfType& other) const { return !isEqual(other); } + + difference_type operator-(const SelfType& other) const { + return computeDistance(other); + } + + /// Return either the index or the member name of the referenced value as a + /// Value. + Value key() const; + + /// Return the index of the referenced Value. -1 if it is not an arrayValue. + UInt index() const; + + /// Return the member name of the referenced Value. "" if it is not an + /// objectValue. + const char* memberName() const; + +protected: + Value& deref() const; + + void increment(); + + void decrement(); + + difference_type computeDistance(const SelfType& other) const; + + bool isEqual(const SelfType& other) const; + + void copy(const SelfType& other); + +private: +#ifndef JSON_VALUE_USE_INTERNAL_MAP + Value::ObjectValues::iterator current_; + // Indicates that iterator is for a null value. + bool isNull_; +#else + union { + ValueInternalArray::IteratorState array_; + ValueInternalMap::IteratorState map_; + } iterator_; + bool isArray_; +#endif +}; + +/** \brief const iterator for object and array value. + * + */ +class JSON_API ValueConstIterator : public ValueIteratorBase { + friend class Value; + +public: + typedef const Value value_type; + typedef unsigned int size_t; + typedef int difference_type; + typedef const Value& reference; + typedef const Value* pointer; + typedef ValueConstIterator SelfType; + + ValueConstIterator(); + +private: +/*! \internal Use by Value to create an iterator. + */ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + explicit ValueConstIterator(const Value::ObjectValues::iterator& current); +#else + ValueConstIterator(const ValueInternalArray::IteratorState& state); + ValueConstIterator(const ValueInternalMap::IteratorState& state); +#endif +public: + SelfType& operator=(const ValueIteratorBase& other); + + SelfType operator++(int) { + SelfType temp(*this); + ++*this; + return temp; + } + + SelfType operator--(int) { + SelfType temp(*this); + --*this; + return temp; + } + + SelfType& operator--() { + decrement(); + return *this; + } + + SelfType& operator++() { + increment(); + return *this; + } + + reference operator*() const { return deref(); } + + pointer operator->() const { return &deref(); } +}; + +/** \brief Iterator for object and array value. + */ +class JSON_API ValueIterator : public ValueIteratorBase { + friend class Value; + +public: + typedef Value value_type; + typedef unsigned int size_t; + typedef int difference_type; + typedef Value& reference; + typedef Value* pointer; + typedef ValueIterator SelfType; + + ValueIterator(); + ValueIterator(const ValueConstIterator& other); + ValueIterator(const ValueIterator& other); + +private: +/*! \internal Use by Value to create an iterator. + */ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + explicit ValueIterator(const Value::ObjectValues::iterator& current); +#else + ValueIterator(const ValueInternalArray::IteratorState& state); + ValueIterator(const ValueInternalMap::IteratorState& state); +#endif +public: + SelfType& operator=(const SelfType& other); + + SelfType operator++(int) { + SelfType temp(*this); + ++*this; + return temp; + } + + SelfType operator--(int) { + SelfType temp(*this); + --*this; + return temp; + } + + SelfType& operator--() { + decrement(); + return *this; + } + + SelfType& operator++() { + increment(); + return *this; + } + + reference operator*() const { return deref(); } + + pointer operator->() const { return &deref(); } +}; + +} // namespace Json +} // namespace VA +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // CPPTL_JSON_H_INCLUDED diff --git a/VrUtils/jsoncpp/json/version.h b/VrUtils/jsoncpp/json/version.h new file mode 100644 index 0000000..ca5c283 --- /dev/null +++ b/VrUtils/jsoncpp/json/version.h @@ -0,0 +1,14 @@ +// DO NOT EDIT. This file is generated by CMake from "version" +// and "version.h.in" files. +// Run CMake configure step to update it. +#ifndef JSON_VERSION_H_INCLUDED +# define JSON_VERSION_H_INCLUDED + +# define JSONCPP_VERSION_STRING "0.7.0" +# define JSONCPP_VERSION_MAJOR 0 +# define JSONCPP_VERSION_MINOR 7 +# define JSONCPP_VERSION_PATCH 0 +# define JSONCPP_VERSION_QUALIFIER +# define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | (JSONCPP_VERSION_PATCH << 8)) + +#endif // JSON_VERSION_H_INCLUDED diff --git a/VrUtils/jsoncpp/json/writer.h b/VrUtils/jsoncpp/json/writer.h new file mode 100644 index 0000000..22dcfb4 --- /dev/null +++ b/VrUtils/jsoncpp/json/writer.h @@ -0,0 +1,214 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_WRITER_H_INCLUDED +#define JSON_WRITER_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "value.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include + +// Disable warning C4251: : needs to have dll-interface to +// be used by... +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(push) +#pragma warning(disable : 4251) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +namespace VA { +namespace Json { + +class Value; + +/** \brief Abstract class for writers. + */ +class JSON_API Writer { +public: + virtual ~Writer(); + + virtual std::string write(const Value& root) = 0; +}; + +/** \brief Outputs a Value in JSON format + *without formatting (not human friendly). + * + * The JSON document is written in a single line. It is not intended for 'human' + *consumption, + * but may be usefull to support feature such as RPC where bandwith is limited. + * \sa Reader, Value + */ +class JSON_API FastWriter : public Writer { +public: + FastWriter(); + virtual ~FastWriter() {} + + void enableYAMLCompatibility(); + + /** \brief Drop the "null" string from the writer's output for nullValues. + * Strictly speaking, this is not valid JSON. But when the output is being + * fed to a browser's Javascript, it makes for smaller output and the + * browser can handle the output just fine. + */ + void dropNullPlaceholders(); + + void omitEndingLineFeed(); + +public: // overridden from Writer + virtual std::string write(const Value& root); + +private: + void writeValue(const Value& value); + + std::string document_; + bool yamlCompatiblityEnabled_; + bool dropNullPlaceholders_; + bool omitEndingLineFeed_; +}; + +/** \brief Writes a Value in JSON format in a + *human friendly way. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per + *line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value + *types, + * and all the values fit on one lines, then print the array on a single + *line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputed according to their + *#CommentPlacement. + * + * \sa Reader, Value, Value::setComment() + */ +class JSON_API StyledWriter : public Writer { +public: + StyledWriter(); + virtual ~StyledWriter() {} + +public: // overridden from Writer + /** \brief Serialize a Value in JSON format. + * \param root Value to serialize. + * \return String containing the JSON document that represents the root value. + */ + virtual std::string write(const Value& root); + +private: + void writeValue(const Value& value); + void writeArrayValue(const Value& value); + bool isMultineArray(const Value& value); + void pushValue(const std::string& value); + void writeIndent(); + void writeWithIndent(const std::string& value); + void indent(); + void unindent(); + void writeCommentBeforeValue(const Value& root); + void writeCommentAfterValueOnSameLine(const Value& root); + bool hasCommentForValue(const Value& value); + static std::string normalizeEOL(const std::string& text); + + typedef std::vector ChildValues; + + ChildValues childValues_; + std::string document_; + std::string indentString_; + int rightMargin_; + int indentSize_; + bool addChildValues_; +}; + +/** \brief Writes a Value in JSON format in a + human friendly way, + to a stream rather than to a string. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per + line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value + types, + * and all the values fit on one lines, then print the array on a single + line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputed according to their + #CommentPlacement. + * + * \param indentation Each level will be indented by this amount extra. + * \sa Reader, Value, Value::setComment() + */ +class JSON_API StyledStreamWriter { +public: + StyledStreamWriter(std::string indentation = "\t"); + ~StyledStreamWriter() {} + +public: + /** \brief Serialize a Value in JSON format. + * \param out Stream to write to. (Can be ostringstream, e.g.) + * \param root Value to serialize. + * \note There is no point in deriving from Writer, since write() should not + * return a value. + */ + void write(std::ostream& out, const Value& root); + +private: + void writeValue(const Value& value); + void writeArrayValue(const Value& value); + bool isMultineArray(const Value& value); + void pushValue(const std::string& value); + void writeIndent(); + void writeWithIndent(const std::string& value); + void indent(); + void unindent(); + void writeCommentBeforeValue(const Value& root); + void writeCommentAfterValueOnSameLine(const Value& root); + bool hasCommentForValue(const Value& value); + static std::string normalizeEOL(const std::string& text); + + typedef std::vector ChildValues; + + ChildValues childValues_; + std::ostream* document_; + std::string indentString_; + int rightMargin_; + std::string indentation_; + bool addChildValues_; +}; + +#if defined(JSON_HAS_INT64) +std::string JSON_API valueToString(Int value); +std::string JSON_API valueToString(UInt value); +#endif // if defined(JSON_HAS_INT64) +std::string JSON_API valueToString(LargestInt value); +std::string JSON_API valueToString(LargestUInt value); +std::string JSON_API valueToString(double value); +std::string JSON_API valueToString(bool value); +std::string JSON_API valueToQuotedString(const char* value); + +/// \brief Output using the StyledStreamWriter. +/// \see Json::operator>>() +JSON_API std::ostream& operator<<(std::ostream&, const Value& root); + +} // namespace Json +} // namespace VA +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // JSON_WRITER_H_INCLUDED diff --git a/VrUtils/jsoncpp/json_batchallocator.h b/VrUtils/jsoncpp/json_batchallocator.h new file mode 100644 index 0000000..ddae908 --- /dev/null +++ b/VrUtils/jsoncpp/json_batchallocator.h @@ -0,0 +1,123 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSONCPP_BATCHALLOCATOR_H_INCLUDED +#define JSONCPP_BATCHALLOCATOR_H_INCLUDED + +#include +#include + +#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + +namespace VA { +namespace Json { + +/* Fast memory allocator. + * + * This memory allocator allocates memory for a batch of object (specified by + * the page size, the number of object in each page). + * + * It does not allow the destruction of a single object. All the allocated + * objects can be destroyed at once. The memory can be either released or reused + * for future allocation. + * + * The in-place new operator must be used to construct the object using the + * pointer returned by allocate. + */ +template +class BatchAllocator { +public: + BatchAllocator(unsigned int objectsPerPage = 255) + : freeHead_(0), objectsPerPage_(objectsPerPage) { + // printf( "Size: %d => %s\n", sizeof(AllocatedType), + // typeid(AllocatedType).name() ); + assert(sizeof(AllocatedType) * objectPerAllocation >= + sizeof(AllocatedType*)); // We must be able to store a slist in the + // object free space. + assert(objectsPerPage >= 16); + batches_ = allocateBatch(0); // allocated a dummy page + currentBatch_ = batches_; + } + + ~BatchAllocator() { + for (BatchInfo* batch = batches_; batch;) { + BatchInfo* nextBatch = batch->next_; + free(batch); + batch = nextBatch; + } + } + + /// allocate space for an array of objectPerAllocation object. + /// @warning it is the responsability of the caller to call objects + /// constructors. + AllocatedType* allocate() { + if (freeHead_) // returns node from free list. + { + AllocatedType* object = freeHead_; + freeHead_ = *(AllocatedType**)object; + return object; + } + if (currentBatch_->used_ == currentBatch_->end_) { + currentBatch_ = currentBatch_->next_; + while (currentBatch_ && currentBatch_->used_ == currentBatch_->end_) + currentBatch_ = currentBatch_->next_; + + if (!currentBatch_) // no free batch found, allocate a new one + { + currentBatch_ = allocateBatch(objectsPerPage_); + currentBatch_->next_ = batches_; // insert at the head of the list + batches_ = currentBatch_; + } + } + AllocatedType* allocated = currentBatch_->used_; + currentBatch_->used_ += objectPerAllocation; + return allocated; + } + + /// Release the object. + /// @warning it is the responsability of the caller to actually destruct the + /// object. + void release(AllocatedType* object) { + assert(object != 0); + *(AllocatedType**)object = freeHead_; + freeHead_ = object; + } + +private: + struct BatchInfo { + BatchInfo* next_; + AllocatedType* used_; + AllocatedType* end_; + AllocatedType buffer_[objectPerAllocation]; + }; + + // disabled copy constructor and assignement operator. + BatchAllocator(const BatchAllocator&); + void operator=(const BatchAllocator&); + + static BatchInfo* allocateBatch(unsigned int objectsPerPage) { + const unsigned int mallocSize = + sizeof(BatchInfo) - sizeof(AllocatedType) * objectPerAllocation + + sizeof(AllocatedType) * objectPerAllocation * objectsPerPage; + BatchInfo* batch = static_cast(malloc(mallocSize)); + batch->next_ = 0; + batch->used_ = batch->buffer_; + batch->end_ = batch->buffer_ + objectsPerPage; + return batch; + } + + BatchInfo* batches_; + BatchInfo* currentBatch_; + /// Head of a single linked list within the allocated space of freeed object + AllocatedType* freeHead_; + unsigned int objectsPerPage_; +}; + +} // namespace Json +} // namespace VA + +#endif // ifndef JSONCPP_DOC_INCLUDE_IMPLEMENTATION + +#endif // JSONCPP_BATCHALLOCATOR_H_INCLUDED diff --git a/VrUtils/jsoncpp/json_internalarray.inl b/VrUtils/jsoncpp/json_internalarray.inl new file mode 100644 index 0000000..9ee15e9 --- /dev/null +++ b/VrUtils/jsoncpp/json_internalarray.inl @@ -0,0 +1,360 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +// included by json_value.cpp + +namespace Json { + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueInternalArray +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueArrayAllocator::~ValueArrayAllocator() {} + +// ////////////////////////////////////////////////////////////////// +// class DefaultValueArrayAllocator +// ////////////////////////////////////////////////////////////////// +#ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR +class DefaultValueArrayAllocator : public ValueArrayAllocator { +public: // overridden from ValueArrayAllocator + virtual ~DefaultValueArrayAllocator() {} + + virtual ValueInternalArray* newArray() { return new ValueInternalArray(); } + + virtual ValueInternalArray* newArrayCopy(const ValueInternalArray& other) { + return new ValueInternalArray(other); + } + + virtual void destructArray(ValueInternalArray* array) { delete array; } + + virtual void + reallocateArrayPageIndex(Value**& indexes, + ValueInternalArray::PageIndex& indexCount, + ValueInternalArray::PageIndex minNewIndexCount) { + ValueInternalArray::PageIndex newIndexCount = (indexCount * 3) / 2 + 1; + if (minNewIndexCount > newIndexCount) + newIndexCount = minNewIndexCount; + void* newIndexes = realloc(indexes, sizeof(Value*) * newIndexCount); + JSON_ASSERT_MESSAGE(newIndexes, "Couldn't realloc."); + indexCount = newIndexCount; + indexes = static_cast(newIndexes); + } + virtual void releaseArrayPageIndex(Value** indexes, + ValueInternalArray::PageIndex indexCount) { + if (indexes) + free(indexes); + } + + virtual Value* allocateArrayPage() { + return static_cast( + malloc(sizeof(Value) * ValueInternalArray::itemsPerPage)); + } + + virtual void releaseArrayPage(Value* value) { + if (value) + free(value); + } +}; + +#else // #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR +/// @todo make this thread-safe (lock when accessign batch allocator) +class DefaultValueArrayAllocator : public ValueArrayAllocator { +public: // overridden from ValueArrayAllocator + virtual ~DefaultValueArrayAllocator() {} + + virtual ValueInternalArray* newArray() { + ValueInternalArray* array = arraysAllocator_.allocate(); + new (array) ValueInternalArray(); // placement new + return array; + } + + virtual ValueInternalArray* newArrayCopy(const ValueInternalArray& other) { + ValueInternalArray* array = arraysAllocator_.allocate(); + new (array) ValueInternalArray(other); // placement new + return array; + } + + virtual void destructArray(ValueInternalArray* array) { + if (array) { + array->~ValueInternalArray(); + arraysAllocator_.release(array); + } + } + + virtual void + reallocateArrayPageIndex(Value**& indexes, + ValueInternalArray::PageIndex& indexCount, + ValueInternalArray::PageIndex minNewIndexCount) { + ValueInternalArray::PageIndex newIndexCount = (indexCount * 3) / 2 + 1; + if (minNewIndexCount > newIndexCount) + newIndexCount = minNewIndexCount; + void* newIndexes = realloc(indexes, sizeof(Value*) * newIndexCount); + JSON_ASSERT_MESSAGE(newIndexes, "Couldn't realloc."); + indexCount = newIndexCount; + indexes = static_cast(newIndexes); + } + virtual void releaseArrayPageIndex(Value** indexes, + ValueInternalArray::PageIndex indexCount) { + if (indexes) + free(indexes); + } + + virtual Value* allocateArrayPage() { + return static_cast(pagesAllocator_.allocate()); + } + + virtual void releaseArrayPage(Value* value) { + if (value) + pagesAllocator_.release(value); + } + +private: + BatchAllocator arraysAllocator_; + BatchAllocator pagesAllocator_; +}; +#endif // #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR + +static ValueArrayAllocator*& arrayAllocator() { + static DefaultValueArrayAllocator defaultAllocator; + static ValueArrayAllocator* arrayAllocator = &defaultAllocator; + return arrayAllocator; +} + +static struct DummyArrayAllocatorInitializer { + DummyArrayAllocatorInitializer() { + arrayAllocator(); // ensure arrayAllocator() statics are initialized before + // main(). + } +} dummyArrayAllocatorInitializer; + +// ////////////////////////////////////////////////////////////////// +// class ValueInternalArray +// ////////////////////////////////////////////////////////////////// +bool ValueInternalArray::equals(const IteratorState& x, + const IteratorState& other) { + return x.array_ == other.array_ && + x.currentItemIndex_ == other.currentItemIndex_ && + x.currentPageIndex_ == other.currentPageIndex_; +} + +void ValueInternalArray::increment(IteratorState& it) { + JSON_ASSERT_MESSAGE( + it.array_ && (it.currentPageIndex_ - it.array_->pages_) * itemsPerPage + + it.currentItemIndex_ != + it.array_->size_, + "ValueInternalArray::increment(): moving iterator beyond end"); + ++(it.currentItemIndex_); + if (it.currentItemIndex_ == itemsPerPage) { + it.currentItemIndex_ = 0; + ++(it.currentPageIndex_); + } +} + +void ValueInternalArray::decrement(IteratorState& it) { + JSON_ASSERT_MESSAGE( + it.array_ && it.currentPageIndex_ == it.array_->pages_ && + it.currentItemIndex_ == 0, + "ValueInternalArray::decrement(): moving iterator beyond end"); + if (it.currentItemIndex_ == 0) { + it.currentItemIndex_ = itemsPerPage - 1; + --(it.currentPageIndex_); + } else { + --(it.currentItemIndex_); + } +} + +Value& ValueInternalArray::unsafeDereference(const IteratorState& it) { + return (*(it.currentPageIndex_))[it.currentItemIndex_]; +} + +Value& ValueInternalArray::dereference(const IteratorState& it) { + JSON_ASSERT_MESSAGE( + it.array_ && (it.currentPageIndex_ - it.array_->pages_) * itemsPerPage + + it.currentItemIndex_ < + it.array_->size_, + "ValueInternalArray::dereference(): dereferencing invalid iterator"); + return unsafeDereference(it); +} + +void ValueInternalArray::makeBeginIterator(IteratorState& it) const { + it.array_ = const_cast(this); + it.currentItemIndex_ = 0; + it.currentPageIndex_ = pages_; +} + +void ValueInternalArray::makeIterator(IteratorState& it, + ArrayIndex index) const { + it.array_ = const_cast(this); + it.currentItemIndex_ = index % itemsPerPage; + it.currentPageIndex_ = pages_ + index / itemsPerPage; +} + +void ValueInternalArray::makeEndIterator(IteratorState& it) const { + makeIterator(it, size_); +} + +ValueInternalArray::ValueInternalArray() : pages_(0), size_(0), pageCount_(0) {} + +ValueInternalArray::ValueInternalArray(const ValueInternalArray& other) + : pages_(0), size_(other.size_), pageCount_(0) { + PageIndex minNewPages = other.size_ / itemsPerPage; + arrayAllocator()->reallocateArrayPageIndex(pages_, pageCount_, minNewPages); + JSON_ASSERT_MESSAGE(pageCount_ >= minNewPages, + "ValueInternalArray::reserve(): bad reallocation"); + IteratorState itOther; + other.makeBeginIterator(itOther); + Value* value; + for (ArrayIndex index = 0; index < size_; ++index, increment(itOther)) { + if (index % itemsPerPage == 0) { + PageIndex pageIndex = index / itemsPerPage; + value = arrayAllocator()->allocateArrayPage(); + pages_[pageIndex] = value; + } + new (value) Value(dereference(itOther)); + } +} + +ValueInternalArray& ValueInternalArray::operator=(ValueInternalArray other) { + swap(other); + return *this; +} + +ValueInternalArray::~ValueInternalArray() { + // destroy all constructed items + IteratorState it; + IteratorState itEnd; + makeBeginIterator(it); + makeEndIterator(itEnd); + for (; !equals(it, itEnd); increment(it)) { + Value* value = &dereference(it); + value->~Value(); + } + // release all pages + PageIndex lastPageIndex = size_ / itemsPerPage; + for (PageIndex pageIndex = 0; pageIndex < lastPageIndex; ++pageIndex) + arrayAllocator()->releaseArrayPage(pages_[pageIndex]); + // release pages index + arrayAllocator()->releaseArrayPageIndex(pages_, pageCount_); +} + +void ValueInternalArray::swap(ValueInternalArray& other) { + Value** tempPages = pages_; + pages_ = other.pages_; + other.pages_ = tempPages; + ArrayIndex tempSize = size_; + size_ = other.size_; + other.size_ = tempSize; + PageIndex tempPageCount = pageCount_; + pageCount_ = other.pageCount_; + other.pageCount_ = tempPageCount; +} + +void ValueInternalArray::clear() { + ValueInternalArray dummy; + swap(dummy); +} + +void ValueInternalArray::resize(ArrayIndex newSize) { + if (newSize == 0) + clear(); + else if (newSize < size_) { + IteratorState it; + IteratorState itEnd; + makeIterator(it, newSize); + makeIterator(itEnd, size_); + for (; !equals(it, itEnd); increment(it)) { + Value* value = &dereference(it); + value->~Value(); + } + PageIndex pageIndex = (newSize + itemsPerPage - 1) / itemsPerPage; + PageIndex lastPageIndex = size_ / itemsPerPage; + for (; pageIndex < lastPageIndex; ++pageIndex) + arrayAllocator()->releaseArrayPage(pages_[pageIndex]); + size_ = newSize; + } else if (newSize > size_) + resolveReference(newSize); +} + +void ValueInternalArray::makeIndexValid(ArrayIndex index) { + // Need to enlarge page index ? + if (index >= pageCount_ * itemsPerPage) { + PageIndex minNewPages = (index + 1) / itemsPerPage; + arrayAllocator()->reallocateArrayPageIndex(pages_, pageCount_, minNewPages); + JSON_ASSERT_MESSAGE(pageCount_ >= minNewPages, + "ValueInternalArray::reserve(): bad reallocation"); + } + + // Need to allocate new pages ? + ArrayIndex nextPageIndex = (size_ % itemsPerPage) != 0 + ? size_ - (size_ % itemsPerPage) + itemsPerPage + : size_; + if (nextPageIndex <= index) { + PageIndex pageIndex = nextPageIndex / itemsPerPage; + PageIndex pageToAllocate = (index - nextPageIndex) / itemsPerPage + 1; + for (; pageToAllocate-- > 0; ++pageIndex) + pages_[pageIndex] = arrayAllocator()->allocateArrayPage(); + } + + // Initialize all new entries + IteratorState it; + IteratorState itEnd; + makeIterator(it, size_); + size_ = index + 1; + makeIterator(itEnd, size_); + for (; !equals(it, itEnd); increment(it)) { + Value* value = &dereference(it); + new (value) Value(); // Construct a default value using placement new + } +} + +Value& ValueInternalArray::resolveReference(ArrayIndex index) { + if (index >= size_) + makeIndexValid(index); + return pages_[index / itemsPerPage][index % itemsPerPage]; +} + +Value* ValueInternalArray::find(ArrayIndex index) const { + if (index >= size_) + return 0; + return &(pages_[index / itemsPerPage][index % itemsPerPage]); +} + +ValueInternalArray::ArrayIndex ValueInternalArray::size() const { + return size_; +} + +int ValueInternalArray::distance(const IteratorState& x, + const IteratorState& y) { + return indexOf(y) - indexOf(x); +} + +ValueInternalArray::ArrayIndex +ValueInternalArray::indexOf(const IteratorState& iterator) { + if (!iterator.array_) + return ArrayIndex(-1); + return ArrayIndex((iterator.currentPageIndex_ - iterator.array_->pages_) * + itemsPerPage + + iterator.currentItemIndex_); +} + +int ValueInternalArray::compare(const ValueInternalArray& other) const { + int sizeDiff(size_ - other.size_); + if (sizeDiff != 0) + return sizeDiff; + + for (ArrayIndex index = 0; index < size_; ++index) { + int diff = pages_[index / itemsPerPage][index % itemsPerPage].compare( + other.pages_[index / itemsPerPage][index % itemsPerPage]); + if (diff != 0) + return diff; + } + return 0; +} + +} // namespace Json diff --git a/VrUtils/jsoncpp/json_internalmap.inl b/VrUtils/jsoncpp/json_internalmap.inl new file mode 100644 index 0000000..ef3f330 --- /dev/null +++ b/VrUtils/jsoncpp/json_internalmap.inl @@ -0,0 +1,473 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +// included by json_value.cpp + +namespace Json { + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueInternalMap +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +/** \internal MUST be safely initialized using memset( this, 0, + * sizeof(ValueInternalLink) ); + * This optimization is used by the fast allocator. + */ +ValueInternalLink::ValueInternalLink() : previous_(0), next_(0) {} + +ValueInternalLink::~ValueInternalLink() { + for (int index = 0; index < itemPerLink; ++index) { + if (!items_[index].isItemAvailable()) { + if (!items_[index].isMemberNameStatic()) + free(keys_[index]); + } else + break; + } +} + +ValueMapAllocator::~ValueMapAllocator() {} + +#ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR +class DefaultValueMapAllocator : public ValueMapAllocator { +public: // overridden from ValueMapAllocator + virtual ValueInternalMap* newMap() { return new ValueInternalMap(); } + + virtual ValueInternalMap* newMapCopy(const ValueInternalMap& other) { + return new ValueInternalMap(other); + } + + virtual void destructMap(ValueInternalMap* map) { delete map; } + + virtual ValueInternalLink* allocateMapBuckets(unsigned int size) { + return new ValueInternalLink[size]; + } + + virtual void releaseMapBuckets(ValueInternalLink* links) { delete[] links; } + + virtual ValueInternalLink* allocateMapLink() { + return new ValueInternalLink(); + } + + virtual void releaseMapLink(ValueInternalLink* link) { delete link; } +}; +#else +/// @todo make this thread-safe (lock when accessign batch allocator) +class DefaultValueMapAllocator : public ValueMapAllocator { +public: // overridden from ValueMapAllocator + virtual ValueInternalMap* newMap() { + ValueInternalMap* map = mapsAllocator_.allocate(); + new (map) ValueInternalMap(); // placement new + return map; + } + + virtual ValueInternalMap* newMapCopy(const ValueInternalMap& other) { + ValueInternalMap* map = mapsAllocator_.allocate(); + new (map) ValueInternalMap(other); // placement new + return map; + } + + virtual void destructMap(ValueInternalMap* map) { + if (map) { + map->~ValueInternalMap(); + mapsAllocator_.release(map); + } + } + + virtual ValueInternalLink* allocateMapBuckets(unsigned int size) { + return new ValueInternalLink[size]; + } + + virtual void releaseMapBuckets(ValueInternalLink* links) { delete[] links; } + + virtual ValueInternalLink* allocateMapLink() { + ValueInternalLink* link = linksAllocator_.allocate(); + memset(link, 0, sizeof(ValueInternalLink)); + return link; + } + + virtual void releaseMapLink(ValueInternalLink* link) { + link->~ValueInternalLink(); + linksAllocator_.release(link); + } + +private: + BatchAllocator mapsAllocator_; + BatchAllocator linksAllocator_; +}; +#endif + +static ValueMapAllocator*& mapAllocator() { + static DefaultValueMapAllocator defaultAllocator; + static ValueMapAllocator* mapAllocator = &defaultAllocator; + return mapAllocator; +} + +static struct DummyMapAllocatorInitializer { + DummyMapAllocatorInitializer() { + mapAllocator(); // ensure mapAllocator() statics are initialized before + // main(). + } +} dummyMapAllocatorInitializer; + +// h(K) = value * K >> w ; with w = 32 & K prime w.r.t. 2^32. + +/* +use linked list hash map. +buckets array is a container. +linked list element contains 6 key/values. (memory = (16+4) * 6 + 4 = 124) +value have extra state: valid, available, deleted +*/ + +ValueInternalMap::ValueInternalMap() + : buckets_(0), tailLink_(0), bucketsSize_(0), itemCount_(0) {} + +ValueInternalMap::ValueInternalMap(const ValueInternalMap& other) + : buckets_(0), tailLink_(0), bucketsSize_(0), itemCount_(0) { + reserve(other.itemCount_); + IteratorState it; + IteratorState itEnd; + other.makeBeginIterator(it); + other.makeEndIterator(itEnd); + for (; !equals(it, itEnd); increment(it)) { + bool isStatic; + const char* memberName = key(it, isStatic); + const Value& aValue = value(it); + resolveReference(memberName, isStatic) = aValue; + } +} + +ValueInternalMap& ValueInternalMap::operator=(ValueInternalMap other) { + swap(other); + return *this; +} + +ValueInternalMap::~ValueInternalMap() { + if (buckets_) { + for (BucketIndex bucketIndex = 0; bucketIndex < bucketsSize_; + ++bucketIndex) { + ValueInternalLink* link = buckets_[bucketIndex].next_; + while (link) { + ValueInternalLink* linkToRelease = link; + link = link->next_; + mapAllocator()->releaseMapLink(linkToRelease); + } + } + mapAllocator()->releaseMapBuckets(buckets_); + } +} + +void ValueInternalMap::swap(ValueInternalMap& other) { + ValueInternalLink* tempBuckets = buckets_; + buckets_ = other.buckets_; + other.buckets_ = tempBuckets; + ValueInternalLink* tempTailLink = tailLink_; + tailLink_ = other.tailLink_; + other.tailLink_ = tempTailLink; + BucketIndex tempBucketsSize = bucketsSize_; + bucketsSize_ = other.bucketsSize_; + other.bucketsSize_ = tempBucketsSize; + BucketIndex tempItemCount = itemCount_; + itemCount_ = other.itemCount_; + other.itemCount_ = tempItemCount; +} + +void ValueInternalMap::clear() { + ValueInternalMap dummy; + swap(dummy); +} + +ValueInternalMap::BucketIndex ValueInternalMap::size() const { + return itemCount_; +} + +bool ValueInternalMap::reserveDelta(BucketIndex growth) { + return reserve(itemCount_ + growth); +} + +bool ValueInternalMap::reserve(BucketIndex newItemCount) { + if (!buckets_ && newItemCount > 0) { + buckets_ = mapAllocator()->allocateMapBuckets(1); + bucketsSize_ = 1; + tailLink_ = &buckets_[0]; + } + // BucketIndex idealBucketCount = (newItemCount + + // ValueInternalLink::itemPerLink) / ValueInternalLink::itemPerLink; + return true; +} + +const Value* ValueInternalMap::find(const char* key) const { + if (!bucketsSize_) + return 0; + HashKey hashedKey = hash(key); + BucketIndex bucketIndex = hashedKey % bucketsSize_; + for (const ValueInternalLink* current = &buckets_[bucketIndex]; current != 0; + current = current->next_) { + for (BucketIndex index = 0; index < ValueInternalLink::itemPerLink; + ++index) { + if (current->items_[index].isItemAvailable()) + return 0; + if (strcmp(key, current->keys_[index]) == 0) + return ¤t->items_[index]; + } + } + return 0; +} + +Value* ValueInternalMap::find(const char* key) { + const ValueInternalMap* constThis = this; + return const_cast(constThis->find(key)); +} + +Value& ValueInternalMap::resolveReference(const char* key, bool isStatic) { + HashKey hashedKey = hash(key); + if (bucketsSize_) { + BucketIndex bucketIndex = hashedKey % bucketsSize_; + ValueInternalLink** previous = 0; + BucketIndex index; + for (ValueInternalLink* current = &buckets_[bucketIndex]; current != 0; + previous = ¤t->next_, current = current->next_) { + for (index = 0; index < ValueInternalLink::itemPerLink; ++index) { + if (current->items_[index].isItemAvailable()) + return setNewItem(key, isStatic, current, index); + if (strcmp(key, current->keys_[index]) == 0) + return current->items_[index]; + } + } + } + + reserveDelta(1); + return unsafeAdd(key, isStatic, hashedKey); +} + +void ValueInternalMap::remove(const char* key) { + HashKey hashedKey = hash(key); + if (!bucketsSize_) + return; + BucketIndex bucketIndex = hashedKey % bucketsSize_; + for (ValueInternalLink* link = &buckets_[bucketIndex]; link != 0; + link = link->next_) { + BucketIndex index; + for (index = 0; index < ValueInternalLink::itemPerLink; ++index) { + if (link->items_[index].isItemAvailable()) + return; + if (strcmp(key, link->keys_[index]) == 0) { + doActualRemove(link, index, bucketIndex); + return; + } + } + } +} + +void ValueInternalMap::doActualRemove(ValueInternalLink* link, + BucketIndex index, + BucketIndex bucketIndex) { + // find last item of the bucket and swap it with the 'removed' one. + // set removed items flags to 'available'. + // if last page only contains 'available' items, then desallocate it (it's + // empty) + ValueInternalLink*& lastLink = getLastLinkInBucket(index); + BucketIndex lastItemIndex = 1; // a link can never be empty, so start at 1 + for (; lastItemIndex < ValueInternalLink::itemPerLink; + ++lastItemIndex) // may be optimized with dicotomic search + { + if (lastLink->items_[lastItemIndex].isItemAvailable()) + break; + } + + BucketIndex lastUsedIndex = lastItemIndex - 1; + Value* valueToDelete = &link->items_[index]; + Value* valueToPreserve = &lastLink->items_[lastUsedIndex]; + if (valueToDelete != valueToPreserve) + valueToDelete->swap(*valueToPreserve); + if (lastUsedIndex == 0) // page is now empty + { // remove it from bucket linked list and delete it. + ValueInternalLink* linkPreviousToLast = lastLink->previous_; + if (linkPreviousToLast != 0) // can not deleted bucket link. + { + mapAllocator()->releaseMapLink(lastLink); + linkPreviousToLast->next_ = 0; + lastLink = linkPreviousToLast; + } + } else { + Value dummy; + valueToPreserve->swap(dummy); // restore deleted to default Value. + valueToPreserve->setItemUsed(false); + } + --itemCount_; +} + +ValueInternalLink*& +ValueInternalMap::getLastLinkInBucket(BucketIndex bucketIndex) { + if (bucketIndex == bucketsSize_ - 1) + return tailLink_; + ValueInternalLink*& previous = buckets_[bucketIndex + 1].previous_; + if (!previous) + previous = &buckets_[bucketIndex]; + return previous; +} + +Value& ValueInternalMap::setNewItem(const char* key, + bool isStatic, + ValueInternalLink* link, + BucketIndex index) { + char* duplicatedKey = makeMemberName(key); + ++itemCount_; + link->keys_[index] = duplicatedKey; + link->items_[index].setItemUsed(); + link->items_[index].setMemberNameIsStatic(isStatic); + return link->items_[index]; // items already default constructed. +} + +Value& +ValueInternalMap::unsafeAdd(const char* key, bool isStatic, HashKey hashedKey) { + JSON_ASSERT_MESSAGE(bucketsSize_ > 0, + "ValueInternalMap::unsafeAdd(): internal logic error."); + BucketIndex bucketIndex = hashedKey % bucketsSize_; + ValueInternalLink*& previousLink = getLastLinkInBucket(bucketIndex); + ValueInternalLink* link = previousLink; + BucketIndex index; + for (index = 0; index < ValueInternalLink::itemPerLink; ++index) { + if (link->items_[index].isItemAvailable()) + break; + } + if (index == ValueInternalLink::itemPerLink) // need to add a new page + { + ValueInternalLink* newLink = mapAllocator()->allocateMapLink(); + index = 0; + link->next_ = newLink; + previousLink = newLink; + link = newLink; + } + return setNewItem(key, isStatic, link, index); +} + +ValueInternalMap::HashKey ValueInternalMap::hash(const char* key) const { + HashKey hash = 0; + while (*key) + hash += *key++ * 37; + return hash; +} + +int ValueInternalMap::compare(const ValueInternalMap& other) const { + int sizeDiff(itemCount_ - other.itemCount_); + if (sizeDiff != 0) + return sizeDiff; + // Strict order guaranty is required. Compare all keys FIRST, then compare + // values. + IteratorState it; + IteratorState itEnd; + makeBeginIterator(it); + makeEndIterator(itEnd); + for (; !equals(it, itEnd); increment(it)) { + if (!other.find(key(it))) + return 1; + } + + // All keys are equals, let's compare values + makeBeginIterator(it); + for (; !equals(it, itEnd); increment(it)) { + const Value* otherValue = other.find(key(it)); + int valueDiff = value(it).compare(*otherValue); + if (valueDiff != 0) + return valueDiff; + } + return 0; +} + +void ValueInternalMap::makeBeginIterator(IteratorState& it) const { + it.map_ = const_cast(this); + it.bucketIndex_ = 0; + it.itemIndex_ = 0; + it.link_ = buckets_; +} + +void ValueInternalMap::makeEndIterator(IteratorState& it) const { + it.map_ = const_cast(this); + it.bucketIndex_ = bucketsSize_; + it.itemIndex_ = 0; + it.link_ = 0; +} + +bool ValueInternalMap::equals(const IteratorState& x, + const IteratorState& other) { + return x.map_ == other.map_ && x.bucketIndex_ == other.bucketIndex_ && + x.link_ == other.link_ && x.itemIndex_ == other.itemIndex_; +} + +void ValueInternalMap::incrementBucket(IteratorState& iterator) { + ++iterator.bucketIndex_; + JSON_ASSERT_MESSAGE( + iterator.bucketIndex_ <= iterator.map_->bucketsSize_, + "ValueInternalMap::increment(): attempting to iterate beyond end."); + if (iterator.bucketIndex_ == iterator.map_->bucketsSize_) + iterator.link_ = 0; + else + iterator.link_ = &(iterator.map_->buckets_[iterator.bucketIndex_]); + iterator.itemIndex_ = 0; +} + +void ValueInternalMap::increment(IteratorState& iterator) { + JSON_ASSERT_MESSAGE(iterator.map_, + "Attempting to iterator using invalid iterator."); + ++iterator.itemIndex_; + if (iterator.itemIndex_ == ValueInternalLink::itemPerLink) { + JSON_ASSERT_MESSAGE( + iterator.link_ != 0, + "ValueInternalMap::increment(): attempting to iterate beyond end."); + iterator.link_ = iterator.link_->next_; + if (iterator.link_ == 0) + incrementBucket(iterator); + } else if (iterator.link_->items_[iterator.itemIndex_].isItemAvailable()) { + incrementBucket(iterator); + } +} + +void ValueInternalMap::decrement(IteratorState& iterator) { + if (iterator.itemIndex_ == 0) { + JSON_ASSERT_MESSAGE(iterator.map_, + "Attempting to iterate using invalid iterator."); + if (iterator.link_ == &iterator.map_->buckets_[iterator.bucketIndex_]) { + JSON_ASSERT_MESSAGE(iterator.bucketIndex_ > 0, + "Attempting to iterate beyond beginning."); + --(iterator.bucketIndex_); + } + iterator.link_ = iterator.link_->previous_; + iterator.itemIndex_ = ValueInternalLink::itemPerLink - 1; + } +} + +const char* ValueInternalMap::key(const IteratorState& iterator) { + JSON_ASSERT_MESSAGE(iterator.link_, + "Attempting to iterate using invalid iterator."); + return iterator.link_->keys_[iterator.itemIndex_]; +} + +const char* ValueInternalMap::key(const IteratorState& iterator, + bool& isStatic) { + JSON_ASSERT_MESSAGE(iterator.link_, + "Attempting to iterate using invalid iterator."); + isStatic = iterator.link_->items_[iterator.itemIndex_].isMemberNameStatic(); + return iterator.link_->keys_[iterator.itemIndex_]; +} + +Value& ValueInternalMap::value(const IteratorState& iterator) { + JSON_ASSERT_MESSAGE(iterator.link_, + "Attempting to iterate using invalid iterator."); + return iterator.link_->items_[iterator.itemIndex_]; +} + +int ValueInternalMap::distance(const IteratorState& x, const IteratorState& y) { + int offset = 0; + IteratorState it = x; + while (!equals(it, y)) + increment(it); + return offset; +} + +} // namespace Json diff --git a/VrUtils/jsoncpp/json_reader.cpp b/VrUtils/jsoncpp/json_reader.cpp new file mode 100644 index 0000000..cec44af --- /dev/null +++ b/VrUtils/jsoncpp/json_reader.cpp @@ -0,0 +1,887 @@ +// Copyright 2007-2011 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +#include "json/assertions.h" +#include "json/reader.h" +#include "json/value.h" +#include "json_tool.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below +#define snprintf _snprintf +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0 +// Disable warning about strdup being deprecated. +#pragma warning(disable : 4996) +#endif + +namespace VA { +namespace Json { + +// Implementation of class Features +// //////////////////////////////// + +Features::Features() + : allowComments_(true), strictRoot_(false), + allowDroppedNullPlaceholders_(false), allowNumericKeys_(false) {} + +Features Features::all() { return Features(); } + +Features Features::strictMode() { + Features features; + features.allowComments_ = false; + features.strictRoot_ = true; + features.allowDroppedNullPlaceholders_ = false; + features.allowNumericKeys_ = false; + return features; +} + +// Implementation of class Reader +// //////////////////////////////// + +static inline bool in(Reader::Char c, + Reader::Char c1, + Reader::Char c2, + Reader::Char c3, + Reader::Char c4) { + return c == c1 || c == c2 || c == c3 || c == c4; +} + +static inline bool in(Reader::Char c, + Reader::Char c1, + Reader::Char c2, + Reader::Char c3, + Reader::Char c4, + Reader::Char c5) { + return c == c1 || c == c2 || c == c3 || c == c4 || c == c5; +} + +static bool containsNewLine(Reader::Location begin, Reader::Location end) { + for (; begin < end; ++begin) + if (*begin == '\n' || *begin == '\r') + return true; + return false; +} + +// Class Reader +// ////////////////////////////////////////////////////////////////// + +Reader::Reader() + : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), + lastValue_(), commentsBefore_(), features_(Features::all()), + collectComments_() {} + +Reader::Reader(const Features& features) + : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), + lastValue_(), commentsBefore_(), features_(features), collectComments_() { +} + +bool +Reader::parse(const std::string& document, Value& root, bool collectComments) { + document_ = document; + const char* begin = document_.c_str(); + const char* end = begin + document_.length(); + return parse(begin, end, root, collectComments); +} + +bool Reader::parse(std::istream& sin, Value& root, bool collectComments) { + // std::istream_iterator begin(sin); + // std::istream_iterator end; + // Those would allow streamed input from a file, if parse() were a + // template function. + + // Since std::string is reference-counted, this at least does not + // create an extra copy. + std::string doc; + std::getline(sin, doc, (char)EOF); + return parse(doc, root, collectComments); +} + +bool Reader::parse(const char* beginDoc, + const char* endDoc, + Value& root, + bool collectComments) { + if (!features_.allowComments_) { + collectComments = false; + } + + begin_ = beginDoc; + end_ = endDoc; + collectComments_ = collectComments; + current_ = begin_; + lastValueEnd_ = 0; + lastValue_ = 0; + commentsBefore_ = ""; + errors_.clear(); + while (!nodes_.empty()) + nodes_.pop(); + nodes_.push(&root); + + bool successful = readValue(); + Token token; + skipCommentTokens(token); + if (collectComments_ && !commentsBefore_.empty()) + root.setComment(commentsBefore_, commentAfter); + if (features_.strictRoot_) { + if (!root.isArray() && !root.isObject()) { + // Set error location to start of doc, ideally should be first token found + // in doc + token.type_ = tokenError; + token.start_ = beginDoc; + token.end_ = endDoc; + addError( + "A valid JSON document must be either an array or an object value.", + token); + return false; + } + } + return successful; +} + +bool Reader::readValue() { + Token token; + skipCommentTokens(token); + bool successful = true; + + if (collectComments_ && !commentsBefore_.empty()) { + // Remove newline characters at the end of the comments + size_t lastNonNewline = commentsBefore_.find_last_not_of("\r\n"); + if (lastNonNewline != std::string::npos) { + commentsBefore_.erase(lastNonNewline + 1); + } else { + commentsBefore_.clear(); + } + + currentValue().setComment(commentsBefore_, commentBefore); + commentsBefore_ = ""; + } + + switch (token.type_) { + case tokenObjectBegin: + successful = readObject(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenArrayBegin: + successful = readArray(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenNumber: + successful = decodeNumber(token); + break; + case tokenString: + successful = decodeString(token); + break; + case tokenTrue: + currentValue() = true; + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + break; + case tokenFalse: + currentValue() = false; + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + break; + case tokenNull: + currentValue() = Value(); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + break; + case tokenArraySeparator: + if (features_.allowDroppedNullPlaceholders_) { + // "Un-read" the current token and mark the current value as a null + // token. + current_--; + currentValue() = Value(); + currentValue().setOffsetStart(current_ - begin_ - 1); + currentValue().setOffsetLimit(current_ - begin_); + break; + } + // Else, fall through... + default: + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return addError("Syntax error: value, object or array expected.", token); + } + + if (collectComments_) { + lastValueEnd_ = current_; + lastValue_ = ¤tValue(); + } + + return successful; +} + +void Reader::skipCommentTokens(Token& token) { + if (features_.allowComments_) { + do { + readToken(token); + } while (token.type_ == tokenComment); + } else { + readToken(token); + } +} + +bool Reader::expectToken(TokenType type, Token& token, const char* message) { + readToken(token); + if (token.type_ != type) + return addError(message, token); + return true; +} + +bool Reader::readToken(Token& token) { + skipSpaces(); + token.start_ = current_; + Char c = getNextChar(); + bool ok = true; + switch (c) { + case '{': + token.type_ = tokenObjectBegin; + break; + case '}': + token.type_ = tokenObjectEnd; + break; + case '[': + token.type_ = tokenArrayBegin; + break; + case ']': + token.type_ = tokenArrayEnd; + break; + case '"': + token.type_ = tokenString; + ok = readString(); + break; + case '/': + token.type_ = tokenComment; + ok = readComment(); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + token.type_ = tokenNumber; + readNumber(); + break; + case 't': + token.type_ = tokenTrue; + ok = match("rue", 3); + break; + case 'f': + token.type_ = tokenFalse; + ok = match("alse", 4); + break; + case 'n': + token.type_ = tokenNull; + ok = match("ull", 3); + break; + case ',': + token.type_ = tokenArraySeparator; + break; + case ':': + token.type_ = tokenMemberSeparator; + break; + case 0: + token.type_ = tokenEndOfStream; + break; + default: + ok = false; + break; + } + if (!ok) + token.type_ = tokenError; + token.end_ = current_; + return true; +} + +void Reader::skipSpaces() { + while (current_ != end_) { + Char c = *current_; + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') + ++current_; + else + break; + } +} + +bool Reader::match(Location pattern, int patternLength) { + if (end_ - current_ < patternLength) + return false; + int index = patternLength; + while (index--) + if (current_[index] != pattern[index]) + return false; + current_ += patternLength; + return true; +} + +bool Reader::readComment() { + Location commentBegin = current_ - 1; + Char c = getNextChar(); + bool successful = false; + if (c == '*') + successful = readCStyleComment(); + else if (c == '/') + successful = readCppStyleComment(); + if (!successful) + return false; + + if (collectComments_) { + CommentPlacement placement = commentBefore; + if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) { + if (c != '*' || !containsNewLine(commentBegin, current_)) + placement = commentAfterOnSameLine; + } + + addComment(commentBegin, current_, placement); + } + return true; +} + +void +Reader::addComment(Location begin, Location end, CommentPlacement placement) { + assert(collectComments_); + if (placement == commentAfterOnSameLine) { + assert(lastValue_ != 0); + lastValue_->setComment(std::string(begin, end), placement); + } else { + commentsBefore_ += std::string(begin, end); + } +} + +bool Reader::readCStyleComment() { + while (current_ != end_) { + Char c = getNextChar(); + if (c == '*' && *current_ == '/') + break; + } + return getNextChar() == '/'; +} + +bool Reader::readCppStyleComment() { + while (current_ != end_) { + Char c = getNextChar(); + if (c == '\r' || c == '\n') + break; + } + return true; +} + +void Reader::readNumber() { + while (current_ != end_) { + if (!(*current_ >= '0' && *current_ <= '9') && + !in(*current_, '.', 'e', 'E', '+', '-')) + break; + ++current_; + } +} + +bool Reader::readString() { + Char c = 0; + while (current_ != end_) { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '"') + break; + } + return c == '"'; +} + +bool Reader::readObject(Token& tokenStart) { + Token tokenName; + std::string name; + currentValue() = Value(objectValue); + currentValue().setOffsetStart(tokenStart.start_ - begin_); + while (readToken(tokenName)) { + bool initialTokenOk = true; + while (tokenName.type_ == tokenComment && initialTokenOk) + initialTokenOk = readToken(tokenName); + if (!initialTokenOk) + break; + if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object + return true; + name = ""; + if (tokenName.type_ == tokenString) { + if (!decodeString(tokenName, name)) + return recoverFromError(tokenObjectEnd); + } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) { + Value numberName; + if (!decodeNumber(tokenName, numberName)) + return recoverFromError(tokenObjectEnd); + name = numberName.asString(); + } else { + break; + } + + Token colon; + if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { + return addErrorAndRecover( + "Missing ':' after object member name", colon, tokenObjectEnd); + } + Value& value = currentValue()[name]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenObjectEnd); + + Token comma; + if (!readToken(comma) || + (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && + comma.type_ != tokenComment)) { + return addErrorAndRecover( + "Missing ',' or '}' in object declaration", comma, tokenObjectEnd); + } + bool finalizeTokenOk = true; + while (comma.type_ == tokenComment && finalizeTokenOk) + finalizeTokenOk = readToken(comma); + if (comma.type_ == tokenObjectEnd) + return true; + } + return addErrorAndRecover( + "Missing '}' or object member name", tokenName, tokenObjectEnd); +} + +bool Reader::readArray(Token& tokenStart) { + currentValue() = Value(arrayValue); + currentValue().setOffsetStart(tokenStart.start_ - begin_); + skipSpaces(); + if (*current_ == ']') // empty array + { + Token endArray; + readToken(endArray); + return true; + } + int index = 0; + for (;;) { + Value& value = currentValue()[index++]; + nodes_.push(&value); + bool ok = readValue(); + nodes_.pop(); + if (!ok) // error already set + return recoverFromError(tokenArrayEnd); + + Token token; + // Accept Comment after last item in the array. + ok = readToken(token); + while (token.type_ == tokenComment && ok) { + ok = readToken(token); + } + bool badTokenType = + (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd); + if (!ok || badTokenType) { + return addErrorAndRecover( + "Missing ',' or ']' in array declaration", token, tokenArrayEnd); + } + if (token.type_ == tokenArrayEnd) + break; + } + return true; +} + +bool Reader::decodeNumber(Token& token) { + Value decoded; + if (!decodeNumber(token, decoded)) + return false; + currentValue() = decoded; + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool Reader::decodeNumber(Token& token, Value& decoded) { + bool isDouble = false; + for (Location inspect = token.start_; inspect != token.end_; ++inspect) { + isDouble = isDouble || in(*inspect, '.', 'e', 'E', '+') || + (*inspect == '-' && inspect != token.start_); + } + if (isDouble) + return decodeDouble(token, decoded); + // Attempts to parse the number as an integer. If the number is + // larger than the maximum supported value of an integer then + // we decode the number as a double. + Location current = token.start_; + bool isNegative = *current == '-'; + if (isNegative) + ++current; + Value::LargestUInt maxIntegerValue = + isNegative ? Value::LargestUInt(-Value::minLargestInt) + : Value::maxLargestUInt; + Value::LargestUInt threshold = maxIntegerValue / 10; + Value::LargestUInt value = 0; + while (current < token.end_) { + Char c = *current++; + if (c < '0' || c > '9') + return addError("'" + std::string(token.start_, token.end_) + + "' is not a number.", + token); + Value::UInt digit(c - '0'); + if (value >= threshold) { + // We've hit or exceeded the max value divided by 10 (rounded down). If + // a) we've only just touched the limit, b) this is the last digit, and + // c) it's small enough to fit in that rounding delta, we're okay. + // Otherwise treat this number as a double to avoid overflow. + if (value > threshold || current != token.end_ || + digit > maxIntegerValue % 10) { + return decodeDouble(token, decoded); + } + } + value = value * 10 + digit; + } + if (isNegative) + decoded = -Value::LargestInt(value); + else if (value <= Value::LargestUInt(Value::maxInt)) + decoded = Value::LargestInt(value); + else + decoded = value; + return true; +} + +bool Reader::decodeDouble(Token& token) { + Value decoded; + if (!decodeDouble(token, decoded)) + return false; + currentValue() = decoded; + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool Reader::decodeDouble(Token& token, Value& decoded) { + double value = 0; + const int bufferSize = 32; + int count; + int length = int(token.end_ - token.start_); + + // Sanity check to avoid buffer overflow exploits. + if (length < 0) { + return addError("Unable to parse token length", token); + } + + // Avoid using a string constant for the format control string given to + // sscanf, as this can cause hard to debug crashes on OS X. See here for more + // info: + // + // http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html + char format[] = "%lf"; + + if (length <= bufferSize) { + Char buffer[bufferSize + 1]; + memcpy(buffer, token.start_, length); + buffer[length] = 0; + count = sscanf(buffer, format, &value); + } else { + std::string buffer(token.start_, token.end_); + count = sscanf(buffer.c_str(), format, &value); + } + + if (count != 1) + return addError("'" + std::string(token.start_, token.end_) + + "' is not a number.", + token); + decoded = value; + return true; +} + +bool Reader::decodeString(Token& token) { + std::string decoded; + if (!decodeString(token, decoded)) + return false; + currentValue() = decoded; + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool Reader::decodeString(Token& token, std::string& decoded) { + decoded.reserve(token.end_ - token.start_ - 2); + Location current = token.start_ + 1; // skip '"' + Location end = token.end_ - 1; // do not include '"' + while (current != end) { + Char c = *current++; + if (c == '"') + break; + else if (c == '\\') { + if (current == end) + return addError("Empty escape sequence in string", token, current); + Char escape = *current++; + switch (escape) { + case '"': + decoded += '"'; + break; + case '/': + decoded += '/'; + break; + case '\\': + decoded += '\\'; + break; + case 'b': + decoded += '\b'; + break; + case 'f': + decoded += '\f'; + break; + case 'n': + decoded += '\n'; + break; + case 'r': + decoded += '\r'; + break; + case 't': + decoded += '\t'; + break; + case 'u': { + unsigned int unicode; + if (!decodeUnicodeCodePoint(token, current, end, unicode)) + return false; + decoded += codePointToUTF8(unicode); + } break; + default: + return addError("Bad escape sequence in string", token, current); + } + } else { + decoded += c; + } + } + return true; +} + +bool Reader::decodeUnicodeCodePoint(Token& token, + Location& current, + Location end, + unsigned int& unicode) { + + if (!decodeUnicodeEscapeSequence(token, current, end, unicode)) + return false; + if (unicode >= 0xD800 && unicode <= 0xDBFF) { + // surrogate pairs + if (end - current < 6) + return addError( + "additional six characters expected to parse unicode surrogate pair.", + token, + current); + unsigned int surrogatePair; + if (*(current++) == '\\' && *(current++) == 'u') { + if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) { + unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); + } else + return false; + } else + return addError("expecting another \\u token to begin the second half of " + "a unicode surrogate pair", + token, + current); + } + return true; +} + +bool Reader::decodeUnicodeEscapeSequence(Token& token, + Location& current, + Location end, + unsigned int& unicode) { + if (end - current < 4) + return addError( + "Bad unicode escape sequence in string: four digits expected.", + token, + current); + unicode = 0; + for (int index = 0; index < 4; ++index) { + Char c = *current++; + unicode *= 16; + if (c >= '0' && c <= '9') + unicode += c - '0'; + else if (c >= 'a' && c <= 'f') + unicode += c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + unicode += c - 'A' + 10; + else + return addError( + "Bad unicode escape sequence in string: hexadecimal digit expected.", + token, + current); + } + return true; +} + +bool +Reader::addError(const std::string& message, Token& token, Location extra) { + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = extra; + errors_.push_back(info); + return false; +} + +bool Reader::recoverFromError(TokenType skipUntilToken) { + int errorCount = int(errors_.size()); + Token skip; + for (;;) { + if (!readToken(skip)) + errors_.resize(errorCount); // discard errors caused by recovery + if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream) + break; + } + errors_.resize(errorCount); + return false; +} + +bool Reader::addErrorAndRecover(const std::string& message, + Token& token, + TokenType skipUntilToken) { + addError(message, token); + return recoverFromError(skipUntilToken); +} + +Value& Reader::currentValue() { return *(nodes_.top()); } + +Reader::Char Reader::getNextChar() { + if (current_ == end_) + return 0; + return *current_++; +} + +void Reader::getLocationLineAndColumn(Location location, + int& line, + int& column) const { + Location current = begin_; + Location lastLineStart = current; + line = 0; + while (current < location && current != end_) { + Char c = *current++; + if (c == '\r') { + if (*current == '\n') + ++current; + lastLineStart = current; + ++line; + } else if (c == '\n') { + lastLineStart = current; + ++line; + } + } + // column & line start at 1 + column = int(location - lastLineStart) + 1; + ++line; +} + +std::string Reader::getLocationLineAndColumn(Location location) const { + int line, column; + getLocationLineAndColumn(location, line, column); + char buffer[18 + 16 + 16 + 1]; +#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) +#if defined(WINCE) + _snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); +#else + sprintf_s(buffer, sizeof(buffer), "Line %d, Column %d", line, column); +#endif +#else + snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); +#endif + return buffer; +} + +// Deprecated. Preserved for backward compatibility +std::string Reader::getFormatedErrorMessages() const { + return getFormattedErrorMessages(); +} + +std::string Reader::getFormattedErrorMessages() const { + std::string formattedMessage; + for (Errors::const_iterator itError = errors_.begin(); + itError != errors_.end(); + ++itError) { + const ErrorInfo& error = *itError; + formattedMessage += + "* " + getLocationLineAndColumn(error.token_.start_) + "\n"; + formattedMessage += " " + error.message_ + "\n"; + if (error.extra_) + formattedMessage += + "See " + getLocationLineAndColumn(error.extra_) + " for detail.\n"; + } + return formattedMessage; +} + +std::vector Reader::getStructuredErrors() const { + std::vector allErrors; + for (Errors::const_iterator itError = errors_.begin(); + itError != errors_.end(); + ++itError) { + const ErrorInfo& error = *itError; + Reader::StructuredError structured; + structured.offset_start = error.token_.start_ - begin_; + structured.offset_limit = error.token_.end_ - begin_; + structured.message = error.message_; + allErrors.push_back(structured); + } + return allErrors; +} + +bool Reader::pushError(const Value& value, const std::string& message) { + if(value.getOffsetStart() > (size_t)end_ - (size_t)begin_ + || value.getOffsetLimit() > (size_t)end_ - (size_t)begin_) + return false; + Token token; + token.type_ = tokenError; + token.start_ = begin_ + value.getOffsetStart(); + token.end_ = end_ + value.getOffsetLimit(); + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = 0; + errors_.push_back(info); + return true; +} + +bool Reader::pushError(const Value& value, const std::string& message, const Value& extra) { + if(value.getOffsetStart() > (size_t)end_ - (size_t)begin_ + || value.getOffsetLimit() > (size_t)end_ - (size_t)begin_ + || extra.getOffsetLimit() > (size_t)end_ - (size_t)begin_) + return false; + Token token; + token.type_ = tokenError; + token.start_ = begin_ + value.getOffsetStart(); + token.end_ = begin_ + value.getOffsetLimit(); + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = begin_ + extra.getOffsetStart(); + errors_.push_back(info); + return true; +} + +bool Reader::good() const { + return !errors_.size(); +} + +std::istream& operator>>(std::istream& sin, Value& root) { + Json::Reader reader; + bool ok = reader.parse(sin, root, true); + if (!ok) { + fprintf(stderr, + "Error from reader: %s", + reader.getFormattedErrorMessages().c_str()); + + JSON_FAIL_MESSAGE("reader error"); + } + return sin; +} + +} // namespace Json +} // namespace VA diff --git a/VrUtils/jsoncpp/json_tool.h b/VrUtils/jsoncpp/json_tool.h new file mode 100644 index 0000000..043c35e --- /dev/null +++ b/VrUtils/jsoncpp/json_tool.h @@ -0,0 +1,89 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED +#define LIB_JSONCPP_JSON_TOOL_H_INCLUDED + +/* This header provides common string manipulation support, such as UTF-8, + * portable conversion from/to string... + * + * It is an internal header that must not be exposed. + */ + +namespace VA { +namespace Json { + +/// Converts a unicode code-point to UTF-8. +static inline std::string codePointToUTF8(unsigned int cp) { + std::string result; + + // based on description from http://en.wikipedia.org/wiki/UTF-8 + + if (cp <= 0x7f) { + result.resize(1); + result[0] = static_cast(cp); + } else if (cp <= 0x7FF) { + result.resize(2); + result[1] = static_cast(0x80 | (0x3f & cp)); + result[0] = static_cast(0xC0 | (0x1f & (cp >> 6))); + } else if (cp <= 0xFFFF) { + result.resize(3); + result[2] = static_cast(0x80 | (0x3f & cp)); + result[1] = 0x80 | static_cast((0x3f & (cp >> 6))); + result[0] = 0xE0 | static_cast((0xf & (cp >> 12))); + } else if (cp <= 0x10FFFF) { + result.resize(4); + result[3] = static_cast(0x80 | (0x3f & cp)); + result[2] = static_cast(0x80 | (0x3f & (cp >> 6))); + result[1] = static_cast(0x80 | (0x3f & (cp >> 12))); + result[0] = static_cast(0xF0 | (0x7 & (cp >> 18))); + } + + return result; +} + +/// Returns true if ch is a control character (in range [0,32[). +static inline bool isControlCharacter(char ch) { return ch > 0 && ch <= 0x1F; } + +enum { + /// Constant that specify the size of the buffer that must be passed to + /// uintToString. + uintToStringBufferSize = 3 * sizeof(LargestUInt) + 1 +}; + +// Defines a char buffer for use with uintToString(). +typedef char UIntToStringBuffer[uintToStringBufferSize]; + +/** Converts an unsigned integer to string. + * @param value Unsigned interger to convert to string + * @param current Input/Output string buffer. + * Must have at least uintToStringBufferSize chars free. + */ +static inline void uintToString(LargestUInt value, char*& current) { + *--current = 0; + do { + *--current = char(value % 10) + '0'; + value /= 10; + } while (value != 0); +} + +/** Change ',' to '.' everywhere in buffer. + * + * We had a sophisticated way, but it did not work in WinCE. + * @see https://github.com/open-source-parsers/jsoncpp/pull/9 + */ +static inline void fixNumericLocale(char* begin, char* end) { + while (begin < end) { + if (*begin == ',') { + *begin = '.'; + } + ++begin; + } +} + +} // namespace Json { +} // namespace VA + +#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED diff --git a/VrUtils/jsoncpp/json_value.cpp b/VrUtils/jsoncpp/json_value.cpp new file mode 100644 index 0000000..eb9873b --- /dev/null +++ b/VrUtils/jsoncpp/json_value.cpp @@ -0,0 +1,1480 @@ +// Copyright 2011 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +#include "json/assertions.h" +#include "json/value.h" +#include "json/writer.h" +#ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR +#include "json_batchallocator.h" +#endif // #ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include +#include +#include +#ifdef JSON_USE_CPPTL +#include +#endif +#include // size_t + +#define JSON_ASSERT_UNREACHABLE assert(false) +namespace VA { +namespace Json { + +// This is a walkaround to avoid the static initialization of Value::null. +// kNull must be word-aligned to avoid crashing on ARM. We use an alignment of +// 8 (instead of 4) as a bit of future-proofing. +#if defined(__ARMEL__) +#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment))) +#else +#define ALIGNAS(byte_alignment) +#endif +static const unsigned char ALIGNAS(8) kNull[sizeof(Value)] = { 0 }; +const unsigned char& kNullRef = kNull[0]; +const Value& Value::null = reinterpret_cast(kNullRef); + +const Int Value::minInt = Int(~(UInt(-1) / 2)); +const Int Value::maxInt = Int(UInt(-1) / 2); +const UInt Value::maxUInt = UInt(-1); +#if defined(JSON_HAS_INT64) +const Int64 Value::minInt64 = Int64(~(UInt64(-1) / 2)); +const Int64 Value::maxInt64 = Int64(UInt64(-1) / 2); +const UInt64 Value::maxUInt64 = UInt64(-1); +// The constant is hard-coded because some compiler have trouble +// converting Value::maxUInt64 to a double correctly (AIX/xlC). +// Assumes that UInt64 is a 64 bits integer. +static const double maxUInt64AsDouble = 18446744073709551615.0; +#endif // defined(JSON_HAS_INT64) +const LargestInt Value::minLargestInt = LargestInt(~(LargestUInt(-1) / 2)); +const LargestInt Value::maxLargestInt = LargestInt(LargestUInt(-1) / 2); +const LargestUInt Value::maxLargestUInt = LargestUInt(-1); + +/// Unknown size marker +static const unsigned int unknown = (unsigned)-1; + +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) +template +static inline bool InRange(double d, T min, U max) { + return d >= min && d <= max; +} +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) +static inline double integerToDouble(Json::UInt64 value) { + return static_cast(Int64(value / 2)) * 2.0 + Int64(value & 1); +} + +template static inline double integerToDouble(T value) { + return static_cast(value); +} + +template +static inline bool InRange(double d, T min, U max) { + return d >= integerToDouble(min) && d <= integerToDouble(max); +} +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + +/** Duplicates the specified string value. + * @param value Pointer to the string to duplicate. Must be zero-terminated if + * length is "unknown". + * @param length Length of the value. if equals to unknown, then it will be + * computed using strlen(value). + * @return Pointer on the duplicate instance of string. + */ +static inline char* duplicateStringValue(const char* value, + unsigned int length = unknown) { + if (length == unknown) + length = (unsigned int)strlen(value); + + // Avoid an integer overflow in the call to malloc below by limiting length + // to a sane value. + if (length >= (unsigned)Value::maxInt) + length = Value::maxInt - 1; + + char* newString = static_cast(malloc(length + 1)); + JSON_ASSERT_MESSAGE(newString != 0, + "in Json::Value::duplicateStringValue(): " + "Failed to allocate string value buffer"); + memcpy(newString, value, length); + newString[length] = 0; + return newString; +} + +/** Free the string duplicated by duplicateStringValue(). + */ +static inline void releaseStringValue(char* value) { free(value); } + +} // namespace Json + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ValueInternals... +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +#if !defined(JSON_IS_AMALGAMATION) +#ifdef JSON_VALUE_USE_INTERNAL_MAP +#include "json_internalarray.inl" +#include "json_internalmap.inl" +#endif // JSON_VALUE_USE_INTERNAL_MAP + +#include "json_valueiterator.inl" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::CommentInfo +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +Value::CommentInfo::CommentInfo() : comment_(0) {} + +Value::CommentInfo::~CommentInfo() { + if (comment_) + releaseStringValue(comment_); +} + +void Value::CommentInfo::setComment(const char* text) { + if (comment_) + releaseStringValue(comment_); + JSON_ASSERT(text != 0); + JSON_ASSERT_MESSAGE( + text[0] == '\0' || text[0] == '/', + "in Json::Value::setComment(): Comments must start with /"); + // It seems that /**/ style comments are acceptable as well. + comment_ = duplicateStringValue(text); +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::CZString +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +#ifndef JSON_VALUE_USE_INTERNAL_MAP + +// Notes: index_ indicates if the string was allocated when +// a string is stored. + +Value::CZString::CZString(ArrayIndex index) : cstr_(0), index_(index) {} + +Value::CZString::CZString(const char* cstr, DuplicationPolicy allocate) + : cstr_(allocate == duplicate ? duplicateStringValue(cstr) : cstr), + index_(allocate) {} + +Value::CZString::CZString(const CZString& other) + : cstr_(other.index_ != noDuplication && other.cstr_ != 0 + ? duplicateStringValue(other.cstr_) + : other.cstr_), + index_(other.cstr_ + ? (other.index_ == noDuplication ? noDuplication : duplicate) + : other.index_) {} + +Value::CZString::~CZString() { + if (cstr_ && index_ == duplicate) + releaseStringValue(const_cast(cstr_)); +} + +void Value::CZString::swap(CZString& other) { + std::swap(cstr_, other.cstr_); + std::swap(index_, other.index_); +} + +Value::CZString& Value::CZString::operator=(CZString other) { + swap(other); + return *this; +} + +bool Value::CZString::operator<(const CZString& other) const { + if (cstr_) + return strcmp(cstr_, other.cstr_) < 0; + return index_ < other.index_; +} + +bool Value::CZString::operator==(const CZString& other) const { + if (cstr_) + return strcmp(cstr_, other.cstr_) == 0; + return index_ == other.index_; +} + +ArrayIndex Value::CZString::index() const { return index_; } + +const char* Value::CZString::c_str() const { return cstr_; } + +bool Value::CZString::isStaticString() const { return index_ == noDuplication; } + +#endif // ifndef JSON_VALUE_USE_INTERNAL_MAP + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::Value +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +/*! \internal Default constructor initialization must be equivalent to: + * memset( this, 0, sizeof(Value) ) + * This optimization is used in ValueInternalMap fast allocator. + */ +Value::Value(ValueType type) { + initBasic(type); + switch (type) { + case nullValue: + break; + case intValue: + case uintValue: + value_.int_ = 0; + break; + case realValue: + value_.real_ = 0.0; + break; + case stringValue: + value_.string_ = 0; + break; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues(); + break; +#else + case arrayValue: + value_.array_ = arrayAllocator()->newArray(); + break; + case objectValue: + value_.map_ = mapAllocator()->newMap(); + break; +#endif + case booleanValue: + value_.bool_ = false; + break; + default: + JSON_ASSERT_UNREACHABLE; + } +} + +Value::Value(Int value) { + initBasic(intValue); + value_.int_ = value; +} + +Value::Value(UInt value) { + initBasic(uintValue); + value_.uint_ = value; +} +#if defined(JSON_HAS_INT64) +Value::Value(Int64 value) { + initBasic(intValue); + value_.int_ = value; +} +Value::Value(UInt64 value) { + initBasic(uintValue); + value_.uint_ = value; +} +#endif // defined(JSON_HAS_INT64) + +Value::Value(double value) { + initBasic(realValue); + value_.real_ = value; +} + +Value::Value(const char* value) { + initBasic(stringValue, true); + value_.string_ = duplicateStringValue(value); +} + +Value::Value(const char* beginValue, const char* endValue) { + initBasic(stringValue, true); + value_.string_ = + duplicateStringValue(beginValue, (unsigned int)(endValue - beginValue)); +} + +Value::Value(const std::string& value) { + initBasic(stringValue, true); + value_.string_ = + duplicateStringValue(value.c_str(), (unsigned int)value.length()); +} + +Value::Value(const StaticString& value) { + initBasic(stringValue); + value_.string_ = const_cast(value.c_str()); +} + +#ifdef JSON_USE_CPPTL +Value::Value(const CppTL::ConstString& value) { + initBasic(stringValue, true); + value_.string_ = duplicateStringValue(value, value.length()); +} +#endif + +Value::Value(bool value) { + initBasic(booleanValue); + value_.bool_ = value; +} + +Value::Value(const Value& other) + : type_(other.type_), allocated_(false) +#ifdef JSON_VALUE_USE_INTERNAL_MAP + , + itemIsUsed_(0) +#endif + , + comments_(0), start_(other.start_), limit_(other.limit_) { + switch (type_) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + value_ = other.value_; + break; + case stringValue: + if (other.value_.string_) { + value_.string_ = duplicateStringValue(other.value_.string_); + allocated_ = true; + } else { + value_.string_ = 0; + allocated_ = false; + } + break; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues(*other.value_.map_); + break; +#else + case arrayValue: + value_.array_ = arrayAllocator()->newArrayCopy(*other.value_.array_); + break; + case objectValue: + value_.map_ = mapAllocator()->newMapCopy(*other.value_.map_); + break; +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + if (other.comments_) { + comments_ = new CommentInfo[numberOfCommentPlacement]; + for (int comment = 0; comment < numberOfCommentPlacement; ++comment) { + const CommentInfo& otherComment = other.comments_[comment]; + if (otherComment.comment_) + comments_[comment].setComment(otherComment.comment_); + } + } +} + +Value::~Value() { + switch (type_) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + break; + case stringValue: + if (allocated_) + releaseStringValue(value_.string_); + break; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + delete value_.map_; + break; +#else + case arrayValue: + arrayAllocator()->destructArray(value_.array_); + break; + case objectValue: + mapAllocator()->destructMap(value_.map_); + break; +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + + if (comments_) + delete[] comments_; +} + +Value& Value::operator=(Value other) { + swap(other); + return *this; +} + +void Value::swap(Value& other) { + ValueType temp = type_; + type_ = other.type_; + other.type_ = temp; + std::swap(value_, other.value_); + int temp2 = allocated_; + allocated_ = other.allocated_; + other.allocated_ = temp2; + std::swap(start_, other.start_); + std::swap(limit_, other.limit_); +} + +ValueType Value::type() const { return type_; } + +int Value::compare(const Value& other) const { + if (*this < other) + return -1; + if (*this > other) + return 1; + return 0; +} + +bool Value::operator<(const Value& other) const { + int typeDelta = type_ - other.type_; + if (typeDelta) + return typeDelta < 0 ? true : false; + switch (type_) { + case nullValue: + return false; + case intValue: + return value_.int_ < other.value_.int_; + case uintValue: + return value_.uint_ < other.value_.uint_; + case realValue: + return value_.real_ < other.value_.real_; + case booleanValue: + return value_.bool_ < other.value_.bool_; + case stringValue: + return (value_.string_ == 0 && other.value_.string_) || + (other.value_.string_ && value_.string_ && + strcmp(value_.string_, other.value_.string_) < 0); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: { + int delta = int(value_.map_->size() - other.value_.map_->size()); + if (delta) + return delta < 0; + return (*value_.map_) < (*other.value_.map_); + } +#else + case arrayValue: + return value_.array_->compare(*(other.value_.array_)) < 0; + case objectValue: + return value_.map_->compare(*(other.value_.map_)) < 0; +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable +} + +bool Value::operator<=(const Value& other) const { return !(other < *this); } + +bool Value::operator>=(const Value& other) const { return !(*this < other); } + +bool Value::operator>(const Value& other) const { return other < *this; } + +bool Value::operator==(const Value& other) const { + // if ( type_ != other.type_ ) + // GCC 2.95.3 says: + // attempt to take address of bit-field structure member `Json::Value::type_' + // Beats me, but a temp solves the problem. + int temp = other.type_; + if (type_ != temp) + return false; + switch (type_) { + case nullValue: + return true; + case intValue: + return value_.int_ == other.value_.int_; + case uintValue: + return value_.uint_ == other.value_.uint_; + case realValue: + return value_.real_ == other.value_.real_; + case booleanValue: + return value_.bool_ == other.value_.bool_; + case stringValue: + return (value_.string_ == other.value_.string_) || + (other.value_.string_ && value_.string_ && + strcmp(value_.string_, other.value_.string_) == 0); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + return value_.map_->size() == other.value_.map_->size() && + (*value_.map_) == (*other.value_.map_); +#else + case arrayValue: + return value_.array_->compare(*(other.value_.array_)) == 0; + case objectValue: + return value_.map_->compare(*(other.value_.map_)) == 0; +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable +} + +bool Value::operator!=(const Value& other) const { return !(*this == other); } + +const char* Value::asCString() const { + JSON_ASSERT_MESSAGE(type_ == stringValue, + "in Json::Value::asCString(): requires stringValue"); + return value_.string_; +} + +std::string Value::asString() const { + switch (type_) { + case nullValue: + return ""; + case stringValue: + return value_.string_ ? value_.string_ : ""; + case booleanValue: + return value_.bool_ ? "true" : "false"; + case intValue: + return valueToString(value_.int_); + case uintValue: + return valueToString(value_.uint_); + case realValue: + return valueToString(value_.real_); + default: + JSON_FAIL_MESSAGE("Type is not convertible to string"); + } +} + +#ifdef JSON_USE_CPPTL +CppTL::ConstString Value::asConstString() const { + return CppTL::ConstString(asString().c_str()); +} +#endif + +Value::Int Value::asInt() const { + switch (type_) { + case intValue: + JSON_ASSERT_MESSAGE(isInt(), "LargestInt out of Int range"); + return Int(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(isInt(), "LargestUInt out of Int range"); + return Int(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt, maxInt), + "double out of Int range"); + return Int(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to Int."); +} + +Value::UInt Value::asUInt() const { + switch (type_) { + case intValue: + JSON_ASSERT_MESSAGE(isUInt(), "LargestInt out of UInt range"); + return UInt(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(isUInt(), "LargestUInt out of UInt range"); + return UInt(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt), + "double out of UInt range"); + return UInt(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to UInt."); +} + +#if defined(JSON_HAS_INT64) + +Value::Int64 Value::asInt64() const { + switch (type_) { + case intValue: + return Int64(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(isInt64(), "LargestUInt out of Int64 range"); + return Int64(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt64, maxInt64), + "double out of Int64 range"); + return Int64(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to Int64."); +} + +Value::UInt64 Value::asUInt64() const { + switch (type_) { + case intValue: + JSON_ASSERT_MESSAGE(isUInt64(), "LargestInt out of UInt64 range"); + return UInt64(value_.int_); + case uintValue: + return UInt64(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt64), + "double out of UInt64 range"); + return UInt64(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to UInt64."); +} +#endif // if defined(JSON_HAS_INT64) + +LargestInt Value::asLargestInt() const { +#if defined(JSON_NO_INT64) + return asInt(); +#else + return asInt64(); +#endif +} + +LargestUInt Value::asLargestUInt() const { +#if defined(JSON_NO_INT64) + return asUInt(); +#else + return asUInt64(); +#endif +} + +double Value::asDouble() const { + switch (type_) { + case intValue: + return static_cast(value_.int_); + case uintValue: +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return static_cast(value_.uint_); +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return integerToDouble(value_.uint_); +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + case realValue: + return value_.real_; + case nullValue: + return 0.0; + case booleanValue: + return value_.bool_ ? 1.0 : 0.0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to double."); +} + +float Value::asFloat() const { + switch (type_) { + case intValue: + return static_cast(value_.int_); + case uintValue: +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return static_cast(value_.uint_); +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return integerToDouble(value_.uint_); +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + case realValue: + return static_cast(value_.real_); + case nullValue: + return 0.0; + case booleanValue: + return value_.bool_ ? 1.0f : 0.0f; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to float."); +} + +bool Value::asBool() const { + switch (type_) { + case booleanValue: + return value_.bool_; + case nullValue: + return false; + case intValue: + return value_.int_ ? true : false; + case uintValue: + return value_.uint_ ? true : false; + case realValue: + return value_.real_ ? true : false; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to bool."); +} + +bool Value::isConvertibleTo(ValueType other) const { + switch (other) { + case nullValue: + return (isNumeric() && asDouble() == 0.0) || + (type_ == booleanValue && value_.bool_ == false) || + (type_ == stringValue && asString() == "") || + (type_ == arrayValue && value_.map_->size() == 0) || + (type_ == objectValue && value_.map_->size() == 0) || + type_ == nullValue; + case intValue: + return isInt() || + (type_ == realValue && InRange(value_.real_, minInt, maxInt)) || + type_ == booleanValue || type_ == nullValue; + case uintValue: + return isUInt() || + (type_ == realValue && InRange(value_.real_, 0, maxUInt)) || + type_ == booleanValue || type_ == nullValue; + case realValue: + return isNumeric() || type_ == booleanValue || type_ == nullValue; + case booleanValue: + return isNumeric() || type_ == booleanValue || type_ == nullValue; + case stringValue: + return isNumeric() || type_ == booleanValue || type_ == stringValue || + type_ == nullValue; + case arrayValue: + return type_ == arrayValue || type_ == nullValue; + case objectValue: + return type_ == objectValue || type_ == nullValue; + } + JSON_ASSERT_UNREACHABLE; + return false; +} + +/// Number of values in array or object +ArrayIndex Value::size() const { + switch (type_) { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + case stringValue: + return 0; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: // size of the array is highest index + 1 + if (!value_.map_->empty()) { + ObjectValues::const_iterator itLast = value_.map_->end(); + --itLast; + return (*itLast).first.index() + 1; + } + return 0; + case objectValue: + return ArrayIndex(value_.map_->size()); +#else + case arrayValue: + return Int(value_.array_->size()); + case objectValue: + return Int(value_.map_->size()); +#endif + } + JSON_ASSERT_UNREACHABLE; + return 0; // unreachable; +} + +bool Value::empty() const { + if (isNull() || isArray() || isObject()) + return size() == 0u; + else + return false; +} + +bool Value::operator!() const { return isNull(); } + +void Value::clear() { + JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue || + type_ == objectValue, + "in Json::Value::clear(): requires complex value"); + start_ = 0; + limit_ = 0; + switch (type_) { +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + value_.map_->clear(); + break; +#else + case arrayValue: + value_.array_->clear(); + break; + case objectValue: + value_.map_->clear(); + break; +#endif + default: + break; + } +} + +void Value::resize(ArrayIndex newSize) { + JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue, + "in Json::Value::resize(): requires arrayValue"); + if (type_ == nullValue) + *this = Value(arrayValue); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + ArrayIndex oldSize = size(); + if (newSize == 0) + clear(); + else if (newSize > oldSize) + (*this)[newSize - 1]; + else { + for (ArrayIndex index = newSize; index < oldSize; ++index) { + value_.map_->erase(index); + } + assert(size() == newSize); + } +#else + value_.array_->resize(newSize); +#endif +} + +Value& Value::operator[](ArrayIndex index) { + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == arrayValue, + "in Json::Value::operator[](ArrayIndex): requires arrayValue"); + if (type_ == nullValue) + *this = Value(arrayValue); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString key(index); + ObjectValues::iterator it = value_.map_->lower_bound(key); + if (it != value_.map_->end() && (*it).first == key) + return (*it).second; + + ObjectValues::value_type defaultValue(key, null); + it = value_.map_->insert(it, defaultValue); + return (*it).second; +#else + return value_.array_->resolveReference(index); +#endif +} + +Value& Value::operator[](int index) { + JSON_ASSERT_MESSAGE( + index >= 0, + "in Json::Value::operator[](int index): index cannot be negative"); + return (*this)[ArrayIndex(index)]; +} + +const Value& Value::operator[](ArrayIndex index) const { + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == arrayValue, + "in Json::Value::operator[](ArrayIndex)const: requires arrayValue"); + if (type_ == nullValue) + return null; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString key(index); + ObjectValues::const_iterator it = value_.map_->find(key); + if (it == value_.map_->end()) + return null; + return (*it).second; +#else + Value* value = value_.array_->find(index); + return value ? *value : null; +#endif +} + +const Value& Value::operator[](int index) const { + JSON_ASSERT_MESSAGE( + index >= 0, + "in Json::Value::operator[](int index) const: index cannot be negative"); + return (*this)[ArrayIndex(index)]; +} + +Value& Value::operator[](const char* key) { + return resolveReference(key, false); +} + +void Value::initBasic(ValueType type, bool allocated) { + type_ = type; + allocated_ = allocated; +#ifdef JSON_VALUE_USE_INTERNAL_MAP + itemIsUsed_ = 0; +#endif + comments_ = 0; + start_ = 0; + limit_ = 0; +} + +Value& Value::resolveReference(const char* key, bool isStatic) { + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == objectValue, + "in Json::Value::resolveReference(): requires objectValue"); + if (type_ == nullValue) + *this = Value(objectValue); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString actualKey( + key, isStatic ? CZString::noDuplication : CZString::duplicateOnCopy); + ObjectValues::iterator it = value_.map_->lower_bound(actualKey); + if (it != value_.map_->end() && (*it).first == actualKey) + return (*it).second; + + ObjectValues::value_type defaultValue(actualKey, null); + it = value_.map_->insert(it, defaultValue); + Value& value = (*it).second; + return value; +#else + return value_.map_->resolveReference(key, isStatic); +#endif +} + +Value Value::get(ArrayIndex index, const Value& defaultValue) const { + const Value* value = &((*this)[index]); + return value == &null ? defaultValue : *value; +} + +bool Value::isValidIndex(ArrayIndex index) const { return index < size(); } + +const Value& Value::operator[](const char* key) const { + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == objectValue, + "in Json::Value::operator[](char const*)const: requires objectValue"); + if (type_ == nullValue) + return null; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString actualKey(key, CZString::noDuplication); + ObjectValues::const_iterator it = value_.map_->find(actualKey); + if (it == value_.map_->end()) + return null; + return (*it).second; +#else + const Value* value = value_.map_->find(key); + return value ? *value : null; +#endif +} + +Value& Value::operator[](const std::string& key) { + return (*this)[key.c_str()]; +} + +const Value& Value::operator[](const std::string& key) const { + return (*this)[key.c_str()]; +} + +Value& Value::operator[](const StaticString& key) { + return resolveReference(key, true); +} + +#ifdef JSON_USE_CPPTL +Value& Value::operator[](const CppTL::ConstString& key) { + return (*this)[key.c_str()]; +} + +const Value& Value::operator[](const CppTL::ConstString& key) const { + return (*this)[key.c_str()]; +} +#endif + +Value& Value::append(const Value& value) { return (*this)[size()] = value; } + +Value Value::get(const char* key, const Value& defaultValue) const { + const Value* value = &((*this)[key]); + return value == &null ? defaultValue : *value; +} + +Value Value::get(const std::string& key, const Value& defaultValue) const { + return get(key.c_str(), defaultValue); +} + +Value Value::removeMember(const char* key) { + JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue, + "in Json::Value::removeMember(): requires objectValue"); + if (type_ == nullValue) + return null; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString actualKey(key, CZString::noDuplication); + ObjectValues::iterator it = value_.map_->find(actualKey); + if (it == value_.map_->end()) + return null; + Value old(it->second); + value_.map_->erase(it); + return old; +#else + Value* value = value_.map_->find(key); + if (value) { + Value old(*value); + value_.map_.remove(key); + return old; + } else { + return null; + } +#endif +} + +Value Value::removeMember(const std::string& key) { + return removeMember(key.c_str()); +} + +#ifdef JSON_USE_CPPTL +Value Value::get(const CppTL::ConstString& key, + const Value& defaultValue) const { + return get(key.c_str(), defaultValue); +} +#endif + +bool Value::isMember(const char* key) const { + const Value* value = &((*this)[key]); + return value != &null; +} + +bool Value::isMember(const std::string& key) const { + return isMember(key.c_str()); +} + +#ifdef JSON_USE_CPPTL +bool Value::isMember(const CppTL::ConstString& key) const { + return isMember(key.c_str()); +} +#endif + +Value::Members Value::getMemberNames() const { + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == objectValue, + "in Json::Value::getMemberNames(), value must be objectValue"); + if (type_ == nullValue) + return Value::Members(); + Members members; + members.reserve(value_.map_->size()); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + ObjectValues::const_iterator it = value_.map_->begin(); + ObjectValues::const_iterator itEnd = value_.map_->end(); + for (; it != itEnd; ++it) + members.push_back(std::string((*it).first.c_str())); +#else + ValueInternalMap::IteratorState it; + ValueInternalMap::IteratorState itEnd; + value_.map_->makeBeginIterator(it); + value_.map_->makeEndIterator(itEnd); + for (; !ValueInternalMap::equals(it, itEnd); ValueInternalMap::increment(it)) + members.push_back(std::string(ValueInternalMap::key(it))); +#endif + return members; +} +// +//# ifdef JSON_USE_CPPTL +// EnumMemberNames +// Value::enumMemberNames() const +//{ +// if ( type_ == objectValue ) +// { +// return CppTL::Enum::any( CppTL::Enum::transform( +// CppTL::Enum::keys( *(value_.map_), CppTL::Type() ), +// MemberNamesTransform() ) ); +// } +// return EnumMemberNames(); +//} +// +// +// EnumValues +// Value::enumValues() const +//{ +// if ( type_ == objectValue || type_ == arrayValue ) +// return CppTL::Enum::anyValues( *(value_.map_), +// CppTL::Type() ); +// return EnumValues(); +//} +// +//# endif + +static bool IsIntegral(double d) { + double integral_part; + return modf(d, &integral_part) == 0.0; +} + +bool Value::isNull() const { return type_ == nullValue; } + +bool Value::isBool() const { return type_ == booleanValue; } + +bool Value::isInt() const { + switch (type_) { + case intValue: + return value_.int_ >= minInt && value_.int_ <= maxInt; + case uintValue: + return value_.uint_ <= UInt(maxInt); + case realValue: + return value_.real_ >= minInt && value_.real_ <= maxInt && + IsIntegral(value_.real_); + default: + break; + } + return false; +} + +bool Value::isUInt() const { + switch (type_) { + case intValue: + return value_.int_ >= 0 && LargestUInt(value_.int_) <= LargestUInt(maxUInt); + case uintValue: + return value_.uint_ <= maxUInt; + case realValue: + return value_.real_ >= 0 && value_.real_ <= maxUInt && + IsIntegral(value_.real_); + default: + break; + } + return false; +} + +bool Value::isInt64() const { +#if defined(JSON_HAS_INT64) + switch (type_) { + case intValue: + return true; + case uintValue: + return value_.uint_ <= UInt64(maxInt64); + case realValue: + // Note that maxInt64 (= 2^63 - 1) is not exactly representable as a + // double, so double(maxInt64) will be rounded up to 2^63. Therefore we + // require the value to be strictly less than the limit. + return value_.real_ >= double(minInt64) && + value_.real_ < double(maxInt64) && IsIntegral(value_.real_); + default: + break; + } +#endif // JSON_HAS_INT64 + return false; +} + +bool Value::isUInt64() const { +#if defined(JSON_HAS_INT64) + switch (type_) { + case intValue: + return value_.int_ >= 0; + case uintValue: + return true; + case realValue: + // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a + // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we + // require the value to be strictly less than the limit. + return value_.real_ >= 0 && value_.real_ < maxUInt64AsDouble && + IsIntegral(value_.real_); + default: + break; + } +#endif // JSON_HAS_INT64 + return false; +} + +bool Value::isIntegral() const { +#if defined(JSON_HAS_INT64) + return isInt64() || isUInt64(); +#else + return isInt() || isUInt(); +#endif +} + +bool Value::isDouble() const { return type_ == realValue || isIntegral(); } + +bool Value::isNumeric() const { return isIntegral() || isDouble(); } + +bool Value::isString() const { return type_ == stringValue; } + +bool Value::isArray() const { return type_ == arrayValue; } + +bool Value::isObject() const { return type_ == objectValue; } + +void Value::setComment(const char* comment, CommentPlacement placement) { + if (!comments_) + comments_ = new CommentInfo[numberOfCommentPlacement]; + comments_[placement].setComment(comment); +} + +void Value::setComment(const std::string& comment, CommentPlacement placement) { + setComment(comment.c_str(), placement); +} + +bool Value::hasComment(CommentPlacement placement) const { + return comments_ != 0 && comments_[placement].comment_ != 0; +} + +std::string Value::getComment(CommentPlacement placement) const { + if (hasComment(placement)) + return comments_[placement].comment_; + return ""; +} + +void Value::setOffsetStart(size_t start) { start_ = start; } + +void Value::setOffsetLimit(size_t limit) { limit_ = limit; } + +size_t Value::getOffsetStart() const { return start_; } + +size_t Value::getOffsetLimit() const { return limit_; } + +std::string Value::toStyledString() const { + StyledWriter writer; + return writer.write(*this); +} + +Value::const_iterator Value::begin() const { + switch (type_) { +#ifdef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + if (value_.array_) { + ValueInternalArray::IteratorState it; + value_.array_->makeBeginIterator(it); + return const_iterator(it); + } + break; + case objectValue: + if (value_.map_) { + ValueInternalMap::IteratorState it; + value_.map_->makeBeginIterator(it); + return const_iterator(it); + } + break; +#else + case arrayValue: + case objectValue: + if (value_.map_) + return const_iterator(value_.map_->begin()); + break; +#endif + default: + break; + } + return const_iterator(); +} + +Value::const_iterator Value::end() const { + switch (type_) { +#ifdef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + if (value_.array_) { + ValueInternalArray::IteratorState it; + value_.array_->makeEndIterator(it); + return const_iterator(it); + } + break; + case objectValue: + if (value_.map_) { + ValueInternalMap::IteratorState it; + value_.map_->makeEndIterator(it); + return const_iterator(it); + } + break; +#else + case arrayValue: + case objectValue: + if (value_.map_) + return const_iterator(value_.map_->end()); + break; +#endif + default: + break; + } + return const_iterator(); +} + +Value::iterator Value::begin() { + switch (type_) { +#ifdef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + if (value_.array_) { + ValueInternalArray::IteratorState it; + value_.array_->makeBeginIterator(it); + return iterator(it); + } + break; + case objectValue: + if (value_.map_) { + ValueInternalMap::IteratorState it; + value_.map_->makeBeginIterator(it); + return iterator(it); + } + break; +#else + case arrayValue: + case objectValue: + if (value_.map_) + return iterator(value_.map_->begin()); + break; +#endif + default: + break; + } + return iterator(); +} + +Value::iterator Value::end() { + switch (type_) { +#ifdef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + if (value_.array_) { + ValueInternalArray::IteratorState it; + value_.array_->makeEndIterator(it); + return iterator(it); + } + break; + case objectValue: + if (value_.map_) { + ValueInternalMap::IteratorState it; + value_.map_->makeEndIterator(it); + return iterator(it); + } + break; +#else + case arrayValue: + case objectValue: + if (value_.map_) + return iterator(value_.map_->end()); + break; +#endif + default: + break; + } + return iterator(); +} + +// class PathArgument +// ////////////////////////////////////////////////////////////////// + +PathArgument::PathArgument() : key_(), index_(), kind_(kindNone) {} + +PathArgument::PathArgument(ArrayIndex index) + : key_(), index_(index), kind_(kindIndex) {} + +PathArgument::PathArgument(const char* key) + : key_(key), index_(), kind_(kindKey) {} + +PathArgument::PathArgument(const std::string& key) + : key_(key.c_str()), index_(), kind_(kindKey) {} + +// class Path +// ////////////////////////////////////////////////////////////////// + +Path::Path(const std::string& path, + const PathArgument& a1, + const PathArgument& a2, + const PathArgument& a3, + const PathArgument& a4, + const PathArgument& a5) { + InArgs in; + in.push_back(&a1); + in.push_back(&a2); + in.push_back(&a3); + in.push_back(&a4); + in.push_back(&a5); + makePath(path, in); +} + +void Path::makePath(const std::string& path, const InArgs& in) { + const char* current = path.c_str(); + const char* end = current + path.length(); + InArgs::const_iterator itInArg = in.begin(); + while (current != end) { + if (*current == '[') { + ++current; + if (*current == '%') + addPathInArg(path, in, itInArg, PathArgument::kindIndex); + else { + ArrayIndex index = 0; + for (; current != end && *current >= '0' && *current <= '9'; ++current) + index = index * 10 + ArrayIndex(*current - '0'); + args_.push_back(index); + } + if (current == end || *current++ != ']') + invalidPath(path, int(current - path.c_str())); + } else if (*current == '%') { + addPathInArg(path, in, itInArg, PathArgument::kindKey); + ++current; + } else if (*current == '.') { + ++current; + } else { + const char* beginName = current; + while (current != end && !strchr("[.", *current)) + ++current; + args_.push_back(std::string(beginName, current)); + } + } +} + +void Path::addPathInArg(const std::string& /*path*/, + const InArgs& in, + InArgs::const_iterator& itInArg, + PathArgument::Kind kind) { + if (itInArg == in.end()) { + // Error: missing argument %d + } else if ((*itInArg)->kind_ != kind) { + // Error: bad argument type + } else { + args_.push_back(**itInArg); + } +} + +void Path::invalidPath(const std::string& /*path*/, int /*location*/) { + // Error: invalid path. +} + +const Value& Path::resolve(const Value& root) const { + const Value* node = &root; + for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { + const PathArgument& arg = *it; + if (arg.kind_ == PathArgument::kindIndex) { + if (!node->isArray() || !node->isValidIndex(arg.index_)) { + // Error: unable to resolve path (array value expected at position... + } + node = &((*node)[arg.index_]); + } else if (arg.kind_ == PathArgument::kindKey) { + if (!node->isObject()) { + // Error: unable to resolve path (object value expected at position...) + } + node = &((*node)[arg.key_]); + if (node == &Value::null) { + // Error: unable to resolve path (object has no member named '' at + // position...) + } + } + } + return *node; +} + +Value Path::resolve(const Value& root, const Value& defaultValue) const { + const Value* node = &root; + for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { + const PathArgument& arg = *it; + if (arg.kind_ == PathArgument::kindIndex) { + if (!node->isArray() || !node->isValidIndex(arg.index_)) + return defaultValue; + node = &((*node)[arg.index_]); + } else if (arg.kind_ == PathArgument::kindKey) { + if (!node->isObject()) + return defaultValue; + node = &((*node)[arg.key_]); + if (node == &Value::null) + return defaultValue; + } + } + return *node; +} + +Value& Path::make(Value& root) const { + Value* node = &root; + for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) { + const PathArgument& arg = *it; + if (arg.kind_ == PathArgument::kindIndex) { + if (!node->isArray()) { + // Error: node is not an array at position ... + } + node = &((*node)[arg.index_]); + } else if (arg.kind_ == PathArgument::kindKey) { + if (!node->isObject()) { + // Error: node is not an object at position... + } + node = &((*node)[arg.key_]); + } + } + return *node; +} + +} // namespace Json +} // namespace VA diff --git a/VrUtils/jsoncpp/json_valueiterator.inl b/VrUtils/jsoncpp/json_valueiterator.inl new file mode 100644 index 0000000..a9f7df6 --- /dev/null +++ b/VrUtils/jsoncpp/json_valueiterator.inl @@ -0,0 +1,241 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +// included by json_value.cpp + +namespace Json { + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueIteratorBase +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueIteratorBase::ValueIteratorBase() +#ifndef JSON_VALUE_USE_INTERNAL_MAP + : current_(), isNull_(true) { +} +#else + : isArray_(true), isNull_(true) { + iterator_.array_ = ValueInternalArray::IteratorState(); +} +#endif + +#ifndef JSON_VALUE_USE_INTERNAL_MAP +ValueIteratorBase::ValueIteratorBase( + const Value::ObjectValues::iterator& current) + : current_(current), isNull_(false) {} +#else +ValueIteratorBase::ValueIteratorBase( + const ValueInternalArray::IteratorState& state) + : isArray_(true) { + iterator_.array_ = state; +} + +ValueIteratorBase::ValueIteratorBase( + const ValueInternalMap::IteratorState& state) + : isArray_(false) { + iterator_.map_ = state; +} +#endif + +Value& ValueIteratorBase::deref() const { +#ifndef JSON_VALUE_USE_INTERNAL_MAP + return current_->second; +#else + if (isArray_) + return ValueInternalArray::dereference(iterator_.array_); + return ValueInternalMap::value(iterator_.map_); +#endif +} + +void ValueIteratorBase::increment() { +#ifndef JSON_VALUE_USE_INTERNAL_MAP + ++current_; +#else + if (isArray_) + ValueInternalArray::increment(iterator_.array_); + ValueInternalMap::increment(iterator_.map_); +#endif +} + +void ValueIteratorBase::decrement() { +#ifndef JSON_VALUE_USE_INTERNAL_MAP + --current_; +#else + if (isArray_) + ValueInternalArray::decrement(iterator_.array_); + ValueInternalMap::decrement(iterator_.map_); +#endif +} + +ValueIteratorBase::difference_type +ValueIteratorBase::computeDistance(const SelfType& other) const { +#ifndef JSON_VALUE_USE_INTERNAL_MAP +#ifdef JSON_USE_CPPTL_SMALLMAP + return current_ - other.current_; +#else + // Iterator for null value are initialized using the default + // constructor, which initialize current_ to the default + // std::map::iterator. As begin() and end() are two instance + // of the default std::map::iterator, they can not be compared. + // To allow this, we handle this comparison specifically. + if (isNull_ && other.isNull_) { + return 0; + } + + // Usage of std::distance is not portable (does not compile with Sun Studio 12 + // RogueWave STL, + // which is the one used by default). + // Using a portable hand-made version for non random iterator instead: + // return difference_type( std::distance( current_, other.current_ ) ); + difference_type myDistance = 0; + for (Value::ObjectValues::iterator it = current_; it != other.current_; + ++it) { + ++myDistance; + } + return myDistance; +#endif +#else + if (isArray_) + return ValueInternalArray::distance(iterator_.array_, + other.iterator_.array_); + return ValueInternalMap::distance(iterator_.map_, other.iterator_.map_); +#endif +} + +bool ValueIteratorBase::isEqual(const SelfType& other) const { +#ifndef JSON_VALUE_USE_INTERNAL_MAP + if (isNull_) { + return other.isNull_; + } + return current_ == other.current_; +#else + if (isArray_) + return ValueInternalArray::equals(iterator_.array_, other.iterator_.array_); + return ValueInternalMap::equals(iterator_.map_, other.iterator_.map_); +#endif +} + +void ValueIteratorBase::copy(const SelfType& other) { +#ifndef JSON_VALUE_USE_INTERNAL_MAP + current_ = other.current_; + isNull_ = other.isNull_; +#else + if (isArray_) + iterator_.array_ = other.iterator_.array_; + iterator_.map_ = other.iterator_.map_; +#endif +} + +Value ValueIteratorBase::key() const { +#ifndef JSON_VALUE_USE_INTERNAL_MAP + const Value::CZString czstring = (*current_).first; + if (czstring.c_str()) { + if (czstring.isStaticString()) + return Value(StaticString(czstring.c_str())); + return Value(czstring.c_str()); + } + return Value(czstring.index()); +#else + if (isArray_) + return Value(ValueInternalArray::indexOf(iterator_.array_)); + bool isStatic; + const char* memberName = ValueInternalMap::key(iterator_.map_, isStatic); + if (isStatic) + return Value(StaticString(memberName)); + return Value(memberName); +#endif +} + +UInt ValueIteratorBase::index() const { +#ifndef JSON_VALUE_USE_INTERNAL_MAP + const Value::CZString czstring = (*current_).first; + if (!czstring.c_str()) + return czstring.index(); + return Value::UInt(-1); +#else + if (isArray_) + return Value::UInt(ValueInternalArray::indexOf(iterator_.array_)); + return Value::UInt(-1); +#endif +} + +const char* ValueIteratorBase::memberName() const { +#ifndef JSON_VALUE_USE_INTERNAL_MAP + const char* name = (*current_).first.c_str(); + return name ? name : ""; +#else + if (!isArray_) + return ValueInternalMap::key(iterator_.map_); + return ""; +#endif +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueConstIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueConstIterator::ValueConstIterator() {} + +#ifndef JSON_VALUE_USE_INTERNAL_MAP +ValueConstIterator::ValueConstIterator( + const Value::ObjectValues::iterator& current) + : ValueIteratorBase(current) {} +#else +ValueConstIterator::ValueConstIterator( + const ValueInternalArray::IteratorState& state) + : ValueIteratorBase(state) {} + +ValueConstIterator::ValueConstIterator( + const ValueInternalMap::IteratorState& state) + : ValueIteratorBase(state) {} +#endif + +ValueConstIterator& ValueConstIterator:: +operator=(const ValueIteratorBase& other) { + copy(other); + return *this; +} + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueIterator::ValueIterator() {} + +#ifndef JSON_VALUE_USE_INTERNAL_MAP +ValueIterator::ValueIterator(const Value::ObjectValues::iterator& current) + : ValueIteratorBase(current) {} +#else +ValueIterator::ValueIterator(const ValueInternalArray::IteratorState& state) + : ValueIteratorBase(state) {} + +ValueIterator::ValueIterator(const ValueInternalMap::IteratorState& state) + : ValueIteratorBase(state) {} +#endif + +ValueIterator::ValueIterator(const ValueConstIterator& other) + : ValueIteratorBase(other) {} + +ValueIterator::ValueIterator(const ValueIterator& other) + : ValueIteratorBase(other) {} + +ValueIterator& ValueIterator::operator=(const SelfType& other) { + copy(other); + return *this; +} + +} // namespace Json diff --git a/VrUtils/jsoncpp/json_writer.cpp b/VrUtils/jsoncpp/json_writer.cpp new file mode 100644 index 0000000..9cf5efc --- /dev/null +++ b/VrUtils/jsoncpp/json_writer.cpp @@ -0,0 +1,691 @@ +// Copyright 2011 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +#include "json/writer.h" +#include "json_tool.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below +#include +#define isfinite _finite +#define snprintf _snprintf +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0 +// Disable warning about strdup being deprecated. +#pragma warning(disable : 4996) +#endif +namespace VA { +namespace Json { + +static bool containsControlCharacter(const char* str) { + while (*str) { + if (isControlCharacter(*(str++))) + return true; + } + return false; +} + +std::string valueToString(LargestInt value) { + UIntToStringBuffer buffer; + char* current = buffer + sizeof(buffer); + bool isNegative = value < 0; + if (isNegative) + value = -value; + uintToString(LargestUInt(value), current); + if (isNegative) + *--current = '-'; + assert(current >= buffer); + return current; +} + +std::string valueToString(LargestUInt value) { + UIntToStringBuffer buffer; + char* current = buffer + sizeof(buffer); + uintToString(value, current); + assert(current >= buffer); + return current; +} + +#if defined(JSON_HAS_INT64) + +std::string valueToString(Int value) { + return valueToString(LargestInt(value)); +} + +std::string valueToString(UInt value) { + return valueToString(LargestUInt(value)); +} + +#endif // # if defined(JSON_HAS_INT64) + +std::string valueToString(double value) { + // Allocate a buffer that is more than large enough to store the 16 digits of + // precision requested below. + char buffer[32]; + int len = -1; + +// Print into the buffer. We need not request the alternative representation +// that always has a decimal point because JSON doesn't distingish the +// concepts of reals and integers. +#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with + // visual studio 2005 to + // avoid warning. +#if defined(WINCE) + len = _snprintf(buffer, sizeof(buffer), "%.16g", value); +#else + len = sprintf_s(buffer, sizeof(buffer), "%.16g", value); +#endif +#else + if (isfinite(value)) { + len = snprintf(buffer, sizeof(buffer), "%.16g", value); + } else { + // IEEE standard states that NaN values will not compare to themselves + if (value != value) { + len = snprintf(buffer, sizeof(buffer), "null"); + } else if (value < 0) { + len = snprintf(buffer, sizeof(buffer), "-1e+9999"); + } else { + len = snprintf(buffer, sizeof(buffer), "1e+9999"); + } + // For those, we do not need to call fixNumLoc, but it is fast. + } +#endif + assert(len >= 0); + fixNumericLocale(buffer, buffer + len); + return buffer; +} + +std::string valueToString(bool value) { return value ? "true" : "false"; } + +std::string valueToQuotedString(const char* value) { + if (value == NULL) + return ""; + // Not sure how to handle unicode... + if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && + !containsControlCharacter(value)) + return std::string("\"") + value + "\""; + // We have to walk value and escape any special characters. + // Appending to std::string is not efficient, but this should be rare. + // (Note: forward slashes are *not* rare, but I am not escaping them.) + std::string::size_type maxsize = + strlen(value) * 2 + 3; // allescaped+quotes+NULL + std::string result; + result.reserve(maxsize); // to avoid lots of mallocs + result += "\""; + for (const char* c = value; *c != 0; ++c) { + switch (*c) { + case '\"': + result += "\\\""; + break; + case '\\': + result += "\\\\"; + break; + case '\b': + result += "\\b"; + break; + case '\f': + result += "\\f"; + break; + case '\n': + result += "\\n"; + break; + case '\r': + result += "\\r"; + break; + case '\t': + result += "\\t"; + break; + // case '/': + // Even though \/ is considered a legal escape in JSON, a bare + // slash is also legal, so I see no reason to escape it. + // (I hope I am not misunderstanding something. + // blep notes: actually escaping \/ may be useful in javascript to avoid (*c); + result += oss.str(); + } else { + result += *c; + } + break; + } + } + result += "\""; + return result; +} + +// Class Writer +// ////////////////////////////////////////////////////////////////// +Writer::~Writer() {} + +// Class FastWriter +// ////////////////////////////////////////////////////////////////// + +FastWriter::FastWriter() + : yamlCompatiblityEnabled_(false), dropNullPlaceholders_(false), + omitEndingLineFeed_(false) {} + +void FastWriter::enableYAMLCompatibility() { yamlCompatiblityEnabled_ = true; } + +void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; } + +void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; } + +std::string FastWriter::write(const Value& root) { + document_ = ""; + writeValue(root); + if (!omitEndingLineFeed_) + document_ += "\n"; + return document_; +} + +void FastWriter::writeValue(const Value& value) { + switch (value.type()) { + case nullValue: + if (!dropNullPlaceholders_) + document_ += "null"; + break; + case intValue: + document_ += valueToString(value.asLargestInt()); + break; + case uintValue: + document_ += valueToString(value.asLargestUInt()); + break; + case realValue: + document_ += valueToString(value.asDouble()); + break; + case stringValue: + document_ += valueToQuotedString(value.asCString()); + break; + case booleanValue: + document_ += valueToString(value.asBool()); + break; + case arrayValue: { + document_ += "["; + int size = value.size(); + for (int index = 0; index < size; ++index) { + if (index > 0) + document_ += ","; + writeValue(value[index]); + } + document_ += "]"; + } break; + case objectValue: { + Value::Members members(value.getMemberNames()); + document_ += "{"; + for (Value::Members::iterator it = members.begin(); it != members.end(); + ++it) { + const std::string& name = *it; + if (it != members.begin()) + document_ += ","; + document_ += valueToQuotedString(name.c_str()); + document_ += yamlCompatiblityEnabled_ ? ": " : ":"; + writeValue(value[name]); + } + document_ += "}"; + } break; + } +} + +// Class StyledWriter +// ////////////////////////////////////////////////////////////////// + +StyledWriter::StyledWriter() + : rightMargin_(74), indentSize_(3), addChildValues_() {} + +std::string StyledWriter::write(const Value& root) { + document_ = ""; + addChildValues_ = false; + indentString_ = ""; + writeCommentBeforeValue(root); + writeValue(root); + writeCommentAfterValueOnSameLine(root); + document_ += "\n"; + return document_; +} + +void StyledWriter::writeValue(const Value& value) { + switch (value.type()) { + case nullValue: + pushValue("null"); + break; + case intValue: + pushValue(valueToString(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble())); + break; + case stringValue: + pushValue(valueToQuotedString(value.asCString())); + break; + case booleanValue: + pushValue(valueToString(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + Value::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + Value::Members::iterator it = members.begin(); + for (;;) { + const std::string& name = *it; + const Value& childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedString(name.c_str())); + document_ += " : "; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + document_ += ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } break; + } +} + +void StyledWriter::writeArrayValue(const Value& value) { + unsigned size = value.size(); + if (size == 0) + pushValue("[]"); + else { + bool isArrayMultiLine = isMultineArray(value); + if (isArrayMultiLine) { + writeWithIndent("["); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index = 0; + for (;;) { + const Value& childValue = value[index]; + writeCommentBeforeValue(childValue); + if (hasChildValue) + writeWithIndent(childValues_[index]); + else { + writeIndent(); + writeValue(childValue); + } + if (++index == size) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + document_ += ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } else // output on a single line + { + assert(childValues_.size() == size); + document_ += "[ "; + for (unsigned index = 0; index < size; ++index) { + if (index > 0) + document_ += ", "; + document_ += childValues_[index]; + } + document_ += " ]"; + } + } +} + +bool StyledWriter::isMultineArray(const Value& value) { + int size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (int index = 0; index < size && !isMultiLine; ++index) { + const Value& childValue = value[index]; + isMultiLine = + isMultiLine || ((childValue.isArray() || childValue.isObject()) && + childValue.size() > 0); + } + if (!isMultiLine) // check if line length > max line length + { + childValues_.reserve(size); + addChildValues_ = true; + int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (int index = 0; index < size; ++index) { + writeValue(value[index]); + lineLength += int(childValues_[index].length()); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + +void StyledWriter::pushValue(const std::string& value) { + if (addChildValues_) + childValues_.push_back(value); + else + document_ += value; +} + +void StyledWriter::writeIndent() { + if (!document_.empty()) { + char last = document_[document_.length() - 1]; + if (last == ' ') // already indented + return; + if (last != '\n') // Comments may add new-line + document_ += '\n'; + } + document_ += indentString_; +} + +void StyledWriter::writeWithIndent(const std::string& value) { + writeIndent(); + document_ += value; +} + +void StyledWriter::indent() { indentString_ += std::string(indentSize_, ' '); } + +void StyledWriter::unindent() { + assert(int(indentString_.size()) >= indentSize_); + indentString_.resize(indentString_.size() - indentSize_); +} + +void StyledWriter::writeCommentBeforeValue(const Value& root) { + if (!root.hasComment(commentBefore)) + return; + + document_ += "\n"; + writeIndent(); + std::string normalizedComment = normalizeEOL(root.getComment(commentBefore)); + std::string::const_iterator iter = normalizedComment.begin(); + while (iter != normalizedComment.end()) { + document_ += *iter; + if (*iter == '\n' && *(iter + 1) == '/') + writeIndent(); + ++iter; + } + + // Comments are stripped of newlines, so add one here + document_ += "\n"; +} + +void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) { + if (root.hasComment(commentAfterOnSameLine)) + document_ += " " + normalizeEOL(root.getComment(commentAfterOnSameLine)); + + if (root.hasComment(commentAfter)) { + document_ += "\n"; + document_ += normalizeEOL(root.getComment(commentAfter)); + document_ += "\n"; + } +} + +bool StyledWriter::hasCommentForValue(const Value& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + +std::string StyledWriter::normalizeEOL(const std::string& text) { + std::string normalized; + normalized.reserve(text.length()); + const char* begin = text.c_str(); + const char* end = begin + text.length(); + const char* current = begin; + while (current != end) { + char c = *current++; + if (c == '\r') // mac or dos EOL + { + if (*current == '\n') // convert dos EOL + ++current; + normalized += '\n'; + } else // handle unix EOL & other char + normalized += c; + } + return normalized; +} + +// Class StyledStreamWriter +// ////////////////////////////////////////////////////////////////// + +StyledStreamWriter::StyledStreamWriter(std::string indentation) + : document_(NULL), rightMargin_(74), indentation_(indentation), + addChildValues_() {} + +void StyledStreamWriter::write(std::ostream& out, const Value& root) { + document_ = &out; + addChildValues_ = false; + indentString_ = ""; + writeCommentBeforeValue(root); + writeValue(root); + writeCommentAfterValueOnSameLine(root); + *document_ << "\n"; + document_ = NULL; // Forget the stream, for safety. +} + +void StyledStreamWriter::writeValue(const Value& value) { + switch (value.type()) { + case nullValue: + pushValue("null"); + break; + case intValue: + pushValue(valueToString(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble())); + break; + case stringValue: + pushValue(valueToQuotedString(value.asCString())); + break; + case booleanValue: + pushValue(valueToString(value.asBool())); + break; + case arrayValue: + writeArrayValue(value); + break; + case objectValue: { + Value::Members members(value.getMemberNames()); + if (members.empty()) + pushValue("{}"); + else { + writeWithIndent("{"); + indent(); + Value::Members::iterator it = members.begin(); + for (;;) { + const std::string& name = *it; + const Value& childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedString(name.c_str())); + *document_ << " : "; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } break; + } +} + +void StyledStreamWriter::writeArrayValue(const Value& value) { + unsigned size = value.size(); + if (size == 0) + pushValue("[]"); + else { + bool isArrayMultiLine = isMultineArray(value); + if (isArrayMultiLine) { + writeWithIndent("["); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index = 0; + for (;;) { + const Value& childValue = value[index]; + writeCommentBeforeValue(childValue); + if (hasChildValue) + writeWithIndent(childValues_[index]); + else { + writeIndent(); + writeValue(childValue); + } + if (++index == size) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } else // output on a single line + { + assert(childValues_.size() == size); + *document_ << "[ "; + for (unsigned index = 0; index < size; ++index) { + if (index > 0) + *document_ << ", "; + *document_ << childValues_[index]; + } + *document_ << " ]"; + } + } +} + +bool StyledStreamWriter::isMultineArray(const Value& value) { + int size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (int index = 0; index < size && !isMultiLine; ++index) { + const Value& childValue = value[index]; + isMultiLine = + isMultiLine || ((childValue.isArray() || childValue.isObject()) && + childValue.size() > 0); + } + if (!isMultiLine) // check if line length > max line length + { + childValues_.reserve(size); + addChildValues_ = true; + int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (int index = 0; index < size; ++index) { + writeValue(value[index]); + lineLength += int(childValues_[index].length()); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + +void StyledStreamWriter::pushValue(const std::string& value) { + if (addChildValues_) + childValues_.push_back(value); + else + *document_ << value; +} + +void StyledStreamWriter::writeIndent() { + /* + Some comments in this method would have been nice. ;-) + + if ( !document_.empty() ) + { + char last = document_[document_.length()-1]; + if ( last == ' ' ) // already indented + return; + if ( last != '\n' ) // Comments may add new-line + *document_ << '\n'; + } + */ + *document_ << '\n' << indentString_; +} + +void StyledStreamWriter::writeWithIndent(const std::string& value) { + writeIndent(); + *document_ << value; +} + +void StyledStreamWriter::indent() { indentString_ += indentation_; } + +void StyledStreamWriter::unindent() { + assert(indentString_.size() >= indentation_.size()); + indentString_.resize(indentString_.size() - indentation_.size()); +} + +void StyledStreamWriter::writeCommentBeforeValue(const Value& root) { + if (!root.hasComment(commentBefore)) + return; + *document_ << normalizeEOL(root.getComment(commentBefore)); + *document_ << "\n"; +} + +void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) { + if (root.hasComment(commentAfterOnSameLine)) + *document_ << " " + normalizeEOL(root.getComment(commentAfterOnSameLine)); + + if (root.hasComment(commentAfter)) { + *document_ << "\n"; + *document_ << normalizeEOL(root.getComment(commentAfter)); + *document_ << "\n"; + } +} + +bool StyledStreamWriter::hasCommentForValue(const Value& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + +std::string StyledStreamWriter::normalizeEOL(const std::string& text) { + std::string normalized; + normalized.reserve(text.length()); + const char* begin = text.c_str(); + const char* end = begin + text.length(); + const char* current = begin; + while (current != end) { + char c = *current++; + if (c == '\r') // mac or dos EOL + { + if (*current == '\n') // convert dos EOL + ++current; + normalized += '\n'; + } else // handle unix EOL & other char + normalized += c; + } + return normalized; +} + +std::ostream& operator<<(std::ostream& sout, const Value& root) { + Json::StyledStreamWriter writer; + writer.write(sout, root); + return sout; +} + +} // namespace Json +} // namespace VA diff --git a/VrUtils/log4cpp/include/log4cpp/AbortAppender.hh b/VrUtils/log4cpp/include/log4cpp/AbortAppender.hh new file mode 100644 index 0000000..a65c1a5 --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/AbortAppender.hh @@ -0,0 +1,46 @@ +/* + * AbortAppender.hh + * + * Copyright 2002, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2002, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_ABORTAPPENDER_HH +#define _LOG4CPP_ABORTAPPENDER_HH + +#include +#include + +namespace log4cpp { + + /** + * This Appender causes the application to abort() upon the first append() + * call. + * + * @since 0.3.5 + **/ + class LOG4CPP_EXPORT AbortAppender : public AppenderSkeleton { + public: + + AbortAppender(const std::string& name); + virtual ~AbortAppender(); + + virtual bool reopen(); + virtual void close(); + + /** + * The AbortAppender does not layout. + * @returns false + **/ + virtual bool requiresLayout() const; + + virtual void setLayout(Layout* layout); + + protected: + virtual void _append(const LoggingEvent& event); + }; +} + +#endif // _LOG4CPP_ABORTAPPENDER_HH diff --git a/VrUtils/log4cpp/include/log4cpp/Appender.hh b/VrUtils/log4cpp/include/log4cpp/Appender.hh new file mode 100644 index 0000000..472c1ee --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/Appender.hh @@ -0,0 +1,169 @@ +/* + * Appender.hh + * + * Copyright 2000, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2000, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_APPENDER_HH +#define _LOG4CPP_APPENDER_HH + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace log4cpp { + class LOG4CPP_EXPORT Filter; + + /** + * Implement this interface for your own strategies for printing log + * statements. + **/ + class LOG4CPP_EXPORT Appender { + friend class Category; + public: + + /** + * Get a pointer to an exitsing Appender. + * @param name The name of the Appender to return. + * @returns a pointer to an existing Appender, or NULL if no appender + * with the specfied name exists. + **/ + static Appender* getAppender(const std::string& name); + + /** + * Call reopen() on all existing Appenders. + * @returns true if all Appenders returned true on their reopen() call. + **/ + static bool reopenAll(); + + /** + * Call reopen() on all existing Appenders. + * @returns true if all Appenders returned true on their reopen() call. + **/ + static void closeAll(); + + protected: + /** + * Constructor for Appender. Will only be used in getAppender() (and + * in derived classes of course). + * @param name The name of this Appender. + **/ + Appender(const std::string& name); + + public: + /** + * Destructor for Appender. + **/ + virtual ~Appender(); + + /** + * Log in Appender specific way. + * @param event The LoggingEvent to log. + **/ + virtual void doAppend(const LoggingEvent& event) = 0; + + /** + * Reopens the output destination of this Appender, e.g. the logfile + * or TCP socket. + * @returns false if an error occured during reopening, true otherwise. + **/ + virtual bool reopen() = 0; + + /** + * Release any resources allocated within the appender such as file + * handles, network connections, etc. + **/ + virtual void close() = 0; + + /** + * Check if the appender uses a layout. + * + * @returns true if the appender implementation requires a layout. + **/ + virtual bool requiresLayout() const = 0; + + /** + * Set the Layout for this appender. + * @param layout The layout to use. + **/ + virtual void setLayout(Layout* layout) = 0; + + /** + * Get the name of this appender. The name identifies the appender. + * @returns the name of the appender. + **/ + inline const std::string& getName() const { return _name; }; + + /** + * Set the threshold priority of this Appender. The Appender will not + * appender LoggingEvents with a priority lower than the threshold. + * Use Priority::NOTSET to disable threshold checking. + * @param priority The priority to set. + **/ + virtual void setThreshold(Priority::Value priority) = 0; + + /** + * Get the threshold priority of this Appender. + * @returns the threshold + **/ + virtual Priority::Value getThreshold() = 0; + + /** + * Set a Filter for this appender. + **/ + virtual void setFilter(Filter* filter) = 0; + + /** + * Get the Filter for this appender. + * @returns the filter, or NULL if no filter has been set. + **/ + virtual Filter* getFilter() = 0; + + private: + typedef std::map AppenderMap; + + static AppenderMap& _getAllAppenders(); + static void _deleteAllAppenders(); + static void _deleteAllAppendersWOLock(std::vector &appenders); + static void _addAppender(Appender* appender); + static void _removeAppender(Appender* appender); + + const std::string _name; + + public: + class AppenderMapStorage { + public: + Appender::AppenderMap* _allAppenders; // single shared instance, nifty-counter defensed + threading::Mutex _appenderMapMutex; // mutex protecting map from multiple thread access + + AppenderMapStorage(); + ~AppenderMapStorage(); + }; + class LOG4CPP_EXPORT AppenderMapStorageInitializer { + public: + AppenderMapStorageInitializer(); + ~AppenderMapStorageInitializer(); + }; + private: + static AppenderMapStorage &_appenderMapStorageInstance; + }; + + static Appender::AppenderMapStorageInitializer appenderMapStorageInitializer; // static initializer for every translation unit + typedef std::set AppenderSet; + +} + +#endif // _LOG4CPP_APPENDER_HH diff --git a/VrUtils/log4cpp/include/log4cpp/AppenderSkeleton.hh b/VrUtils/log4cpp/include/log4cpp/AppenderSkeleton.hh new file mode 100644 index 0000000..fa22190 --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/AppenderSkeleton.hh @@ -0,0 +1,111 @@ +/* + * AppenderSkeleton.hh + * + * Copyright 2001, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2001, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_APPENDERSKELETON_HH +#define _LOG4CPP_APPENDERSKELETON_HH + +#include +#include +#include + +namespace log4cpp { + + /** + * AppenderSkeleton is a helper class, simplifying implementation of + * Appenders: it already takes care of handling of Thresholds and + * Filters. + **/ + class LOG4CPP_EXPORT AppenderSkeleton : public Appender { + protected: + /** + * Constructor for AppenderSkeleton. Will only be used in + * getAppender() (and in derived classes of course). + * @param name The name of this Appender. + **/ + AppenderSkeleton(const std::string& name); + + public: + /** + * Destructor for AppenderSkeleton. + **/ + virtual ~AppenderSkeleton(); + + /** + * Log in Appender specific way. + * @param event The LoggingEvent to log. + **/ + virtual void doAppend(const LoggingEvent& event); + + /** + * Reopens the output destination of this Appender, e.g. the logfile + * or TCP socket. + * @returns false if an error occured during reopening, true otherwise. + **/ + virtual bool reopen(); + + /** + * Release any resources allocated within the appender such as file + * handles, network connections, etc. + **/ + virtual void close() = 0; + + /** + * Check if the appender uses a layout. + * + * @returns true if the appender implementation requires a layout. + **/ + virtual bool requiresLayout() const = 0; + + /** + * Set the Layout for this appender. + * @param layout The layout to use. + **/ + virtual void setLayout(Layout* layout) = 0; + + /** + * Set the threshold priority of this Appender. The Appender will not + * appender LoggingEvents with a priority lower than the threshold. + * Use Priority::NOTSET to disable threshold checking. + * @param priority The priority to set. + **/ + virtual void setThreshold(Priority::Value priority); + + /** + * Get the threshold priority of this Appender. + * @returns the threshold + **/ + virtual Priority::Value getThreshold(); + + /** + * Set a Filter for this appender. + **/ + virtual void setFilter(Filter* filter); + + /** + * Get the Filter for this appender. + * @returns the filter, or NULL if no filter has been set. + **/ + virtual Filter* getFilter(); + + protected: + /** + * Log in Appender specific way. Subclasses of Appender should + * implement this method to perform actual logging. + * @param event The LoggingEvent to log. + **/ + virtual void _append(const LoggingEvent& event) = 0; + + + private: + Priority::Value _threshold; + Filter* _filter; + }; +} + +#endif // _LOG4CPP_APPENDERSKELETON_HH diff --git a/VrUtils/log4cpp/include/log4cpp/AppendersFactory.hh b/VrUtils/log4cpp/include/log4cpp/AppendersFactory.hh new file mode 100644 index 0000000..7c3a860 --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/AppendersFactory.hh @@ -0,0 +1,42 @@ +/* + * Copyright 2002, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2002, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + + +#if !defined(h_738a42b1_1502_4483_948a_a69e7bbbee6a) +#define h_738a42b1_1502_4483_948a_a69e7bbbee6a + +#include +#include +#include +#include "Portability.hh" +#include "Appender.hh" +#include "FactoryParams.hh" + +namespace log4cpp +{ + class LOG4CPP_EXPORT AppendersFactory + { + public: + typedef FactoryParams params_t; + typedef std::auto_ptr (*create_function_t)(const params_t& params); + + static AppendersFactory& getInstance(); + void registerCreator(const std::string& class_name, create_function_t create_function); + std::auto_ptr create(const std::string& class_name, const params_t& params); + bool registered(const std::string& class_name) const; + + private: + AppendersFactory(){}; + + typedef std::map creators_t; + typedef creators_t::const_iterator const_iterator; + + creators_t creators_; + }; +} + +#endif // h_738a42b1_1502_4483_948a_a69e7bbbee6a diff --git a/VrUtils/log4cpp/include/log4cpp/BasicConfigurator.hh b/VrUtils/log4cpp/include/log4cpp/BasicConfigurator.hh new file mode 100644 index 0000000..cc482e1 --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/BasicConfigurator.hh @@ -0,0 +1,31 @@ +/* + * BasicConfigurator.hh + * + * Copyright 2002, Log4cpp Project. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ +#ifndef _LOG4CPP_BASICCONFIGURATOR_HH +#define _LOG4CPP_BASICCONFIGURATOR_HH + +#include + +namespace log4cpp { + + /** + This class implements a trivial default configuration for log4cpp: + it adds a FileAppender that logs to stdout and uses a BasicLayout to + the root Category. + @since 0.3.2 + **/ + class LOG4CPP_EXPORT BasicConfigurator { + public: + + /** + Performs a minimal configuration of log4cpp. + **/ + static void configure(); + }; +} + +#endif diff --git a/VrUtils/log4cpp/include/log4cpp/BasicLayout.hh b/VrUtils/log4cpp/include/log4cpp/BasicLayout.hh new file mode 100644 index 0000000..12a6b73 --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/BasicLayout.hh @@ -0,0 +1,34 @@ +/* + * BasicLayout.hh + * + * Copyright 2000, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2000, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_BASICLAYOUT_HH +#define _LOG4CPP_BASICLAYOUT_HH + +#include +#include + +namespace log4cpp { + + /** + * BasicLayout is a simple fixed format Layout implementation. + **/ + class LOG4CPP_EXPORT BasicLayout : public Layout { + public: + BasicLayout(); + virtual ~BasicLayout(); + + /** + * Formats the LoggingEvent in BasicLayout style:
+ * "timeStamp priority category ndc: message" + **/ + virtual std::string format(const LoggingEvent& event); + }; +} + +#endif // _LOG4CPP_BASICLAYOUT_HH diff --git a/VrUtils/log4cpp/include/log4cpp/BufferingAppender.hh b/VrUtils/log4cpp/include/log4cpp/BufferingAppender.hh new file mode 100644 index 0000000..8035103 --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/BufferingAppender.hh @@ -0,0 +1,45 @@ +/* + * Copyright 2002, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2002, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#if !defined(h_ebd0ee89_622d_4af1_9a9d_d0e057debe86) +#define h_ebd0ee89_622d_4af1_9a9d_d0e057debe86 + +#include +#include +#include +#include + +namespace log4cpp +{ + class LOG4CPP_EXPORT BufferingAppender : public LayoutAppender + { + public: + BufferingAppender(const std::string name, unsigned long max_size, std::auto_ptr sink, + std::auto_ptr evaluator); + + virtual void close() { sink_->close(); } + + bool getLossy() const { return lossy_; } + void setLossy(bool lossy) { lossy_ = lossy; } + + protected: + virtual void _append(const LoggingEvent& event); + + private: + typedef std::list queue_t; + + queue_t queue_; + unsigned long max_size_; + std::auto_ptr sink_; + std::auto_ptr evaluator_; + bool lossy_; + + void dump(); + }; +} + +#endif // h_ebd0ee89_622d_4af1_9a9d_d0e057debe86 diff --git a/VrUtils/log4cpp/include/log4cpp/Category.hh b/VrUtils/log4cpp/include/log4cpp/Category.hh new file mode 100644 index 0000000..b3ff108 --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/Category.hh @@ -0,0 +1,675 @@ +/* + * Category.hh + * + * Copyright 2000, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2000, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_CATEGORY_HH +#define _LOG4CPP_CATEGORY_HH + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace log4cpp { + + /** + * This is the central class in the log4j package. One of the distintive + * features of log4j (and hence log4cpp) are hierarchal categories and + * their evaluation. + **/ + class LOG4CPP_EXPORT Category { + friend class HierarchyMaintainer; + + public: + /** + * Return the root of the Category hierarchy. + * + *

The root category is always instantiated and available. It's + * name is the empty string. + + *

Unlike in log4j, calling Category.getInstance("") + * does retrieve the root category + * and not a category just under root named "". + * @returns The root category + **/ + static Category& getRoot(); + + /** + * Set the priority of the root Category. + * @param priority The new priority for the root Category + **/ + static void setRootPriority(Priority::Value priority); + + /** + * Get the priority of the root Category. + * @returns the priority of the root category + **/ + static Priority::Value getRootPriority() throw(); + + /** + * Instantiate a Category with name name. This + * method does not set priority of the category which is by + * default Priority::NOTSET. + * + * @param name The name of the category to retrieve. + **/ + static Category& getInstance(const std::string& name); + + /** + * If the named category exists (in the default hierarchy) then it + * returns a reference to the category, otherwise it returns NULL. + * @since 0.2.7 + **/ + static Category* exists(const std::string& name); + + /** + * Returns all the currently defined categories as a vector of + * Category pointers. Note: this function does not pass ownership + * of the categories in the vector to the caller, only the ownership + * of the vector. However vector* is not legal C++, + * so we can't follow the default ownership conventions. + * + *

Unlike in log4j, the root category is included + * in the returned set. + * + * @since 0.3.2. Before 0.3.2 this method returned a std::set + **/ + static std::vector* getCurrentCategories(); + + /** + * This method will remove all Appenders from Categories.XXX + **/ + static void shutdown(); + + /** + * This method will remove all Appenders from Categories.XXX and delete all appenders. + * Releases more memory than shutdown() by deleting appenders. + **/ + static void shutdownForced(); + + /** + * Destructor for Category. + **/ + virtual ~Category(); + + /** + * Return the category name. + * @returns The category name. + */ + virtual const std::string& getName() const throw(); + + /** + * Set the priority of this Category. + * @param priority The priority to set. Use Priority::NOTSET to let + * the category use its parents priority as effective priority. + * @exception std::invalid_argument if the caller tries to set + * Priority::NOTSET on the Root Category. + **/ + virtual void setPriority(Priority::Value priority); + + /** + * Returns the assigned Priority, if any, for this Category. + * @return Priority - the assigned Priority, can be Priority::NOTSET + **/ + virtual Priority::Value getPriority() const throw(); + + /** + * Starting from this Category, search the category hierarchy for a + * set priority and return it. Otherwise, return the priority + * of the root category. + * + *

The Category class is designed so that this method executes as + * quickly as possible. + **/ + virtual Priority::Value getChainedPriority() const throw(); + + /** + * Returns true if the chained priority of the Category is equal to + * or higher than given priority. + * @param priority The priority to compare with. + * @returns whether logging is enable for this priority. + **/ + virtual bool isPriorityEnabled(Priority::Value priority) const throw(); + + /** + * Adds an Appender to this Category. + * This method passes ownership from the caller to the Category. + * @since 0.2.7 + * @param appender The Appender to wich this category has to log. + * @exception std::invalid_argument if the appender is NULL. + **/ + virtual void addAppender(Appender* appender); + + /** + * Adds an Appender for this Category. + * This method does not pass ownership from the caller to the Category. + * @since 0.2.7 + * @param appender The Appender this category has to log to. + **/ + virtual void addAppender(Appender& appender); + + /** + * Adds an Appender to this Category. + * This method passes ownership from the caller to the Category. + * @deprecated use addAppender(Appender*) or removeAllAppenders() + * instead. + * @param appender The Appender this category has to log to or NULL + * to remove the current Appenders. + **/ + inline void setAppender(Appender* appender) { + if (appender) { + addAppender(appender); + } else { + removeAllAppenders(); + } + }; + + /** + * Adds an Appender for this Category. + * This method does not pass ownership from the caller to the Category. + * @deprecated use addAppender(Appender&) instead. + * @param appender The Appender this category has to log to. + **/ + inline void setAppender(Appender& appender) { + addAppender(appender); + }; + + /** + * Returns the first Appender for this Category, or NULL if no + * Appender has been set. + * @deprecated use getAppender(const std::string&) + * @returns The Appender. + **/ + virtual Appender* getAppender() const; + + /** + * Returns the specified Appender for this Category, or NULL if + * the Appender is not attached to this Category. + * @since 0.2.7 + * @returns The Appender. + **/ + virtual Appender* getAppender(const std::string& name) const; + + /** + * Returns the set of Appenders currently attached to this Catogory. + * @since 0.3.1 + * @returns The set of attached Appenders. + **/ + virtual AppenderSet getAllAppenders() const; + + /** + * Removes all appenders for this Category. + **/ + virtual void removeAllAppenders(); + + /** + * Removes specified appender for this Category. + * @since 0.2.7 + **/ + virtual void removeAppender(Appender* appender); + + /** + * Returns true if the Category owns the first Appender in its + * Appender set. In that case the Category destructor will delete + * the Appender. + * @deprecated use ownsAppender(Appender*) + **/ + virtual bool ownsAppender() const throw() { + return ownsAppender(getAppender()); + }; + + /** + * Returns true if the Category owns the Appender. In that case the + * Category destructor will delete the Appender. + * @since 0.2.7 + **/ + virtual bool ownsAppender(Appender* appender) const throw(); + + /** + * Call the appenders in the hierarchy starting at + * this. If no appenders could be found, emit a + * warning. + * + *

This method always calls all the appenders inherited form the + * hierracy circumventing any evaluation of whether to log or not to + * log the particular log request. + * + * @param event the LogginEvent to log. + **/ + virtual void callAppenders(const LoggingEvent& event) throw(); + + /** + * Set the additivity flag for this Category instance. + **/ + virtual void setAdditivity(bool additivity); + + /** + * Returns the additivity flag for this Category instance. + **/ + virtual bool getAdditivity() const throw(); + + /** + * Returns the parent category of this category, or NULL + * if the category is the root category. + * @return the parent category. + **/ + virtual Category* getParent() throw(); + + /** + * Returns the parent category of this category, or NULL + * if the category is the root category. + * @return the parent category. + **/ + virtual const Category* getParent() const throw(); + + /** + * Log a message with the specified priority. + * @param priority The priority of this log message. + * @param stringFormat Format specifier for the string to write + * in the log file. + * @param ... The arguments for stringFormat + **/ + virtual void log(Priority::Value priority, const char* stringFormat, + ...) throw(); + + /** + * Log a message with the specified priority. + * @param priority The priority of this log message. + * @param message string to write in the log file + **/ + virtual void log(Priority::Value priority, + const std::string& message) throw(); + + /** + * Log a message with the specified priority. + * @since 0.2.7 + * @param priority The priority of this log message. + * @param stringFormat Format specifier for the string to write + * in the log file. + * @param va The arguments for stringFormat. + **/ + virtual void logva(Priority::Value priority, + const char* stringFormat, + va_list va) throw(); + + /** + * Log a message with debug priority. + * @param stringFormat Format specifier for the string to write + * in the log file. + * @param ... The arguments for stringFormat + **/ + void debug(const char* stringFormat, ...) throw(); + + /** + * Log a message with debug priority. + * @param message string to write in the log file + **/ + void debug(const std::string& message) throw(); + + /** + * Return true if the Category will log messages with priority DEBUG. + * @returns Whether the Category will log. + **/ + inline bool isDebugEnabled() const throw() { + return isPriorityEnabled(Priority::DEBUG); + }; + + /** + * Return a CategoryStream with priority DEBUG. + * @returns The CategoryStream. + **/ + inline CategoryStream debugStream() { + return getStream(Priority::DEBUG); + } + + /** + * Log a message with info priority. + * @param stringFormat Format specifier for the string to write + * in the log file. + * @param ... The arguments for stringFormat + **/ + void info(const char* stringFormat, ...) throw(); + + /** + * Log a message with info priority. + * @param message string to write in the log file + **/ + void info(const std::string& message) throw(); + + /** + * Return true if the Category will log messages with priority INFO. + * @returns Whether the Category will log. + **/ + inline bool isInfoEnabled() const throw() { + return isPriorityEnabled(Priority::INFO); + }; + + /** + * Return a CategoryStream with priority INFO. + * @returns The CategoryStream. + **/ + inline CategoryStream infoStream() { + return getStream(Priority::INFO); + } + + /** + * Log a message with notice priority. + * @param stringFormat Format specifier for the string to write + * in the log file. + * @param ... The arguments for stringFormat + **/ + void notice(const char* stringFormat, ...) throw(); + + /** + * Log a message with notice priority. + * @param message string to write in the log file + **/ + void notice(const std::string& message) throw(); + + /** + * Return true if the Category will log messages with priority NOTICE. + * @returns Whether the Category will log. + **/ + inline bool isNoticeEnabled() const throw() { + return isPriorityEnabled(Priority::NOTICE); + }; + + /** + * Return a CategoryStream with priority NOTICE. + * @returns The CategoryStream. + **/ + inline CategoryStream noticeStream() { + return getStream(Priority::NOTICE); + } + + /** + * Log a message with warn priority. + * @param stringFormat Format specifier for the string to write + * in the log file. + * @param ... The arguments for stringFormat + **/ + void warn(const char* stringFormat, ...) throw(); + + /** + * Log a message with warn priority. + * @param message string to write in the log file + **/ + void warn(const std::string& message) throw(); + + /** + * Return true if the Category will log messages with priority WARN. + * @returns Whether the Category will log. + **/ + inline bool isWarnEnabled() const throw() { + return isPriorityEnabled(Priority::WARN); + }; + + /** + * Return a CategoryStream with priority WARN. + * @returns The CategoryStream. + **/ + inline CategoryStream warnStream() { + return getStream(Priority::WARN); + }; + + /** + * Log a message with error priority. + * @param stringFormat Format specifier for the string to write + * in the log file. + * @param ... The arguments for stringFormat + **/ + void error(const char* stringFormat, ...) throw(); + + /** + * Log a message with error priority. + * @param message string to write in the log file + **/ + void error(const std::string& message) throw(); + + /** + * Return true if the Category will log messages with priority ERROR. + * @returns Whether the Category will log. + **/ + inline bool isErrorEnabled() const throw() { + return isPriorityEnabled(Priority::ERROR); + }; + + /** + * Return a CategoryStream with priority ERROR. + * @returns The CategoryStream. + **/ + inline CategoryStream errorStream() { + return getStream(Priority::ERROR); + }; + + /** + * Log a message with crit priority. + * @param stringFormat Format specifier for the string to write + * in the log file. + * @param ... The arguments for stringFormat + **/ + void crit(const char* stringFormat, ...) throw(); + + /** + * Log a message with crit priority. + * @param message string to write in the log file + **/ + void crit(const std::string& message) throw(); + + /** + * Return true if the Category will log messages with priority CRIT. + * @returns Whether the Category will log. + **/ + inline bool isCritEnabled() const throw() { + return isPriorityEnabled(Priority::CRIT); + }; + + /** + * Return a CategoryStream with priority CRIT. + * @returns The CategoryStream. + **/ + inline CategoryStream critStream() { + return getStream(Priority::CRIT); + }; + + /** + * Log a message with alert priority. + * @param stringFormat Format specifier for the string to write + * in the log file. + * @param ... The arguments for stringFormat + **/ + void alert(const char* stringFormat, ...) throw(); + + /** + * Log a message with alert priority. + * @param message string to write in the log file + **/ + void alert(const std::string& message) throw(); + + /** + * Return true if the Category will log messages with priority ALERT. + * @returns Whether the Category will log. + **/ + inline bool isAlertEnabled() const throw() { + return isPriorityEnabled(Priority::ALERT); + }; + + /** + * Return a CategoryStream with priority ALERT. + * @returns The CategoryStream. + **/ + inline CategoryStream alertStream() throw() { + return getStream(Priority::ALERT); + }; + + /** + * Log a message with emerg priority. + * @param stringFormat Format specifier for the string to write + * in the log file. + * @param ... The arguments for stringFormat + **/ + void emerg(const char* stringFormat, ...) throw(); + + /** + * Log a message with emerg priority. + * @param message string to write in the log file + **/ + void emerg(const std::string& message) throw(); + + /** + * Return true if the Category will log messages with priority EMERG. + * @returns Whether the Category will log. + **/ + inline bool isEmergEnabled() const throw() { + return isPriorityEnabled(Priority::EMERG); + }; + + /** + * Return a CategoryStream with priority EMERG. + * @returns The CategoryStream. + **/ + inline CategoryStream emergStream() { + return getStream(Priority::EMERG); + }; + + /** + * Log a message with fatal priority. + * NB. priority 'fatal' is equivalent to 'emerg'. + * @since 0.2.7 + * @param stringFormat Format specifier for the string to write + * in the log file. + * @param ... The arguments for stringFormat + **/ + void fatal(const char* stringFormat, ...) throw(); + + /** + * Log a message with fatal priority. + * NB. priority 'fatal' is equivalent to 'emerg'. + * @since 0.2.7 + * @param message string to write in the log file + **/ + void fatal(const std::string& message) throw(); + + /** + * Return true if the Category will log messages with priority FATAL. + * NB. priority 'fatal' is equivalent to 'emerg'. + * @since 0.2.7 + * @returns Whether the Category will log. + **/ + inline bool isFatalEnabled() const throw() { + return isPriorityEnabled(Priority::FATAL); + }; + + /** + * Return a CategoryStream with priority FATAL. + * NB. priority 'fatal' is equivalent to 'emerg'. + * @since 0.2.7 + * @returns The CategoryStream. + **/ + inline CategoryStream fatalStream() { + return getStream(Priority::FATAL); + }; + + /** + * Return a CategoryStream with given Priority. + * @param priority The Priority of the CategoryStream. + * @returns The requested CategoryStream. + **/ + virtual CategoryStream getStream(Priority::Value priority); + + /** + * Return a CategoryStream with given Priority. + * @param priority The Priority of the CategoryStream. + * @returns The requested CategoryStream. + **/ + virtual CategoryStream operator<<(Priority::Value priority); + + protected: + + /** + * Constructor + * @param name the fully qualified name of this Category + * @param parent the parent of this parent, or NULL for the root + * Category + * @param priority the priority for this Category. Defaults to + * Priority::NOTSET + **/ + Category(const std::string& name, Category* parent, + Priority::Value priority = Priority::NOTSET); + + virtual void _logUnconditionally(Priority::Value priority, + const char* format, + va_list arguments) throw(); + + /** + * Unconditionally log a message with the specified priority. + * @param priority The priority of this log message. + * @param message string to write in the log file + **/ + virtual void _logUnconditionally2(Priority::Value priority, + const std::string& message) throw(); + + private: + + /* prevent copying and assignment */ + Category(const Category& other); + Category& operator=(const Category& other); + + /** The name of this category. */ + const std::string _name; + + /** + * The parent of this category. All categories have al least one + * ancestor which is the root category. + **/ + Category* _parent; + + /** + * The assigned priority of this category. + **/ + volatile Priority::Value _priority; + + typedef std::map OwnsAppenderMap; + + /** + * Returns the iterator to the Appender if the Category owns the + * Appender. In that case the Category destructor will delete the + * Appender. + **/ + + virtual bool ownsAppender(Appender* appender, + OwnsAppenderMap::iterator& i2) throw(); + + AppenderSet _appender; + mutable threading::Mutex _appenderSetMutex; + + /** + * Whether the category holds the ownership of the appender. If so, + * it deletes the appender in its destructor. + **/ + + OwnsAppenderMap _ownsAppender; + + /** + * Additivity is set to true by default, i.e. a child inherits its + * ancestor's appenders by default. + */ + volatile bool _isAdditive; + + }; + +} +#endif // _LOG4CPP_CATEGORY_HH diff --git a/VrUtils/log4cpp/include/log4cpp/CategoryStream.hh b/VrUtils/log4cpp/include/log4cpp/CategoryStream.hh new file mode 100644 index 0000000..8ae874e --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/CategoryStream.hh @@ -0,0 +1,146 @@ +/* + * CategoryStream.hh + * + * Copyright 2001, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2001, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_CATEGORYSTREAM_HH +#define _LOG4CPP_CATEGORYSTREAM_HH + +#include +#include +#include +#ifdef LOG4CPP_HAVE_SSTREAM +#include +#endif +#include + +namespace log4cpp { + + class LOG4CPP_EXPORT Category; + class LOG4CPP_EXPORT CategoryStream; + /** + * eol manipulator + **/ + LOG4CPP_EXPORT CategoryStream& eol (CategoryStream& os); + + /** + * left manipulator + **/ + LOG4CPP_EXPORT CategoryStream& left (CategoryStream& os); + + /** + * This class enables streaming simple types and objects to a category. + * Use category.errorStream(), etc. to obtain a CategoryStream class. + **/ + class LOG4CPP_EXPORT CategoryStream { + public: + + /** + * Construct a CategoryStream for given Category with given priority. + * @param category The category this stream will send log messages to. + * @param priority The priority the log messages will get or + * Priority::NOTSET to silently discard any streamed in messages. + **/ + CategoryStream(Category& category, Priority::Value priority); + + /** + * Destructor for CategoryStream + **/ + ~CategoryStream(); + + /** + * Returns the destination Category for this stream. + * @returns The Category. + **/ + inline Category& getCategory() const { return _category; }; + + /** + * Returns the priority for this stream. + * @returns The priority. + **/ + inline Priority::Value getPriority() const throw() { + return _priority; + }; + + /** + * Flush the contents of the stream buffer to the Category and + * empties the buffer. + **/ + void flush(); + + /** + * Stream in arbitrary types and objects. + * @param t The value or object to stream in. + * @returns A reference to itself. + **/ + template + CategoryStream& operator<<(const T& t) { + if (getPriority() != Priority::NOTSET) { + if (!_buffer) { + if (!(_buffer = new std::ostringstream)) { + // XXX help help help + } + } + (*_buffer) << t; + } + return *this; + } + + CategoryStream& operator<<(const char* t); + + template + CategoryStream& operator<<(const std::string& t) { + if (getPriority() != Priority::NOTSET) { + if (!_buffer) { + if (!(_buffer = new std::ostringstream)) { + // XXX help help help + } + } + (*_buffer) << t; + } + return *this; + } +#if LOG4CPP_HAS_WCHAR_T != 0 + template + CategoryStream& operator<<(const std::wstring& t) { + if (getPriority() != Priority::NOTSET) { + if (!_wbuffer) { + if (!(_wbuffer = new std::wostringstream)) { + // XXX help help help + } + } + (*_wbuffer) << t; + } + return *this; + } +#endif + /** + * Set the width output on CategoryStream + **/ + std::streamsize width(std::streamsize wide ); + + + private: + Category& _category; + Priority::Value _priority; + union { + std::ostringstream* _buffer; +#if LOG4CPP_HAS_WCHAR_T != 0 + std::wostringstream* _wbuffer; +#endif + }; + + public: + typedef CategoryStream& (*cspf) (CategoryStream&); + + CategoryStream& operator << (cspf); + LOG4CPP_EXPORT friend CategoryStream& eol (CategoryStream& os); + LOG4CPP_EXPORT friend CategoryStream& left (CategoryStream& os); + }; +} + +#endif // _LOG4CPP_CATEGORYSTREAM_HH diff --git a/VrUtils/log4cpp/include/log4cpp/Configurator.hh b/VrUtils/log4cpp/include/log4cpp/Configurator.hh new file mode 100644 index 0000000..eb3f72c --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/Configurator.hh @@ -0,0 +1,31 @@ +/* + * Configurator.hh + * + * Copyright 2001, Glen Scott. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ +#ifndef _LOG4CPP_CONFIGURATOR_HH +#define _LOG4CPP_CONFIGURATOR_HH + +#include +#include +#include +#include + +namespace log4cpp { + + /** + * Exception class for configuration. + */ + class LOG4CPP_EXPORT ConfigureFailure : public std::runtime_error { + public: + /** + * Constructor. + * @param reason String containing the description of the exception. + */ + ConfigureFailure(const std::string& reason); + }; +} + +#endif // _LOG4CPP_CONFIGURATOR_HH diff --git a/VrUtils/log4cpp/include/log4cpp/DailyRollingFileAppender.hh b/VrUtils/log4cpp/include/log4cpp/DailyRollingFileAppender.hh new file mode 100644 index 0000000..e11a4ee --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/DailyRollingFileAppender.hh @@ -0,0 +1,45 @@ +/* + * DailyRollingFileAppender.hh + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_DAILYROLLINGFILEAPPENDER_HH +#define _LOG4CPP_DAILYROLLINGFILEAPPENDER_HH + +#include +#include +#include +#include + +namespace log4cpp { + + /** + DailyRollingFileAppender is a FileAppender that rolls over the logfile once + the next day starts. + @since 1.1.2 + **/ + class LOG4CPP_EXPORT DailyRollingFileAppender : public FileAppender { + public: + DailyRollingFileAppender(const std::string& name, + const std::string& fileName, + unsigned int maxDaysToKeep = maxDaysToKeepDefault, + bool append = true, + mode_t mode = 00644); + + virtual void setMaxDaysToKeep(unsigned int maxDaysToKeep); + virtual unsigned int getMaxDaysToKeep() const; + + virtual void rollOver(); + + static unsigned int maxDaysToKeepDefault; + protected: + virtual void _append(const LoggingEvent& event); + + unsigned int _maxDaysToKeep; + // last log's file creation time (or last modification if appender just created) + struct tm _logsTime; + }; +} + +#endif // _LOG4CPP_DAILYROLLINGFILEAPPENDER_HH diff --git a/VrUtils/log4cpp/include/log4cpp/Export.hh b/VrUtils/log4cpp/include/log4cpp/Export.hh new file mode 100644 index 0000000..c7d1c1a --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/Export.hh @@ -0,0 +1,30 @@ +/* + * Copyright 2002, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2002, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_EXPORT_HH +#define _LOG4CPP_EXPORT_HH + +#ifdef LOG4CPP_HAS_DLL +# ifdef LOG4CPP_BUILD_DLL +# if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__MINGW32__) +# define LOG4CPP_EXPORT __declspec(dllexport) +# else +# define LOG4CPP_EXPORT +# endif +# else +# if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__MINGW32__) +# define LOG4CPP_EXPORT __declspec(dllimport) +# else +# define LOG4CPP_EXPORT +# endif +# endif +#else +# define LOG4CPP_EXPORT +#endif + +#endif // _LOG4CPP_EXPORT_HH + diff --git a/VrUtils/log4cpp/include/log4cpp/FactoryParams.hh b/VrUtils/log4cpp/include/log4cpp/FactoryParams.hh new file mode 100644 index 0000000..22212fe --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/FactoryParams.hh @@ -0,0 +1,158 @@ +/* + * Copyright 2002, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2002, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#if !defined(h_3e645482_ae6a_43e5_8f81_abbc4200212d) +#define h_3e645482_ae6a_43e5_8f81_abbc4200212d + +#include +#include +#include +#include +#include "Portability.hh" + +namespace log4cpp +{ + class FactoryParams; + namespace details + { + class base_validator_data + { + public: + base_validator_data(const char* tag, const FactoryParams* params) : tag_(tag), params_(params){} + + protected: + const char* tag_; + const FactoryParams* params_; + + template + void assign(const std::string& param_value, T& value) const + { + assign_impl(param_value, value); + } + + template + void assign_impl(const std::string& param_value, T& value) const + { + std::stringstream s; + s << param_value; + s >> value; + } + + void assign_impl(const std::string& param_value, std::string& value) const + { + value = param_value; + } + + void throw_error(const char* param_name) const + { + std::stringstream s; + s << "Property '" << param_name << "' required to configure " << tag_; + throw std::runtime_error(s.str()); + } + }; + + class parameter_validator; + } + + class LOG4CPP_EXPORT FactoryParams + { + typedef std::map storage_t; + + storage_t storage_; + + public: + typedef storage_t::const_iterator const_iterator; + + const std::string& operator[](const std::string& v) const; + std::string& operator[](const std::string& v) { return storage_[v]; } + details::parameter_validator get_for(const char* tag) const; + const_iterator find(const std::string& t) const; + const_iterator begin() const { return storage_.begin(); } + const_iterator end() const { return storage_.end(); } + }; + + namespace details + { + class optional_params_validator; + class required_params_validator : public base_validator_data + { + public: + required_params_validator(const char* tag, const FactoryParams* params) : base_validator_data(tag, params) {} + +#if defined(_MSC_VER) && _MSC_VER < 1300 + template + optional_params_validator optional(const char* param, T& value) const { optional_params_validator v(tag_, params_); v(param, value); return v; } +#else + template + optional_params_validator optional(const char* param, T& value) const; +#endif + + template + const required_params_validator& operator()(const char* param, T& value) const + { + FactoryParams::const_iterator i = params_->find(param); + if (i != params_->end()) + assign(i->second, value); + else + throw_error(param); + + return *this; + } + + }; + + class optional_params_validator : public base_validator_data + { + public: + optional_params_validator(const char* tag, const FactoryParams* params) : base_validator_data(tag, params) {} + + template + required_params_validator required(const char* param, T& value) const { required_params_validator v(tag_, params_); v(param, value); return v; } + + template + const optional_params_validator& operator()(const char* param, T& value) const + { + FactoryParams::const_iterator i = params_->find(param); + if (i != params_->end()) + assign(i->second, value); + + return *this; + + } + }; + + class parameter_validator : public base_validator_data + { + public: + parameter_validator(const char* tag, const FactoryParams* params) : base_validator_data(tag, params) {} + + template + required_params_validator required(const char* param, T& value) const { required_params_validator v(tag_, params_); v(param, value); return v; } + + template + optional_params_validator optional(const char* param, T& value) const { optional_params_validator v(tag_, params_); v(param, value); return v; } + }; + +#if !(defined(_MSC_VER) && _MSC_VER < 1300) + template + optional_params_validator + required_params_validator::optional(const char* param, T& value) const + { + optional_params_validator v(tag_, params_); + v(param, value); + return v; + } +#endif + } + + inline details::parameter_validator FactoryParams::get_for(const char* tag) const + { + return details::parameter_validator(tag, this); + } +} + +#endif // h_3e645482_ae6a_43e5_8f81_abbc4200212d diff --git a/VrUtils/log4cpp/include/log4cpp/FileAppender.hh b/VrUtils/log4cpp/include/log4cpp/FileAppender.hh new file mode 100644 index 0000000..7940777 --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/FileAppender.hh @@ -0,0 +1,92 @@ +/* + * FileAppender.hh + * + * Copyright 2000, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2000, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_FILEAPPENDER_HH +#define _LOG4CPP_FILEAPPENDER_HH + +#include +#include +#include +#include + +namespace log4cpp { + + class LOG4CPP_EXPORT FileAppender : public LayoutAppender { + public: + + /** + Constructs a FileAppender. + @param name the name of the Appender. + @param fileName the name of the file to which the Appender has + to log. + @param append whether the Appender has to truncate the file or + just append to it if it already exists. Defaults to 'true'. + @param mode file mode to open the logfile with. Defaults to 00644. + **/ + FileAppender(const std::string& name, const std::string& fileName, + bool append = true, mode_t mode = 00644); + + /** + Constructs a FileAppender to an already open file descriptor. + @param name the name of the Appender. + @param fd the file descriptor to which the Appender has to log. + **/ + FileAppender(const std::string& name, int fd); + virtual ~FileAppender(); + + /** + Reopens the logfile. + This can be useful for logfiles that are rotated externally, + e.g. by logrotate. This method is a NOOP for FileAppenders that + have been constructed with a file descriptor. + @returns true if the reopen succeeded. + **/ + virtual bool reopen(); + + /** + Closes the logfile. + **/ + virtual void close(); + + /** + Sets the append vs truncate flag. + NB. currently the FileAppender opens the logfile in the + constructor. Therefore this method is too late to influence the + first file opening. We'll need something similar to log4j's + activateOptions(). + @param append false to truncate, true to append + **/ + virtual void setAppend(bool append); + + /** + Gets the value of the 'append' option. + **/ + virtual bool getAppend() const; + + /** + Sets the file open mode. + **/ + virtual void setMode(mode_t mode); + + /** + Gets the file open mode. + **/ + virtual mode_t getMode() const; + + protected: + virtual void _append(const LoggingEvent& event); + + const std::string _fileName; + int _fd; + int _flags; + mode_t _mode; + }; +} + +#endif // _LOG4CPP_FILEAPPENDER_HH diff --git a/VrUtils/log4cpp/include/log4cpp/Filter.hh b/VrUtils/log4cpp/include/log4cpp/Filter.hh new file mode 100644 index 0000000..3033056 --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/Filter.hh @@ -0,0 +1,119 @@ +/* + * Filter.hh + * + * Copyright 2001, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2001, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_FILTER_HH +#define _LOG4CPP_FILTER_HH + +#include +#include + +namespace log4cpp { + + /** + Users should extend this class to implement customized logging + event filtering. Note that log4cpp::Category and + lof4cpp::Appender have built-in filtering rules. It is suggested + that you first use and understand the built-in rules before rushing + to write your own custom filters. + +

This abstract class assumes and also imposes that filters be + organized in a linear chain. The decide(LoggingEvent) + method of each filter is called sequentially, in the order of their + addition to the chain. + +

The decide(LoggingEvent) method must return a + Decision value, either DENY, NEUTRAL or ACCCEPT. + +

If the value DENY is returned, then the log event is + dropped immediately without consulting with the remaining + filters. + +

If the value NEUTRAL is returned, then the next filter + in the chain is consulted. If there are no more filters in the + chain, then the log event is logged. Thus, in the presence of no + filters, the default behaviour is to log all logging events. + +

If the value ACCEPT is returned, then the log + event is logged without consulting the remaining filters. + +

The philosophy of log4cpp filters is largely inspired from the + Linux ipchains. + **/ + + class LOG4CPP_EXPORT Filter { + public: + + typedef enum { DENY = -1, + NEUTRAL = 0, + ACCEPT = 1 + } Decision; + + /** + * Default Constructor for Filter + **/ + Filter(); + + /** + * Destructor for Filter + **/ + virtual ~Filter(); + + /** + * Set the next Filter in the Filter chain + * @param filter The filter to chain + **/ + virtual void setChainedFilter(Filter* filter); + + /** + * Get the next Filter in the Filter chain + * @return The next Filter or NULL if the current filter is the last + * in the chain + **/ + virtual Filter* getChainedFilter(); + + /** + * Get the last Filter in the Filter chain + * @return The last Filter in the Filter chain + **/ + virtual Filter* getEndOfChain(); + + /** + * Add a Filter to the end of the Filter chain. Convience method for + * getEndOfChain()->setChainedFilter(filter). + * @param filter The filter to add to the end of the chain. + **/ + virtual void appendChainedFilter(Filter* filter); + + /** + * Decide whether to accept or deny a LoggingEvent. This method will + * walk the entire chain until a non neutral decision has been made + * or the end of the chain has been reached. + * @param event The LoggingEvent to decide on. + * @return The Decision + **/ + virtual Decision decide(const LoggingEvent& event); + + protected: + /** + * Decide whether this Filter accepts or denies the given + * LoggingEvent. Actual implementation of Filter should override this + * method and not decide(LoggingEvent&). + * @param event The LoggingEvent to decide on. + * @return The Decision + **/ + virtual Decision _decide(const LoggingEvent& event) = 0; + + private: + Filter* _chainedFilter; + + }; + +} + +#endif // _LOG4CPP_FILTER_HH diff --git a/VrUtils/log4cpp/include/log4cpp/FixedContextCategory.hh b/VrUtils/log4cpp/include/log4cpp/FixedContextCategory.hh new file mode 100644 index 0000000..4954671 --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/FixedContextCategory.hh @@ -0,0 +1,174 @@ +/* + * FixedContextCategory.hh + * + * Copyright 2001, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2001, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_FIXEDCONTEXTCATEGORY_HH +#define _LOG4CPP_FIXEDCONTEXTCATEGORY_HH + +#include +#include + +namespace log4cpp { + + /** + * This Category subclass replaces the NDC field in LoggingEvents with + * a fixed context string. All handling of Appenders, etc. is delgated + * to the 'normal' Category with the same name. Its intended use is + * for object instances that serve a single client: they contruct a + * FixedContextCategory with the client identifier as context. + * Unlike with regular Category instances one has to explicitly create + * FixedContextCategory instances using the constructor. This also + * implies one has to take cake of destruction of the instance as well. + * @since 0.2.4 + **/ + class LOG4CPP_EXPORT FixedContextCategory : public Category { + + public: + + /** + * Constructor + * @param name the fully qualified name of this Categories delegate + * Category. + * @param context the context to fill the NDC field of LoggingEvents + * with. + **/ + FixedContextCategory(const std::string& name, + const std::string& context = ""); + + + /** + * Destructor for Category. + **/ + virtual ~FixedContextCategory(); + + /** + * Set the context string used as NDC. + * @param context the context string + **/ + virtual void setContext(const std::string& context); + + /** + * Return the context string used as NDC. + * @return the context string. + **/ + virtual std::string getContext() const; + + /** + * Returns the assigned Priority, if any, for this Category. + * @return Priority - the assigned Priority, can be Priority::NOTSET + **/ + virtual Priority::Value getPriority() const throw(); + + /** + * Starting from this Category, search the category hierarchy for a + * set priority and return it. Otherwise, return the priority + * of the root category. + * + *

The Category class is designed so that this method executes as + * quickly as possible. + **/ + virtual Priority::Value getChainedPriority() const throw(); + + /** + * For the moment this method does nothing. + **/ + virtual void addAppender(Appender* appender) throw(); + + /** + * For the moment this method does nothing. + **/ + virtual void addAppender(Appender& appender); + + /** + * Returns the Appender for this Category, or NULL if no Appender has + * been set. + * @returns The Appender. + **/ + virtual Appender* getAppender() const; + + /** + * Returns the specified Appender for this Category, or NULL if + * the Appender is not attached to this Category. + * @since 0.2.7 + * @returns The Appender. + **/ + virtual Appender* getAppender(const std::string& name) const; + + /** + * Returns the set of Appenders currently attached to this Catogory. + * @since 0.3.1 + * @returns The set of attached Appenders. + **/ + virtual AppenderSet getAllAppenders() const; + + /** + * Removes all appenders set for this Category. Currently a Category + * can have only one appender, but this may change in the future. + **/ + virtual void removeAllAppenders(); + + /** + * FixedContextAppenders cannot own Appenders. + * @returns false + **/ + virtual bool ownsAppender() const throw(); + + /** + * FixedContextAppenders cannot own Appenders. + * @returns false + **/ + virtual bool ownsAppender(Appender* appender) + const throw(); + + /** + * Call the appenders in the hierarchy starting at + * this. If no appenders could be found, emit a + * warning. + * + *

This method always calls all the appenders inherited form the + * hierracy circumventing any evaluation of whether to log or not to + * log the particular log request. + * + * @param event The LoggingEvent to log. + **/ + virtual void callAppenders(const LoggingEvent& event) throw(); + + /** + * Set the additivity flag for this Category instance. + **/ + virtual void setAdditivity(bool additivity); + + /** + * Returns the additivity flag for this Category instance. + **/ + virtual bool getAdditivity() const throw(); + + protected: + + /** + * Unconditionally log a message with the specified priority. + * @param priority The priority of this log message. + * @param message string to write in the log file + **/ + virtual void _logUnconditionally2(Priority::Value priority, + const std::string& message) throw(); + + private: + + /** + * The delegate category of this FixedContextCategory. + **/ + Category& _delegate; + + /** The context of this FixedContextCategory. */ + std::string _context; + + }; + +} +#endif // _LOG4CPP_FIXEDCONTEXTCATEGORY_HH diff --git a/VrUtils/log4cpp/include/log4cpp/HierarchyMaintainer.hh b/VrUtils/log4cpp/include/log4cpp/HierarchyMaintainer.hh new file mode 100644 index 0000000..c7dc57b --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/HierarchyMaintainer.hh @@ -0,0 +1,59 @@ +/* + * HierarchyMaintainer.hh + * + * Copyright 2000, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2000, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_HIERARCHYMAINTAINER_HH +#define _LOG4CPP_HIERARCHYMAINTAINER_HH + +#include +#include +#include +#include +#include +#include + +namespace log4cpp { + + /** + * HierarchyMaintainer is an internal log4cpp class. It is responsible + * for maintaining the hierarchy of Categories. Applications should + * not have to use this class directly. + **/ + class HierarchyMaintainer { + friend class Log4cppCleanup; + + public: + typedef std::map CategoryMap; + typedef void (*shutdown_fun_ptr)(); + + static HierarchyMaintainer& getDefaultMaintainer(); + + HierarchyMaintainer(); + virtual ~HierarchyMaintainer(); + virtual Category* getExistingInstance(const std::string& name); + virtual Category& getInstance(const std::string& name); + virtual std::vector* getCurrentCategories() const; + virtual void shutdown(); + void register_shutdown_handler(shutdown_fun_ptr handler); + virtual void deleteAllCategories(); + + protected: + virtual Category* _getExistingInstance(const std::string& name); + virtual Category& _getInstance(const std::string& name); + CategoryMap _categoryMap; + mutable threading::Mutex _categoryMutex; + + private: + typedef std::vector handlers_t; + + static HierarchyMaintainer* _defaultMaintainer; + handlers_t handlers_; + }; +} + +#endif // _LOG4CPP_HIERARCHYMAINTAINER_HH diff --git a/VrUtils/log4cpp/include/log4cpp/IdsaAppender.hh b/VrUtils/log4cpp/include/log4cpp/IdsaAppender.hh new file mode 100644 index 0000000..47f1fb6 --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/IdsaAppender.hh @@ -0,0 +1,76 @@ +/* + * IdsaAppender.hh + * + * Copyright 2000, Marc Welz + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_IDSAAPPENDER_HH +#define _LOG4CPP_IDSAAPPENDER_HH + +#include +#include +#include +#include +#include + +namespace log4cpp { + + /** + * IdsaAppender is an Appender that sends LoggingEvents to the IDS/A + * logger and reference monitor by Marc Welz. + * See http://jade.cs.uct.ac.za/idsa/ for more information on IDS/A. + **/ + class IdsaAppender : public AppenderSkeleton { + public: + + /** + * Instantiate an IdsaAppender with given name and name. + * Unlike the syslog API, idsa allows multiple connections. + * @param name The name of the Appender + * @param idsaName The service parameter of idsa + **/ + IdsaAppender(const std::string& name, const std::string& idsaName); + virtual ~IdsaAppender(); + + /** + * Calls idsa_open() and idsa_close() + **/ + virtual bool reopen(); + + /** + * Calls idsa_close() + **/ + virtual void close(); + + /** + * The IdsaAppender does its own Layout. + * @returns false + **/ + virtual bool requiresLayout() const; + + virtual void setLayout(Layout* layout); + + protected: + + /** + * Calls idsa_open(). + **/ + virtual void open(); + + /** + * Sends a LoggingEvent to idsa. + * @param event the LoggingEvent to log. + **/ + virtual void _append(const LoggingEvent& event); + + const std::string _idsaName; + + IDSA_CONNECTION *_idsaConnection; + + }; +} + +#endif // _LOG4CPP_IDSAAPPENDER_HH + diff --git a/VrUtils/log4cpp/include/log4cpp/Layout.hh b/VrUtils/log4cpp/include/log4cpp/Layout.hh new file mode 100644 index 0000000..a95c91b --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/Layout.hh @@ -0,0 +1,39 @@ +/* + * Layout.hh + * + * Copyright 2000, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2000, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_LAYOUT_HH +#define _LOG4CPP_LAYOUT_HH + +#include +#include +#include + +namespace log4cpp { + +/** + * Extend this abstract class to create your own log layout format. + **/ + class LOG4CPP_EXPORT Layout { + public: + /** + * Destructor for Layout. + **/ + virtual ~Layout() { }; + + /** + * Formats the LoggingEvent data to a string that appenders can log. + * Implement this method to create your own layout format. + * @param event The LoggingEvent. + * @returns an appendable string. + **/ + virtual std::string format(const LoggingEvent& event) = 0; + }; +} + +#endif // _LOG4CPP_LAYOUT_HH diff --git a/VrUtils/log4cpp/include/log4cpp/LayoutAppender.hh b/VrUtils/log4cpp/include/log4cpp/LayoutAppender.hh new file mode 100644 index 0000000..640094c --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/LayoutAppender.hh @@ -0,0 +1,55 @@ +/* + * LayoutAppender.hh + * + * Copyright 2000, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2000, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_LAYOUTAPPENDER_HH +#define _LOG4CPP_LAYOUTAPPENDER_HH + +#include +#include +#include +#include + +namespace log4cpp { + + /** + * LayoutAppender is a common superclass for all Appenders that require + * a Layout. + **/ + class LOG4CPP_EXPORT LayoutAppender : public AppenderSkeleton { + public: + + typedef BasicLayout DefaultLayoutType; + + LayoutAppender(const std::string& name); + virtual ~LayoutAppender(); + + /** + * Check if the appender requires a layout. All LayoutAppenders do, + * therefore this method returns true for all subclasses. + * + * @returns true. + **/ + virtual bool requiresLayout() const; + virtual void setLayout(Layout* layout = NULL); + + protected: + /** + * Return the layout of the appender. + * This method is the Layout accessor for subclasses of LayoutAppender. + * @returns the Layout. + **/ + Layout& _getLayout(); + + private: + Layout* _layout; + }; +} + +#endif // _LOG4CPP_LAYOUTAPPENDER_HH + diff --git a/VrUtils/log4cpp/include/log4cpp/LayoutsFactory.hh b/VrUtils/log4cpp/include/log4cpp/LayoutsFactory.hh new file mode 100644 index 0000000..f25680c --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/LayoutsFactory.hh @@ -0,0 +1,42 @@ +/* + * Copyright 2002, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2002, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#if !defined(h_409ac787_0acf_47ff_ac15_3e9024d40315) +#define h_409ac787_0acf_47ff_ac15_3e9024d40315 + +#include +#include +#include +#include "Portability.hh" +#include "Layout.hh" +#include "FactoryParams.hh" + +namespace log4cpp +{ + class LOG4CPP_EXPORT LayoutsFactory + { + public: + typedef FactoryParams params_t; + typedef std::auto_ptr (*create_function_t)(const params_t& params); + + static LayoutsFactory& getInstance(); + void registerCreator(const std::string& class_name, create_function_t create_function); + std::auto_ptr create(const std::string& class_name, const params_t& params); + bool registed(const std::string& class_name) const; + + private: + LayoutsFactory(){}; + + typedef std::map creators_t; + typedef creators_t::const_iterator const_iterator; + + creators_t creators_; + }; +} + + +#endif // h_409ac787_0acf_47ff_ac15_3e9024d40315 diff --git a/VrUtils/log4cpp/include/log4cpp/LevelEvaluator.hh b/VrUtils/log4cpp/include/log4cpp/LevelEvaluator.hh new file mode 100644 index 0000000..b5ab624 --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/LevelEvaluator.hh @@ -0,0 +1,26 @@ +/* + * Copyright 2002, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2002, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#if !defined(h_3491ecd0_3891_4902_b3ba_15b15d98ae49) +#define h_3491ecd0_3891_4902_b3ba_15b15d98ae49 + +#include + +namespace log4cpp +{ + class LOG4CPP_EXPORT LevelEvaluator : public TriggeringEventEvaluator + { + public: + LevelEvaluator(Priority::Value level) : level_(level) {} + virtual bool eval(const LoggingEvent& event) const { return event.priority <= level_; } + + private: + Priority::Value level_; + }; +} + +#endif // h_3491ecd0_3891_4902_b3ba_15b15d98ae49 diff --git a/VrUtils/log4cpp/include/log4cpp/LoggingEvent.hh b/VrUtils/log4cpp/include/log4cpp/LoggingEvent.hh new file mode 100644 index 0000000..47cbd07 --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/LoggingEvent.hh @@ -0,0 +1,73 @@ +/* + * LoggingEvent.hh + * + * Copyright 2000, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2000, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_LOGGINGEVENT_HH +#define _LOG4CPP_LOGGINGEVENT_HH + +#include +#include + +#include +#include + +/** + * The top level namespace for all 'Log for C++' types and classes. + **/ +namespace log4cpp { + + /** + * The internal representation of logging events. When a affirmative + * logging decision is made a LoggingEvent instance is + * created. This instance is passed around the different log4cpp + * components. + * + *

This class is of concern to those wishing to extend log4cpp. + **/ + struct LOG4CPP_EXPORT LoggingEvent { + public: + /** + * Instantiate a LoggingEvent from the supplied parameters. + * + *

Except timeStamp all the other fields of + * LoggingEvent are filled when actually needed. + *

+ * @param category The category of this event. + * @param message The message of this event. + * @param ndc The nested diagnostic context of this event. + * @param priority The priority of this event. + **/ + LoggingEvent(const std::string& category, const std::string& message, + const std::string& ndc, Priority::Value priority); + + + /** The category name. */ + const std::string categoryName; + + /** The application supplied message of logging event. */ + const std::string message; + + /** The nested diagnostic context (NDC) of logging event. */ + const std::string ndc; + + /** Priority of logging event. */ + Priority::Value priority; + + /** The name of thread in which this logging event was generated, + e.g. the PID. + */ + const std::string threadName; + + /** The number of seconds elapsed since the epoch + (1/1/1970 00:00:00 UTC) until logging event was created. */ + TimeStamp timeStamp; + }; +} + +#endif // _LOG4CPP_LOGGINGEVENT_HH + diff --git a/VrUtils/log4cpp/include/log4cpp/Manipulator.hh b/VrUtils/log4cpp/include/log4cpp/Manipulator.hh new file mode 100644 index 0000000..5c024ad --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/Manipulator.hh @@ -0,0 +1,30 @@ +/* + * Manipulator.hh + * + * Copyright 2005, Francis ANDRE. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_MANIPULATOR_HH +#define _LOG4CPP_MANIPULATOR_HH + +#include +#include +namespace log4cpp { + class LOG4CPP_EXPORT width { + private: + unsigned int size; + public: +inline width(unsigned int i) : size(i) {} +friend LOG4CPP_EXPORT std::ostream& operator<< (std::ostream& os, const width& w); + }; +class LOG4CPP_EXPORT tab { + private: + unsigned int size; + public: +inline tab(unsigned int i) : size(i) {} +friend LOG4CPP_EXPORT std::ostream& operator<< (std::ostream& os, const tab& w); + }; +} +#endif diff --git a/VrUtils/log4cpp/include/log4cpp/NDC.hh b/VrUtils/log4cpp/include/log4cpp/NDC.hh new file mode 100644 index 0000000..fe58505 --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/NDC.hh @@ -0,0 +1,181 @@ +/* + * NDC.hh + * + * Copyright 2000, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2000, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_NDC_HH +#define _LOG4CPP_NDC_HH + +#include +#include +#include + +namespace log4cpp { + /** + The NDC class implements nested diagnostic contexts as + defined by Neil Harrison in the article "Patterns for Logging + Diagnostic Messages" part of the book "Pattern Languages of + Program Design 3" edited by Martin et al. + +

A Nested Diagnostic Context, or NDC in short, is an instrument + to distinguish interleaved log output from different sources. Log + output is typically interleaved when a server handles multiple + clients near-simulatanously. + +

Interleaved log output can still be meaningful if each log entry + from different contexts had a distinctive stamp. This is where NDCs + come into play. + +

Note that NDCs are managed on a per thread + basis. NDC operations such as push, + pop, clear, getDepth and + setMaxDepth affect the NDC of the current thread only. + NDCs of other threads remain unaffected. + +

To build an NDC one uses the push operation. + Simply put, + +

    +
  • Contexts can be nested. + +

  • When entering a context, call NDC.push. As a + side effect, if there is no nested diagnostic context for the + current thread, this method will create it. + +

  • When leaving a context, call NDC.pop. +
+ +

There is no penalty for forgetting to match each + push operation with a corresponding pop, + except the obvious mismatch between the real application context + and the context set in the NDC. + +

Custom Layouts may include the nested diagnostic context for the + current thread in log messages, without any user intervention. + Hence, even if a server is serving multiple clients + simultaneously, the logs emanating from the same code (belonging to + the same category) can still be distinguished because each client + request will have a different NDC tag. + +

Unfortunately, unlike Java, C++ does not have platform + independent multithreading support. Therefore, currently log4cpp is + not multithread aware, it implicitly assumes only one thread exists, + the main process thread. + **/ + class LOG4CPP_EXPORT NDC { + /** + Whether NDC feature is ever used by the user. If it is not used then saves some time by skipping instructions + from: ver.1.1 + **/ + static bool isUsedNDC; + static const std::string emptyString; + public: + + struct DiagnosticContext { + DiagnosticContext(const std::string& message); + DiagnosticContext(const std::string& message, + const DiagnosticContext& parent); + + std::string message; + std::string fullMessage; + }; + + typedef std::vector ContextStack; + + /** + Clear any nested disgnostic information if any. This method is + useful in cases where the same thread can be potentially used + over and over in different unrelated contexts. + +

This method is equivalent to calling the setMaxDepth + method with a zero maxDepth argument. + **/ + static void clear(); + + /** + Clone the diagnostic context for the current thread. + +

Internally a diagnostic context is represented as a stack. A + given thread can supply the stack (i.e. diagnostic context) to a + child thread so that the child can inherit the parent thread's + diagnostic context. + +

The child thread uses the inherit method to + inherit the parent's diagnostic context. + + @return Stack A clone of the current thread's diagnostic context. + **/ + static ContextStack* cloneStack(); + + /** + Get the current diagnostic context string. + @return the context string. + **/ + static const std::string& get(); + + /** + Get the current nesting depth of this diagnostic context. + @return the nesting depth + **/ + static size_t getDepth(); + + static void inherit(ContextStack* stack); + + /** + Clients should call this method before leaving a diagnostic + context. + +

The returned value is the value that was pushed last. If no + context is available, then the empty string "" is returned. + + @return String The innermost diagnostic context. + **/ + + static std::string pop(); + + /** + Push new diagnostic context information for the current thread. + +

The contents of the message parameter is + determined solely by the client. + + @param message The new diagnostic context information. + **/ + static void push(const std::string& message); + + /** + Set the maximum nesting depth for the current NDC. Curently NDCs + do not enforce a maximum depth and consequentially this method + has no effect. + @param maxDepth the maximum nesting depth + **/ + static void setMaxDepth(int maxDepth); + + /** + Return the NDC for the current thread. + @return the NDC for the current thread + **/ + static NDC& getNDC(); + + NDC(); + virtual ~NDC(); + + public: + virtual void _clear(); + virtual ContextStack* _cloneStack(); + virtual const std::string& _get() const; + virtual size_t _getDepth() const; + virtual void _inherit(ContextStack* stack); + virtual std::string _pop(); + virtual void _push(const std::string& message); + virtual void _setMaxDepth(int maxDepth); + + ContextStack _stack; + }; +} + +#endif // _LOG4CPP_NDC_HH diff --git a/VrUtils/log4cpp/include/log4cpp/NTEventLogAppender.hh b/VrUtils/log4cpp/include/log4cpp/NTEventLogAppender.hh new file mode 100644 index 0000000..c2d6d39 --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/NTEventLogAppender.hh @@ -0,0 +1,105 @@ +/* + * NTEventLogAppender.hh + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_NTEVENTLOGAPPENDER_HH +#define _LOG4CPP_NTEVENTLOGAPPENDER_HH + +#ifdef WIN32 // only available on Win32 + +// deal with ERROR #define +// N.B. This #includes windows.h with NOGDI and WIN32_LEAN_AND_MEAN #defined. +// If this is not what the user wants, #include windows.h before this file. +#ifndef _WINDOWS_ +# ifndef NOGDI +# define NOGDI // this will circumvent the ERROR #define in windows.h +# define LOG4CPP_UNDEFINE_NOGDI +# endif + +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# define LOG4CPP_UNDEFINE_WIN32_LEAN_AND_MEAN +# endif + +# include + +# ifdef LOG4CPP_UNDEFINE_NOGDI +# undef NOGDI +# endif + +# ifdef LOG4CPP_UNDEFINE_WIN32_LEAN_AND_MEAN +# undef WIN32_LEAN_AND_MEAN +# endif + +#endif // done dealing with ERROR #define + +#include +#include + +namespace log4cpp { + + /** + * NTEventLogAppender is an Appender that sends LoggingEvents to the + * Windows event log. + * Building log4cpp.dsp/log4cppDLL.dsp creates the resource DLL NTEventLogAppender.dll. + * Do not forget to place this DLL in a directory that is on the PATH + * of the Windows system. Otherwise, the category and message will not display + * correctly in Event Viewer.
+ * NB: This class is only available on Win32 platforms. + **/ + class LOG4CPP_EXPORT NTEventLogAppender : public AppenderSkeleton { + public: + + /** + * Instantiate an NTEventLogAppender with given name and source. + * @param name The name of the Appender + * @param sourceName The source name to log + **/ + NTEventLogAppender(const std::string& name, const std::string& sourceName); + virtual ~NTEventLogAppender(); + + /** + * Calls open() and close() + **/ + virtual bool reopen(); + + virtual void close(); + + /** + * The NTEventLogAppender does its own Layout. + * @returns false + **/ + virtual bool requiresLayout() const; + + virtual void setLayout(Layout* layout); + + protected: + + WORD getCategory(Priority::Value priority); + WORD getType(Priority::Value priority); + HKEY regGetKey(TCHAR *subkey, DWORD *disposition); + void regSetString(HKEY hkey, TCHAR *name, TCHAR *value); + void regSetDword(HKEY hkey, TCHAR *name, DWORD value); + void addRegistryInfo(const char *source); + + virtual void open(); + + /** + * Sends a LoggingEvent to NT Event log. + * @param event the LoggingEvent to log. + **/ + virtual void _append(const LoggingEvent& event); + + HANDLE _hEventSource; + std::string _strSourceName; + }; +} + +#else // WIN32 +#error NTEventLoggAppender is not available on on Win32 platforms +#endif // WIN32 + +#endif // _LOG4CPP_NTEVENTLOGAPPENDER_HH + diff --git a/VrUtils/log4cpp/include/log4cpp/OstreamAppender.hh b/VrUtils/log4cpp/include/log4cpp/OstreamAppender.hh new file mode 100644 index 0000000..027013a --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/OstreamAppender.hh @@ -0,0 +1,38 @@ +/* + * OstreamAppender.hh + * + * Copyright 2000, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2000, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_OSTREAMAPPENDER_HH +#define _LOG4CPP_OSTREAMAPPENDER_HH + +#include +#include +#include +#include + +namespace log4cpp { + + /** + * OstreamAppender appends LoggingEvents to ostreams. + **/ + class LOG4CPP_EXPORT OstreamAppender : public LayoutAppender { + public: + OstreamAppender(const std::string& name, std::ostream* stream); + virtual ~OstreamAppender(); + + virtual bool reopen(); + virtual void close(); + + protected: + virtual void _append(const LoggingEvent& event); + + std::ostream* _stream; + }; +} + +#endif // _LOG4CPP_OSTREAMAPPENDER_HH diff --git a/VrUtils/log4cpp/include/log4cpp/PassThroughLayout.hh b/VrUtils/log4cpp/include/log4cpp/PassThroughLayout.hh new file mode 100644 index 0000000..12cb48a --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/PassThroughLayout.hh @@ -0,0 +1,22 @@ +/* + * Copyright 2002, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2002, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#if !defined(h_8e4861a3_f607_479c_ac2d_0b2d81b4c36c) +#define h_8e4861a3_f607_479c_ac2d_0b2d81b4c36c + +#include + +namespace log4cpp +{ + class PassThroughLayout : public Layout + { + public: + virtual std::string format(const LoggingEvent& event) { return event.message; } + }; +} + +#endif // h_8e4861a3_f607_479c_ac2d_0b2d81b4c36c diff --git a/VrUtils/log4cpp/include/log4cpp/PatternLayout.hh b/VrUtils/log4cpp/include/log4cpp/PatternLayout.hh new file mode 100644 index 0000000..12eea64 --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/PatternLayout.hh @@ -0,0 +1,104 @@ +/* + * PatternLayout.hh + * + * Copyright 2002, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_PATTERNLAYOUT_HH +#define _LOG4CPP_PATTERNLAYOUT_HH + +#include +#include +#include +#include +#ifdef LOG4CPP_HAVE_SSTREAM +#include +#endif + +namespace log4cpp { + + /** + * PatternLayout is a simple fixed format Layout implementation. + **/ + class LOG4CPP_EXPORT PatternLayout : public Layout { + public: + /** + The default conversion pattern + **/ + static const char* DEFAULT_CONVERSION_PATTERN; + + /** + A conversion pattern equivalent to the SimpleLayout. + **/ + static const char* SIMPLE_CONVERSION_PATTERN; + + /** + A conversion pattern equivalent to the BasicLayout. + **/ + static const char* BASIC_CONVERSION_PATTERN; + + /** + A conversion pattern equivalent to the TTCCLayout. + Note: TTCCLayout is in log4j but not log4cpp. + **/ + static const char* TTCC_CONVERSION_PATTERN; + + PatternLayout(); + virtual ~PatternLayout(); + + // NOTE: All double percentage signs ('%%') followed by a character + // in the following comments should actually be a single char. + // The doubles are included so that doxygen will print them correctly. + /** + * Formats the LoggingEvent in the style set by + * the setConversionPattern call. By default, set + * to "%%m%%n" + **/ + virtual std::string format(const LoggingEvent& event); + + /** + * Sets the format of log lines handled by this + * PatternLayout. By default, set to "%%m%%n".
+ * Format characters are as follows:
+ *

  • %% - a single percent sign
  • + *
  • %%c - the category
  • + *
  • %%d - the date\n + * Date format: The date format character may be followed by a date format + * specifier enclosed between braces. For example, %%d{%%H:%%M:%%S,%%l} or %%d{%%d %%m %%Y %%H:%%M:%%S,%%l}. + * If no date format specifier is given then the following format is used: + * "Wed Jan 02 02:03:55 1980". The date format specifier admits the same syntax + * as the ANSI C function strftime, with 1 addition. The addition is the specifier + * %%l for milliseconds, padded with zeros to make 3 digits.
  • + *
  • %%m - the message
  • + *
  • %%n - the platform specific line separator
  • + *
  • %%p - the priority
  • + *
  • %%r - milliseconds since this layout was created.
  • + *
  • %%R - seconds since Jan 1, 1970
  • + *
  • %%u - clock ticks since process start
  • + *
  • %%x - the NDC
  • + * @param conversionPattern the conversion pattern + * @exception ConfigureFailure if the pattern is invalid + **/ + virtual void setConversionPattern(const std::string& conversionPattern); + + virtual std::string getConversionPattern() const; + + virtual void clearConversionPattern(); + + class LOG4CPP_EXPORT PatternComponent { + public: + inline virtual ~PatternComponent() {}; + virtual void append(std::ostringstream& out, const LoggingEvent& event) = 0; + }; + + private: + typedef std::vector ComponentVector; + ComponentVector _components; + + std::string _conversionPattern; + }; +} + +#endif // _LOG4CPP_PATTERNLAYOUT_HH diff --git a/VrUtils/log4cpp/include/log4cpp/Portability.hh b/VrUtils/log4cpp/include/log4cpp/Portability.hh new file mode 100644 index 0000000..ef32f0e --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/Portability.hh @@ -0,0 +1,69 @@ +/* + * Portability.hh + * + * Copyright 2001, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2001, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_PORTABILITY_HH +#define _LOG4CPP_PORTABILITY_HH + +#if defined (_MSC_VER) || defined(__BORLANDC__) +# if defined (LOG4CPP_STLPORT_AND_BOOST_BUILD) +# include +# else +# include +# endif + +#ifdef MSVC_MEMORY_LEAK_CHECK +#define _CRTDBG_MAP_ALLOC + +#include +#include + +#ifdef _DEBUG + #ifndef DBG_NEW + #define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ ) + #define new DBG_NEW + #endif +#endif // _DEBUG +#endif // MSVC_MEMORY_LEAK_CHECK + +#else +#if defined(__OPENVMS__) +# include +#else +# if defined(__MINGW32__) +# include +//# else +//# include +# endif +#endif +#endif + +#include + +#if defined(_MSC_VER) +# pragma warning( disable : 4786 ) // 255 char debug symbol limit +# pragma warning( disable : 4290 ) // throw specifier not implemented +# pragma warning( disable : 4251 ) // "class XXX should be exported" +#endif + +#ifdef __APPLE__ +# include +#else +# ifndef LOG4CPP_HAVE_SSTREAM +# include + namespace std { + class LOG4CPP_EXPORT ostringstream : public ostrstream { + public: + std::string str(); + }; + }; +# endif // LOG4CPP_HAVE_SSTREAM +#endif // _APPLE_ + + +#endif diff --git a/VrUtils/log4cpp/include/log4cpp/Priority.hh b/VrUtils/log4cpp/include/log4cpp/Priority.hh new file mode 100644 index 0000000..728d5ce --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/Priority.hh @@ -0,0 +1,110 @@ +/* + * Priority.hh + * + * Copyright 2000, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2000, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_PRIORITY_HH +#define _LOG4CPP_PRIORITY_HH + +#include +#include +#include + +/* + * Optionally work around rudeness in windows.h on Win32. + */ +#ifdef ERROR +#ifdef LOG4CPP_FIX_ERROR_COLLISION + +namespace log4cpp { + static const int _tmpERRORValue = ERROR; +} + +#undef ERROR + static const int ERROR = log4cpp::_tmpERRORValue; +#define ERROR ERROR + +#else // LOG4CPP_FIX_ERROR_COLLISION +#error Naming collision for 'ERROR' detected. Please read the FAQ for a \ + workaround. +#endif // LOG4CPP_FIX_ERROR_COLLISION + +#endif // ERROR + +/* + * Other Win32 rudeness in EDK.h + */ +#ifdef DEBUG + +#ifdef LOG4CPP_FIX_ERROR_COLLISION + +#undef DEBUG +#define DEBUG DEBUG + +#else // LOG4CPP_FIX_ERROR_COLLISION +#error Naming collision for 'DEBUG' detected. Please read the FAQ for a \ + workaround. +#endif // LOG4CPP_FIX_ERROR_COLLISION + +#endif // DEBUG + +namespace log4cpp { + + /** + * The Priority class provides importance levels with which one + * can categorize log messages. + **/ + class LOG4CPP_EXPORT Priority { + public: + + static const int MESSAGE_SIZE; // = 8; + + /** + * Predefined Levels of Priorities. These correspond to the + * priority levels used by syslog(3). + **/ + typedef enum {EMERG = 0, + FATAL = 0, + ALERT = 100, + CRIT = 200, + ERROR = 300, + WARN = 400, + NOTICE = 500, + INFO = 600, + DEBUG = 700, + NOTSET = 800 + } PriorityLevel; + + /** + * The type of Priority Values + **/ + typedef int Value; + + /** + * Returns the name of the given priority value. + * Currently, if the value is not one of the PriorityLevel values, + * the method returns the name of the largest priority smaller + * the given value. + * @param priority the numeric value of the priority. + * @returns a string representing the name of the priority. + **/ + static const std::string& getPriorityName(int priority) throw(); + + /** + * Returns the value of the given priority name. + * This can be either one of EMERG ... NOTSET or a + * decimal string representation of the value, e.g. '700' for DEBUG. + * @param priorityName the string containing the the of the priority + * @return the value corresponding with the priority name + * @throw std::invalid_argument if the priorityName does not + * correspond with a known Priority name or a number + **/ + static Value getPriorityValue(const std::string& priorityName); + }; +} + +#endif // _LOG4CPP_PRIORITY_HH diff --git a/VrUtils/log4cpp/include/log4cpp/PropertyConfigurator.hh b/VrUtils/log4cpp/include/log4cpp/PropertyConfigurator.hh new file mode 100644 index 0000000..d0229bc --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/PropertyConfigurator.hh @@ -0,0 +1,58 @@ +/* + * SimpleConfigurator.hh + * + * Copyright 2001, Glen Scott. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ +#ifndef _LOG4CPP_PROPERTYCONFIGURATOR_HH +#define _LOG4CPP_PROPERTYCONFIGURATOR_HH + +#include +#include + +#include +#include // configure exceptions + +namespace log4cpp { + + /** + Property configurator will read a config file using the same (or similar) + format to the config file used by log4j. This file is in a standard Java + "properties" file format. +

    Example:
    +

    +       # a simple test config
    +
    +       log4j.rootCategory=DEBUG, rootAppender
    +       log4j.category.sub1=A1
    +       log4j.category.sub2=INFO
    +       log4j.category.sub1.sub2=ERROR, A2
    +       
    +       log4j.appender.rootAppender=org.apache.log4j.ConsoleAppender
    +       log4j.appender.rootAppender.layout=org.apache.log4j.BasicLayout
    +       
    +       log4j.appender.A1=org.apache.log4j.FileAppender
    +       log4j.appender.A1.fileName=A1.log
    +       log4j.appender.A1.layout=org.apache.log4j.BasicLayout
    +       
    +       log4j.appender.A2=org.apache.log4j.ConsoleAppender
    +       log4j.appender.A2.layout=org.apache.log4j.PatternLayout
    +       log4j.appender.A2.layout.ConversionPattern=The message %%m at time %%d%%n
    +       
    + + @since 0.3.2 + **/ + class LOG4CPP_EXPORT PropertyConfigurator { + public: + /** + * + * @param initFileName + * @exception ConfigureFailure if the method encountered a read or + * syntax error. + */ + static void configure(const std::string& initFileName); + }; +} + +#endif // _LOG4CPP_PROPERTYCONFIGURATOR_HH diff --git a/VrUtils/log4cpp/include/log4cpp/RemoteSyslogAppender.hh b/VrUtils/log4cpp/include/log4cpp/RemoteSyslogAppender.hh new file mode 100644 index 0000000..17fdf0b --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/RemoteSyslogAppender.hh @@ -0,0 +1,139 @@ +/* + * SyslogAppender.hh + * + * Copyright 2001, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2001, Walter Stroebel. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_REMOTESYSLOGAPPENDER_HH +#define _LOG4CPP_REMOTESYSLOGAPPENDER_HH + +#include +#include +#include +#include +#include +#ifdef WIN32 +#include +#else +#include +#endif + +#ifdef LOG4CPP_HAVE_SYSLOG +#include +#else +/// from syslog.h +typedef enum { + LOG_EMERG = 0, ///< system is unusable + LOG_ALERT = 1, ///< action must be taken immediately + LOG_CRIT = 2, ///< critical conditions + LOG_ERR = 3, ///< error conditions + LOG_WARNING = 4, ///< warning conditions + LOG_NOTICE = 5, ///< normal but significant condition + LOG_INFO = 6, ///< informational + LOG_DEBUG = 7, ///< debug-level messages +} SyslogLevel; + +typedef enum { + LOG_KERN = (0<<3), ///< kernel messages + LOG_USER = (1<<3), ///< random user-level messages + LOG_MAIL = (2<<3), ///< mail system + LOG_DAEMON = (3<<3), ///< system daemons + LOG_AUTH = (4<<3), ///< security/authorization messages + LOG_SYSLOG = (5<<3), ///< messages generated internally by syslogd + LOG_LPR = (6<<3), ///< line printer subsystem + LOG_NEWS = (7<<3), ///< network news subsystem + LOG_UUCP = (8<<3), ///< UUCP subsystem + LOG_CRON = (9<<3), ///< clock daemon + LOG_AUTHPRIV = (10<<3), ///< security/authorization messages (private) + LOG_FTP = (11<<3), ///< ftp daemon + + /* other codes through 15 reserved for system use */ + LOG_LOCAL0 = (16<<3), ///< reserved for local use + LOG_LOCAL1 = (17<<3), ///< reserved for local use + LOG_LOCAL2 = (18<<3), ///< reserved for local use + LOG_LOCAL3 = (19<<3), ///< reserved for local use + LOG_LOCAL4 = (20<<3), ///< reserved for local use + LOG_LOCAL5 = (21<<3), ///< reserved for local use + LOG_LOCAL6 = (22<<3), ///< reserved for local use + LOG_LOCAL7 = (23<<3), ///< reserved for local use +} SyslogFacility; +#endif + +namespace log4cpp { + + /** + * RemoteSyslogAppender sends LoggingEvents to a remote syslog system. + * + * Also see: draft-ietf-syslog-syslog-12.txt + **/ + class LOG4CPP_EXPORT RemoteSyslogAppender : public LayoutAppender { + public: + + /** + * Translates a log4cpp priority to a syslog priority + * @param priority The log4cpp priority. + * @returns the syslog priority. + **/ + static int toSyslogPriority(Priority::Value priority); + + /** + * Instantiate a RemoteSyslogAppender with given name and name and + * facility for syslog. + * @param name The name of the Appender + * @param syslogName The ident parameter in the openlog(3) call. + * @param relayer The IP address or hostname of a standard syslog host. + * @param facility The syslog facility to log to. Defaults to LOG_USER. + * Value '-1' implies to use the default. + * @param portNumber An alternative port number. Defaults to the + * standard syslog port number (514). + * Value '-1' implies to use the default. + **/ + RemoteSyslogAppender(const std::string& name, + const std::string& syslogName, + const std::string& relayer, + int facility = LOG_USER, + int portNumber = 514); + virtual ~RemoteSyslogAppender(); + + /** + * Closes and reopens the socket. + **/ + virtual bool reopen(); + + /** + * Closes the socket + **/ + virtual void close(); + + protected: + + /** + * Just creates the socket. + **/ + virtual void open(); + + /** + * Sends a LoggingEvent to the remote syslog. + * @param event the LoggingEvent to log. + **/ + virtual void _append(const LoggingEvent& event); + + const std::string _syslogName; + const std::string _relayer; + int _facility; + int _portNumber; +#ifdef WIN32 + SOCKET _socket; +#else + int _socket; +#endif + in_addr_t _ipAddr; + private: + int _cludge; + }; +} + +#endif // _LOG4CPP_REMOTESYSLOGAPPENDER_HH diff --git a/VrUtils/log4cpp/include/log4cpp/RollingFileAppender.hh b/VrUtils/log4cpp/include/log4cpp/RollingFileAppender.hh new file mode 100644 index 0000000..9a18fce --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/RollingFileAppender.hh @@ -0,0 +1,48 @@ +/* + * RollingFileAppender.hh + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_ROLLINGFILEAPPENDER_HH +#define _LOG4CPP_ROLLINGFILEAPPENDER_HH + +#include +#include +#include +#include + +namespace log4cpp { + + /** + RollingFileAppender is a FileAppender that rolls over the logfile once + it has reached a certain size limit. + @since 0.3.1 + **/ + class LOG4CPP_EXPORT RollingFileAppender : public FileAppender { + public: + RollingFileAppender(const std::string& name, + const std::string& fileName, + size_t maxFileSize = 10*1024*1024, + unsigned int maxBackupIndex = 1, + bool append = true, + mode_t mode = 00644); + + virtual void setMaxBackupIndex(unsigned int maxBackups); + virtual unsigned int getMaxBackupIndex() const; + virtual void setMaximumFileSize(size_t maxFileSize); + virtual size_t getMaxFileSize() const; + + virtual void rollOver(); + + protected: + virtual void _append(const LoggingEvent& event); + + unsigned int _maxBackupIndex; + unsigned short int _maxBackupIndexWidth; // keep constant index width by zeroing leading positions + + size_t _maxFileSize; + }; +} + +#endif // _LOG4CPP_ROLLINGFILEAPPENDER_HH diff --git a/VrUtils/log4cpp/include/log4cpp/SimpleConfigurator.hh b/VrUtils/log4cpp/include/log4cpp/SimpleConfigurator.hh new file mode 100644 index 0000000..e8ae268 --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/SimpleConfigurator.hh @@ -0,0 +1,52 @@ +/* + * SimpleConfigurator.hh + * + * Copyright 2001, Glen Scott. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ +#ifndef _LOG4CPP_SIMPLECONFIGURATOR_HH +#define _LOG4CPP_SIMPLECONFIGURATOR_HH + +#include +#include +#include +#include + +namespace log4cpp { + + /** + * This class implements a simple Configurator for log4cpp. + * It is a temporary hack with an undocumented configuration format. + * @deprecated As of version 0.3.2 log4cpp includes a log4j format + * compatible PropertyConfigurator, removing the need for + * SimpleConfigurator. This class will be removed in 0.4.0. + **/ + class LOG4CPP_EXPORT SimpleConfigurator { + public: + + /** + * Configure log4cpp with the configuration in the given file. + * NB. The configuration file format is undocumented and may change + * without notice. + * @since 0.2.6 + * @param initFileName name of the configuration file + * @exception ConfigureFailure if the method encountered a read or + * syntax error. + **/ + static void configure(const std::string& initFileName); + + /** + * Configure log4cpp with the configuration in the given file. + * NB. The configuration file format is undocumented and may change + * without notice. + * @since 0.3.1 + * @param initFile an input stream to the configuration file + * @exception ConfigureFailure if the method encountered a read or + * syntax error. + **/ + static void configure(std::istream& initFile); + }; +} + +#endif diff --git a/VrUtils/log4cpp/include/log4cpp/SimpleLayout.hh b/VrUtils/log4cpp/include/log4cpp/SimpleLayout.hh new file mode 100644 index 0000000..7e2c68f --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/SimpleLayout.hh @@ -0,0 +1,34 @@ +/* + * SimpleLayout.hh + * + * Copyright 2000, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2000, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_SIMPLELAYOUT_HH +#define _LOG4CPP_SIMPLELAYOUT_HH + +#include +#include + +namespace log4cpp { + + /** + * BasicLayout is a simple fixed format Layout implementation. + **/ + class LOG4CPP_EXPORT SimpleLayout : public Layout { + public: + SimpleLayout(); + virtual ~SimpleLayout(); + + /** + * Formats the LoggingEvent in SimpleLayout style:
    + * "priority - message" + **/ + virtual std::string format(const LoggingEvent& event); + }; +} + +#endif // _LOG4CPP_SIMPLELAYOUT_HH diff --git a/VrUtils/log4cpp/include/log4cpp/SmtpAppender.hh b/VrUtils/log4cpp/include/log4cpp/SmtpAppender.hh new file mode 100644 index 0000000..b233bf8 --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/SmtpAppender.hh @@ -0,0 +1,40 @@ +/* + * Copyright 2002, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2002, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#if !defined(h_2c5af17f_8daf_418f_acb8_5cfce724ec1a) +#define h_2c5af17f_8daf_418f_acb8_5cfce724ec1a + +#if defined(LOG4CPP_HAVE_BOOST) +#include +#if BOOST_VERSION > 103400 + +#include "Portability.hh" +#include "LayoutAppender.hh" + +namespace log4cpp +{ + class LOG4CPP_EXPORT SmptAppender : public LayoutAppender + { + public: + struct mail_params; + + SmptAppender(const std::string& name, const std::string& host, const std::string& from, + const std::string& to, const std::string& subject); + virtual ~SmptAppender(); + virtual void close() { } + + protected: + virtual void _append(const LoggingEvent& event); + + private: + mail_params * mail_params_; + }; +} + +#endif // BOOST_VERSION >= 103400 +#endif // LOG4CPP_HAS_BOOST +#endif // h_2c5af17f_8daf_418f_acb8_5cfce724ec1a diff --git a/VrUtils/log4cpp/include/log4cpp/StringQueueAppender.hh b/VrUtils/log4cpp/include/log4cpp/StringQueueAppender.hh new file mode 100644 index 0000000..a54fc5c --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/StringQueueAppender.hh @@ -0,0 +1,72 @@ +/* + * StringQueueAppender.hh + * + * Copyright 2000, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2000, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_STRINGQUEUEAPPENDER_HH +#define _LOG4CPP_STRINGQUEUEAPPENDER_HH + +#include +#include +#include +#include + +namespace log4cpp { + + /** + * This class puts log messages in an in-memory queue. Its primary use + * is in test cases, but it may be useful elsewhere as well. + * + * @since 0.2.4 + **/ + class LOG4CPP_EXPORT StringQueueAppender : public LayoutAppender { + public: + + StringQueueAppender(const std::string& name); + virtual ~StringQueueAppender(); + + virtual bool reopen(); + virtual void close(); + + /** + * Return the current size of the message queue. + * Shorthand for getQueue().size(). + * @returns the queue size + **/ + virtual size_t queueSize() const; + + /** + * Return the queue to which the Appends adds messages. + * @returns the message queue + **/ + virtual std::queue& getQueue(); + + /** + * Return the queue to which the Appends adds messages. + * @returns the message queue + **/ + virtual const std::queue& getQueue() const; + + /** + * Pop the oldest log message from the front of the queue. + * @returns the oldest log message + **/ + virtual std::string popMessage(); + + protected: + + /** + * Appends the LoggingEvent to the queue. + * @param event the LoggingEvent to layout and append to the queue. + **/ + virtual void _append(const LoggingEvent& event); + + std::queue _queue; + }; +} + +#endif // _LOG4CPP_STRINGQUEUEAPPENDER_HH diff --git a/VrUtils/log4cpp/include/log4cpp/SyslogAppender.hh b/VrUtils/log4cpp/include/log4cpp/SyslogAppender.hh new file mode 100644 index 0000000..61450b6 --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/SyslogAppender.hh @@ -0,0 +1,76 @@ +/* + * SyslogAppender.hh + * + * Copyright 2000, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2000, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_SYSLOGAPPENDER_HH +#define _LOG4CPP_SYSLOGAPPENDER_HH + +#include +#include +#include +#include +#include +#include + +namespace log4cpp { + + /** + * SyslogAppender sends LoggingEvents to the local syslog system. + **/ + class SyslogAppender : public LayoutAppender { + public: + + /** + * Translates a log4cpp priority to a syslog priority + * @param priority The log4cpp priority. + * @returns the syslog priority. + **/ + static int toSyslogPriority(Priority::Value priority); + + /** + * Instantiate a SyslogAppender with given name and name and facility + * for syslog. Note that the C syslog API is process global, so + * instantion of a second SyslogAppender will 'overwrite' the + * syslog name of the first. + * @param name The name of the Appender + * @param syslogName The ident parameter in the openlog(3) call. + * @param facility The syslog facility to log to. Defaults to LOG_USER. + **/ + SyslogAppender(const std::string& name, const std::string& syslogName, + int facility = LOG_USER); + virtual ~SyslogAppender(); + + /** + * Calls closelog(3) and openlog(3). + **/ + virtual bool reopen(); + + /** + * Calls closelog(3) to close the syslog file descriptor. + **/ + virtual void close(); + + protected: + + /** + * Calls openlog(3). + **/ + virtual void open(); + + /** + * Sends a LoggingEvent to syslog. + * @param event the LoggingEvent to log. + **/ + virtual void _append(const LoggingEvent& event); + + const std::string _syslogName; + int _facility; + }; +} + +#endif // _LOG4CPP_SYSLOGAPPENDER_HH diff --git a/VrUtils/log4cpp/include/log4cpp/TimeStamp.hh b/VrUtils/log4cpp/include/log4cpp/TimeStamp.hh new file mode 100644 index 0000000..050a966 --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/TimeStamp.hh @@ -0,0 +1,74 @@ +/* + * TimeStamp.hh + * + * Copyright 2001, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2001, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_TIMESTAMP_HH +#define _LOG4CPP_TIMESTAMP_HH + +#include + +namespace log4cpp { + + /** + * A simple TimeStamp abstraction + **/ + class LOG4CPP_EXPORT TimeStamp { + public: + /** + Constructs a TimeStamp representing 'now'. + **/ + TimeStamp(); + + /** + Constructs a TimeStamp representing the given offset since the + epoch ( 00:00:00 1970/1/1 UTC). + **/ + TimeStamp(unsigned int seconds, unsigned int microSeconds = 0); + + /** + Returns the 'seconds' part of the TimeStamp. + **/ + inline int getSeconds() const { + return _seconds; + }; + + /** + Returns the 'subseconds' part of the TimeStamp in milliseconds, + getMilliSeconds() == getMicroSeconds() / 1000. + **/ + inline int getMilliSeconds() const { + return _microSeconds / 1000; + }; + + /** + Returns the subsecond part of the TimeStamp in microseconds. + The actual precision of this value depends on the platform and + may be in the order of milliseconds rather than microseconds. + **/ + inline int getMicroSeconds() const { + return _microSeconds; + }; + + /** + Returns a TimeStamp representing the time at which the application + started. + **/ + static inline const TimeStamp& getStartTime() { + return _startStamp; + }; + + protected: + static TimeStamp _startStamp; + + int _seconds; + int _microSeconds; + }; +} + +#endif // _LOG4CPP_TIMESTAMP_HH + diff --git a/VrUtils/log4cpp/include/log4cpp/TriggeringEventEvaluator.hh b/VrUtils/log4cpp/include/log4cpp/TriggeringEventEvaluator.hh new file mode 100644 index 0000000..41a76b7 --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/TriggeringEventEvaluator.hh @@ -0,0 +1,23 @@ +/* + * Copyright 2002, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2002, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#if !defined(h_fb88639f_85c9_481a_a3a0_f25ae8bac24c) +#define h_fb88639f_85c9_481a_a3a0_f25ae8bac24c + +#include + +namespace log4cpp +{ + class LOG4CPP_EXPORT TriggeringEventEvaluator + { + public: + virtual bool eval(const LoggingEvent& event) const = 0; + virtual ~TriggeringEventEvaluator() {} + }; +} + +#endif // h_fb88639f_85c9_481a_a3a0_f25ae8bac24c diff --git a/VrUtils/log4cpp/include/log4cpp/TriggeringEventEvaluatorFactory.hh b/VrUtils/log4cpp/include/log4cpp/TriggeringEventEvaluatorFactory.hh new file mode 100644 index 0000000..aff4371 --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/TriggeringEventEvaluatorFactory.hh @@ -0,0 +1,41 @@ +/* + * Copyright 2002, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2002, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#if !defined(h_dd233b8b_5c59_4956_9393_6581c95f9779) +#define h_dd233b8b_5c59_4956_9393_6581c95f9779 + +#include +#include +#include +#include "Portability.hh" +#include "TriggeringEventEvaluator.hh" +#include "FactoryParams.hh" + +namespace log4cpp +{ + class LOG4CPP_EXPORT TriggeringEventEvaluatorFactory + { + public: + typedef FactoryParams params_t; + typedef std::auto_ptr (*create_function_t)(const params_t& params); + + static TriggeringEventEvaluatorFactory& getInstance(); + void registerCreator(const std::string& class_name, create_function_t create_function); + std::auto_ptr create(const std::string& class_name, const params_t& params); + bool registered(const std::string& class_name) const; + + private: + TriggeringEventEvaluatorFactory(){}; + + typedef std::map creators_t; + typedef creators_t::const_iterator const_iterator; + + creators_t creators_; + }; +} + +#endif // h_dd233b8b_5c59_4956_9393_6581c95f9779 diff --git a/VrUtils/log4cpp/include/log4cpp/Win32DebugAppender.hh b/VrUtils/log4cpp/include/log4cpp/Win32DebugAppender.hh new file mode 100644 index 0000000..5a6efc4 --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/Win32DebugAppender.hh @@ -0,0 +1,57 @@ +/* + * Win32DebugAppender.hh + * + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_WIN32DEBUGAPPENDER_HH +#define _LOG4CPP_WIN32DEBUGAPPENDER_HH + +#ifdef WIN32 // only use this on Win32 + +#include +#include "log4cpp/Export.hh" +#include "log4cpp/LayoutAppender.hh" + +namespace log4cpp { + + /** + * Win32DebugAppender simply sends the log message to the default system + * debugger on Win32 systems. This is useful for users of MSVC and Borland + * because the log messages will show up in the debugger window.
    + * NB: This class is only available on Win32 platforms. + */ + class LOG4CPP_EXPORT Win32DebugAppender : public LayoutAppender { + public: + /** + * Constructor. + * @param name Name used by the base classes only. + */ + Win32DebugAppender(const std::string& name); + /** + * Destructor. + */ + virtual ~Win32DebugAppender(); + + /** + * Close method. This is called by the framework, but there is nothing + * to do for the OutputDebugString API, so it simply returns. + */ + virtual void close(); + + protected: + /** + * Method that does the actual work. In this case, it simply sets up the layout + * and calls the OutputDebugString API. + * @param event Event for which we are logging. + */ + virtual void _append(const LoggingEvent& event); + }; +} + +#else // WIN32 +#error NTEventLoggAppender is not available on on Win32 platforms +#endif // WIN32 + +#endif // _LOG4CPP_WIN32DEBUGAPPENDER_HH diff --git a/VrUtils/log4cpp/include/log4cpp/config-MinGW32.h b/VrUtils/log4cpp/include/log4cpp/config-MinGW32.h new file mode 100644 index 0000000..42a5350 --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/config-MinGW32.h @@ -0,0 +1,105 @@ +/* + * Copyright 2002, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2002, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _INCLUDE_LOG4CPP_CONFIG_MINGW32_H +#define _INCLUDE_LOG4CPP_CONFIG_MINGW32_H 1 + +/* manually edited from include/log4cpp/config.h */ + +/* Define if you have the syslog function. */ +/* #undef LOG4CPP_HAVE_SYSLOG */ + +/* Define if you have the `ftime' function. */ +#ifndef LOG4CPP_HAVE_FTIME +#define LOG4CPP_HAVE_FTIME 1 +#endif + +/* Define if you have the `gettimeofday' function. */ +/* #undef LOG4CPP_HAVE_GETTIMEOFDAY */ + +/* define if the compiler has int64_t */ +#ifndef LOG4CPP_HAVE_INT64_T +#define LOG4CPP_HAVE_INT64_T +#define int64_t __int64 + +/* define if the compiler has in_addr_t */ +#ifndef LOG4CPP_HAVE_IN_ADDR_T +#define LOG4CPP_HAVE_IN_ADDR_T + +#ifndef u_long +typedef unsigned long u_long; +#endif + +/* u_long is the type of in_addr.s_addr */ +typedef u_long in_addr_t; + +/* u_short is the type of sockaddr_in.sin_port */ +// typedef u_short in_port_t; + +#endif + +#endif + +/* Define if you have the header file. */ +#ifndef LOG4CPP_HAVE_IO_H +#define LOG4CPP_HAVE_IO_H 1 +#endif + +/* Define if you have the header file. */ +/* #undef LOG4CPP_HAVE_UNISTD_H */ + +/* Define if you have the idsa library (-lidsa). */ +/* #undef LOG4CPP_HAVE_LIBIDSA */ + +/* Define if you have the `strcasecmp' function. */ +/* #undef LOG4CPP_HAVE_STRCASECMP */ + +/* Name of package */ +#ifndef LOG4CPP_PACKAGE +#define LOG4CPP_PACKAGE "log4cpp" +#endif + +/* Version number of package */ +#ifndef LOG4CPP_VERSION +#define LOG4CPP_VERSION "1.0" +#endif + +/* define if the compiler implements namespaces */ +#ifndef LOG4CPP_HAVE_NAMESPACES +#define LOG4CPP_HAVE_NAMESPACES 1 +#endif + +/* define if the compiler has stringstream */ +#ifndef LOG4CPP_HAVE_SSTREAM +#define LOG4CPP_HAVE_SSTREAM 1 +#endif + +#define LOG4CPP_HAS_WCHAR_T 0 + +/* define if the C library has snprintf */ +#ifndef LOG4CPP_HAVE_SNPRINTF +#define LOG4CPP_HAVE_SNPRINTF 1 +#endif + +//#define LOG4CPP_HAVE_LOCALTIME_R 0 + +/* define to get around problems with ERROR in windows.h */ +#ifndef LOG4CPP_FIX_ERROR_COLLISION +#define LOG4CPP_FIX_ERROR_COLLISION 1 +#endif + +/* use threads */ +#ifndef LOG4CPP_HAVE_THREADING +#define LOG4CPP_HAVE_THREADING +# if defined(LOG4CPP_STLPORT_AND_BOOST_BUILD) +# define LOG4CPP_USE_BOOSTTHREADS +# else +# define LOG4CPP_USE_MSTHREADS +# endif +#endif + +#endif diff --git a/VrUtils/log4cpp/include/log4cpp/config-openvms.h b/VrUtils/log4cpp/include/log4cpp/config-openvms.h new file mode 100644 index 0000000..f0c17cf --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/config-openvms.h @@ -0,0 +1,74 @@ +/* + * Copyright 2002, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2002, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _INCLUDE_LOG4CPP_CONFIG_OPENVMS_H +#define _INCLUDE_LOG4CPP_CONFIG_OPENVMS_H 1 + +/* include/log4cpp/config.h. Generated automatically at end of configure. */ +/* include/config.h. Generated automatically by configure. */ +/* include/config.h.in. Generated automatically from configure.in by autoheader. */ + +/* Define if you have the header file. */ +#ifndef LOG4CPP_HAVE_DLFCN_H +#define LOG4CPP_HAVE_DLFCN_H 1 +#endif + +/* Define if you have the `ftime' function. */ +#ifndef LOG4CPP_HAVE_FTIME +#define LOG4CPP_HAVE_FTIME 1 +#endif + +/* Define if you have the `gettimeofday' function. */ +#ifndef LOG4CPP_HAVE_GETTIMEOFDAY +#define LOG4CPP_HAVE_GETTIMEOFDAY 1 +#endif + +/* define if the compiler has int64_t */ +#ifndef LOG4CPP_HAVE_INT64_T +#define LOG4CPP_HAVE_INT64_T +#include +#endif + +/* Define if you have the header file. */ +/* #undef LOG4CPP_HAVE_IO_H */ + +/* Define if you have the `idsa' library (-lidsa). */ +/* #undef LOG4CPP_HAVE_LIBIDSA */ + +/* define if the compiler implements namespaces */ +#ifndef LOG4CPP_HAVE_NAMESPACES +#define LOG4CPP_HAVE_NAMESPACES +#endif + +/* define if the C library has snprintf */ +/* #undef LOG4CPP_HAVE_SNPRINTF */ + +/* define if the compiler has stringstream */ +#ifndef LOG4CPP_HAVE_SSTREAM +#define LOG4CPP_HAVE_SSTREAM +#endif + +/* Define if you have the `syslog' function. */ +/* #undef LOG4CPP_HAVE_SYSLOG */ + +/* Define if you have the header file. */ +#ifndef LOG4CPP_HAVE_UNISTD_H +#define LOG4CPP_HAVE_UNISTD_H 1 +#endif + +/* Name of package */ +#ifndef LOG4CPP_PACKAGE +#define LOG4CPP_PACKAGE "log4cpp" +#endif + +/* Version number of package */ +#ifndef LOG4CPP_VERSION +#define LOG4CPP_VERSION "1.0" +#endif + +/* _INCLUDE_LOG4CPP_CONFIG_OPENVMS_H */ +#endif diff --git a/VrUtils/log4cpp/include/log4cpp/config-win32-stlport-boost.h b/VrUtils/log4cpp/include/log4cpp/config-win32-stlport-boost.h new file mode 100644 index 0000000..8905b93 --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/config-win32-stlport-boost.h @@ -0,0 +1,157 @@ +/* + * Copyright 2002, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2002, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _INCLUDE_LOG4CPP_CONFIG_WIN32_H +#define _INCLUDE_LOG4CPP_CONFIG_WIN32_H 1 + +/* manually edited from include/log4cpp/config.h */ + +/* Define if you have the syslog function. */ +/* #undef LOG4CPP_HAVE_SYSLOG */ + +/* Define if you have the `ftime' function. */ +#ifndef LOG4CPP_HAVE_FTIME +#define LOG4CPP_HAVE_FTIME 1 +#endif + +/* Define if you have the `gettimeofday' function. */ +/* #undef LOG4CPP_HAVE_GETTIMEOFDAY */ + +/* define if the compiler has int64_t */ +#ifndef LOG4CPP_HAVE_INT64_T +#define LOG4CPP_HAVE_INT64_T + +#include +using boost::int64_t; + +#endif + +/* define if the compiler has in_addr_t */ +#ifndef LOG4CPP_HAVE_IN_ADDR_T +#define LOG4CPP_HAVE_IN_ADDR_T + +#ifndef u_long +typedef unsigned long u_long; +#endif + +/* u_long is the type of in_addr.s_addr */ +typedef u_long in_addr_t; + +/* u_short is the type of sockaddr_in.sin_port */ +// typedef u_short in_port_t; + +#endif + +/* Define if you have the header file. */ +#ifndef LOG4CPP_HAVE_IO_H +#define LOG4CPP_HAVE_IO_H 1 +#endif + +/* Define if you have the header file. */ +/* #undef LOG4CPP_HAVE_UNISTD_H */ + +/* Define if you have the idsa library (-lidsa). */ +/* #undef LOG4CPP_HAVE_LIBIDSA */ + +/* Define if you have the `strcasecmp' function. */ +/* #undef LOG4CPP_HAVE_STRCASECMP */ + +/* Name of package */ +#ifndef LOG4CPP_PACKAGE +#define LOG4CPP_PACKAGE "log4cpp" +#endif + +/* Version number of package */ +#ifndef LOG4CPP_VERSION +#define LOG4CPP_VERSION "1.0" +#endif + +/* define if the compiler implements namespaces */ +#ifndef LOG4CPP_HAVE_NAMESPACES +#define LOG4CPP_HAVE_NAMESPACES 1 +#endif + +/* define if the compiler has stringstream */ +#ifndef LOG4CPP_HAVE_SSTREAM +#define LOG4CPP_HAVE_SSTREAM 1 +#endif + +#if defined(_MSC_VER) +# if _MSC_VER < 1300 +# define LOG4CPP_HAS_WCHAR_T 0 +# else +# define LOG4CPP_HAS_WCHAR_T 1 +# endif +#else +# define LOG4CPP_HAS_WCHAR_T 1 +#endif + +/* define if the C library has snprintf */ +#ifndef LOG4CPP_HAVE_SNPRINTF +#define LOG4CPP_HAVE_SNPRINTF 1 +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1300 +#define LOG4CPP_HAVE_LOCALTIME_R 1 +#endif + +/* define to get around problems with ERROR in windows.h */ +#ifndef LOG4CPP_FIX_ERROR_COLLISION +#define LOG4CPP_FIX_ERROR_COLLISION 1 +#endif + +/* define WIN32 for Borland */ +#ifndef WIN32 +#define WIN32 +#endif + +/* use threads */ +#ifndef LOG4CPP_HAVE_THREADING +#define LOG4CPP_HAVE_THREADING +#endif + +/* use boost threads */ +#ifndef LOG4CPP_USE_BOOSTTHREADS +#define LOG4CPP_USE_BOOSTTHREADS +#endif + +/* supply DLL main */ +#ifndef LOG4CPP_SUPPLY_DLLMAIN +#define LOG4CPP_SUPPLY_DLLMAIN +#endif + +/* MSVCs and headers are broken in the sense that they + put functions in the global namespace instead of std:: + The #defines below enable a workaround for MSVC 6 and lower. If MSVC 7 + is still broken please adjust the _MSC_VER version check and report it. + See also bug report #628211. +*/ +#if defined(_MSC_VER) && _MSC_VER < 1300 + +#ifndef LOG4CPP_CSTDLIB_NOT_IN_STD +#define LOG4CPP_CSTDLIB_NOT_IN_STD +#endif + +#ifndef LOG4CPP_CSTRING_NOT_IN_STD +#define LOG4CPP_CSTRING_NOT_IN_STD +#endif + +#ifndef LOG4CPP_CTIME_NOT_IN_STD +#define LOG4CPP_CTIME_NOT_IN_STD +#endif + +#ifndef LOG4CPP_CMATH_NOT_IN_STD +#define LOG4CPP_CMATH_NOT_IN_STD +#endif + +#endif + +/* define mode_t. Move to Portability.hh if more platforms need it */ +typedef int mode_t; + +/* _INCLUDE_LOG4CPP_CONFIG_WIN32_H */ +#endif diff --git a/VrUtils/log4cpp/include/log4cpp/config-win32.h b/VrUtils/log4cpp/include/log4cpp/config-win32.h new file mode 100644 index 0000000..12d0cfa --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/config-win32.h @@ -0,0 +1,174 @@ +/* + * Copyright 2002, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2002, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _INCLUDE_LOG4CPP_CONFIG_WIN32_H +#define _INCLUDE_LOG4CPP_CONFIG_WIN32_H 1 + +/* manually edited from include/log4cpp/config.h */ + +/* Define if you have the syslog function. */ +/* #undef LOG4CPP_HAVE_SYSLOG */ + +/* Define if you have the `ftime' function. */ +#ifndef LOG4CPP_HAVE_FTIME +#define LOG4CPP_HAVE_FTIME 1 +#endif + +/* Define if you have the `gettimeofday' function. */ +/* #undef LOG4CPP_HAVE_GETTIMEOFDAY */ + +/* define if the compiler has int64_t */ +#ifndef LOG4CPP_HAVE_INT64_T +#define LOG4CPP_HAVE_INT64_T +typedef __int64 int64_t; + +/* define if the compiler has in_addr_t */ +#ifndef LOG4CPP_HAVE_IN_ADDR_T +#define LOG4CPP_HAVE_IN_ADDR_T + +#ifndef u_long +typedef unsigned long u_long; +#endif + +/* u_long is the type of in_addr.s_addr */ +typedef u_long in_addr_t; + +/* u_short is the type of sockaddr_in.sin_port */ +// typedef u_short in_port_t; + +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1300 +#define LOG4CPP_MISSING_INT64_OSTREAM_OP +#endif + +#endif + +/* Define if you have the header file. */ +#ifndef LOG4CPP_HAVE_IO_H +#define LOG4CPP_HAVE_IO_H 1 +#endif + +/* Define if you have the header file. */ +/* #undef LOG4CPP_HAVE_UNISTD_H */ + +/* Define if you have the idsa library (-lidsa). */ +/* #undef LOG4CPP_HAVE_LIBIDSA */ + +/* Define if you have the `strcasecmp' function. */ +/* #undef LOG4CPP_HAVE_STRCASECMP */ + +/* Name of package */ +#ifndef LOG4CPP_PACKAGE +#define LOG4CPP_PACKAGE "log4cpp" +#endif + +/* Version number of package */ +#ifndef LOG4CPP_VERSION +#define LOG4CPP_VERSION "1.0" +#endif + +/* define if the compiler implements namespaces */ +#ifndef LOG4CPP_HAVE_NAMESPACES +#define LOG4CPP_HAVE_NAMESPACES 1 +#endif + +/* define if the compiler has stringstream */ +#ifndef LOG4CPP_HAVE_SSTREAM +#define LOG4CPP_HAVE_SSTREAM 1 +#endif + +#if defined(_MSC_VER) +# if _MSC_VER < 1300 +# define LOG4CPP_HAS_WCHAR_T 0 +# else +# define LOG4CPP_HAS_WCHAR_T 1 +# endif +#else +# define LOG4CPP_HAS_WCHAR_T 1 +#endif + +/* define if the C library has snprintf */ +#ifndef LOG4CPP_HAVE_SNPRINTF +#define LOG4CPP_HAVE_SNPRINTF 1 +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1300 +#define LOG4CPP_HAVE_LOCALTIME_R 1 +#endif + +/* define to get around problems with ERROR in windows.h */ +#ifndef LOG4CPP_FIX_ERROR_COLLISION +#define LOG4CPP_FIX_ERROR_COLLISION 1 +#endif + +/* ensure auto_ptr is available in newer C++ standards */ +#if defined(_MSC_VER) && _MSC_VER >= 1900 +#ifndef _HAS_AUTO_PTR_ETC +#define _HAS_AUTO_PTR_ETC 1 +#endif +#endif + +/* define WIN32 for Borland */ +#ifndef WIN32 +#define WIN32 +#endif + +/* use threads */ +#ifndef LOG4CPP_HAVE_THREADING +#define LOG4CPP_HAVE_THREADING +#endif + +/* use ms threads */ +#ifndef LOG4CPP_USE_MSTHREADS +#define LOG4CPP_USE_MSTHREADS +#endif + +/* supply DLL main */ +#ifndef LOG4CPP_SUPPLY_DLLMAIN +#define LOG4CPP_SUPPLY_DLLMAIN +#endif + +/* MSVCs and headers are broken in the sense that they + put functions in the global namespace instead of std:: + The #defines below enable a workaround for MSVC 6 and lower. If MSVC 7 + is still broken please adjust the _MSC_VER version check and report it. + See also bug report #628211. +*/ +#if defined(_MSC_VER) && _MSC_VER < 1300 + +#ifndef LOG4CPP_CSTDLIB_NOT_IN_STD +#define LOG4CPP_CSTDLIB_NOT_IN_STD +#endif + +#ifndef LOG4CPP_CSTRING_NOT_IN_STD +#define LOG4CPP_CSTRING_NOT_IN_STD +#endif + +#ifndef LOG4CPP_CTIME_NOT_IN_STD +#define LOG4CPP_CTIME_NOT_IN_STD +#endif + +#ifndef LOG4CPP_CMATH_NOT_IN_STD +#define LOG4CPP_CMATH_NOT_IN_STD +#endif + +#endif + +/* define mode_t. Move to Portability.hh if more platforms need it */ +#if !defined(__BORLANDC__) +typedef int mode_t; +#endif + +#if defined(_MSC_VER) && _MSC_VER == 1310 +// warning C4275: interface non dll class 'std::runtime_error' utilise comme base +// d'une interface dll class 'log4cpp::ConfigureFailure' +#pragma warning(disable: 4275) +#endif + +/* _INCLUDE_LOG4CPP_CONFIG_WIN32_H */ +#endif diff --git a/VrUtils/log4cpp/include/log4cpp/convenience.h b/VrUtils/log4cpp/include/log4cpp/convenience.h new file mode 100644 index 0000000..04cac8d --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/convenience.h @@ -0,0 +1,104 @@ +/* + * Copyright 2002, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2002, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef LOG4CPP_CONVENIENCE_H +#define LOG4CPP_CONVENIENCE_H + +#define LOG4CPP_LOGGER(name) \ + static log4cpp::Category& logger = log4cpp::Category::getInstance( name ); + +#define LOG4CPP_LOGGER_N(var_name, name) \ + static log4cpp::Category& var_name = log4cpp::Category::getInstance( name ); + +// simple logging +#define LOG4CPP_EMERG(logger, msg) \ + if (logger.isEmergEnabled()) logger.emerg( msg ); + +#define LOG4CPP_FATAL(logger, msg) \ + if (logger.isFatalEnabled()) logger.fatal( msg ); + +#define LOG4CPP_ALERT(logger, msg) \ + if (logger.isAlertEnabled()) logger.alert( msg ); + +#define LOG4CPP_CRIT(logger, msg) \ + if (logger.isCritEnabled()) logger.crit( msg ); + +#define LOG4CPP_ERROR(logger, msg) \ + if (logger.isErrorEnabled()) logger.error( msg ); + +#define LOG4CPP_WARN(logger, msg) \ + if (logger.isWarnEnabled()) logger.warn( msg ); + +#define LOG4CPP_NOTICE(logger, msg) \ + if (logger.isNoticeEnabled()) logger.notice( msg ); + +#define LOG4CPP_INFO(logger, msg) \ + if (logger.isInfoEnabled()) logger.info( msg ); + +#define LOG4CPP_DEBUG(logger, msg) \ + if (logger.isDebugEnabled()) logger.debug( msg ); + + +// stream logging +#define LOG4CPP_EMERG_S(logger) \ + if (logger.isEmergEnabled()) logger.emergStream() + +#define LOG4CPP_FATAL_S(logger) \ + if (logger.isFatalEnabled()) logger.fatalStream() + +#define LOG4CPP_ALERT_S(logger) \ + if (logger.isAlertEnabled()) logger.alertStream() + +#define LOG4CPP_CRIT_S(logger) \ + if (logger.isCritEnabled()) logger.critStream() + +#define LOG4CPP_ERROR_S(logger) \ + if (logger.isErrorEnabled()) logger.errorStream() + +#define LOG4CPP_WARN_S(logger) \ + if (logger.isWarnEnabled()) logger.warnStream() + +#define LOG4CPP_NOTICE_S(logger) \ + if (logger.isNoticeEnabled()) logger.noticeStream() + +#define LOG4CPP_INFO_S(logger) \ + if (logger.isInfoEnabled()) logger.infoStream() + +#define LOG4CPP_DEBUG_S(logger) \ + if (logger.isDebugEnabled()) logger.debugStream() + + +// stream logging with default logger "logger" +#define LOG4CPP_EMERG_SD() \ + if (logger.isEmergEnabled()) logger.emergStream() + +#define LOG4CPP_FATAL_SD() \ + if (logger.isFatalEnabled()) logger.fatalStream() + +#define LOG4CPP_ALERT_SD() \ + if (logger.isAlertEnabled()) logger.alertStream() + +#define LOG4CPP_CRIT_SD() \ + if (logger.isCritEnabled()) logger.critStream() + +#define LOG4CPP_ERROR_SD() \ + if (logger.isErrorEnabled()) logger.errorStream() + +#define LOG4CPP_WARN_SD() \ + if (logger.isWarnEnabled()) logger.warnStream() + +#define LOG4CPP_NOTICE_SD() \ + if (logger.isNoticeEnabled()) logger.noticeStream() + +#define LOG4CPP_INFO_SD() \ + if (logger.isInfoEnabled()) logger.infoStream() + +#define LOG4CPP_DEBUG_SD() \ + if (logger.isDebugEnabled()) logger.debugStream() + +#endif + diff --git a/VrUtils/log4cpp/include/log4cpp/threading/BoostThreads.hh b/VrUtils/log4cpp/include/log4cpp/threading/BoostThreads.hh new file mode 100644 index 0000000..21620f2 --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/threading/BoostThreads.hh @@ -0,0 +1,55 @@ +/* + * BoostThreads.hh + * + * Copyright 2002, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2002, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_THREADING_BOOSTTHREADS_HH +#define _LOG4CPP_THREADING_BOOSTTHREADS_HH + +#include +#include +#include +#include +#include +#include + +namespace log4cpp { + namespace threading { + static std::string getThreadId() { + char buffer[14]; + // Boost.Threads stores the thread ID but doesn't expose it + sprintf(buffer, "not available"); + return std::string(buffer); + }; + + typedef boost::mutex Mutex; + typedef boost::mutex::scoped_lock ScopedLock; + + template class ThreadLocalDataHolder { + public: + inline T* get() const { + return _localData.get(); + }; + + inline T* operator->() const { return _localData.get(); }; + inline T& operator*() const { return *_localData.get(); }; + + inline T* release() { + return _localData.release(); + }; + + inline void reset(T* p = NULL) { + _localData.reset(p); + }; + + private: + boost::thread_specific_ptr _localData; + }; + + } +} +#endif diff --git a/VrUtils/log4cpp/include/log4cpp/threading/DummyThreads.hh b/VrUtils/log4cpp/include/log4cpp/threading/DummyThreads.hh new file mode 100644 index 0000000..96369bd --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/threading/DummyThreads.hh @@ -0,0 +1,67 @@ +/* + * DummyThreads.hh + * + * Copyright 2002, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2002, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_THREADING_DUMMYTHREADS_HH +#define _LOG4CPP_THREADING_DUMMYTHREADS_HH + +#include +#include +#include + +namespace log4cpp { + namespace threading { + std::string getThreadId(); + + /** + Dummy type 'int' for Mutex. Yes, this adds a bit of overhead in + the for of extra memory, but unfortunately 'void' is illegal. + **/ + typedef int Mutex; + + /** + Dummy type 'int' defintion of ScopedLock; + **/ + typedef int ScopedLock; + + template class ThreadLocalDataHolder { + public: + typedef T data_type; + + inline ThreadLocalDataHolder() {}; + inline ~ThreadLocalDataHolder() { + if (_data) + delete _data; + }; + + inline T* get() const { + return _data; + }; + + inline T* operator->() const { return get(); }; + inline T& operator*() const { return *get(); }; + + inline T* release() { + T* result = _data; + _data = NULL; + + return result; + }; + + inline void reset(T* p = NULL) { + if (_data) + delete _data; + _data = p; + }; + + private: + T* _data; + }; + } +} +#endif diff --git a/VrUtils/log4cpp/include/log4cpp/threading/MSThreads.hh b/VrUtils/log4cpp/include/log4cpp/threading/MSThreads.hh new file mode 100644 index 0000000..f6b567c --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/threading/MSThreads.hh @@ -0,0 +1,161 @@ +/* + * MSThreads.hh + * + * Copyright 2002, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2002, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_THREADING_MSTHREADS_HH +#define _LOG4CPP_THREADING_MSTHREADS_HH + +#include + +// deal with ERROR #define +// N.B. This #includes windows.h with NOGDI and WIN32_LEAN_AND_MEAN #defined. +// If this is not what the user wants, #include windows.h before this file. +#ifndef _WINDOWS_ +# ifndef NOGDI +# define NOGDI // this will circumvent the ERROR #define in windows.h +# define LOG4CPP_UNDEFINE_NOGDI +# endif + +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# define LOG4CPP_UNDEFINE_WIN32_LEAN_AND_MEAN +# endif + +# include + +# ifdef LOG4CPP_UNDEFINE_NOGDI +# undef NOGDI +# endif + +# ifdef LOG4CPP_UNDEFINE_WIN32_LEAN_AND_MEAN +# undef WIN32_LEAN_AND_MEAN +# endif + +#endif // done dealing with ERROR #define + +namespace log4cpp { + namespace threading { + /** + * Return an identifier for the current thread. What these + * identifiers look like is completely up to the underlying + * thread library. + **/ + std::string getThreadId(); + + /** + * A simple object wrapper around CreateMutex() and DeleteMutex() + */ + class LOG4CPP_EXPORT MSMutex { + public: + MSMutex() { InitializeCriticalSection(&_criticalSection); } + ~MSMutex() { DeleteCriticalSection(&_criticalSection); } + inline LPCRITICAL_SECTION getCriticalSection() { + return &_criticalSection; + } + + private: + MSMutex(const MSMutex& other); + CRITICAL_SECTION _criticalSection; + }; + + /** + * A simple, non recursive Mutex. + **/ + typedef MSMutex Mutex; + + /** + * A simple object wrapper around WaitForSingleObject() and + * ReleaseMutex() + */ + class MSScopedLock { + public: + MSScopedLock(MSMutex& mutex) { + _criticalSection = mutex.getCriticalSection(); + EnterCriticalSection(_criticalSection); + } + + ~MSScopedLock() { LeaveCriticalSection(_criticalSection); } + + private: + MSScopedLock(const MSScopedLock& other); + LPCRITICAL_SECTION _criticalSection; + }; + + /** + * A simple "resource acquisition is initialization" idiom type lock + * for Mutex. + **/ + typedef MSScopedLock ScopedLock; + + /** + * This class holds Thread local data of type T, i.e. for each + * thread a ThreadLocalDataHolder holds 0 or 1 instance of T. + * The held object must be heap allocated and will be deleted + * upon termination of the thread to which it belongs. + **/ + template class ThreadLocalDataHolder { + public: + inline ThreadLocalDataHolder() : + _key(TlsAlloc()) {}; + + inline ~ThreadLocalDataHolder() { TlsFree(_key); }; + + /** + * Obtains the Object held for the current thread. + * @return a pointer to the held Object or NULL if no + * Object has been set for the current thread. + **/ + inline T* get() const { + return (T*)TlsGetValue(_key); + }; + + /** + * Obtains the Object held for the current thread. + * Initially each thread holds NULL. + * @return a pointer to the held Object or NULL if no + * Object has been set for the current thread. + **/ + inline T* operator->() const { return get(); }; + + /** + * Obtains the Object held for the current thread. + * @pre get() != NULL + * @return a reference to the held Object. + **/ + inline T& operator*() const { return *get(); }; + + /** + * Releases the Object held for the current thread. + * @post get() == NULL + * @return a pointer to the Object thas was held for + * the current thread or NULL if no Object was held. + **/ + inline T* release() { + T* result = (T*)TlsGetValue(_key); + TlsSetValue(_key, NULL); + return result; + }; + + /** + * Sets a new Object to be held for the current thread. A + * previously set Object will be deleted. + * @param p the new object to hold. + * @post get() == p + **/ + inline void reset(T* p = NULL) { + T* thing = (T*)TlsGetValue(_key); + delete thing; + TlsSetValue(_key, p); + }; + + private: + DWORD _key; + }; + } +} +#endif diff --git a/VrUtils/log4cpp/include/log4cpp/threading/OmniThreads.hh b/VrUtils/log4cpp/include/log4cpp/threading/OmniThreads.hh new file mode 100644 index 0000000..bde8c99 --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/threading/OmniThreads.hh @@ -0,0 +1,137 @@ +/* + * OmniThreads.hh + * + * Copyright 2002, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2002, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_THREADING_OMNITHREADS_HH +#define _LOG4CPP_THREADING_OMNITHREADS_HH + +#include +#include +#include +#include + +namespace log4cpp { + namespace threading { + /** + * Return an identifier for the current thread. What these + * identifiers look like is completely up to the underlying + * thread library. OmniThreads returns the POSIX thread Id. + **/ + std::string getThreadId(); + + /** + * A simple, non recursive Mutex. + * Equivalent to Boost.Threads boost::mutex + **/ + typedef omni_mutex Mutex; + + /** + * A simple "resource acquisition is initialization" idiom type lock + * for Mutex. + * Equivalent to Boost.Threads boost::scoped_lock. + **/ + typedef omni_mutex_lock ScopedLock; + + /** + * This class holds Thread local data of type T, i.e. for each + * thread a ThreadLocalDataHolder holds 0 or 1 instance of T. + * The held object must be heap allocated and will be deleted + * upon termination of the thread to wich it belongs. + * This is an omni_threads based equivalent of Boost.Threads + * thread_specific_ptr class. + **/ + template class ThreadLocalDataHolder { + public: + typedef T data_type; + + inline ThreadLocalDataHolder() : + _key(omni_thread::allocate_key()) {}; + + inline ~ThreadLocalDataHolder() {}; + + /** + * Obtains the Object held for the current thread. + * @return a pointer to the held Object or NULL if no + * Object has been set for the current thread. + **/ + inline T* get() const { + Holder* holder = dynamic_cast( + ::omni_thread::self()->get_value(_key)); + return (holder) ? holder->data : NULL; + }; + + /** + * Obtains the Object held for the current thread. + * Initially each thread holds NULL. + * @return a pointer to the held Object or NULL if no + * Object has been set for the current thread. + **/ + inline T* operator->() const { return get(); }; + + /** + * Obtains the Object held for the current thread. + * @pre get() != NULL + * @return a reference to the held Object. + **/ + inline T& operator*() const { return *get(); }; + + /** + * Releases the Object held for the current thread. + * @post get() == NULL + * @return a pointer to the Object thas was held for + * the current thread or NULL if no Object was held. + **/ + inline T* release() { + T* result = NULL; + Holder* holder = dynamic_cast( + ::omni_thread::self()->get_value(_key)); + + if (holder) { + result = holder->data; + holder->data = NULL; + } + + return result; + }; + + /** + * Sets a new Object to be held for the current thread. A + * previously set Object will be deleted. + * @param p the new object to hold. + * @post get() == p + **/ + inline void reset(T* p = NULL) { + Holder* holder = dynamic_cast( + ::omni_thread::self()->get_value(_key)); + if (holder) { + if (holder->data) + delete holder->data; + + holder->data = p; + } else { + holder = new Holder(p); + ::omni_thread::self()->set_value(_key, holder); + } + }; + + private: + class Holder : public omni_thread::value_t { + public: + Holder(data_type* data) : data(data) {}; + virtual ~Holder() { if (data) delete (data); }; + data_type* data; + private: + Holder(const Holder& other); + Holder& operator=(const Holder& other); + }; + + omni_thread::key_t _key; + }; + } +} +#endif diff --git a/VrUtils/log4cpp/include/log4cpp/threading/PThreads.hh b/VrUtils/log4cpp/include/log4cpp/threading/PThreads.hh new file mode 100644 index 0000000..5c70a61 --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/threading/PThreads.hh @@ -0,0 +1,125 @@ +/* + * PThreads.hh + * + * Copyright 2002, Emiliano Martin emilianomc@terra.es All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_THREADING_PTHREADS_HH +#define _LOG4CPP_THREADING_PTHREADS_HH + +#include +#include +#include +#include +#include + + +namespace log4cpp { + namespace threading { + + /** + * returns the thread ID + **/ + std::string getThreadId(); + + /** + **/ + class Mutex { + private: + pthread_mutex_t mutex; + + public: + inline Mutex() { + ::pthread_mutex_init(&mutex, NULL); + } + + inline void lock() { + ::pthread_mutex_lock(&mutex); + } + + inline void unlock() { + ::pthread_mutex_unlock(&mutex); + } + + inline ~Mutex() { + ::pthread_mutex_destroy(&mutex); + } + + private: + Mutex(const Mutex& m); + Mutex& operator=(const Mutex &m); + }; + + /** + * definition of ScopedLock; + **/ + class ScopedLock { + private: + Mutex& _mutex; + + public: + inline ScopedLock(Mutex& mutex) : + _mutex(mutex) { + _mutex.lock(); + } + + inline ~ScopedLock() { + _mutex.unlock(); + } + }; + + /** + * + **/ + template class ThreadLocalDataHolder { + private: + pthread_key_t _key; + + public: + typedef T data_type; + + inline ThreadLocalDataHolder() { + ::pthread_key_create(&_key, freeHolder); + } + + inline static void freeHolder(void *p) { + assert(p != NULL); + delete reinterpret_cast(p); + } + + inline ~ThreadLocalDataHolder() { + T *data = get(); + if (data != NULL) { + delete data; + } + ::pthread_key_delete(_key); + } + + inline T* get() const { + return reinterpret_cast(::pthread_getspecific(_key)); + } + + inline T* operator->() const { return get(); } + inline T& operator*() const { return *get(); } + + inline T* release() { + T* result = get(); + ::pthread_setspecific(_key, NULL); + + return result; + } + + inline void reset(T* p = NULL) { + T *data = get(); + if (data != NULL) { + delete data; + } + ::pthread_setspecific(_key, p); + } + }; + + } +} +#endif diff --git a/VrUtils/log4cpp/include/log4cpp/threading/Threading.hh b/VrUtils/log4cpp/include/log4cpp/threading/Threading.hh new file mode 100644 index 0000000..fa1450c --- /dev/null +++ b/VrUtils/log4cpp/include/log4cpp/threading/Threading.hh @@ -0,0 +1,37 @@ +/* + * Threading.hh + * + * Copyright 2002, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2002, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_THREADING_THREADING_HH +#define _LOG4CPP_THREADING_THREADING_HH + +#include + +#ifdef LOG4CPP_HAVE_THREADING + +#ifdef LOG4CPP_USE_OMNITHREADS +#include +#endif + +#ifdef LOG4CPP_USE_BOOSTTHREADS +#include +#endif + +#ifdef LOG4CPP_USE_MSTHREADS +#include +#endif + +#ifdef LOG4CPP_USE_PTHREADS +#include +#endif + +#else /* LOG4CPP_HAVE_THREADING */ +#include +#endif /* LOG4CPP_HAVE_THREADING */ + +#endif diff --git a/VrUtils/log4cpp/src/AbortAppender.cpp b/VrUtils/log4cpp/src/AbortAppender.cpp new file mode 100644 index 0000000..f67a4a5 --- /dev/null +++ b/VrUtils/log4cpp/src/AbortAppender.cpp @@ -0,0 +1,52 @@ +/* + * AbortAppender.cpp + * + * Copyright 2000, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2000, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include "PortabilityImpl.hh" +#include +#include +#include +#include + +namespace log4cpp { + + AbortAppender::AbortAppender(const std::string& name) : + AppenderSkeleton(name) { + } + + AbortAppender::~AbortAppender() { + close(); + } + + void AbortAppender::close() { + // empty + } + + void AbortAppender::_append(const LoggingEvent& event) { + std::abort(); + } + + bool AbortAppender::reopen() { + return true; + } + + bool AbortAppender::requiresLayout() const { + return false; + } + + void AbortAppender::setLayout(Layout* layout) { + return; + } + + std::auto_ptr create_abort_appender(const FactoryParams& params) + { + std::string name; + params.get_for("abort appender").required("name", name); + return std::auto_ptr(new AbortAppender(name)); + } +} diff --git a/VrUtils/log4cpp/src/Appender.cpp b/VrUtils/log4cpp/src/Appender.cpp new file mode 100644 index 0000000..4d06ea2 --- /dev/null +++ b/VrUtils/log4cpp/src/Appender.cpp @@ -0,0 +1,123 @@ +/* + * Appender.cpp + * + * Copyright 2000, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2000, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include "PortabilityImpl.hh" +#include +#include + +namespace log4cpp { + static int appenders_nifty_counter; // zero initialized at load time + static char appenderMapStorage_buf[sizeof(Appender::AppenderMapStorage)]; // memory for the nifty-counter singleton object + Appender::AppenderMapStorage &Appender::_appenderMapStorageInstance = reinterpret_cast (appenderMapStorage_buf); // memory for placement new + + Appender::AppenderMapStorage::AppenderMapStorage() { + _allAppenders = new AppenderMap(); + } + Appender::AppenderMapStorage::~AppenderMapStorage() { + _deleteAllAppenders(); + delete _allAppenders; + } + + Appender::AppenderMapStorageInitializer::AppenderMapStorageInitializer() { + if (appenders_nifty_counter++ == 0) { +// MSVC's requires redefinition of the new operator, but could not deal with placement new form of it +#ifdef MSVC_MEMORY_LEAK_CHECK +#pragma push_macro("new") +#define new new +#endif // MSVC_MEMORY_LEAK_CHECK + new (&_appenderMapStorageInstance) AppenderMapStorage(); // placement new +#ifdef MSVC_MEMORY_LEAK_CHECK +#pragma pop_macro("new") +#endif // MSVC_MEMORY_LEAK_CHECK + } + } + Appender::AppenderMapStorageInitializer::~AppenderMapStorageInitializer() { + if (--appenders_nifty_counter == 0) { + (&_appenderMapStorageInstance)->~AppenderMapStorage (); + } + } + + /* assume _appenderMapMutex locked */ + Appender::AppenderMap& Appender::_getAllAppenders() { + return *_appenderMapStorageInstance._allAppenders; + } + + Appender* Appender::getAppender(const std::string& name) { + threading::ScopedLock lock(_appenderMapStorageInstance._appenderMapMutex); + AppenderMap& allAppenders = Appender::_getAllAppenders(); + AppenderMap::iterator i = allAppenders.find(name); + return (allAppenders.end() == i) ? NULL : ((*i).second); + } + + void Appender::_addAppender(Appender* appender) { + //REQUIRE(_allAppenders.find(appender->getName()) == _getAllAppenders().end()) + threading::ScopedLock lock(_appenderMapStorageInstance._appenderMapMutex); + _getAllAppenders()[appender->getName()] = appender; + } + + void Appender::_removeAppender(Appender* appender) { + threading::ScopedLock lock(_appenderMapStorageInstance._appenderMapMutex); + //private called from destructor only, but may be triggered by client code in several treads + _getAllAppenders().erase(appender->getName()); + } + + bool Appender::reopenAll() { + threading::ScopedLock lock(_appenderMapStorageInstance._appenderMapMutex); + bool result = true; + AppenderMap& allAppenders = _getAllAppenders(); + for(AppenderMap::iterator i = allAppenders.begin(); i != allAppenders.end(); i++) { + result = result && ((*i).second)->reopen(); + } + + return result; + } + + void Appender::closeAll() { + threading::ScopedLock lock(_appenderMapStorageInstance._appenderMapMutex); + AppenderMap& allAppenders = _getAllAppenders(); + for(AppenderMap::iterator i = allAppenders.begin(); i != allAppenders.end(); i++) { + ((*i).second)->close(); + } + } + + void Appender::_deleteAllAppenders() { + // deleting each appenders will cause a lock on Appender::_appenderMapMutex to be obtained again within destructor. to avoid nested locks: + std::vector appenders; + { + threading::ScopedLock lock(_appenderMapStorageInstance._appenderMapMutex); + AppenderMap& allAppenders = _getAllAppenders(); + appenders.reserve(allAppenders.size()); + for(AppenderMap::iterator i = allAppenders.begin(); i != allAppenders.end(); ) { + Appender* app = (*i).second; + ++i; + appenders.push_back(app); + } + allAppenders.clear(); + } + _deleteAllAppendersWOLock(appenders); + } + + void Appender::_deleteAllAppendersWOLock(std::vector &appenders) { + /* assume Appender::_appenderMapMutex not locked */ + AppenderMap& allAppenders = _getAllAppenders(); + for(std::vector::iterator i = appenders.begin(); i != appenders.end(); ++i) { + Appender *app = (*i); + delete (app); + } + } + + Appender::Appender(const std::string& name) : + _name(name) { + _addAppender(this); + } + + Appender::~Appender() { + _removeAppender(this); + } +} diff --git a/VrUtils/log4cpp/src/AppenderSkeleton.cpp b/VrUtils/log4cpp/src/AppenderSkeleton.cpp new file mode 100644 index 0000000..51bab5f --- /dev/null +++ b/VrUtils/log4cpp/src/AppenderSkeleton.cpp @@ -0,0 +1,60 @@ +/* + * AppenderSkeleton.cpp + * + * Copyright 2001, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2001, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include "PortabilityImpl.hh" +#include + +namespace log4cpp { + + AppenderSkeleton::AppenderSkeleton(const std::string& name) : + Appender(name), + _threshold(Priority::NOTSET), + _filter(NULL) { + } + + AppenderSkeleton::~AppenderSkeleton() { + if (_filter) + delete _filter; + } + + bool AppenderSkeleton::reopen() { + return true; + } + + void AppenderSkeleton::doAppend(const LoggingEvent& event) { + if ((Priority::NOTSET == _threshold) || (event.priority <= _threshold)) { + if (!_filter || (_filter->decide(event) != Filter::DENY)) { + _append(event); + } + } + } + + void AppenderSkeleton::setThreshold(Priority::Value priority) { + _threshold = priority; + } + + Priority::Value AppenderSkeleton::getThreshold() { + return _threshold; + } + + void AppenderSkeleton::setFilter(Filter* filter) { + if (_filter != filter) { + if (_filter) + delete _filter; + + _filter = filter; + } + } + + Filter* AppenderSkeleton::getFilter() { + return _filter; + } + +} + diff --git a/VrUtils/log4cpp/src/AppendersFactory.cpp b/VrUtils/log4cpp/src/AppendersFactory.cpp new file mode 100644 index 0000000..0ed0af8 --- /dev/null +++ b/VrUtils/log4cpp/src/AppendersFactory.cpp @@ -0,0 +1,90 @@ +/* + * Copyright 2002, Log4cpp Project. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + + +#include +#include + +namespace log4cpp +{ + static AppendersFactory* appenders_factory_ = 0; + + std::auto_ptr create_file_appender(const FactoryParams&); + std::auto_ptr create_roll_file_appender(const FactoryParams&); + std::auto_ptr create_daily_roll_file_appender(const FactoryParams&); + std::auto_ptr create_idsa_appender(const FactoryParams&); + std::auto_ptr create_nt_event_log_appender(const FactoryParams&); + std::auto_ptr create_remote_syslog_appender(const FactoryParams&); + std::auto_ptr create_syslog_appender(const FactoryParams&); + std::auto_ptr create_win32_debug_appender(const FactoryParams&); + std::auto_ptr create_abort_appender(const FactoryParams&); + std::auto_ptr create_smtp_appender(const FactoryParams&); + + AppendersFactory& AppendersFactory::getInstance() + { + if (!appenders_factory_) + { + std::auto_ptr af(new AppendersFactory); + + af->registerCreator("file", &create_file_appender); + af->registerCreator("roll file", &create_roll_file_appender); + af->registerCreator("daily roll file", &create_daily_roll_file_appender); +#if !defined(LOG4CPP_DISABLE_REMOTE_SYSLOG) + af->registerCreator("remote syslog", &create_remote_syslog_appender); +#endif + af->registerCreator("abort", &create_abort_appender); + +#if defined(LOG4CPP_HAVE_LIBIDSA) + af->registerCreator("idsa", &create_idsa_appender); +#endif + +#if defined(LOG4CPP_HAVE_SYSLOG) + af->registerCreator("syslog", &create_syslog_appender); +#endif + +#if defined(WIN32) + af->registerCreator("win32 debug", &create_win32_debug_appender); + af->registerCreator("nt event log", &create_nt_event_log_appender); +#endif + +#if !defined(LOG4CPP_DISABLE_SMTP) +#if defined(LOG4CPP_HAVE_BOOST) +#include +#if BOOST_VERSION >= 103500 + af->registerCreator("smtp", &create_smtp_appender); +#endif // BOOST_VERSION >= 103500 +#endif // LOG4CPP_HAVE_BOOST +#endif // LOG4CPP_DISABLE_SMTP + + appenders_factory_ = af.release(); + } + + return *appenders_factory_; + } + + void AppendersFactory::registerCreator(const std::string& class_name, create_function_t create_function) + { + const_iterator i = creators_.find(class_name); + if (i != creators_.end()) + throw std::invalid_argument("Appender creator for type name '" + class_name + "' already registered"); + + creators_[class_name] = create_function; + } + + std::auto_ptr AppendersFactory::create(const std::string& class_name, const params_t& params) + { + const_iterator i = creators_.find(class_name); + if (i == creators_.end()) + throw std::invalid_argument("There is no appender with type name '" + class_name + "'"); + + return (*i->second)(params); + } + + bool AppendersFactory::registered(const std::string& class_name) const + { + return creators_.end() != creators_.find(class_name); + } +} diff --git a/VrUtils/log4cpp/src/BasicConfigurator.cpp b/VrUtils/log4cpp/src/BasicConfigurator.cpp new file mode 100644 index 0000000..b7797a3 --- /dev/null +++ b/VrUtils/log4cpp/src/BasicConfigurator.cpp @@ -0,0 +1,35 @@ +/* + * BasicConfigurator.cpp + * + * Copyright 2002, Log4cpp Project. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ +#include "PortabilityImpl.hh" + +#ifdef LOG4CPP_HAVE_IO_H +# include +#endif +#ifdef LOG4CPP_HAVE_UNISTD_H +# include +#endif + +#include +#include +#include +#include +#include + +namespace log4cpp { + + void BasicConfigurator::configure() { + Category& root = Category::getRoot(); + root.setPriority(Priority::INFO); + root.removeAllAppenders(); + root.addAppender(new FileAppender("_", ::dup(fileno(stdout)))); + } + +} + + + diff --git a/VrUtils/log4cpp/src/BasicLayout.cpp b/VrUtils/log4cpp/src/BasicLayout.cpp new file mode 100644 index 0000000..904df20 --- /dev/null +++ b/VrUtils/log4cpp/src/BasicLayout.cpp @@ -0,0 +1,43 @@ +/* + * BasicLayout.cpp + * + * Copyright 2000, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2000, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include "PortabilityImpl.hh" +#include +#include +#include +#include + +#ifdef LOG4CPP_HAVE_SSTREAM +#include +#endif + +namespace log4cpp { + + BasicLayout::BasicLayout() { + } + + BasicLayout::~BasicLayout() { + } + + std::string BasicLayout::format(const LoggingEvent& event) { + std::ostringstream message; + + const std::string& priorityName = Priority::getPriorityName(event.priority); + message << event.timeStamp.getSeconds() << " " << priorityName << " " + << event.categoryName << " " << event.ndc << ": " + << event.message << std::endl; + + return message.str(); + } + + std::auto_ptr create_basic_layout(const FactoryParams& params) + { + return std::auto_ptr(new BasicLayout); + } +} diff --git a/VrUtils/log4cpp/src/BufferingAppender.cpp b/VrUtils/log4cpp/src/BufferingAppender.cpp new file mode 100644 index 0000000..7b36398 --- /dev/null +++ b/VrUtils/log4cpp/src/BufferingAppender.cpp @@ -0,0 +1,53 @@ +/* + * Copyright 2002, Log4cpp Project. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include "PortabilityImpl.hh" +#include +#include +#include +#include + +namespace log4cpp +{ + BufferingAppender::BufferingAppender(const std::string name, unsigned long max_size, + std::auto_ptr sink, std::auto_ptr evaluator) + :LayoutAppender(name), max_size_(max_size), sink_(sink), evaluator_(evaluator), lossy_(false) + { + max_size_ = (max)(1UL, max_size_); + } + + void BufferingAppender::_append(const LoggingEvent& event) + { + if (queue_.size() == max_size_) + if (lossy_) + queue_.pop_back(); + else + dump(); + + queue_.push_front(event); + + if (evaluator_->eval(event)) + { + dump(); + queue_.clear(); + } + } + + static const std::string EMPTY; + + void BufferingAppender::dump() + { + Layout& layout = _getLayout(); + std::ostringstream s; + // Solaris 10 CC can't work with const_reverse_iterator + for(queue_t::reverse_iterator i = queue_.rbegin(), last = queue_.rend(); i != last; ++i) + s << layout.format(*i); + + LoggingEvent event(EMPTY, s.str(), EMPTY, Priority::NOTSET); + sink_->doAppend(event); + } +} + diff --git a/VrUtils/log4cpp/src/Category.cpp b/VrUtils/log4cpp/src/Category.cpp new file mode 100644 index 0000000..1516a19 --- /dev/null +++ b/VrUtils/log4cpp/src/Category.cpp @@ -0,0 +1,430 @@ +/* + * Category.cpp + * + * Copyright 2000, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2000, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include "PortabilityImpl.hh" + +#ifdef LOG4CPP_HAVE_UNISTD_H +# include +#endif + +#include +#include +#include +#include "StringUtil.hh" + +namespace log4cpp { + + Category& Category::getRoot() { + return getInstance(""); + } + + void Category::setRootPriority(Priority::Value priority) { + getRoot().setPriority(priority); + } + + Priority::Value Category::getRootPriority() throw() { + return getRoot().getPriority(); + } + + Category& Category::getInstance(const std::string& name) { + return HierarchyMaintainer::getDefaultMaintainer().getInstance(name); + } + + Category* Category::exists(const std::string& name) { + return HierarchyMaintainer::getDefaultMaintainer().getExistingInstance(name); + } + + std::vector* Category::getCurrentCategories() { + return HierarchyMaintainer::getDefaultMaintainer(). + getCurrentCategories(); + } + + void Category::shutdown() { + HierarchyMaintainer::getDefaultMaintainer().shutdown(); + } + + void Category::shutdownForced() { + HierarchyMaintainer::getDefaultMaintainer().shutdown(); + Appender::_deleteAllAppenders(); + } + + + Category::Category(const std::string& name, Category* parent, Priority::Value priority) : + _name(name), + _parent(parent), + _priority(priority), + _isAdditive(true) { + } + + Category::~Category() { + removeAllAppenders(); + } + + const std::string& Category::getName() const throw() { + return _name; + } + + Priority::Value Category::getPriority() const throw() { + return _priority; + } + + void Category::setPriority(Priority::Value priority) { + if ((priority < Priority::NOTSET) || (getParent() != NULL)) { + _priority = priority; + } else { + /* caller tried to set NOTSET priority to root Category. + Bad caller! + */ + throw std::invalid_argument("cannot set priority NOTSET on Root Category"); + } + } + + Priority::Value Category::getChainedPriority() const throw() { + // REQUIRE(rootCategory->getPriority() != Priority::NOTSET) + + const Category* c = this; + while(c->getPriority() >= Priority::NOTSET) { + c = c->getParent(); + } + + return c->getPriority(); + } + + void Category::addAppender(Appender* appender) { + if (appender) { + threading::ScopedLock lock(_appenderSetMutex); + { + AppenderSet::iterator i = _appender.find(appender); + if (_appender.end() == i) { + // not found + _appender.insert(appender); + _ownsAppender[appender] = true; + } + } + } else { + throw std::invalid_argument("NULL appender"); + } + } + + void Category::addAppender(Appender& appender) { + threading::ScopedLock lock(_appenderSetMutex); + { + AppenderSet::iterator i = _appender.find(&appender); + if (_appender.end() == i) { + _appender.insert(&appender); + _ownsAppender[&appender] = false; + } + } + } + + Appender* Category::getAppender() const { + threading::ScopedLock lock(_appenderSetMutex); + { + AppenderSet::const_iterator i = _appender.begin(); + return (_appender.end() == i) ? NULL : *i; + } + } + + Appender* Category::getAppender(const std::string& name) const { + threading::ScopedLock lock(_appenderSetMutex); + { + AppenderSet::const_iterator i = _appender.begin(); + if (_appender.end() != i) { + // found + return((*i)->getAppender(name)); + } + else { + return(NULL); + } + } + } + + AppenderSet Category::getAllAppenders() const { + threading::ScopedLock lock(_appenderSetMutex); + { + return _appender; + } + } + + void Category::removeAllAppenders() { + threading::ScopedLock lock(_appenderSetMutex); + { + for (AppenderSet::iterator i = _appender.begin(); + i != _appender.end(); i++) { + // found + OwnsAppenderMap::iterator i2; + if (ownsAppender(*i, i2)) { + delete (*i); + } + } + + _ownsAppender.clear(); + _appender.clear(); + } + } + + void Category::removeAppender(Appender* appender) { + threading::ScopedLock lock(_appenderSetMutex); + { + AppenderSet::iterator i = _appender.find(appender); + if (_appender.end() != i) { + OwnsAppenderMap::iterator i2; + if (ownsAppender(*i, i2)) { + _ownsAppender.erase(i2); + delete (*i); + } + _appender.erase(i); + } else { + // appender not found + } + } + } + + bool Category::ownsAppender(Appender* appender) const throw() { + bool owned = false; + + threading::ScopedLock lock(_appenderSetMutex); + { + if (NULL != appender) { + OwnsAppenderMap::const_iterator i = + _ownsAppender.find(appender); + if (_ownsAppender.end() != i) { + owned = (*i).second; + } + } + } + + return owned; + } + + /* assume lock is held */ + bool Category::ownsAppender(Appender* appender, + Category::OwnsAppenderMap::iterator& i2) throw() { + bool owned = false; + + if (NULL != appender) { + OwnsAppenderMap::iterator i = _ownsAppender.find(appender); + if (_ownsAppender.end() != i) { + owned = (*i).second; + if (owned) { + i2 = i; + } + } + } + + return owned; + } + + void Category::callAppenders(const LoggingEvent& event) throw() { + threading::ScopedLock lock(_appenderSetMutex); + { + if (!_appender.empty()) { + for(AppenderSet::const_iterator i = _appender.begin(); + i != _appender.end(); i++) { + (*i)->doAppend(event); + } + } + } + if (getAdditivity() && (getParent() != NULL)) { + getParent()->callAppenders(event); + } + } + + void Category::setAdditivity(bool additivity) { + _isAdditive = additivity; + } + + bool Category::getAdditivity() const throw() { + return _isAdditive; + } + + Category* Category::getParent() throw() { + return _parent; + } + + const Category* Category::getParent() const throw() { + return _parent; + } + + void Category::_logUnconditionally(Priority::Value priority, + const char* format, + va_list arguments) throw() { + _logUnconditionally2(priority, StringUtil::vform(format, arguments)); + } + + void Category::_logUnconditionally2(Priority::Value priority, + const std::string& message) throw() { + LoggingEvent event(getName(), message, NDC::get(), priority); + callAppenders(event); + } + + bool Category::isPriorityEnabled(Priority::Value priority) const throw() { + return(getChainedPriority() >= priority); + } + + void Category::log(Priority::Value priority, + const char* stringFormat, ...) throw() { + if (isPriorityEnabled(priority)) { + va_list va; + va_start(va, stringFormat); + _logUnconditionally(priority, stringFormat, va); + va_end(va); + } + } + + void Category::log(Priority::Value priority, + const std::string& message) throw() { + if (isPriorityEnabled(priority)) + _logUnconditionally2(priority, message); + } + + void Category::logva(Priority::Value priority, + const char* stringFormat, + va_list va) throw() { + if (isPriorityEnabled(priority)) { + _logUnconditionally(priority, stringFormat, va); + } + } + + void Category::debug(const char* stringFormat, ...) throw() { + if (isPriorityEnabled(Priority::DEBUG)) { + va_list va; + va_start(va,stringFormat); + _logUnconditionally(Priority::DEBUG, stringFormat, va); + va_end(va); + } + } + + void Category::debug(const std::string& message) throw() { + if (isPriorityEnabled(Priority::DEBUG)) + _logUnconditionally2(Priority::DEBUG, message); + } + + void Category::info(const char* stringFormat, ...) throw() { + if (isPriorityEnabled(Priority::INFO)) { + va_list va; + va_start(va,stringFormat); + _logUnconditionally(Priority::INFO, stringFormat, va); + va_end(va); + } + } + + void Category::info(const std::string& message) throw() { + if (isPriorityEnabled(Priority::INFO)) + _logUnconditionally2(Priority::INFO, message); + } + + void Category::notice(const char* stringFormat, ...) throw() { + if (isPriorityEnabled(Priority::NOTICE)) { + va_list va; + va_start(va,stringFormat); + _logUnconditionally(Priority::NOTICE, stringFormat, va); + va_end(va); + } + } + + void Category::notice(const std::string& message) throw() { + if (isPriorityEnabled(Priority::NOTICE)) + _logUnconditionally2(Priority::NOTICE, message); + } + + void Category::warn(const char* stringFormat, ...) throw() { + if (isPriorityEnabled(Priority::WARN)) { + va_list va; + va_start(va,stringFormat); + _logUnconditionally(Priority::WARN, stringFormat, va); + va_end(va); + } + } + + void Category::warn(const std::string& message) throw() { + if (isPriorityEnabled(Priority::WARN)) + _logUnconditionally2(Priority::WARN, message); + } + + void Category::error(const char* stringFormat, ...) throw() { + if (isPriorityEnabled(Priority::ERROR)) { + va_list va; + va_start(va,stringFormat); + _logUnconditionally(Priority::ERROR, stringFormat, va); + va_end(va); + } + } + + void Category::error(const std::string& message) throw() { + if (isPriorityEnabled(Priority::ERROR)) + _logUnconditionally2(Priority::ERROR, message); + } + + void Category::crit(const char* stringFormat, ...) throw() { + if (isPriorityEnabled(Priority::CRIT)) { + va_list va; + va_start(va,stringFormat); + _logUnconditionally(Priority::CRIT, stringFormat, va); + va_end(va); + } + } + + void Category::crit(const std::string& message) throw() { + if (isPriorityEnabled(Priority::CRIT)) + _logUnconditionally2(Priority::CRIT, message); + } + + void Category::alert(const char* stringFormat, ...) throw() { + if (isPriorityEnabled(Priority::ALERT)) { + va_list va; + va_start(va,stringFormat); + _logUnconditionally(Priority::ALERT, stringFormat, va); + va_end(va); + } + } + + void Category::alert(const std::string& message) throw() { + if (isPriorityEnabled(Priority::ALERT)) + _logUnconditionally2(Priority::ALERT, message); + } + + void Category::emerg(const char* stringFormat, ...) throw() { + if (isPriorityEnabled(Priority::EMERG)) { + va_list va; + va_start(va,stringFormat); + _logUnconditionally(Priority::EMERG, stringFormat, va); + va_end(va); + } + } + + void Category::emerg(const std::string& message) throw() { + if (isPriorityEnabled(Priority::EMERG)) + _logUnconditionally2(Priority::EMERG, message); + } + + void Category::fatal(const char* stringFormat, ...) throw() { + if (isPriorityEnabled(Priority::FATAL)) { + va_list va; + va_start(va,stringFormat); + _logUnconditionally(Priority::FATAL, stringFormat, va); + va_end(va); + } + } + + void Category::fatal(const std::string& message) throw() { + if (isPriorityEnabled(Priority::FATAL)) + _logUnconditionally2(Priority::FATAL, message); + } + + CategoryStream Category::getStream(Priority::Value priority) { + return CategoryStream(*this, isPriorityEnabled(priority) ? + priority : Priority::NOTSET); + } + + CategoryStream Category::operator<<(Priority::Value priority) { + return getStream(priority); + } +} + diff --git a/VrUtils/log4cpp/src/CategoryStream.cpp b/VrUtils/log4cpp/src/CategoryStream.cpp new file mode 100644 index 0000000..41aa09e --- /dev/null +++ b/VrUtils/log4cpp/src/CategoryStream.cpp @@ -0,0 +1,110 @@ +/* + * CategoryStream.cpp + * + * Copyright 2001, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2001, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include "PortabilityImpl.hh" + +#ifdef LOG4CPP_HAVE_UNISTD_H +# include +#endif + +#include +#include + +namespace log4cpp { + + CategoryStream::CategoryStream(Category& category, Priority::Value priority) : + _category(category), + _priority(priority), + _buffer(NULL) { + } + + CategoryStream::~CategoryStream() { + flush(); + } + + void CategoryStream::flush() { + if (_buffer) { + getCategory().log(getPriority(), _buffer->str()); + delete _buffer; + _buffer = NULL; + } + } + + CategoryStream& CategoryStream::operator<<(const char* t) + { + if (getPriority() != Priority::NOTSET) { + if (!_buffer) { + if (!(_buffer = new std::ostringstream)) { + // XXX help help help + } + } + (*_buffer) << t; + } + return *this; + } + + std::streamsize CategoryStream::width(std::streamsize wide ) { + if (getPriority() != Priority::NOTSET) { + if (!_buffer) { + if (!(_buffer = new std::ostringstream)) { + // XXX help help help + } + } + } + return _buffer->width(wide); + } + CategoryStream& CategoryStream::operator<< (cspf pf) { + return (*pf)(*this); + } + CategoryStream& eol (CategoryStream& os) { + if (os._buffer) { + os.flush(); + } + return os; + } + CategoryStream& left (CategoryStream& os) { + if (os._buffer) { + os._buffer->setf(std::ios::left); + } + return os; + } +} + +// MSVC6 bug in member templates instantiation +#if defined(_MSC_VER) && _MSC_VER < 1300 +namespace +{ + struct dummy + { + void instantiate() + { + using namespace log4cpp; + + CategoryStream t(Category::getInstance(""), Priority::DEBUG); + t << static_cast("") + << Priority::DEBUG + << static_cast(0) + << static_cast(0) + << static_cast(0) + << static_cast(0) + << static_cast(0) + << static_cast(0) + << static_cast(0) + << static_cast(0) + << static_cast(0) + << static_cast(0.0) + << static_cast(0.0) +#if LOG4CPP_HAS_WCHAR_T != 0 + << std::wstring() +#endif + << std::string(); + } + }; +} +#endif diff --git a/VrUtils/log4cpp/src/Configurator.cpp b/VrUtils/log4cpp/src/Configurator.cpp new file mode 100644 index 0000000..77f42ca --- /dev/null +++ b/VrUtils/log4cpp/src/Configurator.cpp @@ -0,0 +1,20 @@ +/* + * Configurator.cpp + * + * Copyright 2001, Glen Scott. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ +#include "PortabilityImpl.hh" +#include + +namespace log4cpp { + + ConfigureFailure::ConfigureFailure(const std::string& reason) : + std::runtime_error(reason) { + } +} + + + + diff --git a/VrUtils/log4cpp/src/DailyRollingFileAppender.cpp b/VrUtils/log4cpp/src/DailyRollingFileAppender.cpp new file mode 100644 index 0000000..64f5374 --- /dev/null +++ b/VrUtils/log4cpp/src/DailyRollingFileAppender.cpp @@ -0,0 +1,186 @@ +/* + * DailyRollingFileAppender.cpp + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include "PortabilityImpl.hh" +#ifdef LOG4CPP_HAVE_IO_H +# include +#endif +#ifdef LOG4CPP_HAVE_UNISTD_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef LOG4CPP_HAVE_SSTREAM +#include +#include +#endif + +#ifndef WIN32 // only available on Win32 +#include +#endif + +namespace log4cpp { + + unsigned int DailyRollingFileAppender::maxDaysToKeepDefault = 30; + + DailyRollingFileAppender::DailyRollingFileAppender(const std::string& name, + const std::string& fileName, + unsigned int maxDaysToKeep, + bool append, + mode_t mode) : + FileAppender(name, fileName, append, mode), + _maxDaysToKeep(maxDaysToKeep != 0 ? maxDaysToKeep : maxDaysToKeepDefault) { + struct stat statBuf; + int res; + time_t t; + + // obtain last modification time + res = ::stat(fileName.c_str(), &statBuf); + if (res < 0) { + t = time(NULL); + } else { + t = statBuf.st_mtime; + } +#ifndef WIN32 // only available on Win32 + localtime_r(&t, &_logsTime); +#else + localtime_s(&_logsTime, &t); +#endif + } + + void DailyRollingFileAppender::setMaxDaysToKeep(unsigned int maxDaysToKeep) { + _maxDaysToKeep = maxDaysToKeep; + } + + unsigned int DailyRollingFileAppender::getMaxDaysToKeep() const { + return _maxDaysToKeep; + } + + void DailyRollingFileAppender::rollOver() + { + std::ostringstream filename_s; + int res_close = ::close(_fd); + if (res_close != 0) { + std::cerr << "Error closing file " << _fileName << std::endl; + } + filename_s << _fileName << "." << _logsTime.tm_year + 1900 << "-" + << std::setfill('0') << std::setw(2) << _logsTime.tm_mon + 1 << "-" + << std::setw(2) << _logsTime.tm_mday << std::ends; + const std::string lastFn = filename_s.str(); + int res_rename = ::rename(_fileName.c_str(), lastFn.c_str()); + if (res_rename != 0) { + std::cerr << "Error renaming file " << _fileName << " to " << lastFn << std::endl; + } + + _fd = ::open(_fileName.c_str(), _flags, _mode); + if (_fd == -1) { + std::cerr << "Error opening file " << _fileName << std::endl; + } + + const time_t oldest = time(NULL) - _maxDaysToKeep * 60 * 60 * 24; + +#ifndef WIN32 +#define PATHDELIMITER "/" +#else +#define PATHDELIMITER "\\" +#endif + // iterate over files around log file and delete older with same prefix + const std::string::size_type last_delimiter = _fileName.rfind(PATHDELIMITER); + const std::string dirname((last_delimiter == std::string::npos)? "." : _fileName.substr(0, last_delimiter)); + const std::string filname((last_delimiter == std::string::npos)? _fileName : _fileName.substr(last_delimiter+1, _fileName.size()-last_delimiter-1)); +#ifndef WIN32 // only available on Win32 + struct dirent **entries; + int nentries = scandir(dirname.c_str(), &entries, 0, alphasort); + if (nentries < 0) + return; + for (int i = 0; i < nentries; i++) { + struct stat statBuf; + const std::string fullfilename = dirname + PATHDELIMITER + entries[i]->d_name; + int res = ::stat(fullfilename.c_str(), &statBuf); + if ((res == -1) || (!S_ISREG(statBuf.st_mode))) { + free(entries[i]); + continue; + } + if (statBuf.st_mtime < oldest && strstr(entries[i]->d_name, filname.c_str())) { + std::cout << " Deleting " << fullfilename.c_str() << std::endl; + ::unlink(fullfilename.c_str()); + } + free(entries[i]); + } + free(entries); +#else + HANDLE hFind = INVALID_HANDLE_VALUE; + WIN32_FIND_DATA ffd; + const std::string pattern = _fileName + "*"; +#ifdef __WIN_ROS__ + hFind = FindFirstFile((LPCSTR)pattern.c_str(), &ffd); +#else + hFind = FindFirstFile((LPCWSTR)pattern.c_str(), &ffd); +#endif + if (hFind != INVALID_HANDLE_VALUE) { + do { + struct stat statBuf; + const std::string fullfilename = dirname + PATHDELIMITER + std::string((char*)ffd.cFileName); + int res = ::stat(fullfilename.c_str(), &statBuf); + if (res != -1 && statBuf.st_mtime < oldest && !(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + std::cout << "Deleting " << fullfilename << "\n"; + ::unlink(fullfilename.c_str()); + } + } while (FindNextFile(hFind, &ffd) != 0); + + if (GetLastError() != ERROR_NO_MORE_FILES) { + // [XXX] some kind of error happened + } + FindClose(hFind); + hFind = INVALID_HANDLE_VALUE; + } +#endif + } + + void DailyRollingFileAppender::_append(const log4cpp::LoggingEvent &event) + { + struct tm now; + time_t t = time(NULL); + +#ifndef WIN32 // only available on Win32 + bool timeok = localtime_r(&t, &now) != NULL; +#else + bool timeok = localtime_s(&now, &t) == 0; +#endif + if (timeok) { + if ((now.tm_mday != _logsTime.tm_mday) || + (now.tm_mon != _logsTime.tm_mon) || + (now.tm_year != _logsTime.tm_year)) { + rollOver(); + _logsTime = now; + } + } + log4cpp::FileAppender::_append(event); + } + + std::auto_ptr create_daily_roll_file_appender(const FactoryParams& params) + { + std::string name, filename; + bool append = true; + mode_t mode = 664; + unsigned int max_days_keep = 0; + params.get_for("daily roll file appender").required("name", name)("filename", filename)("max_days_keep", max_days_keep) + .optional("append", append)("mode", mode); + + return std::auto_ptr(new DailyRollingFileAppender(name, filename, max_days_keep, append, mode)); + } +} diff --git a/VrUtils/log4cpp/src/DllMain.cpp b/VrUtils/log4cpp/src/DllMain.cpp new file mode 100644 index 0000000..0cae2b9 --- /dev/null +++ b/VrUtils/log4cpp/src/DllMain.cpp @@ -0,0 +1,31 @@ +/* + * DllMain.cpp + * + * Copyright 2000, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2000, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifdef LOG4CPP_SUPPLY_DLLMAIN + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#include + +BOOL APIENTRY DllMain( HANDLE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + +#endif // LOG4CPP_NEED_DLLMAIN diff --git a/VrUtils/log4cpp/src/DummyThreads.cpp b/VrUtils/log4cpp/src/DummyThreads.cpp new file mode 100644 index 0000000..ffe16bf --- /dev/null +++ b/VrUtils/log4cpp/src/DummyThreads.cpp @@ -0,0 +1,22 @@ +/* + * Copyright 2002, Log4cpp Project. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include + +#ifndef LOG4CPP_HAVE_THREADING + +namespace log4cpp { + namespace threading { + + std::string getThreadId() { + // more useful would be to return the PID. + return std::string("thread1"); + } + + } +} + +#endif // LOG4CPP_HAVE_THREADING diff --git a/VrUtils/log4cpp/src/FactoryParams.cpp b/VrUtils/log4cpp/src/FactoryParams.cpp new file mode 100644 index 0000000..cc648f9 --- /dev/null +++ b/VrUtils/log4cpp/src/FactoryParams.cpp @@ -0,0 +1,26 @@ +/* + * Copyright 2002, Log4cpp Project. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include +#include + +namespace log4cpp +{ + const std::string& FactoryParams::operator[](const std::string& v) const + { + const_iterator i = storage_.find(v); + if (i != storage_.end()) + return i->second; + + throw std::invalid_argument("There is no parameter '" + v + "'"); + } + + FactoryParams::const_iterator FactoryParams::find(const std::string& v) const + { + return storage_.find(v); + } +} + diff --git a/VrUtils/log4cpp/src/FileAppender.cpp b/VrUtils/log4cpp/src/FileAppender.cpp new file mode 100644 index 0000000..a2731f2 --- /dev/null +++ b/VrUtils/log4cpp/src/FileAppender.cpp @@ -0,0 +1,113 @@ +/* + * FileAppender.cpp + * + * Copyright 2000, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2000, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include "PortabilityImpl.hh" +#ifdef LOG4CPP_HAVE_IO_H +# include +#endif +#ifdef LOG4CPP_HAVE_UNISTD_H +# include +#endif + +#include +#include +#include +#include +#include +#include + +namespace log4cpp { + + FileAppender::FileAppender(const std::string& name, + const std::string& fileName, + bool append, + mode_t mode) : + LayoutAppender(name), + _fileName(fileName), + _flags(O_CREAT | O_APPEND | O_WRONLY), + _mode(mode) { + if (!append) + _flags |= O_TRUNC; + _fd = ::open(_fileName.c_str(), _flags, _mode); + } + + FileAppender::FileAppender(const std::string& name, int fd) : + LayoutAppender(name), + _fileName(""), + _fd(fd), + _flags(O_CREAT | O_APPEND | O_WRONLY), + _mode(00644) { + } + + FileAppender::~FileAppender() { + close(); + } + + void FileAppender::close() { + if (_fd!=-1) { + ::close(_fd); + _fd=-1; + } + } + + void FileAppender::setAppend(bool append) { + if (append) { + _flags &= ~O_TRUNC; + } else { + _flags |= O_TRUNC; + } + } + + bool FileAppender::getAppend() const { + return (_flags & O_TRUNC) == 0; + } + + void FileAppender::setMode(mode_t mode) { + _mode = mode; + } + + mode_t FileAppender::getMode() const { + return _mode; + } + + void FileAppender::_append(const LoggingEvent& event) { + std::string message(_getLayout().format(event)); + if (_fd == -1 || !::write(_fd, message.data(), message.length())) { + // XXX help! help! + } + } + + bool FileAppender::reopen() { + if (_fileName != "") { + int fd = ::open(_fileName.c_str(), _flags, _mode); + if (fd < 0) + return false; + else { + if (_fd != -1) + ::close(_fd); + _fd = fd; + return true; + } + } else { + return true; + } + } + + std::auto_ptr create_file_appender(const FactoryParams& params) + { + std::string name, filename; + bool append = true; + mode_t mode = 664; + + params.get_for("file appender").required("name", name)("filename", filename) + .optional("append", append)("mode", mode); + + return std::auto_ptr(new FileAppender(name, filename, append, mode)); + } +} diff --git a/VrUtils/log4cpp/src/Filter.cpp b/VrUtils/log4cpp/src/Filter.cpp new file mode 100644 index 0000000..fb13219 --- /dev/null +++ b/VrUtils/log4cpp/src/Filter.cpp @@ -0,0 +1,60 @@ +/* + * Filter.cpp + * + * Copyright 2001, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2001, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include "PortabilityImpl.hh" +#include + +namespace log4cpp { + + Filter::Filter() : + _chainedFilter(NULL) { + } + + Filter::~Filter() { + if (_chainedFilter) + delete _chainedFilter; + } + + void Filter::setChainedFilter(Filter* filter) { + if (filter != _chainedFilter) { + if (_chainedFilter) + delete _chainedFilter; + + _chainedFilter = filter; + } + } + + Filter* Filter::getChainedFilter() { + return _chainedFilter; + } + + Filter* Filter::getEndOfChain() { + Filter* end = this; + while(end->getChainedFilter()) { + end = end->getChainedFilter(); + } + return end; + } + + void Filter::appendChainedFilter(Filter* filter) { + Filter* end = getEndOfChain(); + end->setChainedFilter(filter); + } + + Filter::Decision Filter::decide(const LoggingEvent& event) { + Filter::Decision decision = _decide(event); + + if ((Filter::NEUTRAL == decision) && getChainedFilter()) { + decision = getChainedFilter()->decide(event); + } + + return decision; + } + +} diff --git a/VrUtils/log4cpp/src/FixedContextCategory.cpp b/VrUtils/log4cpp/src/FixedContextCategory.cpp new file mode 100644 index 0000000..ef0f03a --- /dev/null +++ b/VrUtils/log4cpp/src/FixedContextCategory.cpp @@ -0,0 +1,100 @@ +/* + * FixedContextCategory.cpp + * + * Copyright 2001, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2001, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include "PortabilityImpl.hh" +#include + +namespace log4cpp { + + FixedContextCategory::FixedContextCategory(const std::string& name, + const std::string& context) : + Category(name, Category::getInstance(name).getParent()), + _delegate(Category::getInstance(name)), + _context(context) { + } + + FixedContextCategory::~FixedContextCategory() { + } + + void FixedContextCategory::setContext(const std::string& context) { + _context = context; + } + + std::string FixedContextCategory::getContext() const { + return _context; + } + + Priority::Value FixedContextCategory::getPriority() const throw() { + return Category::getPriority(); + } + + Priority::Value FixedContextCategory::getChainedPriority() const throw() { + Priority::Value result = getPriority(); + + if (result == Priority::NOTSET) { + result = _delegate.getChainedPriority(); + } + + return result; + } + + void FixedContextCategory::addAppender(Appender* appender) throw() { + // XXX do nothing for now + } + + void FixedContextCategory::addAppender(Appender& appender) { + // XXX do nothing for now + } + + Appender* FixedContextCategory::getAppender() const { + return _delegate.getAppender(); + } + + Appender* FixedContextCategory::getAppender(const std::string& name) + const { + return _delegate.getAppender(name); + } + + AppenderSet FixedContextCategory::getAllAppenders() const { + return _delegate.getAllAppenders(); + } + + void FixedContextCategory::removeAllAppenders() { + // XXX do nothing for now + } + + bool FixedContextCategory::ownsAppender() const throw() { + return false; + } + + bool FixedContextCategory::ownsAppender(Appender* appender) const throw() { + return false; + } + + void FixedContextCategory::callAppenders(const LoggingEvent& event) + throw() { + _delegate.callAppenders(event); + } + + void FixedContextCategory::setAdditivity(bool additivity) { + // XXX do nothing for now + } + + bool FixedContextCategory::getAdditivity() const throw() { + return _delegate.getAdditivity(); + } + + void FixedContextCategory::_logUnconditionally2(Priority::Value priority, + const std::string& message) throw() { + LoggingEvent event(getName(), message, _context, priority); + callAppenders(event); + } + +} + diff --git a/VrUtils/log4cpp/src/HierarchyMaintainer.cpp b/VrUtils/log4cpp/src/HierarchyMaintainer.cpp new file mode 100644 index 0000000..283980b --- /dev/null +++ b/VrUtils/log4cpp/src/HierarchyMaintainer.cpp @@ -0,0 +1,132 @@ +/* + * HierarchyMaintainer.cpp + * + * Copyright 2000, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2000, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include "PortabilityImpl.hh" + +#ifdef LOG4CPP_HAVE_IO_H +# include +#endif +#ifdef LOG4CPP_HAVE_UNISTD_H +# include +#endif + +#include +#include +#include + +namespace log4cpp { + + HierarchyMaintainer& HierarchyMaintainer::getDefaultMaintainer() { + static HierarchyMaintainer defaultMaintainer; + + return defaultMaintainer; + } + + HierarchyMaintainer::HierarchyMaintainer() { + } + + HierarchyMaintainer::~HierarchyMaintainer() { + shutdown(); + deleteAllCategories(); + } + + Category* HierarchyMaintainer::getExistingInstance(const std::string& name) { + threading::ScopedLock lock(_categoryMutex); + return _getExistingInstance(name); + } + + Category* HierarchyMaintainer::_getExistingInstance(const std::string& name) { + Category* result = NULL; + + CategoryMap::iterator i = _categoryMap.find(name); + if (_categoryMap.end() != i) { + result = (*i).second; + } + + return result; + } + + Category& HierarchyMaintainer::getInstance(const std::string& name) { + threading::ScopedLock lock(_categoryMutex); + return _getInstance(name); + } + + /* assume lock is held */ + Category& HierarchyMaintainer::_getInstance(const std::string& name) { + Category* result; + result = _getExistingInstance(name); + + if (NULL == result) { + if (name == "") { + result = new Category(name, NULL, Priority::INFO); + } else { + std::string parentName; + size_t dotIndex = name.find_last_of('.'); + if (name.length() <= dotIndex) { + parentName = ""; + } else { + parentName = name.substr(0, dotIndex); + } + Category& parent = _getInstance(parentName); + result = new Category(name, &parent, Priority::NOTSET); + } + _categoryMap[name] = result; + } + return *result; + } + + std::vector* HierarchyMaintainer::getCurrentCategories() const { + std::vector* categories = new std::vector; + + threading::ScopedLock lock(_categoryMutex); + { + for(CategoryMap::const_iterator i = _categoryMap.begin(); i != _categoryMap.end(); i++) { + categories->push_back((*i).second); + } + } + + return categories; + } + + void HierarchyMaintainer::shutdown() { + threading::ScopedLock lock(_categoryMutex); + { + for(CategoryMap::const_iterator i = _categoryMap.begin(); i != _categoryMap.end(); i++) { + ((*i).second)->removeAllAppenders(); + } + } + + try + { + for(handlers_t::const_iterator i = handlers_.begin(), last = handlers_.end(); i != last; ++i) + (**i)(); + } + catch(...) + { + } + + } + + void HierarchyMaintainer::register_shutdown_handler(shutdown_fun_ptr handler) + { + handlers_.push_back(handler); + } + + void HierarchyMaintainer::deleteAllCategories() { + threading::ScopedLock lock(_categoryMutex); + { + for(CategoryMap::const_iterator i = _categoryMap.begin(); i != _categoryMap.end(); i++) { + delete ((*i).second); + } + _categoryMap.clear(); + } + } + + +} diff --git a/VrUtils/log4cpp/src/IdsaAppender.cpp b/VrUtils/log4cpp/src/IdsaAppender.cpp new file mode 100644 index 0000000..75aa762 --- /dev/null +++ b/VrUtils/log4cpp/src/IdsaAppender.cpp @@ -0,0 +1,89 @@ +/* + * IdsaAppender.cpp + * + * Copyright 2000, Marc Welz + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include "PortabilityImpl.hh" + +#ifdef LOG4CPP_HAVE_LIBIDSA + +#include +#include +#include +#include +#include +#include +#include + +namespace log4cpp { + + IdsaAppender::IdsaAppender(const std::string& name, + const std::string& idsaName) : + AppenderSkeleton(name), + _idsaName(idsaName) + { + _idsaConnection=NULL; + open(); + } + + IdsaAppender::~IdsaAppender() { + close(); + } + + void IdsaAppender::open() { + _idsaConnection=idsa_open((char *)_idsaName.c_str(), NULL, + IDSA_F_FAILOPEN); + } + + void IdsaAppender::close() { + idsa_close(_idsaConnection); + _idsaConnection=NULL; + } + + void IdsaAppender::_append(const LoggingEvent& event) { + IDSA_EVENT *idsaEvent; + + idsaEvent = idsa_event(_idsaConnection); + + if (idsaEvent){ + + idsa_name(idsaEvent,(char *)event.categoryName.c_str()); + idsa_scheme(idsaEvent,"log4cpp"); + + idsa_add_integer(idsaEvent, "priority", event.priority); + idsa_add_string(idsaEvent, "ndc", (char *)event.ndc.c_str()); + idsa_add_string(idsaEvent, "message", + (char*)event.message.c_str()); + + idsa_log(_idsaConnection,idsaEvent); + // idsa_log does its own deallocation */ + } + } + + bool IdsaAppender::reopen() { + close(); + open(); + return true; + } + + bool IdsaAppender::requiresLayout() const { + return false; + } + + void IdsaAppender::setLayout(Layout* layout) { + return; + } + + std::auto_ptr create_idsa_appender(const FactoryParams& params) + { + std::string name, idsa_name; + params.get_for("idsa appender").required("name", name)("idsa_name", idsa_name); + + return std::auto_ptr(new IdsaAppender(name, idsa_name)); + } +} + +#endif // LOG4CPP_HAVE_LIBIDSA diff --git a/VrUtils/log4cpp/src/LayoutAppender.cpp b/VrUtils/log4cpp/src/LayoutAppender.cpp new file mode 100644 index 0000000..5d2f3ba --- /dev/null +++ b/VrUtils/log4cpp/src/LayoutAppender.cpp @@ -0,0 +1,39 @@ +/* + * LayoutAppender.cpp + * + * Copyright 2000, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2000, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include "PortabilityImpl.hh" +#include + +namespace log4cpp { + + LayoutAppender::LayoutAppender(const std::string& name) : + AppenderSkeleton(name), + _layout(new DefaultLayoutType()) { + } + + LayoutAppender::~LayoutAppender() { + delete _layout; + } + + bool LayoutAppender::requiresLayout() const { + return true; + } + + void LayoutAppender::setLayout(Layout* layout) { + if (layout != _layout) { + Layout *oldLayout = _layout; + _layout = (layout == NULL) ? new DefaultLayoutType() : layout; + delete oldLayout; + } + } + + Layout& LayoutAppender::_getLayout() { + return *_layout; + } +} diff --git a/VrUtils/log4cpp/src/LayoutsFactory.cpp b/VrUtils/log4cpp/src/LayoutsFactory.cpp new file mode 100644 index 0000000..232fa16 --- /dev/null +++ b/VrUtils/log4cpp/src/LayoutsFactory.cpp @@ -0,0 +1,57 @@ +/* + * Copyright 2002, Log4cpp Project. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include +#include + +namespace log4cpp +{ + static LayoutsFactory* layouts_factory_ = 0;\ + + std::auto_ptr create_simple_layout(const FactoryParams& params); + std::auto_ptr create_basic_layout(const FactoryParams& params); + std::auto_ptr create_pattern_layout(const FactoryParams& params); + std::auto_ptr create_pattern_layout(const FactoryParams& params); + std::auto_ptr create_pass_through_layout(const FactoryParams& params); + + LayoutsFactory& LayoutsFactory::getInstance() + { + if (!layouts_factory_) + { + std::auto_ptr lf(new LayoutsFactory); + lf->registerCreator("simple", &create_simple_layout); + lf->registerCreator("basic", &create_basic_layout); + lf->registerCreator("pattern", &create_pattern_layout); + lf->registerCreator("pass through", &create_pass_through_layout); + layouts_factory_ = lf.release(); + } + + return *layouts_factory_; + } + + void LayoutsFactory::registerCreator(const std::string& class_name, create_function_t create_function) + { + const_iterator i = creators_.find(class_name); + if (i != creators_.end()) + throw std::invalid_argument("Layout creator for type name '" + class_name + "' allready registered"); + + creators_[class_name] = create_function; + } + + std::auto_ptr LayoutsFactory::create(const std::string& class_name, const params_t& params) + { + const_iterator i = creators_.find(class_name); + if (i == creators_.end()) + throw std::invalid_argument("There is no layout with type name '" + class_name + "'"); + + return (*i->second)(params); + } + + bool LayoutsFactory::registed(const std::string& class_name) const + { + return creators_.find(class_name) != creators_.end(); + } +} diff --git a/VrUtils/log4cpp/src/LevelEvaluator.cpp b/VrUtils/log4cpp/src/LevelEvaluator.cpp new file mode 100644 index 0000000..bc030e3 --- /dev/null +++ b/VrUtils/log4cpp/src/LevelEvaluator.cpp @@ -0,0 +1,21 @@ +/* + * Copyright 2002, Log4cpp Project. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include +#include +#include + +namespace log4cpp +{ + std::auto_ptr create_level_evaluator(const FactoryParams& params) + { + std::string level; + params.get_for("level evaluator").required("level", level); + + return std::auto_ptr(new LevelEvaluator(Priority::getPriorityValue(level))); + } +} + diff --git a/VrUtils/log4cpp/src/Localtime.cpp b/VrUtils/log4cpp/src/Localtime.cpp new file mode 100644 index 0000000..64beecd --- /dev/null +++ b/VrUtils/log4cpp/src/Localtime.cpp @@ -0,0 +1,37 @@ +/* + * Copyright 2002, Log4cpp Project. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include +#include "Localtime.hh" +#include +#include + +namespace log4cpp +{ + +#if defined(_MSC_VER) && defined(LOG4CPP_HAVE_LOCALTIME_R) + void localtime(const ::time_t* time, ::tm* t) + { + localtime_s(t, time); + } +#endif + +#if !defined(_MSC_VER) && defined(LOG4CPP_HAVE_LOCALTIME_R) + void localtime(const ::time_t* time, ::tm* t) + { + localtime_r(time, t); + } +#endif + +#if !defined(LOG4CPP_HAVE_LOCALTIME_R) + void localtime(const ::time_t* time, ::tm* t) + { + ::tm* tmp = ::localtime(time); + memcpy(t, tmp, sizeof(::tm)); + } +#endif + +} diff --git a/VrUtils/log4cpp/src/Localtime.hh b/VrUtils/log4cpp/src/Localtime.hh new file mode 100644 index 0000000..9318456 --- /dev/null +++ b/VrUtils/log4cpp/src/Localtime.hh @@ -0,0 +1,17 @@ +/* + * Copyright 2002, Log4cpp Project. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_LOCALTIME_HH +#define _LOG4CPP_LOCALTIME_HH + +#include + +namespace log4cpp +{ + void localtime(const ::time_t* time, ::tm* t); +} + +#endif diff --git a/VrUtils/log4cpp/src/LoggingEvent.cpp b/VrUtils/log4cpp/src/LoggingEvent.cpp new file mode 100644 index 0000000..ba285fc --- /dev/null +++ b/VrUtils/log4cpp/src/LoggingEvent.cpp @@ -0,0 +1,26 @@ +/* + * LoggingEvent.cpp + * + * Copyright 2000, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2000, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include "PortabilityImpl.hh" +#include +#include + +namespace log4cpp { + + LoggingEvent::LoggingEvent(const std::string& categoryName, + const std::string& message, + const std::string& ndc, + Priority::Value priority) : + categoryName(categoryName), + message(message), + ndc(ndc), + priority(priority), + threadName(threading::getThreadId()) { + } +} diff --git a/VrUtils/log4cpp/src/MSThreads.cpp b/VrUtils/log4cpp/src/MSThreads.cpp new file mode 100644 index 0000000..17a4449 --- /dev/null +++ b/VrUtils/log4cpp/src/MSThreads.cpp @@ -0,0 +1,23 @@ +/* + * Copyright 2002, Log4cpp Project. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include +#include + +#if defined(LOG4CPP_HAVE_THREADING) && defined(LOG4CPP_USE_MSTHREADS) + +namespace log4cpp { + namespace threading { + + std::string getThreadId() { + char buffer[16]; + sprintf(buffer, "%lu", GetCurrentThreadId()); + return std::string(buffer); + }; + } +} + +#endif // LOG4CPP_HAVE_THREADING && LOG4CPP_USE_MSTHREADS diff --git a/VrUtils/log4cpp/src/Manipulator.cpp b/VrUtils/log4cpp/src/Manipulator.cpp new file mode 100644 index 0000000..e3e730f --- /dev/null +++ b/VrUtils/log4cpp/src/Manipulator.cpp @@ -0,0 +1,22 @@ +/* + * Copyright 2002, Log4cpp Project. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include +using namespace std; +namespace log4cpp { + ostream& operator<< (ostream& os, const width& w) { + if (os.good()) { + os.width(w.size); + } + return os; + } + ostream& operator<< (ostream& os, const tab& t) { + if (os.good()) { + for(size_t no = 0; no < t.size; no++) os.put(os.widen('\t')); + } + return os; + } +}; diff --git a/VrUtils/log4cpp/src/NDC.cpp b/VrUtils/log4cpp/src/NDC.cpp new file mode 100644 index 0000000..be824c1 --- /dev/null +++ b/VrUtils/log4cpp/src/NDC.cpp @@ -0,0 +1,128 @@ +/* + * NDC.cpp + * + * Copyright 2000, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2000, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include "PortabilityImpl.hh" +#include +#include + +namespace log4cpp { + + NDC::DiagnosticContext::DiagnosticContext(const std::string& message) : + message(message), + fullMessage(message) { + } + + NDC::DiagnosticContext::DiagnosticContext(const std::string& message, + const DiagnosticContext& parent) : + message(message), + fullMessage(parent.fullMessage + " " + message) { + } + + bool NDC::isUsedNDC = false; + const std::string NDC::emptyString = ""; + + namespace { + threading::ThreadLocalDataHolder _nDC; + } + + void NDC::clear() { + getNDC()._clear(); + } + + NDC::ContextStack* NDC::cloneStack() { + return getNDC()._cloneStack(); + } + + const std::string& NDC::get() { + if (isUsedNDC) + return getNDC()._get(); + else + return emptyString; + } + + size_t NDC::getDepth() { + return getNDC()._getDepth(); + } + + void NDC::inherit(NDC::ContextStack* stack) { + getNDC()._inherit(stack); + } + + std::string NDC::pop() { + return getNDC()._pop(); + } + + void NDC::push(const std::string& message) { + if (!isUsedNDC) + isUsedNDC = true; + getNDC()._push(message); + } + + void NDC::setMaxDepth(int maxDepth) { + getNDC()._setMaxDepth(maxDepth); + } + + NDC& NDC::getNDC() { + NDC* nDC = _nDC.get(); + + if (!nDC) { + nDC = new NDC(); + _nDC.reset(nDC); + } + + return *nDC; + } + + NDC::NDC() { + } + + NDC::~NDC() { + } + + void NDC::_clear() { + _stack.clear(); + } + + NDC::ContextStack* NDC::_cloneStack() { + return new ContextStack(_stack); + } + + const std::string& NDC::_get() const { + static std::string empty = ""; + + return (_stack.empty() ? empty : _stack.back().fullMessage); + } + + size_t NDC::_getDepth() const { + return _stack.size(); + } + + void NDC::_inherit(NDC::ContextStack* stack) { + _stack = *stack; + } + + std::string NDC::_pop() { + std::string result = _stack.back().message; + _stack.pop_back(); + return result; + } + + void NDC::_push(const std::string& message) { + if (_stack.empty()) { + _stack.push_back(DiagnosticContext(message)); + } else { + _stack.push_back(DiagnosticContext(message, _stack.back())); + } + } + + void NDC::_setMaxDepth(int maxDepth) { + // XXX no maximum + } + +} diff --git a/VrUtils/log4cpp/src/NTEventLogAppender.cpp b/VrUtils/log4cpp/src/NTEventLogAppender.cpp new file mode 100644 index 0000000..f03fcf6 --- /dev/null +++ b/VrUtils/log4cpp/src/NTEventLogAppender.cpp @@ -0,0 +1,165 @@ +/* + * NTEventLogAppender.cpp + * + * Copyright 2002, Log4cpp Project. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifdef WIN32 // only available on Win32 + +#include +#include +#include + +namespace log4cpp { + + NTEventLogAppender::NTEventLogAppender(const std::string& name, const std::string& sourceName) : + AppenderSkeleton(name), + _strSourceName(sourceName), + _hEventSource(NULL) + { + open(); + } + + NTEventLogAppender::~NTEventLogAppender() + { + close(); + } + + void NTEventLogAppender::open() + { + addRegistryInfo(_strSourceName.c_str()); +#ifdef __WIN_ROS__ + _hEventSource = ::RegisterEventSource(NULL, (LPCSTR)_strSourceName.c_str()); +#else + _hEventSource = ::RegisterEventSource(NULL, (LPCWSTR)_strSourceName.c_str()); +#endif + } + + void NTEventLogAppender::close() + { + if (_hEventSource) { + ::DeregisterEventSource(_hEventSource); + } + } + + bool NTEventLogAppender::reopen() { + close(); + open(); + return true; + } + + bool NTEventLogAppender::requiresLayout() const { + return false; + } + + void NTEventLogAppender::setLayout(Layout* layout) { + return; + } + + void NTEventLogAppender::_append(const LoggingEvent& event) { + const char* ps[1]; + ps[0] = event.message.c_str(); + + const DWORD messageID = 0x1000; +#ifdef __WIN_ROS__ + ::ReportEvent(_hEventSource, getType(event.priority), getCategory(event.priority), messageID, NULL, 1, 0, (LPCSTR *)ps, NULL); +#else + ::ReportEvent(_hEventSource, getType(event.priority), getCategory(event.priority), messageID, NULL, 1, 0, (LPCWSTR *)ps, NULL); +#endif + } + + /** + * Convert log4cpp Priority to an EventLog category. Each category is + * backed by a message resource so that proper category names will + * be displayed in the NT Event Viewer. + */ + WORD NTEventLogAppender::getCategory(Priority::Value priority) { + // Priority values map directly to EventLog category values + return (WORD)((priority / 100) + 1); + } + + /** + * Convert log4cpp Priority to an EventLog type. The log4cpp package + * supports 8 defined priorites, but the NT EventLog only knows + * 3 event types of interest to us: ERROR, WARNING, and INFO. + */ + WORD NTEventLogAppender::getType(Priority::Value priority) { + + WORD ret_val; + + switch (priority) { + case Priority::EMERG: + // FATAL is the same value as EMERG +// case Priority::FATAL: + case Priority::ALERT: + case Priority::CRIT: + case Priority::ERROR: + ret_val = EVENTLOG_ERROR_TYPE; + break; + case Priority::WARN: + ret_val = EVENTLOG_WARNING_TYPE; + break; + case Priority::NOTICE: + case Priority::INFO: + case Priority::DEBUG: + default: + ret_val = EVENTLOG_INFORMATION_TYPE; + break; + } + return ret_val; + } + + HKEY NTEventLogAppender::regGetKey(TCHAR *subkey, DWORD *disposition) { + HKEY hkey = 0; + RegCreateKeyEx(HKEY_LOCAL_MACHINE, subkey, 0, NULL, + REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, + &hkey, disposition); + return hkey; + } + + void NTEventLogAppender::regSetString(HKEY hkey, TCHAR *name, TCHAR *value) { + RegSetValueEx(hkey, name, 0, REG_SZ, (LPBYTE)value, lstrlen(value)); + } + + void NTEventLogAppender::regSetDword(HKEY hkey, TCHAR *name, DWORD value) { + RegSetValueEx(hkey, name, 0, REG_DWORD, (LPBYTE)&value, sizeof(DWORD)); + } + + /* + * Add this source with appropriate configuration keys to the registry. + */ + void NTEventLogAppender::addRegistryInfo(const char *source) { + const TCHAR *prefix = (TCHAR *)("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\"); + DWORD disposition; + HKEY hkey = 0; + TCHAR subkey[256]; + + lstrcpy(subkey, prefix); +#ifdef __WIN_ROS__ + lstrcat(subkey, (LPCSTR)source); +#else + lstrcat(subkey, (LPCWSTR)source); +#endif + hkey = regGetKey(subkey, &disposition); + if (disposition == REG_CREATED_NEW_KEY) { + regSetString(hkey, (TCHAR*)("EventMessageFile"), (TCHAR*)("NTEventLogAppender.dll")); + regSetString(hkey, (TCHAR*)("CategoryMessageFile"), (TCHAR*)("NTEventLogAppender.dll")); + regSetDword(hkey, (TCHAR*)("TypesSupported"), (DWORD)7); + regSetDword(hkey, (TCHAR*)("CategoryCount"), (DWORD)8); + } + RegCloseKey(hkey); + return; + } + + std::auto_ptr create_nt_event_log_appender(const FactoryParams& params) + { + std::string name, source_name; + params.get_for("nt event log appender").required("name", name)("source_name", source_name); + + return std::auto_ptr(new NTEventLogAppender(name, source_name)); + } +} + +#endif // WIN32 diff --git a/VrUtils/log4cpp/src/OmniThreads.cpp b/VrUtils/log4cpp/src/OmniThreads.cpp new file mode 100644 index 0000000..600c61c --- /dev/null +++ b/VrUtils/log4cpp/src/OmniThreads.cpp @@ -0,0 +1,22 @@ +/* + * Copyright 2002, Log4cpp Project. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include + +#if defined(LOG4CPP_HAVE_THREADING) && defined(LOG4CPP_USE_OMNITHREADS) + +namespace log4cpp { + namespace threading { + + std::string getThreadId() { + char buffer[16]; + sprintf(buffer, "%d", ::omni_thread::self()->id()); + return std::string(buffer); + }; + } +} + +#endif // LOG4CPP_HAVE_THREADING && LOG4CPP_USE_ONMITHREADS diff --git a/VrUtils/log4cpp/src/OstreamAppender.cpp b/VrUtils/log4cpp/src/OstreamAppender.cpp new file mode 100644 index 0000000..1243c43 --- /dev/null +++ b/VrUtils/log4cpp/src/OstreamAppender.cpp @@ -0,0 +1,44 @@ +/* + * OstreamAppender.cpp + * + * Copyright 2000, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2000, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include "PortabilityImpl.hh" +#ifdef LOG4CPP_HAVE_UNISTD_H +# include +#endif +#include +#include +#include +#include + +namespace log4cpp { + + OstreamAppender::OstreamAppender(const std::string& name, std::ostream* stream) : + LayoutAppender(name), + _stream(stream) { + } + + OstreamAppender::~OstreamAppender() { + close(); + } + + void OstreamAppender::close() { + // empty + } + + void OstreamAppender::_append(const LoggingEvent& event) { + (*_stream) << _getLayout().format(event); + if (!_stream->good()) { + // XXX help! help! + } + } + + bool OstreamAppender::reopen() { + return true; + } +} diff --git a/VrUtils/log4cpp/src/PThreads.cpp b/VrUtils/log4cpp/src/PThreads.cpp new file mode 100644 index 0000000..6052508 --- /dev/null +++ b/VrUtils/log4cpp/src/PThreads.cpp @@ -0,0 +1,36 @@ +/* + * Copyright 2002, Log4cpp Project. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include +#include + +#if defined(LOG4CPP_HAVE_THREADING) && defined(LOG4CPP_USE_PTHREADS) + +namespace log4cpp { + namespace threading { + + std::string getThreadId() { + char buffer[4*sizeof(long)]; + int bufsize = sizeof(buffer)/sizeof(buffer[0]); + int n = ::snprintf(buffer, bufsize, "%lu", pthread_self()); // thread id unsigned + if (n >= bufsize) { + // escape to heap allocation + char *buff_alloc; + int size = ::asprintf(&buff_alloc, "%lu", pthread_self()); // thread id unsigned + if (size < 0) { + throw std::bad_alloc(); + } + std::string res(buff_alloc); + free(buff_alloc); + return res; + } + return std::string(buffer); + } + + } +} + +#endif // LOG4CPP_HAVE_THREADING && LOG4CPP_USE_PTHREADS diff --git a/VrUtils/log4cpp/src/PassThroughLayout.cpp b/VrUtils/log4cpp/src/PassThroughLayout.cpp new file mode 100644 index 0000000..e30062f --- /dev/null +++ b/VrUtils/log4cpp/src/PassThroughLayout.cpp @@ -0,0 +1,17 @@ +/* + * Copyright 2002, Log4cpp Project. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include +#include +#include + +namespace log4cpp +{ + std::auto_ptr create_pass_through_layout(const FactoryParams& params) + { + return std::auto_ptr(new PassThroughLayout); + } +} diff --git a/VrUtils/log4cpp/src/PatternLayout.cpp b/VrUtils/log4cpp/src/PatternLayout.cpp new file mode 100644 index 0000000..dbf5672 --- /dev/null +++ b/VrUtils/log4cpp/src/PatternLayout.cpp @@ -0,0 +1,438 @@ +/* + * PatternLayout.cpp + * + * Copyright 2002, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include "PortabilityImpl.hh" + +#include +#include +#include +#include +#include +#include + +#ifdef LOG4CPP_HAVE_SSTREAM +#include +#else +#include +#endif + +#include +#include +#include +#include "Localtime.hh" + +#ifdef LOG4CPP_HAVE_INT64_T +#ifdef LOG4CPP_HAVE_STDINT_H +#include +#endif // LOG4CPP_HAVE_STDINT_H + +#ifdef LOG4CPP_MISSING_INT64_OSTREAM_OP +/* workaround suggested at: + http://support.microsoft.com/default.aspx?scid=kb;EN-US;q168440 +*/ + +#include + +std::ostream& operator<<(std::ostream& os, int64_t i) { + char buf[20]; + ::sprintf(buf,"%I64d", i); + return os << buf; +} +#endif // LOG4CPP_MISSING_INT64_OSTREAM_OP +#endif // LOG4CPP_HAVE_INT64_T + +namespace log4cpp { + + struct StringLiteralComponent : public PatternLayout::PatternComponent { + StringLiteralComponent(const std::string& literal) : + _literal(literal) { + } + + virtual void append(std::ostringstream& out, const LoggingEvent& event) { + out << _literal; + } + + private: + std::string _literal; + }; + + struct CategoryNameComponent : public PatternLayout::PatternComponent { + CategoryNameComponent(std::string specifier) { + if (specifier == "") { + _precision = -1; + } else { +#ifdef LOG4CPP_HAVE_SSTREAM + std::istringstream s(specifier); +#else + std::istrstream s(specifier.c_str()); +#endif + s >> _precision; + } + } + + virtual void append(std::ostringstream& out, const LoggingEvent& event) { + if (_precision == -1) { + out << event.categoryName; + } else { + std::string::size_type begin = std::string::npos; + for(int i = 0; i < _precision; i++) { + begin = event.categoryName.rfind('.', begin - 2); + if (begin == std::string::npos) { + begin = 0; + break; + } + begin++; + } + if (begin == std::string::npos) { + begin = 0; + } + out << event.categoryName.substr(begin); + } + } + + private: + int _precision; + }; + + struct MessageComponent : public PatternLayout::PatternComponent { + virtual void append(std::ostringstream& out, const LoggingEvent& event) { + out << event.message; + } + }; + + struct NDCComponent : public PatternLayout::PatternComponent { + virtual void append(std::ostringstream& out, const LoggingEvent& event) { + out << event.ndc; + } + }; + + struct PriorityComponent : public PatternLayout::PatternComponent { + virtual void append(std::ostringstream& out, const LoggingEvent& event) { + out << Priority::getPriorityName(event.priority); + } + }; + + struct ThreadNameComponent : public PatternLayout::PatternComponent { + virtual void append(std::ostringstream& out, const LoggingEvent& event) { + out << event.threadName; + } + }; + + struct ProcessorTimeComponent : public PatternLayout::PatternComponent { + virtual void append(std::ostringstream& out, const LoggingEvent& event) { + out << std::clock(); + } + }; + + struct TimeStampComponent : public PatternLayout::PatternComponent { + static const char* const FORMAT_ISO8601; + static const char* const FORMAT_ABSOLUTE; + static const char* const FORMAT_DATE; + + TimeStampComponent(std::string timeFormat) { + if ((timeFormat == "") || (timeFormat == "ISO8601")) { + timeFormat = FORMAT_ISO8601; + } else if (timeFormat == "ABSOLUTE") { + timeFormat = FORMAT_ABSOLUTE; + } else if (timeFormat == "DATE") { + timeFormat = FORMAT_DATE; + } + std::string::size_type pos = timeFormat.find("%l"); + if (pos == std::string::npos) { + _printMillis = false; + _timeFormat1 = timeFormat; + } else { + _printMillis = true; + _timeFormat1 = timeFormat.substr(0, pos); + _timeFormat2 = timeFormat.substr(pos + 2); + } + } + + virtual void append(std::ostringstream& out, const LoggingEvent& event) { + struct std::tm currentTime; + std::time_t t = event.timeStamp.getSeconds(); + localtime(&t, ¤tTime); + char formatted[100]; + std::string timeFormat; + if (_printMillis) { + std::ostringstream formatStream; + formatStream << _timeFormat1 + << std::setw(3) << std::setfill('0') + << event.timeStamp.getMilliSeconds() + << _timeFormat2; + timeFormat = formatStream.str(); + } else { + timeFormat = _timeFormat1; + } + std::strftime(formatted, sizeof(formatted), timeFormat.c_str(), ¤tTime); + out << formatted; + } + + private: + std::string _timeFormat1; + std::string _timeFormat2; + bool _printMillis; + }; + + const char* const TimeStampComponent::FORMAT_ISO8601 = "%Y-%m-%d %H:%M:%S,%l"; + const char* const TimeStampComponent::FORMAT_ABSOLUTE = "%H:%M:%S,%l"; + const char* const TimeStampComponent::FORMAT_DATE = "%d %b %Y %H:%M:%S,%l"; + + struct SecondsSinceEpochComponent : public PatternLayout::PatternComponent { + virtual void append(std::ostringstream& out, const LoggingEvent& event) { + out << event.timeStamp.getSeconds(); + } + }; + + struct MillisSinceEpochComponent : public PatternLayout::PatternComponent { + virtual void append(std::ostringstream& out, const LoggingEvent& event) { +#ifdef LOG4CPP_HAVE_INT64_T + int64_t t = event.timeStamp.getSeconds() - + TimeStamp::getStartTime().getSeconds(); + t *= 1000; + t += event.timeStamp.getMilliSeconds() - + TimeStamp::getStartTime().getMilliSeconds(); + + out << t; +#else + double t = event.timeStamp.getSeconds() - + TimeStamp::getStartTime().getSeconds(); + t *= 1000; + t += event.timeStamp.getMilliSeconds() - + TimeStamp::getStartTime().getMilliSeconds(); + + out << std::setiosflags(std::ios::fixed) + << std::setprecision(0) << t; +#endif + } + }; + + struct FormatModifierComponent : public PatternLayout::PatternComponent { + FormatModifierComponent(PatternLayout::PatternComponent* component, + size_t minWidth, size_t maxWidth, bool alignLeft) : + _component(component) , + _minWidth(minWidth), + _maxWidth(maxWidth), + _alignLeft(alignLeft) { + } + + virtual ~FormatModifierComponent() { + delete _component; + } + + virtual void append(std::ostringstream& out, const LoggingEvent& event) { + std::ostringstream s; + _component->append(s, event); + std::string msg = s.str(); + if (_maxWidth > 0 && _maxWidth < msg.length()) { + msg.erase(_maxWidth); + } + size_t fillCount = _minWidth - msg.length(); + if (_minWidth > msg.length()) { + if (_alignLeft) { + out << msg << std::string(fillCount, ' '); + } else { + out << std::string(fillCount, ' ') << msg; + } + } else { + out << msg; + } + } + + private: + PatternLayout::PatternComponent* _component; + size_t _minWidth; + size_t _maxWidth; + bool _alignLeft; + }; + + const char* PatternLayout::DEFAULT_CONVERSION_PATTERN = "%m%n"; + const char* PatternLayout::SIMPLE_CONVERSION_PATTERN = "%p - %m%n"; + const char* PatternLayout::BASIC_CONVERSION_PATTERN = "%R %p %c %x: %m%n"; + const char* PatternLayout::TTCC_CONVERSION_PATTERN = "%r [%t] %p %c %x - %m%n"; + + PatternLayout::PatternLayout() { + try { + setConversionPattern(DEFAULT_CONVERSION_PATTERN); + } catch(ConfigureFailure&) { + } + } + + PatternLayout::~PatternLayout() { + clearConversionPattern(); + } + + void PatternLayout::clearConversionPattern() { + for(ComponentVector::const_iterator i = _components.begin(); + i != _components.end(); ++i) { + delete (*i); + } + _components.clear(); + _conversionPattern = ""; + } + + void PatternLayout::setConversionPattern(const std::string& conversionPattern) { +#ifdef LOG4CPP_HAVE_SSTREAM + std::istringstream conversionStream(conversionPattern); +#else + std::istrstream conversionStream(conversionPattern.c_str()); +#endif + std::string literal; + + char ch; + PatternLayout::PatternComponent* component = NULL; + int minWidth = 0; + size_t maxWidth = 0; + clearConversionPattern(); + while (conversionStream.get(ch)) { + if (ch == '%') { + // readPrefix; + { + char ch2; + conversionStream.get(ch2); + if ((ch2 == '-') || ((ch2 >= '0') && (ch2 <= '9'))) { + conversionStream.putback(ch2); + conversionStream >> minWidth; + conversionStream.get(ch2); + } + if (ch2 == '.') { + conversionStream >> maxWidth; + } else { + conversionStream.putback(ch2); + } + } + if (!conversionStream.get(ch)) { + std::ostringstream msg; + msg << "unterminated conversion specifier in '" << conversionPattern << "' at index " << conversionStream.tellg(); + throw ConfigureFailure(msg.str()); + } + std::string specPostfix = ""; + // read postfix + { + char ch2; + if (conversionStream.get(ch2)) { + if (ch2 == '{') { + while(conversionStream.get(ch2) && (ch2 != '}')) + specPostfix += ch2; + } else { + conversionStream.putback(ch2); + } + } + } + switch (ch) { + case '%': + literal += ch; + break; + case 'm': + component = new MessageComponent(); + break; + case 'n': + { + std::ostringstream endline; + endline << std::endl; + literal += endline.str(); + } + break; + case 'c': + component = new CategoryNameComponent(specPostfix); + break; + case 'd': + component = new TimeStampComponent(specPostfix); + break; + case 'p': + component = new PriorityComponent(); + break; + case 'r': + component = new MillisSinceEpochComponent(); + break; + case 'R': + component = new SecondsSinceEpochComponent(); + break; + case 't': + component = new ThreadNameComponent(); + break; + case 'u': + component = new ProcessorTimeComponent(); + break; + case 'x': + component = new NDCComponent(); + break; + default: + std::ostringstream msg; + msg << "unknown conversion specifier '" << ch << "' in '" << conversionPattern << "' at index " << conversionStream.tellg(); + throw ConfigureFailure(msg.str()); + } + if (component) { + if (!literal.empty()) { + _components.push_back(new StringLiteralComponent(literal)); + literal = ""; + } + if ((minWidth != 0) || (maxWidth != 0)) { + component = new FormatModifierComponent(component, std::abs(minWidth), maxWidth, minWidth < 0); + minWidth = maxWidth = 0; + } + _components.push_back(component); + component = NULL; + } + } else { + literal += ch; + } + } + if (!literal.empty()) { + _components.push_back(new StringLiteralComponent(literal)); + } + + _conversionPattern = conversionPattern; + } + + std::string PatternLayout::getConversionPattern() const { + return _conversionPattern; + } + + std::string PatternLayout::format(const LoggingEvent& event) { + std::ostringstream message; + + for(ComponentVector::const_iterator i = _components.begin(); + i != _components.end(); ++i) { + (*i)->append(message, event); + } + + return message.str(); + } + + std::auto_ptr create_pattern_layout(const FactoryParams& params) + { + std::string pattern; + params.get_for("pattern layout").optional("pattern", pattern); + std::auto_ptr result(new PatternLayout); + PatternLayout* l = static_cast(result.get()); + if (pattern.empty() || pattern == "default") + return result; + + if (pattern == "simple") + { + l->setConversionPattern(PatternLayout::SIMPLE_CONVERSION_PATTERN); + return result; + } + + if (pattern == "basic") + { + l->setConversionPattern(PatternLayout::BASIC_CONVERSION_PATTERN); + return result; + } + + if (pattern == "ttcc") + { + l->setConversionPattern(PatternLayout::TTCC_CONVERSION_PATTERN); + return result; + } + + l->setConversionPattern(pattern); + return result; + } +} diff --git a/VrUtils/log4cpp/src/PortabilityImpl.cpp b/VrUtils/log4cpp/src/PortabilityImpl.cpp new file mode 100644 index 0000000..491fdc0 --- /dev/null +++ b/VrUtils/log4cpp/src/PortabilityImpl.cpp @@ -0,0 +1,27 @@ +/* + * PortabilityImpl.cpp + * + * Copyright 2002, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2002, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +/* + This file contains various portability functions +*/ + +#include "PortabilityImpl.hh" + +#ifndef LOG4CPP_HAVE_SSTREAM + +namespace std { + std::string ostringstream::str() { + (*this) << '\0'; + std::string msg(ostrstream::str()); + ostrstream::freeze(false); //unfreeze stream + return msg; + } +} + +#endif diff --git a/VrUtils/log4cpp/src/PortabilityImpl.hh b/VrUtils/log4cpp/src/PortabilityImpl.hh new file mode 100644 index 0000000..41fea22 --- /dev/null +++ b/VrUtils/log4cpp/src/PortabilityImpl.hh @@ -0,0 +1,70 @@ +/* + * PortabilityImpl.hh + * + * Copyright 2002, Log4cpp Project. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_PORTABILITYIMPL_HH +#define _LOG4CPP_PORTABILITYIMPL_HH + +#include + +#ifdef LOG4CPP_CSTDLIB_NOT_IN_STD +#include +namespace std { + static inline char *getenv(const char *name) { return ::getenv(name); }; + static inline int atoi(const char *nptr) { return ::atoi(nptr); }; + static inline unsigned long int + strtoul(const char *nptr, char **endptr, int base) { + return ::strtol(nptr, endptr, base); + }; + static inline void abort(void) { ::abort(); }; + +} +#endif + +#ifdef LOG4CPP_CSTRING_NOT_IN_STD +#include +namespace std { + static inline void *memmove(void *dest, const void *src, size_t n) { + return ::memmove(dest, src, n); + }; +} +#endif + +#ifdef LOG4CPP_CTIME_NOT_IN_STD +#include +namespace std { + static inline size_t strftime(char *strDest, size_t maxsize, const char *format, const struct tm *timeptr ) { + return ::strftime(strDest,maxsize,format,timeptr); + } + static inline struct tm *localtime( const time_t *timer ) { return ::localtime(timer); } + using ::clock; + using ::tm; + using ::time_t; +} +#endif + +#ifdef LOG4CPP_CMATH_NOT_IN_STD +#include +namespace std { + static inline int abs(int i) { return ::abs(i); } +} +#endif + +namespace log4cpp +{ + template const T& min(const T& a, const T& b) + { + return a < b ? a : b; + } + + template const T& max(const T& a, const T& b) + { + return a > b ? a : b; + } +} + +#endif // _LOG4CPP_PORTABILITYIMPL_HH diff --git a/VrUtils/log4cpp/src/Priority.cpp b/VrUtils/log4cpp/src/Priority.cpp new file mode 100644 index 0000000..dd0fad2 --- /dev/null +++ b/VrUtils/log4cpp/src/Priority.cpp @@ -0,0 +1,70 @@ +/* + * Priority.cpp + * + * Copyright 2000, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2000, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include "PortabilityImpl.hh" +#include +#include + +namespace log4cpp { + + namespace { + const std::string *names() { + static const std::string priority_names[10] = { + "FATAL", + "ALERT", + "CRIT", + "ERROR", + "WARN", + "NOTICE", + "INFO", + "DEBUG", + "NOTSET", + "UNKNOWN" + }; + return priority_names; + } + } + + const int log4cpp::Priority::MESSAGE_SIZE = 8; + + + const std::string& Priority::getPriorityName(int priority) throw() { + + priority++; + priority /= 100; + return names()[((priority < 0) || (priority > 8)) ? 8 : priority]; + } + + Priority::Value Priority::getPriorityValue(const std::string& priorityName) { + Priority::Value value = -1; + + for (unsigned int i = 0; i < 10; i++) { + if (priorityName == names()[i]) { + value = i * 100; + break; + } + } + + if (value == -1) { + if (priorityName == "EMERG") { + value = 0; + } else { + char* endPointer; + value = std::strtoul(priorityName.c_str(), &endPointer, 10); + if (*endPointer != 0) { + throw std::invalid_argument( + std::string("unknown priority name: '") + priorityName + "'" + ); + } + } + } + + return value; + } +} diff --git a/VrUtils/log4cpp/src/Properties.cpp b/VrUtils/log4cpp/src/Properties.cpp new file mode 100644 index 0000000..6eca311 --- /dev/null +++ b/VrUtils/log4cpp/src/Properties.cpp @@ -0,0 +1,158 @@ +/* + * Properties.cpp + * + * Copyright 2002, Log4cpp Project. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include "Properties.hh" +#include +#include "StringUtil.hh" + +namespace log4cpp { + + Properties::Properties() { + } + + Properties::~Properties() { + } + + void Properties::load(std::istream& in) { + clear(); + + std::string fullLine, command; + std::string leftSide, rightSide; + char line[256]; + std::string::size_type length; + bool partiallyRead(false); // fix for bug#137, for strings longer than 256 chars + + while (in) { + if (in.getline(line, 256) || !in.bad()) { + // either string is read fully or only partially (logical but not read/write error) + if (partiallyRead) + fullLine.append(line); + else + fullLine = line; + partiallyRead = (in.fail() && !in.bad()); + if (partiallyRead && !in.eof()) { + in.clear(in.rdstate() & ~std::ios::failbit); + continue; // to get full line + } + } else { + break; + } + /* if the line contains a # then it is a comment + if we find it anywhere other than the beginning, then we assume + there is a command on that line, and it we don't find it at all + we assume there is a command on the line (we test for valid + command later) if neither is true, we continue with the next line + */ + length = fullLine.find('#'); + if (length == std::string::npos) { + command = fullLine; + } else if (length > 0) { + command = fullLine.substr(0, length); + } else { + continue; + } + + // check the command and handle it + length = command.find('='); + if (length != std::string::npos) { + leftSide = StringUtil::trim(command.substr(0, length)); + rightSide = StringUtil::trim(command.substr(length + 1, command.size() - length)); + _substituteVariables(rightSide); + } else { + continue; + } + + /* handle the command by determining what object the left side + refers to and setting the value given on the right + ASSUMPTIONS: + 1. first object given on left side is "log4j" or "log4cpp" + 2. all class names on right side are ignored because we + probably cannot resolve them anyway. + */ + + // strip off the "log4j" or "log4cpp" + length = leftSide.find('.'); + if (leftSide.substr(0, length) == "log4j" || + leftSide.substr(0, length) == "log4cpp") + leftSide = leftSide.substr(length + 1); + + // add to the map of properties + insert(value_type(leftSide, rightSide)); + } + } + + void Properties::save(std::ostream& out) { + for(const_iterator i = begin(); i != end(); ++i) { + out << (*i).first << "=" << (*i).second << std::endl; + } + } + + int Properties::getInt(const std::string& property, int defaultValue) { + const_iterator key = find(property); + return (key == end()) ? defaultValue : std::atoi((*key).second.c_str()); + } + + bool Properties::getBool(const std::string& property, bool defaultValue) { + const_iterator key = find(property); + return (key == end()) ? defaultValue : ((*key).second == "true"); + } + + std::string Properties::getString(const std::string& property, + const char* defaultValue) { + const_iterator key = find(property); + return (key == end()) ? std::string(defaultValue) : (*key).second; + } + + void Properties::_substituteVariables(std::string& value) { + std::string result; + + std::string::size_type left = 0; + std::string::size_type right = value.find("${", left); + if (right == std::string::npos) { + // bail out early for 99% of cases + return; + } + + while(true) { + result += value.substr(left, right - left); + if (right == std::string::npos) { + break; + } + + left = right + 2; + right = value.find('}', left); + if (right == std::string::npos) { + // no close tag, use string literally + result += value.substr(left - 2); + break; + } else { + const std::string key = value.substr(left, right - left); + if (key == "${") { + result += "${"; + } else { + char* env_value = std::getenv(key.c_str()); + if (env_value) { + result += env_value; + } else { + const_iterator it = find(key); + if (it == end()) { + // not found assume empty; + } else { + result += (*it).second; + } + } + } + left = right + 1; + } + + right = value.find("${", left); + } + + value = result; + } +} diff --git a/VrUtils/log4cpp/src/Properties.hh b/VrUtils/log4cpp/src/Properties.hh new file mode 100644 index 0000000..4f3b97e --- /dev/null +++ b/VrUtils/log4cpp/src/Properties.hh @@ -0,0 +1,38 @@ +/* + * Properties.hh + * + * Copyright 2002, Log4cpp Project. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_PROPERTIES_HH +#define _LOG4CPP_PROPERTIES_HH + +#include "PortabilityImpl.hh" +#include +#include +#include + +namespace log4cpp { + + class Properties : public std::map { + public: + Properties(); + virtual ~Properties(); + + virtual void load(std::istream& in); + virtual void save(std::ostream& out); + + virtual int getInt(const std::string& property, int defaultValue); + virtual bool getBool(const std::string& property, bool defaultValue); + virtual std::string getString(const std::string& property, + const char* defaultValue); + + protected: + virtual void _substituteVariables(std::string& value); + }; +} + +#endif // _LOG4CPP_PROPERTIES_HH + diff --git a/VrUtils/log4cpp/src/PropertyConfigurator.cpp b/VrUtils/log4cpp/src/PropertyConfigurator.cpp new file mode 100644 index 0000000..0681bf2 --- /dev/null +++ b/VrUtils/log4cpp/src/PropertyConfigurator.cpp @@ -0,0 +1,20 @@ +/* + * PropertyConfigurator.cpp + * + * Copyright 2001, Glen Scott. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ +#include "PortabilityImpl.hh" +#include +#include "PropertyConfiguratorImpl.hh" + +namespace log4cpp { + + void PropertyConfigurator::configure(const std::string& initFileName) { + static PropertyConfiguratorImpl configurator; + + configurator.doConfigure(initFileName); + } +} + diff --git a/VrUtils/log4cpp/src/PropertyConfiguratorImpl.cpp b/VrUtils/log4cpp/src/PropertyConfiguratorImpl.cpp new file mode 100644 index 0000000..5f62b56 --- /dev/null +++ b/VrUtils/log4cpp/src/PropertyConfiguratorImpl.cpp @@ -0,0 +1,370 @@ +/* + * PropertyConfiguratorImpl.cpp + * + * Copyright 2002, Log4cpp Project. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ +#include "PortabilityImpl.hh" + +#ifdef LOG4CPP_HAVE_UNISTD_H +#include +#endif +#ifdef LOG4CPP_HAVE_IO_H +# include +#endif +#include + +#include +#include + +#include + +// appenders +#include +#include +#include +#include +#include +#include +#ifdef WIN32 +#include +#include +#endif +#ifndef LOG4CPP_DISABLE_REMOTE_SYSLOG +#include +#endif // LOG4CPP_DISABLE_REMOTE_SYSLOG +#ifdef LOG4CPP_HAVE_LIBIDSA +#include +#endif // LOG4CPP_HAVE_LIBIDSA +#ifdef LOG4CPP_HAVE_SYSLOG +#include +#endif + +// layouts +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "PropertyConfiguratorImpl.hh" +#include "StringUtil.hh" + +namespace log4cpp { + + PropertyConfiguratorImpl::PropertyConfiguratorImpl() { + } + + PropertyConfiguratorImpl::~PropertyConfiguratorImpl() { + } + + void PropertyConfiguratorImpl::doConfigure(const std::string& initFileName) { + std::ifstream initFile(initFileName.c_str()); + + if (!initFile) { + throw ConfigureFailure(std::string("File ") + initFileName + " does not exist"); + } + + doConfigure(initFile); + } + + + void PropertyConfiguratorImpl::doConfigure(std::istream& in) { + // parse the file to get all of the configuration + _properties.load(in); + + instantiateAllAppenders(); + // get categories + std::vector catList; + getCategories(catList); + + // configure each category + for(std::vector::const_iterator iter = catList.begin(); + iter != catList.end(); ++iter) { + configureCategory(*iter); + } + } + + void PropertyConfiguratorImpl::instantiateAllAppenders() { + std::string currentAppender; + + std::string prefix("appender"); + Properties::const_iterator from = _properties.lower_bound(prefix + '.'); + Properties::const_iterator to = _properties.lower_bound(prefix + '/'); + for(Properties::const_iterator i = from; i != to; ++i) { + const std::string& key = (*i).first; + const std::string& value = (*i).second; + std::list propNameParts; + std::back_insert_iterator > pnpIt(propNameParts); + StringUtil::split(pnpIt, key, '.'); + std::list::const_iterator i2 = propNameParts.begin(); + std::list::const_iterator iEnd = propNameParts.end(); + if (++i2 == iEnd) { + throw ConfigureFailure(std::string("missing appender name")); + } + + const std::string appenderName = *i2++; + + /* WARNING, approaching lame code section: + skipping of the Appenders properties only to get them + again in instantiateAppender. + */ + if (appenderName == currentAppender) { + // simply skip properties for the current appender + } else { + if (i2 == iEnd) { + // a new appender + currentAppender = appenderName; + _allAppenders[currentAppender] = + instantiateAppender(currentAppender); + } else { + throw ConfigureFailure(std::string("partial appender definition : ") + key); + } + } + } + } + + void PropertyConfiguratorImpl::configureCategory(const std::string& categoryName) { + // start by reading the "rootCategory" key + std::string tempCatName = + (categoryName == "rootCategory") ? categoryName : "category." + categoryName; + + Properties::iterator iter = _properties.find(tempCatName); + + if (iter == _properties.end()) + throw ConfigureFailure(std::string("Unable to find category: ") + tempCatName); + + // need to get the root instance of the category + Category& category = (categoryName == "rootCategory") ? + Category::getRoot() : Category::getInstance(categoryName); + + + std::list tokens; + std::back_insert_iterator > tokIt(tokens); + StringUtil::split(tokIt, (*iter).second, ','); + std::list::const_iterator i = tokens.begin(); + std::list::const_iterator iEnd = tokens.end(); + + Priority::Value priority = Priority::NOTSET; + if (i != iEnd) { + std::string priorityName = StringUtil::trim(*i++); + try { + if (priorityName != "") { + priority = Priority::getPriorityValue(priorityName); + } + } catch(std::invalid_argument& e) { + throw ConfigureFailure(std::string(e.what()) + + " for category '" + categoryName + "'"); + } + } + + try { + category.setPriority(priority); + } catch (std::invalid_argument& e) { + throw ConfigureFailure(std::string(e.what()) + + " for category '" + categoryName + "'"); + } + + bool additive = _properties.getBool("additivity." + categoryName, true); + category.setAdditivity(additive); + + category.removeAllAppenders(); + for(/**/; i != iEnd; ++i) { + std::string appenderName = StringUtil::trim(*i); + AppenderMap::const_iterator appIt = + _allAppenders.find(appenderName); + if (appIt == _allAppenders.end()) { + // appender not found; + throw ConfigureFailure(std::string("Appender '") + + appenderName + "' not found for category '" + categoryName + "'"); + } else { + /* pass by reference, i.e. don't transfer ownership + */ + category.addAppender(*((*appIt).second)); + } + } + } + + Appender* PropertyConfiguratorImpl::instantiateAppender(const std::string& appenderName) { + Appender* appender = NULL; + std::string appenderPrefix = std::string("appender.") + appenderName; + + // determine the type by the appenderName + Properties::iterator key = _properties.find(appenderPrefix); + if (key == _properties.end()) + throw ConfigureFailure(std::string("Appender '") + appenderName + "' not defined"); + + std::string::size_type length = (*key).second.find_last_of("."); + std::string appenderType = (length == std::string::npos) ? + (*key).second : (*key).second.substr(length+1); + + // and instantiate the appropriate object + if (appenderType == "ConsoleAppender") { + std::string target = _properties.getString(appenderPrefix + ".target", "stdout"); + std::transform(target.begin(), target.end(), target.begin(), ::tolower); + if(target.compare("stdout") == 0) { + appender = new OstreamAppender(appenderName, &std::cout); + } + else if(target.compare("stderr") == 0) { + appender = new OstreamAppender(appenderName, &std::cerr); + } + else{ + throw ConfigureFailure(appenderName + "' has invalid target '" + target + "'"); + } + } + else if (appenderType == "FileAppender") { + std::string fileName = _properties.getString(appenderPrefix + ".fileName", "foobar"); + bool append = _properties.getBool(appenderPrefix + ".append", true); + appender = new FileAppender(appenderName, fileName, append); + } + else if (appenderType == "RollingFileAppender") { + std::string fileName = _properties.getString(appenderPrefix + ".fileName", "foobar"); + size_t maxFileSize = _properties.getInt(appenderPrefix + ".maxFileSize", 10*1024*1024); + int maxBackupIndex = _properties.getInt(appenderPrefix + ".maxBackupIndex", 1); + bool append = _properties.getBool(appenderPrefix + ".append", true); + appender = new RollingFileAppender(appenderName, fileName, maxFileSize, maxBackupIndex, + append); + } + else if (appenderType == "DailyRollingFileAppender") { + std::string fileName = _properties.getString(appenderPrefix + ".fileName", "foobar"); + unsigned int maxDaysKeep = _properties.getInt(appenderPrefix + ".maxDaysKeep", 0); + bool append = _properties.getBool(appenderPrefix + ".append", true); + appender = new DailyRollingFileAppender(appenderName, fileName, maxDaysKeep, append); + } +#ifndef LOG4CPP_DISABLE_REMOTE_SYSLOG + else if (appenderType == "SyslogAppender") { + std::string syslogName = _properties.getString(appenderPrefix + ".syslogName", "syslog"); + std::string syslogHost = _properties.getString(appenderPrefix + ".syslogHost", "localhost"); + int facility = _properties.getInt(appenderPrefix + ".facility", -1) * 8; // * 8 to get LOG_KERN, etc. compatible values. + int portNumber = _properties.getInt(appenderPrefix + ".portNumber", -1); + appender = new RemoteSyslogAppender(appenderName, syslogName, + syslogHost, facility, portNumber); + } +#endif // LOG4CPP_DISABLE_REMOTE_SYSLOG +#ifdef LOG4CPP_HAVE_SYSLOG + else if (appenderType == "LocalSyslogAppender") { + std::string syslogName = _properties.getString(appenderPrefix + ".syslogName", "syslog"); + int facility = _properties.getInt(appenderPrefix + ".facility", -1) * 8; // * 8 to get LOG_KERN, etc. compatible values. + appender = new SyslogAppender(appenderName, syslogName, facility); + } +#endif // LOG4CPP_HAVE_SYSLOG + else if (appenderType == "AbortAppender") { + appender = new AbortAppender(appenderName); + } +#ifdef LOG4CPP_HAVE_LIBIDSA + else if (appenderType == "IdsaAppender") { + // default idsa name ??? + std::string idsaName = _properties.getString(appenderPrefix + ".idsaName", "foobar"); + + appender = new IdsaAppender(appenderName, idsaname); + } +#endif // LOG4CPP_HAVE_LIBIDSA + +#ifdef WIN32 + // win32 debug appender + else if (appenderType == "Win32DebugAppender") { + appender = new Win32DebugAppender(appenderName); + } + // win32 NT event log appender + else if (appenderType == "NTEventLogAppender") { + std::string source = _properties.getString(appenderPrefix + ".source", "foobar"); + appender = new NTEventLogAppender(appenderName, source); + } +#endif // WIN32 + else { + throw ConfigureFailure(std::string("Appender '") + appenderName + + "' has unknown type '" + appenderType + "'"); + } + + if (appender->requiresLayout()) { + setLayout(appender, appenderName); + } + + // set threshold + std::string thresholdName = _properties.getString(appenderPrefix + ".threshold", ""); + try { + if (thresholdName != "") { + appender->setThreshold(Priority::getPriorityValue(thresholdName)); + } + } catch(std::invalid_argument& e) { + delete appender; // fix for #3109495 + throw ConfigureFailure(std::string(e.what()) + + " for threshold of appender '" + appenderName + "'"); + } + + return appender; + } + + void PropertyConfiguratorImpl::setLayout(Appender* appender, const std::string& appenderName) { + // determine the type by appenderName + std::string tempString; + Properties::iterator key = + _properties.find(std::string("appender.") + appenderName + ".layout"); + + if (key == _properties.end()) + throw ConfigureFailure(std::string("Missing layout property for appender '") + + appenderName + "'"); + + std::string::size_type length = (*key).second.find_last_of("."); + std::string layoutType = (length == std::string::npos) ? + (*key).second : (*key).second.substr(length+1); + + Layout* layout; + // and instantiate the appropriate object + if (layoutType == "BasicLayout") { + layout = new BasicLayout(); + } + else if (layoutType == "SimpleLayout") { + layout = new SimpleLayout(); + } + else if (layoutType == "PatternLayout") { + // need to read the properties to configure this one + PatternLayout* patternLayout = new PatternLayout(); + + key = _properties.find(std::string("appender.") + appenderName + ".layout.ConversionPattern"); + if (key == _properties.end()) { + // leave default pattern + } else { + // set pattern + patternLayout->setConversionPattern((*key).second); + } + + layout = patternLayout; + } + else { + throw ConfigureFailure(std::string("Unknown layout type '" + layoutType + + "' for appender '") + appenderName + "'"); + } + + appender->setLayout(layout); + } + + /** + * Get the categories contained within the map of properties. Since + * the category looks something like "category.xxxxx.yyy.zzz", we need + * to search the entire map to figure out which properties are category + * listings. Seems like there might be a more elegant solution. + */ + void PropertyConfiguratorImpl::getCategories(std::vector& categories) const { + categories.clear(); + + // add the root category first + categories.push_back(std::string("rootCategory")); + + // then look for "category." + std::string prefix("category"); + Properties::const_iterator from = _properties.lower_bound(prefix + '.'); + Properties::const_iterator to = _properties.lower_bound(prefix + (char)('.' + 1)); + for (Properties::const_iterator iter = from; iter != to; iter++) { + categories.push_back((*iter).first.substr(prefix.size() + 1)); + } + } +} diff --git a/VrUtils/log4cpp/src/PropertyConfiguratorImpl.hh b/VrUtils/log4cpp/src/PropertyConfiguratorImpl.hh new file mode 100644 index 0000000..2091bdf --- /dev/null +++ b/VrUtils/log4cpp/src/PropertyConfiguratorImpl.hh @@ -0,0 +1,96 @@ +/* + * PropertyConiguratorImpl.hh + * + * Copyright 2002, Log4cpp Project. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_PROPERTYCONFIGURATORIMPL_HH +#define _LOG4CPP_PROPERTYCONFIGURATORIMPL_HH + +#include "PortabilityImpl.hh" +#include +#include +#include +#include +#include +#include +#include + +#include "Properties.hh" + +namespace log4cpp { + + class PropertyConfiguratorImpl { + public: + typedef std::map AppenderMap; + + PropertyConfiguratorImpl(); + virtual ~PropertyConfiguratorImpl(); + /** + * + * @param initFileName + * @exception ConfigureFailure if the method encountered a read or + * syntax error. + */ + virtual void doConfigure(const std::string& initFileName); + /** + * + * @param in + * @exception ConfigureFailure if the method encountered a read or + * syntax error. + */ + virtual void doConfigure(std::istream& in); + + protected: + /** + configure the given category. This includes setting its Priority + and adding any Appenders. + @todo setting other properties like 'additivity'. + @param categoryname Name of the category to configure. + The name 'rootCategory' refers to the root Category. + * @exception ConfigureFailure if the method encountered a read or + * syntax error. + **/ + void configureCategory(const std::string& categoryname); + + /** + * Get a list of categories for which we should do the configuration. This simply + * extracts the categories from the map. + * @param categories Reference to a list which is to receive the list of categories. + */ + void getCategories(std::vector& categories) const; + + void instantiateAllAppenders(); + + /** + * Intantiate and configure the appender referred to by the given name. This method searches the + * map to find all configuration parameters for the appender, and adds the appender + * to the given category. This isn't very general in the sense that it will need to + * be modified for each type of appender and layout. A more general solution would + * be to define an "options" interface for each appender and layout, so that we can + * simply call this method with a list of options instead of needing to know what is + * or is not available. This would also require some generic way of instantiating an + * object for which we have no knowledge. An "AppenderFactory" could be used which + * maps the given type to an actual object class registered with the factory (?? is this + * possible?). + * @param name String containing the name of the type of appender to be instantiated. + */ + Appender* instantiateAppender(const std::string& name); + + /** + * Method for instantiating and configuring the layouts associated with each + * appender. + * @param appender Appender to which we are setting this layout. + * @param name Name in the properties of this appender. + */ + void setLayout(Appender* appender, const std::string& name); + + Properties _properties; + AppenderMap _allAppenders; + }; +} + +#endif // _LOG4CPP_PROPERTIES_HH + diff --git a/VrUtils/log4cpp/src/RemoteSyslogAppender.cpp b/VrUtils/log4cpp/src/RemoteSyslogAppender.cpp new file mode 100644 index 0000000..4cf4246 --- /dev/null +++ b/VrUtils/log4cpp/src/RemoteSyslogAppender.cpp @@ -0,0 +1,186 @@ +/* + * RemoteSyslogAppender.cpp + * + * Copyright 2001, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2001, Walter Stroebel. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include "PortabilityImpl.hh" + +#ifdef LOG4CPP_HAVE_UNISTD_H +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef WIN32 +#include +#else +#include +#include +#include +#include +#endif + +namespace log4cpp { + + int RemoteSyslogAppender::toSyslogPriority(Priority::Value priority) { + static int priorities[8] = { LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, + LOG_WARNING, LOG_NOTICE, LOG_INFO, + LOG_DEBUG }; + int result; + + priority++; + priority /= 100; + + if (priority < 0) { + result = LOG_EMERG; + } else if (priority > 7) { + result = LOG_DEBUG; + } else { + result = priorities[priority]; + } + + return result; + } + + + RemoteSyslogAppender::RemoteSyslogAppender(const std::string& name, + const std::string& syslogName, + const std::string& relayer, + int facility, + int portNumber) : + LayoutAppender(name), + _syslogName(syslogName), + _relayer(relayer), + _facility((facility == -1) ? LOG_USER : facility), + _portNumber((portNumber == -1) ? 514 : portNumber), + _socket (0), + _ipAddr (0), + _cludge (0) + { + open(); + } + + RemoteSyslogAppender::~RemoteSyslogAppender() { + close(); +#ifdef WIN32 + if (_cludge) { + // we started it, we end it. + WSACleanup (); + } +#endif + } + + void RemoteSyslogAppender::open() { + if (!_ipAddr) { + struct hostent *pent = gethostbyname (_relayer.c_str ()); +#ifdef WIN32 + if (pent == NULL) { + if (WSAGetLastError () == WSANOTINITIALISED) { + WSADATA wsaData; + int err; + + err = WSAStartup (0x101, &wsaData ); + if (err) { + // loglog("RemoteSyslogAppender: WSAStartup returned %d", err); + return; // fail silently + } + pent = gethostbyname (_relayer.c_str ()); + _cludge = 1; + } else { + // loglog("RemoteSyslogAppender: gethostbyname returned error"); + return; // fail silently + } + } +#endif + if (pent == NULL) { + in_addr_t ip = inet_addr (_relayer.c_str ()); + pent = gethostbyaddr ((const char *) &ip, sizeof(in_addr_t), AF_INET); + if (pent == NULL) { + // loglog("RemoteSyslogAppender: failed to resolve host %s", _relayer.c_str()); + return; // fail silently + } + } + _ipAddr = *(in_addr_t*)(pent->h_addr); // fixed bug #1579890 + } + // Get a datagram socket. + _socket = socket(AF_INET, SOCK_DGRAM, 0); + if +#ifdef WIN32 + (_socket == INVALID_SOCKET) +#else + (_socket < 0) +#endif + { + // loglog("RemoteSyslogAppender: failed to open socket"); + return; // fail silently + } + } + + void RemoteSyslogAppender::close() { + if (_socket) { +#ifdef WIN32 + closesocket (_socket); +#else + ::close (_socket); +#endif + _socket = 0; + } + } + + void RemoteSyslogAppender::_append(const LoggingEvent& event) { + const std::string message(_getLayout().format(event)); + size_t messageLength = message.length(); + char *buf = new char [messageLength + 16]; + int priority = _facility + toSyslogPriority(event.priority); + int preambleLength = sprintf (buf, "<%d>", priority); + memcpy (buf + preambleLength, message.data(), messageLength); + + sockaddr_in sain; + sain.sin_family = AF_INET; + sain.sin_port = htons (_portNumber); + // NO, do NOT use htonl on _ipAddr. Is already in network order. + sain.sin_addr.s_addr = _ipAddr; + + while (messageLength > 0) { + /* if packet larger than maximum (900 bytes), split + into two or more syslog packets. */ + if (preambleLength + messageLength > 900) { + sendto (_socket, buf, 900, 0, (struct sockaddr *) &sain, sizeof (sain)); + messageLength -= (900 - preambleLength); + std::memmove (buf + preambleLength, buf + 900, messageLength); + // note: we might need to sleep a bit here + } else { + sendto (_socket, buf, preambleLength + messageLength, 0, (struct sockaddr *) &sain, sizeof (sain)); + break; + } + } + + delete[] buf; + } + + bool RemoteSyslogAppender::reopen() { + close(); + open(); + return true; + } + + std::auto_ptr create_remote_syslog_appender(const FactoryParams& params) + { + std::string name, syslog_name, relayer; + int facility = -1, port_number = -1; + params.get_for("remote syslog appender").required("name", name)("syslog_name", syslog_name)("relayer", relayer) + .optional("facility", facility)("port", port_number); + return std::auto_ptr(new RemoteSyslogAppender(name, syslog_name, relayer, facility, port_number)); + } +} diff --git a/VrUtils/log4cpp/src/RollingFileAppender.cpp b/VrUtils/log4cpp/src/RollingFileAppender.cpp new file mode 100644 index 0000000..3c563a4 --- /dev/null +++ b/VrUtils/log4cpp/src/RollingFileAppender.cpp @@ -0,0 +1,111 @@ +/* + * RollingFileAppender.cpp + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include "PortabilityImpl.hh" +#ifdef LOG4CPP_HAVE_IO_H +# include +#endif +#ifdef LOG4CPP_HAVE_UNISTD_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef LOG4CPP_HAVE_SSTREAM +#include +#include +#endif + +namespace log4cpp { + + RollingFileAppender::RollingFileAppender(const std::string& name, + const std::string& fileName, + size_t maxFileSize, + unsigned int maxBackupIndex, + bool append, + mode_t mode) : + FileAppender(name, fileName, append, mode), + _maxBackupIndex(maxBackupIndex > 0 ? maxBackupIndex : 1), + _maxBackupIndexWidth((_maxBackupIndex > 0) ? log10((float)_maxBackupIndex)+1 : 1), + _maxFileSize(maxFileSize) { + } + + void RollingFileAppender::setMaxBackupIndex(unsigned int maxBackups) { + _maxBackupIndex = maxBackups; + _maxBackupIndexWidth = (_maxBackupIndex > 0) ? log10((float)_maxBackupIndex)+1 : 1; + } + + unsigned int RollingFileAppender::getMaxBackupIndex() const { + return _maxBackupIndex; + } + + void RollingFileAppender::setMaximumFileSize(size_t maxFileSize) { + _maxFileSize = maxFileSize; + } + + size_t RollingFileAppender::getMaxFileSize() const { + return _maxFileSize; + } + + void RollingFileAppender::rollOver() { + ::close(_fd); + if (_maxBackupIndex > 0) { + std::ostringstream filename_stream; + filename_stream << _fileName << "." << std::setw( _maxBackupIndexWidth ) << std::setfill( '0' ) << _maxBackupIndex << std::ends; + // remove the very last (oldest) file + std::string last_log_filename = filename_stream.str(); + // std::cout << last_log_filename << std::endl; // removed by request on sf.net #140 + ::remove(last_log_filename.c_str()); + + // rename each existing file to the consequent one + for(unsigned int i = _maxBackupIndex; i > 1; i--) { + filename_stream.str(std::string()); + filename_stream << _fileName << '.' << std::setw( _maxBackupIndexWidth ) << std::setfill( '0' ) << i - 1 << std::ends; // set padding so the files are listed in order + ::rename(filename_stream.str().c_str(), last_log_filename.c_str()); + last_log_filename = filename_stream.str(); + } + // new file will be numbered 1 + ::rename(_fileName.c_str(), last_log_filename.c_str()); + } + _fd = ::open(_fileName.c_str(), _flags, _mode); + } + + void RollingFileAppender::_append(const LoggingEvent& event) { + FileAppender::_append(event); + if (-1 != _fd) { + off_t offset = ::lseek(_fd, 0, SEEK_END); + if (offset < 0) { + // XXX we got an error, ignore for now + } + else { + if (static_cast(offset) >= _maxFileSize) { + rollOver(); + } + } + } + } + + std::auto_ptr create_roll_file_appender(const FactoryParams& params) + { + std::string name, filename; + bool append = true; + mode_t mode = 664; + int max_file_size = 0, max_backup_index = 0; + params.get_for("roll file appender").required("name", name)("filename", filename)("max_file_size", max_file_size) + ("max_backup_index", max_backup_index) + .optional("append", append)("mode", mode); + + return std::auto_ptr(new RollingFileAppender(name, filename, max_file_size, max_backup_index, append, mode)); + } +} diff --git a/VrUtils/log4cpp/src/SimpleConfigurator.cpp b/VrUtils/log4cpp/src/SimpleConfigurator.cpp new file mode 100644 index 0000000..82d38fb --- /dev/null +++ b/VrUtils/log4cpp/src/SimpleConfigurator.cpp @@ -0,0 +1,232 @@ +/* + * SimpleConfigurator.cpp + * + * Copyright 2001, Glen Scott. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ +#include "PortabilityImpl.hh" + +#ifdef LOG4CPP_HAVE_UNISTD_H +#include +#endif +#ifdef LOG4CPP_HAVE_IO_H +# include +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if LOG4CPP_HAVE_SYSLOG +#include +#endif +#ifndef LOG4CPP_DISABLE_REMOTE_SYSLOG +#include +#endif +#ifdef WIN32 +#include +#endif + +namespace log4cpp { + + void SimpleConfigurator::configure(const std::string& initFileName) { + std::ifstream initFile(initFileName.c_str()); + + if (!initFile) { + throw ConfigureFailure(std::string("Config File ") + initFileName + " does not exist or is unreadable"); + } + + configure(initFile); + } + + void SimpleConfigurator::configure(std::istream& initFile) { + std::string nextCommand; + std::string categoryName; + + while (initFile >> nextCommand) { + /* skip comment lines */ + if (nextCommand[0] == '#') { + std::string dummy; + std::getline(initFile, dummy); + continue; + } + /* stop on missing categoryName */ + if (!(initFile >> categoryName)) + break; + + log4cpp::Category& category = + (categoryName.compare("root") == 0) ? + log4cpp::Category::getRoot() : + log4cpp::Category::getInstance(categoryName); + + if (nextCommand.compare("appender") == 0) { + std::string layout; + std::string appenderName; + + if (initFile >> layout >> appenderName) { + log4cpp::Appender* appender; + if (appenderName.compare("file") == 0) { + std::string logFileName; + if (!(initFile >> logFileName)) { + throw ConfigureFailure("Missing filename for log file logging configuration file for category: " + categoryName); + } + appender = new log4cpp::FileAppender(categoryName, logFileName); + } + else if (appenderName.compare("rolling") == 0) { + std::string logFileName; + size_t maxFileSize; + unsigned int maxBackupIndex=1; + if (!(initFile >> logFileName)) { + throw ConfigureFailure("Missing filename for log file logging configuration file for category: " + categoryName); + } + if (!(initFile >> maxFileSize)) { + throw ConfigureFailure("Missing maximum size for log file logging configuration file for category: " + categoryName); + } + if (!(initFile >> maxBackupIndex)) { + throw ConfigureFailure("Missing maximum backup index for log file logging configuration file for category: " + categoryName); + } + appender = new log4cpp::RollingFileAppender(categoryName, logFileName, maxFileSize, maxBackupIndex); + } + else if (appenderName.compare("dailyrolling") == 0) { + std::string logFileName; + unsigned int maxKeepDays=1; + if (!(initFile >> logFileName)) { + throw ConfigureFailure("Missing filename for log file logging configuration file for category: " + categoryName); + } + if (!(initFile >> maxKeepDays)) { + throw ConfigureFailure("Missing maximum keep days for log file logging configuration file for category: " + categoryName); + } + appender = new log4cpp::DailyRollingFileAppender(categoryName, logFileName, maxKeepDays); + } + else if (appenderName.compare("console") == 0) { + appender = + new log4cpp::OstreamAppender(categoryName, &std::cout); + } + else if (appenderName.compare("stdout") == 0) { + appender = + new log4cpp::FileAppender(categoryName, ::dup(fileno(stdout))); + } + else if (appenderName.compare("stderr") == 0) { + appender = + new log4cpp::FileAppender(categoryName, ::dup(fileno(stderr))); + } +#if LOG4CPP_HAVE_SYSLOG + else if (appenderName.compare("syslog") == 0) { + std::string syslogName; + int facility; + if (!(initFile >> syslogName)) { + throw ConfigureFailure("Missing syslogname for SysLogAppender for category: " + categoryName); + } + if (!(initFile >> facility)) { + facility = LOG_USER; + } else { + // * 8 + facility *= 8; + } + appender = + new log4cpp::SyslogAppender(categoryName, syslogName, facility); + } +#endif +#if defined(WIN32) + else if (appenderName.compare("nteventlog") == 0) { + std::string source; + if (!(initFile >> source)) { + throw ConfigureFailure("Missing source for NTEventLogAppender for category: " + categoryName); + } + appender = + new log4cpp::NTEventLogAppender(categoryName, source); + } +#endif +#if !defined(LOG4CPP_DISABLE_REMOTE_SYSLOG) + else if (appenderName.compare("remotesyslog") == 0) { + std::string syslogName; + std::string relayer; + int facility; + int portNumber; + if (!(initFile >> syslogName)) { + throw ConfigureFailure("Missing syslogname for SysLogAppender for category: " + categoryName); + } + if (!(initFile >> relayer)) { + throw ConfigureFailure("Missing syslog host for SysLogAppender for category: " + categoryName); + } + if (!(initFile >> facility)) { + facility = LOG_USER; + } + if (!(initFile >> portNumber)) { + portNumber = 514; + } + appender = + new log4cpp::RemoteSyslogAppender(categoryName, syslogName, relayer, facility, portNumber); + } +#endif // LOG4CPP_DISABLE_REMOTE_SYSLOG + else { + throw ConfigureFailure("Invalid appender name (" + + appenderName + + ") in logging configuration file for category: " + + categoryName); + } + if (layout.compare("basic") == 0) + appender->setLayout(new log4cpp::BasicLayout()); + else if (layout.compare("simple") == 0) + appender->setLayout(new log4cpp::SimpleLayout()); + else if (layout.compare("pattern") == 0) { + log4cpp::PatternLayout *layout = + new log4cpp::PatternLayout(); + initFile >> std::ws; // skip whitespace + char pattern[1000]; + initFile.getline(pattern, 1000); + layout->setConversionPattern(std::string(pattern)); + appender->setLayout(layout); + } + else { + throw ConfigureFailure("Invalid layout (" + layout + + ") in logging configuration file for category: " + + categoryName); + } + category.addAppender(appender); + } + } + else if (nextCommand.compare("priority") == 0) { + std::string priority; + if (!(initFile >> priority)) { + throw ConfigureFailure("Missing priority in logging configuration file for category: " + categoryName); + } + + try { + category.setPriority(log4cpp::Priority::getPriorityValue(priority)); + } catch(std::invalid_argument) { + throw ConfigureFailure("Invalid priority ("+priority+") in logging configuration file for category: "+categoryName); + } + } + else if (nextCommand.compare("category") == 0) { + /* + This command means we should "refer" to the category + (in order to have it created). We've already done this + in common setup code for all commands. + */ + } + else { + throw ConfigureFailure("Invalid format in logging configuration file. Command: " + nextCommand); + } + } + } +} + + + diff --git a/VrUtils/log4cpp/src/SimpleLayout.cpp b/VrUtils/log4cpp/src/SimpleLayout.cpp new file mode 100644 index 0000000..632a17c --- /dev/null +++ b/VrUtils/log4cpp/src/SimpleLayout.cpp @@ -0,0 +1,42 @@ +/* + * SimpleLayout.cpp + * + * Copyright 2000, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2000, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include "PortabilityImpl.hh" +#include +#include +#include +#ifdef LOG4CPP_HAVE_SSTREAM +#include +#endif + +#include +#include + +namespace log4cpp { + + SimpleLayout::SimpleLayout() { + } + + SimpleLayout::~SimpleLayout() { + } + + std::string SimpleLayout::format(const LoggingEvent& event) { + std::ostringstream message; + + const std::string& priorityName = Priority::getPriorityName(event.priority); + message.width(Priority::MESSAGE_SIZE);message.setf(std::ios::left); + message << priorityName << ": " << event.message << std::endl; + return message.str(); + } + + std::auto_ptr create_simple_layout(const FactoryParams& params) + { + return std::auto_ptr(new SimpleLayout); + } +} diff --git a/VrUtils/log4cpp/src/SmtpAppender.cpp b/VrUtils/log4cpp/src/SmtpAppender.cpp new file mode 100644 index 0000000..b526cd4 --- /dev/null +++ b/VrUtils/log4cpp/src/SmtpAppender.cpp @@ -0,0 +1,186 @@ +/* + * Copyright 2002, Log4cpp Project. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#if defined(LOG4CPP_HAVE_BOOST) +#include +#if BOOST_VERSION >= 103500 + +#define LOG4CPP_HAVE_INT64_T +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace log4cpp +{ + void sender_shutdown(); + + struct SmptAppender::mail_params + { + mail_params(const std::string& host, + const std::string& from, + const std::string& to, + const std::string& subject) : host_(host), from_(from), to_(to), subject_(subject) + { + } + + std::string host_; + std::string from_; + std::string to_; + std::string subject_; + }; + + namespace + { + struct sender : public boost::noncopyable + { + sender(); + static sender& instance(); + void shutdown(); + static sender* instance_; + void send(const SmptAppender::mail_params& mp, const LoggingEvent& event); + void operator()(); + typedef std::list > mails_t; + + boost::mutex mails_mutex_; + boost::condition data_condition_; + mails_t mails_; + std::auto_ptr sender_thread_; + volatile bool should_exit_; + }; + + sender* sender::instance_ = 0; + boost::once_flag sender_once = BOOST_ONCE_INIT; + + sender::sender() : should_exit_(false) + { + sender_thread_.reset(new boost::thread(boost::ref(*this))); + HierarchyMaintainer::getDefaultMaintainer().register_shutdown_handler(&sender_shutdown); + } + + void create_sender() + { + sender::instance_ = new sender; + } + + sender& sender::instance() + { + boost::call_once(&create_sender, sender_once); + return *instance_; + } + + void sender::shutdown() + { + {boost::mutex::scoped_lock lk(mails_mutex_); + should_exit_ = true; + data_condition_.notify_all(); + } + + sender_thread_->join(); + delete instance_; + } + + void sender::send(const SmptAppender::mail_params& mp, const LoggingEvent& event) + { + boost::mutex::scoped_lock lk(mails_mutex_); + mails_.push_back(mails_t::value_type(mp, event)); + data_condition_.notify_all(); + } + + void sender::operator()() + { + while(!should_exit_) + { + mails_t::iterator i; + + {boost::mutex::scoped_lock lk(mails_mutex_); + if (should_exit_ && mails_.empty()) + return; + + if (mails_.empty()) + { + data_condition_.wait(lk); + if (should_exit_ && mails_.empty()) + return; + } + + i = mails_.begin(); + } + + try + { + boost::asio::ip::tcp::iostream s; + s.exceptions(std::ios::failbit | std::ios::eofbit | std::ios::badbit); + s.connect(i->first.host_, "25"); + std::string buf; + std::getline(s, buf); + s << "HELO test\xd\xa" << std::flush; + std::getline(s, buf); + s << "MAIL FROM:" << i->first.from_<< "\xd\xa" << std::flush; + std::getline(s, buf); + s << "RCPT TO:" << i->first.to_ << "\xd\xa" << std::flush; + std::getline(s, buf); + s << "DATA\xd\xa" << std::flush; + std::getline(s, buf); + s << "Subject: " << i->first.subject_ << "\xd\xa""Content-Transfer-Encoding: 8bit\xd\xa\xd\xa" + << i->second.message << "\xd\xa" ".\xd\xa" << std::flush; + std::getline(s, buf); + s << "QUIT\xd\xa" << std::flush; + std::getline(s, buf); + } + catch(const std::exception& e) + { + std::cout << e.what(); + } + + {boost::mutex::scoped_lock lk(mails_mutex_); + mails_.erase(i); + } + } + } + } + + void sender_shutdown() + { + sender::instance().shutdown(); + } + + SmptAppender::SmptAppender(const std::string& name, + const std::string& host, + const std::string& from, + const std::string& to, + const std::string& subject) : LayoutAppender(name), + mail_params_(new mail_params(host, from, to, subject)) + + { + } + + void SmptAppender::_append(const LoggingEvent& event) + { + sender::instance().send(*mail_params_, event); + } + + SmptAppender::~SmptAppender() + { + delete mail_params_; + } + + std::auto_ptr create_smtp_appender(const FactoryParams& params) + { + std::string name, host, from, to, subject; + params.get_for("SMTP appender").required("name", name)("host", host)("from", from) + ("to", to)("subject", subject); + return std::auto_ptr(new SmptAppender(name, host, from, to, subject)); + } +} +#endif // BOOST_VERSION >= 103500 +#endif // LOG4CPP_HAS_BOOST diff --git a/VrUtils/log4cpp/src/StringQueueAppender.cpp b/VrUtils/log4cpp/src/StringQueueAppender.cpp new file mode 100644 index 0000000..48593ee --- /dev/null +++ b/VrUtils/log4cpp/src/StringQueueAppender.cpp @@ -0,0 +1,57 @@ +/* + * StringQueueAppender.cpp + * + * Copyright 2000, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2000, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include "PortabilityImpl.hh" +#include + +namespace log4cpp { + + StringQueueAppender::StringQueueAppender(const std::string& name) : + LayoutAppender(name) { + } + + StringQueueAppender::~StringQueueAppender() { + close(); + } + + void StringQueueAppender::close() { + // empty + } + + void StringQueueAppender::_append(const LoggingEvent& event) { + _queue.push(_getLayout().format(event)); + } + + bool StringQueueAppender::reopen() { + return true; + } + + std::queue& StringQueueAppender::getQueue() { + return _queue; + } + + const std::queue& StringQueueAppender::getQueue() const { + return _queue; + } + + size_t StringQueueAppender::queueSize() const { + return getQueue().size(); + } + + std::string StringQueueAppender::popMessage() { + std::string message; + + if (!_queue.empty()) { + message = _queue.front(); + _queue.pop(); + } + + return message; + } +} diff --git a/VrUtils/log4cpp/src/StringUtil.cpp b/VrUtils/log4cpp/src/StringUtil.cpp new file mode 100644 index 0000000..74e7222 --- /dev/null +++ b/VrUtils/log4cpp/src/StringUtil.cpp @@ -0,0 +1,98 @@ +/* + * StringUtil.cpp + * + * Copyright 2002, Log4cpp Project. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ +#include "StringUtil.hh" +#include +#include + +#if defined(_MSC_VER) + #define VSNPRINTF _vsnprintf +#else +#ifdef LOG4CPP_HAVE_SNPRINTF + #define VSNPRINTF vsnprintf +#else +/* use alternative snprintf() from http://www.ijs.si/software/snprintf/ */ + +#define HAVE_SNPRINTF +#define PREFER_PORTABLE_SNPRINTF + +#include +#include + +extern "C" { +#include "snprintf.c" +} + +#define VSNPRINTF portable_vsnprintf + +#endif // LOG4CPP_HAVE_SNPRINTF +#endif // _MSC_VER + +namespace log4cpp { + + std::string StringUtil::vform(const char* format, va_list args) { + size_t size = 1024; + char* buffer = new char[size]; + + while (1) { + va_list args_copy; + +#if defined(_MSC_VER) || defined(__BORLANDC__) + args_copy = args; +#else + va_copy(args_copy, args); +#endif + + int n = VSNPRINTF(buffer, size, format, args_copy); + + va_end(args_copy); + + // If that worked, return a string. + if ((n > -1) && (static_cast(n) < size)) { + std::string s(buffer); + delete [] buffer; + return s; + } + + // Else try again with more space. + size = (n > -1) ? + n + 1 : // ISO/IEC 9899:1999 + size * 2; // twice the old size + + delete [] buffer; + buffer = new char[size]; + } + } + + std::string StringUtil::trim(const std::string& s) { + static const char* whiteSpace = " \t\r\n"; + + // test for null string + if(s.empty()) + return s; + + // find first non-space character + std::string::size_type b = s.find_first_not_of(whiteSpace); + if(b == std::string::npos) // No non-spaces + return ""; + + // find last non-space character + std::string::size_type e = s.find_last_not_of(whiteSpace); + + // return the remaining characters + return std::string(s, b, e - b + 1); + } + + unsigned int StringUtil::split(std::vector& v, + const std::string& s, + char delimiter, unsigned int maxSegments) { + v.clear(); + std::back_insert_iterator > it(v); + return split(it, s, delimiter, maxSegments); + } + +} diff --git a/VrUtils/log4cpp/src/StringUtil.hh b/VrUtils/log4cpp/src/StringUtil.hh new file mode 100644 index 0000000..b279642 --- /dev/null +++ b/VrUtils/log4cpp/src/StringUtil.hh @@ -0,0 +1,84 @@ +/* + * StringUtil.hh + * + * Copyright 2002, Log4cpp Project. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifndef _LOG4CPP_STRINGUTIL_HH +#define _LOG4CPP_STRINGUTIL_HH + +#include "PortabilityImpl.hh" +#include +#include +#include +#include + +namespace log4cpp { + + class StringUtil { + public: + + /** + Returns a string contructed from the a format specifier + and a va_list of arguments, analogously to vprintf(3). + @param format the format specifier. + @param args the va_list of arguments. + **/ + static std::string vform(const char* format, va_list args); + + /** + Returns a string identical to the given string but without leading + or trailing HTABs or spaces. + **/ + static std::string trim(const std::string& s); + + /** + splits a string into a vector of string segments based on the + given delimiter. + @param v The vector in which the segments will be stored. The vector + will be emptied before storing the segments + @param s The string to split into segments. + @param delimiter The delimiter character + @param maxSegments the maximum number of segments. Upon return + v.size() <= maxSegments. The string is scanned from left to right + so v[maxSegments - 1] may contain a string containing the delimiter + character. + @return The actual number of segments (limited by maxSegments). + **/ + static unsigned int split(std::vector& v, + const std::string& s, char delimiter, + unsigned int maxSegments = INT_MAX); + /** + splits a string into string segments based on the given delimiter + and assigns the segments through an output_iterator. + @param output The output_iterator through which to assign the string + segments. Typically this will be a back_insertion_iterator. + @param s The string to split into segments. + @param delimiter The delimiter character + @param maxSegments The maximum number of segments. + @return The actual number of segments (limited by maxSegments). + **/ + template static unsigned int split(T& output, + const std::string& s, char delimiter, + unsigned int maxSegments = INT_MAX) { + std::string::size_type left = 0; + unsigned int i; + for(i = 1; i < maxSegments; i++) { + std::string::size_type right = s.find(delimiter, left); + if (right == std::string::npos) { + break; + } + *output++ = s.substr(left, right - left); + left = right + 1; + } + + *output++ = s.substr(left); + return i; + } + }; +} + +#endif // _LOG4CPP_STRINGUTIL_HH + diff --git a/VrUtils/log4cpp/src/SyslogAppender.cpp b/VrUtils/log4cpp/src/SyslogAppender.cpp new file mode 100644 index 0000000..e615608 --- /dev/null +++ b/VrUtils/log4cpp/src/SyslogAppender.cpp @@ -0,0 +1,88 @@ +/* + * SyslogAppender.cpp + * + * Copyright 2000, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2000, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include "PortabilityImpl.hh" +#if LOG4CPP_HAVE_SYSLOG + +#include +#include +#include +#include +#include +#include +#include + +namespace log4cpp { + + int SyslogAppender::toSyslogPriority(Priority::Value priority) { + static int priorities[8] = { LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, + LOG_WARNING, LOG_NOTICE, LOG_INFO, + LOG_DEBUG }; + int result; + + priority++; + priority /= 100; + + if (priority < 0) { + result = LOG_EMERG; + } else if (priority > 7) { + result = LOG_DEBUG; + } else { + result = priorities[priority]; + } + + return result; + } + + + SyslogAppender::SyslogAppender(const std::string& name, + const std::string& syslogName, + int facility) : + LayoutAppender(name), + _syslogName(syslogName), + _facility(facility) + { + open(); + } + + SyslogAppender::~SyslogAppender() { + close(); + } + + void SyslogAppender::open() { + openlog(_syslogName.c_str(), 0, _facility); + } + + void SyslogAppender::close() { + ::closelog(); + } + + void SyslogAppender::_append(const LoggingEvent& event) { + std::string message(_getLayout().format(event)); + int priority = toSyslogPriority(event.priority); + ::syslog(priority | _facility, "%s", message.c_str()); + } + + bool SyslogAppender::reopen() { + close(); + open(); + return true; + } + + std::auto_ptr create_syslog_appender(const FactoryParams& params) + { + std::string name, syslog_name; + int facility = 0; + params.get_for("syslog appender").required("name", name)("syslog_name", syslog_name) + .optional("facility", facility); + return std::auto_ptr(new SyslogAppender(name, syslog_name, facility)); + } +} + +#endif // LOG4CPP_HAVE_SYSLOG diff --git a/VrUtils/log4cpp/src/TimeStamp.cpp b/VrUtils/log4cpp/src/TimeStamp.cpp new file mode 100644 index 0000000..c6df805 --- /dev/null +++ b/VrUtils/log4cpp/src/TimeStamp.cpp @@ -0,0 +1,53 @@ +/* + * TimeStamp.cpp + * + * Copyright 2001, LifeLine Networks BV (www.lifeline.nl). All rights reserved. + * Copyright 2001, Bastiaan Bakker. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include + +#include + +#ifdef LOG4CPP_HAVE_GETTIMEOFDAY +#include +#else +#ifdef LOG4CPP_HAVE_FTIME +#include +#else +#include +#endif +#endif + +namespace log4cpp { + + LOG4CPP_EXPORT TimeStamp TimeStamp::_startStamp; + + TimeStamp::TimeStamp() { +#ifdef LOG4CPP_HAVE_GETTIMEOFDAY + struct timeval tv; + ::gettimeofday(&tv, NULL); + _seconds = tv.tv_sec; + _microSeconds = tv.tv_usec; +#else +#ifdef LOG4CPP_HAVE_FTIME + struct timeb tb; + ::ftime(&tb); + _seconds = tb.time; + _microSeconds = 1000 * tb.millitm; +#else + _seconds = ::time(NULL); + _microSeconds = 0; +#endif +#endif + } + + TimeStamp::TimeStamp(unsigned int seconds, unsigned int microSeconds) : + _seconds(seconds), + _microSeconds(microSeconds) { + } +} + + diff --git a/VrUtils/log4cpp/src/TriggeringEventEvaluatorFactory.cpp b/VrUtils/log4cpp/src/TriggeringEventEvaluatorFactory.cpp new file mode 100644 index 0000000..203e966 --- /dev/null +++ b/VrUtils/log4cpp/src/TriggeringEventEvaluatorFactory.cpp @@ -0,0 +1,50 @@ +/* + * Copyright 2002, Log4cpp Project. All rights reserved. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#include +#include + +namespace log4cpp +{ + static TriggeringEventEvaluatorFactory* evaluators_factory_ = 0; + std::auto_ptr create_level_evaluator(const FactoryParams& params); + + TriggeringEventEvaluatorFactory& TriggeringEventEvaluatorFactory::getInstance() + { + if (!evaluators_factory_) + { + std::auto_ptr af(new TriggeringEventEvaluatorFactory); + af->registerCreator("level", &create_level_evaluator); + evaluators_factory_ = af.release(); + } + + return *evaluators_factory_; + } + + void TriggeringEventEvaluatorFactory::registerCreator(const std::string& class_name, create_function_t create_function) + { + const_iterator i = creators_.find(class_name); + if (i != creators_.end()) + throw std::invalid_argument("Creator for Triggering event evaluator with type name '" + class_name + "' allready registered"); + + creators_[class_name] = create_function; + } + + std::auto_ptr TriggeringEventEvaluatorFactory::create(const std::string& class_name, const params_t& params) + { + const_iterator i = creators_.find(class_name); + if (i == creators_.end()) + throw std::invalid_argument("There is no triggering event evaluator with type name '" + class_name + "'"); + + return (*i->second)(params); + } + + bool TriggeringEventEvaluatorFactory::registered(const std::string& class_name) const + { + return creators_.end() != creators_.find(class_name); + } +} + diff --git a/VrUtils/log4cpp/src/Win32DebugAppender.cpp b/VrUtils/log4cpp/src/Win32DebugAppender.cpp new file mode 100644 index 0000000..1b8d93b --- /dev/null +++ b/VrUtils/log4cpp/src/Win32DebugAppender.cpp @@ -0,0 +1,56 @@ +/* + * Win32DebugAppender.cpp + * + * Copyright 2002, the Log4cpp project. + * + * See the COPYING file for the terms of usage and distribution. + */ + +#ifdef WIN32 // only available on Win32 + +#include "PortabilityImpl.hh" +#ifdef LOG4CPP_HAVE_IO_H +# include +#endif + +#include +#include + +#include +#include "log4cpp/Category.hh" +#include "log4cpp/Win32DebugAppender.hh" +#include +#include + +namespace log4cpp { + + Win32DebugAppender::Win32DebugAppender(const std::string& name) : + LayoutAppender(name) { + } + + Win32DebugAppender::~Win32DebugAppender() { + close(); + } + + void Win32DebugAppender::close() { + } + + void Win32DebugAppender::_append(const LoggingEvent& event) { + std::string message(_getLayout().format(event)); + #ifdef __WIN_ROS__ + ::OutputDebugString((LPCSTR)message.c_str()); + #else + ::OutputDebugString((LPCWSTR)message.c_str()); + #endif + } + + std::auto_ptr create_win32_debug_appender(const FactoryParams& params) + { + std::string name; + params.get_for("win32 debug appender").required("name", name); + + return std::auto_ptr(new Win32DebugAppender(name)); + } +} + +#endif // WIN32 diff --git a/VrUtils/log4cpp/src/snprintf.c b/VrUtils/log4cpp/src/snprintf.c new file mode 100644 index 0000000..f33fe17 --- /dev/null +++ b/VrUtils/log4cpp/src/snprintf.c @@ -0,0 +1,1025 @@ +/* + * snprintf.c - a portable implementation of snprintf + * + * AUTHOR + * Mark Martinec , April 1999. + * + * Copyright 1999, Mark Martinec. All rights reserved. + * + * TERMS AND CONDITIONS + * This program is free software; you can redistribute it and/or modify + * it under the terms of the "Frontier Artistic License" which comes + * with this Kit. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Frontier Artistic License for more details. + * + * You should have received a copy of the Frontier Artistic License + * with this Kit in the file named LICENSE.txt . + * If not, I'll be glad to provide one. + * + * FEATURES + * - careful adherence to specs regarding flags, field width and precision; + * - good performance for large string handling (large format, large + * argument or large paddings). Performance is similar to system's sprintf + * and in several cases significantly better (make sure you compile with + * optimizations turned on, tell the compiler the code is strict ANSI + * if necessary to give it more freedom for optimizations); + * - return value semantics per ISO/IEC 9899:1999 ("ISO C99"); + * - written in standard ISO/ANSI C - requires an ANSI C compiler. + * + * SUPPORTED CONVERSION SPECIFIERS AND DATA TYPES + * + * This snprintf only supports the following conversion specifiers: + * s, c, d, u, o, x, X, p (and synonyms: i, D, U, O - see below) + * with flags: '-', '+', ' ', '0' and '#'. + * An asterisk is supported for field width as well as precision. + * + * Length modifiers 'h' (short int), 'l' (long int), + * and 'll' (long long int) are supported. + * NOTE: + * If macro SNPRINTF_LONGLONG_SUPPORT is not defined (default) the + * length modifier 'll' is recognized but treated the same as 'l', + * which may cause argument value truncation! Defining + * SNPRINTF_LONGLONG_SUPPORT requires that your system's sprintf also + * handles length modifier 'll'. long long int is a language extension + * which may not be portable. + * + * Conversion of numeric data (conversion specifiers d, u, o, x, X, p) + * with length modifiers (none or h, l, ll) is left to the system routine + * sprintf, but all handling of flags, field width and precision as well as + * c and s conversions is done very carefully by this portable routine. + * If a string precision (truncation) is specified (e.g. %.8s) it is + * guaranteed the string beyond the specified precision will not be referenced. + * + * Length modifiers h, l and ll are ignored for c and s conversions (data + * types wint_t and wchar_t are not supported). + * + * The following common synonyms for conversion characters are supported: + * - i is a synonym for d + * - D is a synonym for ld, explicit length modifiers are ignored + * - U is a synonym for lu, explicit length modifiers are ignored + * - O is a synonym for lo, explicit length modifiers are ignored + * The D, O and U conversion characters are nonstandard, they are supported + * for backward compatibility only, and should not be used for new code. + * + * The following is specifically NOT supported: + * - flag ' (thousands' grouping character) is recognized but ignored + * - numeric conversion specifiers: f, e, E, g, G and synonym F, + * as well as the new a and A conversion specifiers + * - length modifier 'L' (long double) and 'q' (quad - use 'll' instead) + * - wide character/string conversions: lc, ls, and nonstandard + * synonyms C and S + * - writeback of converted string length: conversion character n + * - the n$ specification for direct reference to n-th argument + * - locales + * + * It is permitted for str_m to be zero, and it is permitted to specify NULL + * pointer for resulting string argument if str_m is zero (as per ISO C99). + * + * The return value is the number of characters which would be generated + * for the given input, excluding the trailing null. If this value + * is greater or equal to str_m, not all characters from the result + * have been stored in str, output bytes beyond the (str_m-1) -th character + * are discarded. If str_m is greater than zero it is guaranteed + * the resulting string will be null-terminated. + * + * NOTE that this matches the ISO C99, OpenBSD, and GNU C library 2.1, + * but is different from some older and vendor implementations, + * and is also different from XPG, XSH5, SUSv2 specifications. + * For historical discussion on changes in the semantics and standards + * of snprintf see printf(3) man page in the Linux programmers manual. + * + * Routines asprintf and vasprintf return a pointer (in the ptr argument) + * to a buffer sufficiently large to hold the resulting string. This pointer + * should be passed to free(3) to release the allocated storage when it is + * no longer needed. If sufficient space cannot be allocated, these functions + * will return -1 and set ptr to be a NULL pointer. These two routines are a + * GNU C library extensions (glibc). + * + * Routines asnprintf and vasnprintf are similar to asprintf and vasprintf, + * yet, like snprintf and vsnprintf counterparts, will write at most str_m-1 + * characters into the allocated output string, the last character in the + * allocated buffer then gets the terminating null. If the formatted string + * length (the return value) is greater than or equal to the str_m argument, + * the resulting string was truncated and some of the formatted characters + * were discarded. These routines present a handy way to limit the amount + * of allocated memory to some sane value. + * + * AVAILABILITY + * http://www.ijs.si/software/snprintf/ + * + * REVISION HISTORY + * 1999-04 V0.9 Mark Martinec + * - initial version, some modifications after comparing printf + * man pages for Digital Unix 4.0, Solaris 2.6 and HPUX 10, + * and checking how Perl handles sprintf (differently!); + * 1999-04-09 V1.0 Mark Martinec + * - added main test program, fixed remaining inconsistencies, + * added optional (long long int) support; + * 1999-04-12 V1.1 Mark Martinec + * - support the 'p' conversion (pointer to void); + * - if a string precision is specified + * make sure the string beyond the specified precision + * will not be referenced (e.g. by strlen); + * 1999-04-13 V1.2 Mark Martinec + * - support synonyms %D=%ld, %U=%lu, %O=%lo; + * - speed up the case of long format string with few conversions; + * 1999-06-30 V1.3 Mark Martinec + * - fixed runaway loop (eventually crashing when str_l wraps + * beyond 2^31) while copying format string without + * conversion specifiers to a buffer that is too short + * (thanks to Edwin Young for + * spotting the problem); + * - added macros PORTABLE_SNPRINTF_VERSION_(MAJOR|MINOR) + * to snprintf.h + * 2000-02-14 V2.0 (never released) Mark Martinec + * - relaxed license terms: The Artistic License now applies. + * You may still apply the GNU GENERAL PUBLIC LICENSE + * as was distributed with previous versions, if you prefer; + * - changed REVISION HISTORY dates to use ISO 8601 date format; + * - added vsnprintf (patch also independently proposed by + * Caolan McNamara 2000-05-04, and Keith M Willenson 2000-06-01) + * 2000-06-27 V2.1 Mark Martinec + * - removed POSIX check for str_m<1; value 0 for str_m is + * allowed by ISO C99 (and GNU C library 2.1) - (pointed out + * on 2000-05-04 by Caolan McNamara, caolan@ csn dot ul dot ie). + * Besides relaxed license this change in standards adherence + * is the main reason to bump up the major version number; + * - added nonstandard routines asnprintf, vasnprintf, asprintf, + * vasprintf that dynamically allocate storage for the + * resulting string; these routines are not compiled by default, + * see comments where NEED_V?ASN?PRINTF macros are defined; + * - autoconf contributed by Caolan McNamara + * 2000-10-06 V2.2 Mark Martinec + * - BUG FIX: the %c conversion used a temporary variable + * that was no longer in scope when referenced, + * possibly causing incorrect resulting character; + * - BUG FIX: make precision and minimal field width unsigned + * to handle huge values (2^31 <= n < 2^32) correctly; + * also be more careful in the use of signed/unsigned/size_t + * internal variables - probably more careful than many + * vendor implementations, but there may still be a case + * where huge values of str_m, precision or minimal field + * could cause incorrect behaviour; + * - use separate variables for signed/unsigned arguments, + * and for short/int, long, and long long argument lengths + * to avoid possible incompatibilities on certain + * computer architectures. Also use separate variable + * arg_sign to hold sign of a numeric argument, + * to make code more transparent; + * - some fiddling with zero padding and "0x" to make it + * Linux compatible; + * - systematically use macros fast_memcpy and fast_memset + * instead of case-by-case hand optimization; determine some + * breakeven string lengths for different architectures; + * - terminology change: 'format' -> 'conversion specifier', + * 'C9x' -> 'ISO/IEC 9899:1999 ("ISO C99")', + * 'alternative form' -> 'alternate form', + * 'data type modifier' -> 'length modifier'; + * - several comments rephrased and new ones added; + * - make compiler not complain about 'credits' defined but + * not used; + */ + + +/* Define HAVE_SNPRINTF if your system already has snprintf and vsnprintf. + * + * If HAVE_SNPRINTF is defined this module will not produce code for + * snprintf and vsnprintf, unless PREFER_PORTABLE_SNPRINTF is defined as well, + * causing this portable version of snprintf to be called portable_snprintf + * (and portable_vsnprintf). + */ +/* #define HAVE_SNPRINTF */ + +/* Define PREFER_PORTABLE_SNPRINTF if your system does have snprintf and + * vsnprintf but you would prefer to use the portable routine(s) instead. + * In this case the portable routine is declared as portable_snprintf + * (and portable_vsnprintf) and a macro 'snprintf' (and 'vsnprintf') + * is defined to expand to 'portable_v?snprintf' - see file snprintf.h . + * Defining this macro is only useful if HAVE_SNPRINTF is also defined, + * but does does no harm if defined nevertheless. + */ +/* #define PREFER_PORTABLE_SNPRINTF */ + +/* Define SNPRINTF_LONGLONG_SUPPORT if you want to support + * data type (long long int) and length modifier 'll' (e.g. %lld). + * If undefined, 'll' is recognized but treated as a single 'l'. + * + * If the system's sprintf does not handle 'll' + * the SNPRINTF_LONGLONG_SUPPORT must not be defined! + * + * This is off by default as (long long int) is a language extension. + */ +/* #define SNPRINTF_LONGLONG_SUPPORT */ + +/* Define NEED_SNPRINTF_ONLY if you only need snprintf, and not vsnprintf. + * If NEED_SNPRINTF_ONLY is defined, the snprintf will be defined directly, + * otherwise both snprintf and vsnprintf routines will be defined + * and snprintf will be a simple wrapper around vsnprintf, at the expense + * of an extra procedure call. + */ +/* #define NEED_SNPRINTF_ONLY */ + +/* Define NEED_V?ASN?PRINTF macros if you need library extension + * routines asprintf, vasprintf, asnprintf, vasnprintf respectively, + * and your system library does not provide them. They are all small + * wrapper routines around portable_vsnprintf. Defining any of the four + * NEED_V?ASN?PRINTF macros automatically turns off NEED_SNPRINTF_ONLY + * and turns on PREFER_PORTABLE_SNPRINTF. + * + * Watch for name conflicts with the system library if these routines + * are already present there. + * + * NOTE: vasprintf and vasnprintf routines need va_copy() from stdarg.h, as + * specified by C99, to be able to traverse the same list of arguments twice. + * I don't know of any other standard and portable way of achieving the same. + * With some versions of gcc you may use __va_copy(). You might even get away + * with "ap2 = ap", in this case you must not call va_end(ap2) ! + * #define va_copy(ap2,ap) ap2 = ap + */ +/* #define NEED_ASPRINTF */ +/* #define NEED_ASNPRINTF */ +/* #define NEED_VASPRINTF */ +/* #define NEED_VASNPRINTF */ + + +/* Define the following macros if desired: + * SOLARIS_COMPATIBLE, SOLARIS_BUG_COMPATIBLE, + * HPUX_COMPATIBLE, HPUX_BUG_COMPATIBLE, LINUX_COMPATIBLE, + * DIGITAL_UNIX_COMPATIBLE, DIGITAL_UNIX_BUG_COMPATIBLE, + * PERL_COMPATIBLE, PERL_BUG_COMPATIBLE, + * + * - For portable applications it is best not to rely on peculiarities + * of a given implementation so it may be best not to define any + * of the macros that select compatibility and to avoid features + * that vary among the systems. + * + * - Selecting compatibility with more than one operating system + * is not strictly forbidden but is not recommended. + * + * - 'x'_BUG_COMPATIBLE implies 'x'_COMPATIBLE . + * + * - 'x'_COMPATIBLE refers to (and enables) a behaviour that is + * documented in a sprintf man page on a given operating system + * and actually adhered to by the system's sprintf (but not on + * most other operating systems). It may also refer to and enable + * a behaviour that is declared 'undefined' or 'implementation specific' + * in the man page but a given implementation behaves predictably + * in a certain way. + * + * - 'x'_BUG_COMPATIBLE refers to (and enables) a behaviour of system's sprintf + * that contradicts the sprintf man page on the same operating system. + * + * - I do not claim that the 'x'_COMPATIBLE and 'x'_BUG_COMPATIBLE + * conditionals take into account all idiosyncrasies of a particular + * implementation, there may be other incompatibilities. + */ + + + +/* ============================================= */ +/* NO USER SERVICABLE PARTS FOLLOWING THIS POINT */ +/* ============================================= */ + +#define PORTABLE_SNPRINTF_VERSION_MAJOR 2 +#define PORTABLE_SNPRINTF_VERSION_MINOR 2 + +#if defined(NEED_ASPRINTF) || defined(NEED_ASNPRINTF) || defined(NEED_VASPRINTF) || defined(NEED_VASNPRINTF) +# if defined(NEED_SNPRINTF_ONLY) +# undef NEED_SNPRINTF_ONLY +# endif +# if !defined(PREFER_PORTABLE_SNPRINTF) +# define PREFER_PORTABLE_SNPRINTF +# endif +#endif + +#if defined(SOLARIS_BUG_COMPATIBLE) && !defined(SOLARIS_COMPATIBLE) +#define SOLARIS_COMPATIBLE +#endif + +#if defined(HPUX_BUG_COMPATIBLE) && !defined(HPUX_COMPATIBLE) +#define HPUX_COMPATIBLE +#endif + +#if defined(DIGITAL_UNIX_BUG_COMPATIBLE) && !defined(DIGITAL_UNIX_COMPATIBLE) +#define DIGITAL_UNIX_COMPATIBLE +#endif + +#if defined(PERL_BUG_COMPATIBLE) && !defined(PERL_COMPATIBLE) +#define PERL_COMPATIBLE +#endif + +#if defined(LINUX_BUG_COMPATIBLE) && !defined(LINUX_COMPATIBLE) +#define LINUX_COMPATIBLE +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef isdigit +#undef isdigit +#endif +#define isdigit(c) ((c) >= '0' && (c) <= '9') + +/* For copying strings longer or equal to 'breakeven_point' + * it is more efficient to call memcpy() than to do it inline. + * The value depends mostly on the processor architecture, + * but also on the compiler and its optimization capabilities. + * The value is not critical, some small value greater than zero + * will be just fine if you don't care to squeeze every drop + * of performance out of the code. + * + * Small values favor memcpy, large values favor inline code. + */ +#if defined(__alpha__) || defined(__alpha) +# define breakeven_point 2 /* AXP (DEC Alpha) - gcc or cc or egcs */ +#endif +#if defined(__i386__) || defined(__i386) +# define breakeven_point 12 /* Intel Pentium/Linux - gcc 2.96 */ +#endif +#if defined(__hppa) +# define breakeven_point 10 /* HP-PA - gcc */ +#endif +#if defined(__sparc__) || defined(__sparc) +# define breakeven_point 33 /* Sun Sparc 5 - gcc 2.8.1 */ +#endif + +/* some other values of possible interest: */ +/* #define breakeven_point 8 */ /* VAX 4000 - vaxc */ +/* #define breakeven_point 19 */ /* VAX 4000 - gcc 2.7.0 */ + +#ifndef breakeven_point +# define breakeven_point 6 /* some reasonable one-size-fits-all value */ +#endif + +#define fast_memcpy(d,s,n) \ + { register size_t nn = (size_t)(n); \ + if (nn >= breakeven_point) memcpy((d), (s), nn); \ + else if (nn > 0) { /* proc call overhead is worth only for large strings*/\ + register char *dd; register const char *ss; \ + for (ss=(s), dd=(d); nn>0; nn--) *dd++ = *ss++; } } + +#define fast_memset(d,c,n) \ + { register size_t nn = (size_t)(n); \ + if (nn >= breakeven_point) memset((d), (int)(c), nn); \ + else if (nn > 0) { /* proc call overhead is worth only for large strings*/\ + register char *dd; register const int cc=(int)(c); \ + for (dd=(d); nn>0; nn--) *dd++ = cc; } } + +/* prototypes */ + +#if defined(NEED_ASPRINTF) +int asprintf (char **ptr, const char *fmt, /*args*/ ...); +#endif +#if defined(NEED_VASPRINTF) +int vasprintf (char **ptr, const char *fmt, va_list ap); +#endif +#if defined(NEED_ASNPRINTF) +int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...); +#endif +#if defined(NEED_VASNPRINTF) +int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap); +#endif + +#if defined(HAVE_SNPRINTF) +/* declare our portable snprintf routine under name portable_snprintf */ +/* declare our portable vsnprintf routine under name portable_vsnprintf */ +#else +/* declare our portable routines under names snprintf and vsnprintf */ +#define portable_snprintf snprintf +#if !defined(NEED_SNPRINTF_ONLY) +#define portable_vsnprintf vsnprintf +#endif +#endif + +#if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF) +int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...); +#if !defined(NEED_SNPRINTF_ONLY) +int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap); +#endif +#endif + +/* declarations */ + +static char credits[] = "\n\ +@(#)snprintf.c, v2.2: Mark Martinec, \n\ +@(#)snprintf.c, v2.2: Copyright 1999, Mark Martinec. Frontier Artistic License applies.\n\ +@(#)snprintf.c, v2.2: http://www.ijs.si/software/snprintf/\n"; + +#if defined(NEED_ASPRINTF) +int asprintf(char **ptr, const char *fmt, /*args*/ ...) { + va_list ap; + size_t str_m; + int str_l; + + *ptr = NULL; + va_start(ap, fmt); /* measure the required size */ + str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap); + va_end(ap); + assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */ + *ptr = (char *) malloc(str_m = (size_t)str_l + 1); + if (*ptr == NULL) { errno = ENOMEM; str_l = -1; } + else { + int str_l2; + va_start(ap, fmt); + str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap); + va_end(ap); + assert(str_l2 == str_l); + } + return str_l; +} +#endif + +#if defined(NEED_VASPRINTF) +int vasprintf(char **ptr, const char *fmt, va_list ap) { + size_t str_m; + int str_l; + + *ptr = NULL; + { va_list ap2; + va_copy(ap2, ap); /* don't consume the original ap, we'll need it again */ + str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/ + va_end(ap2); + } + assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */ + *ptr = (char *) malloc(str_m = (size_t)str_l + 1); + if (*ptr == NULL) { errno = ENOMEM; str_l = -1; } + else { + int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap); + assert(str_l2 == str_l); + } + return str_l; +} +#endif + +#if defined(NEED_ASNPRINTF) +int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...) { + va_list ap; + int str_l; + + *ptr = NULL; + va_start(ap, fmt); /* measure the required size */ + str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap); + va_end(ap); + assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */ + if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1; /* truncate */ + /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */ + if (str_m == 0) { /* not interested in resulting string, just return size */ + } else { + *ptr = (char *) malloc(str_m); + if (*ptr == NULL) { errno = ENOMEM; str_l = -1; } + else { + int str_l2; + va_start(ap, fmt); + str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap); + va_end(ap); + assert(str_l2 == str_l); + } + } + return str_l; +} +#endif + +#if defined(NEED_VASNPRINTF) +int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap) { + int str_l; + + *ptr = NULL; + { va_list ap2; + va_copy(ap2, ap); /* don't consume the original ap, we'll need it again */ + str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/ + va_end(ap2); + } + assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */ + if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1; /* truncate */ + /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */ + if (str_m == 0) { /* not interested in resulting string, just return size */ + } else { + *ptr = (char *) malloc(str_m); + if (*ptr == NULL) { errno = ENOMEM; str_l = -1; } + else { + int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap); + assert(str_l2 == str_l); + } + } + return str_l; +} +#endif + +/* + * If the system does have snprintf and the portable routine is not + * specifically required, this module produces no code for snprintf/vsnprintf. + */ +#if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF) + +#if !defined(NEED_SNPRINTF_ONLY) +int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) { + va_list ap; + int str_l; + + va_start(ap, fmt); + str_l = portable_vsnprintf(str, str_m, fmt, ap); + va_end(ap); + return str_l; +} +#endif + +#if defined(NEED_SNPRINTF_ONLY) +int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) { +#else +int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap) { +#endif + +#if defined(NEED_SNPRINTF_ONLY) + va_list ap; +#endif + size_t str_l = 0; + const char *p = fmt; + +/* In contrast with POSIX, the ISO C99 now says + * that str can be NULL and str_m can be 0. + * This is more useful than the old: if (str_m < 1) return -1; */ + +#if defined(NEED_SNPRINTF_ONLY) + va_start(ap, fmt); +#endif + if (!p) p = ""; + while (*p) { + if (*p != '%') { + /* if (str_l < str_m) str[str_l++] = *p++; -- this would be sufficient */ + /* but the following code achieves better performance for cases + * where format string is long and contains few conversions */ + const char *q = strchr(p+1,'%'); + size_t n = !q ? strlen(p) : (q-p); + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memcpy(str+str_l, p, (n>avail?avail:n)); + } + p += n; str_l += n; + } else { + const char *starting_p; + size_t min_field_width = 0, precision = 0; + int zero_padding = 0, precision_specified = 0, justify_left = 0; + int alternate_form = 0, force_sign = 0; + int space_for_positive = 1; /* If both the ' ' and '+' flags appear, + the ' ' flag should be ignored. */ + char length_modifier = '\0'; /* allowed values: \0, h, l, L */ + char tmp[32];/* temporary buffer for simple numeric->string conversion */ + + const char *str_arg; /* string address in case of string argument */ + size_t str_arg_l; /* natural field width of arg without padding + and sign */ + unsigned char uchar_arg; + /* unsigned char argument value - only defined for c conversion. + N.B. standard explicitly states the char argument for + the c conversion is unsigned */ + + size_t number_of_zeros_to_pad = 0; + /* number of zeros to be inserted for numeric conversions + as required by the precision or minimal field width */ + + size_t zero_padding_insertion_ind = 0; + /* index into tmp where zero padding is to be inserted */ + + char fmt_spec = '\0'; + /* current conversion specifier character */ + + str_arg = credits;/* just to make compiler happy (defined but not used)*/ + str_arg = NULL; + starting_p = p; p++; /* skip '%' */ + /* parse flags */ + while (*p == '0' || *p == '-' || *p == '+' || + *p == ' ' || *p == '#' || *p == '\'') { + switch (*p) { + case '0': zero_padding = 1; break; + case '-': justify_left = 1; break; + case '+': force_sign = 1; space_for_positive = 0; break; + case ' ': force_sign = 1; + /* If both the ' ' and '+' flags appear, the ' ' flag should be ignored */ +#ifdef PERL_COMPATIBLE + /* ... but in Perl the last of ' ' and '+' applies */ + space_for_positive = 1; +#endif + break; + case '#': alternate_form = 1; break; + case '\'': break; + } + p++; + } + /* If the '0' and '-' flags both appear, the '0' flag should be ignored. */ + + /* parse field width */ + if (*p == '*') { + int j; + p++; j = va_arg(ap, int); + if (j >= 0) min_field_width = j; + else { min_field_width = -j; justify_left = 1; } + } else if (isdigit((int)(*p))) { + /* size_t could be wider than unsigned int; + make sure we treat argument like common implementations do */ + unsigned int uj = *p++ - '0'; + while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0'); + min_field_width = uj; + } + /* parse precision */ + if (*p == '.') { + p++; precision_specified = 1; + if (*p == '*') { + int j = va_arg(ap, int); + p++; + if (j >= 0) precision = j; + else { + precision_specified = 0; precision = 0; + /* NOTE: + * Solaris 2.6 man page claims that in this case the precision + * should be set to 0. Digital Unix 4.0, HPUX 10 and BSD man page + * claim that this case should be treated as unspecified precision, + * which is what we do here. + */ + } + } else if (isdigit((int)(*p))) { + /* size_t could be wider than unsigned int; + make sure we treat argument like common implementations do */ + unsigned int uj = *p++ - '0'; + while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0'); + precision = uj; + } + } + /* parse 'h', 'l' and 'll' length modifiers */ + if (*p == 'h' || *p == 'l') { + length_modifier = *p; p++; + if (length_modifier == 'l' && *p == 'l') { /* double l = long long */ +#ifdef SNPRINTF_LONGLONG_SUPPORT + length_modifier = '2'; /* double l encoded as '2' */ +#else + length_modifier = 'l'; /* treat it as a single 'l' */ +#endif + p++; + } + } + fmt_spec = *p; + /* common synonyms: */ + switch (fmt_spec) { + case 'i': fmt_spec = 'd'; break; + case 'D': fmt_spec = 'd'; length_modifier = 'l'; break; + case 'U': fmt_spec = 'u'; length_modifier = 'l'; break; + case 'O': fmt_spec = 'o'; length_modifier = 'l'; break; + default: break; + } + /* get parameter value, do initial processing */ + switch (fmt_spec) { + case '%': /* % behaves similar to 's' regarding flags and field widths */ + case 'c': /* c behaves similar to 's' regarding flags and field widths */ + case 's': + length_modifier = '\0'; /* wint_t and wchar_t not supported */ + /* the result of zero padding flag with non-numeric conversion specifier*/ + /* is undefined. Solaris and HPUX 10 does zero padding in this case, */ + /* Digital Unix and Linux does not. */ +#if !defined(SOLARIS_COMPATIBLE) && !defined(HPUX_COMPATIBLE) + zero_padding = 0; /* turn zero padding off for string conversions */ +#endif + str_arg_l = 1; + switch (fmt_spec) { + case '%': + str_arg = p; break; + case 'c': { + int j = va_arg(ap, int); + uchar_arg = (unsigned char) j; /* standard demands unsigned char */ + str_arg = (const char *) &uchar_arg; + break; + } + case 's': + str_arg = va_arg(ap, const char *); + if (!str_arg) str_arg_l = 0; + /* make sure not to address string beyond the specified precision !!! */ + else if (!precision_specified) str_arg_l = strlen(str_arg); + /* truncate string if necessary as requested by precision */ + else if (precision == 0) str_arg_l = 0; + else { + /* memchr on HP does not like n > 2^31 !!! */ + const char *q = (const char*)(memchr(str_arg, '\0', + precision <= 0x7fffffff ? precision : 0x7fffffff)); + str_arg_l = !q ? precision : (q-str_arg); + } + break; + default: break; + } + break; + case 'd': case 'u': case 'o': case 'x': case 'X': case 'p': { + /* NOTE: the u, o, x, X and p conversion specifiers imply + the value is unsigned; d implies a signed value */ + + int arg_sign = 0; + /* 0 if numeric argument is zero (or if pointer is NULL for 'p'), + +1 if greater than zero (or nonzero for unsigned arguments), + -1 if negative (unsigned argument is never negative) */ + + int int_arg = 0; unsigned int uint_arg = 0; + /* only defined for length modifier h, or for no length modifiers */ + + long int long_arg = 0; unsigned long int ulong_arg = 0; + /* only defined for length modifier l */ + + void *ptr_arg = NULL; + /* pointer argument value -only defined for p conversion */ + +#ifdef SNPRINTF_LONGLONG_SUPPORT + long long int long_long_arg = 0; + unsigned long long int ulong_long_arg = 0; + /* only defined for length modifier ll */ +#endif + if (fmt_spec == 'p') { + /* HPUX 10: An l, h, ll or L before any other conversion character + * (other than d, i, u, o, x, or X) is ignored. + * Digital Unix: + * not specified, but seems to behave as HPUX does. + * Solaris: If an h, l, or L appears before any other conversion + * specifier (other than d, i, u, o, x, or X), the behavior + * is undefined. (Actually %hp converts only 16-bits of address + * and %llp treats address as 64-bit data which is incompatible + * with (void *) argument on a 32-bit system). + */ +#ifdef SOLARIS_COMPATIBLE +# ifdef SOLARIS_BUG_COMPATIBLE + /* keep length modifiers even if it represents 'll' */ +# else + if (length_modifier == '2') length_modifier = '\0'; +# endif +#else + length_modifier = '\0'; +#endif + ptr_arg = va_arg(ap, void *); + if (ptr_arg != NULL) arg_sign = 1; + } else if (fmt_spec == 'd') { /* signed */ + switch (length_modifier) { + case '\0': + case 'h': + /* It is non-portable to specify a second argument of char or short + * to va_arg, because arguments seen by the called function + * are not char or short. C converts char and short arguments + * to int before passing them to a function. + */ + int_arg = va_arg(ap, int); + if (int_arg > 0) arg_sign = 1; + else if (int_arg < 0) arg_sign = -1; + break; + case 'l': + long_arg = va_arg(ap, long int); + if (long_arg > 0) arg_sign = 1; + else if (long_arg < 0) arg_sign = -1; + break; +#ifdef SNPRINTF_LONGLONG_SUPPORT + case '2': + long_long_arg = va_arg(ap, long long int); + if (long_long_arg > 0) arg_sign = 1; + else if (long_long_arg < 0) arg_sign = -1; + break; +#endif + } + } else { /* unsigned */ + switch (length_modifier) { + case '\0': + case 'h': + uint_arg = va_arg(ap, unsigned int); + if (uint_arg) arg_sign = 1; + break; + case 'l': + ulong_arg = va_arg(ap, unsigned long int); + if (ulong_arg) arg_sign = 1; + break; +#ifdef SNPRINTF_LONGLONG_SUPPORT + case '2': + ulong_long_arg = va_arg(ap, unsigned long long int); + if (ulong_long_arg) arg_sign = 1; + break; +#endif + } + } + str_arg = tmp; str_arg_l = 0; + /* NOTE: + * For d, i, u, o, x, and X conversions, if precision is specified, + * the '0' flag should be ignored. This is so with Solaris 2.6, + * Digital UNIX 4.0, HPUX 10, Linux, FreeBSD, NetBSD; but not with Perl. + */ +#ifndef PERL_COMPATIBLE + if (precision_specified) zero_padding = 0; +#endif + if (fmt_spec == 'd') { + if (force_sign && arg_sign >= 0) + tmp[str_arg_l++] = space_for_positive ? ' ' : '+'; + /* leave negative numbers for sprintf to handle, + to avoid handling tricky cases like (short int)(-32768) */ +#ifdef LINUX_COMPATIBLE + } else if (fmt_spec == 'p' && force_sign && arg_sign > 0) { + tmp[str_arg_l++] = space_for_positive ? ' ' : '+'; +#endif + } else if (alternate_form) { + if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X') ) + { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = fmt_spec; } + /* alternate form should have no effect for p conversion, but ... */ +#ifdef HPUX_COMPATIBLE + else if (fmt_spec == 'p' + /* HPUX 10: for an alternate form of p conversion, + * a nonzero result is prefixed by 0x. */ +#ifndef HPUX_BUG_COMPATIBLE + /* Actually it uses 0x prefix even for a zero value. */ + && arg_sign != 0 +#endif + ) { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = 'x'; } +#endif + } + zero_padding_insertion_ind = str_arg_l; + if (!precision_specified) precision = 1; /* default precision is 1 */ + if (precision == 0 && arg_sign == 0 +#if defined(HPUX_BUG_COMPATIBLE) || defined(LINUX_COMPATIBLE) + && fmt_spec != 'p' + /* HPUX 10 man page claims: With conversion character p the result of + * converting a zero value with a precision of zero is a null string. + * Actually HP returns all zeroes, and Linux returns "(nil)". */ +#endif + ) { + /* converted to null string */ + /* When zero value is formatted with an explicit precision 0, + the resulting formatted string is empty (d, i, u, o, x, X, p). */ + } else { + char f[5]; int f_l = 0; + f[f_l++] = '%'; /* construct a simple format string for sprintf */ + if (!length_modifier) { } + else if (length_modifier=='2') { f[f_l++] = 'l'; f[f_l++] = 'l'; } + else f[f_l++] = length_modifier; + f[f_l++] = fmt_spec; f[f_l++] = '\0'; + if (fmt_spec == 'p') str_arg_l += sprintf(tmp+str_arg_l, f, ptr_arg); + else if (fmt_spec == 'd') { /* signed */ + switch (length_modifier) { + case '\0': + case 'h': str_arg_l+=sprintf(tmp+str_arg_l, f, int_arg); break; + case 'l': str_arg_l+=sprintf(tmp+str_arg_l, f, long_arg); break; +#ifdef SNPRINTF_LONGLONG_SUPPORT + case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,long_long_arg); break; +#endif + } + } else { /* unsigned */ + switch (length_modifier) { + case '\0': + case 'h': str_arg_l+=sprintf(tmp+str_arg_l, f, uint_arg); break; + case 'l': str_arg_l+=sprintf(tmp+str_arg_l, f, ulong_arg); break; +#ifdef SNPRINTF_LONGLONG_SUPPORT + case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,ulong_long_arg);break; +#endif + } + } + /* include the optional minus sign and possible "0x" + in the region before the zero padding insertion point */ + if (zero_padding_insertion_ind < str_arg_l && + tmp[zero_padding_insertion_ind] == '-') { + zero_padding_insertion_ind++; + } + if (zero_padding_insertion_ind+1 < str_arg_l && + tmp[zero_padding_insertion_ind] == '0' && + (tmp[zero_padding_insertion_ind+1] == 'x' || + tmp[zero_padding_insertion_ind+1] == 'X') ) { + zero_padding_insertion_ind += 2; + } + } + { size_t num_of_digits = str_arg_l - zero_padding_insertion_ind; + if (alternate_form && fmt_spec == 'o' +#ifdef HPUX_COMPATIBLE /* ("%#.o",0) -> "" */ + && (str_arg_l > 0) +#endif +#ifdef DIGITAL_UNIX_BUG_COMPATIBLE /* ("%#o",0) -> "00" */ +#else + /* unless zero is already the first character */ + && !(zero_padding_insertion_ind < str_arg_l + && tmp[zero_padding_insertion_ind] == '0') +#endif + ) { /* assure leading zero for alternate-form octal numbers */ + if (!precision_specified || precision < num_of_digits+1) { + /* precision is increased to force the first character to be zero, + except if a zero value is formatted with an explicit precision + of zero */ + precision = num_of_digits+1; precision_specified = 1; + } + } + /* zero padding to specified precision? */ + if (num_of_digits < precision) + number_of_zeros_to_pad = precision - num_of_digits; + } + /* zero padding to specified minimal field width? */ + if (!justify_left && zero_padding) { + size_t n = min_field_width - (str_arg_l+number_of_zeros_to_pad); + if (n > 0) number_of_zeros_to_pad += n; + } + break; + } + default: /* unrecognized conversion specifier, keep format string as-is*/ + zero_padding = 0; /* turn zero padding off for non-numeric convers. */ +#ifndef DIGITAL_UNIX_COMPATIBLE + justify_left = 1; min_field_width = 0; /* reset flags */ +#endif +#if defined(PERL_COMPATIBLE) || defined(LINUX_COMPATIBLE) + /* keep the entire format string unchanged */ + str_arg = starting_p; str_arg_l = p - starting_p; + /* well, not exactly so for Linux, which does something inbetween, + * and I don't feel an urge to imitate it: "%+++++hy" -> "%+y" */ +#else + /* discard the unrecognized conversion, just keep * + * the unrecognized conversion character */ + str_arg = p; str_arg_l = 0; +#endif + if (*p) str_arg_l++; /* include invalid conversion specifier unchanged + if not at end-of-string */ + break; + } + if (*p) p++; /* step over the just processed conversion specifier */ + /* insert padding to the left as requested by min_field_width; + this does not include the zero padding in case of numerical conversions*/ + if (!justify_left) { /* left padding with blank or zero */ + size_t n = min_field_width - (str_arg_l+number_of_zeros_to_pad); + if (n > 0) { + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memset(str+str_l, (zero_padding?'0':' '), (n>avail?avail:n)); + } + str_l += n; + } + } + /* zero padding as requested by the precision or by the minimal field width + * for numeric conversions required? */ + if (number_of_zeros_to_pad <= 0) { + /* will not copy first part of numeric right now, * + * force it to be copied later in its entirety */ + zero_padding_insertion_ind = 0; + } else { + /* insert first part of numerics (sign or '0x') before zero padding */ + size_t n = zero_padding_insertion_ind; + if (n > 0) { + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memcpy(str+str_l, str_arg, (n>avail?avail:n)); + } + str_l += n; + } + /* insert zero padding as requested by the precision or min field width */ + n = number_of_zeros_to_pad; + if (n > 0) { + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memset(str+str_l, '0', (n>avail?avail:n)); + } + str_l += n; + } + } + /* insert formatted string + * (or as-is conversion specifier for unknown conversions) */ + { size_t n = str_arg_l - zero_padding_insertion_ind; + if (n > 0) { + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memcpy(str+str_l, str_arg+zero_padding_insertion_ind, + (n>avail?avail:n)); + } + str_l += n; + } + } + /* insert right padding */ + if (justify_left) { /* right blank padding to the field width */ + size_t n = min_field_width - (str_arg_l+number_of_zeros_to_pad); + if (n > 0) { + if (str_l < str_m) { + size_t avail = str_m-str_l; + fast_memset(str+str_l, ' ', (n>avail?avail:n)); + } + str_l += n; + } + } + } + } +#if defined(NEED_SNPRINTF_ONLY) + va_end(ap); +#endif + if (str_m > 0) { /* make sure the string is null-terminated + even at the expense of overwriting the last character + (shouldn't happen, but just in case) */ + str[str_l <= str_m-1 ? str_l : str_m-1] = '\0'; + } + /* Return the number of characters formatted (excluding trailing null + * character), that is, the number of characters that would have been + * written to the buffer if it were large enough. + * + * The value of str_l should be returned, but str_l is of unsigned type + * size_t, and snprintf is int, possibly leading to an undetected + * integer overflow, resulting in a negative return value, which is illegal. + * Both XSH5 and ISO C99 (at least the draft) are silent on this issue. + * Should errno be set to EOVERFLOW and EOF returned in this case??? + */ + return (int) str_l; +} +#endif diff --git a/VrUtils/resource.h b/VrUtils/resource.h new file mode 100644 index 0000000..acbbbe4 --- /dev/null +++ b/VrUtils/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by VrUtils.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/VrUtils/tinyxml2/tinyxml2.cpp b/VrUtils/tinyxml2/tinyxml2.cpp new file mode 100644 index 0000000..3cbe174 --- /dev/null +++ b/VrUtils/tinyxml2/tinyxml2.cpp @@ -0,0 +1,2987 @@ +/* +Original code by Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include "tinyxml2.h" + +#include // yes, this one new style header, is in the Android SDK. +#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__) +# include +# include +#else +# include +# include +#endif + +#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE) + // Microsoft Visual Studio, version 2005 and higher. Not WinCE. + /*int _snprintf_s( + char *buffer, + size_t sizeOfBuffer, + size_t count, + const char *format [, + argument] ... + );*/ + static inline int TIXML_SNPRINTF( char* buffer, size_t size, const char* format, ... ) + { + va_list va; + va_start( va, format ); + const int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va ); + va_end( va ); + return result; + } + + static inline int TIXML_VSNPRINTF( char* buffer, size_t size, const char* format, va_list va ) + { + const int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va ); + return result; + } + + #define TIXML_VSCPRINTF _vscprintf + #define TIXML_SSCANF sscanf_s +#elif defined _MSC_VER + // Microsoft Visual Studio 2003 and earlier or WinCE + #define TIXML_SNPRINTF _snprintf + #define TIXML_VSNPRINTF _vsnprintf + #define TIXML_SSCANF sscanf + #if (_MSC_VER < 1400 ) && (!defined WINCE) + // Microsoft Visual Studio 2003 and not WinCE. + #define TIXML_VSCPRINTF _vscprintf // VS2003's C runtime has this, but VC6 C runtime or WinCE SDK doesn't have. + #else + // Microsoft Visual Studio 2003 and earlier or WinCE. + static inline int TIXML_VSCPRINTF( const char* format, va_list va ) + { + int len = 512; + for (;;) { + len = len*2; + char* str = new char[len](); + const int required = _vsnprintf(str, len, format, va); + delete[] str; + if ( required != -1 ) { + TIXMLASSERT( required >= 0 ); + len = required; + break; + } + } + TIXMLASSERT( len >= 0 ); + return len; + } + #endif +#else + // GCC version 3 and higher + //#warning( "Using sn* functions." ) + #define TIXML_SNPRINTF snprintf + #define TIXML_VSNPRINTF vsnprintf + static inline int TIXML_VSCPRINTF( const char* format, va_list va ) + { + int len = vsnprintf( 0, 0, format, va ); + TIXMLASSERT( len >= 0 ); + return len; + } + #define TIXML_SSCANF sscanf +#endif + +#if defined(_WIN64) + #define TIXML_FSEEK _fseeki64 + #define TIXML_FTELL _ftelli64 +#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) \ + || defined(__NetBSD__) || defined(__DragonFly__) || defined(__ANDROID__) + #define TIXML_FSEEK fseeko + #define TIXML_FTELL ftello +#elif defined(__unix__) && defined(__x86_64__) + #define TIXML_FSEEK fseeko64 + #define TIXML_FTELL ftello64 +#else + #define TIXML_FSEEK fseek + #define TIXML_FTELL ftell +#endif + + +static const char LINE_FEED = static_cast(0x0a); // all line endings are normalized to LF +static const char LF = LINE_FEED; +static const char CARRIAGE_RETURN = static_cast(0x0d); // CR gets filtered out +static const char CR = CARRIAGE_RETURN; +static const char SINGLE_QUOTE = '\''; +static const char DOUBLE_QUOTE = '\"'; + +// Bunch of unicode info at: +// http://www.unicode.org/faq/utf_bom.html +// ef bb bf (Microsoft "lead bytes") - designates UTF-8 + +static const unsigned char TIXML_UTF_LEAD_0 = 0xefU; +static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; +static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + +namespace tinyxml2 +{ + +struct Entity { + const char* pattern; + int length; + char value; +}; + +static const int NUM_ENTITIES = 5; +static const Entity entities[NUM_ENTITIES] = { + { "quot", 4, DOUBLE_QUOTE }, + { "amp", 3, '&' }, + { "apos", 4, SINGLE_QUOTE }, + { "lt", 2, '<' }, + { "gt", 2, '>' } +}; + + +StrPair::~StrPair() +{ + Reset(); +} + + +void StrPair::TransferTo( StrPair* other ) +{ + if ( this == other ) { + return; + } + // This in effect implements the assignment operator by "moving" + // ownership (as in auto_ptr). + + TIXMLASSERT( other != 0 ); + TIXMLASSERT( other->_flags == 0 ); + TIXMLASSERT( other->_start == 0 ); + TIXMLASSERT( other->_end == 0 ); + + other->Reset(); + + other->_flags = _flags; + other->_start = _start; + other->_end = _end; + + _flags = 0; + _start = 0; + _end = 0; +} + + +void StrPair::Reset() +{ + if ( _flags & NEEDS_DELETE ) { + delete [] _start; + } + _flags = 0; + _start = 0; + _end = 0; +} + + +void StrPair::SetStr( const char* str, int flags ) +{ + TIXMLASSERT( str ); + Reset(); + size_t len = strlen( str ); + TIXMLASSERT( _start == 0 ); + _start = new char[ len+1 ]; + memcpy( _start, str, len+1 ); + _end = _start + len; + _flags = flags | NEEDS_DELETE; +} + + +char* StrPair::ParseText( char* p, const char* endTag, int strFlags, int* curLineNumPtr ) +{ + TIXMLASSERT( p ); + TIXMLASSERT( endTag && *endTag ); + TIXMLASSERT(curLineNumPtr); + + char* start = p; + const char endChar = *endTag; + size_t length = strlen( endTag ); + + // Inner loop of text parsing. + while ( *p ) { + if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) { + Set( start, p, strFlags ); + return p + length; + } else if (*p == '\n') { + ++(*curLineNumPtr); + } + ++p; + TIXMLASSERT( p ); + } + return 0; +} + + +char* StrPair::ParseName( char* p ) +{ + if ( !p || !(*p) ) { + return 0; + } + if ( !XMLUtil::IsNameStartChar( (unsigned char) *p ) ) { + return 0; + } + + char* const start = p; + ++p; + while ( *p && XMLUtil::IsNameChar( (unsigned char) *p ) ) { + ++p; + } + + Set( start, p, 0 ); + return p; +} + + +void StrPair::CollapseWhitespace() +{ + // Adjusting _start would cause undefined behavior on delete[] + TIXMLASSERT( ( _flags & NEEDS_DELETE ) == 0 ); + // Trim leading space. + _start = XMLUtil::SkipWhiteSpace( _start, 0 ); + + if ( *_start ) { + const char* p = _start; // the read pointer + char* q = _start; // the write pointer + + while( *p ) { + if ( XMLUtil::IsWhiteSpace( *p )) { + p = XMLUtil::SkipWhiteSpace( p, 0 ); + if ( *p == 0 ) { + break; // don't write to q; this trims the trailing space. + } + *q = ' '; + ++q; + } + *q = *p; + ++q; + ++p; + } + *q = 0; + } +} + + +const char* StrPair::GetStr() +{ + TIXMLASSERT( _start ); + TIXMLASSERT( _end ); + if ( _flags & NEEDS_FLUSH ) { + *_end = 0; + _flags ^= NEEDS_FLUSH; + + if ( _flags ) { + const char* p = _start; // the read pointer + char* q = _start; // the write pointer + + while( p < _end ) { + if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) { + // CR-LF pair becomes LF + // CR alone becomes LF + // LF-CR becomes LF + if ( *(p+1) == LF ) { + p += 2; + } + else { + ++p; + } + *q = LF; + ++q; + } + else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) { + if ( *(p+1) == CR ) { + p += 2; + } + else { + ++p; + } + *q = LF; + ++q; + } + else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) { + // Entities handled by tinyXML2: + // - special entities in the entity table [in/out] + // - numeric character reference [in] + // 中 or 中 + + if ( *(p+1) == '#' ) { + const int buflen = 10; + char buf[buflen] = { 0 }; + int len = 0; + const char* adjusted = const_cast( XMLUtil::GetCharacterRef( p, buf, &len ) ); + if ( adjusted == 0 ) { + *q = *p; + ++p; + ++q; + } + else { + TIXMLASSERT( 0 <= len && len <= buflen ); + TIXMLASSERT( q + len <= adjusted ); + p = adjusted; + memcpy( q, buf, len ); + q += len; + } + } + else { + bool entityFound = false; + for( int i = 0; i < NUM_ENTITIES; ++i ) { + const Entity& entity = entities[i]; + if ( strncmp( p + 1, entity.pattern, entity.length ) == 0 + && *( p + entity.length + 1 ) == ';' ) { + // Found an entity - convert. + *q = entity.value; + ++q; + p += entity.length + 2; + entityFound = true; + break; + } + } + if ( !entityFound ) { + // fixme: treat as error? + ++p; + ++q; + } + } + } + else { + *q = *p; + ++p; + ++q; + } + } + *q = 0; + } + // The loop below has plenty going on, and this + // is a less useful mode. Break it out. + if ( _flags & NEEDS_WHITESPACE_COLLAPSING ) { + CollapseWhitespace(); + } + _flags = (_flags & NEEDS_DELETE); + } + TIXMLASSERT( _start ); + return _start; +} + + + + +// --------- XMLUtil ----------- // + +const char* XMLUtil::writeBoolTrue = "true"; +const char* XMLUtil::writeBoolFalse = "false"; + +void XMLUtil::SetBoolSerialization(const char* writeTrue, const char* writeFalse) +{ + static const char* defTrue = "true"; + static const char* defFalse = "false"; + + writeBoolTrue = (writeTrue) ? writeTrue : defTrue; + writeBoolFalse = (writeFalse) ? writeFalse : defFalse; +} + + +const char* XMLUtil::ReadBOM( const char* p, bool* bom ) +{ + TIXMLASSERT( p ); + TIXMLASSERT( bom ); + *bom = false; + const unsigned char* pu = reinterpret_cast(p); + // Check for BOM: + if ( *(pu+0) == TIXML_UTF_LEAD_0 + && *(pu+1) == TIXML_UTF_LEAD_1 + && *(pu+2) == TIXML_UTF_LEAD_2 ) { + *bom = true; + p += 3; + } + TIXMLASSERT( p ); + return p; +} + + +void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) +{ + const unsigned long BYTE_MASK = 0xBF; + const unsigned long BYTE_MARK = 0x80; + const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + + if (input < 0x80) { + *length = 1; + } + else if ( input < 0x800 ) { + *length = 2; + } + else if ( input < 0x10000 ) { + *length = 3; + } + else if ( input < 0x200000 ) { + *length = 4; + } + else { + *length = 0; // This code won't convert this correctly anyway. + return; + } + + output += *length; + + // Scary scary fall throughs are annotated with carefully designed comments + // to suppress compiler warnings such as -Wimplicit-fallthrough in gcc + switch (*length) { + case 4: + --output; + *output = static_cast((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + //fall through + case 3: + --output; + *output = static_cast((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + //fall through + case 2: + --output; + *output = static_cast((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + //fall through + case 1: + --output; + *output = static_cast(input | FIRST_BYTE_MARK[*length]); + break; + default: + TIXMLASSERT( false ); + } +} + + +const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length ) +{ + // Presume an entity, and pull it out. + *length = 0; + + if ( *(p+1) == '#' && *(p+2) ) { + unsigned long ucs = 0; + TIXMLASSERT( sizeof( ucs ) >= 4 ); + ptrdiff_t delta = 0; + unsigned mult = 1; + static const char SEMICOLON = ';'; + + if ( *(p+2) == 'x' ) { + // Hexadecimal. + const char* q = p+3; + if ( !(*q) ) { + return 0; + } + + q = strchr( q, SEMICOLON ); + + if ( !q ) { + return 0; + } + TIXMLASSERT( *q == SEMICOLON ); + + delta = q-p; + --q; + + while ( *q != 'x' ) { + unsigned int digit = 0; + + if ( *q >= '0' && *q <= '9' ) { + digit = *q - '0'; + } + else if ( *q >= 'a' && *q <= 'f' ) { + digit = *q - 'a' + 10; + } + else if ( *q >= 'A' && *q <= 'F' ) { + digit = *q - 'A' + 10; + } + else { + return 0; + } + TIXMLASSERT( digit < 16 ); + TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit ); + const unsigned int digitScaled = mult * digit; + TIXMLASSERT( ucs <= ULONG_MAX - digitScaled ); + ucs += digitScaled; + TIXMLASSERT( mult <= UINT_MAX / 16 ); + mult *= 16; + --q; + } + } + else { + // Decimal. + const char* q = p+2; + if ( !(*q) ) { + return 0; + } + + q = strchr( q, SEMICOLON ); + + if ( !q ) { + return 0; + } + TIXMLASSERT( *q == SEMICOLON ); + + delta = q-p; + --q; + + while ( *q != '#' ) { + if ( *q >= '0' && *q <= '9' ) { + const unsigned int digit = *q - '0'; + TIXMLASSERT( digit < 10 ); + TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit ); + const unsigned int digitScaled = mult * digit; + TIXMLASSERT( ucs <= ULONG_MAX - digitScaled ); + ucs += digitScaled; + } + else { + return 0; + } + TIXMLASSERT( mult <= UINT_MAX / 10 ); + mult *= 10; + --q; + } + } + // convert the UCS to UTF-8 + ConvertUTF32ToUTF8( ucs, value, length ); + return p + delta + 1; + } + return p+1; +} + + +void XMLUtil::ToStr( int v, char* buffer, int bufferSize ) +{ + TIXML_SNPRINTF( buffer, bufferSize, "%d", v ); +} + + +void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize ) +{ + TIXML_SNPRINTF( buffer, bufferSize, "%u", v ); +} + + +void XMLUtil::ToStr( bool v, char* buffer, int bufferSize ) +{ + TIXML_SNPRINTF( buffer, bufferSize, "%s", v ? writeBoolTrue : writeBoolFalse); +} + +/* + ToStr() of a number is a very tricky topic. + https://github.com/leethomason/tinyxml2/issues/106 +*/ +void XMLUtil::ToStr( float v, char* buffer, int bufferSize ) +{ + TIXML_SNPRINTF( buffer, bufferSize, "%.8g", v ); +} + + +void XMLUtil::ToStr( double v, char* buffer, int bufferSize ) +{ + TIXML_SNPRINTF( buffer, bufferSize, "%.17g", v ); +} + + +void XMLUtil::ToStr( int64_t v, char* buffer, int bufferSize ) +{ + // horrible syntax trick to make the compiler happy about %lld + TIXML_SNPRINTF(buffer, bufferSize, "%lld", static_cast(v)); +} + +void XMLUtil::ToStr( uint64_t v, char* buffer, int bufferSize ) +{ + // horrible syntax trick to make the compiler happy about %llu + TIXML_SNPRINTF(buffer, bufferSize, "%llu", (long long)v); +} + +bool XMLUtil::ToInt(const char* str, int* value) +{ + if (IsPrefixHex(str)) { + unsigned v; + if (TIXML_SSCANF(str, "%x", &v) == 1) { + *value = static_cast(v); + return true; + } + } + else { + if (TIXML_SSCANF(str, "%d", value) == 1) { + return true; + } + } + return false; +} + +bool XMLUtil::ToUnsigned(const char* str, unsigned* value) +{ + if (TIXML_SSCANF(str, IsPrefixHex(str) ? "%x" : "%u", value) == 1) { + return true; + } + return false; +} + +bool XMLUtil::ToBool( const char* str, bool* value ) +{ + int ival = 0; + if ( ToInt( str, &ival )) { + *value = (ival==0) ? false : true; + return true; + } + static const char* TRUE_VALS[] = { "true", "True", "TRUE", 0 }; + static const char* FALSE_VALS[] = { "false", "False", "FALSE", 0 }; + + for (int i = 0; TRUE_VALS[i]; ++i) { + if (StringEqual(str, TRUE_VALS[i])) { + *value = true; + return true; + } + } + for (int i = 0; FALSE_VALS[i]; ++i) { + if (StringEqual(str, FALSE_VALS[i])) { + *value = false; + return true; + } + } + return false; +} + + +bool XMLUtil::ToFloat( const char* str, float* value ) +{ + if ( TIXML_SSCANF( str, "%f", value ) == 1 ) { + return true; + } + return false; +} + + +bool XMLUtil::ToDouble( const char* str, double* value ) +{ + if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) { + return true; + } + return false; +} + + +bool XMLUtil::ToInt64(const char* str, int64_t* value) +{ + if (IsPrefixHex(str)) { + unsigned long long v = 0; // horrible syntax trick to make the compiler happy about %llx + if (TIXML_SSCANF(str, "%llx", &v) == 1) { + *value = static_cast(v); + return true; + } + } + else { + long long v = 0; // horrible syntax trick to make the compiler happy about %lld + if (TIXML_SSCANF(str, "%lld", &v) == 1) { + *value = static_cast(v); + return true; + } + } + return false; +} + + +bool XMLUtil::ToUnsigned64(const char* str, uint64_t* value) { + unsigned long long v = 0; // horrible syntax trick to make the compiler happy about %llu + if(TIXML_SSCANF(str, IsPrefixHex(str) ? "%llx" : "%llu", &v) == 1) { + *value = (uint64_t)v; + return true; + } + return false; +} + + +char* XMLDocument::Identify( char* p, XMLNode** node ) +{ + TIXMLASSERT( node ); + TIXMLASSERT( p ); + char* const start = p; + int const startLine = _parseCurLineNum; + p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum ); + if( !*p ) { + *node = 0; + TIXMLASSERT( p ); + return p; + } + + // These strings define the matching patterns: + static const char* xmlHeader = { "( _commentPool ); + returnNode->_parseLineNum = _parseCurLineNum; + p += xmlHeaderLen; + } + else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) { + returnNode = CreateUnlinkedNode( _commentPool ); + returnNode->_parseLineNum = _parseCurLineNum; + p += commentHeaderLen; + } + else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) { + XMLText* text = CreateUnlinkedNode( _textPool ); + returnNode = text; + returnNode->_parseLineNum = _parseCurLineNum; + p += cdataHeaderLen; + text->SetCData( true ); + } + else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) { + returnNode = CreateUnlinkedNode( _commentPool ); + returnNode->_parseLineNum = _parseCurLineNum; + p += dtdHeaderLen; + } + else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) { + returnNode = CreateUnlinkedNode( _elementPool ); + returnNode->_parseLineNum = _parseCurLineNum; + p += elementHeaderLen; + } + else { + returnNode = CreateUnlinkedNode( _textPool ); + returnNode->_parseLineNum = _parseCurLineNum; // Report line of first non-whitespace character + p = start; // Back it up, all the text counts. + _parseCurLineNum = startLine; + } + + TIXMLASSERT( returnNode ); + TIXMLASSERT( p ); + *node = returnNode; + return p; +} + + +bool XMLDocument::Accept( XMLVisitor* visitor ) const +{ + TIXMLASSERT( visitor ); + if ( visitor->VisitEnter( *this ) ) { + for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) { + if ( !node->Accept( visitor ) ) { + break; + } + } + } + return visitor->VisitExit( *this ); +} + + +// --------- XMLNode ----------- // + +XMLNode::XMLNode( XMLDocument* doc ) : + _document( doc ), + _parent( 0 ), + _value(), + _parseLineNum( 0 ), + _firstChild( 0 ), _lastChild( 0 ), + _prev( 0 ), _next( 0 ), + _userData( 0 ), + _memPool( 0 ) +{ +} + + +XMLNode::~XMLNode() +{ + DeleteChildren(); + if ( _parent ) { + _parent->Unlink( this ); + } +} + +const char* XMLNode::Value() const +{ + // Edge case: XMLDocuments don't have a Value. Return null. + if ( this->ToDocument() ) + return 0; + return _value.GetStr(); +} + +void XMLNode::SetValue( const char* str, bool staticMem ) +{ + if ( staticMem ) { + _value.SetInternedStr( str ); + } + else { + _value.SetStr( str ); + } +} + +XMLNode* XMLNode::DeepClone(XMLDocument* target) const +{ + XMLNode* clone = this->ShallowClone(target); + if (!clone) return 0; + + for (const XMLNode* child = this->FirstChild(); child; child = child->NextSibling()) { + XMLNode* childClone = child->DeepClone(target); + TIXMLASSERT(childClone); + clone->InsertEndChild(childClone); + } + return clone; +} + +void XMLNode::DeleteChildren() +{ + while( _firstChild ) { + TIXMLASSERT( _lastChild ); + DeleteChild( _firstChild ); + } + _firstChild = _lastChild = 0; +} + + +void XMLNode::Unlink( XMLNode* child ) +{ + TIXMLASSERT( child ); + TIXMLASSERT( child->_document == _document ); + TIXMLASSERT( child->_parent == this ); + if ( child == _firstChild ) { + _firstChild = _firstChild->_next; + } + if ( child == _lastChild ) { + _lastChild = _lastChild->_prev; + } + + if ( child->_prev ) { + child->_prev->_next = child->_next; + } + if ( child->_next ) { + child->_next->_prev = child->_prev; + } + child->_next = 0; + child->_prev = 0; + child->_parent = 0; +} + + +void XMLNode::DeleteChild( XMLNode* node ) +{ + TIXMLASSERT( node ); + TIXMLASSERT( node->_document == _document ); + TIXMLASSERT( node->_parent == this ); + Unlink( node ); + TIXMLASSERT(node->_prev == 0); + TIXMLASSERT(node->_next == 0); + TIXMLASSERT(node->_parent == 0); + DeleteNode( node ); +} + + +XMLNode* XMLNode::InsertEndChild( XMLNode* addThis ) +{ + TIXMLASSERT( addThis ); + if ( addThis->_document != _document ) { + TIXMLASSERT( false ); + return 0; + } + InsertChildPreamble( addThis ); + + if ( _lastChild ) { + TIXMLASSERT( _firstChild ); + TIXMLASSERT( _lastChild->_next == 0 ); + _lastChild->_next = addThis; + addThis->_prev = _lastChild; + _lastChild = addThis; + + addThis->_next = 0; + } + else { + TIXMLASSERT( _firstChild == 0 ); + _firstChild = _lastChild = addThis; + + addThis->_prev = 0; + addThis->_next = 0; + } + addThis->_parent = this; + return addThis; +} + + +XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis ) +{ + TIXMLASSERT( addThis ); + if ( addThis->_document != _document ) { + TIXMLASSERT( false ); + return 0; + } + InsertChildPreamble( addThis ); + + if ( _firstChild ) { + TIXMLASSERT( _lastChild ); + TIXMLASSERT( _firstChild->_prev == 0 ); + + _firstChild->_prev = addThis; + addThis->_next = _firstChild; + _firstChild = addThis; + + addThis->_prev = 0; + } + else { + TIXMLASSERT( _lastChild == 0 ); + _firstChild = _lastChild = addThis; + + addThis->_prev = 0; + addThis->_next = 0; + } + addThis->_parent = this; + return addThis; +} + + +XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis ) +{ + TIXMLASSERT( addThis ); + if ( addThis->_document != _document ) { + TIXMLASSERT( false ); + return 0; + } + + TIXMLASSERT( afterThis ); + + if ( afterThis->_parent != this ) { + TIXMLASSERT( false ); + return 0; + } + if ( afterThis == addThis ) { + // Current state: BeforeThis -> AddThis -> OneAfterAddThis + // Now AddThis must disappear from it's location and then + // reappear between BeforeThis and OneAfterAddThis. + // So just leave it where it is. + return addThis; + } + + if ( afterThis->_next == 0 ) { + // The last node or the only node. + return InsertEndChild( addThis ); + } + InsertChildPreamble( addThis ); + addThis->_prev = afterThis; + addThis->_next = afterThis->_next; + afterThis->_next->_prev = addThis; + afterThis->_next = addThis; + addThis->_parent = this; + return addThis; +} + + + + +const XMLElement* XMLNode::FirstChildElement( const char* name ) const +{ + for( const XMLNode* node = _firstChild; node; node = node->_next ) { + const XMLElement* element = node->ToElementWithName( name ); + if ( element ) { + return element; + } + } + return 0; +} + + +const XMLElement* XMLNode::LastChildElement( const char* name ) const +{ + for( const XMLNode* node = _lastChild; node; node = node->_prev ) { + const XMLElement* element = node->ToElementWithName( name ); + if ( element ) { + return element; + } + } + return 0; +} + + +const XMLElement* XMLNode::NextSiblingElement( const char* name ) const +{ + for( const XMLNode* node = _next; node; node = node->_next ) { + const XMLElement* element = node->ToElementWithName( name ); + if ( element ) { + return element; + } + } + return 0; +} + + +const XMLElement* XMLNode::PreviousSiblingElement( const char* name ) const +{ + for( const XMLNode* node = _prev; node; node = node->_prev ) { + const XMLElement* element = node->ToElementWithName( name ); + if ( element ) { + return element; + } + } + return 0; +} + + +char* XMLNode::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ) +{ + // This is a recursive method, but thinking about it "at the current level" + // it is a pretty simple flat list: + // + // + // + // With a special case: + // + // + // + // + // Where the closing element (/foo) *must* be the next thing after the opening + // element, and the names must match. BUT the tricky bit is that the closing + // element will be read by the child. + // + // 'endTag' is the end tag for this node, it is returned by a call to a child. + // 'parentEnd' is the end tag for the parent, which is filled in and returned. + + XMLDocument::DepthTracker tracker(_document); + if (_document->Error()) + return 0; + + while( p && *p ) { + XMLNode* node = 0; + + p = _document->Identify( p, &node ); + TIXMLASSERT( p ); + if ( node == 0 ) { + break; + } + + const int initialLineNum = node->_parseLineNum; + + StrPair endTag; + p = node->ParseDeep( p, &endTag, curLineNumPtr ); + if ( !p ) { + DeleteNode( node ); + if ( !_document->Error() ) { + _document->SetError( XML_ERROR_PARSING, initialLineNum, 0); + } + break; + } + + const XMLDeclaration* const decl = node->ToDeclaration(); + if ( decl ) { + // Declarations are only allowed at document level + // + // Multiple declarations are allowed but all declarations + // must occur before anything else. + // + // Optimized due to a security test case. If the first node is + // a declaration, and the last node is a declaration, then only + // declarations have so far been added. + bool wellLocated = false; + + if (ToDocument()) { + if (FirstChild()) { + wellLocated = + FirstChild() && + FirstChild()->ToDeclaration() && + LastChild() && + LastChild()->ToDeclaration(); + } + else { + wellLocated = true; + } + } + if ( !wellLocated ) { + _document->SetError( XML_ERROR_PARSING_DECLARATION, initialLineNum, "XMLDeclaration value=%s", decl->Value()); + DeleteNode( node ); + break; + } + } + + XMLElement* ele = node->ToElement(); + if ( ele ) { + // We read the end tag. Return it to the parent. + if ( ele->ClosingType() == XMLElement::CLOSING ) { + if ( parentEndTag ) { + ele->_value.TransferTo( parentEndTag ); + } + node->_memPool->SetTracked(); // created and then immediately deleted. + DeleteNode( node ); + return p; + } + + // Handle an end tag returned to this level. + // And handle a bunch of annoying errors. + bool mismatch = false; + if ( endTag.Empty() ) { + if ( ele->ClosingType() == XMLElement::OPEN ) { + mismatch = true; + } + } + else { + if ( ele->ClosingType() != XMLElement::OPEN ) { + mismatch = true; + } + else if ( !XMLUtil::StringEqual( endTag.GetStr(), ele->Name() ) ) { + mismatch = true; + } + } + if ( mismatch ) { + _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, initialLineNum, "XMLElement name=%s", ele->Name()); + DeleteNode( node ); + break; + } + } + InsertEndChild( node ); + } + return 0; +} + +/*static*/ void XMLNode::DeleteNode( XMLNode* node ) +{ + if ( node == 0 ) { + return; + } + TIXMLASSERT(node->_document); + if (!node->ToDocument()) { + node->_document->MarkInUse(node); + } + + MemPool* pool = node->_memPool; + node->~XMLNode(); + pool->Free( node ); +} + +void XMLNode::InsertChildPreamble( XMLNode* insertThis ) const +{ + TIXMLASSERT( insertThis ); + TIXMLASSERT( insertThis->_document == _document ); + + if (insertThis->_parent) { + insertThis->_parent->Unlink( insertThis ); + } + else { + insertThis->_document->MarkInUse(insertThis); + insertThis->_memPool->SetTracked(); + } +} + +const XMLElement* XMLNode::ToElementWithName( const char* name ) const +{ + const XMLElement* element = this->ToElement(); + if ( element == 0 ) { + return 0; + } + if ( name == 0 ) { + return element; + } + if ( XMLUtil::StringEqual( element->Name(), name ) ) { + return element; + } + return 0; +} + +// --------- XMLText ---------- // +char* XMLText::ParseDeep( char* p, StrPair*, int* curLineNumPtr ) +{ + if ( this->CData() ) { + p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr ); + if ( !p ) { + _document->SetError( XML_ERROR_PARSING_CDATA, _parseLineNum, 0 ); + } + return p; + } + else { + int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES; + if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) { + flags |= StrPair::NEEDS_WHITESPACE_COLLAPSING; + } + + p = _value.ParseText( p, "<", flags, curLineNumPtr ); + if ( p && *p ) { + return p-1; + } + if ( !p ) { + _document->SetError( XML_ERROR_PARSING_TEXT, _parseLineNum, 0 ); + } + } + return 0; +} + + +XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const +{ + if ( !doc ) { + doc = _document; + } + XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern? + text->SetCData( this->CData() ); + return text; +} + + +bool XMLText::ShallowEqual( const XMLNode* compare ) const +{ + TIXMLASSERT( compare ); + const XMLText* text = compare->ToText(); + return ( text && XMLUtil::StringEqual( text->Value(), Value() ) ); +} + + +bool XMLText::Accept( XMLVisitor* visitor ) const +{ + TIXMLASSERT( visitor ); + return visitor->Visit( *this ); +} + + +// --------- XMLComment ---------- // + +XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc ) +{ +} + + +XMLComment::~XMLComment() +{ +} + + +char* XMLComment::ParseDeep( char* p, StrPair*, int* curLineNumPtr ) +{ + // Comment parses as text. + p = _value.ParseText( p, "-->", StrPair::COMMENT, curLineNumPtr ); + if ( p == 0 ) { + _document->SetError( XML_ERROR_PARSING_COMMENT, _parseLineNum, 0 ); + } + return p; +} + + +XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const +{ + if ( !doc ) { + doc = _document; + } + XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern? + return comment; +} + + +bool XMLComment::ShallowEqual( const XMLNode* compare ) const +{ + TIXMLASSERT( compare ); + const XMLComment* comment = compare->ToComment(); + return ( comment && XMLUtil::StringEqual( comment->Value(), Value() )); +} + + +bool XMLComment::Accept( XMLVisitor* visitor ) const +{ + TIXMLASSERT( visitor ); + return visitor->Visit( *this ); +} + + +// --------- XMLDeclaration ---------- // + +XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc ) +{ +} + + +XMLDeclaration::~XMLDeclaration() +{ + //printf( "~XMLDeclaration\n" ); +} + + +char* XMLDeclaration::ParseDeep( char* p, StrPair*, int* curLineNumPtr ) +{ + // Declaration parses as text. + p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr ); + if ( p == 0 ) { + _document->SetError( XML_ERROR_PARSING_DECLARATION, _parseLineNum, 0 ); + } + return p; +} + + +XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const +{ + if ( !doc ) { + doc = _document; + } + XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern? + return dec; +} + + +bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const +{ + TIXMLASSERT( compare ); + const XMLDeclaration* declaration = compare->ToDeclaration(); + return ( declaration && XMLUtil::StringEqual( declaration->Value(), Value() )); +} + + + +bool XMLDeclaration::Accept( XMLVisitor* visitor ) const +{ + TIXMLASSERT( visitor ); + return visitor->Visit( *this ); +} + +// --------- XMLUnknown ---------- // + +XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc ) +{ +} + + +XMLUnknown::~XMLUnknown() +{ +} + + +char* XMLUnknown::ParseDeep( char* p, StrPair*, int* curLineNumPtr ) +{ + // Unknown parses as text. + p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr ); + if ( !p ) { + _document->SetError( XML_ERROR_PARSING_UNKNOWN, _parseLineNum, 0 ); + } + return p; +} + + +XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const +{ + if ( !doc ) { + doc = _document; + } + XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern? + return text; +} + + +bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const +{ + TIXMLASSERT( compare ); + const XMLUnknown* unknown = compare->ToUnknown(); + return ( unknown && XMLUtil::StringEqual( unknown->Value(), Value() )); +} + + +bool XMLUnknown::Accept( XMLVisitor* visitor ) const +{ + TIXMLASSERT( visitor ); + return visitor->Visit( *this ); +} + +// --------- XMLAttribute ---------- // + +const char* XMLAttribute::Name() const +{ + return _name.GetStr(); +} + +const char* XMLAttribute::Value() const +{ + return _value.GetStr(); +} + +char* XMLAttribute::ParseDeep( char* p, bool processEntities, int* curLineNumPtr ) +{ + // Parse using the name rules: bug fix, was using ParseText before + p = _name.ParseName( p ); + if ( !p || !*p ) { + return 0; + } + + // Skip white space before = + p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); + if ( *p != '=' ) { + return 0; + } + + ++p; // move up to opening quote + p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); + if ( *p != '\"' && *p != '\'' ) { + return 0; + } + + const char endTag[2] = { *p, 0 }; + ++p; // move past opening quote + + p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES, curLineNumPtr ); + return p; +} + + +void XMLAttribute::SetName( const char* n ) +{ + _name.SetStr( n ); +} + + +XMLError XMLAttribute::QueryIntValue( int* value ) const +{ + if ( XMLUtil::ToInt( Value(), value )) { + return XML_SUCCESS; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const +{ + if ( XMLUtil::ToUnsigned( Value(), value )) { + return XML_SUCCESS; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +XMLError XMLAttribute::QueryInt64Value(int64_t* value) const +{ + if (XMLUtil::ToInt64(Value(), value)) { + return XML_SUCCESS; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +XMLError XMLAttribute::QueryUnsigned64Value(uint64_t* value) const +{ + if(XMLUtil::ToUnsigned64(Value(), value)) { + return XML_SUCCESS; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +XMLError XMLAttribute::QueryBoolValue( bool* value ) const +{ + if ( XMLUtil::ToBool( Value(), value )) { + return XML_SUCCESS; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +XMLError XMLAttribute::QueryFloatValue( float* value ) const +{ + if ( XMLUtil::ToFloat( Value(), value )) { + return XML_SUCCESS; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +XMLError XMLAttribute::QueryDoubleValue( double* value ) const +{ + if ( XMLUtil::ToDouble( Value(), value )) { + return XML_SUCCESS; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +void XMLAttribute::SetAttribute( const char* v ) +{ + _value.SetStr( v ); +} + + +void XMLAttribute::SetAttribute( int v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + _value.SetStr( buf ); +} + + +void XMLAttribute::SetAttribute( unsigned v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + _value.SetStr( buf ); +} + + +void XMLAttribute::SetAttribute(int64_t v) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr(v, buf, BUF_SIZE); + _value.SetStr(buf); +} + +void XMLAttribute::SetAttribute(uint64_t v) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr(v, buf, BUF_SIZE); + _value.SetStr(buf); +} + + +void XMLAttribute::SetAttribute( bool v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + _value.SetStr( buf ); +} + +void XMLAttribute::SetAttribute( double v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + _value.SetStr( buf ); +} + +void XMLAttribute::SetAttribute( float v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + _value.SetStr( buf ); +} + + +// --------- XMLElement ---------- // +XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ), + _closingType( OPEN ), + _rootAttribute( 0 ) +{ +} + + +XMLElement::~XMLElement() +{ + while( _rootAttribute ) { + XMLAttribute* next = _rootAttribute->_next; + DeleteAttribute( _rootAttribute ); + _rootAttribute = next; + } +} + + +const XMLAttribute* XMLElement::FindAttribute( const char* name ) const +{ + for( XMLAttribute* a = _rootAttribute; a; a = a->_next ) { + if ( XMLUtil::StringEqual( a->Name(), name ) ) { + return a; + } + } + return 0; +} + + +const char* XMLElement::Attribute( const char* name, const char* value ) const +{ + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return 0; + } + if ( !value || XMLUtil::StringEqual( a->Value(), value )) { + return a->Value(); + } + return 0; +} + +int XMLElement::IntAttribute(const char* name, int defaultValue) const +{ + int i = defaultValue; + QueryIntAttribute(name, &i); + return i; +} + +unsigned XMLElement::UnsignedAttribute(const char* name, unsigned defaultValue) const +{ + unsigned i = defaultValue; + QueryUnsignedAttribute(name, &i); + return i; +} + +int64_t XMLElement::Int64Attribute(const char* name, int64_t defaultValue) const +{ + int64_t i = defaultValue; + QueryInt64Attribute(name, &i); + return i; +} + +uint64_t XMLElement::Unsigned64Attribute(const char* name, uint64_t defaultValue) const +{ + uint64_t i = defaultValue; + QueryUnsigned64Attribute(name, &i); + return i; +} + +bool XMLElement::BoolAttribute(const char* name, bool defaultValue) const +{ + bool b = defaultValue; + QueryBoolAttribute(name, &b); + return b; +} + +double XMLElement::DoubleAttribute(const char* name, double defaultValue) const +{ + double d = defaultValue; + QueryDoubleAttribute(name, &d); + return d; +} + +float XMLElement::FloatAttribute(const char* name, float defaultValue) const +{ + float f = defaultValue; + QueryFloatAttribute(name, &f); + return f; +} + +const char* XMLElement::GetText() const +{ + /* skip comment node */ + const XMLNode* node = FirstChild(); + while (node) { + if (node->ToComment()) { + node = node->NextSibling(); + continue; + } + break; + } + + if ( node && node->ToText() ) { + return node->Value(); + } + return 0; +} + + +void XMLElement::SetText( const char* inText ) +{ + if ( FirstChild() && FirstChild()->ToText() ) + FirstChild()->SetValue( inText ); + else { + XMLText* theText = GetDocument()->NewText( inText ); + InsertFirstChild( theText ); + } +} + + +void XMLElement::SetText( int v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + SetText( buf ); +} + + +void XMLElement::SetText( unsigned v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + SetText( buf ); +} + + +void XMLElement::SetText(int64_t v) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr(v, buf, BUF_SIZE); + SetText(buf); +} + +void XMLElement::SetText(uint64_t v) { + char buf[BUF_SIZE]; + XMLUtil::ToStr(v, buf, BUF_SIZE); + SetText(buf); +} + + +void XMLElement::SetText( bool v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + SetText( buf ); +} + + +void XMLElement::SetText( float v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + SetText( buf ); +} + + +void XMLElement::SetText( double v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + SetText( buf ); +} + + +XMLError XMLElement::QueryIntText( int* ival ) const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + const char* t = FirstChild()->Value(); + if ( XMLUtil::ToInt( t, ival ) ) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + +XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + const char* t = FirstChild()->Value(); + if ( XMLUtil::ToUnsigned( t, uval ) ) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + +XMLError XMLElement::QueryInt64Text(int64_t* ival) const +{ + if (FirstChild() && FirstChild()->ToText()) { + const char* t = FirstChild()->Value(); + if (XMLUtil::ToInt64(t, ival)) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + +XMLError XMLElement::QueryUnsigned64Text(uint64_t* ival) const +{ + if(FirstChild() && FirstChild()->ToText()) { + const char* t = FirstChild()->Value(); + if(XMLUtil::ToUnsigned64(t, ival)) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + +XMLError XMLElement::QueryBoolText( bool* bval ) const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + const char* t = FirstChild()->Value(); + if ( XMLUtil::ToBool( t, bval ) ) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + +XMLError XMLElement::QueryDoubleText( double* dval ) const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + const char* t = FirstChild()->Value(); + if ( XMLUtil::ToDouble( t, dval ) ) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + +XMLError XMLElement::QueryFloatText( float* fval ) const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + const char* t = FirstChild()->Value(); + if ( XMLUtil::ToFloat( t, fval ) ) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + +int XMLElement::IntText(int defaultValue) const +{ + int i = defaultValue; + QueryIntText(&i); + return i; +} + +unsigned XMLElement::UnsignedText(unsigned defaultValue) const +{ + unsigned i = defaultValue; + QueryUnsignedText(&i); + return i; +} + +int64_t XMLElement::Int64Text(int64_t defaultValue) const +{ + int64_t i = defaultValue; + QueryInt64Text(&i); + return i; +} + +uint64_t XMLElement::Unsigned64Text(uint64_t defaultValue) const +{ + uint64_t i = defaultValue; + QueryUnsigned64Text(&i); + return i; +} + +bool XMLElement::BoolText(bool defaultValue) const +{ + bool b = defaultValue; + QueryBoolText(&b); + return b; +} + +double XMLElement::DoubleText(double defaultValue) const +{ + double d = defaultValue; + QueryDoubleText(&d); + return d; +} + +float XMLElement::FloatText(float defaultValue) const +{ + float f = defaultValue; + QueryFloatText(&f); + return f; +} + + +XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name ) +{ + XMLAttribute* last = 0; + XMLAttribute* attrib = 0; + for( attrib = _rootAttribute; + attrib; + last = attrib, attrib = attrib->_next ) { + if ( XMLUtil::StringEqual( attrib->Name(), name ) ) { + break; + } + } + if ( !attrib ) { + attrib = CreateAttribute(); + TIXMLASSERT( attrib ); + if ( last ) { + TIXMLASSERT( last->_next == 0 ); + last->_next = attrib; + } + else { + TIXMLASSERT( _rootAttribute == 0 ); + _rootAttribute = attrib; + } + attrib->SetName( name ); + } + return attrib; +} + + +void XMLElement::DeleteAttribute( const char* name ) +{ + XMLAttribute* prev = 0; + for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) { + if ( XMLUtil::StringEqual( name, a->Name() ) ) { + if ( prev ) { + prev->_next = a->_next; + } + else { + _rootAttribute = a->_next; + } + DeleteAttribute( a ); + break; + } + prev = a; + } +} + + +char* XMLElement::ParseAttributes( char* p, int* curLineNumPtr ) +{ + XMLAttribute* prevAttribute = 0; + + // Read the attributes. + while( p ) { + p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); + if ( !(*p) ) { + _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, "XMLElement name=%s", Name() ); + return 0; + } + + // attribute. + if (XMLUtil::IsNameStartChar( (unsigned char) *p ) ) { + XMLAttribute* attrib = CreateAttribute(); + TIXMLASSERT( attrib ); + attrib->_parseLineNum = _document->_parseCurLineNum; + + const int attrLineNum = attrib->_parseLineNum; + + p = attrib->ParseDeep( p, _document->ProcessEntities(), curLineNumPtr ); + if ( !p || Attribute( attrib->Name() ) ) { + DeleteAttribute( attrib ); + _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, attrLineNum, "XMLElement name=%s", Name() ); + return 0; + } + // There is a minor bug here: if the attribute in the source xml + // document is duplicated, it will not be detected and the + // attribute will be doubly added. However, tracking the 'prevAttribute' + // avoids re-scanning the attribute list. Preferring performance for + // now, may reconsider in the future. + if ( prevAttribute ) { + TIXMLASSERT( prevAttribute->_next == 0 ); + prevAttribute->_next = attrib; + } + else { + TIXMLASSERT( _rootAttribute == 0 ); + _rootAttribute = attrib; + } + prevAttribute = attrib; + } + // end of the tag + else if ( *p == '>' ) { + ++p; + break; + } + // end of the tag + else if ( *p == '/' && *(p+1) == '>' ) { + _closingType = CLOSED; + return p+2; // done; sealed element. + } + else { + _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, 0 ); + return 0; + } + } + return p; +} + +void XMLElement::DeleteAttribute( XMLAttribute* attribute ) +{ + if ( attribute == 0 ) { + return; + } + MemPool* pool = attribute->_memPool; + attribute->~XMLAttribute(); + pool->Free( attribute ); +} + +XMLAttribute* XMLElement::CreateAttribute() +{ + TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() ); + XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute(); + TIXMLASSERT( attrib ); + attrib->_memPool = &_document->_attributePool; + attrib->_memPool->SetTracked(); + return attrib; +} + + +XMLElement* XMLElement::InsertNewChildElement(const char* name) +{ + XMLElement* node = _document->NewElement(name); + return InsertEndChild(node) ? node : 0; +} + +XMLComment* XMLElement::InsertNewComment(const char* comment) +{ + XMLComment* node = _document->NewComment(comment); + return InsertEndChild(node) ? node : 0; +} + +XMLText* XMLElement::InsertNewText(const char* text) +{ + XMLText* node = _document->NewText(text); + return InsertEndChild(node) ? node : 0; +} + +XMLDeclaration* XMLElement::InsertNewDeclaration(const char* text) +{ + XMLDeclaration* node = _document->NewDeclaration(text); + return InsertEndChild(node) ? node : 0; +} + +XMLUnknown* XMLElement::InsertNewUnknown(const char* text) +{ + XMLUnknown* node = _document->NewUnknown(text); + return InsertEndChild(node) ? node : 0; +} + + + +// +// +// foobar +// +char* XMLElement::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ) +{ + // Read the element name. + p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); + + // The closing element is the form. It is + // parsed just like a regular element then deleted from + // the DOM. + if ( *p == '/' ) { + _closingType = CLOSING; + ++p; + } + + p = _value.ParseName( p ); + if ( _value.Empty() ) { + return 0; + } + + p = ParseAttributes( p, curLineNumPtr ); + if ( !p || !*p || _closingType != OPEN ) { + return p; + } + + p = XMLNode::ParseDeep( p, parentEndTag, curLineNumPtr ); + return p; +} + + + +XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const +{ + if ( !doc ) { + doc = _document; + } + XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern? + for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) { + element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern? + } + return element; +} + + +bool XMLElement::ShallowEqual( const XMLNode* compare ) const +{ + TIXMLASSERT( compare ); + const XMLElement* other = compare->ToElement(); + if ( other && XMLUtil::StringEqual( other->Name(), Name() )) { + + const XMLAttribute* a=FirstAttribute(); + const XMLAttribute* b=other->FirstAttribute(); + + while ( a && b ) { + if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) { + return false; + } + a = a->Next(); + b = b->Next(); + } + if ( a || b ) { + // different count + return false; + } + return true; + } + return false; +} + + +bool XMLElement::Accept( XMLVisitor* visitor ) const +{ + TIXMLASSERT( visitor ); + if ( visitor->VisitEnter( *this, _rootAttribute ) ) { + for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) { + if ( !node->Accept( visitor ) ) { + break; + } + } + } + return visitor->VisitExit( *this ); +} + + +// --------- XMLDocument ----------- // + +// Warning: List must match 'enum XMLError' +const char* XMLDocument::_errorNames[XML_ERROR_COUNT] = { + "XML_SUCCESS", + "XML_NO_ATTRIBUTE", + "XML_WRONG_ATTRIBUTE_TYPE", + "XML_ERROR_FILE_NOT_FOUND", + "XML_ERROR_FILE_COULD_NOT_BE_OPENED", + "XML_ERROR_FILE_READ_ERROR", + "XML_ERROR_PARSING_ELEMENT", + "XML_ERROR_PARSING_ATTRIBUTE", + "XML_ERROR_PARSING_TEXT", + "XML_ERROR_PARSING_CDATA", + "XML_ERROR_PARSING_COMMENT", + "XML_ERROR_PARSING_DECLARATION", + "XML_ERROR_PARSING_UNKNOWN", + "XML_ERROR_EMPTY_DOCUMENT", + "XML_ERROR_MISMATCHED_ELEMENT", + "XML_ERROR_PARSING", + "XML_CAN_NOT_CONVERT_TEXT", + "XML_NO_TEXT_NODE", + "XML_ELEMENT_DEPTH_EXCEEDED" +}; + + +XMLDocument::XMLDocument( bool processEntities, Whitespace whitespaceMode ) : + XMLNode( 0 ), + _writeBOM( false ), + _processEntities( processEntities ), + _errorID(XML_SUCCESS), + _whitespaceMode( whitespaceMode ), + _errorStr(), + _errorLineNum( 0 ), + _charBuffer( 0 ), + _parseCurLineNum( 0 ), + _parsingDepth(0), + _unlinked(), + _elementPool(), + _attributePool(), + _textPool(), + _commentPool() +{ + // avoid VC++ C4355 warning about 'this' in initializer list (C4355 is off by default in VS2012+) + _document = this; +} + + +XMLDocument::~XMLDocument() +{ + Clear(); +} + + +void XMLDocument::MarkInUse(const XMLNode* const node) +{ + TIXMLASSERT(node); + TIXMLASSERT(node->_parent == 0); + + for (int i = 0; i < _unlinked.Size(); ++i) { + if (node == _unlinked[i]) { + _unlinked.SwapRemove(i); + break; + } + } +} + +void XMLDocument::Clear() +{ + DeleteChildren(); + while( _unlinked.Size()) { + DeleteNode(_unlinked[0]); // Will remove from _unlinked as part of delete. + } + +#ifdef TINYXML2_DEBUG + const bool hadError = Error(); +#endif + ClearError(); + + delete [] _charBuffer; + _charBuffer = 0; + _parsingDepth = 0; + +#if 0 + _textPool.Trace( "text" ); + _elementPool.Trace( "element" ); + _commentPool.Trace( "comment" ); + _attributePool.Trace( "attribute" ); +#endif + +#ifdef TINYXML2_DEBUG + if ( !hadError ) { + TIXMLASSERT( _elementPool.CurrentAllocs() == _elementPool.Untracked() ); + TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() ); + TIXMLASSERT( _textPool.CurrentAllocs() == _textPool.Untracked() ); + TIXMLASSERT( _commentPool.CurrentAllocs() == _commentPool.Untracked() ); + } +#endif +} + + +void XMLDocument::DeepCopy(XMLDocument* target) const +{ + TIXMLASSERT(target); + if (target == this) { + return; // technically success - a no-op. + } + + target->Clear(); + for (const XMLNode* node = this->FirstChild(); node; node = node->NextSibling()) { + target->InsertEndChild(node->DeepClone(target)); + } +} + +XMLElement* XMLDocument::NewElement( const char* name ) +{ + XMLElement* ele = CreateUnlinkedNode( _elementPool ); + ele->SetName( name ); + return ele; +} + + +XMLComment* XMLDocument::NewComment( const char* str ) +{ + XMLComment* comment = CreateUnlinkedNode( _commentPool ); + comment->SetValue( str ); + return comment; +} + + +XMLText* XMLDocument::NewText( const char* str ) +{ + XMLText* text = CreateUnlinkedNode( _textPool ); + text->SetValue( str ); + return text; +} + + +XMLDeclaration* XMLDocument::NewDeclaration( const char* str ) +{ + XMLDeclaration* dec = CreateUnlinkedNode( _commentPool ); + dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" ); + return dec; +} + + +XMLUnknown* XMLDocument::NewUnknown( const char* str ) +{ + XMLUnknown* unk = CreateUnlinkedNode( _commentPool ); + unk->SetValue( str ); + return unk; +} + +static FILE* callfopen( const char* filepath, const char* mode ) +{ + TIXMLASSERT( filepath ); + TIXMLASSERT( mode ); +#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE) + FILE* fp = 0; + const errno_t err = fopen_s( &fp, filepath, mode ); + if ( err ) { + return 0; + } +#else + FILE* fp = fopen( filepath, mode ); +#endif + return fp; +} + +void XMLDocument::DeleteNode( XMLNode* node ) { + TIXMLASSERT( node ); + TIXMLASSERT(node->_document == this ); + if (node->_parent) { + node->_parent->DeleteChild( node ); + } + else { + // Isn't in the tree. + // Use the parent delete. + // Also, we need to mark it tracked: we 'know' + // it was never used. + node->_memPool->SetTracked(); + // Call the static XMLNode version: + XMLNode::DeleteNode(node); + } +} + + +XMLError XMLDocument::LoadFile( const char* filename ) +{ + if ( !filename ) { + TIXMLASSERT( false ); + SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=" ); + return _errorID; + } + + Clear(); + FILE* fp = callfopen( filename, "rb" ); + if ( !fp ) { + SetError( XML_ERROR_FILE_NOT_FOUND, 0, "filename=%s", filename ); + return _errorID; + } + LoadFile( fp ); + fclose( fp ); + return _errorID; +} + +XMLError XMLDocument::LoadFile( FILE* fp ) +{ + Clear(); + + TIXML_FSEEK( fp, 0, SEEK_SET ); + if ( fgetc( fp ) == EOF && ferror( fp ) != 0 ) { + SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); + return _errorID; + } + + TIXML_FSEEK( fp, 0, SEEK_END ); + + unsigned long long filelength; + { + const long long fileLengthSigned = TIXML_FTELL( fp ); + TIXML_FSEEK( fp, 0, SEEK_SET ); + if ( fileLengthSigned == -1L ) { + SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); + return _errorID; + } + TIXMLASSERT( fileLengthSigned >= 0 ); + filelength = static_cast(fileLengthSigned); + } + + const size_t maxSizeT = static_cast(-1); + // We'll do the comparison as an unsigned long long, because that's guaranteed to be at + // least 8 bytes, even on a 32-bit platform. + if ( filelength >= static_cast(maxSizeT) ) { + // Cannot handle files which won't fit in buffer together with null terminator + SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); + return _errorID; + } + + if ( filelength == 0 ) { + SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); + return _errorID; + } + + const size_t size = static_cast(filelength); + TIXMLASSERT( _charBuffer == 0 ); + _charBuffer = new char[size+1]; + const size_t read = fread( _charBuffer, 1, size, fp ); + if ( read != size ) { + SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); + return _errorID; + } + + _charBuffer[size] = 0; + + Parse(); + return _errorID; +} + + +XMLError XMLDocument::SaveFile( const char* filename, bool compact ) +{ + if ( !filename ) { + TIXMLASSERT( false ); + SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=" ); + return _errorID; + } + + FILE* fp = callfopen( filename, "w" ); + if ( !fp ) { + SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=%s", filename ); + return _errorID; + } + SaveFile(fp, compact); + fclose( fp ); + return _errorID; +} + + +XMLError XMLDocument::SaveFile( FILE* fp, bool compact ) +{ + // Clear any error from the last save, otherwise it will get reported + // for *this* call. + ClearError(); + XMLPrinter stream( fp, compact ); + Print( &stream ); + return _errorID; +} + + +XMLError XMLDocument::Parse( const char* p, size_t len ) +{ + Clear(); + + if ( len == 0 || !p || !*p ) { + SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); + return _errorID; + } + if ( len == static_cast(-1) ) { + len = strlen( p ); + } + TIXMLASSERT( _charBuffer == 0 ); + _charBuffer = new char[ len+1 ]; + memcpy( _charBuffer, p, len ); + _charBuffer[len] = 0; + + Parse(); + if ( Error() ) { + // clean up now essentially dangling memory. + // and the parse fail can put objects in the + // pools that are dead and inaccessible. + DeleteChildren(); + _elementPool.Clear(); + _attributePool.Clear(); + _textPool.Clear(); + _commentPool.Clear(); + } + return _errorID; +} + + +void XMLDocument::Print( XMLPrinter* streamer ) const +{ + if ( streamer ) { + Accept( streamer ); + } + else { + XMLPrinter stdoutStreamer( stdout ); + Accept( &stdoutStreamer ); + } +} + + +void XMLDocument::ClearError() { + _errorID = XML_SUCCESS; + _errorLineNum = 0; + _errorStr.Reset(); +} + + +void XMLDocument::SetError( XMLError error, int lineNum, const char* format, ... ) +{ + TIXMLASSERT( error >= 0 && error < XML_ERROR_COUNT ); + _errorID = error; + _errorLineNum = lineNum; + _errorStr.Reset(); + + const size_t BUFFER_SIZE = 1000; + char* buffer = new char[BUFFER_SIZE]; + + TIXMLASSERT(sizeof(error) <= sizeof(int)); + TIXML_SNPRINTF(buffer, BUFFER_SIZE, "Error=%s ErrorID=%d (0x%x) Line number=%d", ErrorIDToName(error), int(error), int(error), lineNum); + + if (format) { + size_t len = strlen(buffer); + TIXML_SNPRINTF(buffer + len, BUFFER_SIZE - len, ": "); + len = strlen(buffer); + + va_list va; + va_start(va, format); + TIXML_VSNPRINTF(buffer + len, BUFFER_SIZE - len, format, va); + va_end(va); + } + _errorStr.SetStr(buffer); + delete[] buffer; +} + + +/*static*/ const char* XMLDocument::ErrorIDToName(XMLError errorID) +{ + TIXMLASSERT( errorID >= 0 && errorID < XML_ERROR_COUNT ); + const char* errorName = _errorNames[errorID]; + TIXMLASSERT( errorName && errorName[0] ); + return errorName; +} + +const char* XMLDocument::ErrorStr() const +{ + return _errorStr.Empty() ? "" : _errorStr.GetStr(); +} + + +void XMLDocument::PrintError() const +{ + printf("%s\n", ErrorStr()); +} + +const char* XMLDocument::ErrorName() const +{ + return ErrorIDToName(_errorID); +} + +void XMLDocument::Parse() +{ + TIXMLASSERT( NoChildren() ); // Clear() must have been called previously + TIXMLASSERT( _charBuffer ); + _parseCurLineNum = 1; + _parseLineNum = 1; + char* p = _charBuffer; + p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum ); + p = const_cast( XMLUtil::ReadBOM( p, &_writeBOM ) ); + if ( !*p ) { + SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); + return; + } + ParseDeep(p, 0, &_parseCurLineNum ); +} + +void XMLDocument::PushDepth() +{ + _parsingDepth++; + if (_parsingDepth == TINYXML2_MAX_ELEMENT_DEPTH) { + SetError(XML_ELEMENT_DEPTH_EXCEEDED, _parseCurLineNum, "Element nesting is too deep." ); + } +} + +void XMLDocument::PopDepth() +{ + TIXMLASSERT(_parsingDepth > 0); + --_parsingDepth; +} + +XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) : + _elementJustOpened( false ), + _stack(), + _firstElement( true ), + _fp( file ), + _depth( depth ), + _textDepth( -1 ), + _processEntities( true ), + _compactMode( compact ), + _buffer() +{ + for( int i=0; i(entityValue); + TIXMLASSERT( flagIndex < ENTITY_RANGE ); + _entityFlag[flagIndex] = true; + } + _restrictedEntityFlag[static_cast('&')] = true; + _restrictedEntityFlag[static_cast('<')] = true; + _restrictedEntityFlag[static_cast('>')] = true; // not required, but consistency is nice + _buffer.Push( 0 ); +} + + +void XMLPrinter::Print( const char* format, ... ) +{ + va_list va; + va_start( va, format ); + + if ( _fp ) { + vfprintf( _fp, format, va ); + } + else { + const int len = TIXML_VSCPRINTF( format, va ); + // Close out and re-start the va-args + va_end( va ); + TIXMLASSERT( len >= 0 ); + va_start( va, format ); + TIXMLASSERT( _buffer.Size() > 0 && _buffer[_buffer.Size() - 1] == 0 ); + char* p = _buffer.PushArr( len ) - 1; // back up over the null terminator. + TIXML_VSNPRINTF( p, len+1, format, va ); + } + va_end( va ); +} + + +void XMLPrinter::Write( const char* data, size_t size ) +{ + if ( _fp ) { + fwrite ( data , sizeof(char), size, _fp); + } + else { + char* p = _buffer.PushArr( static_cast(size) ) - 1; // back up over the null terminator. + memcpy( p, data, size ); + p[size] = 0; + } +} + + +void XMLPrinter::Putc( char ch ) +{ + if ( _fp ) { + fputc ( ch, _fp); + } + else { + char* p = _buffer.PushArr( sizeof(char) ) - 1; // back up over the null terminator. + p[0] = ch; + p[1] = 0; + } +} + + +void XMLPrinter::PrintSpace( int depth ) +{ + for( int i=0; i 0 && *q < ENTITY_RANGE ) { + // Check for entities. If one is found, flush + // the stream up until the entity, write the + // entity, and keep looking. + if ( flag[static_cast(*q)] ) { + while ( p < q ) { + const size_t delta = q - p; + const int toPrint = ( INT_MAX < delta ) ? INT_MAX : static_cast(delta); + Write( p, toPrint ); + p += toPrint; + } + bool entityPatternPrinted = false; + for( int i=0; i(delta); + Write( p, toPrint ); + } + } + else { + Write( p ); + } +} + + +void XMLPrinter::PushHeader( bool writeBOM, bool writeDec ) +{ + if ( writeBOM ) { + static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 }; + Write( reinterpret_cast< const char* >( bom ) ); + } + if ( writeDec ) { + PushDeclaration( "xml version=\"1.0\"" ); + } +} + +void XMLPrinter::PrepareForNewNode( bool compactMode ) +{ + SealElementIfJustOpened(); + + if ( compactMode ) { + return; + } + + if ( _firstElement ) { + PrintSpace (_depth); + } else if ( _textDepth < 0) { + Putc( '\n' ); + PrintSpace( _depth ); + } + + _firstElement = false; +} + +void XMLPrinter::OpenElement( const char* name, bool compactMode ) +{ + PrepareForNewNode( compactMode ); + _stack.Push( name ); + + Write ( "<" ); + Write ( name ); + + _elementJustOpened = true; + ++_depth; +} + + +void XMLPrinter::PushAttribute( const char* name, const char* value ) +{ + TIXMLASSERT( _elementJustOpened ); + Putc ( ' ' ); + Write( name ); + Write( "=\"" ); + PrintString( value, false ); + Putc ( '\"' ); +} + + +void XMLPrinter::PushAttribute( const char* name, int v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + PushAttribute( name, buf ); +} + + +void XMLPrinter::PushAttribute( const char* name, unsigned v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + PushAttribute( name, buf ); +} + + +void XMLPrinter::PushAttribute(const char* name, int64_t v) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr(v, buf, BUF_SIZE); + PushAttribute(name, buf); +} + + +void XMLPrinter::PushAttribute(const char* name, uint64_t v) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr(v, buf, BUF_SIZE); + PushAttribute(name, buf); +} + + +void XMLPrinter::PushAttribute( const char* name, bool v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + PushAttribute( name, buf ); +} + + +void XMLPrinter::PushAttribute( const char* name, double v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + PushAttribute( name, buf ); +} + + +void XMLPrinter::CloseElement( bool compactMode ) +{ + --_depth; + const char* name = _stack.Pop(); + + if ( _elementJustOpened ) { + Write( "/>" ); + } + else { + if ( _textDepth < 0 && !compactMode) { + Putc( '\n' ); + PrintSpace( _depth ); + } + Write ( "" ); + } + + if ( _textDepth == _depth ) { + _textDepth = -1; + } + if ( _depth == 0 && !compactMode) { + Putc( '\n' ); + } + _elementJustOpened = false; +} + + +void XMLPrinter::SealElementIfJustOpened() +{ + if ( !_elementJustOpened ) { + return; + } + _elementJustOpened = false; + Putc( '>' ); +} + + +void XMLPrinter::PushText( const char* text, bool cdata ) +{ + _textDepth = _depth-1; + + SealElementIfJustOpened(); + if ( cdata ) { + Write( "" ); + } + else { + PrintString( text, true ); + } +} + + +void XMLPrinter::PushText( int64_t value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + + +void XMLPrinter::PushText( uint64_t value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr(value, buf, BUF_SIZE); + PushText(buf, false); +} + + +void XMLPrinter::PushText( int value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + + +void XMLPrinter::PushText( unsigned value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + + +void XMLPrinter::PushText( bool value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + + +void XMLPrinter::PushText( float value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + + +void XMLPrinter::PushText( double value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + + +void XMLPrinter::PushComment( const char* comment ) +{ + PrepareForNewNode( _compactMode ); + + Write( "" ); +} + + +void XMLPrinter::PushDeclaration( const char* value ) +{ + PrepareForNewNode( _compactMode ); + + Write( "" ); +} + + +void XMLPrinter::PushUnknown( const char* value ) +{ + PrepareForNewNode( _compactMode ); + + Write( "' ); +} + + +bool XMLPrinter::VisitEnter( const XMLDocument& doc ) +{ + _processEntities = doc.ProcessEntities(); + if ( doc.HasBOM() ) { + PushHeader( true, false ); + } + return true; +} + + +bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute ) +{ + const XMLElement* parentElem = 0; + if ( element.Parent() ) { + parentElem = element.Parent()->ToElement(); + } + const bool compactMode = parentElem ? CompactMode( *parentElem ) : _compactMode; + OpenElement( element.Name(), compactMode ); + while ( attribute ) { + PushAttribute( attribute->Name(), attribute->Value() ); + attribute = attribute->Next(); + } + return true; +} + + +bool XMLPrinter::VisitExit( const XMLElement& element ) +{ + CloseElement( CompactMode(element) ); + return true; +} + + +bool XMLPrinter::Visit( const XMLText& text ) +{ + PushText( text.Value(), text.CData() ); + return true; +} + + +bool XMLPrinter::Visit( const XMLComment& comment ) +{ + PushComment( comment.Value() ); + return true; +} + +bool XMLPrinter::Visit( const XMLDeclaration& declaration ) +{ + PushDeclaration( declaration.Value() ); + return true; +} + + +bool XMLPrinter::Visit( const XMLUnknown& unknown ) +{ + PushUnknown( unknown.Value() ); + return true; +} + +} // namespace tinyxml2 diff --git a/VrUtils/tinyxml2/tinyxml2.h b/VrUtils/tinyxml2/tinyxml2.h new file mode 100644 index 0000000..36f8548 --- /dev/null +++ b/VrUtils/tinyxml2/tinyxml2.h @@ -0,0 +1,2380 @@ +/* +Original code by Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#ifndef TINYXML2_INCLUDED +#define TINYXML2_INCLUDED + +#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__) +# include +# include +# include +# include +# include +# if defined(__PS3__) +# include +# endif +#else +# include +# include +# include +# include +# include +#endif +#include + +/* + TODO: intern strings instead of allocation. +*/ +/* + gcc: + g++ -Wall -DTINYXML2_DEBUG tinyxml2.cpp xmltest.cpp -o gccxmltest.exe + + Formatting, Artistic Style: + AStyle.exe --style=1tbs --indent-switches --break-closing-brackets --indent-preprocessor tinyxml2.cpp tinyxml2.h +*/ + +#if defined( _DEBUG ) || defined (__DEBUG__) +# ifndef TINYXML2_DEBUG +# define TINYXML2_DEBUG +# endif +#endif + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4251) +#endif + +#ifdef _WIN32 +# ifdef TINYXML2_EXPORT +# define TINYXML2_LIB __declspec(dllexport) +# elif defined(TINYXML2_IMPORT) +# define TINYXML2_LIB __declspec(dllimport) +# else +# define TINYXML2_LIB +# endif +#elif __GNUC__ >= 4 +# define TINYXML2_LIB __attribute__((visibility("default"))) +#else +# define TINYXML2_LIB +#endif + + +#if !defined(TIXMLASSERT) +#if defined(TINYXML2_DEBUG) +# if defined(_MSC_VER) +# // "(void)0," is for suppressing C4127 warning in "assert(false)", "assert(true)" and the like +# define TIXMLASSERT( x ) if ( !((void)0,(x))) { __debugbreak(); } +# elif defined (ANDROID_NDK) +# include +# define TIXMLASSERT( x ) if ( !(x)) { __android_log_assert( "assert", "grinliz", "ASSERT in '%s' at %d.", __FILE__, __LINE__ ); } +# else +# include +# define TIXMLASSERT assert +# endif +#else +# define TIXMLASSERT( x ) {} +#endif +#endif + +/* Versioning, past 1.0.14: + http://semver.org/ +*/ +static const int TIXML2_MAJOR_VERSION = 9; +static const int TIXML2_MINOR_VERSION = 0; +static const int TIXML2_PATCH_VERSION = 0; + +#define TINYXML2_MAJOR_VERSION 9 +#define TINYXML2_MINOR_VERSION 0 +#define TINYXML2_PATCH_VERSION 0 + +// A fixed element depth limit is problematic. There needs to be a +// limit to avoid a stack overflow. However, that limit varies per +// system, and the capacity of the stack. On the other hand, it's a trivial +// attack that can result from ill, malicious, or even correctly formed XML, +// so there needs to be a limit in place. +static const int TINYXML2_MAX_ELEMENT_DEPTH = 100; + +namespace tinyxml2 +{ +class XMLDocument; +class XMLElement; +class XMLAttribute; +class XMLComment; +class XMLText; +class XMLDeclaration; +class XMLUnknown; +class XMLPrinter; + +/* + A class that wraps strings. Normally stores the start and end + pointers into the XML file itself, and will apply normalization + and entity translation if actually read. Can also store (and memory + manage) a traditional char[] + + Isn't clear why TINYXML2_LIB is needed; but seems to fix #719 +*/ +class TINYXML2_LIB StrPair +{ +public: + enum Mode { + NEEDS_ENTITY_PROCESSING = 0x01, + NEEDS_NEWLINE_NORMALIZATION = 0x02, + NEEDS_WHITESPACE_COLLAPSING = 0x04, + + TEXT_ELEMENT = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION, + TEXT_ELEMENT_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION, + ATTRIBUTE_NAME = 0, + ATTRIBUTE_VALUE = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION, + ATTRIBUTE_VALUE_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION, + COMMENT = NEEDS_NEWLINE_NORMALIZATION + }; + + StrPair() : _flags( 0 ), _start( 0 ), _end( 0 ) {} + ~StrPair(); + + void Set( char* start, char* end, int flags ) { + TIXMLASSERT( start ); + TIXMLASSERT( end ); + Reset(); + _start = start; + _end = end; + _flags = flags | NEEDS_FLUSH; + } + + const char* GetStr(); + + bool Empty() const { + return _start == _end; + } + + void SetInternedStr( const char* str ) { + Reset(); + _start = const_cast(str); + } + + void SetStr( const char* str, int flags=0 ); + + char* ParseText( char* in, const char* endTag, int strFlags, int* curLineNumPtr ); + char* ParseName( char* in ); + + void TransferTo( StrPair* other ); + void Reset(); + +private: + void CollapseWhitespace(); + + enum { + NEEDS_FLUSH = 0x100, + NEEDS_DELETE = 0x200 + }; + + int _flags; + char* _start; + char* _end; + + StrPair( const StrPair& other ); // not supported + void operator=( const StrPair& other ); // not supported, use TransferTo() +}; + + +/* + A dynamic array of Plain Old Data. Doesn't support constructors, etc. + Has a small initial memory pool, so that low or no usage will not + cause a call to new/delete +*/ +template +class DynArray +{ +public: + DynArray() : + _mem( _pool ), + _allocated( INITIAL_SIZE ), + _size( 0 ) + { + } + + ~DynArray() { + if ( _mem != _pool ) { + delete [] _mem; + } + } + + void Clear() { + _size = 0; + } + + void Push( T t ) { + TIXMLASSERT( _size < INT_MAX ); + EnsureCapacity( _size+1 ); + _mem[_size] = t; + ++_size; + } + + T* PushArr( int count ) { + TIXMLASSERT( count >= 0 ); + TIXMLASSERT( _size <= INT_MAX - count ); + EnsureCapacity( _size+count ); + T* ret = &_mem[_size]; + _size += count; + return ret; + } + + T Pop() { + TIXMLASSERT( _size > 0 ); + --_size; + return _mem[_size]; + } + + void PopArr( int count ) { + TIXMLASSERT( _size >= count ); + _size -= count; + } + + bool Empty() const { + return _size == 0; + } + + T& operator[](int i) { + TIXMLASSERT( i>= 0 && i < _size ); + return _mem[i]; + } + + const T& operator[](int i) const { + TIXMLASSERT( i>= 0 && i < _size ); + return _mem[i]; + } + + const T& PeekTop() const { + TIXMLASSERT( _size > 0 ); + return _mem[ _size - 1]; + } + + int Size() const { + TIXMLASSERT( _size >= 0 ); + return _size; + } + + int Capacity() const { + TIXMLASSERT( _allocated >= INITIAL_SIZE ); + return _allocated; + } + + void SwapRemove(int i) { + TIXMLASSERT(i >= 0 && i < _size); + TIXMLASSERT(_size > 0); + _mem[i] = _mem[_size - 1]; + --_size; + } + + const T* Mem() const { + TIXMLASSERT( _mem ); + return _mem; + } + + T* Mem() { + TIXMLASSERT( _mem ); + return _mem; + } + +private: + DynArray( const DynArray& ); // not supported + void operator=( const DynArray& ); // not supported + + void EnsureCapacity( int cap ) { + TIXMLASSERT( cap > 0 ); + if ( cap > _allocated ) { + TIXMLASSERT( cap <= INT_MAX / 2 ); + const int newAllocated = cap * 2; + T* newMem = new T[newAllocated]; + TIXMLASSERT( newAllocated >= _size ); + memcpy( newMem, _mem, sizeof(T)*_size ); // warning: not using constructors, only works for PODs + if ( _mem != _pool ) { + delete [] _mem; + } + _mem = newMem; + _allocated = newAllocated; + } + } + + T* _mem; + T _pool[INITIAL_SIZE]; + int _allocated; // objects allocated + int _size; // number objects in use +}; + + +/* + Parent virtual class of a pool for fast allocation + and deallocation of objects. +*/ +class MemPool +{ +public: + MemPool() {} + virtual ~MemPool() {} + + virtual int ItemSize() const = 0; + virtual void* Alloc() = 0; + virtual void Free( void* ) = 0; + virtual void SetTracked() = 0; +}; + + +/* + Template child class to create pools of the correct type. +*/ +template< int ITEM_SIZE > +class MemPoolT : public MemPool +{ +public: + MemPoolT() : _blockPtrs(), _root(0), _currentAllocs(0), _nAllocs(0), _maxAllocs(0), _nUntracked(0) {} + ~MemPoolT() { + MemPoolT< ITEM_SIZE >::Clear(); + } + + void Clear() { + // Delete the blocks. + while( !_blockPtrs.Empty()) { + Block* lastBlock = _blockPtrs.Pop(); + delete lastBlock; + } + _root = 0; + _currentAllocs = 0; + _nAllocs = 0; + _maxAllocs = 0; + _nUntracked = 0; + } + + virtual int ItemSize() const { + return ITEM_SIZE; + } + int CurrentAllocs() const { + return _currentAllocs; + } + + virtual void* Alloc() { + if ( !_root ) { + // Need a new block. + Block* block = new Block(); + _blockPtrs.Push( block ); + + Item* blockItems = block->items; + for( int i = 0; i < ITEMS_PER_BLOCK - 1; ++i ) { + blockItems[i].next = &(blockItems[i + 1]); + } + blockItems[ITEMS_PER_BLOCK - 1].next = 0; + _root = blockItems; + } + Item* const result = _root; + TIXMLASSERT( result != 0 ); + _root = _root->next; + + ++_currentAllocs; + if ( _currentAllocs > _maxAllocs ) { + _maxAllocs = _currentAllocs; + } + ++_nAllocs; + ++_nUntracked; + return result; + } + + virtual void Free( void* mem ) { + if ( !mem ) { + return; + } + --_currentAllocs; + Item* item = static_cast( mem ); +#ifdef TINYXML2_DEBUG + memset( item, 0xfe, sizeof( *item ) ); +#endif + item->next = _root; + _root = item; + } + void Trace( const char* name ) { + printf( "Mempool %s watermark=%d [%dk] current=%d size=%d nAlloc=%d blocks=%d\n", + name, _maxAllocs, _maxAllocs * ITEM_SIZE / 1024, _currentAllocs, + ITEM_SIZE, _nAllocs, _blockPtrs.Size() ); + } + + void SetTracked() { + --_nUntracked; + } + + int Untracked() const { + return _nUntracked; + } + + // This number is perf sensitive. 4k seems like a good tradeoff on my machine. + // The test file is large, 170k. + // Release: VS2010 gcc(no opt) + // 1k: 4000 + // 2k: 4000 + // 4k: 3900 21000 + // 16k: 5200 + // 32k: 4300 + // 64k: 4000 21000 + // Declared public because some compilers do not accept to use ITEMS_PER_BLOCK + // in private part if ITEMS_PER_BLOCK is private + enum { ITEMS_PER_BLOCK = (4 * 1024) / ITEM_SIZE }; + +private: + MemPoolT( const MemPoolT& ); // not supported + void operator=( const MemPoolT& ); // not supported + + union Item { + Item* next; + char itemData[ITEM_SIZE]; + }; + struct Block { + Item items[ITEMS_PER_BLOCK]; + }; + DynArray< Block*, 10 > _blockPtrs; + Item* _root; + + int _currentAllocs; + int _nAllocs; + int _maxAllocs; + int _nUntracked; +}; + + + +/** + Implements the interface to the "Visitor pattern" (see the Accept() method.) + If you call the Accept() method, it requires being passed a XMLVisitor + class to handle callbacks. For nodes that contain other nodes (Document, Element) + you will get called with a VisitEnter/VisitExit pair. Nodes that are always leafs + are simply called with Visit(). + + If you return 'true' from a Visit method, recursive parsing will continue. If you return + false, no children of this node or its siblings will be visited. + + All flavors of Visit methods have a default implementation that returns 'true' (continue + visiting). You need to only override methods that are interesting to you. + + Generally Accept() is called on the XMLDocument, although all nodes support visiting. + + You should never change the document from a callback. + + @sa XMLNode::Accept() +*/ +class TINYXML2_LIB XMLVisitor +{ +public: + virtual ~XMLVisitor() {} + + /// Visit a document. + virtual bool VisitEnter( const XMLDocument& /*doc*/ ) { + return true; + } + /// Visit a document. + virtual bool VisitExit( const XMLDocument& /*doc*/ ) { + return true; + } + + /// Visit an element. + virtual bool VisitEnter( const XMLElement& /*element*/, const XMLAttribute* /*firstAttribute*/ ) { + return true; + } + /// Visit an element. + virtual bool VisitExit( const XMLElement& /*element*/ ) { + return true; + } + + /// Visit a declaration. + virtual bool Visit( const XMLDeclaration& /*declaration*/ ) { + return true; + } + /// Visit a text node. + virtual bool Visit( const XMLText& /*text*/ ) { + return true; + } + /// Visit a comment node. + virtual bool Visit( const XMLComment& /*comment*/ ) { + return true; + } + /// Visit an unknown node. + virtual bool Visit( const XMLUnknown& /*unknown*/ ) { + return true; + } +}; + +// WARNING: must match XMLDocument::_errorNames[] +enum XMLError { + XML_SUCCESS = 0, + XML_NO_ATTRIBUTE, + XML_WRONG_ATTRIBUTE_TYPE, + XML_ERROR_FILE_NOT_FOUND, + XML_ERROR_FILE_COULD_NOT_BE_OPENED, + XML_ERROR_FILE_READ_ERROR, + XML_ERROR_PARSING_ELEMENT, + XML_ERROR_PARSING_ATTRIBUTE, + XML_ERROR_PARSING_TEXT, + XML_ERROR_PARSING_CDATA, + XML_ERROR_PARSING_COMMENT, + XML_ERROR_PARSING_DECLARATION, + XML_ERROR_PARSING_UNKNOWN, + XML_ERROR_EMPTY_DOCUMENT, + XML_ERROR_MISMATCHED_ELEMENT, + XML_ERROR_PARSING, + XML_CAN_NOT_CONVERT_TEXT, + XML_NO_TEXT_NODE, + XML_ELEMENT_DEPTH_EXCEEDED, + + XML_ERROR_COUNT +}; + + +/* + Utility functionality. +*/ +class TINYXML2_LIB XMLUtil +{ +public: + static const char* SkipWhiteSpace( const char* p, int* curLineNumPtr ) { + TIXMLASSERT( p ); + + while( IsWhiteSpace(*p) ) { + if (curLineNumPtr && *p == '\n') { + ++(*curLineNumPtr); + } + ++p; + } + TIXMLASSERT( p ); + return p; + } + static char* SkipWhiteSpace( char* const p, int* curLineNumPtr ) { + return const_cast( SkipWhiteSpace( const_cast(p), curLineNumPtr ) ); + } + + // Anything in the high order range of UTF-8 is assumed to not be whitespace. This isn't + // correct, but simple, and usually works. + static bool IsWhiteSpace( char p ) { + return !IsUTF8Continuation(p) && isspace( static_cast(p) ); + } + + inline static bool IsNameStartChar( unsigned char ch ) { + if ( ch >= 128 ) { + // This is a heuristic guess in attempt to not implement Unicode-aware isalpha() + return true; + } + if ( isalpha( ch ) ) { + return true; + } + return ch == ':' || ch == '_'; + } + + inline static bool IsNameChar( unsigned char ch ) { + return IsNameStartChar( ch ) + || isdigit( ch ) + || ch == '.' + || ch == '-'; + } + + inline static bool IsPrefixHex( const char* p) { + p = SkipWhiteSpace(p, 0); + return p && *p == '0' && ( *(p + 1) == 'x' || *(p + 1) == 'X'); + } + + inline static bool StringEqual( const char* p, const char* q, int nChar=INT_MAX ) { + if ( p == q ) { + return true; + } + TIXMLASSERT( p ); + TIXMLASSERT( q ); + TIXMLASSERT( nChar >= 0 ); + return strncmp( p, q, nChar ) == 0; + } + + inline static bool IsUTF8Continuation( const char p ) { + return ( p & 0x80 ) != 0; + } + + static const char* ReadBOM( const char* p, bool* hasBOM ); + // p is the starting location, + // the UTF-8 value of the entity will be placed in value, and length filled in. + static const char* GetCharacterRef( const char* p, char* value, int* length ); + static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ); + + // converts primitive types to strings + static void ToStr( int v, char* buffer, int bufferSize ); + static void ToStr( unsigned v, char* buffer, int bufferSize ); + static void ToStr( bool v, char* buffer, int bufferSize ); + static void ToStr( float v, char* buffer, int bufferSize ); + static void ToStr( double v, char* buffer, int bufferSize ); + static void ToStr(int64_t v, char* buffer, int bufferSize); + static void ToStr(uint64_t v, char* buffer, int bufferSize); + + // converts strings to primitive types + static bool ToInt( const char* str, int* value ); + static bool ToUnsigned( const char* str, unsigned* value ); + static bool ToBool( const char* str, bool* value ); + static bool ToFloat( const char* str, float* value ); + static bool ToDouble( const char* str, double* value ); + static bool ToInt64(const char* str, int64_t* value); + static bool ToUnsigned64(const char* str, uint64_t* value); + // Changes what is serialized for a boolean value. + // Default to "true" and "false". Shouldn't be changed + // unless you have a special testing or compatibility need. + // Be careful: static, global, & not thread safe. + // Be sure to set static const memory as parameters. + static void SetBoolSerialization(const char* writeTrue, const char* writeFalse); + +private: + static const char* writeBoolTrue; + static const char* writeBoolFalse; +}; + + +/** XMLNode is a base class for every object that is in the + XML Document Object Model (DOM), except XMLAttributes. + Nodes have siblings, a parent, and children which can + be navigated. A node is always in a XMLDocument. + The type of a XMLNode can be queried, and it can + be cast to its more defined type. + + A XMLDocument allocates memory for all its Nodes. + When the XMLDocument gets deleted, all its Nodes + will also be deleted. + + @verbatim + A Document can contain: Element (container or leaf) + Comment (leaf) + Unknown (leaf) + Declaration( leaf ) + + An Element can contain: Element (container or leaf) + Text (leaf) + Attributes (not on tree) + Comment (leaf) + Unknown (leaf) + + @endverbatim +*/ +class TINYXML2_LIB XMLNode +{ + friend class XMLDocument; + friend class XMLElement; +public: + + /// Get the XMLDocument that owns this XMLNode. + const XMLDocument* GetDocument() const { + TIXMLASSERT( _document ); + return _document; + } + /// Get the XMLDocument that owns this XMLNode. + XMLDocument* GetDocument() { + TIXMLASSERT( _document ); + return _document; + } + + /// Safely cast to an Element, or null. + virtual XMLElement* ToElement() { + return 0; + } + /// Safely cast to Text, or null. + virtual XMLText* ToText() { + return 0; + } + /// Safely cast to a Comment, or null. + virtual XMLComment* ToComment() { + return 0; + } + /// Safely cast to a Document, or null. + virtual XMLDocument* ToDocument() { + return 0; + } + /// Safely cast to a Declaration, or null. + virtual XMLDeclaration* ToDeclaration() { + return 0; + } + /// Safely cast to an Unknown, or null. + virtual XMLUnknown* ToUnknown() { + return 0; + } + + virtual const XMLElement* ToElement() const { + return 0; + } + virtual const XMLText* ToText() const { + return 0; + } + virtual const XMLComment* ToComment() const { + return 0; + } + virtual const XMLDocument* ToDocument() const { + return 0; + } + virtual const XMLDeclaration* ToDeclaration() const { + return 0; + } + virtual const XMLUnknown* ToUnknown() const { + return 0; + } + + /** The meaning of 'value' changes for the specific type. + @verbatim + Document: empty (NULL is returned, not an empty string) + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + */ + const char* Value() const; + + /** Set the Value of an XML node. + @sa Value() + */ + void SetValue( const char* val, bool staticMem=false ); + + /// Gets the line number the node is in, if the document was parsed from a file. + int GetLineNum() const { return _parseLineNum; } + + /// Get the parent of this node on the DOM. + const XMLNode* Parent() const { + return _parent; + } + + XMLNode* Parent() { + return _parent; + } + + /// Returns true if this node has no children. + bool NoChildren() const { + return !_firstChild; + } + + /// Get the first child node, or null if none exists. + const XMLNode* FirstChild() const { + return _firstChild; + } + + XMLNode* FirstChild() { + return _firstChild; + } + + /** Get the first child element, or optionally the first child + element with the specified name. + */ + const XMLElement* FirstChildElement( const char* name = 0 ) const; + + XMLElement* FirstChildElement( const char* name = 0 ) { + return const_cast(const_cast(this)->FirstChildElement( name )); + } + + /// Get the last child node, or null if none exists. + const XMLNode* LastChild() const { + return _lastChild; + } + + XMLNode* LastChild() { + return _lastChild; + } + + /** Get the last child element or optionally the last child + element with the specified name. + */ + const XMLElement* LastChildElement( const char* name = 0 ) const; + + XMLElement* LastChildElement( const char* name = 0 ) { + return const_cast(const_cast(this)->LastChildElement(name) ); + } + + /// Get the previous (left) sibling node of this node. + const XMLNode* PreviousSibling() const { + return _prev; + } + + XMLNode* PreviousSibling() { + return _prev; + } + + /// Get the previous (left) sibling element of this node, with an optionally supplied name. + const XMLElement* PreviousSiblingElement( const char* name = 0 ) const ; + + XMLElement* PreviousSiblingElement( const char* name = 0 ) { + return const_cast(const_cast(this)->PreviousSiblingElement( name ) ); + } + + /// Get the next (right) sibling node of this node. + const XMLNode* NextSibling() const { + return _next; + } + + XMLNode* NextSibling() { + return _next; + } + + /// Get the next (right) sibling element of this node, with an optionally supplied name. + const XMLElement* NextSiblingElement( const char* name = 0 ) const; + + XMLElement* NextSiblingElement( const char* name = 0 ) { + return const_cast(const_cast(this)->NextSiblingElement( name ) ); + } + + /** + Add a child node as the last (right) child. + If the child node is already part of the document, + it is moved from its old location to the new location. + Returns the addThis argument or 0 if the node does not + belong to the same document. + */ + XMLNode* InsertEndChild( XMLNode* addThis ); + + XMLNode* LinkEndChild( XMLNode* addThis ) { + return InsertEndChild( addThis ); + } + /** + Add a child node as the first (left) child. + If the child node is already part of the document, + it is moved from its old location to the new location. + Returns the addThis argument or 0 if the node does not + belong to the same document. + */ + XMLNode* InsertFirstChild( XMLNode* addThis ); + /** + Add a node after the specified child node. + If the child node is already part of the document, + it is moved from its old location to the new location. + Returns the addThis argument or 0 if the afterThis node + is not a child of this node, or if the node does not + belong to the same document. + */ + XMLNode* InsertAfterChild( XMLNode* afterThis, XMLNode* addThis ); + + /** + Delete all the children of this node. + */ + void DeleteChildren(); + + /** + Delete a child of this node. + */ + void DeleteChild( XMLNode* node ); + + /** + Make a copy of this node, but not its children. + You may pass in a Document pointer that will be + the owner of the new Node. If the 'document' is + null, then the node returned will be allocated + from the current Document. (this->GetDocument()) + + Note: if called on a XMLDocument, this will return null. + */ + virtual XMLNode* ShallowClone( XMLDocument* document ) const = 0; + + /** + Make a copy of this node and all its children. + + If the 'target' is null, then the nodes will + be allocated in the current document. If 'target' + is specified, the memory will be allocated is the + specified XMLDocument. + + NOTE: This is probably not the correct tool to + copy a document, since XMLDocuments can have multiple + top level XMLNodes. You probably want to use + XMLDocument::DeepCopy() + */ + XMLNode* DeepClone( XMLDocument* target ) const; + + /** + Test if 2 nodes are the same, but don't test children. + The 2 nodes do not need to be in the same Document. + + Note: if called on a XMLDocument, this will return false. + */ + virtual bool ShallowEqual( const XMLNode* compare ) const = 0; + + /** Accept a hierarchical visit of the nodes in the TinyXML-2 DOM. Every node in the + XML tree will be conditionally visited and the host will be called back + via the XMLVisitor interface. + + This is essentially a SAX interface for TinyXML-2. (Note however it doesn't re-parse + the XML for the callbacks, so the performance of TinyXML-2 is unchanged by using this + interface versus any other.) + + The interface has been based on ideas from: + + - http://www.saxproject.org/ + - http://c2.com/cgi/wiki?HierarchicalVisitorPattern + + Which are both good references for "visiting". + + An example of using Accept(): + @verbatim + XMLPrinter printer; + tinyxmlDoc.Accept( &printer ); + const char* xmlcstr = printer.CStr(); + @endverbatim + */ + virtual bool Accept( XMLVisitor* visitor ) const = 0; + + /** + Set user data into the XMLNode. TinyXML-2 in + no way processes or interprets user data. + It is initially 0. + */ + void SetUserData(void* userData) { _userData = userData; } + + /** + Get user data set into the XMLNode. TinyXML-2 in + no way processes or interprets user data. + It is initially 0. + */ + void* GetUserData() const { return _userData; } + +protected: + explicit XMLNode( XMLDocument* ); + virtual ~XMLNode(); + + virtual char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr); + + XMLDocument* _document; + XMLNode* _parent; + mutable StrPair _value; + int _parseLineNum; + + XMLNode* _firstChild; + XMLNode* _lastChild; + + XMLNode* _prev; + XMLNode* _next; + + void* _userData; + +private: + MemPool* _memPool; + void Unlink( XMLNode* child ); + static void DeleteNode( XMLNode* node ); + void InsertChildPreamble( XMLNode* insertThis ) const; + const XMLElement* ToElementWithName( const char* name ) const; + + XMLNode( const XMLNode& ); // not supported + XMLNode& operator=( const XMLNode& ); // not supported +}; + + +/** XML text. + + Note that a text node can have child element nodes, for example: + @verbatim + This is bold + @endverbatim + + A text node can have 2 ways to output the next. "normal" output + and CDATA. It will default to the mode it was parsed from the XML file and + you generally want to leave it alone, but you can change the output mode with + SetCData() and query it with CData(). +*/ +class TINYXML2_LIB XMLText : public XMLNode +{ + friend class XMLDocument; +public: + virtual bool Accept( XMLVisitor* visitor ) const; + + virtual XMLText* ToText() { + return this; + } + virtual const XMLText* ToText() const { + return this; + } + + /// Declare whether this should be CDATA or standard text. + void SetCData( bool isCData ) { + _isCData = isCData; + } + /// Returns true if this is a CDATA text element. + bool CData() const { + return _isCData; + } + + virtual XMLNode* ShallowClone( XMLDocument* document ) const; + virtual bool ShallowEqual( const XMLNode* compare ) const; + +protected: + explicit XMLText( XMLDocument* doc ) : XMLNode( doc ), _isCData( false ) {} + virtual ~XMLText() {} + + char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ); + +private: + bool _isCData; + + XMLText( const XMLText& ); // not supported + XMLText& operator=( const XMLText& ); // not supported +}; + + +/** An XML Comment. */ +class TINYXML2_LIB XMLComment : public XMLNode +{ + friend class XMLDocument; +public: + virtual XMLComment* ToComment() { + return this; + } + virtual const XMLComment* ToComment() const { + return this; + } + + virtual bool Accept( XMLVisitor* visitor ) const; + + virtual XMLNode* ShallowClone( XMLDocument* document ) const; + virtual bool ShallowEqual( const XMLNode* compare ) const; + +protected: + explicit XMLComment( XMLDocument* doc ); + virtual ~XMLComment(); + + char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr); + +private: + XMLComment( const XMLComment& ); // not supported + XMLComment& operator=( const XMLComment& ); // not supported +}; + + +/** In correct XML the declaration is the first entry in the file. + @verbatim + + @endverbatim + + TinyXML-2 will happily read or write files without a declaration, + however. + + The text of the declaration isn't interpreted. It is parsed + and written as a string. +*/ +class TINYXML2_LIB XMLDeclaration : public XMLNode +{ + friend class XMLDocument; +public: + virtual XMLDeclaration* ToDeclaration() { + return this; + } + virtual const XMLDeclaration* ToDeclaration() const { + return this; + } + + virtual bool Accept( XMLVisitor* visitor ) const; + + virtual XMLNode* ShallowClone( XMLDocument* document ) const; + virtual bool ShallowEqual( const XMLNode* compare ) const; + +protected: + explicit XMLDeclaration( XMLDocument* doc ); + virtual ~XMLDeclaration(); + + char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ); + +private: + XMLDeclaration( const XMLDeclaration& ); // not supported + XMLDeclaration& operator=( const XMLDeclaration& ); // not supported +}; + + +/** Any tag that TinyXML-2 doesn't recognize is saved as an + unknown. It is a tag of text, but should not be modified. + It will be written back to the XML, unchanged, when the file + is saved. + + DTD tags get thrown into XMLUnknowns. +*/ +class TINYXML2_LIB XMLUnknown : public XMLNode +{ + friend class XMLDocument; +public: + virtual XMLUnknown* ToUnknown() { + return this; + } + virtual const XMLUnknown* ToUnknown() const { + return this; + } + + virtual bool Accept( XMLVisitor* visitor ) const; + + virtual XMLNode* ShallowClone( XMLDocument* document ) const; + virtual bool ShallowEqual( const XMLNode* compare ) const; + +protected: + explicit XMLUnknown( XMLDocument* doc ); + virtual ~XMLUnknown(); + + char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ); + +private: + XMLUnknown( const XMLUnknown& ); // not supported + XMLUnknown& operator=( const XMLUnknown& ); // not supported +}; + + + +/** An attribute is a name-value pair. Elements have an arbitrary + number of attributes, each with a unique name. + + @note The attributes are not XMLNodes. You may only query the + Next() attribute in a list. +*/ +class TINYXML2_LIB XMLAttribute +{ + friend class XMLElement; +public: + /// The name of the attribute. + const char* Name() const; + + /// The value of the attribute. + const char* Value() const; + + /// Gets the line number the attribute is in, if the document was parsed from a file. + int GetLineNum() const { return _parseLineNum; } + + /// The next attribute in the list. + const XMLAttribute* Next() const { + return _next; + } + + /** IntValue interprets the attribute as an integer, and returns the value. + If the value isn't an integer, 0 will be returned. There is no error checking; + use QueryIntValue() if you need error checking. + */ + int IntValue() const { + int i = 0; + QueryIntValue(&i); + return i; + } + + int64_t Int64Value() const { + int64_t i = 0; + QueryInt64Value(&i); + return i; + } + + uint64_t Unsigned64Value() const { + uint64_t i = 0; + QueryUnsigned64Value(&i); + return i; + } + + /// Query as an unsigned integer. See IntValue() + unsigned UnsignedValue() const { + unsigned i=0; + QueryUnsignedValue( &i ); + return i; + } + /// Query as a boolean. See IntValue() + bool BoolValue() const { + bool b=false; + QueryBoolValue( &b ); + return b; + } + /// Query as a double. See IntValue() + double DoubleValue() const { + double d=0; + QueryDoubleValue( &d ); + return d; + } + /// Query as a float. See IntValue() + float FloatValue() const { + float f=0; + QueryFloatValue( &f ); + return f; + } + + /** QueryIntValue interprets the attribute as an integer, and returns the value + in the provided parameter. The function will return XML_SUCCESS on success, + and XML_WRONG_ATTRIBUTE_TYPE if the conversion is not successful. + */ + XMLError QueryIntValue( int* value ) const; + /// See QueryIntValue + XMLError QueryUnsignedValue( unsigned int* value ) const; + /// See QueryIntValue + XMLError QueryInt64Value(int64_t* value) const; + /// See QueryIntValue + XMLError QueryUnsigned64Value(uint64_t* value) const; + /// See QueryIntValue + XMLError QueryBoolValue( bool* value ) const; + /// See QueryIntValue + XMLError QueryDoubleValue( double* value ) const; + /// See QueryIntValue + XMLError QueryFloatValue( float* value ) const; + + /// Set the attribute to a string value. + void SetAttribute( const char* value ); + /// Set the attribute to value. + void SetAttribute( int value ); + /// Set the attribute to value. + void SetAttribute( unsigned value ); + /// Set the attribute to value. + void SetAttribute(int64_t value); + /// Set the attribute to value. + void SetAttribute(uint64_t value); + /// Set the attribute to value. + void SetAttribute( bool value ); + /// Set the attribute to value. + void SetAttribute( double value ); + /// Set the attribute to value. + void SetAttribute( float value ); + +private: + enum { BUF_SIZE = 200 }; + + XMLAttribute() : _name(), _value(),_parseLineNum( 0 ), _next( 0 ), _memPool( 0 ) {} + virtual ~XMLAttribute() {} + + XMLAttribute( const XMLAttribute& ); // not supported + void operator=( const XMLAttribute& ); // not supported + void SetName( const char* name ); + + char* ParseDeep( char* p, bool processEntities, int* curLineNumPtr ); + + mutable StrPair _name; + mutable StrPair _value; + int _parseLineNum; + XMLAttribute* _next; + MemPool* _memPool; +}; + + +/** The element is a container class. It has a value, the element name, + and can contain other elements, text, comments, and unknowns. + Elements also contain an arbitrary number of attributes. +*/ +class TINYXML2_LIB XMLElement : public XMLNode +{ + friend class XMLDocument; +public: + /// Get the name of an element (which is the Value() of the node.) + const char* Name() const { + return Value(); + } + /// Set the name of the element. + void SetName( const char* str, bool staticMem=false ) { + SetValue( str, staticMem ); + } + + virtual XMLElement* ToElement() { + return this; + } + virtual const XMLElement* ToElement() const { + return this; + } + virtual bool Accept( XMLVisitor* visitor ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none + exists. For example: + + @verbatim + const char* value = ele->Attribute( "foo" ); + @endverbatim + + The 'value' parameter is normally null. However, if specified, + the attribute will only be returned if the 'name' and 'value' + match. This allow you to write code: + + @verbatim + if ( ele->Attribute( "foo", "bar" ) ) callFooIsBar(); + @endverbatim + + rather than: + @verbatim + if ( ele->Attribute( "foo" ) ) { + if ( strcmp( ele->Attribute( "foo" ), "bar" ) == 0 ) callFooIsBar(); + } + @endverbatim + */ + const char* Attribute( const char* name, const char* value=0 ) const; + + /** Given an attribute name, IntAttribute() returns the value + of the attribute interpreted as an integer. The default + value will be returned if the attribute isn't present, + or if there is an error. (For a method with error + checking, see QueryIntAttribute()). + */ + int IntAttribute(const char* name, int defaultValue = 0) const; + /// See IntAttribute() + unsigned UnsignedAttribute(const char* name, unsigned defaultValue = 0) const; + /// See IntAttribute() + int64_t Int64Attribute(const char* name, int64_t defaultValue = 0) const; + /// See IntAttribute() + uint64_t Unsigned64Attribute(const char* name, uint64_t defaultValue = 0) const; + /// See IntAttribute() + bool BoolAttribute(const char* name, bool defaultValue = false) const; + /// See IntAttribute() + double DoubleAttribute(const char* name, double defaultValue = 0) const; + /// See IntAttribute() + float FloatAttribute(const char* name, float defaultValue = 0) const; + + /** Given an attribute name, QueryIntAttribute() returns + XML_SUCCESS, XML_WRONG_ATTRIBUTE_TYPE if the conversion + can't be performed, or XML_NO_ATTRIBUTE if the attribute + doesn't exist. If successful, the result of the conversion + will be written to 'value'. If not successful, nothing will + be written to 'value'. This allows you to provide default + value: + + @verbatim + int value = 10; + QueryIntAttribute( "foo", &value ); // if "foo" isn't found, value will still be 10 + @endverbatim + */ + XMLError QueryIntAttribute( const char* name, int* value ) const { + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return XML_NO_ATTRIBUTE; + } + return a->QueryIntValue( value ); + } + + /// See QueryIntAttribute() + XMLError QueryUnsignedAttribute( const char* name, unsigned int* value ) const { + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return XML_NO_ATTRIBUTE; + } + return a->QueryUnsignedValue( value ); + } + + /// See QueryIntAttribute() + XMLError QueryInt64Attribute(const char* name, int64_t* value) const { + const XMLAttribute* a = FindAttribute(name); + if (!a) { + return XML_NO_ATTRIBUTE; + } + return a->QueryInt64Value(value); + } + + /// See QueryIntAttribute() + XMLError QueryUnsigned64Attribute(const char* name, uint64_t* value) const { + const XMLAttribute* a = FindAttribute(name); + if(!a) { + return XML_NO_ATTRIBUTE; + } + return a->QueryUnsigned64Value(value); + } + + /// See QueryIntAttribute() + XMLError QueryBoolAttribute( const char* name, bool* value ) const { + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return XML_NO_ATTRIBUTE; + } + return a->QueryBoolValue( value ); + } + /// See QueryIntAttribute() + XMLError QueryDoubleAttribute( const char* name, double* value ) const { + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return XML_NO_ATTRIBUTE; + } + return a->QueryDoubleValue( value ); + } + /// See QueryIntAttribute() + XMLError QueryFloatAttribute( const char* name, float* value ) const { + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return XML_NO_ATTRIBUTE; + } + return a->QueryFloatValue( value ); + } + + /// See QueryIntAttribute() + XMLError QueryStringAttribute(const char* name, const char** value) const { + const XMLAttribute* a = FindAttribute(name); + if (!a) { + return XML_NO_ATTRIBUTE; + } + *value = a->Value(); + return XML_SUCCESS; + } + + + + /** Given an attribute name, QueryAttribute() returns + XML_SUCCESS, XML_WRONG_ATTRIBUTE_TYPE if the conversion + can't be performed, or XML_NO_ATTRIBUTE if the attribute + doesn't exist. It is overloaded for the primitive types, + and is a generally more convenient replacement of + QueryIntAttribute() and related functions. + + If successful, the result of the conversion + will be written to 'value'. If not successful, nothing will + be written to 'value'. This allows you to provide default + value: + + @verbatim + int value = 10; + QueryAttribute( "foo", &value ); // if "foo" isn't found, value will still be 10 + @endverbatim + */ + XMLError QueryAttribute( const char* name, int* value ) const { + return QueryIntAttribute( name, value ); + } + + XMLError QueryAttribute( const char* name, unsigned int* value ) const { + return QueryUnsignedAttribute( name, value ); + } + + XMLError QueryAttribute(const char* name, int64_t* value) const { + return QueryInt64Attribute(name, value); + } + + XMLError QueryAttribute(const char* name, uint64_t* value) const { + return QueryUnsigned64Attribute(name, value); + } + + XMLError QueryAttribute( const char* name, bool* value ) const { + return QueryBoolAttribute( name, value ); + } + + XMLError QueryAttribute( const char* name, double* value ) const { + return QueryDoubleAttribute( name, value ); + } + + XMLError QueryAttribute( const char* name, float* value ) const { + return QueryFloatAttribute( name, value ); + } + + XMLError QueryAttribute(const char* name, const char** value) const { + return QueryStringAttribute(name, value); + } + + /// Sets the named attribute to value. + void SetAttribute( const char* name, const char* value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + /// Sets the named attribute to value. + void SetAttribute( const char* name, int value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + /// Sets the named attribute to value. + void SetAttribute( const char* name, unsigned value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + + /// Sets the named attribute to value. + void SetAttribute(const char* name, int64_t value) { + XMLAttribute* a = FindOrCreateAttribute(name); + a->SetAttribute(value); + } + + /// Sets the named attribute to value. + void SetAttribute(const char* name, uint64_t value) { + XMLAttribute* a = FindOrCreateAttribute(name); + a->SetAttribute(value); + } + + /// Sets the named attribute to value. + void SetAttribute( const char* name, bool value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + /// Sets the named attribute to value. + void SetAttribute( const char* name, double value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + /// Sets the named attribute to value. + void SetAttribute( const char* name, float value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + + /** + Delete an attribute. + */ + void DeleteAttribute( const char* name ); + + /// Return the first attribute in the list. + const XMLAttribute* FirstAttribute() const { + return _rootAttribute; + } + /// Query a specific attribute in the list. + const XMLAttribute* FindAttribute( const char* name ) const; + + /** Convenience function for easy access to the text inside an element. Although easy + and concise, GetText() is limited compared to getting the XMLText child + and accessing it directly. + + If the first child of 'this' is a XMLText, the GetText() + returns the character string of the Text node, else null is returned. + + This is a convenient method for getting the text of simple contained text: + @verbatim + This is text + const char* str = fooElement->GetText(); + @endverbatim + + 'str' will be a pointer to "This is text". + + Note that this function can be misleading. If the element foo was created from + this XML: + @verbatim + This is text + @endverbatim + + then the value of str would be null. The first child node isn't a text node, it is + another element. From this XML: + @verbatim + This is text + @endverbatim + GetText() will return "This is ". + */ + const char* GetText() const; + + /** Convenience function for easy access to the text inside an element. Although easy + and concise, SetText() is limited compared to creating an XMLText child + and mutating it directly. + + If the first child of 'this' is a XMLText, SetText() sets its value to + the given string, otherwise it will create a first child that is an XMLText. + + This is a convenient method for setting the text of simple contained text: + @verbatim + This is text + fooElement->SetText( "Hullaballoo!" ); + Hullaballoo! + @endverbatim + + Note that this function can be misleading. If the element foo was created from + this XML: + @verbatim + This is text + @endverbatim + + then it will not change "This is text", but rather prefix it with a text element: + @verbatim + Hullaballoo!This is text + @endverbatim + + For this XML: + @verbatim + + @endverbatim + SetText() will generate + @verbatim + Hullaballoo! + @endverbatim + */ + void SetText( const char* inText ); + /// Convenience method for setting text inside an element. See SetText() for important limitations. + void SetText( int value ); + /// Convenience method for setting text inside an element. See SetText() for important limitations. + void SetText( unsigned value ); + /// Convenience method for setting text inside an element. See SetText() for important limitations. + void SetText(int64_t value); + /// Convenience method for setting text inside an element. See SetText() for important limitations. + void SetText(uint64_t value); + /// Convenience method for setting text inside an element. See SetText() for important limitations. + void SetText( bool value ); + /// Convenience method for setting text inside an element. See SetText() for important limitations. + void SetText( double value ); + /// Convenience method for setting text inside an element. See SetText() for important limitations. + void SetText( float value ); + + /** + Convenience method to query the value of a child text node. This is probably best + shown by example. Given you have a document is this form: + @verbatim + + 1 + 1.4 + + @endverbatim + + The QueryIntText() and similar functions provide a safe and easier way to get to the + "value" of x and y. + + @verbatim + int x = 0; + float y = 0; // types of x and y are contrived for example + const XMLElement* xElement = pointElement->FirstChildElement( "x" ); + const XMLElement* yElement = pointElement->FirstChildElement( "y" ); + xElement->QueryIntText( &x ); + yElement->QueryFloatText( &y ); + @endverbatim + + @returns XML_SUCCESS (0) on success, XML_CAN_NOT_CONVERT_TEXT if the text cannot be converted + to the requested type, and XML_NO_TEXT_NODE if there is no child text to query. + + */ + XMLError QueryIntText( int* ival ) const; + /// See QueryIntText() + XMLError QueryUnsignedText( unsigned* uval ) const; + /// See QueryIntText() + XMLError QueryInt64Text(int64_t* uval) const; + /// See QueryIntText() + XMLError QueryUnsigned64Text(uint64_t* uval) const; + /// See QueryIntText() + XMLError QueryBoolText( bool* bval ) const; + /// See QueryIntText() + XMLError QueryDoubleText( double* dval ) const; + /// See QueryIntText() + XMLError QueryFloatText( float* fval ) const; + + int IntText(int defaultValue = 0) const; + + /// See QueryIntText() + unsigned UnsignedText(unsigned defaultValue = 0) const; + /// See QueryIntText() + int64_t Int64Text(int64_t defaultValue = 0) const; + /// See QueryIntText() + uint64_t Unsigned64Text(uint64_t defaultValue = 0) const; + /// See QueryIntText() + bool BoolText(bool defaultValue = false) const; + /// See QueryIntText() + double DoubleText(double defaultValue = 0) const; + /// See QueryIntText() + float FloatText(float defaultValue = 0) const; + + /** + Convenience method to create a new XMLElement and add it as last (right) + child of this node. Returns the created and inserted element. + */ + XMLElement* InsertNewChildElement(const char* name); + /// See InsertNewChildElement() + XMLComment* InsertNewComment(const char* comment); + /// See InsertNewChildElement() + XMLText* InsertNewText(const char* text); + /// See InsertNewChildElement() + XMLDeclaration* InsertNewDeclaration(const char* text); + /// See InsertNewChildElement() + XMLUnknown* InsertNewUnknown(const char* text); + + + // internal: + enum ElementClosingType { + OPEN, // + CLOSED, // + CLOSING // + }; + ElementClosingType ClosingType() const { + return _closingType; + } + virtual XMLNode* ShallowClone( XMLDocument* document ) const; + virtual bool ShallowEqual( const XMLNode* compare ) const; + +protected: + char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ); + +private: + XMLElement( XMLDocument* doc ); + virtual ~XMLElement(); + XMLElement( const XMLElement& ); // not supported + void operator=( const XMLElement& ); // not supported + + XMLAttribute* FindOrCreateAttribute( const char* name ); + char* ParseAttributes( char* p, int* curLineNumPtr ); + static void DeleteAttribute( XMLAttribute* attribute ); + XMLAttribute* CreateAttribute(); + + enum { BUF_SIZE = 200 }; + ElementClosingType _closingType; + // The attribute list is ordered; there is no 'lastAttribute' + // because the list needs to be scanned for dupes before adding + // a new attribute. + XMLAttribute* _rootAttribute; +}; + + +enum Whitespace { + PRESERVE_WHITESPACE, + COLLAPSE_WHITESPACE +}; + + +/** A Document binds together all the functionality. + It can be saved, loaded, and printed to the screen. + All Nodes are connected and allocated to a Document. + If the Document is deleted, all its Nodes are also deleted. +*/ +class TINYXML2_LIB XMLDocument : public XMLNode +{ + friend class XMLElement; + // Gives access to SetError and Push/PopDepth, but over-access for everything else. + // Wishing C++ had "internal" scope. + friend class XMLNode; + friend class XMLText; + friend class XMLComment; + friend class XMLDeclaration; + friend class XMLUnknown; +public: + /// constructor + XMLDocument( bool processEntities = true, Whitespace whitespaceMode = PRESERVE_WHITESPACE ); + ~XMLDocument(); + + virtual XMLDocument* ToDocument() { + TIXMLASSERT( this == _document ); + return this; + } + virtual const XMLDocument* ToDocument() const { + TIXMLASSERT( this == _document ); + return this; + } + + /** + Parse an XML file from a character string. + Returns XML_SUCCESS (0) on success, or + an errorID. + + You may optionally pass in the 'nBytes', which is + the number of bytes which will be parsed. If not + specified, TinyXML-2 will assume 'xml' points to a + null terminated string. + */ + XMLError Parse( const char* xml, size_t nBytes=static_cast(-1) ); + + /** + Load an XML file from disk. + Returns XML_SUCCESS (0) on success, or + an errorID. + */ + XMLError LoadFile( const char* filename ); + + /** + Load an XML file from disk. You are responsible + for providing and closing the FILE*. + + NOTE: The file should be opened as binary ("rb") + not text in order for TinyXML-2 to correctly + do newline normalization. + + Returns XML_SUCCESS (0) on success, or + an errorID. + */ + XMLError LoadFile( FILE* ); + + /** + Save the XML file to disk. + Returns XML_SUCCESS (0) on success, or + an errorID. + */ + XMLError SaveFile( const char* filename, bool compact = false ); + + /** + Save the XML file to disk. You are responsible + for providing and closing the FILE*. + + Returns XML_SUCCESS (0) on success, or + an errorID. + */ + XMLError SaveFile( FILE* fp, bool compact = false ); + + bool ProcessEntities() const { + return _processEntities; + } + Whitespace WhitespaceMode() const { + return _whitespaceMode; + } + + /** + Returns true if this document has a leading Byte Order Mark of UTF8. + */ + bool HasBOM() const { + return _writeBOM; + } + /** Sets whether to write the BOM when writing the file. + */ + void SetBOM( bool useBOM ) { + _writeBOM = useBOM; + } + + /** Return the root element of DOM. Equivalent to FirstChildElement(). + To get the first node, use FirstChild(). + */ + XMLElement* RootElement() { + return FirstChildElement(); + } + const XMLElement* RootElement() const { + return FirstChildElement(); + } + + /** Print the Document. If the Printer is not provided, it will + print to stdout. If you provide Printer, this can print to a file: + @verbatim + XMLPrinter printer( fp ); + doc.Print( &printer ); + @endverbatim + + Or you can use a printer to print to memory: + @verbatim + XMLPrinter printer; + doc.Print( &printer ); + // printer.CStr() has a const char* to the XML + @endverbatim + */ + void Print( XMLPrinter* streamer=0 ) const; + virtual bool Accept( XMLVisitor* visitor ) const; + + /** + Create a new Element associated with + this Document. The memory for the Element + is managed by the Document. + */ + XMLElement* NewElement( const char* name ); + /** + Create a new Comment associated with + this Document. The memory for the Comment + is managed by the Document. + */ + XMLComment* NewComment( const char* comment ); + /** + Create a new Text associated with + this Document. The memory for the Text + is managed by the Document. + */ + XMLText* NewText( const char* text ); + /** + Create a new Declaration associated with + this Document. The memory for the object + is managed by the Document. + + If the 'text' param is null, the standard + declaration is used.: + @verbatim + + @endverbatim + */ + XMLDeclaration* NewDeclaration( const char* text=0 ); + /** + Create a new Unknown associated with + this Document. The memory for the object + is managed by the Document. + */ + XMLUnknown* NewUnknown( const char* text ); + + /** + Delete a node associated with this document. + It will be unlinked from the DOM. + */ + void DeleteNode( XMLNode* node ); + + /// Clears the error flags. + void ClearError(); + + /// Return true if there was an error parsing the document. + bool Error() const { + return _errorID != XML_SUCCESS; + } + /// Return the errorID. + XMLError ErrorID() const { + return _errorID; + } + const char* ErrorName() const; + static const char* ErrorIDToName(XMLError errorID); + + /** Returns a "long form" error description. A hopefully helpful + diagnostic with location, line number, and/or additional info. + */ + const char* ErrorStr() const; + + /// A (trivial) utility function that prints the ErrorStr() to stdout. + void PrintError() const; + + /// Return the line where the error occurred, or zero if unknown. + int ErrorLineNum() const + { + return _errorLineNum; + } + + /// Clear the document, resetting it to the initial state. + void Clear(); + + /** + Copies this document to a target document. + The target will be completely cleared before the copy. + If you want to copy a sub-tree, see XMLNode::DeepClone(). + + NOTE: that the 'target' must be non-null. + */ + void DeepCopy(XMLDocument* target) const; + + // internal + char* Identify( char* p, XMLNode** node ); + + // internal + void MarkInUse(const XMLNode* const); + + virtual XMLNode* ShallowClone( XMLDocument* /*document*/ ) const { + return 0; + } + virtual bool ShallowEqual( const XMLNode* /*compare*/ ) const { + return false; + } + +private: + XMLDocument( const XMLDocument& ); // not supported + void operator=( const XMLDocument& ); // not supported + + bool _writeBOM; + bool _processEntities; + XMLError _errorID; + Whitespace _whitespaceMode; + mutable StrPair _errorStr; + int _errorLineNum; + char* _charBuffer; + int _parseCurLineNum; + int _parsingDepth; + // Memory tracking does add some overhead. + // However, the code assumes that you don't + // have a bunch of unlinked nodes around. + // Therefore it takes less memory to track + // in the document vs. a linked list in the XMLNode, + // and the performance is the same. + DynArray _unlinked; + + MemPoolT< sizeof(XMLElement) > _elementPool; + MemPoolT< sizeof(XMLAttribute) > _attributePool; + MemPoolT< sizeof(XMLText) > _textPool; + MemPoolT< sizeof(XMLComment) > _commentPool; + + static const char* _errorNames[XML_ERROR_COUNT]; + + void Parse(); + + void SetError( XMLError error, int lineNum, const char* format, ... ); + + // Something of an obvious security hole, once it was discovered. + // Either an ill-formed XML or an excessively deep one can overflow + // the stack. Track stack depth, and error out if needed. + class DepthTracker { + public: + explicit DepthTracker(XMLDocument * document) { + this->_document = document; + document->PushDepth(); + } + ~DepthTracker() { + _document->PopDepth(); + } + private: + XMLDocument * _document; + }; + void PushDepth(); + void PopDepth(); + + template + NodeType* CreateUnlinkedNode( MemPoolT& pool ); +}; + +template +inline NodeType* XMLDocument::CreateUnlinkedNode( MemPoolT& pool ) +{ + TIXMLASSERT( sizeof( NodeType ) == PoolElementSize ); + TIXMLASSERT( sizeof( NodeType ) == pool.ItemSize() ); + NodeType* returnNode = new (pool.Alloc()) NodeType( this ); + TIXMLASSERT( returnNode ); + returnNode->_memPool = &pool; + + _unlinked.Push(returnNode); + return returnNode; +} + +/** + A XMLHandle is a class that wraps a node pointer with null checks; this is + an incredibly useful thing. Note that XMLHandle is not part of the TinyXML-2 + DOM structure. It is a separate utility class. + + Take an example: + @verbatim + + + + + + + @endverbatim + + Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very + easy to write a *lot* of code that looks like: + + @verbatim + XMLElement* root = document.FirstChildElement( "Document" ); + if ( root ) + { + XMLElement* element = root->FirstChildElement( "Element" ); + if ( element ) + { + XMLElement* child = element->FirstChildElement( "Child" ); + if ( child ) + { + XMLElement* child2 = child->NextSiblingElement( "Child" ); + if ( child2 ) + { + // Finally do something useful. + @endverbatim + + And that doesn't even cover "else" cases. XMLHandle addresses the verbosity + of such code. A XMLHandle checks for null pointers so it is perfectly safe + and correct to use: + + @verbatim + XMLHandle docHandle( &document ); + XMLElement* child2 = docHandle.FirstChildElement( "Document" ).FirstChildElement( "Element" ).FirstChildElement().NextSiblingElement(); + if ( child2 ) + { + // do something useful + @endverbatim + + Which is MUCH more concise and useful. + + It is also safe to copy handles - internally they are nothing more than node pointers. + @verbatim + XMLHandle handleCopy = handle; + @endverbatim + + See also XMLConstHandle, which is the same as XMLHandle, but operates on const objects. +*/ +class TINYXML2_LIB XMLHandle +{ +public: + /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. + explicit XMLHandle( XMLNode* node ) : _node( node ) { + } + /// Create a handle from a node. + explicit XMLHandle( XMLNode& node ) : _node( &node ) { + } + /// Copy constructor + XMLHandle( const XMLHandle& ref ) : _node( ref._node ) { + } + /// Assignment + XMLHandle& operator=( const XMLHandle& ref ) { + _node = ref._node; + return *this; + } + + /// Get the first child of this handle. + XMLHandle FirstChild() { + return XMLHandle( _node ? _node->FirstChild() : 0 ); + } + /// Get the first child element of this handle. + XMLHandle FirstChildElement( const char* name = 0 ) { + return XMLHandle( _node ? _node->FirstChildElement( name ) : 0 ); + } + /// Get the last child of this handle. + XMLHandle LastChild() { + return XMLHandle( _node ? _node->LastChild() : 0 ); + } + /// Get the last child element of this handle. + XMLHandle LastChildElement( const char* name = 0 ) { + return XMLHandle( _node ? _node->LastChildElement( name ) : 0 ); + } + /// Get the previous sibling of this handle. + XMLHandle PreviousSibling() { + return XMLHandle( _node ? _node->PreviousSibling() : 0 ); + } + /// Get the previous sibling element of this handle. + XMLHandle PreviousSiblingElement( const char* name = 0 ) { + return XMLHandle( _node ? _node->PreviousSiblingElement( name ) : 0 ); + } + /// Get the next sibling of this handle. + XMLHandle NextSibling() { + return XMLHandle( _node ? _node->NextSibling() : 0 ); + } + /// Get the next sibling element of this handle. + XMLHandle NextSiblingElement( const char* name = 0 ) { + return XMLHandle( _node ? _node->NextSiblingElement( name ) : 0 ); + } + + /// Safe cast to XMLNode. This can return null. + XMLNode* ToNode() { + return _node; + } + /// Safe cast to XMLElement. This can return null. + XMLElement* ToElement() { + return ( _node ? _node->ToElement() : 0 ); + } + /// Safe cast to XMLText. This can return null. + XMLText* ToText() { + return ( _node ? _node->ToText() : 0 ); + } + /// Safe cast to XMLUnknown. This can return null. + XMLUnknown* ToUnknown() { + return ( _node ? _node->ToUnknown() : 0 ); + } + /// Safe cast to XMLDeclaration. This can return null. + XMLDeclaration* ToDeclaration() { + return ( _node ? _node->ToDeclaration() : 0 ); + } + +private: + XMLNode* _node; +}; + + +/** + A variant of the XMLHandle class for working with const XMLNodes and Documents. It is the + same in all regards, except for the 'const' qualifiers. See XMLHandle for API. +*/ +class TINYXML2_LIB XMLConstHandle +{ +public: + explicit XMLConstHandle( const XMLNode* node ) : _node( node ) { + } + explicit XMLConstHandle( const XMLNode& node ) : _node( &node ) { + } + XMLConstHandle( const XMLConstHandle& ref ) : _node( ref._node ) { + } + + XMLConstHandle& operator=( const XMLConstHandle& ref ) { + _node = ref._node; + return *this; + } + + const XMLConstHandle FirstChild() const { + return XMLConstHandle( _node ? _node->FirstChild() : 0 ); + } + const XMLConstHandle FirstChildElement( const char* name = 0 ) const { + return XMLConstHandle( _node ? _node->FirstChildElement( name ) : 0 ); + } + const XMLConstHandle LastChild() const { + return XMLConstHandle( _node ? _node->LastChild() : 0 ); + } + const XMLConstHandle LastChildElement( const char* name = 0 ) const { + return XMLConstHandle( _node ? _node->LastChildElement( name ) : 0 ); + } + const XMLConstHandle PreviousSibling() const { + return XMLConstHandle( _node ? _node->PreviousSibling() : 0 ); + } + const XMLConstHandle PreviousSiblingElement( const char* name = 0 ) const { + return XMLConstHandle( _node ? _node->PreviousSiblingElement( name ) : 0 ); + } + const XMLConstHandle NextSibling() const { + return XMLConstHandle( _node ? _node->NextSibling() : 0 ); + } + const XMLConstHandle NextSiblingElement( const char* name = 0 ) const { + return XMLConstHandle( _node ? _node->NextSiblingElement( name ) : 0 ); + } + + + const XMLNode* ToNode() const { + return _node; + } + const XMLElement* ToElement() const { + return ( _node ? _node->ToElement() : 0 ); + } + const XMLText* ToText() const { + return ( _node ? _node->ToText() : 0 ); + } + const XMLUnknown* ToUnknown() const { + return ( _node ? _node->ToUnknown() : 0 ); + } + const XMLDeclaration* ToDeclaration() const { + return ( _node ? _node->ToDeclaration() : 0 ); + } + +private: + const XMLNode* _node; +}; + + +/** + Printing functionality. The XMLPrinter gives you more + options than the XMLDocument::Print() method. + + It can: + -# Print to memory. + -# Print to a file you provide. + -# Print XML without a XMLDocument. + + Print to Memory + + @verbatim + XMLPrinter printer; + doc.Print( &printer ); + SomeFunction( printer.CStr() ); + @endverbatim + + Print to a File + + You provide the file pointer. + @verbatim + XMLPrinter printer( fp ); + doc.Print( &printer ); + @endverbatim + + Print without a XMLDocument + + When loading, an XML parser is very useful. However, sometimes + when saving, it just gets in the way. The code is often set up + for streaming, and constructing the DOM is just overhead. + + The Printer supports the streaming case. The following code + prints out a trivially simple XML file without ever creating + an XML document. + + @verbatim + XMLPrinter printer( fp ); + printer.OpenElement( "foo" ); + printer.PushAttribute( "foo", "bar" ); + printer.CloseElement(); + @endverbatim +*/ +class TINYXML2_LIB XMLPrinter : public XMLVisitor +{ +public: + /** Construct the printer. If the FILE* is specified, + this will print to the FILE. Else it will print + to memory, and the result is available in CStr(). + If 'compact' is set to true, then output is created + with only required whitespace and newlines. + */ + XMLPrinter( FILE* file=0, bool compact = false, int depth = 0 ); + virtual ~XMLPrinter() {} + + /** If streaming, write the BOM and declaration. */ + void PushHeader( bool writeBOM, bool writeDeclaration ); + /** If streaming, start writing an element. + The element must be closed with CloseElement() + */ + void OpenElement( const char* name, bool compactMode=false ); + /// If streaming, add an attribute to an open element. + void PushAttribute( const char* name, const char* value ); + void PushAttribute( const char* name, int value ); + void PushAttribute( const char* name, unsigned value ); + void PushAttribute( const char* name, int64_t value ); + void PushAttribute( const char* name, uint64_t value ); + void PushAttribute( const char* name, bool value ); + void PushAttribute( const char* name, double value ); + /// If streaming, close the Element. + virtual void CloseElement( bool compactMode=false ); + + /// Add a text node. + void PushText( const char* text, bool cdata=false ); + /// Add a text node from an integer. + void PushText( int value ); + /// Add a text node from an unsigned. + void PushText( unsigned value ); + /// Add a text node from a signed 64bit integer. + void PushText( int64_t value ); + /// Add a text node from an unsigned 64bit integer. + void PushText( uint64_t value ); + /// Add a text node from a bool. + void PushText( bool value ); + /// Add a text node from a float. + void PushText( float value ); + /// Add a text node from a double. + void PushText( double value ); + + /// Add a comment + void PushComment( const char* comment ); + + void PushDeclaration( const char* value ); + void PushUnknown( const char* value ); + + virtual bool VisitEnter( const XMLDocument& /*doc*/ ); + virtual bool VisitExit( const XMLDocument& /*doc*/ ) { + return true; + } + + virtual bool VisitEnter( const XMLElement& element, const XMLAttribute* attribute ); + virtual bool VisitExit( const XMLElement& element ); + + virtual bool Visit( const XMLText& text ); + virtual bool Visit( const XMLComment& comment ); + virtual bool Visit( const XMLDeclaration& declaration ); + virtual bool Visit( const XMLUnknown& unknown ); + + /** + If in print to memory mode, return a pointer to + the XML file in memory. + */ + const char* CStr() const { + return _buffer.Mem(); + } + /** + If in print to memory mode, return the size + of the XML file in memory. (Note the size returned + includes the terminating null.) + */ + int CStrSize() const { + return _buffer.Size(); + } + /** + If in print to memory mode, reset the buffer to the + beginning. + */ + void ClearBuffer( bool resetToFirstElement = true ) { + _buffer.Clear(); + _buffer.Push(0); + _firstElement = resetToFirstElement; + } + +protected: + virtual bool CompactMode( const XMLElement& ) { return _compactMode; } + + /** Prints out the space before an element. You may override to change + the space and tabs used. A PrintSpace() override should call Print(). + */ + virtual void PrintSpace( int depth ); + virtual void Print( const char* format, ... ); + virtual void Write( const char* data, size_t size ); + virtual void Putc( char ch ); + + inline void Write(const char* data) { Write(data, strlen(data)); } + + void SealElementIfJustOpened(); + bool _elementJustOpened; + DynArray< const char*, 10 > _stack; + +private: + /** + Prepares to write a new node. This includes sealing an element that was + just opened, and writing any whitespace necessary if not in compact mode. + */ + void PrepareForNewNode( bool compactMode ); + void PrintString( const char*, bool restrictedEntitySet ); // prints out, after detecting entities. + + bool _firstElement; + FILE* _fp; + int _depth; + int _textDepth; + bool _processEntities; + bool _compactMode; + + enum { + ENTITY_RANGE = 64, + BUF_SIZE = 200 + }; + bool _entityFlag[ENTITY_RANGE]; + bool _restrictedEntityFlag[ENTITY_RANGE]; + + DynArray< char, 20 > _buffer; + + // Prohibit cloning, intentionally not implemented + XMLPrinter( const XMLPrinter& ); + XMLPrinter& operator=( const XMLPrinter& ); +}; + + +} // tinyxml2 + +#if defined(_MSC_VER) +# pragma warning(pop) +#endif + +#endif // TINYXML2_INCLUDED From c83b7143daf1795087a0d19a21c1fb5a34dbe781 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=B0=E4=BB=94?= Date: Wed, 18 Feb 2026 18:46:22 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E6=B8=85=E7=90=86=E6=96=87=E6=A1=A3?= =?UTF-8?q?=EF=BC=8C=E5=8F=AA=E4=BF=9D=E7=95=99=20README.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MIGRATION.md | 197 ------------------------------------------------- QUICKSTART.md | 201 -------------------------------------------------- 2 files changed, 398 deletions(-) delete mode 100644 MIGRATION.md delete mode 100644 QUICKSTART.md diff --git a/MIGRATION.md b/MIGRATION.md deleted file mode 100644 index f0cda3d..0000000 --- a/MIGRATION.md +++ /dev/null @@ -1,197 +0,0 @@ -# Utils 模块重构总结 - -## 修改概述 - -已成功将 Utils 目录设置为独立的 git 仓库,并更新了主项目中所有对 VrCommon、VrUtils 和 CloudUtils 的引用。 - -## 主要修改内容 - -### 1. Utils 目录结构 - -创建了独立的 Utils 模块,包含以下子模块: - -``` -Utils/ -├── VrCommon/ # 核心接口和数据结构 -├── VrUtils/ # 工具类库(JSON、log4cpp、tinyxml2、INI、MD5、CRC) -├── CloudUtils/ # 点云工具 -├── DataUtils/ # 数据处理工具 -│ ├── CloudMathClac/ # 点云数学计算 -│ └── CoordinateTransform/ # 坐标转换 -└── CloudView/ # 点云查看工具 -``` - -### 2. 创建的新文件 - -- `Utils/Utils.pro` - Utils 模块主项目文件 -- `Utils/README.md` - Utils 模块说明文档 -- `Utils/.gitignore` - Git 忽略文件配置 - -### 3. 修改的 .pro 文件 - -#### 主项目 -- `GrabBagPrj/GrabBagPrj.pro` - 将 VrCommon 和 VrUtils 的引用改为 Utils 模块 - -#### Utils 内部模块 -- `Utils/VrCommon/VrCommon.pro` - 添加 VrConfigCmd.h 头文件 -- `Utils/VrUtils/VrUtils.pro` - 保持不变 -- `Utils/CloudUtils/CloudUtils.pro` - 更新 INCLUDEPATH 为相对路径 -- `Utils/CloudView/CloudView.pro` - 更新 INCLUDEPATH 和 LIBS 路径 -- `Utils/DataUtils/CloudMathClac/CloudMathClac.pro` - 更新 SDK 路径 -- `Utils/DataUtils/CoordinateTransform/CoordinateTransform.pro` - 更新 SDK 路径 - -#### VrNets 模块 -- `VrNets/VrTcpClient.pro` - 更新 INCLUDEPATH 和 LIBS 路径 -- `VrNets/VrTcpServer.pro` - 更新 INCLUDEPATH 和 LIBS 路径 - -#### Module 模块 -- `Module/ShareMem/ShareMem.pro` - 更新 INCLUDEPATH -- `Module/ThreadPool/ThreadPool.pro` - 更新 INCLUDEPATH(修正错误路径) -- `Module/ModbusTCPServer/ModbusTCPServer.pro` - 更新 INCLUDEPATH 和 LIBS -- `Module/BinocularMarkReceiver/BinocularMarkReceiver.pro` - 更新 INCLUDEPATH 和 LIBS -- `Module/HandEyeCalib/HandEyeCalib.pro` - 更新 INCLUDEPATH -- `Module/ChessboardDetector/ChessboardDetector.pro` - 更新 INCLUDEPATH -- `Module/AuthModule/AuthModule.pro` - 更新 INCLUDEPATH - -#### Device 模块 -- `Device/VrEyeDevice/VrEyeDevice.pro` - 更新 INCLUDEPATH 和 LIBS -- `Device/HikDevice/HikDevice.pro` - 更新 INCLUDEPATH 和 LIBS -- `Device/GalaxyDevice/GalaxyDevice.pro` - 更新 INCLUDEPATH 和 LIBS -- `Device/GlLineLaserDevice/GlLineLaserDevice.pro` - 更新 INCLUDEPATH 和 LIBS -- `Device/EpicEyeDevice/EpicEyeDevice.pro` - 更新 INCLUDEPATH - -#### AppUtils 模块 -- `AppUtils/AppCommon/AppCommon.pro` - 更新 INCLUDEPATH 和 LIBS -- `AppUtils/UICommon/UICommon.pro` - 更新 INCLUDEPATH - -#### Tools 模块 -- `Tools/AuthRegister/AuthRegister.pro` - 更新 LIBS -- `Tools/ConfigDecryptor/ConfigDecryptor.pro` - 更新 INCLUDEPATH 和 LIBS -- `Tools/CalibView/CalibView.pro` - 更新 INCLUDEPATH 和 LIBS -- `Tools/VrEyeView/VrEyeView.pro` - 更新 INCLUDEPATH 和 LIBS - -#### App 模块(批量更新) -批量更新了 App 目录下所有应用程序的 .pro 文件(约 27 个文件),包括: -- GrabBag -- BeltTearing -- LapWeld -- Workpiece -- WorkpieceProject -- BinocularMark -- BagThreadPosition -- ParticleSize -- ScrewPosition -- TunnelChannel -- WheelMeasure -- WorkpieceHole - -#### Test 模块 -- `Test/tcpclient/tcpclient_test.pro` - 更新路径 -- `Test/tcpserver/tcpserver_test.pro` - 更新路径 - -### 4. 路径替换规则 - -所有 .pro 文件中的路径按以下规则进行了替换: - -``` -旧路径 新路径 -../../VrCommon/Inc -> ../../Utils/VrCommon/Inc -../../VrUtils/Inc -> ../../Utils/VrUtils/Inc -../../VrCommon/release -> ../../Utils/VrCommon/release -../../VrCommon/debug -> ../../Utils/VrCommon/debug -../../VrUtils/release -> ../../Utils/VrUtils/release -../../VrUtils/debug -> ../../Utils/VrUtils/debug -../../AppUtils/CloudUtils -> ../../Utils/CloudUtils - -../../../VrCommon/Inc -> ../../../Utils/VrCommon/Inc -../../../VrUtils/Inc -> ../../../Utils/VrUtils/Inc -../../../VrCommon/release -> ../../../Utils/VrCommon/release -../../../VrCommon/debug -> ../../../Utils/VrCommon/debug -../../../VrUtils/release -> ../../../Utils/VrUtils/release -../../../VrUtils/debug -> ../../../Utils/VrUtils/debug -../../../AppUtils/CloudUtils -> ../../../Utils/CloudUtils -``` - -### 5. Git 仓库初始化 - -已在 Utils 目录下初始化 git 仓库: -```bash -cd Utils -git init -git add . -``` - -所有文件已添加到暂存区,等待提交。 - -## 依赖关系 - -更新后的依赖关系: - -``` -主项目 (GrabBagPrj) - └── Utils - ├── VrCommon - ├── VrUtils (依赖 VrCommon) - ├── CloudUtils (依赖 VrCommon, VrUtils) - ├── DataUtils (依赖 VrCommon) - └── CloudView (依赖 VrCommon, VrUtils, CloudUtils) - └── VrNets (依赖 Utils) - └── Module (依赖 Utils) - └── Device (依赖 Utils) - └── AppUtils (依赖 Utils) - └── App (依赖 Utils, VrNets, Module, Device, AppUtils) - └── Tools (依赖 Utils, Module, Robot) -``` - -## 下一步操作 - -1. 在 Utils 目录下提交初始版本: - ```bash - cd Utils - git commit -m "初始提交:Utils 工具库模块" - ``` - -2. 如果需要推送到远程仓库: - ```bash - git remote add origin <远程仓库地址> - git push -u origin master - ``` - -3. 在主项目中,可以将 Utils 作为 git submodule 引用: - ```bash - cd .. - git submodule add Utils - ``` - -## 验证构建 - -建议执行以下步骤验证构建: - -1. 清理旧的构建产物: - ```bash - cd GrabBagPrj - make clean - ``` - -2. 重新生成 Makefile: - ```bash - qmake GrabBagPrj.pro - ``` - -3. 编译项目: - ```bash - make - ``` - -## 注意事项 - -1. 所有路径更新已完成,但建议在实际编译前进行测试 -2. Utils 模块现在是独立的,可以被其他项目引用 -3. 如果需要修改 Utils 模块,应该在 Utils 目录下进行 git 操作 -4. 主项目的 .gitignore 应该排除 Utils 目录(如果使用 submodule) - -## 文件统计 - -- 修改的 .pro 文件:约 60+ 个 -- 新建的文件:3 个(Utils.pro, README.md, .gitignore) -- Utils 模块包含的文件:约 230+ 个 diff --git a/QUICKSTART.md b/QUICKSTART.md deleted file mode 100644 index 1b24496..0000000 --- a/QUICKSTART.md +++ /dev/null @@ -1,201 +0,0 @@ -# Utils 模块快速参考指南 - -## 快速开始 - -### 1. 提交 Utils 仓库 - -```bash -cd Utils -git commit -m "初始提交:Utils 工具库模块 - -包含以下子模块: -- VrCommon: 核心接口和数据结构 -- VrUtils: 工具类库(JSON、log4cpp、tinyxml2、INI、MD5、CRC) -- CloudUtils: 点云工具 -- DataUtils: 数据处理工具(CloudMathClac、CoordinateTransform) -- CloudView: 点云查看工具 -" -``` - -### 2. 添加远程仓库(可选) - -```bash -# 添加远程仓库 -git remote add origin <你的远程仓库地址> - -# 推送到远程 -git push -u origin master -``` - -### 3. 在主项目中使用 submodule(可选) - -如果你想将 Utils 作为 git submodule 使用: - -```bash -# 在主项目根目录 -cd .. - -# 删除当前的 Utils 目录(确保已备份) -# rm -rf Utils - -# 添加为 submodule -git submodule add Utils - -# 提交 submodule 配置 -git add .gitmodules Utils -git commit -m "将 Utils 添加为 git submodule" -``` - -## 在其他项目中使用 Utils - -### 方法 1: 作为 git submodule - -```bash -# 在你的项目根目录 -git submodule add Utils -git submodule update --init --recursive -``` - -### 方法 2: 直接克隆 - -```bash -# 在你的项目根目录 -git clone Utils -``` - -### 方法 3: 复制文件 - -直接将 Utils 目录复制到你的项目中。 - -## 在项目中引用 Utils - -### 在 .pro 文件中添加 - -```qmake -# 包含路径 -INCLUDEPATH += $$PWD/../Utils/VrCommon/Inc -INCLUDEPATH += $$PWD/../Utils/VrUtils/Inc - -# 链接库(Windows) -win32:CONFIG(release, debug|release): { - LIBS += -L$$PWD/../Utils/VrCommon/release -lVrCommon - LIBS += -L$$PWD/../Utils/VrUtils/release -lVrUtils -} -else:win32:CONFIG(debug, debug|release): { - LIBS += -L$$PWD/../Utils/VrCommon/debug -lVrCommon - LIBS += -L$$PWD/../Utils/VrUtils/debug -lVrUtils -} - -# 链接库(Unix/Linux) -else:unix:!macx { - LIBS += -L$$PWD/../Utils/VrCommon -lVrCommon - LIBS += -L$$PWD/../Utils/VrUtils -lVrUtils -} -``` - -### 在主项目中添加 Utils 子项目 - -```qmake -# 在主项目的 .pro 文件中 -TEMPLATE = subdirs - -Utils.file = ../Utils/Utils.pro -SUBDIRS += Utils - -# 设置依赖关系 -YourModule.depends = Utils -``` - -## 常用命令 - -### 构建 Utils 模块 - -```bash -cd Utils -qmake Utils.pro -make -``` - -### 清理构建产物 - -```bash -cd Utils -make clean -``` - -### 更新 Utils 模块(如果使用 submodule) - -```bash -# 在主项目根目录 -git submodule update --remote Utils -``` - -## 模块说明 - -### VrCommon -- **用途**: 核心接口和数据结构定义 -- **主要文件**: VrCommon.h, VrConfigCmd.h, VrError.h -- **依赖**: 无 - -### VrUtils -- **用途**: 通用工具类库 -- **主要功能**: 日志、JSON、XML、INI、MD5、CRC、时间、网络、文件、字符串 -- **依赖**: VrCommon - -### CloudUtils -- **用途**: 点云数据处理工具 -- **主要功能**: 激光数据加载、点云图像转换 -- **依赖**: VrCommon, VrUtils - -### DataUtils -- **用途**: 数据处理工具集 -- **子模块**: CloudMathClac(曲线拟合)、CoordinateTransform(坐标转换) -- **依赖**: VrCommon - -### CloudView -- **用途**: 点云可视化查看工具(独立应用) -- **依赖**: VrCommon, VrUtils, CloudUtils - -## 故障排查 - -### 问题 1: 找不到头文件 - -**症状**: 编译时提示找不到 VrCommon.h 或 VrUtils 相关头文件 - -**解决方案**: -1. 检查 INCLUDEPATH 是否正确设置 -2. 确保相对路径正确(根据你的项目结构调整 `../` 的数量) -3. 确保 Utils 目录存在且包含所有必要文件 - -### 问题 2: 链接错误 - -**症状**: 编译通过但链接时提示找不到库文件 - -**解决方案**: -1. 确保先编译 Utils 模块 -2. 检查 LIBS 路径是否正确 -3. 确认 debug/release 配置匹配 - -### 问题 3: submodule 未初始化 - -**症状**: Utils 目录为空 - -**解决方案**: -```bash -git submodule update --init --recursive -``` - -## 版本管理建议 - -### Utils 仓库 -- 使用语义化版本号(如 v1.0.0) -- 为重要版本打 tag -- 维护 CHANGELOG.md - -### 主项目 -- 如果使用 submodule,锁定 Utils 到特定版本 -- 定期更新 Utils 到最新稳定版本 - -## 联系方式 - -如有问题或建议,请联系项目维护者。