Compare commits
201 Commits
version/20
...
version/20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9840302931 | ||
|
|
983047982f | ||
|
|
f977be5fe4 | ||
|
|
e21ad4b5c1 | ||
|
|
619055f544 | ||
|
|
c9611fc45b | ||
|
|
79f869a7f3 | ||
|
|
1b8dfb2bef | ||
|
|
143a47482b | ||
|
|
a28cf0f860 | ||
|
|
d9f4d7373f | ||
|
|
c3f48c7261 | ||
|
|
48b7b70e23 | ||
|
|
b93b362e2f | ||
|
|
2082b18e2e | ||
|
|
7f65e7f905 | ||
|
|
3417ca7e49 | ||
|
|
6334c30eb6 | ||
|
|
c87dff7e8f | ||
|
|
332f76f34d | ||
|
|
3387f3d084 | ||
|
|
bd421c381c | ||
|
|
0cce949837 | ||
|
|
ed3ba67925 | ||
|
|
866f85064a | ||
|
|
9215c530b3 | ||
|
|
a6437f4e96 | ||
|
|
5bd7be6ed1 | ||
|
|
d9cc3738b9 | ||
|
|
edec5bbc01 | ||
|
|
6a2d86c526 | ||
|
|
6c64e9b36c | ||
|
|
bcecee0f76 | ||
|
|
c5cdfa1a1d | ||
|
|
f9f2b4cbdb | ||
|
|
33feb9a416 | ||
|
|
9e1aaa8b56 | ||
|
|
ef2eb635af | ||
|
|
2db412a923 | ||
|
|
061fea48c8 | ||
|
|
c4ea62a899 | ||
|
|
789c09a402 | ||
|
|
de9b329115 | ||
|
|
1b793a127c | ||
|
|
3cb3084725 | ||
|
|
637f67888a | ||
|
|
fbc0986fd8 | ||
|
|
14ebe0b618 | ||
|
|
fd34cc30b8 | ||
|
|
7b0faed03a | ||
|
|
8d1dc30b07 | ||
|
|
03cff6abca | ||
|
|
d7821324b8 | ||
|
|
f3e066cce0 | ||
|
|
45ac758cc9 | ||
|
|
f0e6402fff | ||
|
|
41bf142e31 | ||
|
|
2e9efa98d7 | ||
|
|
2c2a57f368 | ||
|
|
4ea1326126 | ||
|
|
09b44ac68a | ||
|
|
a48ab434ab | ||
|
|
d39a56d4fb | ||
|
|
892579456d | ||
|
|
4dde1d365c | ||
|
|
2f21b582cd | ||
|
|
b29536f8b7 | ||
|
|
039f9920db | ||
|
|
7c254e9c04 | ||
|
|
35a115bfd4 | ||
|
|
70dd9d35b1 | ||
|
|
31e3cf06fb | ||
|
|
175eddd1fa | ||
|
|
fe73247b82 | ||
|
|
203db3d095 | ||
|
|
8c4695b991 | ||
|
|
a2b111bb09 | ||
|
|
2a1542d544 | ||
|
|
e5b51677c5 | ||
|
|
f9450d136d | ||
|
|
0586cb62c3 | ||
|
|
8fee04b32b | ||
|
|
04e16c95e2 | ||
|
|
e257dbe6ed | ||
|
|
abf78f8e31 | ||
|
|
da13bd9f04 | ||
|
|
86fb1ed00f | ||
|
|
56fb81dc03 | ||
|
|
343ce57468 | ||
|
|
3e52e37181 | ||
|
|
e768553a4a | ||
|
|
b74d1a8351 | ||
|
|
801d8c4af5 | ||
|
|
3a4693803b | ||
|
|
79f0d3356e | ||
|
|
6a1bf02ddb | ||
|
|
6662800deb | ||
|
|
c9bb6102c0 | ||
|
|
90479419cc | ||
|
|
b4178ae888 | ||
|
|
ab4814c916 | ||
|
|
6b16f96c8a | ||
|
|
e655d41817 | ||
|
|
b5c1902a2d | ||
|
|
d088259739 | ||
|
|
d8acf44a3a | ||
|
|
4664af12fa | ||
|
|
7a909d0c0b | ||
|
|
919c25769c | ||
|
|
5a0908d5bb | ||
|
|
1c39daec07 | ||
|
|
835ae941ce | ||
|
|
63edff078f | ||
|
|
0ea9786601 | ||
|
|
9e0bb33d58 | ||
|
|
64531c85e3 | ||
|
|
14cdae5102 | ||
|
|
2aaad212e8 | ||
|
|
bd88bf1126 | ||
|
|
ea9da65b7c | ||
|
|
e7f80cf5f3 | ||
|
|
16d62f93c8 | ||
|
|
e96834fcc6 | ||
|
|
22a74c63b4 | ||
|
|
5681fcbdc5 | ||
|
|
7755f8e094 | ||
|
|
e266e44f63 | ||
|
|
4dc66a385e | ||
|
|
aec29a3a37 | ||
|
|
0d213a1990 | ||
|
|
8162a49f6c | ||
|
|
d2e2603400 | ||
|
|
43a8277bdb | ||
|
|
61a8bd5cd3 | ||
|
|
ad6f3d2db2 | ||
|
|
9088f41352 | ||
|
|
73f57bbbd8 | ||
|
|
a8673356a2 | ||
|
|
5ecc1ab6f2 | ||
|
|
0702f85540 | ||
|
|
277dab0d55 | ||
|
|
321a3fdaba | ||
|
|
2eb17d0083 | ||
|
|
ff7e4597e7 | ||
|
|
4a4baf1b42 | ||
|
|
2672d5cd11 | ||
|
|
1bf3d7c9b1 | ||
|
|
f7c0a7f933 | ||
|
|
6bf864babb | ||
|
|
43bd1b15ee | ||
|
|
14c79d9ffb | ||
|
|
3009aadaa6 | ||
|
|
fca64343ae | ||
|
|
042a2659f6 | ||
|
|
fe54af405c | ||
|
|
ab70090a0a | ||
|
|
ee02750e95 | ||
|
|
f55007394e | ||
|
|
60a9e8fb7e | ||
|
|
7f8455f731 | ||
|
|
6ae86fc4ca | ||
|
|
e1fb13bed8 | ||
|
|
27fff3b72a | ||
|
|
6a9235223e | ||
|
|
c1e50e9a9c | ||
|
|
dcbf5b7c11 | ||
|
|
4b571f2a24 | ||
|
|
679b7b845c | ||
|
|
50d7127c51 | ||
|
|
0e09ee4bce | ||
|
|
76ebd569d5 | ||
|
|
edcd42bc2d | ||
|
|
f04e501472 | ||
|
|
32d152ba38 | ||
|
|
94c4c44d92 | ||
|
|
ab1d4e651e | ||
|
|
5cd250e452 | ||
|
|
2dcff4bb8e | ||
|
|
604a9ff614 | ||
|
|
a09630bcca | ||
|
|
b44c70b3f4 | ||
|
|
e4cddb100e | ||
|
|
e3a4144e6c | ||
|
|
51e7d95bf2 | ||
|
|
202571386b | ||
|
|
7837bd0e11 | ||
|
|
11c6e5bf04 | ||
|
|
8277857827 | ||
|
|
412111ba5a | ||
|
|
dd52b6af50 | ||
|
|
a97c145f56 | ||
|
|
b9deebb59d | ||
|
|
906813c90b | ||
|
|
1711592e64 | ||
|
|
7b2507cb19 | ||
|
|
2e19aaaff9 | ||
|
|
6854598b79 | ||
|
|
809ddb21c9 | ||
|
|
38bab59c1a | ||
|
|
87590cafb2 | ||
|
|
6e5cbd7fc5 |
184
CMakeLists.txt
184
CMakeLists.txt
@@ -1,4 +1,4 @@
|
||||
cmake_minimum_required (VERSION 2.8.11)
|
||||
cmake_minimum_required (VERSION 3.0)
|
||||
|
||||
if(COMMAND cmake_policy)
|
||||
if(POLICY CMP0054)
|
||||
@@ -9,6 +9,17 @@ if(COMMAND cmake_policy)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
message(STATUS "CMAKE Build type: ${CMAKE_BUILD_TYPE}")
|
||||
# Set a default build type if none was specified
|
||||
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||
message(STATUS "Setting build type to 'Debug' as none was specified.")
|
||||
set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE)
|
||||
# Set the possible values of build type for cmake-gui
|
||||
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release"
|
||||
"MinSizeRel" "RelWithDebInfo")
|
||||
endif()
|
||||
|
||||
include (CheckFunctionExists)
|
||||
include (CheckIncludeFile)
|
||||
include (CheckLibraryExists)
|
||||
@@ -16,28 +27,25 @@ include (CheckCXXSourceCompiles)
|
||||
include (CheckCXXCompilerFlag)
|
||||
include (GenerateExportHeader)
|
||||
|
||||
# using 10.7 because boost requires libc++ and 10.6 doesn't include it
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.7)
|
||||
|
||||
# only relevant for building shared libs but let's set it regardless
|
||||
set(CMAKE_OSX_RPATH 1)
|
||||
|
||||
# Set the C++ standard to C++98 to avoid compilation errors on GCC 6 (which
|
||||
# defaults to C++14).
|
||||
if(CMAKE_VERSION VERSION_LESS "3.1")
|
||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
set (CMAKE_CXX_FLAGS "-std=gnu++98 ${CMAKE_CXX_FLAGS}")
|
||||
endif()
|
||||
else()
|
||||
set (CMAKE_CXX_STANDARD 98)
|
||||
endif()
|
||||
|
||||
project(SimGear)
|
||||
# let's use & require C++11 - note these are only functional with CMake 3.1
|
||||
# we do manual fallbacks for CMake 3.0 in the compilers section
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED YES)
|
||||
|
||||
# read 'version' file into a variable (stripping any newlines or spaces)
|
||||
file(READ version versionFile)
|
||||
string(STRIP ${versionFile} SIMGEAR_VERSION)
|
||||
|
||||
project(SimGear VERSION ${SIMGEAR_VERSION} LANGUAGES C CXX)
|
||||
|
||||
# using 10.7 because boost requires libc++ and 10.6 doesn't include it
|
||||
# Cmake documentation says we must set this before calling project(), but
|
||||
# it only seems to be picked up setting it /after/ the call to project()
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.7")
|
||||
|
||||
# add a dependency on the versino file
|
||||
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS version)
|
||||
|
||||
@@ -76,8 +84,6 @@ set(CPACK_SOURCE_PACKAGE_FILE_NAME "simgear-${SIMGEAR_VERSION}" CACHE INTERNAL "
|
||||
set(CPACK_SOURCE_IGNORE_FILES
|
||||
"^${PROJECT_SOURCE_DIR}/.git;\\\\.gitignore;Makefile.am;~$;${CPACK_SOURCE_IGNORE_FILES}")
|
||||
|
||||
message(STATUS "ignoring: ${CPACK_SOURCE_IGNORE_FILES}")
|
||||
|
||||
include (CPack)
|
||||
|
||||
# We have some custom .cmake scripts not in the official distribution.
|
||||
@@ -100,21 +106,6 @@ message(STATUS "Library installation directory: ${CMAKE_INSTALL_LIBDIR}")
|
||||
# Configure library search paths
|
||||
#####################################################################################
|
||||
|
||||
if(NOT "${CMAKE_LIBRARY_ARCHITECTURE}" STREQUAL "")
|
||||
# Workaround for Ubuntu/Debian which introduced the "multiarch" library
|
||||
# directory structure, which is unsupported by CMake < 2.8.10, so we need to
|
||||
# add paths manually
|
||||
# see http://www.cmake.org/Bug/view.php?id=12049 and
|
||||
# http://www.cmake.org/Bug/view.php?id=12037
|
||||
list(APPEND ADDITIONAL_LIBRARY_PATHS
|
||||
/usr/local/lib/${CMAKE_LIBRARY_ARCHITECTURE}
|
||||
/usr/lib/${CMAKE_LIBRARY_ARCHITECTURE}
|
||||
/lib/${CMAKE_LIBRARY_ARCHITECTURE})
|
||||
message(STATUS "additional library directories: ${ADDITIONAL_LIBRARY_PATHS}")
|
||||
endif()
|
||||
|
||||
#####################################################################################
|
||||
|
||||
if (NOT MSVC)
|
||||
option(SIMGEAR_SHARED "Set to ON to build SimGear as a shared library/framework" OFF)
|
||||
option(SYSTEM_EXPAT "Set to ON to build SimGear using the system expat library" OFF)
|
||||
@@ -134,6 +125,9 @@ option(ENABLE_SOUND "Set to OFF to disable building SimGear's sound support"
|
||||
option(USE_AEONWAVE "Set to ON to use AeonWave instead of OpenAL" OFF)
|
||||
option(ENABLE_PKGUTIL "Set to ON to build the sg_pkgutil application (default)" ON)
|
||||
option(ENABLE_DNS "Set to ON to use udns library and DNS service resolver" ON)
|
||||
option(ENABLE_SIMD "Enable SSE/SSE2 support for x86 compilers" ON)
|
||||
|
||||
include (DetectArch)
|
||||
|
||||
# until the fstream fix is applied and generally available in OSG,
|
||||
# keep the compatability link option as the default
|
||||
@@ -163,10 +157,8 @@ if (MSVC AND MSVC_3RDPARTY_ROOT)
|
||||
set( OSG_MSVC ${OSG_MSVC}140 )
|
||||
elseif (${MSVC_VERSION} EQUAL 1800)
|
||||
set( OSG_MSVC ${OSG_MSVC}120 )
|
||||
elseif (${MSVC_VERSION} EQUAL 1700)
|
||||
set( OSG_MSVC ${OSG_MSVC}110 )
|
||||
elseif (${MSVC_VERSION} EQUAL 1600)
|
||||
set( OSG_MSVC ${OSG_MSVC}100 )
|
||||
else ()
|
||||
message(FATAL_ERROR "Visual Studio 2013/2015 is required now")
|
||||
endif ()
|
||||
if (CMAKE_CL_64)
|
||||
set( OSG_MSVC ${OSG_MSVC}-64 )
|
||||
@@ -178,24 +170,15 @@ if (MSVC AND MSVC_3RDPARTY_ROOT)
|
||||
set (CMAKE_LIBRARY_PATH ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/lib ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenScenegraph/lib ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenRTI/lib )
|
||||
set (CMAKE_INCLUDE_PATH ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/include ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenScenegraph/include ${MSVC_3RDPARTY_ROOT}/install/${OSG_MSVC}/OpenRTI/include)
|
||||
|
||||
GET_FILENAME_COMPONENT(MSVC_ROOT_PARENT_DIR ${MSVC_3RDPARTY_ROOT} PATH)
|
||||
find_path(BOOST_ROOT boost/version.hpp
|
||||
PATHS
|
||||
${MSVC_ROOT_PARENT_DIR}
|
||||
${MSVC_3RDPARTY_ROOT}/boost
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_52_0
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_51_0
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_50_0
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_49_0
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_48_0
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_47_0
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_46_1
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_46_0
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_45_0
|
||||
${MSVC_3RDPARTY_ROOT}/boost_1_44_0
|
||||
)
|
||||
# set (BOOST_ROOT ${MSVC_3RDPARTY_ROOT}/boost_1_44_0)
|
||||
message(STATUS "BOOST_ROOT is ${BOOST_ROOT}")
|
||||
if(NOT BOOST_INCLUDEDIR)
|
||||
# if this variable was not set by the user, set it to 3rdparty root's
|
||||
# parent dir, which is the normal location for people using our
|
||||
# windows-3rd-party repo
|
||||
GET_FILENAME_COMPONENT(MSVC_ROOT_PARENT_DIR ${MSVC_3RDPARTY_ROOT} PATH)
|
||||
set(BOOST_INCLUDEDIR ${MSVC_ROOT_PARENT_DIR})
|
||||
message(STATUS "BOOST_INCLUDEDIR is ${BOOST_INCLUDEDIR}")
|
||||
endif()
|
||||
|
||||
if (NOT USE_AEONWAVE)
|
||||
set (OPENAL_INCLUDE_DIR ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/include)
|
||||
set (OPENAL_LIBRARY_DIR ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/lib)
|
||||
@@ -205,15 +188,9 @@ endif (MSVC AND MSVC_3RDPARTY_ROOT)
|
||||
|
||||
if(APPLE)
|
||||
find_library(COCOA_LIBRARY Cocoa)
|
||||
|
||||
# this should be handled by setting CMAKE_OSX_DEPLOYMENT_TARGET
|
||||
# but it's not working reliably, so forcing it for now
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.7")
|
||||
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=10.7")
|
||||
endif()
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR
|
||||
${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
|
||||
find_package(Threads REQUIRED)
|
||||
endif()
|
||||
|
||||
@@ -231,17 +208,14 @@ else()
|
||||
if (ENABLE_SOUND)
|
||||
if (USE_AEONWAVE)
|
||||
find_package(AAX COMPONENTS aax REQUIRED)
|
||||
include_directories( ${AAX_INCLUDE_DIR} )
|
||||
else()
|
||||
find_package(OpenAL REQUIRED)
|
||||
include_directories( ${OPENAL_INCLUDE_DIR} )
|
||||
endif()
|
||||
|
||||
message(STATUS "Sound support: ENABLED")
|
||||
endif(ENABLE_SOUND)
|
||||
|
||||
find_package(OpenSceneGraph 3.2.0 REQUIRED osgText osgSim osgDB osgParticle osgGA osgViewer osgUtil)
|
||||
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS})
|
||||
|
||||
if (MSVC)
|
||||
set(CMAKE_REQUIRED_INCLUDES ${OPENSCENEGRAPH_INCLUDE_DIRS})
|
||||
@@ -254,12 +228,12 @@ else()
|
||||
int main() { return 0; }"
|
||||
SIMGEAR_OSG_USE_UTF8_FILENAME)
|
||||
if (NOT SIMGEAR_OSG_USE_UTF8_FILENAME)
|
||||
message(WARNING "Please rebuild OSG with OSG_USE_UTF8_FILENAME set to ON")
|
||||
message(FATAL_ERROR "Please rebuild OSG with OSG_USE_UTF8_FILENAME set to ON")
|
||||
endif()
|
||||
endif()
|
||||
endif(SIMGEAR_HEADLESS)
|
||||
|
||||
find_package(ZLIB REQUIRED)
|
||||
find_package(ZLIB 1.2.4 REQUIRED)
|
||||
find_package(CURL REQUIRED)
|
||||
|
||||
if (SYSTEM_EXPAT)
|
||||
@@ -277,11 +251,8 @@ else()
|
||||
${PROJECT_BINARY_DIR}/3rdparty/expat)
|
||||
endif(SYSTEM_EXPAT)
|
||||
|
||||
include_directories(${EXPAT_INCLUDE_DIRS})
|
||||
|
||||
check_include_file(inttypes.h HAVE_INTTYPES_H)
|
||||
check_include_file(sys/time.h HAVE_SYS_TIME_H)
|
||||
check_include_file(sys/timeb.h HAVE_SYS_TIMEB_H)
|
||||
check_include_file(unistd.h HAVE_UNISTD_H)
|
||||
check_include_file(windows.h HAVE_WINDOWS_H)
|
||||
|
||||
@@ -299,13 +270,18 @@ else()
|
||||
endif(ENABLE_RTI)
|
||||
|
||||
check_function_exists(gettimeofday HAVE_GETTIMEOFDAY)
|
||||
check_function_exists(ftime HAVE_FTIME)
|
||||
check_function_exists(timegm HAVE_TIMEGM)
|
||||
check_function_exists(rint HAVE_RINT)
|
||||
check_function_exists(mkdtemp HAVE_MKDTEMP)
|
||||
check_function_exists(bcopy HAVE_BCOPY)
|
||||
check_function_exists(mmap HAVE_MMAP)
|
||||
|
||||
if (NOT MSVC)
|
||||
check_function_exists(timegm HAVE_TIMEGM)
|
||||
if (NOT HAVE_TIMEGM)
|
||||
message(FATAL_ERROR "Non-Windows platforms must support timegm()")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(HAVE_UNISTD_H)
|
||||
set(CMAKE_REQUIRED_INCLUDES ${CMAKE_INCLUDE_PATH})
|
||||
check_cxx_source_compiles(
|
||||
@@ -350,19 +326,33 @@ SET(CMAKE_MINSIZEREL_POSTFIX "" CACHE STRING "add a postfix, usually empty on wi
|
||||
|
||||
# isnan might not be real symbol, so can't check using function_exists
|
||||
check_cxx_source_compiles(
|
||||
"#include <cmath>
|
||||
int main() { return isnan(0.0);} "
|
||||
HAVE_ISNAN)
|
||||
|
||||
check_cxx_source_compiles(
|
||||
"#include <cmath>
|
||||
"#include <cstdlib>
|
||||
int main() { return std::isnan(0.0);} "
|
||||
HAVE_STD_ISNAN)
|
||||
|
||||
if (NOT ${HAVE_STD_ISNAN})
|
||||
message(FATAL_ERROR "Your compiler lacks C++11 std::isnan, please update it")
|
||||
endif()
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
set(WARNING_FLAGS_CXX "-Wall")
|
||||
set(WARNING_FLAGS_C "-Wall")
|
||||
|
||||
if (CMAKE_VERSION VERSION_LESS 3.1)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.4)
|
||||
message(WARNING "GCC 4.4 will be required soon, please upgrade")
|
||||
endif()
|
||||
|
||||
if(ENABLE_SIMD)
|
||||
if (X86 OR X86_64)
|
||||
set(CMAKE_C_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# certain GCC versions don't provide the atomic builds, and hence
|
||||
# require is to provide them in SGAtomic.cxx
|
||||
set(CMAKE_REQUIRED_INCLUDES ${CMAKE_INCLUDE_PATH})
|
||||
@@ -371,13 +361,24 @@ if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
GCC_ATOMIC_BUILTINS_FOUND)
|
||||
endif(CMAKE_COMPILER_IS_GNUCXX)
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
if (CLANG)
|
||||
# Boost redeclares class members
|
||||
set(WARNING_FLAGS_CXX "-Wall -Wno-overloaded-virtual -Wno-redeclared-class-member")
|
||||
set(WARNING_FLAGS_C "-Wall")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++11")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -stdlib=libc++")
|
||||
# fix Boost compilation :(
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
|
||||
|
||||
if (CMAKE_VERSION VERSION_LESS 3.1)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
endif()
|
||||
|
||||
if(ENABLE_SIMD)
|
||||
if (X86 OR X86_64)
|
||||
set(CMAKE_C_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
@@ -396,8 +397,17 @@ if(WIN32)
|
||||
|
||||
if(MSVC)
|
||||
set(MSVC_FLAGS "-DWIN32 -DNOMINMAX -D_USE_MATH_DEFINES -D_CRT_SECURE_NO_WARNINGS -D__CRT_NONSTDC_NO_WARNINGS /MP")
|
||||
if(ENABLE_SIMD)
|
||||
if (X86)
|
||||
SET(CMAKE_C_FLAGS_RELEASE "/O2 /arch:SSE /arch:SSE2")
|
||||
SET(CMAKE_CXX_FLAGS_RELEASE "/O2 /arch:SSE /arch:SSE2")
|
||||
else()
|
||||
SET(CMAKE_C_FLAGS_RELEASE "/O2")
|
||||
SET(CMAKE_CXX_FLAGS_RELEASE "/O2")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (NOT OSG_FSTREAM_EXPORT_FIXED AND ${MSVC_VERSION} GREATER 1599)
|
||||
if (NOT OSG_FSTREAM_EXPORT_FIXED)
|
||||
message(STATUS "For better linking performance, use OSG with patched fstream header")
|
||||
# needed to avoid link errors on multiply-defined standard C++
|
||||
# symbols. Suspect this may be an OSG-DB export bug
|
||||
@@ -424,16 +434,8 @@ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${MSVC_LD_FLAGS}")
|
||||
|
||||
# use BEFORE to ensure local directories are used first,
|
||||
# ahead of system-installed libs
|
||||
include_directories(BEFORE ${PROJECT_SOURCE_DIR})
|
||||
include_directories(BEFORE ${PROJECT_SOURCE_DIR}/simgear/canvas/ShivaVG/include)
|
||||
include_directories(BEFORE ${PROJECT_BINARY_DIR}/simgear)
|
||||
|
||||
include_directories(
|
||||
${Boost_INCLUDE_DIRS}
|
||||
${ZLIB_INCLUDE_DIR}
|
||||
${CURL_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
add_definitions(-DHAVE_CONFIG_H)
|
||||
|
||||
# configure a header file to pass some of the CMake settings
|
||||
@@ -471,8 +473,6 @@ endif()
|
||||
|
||||
install (FILES ${PROJECT_BINARY_DIR}/simgear/simgear_config.h DESTINATION include/simgear/)
|
||||
|
||||
include_directories(3rdparty/utf8/source)
|
||||
|
||||
if(ENABLE_DNS)
|
||||
if(SYSTEM_UDNS)
|
||||
message(STATUS "Requested to use system udns library, forcing SIMGEAR_SHARED to true")
|
||||
@@ -512,7 +512,7 @@ configure_file(SimGearConfig.cmake.in
|
||||
@ONLY
|
||||
)
|
||||
|
||||
set(ConfigPackageLocation lib/cmake/SimGear)
|
||||
set(ConfigPackageLocation ${CMAKE_INSTALL_LIBDIR}/cmake/SimGear)
|
||||
install(EXPORT SimGearTargets
|
||||
DESTINATION ${ConfigPackageLocation}
|
||||
)
|
||||
|
||||
@@ -157,7 +157,7 @@ function(add_boost_test _name)
|
||||
endforeach()
|
||||
|
||||
if(NOT _boostTestTargetsNagged${_name} STREQUAL "${includeType}")
|
||||
if("includeType" STREQUAL "CONFIGURED")
|
||||
if("${includeType}" STREQUAL "CONFIGURED")
|
||||
message(STATUS
|
||||
"Test '${_name}' uses the CMake-configurable form of the boost test framework - congrats! (Including File: ${includeFileLoc})")
|
||||
elseif("${includeType}" STREQUAL "INCLUDED")
|
||||
|
||||
37
CMakeModules/DetectArch.cmake
Normal file
37
CMakeModules/DetectArch.cmake
Normal file
@@ -0,0 +1,37 @@
|
||||
IF(CMAKE_SYSTEM_PROCESSOR MATCHES amd64.*|x86_64.* OR CMAKE_GENERATOR MATCHES "Visual Studio.*Win64")
|
||||
IF(CMAKE_C_FLAGS MATCHES -m32 OR CMAKE_CXX_FLAGS MATCHES -m32)
|
||||
SET(X86 1)
|
||||
ELSE(CMAKE_C_FLAGS MATCHES -m32 OR CMAKE_CXX_FLAGS MATCHES -m32)
|
||||
SET(X86_64 1)
|
||||
ENDIF(CMAKE_C_FLAGS MATCHES -m32 OR CMAKE_CXX_FLAGS MATCHES -m32)
|
||||
ELSEIF(CMAKE_SYSTEM_PROCESSOR MATCHES i686.*|i386.*|x86.* OR WIN32)
|
||||
IF(CMAKE_C_FLAGS MATCHES -m64 OR CMAKE_CXX_FLAGS MATCHES -m64)
|
||||
SET(X86_64 1)
|
||||
ELSE(CMAKE_C_FLAGS MATCHES -m64 OR CMAKE_CXX_FLAGS MATCHES -m64)
|
||||
SET(X86 1)
|
||||
ENDIF(CMAKE_C_FLAGS MATCHES -m64 OR CMAKE_CXX_FLAGS MATCHES -m64)
|
||||
ELSEIF(CMAKE_SYSTEM_PROCESSOR MATCHES arm.* AND CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
SET(ARM 1)
|
||||
ELSEIF(CMAKE_SYSTEM_PROCESSOR MATCHES mips)
|
||||
SET(MIPS 1)
|
||||
ENDIF()
|
||||
|
||||
IF ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang")
|
||||
# using Clang
|
||||
SET(CLANG 1)
|
||||
ELSEIF ("${CMAKE_C_COMPILER_ID}" STREQUAL "TinyCC")
|
||||
# using TinyCC
|
||||
SET(TINYCC 1)
|
||||
ELSEIF ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
|
||||
# using GCC
|
||||
SET(GCC 1)
|
||||
ELSEIF ("${CMAKE_C_COMPILER_ID}" STREQUAL "Intel")
|
||||
# using Intel C++
|
||||
SET(INTELCC 1)
|
||||
ELSEIF ("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")
|
||||
# using Visual Studio C++
|
||||
SET(MSVC 1)
|
||||
ELSEIF ("${CMAKE_C_COMPILER_ID}" STREQUAL "MIPSpro")
|
||||
# using SGI MIPSpro
|
||||
SET(MIPSPRO 1)
|
||||
ENDIF()
|
||||
@@ -13,4 +13,8 @@ set(SIMGEAR_SOUND @ENABLE_SOUND@)
|
||||
# find_dependency(OpenAL)
|
||||
#endif()
|
||||
|
||||
# SSE/SSE2 support
|
||||
|
||||
set(ENABLE_SIMD @ENABLE_SIMD@)
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/SimGearTargets.cmake")
|
||||
|
||||
@@ -54,24 +54,14 @@ if(SIMGEAR_SHARED)
|
||||
set_property(TARGET SimGearCore PROPERTY LINKER_LANGUAGE CXX)
|
||||
set_property(TARGET SimGearCore PROPERTY VERSION ${SIMGEAR_VERSION})
|
||||
set_property(TARGET SimGearCore PROPERTY SOVERSION ${SIMGEAR_SOVERSION})
|
||||
install(TARGETS SimGearCore
|
||||
EXPORT SimGearTargets
|
||||
LIBRARY DESTINATION
|
||||
${CMAKE_INSTALL_LIBDIR})
|
||||
|
||||
|
||||
if(NOT SIMGEAR_HEADLESS)
|
||||
add_library(SimGearScene SHARED ${sceneSources})
|
||||
set_property(TARGET SimGearScene PROPERTY LINKER_LANGUAGE CXX)
|
||||
set_property(TARGET SimGearScene PROPERTY VERSION ${SIMGEAR_VERSION})
|
||||
set_property(TARGET SimGearScene PROPERTY SOVERSION ${SIMGEAR_SOVERSION})
|
||||
|
||||
# EXPORT SimGearSceneConfig
|
||||
install(TARGETS SimGearScene
|
||||
EXPORT SimGearTargets
|
||||
LIBRARY
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR} )
|
||||
endif()
|
||||
|
||||
else()
|
||||
message(STATUS "Library building mode: STATIC LIBRARIES")
|
||||
|
||||
@@ -94,9 +84,6 @@ else()
|
||||
endforeach()
|
||||
|
||||
add_library(SimGearCore STATIC ${coreSources} ${localExpatSources})
|
||||
install(TARGETS SimGearCore
|
||||
EXPORT SimGearTargets
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
|
||||
if(NOT SIMGEAR_HEADLESS)
|
||||
get_property(FG_GROUPS_SCENE_SOURCES_C GLOBAL PROPERTY FG_GROUPS_SCENE_SOURCES_C)
|
||||
@@ -118,19 +105,51 @@ else()
|
||||
endforeach()
|
||||
|
||||
add_library(SimGearScene STATIC ${sceneSources})
|
||||
install(TARGETS SimGearScene
|
||||
EXPORT SimGearTargets
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
endif(NOT SIMGEAR_HEADLESS)
|
||||
endif(SIMGEAR_SHARED)
|
||||
|
||||
target_include_directories(SimGearCore BEFORE PUBLIC
|
||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
|
||||
$<INSTALL_INTERFACE:include>)
|
||||
|
||||
target_include_directories(SimGearCore PUBLIC
|
||||
${Boost_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIR})
|
||||
target_include_directories(SimGearCore PRIVATE
|
||||
${EXPAT_INCLUDE_DIRS} ${CURL_INCLUDE_DIRS})
|
||||
|
||||
install(TARGETS SimGearCore
|
||||
EXPORT SimGearTargets
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
|
||||
if (NOT SIMGEAR_HEADLESS)
|
||||
install(TARGETS SimGearScene
|
||||
EXPORT SimGearTargets
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
|
||||
target_include_directories(SimGearScene BEFORE PUBLIC
|
||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
|
||||
$<INSTALL_INTERFACE:include>)
|
||||
|
||||
target_include_directories(SimGearScene PUBLIC ${OPENSCENEGRAPH_INCLUDE_DIRS})
|
||||
|
||||
if (USE_AEONWAVE)
|
||||
target_include_directories(SimGearScene PRIVATE ${AAX_INCLUDE_DIR} )
|
||||
else()
|
||||
target_include_directories(SimGearScene PRIVATE ${OPENAL_INCLUDE_DIR} )
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
target_link_libraries(SimGearCore
|
||||
${ZLIB_LIBRARY}
|
||||
${RT_LIBRARY}
|
||||
${DL_LIBRARY}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${COCOA_LIBRARY}
|
||||
${CURL_LIBRARIES})
|
||||
${CURL_LIBRARIES}
|
||||
${WINSOCK_LIBRARY})
|
||||
|
||||
if(SYSTEM_EXPAT)
|
||||
target_link_libraries(SimGearCore
|
||||
@@ -143,6 +162,8 @@ if(ENABLE_DNS AND SYSTEM_UDNS)
|
||||
endif()
|
||||
|
||||
if(NOT SIMGEAR_HEADLESS)
|
||||
target_include_directories(SimGearScene PRIVATE ${PROJECT_SOURCE_DIR}/simgear/canvas/ShivaVG/include)
|
||||
|
||||
target_link_libraries(SimGearScene
|
||||
SimGearCore
|
||||
${ZLIB_LIBRARY}
|
||||
@@ -150,6 +171,9 @@ if(NOT SIMGEAR_HEADLESS)
|
||||
${OPENAL_LIBRARY}
|
||||
${OPENGL_LIBRARY}
|
||||
${JPEG_LIBRARY})
|
||||
|
||||
# only actually needed by canvas/KeyboardEvent.cxx
|
||||
target_include_directories(SimGearScene PRIVATE ${PROJECT_SOURCE_DIR}/3rdparty/utf8/source)
|
||||
endif()
|
||||
|
||||
if(ENABLE_RTI)
|
||||
|
||||
@@ -35,123 +35,124 @@ using std::endl;
|
||||
|
||||
void testBucketSpans()
|
||||
{
|
||||
COMPARE(sg_bucket_span(0.0), 0.125);
|
||||
COMPARE(sg_bucket_span(-20), 0.125);
|
||||
COMPARE(sg_bucket_span(-40), 0.25);
|
||||
COMPARE(sg_bucket_span(89.9), 12.0);
|
||||
COMPARE(sg_bucket_span(88.1), 4.0);
|
||||
COMPARE(sg_bucket_span(-89.9), 12.0);
|
||||
SG_CHECK_EQUAL(sg_bucket_span(0.0), 0.125);
|
||||
SG_CHECK_EQUAL(sg_bucket_span(-20), 0.125);
|
||||
SG_CHECK_EQUAL(sg_bucket_span(-40), 0.25);
|
||||
SG_CHECK_EQUAL(sg_bucket_span(89.9), 12.0);
|
||||
SG_CHECK_EQUAL(sg_bucket_span(88.1), 4.0);
|
||||
SG_CHECK_EQUAL(sg_bucket_span(-89.9), 12.0);
|
||||
}
|
||||
|
||||
void testBasic()
|
||||
{
|
||||
SGBucket b1(5.1, 55.05);
|
||||
COMPARE(b1.get_chunk_lon(), 5);
|
||||
COMPARE(b1.get_chunk_lat(), 55);
|
||||
COMPARE(b1.get_x(), 0);
|
||||
COMPARE(b1.get_y(), 0);
|
||||
COMPARE(b1.gen_index(), 3040320);
|
||||
COMPARE(b1.gen_base_path(), "e000n50/e005n55");
|
||||
VERIFY(b1.isValid());
|
||||
SG_CHECK_EQUAL(b1.get_chunk_lon(), 5);
|
||||
SG_CHECK_EQUAL(b1.get_chunk_lat(), 55);
|
||||
SG_CHECK_EQUAL(b1.get_x(), 0);
|
||||
SG_CHECK_EQUAL(b1.get_y(), 0);
|
||||
SG_CHECK_EQUAL(b1.gen_index(), 3040320);
|
||||
SG_CHECK_EQUAL(b1.gen_base_path(), "e000n50/e005n55");
|
||||
SG_VERIFY(b1.isValid());
|
||||
|
||||
SGBucket b2(-10.1, -43.8);
|
||||
COMPARE(b2.get_chunk_lon(), -11);
|
||||
COMPARE(b2.get_chunk_lat(), -44);
|
||||
COMPARE(b2.get_x(), 3);
|
||||
COMPARE(b2.get_y(), 1); // latitude chunks numbered bottom to top, it seems
|
||||
COMPARE(b2.gen_base_path(), "w020s50/w011s44");
|
||||
VERIFY(b2.isValid());
|
||||
SG_CHECK_EQUAL(b2.get_chunk_lon(), -11);
|
||||
SG_CHECK_EQUAL(b2.get_chunk_lat(), -44);
|
||||
SG_CHECK_EQUAL(b2.get_x(), 3);
|
||||
// Latitude chunks numbered bottom to top, it seems
|
||||
SG_CHECK_EQUAL(b2.get_y(), 1);
|
||||
SG_CHECK_EQUAL(b2.gen_base_path(), "w020s50/w011s44");
|
||||
SG_VERIFY(b2.isValid());
|
||||
|
||||
SGBucket b3(123.48, 9.01);
|
||||
COMPARE(b3.get_chunk_lon(), 123);
|
||||
COMPARE(b3.get_chunk_lat(), 9);
|
||||
COMPARE(b3.get_x(), 3);
|
||||
COMPARE(b3.get_y(), 0);
|
||||
COMPARE(b3.gen_base_path(), "e120n00/e123n09");
|
||||
VERIFY(b3.isValid());
|
||||
SG_CHECK_EQUAL(b3.get_chunk_lon(), 123);
|
||||
SG_CHECK_EQUAL(b3.get_chunk_lat(), 9);
|
||||
SG_CHECK_EQUAL(b3.get_x(), 3);
|
||||
SG_CHECK_EQUAL(b3.get_y(), 0);
|
||||
SG_CHECK_EQUAL(b3.gen_base_path(), "e120n00/e123n09");
|
||||
SG_VERIFY(b3.isValid());
|
||||
|
||||
SGBucket defBuck;
|
||||
VERIFY(!defBuck.isValid());
|
||||
SG_VERIFY(!defBuck.isValid());
|
||||
|
||||
b3.make_bad();
|
||||
VERIFY(!b3.isValid());
|
||||
SG_VERIFY(!b3.isValid());
|
||||
|
||||
SGBucket atAntiMeridian(180.0, 12.3);
|
||||
VERIFY(atAntiMeridian.isValid());
|
||||
COMPARE(atAntiMeridian.get_chunk_lon(), -180);
|
||||
COMPARE(atAntiMeridian.get_x(), 0);
|
||||
|
||||
SG_VERIFY(atAntiMeridian.isValid());
|
||||
SG_CHECK_EQUAL(atAntiMeridian.get_chunk_lon(), -180);
|
||||
SG_CHECK_EQUAL(atAntiMeridian.get_x(), 0);
|
||||
|
||||
SGBucket atAntiMeridian2(-180.0, -78.1);
|
||||
VERIFY(atAntiMeridian2.isValid());
|
||||
COMPARE(atAntiMeridian2.get_chunk_lon(), -180);
|
||||
COMPARE(atAntiMeridian2.get_x(), 0);
|
||||
|
||||
SG_VERIFY(atAntiMeridian2.isValid());
|
||||
SG_CHECK_EQUAL(atAntiMeridian2.get_chunk_lon(), -180);
|
||||
SG_CHECK_EQUAL(atAntiMeridian2.get_x(), 0);
|
||||
|
||||
// check comparisom operator overload
|
||||
SGBucket b4(5.11, 55.1);
|
||||
VERIFY(b1 == b4); // should be equal
|
||||
VERIFY(b1 == b1);
|
||||
VERIFY(b1 != defBuck);
|
||||
VERIFY(b1 != b2);
|
||||
SG_VERIFY(b1 == b4); // should be equal
|
||||
SG_VERIFY(b1 == b1);
|
||||
SG_VERIFY(b1 != defBuck);
|
||||
SG_VERIFY(b1 != b2);
|
||||
|
||||
// check wrapping/clipping of inputs
|
||||
SGBucket wrapMeridian(-200.0, 45.0);
|
||||
COMPARE(wrapMeridian.get_chunk_lon(), 160);
|
||||
|
||||
SG_CHECK_EQUAL(wrapMeridian.get_chunk_lon(), 160);
|
||||
|
||||
SGBucket clipPole(48.9, 91);
|
||||
COMPARE(clipPole.get_chunk_lat(), 89);
|
||||
SG_CHECK_EQUAL(clipPole.get_chunk_lat(), 89);
|
||||
}
|
||||
|
||||
void testPolar()
|
||||
{
|
||||
SGBucket b1(0.0, 89.92);
|
||||
SGBucket b2(10.0, 89.96);
|
||||
COMPARE(b1.get_chunk_lat(), 89);
|
||||
COMPARE(b1.get_chunk_lon(), 0);
|
||||
COMPARE(b1.get_x(), 0);
|
||||
COMPARE(b1.get_y(), 7);
|
||||
SG_CHECK_EQUAL(b1.get_chunk_lat(), 89);
|
||||
SG_CHECK_EQUAL(b1.get_chunk_lon(), 0);
|
||||
SG_CHECK_EQUAL(b1.get_x(), 0);
|
||||
SG_CHECK_EQUAL(b1.get_y(), 7);
|
||||
|
||||
COMPARE_EP(b1.get_highest_lat(), 90.0);
|
||||
COMPARE_EP(b1.get_width_m(), 10.0);
|
||||
SG_CHECK_EQUAL_EP(b1.get_highest_lat(), 90.0);
|
||||
SG_CHECK_EQUAL_EP(b1.get_width_m(), 10.0);
|
||||
|
||||
COMPARE(b2.get_chunk_lat(), 89);
|
||||
COMPARE(b2.get_chunk_lon(), 0);
|
||||
COMPARE(b2.get_x(), 0);
|
||||
COMPARE(b2.get_y(), 7);
|
||||
SG_CHECK_EQUAL(b2.get_chunk_lat(), 89);
|
||||
SG_CHECK_EQUAL(b2.get_chunk_lon(), 0);
|
||||
SG_CHECK_EQUAL(b2.get_x(), 0);
|
||||
SG_CHECK_EQUAL(b2.get_y(), 7);
|
||||
|
||||
COMPARE(b1.gen_index(), b2.gen_index());
|
||||
SG_CHECK_EQUAL(b1.gen_index(), b2.gen_index());
|
||||
|
||||
SGGeod actualNorthPole1 = b1.get_corner(2);
|
||||
SGGeod actualNorthPole2 = b1.get_corner(3);
|
||||
COMPARE_EP(actualNorthPole1.getLatitudeDeg(), 90.0);
|
||||
COMPARE_EP(actualNorthPole1.getLongitudeDeg(), 12.0);
|
||||
COMPARE_EP(actualNorthPole2.getLatitudeDeg(), 90.0);
|
||||
COMPARE_EP(actualNorthPole2.getLongitudeDeg(), 0.0);
|
||||
SG_CHECK_EQUAL_EP(actualNorthPole1.getLatitudeDeg(), 90.0);
|
||||
SG_CHECK_EQUAL_EP(actualNorthPole1.getLongitudeDeg(), 12.0);
|
||||
SG_CHECK_EQUAL_EP(actualNorthPole2.getLatitudeDeg(), 90.0);
|
||||
SG_CHECK_EQUAL_EP(actualNorthPole2.getLongitudeDeg(), 0.0);
|
||||
|
||||
SGBucket b3(-2, 89.88);
|
||||
SGBucket b4(-7, 89.88);
|
||||
COMPARE(b3.gen_index(), b4.gen_index());
|
||||
SG_CHECK_EQUAL(b3.gen_index(), b4.gen_index());
|
||||
|
||||
// south pole
|
||||
SGBucket b5(-170, -89.88);
|
||||
SGBucket b6(-179, -89.88);
|
||||
|
||||
COMPARE(b5.get_chunk_lat(), -90);
|
||||
COMPARE(b5.get_chunk_lon(), -180);
|
||||
COMPARE(b5.get_x(), 0);
|
||||
COMPARE(b5.get_y(), 0);
|
||||
COMPARE(b5.gen_index(), b6.gen_index());
|
||||
COMPARE_EP(b5.get_highest_lat(), -90.0);
|
||||
COMPARE_EP(b5.get_width_m(), 10.0);
|
||||
SG_CHECK_EQUAL(b5.get_chunk_lat(), -90);
|
||||
SG_CHECK_EQUAL(b5.get_chunk_lon(), -180);
|
||||
SG_CHECK_EQUAL(b5.get_x(), 0);
|
||||
SG_CHECK_EQUAL(b5.get_y(), 0);
|
||||
SG_CHECK_EQUAL(b5.gen_index(), b6.gen_index());
|
||||
SG_CHECK_EQUAL_EP(b5.get_highest_lat(), -90.0);
|
||||
SG_CHECK_EQUAL_EP(b5.get_width_m(), 10.0);
|
||||
|
||||
SGGeod actualSouthPole1 = b5.get_corner(0);
|
||||
SGGeod actualSouthPole2 = b5.get_corner(1);
|
||||
COMPARE_EP(actualSouthPole1.getLatitudeDeg(), -90.0);
|
||||
COMPARE_EP(actualSouthPole1.getLongitudeDeg(), -180);
|
||||
COMPARE_EP(actualSouthPole2.getLatitudeDeg(), -90.0);
|
||||
COMPARE_EP(actualSouthPole2.getLongitudeDeg(), -168);
|
||||
SG_CHECK_EQUAL_EP(actualSouthPole1.getLatitudeDeg(), -90.0);
|
||||
SG_CHECK_EQUAL_EP(actualSouthPole1.getLongitudeDeg(), -180);
|
||||
SG_CHECK_EQUAL_EP(actualSouthPole2.getLatitudeDeg(), -90.0);
|
||||
SG_CHECK_EQUAL_EP(actualSouthPole2.getLongitudeDeg(), -168);
|
||||
|
||||
SGBucket b7(200, 89.88);
|
||||
COMPARE(b7.get_chunk_lon(), -168);
|
||||
SG_CHECK_EQUAL(b7.get_chunk_lon(), -168);
|
||||
|
||||
}
|
||||
|
||||
@@ -160,15 +161,15 @@ void testNearPolar()
|
||||
{
|
||||
SGBucket b1(1, 88.5);
|
||||
SGBucket b2(-1, 88.8);
|
||||
COMPARE(b1.get_chunk_lon(), 0);
|
||||
COMPARE(b1.get_chunk_lat(), 88);
|
||||
VERIFY(b1.gen_index() != b2.gen_index());
|
||||
SG_CHECK_EQUAL(b1.get_chunk_lon(), 0);
|
||||
SG_CHECK_EQUAL(b1.get_chunk_lat(), 88);
|
||||
SG_VERIFY(b1.gen_index() != b2.gen_index());
|
||||
|
||||
SGBucket b3(176.1, 88.5);
|
||||
COMPARE(b3.get_chunk_lon(), 176);
|
||||
SG_CHECK_EQUAL(b3.get_chunk_lon(), 176);
|
||||
|
||||
SGBucket b4(-178, 88.5);
|
||||
COMPARE(b4.get_chunk_lon(), -180);
|
||||
SG_CHECK_EQUAL(b4.get_chunk_lon(), -180);
|
||||
}
|
||||
|
||||
void testOffset()
|
||||
@@ -176,74 +177,74 @@ void testOffset()
|
||||
// bucket just below the 22 degree cutoff, so the next tile north
|
||||
// is twice the width
|
||||
SGBucket b1(-59.8, 21.9);
|
||||
COMPARE(b1.get_chunk_lat(), 21);
|
||||
COMPARE(b1.get_chunk_lon(), -60);
|
||||
COMPARE(b1.get_x(), 1);
|
||||
COMPARE(b1.get_y(), 7);
|
||||
SG_CHECK_EQUAL(b1.get_chunk_lat(), 21);
|
||||
SG_CHECK_EQUAL(b1.get_chunk_lon(), -60);
|
||||
SG_CHECK_EQUAL(b1.get_x(), 1);
|
||||
SG_CHECK_EQUAL(b1.get_y(), 7);
|
||||
|
||||
// offset vertically
|
||||
SGBucket b2(b1.sibling(0, 1));
|
||||
COMPARE(b2.get_chunk_lat(), 22);
|
||||
COMPARE(b2.get_chunk_lon(), -60);
|
||||
COMPARE(b2.get_x(), 0);
|
||||
COMPARE(b2.get_y(), 0);
|
||||
SG_CHECK_EQUAL(b2.get_chunk_lat(), 22);
|
||||
SG_CHECK_EQUAL(b2.get_chunk_lon(), -60);
|
||||
SG_CHECK_EQUAL(b2.get_x(), 0);
|
||||
SG_CHECK_EQUAL(b2.get_y(), 0);
|
||||
|
||||
COMPARE(b2.gen_index(), sgBucketOffset(-59.8, 21.9, 0, 1));
|
||||
SG_CHECK_EQUAL(b2.gen_index(), sgBucketOffset(-59.8, 21.9, 0, 1));
|
||||
|
||||
// offset vertically and horizontally. We compute horizontal (x)
|
||||
// movement at the target latitude, so this should move 0.25 * -3 degrees,
|
||||
// NOT 0.125 * -3 degrees.
|
||||
SGBucket b3(b1.sibling(-3, 1));
|
||||
COMPARE(b3.get_chunk_lat(), 22);
|
||||
COMPARE(b3.get_chunk_lon(), -61);
|
||||
COMPARE(b3.get_x(), 1);
|
||||
COMPARE(b3.get_y(), 0);
|
||||
SG_CHECK_EQUAL(b3.get_chunk_lat(), 22);
|
||||
SG_CHECK_EQUAL(b3.get_chunk_lon(), -61);
|
||||
SG_CHECK_EQUAL(b3.get_x(), 1);
|
||||
SG_CHECK_EQUAL(b3.get_y(), 0);
|
||||
|
||||
COMPARE(b3.gen_index(), sgBucketOffset(-59.8, 21.9, -3, 1));
|
||||
SG_CHECK_EQUAL(b3.gen_index(), sgBucketOffset(-59.8, 21.9, -3, 1));
|
||||
}
|
||||
|
||||
void testPolarOffset()
|
||||
{
|
||||
SGBucket b1(-11.7, -89.6);
|
||||
COMPARE(b1.get_chunk_lat(), -90);
|
||||
COMPARE(b1.get_chunk_lon(), -12);
|
||||
COMPARE(b1.get_x(), 0);
|
||||
COMPARE(b1.get_y(), 3);
|
||||
SG_CHECK_EQUAL(b1.get_chunk_lat(), -90);
|
||||
SG_CHECK_EQUAL(b1.get_chunk_lon(), -12);
|
||||
SG_CHECK_EQUAL(b1.get_x(), 0);
|
||||
SG_CHECK_EQUAL(b1.get_y(), 3);
|
||||
|
||||
// offset horizontally
|
||||
SGBucket b2(b1.sibling(-2, 0));
|
||||
COMPARE(b2.get_chunk_lat(), -90);
|
||||
COMPARE(b2.get_chunk_lon(), -36);
|
||||
COMPARE(b2.get_x(), 0);
|
||||
COMPARE(b2.get_y(), 3);
|
||||
SG_CHECK_EQUAL(b2.get_chunk_lat(), -90);
|
||||
SG_CHECK_EQUAL(b2.get_chunk_lon(), -36);
|
||||
SG_CHECK_EQUAL(b2.get_x(), 0);
|
||||
SG_CHECK_EQUAL(b2.get_y(), 3);
|
||||
|
||||
COMPARE(b2.gen_index(), sgBucketOffset(-11.7, -89.6, -2, 0));
|
||||
SG_CHECK_EQUAL(b2.gen_index(), sgBucketOffset(-11.7, -89.6, -2, 0));
|
||||
|
||||
// offset and wrap
|
||||
SGBucket b3(-170, 89.1);
|
||||
SGBucket b4(b3.sibling(-1, 0));
|
||||
COMPARE(b4.get_chunk_lat(), 89);
|
||||
COMPARE(b4.get_chunk_lon(), 168);
|
||||
COMPARE(b4.get_x(), 0);
|
||||
COMPARE(b4.get_y(), 0);
|
||||
SG_CHECK_EQUAL(b4.get_chunk_lat(), 89);
|
||||
SG_CHECK_EQUAL(b4.get_chunk_lon(), 168);
|
||||
SG_CHECK_EQUAL(b4.get_x(), 0);
|
||||
SG_CHECK_EQUAL(b4.get_y(), 0);
|
||||
|
||||
COMPARE(b4.gen_index(), sgBucketOffset(-170, 89.1, -1, 0));
|
||||
SG_CHECK_EQUAL(b4.gen_index(), sgBucketOffset(-170, 89.1, -1, 0));
|
||||
|
||||
|
||||
SGBucket b5(177, 87.3);
|
||||
SGBucket b6(b5.sibling(1, 1));
|
||||
COMPARE(b6.get_chunk_lat(), 87);
|
||||
COMPARE(b6.get_chunk_lon(), -180);
|
||||
COMPARE(b6.get_x(), 0);
|
||||
COMPARE(b6.get_y(), 3);
|
||||
SG_CHECK_EQUAL(b6.get_chunk_lat(), 87);
|
||||
SG_CHECK_EQUAL(b6.get_chunk_lon(), -180);
|
||||
SG_CHECK_EQUAL(b6.get_x(), 0);
|
||||
SG_CHECK_EQUAL(b6.get_y(), 3);
|
||||
|
||||
COMPARE(b6.gen_index(), sgBucketOffset(177, 87.3, 1, 1));
|
||||
SG_CHECK_EQUAL(b6.gen_index(), sgBucketOffset(177, 87.3, 1, 1));
|
||||
|
||||
// offset vertically towards the pole
|
||||
SGBucket b7(b1.sibling(0, -5));
|
||||
VERIFY(!b7.isValid());
|
||||
SG_VERIFY(!b7.isValid());
|
||||
|
||||
VERIFY(!SGBucket(0, 90).sibling(0, 1).isValid());
|
||||
SG_VERIFY(!SGBucket(0, 90).sibling(0, 1).isValid());
|
||||
}
|
||||
|
||||
// test behaviour of bucket-offset near the anti-meridian (180-meridian)
|
||||
@@ -251,17 +252,17 @@ void testOffsetWrap()
|
||||
{
|
||||
// near the equator
|
||||
SGBucket b1(-179.8, 16.8);
|
||||
COMPARE(b1.get_chunk_lat(), 16);
|
||||
COMPARE(b1.get_chunk_lon(), -180);
|
||||
COMPARE(b1.get_x(), 1);
|
||||
COMPARE(b1.get_y(), 6);
|
||||
SG_CHECK_EQUAL(b1.get_chunk_lat(), 16);
|
||||
SG_CHECK_EQUAL(b1.get_chunk_lon(), -180);
|
||||
SG_CHECK_EQUAL(b1.get_x(), 1);
|
||||
SG_CHECK_EQUAL(b1.get_y(), 6);
|
||||
|
||||
SGBucket b2(b1.sibling(-2, 0));
|
||||
COMPARE(b2.get_chunk_lat(), 16);
|
||||
COMPARE(b2.get_chunk_lon(), 179);
|
||||
COMPARE(b2.get_x(), 7);
|
||||
COMPARE(b2.get_y(), 6);
|
||||
COMPARE(b2.gen_index(), sgBucketOffset(-179.8, 16.8, -2, 0));
|
||||
SG_CHECK_EQUAL(b2.get_chunk_lat(), 16);
|
||||
SG_CHECK_EQUAL(b2.get_chunk_lon(), 179);
|
||||
SG_CHECK_EQUAL(b2.get_x(), 7);
|
||||
SG_CHECK_EQUAL(b2.get_y(), 6);
|
||||
SG_CHECK_EQUAL(b2.gen_index(), sgBucketOffset(-179.8, 16.8, -2, 0));
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -44,6 +44,11 @@
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(disable:4311)
|
||||
# pragma warning(disable:4312)
|
||||
# define ALIGN16 __declspec(align(16))
|
||||
# define ALIGN16C
|
||||
#elif defined(__GNUC__)
|
||||
# define ALIGN16
|
||||
# define ALIGN16C __attribute__((aligned(16)))
|
||||
#endif
|
||||
|
||||
/* Type definitions */
|
||||
|
||||
@@ -35,21 +35,33 @@ void SHVector2_dtor(SHVector2 *v) {
|
||||
}
|
||||
|
||||
void SHVector3_ctor(SHVector3 *v) {
|
||||
#ifdef __SSE__
|
||||
v->vec = _mm_setzero_ps();
|
||||
#else
|
||||
v->x=0.0f; v->y=0.0f; v->z=0.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
void SHVector3_dtor(SHVector3 *v) {
|
||||
}
|
||||
|
||||
void SHVector4_ctor(SHVector4 *v) {
|
||||
#ifdef __SSE__
|
||||
v->vec = _mm_setzero_ps();
|
||||
#else
|
||||
v->x=0.0f; v->y=0.0f; v->z=0.0f; v->w=0.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
void SHVector4_dtor(SHVector4 *v) {
|
||||
}
|
||||
|
||||
void SHRectangle_ctor(SHRectangle *r) {
|
||||
#ifdef __SSE__
|
||||
r->vec = _mm_setzero_ps();
|
||||
#else
|
||||
r->x=0.0f; r->y=0.0f; r->w=0.0f; r->h=0.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
void SHRectangle_dtor(SHRectangle *r) {
|
||||
@@ -135,3 +147,24 @@ int shLineLineXsection(SHVector2 *o1, SHVector2 *v1,
|
||||
xsection->y = o1->y + t1*v1->y;
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef __SSE__
|
||||
# ifdef __SSE3__
|
||||
# include <pmmintrin.h>
|
||||
inline float hsum_ps_sse(__m128 v) {
|
||||
__m128 shuf = _mm_movehdup_ps(v);
|
||||
__m128 sums = _mm_add_ps(v, shuf);
|
||||
shuf = _mm_movehl_ps(shuf, sums);
|
||||
sums = _mm_add_ss(sums, shuf);
|
||||
return _mm_cvtss_f32(sums);
|
||||
}
|
||||
# else
|
||||
inline float hsum_ps_sse(__m128 v) {
|
||||
__m128 shuf = _mm_shuffle_ps(v, v, _MM_SHUFFLE(2, 3, 0, 1));
|
||||
__m128 sums = _mm_add_ps(v, shuf);
|
||||
shuf = _mm_movehl_ps(shuf, sums);
|
||||
sums = _mm_add_ss(sums, shuf);
|
||||
return _mm_cvtss_f32(sums);
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
|
||||
@@ -21,6 +21,11 @@
|
||||
#ifndef __SHVECTORS_H
|
||||
#define __SHVECTORS_H
|
||||
|
||||
#ifdef __SSE__
|
||||
# include <xmmintrin.h>
|
||||
float hsum_ps_sse(__m128 v);
|
||||
#endif
|
||||
|
||||
#include "shDefs.h"
|
||||
|
||||
/* Vector structures
|
||||
@@ -33,9 +38,17 @@ typedef struct
|
||||
void SHVector2_ctor(SHVector2 *v);
|
||||
void SHVector2_dtor(SHVector2 *v);
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
#ifdef __SSE__
|
||||
union ALIGN16 {
|
||||
__m128 vec;
|
||||
struct { SHfloat x,y,z,w; };
|
||||
} ALIGN16C;
|
||||
#else
|
||||
SHfloat x,y,z;
|
||||
#endif
|
||||
} SHVector3;
|
||||
|
||||
void SHVector3_ctor(SHVector3 *v);
|
||||
@@ -43,7 +56,14 @@ void SHVector3_dtor(SHVector3 *v);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
#ifdef __SSE__
|
||||
union ALIGN16 {
|
||||
__m128 vec;
|
||||
struct { SHfloat x,y,z,w; };
|
||||
} ALIGN16C;
|
||||
#else
|
||||
SHfloat x,y,z,w;
|
||||
#endif
|
||||
} SHVector4;
|
||||
|
||||
void SHVector4_ctor(SHVector4 *v);
|
||||
@@ -51,7 +71,14 @@ void SHVector4_dtor(SHVector4 *v);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
#ifdef __SSE__
|
||||
union ALIGN16 {
|
||||
__m128 vec;
|
||||
struct { SHfloat x,y,w,h; };
|
||||
} ALIGN16C;
|
||||
#else
|
||||
SHfloat x,y,w,h;
|
||||
#endif
|
||||
} SHRectangle;
|
||||
|
||||
void SHRectangle_ctor(SHRectangle *r);
|
||||
@@ -61,7 +88,14 @@ void shRectangleSet(SHRectangle *r, SHfloat x,
|
||||
|
||||
typedef struct
|
||||
{
|
||||
#ifdef __SSE__
|
||||
union ALIGN16 {
|
||||
__m128 mtx[4];
|
||||
SHfloat m[4][4];
|
||||
} ALIGN16C;
|
||||
#else
|
||||
SHfloat m[3][3];
|
||||
#endif
|
||||
} SHMatrix3x3;
|
||||
|
||||
void SHMatrix3x3_ctor(SHMatrix3x3 *m);
|
||||
@@ -83,12 +117,22 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
|
||||
*--------------------------------------------------------- */
|
||||
|
||||
#define SET2(v,xs,ys) { v.x=xs; v.y=ys; }
|
||||
#define SET3(v,xs,ys,zs) { v.x=xs; v.y=ys; v.z=zs; }
|
||||
#define SET4(v,xs,ys,zs,ws) { v.x=xs; v.y=ys; v.z=zs; v.w=ws; }
|
||||
#ifdef __SSE__
|
||||
# define SET3(v,xs,ys,zs,ws) { v.vec=_mm_set_ps(0,zs,ys,xs); }
|
||||
# define SET4(v,xs,ys,zs,ws) { v.vec=_mm_set_ps(ws,zs,ys,xs); }
|
||||
#else
|
||||
# define SET3(v,xs,ys,zs) { v.x=xs; v.y=ys; v.z=zs; }
|
||||
# define SET4(v,xs,ys,zs,ws) { v.x=xs; v.y=ys; v.z=zs; v.w=ws; }
|
||||
#endif
|
||||
|
||||
#define SET2V(v1,v2) { v1.x=v2.x; v1.y=v2.y; }
|
||||
#define SET3V(v1,v2) { v1.x=v2.x; v1.y=v2.y; v1.z=v2.z; }
|
||||
#define SET4V(v1,v2) { v1.x=v2.x; v1.y=v2.y; v1.z=v2.z; v1.w=v2.w; }
|
||||
#ifdef __SSE__
|
||||
# define SET3V(v1,v2) { v1.vec=v2.vec; }
|
||||
# define SET4V(v1,v2) { v1.vec=v2.vec; }
|
||||
#else
|
||||
# define SET3V(v1,v2) { v1.x=v2.x; v1.y=v2.y; v1.z=v2.z; }
|
||||
# define SET4V(v1,v2) { v1.x=v2.x; v1.y=v2.y; v1.z=v2.z; v1.w=v2.w; }
|
||||
#endif
|
||||
|
||||
#define EQ2(v,xx,yy) ( v.x==xx && v.y==yy )
|
||||
#define EQ3(v,xx,yy,zz) ( v.x==xx && v.y==yy && v.z==zz )
|
||||
@@ -103,48 +147,89 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
|
||||
#define EQ4V(v1,v2) ( v1.x==v2.x && v1.y==v2.y && v1.z==v2.z && v1.w==v2.w )
|
||||
|
||||
#define ADD2(v,xx,yy) { v.x+=xx; v.y+=yy; }
|
||||
#define ADD3(v,xx,yy,zz) { v.x+=xx; v.y+=yy; v.z+=zz; }
|
||||
#define ADD4(v,xx,yy,zz,ww) { v.x+=xx; v.y+=yy; v.z+=zz; v.w+=ww; }
|
||||
#ifdef __SSE__
|
||||
# define ADD3(v,xx,yy,zz,ww) { v.vec=_mm_add_ps(v.vec,_mm_set_ps(0,zz,yy,xx)); }
|
||||
# define ADD4(v,xx,yy,zz,ww) { v.vec=_mm_add_ps(v.vec,_mm_set_ps(ww,zz,yy,xx)); }
|
||||
#else
|
||||
# define ADD3(v,xx,yy,zz) { v.x+=xx; v.y+=yy; v.z+=zz; }
|
||||
# define ADD4(v,xx,yy,zz,ww) { v.x+=xx; v.y+=yy; v.z+=zz; v.w+=ww; }
|
||||
#endif
|
||||
|
||||
#define ADD2V(v1,v2) { v1.x+=v2.x; v1.y+=v2.y; }
|
||||
#define ADD3V(v1,v2) { v1.x+=v2.x; v1.y+=v2.y; v1.z+=v2.z; }
|
||||
#define ADD4V(v1,v2) { v1.x+=v2.x; v1.y+=v2.y; v1.z+=v2.z; v1.w+=v2.w; }
|
||||
#ifdef __SSE__
|
||||
# define ADD3V(v1,v2) { v1.vec=_mm_add_ps(v1.vec,v2.vec); }
|
||||
# define ADD4V(v1,v2) { v1.vec=_mm_add_ps(v1.vec,v2.vec); }
|
||||
#else
|
||||
# define ADD3V(v1,v2) { v1.x+=v2.x; v1.y+=v2.y; v1.z+=v2.z; }
|
||||
# define ADD4V(v1,v2) { v1.x+=v2.x; v1.y+=v2.y; v1.z+=v2.z; v1.w+=v2.w; }
|
||||
#endif
|
||||
|
||||
#define SUB2(v,xx,yy) { v.x-=xx; v.y-=yy; }
|
||||
#define SUB3(v,xx,yy,zz) { v.x-=xx; v.y-=yy; v.z-=zz; }
|
||||
#define SUB4(v,xx,yy,zz,ww) { v.x-=xx; v.y-=yy; v.z-=zz; v.w-=v2.w; }
|
||||
#ifdef __SSE__
|
||||
# define SUB3(v,xx,yy,zz,ww) { v.vec=_mm_sub_ps(v.vec,_mm_set_ps(0,zz,yy,xx)); }
|
||||
# define SUB4(v,xx,yy,zz,ww) { v.vec=_mm_sub_ps(v.vec,_mm_set_ps(ww,zz,yy,xx)); }
|
||||
#else
|
||||
# define SUB3(v,xx,yy,zz) { v.x-=xx; v.y-=yy; v.z-=zz; }
|
||||
# define SUB4(v,xx,yy,zz,ww) { v.x-=xx; v.y-=yy; v.z-=zz; v.w-=v2.w; }
|
||||
#endif
|
||||
|
||||
#define SUB2V(v1,v2) { v1.x-=v2.x; v1.y-=v2.y; }
|
||||
#define SUB3V(v1,v2) { v1.x-=v2.x; v1.y-=v2.y; v1.z-=v2.z; }
|
||||
#define SUB4V(v1,v2) { v1.x-=v2.x; v1.y-=v2.y; v1.z-=v2.z; v1.w-=v2.w; }
|
||||
#ifdef __SSE__
|
||||
# define SUB3V(v1,v2) { v1.vec=_mm_sub_ps(v1.vec,v2.vec); }
|
||||
# define SUB4V(v1,v2) { v1.vec=_mm_sub_ps(v1.vec,v2.vec); }
|
||||
#else
|
||||
# define SUB3V(v1,v2) { v1.x-=v2.x; v1.y-=v2.y; v1.z-=v2.z; }
|
||||
# define SUB4V(v1,v2) { v1.x-=v2.x; v1.y-=v2.y; v1.z-=v2.z; v1.w-=v2.w; }
|
||||
#endif
|
||||
|
||||
#define MUL2(v,f) { v.x*=f; v.y*=f; }
|
||||
#define MUL3(v,f) { v.x*=f; v.y*=f; v.z*=z; }
|
||||
#define MUL4(v,f) { v.x*=f; v.y*=f; v.z*=z; v.w*=w; }
|
||||
#ifdef __SSE__
|
||||
# define MUL3(v,f) { v.vec=_mm_mul_ps(v.vec,_mm_set1_ps(f)); }
|
||||
# define MUL4(v,f) { v.vec=_mm_mul_ps(v.vec,_mm_set1_ps(f)); }
|
||||
#else
|
||||
# define MUL3(v,f) { v.x*=f; v.y*=f; v.z*=z; }
|
||||
# define MUL4(v,f) { v.x*=f; v.y*=f; v.z*=z; v.w*=w; }
|
||||
#endif
|
||||
|
||||
#define DIV2(v,f) { v.x/=f; v.y/=f; }
|
||||
#define DIV3(v,f) { v.x/=f; v.y/=f; v.z/=z; }
|
||||
#define DIV4(v,f) { v.x/=f; v.y/=f; v.z/=z; v.w/=w; }
|
||||
#ifdef __SSE__
|
||||
# define DIV3(v,f) { v.vec=_mm_div_ps(v.vec,_mm_set1_ps(f)); }
|
||||
# define DIV4(v,f) { v.vec=_mm_div_ps(v.vec,_mm_set1_ps(f)); }
|
||||
#else
|
||||
# define DIV3(v,f) { v.x/=f; v.y/=f; v.z/=z; }
|
||||
# define DIV4(v,f) { v.x/=f; v.y/=f; v.z/=z; v.w/=w; }
|
||||
#endif
|
||||
|
||||
#define ABS2(v) { v.x=SH_ABS(v.x); v.y=SH_ABS(v.y); }
|
||||
#define ABS3(v) { v.x=SH_ABS(v.x); v.y=SH_ABS(v.y); v.z=SH_ABS(v.z); }
|
||||
#define ABS4(v) { v.x=SH_ABS(v.x); v.y=SH_ABS(v.y); v.z=SH_ABS(v.z); v.w=SH_ABS(v.w); }
|
||||
#ifdef __SSE__
|
||||
# define ABS_MASK _mm_set1_ps(-0.f)
|
||||
# define ABS3(v) { v.vec=_mm_andnot_ps(ABS_MASK, v.vec); }
|
||||
# define ABS4(v) { v.vec=_mm_andnot_ps(ABS_MASK, v.vec); }
|
||||
#else
|
||||
# define ABS3(v) { v.x=SH_ABS(v.x); v.y=SH_ABS(v.y); v.z=SH_ABS(v.z); }
|
||||
# define ABS4(v) { v.x=SH_ABS(v.x); v.y=SH_ABS(v.y); v.z=SH_ABS(v.z); v.w=SH_ABS(v.w); }
|
||||
#endif
|
||||
|
||||
#define NORMSQ2(v) (v.x*v.x + v.y*v.y)
|
||||
#define NORMSQ3(v) (v.x*v.x + v.y*v.y + v.z*v.z)
|
||||
#define NORMSQ4(v) (v.x*v.x + v.y*v.y + v.z*v.z + v.w*v.w)
|
||||
#define NORMSQ2(v) DOT2(v,v)
|
||||
#define NORMSQ3(v) DOT3(v,v)
|
||||
#define NORMSQ4(v) DOT4(v,v)
|
||||
|
||||
#define NORM2(v) SH_SQRT(NORMSQ2(v))
|
||||
#define NORM3(v) SH_SQRT(NORMSQ3(v))
|
||||
#define NORM4(v) SH_SQRT(NORMSQ4(v))
|
||||
|
||||
#define NORMALIZE2(v) { SHfloat n=NORM2(v); v.x/=n; v.y/=n; }
|
||||
#define NORMALIZE3(v) { SHfloat n=NORM3(v); v.x/=n; v.y/=n; v.z/=n; }
|
||||
#define NORMALIZE4(v) { SHfloat n=NORM4(v); v.x/=n; v.y/=n; v.z/=n; v.w/=w; }
|
||||
#define NORMALIZE2(v) { SHfloat n=NORM2(v); DIV2(v,n); }
|
||||
#define NORMALIZE3(v) { SHfloat n=NORM3(v); DIV3(v,n); }
|
||||
#define NORMALIZE4(v) { SHfloat n=NORM4(v); DIV4(v,n); }
|
||||
|
||||
#define DOT2(v1,v2) (v1.x*v2.x + v1.y*v2.y)
|
||||
#define DOT3(v1,v2) (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z)
|
||||
#define DOT4(v1,v2) (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z + v1.w*v2.w)
|
||||
#ifdef __SSE__
|
||||
# define DOT4(v1,v2) hsum_ps_sse(_mm_mul_ps(v1.vec,v2.vec))
|
||||
# define DOT4(v1,v2) hsum_ps_sse(_mm_mul_ps(v1.vec,v2.vec))
|
||||
#else
|
||||
# define DOT3(v1,v2) (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z)
|
||||
# define DOT4(v1,v2) (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z + v1.w*v2.w)
|
||||
#endif
|
||||
|
||||
#define CROSS2(v1,v2) (v1.x*v2.y - v2.x*v1.y)
|
||||
|
||||
@@ -152,37 +237,84 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
|
||||
#define ANGLE2N(v1,v2) (SH_ACOS( DOT2(v1,v2) ))
|
||||
|
||||
#define OFFSET2V(v, o, s) { v.x += o.x*s; v.y += o.y*s; }
|
||||
#define OFFSET3V(v, o, s) { v.x += o.x*s; v.y += o.y*s; v.z += o.z*s; }
|
||||
#define OFFSET4V(v, o, s) { v.x += o.x*s; v.y += o.y*s; v.z += o.z*s; v.w += o.w*s; }
|
||||
#ifdef __SSE__
|
||||
# define OFFSET4V(v, o, s) { v.vec=_mm_add_ps(v.vec,_mm_mul_ps(o.vec,_mm_set1_ps(s))); }
|
||||
# define OFFSET4V(v, o, s) { v.vec=_mm_add_ps(v.vec,_mm_mul_ps(o.vec,_mm_set1_ps(s))); }
|
||||
#else
|
||||
# define OFFSET3V(v, o, s) { v.x += o.x*s; v.y += o.y*s; v.z += o.z*s; }
|
||||
# define OFFSET4V(v, o, s) { v.x += o.x*s; v.y += o.y*s; v.z += o.z*s; v.w += o.w*s; }
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------
|
||||
* Macros for matrix operations
|
||||
*-----------------------------------------------------*/
|
||||
|
||||
#define SETMAT(mat, m00, m01, m02, m10, m11, m12, m20, m21, m22) { \
|
||||
mat.m[0][0] = m00; mat.m[0][1] = m01; mat.m[0][2] = m02; \
|
||||
#ifdef __SSE__
|
||||
# define SETMAT(mat, m00, m01, m02, m10, m11, m12, m20, m21, m22) { \
|
||||
mat.mtx[0] = _mm_set_ps(0,m02,m01,m00); \
|
||||
mat.mtx[1] = _mm_set_ps(0,m12,m11,m10); \
|
||||
mat.mtx[2] = _mm_set_ps(0,m22,m21,m20); \
|
||||
mat.mtx[3] = _mm_setzero_ps(); }
|
||||
#else
|
||||
# define SETMAT(mat, m00, m01, m02, m10, m11, m12, m20, m21, m22) { \
|
||||
mat.m[0][0] = m00; mat.m[0][1] = m01; mat.m[0][2] = m02; \
|
||||
mat.m[1][0] = m10; mat.m[1][1] = m11; mat.m[1][2] = m12; \
|
||||
mat.m[2][0] = m20; mat.m[2][1] = m21; mat.m[2][2] = m22; }
|
||||
#endif
|
||||
|
||||
#define SETMATMAT(m1, m2) { \
|
||||
#ifdef __SSE__
|
||||
# define SETMATMAT(m1, m2) { \
|
||||
m1.mtx[0] = m2.mtx[0]; \
|
||||
m1.mtx[1] = m2.mtx[1]; \
|
||||
m1.mtx[2] = m2.mtx[2]; }
|
||||
#else
|
||||
# define SETMATMAT(m1, m2) { \
|
||||
int i,j; \
|
||||
for(i=0;i<3;i++) \
|
||||
for(j=0;j<3;j++) \
|
||||
m1.m[i][j] = m2.m[i][j]; }
|
||||
#endif
|
||||
|
||||
#define MULMATS(mat, s) { \
|
||||
#ifdef __SSE__
|
||||
# define MULMATS(mat, s) { \
|
||||
mat.mtx[0] = _mm_mul_ps(mat.mtx[0],_mm_set1_ps(s)); \
|
||||
mat.mtx[1] = _mm_mul_ps(mat.mtx[1],_mm_set1_ps(s)); \
|
||||
mat.mtx[2] = _mm_mul_ps(mat.mtx[2],_mm_set1_ps(s)); }
|
||||
#else
|
||||
# define MULMATS(mat, s) { \
|
||||
int i,j; \
|
||||
for(i=0;i<3;i++) \
|
||||
for(j=0;j<3;j++) \
|
||||
mat.m[i][j] *= s; }
|
||||
#endif
|
||||
|
||||
#define DIVMATS(mat, s) { \
|
||||
#ifdef __SSE__
|
||||
# define DIVMATS(mat, s) { \
|
||||
mat.mtx[0] = _mm_mul_ps(mat.mtx[0],_mm_set1_ps(1/s)); \
|
||||
mat.mtx[1] = _mm_mul_ps(mat.mtx[1],_mm_set1_ps(1/s)); \
|
||||
mat.mtx[2] = _mm_mul_ps(mat.mtx[2],_mm_set1_ps(1/s)); }
|
||||
#else
|
||||
# define DIVMATS(mat, s) { \
|
||||
int i,j; \
|
||||
for(i=0;i<3;i++) \
|
||||
for(j=0;j<3;j++) \
|
||||
mat.m[i][j] /= s; }
|
||||
#endif
|
||||
|
||||
#define MULMATMAT(m1, m2, mout) { \
|
||||
#ifdef __SSE__
|
||||
# define MULMATMAT(m2, m1, mout) { \
|
||||
int i,j; \
|
||||
for (i=0;i<4;i++) { \
|
||||
__m128 a = m1.mtx[0]; \
|
||||
__m128 b = _mm_set1_ps(m2.m[i][0]); \
|
||||
mout.mtx[i] = a*b; \
|
||||
for (j=1;j<4;j++) { \
|
||||
a = m1.mtx[j]; \
|
||||
b = _mm_set1_ps(m2.m[i][j]); \
|
||||
mout.mtx[i] += a*b; } } }
|
||||
|
||||
#else
|
||||
# define MULMATMAT(m1, m2, mout) { \
|
||||
int i,j; \
|
||||
for(i=0;i<3;i++) \
|
||||
for(j=0;j<3;j++) \
|
||||
@@ -190,6 +322,7 @@ int i,j; \
|
||||
m1.m[i][0] * m2.m[0][j] + \
|
||||
m1.m[i][1] * m2.m[1][j] + \
|
||||
m1.m[i][2] * m2.m[2][j]; }
|
||||
#endif
|
||||
|
||||
#define IDMAT(mat) SETMAT(mat, 1,0,0, 0,1,0, 0,0,1)
|
||||
|
||||
|
||||
@@ -640,6 +640,27 @@ namespace canvas
|
||||
_scissor->_coord_reference = rf;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::setRotation(unsigned int index, double r)
|
||||
{
|
||||
_node->getChild(NAME_TRANSFORM, index, true)->setDoubleValue("rot", r);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::setTranslation(unsigned int index, double x, double y)
|
||||
{
|
||||
SGPropertyNode* tf = _node->getChild(NAME_TRANSFORM, index, true);
|
||||
tf->getChild("t", 0, true)->setDoubleValue(x);
|
||||
tf->getChild("t", 1, true)->setDoubleValue(y);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::setTransformEnabled(unsigned int index, bool enabled)
|
||||
{
|
||||
SGPropertyNode* tf = _node->getChild(NAME_TRANSFORM, index, true);
|
||||
tf->setBoolValue("enabled", enabled);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::BoundingBox Element::getBoundingBox() const
|
||||
{
|
||||
@@ -701,6 +722,9 @@ namespace canvas
|
||||
continue;
|
||||
|
||||
SGPropertyNode* tf_node = _node->getChild("tf", i, true);
|
||||
if (!tf_node->getBoolValue("enabled", true)) {
|
||||
continue; // skip disabled transforms
|
||||
}
|
||||
|
||||
// Build up the matrix representation of the current transform node
|
||||
osg::Matrix tf;
|
||||
|
||||
@@ -172,6 +172,22 @@ namespace canvas
|
||||
*/
|
||||
void setClipFrame(ReferenceFrame rf);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void setRotation(unsigned int index, double r);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void setTranslation(unsigned int index, double x, double y);
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
void setTransformEnabled(unsigned int index, bool enabled);
|
||||
|
||||
/**
|
||||
* Get bounding box (may not be as tight as bounding box returned by
|
||||
* #getTightBoundingBox)
|
||||
|
||||
@@ -373,7 +373,7 @@ namespace canvas
|
||||
SG_LOG
|
||||
(
|
||||
SG_GENERAL,
|
||||
SG_INFO,
|
||||
SG_DEBUG,
|
||||
"canvas::Group: Moved element " << index << " to position " << index_new
|
||||
);
|
||||
}
|
||||
|
||||
@@ -487,11 +487,17 @@ namespace canvas
|
||||
if( !fill.empty() // If no color is given default to white
|
||||
&& !parseColor(fill, color) )
|
||||
return;
|
||||
|
||||
_colors->front() = color;
|
||||
_colors->dirty();
|
||||
setFill(color);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setFill(const osg::Vec4& color)
|
||||
{
|
||||
_colors->front() = color;
|
||||
_colors->dirty();
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setOutset(const std::string& outset)
|
||||
{
|
||||
@@ -506,6 +512,13 @@ namespace canvas
|
||||
_attributes_dirty |= SRC_RECT;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setSourceRect(const SGRect<float>& sourceRect)
|
||||
{
|
||||
_attributes_dirty |= SRC_RECT;
|
||||
_src_rect = sourceRect;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setSlice(const std::string& slice)
|
||||
{
|
||||
|
||||
@@ -61,6 +61,7 @@ namespace canvas
|
||||
|
||||
void setImage(osg::ref_ptr<osg::Image> img);
|
||||
void setFill(const std::string& fill);
|
||||
void setFill(const osg::Vec4& color);
|
||||
|
||||
/**
|
||||
* @see http://www.w3.org/TR/css3-background/#border-image-outset
|
||||
@@ -95,6 +96,10 @@ namespace canvas
|
||||
|
||||
bool handleEvent(const EventPtr& event);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void setSourceRect(const SGRect<float>& sourceRect);
|
||||
protected:
|
||||
|
||||
enum ImageAttributes
|
||||
|
||||
@@ -18,12 +18,14 @@
|
||||
|
||||
#include "CanvasPath.hxx"
|
||||
#include <simgear/scene/util/parse_color.hxx>
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
|
||||
#include <osg/Drawable>
|
||||
#include <osg/Version>
|
||||
|
||||
#include <vg/openvg.h>
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
@@ -57,6 +59,153 @@ namespace canvas
|
||||
*/
|
||||
std::vector<float> splitAndConvert(const char del[], const std::string& str);
|
||||
|
||||
static float parseCSSNumber(const std::string& s)
|
||||
{
|
||||
if (strutils::ends_with(s, "px")) {
|
||||
return std::stof(s.substr(0, s.length() - 2));
|
||||
} else if (s.back() == '%') {
|
||||
float f = std::stof(s.substr(0, s.length() - 1));
|
||||
return f / 100.0f;
|
||||
}
|
||||
return std::stof(s);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
static bool parseSVGPathToVGPath(const std::string& svgPath, CmdList& commands, CoordList& coords)
|
||||
{
|
||||
const string_list& tokens = simgear::strutils::split_on_any_of(svgPath, "\t \n\r,");
|
||||
char activeSVGCommand = 0;
|
||||
bool isRelative = false;
|
||||
int tokensNeeded = 0;
|
||||
|
||||
for (auto it = tokens.begin(); it != tokens.end(); ) {
|
||||
// set up the new command data
|
||||
if ((it->size() == 1) && std::isalpha(it->at(0))) {
|
||||
const char svgCommand = std::toupper(it->at(0));
|
||||
isRelative = std::islower(it->at(0));
|
||||
switch (svgCommand) {
|
||||
case 'Z':
|
||||
tokensNeeded = 0;
|
||||
break;
|
||||
case 'M':
|
||||
case 'L':
|
||||
case 'T':
|
||||
tokensNeeded = 2;
|
||||
break;
|
||||
case 'H':
|
||||
case 'V':
|
||||
tokensNeeded = 1;
|
||||
break;
|
||||
case 'C':
|
||||
tokensNeeded = 6;
|
||||
break;
|
||||
case 'S':
|
||||
case 'Q':
|
||||
tokensNeeded = 4;
|
||||
break;
|
||||
case 'A':
|
||||
tokensNeeded = 7;
|
||||
break;
|
||||
default:
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "unrecognized SVG path command: "
|
||||
<< *it << " at token " << std::distance(tokens.begin(), it));
|
||||
return false;
|
||||
}
|
||||
|
||||
activeSVGCommand = svgCommand;
|
||||
++it; // advance to first coordinate token
|
||||
}
|
||||
|
||||
const int numTokensRemaining = std::distance(it, tokens.end());
|
||||
if (numTokensRemaining < tokensNeeded) {
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "insufficent SVG path tokens");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool pushTokensDirectly = true;
|
||||
if (activeSVGCommand == 'Z') {
|
||||
commands.push_back(VG_CLOSE_PATH);
|
||||
activeSVGCommand = 0;
|
||||
} else if (activeSVGCommand == 'M') {
|
||||
commands.push_back(VG_MOVE_TO | isRelative);
|
||||
activeSVGCommand = 'L';
|
||||
} else if (activeSVGCommand == 'L') {
|
||||
commands.push_back(VG_LINE_TO | isRelative);
|
||||
} else if (activeSVGCommand == 'H') {
|
||||
commands.push_back(VG_HLINE_TO | isRelative);
|
||||
} else if (activeSVGCommand == 'V') {
|
||||
commands.push_back(VG_HLINE_TO | isRelative);
|
||||
} else if (activeSVGCommand == 'C') {
|
||||
commands.push_back(VG_CUBIC_TO | isRelative);
|
||||
} else if (activeSVGCommand == 'S') {
|
||||
commands.push_back(VG_SCUBIC_TO | isRelative);
|
||||
} else if (activeSVGCommand == 'Q') {
|
||||
commands.push_back(VG_SCUBIC_TO | isRelative);
|
||||
} else if (activeSVGCommand == 'T') {
|
||||
commands.push_back(VG_SCUBIC_TO | isRelative);
|
||||
} else if (activeSVGCommand == 'A') {
|
||||
pushTokensDirectly = false; // deal with tokens manually
|
||||
coords.push_back(parseCSSNumber(*it++)); // rx
|
||||
coords.push_back(parseCSSNumber(*it++)); // ry
|
||||
coords.push_back(parseCSSNumber(*it++)); // x-axis rotation
|
||||
|
||||
const bool isLargeArc = std::stoi(*it++); // large-angle
|
||||
const bool isCCW = std::stoi(*it++); // sweep-flag
|
||||
|
||||
int vgCmd = isLargeArc ? (isCCW ? VG_LCCWARC_TO : VG_LCWARC_TO) :
|
||||
(isCCW ? VG_SCCWARC_TO : VG_SCWARC_TO);
|
||||
|
||||
coords.push_back(parseCSSNumber(*it++));
|
||||
coords.push_back(parseCSSNumber(*it++));
|
||||
commands.push_back(vgCmd | isRelative);
|
||||
} else {
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "malformed SVG path string: expected a command at token:"
|
||||
<< std::distance(tokens.begin(), it) << " :" << *it);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pushTokensDirectly) {
|
||||
for (int i=0; i<tokensNeeded;++i) {
|
||||
coords.push_back(parseCSSNumber(*it++));
|
||||
}
|
||||
}
|
||||
} // of tokens iteration
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
static SGVec2f parseRectCornerRadius(SGPropertyNode* node, const std::string& xDir, const std::string& yDir, bool& haveCorner)
|
||||
{
|
||||
haveCorner = false;
|
||||
std::string propName = "border-" + yDir + "-" + xDir + "-radius";
|
||||
if (!node->hasChild(propName)) {
|
||||
propName = "border-" + yDir + "-radius";
|
||||
if (!node->hasChild(propName)) {
|
||||
propName = "border-radius";
|
||||
}
|
||||
}
|
||||
|
||||
PropertyList props = node->getChildren(propName);
|
||||
if (props.size() == 1) {
|
||||
double r = props.at(0)->getDoubleValue(propName);
|
||||
haveCorner = true;
|
||||
return SGVec2f(r, r);
|
||||
}
|
||||
|
||||
if (props.size() >= 2 ) {
|
||||
haveCorner = true;
|
||||
return SGVec2f(props.at(0)->getDoubleValue(),
|
||||
props.at(1)->getDoubleValue());
|
||||
}
|
||||
|
||||
return SGVec2f(-1.0f, -1.0f);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
class Path::PathDrawable:
|
||||
public osg::Drawable
|
||||
{
|
||||
@@ -533,7 +682,9 @@ namespace canvas
|
||||
const Style& parent_style,
|
||||
ElementWeakPtr parent ):
|
||||
Element(canvas, node, parent_style, parent),
|
||||
_path( new PathDrawable(this) )
|
||||
_path( new PathDrawable(this) ),
|
||||
_hasSVG(false),
|
||||
_hasRect(false)
|
||||
{
|
||||
staticInit();
|
||||
|
||||
@@ -561,6 +712,23 @@ namespace canvas
|
||||
_attributes_dirty &= ~(CMDS | COORDS);
|
||||
}
|
||||
|
||||
// SVG path overrides manual cmd/coord specification
|
||||
if ( _hasSVG && (_attributes_dirty & SVG))
|
||||
{
|
||||
CmdList cmds;
|
||||
CoordList coords;
|
||||
parseSVGPathToVGPath(_node->getStringValue("svg"), cmds, coords);
|
||||
_path->setSegments(cmds, coords);
|
||||
_attributes_dirty &= ~SVG;
|
||||
}
|
||||
|
||||
if ( _hasRect &&(_attributes_dirty & RECT))
|
||||
{
|
||||
parseRectToVGPath();
|
||||
_attributes_dirty &= ~RECT;
|
||||
|
||||
}
|
||||
|
||||
Element::update(dt);
|
||||
}
|
||||
|
||||
@@ -630,16 +798,75 @@ namespace canvas
|
||||
childChanged(child);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Path::setSVGPath(const std::string& svgPath)
|
||||
{
|
||||
_node->setStringValue("svg", svgPath);
|
||||
_hasSVG = true;
|
||||
_attributes_dirty |= SVG;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Path::setRect(const SGRect<float> &r)
|
||||
{
|
||||
_rect = r;
|
||||
_hasRect = true;
|
||||
_attributes_dirty |= RECT;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Path::setRoundRect(const SGRect<float> &r, float radiusX, float radiusY)
|
||||
{
|
||||
if (radiusY < 0.0) {
|
||||
radiusY = radiusX;
|
||||
}
|
||||
|
||||
setRect(r);
|
||||
_node->getChild("border-radius", 0, true)->setDoubleValue(radiusX);
|
||||
_node->getChild("border-radius", 1, true)->setDoubleValue(radiusY);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Path::childChanged(SGPropertyNode* child)
|
||||
{
|
||||
const std::string& name = child->getNameString();
|
||||
const std::string &prName = child->getParent()->getNameString();
|
||||
|
||||
if (simgear::strutils::starts_with(name, "border-"))
|
||||
{
|
||||
_attributes_dirty |= RECT;
|
||||
return;
|
||||
}
|
||||
|
||||
if (prName == "rect") {
|
||||
_hasRect = true;
|
||||
if (name == "left") {
|
||||
_rect.setLeft(child->getDoubleValue());
|
||||
} else if (name == "top") {
|
||||
_rect.setTop(child->getDoubleValue());
|
||||
} else if (name == "right") {
|
||||
_rect.setRight(child->getDoubleValue());
|
||||
} else if (name == "bottom") {
|
||||
_rect.setBottom(child->getDoubleValue());
|
||||
} else if (name == "width") {
|
||||
_rect.setWidth(child->getDoubleValue());
|
||||
} else if (name == "height") {
|
||||
_rect.setHeight(child->getDoubleValue());
|
||||
}
|
||||
_attributes_dirty |= RECT;
|
||||
return;
|
||||
}
|
||||
|
||||
if( child->getParent() != _node )
|
||||
return;
|
||||
|
||||
if( child->getNameString() == "cmd" )
|
||||
if( name == "cmd" )
|
||||
_attributes_dirty |= CMDS;
|
||||
else if( child->getNameString() == "coord" )
|
||||
else if( name == "coord" )
|
||||
_attributes_dirty |= COORDS;
|
||||
else if ( name == "svg")
|
||||
_hasSVG = true;
|
||||
_attributes_dirty |= SVG;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -664,5 +891,67 @@ namespace canvas
|
||||
return values;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void operator+=(CoordList& base, const std::initializer_list<VGfloat>& other)
|
||||
{
|
||||
base.insert(base.end(), other.begin(), other.end());
|
||||
}
|
||||
|
||||
void Path::parseRectToVGPath()
|
||||
{
|
||||
CmdList commands;
|
||||
CoordList coords;
|
||||
commands.reserve(4);
|
||||
coords.reserve(8);
|
||||
|
||||
bool haveCorner = false;
|
||||
SGVec2f topLeft = parseRectCornerRadius(_node, "left", "top", haveCorner);
|
||||
if (haveCorner) {
|
||||
commands.push_back(VG_MOVE_TO_ABS);
|
||||
coords += {_rect.l(), _rect.t() + topLeft.y()};
|
||||
commands.push_back(VG_SCCWARC_TO_REL);
|
||||
coords += {topLeft.x(), topLeft.y(), 0.0, topLeft.x(), -topLeft.y()};
|
||||
} else {
|
||||
commands.push_back(VG_MOVE_TO_ABS);
|
||||
coords += {_rect.l(), _rect.t()};
|
||||
}
|
||||
|
||||
SGVec2f topRight = parseRectCornerRadius(_node, "right", "top", haveCorner);
|
||||
if (haveCorner) {
|
||||
commands.push_back(VG_HLINE_TO_ABS);
|
||||
coords += {_rect.r() - topRight.x()};
|
||||
commands.push_back(VG_SCCWARC_TO_REL);
|
||||
coords += {topRight.x(), topRight.y(), 0.0, topRight.x(), topRight.y()};
|
||||
} else {
|
||||
commands.push_back(VG_HLINE_TO_ABS);
|
||||
coords += {_rect.r()};
|
||||
}
|
||||
|
||||
SGVec2f bottomRight = parseRectCornerRadius(_node, "right", "bottom", haveCorner);
|
||||
if (haveCorner) {
|
||||
commands.push_back(VG_VLINE_TO_ABS);
|
||||
coords += {_rect.b() - bottomRight.y()};
|
||||
commands.push_back(VG_SCCWARC_TO_REL);
|
||||
coords += {bottomRight.x(), bottomRight.y(), 0.0, -bottomRight.x(), bottomRight.y()};
|
||||
} else {
|
||||
commands.push_back(VG_VLINE_TO_ABS);
|
||||
coords += {_rect.b()};
|
||||
}
|
||||
|
||||
SGVec2f bottomLeft = parseRectCornerRadius(_node, "left", "bottom", haveCorner);
|
||||
if (haveCorner) {
|
||||
commands.push_back(VG_HLINE_TO_ABS);
|
||||
coords += {_rect.l() + bottomLeft.x()};
|
||||
commands.push_back(VG_SCCWARC_TO_REL);
|
||||
coords += {bottomLeft.x(), bottomLeft.y(), 0.0, -bottomLeft.x(), -bottomLeft.y()};
|
||||
} else {
|
||||
commands.push_back(VG_HLINE_TO_ABS);
|
||||
coords += {_rect.l()};
|
||||
}
|
||||
|
||||
commands.push_back(VG_CLOSE_PATH);
|
||||
_path->setSegments(commands, coords);
|
||||
}
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include "CanvasElement.hxx"
|
||||
#include <boost/preprocessor/iteration/iterate.hpp>
|
||||
#include <simgear/math/SGRect.hxx>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
@@ -67,18 +68,30 @@ namespace canvas
|
||||
/** Close the path (implicit lineTo to first point of path) */
|
||||
Path& close();
|
||||
|
||||
void setSVGPath(const std::string& svgPath);
|
||||
|
||||
void setRect(const SGRect<float>& r);
|
||||
void setRoundRect(const SGRect<float>& r, float radiusX, float radiusY = -1.0);
|
||||
protected:
|
||||
|
||||
enum PathAttributes
|
||||
{
|
||||
CMDS = LAST_ATTRIBUTE << 1,
|
||||
COORDS = CMDS << 1
|
||||
COORDS = CMDS << 1,
|
||||
SVG = COORDS << 1,
|
||||
RECT = SVG << 1
|
||||
};
|
||||
|
||||
class PathDrawable;
|
||||
typedef osg::ref_ptr<PathDrawable> PathDrawableRef;
|
||||
PathDrawableRef _path;
|
||||
|
||||
bool _hasSVG : 1;
|
||||
bool _hasRect : 1;
|
||||
SGRect<float> _rect;
|
||||
|
||||
void parseRectToVGPath();
|
||||
|
||||
virtual void childRemoved(SGPropertyNode * child);
|
||||
virtual void childChanged(SGPropertyNode * child);
|
||||
};
|
||||
|
||||
@@ -40,23 +40,12 @@
|
||||
#define SG_DO_STRINGIZE(X) #X
|
||||
|
||||
#ifdef __GNUC__
|
||||
# if __GNUC__ < 3
|
||||
# error Time to upgrade. GNU compilers < 3.0 not supported
|
||||
# elif (__GNUC__ == 3) && (__GNUC_MINOR__ < 4)
|
||||
# warning GCC compilers prior to 3.4 are suspect
|
||||
# endif
|
||||
|
||||
# define SG_GCC_VERSION (__GNUC__ * 10000 \
|
||||
+ __GNUC_MINOR__ * 100 \
|
||||
+ __GNUC_PATCHLEVEL__)
|
||||
# define SG_COMPILER_STR "GNU C++ version " SG_STRINGIZE(__GNUC__) "." SG_STRINGIZE(__GNUC_MINOR__)
|
||||
#endif // __GNUC__
|
||||
|
||||
/* KAI C++ */
|
||||
#if defined(__KCC)
|
||||
# define SG_COMPILER_STR "Kai C++ version " SG_STRINGIZE(__KCC_VERSION)
|
||||
#endif // __KCC
|
||||
|
||||
//
|
||||
// Microsoft compilers.
|
||||
//
|
||||
@@ -70,12 +59,6 @@
|
||||
# define strdup _strdup
|
||||
# define copysign _copysign
|
||||
# endif
|
||||
# if _MSC_VER < 1800
|
||||
# define isnan _isnan
|
||||
# endif
|
||||
# if _MSC_VER < 1500
|
||||
# define vsnprintf _vsnprintf
|
||||
# endif
|
||||
|
||||
# pragma warning(disable: 4786) // identifier was truncated to '255' characters
|
||||
# pragma warning(disable: 4244) // conversion from double to float
|
||||
@@ -91,49 +74,6 @@
|
||||
|
||||
#endif // _MSC_VER
|
||||
|
||||
//
|
||||
// Native SGI compilers
|
||||
//
|
||||
|
||||
#if defined ( sgi ) && !defined( __GNUC__ )
|
||||
# if (_COMPILER_VERSION < 740)
|
||||
# error Need MipsPro 7.4.0 or higher now
|
||||
# endif
|
||||
|
||||
#define SG_HAVE_NATIVE_SGI_COMPILERS
|
||||
|
||||
#pragma set woff 1001,1012,1014,1116,1155,1172,1174
|
||||
#pragma set woff 1401,1460,1551,1552,1681
|
||||
|
||||
#ifdef __cplusplus
|
||||
# pragma set woff 1682,3303
|
||||
# pragma set woff 3624
|
||||
#endif
|
||||
|
||||
# define SG_COMPILER_STR "SGI MipsPro compiler version " SG_STRINGIZE(_COMPILER_VERSION)
|
||||
|
||||
#endif // Native SGI compilers
|
||||
|
||||
|
||||
#if defined (__sun)
|
||||
# define SG_UNIX
|
||||
# include <strings.h>
|
||||
# include <memory.h>
|
||||
# if defined ( __cplusplus )
|
||||
// typedef unsigned int size_t;
|
||||
extern "C" {
|
||||
extern void *memmove(void *, const void *, size_t);
|
||||
}
|
||||
# else
|
||||
extern void *memmove(void *, const void *, size_t);
|
||||
# endif // __cplusplus
|
||||
|
||||
# if !defined( __GNUC__ )
|
||||
# define SG_COMPILER_STR "Sun compiler version " SG_STRINGIZE(__SUNPRO_CC)
|
||||
# endif
|
||||
|
||||
#endif // sun
|
||||
|
||||
//
|
||||
// Intel C++ Compiler
|
||||
//
|
||||
@@ -148,29 +88,10 @@
|
||||
#ifdef __APPLE__
|
||||
# define SG_MAC
|
||||
# define SG_UNIX
|
||||
# ifdef __GNUC__
|
||||
# if ( __GNUC__ > 3 ) || ( __GNUC__ == 3 && __GNUC_MINOR__ >= 3 )
|
||||
inline int (isnan)(double r) { return !(r <= 0 || r >= 0); }
|
||||
# else
|
||||
// any C++ header file undefines isinf and isnan
|
||||
// so this should be included before <iostream>
|
||||
// the functions are STILL in libm (libSystem on mac os x)
|
||||
extern "C" int (isnan)(double);
|
||||
extern "C" int (isinf)(double);
|
||||
# endif
|
||||
# else
|
||||
inline int (isnan)(double r) { return !(r <= 0 || r >= 0); }
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined (__FreeBSD__)
|
||||
# define SG_UNIX
|
||||
#include <sys/param.h>
|
||||
# if __FreeBSD_version < 500000
|
||||
extern "C" {
|
||||
inline int isnan(double r) { return !(r <= 0 || r >= 0); }
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined (__CYGWIN__)
|
||||
|
||||
@@ -211,8 +211,10 @@ const float SG_RADIANS_TO_DEGREES = 180.0f / SG_PI;
|
||||
/** for backwards compatibility */
|
||||
#define SG_SCENERY_FILE_FORMAT "0.4"
|
||||
|
||||
/** Default range in m at which all objects are displayed. Overridden by /sim/rendering/static-lod/rough **/
|
||||
#define SG_OBJECT_RANGE 9000.0
|
||||
/** Default object ranges. Overridden by /sim/rendering/static-lod/[bare|rough|detailed] **/
|
||||
#define SG_OBJECT_RANGE_BARE 30000.0
|
||||
#define SG_OBJECT_RANGE_ROUGH 9000.0
|
||||
#define SG_OBJECT_RANGE_DETAILED 1500.0
|
||||
|
||||
/** Radius of scenery tiles in m **/
|
||||
#define SG_TILE_RADIUS 14000.0
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#define SG_DEBUG_BUFFEREDLOGCALLBACK_HXX
|
||||
|
||||
#include <vector>
|
||||
#include <memory> // for std::auto_ptr
|
||||
#include <memory> // for std::unique_ptr
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
@@ -70,10 +70,10 @@ public:
|
||||
unsigned int threadsafeCopy(vector_cstring& aOutput);
|
||||
private:
|
||||
class BufferedLogCallbackPrivate;
|
||||
std::auto_ptr<BufferedLogCallbackPrivate> d;
|
||||
std::unique_ptr<BufferedLogCallbackPrivate> d;
|
||||
};
|
||||
|
||||
|
||||
} // of namespace simgear
|
||||
|
||||
#endif // of SG_DEBUG_BUFFEREDLOGCALLBACK_HXX
|
||||
#endif // of SG_DEBUG_BUFFEREDLOGCALLBACK_HXX
|
||||
|
||||
@@ -42,6 +42,10 @@ typedef enum {
|
||||
|
||||
/**
|
||||
* Define the possible logging priorities (and their order).
|
||||
*
|
||||
* Caution - unfortunately, this enum is exposed to Nasal via the logprint()
|
||||
* function as an integer parameter. Therefore, new values should only be
|
||||
* appended, or the priority Nasal reports to compiled code will change.
|
||||
*/
|
||||
typedef enum {
|
||||
SG_BULK = 1, // For frequent messages
|
||||
@@ -49,8 +53,11 @@ typedef enum {
|
||||
SG_INFO, // Informatory messages
|
||||
SG_WARN, // Possible impending problem
|
||||
SG_ALERT, // Very possible impending problem
|
||||
SG_POPUP // Severe enough to alert using a pop-up window
|
||||
SG_POPUP, // Severe enough to alert using a pop-up window
|
||||
// SG_EXIT, // Problem (no core)
|
||||
// SG_ABORT // Abandon ship (core)
|
||||
|
||||
SG_DEV_WARN, // Warning for developers, translated to other priority
|
||||
SG_DEV_ALERT // Alert for developers, translated
|
||||
} sgDebugPriority;
|
||||
|
||||
|
||||
@@ -36,12 +36,14 @@
|
||||
#include <simgear/threads/SGQueue.hxx>
|
||||
#include <simgear/threads/SGGuard.hxx>
|
||||
|
||||
#include <simgear/misc/sgstream.hxx>
|
||||
#include <simgear/io/iostreams/sgstream.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
|
||||
#if defined (SG_WINDOWS)
|
||||
// for AllocConsole, OutputDebugString
|
||||
#include <windows.h>
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#endif
|
||||
|
||||
const char* debugClassToString(sgDebugClass c)
|
||||
@@ -179,9 +181,9 @@ class LogStreamPrivate : public SGThread
|
||||
{
|
||||
private:
|
||||
/**
|
||||
* storage of a single log entry. Note this is not used for a persistent
|
||||
* store, but rather for short term buffering between the submitting
|
||||
* and output threads.
|
||||
* storage of a single log entry. This is used to pass log entries from
|
||||
* the various threads to the logging thread, and also to store the startup
|
||||
* entries
|
||||
*/
|
||||
class LogEntry
|
||||
{
|
||||
@@ -193,19 +195,24 @@ private:
|
||||
{
|
||||
}
|
||||
|
||||
sgDebugClass debugClass;
|
||||
sgDebugPriority debugPriority;
|
||||
const sgDebugClass debugClass;
|
||||
const sgDebugPriority debugPriority;
|
||||
const char* file;
|
||||
int line;
|
||||
std::string message;
|
||||
const int line;
|
||||
const std::string message;
|
||||
};
|
||||
|
||||
/**
|
||||
* RAII object to pause the logging thread if it's running, and restart it.
|
||||
* used to safely make configuration changes.
|
||||
*/
|
||||
class PauseThread
|
||||
{
|
||||
public:
|
||||
PauseThread(LogStreamPrivate* parent) : m_parent(parent)
|
||||
PauseThread(LogStreamPrivate* parent)
|
||||
: m_parent(parent)
|
||||
, m_wasRunning(m_parent->stop())
|
||||
{
|
||||
m_wasRunning = m_parent->stop();
|
||||
}
|
||||
|
||||
~PauseThread()
|
||||
@@ -216,32 +223,108 @@ private:
|
||||
}
|
||||
private:
|
||||
LogStreamPrivate* m_parent;
|
||||
bool m_wasRunning;
|
||||
const bool m_wasRunning;
|
||||
};
|
||||
|
||||
public:
|
||||
LogStreamPrivate() :
|
||||
m_logClass(SG_ALL),
|
||||
m_logPriority(SG_ALERT),
|
||||
m_isRunning(false)
|
||||
m_logPriority(SG_ALERT)
|
||||
{
|
||||
bool addStderr = true;
|
||||
#if defined (SG_WINDOWS)
|
||||
// Check for stream redirection, has to be done before we call
|
||||
// Attach / AllocConsole
|
||||
const bool isFile = (GetFileType(GetStdHandle(STD_ERROR_HANDLE)) == FILE_TYPE_DISK); // Redirect to file?
|
||||
if (AttachConsole(ATTACH_PARENT_PROCESS) == 0) {
|
||||
// attach failed, don't install the callback
|
||||
addStderr = false;
|
||||
} else if (!isFile) {
|
||||
// No - OK! now set streams to attached console
|
||||
freopen("conout$", "w", stdout);
|
||||
freopen("conout$", "w", stderr);
|
||||
}
|
||||
#endif
|
||||
if (addStderr) {
|
||||
m_callbacks.push_back(new StderrLogCallback(m_logClass, m_logPriority));
|
||||
m_consoleCallbacks.push_back(m_callbacks.back());
|
||||
/*
|
||||
* 2016-09-20(RJH) - Reworked console handling
|
||||
* 1) When started from the console use the console (when no --console)
|
||||
* 2) When started from the GUI (with --console) open a new console window
|
||||
* 3) When started from the GUI (without --console) don't open a new console
|
||||
* window; stdout/stderr will not appear (except in logfiles as they do now)
|
||||
* 4) When started from the Console (with --console) open a new console window
|
||||
* 5) Ensure that IO redirection still works when started from the console
|
||||
*
|
||||
* Notes:
|
||||
* 1) fgfs needs to be a GUI subsystem app - which it already is
|
||||
* 2) What can't be done is to make the cmd prompt run fgfs synchronously;
|
||||
* this is only something that can be done via "start /wait fgfs".
|
||||
*/
|
||||
|
||||
int stderr_handle_type = GetFileType(GetStdHandle(STD_ERROR_HANDLE));
|
||||
int stdout_handle_type = GetFileType(GetStdHandle(STD_OUTPUT_HANDLE));
|
||||
int stdout_isNull = 0;
|
||||
int stderr_isNull = 0;
|
||||
|
||||
m_stderr_isRedirectedAlready = stderr_handle_type == FILE_TYPE_DISK || stderr_handle_type == FILE_TYPE_PIPE || stderr_handle_type == FILE_TYPE_CHAR;
|
||||
m_stdout_isRedirectedAlready = stdout_handle_type == FILE_TYPE_DISK || stdout_handle_type == FILE_TYPE_PIPE || stdout_handle_type == FILE_TYPE_CHAR;
|
||||
|
||||
/*
|
||||
* We don't want to attach to the console if either stream has been redirected - so in this case ensure that both streams
|
||||
* are redirected as otherwise something will be lost (as Alloc or Attach Console will cause the handles that were bound
|
||||
* to disappear)
|
||||
*/
|
||||
if (m_stdout_isRedirectedAlready){
|
||||
if (!m_stderr_isRedirectedAlready) {
|
||||
MessageBox(0, "Redirection only works when you use 2>&1 before using > or |\r\n(e.g. fgfs 2>&1 | more)", "Simgear Error", MB_OK | MB_ICONERROR);
|
||||
exit(3);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Attempt to attach to the console process of the parent process; when launched from cmd.exe this should be the console,
|
||||
* when launched via the RUN menu explorer, or another GUI app that wasn't started from the console this will fail.
|
||||
* When it fails we will redirect to the NUL device. This is to ensure that we have valid streams.
|
||||
* Later on in the initialisation sequence the --console option will be processed and this will cause the requestConsole() to
|
||||
* always open a new console, except for streams that are redirected. The same rules apply there, if both streams are redirected
|
||||
* the console will be opened, and it will contain a message to indicate that no output will be present because the streams are redirected
|
||||
*/
|
||||
if (AttachConsole(ATTACH_PARENT_PROCESS) == 0) {
|
||||
/*
|
||||
* attach failed - so ensure that the streams are bound to the null device - but only when not already redirected
|
||||
*/
|
||||
if (!m_stdout_isRedirectedAlready)
|
||||
{
|
||||
stdout_isNull = true;
|
||||
freopen("NUL", "w", stdout);
|
||||
}
|
||||
|
||||
if (!m_stderr_isRedirectedAlready)
|
||||
{
|
||||
stderr_isNull = true;
|
||||
freopen("NUL", "w", stderr);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* providing that AttachConsole succeeded - we can then either reopen the stream onto the console, or use
|
||||
* _fdopen to attached to the currently redirected (and open stream)
|
||||
*/
|
||||
if (!stdout_isNull){
|
||||
if (!m_stdout_isRedirectedAlready)
|
||||
freopen("conout$", "w", stdout);
|
||||
else
|
||||
/*
|
||||
* for already redirected streams we need to attach the stream to the OS handle that is open.
|
||||
* - this comes from part of the answer http://stackoverflow.com/a/13841522
|
||||
* _open_osfhandle returns an FD for the Win32 Handle, which is then opened using fdopen and
|
||||
* hopefully safely assigned to the stream (although it does look wrong to me it works)
|
||||
* Removing this bit will stop pipes and command line redirection (> 2> and 2>&1 from working)
|
||||
*/
|
||||
*stdout = *_fdopen(_open_osfhandle((intptr_t) GetStdHandle(STD_OUTPUT_HANDLE), _O_WRONLY), "a");
|
||||
}
|
||||
|
||||
if (!stderr_isNull){
|
||||
if (!m_stderr_isRedirectedAlready)
|
||||
freopen("conout$", "w", stderr);
|
||||
else
|
||||
*stderr = *_fdopen(_open_osfhandle((intptr_t) GetStdHandle(STD_ERROR_HANDLE), _O_WRONLY), "a");
|
||||
}
|
||||
}
|
||||
//http://stackoverflow.com/a/25927081
|
||||
//Clear the error state for each of the C++ standard stream objects.
|
||||
std::wcout.clear();
|
||||
std::cout.clear();
|
||||
std::wcerr.clear();
|
||||
std::cerr.clear();
|
||||
#endif
|
||||
|
||||
m_callbacks.push_back(new StderrLogCallback(m_logClass, m_logPriority));
|
||||
m_consoleCallbacks.push_back(m_callbacks.back());
|
||||
#if defined (SG_WINDOWS) && !defined(NDEBUG)
|
||||
m_callbacks.push_back(new WinDebugLogCallback(m_logClass, m_logPriority));
|
||||
m_consoleCallbacks.push_back(m_callbacks.back());
|
||||
@@ -250,7 +333,7 @@ public:
|
||||
|
||||
~LogStreamPrivate()
|
||||
{
|
||||
BOOST_FOREACH(simgear::LogCallback* cb, m_callbacks) {
|
||||
for (simgear::LogCallback* cb : m_callbacks) {
|
||||
delete cb;
|
||||
}
|
||||
}
|
||||
@@ -258,6 +341,10 @@ public:
|
||||
SGMutex m_lock;
|
||||
SGBlockingQueue<LogEntry> m_entries;
|
||||
|
||||
// log entries posted during startup
|
||||
std::vector<LogEntry> m_startupEntries;
|
||||
bool m_startupLogging = false;
|
||||
|
||||
typedef std::vector<simgear::LogCallback*> CallbackVec;
|
||||
CallbackVec m_callbacks;
|
||||
/// subset of callbacks which correspond to stdout / console,
|
||||
@@ -266,7 +353,13 @@ public:
|
||||
|
||||
sgDebugClass m_logClass;
|
||||
sgDebugPriority m_logPriority;
|
||||
bool m_isRunning;
|
||||
bool m_isRunning = false;
|
||||
#if defined (SG_WINDOWS)
|
||||
// track whether the console was redirected on launch (in the constructor, which is called early on)
|
||||
bool m_stderr_isRedirectedAlready = false;
|
||||
bool m_stdout_isRedirectedAlready = false;
|
||||
#endif
|
||||
bool m_developerMode = false;
|
||||
|
||||
void startLog()
|
||||
{
|
||||
@@ -276,6 +369,16 @@ public:
|
||||
start();
|
||||
}
|
||||
|
||||
void setStartupLoggingEnabled(bool on)
|
||||
{
|
||||
if (m_startupLogging == on) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_startupLogging = on;
|
||||
m_startupEntries.clear();
|
||||
}
|
||||
|
||||
virtual void run()
|
||||
{
|
||||
while (1) {
|
||||
@@ -286,8 +389,14 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_startupLogging) {
|
||||
// save to the startup list for not-yet-added callbacks to
|
||||
// pull down on startup
|
||||
m_startupEntries.push_back(entry);
|
||||
}
|
||||
|
||||
// submit to each installed callback in turn
|
||||
BOOST_FOREACH(simgear::LogCallback* cb, m_callbacks) {
|
||||
for (simgear::LogCallback* cb : m_callbacks) {
|
||||
(*cb)(entry.debugClass, entry.debugPriority,
|
||||
entry.file, entry.line, entry.message);
|
||||
}
|
||||
@@ -314,6 +423,13 @@ public:
|
||||
{
|
||||
PauseThread pause(this);
|
||||
m_callbacks.push_back(cb);
|
||||
|
||||
// we clear startup entries not using this, so always safe to run
|
||||
// this code, container will simply be empty
|
||||
for (auto entry : m_startupEntries) {
|
||||
(*cb)(entry.debugClass, entry.debugPriority,
|
||||
entry.file, entry.line, entry.message);
|
||||
}
|
||||
}
|
||||
|
||||
void removeCallback(simgear::LogCallback* cb)
|
||||
@@ -337,6 +453,7 @@ public:
|
||||
|
||||
bool would_log( sgDebugClass c, sgDebugPriority p ) const
|
||||
{
|
||||
p = translatePriority(p);
|
||||
if (p >= SG_INFO) return true;
|
||||
return ((c & m_logClass) != 0 && p >= m_logPriority);
|
||||
}
|
||||
@@ -344,9 +461,23 @@ public:
|
||||
void log( sgDebugClass c, sgDebugPriority p,
|
||||
const char* fileName, int line, const std::string& msg)
|
||||
{
|
||||
p = translatePriority(p);
|
||||
LogEntry entry(c, p, fileName, line, msg);
|
||||
m_entries.push(entry);
|
||||
}
|
||||
|
||||
sgDebugPriority translatePriority(sgDebugPriority in) const
|
||||
{
|
||||
if (in == SG_DEV_WARN) {
|
||||
return m_developerMode ? SG_WARN : SG_DEBUG;
|
||||
}
|
||||
|
||||
if (in == SG_DEV_ALERT) {
|
||||
return m_developerMode ? SG_POPUP : SG_WARN;
|
||||
}
|
||||
|
||||
return in;
|
||||
}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
@@ -374,6 +505,12 @@ logstream::setLogLevels( sgDebugClass c, sgDebugPriority p )
|
||||
global_privateLogstream->setLogLevels(c, p);
|
||||
}
|
||||
|
||||
void logstream::setDeveloperMode(bool devMode)
|
||||
{
|
||||
global_privateLogstream->m_developerMode = devMode;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
logstream::addCallback(simgear::LogCallback* cb)
|
||||
{
|
||||
@@ -470,14 +607,56 @@ logstream::logToFile( const SGPath& aPath, sgDebugClass c, sgDebugPriority p )
|
||||
global_privateLogstream->addCallback(new FileLogCallback(aPath, c, p));
|
||||
}
|
||||
|
||||
void logstream::setStartupLoggingEnabled(bool enabled)
|
||||
{
|
||||
global_privateLogstream->setStartupLoggingEnabled(enabled);
|
||||
}
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
void requestConsole()
|
||||
{
|
||||
// this is a no-op now, stub exists for compatability for the moment.
|
||||
{
|
||||
#if defined (SG_WINDOWS)
|
||||
/*
|
||||
* 2016-09-20(RJH) - Reworked console handling
|
||||
* This is part of the reworked console handling for Win32. This is for building as a Win32 GUI Subsystem where no
|
||||
* console is allocated on launch. If building as a console app then the startup will ensure that a console is created - but
|
||||
* we don't need to handle that.
|
||||
* The new handling is quite simple:
|
||||
* 1. The constructor will ensure that these streams exists. It will attach to the
|
||||
* parent command prompt if started from the command prompt, otherwise the
|
||||
* stdout/stderr will be bound to the NUL device.
|
||||
* 2. with --console a window will always appear regardless of where the process was
|
||||
* started from. Any non redirected streams will be redirected
|
||||
* 3. You cannot use --console and either redirected stream.
|
||||
*
|
||||
* This is called after the Private Log Stream constructor so we need to undo any console that it has attached to.
|
||||
*/
|
||||
|
||||
if (!global_privateLogstream->m_stderr_isRedirectedAlready && !global_privateLogstream->m_stdout_isRedirectedAlready) {
|
||||
FreeConsole();
|
||||
if (AllocConsole()) {
|
||||
if (!global_privateLogstream->m_stdout_isRedirectedAlready)
|
||||
freopen("conout$", "w", stdout);
|
||||
|
||||
if (!global_privateLogstream->m_stderr_isRedirectedAlready)
|
||||
freopen("conout$", "w", stderr);
|
||||
|
||||
//http://stackoverflow.com/a/25927081
|
||||
//Clear the error state for each of the C++ standard stream objects.
|
||||
std::wcout.clear();
|
||||
std::cout.clear();
|
||||
std::wcerr.clear();
|
||||
std::cerr.clear();
|
||||
}
|
||||
} else {
|
||||
MessageBox(0, "--console ignored because stdout or stderr redirected with > or 2>", "Simgear Error", MB_OK | MB_ICONERROR);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void shutdownLogging()
|
||||
{
|
||||
SGGuard<SGMutex> g(global_logStreamLock);
|
||||
|
||||
@@ -93,6 +93,13 @@ public:
|
||||
|
||||
sgDebugPriority get_log_priority() const;
|
||||
|
||||
/**
|
||||
* set developer mode on/off. In developer mode, SG_DEV_WARN messags
|
||||
* are treated as warnings. In normal (non-developer) mode they are
|
||||
* treated as SG_DEBUG.
|
||||
*/
|
||||
void setDeveloperMode(bool devMode);
|
||||
|
||||
/**
|
||||
* the core logging method
|
||||
*/
|
||||
@@ -134,6 +141,12 @@ public:
|
||||
|
||||
void removeCallback(simgear::LogCallback* cb);
|
||||
|
||||
/**
|
||||
* optionally record all entries and submit them to new log callbacks that
|
||||
* are added. This allows simplified logging configuration, but still including
|
||||
* early startup information in all logs.
|
||||
*/
|
||||
void setStartupLoggingEnabled(bool enabled);
|
||||
private:
|
||||
// constructor
|
||||
logstream();
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#endif
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/misc/test_macros.hxx>
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
@@ -23,75 +24,56 @@ using std::cerr;
|
||||
using std::endl;
|
||||
using std::string;
|
||||
|
||||
#define COMPARE(a, b) \
|
||||
if ((a) != (b)) { \
|
||||
cerr << "failed:" << #a << " != " << #b << endl; \
|
||||
cerr << "\tgot:" << a << endl; \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
#define FUZZY_COMPARE(a, b, epsilon) \
|
||||
if (fabs(a - b) > epsilon) { \
|
||||
cerr << "failed:" << #a << " != " << #b << endl; \
|
||||
cerr << "\tgot:" << a << endl; \
|
||||
cerr << "\tepsilon:" << epsilon << endl; \
|
||||
}
|
||||
|
||||
#define VERIFY(a) \
|
||||
if (!(a)) { \
|
||||
cerr << "failed:" << #a << endl; \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
const double TEST_EPSILON = 1e-9;
|
||||
|
||||
void test_basic()
|
||||
{
|
||||
SGMetar m1("2011/10/20 11:25 EHAM 201125Z 27012KT 240V300 9999 VCSH FEW025CB SCT048 10/05 Q1025 TEMPO VRB03KT");
|
||||
COMPARE(m1.getYear(), 2011);
|
||||
COMPARE(m1.getMonth(), 10);
|
||||
COMPARE(m1.getDay(), 20);
|
||||
COMPARE(m1.getHour(), 11);
|
||||
COMPARE(m1.getMinute(), 25);
|
||||
COMPARE(m1.getReportType(), -1); // should default to NIL?
|
||||
|
||||
COMPARE(m1.getWindDir(), 270);
|
||||
FUZZY_COMPARE(m1.getWindSpeed_kt(), 12, TEST_EPSILON);
|
||||
|
||||
COMPARE(m1.getWeather().size(), 1);
|
||||
COMPARE(m1.getClouds().size(), 2);
|
||||
SG_CHECK_EQUAL(m1.getYear(), 2011);
|
||||
SG_CHECK_EQUAL(m1.getMonth(), 10);
|
||||
SG_CHECK_EQUAL(m1.getDay(), 20);
|
||||
SG_CHECK_EQUAL(m1.getHour(), 11);
|
||||
SG_CHECK_EQUAL(m1.getMinute(), 25);
|
||||
SG_CHECK_EQUAL(m1.getReportType(), -1); // should default to NIL?
|
||||
|
||||
FUZZY_COMPARE(m1.getTemperature_C(), 10, TEST_EPSILON);
|
||||
FUZZY_COMPARE(m1.getDewpoint_C(), 5, TEST_EPSILON);
|
||||
FUZZY_COMPARE(m1.getPressure_hPa(), 1025, TEST_EPSILON);
|
||||
SG_CHECK_EQUAL(m1.getWindDir(), 270);
|
||||
SG_CHECK_EQUAL_EP2(m1.getWindSpeed_kt(), 12, TEST_EPSILON);
|
||||
|
||||
SG_CHECK_EQUAL(m1.getWeather().size(), 1);
|
||||
SG_CHECK_EQUAL(m1.getClouds().size(), 2);
|
||||
|
||||
SG_CHECK_EQUAL_EP2(m1.getTemperature_C(), 10, TEST_EPSILON);
|
||||
SG_CHECK_EQUAL_EP2(m1.getDewpoint_C(), 5, TEST_EPSILON);
|
||||
SG_CHECK_EQUAL_EP2(m1.getPressure_hPa(), 1025, TEST_EPSILON);
|
||||
}
|
||||
|
||||
void test_sensor_failure_weather()
|
||||
{
|
||||
SGMetar m1("2011/10/20 11:25 EHAM 201125Z 27012KT 240V300 9999 // FEW025CB SCT048 10/05 Q1025");
|
||||
COMPARE(m1.getWindDir(), 270);
|
||||
FUZZY_COMPARE(m1.getWindSpeed_kt(), 12, TEST_EPSILON);
|
||||
SG_CHECK_EQUAL(m1.getWindDir(), 270);
|
||||
SG_CHECK_EQUAL_EP2(m1.getWindSpeed_kt(), 12, TEST_EPSILON);
|
||||
|
||||
COMPARE(m1.getWeather().size(), 0);
|
||||
COMPARE(m1.getClouds().size(), 2);
|
||||
SG_CHECK_EQUAL(m1.getWeather().size(), 0);
|
||||
SG_CHECK_EQUAL(m1.getClouds().size(), 2);
|
||||
|
||||
FUZZY_COMPARE(m1.getTemperature_C(), 10, TEST_EPSILON);
|
||||
FUZZY_COMPARE(m1.getDewpoint_C(), 5, TEST_EPSILON);
|
||||
FUZZY_COMPARE(m1.getPressure_hPa(), 1025, TEST_EPSILON);
|
||||
SG_CHECK_EQUAL_EP2(m1.getTemperature_C(), 10, TEST_EPSILON);
|
||||
SG_CHECK_EQUAL_EP2(m1.getDewpoint_C(), 5, TEST_EPSILON);
|
||||
SG_CHECK_EQUAL_EP2(m1.getPressure_hPa(), 1025, TEST_EPSILON);
|
||||
}
|
||||
|
||||
void test_sensor_failure_cloud()
|
||||
{
|
||||
SGMetar m1("2011/10/20 11:25 EHAM 201125Z 27012KT 240V300 9999 FEW025CB/// SCT048/// 10/05 Q1025");
|
||||
COMPARE(m1.getWindDir(), 270);
|
||||
FUZZY_COMPARE(m1.getWindSpeed_kt(), 12, TEST_EPSILON);
|
||||
SG_CHECK_EQUAL(m1.getWindDir(), 270);
|
||||
SG_CHECK_EQUAL_EP2(m1.getWindSpeed_kt(), 12, TEST_EPSILON);
|
||||
|
||||
COMPARE(m1.getWeather().size(), 0);
|
||||
COMPARE(m1.getClouds().size(), 2);
|
||||
SG_CHECK_EQUAL(m1.getWeather().size(), 0);
|
||||
SG_CHECK_EQUAL(m1.getClouds().size(), 2);
|
||||
|
||||
FUZZY_COMPARE(m1.getTemperature_C(), 10, TEST_EPSILON);
|
||||
FUZZY_COMPARE(m1.getDewpoint_C(), 5, TEST_EPSILON);
|
||||
FUZZY_COMPARE(m1.getPressure_hPa(), 1025, TEST_EPSILON);
|
||||
SG_CHECK_EQUAL_EP2(m1.getTemperature_C(), 10, TEST_EPSILON);
|
||||
SG_CHECK_EQUAL_EP2(m1.getDewpoint_C(), 5, TEST_EPSILON);
|
||||
SG_CHECK_EQUAL_EP2(m1.getPressure_hPa(), 1025, TEST_EPSILON);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
|
||||
@@ -52,7 +52,8 @@
|
||||
void CelestialBody::updatePosition(double mjd, Star *ourSun)
|
||||
{
|
||||
double eccAnom, v, ecl, actTime,
|
||||
xv, yv, xh, yh, zh, xg, yg, zg, xe, ye, ze;
|
||||
xv, yv, xh, yh, zh, xg, yg, zg, xe, ye, ze,
|
||||
cosN, sinN, cosvw, sinvw, sinvw_cosi, cosecl, sinecl;
|
||||
|
||||
updateOrbElements(mjd);
|
||||
actTime = sgCalcActTime(mjd);
|
||||
@@ -66,10 +67,19 @@ void CelestialBody::updatePosition(double mjd, Star *ourSun)
|
||||
v = atan2(yv, xv); // the planet's true anomaly
|
||||
r = sqrt (xv*xv + yv*yv); // the planet's distance
|
||||
|
||||
// repetitive calculations, minimised for speed
|
||||
cosN = cos(N);
|
||||
sinN = sin(N);
|
||||
cosvw = cos(v+w);
|
||||
sinvw = sin(v+w);
|
||||
sinvw_cosi = sinvw * cos(i);
|
||||
cosecl = cos(ecl);
|
||||
sinecl = sin(ecl);
|
||||
|
||||
// calculate the planet's position in 3D space
|
||||
xh = r * (cos(N) * cos(v+w) - sin(N) * sin(v+w) * cos(i));
|
||||
yh = r * (sin(N) * cos(v+w) + cos(N) * sin(v+w) * cos(i));
|
||||
zh = r * (sin(v+w) * sin(i));
|
||||
xh = r * (cosN * cosvw - sinN * sinvw_cosi);
|
||||
yh = r * (sinN * cosvw + cosN * sinvw_cosi);
|
||||
zh = r * (sinvw * sin(i));
|
||||
|
||||
// calculate the ecliptic longitude and latitude
|
||||
xg = xh + ourSun->getxs();
|
||||
@@ -80,8 +90,8 @@ void CelestialBody::updatePosition(double mjd, Star *ourSun)
|
||||
latEcl = atan2(zh, sqrt(xh*xh+yh*yh));
|
||||
|
||||
xe = xg;
|
||||
ye = yg * cos(ecl) - zg * sin(ecl);
|
||||
ze = yg * sin(ecl) + zg * cos(ecl);
|
||||
ye = yg * cosecl - zg * sinecl;
|
||||
ze = yg * sinecl + zg * cosecl;
|
||||
rightAscension = atan2(ye, xe);
|
||||
declination = atan2(ze, sqrt(xe*xe + ye*ye));
|
||||
/* SG_LOG(SG_GENERAL, SG_INFO, "Planet found at : "
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/math/SGMath.hxx>
|
||||
|
||||
#include <math.h>
|
||||
|
||||
@@ -78,69 +79,90 @@ void MoonPos::updatePosition(double mjd, double lst, double lat, Star *ourSun)
|
||||
{
|
||||
double
|
||||
eccAnom, ecl, actTime,
|
||||
xv, yv, v, r, xh, yh, zh, xg, yg, zg, xe, ye, ze,
|
||||
xv, yv, v, r, xh, yh, zh, zg, xe,
|
||||
Ls, Lm, D, F, mpar, gclat, rho, HA, g,
|
||||
geoRa, geoDec;
|
||||
geoRa, geoDec,
|
||||
cosN, sinN, cosvw, sinvw, sinvw_cosi, cosecl, sinecl, rcoslatEcl,
|
||||
FlesstwoD, MlesstwoD, twoD, twoM, twolat, alpha;
|
||||
|
||||
double max_loglux = -0.504030345621;
|
||||
double min_loglux = -4.39964634562;
|
||||
double conv = 1.0319696543787917; // The log foot-candle to log lux conversion factor.
|
||||
updateOrbElements(mjd);
|
||||
actTime = sgCalcActTime(mjd);
|
||||
|
||||
// calculate the angle between ecliptic and equatorial coordinate system
|
||||
// in Radians
|
||||
ecl = ((SGD_DEGREES_TO_RADIANS * 23.4393) - (SGD_DEGREES_TO_RADIANS * 3.563E-7) * actTime);
|
||||
ecl = SGD_DEGREES_TO_RADIANS * (23.4393 - 3.563E-7 * actTime);
|
||||
eccAnom = sgCalcEccAnom(M, e); // Calculate the eccentric anomaly
|
||||
xv = a * (cos(eccAnom) - e);
|
||||
yv = a * (sqrt(1.0 - e*e) * sin(eccAnom));
|
||||
v = atan2(yv, xv); // the moon's true anomaly
|
||||
r = sqrt (xv*xv + yv*yv); // and its distance
|
||||
|
||||
// repetitive calculations, minimised for speed
|
||||
cosN = cos(N);
|
||||
sinN = sin(N);
|
||||
cosvw = cos(v+w);
|
||||
sinvw = sin(v+w);
|
||||
sinvw_cosi = sinvw * cos(i);
|
||||
cosecl = cos(ecl);
|
||||
sinecl = sin(ecl);
|
||||
|
||||
// estimate the geocentric rectangular coordinates here
|
||||
xh = r * (cos(N) * cos (v+w) - sin (N) * sin(v+w) * cos(i));
|
||||
yh = r * (sin(N) * cos (v+w) + cos (N) * sin(v+w) * cos(i));
|
||||
zh = r * (sin(v+w) * sin(i));
|
||||
xh = r * (cosN * cosvw - sinN * sinvw_cosi);
|
||||
yh = r * (sinN * cosvw + cosN * sinvw_cosi);
|
||||
zh = r * (sinvw * sin(i));
|
||||
|
||||
// calculate the ecliptic latitude and longitude here
|
||||
lonEcl = atan2 (yh, xh);
|
||||
latEcl = atan2(zh, sqrt(xh*xh + yh*yh));
|
||||
|
||||
/* Calculate a number of perturbatioin, i.e. disturbances caused by the
|
||||
* gravitational infuence of the sun and the other major planets.
|
||||
/* Calculate a number of perturbation, i.e. disturbances caused by the
|
||||
* gravitational influence of the sun and the other major planets.
|
||||
* The largest of these even have a name */
|
||||
Ls = ourSun->getM() + ourSun->getw();
|
||||
Lm = M + w + N;
|
||||
D = Lm - Ls;
|
||||
F = Lm - N;
|
||||
|
||||
twoD = 2 * D;
|
||||
twoM = 2 * M;
|
||||
FlesstwoD = F - twoD;
|
||||
MlesstwoD = M - twoD;
|
||||
|
||||
lonEcl += SGD_DEGREES_TO_RADIANS * (-1.274 * sin (M - 2*D)
|
||||
+0.658 * sin (2*D)
|
||||
lonEcl += SGD_DEGREES_TO_RADIANS * (-1.274 * sin(MlesstwoD)
|
||||
+0.658 * sin(twoD)
|
||||
-0.186 * sin(ourSun->getM())
|
||||
-0.059 * sin(2*M - 2*D)
|
||||
-0.057 * sin(M - 2*D + ourSun->getM())
|
||||
+0.053 * sin(M + 2*D)
|
||||
+0.046 * sin(2*D - ourSun->getM())
|
||||
-0.059 * sin(twoM - twoD)
|
||||
-0.057 * sin(MlesstwoD + ourSun->getM())
|
||||
+0.053 * sin(M + twoD)
|
||||
+0.046 * sin(twoD - ourSun->getM())
|
||||
+0.041 * sin(M - ourSun->getM())
|
||||
-0.035 * sin(D)
|
||||
-0.031 * sin(M + ourSun->getM())
|
||||
-0.015 * sin(2*F - 2*D)
|
||||
-0.015 * sin(2*F - twoD)
|
||||
+0.011 * sin(M - 4*D)
|
||||
);
|
||||
latEcl += SGD_DEGREES_TO_RADIANS * (-0.173 * sin(F-2*D)
|
||||
-0.055 * sin(M - F - 2*D)
|
||||
-0.046 * sin(M + F - 2*D)
|
||||
+0.033 * sin(F + 2*D)
|
||||
+0.017 * sin(2*M + F)
|
||||
latEcl += SGD_DEGREES_TO_RADIANS * (-0.173 * sin(FlesstwoD)
|
||||
-0.055 * sin(M - FlesstwoD)
|
||||
-0.046 * sin(M + FlesstwoD)
|
||||
+0.033 * sin(F + twoD)
|
||||
+0.017 * sin(twoM + F)
|
||||
);
|
||||
r += (-0.58 * cos(M - 2*D)
|
||||
-0.46 * cos(2*D)
|
||||
r += (-0.58 * cos(MlesstwoD)
|
||||
-0.46 * cos(twoD)
|
||||
);
|
||||
distance = r;
|
||||
// SG_LOG(SG_GENERAL, SG_INFO, "Running moon update");
|
||||
xg = r * cos(lonEcl) * cos(latEcl);
|
||||
yg = r * sin(lonEcl) * cos(latEcl);
|
||||
rcoslatEcl = r * cos(latEcl);
|
||||
xg = cos(lonEcl) * rcoslatEcl;
|
||||
yg = sin(lonEcl) * rcoslatEcl;
|
||||
zg = r * sin(latEcl);
|
||||
|
||||
xe = xg;
|
||||
ye = yg * cos(ecl) -zg * sin(ecl);
|
||||
ze = yg * sin(ecl) +zg * cos(ecl);
|
||||
ye = yg * cosecl -zg * sinecl;
|
||||
ze = yg * sinecl +zg * cosecl;
|
||||
|
||||
geoRa = atan2(ye, xe);
|
||||
geoDec = atan2(ze, sqrt(xe*xe + ye*ye));
|
||||
@@ -154,17 +176,17 @@ void MoonPos::updatePosition(double mjd, double lst, double lat, Star *ourSun)
|
||||
// topocentric ra and dec. i.e. the position as seen from the
|
||||
// surface of the earth, instead of the center of the earth
|
||||
|
||||
// First calculate the moon's parrallax, that is, the apparent size of the
|
||||
// First calculate the moon's parallax, that is, the apparent size of the
|
||||
// (equatorial) radius of the earth, as seen from the moon
|
||||
mpar = asin ( 1 / r);
|
||||
// SG_LOG( SG_GENERAL, SG_INFO, "r = " << r << " mpar = " << mpar );
|
||||
// SG_LOG( SG_GENERAL, SG_INFO, "lat = " << f->get_Latitude() );
|
||||
|
||||
gclat = lat - 0.003358 *
|
||||
sin (2 * SGD_DEGREES_TO_RADIANS * lat );
|
||||
twolat = 2 * SGD_DEGREES_TO_RADIANS * lat;
|
||||
gclat = lat - 0.003358 * sin(twolat);
|
||||
// SG_LOG( SG_GENERAL, SG_INFO, "gclat = " << gclat );
|
||||
|
||||
rho = 0.99883 + 0.00167 * cos(2 * SGD_DEGREES_TO_RADIANS * lat);
|
||||
rho = 0.99883 + 0.00167 * cos(twolat);
|
||||
// SG_LOG( SG_GENERAL, SG_INFO, "rho = " << rho );
|
||||
|
||||
if (geoRa < 0)
|
||||
@@ -193,4 +215,22 @@ void MoonPos::updatePosition(double mjd, double lst, double lat, Star *ourSun)
|
||||
/* SG_LOG( SG_GENERAL, SG_INFO,
|
||||
"Ra = (" << (SGD_RADIANS_TO_DEGREES *rightAscension)
|
||||
<< "), Dec= (" << (SGD_RADIANS_TO_DEGREES *declination) << ")" ); */
|
||||
|
||||
// Moon age and phase calculation
|
||||
age = lonEcl - ourSun->getlonEcl();
|
||||
phase = (1 - cos(age)) / 2;
|
||||
|
||||
// The log of the illuminance of the moon outside the atmosphere.
|
||||
// This is the base 10 log of equation 20 from Krisciunas K. and Schaefer B.E.
|
||||
// (1991). A model of the brightness of moonlight, Publ. Astron. Soc. Pacif.
|
||||
// 103(667), 1033-1039 (DOI: http://dx.doi.org/10.1086/132921).
|
||||
alpha = SGD_RADIANS_TO_DEGREES * SGMiscd::normalizeAngle(age + SGMiscd::pi());
|
||||
log_I = -0.4 * (3.84 + 0.026*fabs(alpha) + 4e-9*pow(alpha, 4.0));
|
||||
|
||||
// Convert from foot-candles to lux.
|
||||
log_I += conv;
|
||||
|
||||
// The moon's illuminance factor, bracketed between 0 and 1.
|
||||
I_factor = (log_I - max_loglux) / (max_loglux - min_loglux) + 1.0;
|
||||
I_factor = SGMiscd::clip(I_factor, 0, 1);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,13 @@ class MoonPos : public CelestialBody
|
||||
|
||||
private:
|
||||
|
||||
double xg, yg; // the moon's rectangular geocentric coordinates
|
||||
double ye, ze; // the moon's rectangular equatorial coordinates
|
||||
double distance; // the moon's distance to the earth
|
||||
double age; // the moon's age from 0 to 2pi
|
||||
double phase; // the moon's phase
|
||||
double log_I; // the moon's illuminance outside the atmosphere (logged)
|
||||
double I_factor; // the illuminance factor for the moon, between 0 and 1.
|
||||
// void TexInit(); // This should move to the constructor eventually.
|
||||
|
||||
// GLUquadricObj *moonObject;
|
||||
@@ -54,7 +61,72 @@ public:
|
||||
~MoonPos();
|
||||
void updatePosition(double mjd, double lst, double lat, Star *ourSun);
|
||||
// void newImage();
|
||||
double getM() const;
|
||||
double getw() const;
|
||||
double getxg() const;
|
||||
double getyg() const;
|
||||
double getye() const;
|
||||
double getze() const;
|
||||
double getDistance() const;
|
||||
double getAge() const;
|
||||
double getPhase() const;
|
||||
double getLogIlluminance() const;
|
||||
double getIlluminanceFactor() const;
|
||||
};
|
||||
|
||||
inline double MoonPos::getM() const
|
||||
{
|
||||
return M;
|
||||
}
|
||||
|
||||
inline double MoonPos::getw() const
|
||||
{
|
||||
return w;
|
||||
}
|
||||
|
||||
inline double MoonPos::getxg() const
|
||||
{
|
||||
return xg;
|
||||
}
|
||||
|
||||
inline double MoonPos::getyg() const
|
||||
{
|
||||
return yg;
|
||||
}
|
||||
|
||||
inline double MoonPos::getye() const
|
||||
{
|
||||
return ye;
|
||||
}
|
||||
|
||||
inline double MoonPos::getze() const
|
||||
{
|
||||
return ze;
|
||||
}
|
||||
|
||||
inline double MoonPos::getDistance() const
|
||||
{
|
||||
return distance;
|
||||
}
|
||||
|
||||
inline double MoonPos::getAge() const
|
||||
{
|
||||
return age;
|
||||
}
|
||||
|
||||
inline double MoonPos::getPhase() const
|
||||
{
|
||||
return phase;
|
||||
}
|
||||
|
||||
inline double MoonPos::getLogIlluminance() const
|
||||
{
|
||||
return log_I;
|
||||
}
|
||||
|
||||
inline double MoonPos::getIlluminanceFactor() const
|
||||
{
|
||||
return I_factor;
|
||||
}
|
||||
|
||||
#endif // _MOONPOS_HXX_
|
||||
|
||||
@@ -33,6 +33,7 @@ class Star : public CelestialBody
|
||||
|
||||
private:
|
||||
|
||||
double lonEcl; // the sun's true longitude
|
||||
double xs, ys; // the sun's rectangular geocentric coordinates
|
||||
double ye, ze; // the sun's rectangularequatorial rectangular geocentric coordinates
|
||||
double distance; // the sun's distance to the earth
|
||||
@@ -50,6 +51,7 @@ public:
|
||||
double getye() const;
|
||||
double getze() const;
|
||||
double getDistance() const;
|
||||
double getlonEcl() const;
|
||||
};
|
||||
|
||||
|
||||
@@ -88,6 +90,10 @@ inline double Star::getDistance() const
|
||||
return distance;
|
||||
}
|
||||
|
||||
inline double Star::getlonEcl() const
|
||||
{
|
||||
return lonEcl;
|
||||
}
|
||||
|
||||
#endif // _STAR_HXX_
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/misc/sgstream.hxx>
|
||||
#include <simgear/io/iostreams/sgstream.hxx>
|
||||
|
||||
#include "stardata.hxx"
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ public:
|
||||
// bool unconditionalAttributeOwnershipDivestiture(const RTIHandle& objectHandle, const RTIHandleSet& attributeHandles)
|
||||
// {
|
||||
// try {
|
||||
// std::auto_ptr<RTI::AttributeHandleSet> attributeHandleSet(RTI::AttributeHandleSetFactory::create(attributeHandles.size()));
|
||||
// std::unique_ptr<RTI::AttributeHandleSet> attributeHandleSet(RTI::AttributeHandleSetFactory::create(attributeHandles.size()));
|
||||
// for (RTIHandleSet::const_iterator i = attributeHandles.begin(); i != attributeHandles.end(); ++i)
|
||||
// attributeHandleSet->add(*i);
|
||||
// _rtiAmbassador.unconditionalAttributeOwnershipDivestiture(objectHandle, *attributeHandleSet);
|
||||
@@ -136,7 +136,7 @@ public:
|
||||
// bool negotiatedAttributeOwnershipDivestiture(const RTIHandle& objectHandle, const RTIHandleSet& attributeHandles, const RTIData& tag)
|
||||
// {
|
||||
// try {
|
||||
// std::auto_ptr<RTI::AttributeHandleSet> attributeHandleSet(RTI::AttributeHandleSetFactory::create(attributeHandles.size()));
|
||||
// std::unique_ptr<RTI::AttributeHandleSet> attributeHandleSet(RTI::AttributeHandleSetFactory::create(attributeHandles.size()));
|
||||
// for (RTIHandleSet::const_iterator i = attributeHandles.begin(); i != attributeHandles.end(); ++i)
|
||||
// attributeHandleSet->add(*i);
|
||||
// _rtiAmbassador.negotiatedAttributeOwnershipDivestiture(objectHandle, *attributeHandleSet, tag.data());
|
||||
@@ -156,7 +156,7 @@ public:
|
||||
// bool attributeOwnershipAcquisition(const RTIHandle& objectHandle, const RTIHandleSet& attributeHandles, const RTIData& tag)
|
||||
// {
|
||||
// try {
|
||||
// std::auto_ptr<RTI::AttributeHandleSet> attributeHandleSet(RTI::AttributeHandleSetFactory::create(attributeHandles.size()));
|
||||
// std::unique_ptr<RTI::AttributeHandleSet> attributeHandleSet(RTI::AttributeHandleSetFactory::create(attributeHandles.size()));
|
||||
// for (RTIHandleSet::const_iterator i = attributeHandles.begin(); i != attributeHandles.end(); ++i)
|
||||
// attributeHandleSet->add(*i);
|
||||
// _rtiAmbassador.attributeOwnershipAcquisition(objectHandle, *attributeHandleSet, tag.data());
|
||||
@@ -177,7 +177,7 @@ public:
|
||||
// bool attributeOwnershipAcquisitionIfAvailable(const RTIHandle& objectHandle, const RTIHandleSet& attributeHandles)
|
||||
// {
|
||||
// try {
|
||||
// std::auto_ptr<RTI::AttributeHandleSet> attributeHandleSet(RTI::AttributeHandleSetFactory::create(attributeHandles.size()));
|
||||
// std::unique_ptr<RTI::AttributeHandleSet> attributeHandleSet(RTI::AttributeHandleSetFactory::create(attributeHandles.size()));
|
||||
// for (RTIHandleSet::const_iterator i = attributeHandles.begin(); i != attributeHandles.end(); ++i)
|
||||
// attributeHandleSet->add(*i);
|
||||
// _rtiAmbassador.attributeOwnershipAcquisitionIfAvailable(objectHandle, *attributeHandleSet);
|
||||
@@ -199,7 +199,7 @@ public:
|
||||
// RTIHandleSet attributeOwnershipReleaseResponse(const RTIHandle& objectHandle, const RTIHandleSet& attributeHandles)
|
||||
// {
|
||||
// try {
|
||||
// std::auto_ptr<RTI::AttributeHandleSet> attributeHandleSet(RTI::AttributeHandleSetFactory::create(attributeHandles.size()));
|
||||
// std::unique_ptr<RTI::AttributeHandleSet> attributeHandleSet(RTI::AttributeHandleSetFactory::create(attributeHandles.size()));
|
||||
// for (RTIHandleSet::const_iterator i = attributeHandles.begin(); i != attributeHandles.end(); ++i)
|
||||
// attributeHandleSet->add(*i);
|
||||
// attributeHandleSet.reset(_rtiAmbassador.attributeOwnershipReleaseResponse(objectHandle, *attributeHandleSet));
|
||||
@@ -223,7 +223,7 @@ public:
|
||||
// bool cancelNegotiatedAttributeOwnershipDivestiture(const RTIHandle& objectHandle, const RTIHandleSet& attributeHandles)
|
||||
// {
|
||||
// try {
|
||||
// std::auto_ptr<RTI::AttributeHandleSet> attributeHandleSet(RTI::AttributeHandleSetFactory::create(attributeHandles.size()));
|
||||
// std::unique_ptr<RTI::AttributeHandleSet> attributeHandleSet(RTI::AttributeHandleSetFactory::create(attributeHandles.size()));
|
||||
// for (RTIHandleSet::const_iterator i = attributeHandles.begin(); i != attributeHandles.end(); ++i)
|
||||
// attributeHandleSet->add(*i);
|
||||
// _rtiAmbassador.cancelNegotiatedAttributeOwnershipDivestiture(objectHandle, *attributeHandleSet);
|
||||
@@ -243,7 +243,7 @@ public:
|
||||
// bool cancelAttributeOwnershipAcquisition(const RTIHandle& objectHandle, const RTIHandleSet& attributeHandles)
|
||||
// {
|
||||
// try {
|
||||
// std::auto_ptr<RTI::AttributeHandleSet> attributeHandleSet(RTI::AttributeHandleSetFactory::create(attributeHandles.size()));
|
||||
// std::unique_ptr<RTI::AttributeHandleSet> attributeHandleSet(RTI::AttributeHandleSetFactory::create(attributeHandles.size()));
|
||||
// for (RTIHandleSet::const_iterator i = attributeHandles.begin(); i != attributeHandles.end(); ++i)
|
||||
// attributeHandleSet->add(*i);
|
||||
// _rtiAmbassador.cancelAttributeOwnershipAcquisition(objectHandle, *attributeHandleSet);
|
||||
|
||||
@@ -101,7 +101,7 @@ RTI13ObjectClass::publish(const HLAIndexList& indexList)
|
||||
|
||||
try {
|
||||
unsigned numAttributes = getNumAttributes();
|
||||
std::auto_ptr<RTI::AttributeHandleSet> attributeHandleSet(RTI::AttributeHandleSetFactory::create(numAttributes));
|
||||
std::unique_ptr<RTI::AttributeHandleSet> attributeHandleSet(RTI::AttributeHandleSetFactory::create(numAttributes));
|
||||
for (HLAIndexList::const_iterator i = indexList.begin(); i != indexList.end(); ++i) {
|
||||
if (_attributeHandleVector.size() <= *i) {
|
||||
SG_LOG(SG_NETWORK, SG_WARN, "RTI13ObjectClass::publish(): Invalid attribute index!");
|
||||
@@ -195,7 +195,7 @@ RTI13ObjectClass::subscribe(const HLAIndexList& indexList, bool active)
|
||||
|
||||
try {
|
||||
unsigned numAttributes = getNumAttributes();
|
||||
std::auto_ptr<RTI::AttributeHandleSet> attributeHandleSet(RTI::AttributeHandleSetFactory::create(numAttributes));
|
||||
std::unique_ptr<RTI::AttributeHandleSet> attributeHandleSet(RTI::AttributeHandleSetFactory::create(numAttributes));
|
||||
for (HLAIndexList::const_iterator i = indexList.begin(); i != indexList.end(); ++i) {
|
||||
if (_attributeHandleVector.size() <= *i) {
|
||||
SG_LOG(SG_NETWORK, SG_WARN, "RTI13ObjectClass::subscribe(): Invalid attribute index!");
|
||||
|
||||
@@ -243,7 +243,7 @@ RTI13ObjectInstance::requestObjectAttributeValueUpdate(const HLAIndexList& index
|
||||
|
||||
try {
|
||||
unsigned numAttributes = getNumAttributes();
|
||||
std::auto_ptr<RTI::AttributeHandleSet> attributeHandleSet(RTI::AttributeHandleSetFactory::create(numAttributes));
|
||||
std::unique_ptr<RTI::AttributeHandleSet> attributeHandleSet(RTI::AttributeHandleSetFactory::create(numAttributes));
|
||||
for (HLAIndexList::const_iterator i = indexList.begin(); i != indexList.end(); ++i) {
|
||||
if (getAttributeOwned(*i)) {
|
||||
SG_LOG(SG_NETWORK, SG_WARN, "RTI13ObjectInstance::requestObjectAttributeValueUpdate(): "
|
||||
|
||||
@@ -101,7 +101,7 @@ private:
|
||||
SGSharedPtr<RTI13Ambassador> _ambassador;
|
||||
|
||||
// cached storage for updates
|
||||
std::auto_ptr<RTI::AttributeHandleValuePairSet> _attributeValuePairSet;
|
||||
std::unique_ptr<RTI::AttributeHandleValuePairSet> _attributeValuePairSet;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
|
||||
add_subdirectory(iostreams)
|
||||
|
||||
include (SimGearComponent)
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
//
|
||||
|
||||
#include "DNSClient.hxx"
|
||||
#include "udns.h"
|
||||
#include <udns.h>
|
||||
#include <time.h>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
@@ -33,18 +33,33 @@ namespace DNS {
|
||||
class Client::ClientPrivate {
|
||||
public:
|
||||
ClientPrivate() {
|
||||
if (dns_init(NULL, 0) < 0)
|
||||
SG_LOG(SG_IO, SG_ALERT, "Can't init udns library" );
|
||||
|
||||
if( dns_open(NULL) < 0 )
|
||||
if( instanceCounter++ == 0 )
|
||||
if (dns_init(NULL, 0) < 0)
|
||||
SG_LOG(SG_IO, SG_ALERT, "Can't init udns library" );
|
||||
|
||||
ctx = dns_new(NULL);
|
||||
|
||||
if (dns_init(ctx, 0) < 0)
|
||||
SG_LOG(SG_IO, SG_ALERT, "Can't create udns context" );
|
||||
|
||||
if( dns_open(ctx) < 0 )
|
||||
SG_LOG(SG_IO, SG_ALERT, "Can't open udns context" );
|
||||
}
|
||||
|
||||
~ClientPrivate() {
|
||||
dns_close(NULL);
|
||||
dns_close(ctx);
|
||||
dns_free(ctx);
|
||||
if( --instanceCounter == 0 )
|
||||
dns_close(NULL);
|
||||
}
|
||||
|
||||
struct dns_ctx * ctx;
|
||||
static size_t instanceCounter;
|
||||
};
|
||||
|
||||
size_t Client::ClientPrivate::instanceCounter = 0;
|
||||
|
||||
Request::Request( const std::string & dn ) :
|
||||
_dn(dn),
|
||||
_type(DNS_T_ANY),
|
||||
@@ -69,6 +84,96 @@ NAPTRRequest::NAPTRRequest( const std::string & dn ) :
|
||||
_type = DNS_T_NAPTR;
|
||||
}
|
||||
|
||||
SRVRequest::SRVRequest( const std::string & dn ) :
|
||||
Request(dn)
|
||||
{
|
||||
_type = DNS_T_SRV;
|
||||
}
|
||||
|
||||
SRVRequest::SRVRequest( const std::string & dn, const string & service, const string & protocol ) :
|
||||
Request(dn),
|
||||
_service(service),
|
||||
_protocol(protocol)
|
||||
{
|
||||
_type = DNS_T_SRV;
|
||||
}
|
||||
|
||||
static bool sortSRV( const SRVRequest::SRV_ptr a, const SRVRequest::SRV_ptr b )
|
||||
{
|
||||
if( a->priority > b->priority ) return false;
|
||||
if( a->priority < b->priority ) return true;
|
||||
return a->weight > b->weight;
|
||||
}
|
||||
|
||||
static void dnscbSRV(struct dns_ctx *ctx, struct dns_rr_srv *result, void *data)
|
||||
{
|
||||
SRVRequest * r = static_cast<SRVRequest*>(data);
|
||||
if (result) {
|
||||
r->cname = result->dnssrv_cname;
|
||||
r->qname = result->dnssrv_qname;
|
||||
r->ttl = result->dnssrv_ttl;
|
||||
for (int i = 0; i < result->dnssrv_nrr; i++) {
|
||||
SRVRequest::SRV_ptr srv(new SRVRequest::SRV);
|
||||
r->entries.push_back(srv);
|
||||
srv->priority = result->dnssrv_srv[i].priority;
|
||||
srv->weight = result->dnssrv_srv[i].weight;
|
||||
srv->port = result->dnssrv_srv[i].port;
|
||||
srv->target = result->dnssrv_srv[i].name;
|
||||
}
|
||||
std::sort( r->entries.begin(), r->entries.end(), sortSRV );
|
||||
free(result);
|
||||
}
|
||||
r->setComplete();
|
||||
}
|
||||
|
||||
void SRVRequest::submit( Client * client )
|
||||
{
|
||||
// if service is defined, pass service and protocol
|
||||
if (!dns_submit_srv(client->d->ctx, getDn().c_str(), _service.empty() ? NULL : _service.c_str(), _service.empty() ? NULL : _protocol.c_str(), 0, dnscbSRV, this )) {
|
||||
SG_LOG(SG_IO, SG_ALERT, "Can't submit dns request for " << getDn());
|
||||
return;
|
||||
}
|
||||
_start = time(NULL);
|
||||
}
|
||||
|
||||
TXTRequest::TXTRequest( const std::string & dn ) :
|
||||
Request(dn)
|
||||
{
|
||||
_type = DNS_T_TXT;
|
||||
}
|
||||
|
||||
static void dnscbTXT(struct dns_ctx *ctx, struct dns_rr_txt *result, void *data)
|
||||
{
|
||||
TXTRequest * r = static_cast<TXTRequest*>(data);
|
||||
if (result) {
|
||||
r->cname = result->dnstxt_cname;
|
||||
r->qname = result->dnstxt_qname;
|
||||
r->ttl = result->dnstxt_ttl;
|
||||
for (int i = 0; i < result->dnstxt_nrr; i++) {
|
||||
//TODO: interprete the .len field of dnstxt_txt?
|
||||
string txt = string((char*)result->dnstxt_txt[i].txt);
|
||||
r->entries.push_back( txt );
|
||||
string_list tokens = simgear::strutils::split( txt, "=", 1 );
|
||||
if( tokens.size() == 2 ) {
|
||||
r->attributes[tokens[0]] = tokens[1];
|
||||
}
|
||||
}
|
||||
free(result);
|
||||
}
|
||||
r->setComplete();
|
||||
}
|
||||
|
||||
void TXTRequest::submit( Client * client )
|
||||
{
|
||||
// protocol and service an already encoded in DN so pass in NULL for both
|
||||
if (!dns_submit_txt(client->d->ctx, getDn().c_str(), DNS_C_IN, 0, dnscbTXT, this )) {
|
||||
SG_LOG(SG_IO, SG_ALERT, "Can't submit dns request for " << getDn());
|
||||
return;
|
||||
}
|
||||
_start = time(NULL);
|
||||
}
|
||||
|
||||
|
||||
static bool sortNAPTR( const NAPTRRequest::NAPTR_ptr a, const NAPTRRequest::NAPTR_ptr b )
|
||||
{
|
||||
if( a->order > b->order ) return false;
|
||||
@@ -106,9 +211,9 @@ static void dnscbNAPTR(struct dns_ctx *ctx, struct dns_rr_naptr *result, void *d
|
||||
r->setComplete();
|
||||
}
|
||||
|
||||
void NAPTRRequest::submit()
|
||||
void NAPTRRequest::submit( Client * client )
|
||||
{
|
||||
if (!dns_submit_naptr(NULL, getDn().c_str(), 0, dnscbNAPTR, this )) {
|
||||
if (!dns_submit_naptr(client->d->ctx, getDn().c_str(), 0, dnscbNAPTR, this )) {
|
||||
SG_LOG(SG_IO, SG_ALERT, "Can't submit dns request for " << getDn());
|
||||
return;
|
||||
}
|
||||
@@ -127,16 +232,16 @@ Client::Client() :
|
||||
|
||||
void Client::makeRequest(const Request_ptr& r)
|
||||
{
|
||||
r->submit();
|
||||
r->submit(this);
|
||||
}
|
||||
|
||||
|
||||
void Client::update(int waitTimeout)
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
if( dns_timeouts( NULL, waitTimeout, now ) < 0 )
|
||||
if( dns_timeouts( d->ctx, -1, now ) < 0 )
|
||||
return;
|
||||
dns_ioevent(NULL, now);
|
||||
|
||||
dns_ioevent(d->ctx, now);
|
||||
}
|
||||
|
||||
} // of namespace DNS
|
||||
|
||||
@@ -24,13 +24,14 @@
|
||||
#ifndef SG_DNS_CLIENT_HXX
|
||||
#define SG_DNS_CLIENT_HXX
|
||||
|
||||
#include <memory> // for std::auto_ptr
|
||||
#include <memory> // for std::unique_ptr
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <ctime> // for time_t
|
||||
|
||||
#include <simgear/structure/SGReferenced.hxx>
|
||||
#include <simgear/structure/SGSharedPtr.hxx>
|
||||
#include <simgear/structure/event_mgr.hxx>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
@@ -38,6 +39,7 @@ namespace simgear
|
||||
namespace DNS
|
||||
{
|
||||
|
||||
class Client;
|
||||
class Request : public SGReferenced
|
||||
{
|
||||
public:
|
||||
@@ -49,7 +51,7 @@ public:
|
||||
bool isTimeout() const;
|
||||
void setComplete( bool b = true ) { _complete = b; }
|
||||
|
||||
virtual void submit() = 0;
|
||||
virtual void submit( Client * client) = 0;
|
||||
|
||||
std::string cname;
|
||||
std::string qname;
|
||||
@@ -61,12 +63,13 @@ protected:
|
||||
time_t _timeout_secs;
|
||||
time_t _start;
|
||||
};
|
||||
typedef SGSharedPtr<Request> Request_ptr;
|
||||
|
||||
class NAPTRRequest : public Request
|
||||
{
|
||||
public:
|
||||
NAPTRRequest( const std::string & dn );
|
||||
virtual void submit();
|
||||
virtual void submit( Client * client );
|
||||
|
||||
struct NAPTR : SGReferenced {
|
||||
int order;
|
||||
@@ -84,7 +87,38 @@ public:
|
||||
std::string qservice;
|
||||
};
|
||||
|
||||
typedef SGSharedPtr<Request> Request_ptr;
|
||||
class SRVRequest : public Request
|
||||
{
|
||||
public:
|
||||
SRVRequest( const std::string & dn );
|
||||
SRVRequest( const std::string & dn, const string & service, const string & protocol );
|
||||
virtual void submit( Client * client );
|
||||
|
||||
struct SRV : SGReferenced {
|
||||
int priority;
|
||||
int weight;
|
||||
int port;
|
||||
std::string target;
|
||||
};
|
||||
typedef SGSharedPtr<SRV> SRV_ptr;
|
||||
typedef std::vector<SRV_ptr> SRV_list;
|
||||
SRV_list entries;
|
||||
private:
|
||||
std::string _service;
|
||||
std::string _protocol;
|
||||
};
|
||||
|
||||
class TXTRequest : public Request
|
||||
{
|
||||
public:
|
||||
TXTRequest( const std::string & dn );
|
||||
virtual void submit( Client * client );
|
||||
|
||||
typedef std::vector<string> TXT_list;
|
||||
typedef std::map<std::string,std::string> TXT_Attribute_map;
|
||||
TXT_list entries;
|
||||
TXT_Attribute_map attributes;
|
||||
};
|
||||
|
||||
class Client
|
||||
{
|
||||
@@ -98,10 +132,8 @@ public:
|
||||
|
||||
// void cancelRequest(const Request_ptr& r, std::string reason = std::string());
|
||||
|
||||
private:
|
||||
|
||||
class ClientPrivate;
|
||||
std::auto_ptr<ClientPrivate> d;
|
||||
std::unique_ptr<ClientPrivate> d;
|
||||
};
|
||||
|
||||
} // of namespace DNS
|
||||
|
||||
@@ -224,8 +224,7 @@ void Client::makeRequest(const Request_ptr& r)
|
||||
|
||||
r->_client = this;
|
||||
|
||||
ClientPrivate::RequestCurlMap::iterator rit = d->requests.find(r);
|
||||
assert(rit == d->requests.end());
|
||||
assert(d->requests.find(r) == d->requests.end());
|
||||
|
||||
CURL* curlRequest = curl_easy_init();
|
||||
curl_easy_setopt(curlRequest, CURLOPT_URL, r->url().c_str());
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#ifndef SG_HTTP_CLIENT_HXX
|
||||
#define SG_HTTP_CLIENT_HXX
|
||||
|
||||
#include <memory> // for std::auto_ptr
|
||||
#include <memory> // for std::unique_ptr
|
||||
#include <stdint.h> // for uint_64t
|
||||
|
||||
#include <simgear/io/HTTPFileRequest.hxx>
|
||||
@@ -125,7 +125,7 @@ private:
|
||||
friend class Request;
|
||||
|
||||
class ClientPrivate;
|
||||
std::auto_ptr<ClientPrivate> d;
|
||||
std::unique_ptr<ClientPrivate> d;
|
||||
};
|
||||
|
||||
} // of namespace HTTP
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
#include "HTTPRequest.hxx"
|
||||
|
||||
#include <simgear/misc/sgstream.hxx>
|
||||
#include <simgear/io/iostreams/sgstream.hxx>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
#include <simgear/misc/sg_dir.hxx>
|
||||
#include <simgear/io/HTTPClient.hxx>
|
||||
#include <simgear/io/sg_file.hxx>
|
||||
#include <simgear/misc/sgstream.hxx>
|
||||
#include <simgear/io/iostreams/sgstream.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include <simgear/timing/timestamp.hxx>
|
||||
|
||||
@@ -57,6 +57,8 @@ namespace simgear
|
||||
{
|
||||
}
|
||||
|
||||
virtual void cancel();
|
||||
|
||||
size_t contentSize() const
|
||||
{
|
||||
return _contentSize;
|
||||
@@ -68,7 +70,7 @@ namespace simgear
|
||||
}
|
||||
protected:
|
||||
HTTPDirectory* _directory;
|
||||
size_t _contentSize;
|
||||
size_t _contentSize = 0;
|
||||
};
|
||||
|
||||
typedef SGSharedPtr<HTTPRepoGetRequest> RepoRequestPtr;
|
||||
@@ -102,7 +104,6 @@ public:
|
||||
hashCacheDirty(false),
|
||||
p(parent),
|
||||
isUpdating(false),
|
||||
updateEverything(false),
|
||||
status(HTTPRepository::REPO_NO_ERROR),
|
||||
totalDownloaded(0)
|
||||
{ ; }
|
||||
@@ -114,14 +115,10 @@ public:
|
||||
std::string baseUrl;
|
||||
SGPath basePath;
|
||||
bool isUpdating;
|
||||
bool updateEverything;
|
||||
string_list updatePaths;
|
||||
HTTPRepository::ResultCode status;
|
||||
HTTPDirectory* rootDir;
|
||||
size_t totalDownloaded;
|
||||
|
||||
void updateWaiting();
|
||||
|
||||
HTTP::Request_ptr updateFile(HTTPDirectory* dir, const std::string& name,
|
||||
size_t sz);
|
||||
HTTP::Request_ptr updateDir(HTTPDirectory* dir, const std::string& hash,
|
||||
@@ -196,12 +193,10 @@ class HTTPDirectory
|
||||
typedef std::vector<ChildInfo> ChildInfoList;
|
||||
ChildInfoList children;
|
||||
|
||||
|
||||
public:
|
||||
HTTPDirectory(HTTPRepoPrivate* repo, const std::string& path) :
|
||||
_repository(repo),
|
||||
_relativePath(path),
|
||||
_state(DoNotUpdate)
|
||||
_relativePath(path)
|
||||
{
|
||||
assert(repo);
|
||||
|
||||
@@ -238,8 +233,6 @@ public:
|
||||
fpath.append(".dirindex");
|
||||
_repository->updatedFileContents(fpath, hash);
|
||||
|
||||
_state = Updated;
|
||||
|
||||
children.clear();
|
||||
parseDirIndex(children);
|
||||
std::sort(children.begin(), children.end());
|
||||
@@ -247,7 +240,6 @@ public:
|
||||
|
||||
void failedToUpdate(HTTPRepository::ResultCode status)
|
||||
{
|
||||
_state = UpdateFailed;
|
||||
if (_relativePath.empty()) {
|
||||
// root dir failed
|
||||
_repository->failedToGetRootIndex(status);
|
||||
@@ -261,7 +253,7 @@ public:
|
||||
if (_repository->installedCopyPath.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
string_list indexNames = indexChildren();
|
||||
const_string_list_iterator nameIt = indexNames.begin();
|
||||
for (; nameIt != indexNames.end(); ++nameIt) {
|
||||
@@ -284,7 +276,7 @@ public:
|
||||
}
|
||||
|
||||
SG_LOG(SG_TERRASYNC, SG_BULK, "new child, copying existing file" << cp << p);
|
||||
|
||||
|
||||
SGBinaryFile src(cp);
|
||||
SGBinaryFile dst(p);
|
||||
src.open(SG_IO_IN);
|
||||
@@ -306,11 +298,7 @@ public:
|
||||
|
||||
void updateChildrenBasedOnHash()
|
||||
{
|
||||
// if we got here for a dir which is still updating or excluded
|
||||
// from updates, just bail out right now.
|
||||
if (_state != Updated) {
|
||||
return;
|
||||
}
|
||||
//SG_LOG(SG_TERRASYNC, SG_DEBUG, "updated children for:" << relativePath());
|
||||
|
||||
copyInstalledChildren();
|
||||
|
||||
@@ -345,9 +333,6 @@ public:
|
||||
SG_LOG(SG_TERRASYNC, SG_DEBUG, "file exists hash is good:" << it->file() );
|
||||
if (c->type == ChildInfo::DirectoryType) {
|
||||
HTTPDirectory* childDir = childDirectory(it->file());
|
||||
if (childDir->_state == NotUpdated) {
|
||||
childDir->_state = Updated;
|
||||
}
|
||||
childDir->updateChildrenBasedOnHash();
|
||||
}
|
||||
}
|
||||
@@ -365,95 +350,6 @@ public:
|
||||
scheduleUpdates(toBeUpdated);
|
||||
}
|
||||
|
||||
void markAsUpToDate()
|
||||
{
|
||||
_state = Updated;
|
||||
}
|
||||
|
||||
void markAsUpdating()
|
||||
{
|
||||
assert(_state == NotUpdated);
|
||||
_state = HTTPDirectory::UpdateInProgress;
|
||||
}
|
||||
|
||||
void markAsEnabled()
|
||||
{
|
||||
// assert because this should only get invoked on newly created
|
||||
// directory objects which are inside the sub-tree(s) to be updated
|
||||
assert(_state == DoNotUpdate);
|
||||
_state = NotUpdated;
|
||||
}
|
||||
|
||||
void markSubtreeAsNeedingUpdate()
|
||||
{
|
||||
if (_state == Updated) {
|
||||
_state = NotUpdated; // reset back to not-updated
|
||||
}
|
||||
|
||||
ChildInfoList::iterator cit;
|
||||
for (cit = children.begin(); cit != children.end(); ++cit) {
|
||||
if (cit->type == ChildInfo::DirectoryType) {
|
||||
HTTPDirectory* childDir = childDirectory(cit->name);
|
||||
childDir->markSubtreeAsNeedingUpdate();
|
||||
}
|
||||
} // of child iteration
|
||||
}
|
||||
|
||||
void markSubtreeAsEnabled()
|
||||
{
|
||||
if (_state == DoNotUpdate) {
|
||||
markAsEnabled();
|
||||
}
|
||||
|
||||
ChildInfoList::iterator cit;
|
||||
for (cit = children.begin(); cit != children.end(); ++cit) {
|
||||
if (cit->type == ChildInfo::DirectoryType) {
|
||||
HTTPDirectory* childDir = childDirectory(cit->name);
|
||||
childDir->markSubtreeAsEnabled();
|
||||
}
|
||||
} // of child iteration
|
||||
}
|
||||
|
||||
|
||||
void markAncestorChainAsEnabled()
|
||||
{
|
||||
if (_state == DoNotUpdate) {
|
||||
markAsEnabled();
|
||||
}
|
||||
|
||||
if (_relativePath.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string prPath = SGPath(_relativePath).dir();
|
||||
if (prPath.empty()) {
|
||||
_repository->rootDir->markAncestorChainAsEnabled();
|
||||
} else {
|
||||
HTTPDirectory* prDir = _repository->getOrCreateDirectory(prPath);
|
||||
prDir->markAncestorChainAsEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
void updateIfWaiting(const std::string& hash, size_t sz)
|
||||
{
|
||||
if (_state == NotUpdated) {
|
||||
_repository->updateDir(this, hash, sz);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((_state == DoNotUpdate) || (_state == UpdateInProgress)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ChildInfoList::iterator cit;
|
||||
for (cit = children.begin(); cit != children.end(); ++cit) {
|
||||
if (cit->type == ChildInfo::DirectoryType) {
|
||||
HTTPDirectory* childDir = childDirectory(cit->name);
|
||||
childDir->updateIfWaiting(cit->hash, cit->sizeInBytes);
|
||||
}
|
||||
} // of child iteration
|
||||
}
|
||||
|
||||
HTTPDirectory* childDirectory(const std::string& name)
|
||||
{
|
||||
std::string childPath = relativePath().empty() ? name : relativePath() + "/" + name;
|
||||
@@ -494,11 +390,6 @@ public:
|
||||
_repository->updateFile(this, *it, cit->sizeInBytes);
|
||||
} else {
|
||||
HTTPDirectory* childDir = childDirectory(*it);
|
||||
if (childDir->_state == DoNotUpdate) {
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "scheduleUpdate, child:" << *it << " is marked do not update so skipping");
|
||||
continue;
|
||||
}
|
||||
|
||||
_repository->updateDir(childDir, cit->hash, cit->sizeInBytes);
|
||||
}
|
||||
}
|
||||
@@ -603,6 +494,11 @@ private:
|
||||
continue; // ignore path, next line
|
||||
}
|
||||
|
||||
if( typeData == "time" && tokens.size() > 1 ) {
|
||||
SG_LOG(SG_TERRASYNC, SG_INFO, ".dirindex at '" << p.str() << "' timestamp: " << tokens[1] );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( tokens.size() < 3 ) {
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "malformed .dirindex file: not enough tokens in line '" << line << "' (ignoring line)" );
|
||||
continue;
|
||||
@@ -612,6 +508,14 @@ private:
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "malformed .dirindex file: invalid type in line '" << line << "', expected 'd' or 'f', (ignoring line)" );
|
||||
continue;
|
||||
}
|
||||
|
||||
// security: prevent writing outside the repository via ../../.. filenames
|
||||
// (valid filenames never contain / - subdirectories have their own .dirindex)
|
||||
if ((tokens[1] == "..") || (tokens[1].find_first_of("/\\") != std::string::npos)) {
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "malformed .dirindex file: invalid filename in line '" << line << "', (ignoring line)" );
|
||||
continue;
|
||||
}
|
||||
|
||||
children.push_back(ChildInfo(typeData == "f" ? ChildInfo::FileType : ChildInfo::DirectoryType, tokens[1], tokens[2]));
|
||||
|
||||
if (tokens.size() > 3) {
|
||||
@@ -656,16 +560,7 @@ private:
|
||||
HTTPRepoPrivate* _repository;
|
||||
std::string _relativePath; // in URL and file-system space
|
||||
|
||||
typedef enum
|
||||
{
|
||||
NotUpdated,
|
||||
UpdateInProgress,
|
||||
Updated,
|
||||
UpdateFailed,
|
||||
DoNotUpdate
|
||||
} State;
|
||||
|
||||
State _state;
|
||||
};
|
||||
|
||||
HTTPRepository::HTTPRepository(const SGPath& base, HTTP::Client *cl) :
|
||||
@@ -703,38 +598,14 @@ SGPath HTTPRepository::fsBase() const
|
||||
|
||||
void HTTPRepository::update()
|
||||
{
|
||||
_d->rootDir->markSubtreeAsNeedingUpdate();
|
||||
_d->updateWaiting();
|
||||
}
|
||||
|
||||
void HTTPRepository::setEntireRepositoryMode()
|
||||
{
|
||||
if (!_d->updateEverything) {
|
||||
// this is a one-way decision
|
||||
_d->updateEverything = true;
|
||||
}
|
||||
|
||||
// probably overkill but not expensive so let's check everything
|
||||
// we have in case someone did something funky and switched from partial
|
||||
// to 'whole repo' updating.
|
||||
_d->rootDir->markSubtreeAsEnabled();
|
||||
}
|
||||
|
||||
|
||||
void HTTPRepository::addSubpath(const std::string& relPath)
|
||||
{
|
||||
if (_d->updateEverything) {
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "called HTTPRepository::addSubpath but updating everything");
|
||||
if (_d->isUpdating) {
|
||||
return;
|
||||
}
|
||||
|
||||
_d->updatePaths.push_back(relPath);
|
||||
|
||||
HTTPDirectory* dir = _d->getOrCreateDirectory(relPath);
|
||||
dir->markSubtreeAsEnabled();
|
||||
dir->markAncestorChainAsEnabled();
|
||||
|
||||
_d->updateWaiting();
|
||||
_d->status = REPO_NO_ERROR;
|
||||
_d->isUpdating = true;
|
||||
_d->failures.clear();
|
||||
_d->updateDir(_d->rootDir, std::string(), 0);
|
||||
}
|
||||
|
||||
bool HTTPRepository::isDoingSync() const
|
||||
@@ -756,7 +627,12 @@ size_t HTTPRepository::bytesToDownload() const
|
||||
}
|
||||
|
||||
for (r = _d->activeRequests.begin(); r != _d->activeRequests.end(); ++r) {
|
||||
result += (*r)->contentSize() - (*r)->responseBytesReceived();
|
||||
if ((*r)->contentSize() > 0) {
|
||||
// Content size for root dirindex of a repository is zero,
|
||||
// and returing a negative value breaks everyting, so just ignore
|
||||
// it
|
||||
result += (*r)->contentSize() - (*r)->responseBytesReceived();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -789,6 +665,12 @@ HTTPRepository::failure() const
|
||||
return _d->status;
|
||||
}
|
||||
|
||||
void HTTPRepoGetRequest::cancel()
|
||||
{
|
||||
_directory->repository()->http->cancelRequest(this, "Reposiotry cancelled");
|
||||
_directory = 0;
|
||||
}
|
||||
|
||||
class FileGetRequest : public HTTPRepoGetRequest
|
||||
{
|
||||
public:
|
||||
@@ -820,7 +702,6 @@ HTTPRepository::failure() const
|
||||
virtual void onDone()
|
||||
{
|
||||
file->close();
|
||||
|
||||
if (responseCode() == 200) {
|
||||
std::string hash = strutils::encodeHex(sha1_result(&hashContext), HASH_LENGTH);
|
||||
_directory->didUpdateFile(fileName, hash, contentSize());
|
||||
@@ -857,7 +738,7 @@ HTTPRepository::failure() const
|
||||
std::string fileName; // if empty, we're getting the directory itself
|
||||
SGPath pathInRepo;
|
||||
simgear::sha1nfo hashContext;
|
||||
std::auto_ptr<SGBinaryFile> file;
|
||||
std::unique_ptr<SGBinaryFile> file;
|
||||
};
|
||||
|
||||
class DirGetRequest : public HTTPRepoGetRequest
|
||||
@@ -917,8 +798,8 @@ HTTPRepository::failure() const
|
||||
of.write(body.data(), body.size());
|
||||
of.close();
|
||||
_directory->dirIndexUpdated(hash);
|
||||
} else {
|
||||
_directory->markAsUpToDate();
|
||||
|
||||
//SG_LOG(SG_TERRASYNC, SG_INFO, "updated dir index " << _directory->absolutePath());
|
||||
}
|
||||
|
||||
_directory->repository()->totalDownloaded += contentSize();
|
||||
@@ -929,7 +810,7 @@ HTTPRepository::failure() const
|
||||
SGTimeStamp st;
|
||||
st.stamp();
|
||||
_directory->updateChildrenBasedOnHash();
|
||||
SG_LOG(SG_TERRASYNC, SG_INFO, "after update of:" << _directory->absolutePath() << " child update took:" << st.elapsedMSec());
|
||||
SG_LOG(SG_TERRASYNC, SG_DEBUG, "after update of:" << _directory->absolutePath() << " child update took:" << st.elapsedMSec());
|
||||
} catch (sg_exception& ) {
|
||||
_directory->failedToUpdate(HTTPRepository::REPO_ERROR_IO);
|
||||
}
|
||||
@@ -994,7 +875,6 @@ HTTPRepository::failure() const
|
||||
|
||||
HTTP::Request_ptr HTTPRepoPrivate::updateDir(HTTPDirectory* dir, const std::string& hash, size_t sz)
|
||||
{
|
||||
dir->markAsUpdating();
|
||||
RepoRequestPtr r(new DirGetRequest(dir, hash));
|
||||
r->setContentSize(sz);
|
||||
makeRequest(r);
|
||||
@@ -1161,25 +1041,6 @@ HTTPRepository::failure() const
|
||||
|
||||
HTTPDirectory* d = new HTTPDirectory(this, path);
|
||||
directories.push_back(d);
|
||||
if (updateEverything) {
|
||||
d->markAsEnabled();
|
||||
} else {
|
||||
string_list::const_iterator s;
|
||||
bool shouldUpdate = false;
|
||||
|
||||
for (s = updatePaths.begin(); s != updatePaths.end(); ++s) {
|
||||
size_t minLen = std::min(path.size(), s->size());
|
||||
if (s->compare(0, minLen, path, 0, minLen) == 0) {
|
||||
shouldUpdate = true;
|
||||
break;
|
||||
}
|
||||
} // of paths iteration
|
||||
|
||||
if (shouldUpdate) {
|
||||
d->markAsEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
@@ -1239,7 +1100,7 @@ HTTPRepository::failure() const
|
||||
|
||||
void HTTPRepoPrivate::failedToGetRootIndex(HTTPRepository::ResultCode st)
|
||||
{
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "Failed to get root of repo:" << baseUrl);
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "Failed to get root of repo:" << baseUrl << " " << st);
|
||||
status = st;
|
||||
}
|
||||
|
||||
@@ -1275,22 +1136,4 @@ HTTPRepository::failure() const
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "failed to update entry:" << relativePath << " code:" << fileStatus);
|
||||
}
|
||||
|
||||
void HTTPRepoPrivate::updateWaiting()
|
||||
{
|
||||
if (!isUpdating) {
|
||||
status = HTTPRepository::REPO_NO_ERROR;
|
||||
isUpdating = true;
|
||||
failures.clear();
|
||||
}
|
||||
|
||||
// find to-be-updated sub-trees and kick them off
|
||||
rootDir->updateIfWaiting(std::string(), 0);
|
||||
|
||||
// maybe there was nothing to do
|
||||
if (activeRequests.empty()) {
|
||||
status = HTTPRepository::REPO_NO_ERROR;
|
||||
isUpdating = false;
|
||||
}
|
||||
}
|
||||
|
||||
} // of namespace simgear
|
||||
|
||||
@@ -57,13 +57,6 @@ public:
|
||||
|
||||
virtual void update();
|
||||
|
||||
/**
|
||||
* set if we should sync the entire repository
|
||||
*/
|
||||
void setEntireRepositoryMode();
|
||||
|
||||
void addSubpath(const std::string& relPath);
|
||||
|
||||
virtual bool isDoingSync() const;
|
||||
|
||||
virtual ResultCode failure() const;
|
||||
@@ -80,7 +73,7 @@ public:
|
||||
private:
|
||||
bool isBare() const;
|
||||
|
||||
std::auto_ptr<HTTPRepoPrivate> _d;
|
||||
std::unique_ptr<HTTPRepoPrivate> _d;
|
||||
};
|
||||
|
||||
} // of namespace simgear
|
||||
|
||||
@@ -132,15 +132,15 @@ Request::HTTPVersion decodeHTTPVersion(const std::string& v)
|
||||
//------------------------------------------------------------------------------
|
||||
void Request::responseStart(const std::string& r)
|
||||
{
|
||||
const int maxSplit = 2; // HTTP/1.1 nnn reason-string
|
||||
const int maxSplit = 2; // HTTP/1.1 nnn reason-string?
|
||||
string_list parts = strutils::split(r, NULL, maxSplit);
|
||||
if (parts.size() != 3) {
|
||||
if (parts.size() < 2) {
|
||||
throw sg_io_exception("bad HTTP response:" + r);
|
||||
}
|
||||
|
||||
_responseVersion = decodeHTTPVersion(parts[0]);
|
||||
_responseStatus = strutils::to_int(parts[1]);
|
||||
_responseReason = parts[2];
|
||||
_responseReason = parts.size() > 2 ? parts[2] : "";
|
||||
|
||||
setReadyState(STATUS_RECEIVED);
|
||||
}
|
||||
|
||||
23
simgear/io/iostreams/CMakeLists.txt
Normal file
23
simgear/io/iostreams/CMakeLists.txt
Normal file
@@ -0,0 +1,23 @@
|
||||
include (SimGearComponent)
|
||||
|
||||
set(HEADERS
|
||||
sgstream.hxx
|
||||
gzfstream.hxx
|
||||
gzcontainerfile.hxx
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
sgstream.cxx
|
||||
gzfstream.cxx
|
||||
gzcontainerfile.cxx
|
||||
)
|
||||
|
||||
simgear_component(IOStreams io/iostreams "${SOURCES}" "${HEADERS}")
|
||||
|
||||
if(ENABLE_TESTS)
|
||||
|
||||
add_executable(test_streams sgstream_test.cxx )
|
||||
target_link_libraries(test_streams ${TEST_LIBS})
|
||||
add_test(streams ${EXECUTABLE_OUTPUT_PATH}/test_streams)
|
||||
|
||||
endif(ENABLE_TESTS)
|
||||
@@ -22,7 +22,7 @@
|
||||
#define GZ_CONTAINER_FILE_HXX
|
||||
|
||||
#include <string>
|
||||
#include <simgear/misc/sgstream.hxx>
|
||||
#include <simgear/io/iostreams/sgstream.hxx>
|
||||
|
||||
class SGPropertyNode;
|
||||
|
||||
@@ -30,8 +30,11 @@
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
|
||||
#include "zfstream.hxx"
|
||||
#include <zlib.h>
|
||||
#include "gzfstream.hxx"
|
||||
|
||||
//
|
||||
// Construct a gzfilebuf object.
|
||||
@@ -180,6 +183,27 @@ gzfilebuf::setcompressionstrategy( int comp_strategy )
|
||||
return gzsetparams(file, -2, comp_strategy);
|
||||
}
|
||||
|
||||
z_off_t
|
||||
gzfilebuf::approxOffset() {
|
||||
z_off_t res = gzoffset(file);
|
||||
|
||||
if (res == -1) {
|
||||
int errnum;
|
||||
std::string errMsg = "gzoffset() error: ";
|
||||
const char *gzMsg = gzerror(file, &errnum);
|
||||
|
||||
if (errnum == Z_ERRNO) {
|
||||
errMsg += simgear::strutils::error_string(errno);
|
||||
} else {
|
||||
errMsg += std::string(gzMsg);
|
||||
}
|
||||
|
||||
SG_LOG( SG_GENERAL, SG_ALERT, errMsg );
|
||||
throw sg_io_exception(errMsg);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::streampos
|
||||
gzfilebuf::seekoff( std::streamoff, ios_seekdir, ios_openmode )
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* \file zfstream.hxx
|
||||
* \file gzfstream.hxx
|
||||
* A C++ I/O streams interface to the zlib gz* functions.
|
||||
*/
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
#ifndef _zfstream_hxx
|
||||
#define _zfstream_hxx
|
||||
#ifndef _gzfstream_hxx
|
||||
#define _gzfstream_hxx
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
@@ -89,6 +89,15 @@ public:
|
||||
/** @return true if open, false otherwise */
|
||||
bool is_open() const { return (file != NULL); }
|
||||
|
||||
/**
|
||||
* @return the current offset in the file being read or written.
|
||||
* The offset corresponds to compressed data if the file is compressed,
|
||||
* and is influenced by buffering performed in zlib, hence the "approx"
|
||||
* qualifier. It should be suitable for progress indicators and such,
|
||||
* though.
|
||||
*/
|
||||
z_off_t approxOffset();
|
||||
|
||||
/** @return stream position */
|
||||
virtual std::streampos seekoff( std::streamoff off, ios_seekdir way, ios_openmode which );
|
||||
|
||||
@@ -155,4 +164,4 @@ struct gzofstream_base
|
||||
gzfilebuf gzbuf;
|
||||
};
|
||||
|
||||
#endif // _zfstream_hxx
|
||||
#endif // _gzfstream_hxx
|
||||
@@ -25,6 +25,8 @@
|
||||
#include <ctype.h> // isspace()
|
||||
#include <cerrno>
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
#include "sgstream.hxx"
|
||||
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
@@ -41,10 +43,11 @@ sg_gzifstream::sg_gzifstream()
|
||||
//
|
||||
// Open a possibly gzipped file for reading.
|
||||
//
|
||||
sg_gzifstream::sg_gzifstream( const SGPath& name, ios_openmode io_mode )
|
||||
sg_gzifstream::sg_gzifstream( const SGPath& name, ios_openmode io_mode,
|
||||
bool use_exact_name )
|
||||
: istream(&gzbuf)
|
||||
{
|
||||
this->open( name, io_mode );
|
||||
this->open( name, io_mode, use_exact_name );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -60,17 +63,20 @@ sg_gzifstream::sg_gzifstream( int fd, ios_openmode io_mode )
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Open a possibly gzipped file for reading.
|
||||
// If the initial open fails and the filename has a ".gz" extension then
|
||||
// remove the extension and try again.
|
||||
// If the initial open fails and the filename doesn't have a ".gz" extension
|
||||
// then append ".gz" and try again.
|
||||
// If 'use_exact_name' is true, just try to open the indicated file, nothing
|
||||
// else. Otherwise:
|
||||
// - if the initial open fails and the filename has a ".gz" extension, then
|
||||
// remove it and try again;
|
||||
// - if the initial open fails and the filename doesn't have a ".gz"
|
||||
// extension, then append ".gz" and try again.
|
||||
//
|
||||
void
|
||||
sg_gzifstream::open( const SGPath& name, ios_openmode io_mode )
|
||||
sg_gzifstream::open( const SGPath& name, ios_openmode io_mode,
|
||||
bool use_exact_name )
|
||||
{
|
||||
std::string s = name.utf8Str();
|
||||
gzbuf.open( s.c_str(), io_mode );
|
||||
if ( ! gzbuf.is_open() )
|
||||
if ( ! (gzbuf.is_open() || use_exact_name) )
|
||||
{
|
||||
if ( s.substr( s.length() - 3, 3 ) == ".gz" )
|
||||
{
|
||||
@@ -95,6 +101,11 @@ sg_gzifstream::attach( int fd, ios_openmode io_mode )
|
||||
gzbuf.attach( fd, io_mode );
|
||||
}
|
||||
|
||||
z_off_t
|
||||
sg_gzifstream::approxOffset() {
|
||||
return gzbuf.approxOffset();
|
||||
}
|
||||
|
||||
//
|
||||
// Manipulators
|
||||
//
|
||||
@@ -39,7 +39,8 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <simgear/misc/zfstream.hxx>
|
||||
#include <zlib.h>
|
||||
#include <simgear/io/iostreams/gzfstream.hxx>
|
||||
|
||||
class SGPath;
|
||||
|
||||
@@ -53,13 +54,15 @@ public:
|
||||
sg_gzifstream();
|
||||
|
||||
/**
|
||||
* Constructor that attempt to open a file with and without
|
||||
* ".gz" extension.
|
||||
* Constructor that attempts to open a file.
|
||||
* @param name name of file
|
||||
* @param io_mode file open mode(s) "or'd" together
|
||||
* @param use_exact_name if false, try to add or remove a ".gz" extension
|
||||
* in case the indicated file can't be opened
|
||||
*/
|
||||
sg_gzifstream( const SGPath& name,
|
||||
ios_openmode io_mode = ios_in | ios_binary );
|
||||
ios_openmode io_mode = ios_in | ios_binary,
|
||||
bool use_exact_name = false );
|
||||
|
||||
/**
|
||||
* Constructor that attaches itself to an existing file descriptor.
|
||||
@@ -69,12 +72,15 @@ public:
|
||||
sg_gzifstream( int fd, ios_openmode io_mode = ios_in|ios_binary );
|
||||
|
||||
/**
|
||||
* Attempt to open a file with and without ".gz" extension.
|
||||
* Attempt to open a file.
|
||||
* @param name name of file
|
||||
* @param io_mode file open mode(s) "or'd" together
|
||||
* @param use_exact_name if false, try to add or remove a ".gz" extension
|
||||
* in case the indicated file can't be opened
|
||||
*/
|
||||
void open( const SGPath& name,
|
||||
ios_openmode io_mode = ios_in|ios_binary );
|
||||
ios_openmode io_mode = ios_in|ios_binary,
|
||||
bool use_exact_name = false );
|
||||
|
||||
/**
|
||||
* Attach to an existing file descriptor.
|
||||
@@ -91,6 +97,15 @@ public:
|
||||
/** @return true if the file is successfully opened, false otherwise. */
|
||||
bool is_open() { return gzbuf.is_open(); }
|
||||
|
||||
/**
|
||||
* @return the current offset in the file being read or written.
|
||||
* The offset corresponds to compressed data if the file is compressed,
|
||||
* and is influenced by buffering performed in zlib, hence the "approx"
|
||||
* qualifier. It should be suitable for progress indicators and such,
|
||||
* though.
|
||||
*/
|
||||
z_off_t approxOffset();
|
||||
|
||||
private:
|
||||
// Not defined!
|
||||
sg_gzifstream( const sg_gzifstream& );
|
||||
@@ -1,52 +1,53 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <cstdlib> // for EXIT_SUCCESS
|
||||
|
||||
#include <cstdlib> // for EXIT_FAILURE
|
||||
|
||||
using std::ofstream;
|
||||
using std::string;
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
#include <simgear/misc/sgstream.hxx>
|
||||
#include <simgear/misc/test_macros.hxx>
|
||||
#include <simgear/io/iostreams/sgstream.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/misc/sg_dir.hxx>
|
||||
|
||||
int main()
|
||||
{
|
||||
const char* fileName = "testfile";
|
||||
const string fileName = "testfile";
|
||||
simgear::Dir tmpDir = simgear::Dir::tempDir("FlightGear");
|
||||
tmpDir.setRemoveOnDestroy();
|
||||
SGPath p(tmpDir.path() / fileName);
|
||||
|
||||
{
|
||||
ofstream f;
|
||||
f.open(fileName, std::ios::binary | std::ios::trunc | std::ios::out);
|
||||
sg_ofstream f(p, std::ios::binary | std::ios::trunc | std::ios::out);
|
||||
f.write("first line ends with line-feed\n"
|
||||
"second line ends with just a cr\r"
|
||||
"third line ends with both\r\n"
|
||||
"fourth line as well\r\n"
|
||||
"fifth line is another CR/LF line\r\n"
|
||||
"end of test\r\n", 158);
|
||||
f.close();
|
||||
}
|
||||
}
|
||||
|
||||
SGPath p(fileName);
|
||||
sg_gzifstream sg(p);
|
||||
std::string stuff;
|
||||
sg >> skipeol;
|
||||
sg >> stuff;
|
||||
if (stuff != "second") return EXIT_FAILURE;
|
||||
SG_CHECK_EQUAL(stuff, "second");
|
||||
cout << "Detection of LF works." << endl;
|
||||
|
||||
sg >> skipeol;
|
||||
sg >> stuff;
|
||||
if (stuff != "third") return EXIT_FAILURE;
|
||||
SG_CHECK_EQUAL(stuff, "third");
|
||||
cout << "Detection of CR works." << endl;
|
||||
|
||||
sg >> skipeol;
|
||||
sg >> stuff;
|
||||
if (stuff != "fourth") return EXIT_FAILURE;
|
||||
SG_CHECK_EQUAL(stuff, "fourth");
|
||||
cout << "Detection of CR/LF works." << endl;
|
||||
|
||||
sg >> skipeol;
|
||||
sg >> skipeol;
|
||||
sg >> stuff;
|
||||
if (stuff != "end") return EXIT_FAILURE;
|
||||
SG_CHECK_EQUAL(stuff, "end");
|
||||
cout << "Detection of 2 following CR/LF lines works." << endl;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
717
simgear/io/iostreams/zlibstream.cxx
Normal file
717
simgear/io/iostreams/zlibstream.cxx
Normal file
@@ -0,0 +1,717 @@
|
||||
// -*- coding: utf-8 -*-
|
||||
//
|
||||
// zlibstream.cxx --- IOStreams classes for working with RFC 1950 and RFC 1952
|
||||
// compression formats (respectively known as the zlib and
|
||||
// gzip formats)
|
||||
//
|
||||
// Copyright (C) 2017 Florent Rougon
|
||||
//
|
||||
// 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 of the License, 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, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
#include <string>
|
||||
#include <ios> // std::streamsize
|
||||
#include <istream>
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
#include <limits> // std::numeric_limits
|
||||
#include <cstddef> // std::size_t, std::ptrdiff_t
|
||||
#include <cassert>
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
#include <simgear/io/iostreams/zlibstream.hxx>
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
using std::string;
|
||||
using traits = std::char_traits<char>;
|
||||
|
||||
|
||||
// Private utility function
|
||||
static string zlibErrorMessage(const z_stream& zstream, int errorCode)
|
||||
{
|
||||
string res;
|
||||
std::unordered_map<int, string> errorCodeToMessageMap = {
|
||||
{Z_OK, "zlib: no error (code Z_OK)"},
|
||||
{Z_STREAM_END, "zlib stream end"},
|
||||
{Z_NEED_DICT, "zlib: Z_NEED_DICT"},
|
||||
{Z_STREAM_ERROR, "zlib stream error"},
|
||||
{Z_DATA_ERROR, "zlib data error"},
|
||||
{Z_MEM_ERROR, "zlib memory error"},
|
||||
{Z_BUF_ERROR, "zlib buffer error"},
|
||||
{Z_VERSION_ERROR, "zlib version error"}
|
||||
};
|
||||
|
||||
if (errorCode == Z_ERRNO) {
|
||||
res = simgear::strutils::error_string(errno);
|
||||
} else if (zstream.msg != nullptr) {
|
||||
// Caution: this only works if the zstream structure hasn't been
|
||||
// deallocated!
|
||||
res = "zlib: " + string(zstream.msg);
|
||||
} else {
|
||||
try {
|
||||
res = errorCodeToMessageMap.at(errorCode);
|
||||
} catch (const std::out_of_range&) {
|
||||
res = string("unknown zlib error (code " + std::to_string(errorCode) +
|
||||
")");
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// Return the largest value that can be represented with zlib's uInt type,
|
||||
// which is the type of z_stream.avail_in and z_stream.avail_out, hence the
|
||||
// function name.
|
||||
static std::size_t zlibMaxChunkSize()
|
||||
{
|
||||
uLong flags = ::zlibCompileFlags();
|
||||
std::size_t res;
|
||||
|
||||
switch (flags & 0x3) {
|
||||
case 0x3:
|
||||
SG_LOG(SG_IO, SG_WARN,
|
||||
"Unknown size for zlib's uInt type (code 3). Will assume 64 bits, "
|
||||
"but the actual value is probably higher.");
|
||||
// No 'break' here, this is intentional.
|
||||
case 0x2:
|
||||
res = 0xFFFFFFFFFFFFFFFF; // 2^64 - 1
|
||||
break;
|
||||
case 0x1:
|
||||
res = 0xFFFFFFFF; // 2^32 - 1
|
||||
break;
|
||||
case 0x0:
|
||||
res = 0xFFFF; // 2^16 - 1
|
||||
break;
|
||||
default:
|
||||
throw std::logic_error("It should be impossible to get here.");
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static const std::size_t zlibMaxChunk = ::zlibMaxChunkSize();
|
||||
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
// ***************************************************************************
|
||||
// * ZlibAbstractIStreambuf class *
|
||||
// ***************************************************************************
|
||||
|
||||
// Common initialization. Subclasses must complete the z_stream struct
|
||||
// initialization with a call to deflateInit2() or inflateInit2(), typically.
|
||||
ZlibAbstractIStreambuf::ZlibAbstractIStreambuf(std::istream& iStream,
|
||||
const SGPath& path,
|
||||
char* inBuf,
|
||||
std::size_t inBufSize,
|
||||
char *outBuf,
|
||||
std::size_t outBufSize,
|
||||
std::size_t putbackSize)
|
||||
: _iStream(iStream),
|
||||
_path(path),
|
||||
_inBuf(inBuf),
|
||||
_inBufSize(inBufSize),
|
||||
_outBuf(outBuf),
|
||||
_outBufSize(outBufSize),
|
||||
_putbackSize(putbackSize)
|
||||
|
||||
{
|
||||
assert(_inBufSize > 0);
|
||||
assert(_putbackSize >= 0); // guaranteed unless the type is changed...
|
||||
assert(_putbackSize < _outBufSize);
|
||||
|
||||
if (_inBuf == nullptr) {
|
||||
_inBuf = new char[_inBufSize];
|
||||
_inBufMustBeFreed = true;
|
||||
}
|
||||
|
||||
if (_outBuf == nullptr) {
|
||||
_outBuf = new char[_outBufSize];
|
||||
_outBufMustBeFreed = true;
|
||||
}
|
||||
|
||||
_inBufEndPtr = _inBuf;
|
||||
// The input buffer is empty.
|
||||
_zstream.next_in = reinterpret_cast<unsigned char *>(_inBuf);
|
||||
_zstream.avail_in = 0;
|
||||
// ZLib's documentation says its init functions such as inflateInit2() might
|
||||
// consume stream input, therefore let's fill the input buffer now. This
|
||||
// way, constructors of derived classes just have to call the appropriate
|
||||
// ZLib init function: the data will already be in place.
|
||||
getInputData();
|
||||
|
||||
// Force underflow() of the stream buffer on the first read. We could use
|
||||
// some other value, but I avoid nullptr in order to be sure we can always
|
||||
// reliably compare the three pointers with < and >, as well as compute the
|
||||
// difference between any two of them.
|
||||
setg(_outBuf, _outBuf, _outBuf);
|
||||
}
|
||||
|
||||
ZlibAbstractIStreambuf::~ZlibAbstractIStreambuf()
|
||||
{
|
||||
if (_inBufMustBeFreed) {
|
||||
delete[] _inBuf;
|
||||
}
|
||||
|
||||
if (_outBufMustBeFreed) {
|
||||
delete[] _outBuf;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill or refill the output buffer, and update the three pointers
|
||||
// corresponding to eback(), gptr() and egptr().
|
||||
int ZlibAbstractIStreambuf::underflow()
|
||||
{
|
||||
if (_allFinished) {
|
||||
return traits::eof();
|
||||
}
|
||||
|
||||
// According to the C++11 standard: “The public members of basic_streambuf
|
||||
// call this virtual function only if gptr() is null or gptr() >= egptr()”.
|
||||
// Still, it seems some people do the following or similar (maybe in case
|
||||
// underflow() is called “incorrectly”?). See for instance N. Josuttis, The
|
||||
// C++ Standard Library (1st edition), p. 584. One sure thing is that it
|
||||
// can't hurt (except performance, marginally), so let's do it.
|
||||
if (gptr() < egptr()) {
|
||||
return traits::to_int_type(*gptr());
|
||||
}
|
||||
|
||||
assert(gptr() == egptr());
|
||||
assert(egptr() - eback() >= 0);
|
||||
std::size_t nbPutbackChars = std::min(
|
||||
static_cast<std::size_t>(egptr() - eback()), // OK because egptr() >= eback()
|
||||
_putbackSize);
|
||||
std::copy(egptr() - nbPutbackChars, egptr(),
|
||||
_outBuf + _putbackSize - nbPutbackChars);
|
||||
|
||||
setg(_outBuf + _putbackSize - nbPutbackChars, // start of putback area
|
||||
_outBuf + _putbackSize, // start of obtained data
|
||||
fillOutputBuffer()); // one-past-end of obtained data
|
||||
|
||||
return (gptr() == egptr()) ? traits::eof() : traits::to_int_type(*gptr());
|
||||
}
|
||||
|
||||
// Simple utility method for fillOutputBuffer(), used to improve readability.
|
||||
// Return the remaining space available in the output buffer, where zlib can
|
||||
// write.
|
||||
std::size_t
|
||||
ZlibAbstractIStreambuf::fOB_remainingSpace(unsigned char* nextOutPtr) const
|
||||
{
|
||||
std::ptrdiff_t remainingSpaceInOutBuf = _outBuf + _outBufSize -
|
||||
reinterpret_cast<char*>(nextOutPtr);
|
||||
assert(remainingSpaceInOutBuf >= 0);
|
||||
|
||||
return static_cast<std::size_t>(remainingSpaceInOutBuf);
|
||||
}
|
||||
|
||||
// Simple method for dealing with the Z_BUF_ERROR code that may be returned
|
||||
// by zlib's deflate() and inflate() methods.
|
||||
[[ noreturn ]] void ZlibAbstractIStreambuf::handleZ_BUF_ERROR() const
|
||||
{
|
||||
switch (operationType()) {
|
||||
case OPERATION_TYPE_DECOMPRESSION:
|
||||
{
|
||||
string message = (_path.isNull()) ?
|
||||
"Got Z_BUF_ERROR from zlib while decompressing a stream. The stream "
|
||||
"was probably incomplete."
|
||||
:
|
||||
"Got Z_BUF_ERROR from zlib during decompression. The compressed stream "
|
||||
"was probably incomplete.";
|
||||
// When _path.isNull(), sg_location(_path) is equivalent to sg_location()
|
||||
throw sg_io_exception(message, sg_location(_path));
|
||||
}
|
||||
case OPERATION_TYPE_COMPRESSION:
|
||||
throw std::logic_error(
|
||||
"Called ZlibAbstractIStreambuf::handleZ_BUF_ERROR() with "
|
||||
"operationType() == OPERATION_TYPE_DECOMPRESSION");
|
||||
default:
|
||||
throw std::logic_error(
|
||||
"Unexpected operationType() in "
|
||||
"ZlibAbstractIStreambuf::handleZ_BUF_ERROR(): " +
|
||||
std::to_string(operationType()));
|
||||
}
|
||||
}
|
||||
|
||||
// Fill or refill the output buffer. Return a pointer to the first unused char
|
||||
// in _outBuf (i.e., right after the data written by this method, if any;
|
||||
// otherwise: _outBuf + _putbackSize).
|
||||
char* ZlibAbstractIStreambuf::fillOutputBuffer()
|
||||
{
|
||||
bool allInputRead = false;
|
||||
std::size_t remainingSpaceInOutBuf;
|
||||
int retCode;
|
||||
|
||||
// We have to do these unpleasant casts, because zlib uses pointers to
|
||||
// unsigned char for its input and output buffers, while the IOStreams
|
||||
// library in the C++ standard uses plain char pointers...
|
||||
_zstream.next_out = reinterpret_cast<unsigned char*>(_outBuf + _putbackSize);
|
||||
remainingSpaceInOutBuf = fOB_remainingSpace(_zstream.next_out);
|
||||
|
||||
while (remainingSpaceInOutBuf > 0) {
|
||||
// This does fit in a zlib uInt: that's the whole point of zlibMaxChunk.
|
||||
_zstream.avail_out = static_cast<uInt>(
|
||||
std::min(remainingSpaceInOutBuf, zlibMaxChunk));
|
||||
|
||||
if (_zstream.avail_in == 0 && !allInputRead) {
|
||||
// Get data from _iStream, store it in _inBuf
|
||||
allInputRead = getInputData();
|
||||
}
|
||||
|
||||
// Make zlib process some data (compress or decompress it). This updates
|
||||
// _zstream.{avail,next}_{in,out} (4 fields of the z_stream struct).
|
||||
retCode = zlibProcessData();
|
||||
|
||||
if (retCode == Z_BUF_ERROR) {
|
||||
handleZ_BUF_ERROR(); // doesn't return
|
||||
} else if (retCode == Z_STREAM_END) {
|
||||
assert(_zstream.avail_in == 0); // all of _inBuf must have been used
|
||||
_allFinished = true;
|
||||
break;
|
||||
} else if (retCode < 0) { // negative codes are errors
|
||||
throw sg_io_exception(::zlibErrorMessage(_zstream, retCode),
|
||||
sg_location(_path));
|
||||
}
|
||||
|
||||
remainingSpaceInOutBuf = fOB_remainingSpace(_zstream.next_out);
|
||||
}
|
||||
|
||||
return reinterpret_cast<char*>(_zstream.next_out);
|
||||
}
|
||||
|
||||
// This method provides input data to zlib:
|
||||
// - if data is already available in _inBuf, it updates _zstream.avail_in
|
||||
// accordingly (using the largest possible amount given _zstream.avail_in's
|
||||
// type, i.e., uInt);
|
||||
// - otherwise, it reads() as much data as possible into _inBuf from
|
||||
// _iStream and updates _zstream.avail_in to tell zlib about this new
|
||||
// available data (again: largest possible amount given the uInt type).
|
||||
//
|
||||
// This method must be called only when _zstream.avail_in == 0, which means
|
||||
// zlib has read everything we fed it (and does *not* mean, by the way, that
|
||||
// the input buffer starting at _inBuf is empty; this is because the buffer
|
||||
// size might exceed what can be represented by an uInt).
|
||||
//
|
||||
// On return:
|
||||
// - either EOF has been reached for _iStream, and the return value is true;
|
||||
// - or _zstream.avail_in > 0, and the return value is false.
|
||||
//
|
||||
// Note: don't read more in the previous paragraph concerning the state on
|
||||
// return. In the first case, there is *no guarantee* about
|
||||
// _zstream.avail_in's value; in the second case, there is *no guarantee*
|
||||
// about whether EOF has been reached for _iStream.
|
||||
bool ZlibAbstractIStreambuf::getInputData()
|
||||
{
|
||||
bool allInputRead = false;
|
||||
|
||||
assert(_zstream.avail_in == 0);
|
||||
std::ptrdiff_t alreadyAvailable =
|
||||
_inBufEndPtr - reinterpret_cast<char*>(_zstream.next_in);
|
||||
assert(alreadyAvailable >= 0);
|
||||
|
||||
// Data already available?
|
||||
if (alreadyAvailable > 0) {
|
||||
_zstream.avail_in = static_cast<uInt>(
|
||||
std::min(static_cast<std::size_t>(alreadyAvailable),
|
||||
zlibMaxChunk));
|
||||
return allInputRead;
|
||||
}
|
||||
|
||||
if (_inBufEndPtr == _inBuf + _inBufSize) { // buffer full, rewind
|
||||
_inBufEndPtr = _inBuf;
|
||||
_zstream.next_in = reinterpret_cast<unsigned char*>(_inBuf);
|
||||
}
|
||||
|
||||
// Fill the input buffer (as much as possible)
|
||||
while (_inBufEndPtr < _inBuf + _inBufSize && !_iStream.eof()) {
|
||||
std::streamsize nbCharsToRead = std::min(
|
||||
_inBuf + _inBufSize - _inBufEndPtr,
|
||||
std::numeric_limits<std::streamsize>::max()); // max we can pass to read()
|
||||
_iStream.read(_inBufEndPtr, nbCharsToRead);
|
||||
|
||||
if (_iStream.bad()) {
|
||||
string errMsg = simgear::strutils::error_string(errno);
|
||||
string msgStart = (_path.isNull()) ?
|
||||
"Error while reading from a stream" : "Read error";
|
||||
throw sg_io_exception(msgStart + ": " + errMsg, sg_location(_path));
|
||||
}
|
||||
|
||||
// Could be zero if at EOF
|
||||
std::streamsize nbCharsRead = _iStream.gcount();
|
||||
// std::streamsize is a signed integral type!
|
||||
assert(0 <= nbCharsRead);
|
||||
_inBufEndPtr += nbCharsRead;
|
||||
}
|
||||
|
||||
std::ptrdiff_t availableChars =
|
||||
_inBufEndPtr - reinterpret_cast<char*>(_zstream.next_in);
|
||||
assert(availableChars >= 0);
|
||||
_zstream.avail_in = static_cast<uInt>(
|
||||
std::min(static_cast<std::size_t>(availableChars),
|
||||
zlibMaxChunk));
|
||||
|
||||
if (_iStream.eof()) {
|
||||
allInputRead = true;
|
||||
} else {
|
||||
// Trying to rewind a fully read std::istringstream with seekg() can lead
|
||||
// to a weird state, where the stream doesn't return any character but
|
||||
// doesn't report EOF either. Make sure we are not in this situation.
|
||||
assert(_zstream.avail_in > 0);
|
||||
}
|
||||
|
||||
return allInputRead;
|
||||
}
|
||||
|
||||
// Implementing this method is optional, but should provide better
|
||||
// performance. It makes zlib write the data directly in the buffer starting
|
||||
// at 'dest'. Without it, the data would go through an intermediate buffer
|
||||
// (_outBuf) before being copied to its (hopefully) final destination.
|
||||
std::streamsize ZlibAbstractIStreambuf::xsgetn(char* dest, std::streamsize n)
|
||||
{
|
||||
std::size_t remaining = static_cast<std::size_t>(n);
|
||||
std::size_t chunkSize;
|
||||
std::ptrdiff_t avail;
|
||||
const char* origGptr = gptr();
|
||||
char* writePtr = dest; // we'll need dest later -> work with a copy
|
||||
|
||||
// First, let's take data present in our internal buffer (_outBuf)
|
||||
while (remaining > 0) {
|
||||
avail = egptr() - gptr(); // number of available chars in _outBuf
|
||||
if (avail == 0) { // our internal buffer is empty
|
||||
break;
|
||||
}
|
||||
|
||||
chunkSize = std::min(remaining, static_cast<std::size_t>(avail));
|
||||
std::copy(gptr(), gptr() + chunkSize, writePtr);
|
||||
gbump(chunkSize);
|
||||
writePtr += chunkSize;
|
||||
remaining -= chunkSize;
|
||||
}
|
||||
|
||||
if (remaining == 0) {
|
||||
// Everything we needed was already in _outBuf. The putback area is set up
|
||||
// as it should between eback() and the current gptr(), so we are fine to
|
||||
// return.
|
||||
return n;
|
||||
}
|
||||
|
||||
// Now, let's make it so that the remaining data we need is directly written
|
||||
// to the destination area, without going through _outBuf.
|
||||
_zstream.next_out = reinterpret_cast<unsigned char*>(writePtr);
|
||||
bool allInputRead = false;
|
||||
int retCode;
|
||||
|
||||
while (remaining > 0) {
|
||||
chunkSize = std::min(remaining, zlibMaxChunk);
|
||||
// It does fit in a zlib uInt: that's the whole point of zlibMaxChunk.
|
||||
_zstream.avail_out = static_cast<uInt>(chunkSize);
|
||||
|
||||
if (_zstream.avail_in == 0 && !allInputRead) {
|
||||
allInputRead = getInputData();
|
||||
}
|
||||
|
||||
// Make zlib process some data (compress or decompress). This updates
|
||||
// _zstream.{avail,next}_{in,out} (4 fields of the z_stream struct).
|
||||
retCode = zlibProcessData();
|
||||
// chunkSize - _zstream.avail_out is the nb of chars written by zlib
|
||||
remaining -= chunkSize - _zstream.avail_out;
|
||||
|
||||
if (retCode == Z_BUF_ERROR) {
|
||||
handleZ_BUF_ERROR(); // doesn't return
|
||||
} else if (retCode == Z_STREAM_END) {
|
||||
assert(_zstream.avail_in == 0); // all of _inBuf must have been used
|
||||
_allFinished = true;
|
||||
break;
|
||||
} else if (retCode < 0) { // negative codes are errors
|
||||
throw sg_io_exception(::zlibErrorMessage(_zstream, retCode),
|
||||
sg_location(_path));
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, prepare the putback area.
|
||||
//
|
||||
// We could use reinterpret_cast<char*>(_zstream.next_out) everywhere below,
|
||||
// except it is hardly readable. This points after the latest data we wrote.
|
||||
writePtr = reinterpret_cast<char*>(_zstream.next_out);
|
||||
|
||||
// There are two buffers containing characters we potentially have to copy
|
||||
// to the putback area: the one starting at _outBuf and the one starting at
|
||||
// dest. In the following diagram, ***** represents those from _outBuf,
|
||||
// which are right-aligned at origGptr[1], and the series of # represents
|
||||
// those in the [dest, writePtr) address range:
|
||||
//
|
||||
// |_outBuf |eback() *****|origGptr |_outBuf + _outBufSize
|
||||
//
|
||||
// |dest #################|writePtr
|
||||
//
|
||||
// Together, these two memory blocks logically form a contiguous stream of
|
||||
// chars we gave to the “client”: *****#################. All we have to do
|
||||
// now is copy the appropriate amount of chars from this logical stream to
|
||||
// the putback area, according to _putbackSize. These chars are the last
|
||||
// ones in said stream (i.e., right portion of *****#################), but
|
||||
// we have to copy them in order of increasing address to avoid possible
|
||||
// overlapping problems in _outBuf. This is because some of the chars to
|
||||
// copy may be located before _outBuf + _putbackSize (i.e., already be in
|
||||
// the putback area).
|
||||
//
|
||||
// [1] This means that the last char represented by a star is at address
|
||||
// origGptr-1.
|
||||
assert(writePtr - dest >= 0);
|
||||
std::size_t inDestBuffer = static_cast<std::size_t>(writePtr - dest);
|
||||
assert(origGptr - eback() >= 0);
|
||||
std::size_t nbPutbackChars = std::min(
|
||||
static_cast<std::size_t>(origGptr - eback()) + inDestBuffer,
|
||||
_putbackSize);
|
||||
std::size_t nbPutbackCharsToGo = nbPutbackChars;
|
||||
|
||||
// chunkSize has an unsigned type; precomputing it before the following test
|
||||
// wouldn't work.
|
||||
// Are there chars in _outBuf that need to be copied to the putback area?
|
||||
if (nbPutbackChars > inDestBuffer) {
|
||||
chunkSize = nbPutbackChars - inDestBuffer; // yes, this number
|
||||
std::copy(origGptr - chunkSize, origGptr,
|
||||
_outBuf + _putbackSize - nbPutbackChars);
|
||||
nbPutbackCharsToGo -= chunkSize;
|
||||
}
|
||||
|
||||
std::copy(writePtr - nbPutbackCharsToGo, writePtr,
|
||||
_outBuf + _putbackSize - nbPutbackCharsToGo);
|
||||
setg(_outBuf + _putbackSize - nbPutbackChars, // start of putback area
|
||||
_outBuf + _putbackSize, // the buffer for pending,
|
||||
_outBuf + _putbackSize); // available data is empty
|
||||
|
||||
std::streamsize rem = static_cast<std::streamsize>(remaining);
|
||||
// This is guaranteed because n is of type std::streamsize and the whole
|
||||
// algorithm ensures that 0 <= remaining <= n.
|
||||
assert(rem >= 0);
|
||||
assert(static_cast<std::size_t>(rem) == remaining);
|
||||
assert(n - rem >= 0);
|
||||
// Total number of chars copied.
|
||||
return n - rem;
|
||||
}
|
||||
|
||||
// ***************************************************************************
|
||||
// * ZlibCompressorIStreambuf class *
|
||||
// ***************************************************************************
|
||||
|
||||
ZlibCompressorIStreambuf::ZlibCompressorIStreambuf(
|
||||
std::istream& iStream,
|
||||
const SGPath& path,
|
||||
int compressionLevel,
|
||||
ZLibCompressionFormat format,
|
||||
ZLibMemoryStrategy memStrategy,
|
||||
char* inBuf,
|
||||
std::size_t inBufSize,
|
||||
char *outBuf,
|
||||
std::size_t outBufSize,
|
||||
std::size_t putbackSize)
|
||||
: ZlibAbstractIStreambuf(iStream, path, inBuf, inBufSize, outBuf, outBufSize,
|
||||
putbackSize)
|
||||
{
|
||||
zStreamInit(compressionLevel, format, memStrategy);
|
||||
}
|
||||
|
||||
ZlibCompressorIStreambuf::~ZlibCompressorIStreambuf()
|
||||
{
|
||||
int retCode = deflateEnd(&_zstream); // deallocate the z_stream struct
|
||||
if (retCode != Z_OK) {
|
||||
// In C++11, we can't throw exceptions from a destructor.
|
||||
SG_LOG(SG_IO, SG_ALERT, "ZlibCompressorIStreambuf: " <<
|
||||
::zlibErrorMessage(_zstream, retCode));
|
||||
}
|
||||
}
|
||||
|
||||
ZlibAbstractIStreambuf::OperationType
|
||||
ZlibCompressorIStreambuf::operationType() const
|
||||
{
|
||||
return OPERATION_TYPE_COMPRESSION;
|
||||
}
|
||||
|
||||
void ZlibCompressorIStreambuf::zStreamInit(int compressionLevel,
|
||||
ZLibCompressionFormat format,
|
||||
ZLibMemoryStrategy memStrategy)
|
||||
{
|
||||
int windowBits, memLevel;
|
||||
|
||||
// Intentionally not listing ZLIB_COMPRESSION_FORMAT_AUTODETECT here (it is
|
||||
// only for decompression!)
|
||||
switch (format) {
|
||||
case ZLIB_COMPRESSION_FORMAT_ZLIB:
|
||||
windowBits = 15;
|
||||
break;
|
||||
case ZLIB_COMPRESSION_FORMAT_GZIP:
|
||||
windowBits = 31;
|
||||
break;
|
||||
default:
|
||||
throw std::logic_error("Unexpected compression format: " +
|
||||
std::to_string(format));
|
||||
}
|
||||
|
||||
switch (memStrategy) {
|
||||
case ZLIB_FAVOR_MEMORY_OVER_SPEED:
|
||||
memLevel = 8;
|
||||
break;
|
||||
case ZLIB_FAVOR_SPEED_OVER_MEMORY:
|
||||
memLevel = 9;
|
||||
break;
|
||||
default:
|
||||
throw std::logic_error("Unexpected memory strategy: " +
|
||||
std::to_string(memStrategy));
|
||||
}
|
||||
|
||||
_zstream.zalloc = Z_NULL; // No custom memory allocation routines
|
||||
_zstream.zfree = Z_NULL; // Ditto. Therefore, the 'opaque' field won't
|
||||
_zstream.opaque = Z_NULL; // be used, actually.
|
||||
|
||||
int retCode = deflateInit2(&_zstream, compressionLevel, Z_DEFLATED,
|
||||
windowBits, memLevel, Z_DEFAULT_STRATEGY);
|
||||
if (retCode != Z_OK) {
|
||||
throw sg_io_exception(::zlibErrorMessage(_zstream, retCode),
|
||||
sg_location(_path));
|
||||
}
|
||||
}
|
||||
|
||||
int ZlibCompressorIStreambuf::zlibProcessData()
|
||||
{
|
||||
// Compress as much data as possible given _zstream.avail_in and
|
||||
// _zstream.avail_out. The input data starts at _zstream.next_in, the output
|
||||
// at _zstream.next_out, and these four fields are all updated by this call
|
||||
// to reflect exactly the amount of data zlib has consumed and produced.
|
||||
return deflate(&_zstream, _zstream.avail_in ? Z_NO_FLUSH : Z_FINISH);
|
||||
}
|
||||
|
||||
// ***************************************************************************
|
||||
// * ZlibDecompressorIStreambuf class *
|
||||
// ***************************************************************************
|
||||
|
||||
ZlibDecompressorIStreambuf::ZlibDecompressorIStreambuf(
|
||||
std::istream& iStream,
|
||||
const SGPath& path,
|
||||
ZLibCompressionFormat format,
|
||||
char* inBuf,
|
||||
std::size_t inBufSize,
|
||||
char *outBuf,
|
||||
std::size_t outBufSize,
|
||||
std::size_t putbackSize)
|
||||
: ZlibAbstractIStreambuf(iStream, path, inBuf, inBufSize, outBuf, outBufSize,
|
||||
putbackSize)
|
||||
{
|
||||
zStreamInit(format);
|
||||
}
|
||||
|
||||
ZlibDecompressorIStreambuf::~ZlibDecompressorIStreambuf()
|
||||
{
|
||||
int retCode = inflateEnd(&_zstream); // deallocate the z_stream struct
|
||||
if (retCode != Z_OK) {
|
||||
// In C++11, we can't throw exceptions from a destructor.
|
||||
SG_LOG(SG_IO, SG_ALERT, "ZlibDecompressorIStreambuf: " <<
|
||||
::zlibErrorMessage(_zstream, retCode));
|
||||
}
|
||||
}
|
||||
|
||||
ZlibAbstractIStreambuf::OperationType
|
||||
ZlibDecompressorIStreambuf::operationType() const
|
||||
{
|
||||
return OPERATION_TYPE_DECOMPRESSION;
|
||||
}
|
||||
|
||||
void ZlibDecompressorIStreambuf::zStreamInit(ZLibCompressionFormat format)
|
||||
{
|
||||
int windowBits;
|
||||
|
||||
switch (format) {
|
||||
case ZLIB_COMPRESSION_FORMAT_ZLIB:
|
||||
windowBits = 15;
|
||||
break;
|
||||
case ZLIB_COMPRESSION_FORMAT_GZIP:
|
||||
windowBits = 31;
|
||||
break;
|
||||
case ZLIB_COMPRESSION_FORMAT_AUTODETECT:
|
||||
windowBits = 47; // 47 = 32 + 15
|
||||
break;
|
||||
default:
|
||||
throw std::logic_error("Unexpected compression format: " +
|
||||
std::to_string(format));
|
||||
}
|
||||
|
||||
_zstream.zalloc = Z_NULL; // No custom memory allocation routines
|
||||
_zstream.zfree = Z_NULL; // Ditto. Therefore, the 'opaque' field won't
|
||||
_zstream.opaque = Z_NULL; // be used, actually.
|
||||
|
||||
int retCode = inflateInit2(&_zstream, windowBits);
|
||||
if (retCode != Z_OK) {
|
||||
throw sg_io_exception(::zlibErrorMessage(_zstream, retCode),
|
||||
sg_location(_path));
|
||||
}
|
||||
}
|
||||
|
||||
int ZlibDecompressorIStreambuf::zlibProcessData()
|
||||
{
|
||||
// Decompress as much data as possible given _zstream.avail_in and
|
||||
// _zstream.avail_out. The input data starts at _zstream.next_in, the output
|
||||
// at _zstream.next_out, and these four fields are all updated by this call
|
||||
// to reflect exactly the amount of data zlib has consumed and produced.
|
||||
return inflate(&_zstream, _zstream.avail_in ? Z_NO_FLUSH : Z_FINISH);
|
||||
}
|
||||
|
||||
// ***************************************************************************
|
||||
// * ZlibCompressorIStream class *
|
||||
// ***************************************************************************
|
||||
|
||||
ZlibCompressorIStream::ZlibCompressorIStream(std::istream& iStream,
|
||||
const SGPath& path,
|
||||
int compressionLevel,
|
||||
ZLibCompressionFormat format,
|
||||
ZLibMemoryStrategy memStrategy,
|
||||
char* inBuf,
|
||||
std::size_t inBufSize,
|
||||
char *outBuf,
|
||||
std::size_t outBufSize,
|
||||
std::size_t putbackSize)
|
||||
: _streamBuf(iStream, path, compressionLevel, format, memStrategy, inBuf,
|
||||
inBufSize, outBuf, outBufSize, putbackSize)
|
||||
{
|
||||
rdbuf(&_streamBuf); // Associate _streamBuf to 'this'
|
||||
}
|
||||
|
||||
ZlibCompressorIStream::~ZlibCompressorIStream()
|
||||
{ }
|
||||
|
||||
// ***************************************************************************
|
||||
// * ZlibDecompressorIStream class *
|
||||
// ***************************************************************************
|
||||
|
||||
ZlibDecompressorIStream::ZlibDecompressorIStream(std::istream& iStream,
|
||||
const SGPath& path,
|
||||
ZLibCompressionFormat format,
|
||||
char* inBuf,
|
||||
std::size_t inBufSize,
|
||||
char *outBuf,
|
||||
std::size_t outBufSize,
|
||||
std::size_t putbackSize)
|
||||
: _streamBuf(iStream, path, format, inBuf, inBufSize, outBuf, outBufSize,
|
||||
putbackSize)
|
||||
{
|
||||
rdbuf(&_streamBuf); // Associate _streamBuf to 'this'
|
||||
}
|
||||
|
||||
ZlibDecompressorIStream::~ZlibDecompressorIStream()
|
||||
{ }
|
||||
|
||||
} // of namespace simgear
|
||||
401
simgear/io/iostreams/zlibstream.hxx
Normal file
401
simgear/io/iostreams/zlibstream.hxx
Normal file
@@ -0,0 +1,401 @@
|
||||
// -*- coding: utf-8 -*-
|
||||
//
|
||||
// zlibstream.hxx --- IOStreams classes for working with RFC 1950 and RFC 1952
|
||||
// compression formats (respectively known as the zlib and
|
||||
// gzip formats)
|
||||
//
|
||||
// Copyright (C) 2017 Florent Rougon
|
||||
//
|
||||
// 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 of the License, 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, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
#ifndef SG_ZLIBSTREAM_HXX
|
||||
#define SG_ZLIBSTREAM_HXX
|
||||
|
||||
#include <iosfwd>
|
||||
#include <ios> // std::streamsize
|
||||
#include <zlib.h> // struct z_stream
|
||||
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
|
||||
// This file contains:
|
||||
//
|
||||
// - two stream buffer classes (ZlibCompressorIStreambuf and
|
||||
// ZlibDecompressorIStreambuf), both based on the same abstract class:
|
||||
// ZlibAbstractIStreambuf;
|
||||
//
|
||||
// - two std::istream subclasses (ZlibCompressorIStream and
|
||||
// ZlibDecompressorIStream), each creating and using the corresponding
|
||||
// stream buffer class from the previous item.
|
||||
//
|
||||
// All these allow one to work with RFC 1950 and RFC 1952 compression
|
||||
// formats, respectively known as the zlib and gzip formats.
|
||||
//
|
||||
// These classes are *input* streaming classes, which means they can
|
||||
// efficiently handle arbitrary amounts of data without using any disk
|
||||
// space nor increasing amounts of memory, and allow “client code” to pull
|
||||
// exactly as much data as it wants at any given time, resuming later
|
||||
// when it is ready to handle the next chunk.
|
||||
//
|
||||
// So, for example, assuming you've created an instance of
|
||||
// ZlibCompressorIStream (bound to some input stream of your choice, let's
|
||||
// call it iStream), you could read 512 bytes of data from it, and you
|
||||
// would get the first 512 bytes of *compressed* data corresponding to what
|
||||
// iStream provided. Then you could resume at any time and ask for the next
|
||||
// 512 bytes of compressed data (or any other amount), etc.
|
||||
//
|
||||
// Therefore, these classes are well suited, among others, to compress or
|
||||
// decompress data streams while at the same time packing the result into
|
||||
// discrete chunks or packets with size constraints (you can think of the
|
||||
// process as making sausages :).
|
||||
//
|
||||
// The input being in each case an std::istream (for compressing as well as
|
||||
// for decompressing), it can be tied to an arbitrary source: a file with
|
||||
// sg_ifstream or std::ifstream, a memory buffer with std::istringstream or
|
||||
// std::stringstream, a TCP socket with a custom std::streambuf subclass[1]
|
||||
// to interface with the sockets API, etc.
|
||||
//
|
||||
// [1] Possibly wrapped in an std::istream.
|
||||
//
|
||||
// The stream buffer classes upon which ZlibCompressorIStream and
|
||||
// ZlibDecompressorIStream are built have an xsgetn() implementation that
|
||||
// avoids useless copies of data by asking zlib to write directly to the
|
||||
// destination buffer. This xsgetn() method is used when calling read() on
|
||||
// the std::istream subclasses, or sgetn() if you are using the stream
|
||||
// buffer classes directly (i.e., ZlibCompressorIStreambuf and
|
||||
// ZlibDecompressorIStreambuf). Other std::istream methods may instead rely
|
||||
// only on the internal buffer and the underflow() method, and therefore be
|
||||
// less efficient for large amounts of data. You may want to take a look at
|
||||
// zlibstream_test.cxx to see various ways of using these classes.
|
||||
//
|
||||
// In case you use std::istream& operator>>(std::istream&, std::string&) or
|
||||
// its overload friends, beware that it splits fields at spaces, and by
|
||||
// default ignores spaces at the beginning of a field (cf. std::skipws,
|
||||
// std::noskipws and friends). As far as I understand it, most of these
|
||||
// operators are mainly intended in the IOStreams library to be used to
|
||||
// read an int here, a double there, a space-delimited string afterwards,
|
||||
// etc. (the exception could be the overload writing to a stream buffer,
|
||||
// however it doesn't seem to be very efficient on my system with GNU
|
||||
// libstdc++ [it is not using xsgetn()], so beware also of this one if you
|
||||
// are handling large amounts of data). For moderately complex or large
|
||||
// input handling, I'd suggest to use std::istream methods such as read(),
|
||||
// gcount() and getline() (std::getline() can be useful too). Or directly
|
||||
// use the stream buffer classes, in particular with sgetn().
|
||||
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
enum ZLibCompressionFormat {
|
||||
ZLIB_COMPRESSION_FORMAT_ZLIB = 0,
|
||||
ZLIB_COMPRESSION_FORMAT_GZIP,
|
||||
ZLIB_COMPRESSION_FORMAT_AUTODETECT
|
||||
};
|
||||
|
||||
enum ZLibMemoryStrategy {
|
||||
ZLIB_FAVOR_MEMORY_OVER_SPEED = 0,
|
||||
ZLIB_FAVOR_SPEED_OVER_MEMORY
|
||||
};
|
||||
|
||||
// Abstract base class for both the compressor and decompressor stream buffers.
|
||||
class ZlibAbstractIStreambuf: public std::streambuf
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor for ZlibAbstractIStreambuf.
|
||||
* @param iStream Input stream to read from.
|
||||
* @param path Optional path to the file corresponding to iStream,
|
||||
* if any. Only used for error messages.
|
||||
* @param inBuf Pointer to the input buffer (data read from iStream is
|
||||
* written there before being compressed or decompressed).
|
||||
* If nullptr, the buffer is allocated on the heap in the
|
||||
* constructor and deallocated in the destructor.
|
||||
* @param inBufSize Size of the input buffer, in chars.
|
||||
* @param outBuf Pointer to the output buffer. Data is read by zlib
|
||||
* from the input buffer, compressed or decompressed, and
|
||||
* the result is directly written to the output buffer.
|
||||
* If nullptr, the buffer is allocated on the heap in the
|
||||
* constructor and deallocated in the destructor.
|
||||
* @param outBufSize Size of the output buffer, in chars.
|
||||
* @param putbackSize Size of the putback area inside the output buffer, in
|
||||
* chars.
|
||||
*
|
||||
* It is required that putbackSize < outBufSize. It is guaranteed that,
|
||||
* if at least putbackSize chars have been read without any putback (or
|
||||
* unget) operation intermixed, then at least putbackSize chars can be
|
||||
* put back in sequence. If you don't need this feature, use zero for the
|
||||
* putbackSize value (the default) for best performance.
|
||||
*/
|
||||
explicit ZlibAbstractIStreambuf(std::istream& iStream,
|
||||
const SGPath& path = SGPath(),
|
||||
char* inBuf = nullptr,
|
||||
std::size_t inBufSize = 262144,
|
||||
char* outBuf = nullptr,
|
||||
std::size_t outBufSize = 262144,
|
||||
std::size_t putbackSize = 0);
|
||||
ZlibAbstractIStreambuf(const ZlibAbstractIStreambuf&) = delete;
|
||||
ZlibAbstractIStreambuf& operator=(const ZlibAbstractIStreambuf&) = delete;
|
||||
~ZlibAbstractIStreambuf();
|
||||
|
||||
protected:
|
||||
enum OperationType {
|
||||
OPERATION_TYPE_COMPRESSION = 0,
|
||||
OPERATION_TYPE_DECOMPRESSION
|
||||
};
|
||||
|
||||
virtual OperationType operationType() const = 0;
|
||||
|
||||
// Either compress or decompress a chunk of data (depending on the
|
||||
// particular subclass implementation). The semantics are the same as for
|
||||
// zlib's inflate() and deflate() functions applied to member _zstream,
|
||||
// concerning 1) the return value and 2) where, and how much to read and
|
||||
// write (which thus depends on _zstream.avail_in, _zstream.next_in,
|
||||
// _zstream.avail_out and _zstream.next_out).
|
||||
virtual int zlibProcessData() = 0;
|
||||
|
||||
// The input stream, from which data is read before being processed by zlib
|
||||
std::istream& _iStream;
|
||||
// Corresponding path, if any (default-constructed SGPath instance otherwise)
|
||||
const SGPath _path;
|
||||
// Structure used to communicate with zlib
|
||||
z_stream _zstream;
|
||||
|
||||
private:
|
||||
// Callback whose role is to refill the output buffer when it's empty and
|
||||
// the “client” tries to read more.
|
||||
int underflow() override;
|
||||
// Optional override when subclassing std::streambuf. This is the most
|
||||
// efficient way of reading several characters (as soon as we've emptied the
|
||||
// output buffer, data is written by zlib directly to the destination
|
||||
// buffer).
|
||||
std::streamsize xsgetn(char* dest, std::streamsize n) override;
|
||||
// Make sure there is data to read in the input buffer, or signal EOF.
|
||||
bool getInputData();
|
||||
// Utility method for fillOutputBuffer()
|
||||
std::size_t fOB_remainingSpace(unsigned char* nextOutPtr) const;
|
||||
// Fill the output buffer (using zlib functions) as much as possible.
|
||||
char* fillOutputBuffer();
|
||||
// Utility method
|
||||
[[ noreturn ]] void handleZ_BUF_ERROR() const;
|
||||
|
||||
bool _allFinished = false;
|
||||
|
||||
// The buffers
|
||||
// ~~~~~~~~~~~
|
||||
//
|
||||
// The input buffer receives data obtained from _iStream, before it is
|
||||
// processed by zlib. In underflow(), zlib reads from this buffer it and
|
||||
// writes the resulting data(*) to the output buffer. Then we point the
|
||||
// standard std::streambuf pointers (gptr() and friends) directly towards
|
||||
// the data inside that output buffer. xsgetn() is even more optimized: it
|
||||
// first empties the output buffer, then makes zlib write the remaining data
|
||||
// directly to the destination area.
|
||||
//
|
||||
// (*) Compressed or decompressed, depending on the particular
|
||||
// implementation of zlibProcessData() in each subclass.
|
||||
char* _inBuf;
|
||||
const std::size_t _inBufSize;
|
||||
// _inBufEndPtr points right after the last data retrieved from _iStream and
|
||||
// stored into _inBuf. When zlib has read all such data, _zstream.next_in is
|
||||
// equal to _inBufEndPtr (after proper casting). Except in this particular
|
||||
// situation, only _zstream.next_in <= _inBufEndPtr is guaranteed.
|
||||
char* _inBufEndPtr;
|
||||
// Layout of the _outBuf buffer:
|
||||
//
|
||||
// |_outBuf <putback area> |_outBuf + _putbackSize |_outBuf + _outBufSize
|
||||
//
|
||||
// The first _putbackSize chars in _outBuf are reserved for the putback area
|
||||
// (right-aligned at _outBuf + _putbackSize). The actual output buffer thus
|
||||
// starts at _outBuf + _putbackSize. At any given time for callers of this
|
||||
// class, the number of characters that can be put back is gptr() - eback().
|
||||
// It may be lower than _putbackSize if we haven't read that many characters
|
||||
// yet. It may also be larger if gptr() > _outBuf + _putbackSize, i.e.,
|
||||
// when the buffer for pending data is non-empty.
|
||||
//
|
||||
// At any given time, callers should see:
|
||||
//
|
||||
// _outBuf <= eback() <= _outBuf + _putbackSize <= gptr() <= egptr()
|
||||
// <= _outBuf + _outBufSize
|
||||
//
|
||||
// (hoping this won't get out of sync with the code!)
|
||||
char *_outBuf;
|
||||
const std::size_t _outBufSize;
|
||||
// Space reserved for characters to be put back into the stream. Must be
|
||||
// strictly smaller than _outBufSize (this is checked in the constructor).
|
||||
// It is guaranteed that this number of chars can be put back, except of
|
||||
// course if we haven't read that many characters from the input stream yet.
|
||||
// If characters are buffered in _outBuf[2], then it may be that more
|
||||
// characters than _putbackSize can be put back (it is essentially a matter
|
||||
// for std::streambuf of decreasing the “next pointer for the input
|
||||
// sequence”, i.e., the one returned by gptr()).
|
||||
//
|
||||
// [2] In the [_outBuf + _putbackSize, _outBuf + _outBufSize) area.
|
||||
const std::size_t _putbackSize;
|
||||
|
||||
// Since the constructor optionally allocates memory for the input and
|
||||
// output buffers, these members allow the destructor to know which buffers
|
||||
// have to be deallocated, if any.
|
||||
bool _inBufMustBeFreed = false;
|
||||
bool _outBufMustBeFreed = false;
|
||||
};
|
||||
|
||||
|
||||
// Stream buffer class for compressing data. Input data is obtained from an
|
||||
// std::istream instance; the corresponding compressed data can be read using
|
||||
// the standard std::streambuf read interface (mainly: sbumpc(), sgetc(),
|
||||
// snextc(), sgetn(), sputbackc(), sungetc()). Input, uncompressed data is
|
||||
// “pulled” as needed for the amount of compressed data requested by the
|
||||
// “client” using the methods I just listed.
|
||||
class ZlibCompressorIStreambuf: public ZlibAbstractIStreambuf
|
||||
{
|
||||
public:
|
||||
// Same parameters as for ZlibAbstractIStreambuf, except:
|
||||
//
|
||||
// compressionLevel: in the [0,9] range. 0 means no compression at all.
|
||||
// Levels 1 to 9 yield compressed data, with 1 giving
|
||||
// the highest compression speed but worst compression
|
||||
// ratio, and 9 the highest compression ratio but lowest
|
||||
// compression speed.
|
||||
// format either ZLIB_COMPRESSION_FORMAT_ZLIB or
|
||||
// ZLIB_COMPRESSION_FORMAT_GZIP
|
||||
// memStrategy either ZLIB_FAVOR_MEMORY_OVER_SPEED or
|
||||
// ZLIB_FAVOR_SPEED_OVER_MEMORY
|
||||
explicit ZlibCompressorIStreambuf(
|
||||
std::istream& iStream,
|
||||
const SGPath& path = SGPath(),
|
||||
int compressionLevel = Z_DEFAULT_COMPRESSION,
|
||||
ZLibCompressionFormat format = ZLIB_COMPRESSION_FORMAT_ZLIB,
|
||||
ZLibMemoryStrategy memStrategy = ZLIB_FAVOR_SPEED_OVER_MEMORY,
|
||||
char* inBuf = nullptr,
|
||||
std::size_t inBufSize = 262144,
|
||||
char* outBuf = nullptr,
|
||||
std::size_t outBufSize = 262144,
|
||||
std::size_t putbackSize = 0);
|
||||
ZlibCompressorIStreambuf(const ZlibCompressorIStreambuf&) = delete;
|
||||
ZlibCompressorIStreambuf& operator=(const ZlibCompressorIStreambuf&) = delete;
|
||||
~ZlibCompressorIStreambuf();
|
||||
|
||||
protected:
|
||||
OperationType operationType() const override;
|
||||
// Initialize the z_stream struct used by zlib
|
||||
void zStreamInit(int compressionLevel, ZLibCompressionFormat format,
|
||||
ZLibMemoryStrategy memStrategy);
|
||||
// Call zlib's deflate() function to compress data.
|
||||
int zlibProcessData() override;
|
||||
};
|
||||
|
||||
|
||||
// Stream buffer class for decompressing data. Input data is obtained from an
|
||||
// std::istream instance; the corresponding decompressed data can be read
|
||||
// using the standard std::streambuf read interface (mainly: sbumpc(),
|
||||
// sgetc(), snextc(), sgetn(), sputbackc(), sungetc()). Input, compressed data
|
||||
// is “pulled” as needed for the amount of uncompressed data requested by the
|
||||
// “client” using the methods I just listed.
|
||||
class ZlibDecompressorIStreambuf: public ZlibAbstractIStreambuf
|
||||
{
|
||||
public:
|
||||
// Same parameters as for ZlibAbstractIStreambuf, except:
|
||||
//
|
||||
// format ZLIB_COMPRESSION_FORMAT_ZLIB,
|
||||
// ZLIB_COMPRESSION_FORMAT_GZIP or
|
||||
// ZLIB_COMPRESSION_FORMAT_AUTODETECT
|
||||
explicit ZlibDecompressorIStreambuf(
|
||||
std::istream& iStream,
|
||||
const SGPath& path = SGPath(),
|
||||
ZLibCompressionFormat format = ZLIB_COMPRESSION_FORMAT_ZLIB,
|
||||
char* inBuf = nullptr,
|
||||
std::size_t inBufSize = 262144,
|
||||
char* outBuf = nullptr,
|
||||
std::size_t outBufSize = 262144,
|
||||
std::size_t putbackSize = 0); // default optimized for speed
|
||||
ZlibDecompressorIStreambuf(const ZlibDecompressorIStreambuf&) = delete;
|
||||
ZlibDecompressorIStreambuf& operator=(const ZlibDecompressorIStreambuf&)
|
||||
= delete;
|
||||
~ZlibDecompressorIStreambuf();
|
||||
|
||||
protected:
|
||||
OperationType operationType() const override;
|
||||
void zStreamInit(ZLibCompressionFormat format);
|
||||
int zlibProcessData() override;
|
||||
};
|
||||
|
||||
// std::istream subclass for compressing data. Input data is obtained from an
|
||||
// std::istream instance; the corresponding compressed data can be read using
|
||||
// the standard std::istream interface (read(), readsome(), gcount(), get(),
|
||||
// getline(), operator>>(), peek(), putback(), ignore(), unget()... plus
|
||||
// operator overloads such as istream& operator>>(istream&, string&) as
|
||||
// defined in <string>, and std::getline()). Input, uncompressed data is
|
||||
// “pulled” as needed for the amount of compressed data requested by the
|
||||
// “client”.
|
||||
//
|
||||
// To get data efficiently from an instance of this class, use its read()
|
||||
// method (typically in conjunction with gcount(), inside a loop).
|
||||
class ZlibCompressorIStream: public std::istream
|
||||
{
|
||||
public:
|
||||
// Same parameters as for ZlibCompressorIStreambuf
|
||||
explicit ZlibCompressorIStream(
|
||||
std::istream& iStream,
|
||||
const SGPath& path = SGPath(),
|
||||
int compressionLevel = Z_DEFAULT_COMPRESSION,
|
||||
ZLibCompressionFormat format = ZLIB_COMPRESSION_FORMAT_ZLIB,
|
||||
ZLibMemoryStrategy memStrategy = ZLIB_FAVOR_SPEED_OVER_MEMORY,
|
||||
char* inBuf = nullptr,
|
||||
std::size_t inBufSize = 262144,
|
||||
char* outBuf = nullptr,
|
||||
std::size_t outBufSize = 262144,
|
||||
std::size_t putbackSize = 0); // default optimized for speed
|
||||
ZlibCompressorIStream(const ZlibCompressorIStream&) = delete;
|
||||
ZlibCompressorIStream& operator=(const ZlibCompressorIStream&) = delete;
|
||||
~ZlibCompressorIStream();
|
||||
|
||||
private:
|
||||
ZlibCompressorIStreambuf _streamBuf;
|
||||
};
|
||||
|
||||
// std::istream subclass for decompressing data. Input data is obtained from
|
||||
// an std::istream instance; the corresponding decompressed data can be read
|
||||
// using the standard std::istream interface (read(), readsome(), gcount(),
|
||||
// get(), getline(), operator>>(), peek(), putback(), ignore(), unget()...
|
||||
// plus operator overloads such as istream& operator>>(istream&, string&) as
|
||||
// defined in <string>, and std::getline()). Input, compressed data is
|
||||
// “pulled” as needed for the amount of uncompressed data requested by the
|
||||
// “client”.
|
||||
//
|
||||
// To get data efficiently from an instance of this class, use its read()
|
||||
// method (typically in conjunction with gcount(), inside a loop).
|
||||
class ZlibDecompressorIStream: public std::istream
|
||||
{
|
||||
public:
|
||||
// Same parameters as for ZlibDecompressorIStreambuf
|
||||
explicit ZlibDecompressorIStream(
|
||||
std::istream& iStream,
|
||||
const SGPath& path = SGPath(),
|
||||
ZLibCompressionFormat format = ZLIB_COMPRESSION_FORMAT_ZLIB,
|
||||
char* inBuf = nullptr,
|
||||
std::size_t inBufSize = 262144,
|
||||
char* outBuf = nullptr,
|
||||
std::size_t outBufSize = 262144,
|
||||
std::size_t putbackSize = 0); // default optimized for speed
|
||||
ZlibDecompressorIStream(const ZlibDecompressorIStream&) = delete;
|
||||
ZlibDecompressorIStream& operator=(const ZlibDecompressorIStream&) = delete;
|
||||
~ZlibDecompressorIStream();
|
||||
|
||||
private:
|
||||
ZlibDecompressorIStreambuf _streamBuf;
|
||||
};
|
||||
|
||||
} // of namespace simgear
|
||||
|
||||
#endif // of SG_ZLIBSTREAM_HXX
|
||||
562
simgear/io/iostreams/zlibstream_test.cxx
Normal file
562
simgear/io/iostreams/zlibstream_test.cxx
Normal file
@@ -0,0 +1,562 @@
|
||||
// -*- coding: utf-8 -*-
|
||||
//
|
||||
// zlibstream_test.cxx --- Automated tests for zlibstream.cxx / zlibstream.hxx
|
||||
//
|
||||
// Copyright (C) 2017 Florent Rougon
|
||||
//
|
||||
// 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 of the License, 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, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
#include <ios> // std::basic_ios, std::streamsize...
|
||||
#include <iostream> // std::ios_base, std::cerr, etc.
|
||||
#include <sstream>
|
||||
#include <array>
|
||||
#include <random>
|
||||
#include <limits> // std::numeric_limits
|
||||
#include <functional> // std::bind()
|
||||
#include <cassert>
|
||||
#include <cstdlib> // EXIT_SUCCESS
|
||||
#include <cstddef> // std::size_t
|
||||
#include <cstring> // strcmp()
|
||||
|
||||
using std::string;
|
||||
using std::cout;
|
||||
using std::cerr;
|
||||
using traits = std::char_traits<char>;
|
||||
|
||||
#include <simgear/misc/test_macros.hxx>
|
||||
#include <simgear/io/iostreams/sgstream.hxx>
|
||||
#include <simgear/io/iostreams/zlibstream.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/misc/sg_dir.hxx>
|
||||
|
||||
// In many tests below, I use very small buffer sizes. Of course, this is bad
|
||||
// for performance. The reason I do it this way is simply because it better
|
||||
// exercises the code we want to *test* here (we are more likely to find bugs
|
||||
// if the buffer(s) has/have to be refilled one or more times). Similarly, if
|
||||
// you don't need the putback feature in non-test code, best performance is
|
||||
// achieved with putback size = 0.
|
||||
//
|
||||
// I suggest you read roundTripWithIStreams() below to see how to use the
|
||||
// classes most efficiently (especially the comments!).
|
||||
|
||||
static std::default_random_engine randomNumbersGenerator;
|
||||
|
||||
// Sample string for tests
|
||||
static const string lipsum = "\
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque congue ornare\n\
|
||||
congue. Mauris mollis est et porttitor condimentum. Vivamus laoreet blandit\n\
|
||||
odio eget consectetur. Etiam quis magna eu enim luctus pretium. In et\n\
|
||||
tristique nunc, non efficitur metus. Nullam efficitur tristique velit.\n\
|
||||
Praesent et luctus nunc. Mauris eros eros, rutrum at molestie quis, egestas et\n\
|
||||
lorem. Ut nulla turpis, eleifend sed mauris ac, faucibus molestie nulla.\n\
|
||||
Quisque viverra vel turpis nec efficitur. Proin non rutrum velit. Nam sodales\n\
|
||||
metus felis, eu pharetra velit posuere ut.\n\
|
||||
\n\
|
||||
Suspendisse pellentesque tincidunt ligula et pretium. Etiam id justo mauris.\n\
|
||||
Aenean porta, sapien in suscipit tristique, metus diam malesuada dui, in porta\n\
|
||||
felis mi non felis. Etiam vel aliquam leo, non vehicula magna. Proin justo\n\
|
||||
massa, ultrices at porta eu, tempor in ligula. Duis enim ipsum, dictum quis\n\
|
||||
ultricies et, tempus eu libero. Morbi vulputate libero ut dolor rutrum, a\n\
|
||||
imperdiet nibh egestas. Phasellus finibus massa vel tempus hendrerit. Nulla\n\
|
||||
lobortis est non ligula viverra, quis egestas ante hendrerit. Pellentesque\n\
|
||||
finibus mollis blandit. In ac sollicitudin mauris, eget dignissim mauris.";
|
||||
|
||||
// Utility function: generate a random string whose length is in the
|
||||
// [minLen, maxLen] range.
|
||||
string randomString(string::size_type minLen, string::size_type maxLen)
|
||||
{
|
||||
std::uniform_int_distribution<string::size_type> sLenDist(minLen, maxLen);
|
||||
std::uniform_int_distribution<int> byteDist(0, 255);
|
||||
auto randomByte = std::bind(byteDist, randomNumbersGenerator);
|
||||
|
||||
string::size_type len = sLenDist(randomNumbersGenerator);
|
||||
string str;
|
||||
|
||||
while (str.size() < len) {
|
||||
str += traits::to_char_type(randomByte());
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
// Utility function: perform a compression-decompression cycle on the input
|
||||
// string using the stream buffer classes:
|
||||
//
|
||||
// 1) Compress the input string, write the result to a temporary file using
|
||||
// std::ostream& operator<<(streambuf* sb), where 'sb' points to a
|
||||
// ZlibCompressorIStreambuf instance.
|
||||
//
|
||||
// 2) Close the temporary file to make sure all the data is flushed.
|
||||
//
|
||||
// 3) Create a ZlibDecompressorIStreambuf instance reading from the
|
||||
// temporary file, then decompress to an std::ostringstream using
|
||||
// std::ostream& operator<<(streambuf* sb), where 'sb' points to our
|
||||
// ZlibDecompressorIStreambuf instance.
|
||||
//
|
||||
// 4) Compare the result with the input string.
|
||||
//
|
||||
// Of course, it is possible to do this without any temporary file, even
|
||||
// without any std::stringstream or such to hold the intermediate, compressed
|
||||
// data. See the other tests for actual compressor + decompressor pipelines.
|
||||
void pipeCompOrDecompIStreambufIntoOStream(const string& input)
|
||||
{
|
||||
// Test “std::ostream << &ZlibCompressorIStreambuf”
|
||||
std::istringstream input_ss(input);
|
||||
simgear::ZlibCompressorIStreambuf compSBuf(input_ss, SGPath(), 9);
|
||||
simgear::Dir tmpDir = simgear::Dir::tempDir("FlightGear");
|
||||
tmpDir.setRemoveOnDestroy();
|
||||
SGPath path = tmpDir.path() / "testFile.dat";
|
||||
sg_ofstream oFile(path,
|
||||
std::ios::binary | std::ios::trunc | std::ios::out);
|
||||
|
||||
oFile << &compSBuf;
|
||||
// Make sure the compressed stream is flushed, otherwise when we read the
|
||||
// file back, decompression is likely to fail with Z_BUF_ERROR.
|
||||
oFile.close();
|
||||
SG_CHECK_EQUAL(compSBuf.sgetc(), EOF);
|
||||
|
||||
// Read back and decompress the data we've written to 'path', testing
|
||||
// “std::ostream << &ZlibDecompressorIStreambuf”
|
||||
sg_ifstream iFile(path, std::ios::binary | std::ios::in);
|
||||
simgear::ZlibDecompressorIStreambuf decompSBuf(iFile, path);
|
||||
std::ostringstream roundTripResult_ss;
|
||||
// This is also possible, though maybe not as good for error detection:
|
||||
//
|
||||
// decompSBuf >> roundTripResult_ss.rdbuf();
|
||||
roundTripResult_ss << &decompSBuf;
|
||||
SG_CHECK_EQUAL(decompSBuf.sgetc(), EOF);
|
||||
SG_CHECK_EQUAL(roundTripResult_ss.str(), input);
|
||||
}
|
||||
|
||||
// Perform a compression-decompression cycle on bunch of strings, using the
|
||||
// stream buffer classes directly (not the std::istream subclasses).
|
||||
void test_pipeCompOrDecompIStreambufIntoOStream()
|
||||
{
|
||||
cerr << "Compression-decompression cycles using the stream buffer classes "
|
||||
"directly\n";
|
||||
|
||||
pipeCompOrDecompIStreambufIntoOStream(""); // empty string
|
||||
pipeCompOrDecompIStreambufIntoOStream("a");
|
||||
pipeCompOrDecompIStreambufIntoOStream("lorem ipsum");
|
||||
|
||||
for (int i=0; i < 10; i++) {
|
||||
pipeCompOrDecompIStreambufIntoOStream(randomString(0, 20));
|
||||
}
|
||||
|
||||
for (int i=0; i < 10; i++) {
|
||||
pipeCompOrDecompIStreambufIntoOStream(randomString(21, 1000));
|
||||
}
|
||||
|
||||
assert(std::numeric_limits<string::size_type>::max() >= 65535);
|
||||
for (int i=0; i < 10; i++) {
|
||||
pipeCompOrDecompIStreambufIntoOStream(randomString(1000, 65535));
|
||||
}
|
||||
}
|
||||
|
||||
void test_StreambufBasicOperations()
|
||||
{
|
||||
cerr << "Testing basic operations on ZlibDecompressorIStreambuf\n";
|
||||
|
||||
const string text = "0123456789abcdefghijklmnopqrstuvwxyz\nABCDEF\nGHIJK "
|
||||
"LMNOPQ";
|
||||
std::istringstream text_ss(text);
|
||||
|
||||
static constexpr std::size_t compInBufSize = 6;
|
||||
static constexpr std::size_t compOutBufSize = 4;
|
||||
static constexpr std::size_t compPutbackSize = 0;
|
||||
simgear::ZlibCompressorIStreambuf compSBuf(
|
||||
text_ss, SGPath(), 8, simgear::ZLIB_COMPRESSION_FORMAT_ZLIB,
|
||||
simgear::ZLIB_FAVOR_SPEED_OVER_MEMORY, nullptr, compInBufSize, nullptr,
|
||||
compOutBufSize, compPutbackSize);
|
||||
std::stringstream compressedOutput_ss;
|
||||
compressedOutput_ss << &compSBuf;
|
||||
|
||||
static constexpr std::size_t decompInBufSize = 5;
|
||||
static constexpr std::size_t decompOutBufSize = 4;
|
||||
static constexpr std::size_t decompPutbackSize = 2;
|
||||
simgear::ZlibDecompressorIStreambuf decompSBuf(
|
||||
compressedOutput_ss, SGPath(), simgear::ZLIB_COMPRESSION_FORMAT_ZLIB,
|
||||
nullptr, decompInBufSize, nullptr, decompOutBufSize, decompPutbackSize);
|
||||
|
||||
int ch = decompSBuf.sgetc();
|
||||
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == '0');
|
||||
ch = decompSBuf.sbumpc();
|
||||
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == '0');
|
||||
ch = decompSBuf.sbumpc();
|
||||
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == '1');
|
||||
ch = decompSBuf.snextc();
|
||||
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == '3');
|
||||
ch = decompSBuf.sbumpc();
|
||||
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == '3');
|
||||
ch = decompSBuf.sbumpc();
|
||||
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == '4');
|
||||
ch = decompSBuf.sputbackc('4');
|
||||
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == '4');
|
||||
ch = decompSBuf.sputbackc('u'); // doesn't match what we read from the stream
|
||||
SG_VERIFY(ch == EOF);
|
||||
ch = decompSBuf.sputbackc('3'); // this one does
|
||||
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == '3');
|
||||
|
||||
static constexpr std::streamsize bufSize = 10;
|
||||
char buf[bufSize];
|
||||
// Most efficient way (with the underlying xsgetn()) to read several chars
|
||||
// at once.
|
||||
std::streamsize n = decompSBuf.sgetn(buf, bufSize);
|
||||
SG_VERIFY(n == bufSize && string(buf, bufSize) == "3456789abc");
|
||||
|
||||
ch = decompSBuf.sungetc(); // same as sputbackc(), except no value to check
|
||||
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == 'c');
|
||||
ch = decompSBuf.sungetc();
|
||||
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == 'b');
|
||||
ch = decompSBuf.sbumpc();
|
||||
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == 'b');
|
||||
ch = decompSBuf.sputbackc('b'); // this one does
|
||||
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == 'b');
|
||||
|
||||
n = decompSBuf.sgetn(buf, bufSize);
|
||||
SG_VERIFY(n == bufSize && string(buf, bufSize) == "bcdefghijk");
|
||||
|
||||
ch = decompSBuf.sungetc();
|
||||
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == 'k');
|
||||
|
||||
static char buf2[64];
|
||||
n = decompSBuf.sgetn(buf2, sizeof(buf2));
|
||||
SG_VERIFY(n == 36 && string(buf2, n) == "klmnopqrstuvwxyz\nABCDEF\nGHIJK "
|
||||
"LMNOPQ");
|
||||
|
||||
ch = decompSBuf.sbumpc();
|
||||
SG_CHECK_EQUAL(ch, EOF);
|
||||
}
|
||||
|
||||
// Utility function: take a string as input to compress and return the
|
||||
// compressed output as a string.
|
||||
string compress(const string& dataToCompress,
|
||||
simgear::ZLibCompressionFormat compressionFormat,
|
||||
int compressionLevel,
|
||||
simgear::ZLibMemoryStrategy memStrategy,
|
||||
std::size_t putbackSize,
|
||||
SGPath path = SGPath())
|
||||
{
|
||||
// Static storage is only okay for very small sizes like these, and because
|
||||
// we won't call the function from several threads at the same time. Plain
|
||||
// char arrays would be fine here, but I'll use std::array to show it can be
|
||||
// used here too.
|
||||
static std::array<char, 7> inBuf;
|
||||
static std::array<char, 7> outBuf;
|
||||
|
||||
std::istringstream iss(dataToCompress);
|
||||
simgear::ZlibCompressorIStream compressor(
|
||||
iss, path, compressionLevel, compressionFormat, memStrategy,
|
||||
&inBuf.front(), inBuf.size(), &outBuf.front(), outBuf.size(),
|
||||
putbackSize);
|
||||
compressor.exceptions(std::ios_base::badbit); // throw if badbit is set
|
||||
std::ostringstream compressedData_ss;
|
||||
compressor >> compressedData_ss.rdbuf();
|
||||
|
||||
return compressedData_ss.str();
|
||||
}
|
||||
|
||||
void test_formattedInputFromDecompressor()
|
||||
{
|
||||
cerr << "Testing ZlibDecompressorIStream >> std::string\n";
|
||||
|
||||
static char inBuf[6];
|
||||
static char outBuf[15];
|
||||
string compressed = compress(
|
||||
lipsum, simgear::ZLIB_COMPRESSION_FORMAT_ZLIB, Z_BEST_COMPRESSION,
|
||||
simgear::ZLIB_FAVOR_MEMORY_OVER_SPEED, /* putback size */ 0);
|
||||
std::istringstream compressed_ss(compressed);
|
||||
|
||||
simgear::ZlibDecompressorIStream decompressor(
|
||||
compressed_ss, SGPath(), simgear::ZLIB_COMPRESSION_FORMAT_ZLIB,
|
||||
inBuf, sizeof(inBuf), outBuf, sizeof(outBuf), /* putback size */ 1);
|
||||
decompressor.exceptions(std::ios_base::badbit); // throw if badbit is set
|
||||
|
||||
int count = 0;
|
||||
string word;
|
||||
|
||||
while (decompressor >> word) {
|
||||
count++;
|
||||
}
|
||||
|
||||
SG_CHECK_EQUAL(count, 175); // Number of words in 'lipsum'
|
||||
// If set, badbit would have caused an exception to be raised
|
||||
SG_VERIFY(!decompressor.bad());
|
||||
|
||||
if (!decompressor.eof()) {
|
||||
assert(decompressor.fail());
|
||||
cerr << "Decompressor: stream extraction (operator>>) failed before "
|
||||
"reaching EOF.\n";
|
||||
SG_TEST_FAIL("Did not expect operator>> to fail with an std::string "
|
||||
"argument.");
|
||||
}
|
||||
}
|
||||
|
||||
void test_ZlibDecompressorIStream_readPutbackEtc()
|
||||
{
|
||||
cerr << "Testing many operations on ZlibDecompressorIStream (read(), "
|
||||
"putback(), etc.\n";
|
||||
|
||||
static char compInBuf[4];
|
||||
static char compOutBuf[6];
|
||||
static char decompInBuf[8];
|
||||
static char decompOutBuf[5];
|
||||
const string text = "0123456789abcdefghijklmnopqrstuvwxyz\nABCDEF\nGHIJK "
|
||||
"LMNOPQ";
|
||||
std::istringstream text_ss(text);
|
||||
|
||||
simgear::ZlibCompressorIStream compressor(
|
||||
text_ss, SGPath(), Z_BEST_COMPRESSION,
|
||||
simgear::ZLIB_COMPRESSION_FORMAT_ZLIB,
|
||||
simgear::ZLIB_FAVOR_MEMORY_OVER_SPEED,
|
||||
compInBuf, sizeof(compInBuf), compOutBuf, sizeof(compOutBuf),
|
||||
/* putback size */ 0);
|
||||
compressor.exceptions(std::ios_base::badbit); // throw if badbit is set
|
||||
|
||||
// Use the compressor (subclass of std::istream) as input to the decompressor
|
||||
simgear::ZlibDecompressorIStream decompressor(
|
||||
compressor, SGPath(), simgear::ZLIB_COMPRESSION_FORMAT_ZLIB,
|
||||
decompInBuf, sizeof(decompInBuf), decompOutBuf, sizeof(decompOutBuf),
|
||||
/* putback size */ 3);
|
||||
decompressor.exceptions(std::ios_base::badbit);
|
||||
|
||||
{
|
||||
static std::array<char, 17> buf;
|
||||
decompressor.read(&buf.front(), 10);
|
||||
SG_VERIFY(decompressor.good() && decompressor.gcount() == 10);
|
||||
SG_CHECK_EQUAL(string(&buf.front(), 10), "0123456789");
|
||||
|
||||
SG_VERIFY(decompressor.putback('9'));
|
||||
SG_VERIFY(decompressor.putback('8'));
|
||||
SG_VERIFY(decompressor.putback('7'));
|
||||
|
||||
SG_VERIFY(decompressor.get(buf[10]));
|
||||
SG_VERIFY(decompressor.read(&buf[11], 6));
|
||||
SG_CHECK_EQUAL(string(&buf.front(), 17), "0123456789789abcd");
|
||||
}
|
||||
|
||||
{
|
||||
bool gotException = false;
|
||||
try {
|
||||
// 'Z' is not the last character read from the stream
|
||||
decompressor.putback('Z');
|
||||
} catch (std::ios_base::failure) {
|
||||
gotException = true;
|
||||
}
|
||||
SG_VERIFY(gotException && decompressor.bad());
|
||||
}
|
||||
|
||||
{
|
||||
int c;
|
||||
|
||||
decompressor.clear();
|
||||
// Check we can resume normally now that we've cleared the error state flags
|
||||
c = decompressor.get();
|
||||
SG_VERIFY(c != traits::eof() && traits::to_char_type(c) == 'e');
|
||||
|
||||
// unget() and get() in sequence
|
||||
SG_VERIFY(decompressor.unget());
|
||||
c = decompressor.get();
|
||||
SG_VERIFY(c != traits::eof() && traits::to_char_type(c)== 'e');
|
||||
|
||||
// peek() looks at, but doesn't consume
|
||||
c = decompressor.peek();
|
||||
SG_VERIFY(c != traits::eof() && traits::to_char_type(c) == 'f');
|
||||
c = decompressor.get();
|
||||
SG_VERIFY(c != traits::eof() && traits::to_char_type(c)== 'f');
|
||||
}
|
||||
|
||||
{
|
||||
static std::array<char, 21> buf; // 20 chars + terminating NUL char
|
||||
|
||||
// std::istream::getline() will be stopped by \n after reading 20 chars
|
||||
SG_VERIFY(decompressor.getline(&buf.front(), 25));
|
||||
SG_VERIFY(decompressor.gcount() == 21 &&
|
||||
!strcmp(&buf.front(), "ghijklmnopqrstuvwxyz"));
|
||||
|
||||
string str;
|
||||
// std::getline() will be stopped by \n after 7 chars have been extracted,
|
||||
// namely 6 chars plus the \n which is extracted and discarded.
|
||||
SG_VERIFY(std::getline(decompressor, str));
|
||||
SG_CHECK_EQUAL(str, "ABCDEF");
|
||||
|
||||
SG_VERIFY(decompressor.ignore(2));
|
||||
SG_VERIFY(decompressor >> str);
|
||||
SG_CHECK_EQUAL(str, "IJK");
|
||||
|
||||
static char buf2[5];
|
||||
// Read up to sizeof(buf) chars, without waiting if not that many are
|
||||
// immediately avaiable.
|
||||
int nbCharsRead = decompressor.readsome(buf2, sizeof(buf2));
|
||||
|
||||
string rest(buf2, nbCharsRead);
|
||||
do {
|
||||
decompressor.read(buf2, sizeof(buf2));
|
||||
rest += string(buf2, decompressor.gcount());
|
||||
} while (decompressor);
|
||||
|
||||
SG_CHECK_EQUAL(rest, " LMNOPQ");
|
||||
}
|
||||
|
||||
// If set, badbit would have caused an exception to be raised, anyway
|
||||
SG_VERIFY(decompressor.eof() && !decompressor.bad());
|
||||
}
|
||||
|
||||
// Utility function: parametrized round-trip test with a compressor +
|
||||
// decompressor pipeline.
|
||||
//
|
||||
//
|
||||
// Note: this is nice conceptually, allows to keep memory use constant even in
|
||||
// case an arbitrary amount of data is passed through, and exercises the
|
||||
// stream buffer classes well, however this technique is more than twice
|
||||
// as slow on my computer than using an intermediate buffer like this:
|
||||
//
|
||||
// std::stringstream compressedData_ss;
|
||||
// compressor >> compressedData_ss.rdbuf();
|
||||
//
|
||||
// simgear::ZlibDecompressorIStream decompressor(compressedData_ss,
|
||||
// SGPath(), ...
|
||||
void roundTripWithIStreams(
|
||||
simgear::ZLibCompressionFormat compressionFormat,
|
||||
int compressionLevel,
|
||||
simgear::ZLibMemoryStrategy memStrategy,
|
||||
std::size_t compInBufSize,
|
||||
std::size_t compOutBufSize,
|
||||
std::size_t decompInBufSize,
|
||||
std::size_t decompOutBufSize,
|
||||
std::size_t compPutbackSize,
|
||||
std::size_t decompPutbackSize,
|
||||
bool useAutoFormatForDecompression = false)
|
||||
{
|
||||
const simgear::ZLibCompressionFormat decompFormat =
|
||||
(useAutoFormatForDecompression) ?
|
||||
simgear::ZLIB_COMPRESSION_FORMAT_AUTODETECT : compressionFormat;
|
||||
|
||||
std::istringstream lipsum_ss(lipsum);
|
||||
// This tests the optional dynamic buffer allocation in ZlibAbstractIStreambuf
|
||||
simgear::ZlibCompressorIStream compressor(
|
||||
lipsum_ss, SGPath(), compressionLevel, compressionFormat, memStrategy,
|
||||
nullptr, compInBufSize, nullptr, compOutBufSize, compPutbackSize);
|
||||
compressor.exceptions(std::ios_base::badbit); // throw if badbit is set
|
||||
|
||||
// Use the compressor as input to the decompressor (pipeline). The
|
||||
// decompressor uses read() with chunks that are as large as possible given
|
||||
// the available space in its input buffer. These read() calls are served by
|
||||
// ZlibCompressorIStreambuf::xsgetn(), which is efficient.
|
||||
simgear::ZlibDecompressorIStream decompressor(
|
||||
compressor, SGPath(), decompFormat,
|
||||
nullptr, decompInBufSize, nullptr, decompOutBufSize, decompPutbackSize);
|
||||
decompressor.exceptions(std::ios_base::badbit);
|
||||
std::ostringstream roundTripResult;
|
||||
// This, on the other hand, appears not to use xsgetn() (tested with the GNU
|
||||
// libstdc++ from g++ 6.3.0, 20170124). This causes useless copying of the
|
||||
// data to the decompressor output buffer, instead of writing it directly to
|
||||
// roundTripResult's internal buffer. This is simple and nice in appearance,
|
||||
// but if you are after performance, better use decompressor.read()
|
||||
// (typically in conjunction with gcount(), inside a loop).
|
||||
decompressor >> roundTripResult.rdbuf();
|
||||
SG_CHECK_EQUAL(roundTripResult.str(), lipsum);
|
||||
}
|
||||
|
||||
// Round-trip conversion with simgear::ZlibCompressorIStream and
|
||||
// simgear::ZlibDecompressorIStream, using various parameter combinations.
|
||||
void test_RoundTripMultiWithIStreams()
|
||||
{
|
||||
cerr <<
|
||||
"Compression-decompression cycles using the std::istream subclasses (many\n"
|
||||
"combinations of buffer sizes and compression parameters tested)\n";
|
||||
|
||||
{ // More variations on these later
|
||||
const std::size_t compPutbackSize = 1;
|
||||
const std::size_t decompPutbackSize = 1;
|
||||
|
||||
for (auto format: {simgear::ZLIB_COMPRESSION_FORMAT_ZLIB,
|
||||
simgear::ZLIB_COMPRESSION_FORMAT_GZIP}) {
|
||||
for (int compressionLevel: {1, 4, 7, 9}) {
|
||||
for (auto memStrategy: {simgear::ZLIB_FAVOR_MEMORY_OVER_SPEED,
|
||||
simgear::ZLIB_FAVOR_SPEED_OVER_MEMORY}) {
|
||||
for (std::size_t compInBufSize: {3, 4}) {
|
||||
for (std::size_t compOutBufSize: {3, 5}) {
|
||||
for (std::size_t decompInBufSize: {3, 4}) {
|
||||
for (std::size_t decompOutBufSize: {3, 4,}) {
|
||||
roundTripWithIStreams(
|
||||
format, compressionLevel, memStrategy, compInBufSize,
|
||||
compOutBufSize, decompInBufSize, decompOutBufSize,
|
||||
compPutbackSize, decompPutbackSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const auto format = simgear::ZLIB_COMPRESSION_FORMAT_ZLIB;
|
||||
const int compressionLevel = Z_DEFAULT_COMPRESSION;
|
||||
const auto memStrategy = simgear::ZLIB_FAVOR_SPEED_OVER_MEMORY;
|
||||
|
||||
for (std::size_t compInBufSize: {3, 4, 31, 256, 19475}) {
|
||||
for (std::size_t compOutBufSize: {3, 5, 9, 74, 4568}) {
|
||||
for (std::size_t decompInBufSize: {3, 4, 256, 24568}) {
|
||||
for (std::size_t decompOutBufSize: {3, 5, 42, 4568}) {
|
||||
for (std::size_t compPutbackSize: {0, 1, 2}) {
|
||||
for (std::size_t decompPutbackSize: {0, 1, 2}) {
|
||||
roundTripWithIStreams(
|
||||
format, compressionLevel, memStrategy, compInBufSize,
|
||||
compOutBufSize, decompInBufSize, decompOutBufSize,
|
||||
compPutbackSize, decompPutbackSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const std::size_t compInBufSize = 5;
|
||||
const std::size_t compOutBufSize = 107;
|
||||
const std::size_t decompInBufSize = 65536;
|
||||
const std::size_t decompOutBufSize = 84;
|
||||
int i = 0;
|
||||
|
||||
for (std::size_t compPutbackSize: {25, 40, 105}) {
|
||||
for (std::size_t decompPutbackSize: {30, 60, 81}) {
|
||||
const simgear::ZLibCompressionFormat compFormat = (i++ % 2) ?
|
||||
simgear::ZLIB_COMPRESSION_FORMAT_ZLIB :
|
||||
simgear::ZLIB_COMPRESSION_FORMAT_GZIP;
|
||||
|
||||
roundTripWithIStreams(
|
||||
compFormat, Z_BEST_COMPRESSION, simgear::ZLIB_FAVOR_MEMORY_OVER_SPEED,
|
||||
compInBufSize, compOutBufSize, decompInBufSize, decompOutBufSize,
|
||||
compPutbackSize, decompPutbackSize,
|
||||
/* automatic format detection for decompression */ true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
test_pipeCompOrDecompIStreambufIntoOStream();
|
||||
test_StreambufBasicOperations();
|
||||
test_RoundTripMultiWithIStreams();
|
||||
test_formattedInputFromDecompressor();
|
||||
test_ZlibDecompressorIStream_readPutbackEtc();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -94,6 +94,17 @@ enum sgVertexAttributeTypes {
|
||||
SG_VA_FLOAT_3 = 0x00000800,
|
||||
};
|
||||
|
||||
static gzFile gzFileFromSGPath(const SGPath& path, const char* mode)
|
||||
{
|
||||
#if defined(SG_WINDOWS)
|
||||
std::wstring ws = path.wstr();
|
||||
return gzopen_w(ws.c_str(), mode);
|
||||
#else
|
||||
std::string ps = path.utf8Str();
|
||||
return gzopen(ps.c_str(), mode);
|
||||
#endif
|
||||
}
|
||||
|
||||
enum sgPropertyTypes {
|
||||
SG_MATERIAL = 0,
|
||||
SG_INDEX_TYPES = 1,
|
||||
@@ -534,15 +545,16 @@ bool SGBinObject::read_bin( const SGPath& file ) {
|
||||
fans_vas.clear();
|
||||
fan_materials.clear();
|
||||
|
||||
gzFile fp;
|
||||
string f = file.local8BitStr();
|
||||
if ( (fp = gzopen( f.c_str(), "rb" )) == NULL ) {
|
||||
string filegz = f + ".gz";
|
||||
if ( (fp = gzopen( filegz.c_str(), "rb" )) == NULL ) {
|
||||
gzFile fp = gzFileFromSGPath(file, "rb");
|
||||
if ( fp == NULL ) {
|
||||
SGPath withGZ = file;
|
||||
withGZ.concat(".gz");
|
||||
fp = gzFileFromSGPath(withGZ, "rb");
|
||||
if (fp == nullptr) {
|
||||
SG_LOG( SG_EVENT, SG_ALERT,
|
||||
"ERROR: opening " << file << " or " << filegz << " for reading!");
|
||||
"ERROR: opening " << file << " or " << withGZ << " for reading!");
|
||||
|
||||
throw sg_io_exception("Error opening for reading (and .gz)", sg_location(f));
|
||||
throw sg_io_exception("Error opening for reading (and .gz)", sg_location(file));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -957,9 +969,8 @@ bool SGBinObject::write_bin_file(const SGPath& file)
|
||||
SGPath file2(file);
|
||||
file2.create_dir( 0755 );
|
||||
|
||||
gzFile fp;
|
||||
std::string localPath = file.local8BitStr();
|
||||
if ( (fp = gzopen( localPath.c_str(), "wb9" )) == NULL ) {
|
||||
gzFile fp = gzFileFromSGPath(file, "wb9");
|
||||
if ( fp == nullptr ) {
|
||||
cout << "ERROR: opening " << file << " for writing!" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -69,16 +69,27 @@ SGFile::~SGFile() {
|
||||
bool SGFile::open( const SGProtocolDir d ) {
|
||||
set_dir( d );
|
||||
|
||||
std::string n = file_name.local8BitStr();
|
||||
#if defined(SG_WINDOWS)
|
||||
std::wstring n = file_name.wstr();
|
||||
#else
|
||||
std::string n = file_name.utf8Str();
|
||||
#endif
|
||||
|
||||
|
||||
if ( get_dir() == SG_IO_OUT ) {
|
||||
#ifdef _WIN32
|
||||
#if defined(SG_WINDOWS)
|
||||
int mode = _S_IREAD | _S_IWRITE;
|
||||
fp = ::_wopen(n.c_str(), O_WRONLY | O_CREAT | O_TRUNC | extraoflags, mode);
|
||||
#else
|
||||
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
|
||||
#endif
|
||||
fp = ::open( n.c_str(), O_WRONLY | O_CREAT | O_TRUNC | extraoflags, mode );
|
||||
#endif
|
||||
} else if ( get_dir() == SG_IO_IN ) {
|
||||
#if defined(SG_WINDOWS)
|
||||
fp = ::_wopen( n.c_str(), O_RDONLY | extraoflags );
|
||||
#else
|
||||
fp = ::open( n.c_str(), O_RDONLY | extraoflags );
|
||||
#endif
|
||||
} else {
|
||||
SG_LOG( SG_IO, SG_ALERT,
|
||||
"Error: bidirection mode not available for files." );
|
||||
@@ -145,7 +156,7 @@ int SGFile::readline( char *buf, int length ) {
|
||||
result = i;
|
||||
}
|
||||
lseek( fp, pos + result, SEEK_SET );
|
||||
|
||||
|
||||
// just in case ...
|
||||
buf[ result ] = '\0';
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <errno.h>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
|
||||
@@ -16,6 +18,7 @@
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
#include <simgear/timing/timestamp.hxx>
|
||||
#include <simgear/misc/test_macros.hxx>
|
||||
|
||||
using std::cout;
|
||||
using std::cerr;
|
||||
@@ -23,23 +26,80 @@ using std::endl;
|
||||
|
||||
using namespace simgear;
|
||||
|
||||
#define COMPARE(a, b) \
|
||||
if ((a) != (b)) { \
|
||||
cerr << "failed:" << #a << " != " << #b << endl; \
|
||||
cerr << "\tgot:'" << a << "'" << endl; \
|
||||
exit(1); \
|
||||
|
||||
class Watchdog
|
||||
{
|
||||
public:
|
||||
Watchdog() : _interval(0), _timer(0), _running(false) {}
|
||||
~Watchdog() {
|
||||
stop();
|
||||
}
|
||||
|
||||
void start(unsigned int milliseconds)
|
||||
{
|
||||
_interval = milliseconds;
|
||||
_timer = 0;
|
||||
_running = true;
|
||||
_thread = std::thread(&Watchdog::loop, this);
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
_running = false;
|
||||
_thread.join();
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned int _interval;
|
||||
std::atomic<unsigned int> _timer;
|
||||
std::atomic<bool> _running;
|
||||
std::thread _thread;
|
||||
|
||||
void loop()
|
||||
{
|
||||
while (_running)
|
||||
{
|
||||
_timer++;
|
||||
|
||||
if (_timer >= _interval)
|
||||
{
|
||||
_running = false;
|
||||
std::cerr << "Failure: timeout." << endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
sglog().setLogLevels( SG_ALL, SG_DEBUG );
|
||||
|
||||
DNS::Client cl;
|
||||
#define EXISTING_RECORD "terrasync.flightgear.org"
|
||||
Watchdog watchdog;
|
||||
watchdog.start(100);
|
||||
|
||||
// test existing NAPTR
|
||||
// fgtest.t3r.de. 600 IN NAPTR 999 99 "U" "test" "!^.*$!http://dnstest.flightgear.org/!" .
|
||||
simgear::Socket::initSockets();
|
||||
|
||||
|
||||
DNS::Client cl;
|
||||
|
||||
cout << "test update without prior pending request" << endl;
|
||||
{
|
||||
cout << "polling.";
|
||||
for( int i = 0; i < 20; i++ ) {
|
||||
SGTimeStamp::sleepForMSec(200);
|
||||
cl.update(0);
|
||||
cout << ".";
|
||||
cout.flush();
|
||||
}
|
||||
cout << "done" << endl;
|
||||
}
|
||||
|
||||
#define EXISTING_RECORD "terrasync.flightgear.org"
|
||||
cout << "test existing NAPTR: " EXISTING_RECORD << endl;
|
||||
{
|
||||
DNS::NAPTRRequest * naptrRequest = new DNS::NAPTRRequest(EXISTING_RECORD);
|
||||
DNS::Request_ptr r(naptrRequest);
|
||||
@@ -58,15 +118,15 @@ int main(int argc, char* argv[])
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// test for ascending preference/order
|
||||
cout << "test for ascending preference/order" << endl;
|
||||
int order = -1, preference = -1;
|
||||
for( DNS::NAPTRRequest::NAPTR_list::const_iterator it = naptrRequest->entries.begin(); it != naptrRequest->entries.end(); ++it ) {
|
||||
// currently only support "U" which implies empty replacement
|
||||
COMPARE((*it)->flags, "U" );
|
||||
COMPARE(naptrRequest->entries[0]->replacement, "" );
|
||||
SG_CHECK_EQUAL((*it)->flags, "U" );
|
||||
SG_CHECK_EQUAL(naptrRequest->entries[0]->replacement, "" );
|
||||
|
||||
// currently only support ws20
|
||||
COMPARE((*it)->service, "ws20" );
|
||||
SG_CHECK_EQUAL((*it)->service, "ws20" );
|
||||
|
||||
if( (*it)->order < order ) {
|
||||
cerr << "NAPTR entries not ascending for field 'order'" << endl;
|
||||
@@ -95,7 +155,7 @@ int main(int argc, char* argv[])
|
||||
}
|
||||
}
|
||||
|
||||
// test non-existing NAPTR
|
||||
cout << "test non-existing NAPTR" << endl;
|
||||
{
|
||||
DNS::NAPTRRequest * naptrRequest = new DNS::NAPTRRequest("jurkxkqdiufqzpfvzqok.prozhqrlcaavbxifkkhf");
|
||||
DNS::Request_ptr r(naptrRequest);
|
||||
@@ -109,7 +169,7 @@ int main(int argc, char* argv[])
|
||||
cerr << "timeout testing non-existing record." << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
COMPARE(naptrRequest->entries.size(), 0 );
|
||||
SG_CHECK_EQUAL(naptrRequest->entries.size(), 0 );
|
||||
}
|
||||
|
||||
cout << "all tests passed ok" << endl;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <errno.h>
|
||||
#include <cerrno>
|
||||
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
#include <simgear/timing/timestamp.hxx>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/misc/test_macros.hxx>
|
||||
|
||||
#include <curl/multi.h>
|
||||
|
||||
@@ -41,18 +42,6 @@ const char* BODY3 = "Cras ut neque nulla. Duis ut velit neque, sit amet "
|
||||
const unsigned int body2Size = 8 * 1024;
|
||||
char body2[body2Size];
|
||||
|
||||
#define COMPARE(a, b) \
|
||||
if ((a) != (b)) { \
|
||||
cerr << "failed:" << #a << " != " << #b << endl; \
|
||||
cerr << "\tgot:'" << a << "'" << endl; \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
#define VERIFY(a) \
|
||||
if (!(a)) { \
|
||||
cerr << "failed:" << #a << endl; \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
class TestRequest : public HTTP::Request
|
||||
{
|
||||
@@ -126,8 +115,9 @@ public:
|
||||
d << contentStr;
|
||||
push(d.str().c_str());
|
||||
} else if (path == "/test_headers") {
|
||||
COMPARE(requestHeaders["X-Foo"], string("Bar"));
|
||||
COMPARE(requestHeaders["X-AnotherHeader"], string("A longer value"));
|
||||
SG_CHECK_EQUAL(requestHeaders["X-Foo"], string("Bar"));
|
||||
SG_CHECK_EQUAL(requestHeaders["X-AnotherHeader"],
|
||||
string("A longer value"));
|
||||
|
||||
string contentStr(BODY1);
|
||||
stringstream d;
|
||||
@@ -304,7 +294,7 @@ public:
|
||||
} else if (path == "/test_put") {
|
||||
std::cerr << "sending PUT response" << std::endl;
|
||||
|
||||
COMPARE(buffer, BODY3);
|
||||
SG_CHECK_EQUAL(buffer, BODY3);
|
||||
stringstream d;
|
||||
d << "HTTP/1.1 " << 204 << " " << reasonForCode(204) << "\r\n";
|
||||
d << "\r\n"; // final CRLF to terminate the headers
|
||||
@@ -314,7 +304,7 @@ public:
|
||||
|
||||
std::string entityStr = "http://localhost:2000/something.txt";
|
||||
|
||||
COMPARE(buffer, BODY3);
|
||||
SG_CHECK_EQUAL(buffer, BODY3);
|
||||
stringstream d;
|
||||
d << "HTTP/1.1 " << 201 << " " << reasonForCode(201) << "\r\n";
|
||||
d << "Location:" << entityStr << "\r\n";
|
||||
@@ -385,18 +375,18 @@ int main(int argc, char* argv[])
|
||||
|
||||
// test URL parsing
|
||||
TestRequest* tr1 = new TestRequest("http://localhost.woo.zar:2000/test1?foo=bar");
|
||||
COMPARE(tr1->scheme(), "http");
|
||||
COMPARE(tr1->hostAndPort(), "localhost.woo.zar:2000");
|
||||
COMPARE(tr1->host(), "localhost.woo.zar");
|
||||
COMPARE(tr1->port(), 2000);
|
||||
COMPARE(tr1->path(), "/test1");
|
||||
SG_CHECK_EQUAL(tr1->scheme(), "http");
|
||||
SG_CHECK_EQUAL(tr1->hostAndPort(), "localhost.woo.zar:2000");
|
||||
SG_CHECK_EQUAL(tr1->host(), "localhost.woo.zar");
|
||||
SG_CHECK_EQUAL(tr1->port(), 2000);
|
||||
SG_CHECK_EQUAL(tr1->path(), "/test1");
|
||||
|
||||
TestRequest* tr2 = new TestRequest("http://192.168.1.1/test1/dir/thing/file.png");
|
||||
COMPARE(tr2->scheme(), "http");
|
||||
COMPARE(tr2->hostAndPort(), "192.168.1.1");
|
||||
COMPARE(tr2->host(), "192.168.1.1");
|
||||
COMPARE(tr2->port(), 80);
|
||||
COMPARE(tr2->path(), "/test1/dir/thing/file.png");
|
||||
SG_CHECK_EQUAL(tr2->scheme(), "http");
|
||||
SG_CHECK_EQUAL(tr2->hostAndPort(), "192.168.1.1");
|
||||
SG_CHECK_EQUAL(tr2->host(), "192.168.1.1");
|
||||
SG_CHECK_EQUAL(tr2->port(), 80);
|
||||
SG_CHECK_EQUAL(tr2->path(), "/test1/dir/thing/file.png");
|
||||
|
||||
// basic get request
|
||||
{
|
||||
@@ -405,11 +395,11 @@ int main(int argc, char* argv[])
|
||||
cl.makeRequest(tr);
|
||||
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseReason(), string("OK"));
|
||||
COMPARE(tr->responseLength(), strlen(BODY1));
|
||||
COMPARE(tr->responseBytesReceived(), strlen(BODY1));
|
||||
COMPARE(tr->bodyData, string(BODY1));
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->responseReason(), string("OK"));
|
||||
SG_CHECK_EQUAL(tr->responseLength(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr->responseBytesReceived(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(BODY1));
|
||||
}
|
||||
|
||||
{
|
||||
@@ -418,11 +408,11 @@ int main(int argc, char* argv[])
|
||||
cl.makeRequest(tr);
|
||||
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseReason(), string("OK"));
|
||||
COMPARE(tr->responseLength(), strlen(BODY3));
|
||||
COMPARE(tr->responseBytesReceived(), strlen(BODY3));
|
||||
COMPARE(tr->bodyData, string(BODY3));
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->responseReason(), string("OK"));
|
||||
SG_CHECK_EQUAL(tr->responseLength(), strlen(BODY3));
|
||||
SG_CHECK_EQUAL(tr->responseBytesReceived(), strlen(BODY3));
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(BODY3));
|
||||
}
|
||||
|
||||
{
|
||||
@@ -430,7 +420,7 @@ int main(int argc, char* argv[])
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -441,11 +431,11 @@ int main(int argc, char* argv[])
|
||||
cl.makeRequest(tr);
|
||||
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseReason(), string("OK"));
|
||||
COMPARE(tr->responseLength(), strlen(BODY1));
|
||||
COMPARE(tr->responseBytesReceived(), strlen(BODY1));
|
||||
COMPARE(tr->bodyData, string(BODY1));
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->responseReason(), string("OK"));
|
||||
SG_CHECK_EQUAL(tr->responseLength(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr->responseBytesReceived(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(BODY1));
|
||||
}
|
||||
|
||||
// larger get request
|
||||
@@ -458,9 +448,9 @@ int main(int argc, char* argv[])
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseBytesReceived(), body2Size);
|
||||
COMPARE(tr->bodyData, string(body2, body2Size));
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->responseBytesReceived(), body2Size);
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(body2, body2Size));
|
||||
}
|
||||
|
||||
cerr << "testing chunked transfer encoding" << endl;
|
||||
@@ -470,12 +460,12 @@ int main(int argc, char* argv[])
|
||||
cl.makeRequest(tr);
|
||||
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseReason(), string("OK"));
|
||||
COMPARE(tr->responseBytesReceived(), 30);
|
||||
COMPARE(tr->bodyData, "ABCDEFGHABCDEFABCDSTUVABCDSTUV");
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->responseReason(), string("OK"));
|
||||
SG_CHECK_EQUAL(tr->responseBytesReceived(), 30);
|
||||
SG_CHECK_EQUAL(tr->bodyData, "ABCDEFGHABCDEFABCDSTUVABCDSTUV");
|
||||
// check trailers made it too
|
||||
COMPARE(tr->headers["x-foobar"], string("wibble"));
|
||||
SG_CHECK_EQUAL(tr->headers["x-foobar"], string("wibble"));
|
||||
}
|
||||
|
||||
// test 404
|
||||
@@ -484,9 +474,9 @@ int main(int argc, char* argv[])
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 404);
|
||||
COMPARE(tr->responseReason(), string("not found"));
|
||||
COMPARE(tr->responseLength(), 0);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 404);
|
||||
SG_CHECK_EQUAL(tr->responseReason(), string("not found"));
|
||||
SG_CHECK_EQUAL(tr->responseLength(), 0);
|
||||
}
|
||||
|
||||
cout << "done 404 test" << endl;
|
||||
@@ -496,7 +486,7 @@ int main(int argc, char* argv[])
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
}
|
||||
|
||||
cout << "done1" << endl;
|
||||
@@ -506,9 +496,9 @@ int main(int argc, char* argv[])
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseLength(), strlen(BODY1));
|
||||
COMPARE(tr->bodyData, string(BODY1));
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->responseLength(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(BODY1));
|
||||
}
|
||||
|
||||
cout << "done2" << endl;
|
||||
@@ -518,9 +508,9 @@ int main(int argc, char* argv[])
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseLength(), strlen(BODY1));
|
||||
COMPARE(tr->bodyData, string(BODY1));
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->responseLength(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(BODY1));
|
||||
}
|
||||
cout << "done3" << endl;
|
||||
// test connectToHost failure
|
||||
@@ -534,7 +524,7 @@ int main(int argc, char* argv[])
|
||||
|
||||
|
||||
const int HOST_NOT_FOUND_CODE = CURLE_COULDNT_RESOLVE_HOST;
|
||||
COMPARE(tr->responseCode(), HOST_NOT_FOUND_CODE);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), HOST_NOT_FOUND_CODE);
|
||||
}
|
||||
|
||||
cout << "testing abrupt close" << endl;
|
||||
@@ -546,7 +536,7 @@ int main(int argc, char* argv[])
|
||||
waitForFailed(&cl, tr);
|
||||
|
||||
const int SERVER_NO_DATA_CODE = CURLE_GOT_NOTHING;
|
||||
COMPARE(tr->responseCode(), SERVER_NO_DATA_CODE);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), SERVER_NO_DATA_CODE);
|
||||
}
|
||||
|
||||
cout << "testing proxy close" << endl;
|
||||
@@ -557,9 +547,9 @@ cout << "testing proxy close" << endl;
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseLength(), body2Size);
|
||||
COMPARE(tr->bodyData, string(body2, body2Size));
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->responseLength(), body2Size);
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(body2, body2Size));
|
||||
}
|
||||
|
||||
{
|
||||
@@ -568,9 +558,9 @@ cout << "testing proxy close" << endl;
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseBytesReceived(), body2Size);
|
||||
COMPARE(tr->bodyData, string(body2, body2Size));
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->responseBytesReceived(), body2Size);
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(body2, body2Size));
|
||||
}
|
||||
|
||||
// pipelining
|
||||
@@ -595,17 +585,17 @@ cout << "testing proxy close" << endl;
|
||||
cl.makeRequest(tr3);
|
||||
|
||||
waitForComplete(&cl, tr3);
|
||||
VERIFY(tr->complete);
|
||||
VERIFY(tr2->complete);
|
||||
COMPARE(tr->bodyData, string(BODY1));
|
||||
SG_VERIFY(tr->complete);
|
||||
SG_VERIFY(tr2->complete);
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(BODY1));
|
||||
|
||||
COMPARE(tr2->responseLength(), strlen(BODY3));
|
||||
COMPARE(tr2->responseBytesReceived(), strlen(BODY3));
|
||||
COMPARE(tr2->bodyData, string(BODY3));
|
||||
SG_CHECK_EQUAL(tr2->responseLength(), strlen(BODY3));
|
||||
SG_CHECK_EQUAL(tr2->responseBytesReceived(), strlen(BODY3));
|
||||
SG_CHECK_EQUAL(tr2->bodyData, string(BODY3));
|
||||
|
||||
COMPARE(tr3->bodyData, string(BODY1));
|
||||
SG_CHECK_EQUAL(tr3->bodyData, string(BODY1));
|
||||
|
||||
COMPARE(testServer.connectCount(), 1);
|
||||
SG_CHECK_EQUAL(testServer.connectCount(), 1);
|
||||
}
|
||||
|
||||
// multiple requests with an HTTP 1.0 server
|
||||
@@ -626,17 +616,17 @@ cout << "testing proxy close" << endl;
|
||||
cl.makeRequest(tr3);
|
||||
|
||||
waitForComplete(&cl, tr3);
|
||||
VERIFY(tr->complete);
|
||||
VERIFY(tr2->complete);
|
||||
SG_VERIFY(tr->complete);
|
||||
SG_VERIFY(tr2->complete);
|
||||
|
||||
COMPARE(tr->responseLength(), strlen(BODY1));
|
||||
COMPARE(tr->responseBytesReceived(), strlen(BODY1));
|
||||
COMPARE(tr->bodyData, string(BODY1));
|
||||
SG_CHECK_EQUAL(tr->responseLength(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr->responseBytesReceived(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(BODY1));
|
||||
|
||||
COMPARE(tr2->responseLength(), strlen(BODY3));
|
||||
COMPARE(tr2->responseBytesReceived(), strlen(BODY3));
|
||||
COMPARE(tr2->bodyData, string(BODY3));
|
||||
COMPARE(tr3->bodyData, string(BODY1));
|
||||
SG_CHECK_EQUAL(tr2->responseLength(), strlen(BODY3));
|
||||
SG_CHECK_EQUAL(tr2->responseBytesReceived(), strlen(BODY3));
|
||||
SG_CHECK_EQUAL(tr2->bodyData, string(BODY3));
|
||||
SG_CHECK_EQUAL(tr3->bodyData, string(BODY1));
|
||||
}
|
||||
|
||||
// POST
|
||||
@@ -648,7 +638,7 @@ cout << "testing proxy close" << endl;
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 204);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 204);
|
||||
}
|
||||
|
||||
// PUT
|
||||
@@ -660,7 +650,7 @@ cout << "testing proxy close" << endl;
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 204);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 204);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -671,7 +661,7 @@ cout << "testing proxy close" << endl;
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 201);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 201);
|
||||
}
|
||||
|
||||
// test_zero_length_content
|
||||
@@ -681,9 +671,9 @@ cout << "testing proxy close" << endl;
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->bodyData, string());
|
||||
COMPARE(tr->responseBytesReceived(), 0);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->bodyData, string());
|
||||
SG_CHECK_EQUAL(tr->responseBytesReceived(), 0);
|
||||
}
|
||||
|
||||
// test cancel
|
||||
@@ -711,12 +701,12 @@ cout << "testing proxy close" << endl;
|
||||
|
||||
waitForComplete(&cl, tr3);
|
||||
|
||||
COMPARE(tr->responseCode(), -1);
|
||||
COMPARE(tr2->responseReason(), "my reason 2");
|
||||
SG_CHECK_EQUAL(tr->responseCode(), -1);
|
||||
SG_CHECK_EQUAL(tr2->responseReason(), "my reason 2");
|
||||
|
||||
COMPARE(tr3->responseLength(), strlen(BODY1));
|
||||
COMPARE(tr3->responseBytesReceived(), strlen(BODY1));
|
||||
COMPARE(tr3->bodyData, string(BODY1));
|
||||
SG_CHECK_EQUAL(tr3->responseLength(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr3->responseBytesReceived(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr3->bodyData, string(BODY1));
|
||||
}
|
||||
|
||||
// test cancel
|
||||
@@ -742,16 +732,16 @@ cout << "testing proxy close" << endl;
|
||||
|
||||
waitForComplete(&cl, tr3);
|
||||
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseLength(), strlen(BODY1));
|
||||
COMPARE(tr->responseBytesReceived(), strlen(BODY1));
|
||||
COMPARE(tr->bodyData, string(BODY1));
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->responseLength(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr->responseBytesReceived(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(BODY1));
|
||||
|
||||
COMPARE(tr2->responseCode(), -1);
|
||||
SG_CHECK_EQUAL(tr2->responseCode(), -1);
|
||||
|
||||
COMPARE(tr3->responseLength(), strlen(BODY1));
|
||||
COMPARE(tr3->responseBytesReceived(), strlen(BODY1));
|
||||
COMPARE(tr3->bodyData, string(BODY1));
|
||||
SG_CHECK_EQUAL(tr3->responseLength(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr3->responseBytesReceived(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr3->bodyData, string(BODY1));
|
||||
}
|
||||
|
||||
{
|
||||
@@ -776,12 +766,12 @@ cout << "testing proxy close" << endl;
|
||||
cl.makeRequest(tr2);
|
||||
|
||||
waitForComplete(&cl, tr2);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->bodyData, string(BODY3));
|
||||
COMPARE(tr->responseBytesReceived(), strlen(BODY3));
|
||||
COMPARE(tr2->responseCode(), 200);
|
||||
COMPARE(tr2->bodyData, string(BODY1));
|
||||
COMPARE(tr2->responseBytesReceived(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(BODY3));
|
||||
SG_CHECK_EQUAL(tr->responseBytesReceived(), strlen(BODY3));
|
||||
SG_CHECK_EQUAL(tr2->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr2->bodyData, string(BODY1));
|
||||
SG_CHECK_EQUAL(tr2->responseBytesReceived(), strlen(BODY1));
|
||||
}
|
||||
|
||||
cout << "all tests passed ok" << endl;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#endif
|
||||
|
||||
#include <simgear/misc/sg_dir.hxx>
|
||||
#include <simgear/misc/test_macros.hxx>
|
||||
|
||||
#include "sg_binobj.hxx"
|
||||
|
||||
@@ -22,19 +23,7 @@ using std::cerr;
|
||||
using std::endl;
|
||||
using std::string;
|
||||
|
||||
#define COMPARE(a, b) \
|
||||
if ((a) != (b)) { \
|
||||
cerr << "failed:" << #a << " != " << #b << endl; \
|
||||
cerr << "\tgot:" << a << endl; \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
#define VERIFY(a) \
|
||||
if (!(a)) { \
|
||||
cerr << "failed:" << #a << endl; \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
void generate_points(int count, std::vector<SGVec3d>& vec)
|
||||
{
|
||||
for (int i=0; i<count; ++i) {
|
||||
@@ -61,15 +50,15 @@ void test_empty()
|
||||
SGBinObject empty;
|
||||
SGPath path(simgear::Dir::current().file("empty.btg.gz"));
|
||||
bool ok = empty.write_bin_file(path);
|
||||
VERIFY( ok );
|
||||
SG_VERIFY(ok);
|
||||
SGBinObject rd;
|
||||
ok = rd.read_bin(path) ;
|
||||
VERIFY( ok);
|
||||
ok = rd.read_bin(path) ;
|
||||
SG_VERIFY(ok);
|
||||
|
||||
SG_CHECK_EQUAL(rd.get_wgs84_nodes().size(), 0);
|
||||
SG_VERIFY(rd.get_pt_materials().empty());
|
||||
}
|
||||
|
||||
COMPARE(rd.get_wgs84_nodes().size(), 0);
|
||||
VERIFY(rd.get_pt_materials().empty());
|
||||
}
|
||||
|
||||
void comparePoints(const SGBinObject& rd, const std::vector<SGVec3d>& b)
|
||||
{
|
||||
for (unsigned int i=1; i<b.size(); i += 10) {
|
||||
@@ -82,7 +71,7 @@ void comparePoints(const SGBinObject& rd, const std::vector<SGVec3d>& b)
|
||||
cout << pos << endl;
|
||||
}
|
||||
|
||||
VERIFY(equivalent(pos, b[i], 0.1));
|
||||
SG_VERIFY(equivalent(pos, b[i], 0.1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +79,7 @@ void compareTexCoords(const SGBinObject& rd, const std::vector<SGVec2f>& b)
|
||||
{
|
||||
for (unsigned int i=1; i<b.size(); i += 10) {
|
||||
SGVec2f pos = rd.get_texcoords()[i];
|
||||
VERIFY(equivalent(pos, b[i], 0.001f));
|
||||
SG_VERIFY(equivalent(pos, b[i], 0.001f));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,13 +126,13 @@ void compareTris(const SGBinObject& a, const SGBinObject& b)
|
||||
for (unsigned int i=0; i<count; i += 39) {
|
||||
const int_list& vA(a.get_tris_v()[i]);
|
||||
const int_list& vB(b.get_tris_v()[i]);
|
||||
VERIFY(vA == vB);
|
||||
SG_VERIFY(vA == vB);
|
||||
|
||||
SG_CHECK_EQUAL(a.get_tri_materials()[i], b.get_tri_materials()[i]);
|
||||
|
||||
COMPARE(a.get_tri_materials()[i], b.get_tri_materials()[i]);
|
||||
|
||||
const int_list& tA(a.get_tris_tcs()[i][0]);
|
||||
const int_list& tB(b.get_tris_tcs()[i][0]);
|
||||
VERIFY(tA == tB);
|
||||
SG_VERIFY(tA == tB);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,16 +178,17 @@ void test_basic()
|
||||
basic.set_texcoords(texCoords);
|
||||
|
||||
bool ok = basic.write_bin_file(path);
|
||||
VERIFY( ok );
|
||||
SG_VERIFY( ok );
|
||||
|
||||
SGBinObject rd;
|
||||
ok = rd.read_bin(path) ;
|
||||
VERIFY( ok);
|
||||
COMPARE(rd.get_version(), 7); // should be version 7 since indices are < 2^16
|
||||
COMPARE(rd.get_gbs_center(), center);
|
||||
COMPARE(rd.get_gbs_radius(), 12345);
|
||||
COMPARE(rd.get_wgs84_nodes().size(), points.size());
|
||||
|
||||
SG_VERIFY( ok);
|
||||
// Should be version 7 since indices are < 2^16
|
||||
SG_CHECK_EQUAL(rd.get_version(), 7);
|
||||
SG_CHECK_EQUAL(rd.get_gbs_center(), center);
|
||||
SG_CHECK_EQUAL(rd.get_gbs_radius(), 12345);
|
||||
SG_CHECK_EQUAL(rd.get_wgs84_nodes().size(), points.size());
|
||||
|
||||
comparePoints(rd, points);
|
||||
compareTexCoords(rd, texCoords);
|
||||
}
|
||||
@@ -226,15 +216,16 @@ void test_many_tcs()
|
||||
generate_tris(basic, 20000);
|
||||
|
||||
bool ok = basic.write_bin_file(path);
|
||||
VERIFY( ok );
|
||||
SG_VERIFY( ok );
|
||||
|
||||
SGBinObject rd;
|
||||
ok = rd.read_bin(path) ;
|
||||
VERIFY( ok);
|
||||
COMPARE(rd.get_version(), 10); // should be version 10 since indices are > 2^16
|
||||
COMPARE(rd.get_wgs84_nodes().size(), points.size());
|
||||
COMPARE(rd.get_texcoords().size(), texCoords.size());
|
||||
|
||||
SG_VERIFY( ok);
|
||||
// Should be version 10 since indices are > 2^16
|
||||
SG_CHECK_EQUAL(rd.get_version(), 10);
|
||||
SG_CHECK_EQUAL(rd.get_wgs84_nodes().size(), points.size());
|
||||
SG_CHECK_EQUAL(rd.get_texcoords().size(), texCoords.size());
|
||||
|
||||
comparePoints(rd, points);
|
||||
compareTexCoords(rd, texCoords);
|
||||
compareTris(basic, rd);
|
||||
@@ -263,15 +254,16 @@ void test_big()
|
||||
generate_tris(basic, 200000);
|
||||
|
||||
bool ok = basic.write_bin_file(path);
|
||||
VERIFY( ok );
|
||||
SG_VERIFY( ok );
|
||||
|
||||
SGBinObject rd;
|
||||
ok = rd.read_bin(path) ;
|
||||
VERIFY( ok);
|
||||
COMPARE(rd.get_version(), 10); // should be version 10 since indices are > 2^16
|
||||
COMPARE(rd.get_wgs84_nodes().size(), points.size());
|
||||
COMPARE(rd.get_texcoords().size(), texCoords.size());
|
||||
|
||||
SG_VERIFY( ok);
|
||||
// Should be version 10 since indices are > 2^16
|
||||
SG_CHECK_EQUAL(rd.get_version(), 10);
|
||||
SG_CHECK_EQUAL(rd.get_wgs84_nodes().size(), points.size());
|
||||
SG_CHECK_EQUAL(rd.get_texcoords().size(), texCoords.size());
|
||||
|
||||
comparePoints(rd, points);
|
||||
compareTexCoords(rd, texCoords);
|
||||
compareTris(basic, rd);
|
||||
@@ -300,15 +292,15 @@ void test_some_objects()
|
||||
generate_tris(basic, 30000); // a number smaller than 2^15!
|
||||
|
||||
bool ok = basic.write_bin_file(path);
|
||||
VERIFY( ok );
|
||||
SG_VERIFY( ok );
|
||||
|
||||
SGBinObject rd;
|
||||
ok = rd.read_bin(path) ;
|
||||
VERIFY( ok);
|
||||
COMPARE(rd.get_version(), 7); // since we have less than 2^15 tris
|
||||
COMPARE(rd.get_wgs84_nodes().size(), points.size());
|
||||
COMPARE(rd.get_texcoords().size(), texCoords.size());
|
||||
|
||||
SG_VERIFY( ok);
|
||||
SG_CHECK_EQUAL(rd.get_version(), 7); // since we have less than 2^15 tris
|
||||
SG_CHECK_EQUAL(rd.get_wgs84_nodes().size(), points.size());
|
||||
SG_CHECK_EQUAL(rd.get_texcoords().size(), texCoords.size());
|
||||
|
||||
comparePoints(rd, points);
|
||||
compareTexCoords(rd, texCoords);
|
||||
compareTris(basic, rd);
|
||||
@@ -337,15 +329,16 @@ void test_many_objects()
|
||||
generate_tris(basic, 200000);
|
||||
|
||||
bool ok = basic.write_bin_file(path);
|
||||
VERIFY( ok );
|
||||
SG_VERIFY( ok );
|
||||
|
||||
SGBinObject rd;
|
||||
ok = rd.read_bin(path) ;
|
||||
VERIFY( ok);
|
||||
COMPARE(rd.get_version(), 10); // should be version 10 since indices are > 2^16
|
||||
COMPARE(rd.get_wgs84_nodes().size(), points.size());
|
||||
COMPARE(rd.get_texcoords().size(), texCoords.size());
|
||||
|
||||
SG_VERIFY( ok);
|
||||
// Should be version 10 since indices are > 2^16
|
||||
SG_CHECK_EQUAL(rd.get_version(), 10);
|
||||
SG_CHECK_EQUAL(rd.get_wgs84_nodes().size(), points.size());
|
||||
SG_CHECK_EQUAL(rd.get_texcoords().size(), texCoords.size());
|
||||
|
||||
comparePoints(rd, points);
|
||||
compareTexCoords(rd, texCoords);
|
||||
compareTris(basic, rd);
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#include <simgear/timing/timestamp.hxx>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/misc/sg_dir.hxx>
|
||||
#include <simgear/misc/sgstream.hxx>
|
||||
#include <simgear/io/iostreams/sgstream.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include <simgear/structure/callback.hxx>
|
||||
|
||||
@@ -71,7 +71,7 @@ public:
|
||||
int requestCount;
|
||||
bool getWillFail;
|
||||
bool returnCorruptData;
|
||||
std::auto_ptr<SGCallback> accessCallback;
|
||||
std::unique_ptr<SGCallback> accessCallback;
|
||||
|
||||
void clearRequestCounts();
|
||||
|
||||
@@ -400,7 +400,7 @@ void waitForUpdateComplete(HTTP::Client* cl, HTTPRepository* repo)
|
||||
|
||||
void testBasicClone(HTTP::Client* cl)
|
||||
{
|
||||
std::auto_ptr<HTTPRepository> repo;
|
||||
std::unique_ptr<HTTPRepository> repo;
|
||||
SGPath p(simgear::Dir::current().path());
|
||||
p.append("http_repo_basic");
|
||||
simgear::Dir pd(p);
|
||||
@@ -408,7 +408,6 @@ void testBasicClone(HTTP::Client* cl)
|
||||
|
||||
repo.reset(new HTTPRepository(p, cl));
|
||||
repo->setBaseUrl("http://localhost:2000/repo");
|
||||
repo->setEntireRepositoryMode();
|
||||
repo->update();
|
||||
|
||||
waitForUpdateComplete(cl, repo.get());
|
||||
@@ -436,7 +435,7 @@ void testBasicClone(HTTP::Client* cl)
|
||||
|
||||
void testModifyLocalFiles(HTTP::Client* cl)
|
||||
{
|
||||
std::auto_ptr<HTTPRepository> repo;
|
||||
std::unique_ptr<HTTPRepository> repo;
|
||||
SGPath p(simgear::Dir::current().path());
|
||||
p.append("http_repo_modify_local_2");
|
||||
simgear::Dir pd(p);
|
||||
@@ -446,7 +445,6 @@ void testModifyLocalFiles(HTTP::Client* cl)
|
||||
|
||||
repo.reset(new HTTPRepository(p, cl));
|
||||
repo->setBaseUrl("http://localhost:2000/repo");
|
||||
repo->setEntireRepositoryMode();
|
||||
repo->update();
|
||||
|
||||
waitForUpdateComplete(cl, repo.get());
|
||||
@@ -478,7 +476,7 @@ void testNoChangesUpdate()
|
||||
|
||||
void testMergeExistingFileWithoutDownload(HTTP::Client* cl)
|
||||
{
|
||||
std::auto_ptr<HTTPRepository> repo;
|
||||
std::unique_ptr<HTTPRepository> repo;
|
||||
SGPath p(simgear::Dir::current().path());
|
||||
p.append("http_repo_merge_existing");
|
||||
simgear::Dir pd(p);
|
||||
@@ -488,7 +486,6 @@ void testMergeExistingFileWithoutDownload(HTTP::Client* cl)
|
||||
|
||||
repo.reset(new HTTPRepository(p, cl));
|
||||
repo->setBaseUrl("http://localhost:2000/repo");
|
||||
repo->setEntireRepositoryMode();
|
||||
|
||||
createFile(p, "dirC/fileCB", 4); // should match
|
||||
createFile(p, "dirC/fileCC", 3); // mismatch
|
||||
@@ -521,7 +518,7 @@ void testMergeExistingFileWithoutDownload(HTTP::Client* cl)
|
||||
|
||||
void testLossOfLocalFiles(HTTP::Client* cl)
|
||||
{
|
||||
std::auto_ptr<HTTPRepository> repo;
|
||||
std::unique_ptr<HTTPRepository> repo;
|
||||
SGPath p(simgear::Dir::current().path());
|
||||
p.append("http_repo_lose_local");
|
||||
simgear::Dir pd(p);
|
||||
@@ -531,7 +528,6 @@ void testLossOfLocalFiles(HTTP::Client* cl)
|
||||
|
||||
repo.reset(new HTTPRepository(p, cl));
|
||||
repo->setBaseUrl("http://localhost:2000/repo");
|
||||
repo->setEntireRepositoryMode();
|
||||
repo->update();
|
||||
waitForUpdateComplete(cl, repo.get());
|
||||
verifyFileState(p, "dirB/subdirA/fileBAA");
|
||||
@@ -556,7 +552,7 @@ void testLossOfLocalFiles(HTTP::Client* cl)
|
||||
|
||||
void testAbandonMissingFiles(HTTP::Client* cl)
|
||||
{
|
||||
std::auto_ptr<HTTPRepository> repo;
|
||||
std::unique_ptr<HTTPRepository> repo;
|
||||
SGPath p(simgear::Dir::current().path());
|
||||
p.append("http_repo_missing_files");
|
||||
simgear::Dir pd(p);
|
||||
@@ -569,7 +565,6 @@ void testAbandonMissingFiles(HTTP::Client* cl)
|
||||
|
||||
repo.reset(new HTTPRepository(p, cl));
|
||||
repo->setBaseUrl("http://localhost:2000/repo");
|
||||
repo->setEntireRepositoryMode();
|
||||
repo->update();
|
||||
waitForUpdateComplete(cl, repo.get());
|
||||
if (repo->failure() != HTTPRepository::REPO_PARTIAL_UPDATE) {
|
||||
@@ -581,7 +576,7 @@ void testAbandonMissingFiles(HTTP::Client* cl)
|
||||
|
||||
void testAbandonCorruptFiles(HTTP::Client* cl)
|
||||
{
|
||||
std::auto_ptr<HTTPRepository> repo;
|
||||
std::unique_ptr<HTTPRepository> repo;
|
||||
SGPath p(simgear::Dir::current().path());
|
||||
p.append("http_repo_corrupt_files");
|
||||
simgear::Dir pd(p);
|
||||
@@ -594,11 +589,10 @@ void testAbandonCorruptFiles(HTTP::Client* cl)
|
||||
|
||||
repo.reset(new HTTPRepository(p, cl));
|
||||
repo->setBaseUrl("http://localhost:2000/repo");
|
||||
repo->setEntireRepositoryMode();
|
||||
|
||||
repo->update();
|
||||
waitForUpdateComplete(cl, repo.get());
|
||||
if (repo->failure() != HTTPRepository::REPO_ERROR_CHECKSUM) {
|
||||
std::cerr << "Got failure state:" << repo->failure() << std::endl;
|
||||
throw sg_exception("Bad result from corrupt files test");
|
||||
}
|
||||
|
||||
@@ -611,149 +605,6 @@ void testAbandonCorruptFiles(HTTP::Client* cl)
|
||||
std::cout << "Passed test: detect corrupted download" << std::endl;
|
||||
}
|
||||
|
||||
void testPartialUpdateBasic(HTTP::Client* cl)
|
||||
{
|
||||
std::auto_ptr<HTTPRepository> repo;
|
||||
SGPath p(simgear::Dir::current().path());
|
||||
p.append("http_repo_partial_update");
|
||||
simgear::Dir pd(p);
|
||||
if (pd.exists()) {
|
||||
pd.removeChildren();
|
||||
}
|
||||
|
||||
global_repo->clearRequestCounts();
|
||||
global_repo->clearFailFlags();
|
||||
global_repo->defineFile("dirA/subdirF/fileAFA");
|
||||
global_repo->defineFile("dirA/subdirF/fileAFB");
|
||||
global_repo->defineFile("dirA/subdirH/fileAHA");
|
||||
global_repo->defineFile("dirA/subdirH/fileAHB");
|
||||
|
||||
global_repo->defineFile("dirG/subdirA/subsubA/fileGAAB");
|
||||
|
||||
// request subdir of A
|
||||
repo.reset(new HTTPRepository(p, cl));
|
||||
repo->setBaseUrl("http://localhost:2000/repo");
|
||||
repo->addSubpath("dirA/subdirF");
|
||||
waitForUpdateComplete(cl, repo.get());
|
||||
|
||||
verifyFileState(p, "dirA/subdirF/fileAFA");
|
||||
verifyFileState(p, "dirA/subdirF/fileAFB");
|
||||
|
||||
verifyFileState(p, "fileA"); // files are always synced
|
||||
verifyFileState(p, "dirA/fileAB");
|
||||
verifyFileNotPresent(p, "dirB/subdirB/fileBBB");
|
||||
verifyFileNotPresent(p, "dirD");
|
||||
verifyFileNotPresent(p, "dirA/subdirH/fileAHB");
|
||||
|
||||
verifyRequestCount("dirA", 1);
|
||||
verifyRequestCount("dirA/fileAA", 1);
|
||||
verifyRequestCount("dirA/subdirF", 1);
|
||||
verifyRequestCount("dirA/subdirF/fileAFA", 1);
|
||||
verifyRequestCount("dirA/subdirF/fileAFB", 1);
|
||||
verifyRequestCount("dirB", 0);
|
||||
verifyRequestCount("dirG", 0);
|
||||
|
||||
// now request dir B
|
||||
repo->addSubpath("dirB");
|
||||
waitForUpdateComplete(cl, repo.get());
|
||||
|
||||
verifyFileState(p, "dirA/subdirF/fileAFB");
|
||||
verifyFileState(p, "dirB/subdirB/fileBBA");
|
||||
verifyFileState(p, "dirB/subdirB/fileBBB");
|
||||
|
||||
verifyRequestCount("dirB", 1);
|
||||
verifyRequestCount("dirB/subdirA/fileBAC", 1);
|
||||
verifyRequestCount("dirA", 1);
|
||||
verifyRequestCount("dirA/fileAA", 1);
|
||||
verifyRequestCount("dirG", 0);
|
||||
|
||||
// widen subdir to parent
|
||||
repo->addSubpath("dirA");
|
||||
waitForUpdateComplete(cl, repo.get());
|
||||
|
||||
verifyFileState(p, "dirA/subdirH/fileAHA");
|
||||
verifyFileState(p, "dirA/subdirH/fileAHB");
|
||||
|
||||
verifyRequestCount("dirA", 1);
|
||||
verifyRequestCount("dirB/subdirA/fileBAC", 1);
|
||||
verifyRequestCount("dirA/subdirF/fileAFA", 1);
|
||||
|
||||
// request an already fetched subdir - should be a no-op
|
||||
repo->addSubpath("dirB/subdirB");
|
||||
waitForUpdateComplete(cl, repo.get());
|
||||
|
||||
verifyRequestCount("dirB", 1);
|
||||
verifyRequestCount("dirB/subdirB/fileBBB", 1);
|
||||
|
||||
// add new / modify files inside
|
||||
global_repo->defineFile("dirA/subdirF/fileAFC");
|
||||
global_repo->defineFile("dirA/subdirF/fileAFD");
|
||||
repo->update();
|
||||
waitForUpdateComplete(cl, repo.get());
|
||||
|
||||
if (global_repo->requestCount != 2) {
|
||||
throw sg_exception("Bad root request count");
|
||||
}
|
||||
|
||||
verifyFileState(p, "dirA/subdirF/fileAFC");
|
||||
verifyFileState(p, "dirA/subdirF/fileAFD");
|
||||
|
||||
std::cout << "Passed test: basic partial clone and update" << std::endl;
|
||||
}
|
||||
|
||||
void testPartialUpdateExisting(HTTP::Client* cl)
|
||||
{
|
||||
std::auto_ptr<HTTPRepository> repo;
|
||||
SGPath p(simgear::Dir::current().path());
|
||||
p.append("http_repo_partial_update_existing");
|
||||
simgear::Dir pd(p);
|
||||
if (pd.exists()) {
|
||||
pd.removeChildren();
|
||||
}
|
||||
|
||||
global_repo->clearRequestCounts();
|
||||
global_repo->clearFailFlags();
|
||||
|
||||
// full update to sync everything
|
||||
repo.reset(new HTTPRepository(p, cl));
|
||||
repo->setBaseUrl("http://localhost:2000/repo");
|
||||
repo->setEntireRepositoryMode();
|
||||
repo->update();
|
||||
waitForUpdateComplete(cl, repo.get());
|
||||
|
||||
// new repo for partial
|
||||
global_repo->clearRequestCounts();
|
||||
repo.reset(new HTTPRepository(p, cl));
|
||||
repo->setBaseUrl("http://localhost:2000/repo");
|
||||
repo->addSubpath("dirA/subdirF");
|
||||
waitForUpdateComplete(cl, repo.get());
|
||||
|
||||
if (global_repo->requestCount != 1) {
|
||||
throw sg_exception("Bad root request count");
|
||||
}
|
||||
|
||||
verifyRequestCount("dirA", 0);
|
||||
verifyRequestCount("dirA/fileAA", 0);
|
||||
verifyRequestCount("dirA/subdirF", 0);
|
||||
verifyRequestCount("dirA/subdirF/fileAFA", 0);
|
||||
verifyRequestCount("dirA/subdirF/fileAFB", 0);
|
||||
|
||||
// and request more dirs
|
||||
// this is a good simulation of terrasync requesting more subdirs of
|
||||
// an already created and in sync tree. should not generate any more
|
||||
// network trip
|
||||
repo->addSubpath("dirC");
|
||||
|
||||
verifyFileState(p, "dirC/subdirA/subsubA/fileCAAA");
|
||||
verifyRequestCount("dirC/subdirA/subsubA/fileCAAA", 0);
|
||||
|
||||
if (global_repo->requestCount != 1) {
|
||||
throw sg_exception("Bad root request count");
|
||||
}
|
||||
|
||||
std::cout << "Passed test: partial update of existing" << std::endl;
|
||||
}
|
||||
|
||||
void modifyBTree()
|
||||
{
|
||||
std::cout << "Modifying sub-tree" << std::endl;
|
||||
@@ -763,53 +614,9 @@ void modifyBTree()
|
||||
global_repo->findEntry("dirB/subdirB/fileBBB")->revision++;
|
||||
}
|
||||
|
||||
void testPartialUpdateWidenWhileInProgress(HTTP::Client* cl)
|
||||
{
|
||||
std::auto_ptr<HTTPRepository> repo;
|
||||
SGPath p(simgear::Dir::current().path());
|
||||
p.append("http_repo_partial_update_widen");
|
||||
simgear::Dir pd(p);
|
||||
if (pd.exists()) {
|
||||
pd.removeChildren();
|
||||
}
|
||||
|
||||
global_repo->clearRequestCounts();
|
||||
global_repo->clearFailFlags();
|
||||
|
||||
// full update to sync everything
|
||||
repo.reset(new HTTPRepository(p, cl));
|
||||
repo->setBaseUrl("http://localhost:2000/repo");
|
||||
|
||||
repo->addSubpath("dirA/subdirF");
|
||||
repo->addSubpath("dirB/subdirB");
|
||||
waitForUpdateComplete(cl, repo.get());
|
||||
|
||||
verifyRequestCount("dirA/subdirF", 1);
|
||||
if (global_repo->requestCount != 1) {
|
||||
throw sg_exception("Bad root request count");
|
||||
}
|
||||
|
||||
repo->addSubpath("dirA");
|
||||
repo->addSubpath("dirB");
|
||||
repo->addSubpath("dirC");
|
||||
|
||||
waitForUpdateComplete(cl, repo.get());
|
||||
|
||||
// should not request the root again
|
||||
verifyRequestCount("dirA/subdirF", 1);
|
||||
if (global_repo->requestCount != 1) {
|
||||
throw sg_exception("Bad root request count");
|
||||
}
|
||||
|
||||
verifyFileState(p, "dirA/subdirF/fileAFA");
|
||||
verifyFileState(p, "dirC/subdirA/subsubA/fileCAAA");
|
||||
|
||||
std::cout << "Passed test: partial update with widen" << std::endl;
|
||||
}
|
||||
|
||||
void testServerModifyDuringSync(HTTP::Client* cl)
|
||||
{
|
||||
std::auto_ptr<HTTPRepository> repo;
|
||||
std::unique_ptr<HTTPRepository> repo;
|
||||
SGPath p(simgear::Dir::current().path());
|
||||
p.append("http_repo_server_modify_during_sync");
|
||||
simgear::Dir pd(p);
|
||||
@@ -822,7 +629,6 @@ void testServerModifyDuringSync(HTTP::Client* cl)
|
||||
|
||||
repo.reset(new HTTPRepository(p, cl));
|
||||
repo->setBaseUrl("http://localhost:2000/repo");
|
||||
repo->setEntireRepositoryMode();
|
||||
|
||||
global_repo->findEntry("dirA/fileAA")->accessCallback.reset(make_callback(&modifyBTree));
|
||||
|
||||
@@ -832,7 +638,7 @@ void testServerModifyDuringSync(HTTP::Client* cl)
|
||||
global_repo->findEntry("dirA/fileAA")->accessCallback.reset();
|
||||
|
||||
if (repo->failure() != HTTPRepository::REPO_ERROR_CHECKSUM) {
|
||||
throw sg_exception("Bad result from corrupt files test");
|
||||
throw sg_exception("Bad result from modify during sync test");
|
||||
}
|
||||
|
||||
std::cout << "Passed test modify server during sync" << std::endl;
|
||||
@@ -841,7 +647,7 @@ void testServerModifyDuringSync(HTTP::Client* cl)
|
||||
|
||||
void testDestroyDuringSync(HTTP::Client* cl)
|
||||
{
|
||||
std::auto_ptr<HTTPRepository> repo;
|
||||
std::unique_ptr<HTTPRepository> repo;
|
||||
SGPath p(simgear::Dir::current().path());
|
||||
p.append("http_repo_destory_during_sync");
|
||||
simgear::Dir pd(p);
|
||||
@@ -854,7 +660,6 @@ void testDestroyDuringSync(HTTP::Client* cl)
|
||||
|
||||
repo.reset(new HTTPRepository(p, cl));
|
||||
repo->setBaseUrl("http://localhost:2000/repo");
|
||||
repo->setEntireRepositoryMode();
|
||||
|
||||
repo->update();
|
||||
|
||||
@@ -869,10 +674,63 @@ void testDestroyDuringSync(HTTP::Client* cl)
|
||||
std::cout << "Passed test destory during sync" << std::endl;
|
||||
}
|
||||
|
||||
void testCopyInstalledChildren(HTTP::Client* cl)
|
||||
{
|
||||
std::unique_ptr<HTTPRepository> repo;
|
||||
SGPath p(simgear::Dir::current().path());
|
||||
p.append("http_repo_copy_installed_children");
|
||||
simgear::Dir pd(p);
|
||||
if (pd.exists()) {
|
||||
pd.removeChildren();
|
||||
}
|
||||
|
||||
// setup installation data
|
||||
SGPath p2(simgear::Dir::current().path());
|
||||
p2.append("http_repo_copy_installed_children_install");
|
||||
simgear::Dir pd2(p2);
|
||||
if (pd2.exists()) {
|
||||
pd2.removeChildren();
|
||||
} else {
|
||||
pd2.create(0700);
|
||||
}
|
||||
|
||||
// fill in 'install' tree data
|
||||
createFile(p, "dirJ/fileJA", 2);
|
||||
createFile(p, "dirJ/fileJB", 3);
|
||||
createFile(p, "dirJ/fileJC", 1);
|
||||
|
||||
global_repo->defineFile("dirJ/fileJA", 2);
|
||||
global_repo->defineFile("dirJ/fileJB", 3);
|
||||
global_repo->defineFile("dirJ/fileJC", 3); // newer
|
||||
global_repo->defineFile("dirJ/fileJD", 3); // not present in install
|
||||
|
||||
global_repo->clearRequestCounts();
|
||||
global_repo->clearFailFlags();
|
||||
|
||||
repo.reset(new HTTPRepository(p, cl));
|
||||
repo->setBaseUrl("http://localhost:2000/repo");
|
||||
repo->setInstalledCopyPath(p2);
|
||||
repo->update();
|
||||
|
||||
// verify correct files were downloaded, only dirs
|
||||
|
||||
waitForUpdateComplete(cl, repo.get());
|
||||
verifyFileState(p, "dirJ/fileJA");
|
||||
verifyFileState(p, "dirJ/fileJB");
|
||||
verifyFileState(p, "dirJ/fileJC");
|
||||
verifyFileState(p, "dirJ/fileJD");
|
||||
|
||||
verifyRequestCount("dirJ/fileJA", 0);
|
||||
verifyRequestCount("dirJ/fileJB", 0);
|
||||
verifyRequestCount("dirJ/fileJC", 1);
|
||||
verifyRequestCount("dirJ/fileJD", 1);
|
||||
|
||||
std::cout << "Copy installed children" << std::endl;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
sglog().setLogLevels( SG_ALL, SG_DEBUG );
|
||||
sglog().setLogLevels( SG_ALL, SG_INFO );
|
||||
|
||||
HTTP::Client cl;
|
||||
cl.setMaxConnections(1);
|
||||
@@ -907,13 +765,14 @@ int main(int argc, char* argv[])
|
||||
testServer.disconnectAll();
|
||||
cl.clearAllConnections();
|
||||
|
||||
testPartialUpdateBasic(&cl);
|
||||
testPartialUpdateExisting(&cl);
|
||||
testPartialUpdateWidenWhileInProgress(&cl);
|
||||
|
||||
testServerModifyDuringSync(&cl);
|
||||
|
||||
testDestroyDuringSync(&cl);
|
||||
|
||||
testServer.disconnectAll();
|
||||
cl.clearAllConnections();
|
||||
|
||||
testCopyInstalledChildren(&cl);
|
||||
|
||||
std::cout << "all tests passed ok" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ void testTarGz()
|
||||
uint8_t* buf = (uint8_t*) alloca(8192);
|
||||
size_t bufSize = f.read((char*) buf, 8192);
|
||||
|
||||
VERIFY(TarExtractor::isTarData(buf, bufSize));
|
||||
SG_VERIFY(TarExtractor::isTarData(buf, bufSize));
|
||||
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ void testPlainTar()
|
||||
uint8_t* buf = (uint8_t*) alloca(8192);
|
||||
size_t bufSize = f.read((char*) buf, 8192);
|
||||
|
||||
VERIFY(TarExtractor::isTarData(buf, bufSize));
|
||||
SG_VERIFY(TarExtractor::isTarData(buf, bufSize));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
#include <simgear/timing/timestamp.hxx>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/misc/test_macros.hxx>
|
||||
|
||||
#include <curl/multi.h>
|
||||
|
||||
@@ -41,18 +42,6 @@ const char* BODY3 = "Cras ut neque nulla. Duis ut velit neque, sit amet "
|
||||
const unsigned int body2Size = 8 * 1024;
|
||||
char body2[body2Size];
|
||||
|
||||
#define COMPARE(a, b) \
|
||||
if ((a) != (b)) { \
|
||||
cerr << "failed:" << #a << " != " << #b << endl; \
|
||||
cerr << "\tgot:'" << a << "'" << endl; \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
#define VERIFY(a) \
|
||||
if (!(a)) { \
|
||||
cerr << "failed:" << #a << endl; \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
class TestRequest : public HTTP::Request
|
||||
{
|
||||
@@ -126,8 +115,9 @@ public:
|
||||
d << contentStr;
|
||||
push(d.str().c_str());
|
||||
} else if (path == "/test_headers") {
|
||||
COMPARE(requestHeaders["X-Foo"], string("Bar"));
|
||||
COMPARE(requestHeaders["X-AnotherHeader"], string("A longer value"));
|
||||
SG_CHECK_EQUAL(requestHeaders["X-Foo"], string("Bar"));
|
||||
SG_CHECK_EQUAL(requestHeaders["X-AnotherHeader"],
|
||||
string("A longer value"));
|
||||
|
||||
string contentStr(BODY1);
|
||||
stringstream d;
|
||||
@@ -304,7 +294,7 @@ public:
|
||||
} else if (path == "/test_put") {
|
||||
std::cerr << "sending PUT response" << std::endl;
|
||||
|
||||
COMPARE(buffer, BODY3);
|
||||
SG_CHECK_EQUAL(buffer, BODY3);
|
||||
stringstream d;
|
||||
d << "HTTP/1.1 " << 204 << " " << reasonForCode(204) << "\r\n";
|
||||
d << "\r\n"; // final CRLF to terminate the headers
|
||||
@@ -314,7 +304,7 @@ public:
|
||||
|
||||
std::string entityStr = "http://localhost:2000/something.txt";
|
||||
|
||||
COMPARE(buffer, BODY3);
|
||||
SG_CHECK_EQUAL(buffer, BODY3);
|
||||
stringstream d;
|
||||
d << "HTTP/1.1 " << 201 << " " << reasonForCode(201) << "\r\n";
|
||||
d << "Location:" << entityStr << "\r\n";
|
||||
@@ -385,18 +375,18 @@ int main(int argc, char* argv[])
|
||||
|
||||
// test URL parsing
|
||||
TestRequest* tr1 = new TestRequest("http://localhost.woo.zar:2000/test1?foo=bar");
|
||||
COMPARE(tr1->scheme(), "http");
|
||||
COMPARE(tr1->hostAndPort(), "localhost.woo.zar:2000");
|
||||
COMPARE(tr1->host(), "localhost.woo.zar");
|
||||
COMPARE(tr1->port(), 2000);
|
||||
COMPARE(tr1->path(), "/test1");
|
||||
SG_CHECK_EQUAL(tr1->scheme(), "http");
|
||||
SG_CHECK_EQUAL(tr1->hostAndPort(), "localhost.woo.zar:2000");
|
||||
SG_CHECK_EQUAL(tr1->host(), "localhost.woo.zar");
|
||||
SG_CHECK_EQUAL(tr1->port(), 2000);
|
||||
SG_CHECK_EQUAL(tr1->path(), "/test1");
|
||||
|
||||
TestRequest* tr2 = new TestRequest("http://192.168.1.1/test1/dir/thing/file.png");
|
||||
COMPARE(tr2->scheme(), "http");
|
||||
COMPARE(tr2->hostAndPort(), "192.168.1.1");
|
||||
COMPARE(tr2->host(), "192.168.1.1");
|
||||
COMPARE(tr2->port(), 80);
|
||||
COMPARE(tr2->path(), "/test1/dir/thing/file.png");
|
||||
SG_CHECK_EQUAL(tr2->scheme(), "http");
|
||||
SG_CHECK_EQUAL(tr2->hostAndPort(), "192.168.1.1");
|
||||
SG_CHECK_EQUAL(tr2->host(), "192.168.1.1");
|
||||
SG_CHECK_EQUAL(tr2->port(), 80);
|
||||
SG_CHECK_EQUAL(tr2->path(), "/test1/dir/thing/file.png");
|
||||
|
||||
// basic get request
|
||||
{
|
||||
@@ -405,11 +395,11 @@ int main(int argc, char* argv[])
|
||||
cl.makeRequest(tr);
|
||||
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseReason(), string("OK"));
|
||||
COMPARE(tr->responseLength(), strlen(BODY1));
|
||||
COMPARE(tr->responseBytesReceived(), strlen(BODY1));
|
||||
COMPARE(tr->bodyData, string(BODY1));
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->responseReason(), string("OK"));
|
||||
SG_CHECK_EQUAL(tr->responseLength(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr->responseBytesReceived(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(BODY1));
|
||||
}
|
||||
|
||||
{
|
||||
@@ -418,11 +408,11 @@ int main(int argc, char* argv[])
|
||||
cl.makeRequest(tr);
|
||||
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseReason(), string("OK"));
|
||||
COMPARE(tr->responseLength(), strlen(BODY3));
|
||||
COMPARE(tr->responseBytesReceived(), strlen(BODY3));
|
||||
COMPARE(tr->bodyData, string(BODY3));
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->responseReason(), string("OK"));
|
||||
SG_CHECK_EQUAL(tr->responseLength(), strlen(BODY3));
|
||||
SG_CHECK_EQUAL(tr->responseBytesReceived(), strlen(BODY3));
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(BODY3));
|
||||
}
|
||||
|
||||
{
|
||||
@@ -430,7 +420,7 @@ int main(int argc, char* argv[])
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -441,11 +431,11 @@ int main(int argc, char* argv[])
|
||||
cl.makeRequest(tr);
|
||||
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseReason(), string("OK"));
|
||||
COMPARE(tr->responseLength(), strlen(BODY1));
|
||||
COMPARE(tr->responseBytesReceived(), strlen(BODY1));
|
||||
COMPARE(tr->bodyData, string(BODY1));
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->responseReason(), string("OK"));
|
||||
SG_CHECK_EQUAL(tr->responseLength(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr->responseBytesReceived(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(BODY1));
|
||||
}
|
||||
|
||||
// larger get request
|
||||
@@ -458,9 +448,9 @@ int main(int argc, char* argv[])
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseBytesReceived(), body2Size);
|
||||
COMPARE(tr->bodyData, string(body2, body2Size));
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->responseBytesReceived(), body2Size);
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(body2, body2Size));
|
||||
}
|
||||
|
||||
cerr << "testing chunked transfer encoding" << endl;
|
||||
@@ -470,12 +460,12 @@ int main(int argc, char* argv[])
|
||||
cl.makeRequest(tr);
|
||||
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseReason(), string("OK"));
|
||||
COMPARE(tr->responseBytesReceived(), 30);
|
||||
COMPARE(tr->bodyData, "ABCDEFGHABCDEFABCDSTUVABCDSTUV");
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->responseReason(), string("OK"));
|
||||
SG_CHECK_EQUAL(tr->responseBytesReceived(), 30);
|
||||
SG_CHECK_EQUAL(tr->bodyData, "ABCDEFGHABCDEFABCDSTUVABCDSTUV");
|
||||
// check trailers made it too
|
||||
COMPARE(tr->headers["x-foobar"], string("wibble"));
|
||||
SG_CHECK_EQUAL(tr->headers["x-foobar"], string("wibble"));
|
||||
}
|
||||
|
||||
// test 404
|
||||
@@ -484,9 +474,9 @@ int main(int argc, char* argv[])
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 404);
|
||||
COMPARE(tr->responseReason(), string("not found"));
|
||||
COMPARE(tr->responseLength(), 0);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 404);
|
||||
SG_CHECK_EQUAL(tr->responseReason(), string("not found"));
|
||||
SG_CHECK_EQUAL(tr->responseLength(), 0);
|
||||
}
|
||||
|
||||
cout << "done 404 test" << endl;
|
||||
@@ -496,7 +486,7 @@ int main(int argc, char* argv[])
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
}
|
||||
|
||||
cout << "done1" << endl;
|
||||
@@ -506,9 +496,9 @@ int main(int argc, char* argv[])
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseLength(), strlen(BODY1));
|
||||
COMPARE(tr->bodyData, string(BODY1));
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->responseLength(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(BODY1));
|
||||
}
|
||||
|
||||
cout << "done2" << endl;
|
||||
@@ -518,9 +508,9 @@ int main(int argc, char* argv[])
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseLength(), strlen(BODY1));
|
||||
COMPARE(tr->bodyData, string(BODY1));
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->responseLength(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(BODY1));
|
||||
}
|
||||
cout << "done3" << endl;
|
||||
// test connectToHost failure
|
||||
@@ -532,7 +522,7 @@ int main(int argc, char* argv[])
|
||||
waitForFailed(&cl, tr);
|
||||
|
||||
const int HOST_NOT_FOUND_CODE = CURLE_COULDNT_RESOLVE_HOST;
|
||||
COMPARE(tr->responseCode(), HOST_NOT_FOUND_CODE);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), HOST_NOT_FOUND_CODE);
|
||||
}
|
||||
|
||||
cout << "testing abrupt close" << endl;
|
||||
@@ -544,7 +534,7 @@ int main(int argc, char* argv[])
|
||||
waitForFailed(&cl, tr);
|
||||
|
||||
const int SERVER_NO_DATA_CODE = CURLE_GOT_NOTHING;
|
||||
COMPARE(tr->responseCode(), SERVER_NO_DATA_CODE);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), SERVER_NO_DATA_CODE);
|
||||
}
|
||||
|
||||
cout << "testing proxy close" << endl;
|
||||
@@ -555,9 +545,9 @@ cout << "testing proxy close" << endl;
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseLength(), body2Size);
|
||||
COMPARE(tr->bodyData, string(body2, body2Size));
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->responseLength(), body2Size);
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(body2, body2Size));
|
||||
}
|
||||
|
||||
{
|
||||
@@ -566,9 +556,9 @@ cout << "testing proxy close" << endl;
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseBytesReceived(), body2Size);
|
||||
COMPARE(tr->bodyData, string(body2, body2Size));
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->responseBytesReceived(), body2Size);
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(body2, body2Size));
|
||||
}
|
||||
|
||||
// pipelining
|
||||
@@ -593,17 +583,17 @@ cout << "testing proxy close" << endl;
|
||||
cl.makeRequest(tr3);
|
||||
|
||||
waitForComplete(&cl, tr3);
|
||||
VERIFY(tr->complete);
|
||||
VERIFY(tr2->complete);
|
||||
COMPARE(tr->bodyData, string(BODY1));
|
||||
SG_VERIFY(tr->complete);
|
||||
SG_VERIFY(tr2->complete);
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(BODY1));
|
||||
|
||||
COMPARE(tr2->responseLength(), strlen(BODY3));
|
||||
COMPARE(tr2->responseBytesReceived(), strlen(BODY3));
|
||||
COMPARE(tr2->bodyData, string(BODY3));
|
||||
SG_CHECK_EQUAL(tr2->responseLength(), strlen(BODY3));
|
||||
SG_CHECK_EQUAL(tr2->responseBytesReceived(), strlen(BODY3));
|
||||
SG_CHECK_EQUAL(tr2->bodyData, string(BODY3));
|
||||
|
||||
COMPARE(tr3->bodyData, string(BODY1));
|
||||
SG_CHECK_EQUAL(tr3->bodyData, string(BODY1));
|
||||
|
||||
COMPARE(testServer.connectCount(), 1);
|
||||
SG_CHECK_EQUAL(testServer.connectCount(), 1);
|
||||
}
|
||||
|
||||
// multiple requests with an HTTP 1.0 server
|
||||
@@ -624,17 +614,17 @@ cout << "testing proxy close" << endl;
|
||||
cl.makeRequest(tr3);
|
||||
|
||||
waitForComplete(&cl, tr3);
|
||||
VERIFY(tr->complete);
|
||||
VERIFY(tr2->complete);
|
||||
SG_VERIFY(tr->complete);
|
||||
SG_VERIFY(tr2->complete);
|
||||
|
||||
COMPARE(tr->responseLength(), strlen(BODY1));
|
||||
COMPARE(tr->responseBytesReceived(), strlen(BODY1));
|
||||
COMPARE(tr->bodyData, string(BODY1));
|
||||
SG_CHECK_EQUAL(tr->responseLength(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr->responseBytesReceived(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(BODY1));
|
||||
|
||||
COMPARE(tr2->responseLength(), strlen(BODY3));
|
||||
COMPARE(tr2->responseBytesReceived(), strlen(BODY3));
|
||||
COMPARE(tr2->bodyData, string(BODY3));
|
||||
COMPARE(tr3->bodyData, string(BODY1));
|
||||
SG_CHECK_EQUAL(tr2->responseLength(), strlen(BODY3));
|
||||
SG_CHECK_EQUAL(tr2->responseBytesReceived(), strlen(BODY3));
|
||||
SG_CHECK_EQUAL(tr2->bodyData, string(BODY3));
|
||||
SG_CHECK_EQUAL(tr3->bodyData, string(BODY1));
|
||||
}
|
||||
|
||||
// POST
|
||||
@@ -646,7 +636,7 @@ cout << "testing proxy close" << endl;
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 204);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 204);
|
||||
}
|
||||
|
||||
// PUT
|
||||
@@ -658,7 +648,7 @@ cout << "testing proxy close" << endl;
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 204);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 204);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -669,7 +659,7 @@ cout << "testing proxy close" << endl;
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 201);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 201);
|
||||
}
|
||||
|
||||
// test_zero_length_content
|
||||
@@ -679,9 +669,9 @@ cout << "testing proxy close" << endl;
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->bodyData, string());
|
||||
COMPARE(tr->responseBytesReceived(), 0);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->bodyData, string());
|
||||
SG_CHECK_EQUAL(tr->responseBytesReceived(), 0);
|
||||
}
|
||||
|
||||
// test cancel
|
||||
@@ -709,12 +699,12 @@ cout << "testing proxy close" << endl;
|
||||
|
||||
waitForComplete(&cl, tr3);
|
||||
|
||||
COMPARE(tr->responseCode(), -1);
|
||||
COMPARE(tr2->responseReason(), "my reason 2");
|
||||
SG_CHECK_EQUAL(tr->responseCode(), -1);
|
||||
SG_CHECK_EQUAL(tr2->responseReason(), "my reason 2");
|
||||
|
||||
COMPARE(tr3->responseLength(), strlen(BODY1));
|
||||
COMPARE(tr3->responseBytesReceived(), strlen(BODY1));
|
||||
COMPARE(tr3->bodyData, string(BODY1));
|
||||
SG_CHECK_EQUAL(tr3->responseLength(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr3->responseBytesReceived(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr3->bodyData, string(BODY1));
|
||||
}
|
||||
|
||||
// test cancel
|
||||
@@ -740,16 +730,16 @@ cout << "testing proxy close" << endl;
|
||||
|
||||
waitForComplete(&cl, tr3);
|
||||
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseLength(), strlen(BODY1));
|
||||
COMPARE(tr->responseBytesReceived(), strlen(BODY1));
|
||||
COMPARE(tr->bodyData, string(BODY1));
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->responseLength(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr->responseBytesReceived(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(BODY1));
|
||||
|
||||
COMPARE(tr2->responseCode(), -1);
|
||||
SG_CHECK_EQUAL(tr2->responseCode(), -1);
|
||||
|
||||
COMPARE(tr3->responseLength(), strlen(BODY1));
|
||||
COMPARE(tr3->responseBytesReceived(), strlen(BODY1));
|
||||
COMPARE(tr3->bodyData, string(BODY1));
|
||||
SG_CHECK_EQUAL(tr3->responseLength(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr3->responseBytesReceived(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr3->bodyData, string(BODY1));
|
||||
}
|
||||
|
||||
{
|
||||
@@ -774,12 +764,12 @@ cout << "testing proxy close" << endl;
|
||||
cl.makeRequest(tr2);
|
||||
|
||||
waitForComplete(&cl, tr2);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->bodyData, string(BODY3));
|
||||
COMPARE(tr->responseBytesReceived(), strlen(BODY3));
|
||||
COMPARE(tr2->responseCode(), 200);
|
||||
COMPARE(tr2->bodyData, string(BODY1));
|
||||
COMPARE(tr2->responseBytesReceived(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(BODY3));
|
||||
SG_CHECK_EQUAL(tr->responseBytesReceived(), strlen(BODY3));
|
||||
SG_CHECK_EQUAL(tr2->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr2->bodyData, string(BODY1));
|
||||
SG_CHECK_EQUAL(tr2->responseBytesReceived(), strlen(BODY1));
|
||||
}
|
||||
|
||||
cout << "all tests passed ok" << endl;
|
||||
|
||||
@@ -101,7 +101,7 @@ public:
|
||||
};
|
||||
|
||||
size_t bytesRemaining;
|
||||
std::auto_ptr<SGFile> currentFile;
|
||||
std::unique_ptr<SGFile> currentFile;
|
||||
size_t currentFileSize;
|
||||
z_stream zlibStream;
|
||||
uint8_t* zlibOutput;
|
||||
|
||||
@@ -44,7 +44,7 @@ public:
|
||||
bool hasError() const;
|
||||
|
||||
private:
|
||||
std::auto_ptr<TarExtractorPrivate> d;
|
||||
std::unique_ptr<TarExtractorPrivate> d;
|
||||
};
|
||||
|
||||
} // of namespace simgear
|
||||
|
||||
@@ -32,6 +32,8 @@ set(HEADERS
|
||||
sg_geodesy.hxx
|
||||
sg_types.hxx
|
||||
sg_random.h
|
||||
simd.hxx
|
||||
simd4x4.hxx
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
@@ -45,6 +47,10 @@ simgear_component(math math "${SOURCES}" "${HEADERS}")
|
||||
|
||||
if(ENABLE_TESTS)
|
||||
|
||||
add_executable(sgvec4_test test_sgvec4.cxx)
|
||||
target_link_libraries(sgvec4_test ${TEST_LIBS})
|
||||
add_test(sgvec4 ${EXECUTABLE_OUTPUT_PATH}/sgvec4_test)
|
||||
|
||||
add_executable(math_test SGMathTest.cxx)
|
||||
target_link_libraries(math_test ${TEST_LIBS})
|
||||
add_test(math ${EXECUTABLE_OUTPUT_PATH}/math_test)
|
||||
|
||||
@@ -28,6 +28,9 @@
|
||||
#include "SGRect.hxx"
|
||||
#include "sg_random.h"
|
||||
|
||||
int lineno = 0;
|
||||
|
||||
|
||||
template<typename T>
|
||||
bool
|
||||
Vec3Test(void)
|
||||
@@ -38,45 +41,45 @@ Vec3Test(void)
|
||||
v1 = SGVec3<T>(1, 2, 3);
|
||||
v2 = SGVec3<T>(3, 2, 1);
|
||||
if (equivalent(v1, v2))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
|
||||
// Check the unary minus operator
|
||||
v3 = SGVec3<T>(-1, -2, -3);
|
||||
if (!equivalent(-v1, v3))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
|
||||
// Check the unary plus operator
|
||||
v3 = SGVec3<T>(1, 2, 3);
|
||||
if (!equivalent(+v1, v3))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
|
||||
// Check the addition operator
|
||||
v3 = SGVec3<T>(4, 4, 4);
|
||||
if (!equivalent(v1 + v2, v3))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
|
||||
// Check the subtraction operator
|
||||
v3 = SGVec3<T>(-2, 0, 2);
|
||||
if (!equivalent(v1 - v2, v3))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
|
||||
// Check the scaler multiplication operator
|
||||
v3 = SGVec3<T>(2, 4, 6);
|
||||
if (!equivalent(2*v1, v3))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
|
||||
// Check the dot product
|
||||
if (fabs(dot(v1, v2) - 10) > 10*SGLimits<T>::epsilon())
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
|
||||
// Check the cross product
|
||||
v3 = SGVec3<T>(-4, 8, -4);
|
||||
if (!equivalent(cross(v1, v2), v3))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
|
||||
// Check the euclidean length
|
||||
if (fabs(14 - length(v1)*length(v1)) > 14*SGLimits<T>::epsilon())
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -89,11 +92,11 @@ isSameRotation(const SGQuat<T>& q1, const SGQuat<T>& q2)
|
||||
const SGVec3<T> e2(0, 1, 0);
|
||||
const SGVec3<T> e3(0, 0, 1);
|
||||
if (!equivalent(q1.transform(e1), q2.transform(e1)))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
if (!equivalent(q1.transform(e2), q2.transform(e2)))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
if (!equivalent(q1.transform(e3), q2.transform(e3)))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -111,37 +114,37 @@ QuatTest(void)
|
||||
v1 = SGVec3<T>(1, 2, 3);
|
||||
v2 = SGVec3<T>(1, -2, -3);
|
||||
if (!equivalent(q1.transform(v1), v2))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
|
||||
// Check a rotation around the x axis
|
||||
q1 = SGQuat<T>::fromAngleAxis(0.5*SGMisc<T>::pi(), e1);
|
||||
v2 = SGVec3<T>(1, 3, -2);
|
||||
if (!equivalent(q1.transform(v1), v2))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
|
||||
// Check a rotation around the y axis
|
||||
q1 = SGQuat<T>::fromAngleAxis(SGMisc<T>::pi(), e2);
|
||||
v2 = SGVec3<T>(-1, 2, -3);
|
||||
if (!equivalent(q1.transform(v1), v2))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
|
||||
// Check a rotation around the y axis
|
||||
q1 = SGQuat<T>::fromAngleAxis(0.5*SGMisc<T>::pi(), e2);
|
||||
v2 = SGVec3<T>(-3, 2, 1);
|
||||
if (!equivalent(q1.transform(v1), v2))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
|
||||
// Check a rotation around the z axis
|
||||
q1 = SGQuat<T>::fromAngleAxis(SGMisc<T>::pi(), e3);
|
||||
v2 = SGVec3<T>(-1, -2, 3);
|
||||
if (!equivalent(q1.transform(v1), v2))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
|
||||
// Check a rotation around the z axis
|
||||
q1 = SGQuat<T>::fromAngleAxis(0.5*SGMisc<T>::pi(), e3);
|
||||
v2 = SGVec3<T>(2, -1, 3);
|
||||
if (!equivalent(q1.transform(v1), v2))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
|
||||
// Now check some successive transforms
|
||||
// We can reuse the prevously tested stuff
|
||||
@@ -150,7 +153,7 @@ QuatTest(void)
|
||||
q3 = q1*q2;
|
||||
v2 = q2.transform(q1.transform(v1));
|
||||
if (!equivalent(q3.transform(v1), v2))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
|
||||
/// Test from Euler angles
|
||||
float x = 0.2*SGMisc<T>::pi();
|
||||
@@ -162,7 +165,7 @@ QuatTest(void)
|
||||
v2 = q3.transform(q2.transform(q1.transform(v1)));
|
||||
q4 = SGQuat<T>::fromEulerRad(z, y, x);
|
||||
if (!equivalent(q4.transform(v1), v2))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
|
||||
/// Test angle axis forward and back transform
|
||||
q1 = SGQuat<T>::fromAngleAxis(0.2*SGMisc<T>::pi(), e1);
|
||||
@@ -172,15 +175,15 @@ QuatTest(void)
|
||||
q1.getAngleAxis(angleAxis);
|
||||
q4 = SGQuat<T>::fromAngleAxis(angleAxis);
|
||||
if (!isSameRotation(q1, q4))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
q2.getAngleAxis(angleAxis);
|
||||
q4 = SGQuat<T>::fromAngleAxis(angleAxis);
|
||||
if (!isSameRotation(q2, q4))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
q3.getAngleAxis(angleAxis);
|
||||
q4 = SGQuat<T>::fromAngleAxis(angleAxis);
|
||||
if (!isSameRotation(q3, q4))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
|
||||
/// Test angle axis forward and back transform
|
||||
q1 = SGQuat<T>::fromAngleAxis(0.2*SGMisc<T>::pi(), e1);
|
||||
@@ -189,15 +192,15 @@ QuatTest(void)
|
||||
SGVec3<T> positiveAngleAxis = q1.getPositiveRealImag();
|
||||
q4 = SGQuat<T>::fromPositiveRealImag(positiveAngleAxis);
|
||||
if (!isSameRotation(q1, q4))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
positiveAngleAxis = q2.getPositiveRealImag();
|
||||
q4 = SGQuat<T>::fromPositiveRealImag(positiveAngleAxis);
|
||||
if (!isSameRotation(q2, q4))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
positiveAngleAxis = q3.getPositiveRealImag();
|
||||
q4 = SGQuat<T>::fromPositiveRealImag(positiveAngleAxis);
|
||||
if (!isSameRotation(q3, q4))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -219,13 +222,13 @@ QuatDerivativeTest(void)
|
||||
// Check if we can restore the angular velocity
|
||||
SGVec3<T> av2 = SGQuat<T>::forwardDifferenceVelocity(o0, o1, dt);
|
||||
if (!equivalent(av, av2))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
|
||||
// Test with the equivalent orientation
|
||||
o1 = -o1;
|
||||
av2 = SGQuat<T>::forwardDifferenceVelocity(o0, o1, dt);
|
||||
if (!equivalent(av, av2))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -250,23 +253,23 @@ MatrixTest(void)
|
||||
invert(m2, m0);
|
||||
m3 = transNeg(m0);
|
||||
if (!equivalent(m1, m2))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
if (!equivalent(m2, m3))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
|
||||
// Check matrix multiplication and inversion
|
||||
if (!equivalent(m0*m1, SGMatrix<T>::unit()))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
if (!equivalent(m1*m0, SGMatrix<T>::unit()))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
if (!equivalent(m0*m2, SGMatrix<T>::unit()))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
if (!equivalent(m2*m0, SGMatrix<T>::unit()))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
if (!equivalent(m0*m3, SGMatrix<T>::unit()))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
if (!equivalent(m3*m0, SGMatrix<T>::unit()))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -276,26 +279,26 @@ void doRectTest()
|
||||
{
|
||||
SGRect<T> rect(10, 15, 20, 25);
|
||||
|
||||
COMPARE(rect.x(), 10)
|
||||
COMPARE(rect.y(), 15)
|
||||
COMPARE(rect.width(), 20)
|
||||
COMPARE(rect.height(), 25)
|
||||
SG_CHECK_EQUAL(rect.x(), 10)
|
||||
SG_CHECK_EQUAL(rect.y(), 15)
|
||||
SG_CHECK_EQUAL(rect.width(), 20)
|
||||
SG_CHECK_EQUAL(rect.height(), 25)
|
||||
|
||||
COMPARE(rect.pos(), SGVec2<T>(10, 15))
|
||||
COMPARE(rect.size(), SGVec2<T>(20, 25))
|
||||
SG_CHECK_EQUAL(rect.pos(), SGVec2<T>(10, 15))
|
||||
SG_CHECK_EQUAL(rect.size(), SGVec2<T>(20, 25))
|
||||
|
||||
COMPARE(rect.l(), 10)
|
||||
COMPARE(rect.t(), 15)
|
||||
COMPARE(rect.r(), 30)
|
||||
COMPARE(rect.b(), 40)
|
||||
SG_CHECK_EQUAL(rect.l(), 10)
|
||||
SG_CHECK_EQUAL(rect.t(), 15)
|
||||
SG_CHECK_EQUAL(rect.r(), 30)
|
||||
SG_CHECK_EQUAL(rect.b(), 40)
|
||||
|
||||
VERIFY(rect == rect)
|
||||
VERIFY(rect == SGRect<T>(10, 15, 20, 25))
|
||||
VERIFY(rect != SGRect<T>(11, 15, 20, 25))
|
||||
SG_VERIFY(rect == rect)
|
||||
SG_VERIFY(rect == SGRect<T>(10, 15, 20, 25))
|
||||
SG_VERIFY(rect != SGRect<T>(11, 15, 20, 25))
|
||||
|
||||
VERIFY(rect.contains(10, 15))
|
||||
VERIFY(!rect.contains(9, 15))
|
||||
VERIFY(rect.contains(9, 15, 1))
|
||||
SG_VERIFY(rect.contains(10, 15))
|
||||
SG_VERIFY(!rect.contains(9, 15))
|
||||
SG_VERIFY(rect.contains(9, 15, 1))
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -320,13 +323,13 @@ GeodesyTest(void)
|
||||
if (epsDeg < fabs(geod0.getLongitudeDeg() - geod1.getLongitudeDeg()) ||
|
||||
epsDeg < fabs(geod0.getLatitudeDeg() - geod1.getLatitudeDeg()) ||
|
||||
epsM < fabs(geod0.getElevationM() - geod1.getElevationM()))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
|
||||
// Test the conversion routines to radial coordinates
|
||||
geoc0 = SGGeoc::fromCart(cart0);
|
||||
cart1 = SGVec3<double>::fromGeoc(geoc0);
|
||||
if (!equivalent(cart0, cart1))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
|
||||
// test course / advance routines
|
||||
// uses examples from Williams aviation formulary
|
||||
@@ -336,12 +339,12 @@ GeodesyTest(void)
|
||||
double distNm = SGGeodesy::distanceRad(lax, jfk) * SG_RAD_TO_NM;
|
||||
std::cout << "distance is " << distNm << std::endl;
|
||||
if (0.5 < fabs(distNm - 2144)) // 2144 nm
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
|
||||
double crsDeg = SGGeodesy::courseRad(lax, jfk) * SG_RADIANS_TO_DEGREES;
|
||||
std::cout << "course is " << crsDeg << std::endl;
|
||||
if (0.5 < fabs(crsDeg - 66)) // 66 degrees
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
|
||||
SGGeoc adv;
|
||||
SGGeodesy::advanceRadM(lax, crsDeg * SG_DEGREES_TO_RADIANS, 100 * SG_NM_TO_METER, adv);
|
||||
@@ -349,7 +352,7 @@ GeodesyTest(void)
|
||||
|
||||
if (0.01 < fabs(adv.getLongitudeRad() - (-2.034206)) ||
|
||||
0.01 < fabs(adv.getLatitudeRad() - 0.604180))
|
||||
return false;
|
||||
{ lineno = __LINE__; return false; }
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -361,25 +364,25 @@ main(void)
|
||||
|
||||
// Do vector tests
|
||||
if (!Vec3Test<float>())
|
||||
return EXIT_FAILURE;
|
||||
{ fprintf(stderr, "Error at line: %i called from line: %i\n", lineno, __LINE__); return EXIT_FAILURE; }
|
||||
if (!Vec3Test<double>())
|
||||
return EXIT_FAILURE;
|
||||
{ fprintf(stderr, "Error at line: %i called from line: %i\n", lineno, __LINE__); return EXIT_FAILURE; }
|
||||
|
||||
// Do quaternion tests
|
||||
if (!QuatTest<float>())
|
||||
return EXIT_FAILURE;
|
||||
{ fprintf(stderr, "Error at line: %i called from line: %i\n", lineno, __LINE__); return EXIT_FAILURE; }
|
||||
if (!QuatTest<double>())
|
||||
return EXIT_FAILURE;
|
||||
{ fprintf(stderr, "Error at line: %i called from line: %i\n", lineno, __LINE__); return EXIT_FAILURE; }
|
||||
if (!QuatDerivativeTest<float>())
|
||||
return EXIT_FAILURE;
|
||||
{ fprintf(stderr, "Error at line: %i called from line: %i\n", lineno, __LINE__); return EXIT_FAILURE; }
|
||||
if (!QuatDerivativeTest<double>())
|
||||
return EXIT_FAILURE;
|
||||
{ fprintf(stderr, "Error at line: %i called from line: %i\n", lineno, __LINE__); return EXIT_FAILURE; }
|
||||
|
||||
// Do matrix tests
|
||||
if (!MatrixTest<float>())
|
||||
return EXIT_FAILURE;
|
||||
{ fprintf(stderr, "Error at line: %i called from line: %i\n", lineno, __LINE__); return EXIT_FAILURE; }
|
||||
if (!MatrixTest<double>())
|
||||
return EXIT_FAILURE;
|
||||
{ fprintf(stderr, "Error at line: %i called from line: %i\n", lineno, __LINE__); return EXIT_FAILURE; }
|
||||
|
||||
// Do rect tests
|
||||
doRectTest<int>();
|
||||
@@ -387,7 +390,7 @@ main(void)
|
||||
|
||||
// Check geodetic/geocentric/cartesian conversions
|
||||
if (!GeodesyTest())
|
||||
return EXIT_FAILURE;
|
||||
{ fprintf(stderr, "Error at line: %i called from line: %i\n", lineno, __LINE__); return EXIT_FAILURE; }
|
||||
|
||||
std::cout << "Successfully passed all tests!" << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
#ifndef SGMatrix_H
|
||||
#define SGMatrix_H
|
||||
|
||||
#include <simgear/math/simd4x4.hxx>
|
||||
|
||||
/// Expression templates for poor programmers ... :)
|
||||
template<typename T>
|
||||
struct TransNegRef;
|
||||
@@ -38,13 +40,13 @@ public:
|
||||
/// uninitialized values in the debug build very fast ...
|
||||
#ifndef NDEBUG
|
||||
for (unsigned i = 0; i < nEnts; ++i)
|
||||
_data.flat[i] = SGLimits<T>::quiet_NaN();
|
||||
_data[i] = SGLimits<T>::quiet_NaN();
|
||||
#endif
|
||||
}
|
||||
/// Constructor. Initialize by the content of a plain array,
|
||||
/// make sure it has at least 16 elements
|
||||
explicit SGMatrix(const T* data)
|
||||
{ for (unsigned i = 0; i < nEnts; ++i) _data.flat[i] = data[i]; }
|
||||
{ _data = simd4x4_t<T,4>(data); }
|
||||
|
||||
/// Constructor, build up a SGMatrix from given elements
|
||||
SGMatrix(T m00, T m01, T m02, T m03,
|
||||
@@ -52,14 +54,8 @@ public:
|
||||
T m20, T m21, T m22, T m23,
|
||||
T m30, T m31, T m32, T m33)
|
||||
{
|
||||
_data.flat[0] = m00; _data.flat[1] = m10;
|
||||
_data.flat[2] = m20; _data.flat[3] = m30;
|
||||
_data.flat[4] = m01; _data.flat[5] = m11;
|
||||
_data.flat[6] = m21; _data.flat[7] = m31;
|
||||
_data.flat[8] = m02; _data.flat[9] = m12;
|
||||
_data.flat[10] = m22; _data.flat[11] = m32;
|
||||
_data.flat[12] = m03; _data.flat[13] = m13;
|
||||
_data.flat[14] = m23; _data.flat[15] = m33;
|
||||
_data = simd4x4_t<T,4>(m00,m01,m02,m03,m10,m11,m12,m13,
|
||||
m20,m21,m22,m23,m30,m31,m32,m33);
|
||||
}
|
||||
|
||||
/// Constructor, build up a SGMatrix from a translation
|
||||
@@ -80,14 +76,8 @@ public:
|
||||
template<typename S>
|
||||
void set(const SGVec3<S>& trans)
|
||||
{
|
||||
_data.flat[0] = 1; _data.flat[4] = 0;
|
||||
_data.flat[8] = 0; _data.flat[12] = T(trans(0));
|
||||
_data.flat[1] = 0; _data.flat[5] = 1;
|
||||
_data.flat[9] = 0; _data.flat[13] = T(trans(1));
|
||||
_data.flat[2] = 0; _data.flat[6] = 0;
|
||||
_data.flat[10] = 1; _data.flat[14] = T(trans(2));
|
||||
_data.flat[3] = 0; _data.flat[7] = 0;
|
||||
_data.flat[11] = 0; _data.flat[15] = 1;
|
||||
simd4x4::unit(_data);
|
||||
simd4x4::translate(_data, trans.simd3());
|
||||
}
|
||||
|
||||
/// Set from a scale/rotation and tranlation
|
||||
@@ -98,82 +88,77 @@ public:
|
||||
T xx = x*x; T yy = y*y; T zz = z*z;
|
||||
T wx = w*x; T wy = w*y; T wz = w*z;
|
||||
T xy = x*y; T xz = x*z; T yz = y*z;
|
||||
_data.flat[0] = 1-2*(yy+zz); _data.flat[1] = 2*(xy-wz);
|
||||
_data.flat[2] = 2*(xz+wy); _data.flat[3] = 0;
|
||||
_data.flat[4] = 2*(xy+wz); _data.flat[5] = 1-2*(xx+zz);
|
||||
_data.flat[6] = 2*(yz-wx); _data.flat[7] = 0;
|
||||
_data.flat[8] = 2*(xz-wy); _data.flat[9] = 2*(yz+wx);
|
||||
_data.flat[10] = 1-2*(xx+yy); _data.flat[11] = 0;
|
||||
_data.flat[12] = 0; _data.flat[13] = 0;
|
||||
_data.flat[14] = 0; _data.flat[15] = 1;
|
||||
_data[0] = 1-2*(yy+zz); _data[1] = 2*(xy-wz);
|
||||
_data[2] = 2*(xz+wy); _data[3] = 0;
|
||||
_data[4] = 2*(xy+wz); _data[5] = 1-2*(xx+zz);
|
||||
_data[6] = 2*(yz-wx); _data[7] = 0;
|
||||
_data[8] = 2*(xz-wy); _data[9] = 2*(yz+wx);
|
||||
_data[10] = 1-2*(xx+yy); _data[11] = 0;
|
||||
_data[12] = 0; _data[13] = 0;
|
||||
_data[14] = 0; _data[15] = 1;
|
||||
}
|
||||
|
||||
/// set from a transposed negated matrix
|
||||
void set(const TransNegRef<T>& tm)
|
||||
{
|
||||
const SGMatrix& m = tm.m;
|
||||
_data.flat[0] = m(0,0);
|
||||
_data.flat[1] = m(0,1);
|
||||
_data.flat[2] = m(0,2);
|
||||
_data.flat[3] = m(3,0);
|
||||
|
||||
_data.flat[4] = m(1,0);
|
||||
_data.flat[5] = m(1,1);
|
||||
_data.flat[6] = m(1,2);
|
||||
_data.flat[7] = m(3,1);
|
||||
|
||||
_data.flat[8] = m(2,0);
|
||||
_data.flat[9] = m(2,1);
|
||||
_data.flat[10] = m(2,2);
|
||||
_data.flat[11] = m(3,2);
|
||||
_data = simd4x4::transpose(m.simd4x4());
|
||||
_data[3] = m(3,0);
|
||||
_data[7] = m(3,1);
|
||||
_data[11] = m(3,2);
|
||||
|
||||
// Well, this one is ugly here, as that xform method on the current
|
||||
// object needs the above data to be already set ...
|
||||
SGVec3<T> t = xformVec(SGVec3<T>(m(0,3), m(1,3), m(2,3)));
|
||||
_data.flat[12] = -t(0);
|
||||
_data.flat[13] = -t(1);
|
||||
_data.flat[14] = -t(2);
|
||||
_data.flat[15] = m(3,3);
|
||||
_data.set(3, -t.simd3());
|
||||
_data[15] = m(3,3);
|
||||
}
|
||||
|
||||
/// Access by index, the index is unchecked
|
||||
const T& operator()(unsigned i, unsigned j) const
|
||||
{ return _data.flat[i + 4*j]; }
|
||||
{ return _data[i + 4*j]; }
|
||||
/// Access by index, the index is unchecked
|
||||
T& operator()(unsigned i, unsigned j)
|
||||
{ return _data.flat[i + 4*j]; }
|
||||
{ return _data[i + 4*j]; }
|
||||
|
||||
/// Access raw data by index, the index is unchecked
|
||||
const T& operator[](unsigned i) const
|
||||
{ return _data.flat[i]; }
|
||||
{ return _data[i]; }
|
||||
/// Access by index, the index is unchecked
|
||||
T& operator[](unsigned i)
|
||||
{ return _data.flat[i]; }
|
||||
{ return _data[i]; }
|
||||
|
||||
/// Get the data pointer
|
||||
const T* data(void) const
|
||||
{ return _data.flat; }
|
||||
{ return _data; }
|
||||
/// Get the data pointer
|
||||
T* data(void)
|
||||
{ return _data.flat; }
|
||||
{ return _data; }
|
||||
|
||||
/// Readonly interface function to ssg's sgMat4/sgdMat4
|
||||
const T (&sg(void) const)[4][4]
|
||||
{ return _data.carray; }
|
||||
{ return _data.ptr(); }
|
||||
/// Interface function to ssg's sgMat4/sgdMat4
|
||||
T (&sg(void))[4][4]
|
||||
{ return _data.carray; }
|
||||
{ return _data.ptr(); }
|
||||
/// Readonly raw storage interface
|
||||
const simd4x4_t<T,4> (&simd4x4(void) const)
|
||||
{ return _data; }
|
||||
/// Readonly raw storage interface
|
||||
simd4x4_t<T,4> (&simd4x4(void))
|
||||
{ return _data; }
|
||||
|
||||
|
||||
/// Inplace addition
|
||||
SGMatrix& operator+=(const SGMatrix& m)
|
||||
{ for (unsigned i = 0; i < nEnts; ++i) _data.flat[i] += m._data.flat[i]; return *this; }
|
||||
{ _data += m.simd4x4(); return *this; }
|
||||
/// Inplace subtraction
|
||||
SGMatrix& operator-=(const SGMatrix& m)
|
||||
{ for (unsigned i = 0; i < nEnts; ++i) _data.flat[i] -= m._data.flat[i]; return *this; }
|
||||
{ _data -= m.simd4x4(); return *this; }
|
||||
/// Inplace scalar multiplication
|
||||
template<typename S>
|
||||
SGMatrix& operator*=(S s)
|
||||
{ for (unsigned i = 0; i < nEnts; ++i) _data.flat[i] *= s; return *this; }
|
||||
{ _data *= s; return *this; }
|
||||
/// Inplace scalar multiplication by 1/s
|
||||
template<typename S>
|
||||
SGMatrix& operator/=(S s)
|
||||
@@ -184,27 +169,13 @@ public:
|
||||
template<typename S>
|
||||
SGMatrix& preMultTranslate(const SGVec3<S>& t)
|
||||
{
|
||||
for (unsigned i = 0; i < 3; ++i) {
|
||||
T tmp = T(t(i));
|
||||
if (tmp == 0)
|
||||
continue;
|
||||
(*this)(i,0) += tmp*(*this)(3,0);
|
||||
(*this)(i,1) += tmp*(*this)(3,1);
|
||||
(*this)(i,2) += tmp*(*this)(3,2);
|
||||
(*this)(i,3) += tmp*(*this)(3,3);
|
||||
}
|
||||
simd4x4::pre_translate(_data,t.simd3());
|
||||
return *this;
|
||||
}
|
||||
template<typename S>
|
||||
SGMatrix& postMultTranslate(const SGVec3<S>& t)
|
||||
{
|
||||
SGVec4<T> col3((*this)(0,3), (*this)(1,3), (*this)(2,3), (*this)(3,3));
|
||||
for (unsigned i = 0; i < SGMatrix<T>::nCols-1; ++i) {
|
||||
SGVec4<T> tmp((*this)(0,i), (*this)(1,i), (*this)(2,i), (*this)(3,i));
|
||||
col3 += T(t(i))*tmp;
|
||||
}
|
||||
(*this)(0,3) = col3(0); (*this)(1,3) = col3(1);
|
||||
(*this)(2,3) = col3(2); (*this)(3,3) = col3(3);
|
||||
simd4x4::post_translate(_data,t.simd3());
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -230,51 +201,27 @@ public:
|
||||
SGVec3<T> xformPt(const SGVec3<T>& pt) const
|
||||
{
|
||||
SGVec3<T> tpt;
|
||||
tpt(0) = (*this)(0,3);
|
||||
tpt(1) = (*this)(1,3);
|
||||
tpt(2) = (*this)(2,3);
|
||||
for (unsigned i = 0; i < SGMatrix<T>::nCols-1; ++i) {
|
||||
T tmp = pt(i);
|
||||
tpt(0) += tmp*(*this)(0,i);
|
||||
tpt(1) += tmp*(*this)(1,i);
|
||||
tpt(2) += tmp*(*this)(2,i);
|
||||
}
|
||||
tpt.simd3() = simd4x4::transform(_data,pt.simd3());
|
||||
return tpt;
|
||||
}
|
||||
SGVec3<T> xformVec(const SGVec3<T>& v) const
|
||||
{
|
||||
SGVec3<T> tv;
|
||||
T tmp = v(0);
|
||||
tv(0) = tmp*(*this)(0,0);
|
||||
tv(1) = tmp*(*this)(1,0);
|
||||
tv(2) = tmp*(*this)(2,0);
|
||||
for (unsigned i = 1; i < SGMatrix<T>::nCols-1; ++i) {
|
||||
T tmp = v(i);
|
||||
tv(0) += tmp*(*this)(0,i);
|
||||
tv(1) += tmp*(*this)(1,i);
|
||||
tv(2) += tmp*(*this)(2,i);
|
||||
}
|
||||
tv.simd3() = _data * v.simd3();
|
||||
return tv;
|
||||
}
|
||||
|
||||
/// Return an all zero matrix
|
||||
static SGMatrix zeros(void)
|
||||
{ return SGMatrix(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); }
|
||||
{ SGMatrix r; simd4x4::zeros(r.simd4x4()); return r; }
|
||||
|
||||
/// Return a unit matrix
|
||||
static SGMatrix unit(void)
|
||||
{ return SGMatrix(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); }
|
||||
{ SGMatrix r; simd4x4::unit(r.simd4x4()); return r; }
|
||||
|
||||
private:
|
||||
/// Required to make that alias safe.
|
||||
union Data {
|
||||
T flat[16];
|
||||
T carray[4][4];
|
||||
};
|
||||
|
||||
/// The actual data, the matrix is stored in column major order,
|
||||
/// that matches the storage format of OpenGL
|
||||
Data _data;
|
||||
simd4x4_t<T,4> _data;
|
||||
};
|
||||
|
||||
/// Class to distinguish between a matrix and the matrix with a transposed
|
||||
@@ -296,61 +243,45 @@ operator+(const SGMatrix<T>& m)
|
||||
template<typename T>
|
||||
inline
|
||||
SGMatrix<T>
|
||||
operator-(const SGMatrix<T>& m)
|
||||
operator-(SGMatrix<T> m)
|
||||
{
|
||||
SGMatrix<T> ret;
|
||||
for (unsigned i = 0; i < SGMatrix<T>::nEnts; ++i)
|
||||
ret[i] = -m[i];
|
||||
return ret;
|
||||
m.simd4x4() = -m.simd4x4();
|
||||
return m;
|
||||
}
|
||||
|
||||
/// Binary +
|
||||
template<typename T>
|
||||
inline
|
||||
SGMatrix<T>
|
||||
operator+(const SGMatrix<T>& m1, const SGMatrix<T>& m2)
|
||||
operator+(SGMatrix<T> m1, const SGMatrix<T>& m2)
|
||||
{
|
||||
SGMatrix<T> ret;
|
||||
for (unsigned i = 0; i < SGMatrix<T>::nEnts; ++i)
|
||||
ret[i] = m1[i] + m2[i];
|
||||
return ret;
|
||||
m1.simd4x4() += m2.simd4x4();
|
||||
return m1;
|
||||
}
|
||||
|
||||
/// Binary -
|
||||
template<typename T>
|
||||
inline
|
||||
SGMatrix<T>
|
||||
operator-(const SGMatrix<T>& m1, const SGMatrix<T>& m2)
|
||||
operator-(SGMatrix<T> m1, const SGMatrix<T>& m2)
|
||||
{
|
||||
SGMatrix<T> ret;
|
||||
for (unsigned i = 0; i < SGMatrix<T>::nEnts; ++i)
|
||||
ret[i] = m1[i] - m2[i];
|
||||
return ret;
|
||||
m1.simd4x4() -= m2.simd4x4();
|
||||
return m1;
|
||||
}
|
||||
|
||||
/// Scalar multiplication
|
||||
template<typename S, typename T>
|
||||
inline
|
||||
SGMatrix<T>
|
||||
operator*(S s, const SGMatrix<T>& m)
|
||||
{
|
||||
SGMatrix<T> ret;
|
||||
for (unsigned i = 0; i < SGMatrix<T>::nEnts; ++i)
|
||||
ret[i] = s*m[i];
|
||||
return ret;
|
||||
}
|
||||
operator*(S s, SGMatrix<T> m)
|
||||
{ m.simd4x4() *= s; return m; }
|
||||
|
||||
/// Scalar multiplication
|
||||
template<typename S, typename T>
|
||||
inline
|
||||
SGMatrix<T>
|
||||
operator*(const SGMatrix<T>& m, S s)
|
||||
{
|
||||
SGMatrix<T> ret;
|
||||
for (unsigned i = 0; i < SGMatrix<T>::nEnts; ++i)
|
||||
ret[i] = s*m[i];
|
||||
return ret;
|
||||
}
|
||||
operator*(SGMatrix<T> m, S s)
|
||||
{ m.simd4x4() *= s; return m; }
|
||||
|
||||
/// Vector multiplication
|
||||
template<typename T>
|
||||
@@ -359,18 +290,7 @@ SGVec4<T>
|
||||
operator*(const SGMatrix<T>& m, const SGVec4<T>& v)
|
||||
{
|
||||
SGVec4<T> mv;
|
||||
T tmp = v(0);
|
||||
mv(0) = tmp*m(0,0);
|
||||
mv(1) = tmp*m(1,0);
|
||||
mv(2) = tmp*m(2,0);
|
||||
mv(3) = tmp*m(3,0);
|
||||
for (unsigned i = 1; i < SGMatrix<T>::nCols; ++i) {
|
||||
T tmp = v(i);
|
||||
mv(0) += tmp*m(0,i);
|
||||
mv(1) += tmp*m(1,i);
|
||||
mv(2) += tmp*m(2,i);
|
||||
mv(3) += tmp*m(3,i);
|
||||
}
|
||||
mv.simd4() = m.simd4x4() * v.simd4();
|
||||
return mv;
|
||||
}
|
||||
|
||||
@@ -405,20 +325,7 @@ SGMatrix<T>
|
||||
operator*(const SGMatrix<T>& m1, const SGMatrix<T>& m2)
|
||||
{
|
||||
SGMatrix<T> m;
|
||||
for (unsigned j = 0; j < SGMatrix<T>::nCols; ++j) {
|
||||
T tmp = m2(0,j);
|
||||
m(0,j) = tmp*m1(0,0);
|
||||
m(1,j) = tmp*m1(1,0);
|
||||
m(2,j) = tmp*m1(2,0);
|
||||
m(3,j) = tmp*m1(3,0);
|
||||
for (unsigned i = 1; i < SGMatrix<T>::nCols; ++i) {
|
||||
T tmp = m2(i,j);
|
||||
m(0,j) += tmp*m1(0,i);
|
||||
m(1,j) += tmp*m1(1,i);
|
||||
m(2,j) += tmp*m1(2,i);
|
||||
m(3,j) += tmp*m1(3,i);
|
||||
}
|
||||
}
|
||||
m.simd4x4() = m1.simd4x4() * m2.simd4x4();
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
@@ -153,17 +153,7 @@ public:
|
||||
/// Use with care: allways code that you do not need to use that!
|
||||
static bool isNaN(const T& v)
|
||||
{
|
||||
#ifdef HAVE_STD_ISNAN
|
||||
return std::isnan(v);
|
||||
#elif defined HAVE_ISNAN
|
||||
return (isnan(v) != 0);
|
||||
#else
|
||||
// Use that every compare involving a NaN returns false
|
||||
// But be careful, some usual compiler switches like for example
|
||||
// -fast-math from gcc might optimize that expression to v != v which
|
||||
// behaves exactly like the opposite ...
|
||||
return !(v == v);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <simgear/math/SGLimits.hxx>
|
||||
#include <simgear/math/SGMisc.hxx>
|
||||
#include <simgear/math/SGMathFwd.hxx>
|
||||
#include <simgear/math/simd.hxx>
|
||||
|
||||
/// 2D Vector Class
|
||||
template<typename T>
|
||||
@@ -44,11 +45,11 @@ public:
|
||||
}
|
||||
/// Constructor. Initialize by the given values
|
||||
SGVec2(T x, T y)
|
||||
{ data()[0] = x; data()[1] = y; }
|
||||
{ _data = simd4_t<T,2>(x, y); }
|
||||
/// Constructor. Initialize by the content of a plain array,
|
||||
/// make sure it has at least 2 elements
|
||||
explicit SGVec2(const T* d)
|
||||
{ data()[0] = d[0]; data()[1] = d[1]; }
|
||||
{ _data = d ? simd4_t<T,2>(d) : simd4_t<T,2>(T(0)); }
|
||||
template<typename S>
|
||||
explicit SGVec2(const SGVec2<S>& d)
|
||||
{ data()[0] = d[0]; data()[1] = d[1]; }
|
||||
@@ -82,25 +83,30 @@ public:
|
||||
|
||||
/// Access raw data
|
||||
const T (&data(void) const)[2]
|
||||
{ return _data; }
|
||||
{ return _data.ptr(); }
|
||||
/// Access raw data
|
||||
T (&data(void))[2]
|
||||
{ return _data.ptr(); }
|
||||
const simd4_t<T,2> (&simd2(void) const)
|
||||
{ return _data; }
|
||||
/// Readonly raw storage interface
|
||||
simd4_t<T,2> (&simd2(void))
|
||||
{ return _data; }
|
||||
|
||||
/// Inplace addition
|
||||
SGVec2& operator+=(const SGVec2& v)
|
||||
{ data()[0] += v(0); data()[1] += v(1); return *this; }
|
||||
{ _data += v.simd2(); return *this; }
|
||||
/// Inplace subtraction
|
||||
SGVec2& operator-=(const SGVec2& v)
|
||||
{ data()[0] -= v(0); data()[1] -= v(1); return *this; }
|
||||
{ _data -= v.simd2(); return *this; }
|
||||
/// Inplace scalar multiplication
|
||||
template<typename S>
|
||||
SGVec2& operator*=(S s)
|
||||
{ data()[0] *= s; data()[1] *= s; return *this; }
|
||||
{ _data *= s; return *this; }
|
||||
/// Inplace scalar multiplication by 1/s
|
||||
template<typename S>
|
||||
SGVec2& operator/=(S s)
|
||||
{ return operator*=(1/T(s)); }
|
||||
{ _data*=(1/T(s)); return *this; }
|
||||
|
||||
/// Return an all zero vector
|
||||
static SGVec2 zeros(void)
|
||||
@@ -112,7 +118,7 @@ public:
|
||||
{ return SGVec2(0, 1); }
|
||||
|
||||
private:
|
||||
T _data[2];
|
||||
simd4_t<T,2> _data;
|
||||
};
|
||||
|
||||
/// Unary +, do nothing ...
|
||||
@@ -126,36 +132,36 @@ operator+(const SGVec2<T>& v)
|
||||
template<typename T>
|
||||
inline
|
||||
SGVec2<T>
|
||||
operator-(const SGVec2<T>& v)
|
||||
{ return SGVec2<T>(-v(0), -v(1)); }
|
||||
operator-(SGVec2<T> v)
|
||||
{ v *= -1; return v; }
|
||||
|
||||
/// Binary +
|
||||
template<typename T>
|
||||
inline
|
||||
SGVec2<T>
|
||||
operator+(const SGVec2<T>& v1, const SGVec2<T>& v2)
|
||||
{ return SGVec2<T>(v1(0)+v2(0), v1(1)+v2(1)); }
|
||||
operator+(SGVec2<T> v1, const SGVec2<T>& v2)
|
||||
{ v1.simd2() += v2.simd2(); return v1; }
|
||||
|
||||
/// Binary -
|
||||
template<typename T>
|
||||
inline
|
||||
SGVec2<T>
|
||||
operator-(const SGVec2<T>& v1, const SGVec2<T>& v2)
|
||||
{ return SGVec2<T>(v1(0)-v2(0), v1(1)-v2(1)); }
|
||||
operator-(SGVec2<T> v1, const SGVec2<T>& v2)
|
||||
{ v1.simd2() -= v2.simd2(); return v1; }
|
||||
|
||||
/// Scalar multiplication
|
||||
template<typename S, typename T>
|
||||
inline
|
||||
SGVec2<T>
|
||||
operator*(S s, const SGVec2<T>& v)
|
||||
{ return SGVec2<T>(s*v(0), s*v(1)); }
|
||||
operator*(S s, SGVec2<T> v)
|
||||
{ v.simd2() *= s; return v; }
|
||||
|
||||
/// Scalar multiplication
|
||||
template<typename S, typename T>
|
||||
inline
|
||||
SGVec2<T>
|
||||
operator*(const SGVec2<T>& v, S s)
|
||||
{ return SGVec2<T>(s*v(0), s*v(1)); }
|
||||
operator*(SGVec2<T> v, S s)
|
||||
{ v.simd2() *= s; return v; }
|
||||
|
||||
/// multiplication as a multiplicator, that is assume that the first vector
|
||||
/// represents a 2x2 diagonal matrix with the diagonal elements in the vector.
|
||||
@@ -163,42 +169,42 @@ operator*(const SGVec2<T>& v, S s)
|
||||
template<typename T>
|
||||
inline
|
||||
SGVec2<T>
|
||||
mult(const SGVec2<T>& v1, const SGVec2<T>& v2)
|
||||
{ return SGVec2<T>(v1(0)*v2(0), v1(1)*v2(1)); }
|
||||
mult(SGVec2<T> v1, const SGVec2<T>& v2)
|
||||
{ v1.simd2() *= v2.simd2(); return v1; }
|
||||
|
||||
/// component wise min
|
||||
template<typename T>
|
||||
inline
|
||||
SGVec2<T>
|
||||
min(const SGVec2<T>& v1, const SGVec2<T>& v2)
|
||||
{return SGVec2<T>(SGMisc<T>::min(v1(0), v2(0)), SGMisc<T>::min(v1(1), v2(1)));}
|
||||
min(SGVec2<T> v1, const SGVec2<T>& v2)
|
||||
{ v1.simd2() = simd4::min(v1.simd2(), v2.simd2()); return v1; }
|
||||
template<typename S, typename T>
|
||||
inline
|
||||
SGVec2<T>
|
||||
min(const SGVec2<T>& v, S s)
|
||||
{ return SGVec2<T>(SGMisc<T>::min(s, v(0)), SGMisc<T>::min(s, v(1))); }
|
||||
min(SGVec2<T> v, S s)
|
||||
{ v.simd2() = simd4::min(v.simd2(), simd4_t<T,2>(s)); return v; }
|
||||
template<typename S, typename T>
|
||||
inline
|
||||
SGVec2<T>
|
||||
min(S s, const SGVec2<T>& v)
|
||||
{ return SGVec2<T>(SGMisc<T>::min(s, v(0)), SGMisc<T>::min(s, v(1))); }
|
||||
min(S s, SGVec2<T> v)
|
||||
{ v.sim2() = simd4::min(v.simd2(), simd4_t<T,2>(s)); return v; }
|
||||
|
||||
/// component wise max
|
||||
template<typename T>
|
||||
inline
|
||||
SGVec2<T>
|
||||
max(const SGVec2<T>& v1, const SGVec2<T>& v2)
|
||||
{return SGVec2<T>(SGMisc<T>::max(v1(0), v2(0)), SGMisc<T>::max(v1(1), v2(1)));}
|
||||
{ v1 = simd4::max(v1.simd2(), v2.simd2()); return v1; }
|
||||
template<typename S, typename T>
|
||||
inline
|
||||
SGVec2<T>
|
||||
max(const SGVec2<T>& v, S s)
|
||||
{ return SGVec2<T>(SGMisc<T>::max(s, v(0)), SGMisc<T>::max(s, v(1))); }
|
||||
{ v = simd4::max(v.simd2(), simd4_t<T,2>(s)); return v; }
|
||||
template<typename S, typename T>
|
||||
inline
|
||||
SGVec2<T>
|
||||
max(S s, const SGVec2<T>& v)
|
||||
{ return SGVec2<T>(SGMisc<T>::max(s, v(0)), SGMisc<T>::max(s, v(1))); }
|
||||
{ v = simd4::max(v.simd2(), simd4_t<T,2>(s)); return v; }
|
||||
|
||||
/// Add two vectors taking care of (integer) overflows. The values are limited
|
||||
/// to the respective minimum and maximum values.
|
||||
@@ -216,36 +222,39 @@ template<typename T>
|
||||
inline
|
||||
T
|
||||
dot(const SGVec2<T>& v1, const SGVec2<T>& v2)
|
||||
{ return v1(0)*v2(0) + v1(1)*v2(1); }
|
||||
{ return simd4::dot(v1.simd2(), v2.simd2()); }
|
||||
|
||||
/// The euclidean norm of the vector, that is what most people call length
|
||||
template<typename T>
|
||||
inline
|
||||
T
|
||||
norm(const SGVec2<T>& v)
|
||||
{ return sqrt(dot(v, v)); }
|
||||
{ return simd4::magnitude(v.simd2()); }
|
||||
|
||||
/// The euclidean norm of the vector, that is what most people call length
|
||||
template<typename T>
|
||||
inline
|
||||
T
|
||||
length(const SGVec2<T>& v)
|
||||
{ return sqrt(dot(v, v)); }
|
||||
{ return simd4::magnitude(v.simd2()); }
|
||||
|
||||
/// The 1-norm of the vector, this one is the fastest length function we
|
||||
/// can implement on modern cpu's
|
||||
template<typename T>
|
||||
inline
|
||||
T
|
||||
norm1(const SGVec2<T>& v)
|
||||
{ return fabs(v(0)) + fabs(v(1)); }
|
||||
norm1(SGVec2<T> v)
|
||||
{ v.simd2() = simd4::abs(v.simd2()); return (v(0)+v(1)); }
|
||||
|
||||
/// The inf-norm of the vector
|
||||
template<typename T>
|
||||
inline
|
||||
T
|
||||
normI(const SGVec2<T>& v)
|
||||
{ return SGMisc<T>::max(fabs(v(0)), fabs(v(1))); }
|
||||
normI(SGVec2<T> v)
|
||||
{
|
||||
v.simd2() = simd4::abs(v.simd2());
|
||||
return SGMisc<T>::max(v(0), v(1));
|
||||
}
|
||||
|
||||
/// The euclidean norm of the vector, that is what most people call length
|
||||
template<typename T>
|
||||
@@ -335,14 +344,14 @@ template<typename T>
|
||||
inline
|
||||
T
|
||||
dist(const SGVec2<T>& v1, const SGVec2<T>& v2)
|
||||
{ return norm(v1 - v2); }
|
||||
{ return simd4::magnitude(v1.simd2() - v2.simd2()); }
|
||||
|
||||
/// The squared euclidean distance of the two vectors
|
||||
template<typename T>
|
||||
inline
|
||||
T
|
||||
distSqr(const SGVec2<T>& v1, const SGVec2<T>& v2)
|
||||
{ SGVec2<T> tmp = v1 - v2; return dot(tmp, tmp); }
|
||||
distSqr(SGVec2<T> v1, const SGVec2<T>& v2)
|
||||
{ return simd4::magnitude2(v1.simd2() - v2.simd2()); }
|
||||
|
||||
// calculate the projection of u along the direction of d.
|
||||
template<typename T>
|
||||
@@ -350,12 +359,22 @@ inline
|
||||
SGVec2<T>
|
||||
projection(const SGVec2<T>& u, const SGVec2<T>& d)
|
||||
{
|
||||
T denom = dot(d, d);
|
||||
T denom = simd4::magnitude2(d.simd2());
|
||||
T ud = dot(u, d);
|
||||
if (SGLimits<T>::min() < denom) return u;
|
||||
else return d * (dot(u, d) / denom);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline
|
||||
SGVec2<T>
|
||||
interpolate(T tau, const SGVec2<T>& v1, const SGVec2<T>& v2)
|
||||
{
|
||||
SGVec2<T> r;
|
||||
r.simd2() = simd4::interpolate(tau, v1.simd2(), v2.simd2());
|
||||
return r;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
template<typename T>
|
||||
inline
|
||||
@@ -376,11 +395,11 @@ operator<<(std::basic_ostream<char_type, traits_type>& s, const SGVec2<T>& v)
|
||||
inline
|
||||
SGVec2f
|
||||
toVec2f(const SGVec2d& v)
|
||||
{ return SGVec2f((float)v(0), (float)v(1)); }
|
||||
{ SGVec2f f(v); return f; }
|
||||
|
||||
inline
|
||||
SGVec2d
|
||||
toVec2d(const SGVec2f& v)
|
||||
{ return SGVec2d(v(0), v(1)); }
|
||||
{ SGVec2d d(v); return d; }
|
||||
|
||||
#endif
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include <simgear/math/SGVec2.hxx>
|
||||
#include <simgear/math/SGGeodesy.hxx>
|
||||
#include <simgear/math/simd.hxx>
|
||||
|
||||
/// 3D Vector Class
|
||||
template<typename T>
|
||||
@@ -54,16 +55,16 @@ public:
|
||||
|
||||
/// Constructor. Initialize by the given values
|
||||
SGVec3(T x, T y, T z)
|
||||
{ data()[0] = x; data()[1] = y; data()[2] = z; }
|
||||
{ _data = simd4_t<T,3>(x, y, z); }
|
||||
/// Constructor. Initialize by the content of a plain array,
|
||||
/// make sure it has at least 3 elements
|
||||
explicit SGVec3(const T* d)
|
||||
{ data()[0] = d[0]; data()[1] = d[1]; data()[2] = d[2]; }
|
||||
{ _data = d ? simd4_t<T,3>(d) : simd4_t<T,3>(T(0)); }
|
||||
template<typename S>
|
||||
explicit SGVec3(const SGVec3<S>& d)
|
||||
{ data()[0] = d[0]; data()[1] = d[1]; data()[2] = d[2]; }
|
||||
explicit SGVec3(const SGVec2<T>& v2, const T& v3 = 0)
|
||||
{ data()[0] = v2[0]; data()[1] = v2[1]; data()[2] = v3; }
|
||||
{ _data = v2.simd2(); data()[2] = v3; }
|
||||
|
||||
/// Access by index, the index is unchecked
|
||||
const T& operator()(unsigned i) const
|
||||
@@ -100,25 +101,31 @@ public:
|
||||
|
||||
/// Readonly raw storage interface
|
||||
const T (&data(void) const)[3]
|
||||
{ return _data; }
|
||||
{ return _data.ptr(); }
|
||||
/// Readonly raw storage interface
|
||||
T (&data(void))[3]
|
||||
{ return _data.ptr(); }
|
||||
/// Readonly raw storage interface
|
||||
const simd4_t<T,3> (&simd3(void) const)
|
||||
{ return _data; }
|
||||
/// Readonly raw storage interface
|
||||
simd4_t<T,3> (&simd3(void))
|
||||
{ return _data; }
|
||||
|
||||
/// Inplace addition
|
||||
SGVec3& operator+=(const SGVec3& v)
|
||||
{ data()[0] += v(0); data()[1] += v(1); data()[2] += v(2); return *this; }
|
||||
{ _data += v.simd3(); return *this; }
|
||||
/// Inplace subtraction
|
||||
SGVec3& operator-=(const SGVec3& v)
|
||||
{ data()[0] -= v(0); data()[1] -= v(1); data()[2] -= v(2); return *this; }
|
||||
{ _data -= v.simd3(); return *this; }
|
||||
/// Inplace scalar multiplication
|
||||
template<typename S>
|
||||
SGVec3& operator*=(S s)
|
||||
{ data()[0] *= s; data()[1] *= s; data()[2] *= s; return *this; }
|
||||
{ _data *= s; return *this; }
|
||||
/// Inplace scalar multiplication by 1/s
|
||||
template<typename S>
|
||||
SGVec3& operator/=(S s)
|
||||
{ return operator*=(1/T(s)); }
|
||||
{ _data*=(1/T(s)); return *this; }
|
||||
|
||||
/// Return an all zero vector
|
||||
static SGVec3 zeros(void)
|
||||
@@ -139,7 +146,8 @@ public:
|
||||
static SGVec3 fromGeoc(const SGGeoc& geoc);
|
||||
|
||||
private:
|
||||
T _data[3];
|
||||
simd4_t<T,3> _data;
|
||||
|
||||
};
|
||||
|
||||
template<>
|
||||
@@ -193,36 +201,36 @@ operator+(const SGVec3<T>& v)
|
||||
template<typename T>
|
||||
inline
|
||||
SGVec3<T>
|
||||
operator-(const SGVec3<T>& v)
|
||||
{ return SGVec3<T>(-v(0), -v(1), -v(2)); }
|
||||
operator-(SGVec3<T> v)
|
||||
{ v *= -1; return v; }
|
||||
|
||||
/// Binary +
|
||||
template<typename T>
|
||||
inline
|
||||
SGVec3<T>
|
||||
operator+(const SGVec3<T>& v1, const SGVec3<T>& v2)
|
||||
{ return SGVec3<T>(v1(0)+v2(0), v1(1)+v2(1), v1(2)+v2(2)); }
|
||||
operator+(SGVec3<T> v1, const SGVec3<T>& v2)
|
||||
{ v1.simd3() += v2.simd3(); return v1; }
|
||||
|
||||
/// Binary -
|
||||
template<typename T>
|
||||
inline
|
||||
SGVec3<T>
|
||||
operator-(const SGVec3<T>& v1, const SGVec3<T>& v2)
|
||||
{ return SGVec3<T>(v1(0)-v2(0), v1(1)-v2(1), v1(2)-v2(2)); }
|
||||
operator-(SGVec3<T> v1, const SGVec3<T>& v2)
|
||||
{ v1.simd3() -= v2.simd3(); return v1; }
|
||||
|
||||
/// Scalar multiplication
|
||||
template<typename S, typename T>
|
||||
inline
|
||||
SGVec3<T>
|
||||
operator*(S s, const SGVec3<T>& v)
|
||||
{ return SGVec3<T>(s*v(0), s*v(1), s*v(2)); }
|
||||
operator*(S s, SGVec3<T> v)
|
||||
{ v.simd3() *= s; return v; }
|
||||
|
||||
/// Scalar multiplication
|
||||
template<typename S, typename T>
|
||||
inline
|
||||
SGVec3<T>
|
||||
operator*(const SGVec3<T>& v, S s)
|
||||
{ return SGVec3<T>(s*v(0), s*v(1), s*v(2)); }
|
||||
operator*(SGVec3<T> v, S s)
|
||||
{ v.simd3() *= s; return v; }
|
||||
|
||||
/// multiplication as a multiplicator, that is assume that the first vector
|
||||
/// represents a 3x3 diagonal matrix with the diagonal elements in the vector.
|
||||
@@ -230,66 +238,42 @@ operator*(const SGVec3<T>& v, S s)
|
||||
template<typename T>
|
||||
inline
|
||||
SGVec3<T>
|
||||
mult(const SGVec3<T>& v1, const SGVec3<T>& v2)
|
||||
{ return SGVec3<T>(v1(0)*v2(0), v1(1)*v2(1), v1(2)*v2(2)); }
|
||||
mult(SGVec3<T> v1, const SGVec3<T>& v2)
|
||||
{ v1.simd3() *= v2.simd3(); return v1; }
|
||||
|
||||
/// component wise min
|
||||
template<typename T>
|
||||
inline
|
||||
SGVec3<T>
|
||||
min(const SGVec3<T>& v1, const SGVec3<T>& v2)
|
||||
{
|
||||
return SGVec3<T>(SGMisc<T>::min(v1(0), v2(0)),
|
||||
SGMisc<T>::min(v1(1), v2(1)),
|
||||
SGMisc<T>::min(v1(2), v2(2)));
|
||||
}
|
||||
min(SGVec3<T> v1, const SGVec3<T>& v2)
|
||||
{ v1.simd3() = simd4::min(v1.simd3(), v2.simd3()); return v1; }
|
||||
template<typename S, typename T>
|
||||
inline
|
||||
SGVec3<T>
|
||||
min(const SGVec3<T>& v, S s)
|
||||
{
|
||||
return SGVec3<T>(SGMisc<T>::min(s, v(0)),
|
||||
SGMisc<T>::min(s, v(1)),
|
||||
SGMisc<T>::min(s, v(2)));
|
||||
}
|
||||
min(SGVec3<T> v, S s)
|
||||
{ v.simd3() = simd4::min(v.simd3(), simd4_t<T,3>(s)); return v; }
|
||||
template<typename S, typename T>
|
||||
inline
|
||||
SGVec3<T>
|
||||
min(S s, const SGVec3<T>& v)
|
||||
{
|
||||
return SGVec3<T>(SGMisc<T>::min(s, v(0)),
|
||||
SGMisc<T>::min(s, v(1)),
|
||||
SGMisc<T>::min(s, v(2)));
|
||||
}
|
||||
min(S s, SGVec3<T> v)
|
||||
{ v.simd3() = simd4::min(v.simd3(), simd4_t<T,3>(s)); return v; }
|
||||
|
||||
/// component wise max
|
||||
template<typename T>
|
||||
inline
|
||||
SGVec3<T>
|
||||
max(const SGVec3<T>& v1, const SGVec3<T>& v2)
|
||||
{
|
||||
return SGVec3<T>(SGMisc<T>::max(v1(0), v2(0)),
|
||||
SGMisc<T>::max(v1(1), v2(1)),
|
||||
SGMisc<T>::max(v1(2), v2(2)));
|
||||
}
|
||||
max(SGVec3<T> v1, const SGVec3<T>& v2)
|
||||
{ v1.simd3() = simd4::max(v1.simd3(), v2.simd3()); return v1; }
|
||||
template<typename S, typename T>
|
||||
inline
|
||||
SGVec3<T>
|
||||
max(const SGVec3<T>& v, S s)
|
||||
{
|
||||
return SGVec3<T>(SGMisc<T>::max(s, v(0)),
|
||||
SGMisc<T>::max(s, v(1)),
|
||||
SGMisc<T>::max(s, v(2)));
|
||||
}
|
||||
max(SGVec3<T> v, S s)
|
||||
{ v.simd3() = simd4::max(v.simd3(), simd4_t<T,3>(s)); return v; }
|
||||
template<typename S, typename T>
|
||||
inline
|
||||
SGVec3<T>
|
||||
max(S s, const SGVec3<T>& v)
|
||||
{
|
||||
return SGVec3<T>(SGMisc<T>::max(s, v(0)),
|
||||
SGMisc<T>::max(s, v(1)),
|
||||
SGMisc<T>::max(s, v(2)));
|
||||
}
|
||||
max(S s, SGVec3<T> v)
|
||||
{ v.simd3() = simd4::max(v.simd3(), simd4_t<T,3>(s)); return v; }
|
||||
|
||||
/// Add two vectors taking care of (integer) overflows. The values are limited
|
||||
/// to the respective minimum and maximum values.
|
||||
@@ -308,47 +292,46 @@ template<typename T>
|
||||
inline
|
||||
T
|
||||
dot(const SGVec3<T>& v1, const SGVec3<T>& v2)
|
||||
{ return v1(0)*v2(0) + v1(1)*v2(1) + v1(2)*v2(2); }
|
||||
{ return simd4::dot(v1.simd3(), v2.simd3()); }
|
||||
|
||||
/// The euclidean norm of the vector, that is what most people call length
|
||||
template<typename T>
|
||||
inline
|
||||
T
|
||||
norm(const SGVec3<T>& v)
|
||||
{ return sqrt(dot(v, v)); }
|
||||
{ return simd4::magnitude(v.simd3()); }
|
||||
|
||||
/// The euclidean norm of the vector, that is what most people call length
|
||||
template<typename T>
|
||||
inline
|
||||
T
|
||||
length(const SGVec3<T>& v)
|
||||
{ return sqrt(dot(v, v)); }
|
||||
{ return simd4::magnitude(v.simd3()); }
|
||||
|
||||
/// The 1-norm of the vector, this one is the fastest length function we
|
||||
/// can implement on modern cpu's
|
||||
template<typename T>
|
||||
inline
|
||||
T
|
||||
norm1(const SGVec3<T>& v)
|
||||
{ return fabs(v(0)) + fabs(v(1)) + fabs(v(2)); }
|
||||
norm1(SGVec3<T> v)
|
||||
{ v.simd3() = simd4::abs(v.simd3()); return (v(0)+v(1)+v(2)); }
|
||||
|
||||
/// The inf-norm of the vector
|
||||
template<typename T>
|
||||
inline
|
||||
T
|
||||
normI(const SGVec3<T>& v)
|
||||
{ return SGMisc<T>::max(fabs(v(0)), fabs(v(1)), fabs(v(2))); }
|
||||
normI(SGVec3<T> v)
|
||||
{
|
||||
v.simd3() = simd4::abs(v.simd3());
|
||||
return SGMisc<T>::max(v(0), v(1), v(2));
|
||||
}
|
||||
|
||||
/// Vector cross product
|
||||
template<typename T>
|
||||
inline
|
||||
SGVec3<T>
|
||||
cross(const SGVec3<T>& v1, const SGVec3<T>& v2)
|
||||
{
|
||||
return SGVec3<T>(v1(1)*v2(2) - v1(2)*v2(1),
|
||||
v1(2)*v2(0) - v1(0)*v2(2),
|
||||
v1(0)*v2(1) - v1(1)*v2(0));
|
||||
}
|
||||
cross(SGVec3<T> v1, const SGVec3<T>& v2)
|
||||
{ v1.simd3() = simd4::cross(v1.simd3(), v2.simd3()); return v1; }
|
||||
|
||||
/// return any normalized vector perpendicular to v
|
||||
template<typename T>
|
||||
@@ -468,14 +451,14 @@ template<typename T>
|
||||
inline
|
||||
T
|
||||
dist(const SGVec3<T>& v1, const SGVec3<T>& v2)
|
||||
{ return norm(v1 - v2); }
|
||||
{ return simd4::magnitude(v1.simd3() - v2.simd3()); }
|
||||
|
||||
/// The squared euclidean distance of the two vectors
|
||||
template<typename T>
|
||||
inline
|
||||
T
|
||||
distSqr(const SGVec3<T>& v1, const SGVec3<T>& v2)
|
||||
{ SGVec3<T> tmp = v1 - v2; return dot(tmp, tmp); }
|
||||
distSqr(SGVec3<T> v1, const SGVec3<T>& v2)
|
||||
{ return simd4::magnitude2(v1.simd3() - v2.simd3()); }
|
||||
|
||||
// calculate the projection of u along the direction of d.
|
||||
template<typename T>
|
||||
@@ -483,12 +466,22 @@ inline
|
||||
SGVec3<T>
|
||||
projection(const SGVec3<T>& u, const SGVec3<T>& d)
|
||||
{
|
||||
T denom = dot(d, d);
|
||||
T denom = simd4::magnitude2(d.simd3());
|
||||
T ud = dot(u, d);
|
||||
if (SGLimits<T>::min() < denom) return u;
|
||||
else return d * (dot(u, d) / denom);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline
|
||||
SGVec3<T>
|
||||
interpolate(T tau, const SGVec3<T>& v1, const SGVec3<T>& v2)
|
||||
{
|
||||
SGVec3<T> r;
|
||||
r.simd3() = simd4::interpolate(tau, v1.simd3(), v2.simd3());
|
||||
return r;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
template<typename T>
|
||||
inline
|
||||
@@ -510,11 +503,11 @@ operator<<(std::basic_ostream<char_type, traits_type>& s, const SGVec3<T>& v)
|
||||
inline
|
||||
SGVec3f
|
||||
toVec3f(const SGVec3d& v)
|
||||
{ return SGVec3f((float)v(0), (float)v(1), (float)v(2)); }
|
||||
{ SGVec3f f(v); return f; }
|
||||
|
||||
inline
|
||||
SGVec3d
|
||||
toVec3d(const SGVec3f& v)
|
||||
{ return SGVec3d(v(0), v(1), v(2)); }
|
||||
{ SGVec3d d(v); return d; }
|
||||
|
||||
#endif
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
|
||||
#include <iosfwd>
|
||||
|
||||
#include <simgear/math/simd.hxx>
|
||||
|
||||
/// 4D Vector Class
|
||||
template<typename T>
|
||||
class SGVec4 {
|
||||
@@ -40,16 +42,16 @@ public:
|
||||
}
|
||||
/// Constructor. Initialize by the given values
|
||||
SGVec4(T x, T y, T z, T w)
|
||||
{ data()[0] = x; data()[1] = y; data()[2] = z; data()[3] = w; }
|
||||
{ _data = simd4_t<T,4>(x, y, z, w); }
|
||||
/// Constructor. Initialize by the content of a plain array,
|
||||
/// make sure it has at least 3 elements
|
||||
explicit SGVec4(const T* d)
|
||||
{ data()[0] = d[0]; data()[1] = d[1]; data()[2] = d[2]; data()[3] = d[3]; }
|
||||
{ _data = d ? simd4_t<T,4>(d) : simd4_t<T,4>(T(0)); }
|
||||
template<typename S>
|
||||
explicit SGVec4(const SGVec4<S>& d)
|
||||
{ data()[0] = d[0]; data()[1] = d[1]; data()[2] = d[2]; data()[3] = d[3]; }
|
||||
explicit SGVec4(const SGVec3<T>& v3, const T& v4 = 0)
|
||||
{ data()[0] = v3[0]; data()[1] = v3[1]; data()[2] = v3[2]; data()[3] = v4; }
|
||||
{ _data = v3.simd3(); data()[3] = v4; }
|
||||
|
||||
/// Access by index, the index is unchecked
|
||||
const T& operator()(unsigned i) const
|
||||
@@ -92,25 +94,31 @@ public:
|
||||
|
||||
/// Readonly raw storage interface
|
||||
const T (&data(void) const)[4]
|
||||
{ return _data; }
|
||||
{ return _data.ptr(); }
|
||||
/// Readonly raw storage interface
|
||||
T (&data(void))[4]
|
||||
{ return _data.ptr(); }
|
||||
/// Readonly raw storage interface
|
||||
const simd4_t<T,4> (&simd4(void) const)
|
||||
{ return _data; }
|
||||
/// Readonly raw storage interface
|
||||
simd4_t<T,4> (&simd4(void))
|
||||
{ return _data; }
|
||||
|
||||
/// Inplace addition
|
||||
SGVec4& operator+=(const SGVec4& v)
|
||||
{ data()[0]+=v(0);data()[1]+=v(1);data()[2]+=v(2);data()[3]+=v(3);return *this; }
|
||||
{ _data += v.simd4(); return *this; }
|
||||
/// Inplace subtraction
|
||||
SGVec4& operator-=(const SGVec4& v)
|
||||
{ data()[0]-=v(0);data()[1]-=v(1);data()[2]-=v(2);data()[3]-=v(3);return *this; }
|
||||
{ _data -= v.simd4(); return *this; }
|
||||
/// Inplace scalar multiplication
|
||||
template<typename S>
|
||||
SGVec4& operator*=(S s)
|
||||
{ data()[0] *= s; data()[1] *= s; data()[2] *= s; data()[3] *= s; return *this; }
|
||||
{ _data *= s; return *this; }
|
||||
/// Inplace scalar multiplication by 1/s
|
||||
template<typename S>
|
||||
SGVec4& operator/=(S s)
|
||||
{ return operator*=(1/T(s)); }
|
||||
{ _data*=(1/T(s)); return *this; }
|
||||
|
||||
/// Return an all zero vector
|
||||
static SGVec4 zeros(void)
|
||||
@@ -126,7 +134,7 @@ public:
|
||||
{ return SGVec4(0, 0, 0, 1); }
|
||||
|
||||
private:
|
||||
T _data[4];
|
||||
simd4_t<T,4> _data;
|
||||
};
|
||||
|
||||
/// Unary +, do nothing ...
|
||||
@@ -140,36 +148,36 @@ operator+(const SGVec4<T>& v)
|
||||
template<typename T>
|
||||
inline
|
||||
SGVec4<T>
|
||||
operator-(const SGVec4<T>& v)
|
||||
{ return SGVec4<T>(-v(0), -v(1), -v(2), -v(3)); }
|
||||
operator-(SGVec4<T> v)
|
||||
{ v *= -1; return v; }
|
||||
|
||||
/// Binary +
|
||||
template<typename T>
|
||||
inline
|
||||
SGVec4<T>
|
||||
operator+(const SGVec4<T>& v1, const SGVec4<T>& v2)
|
||||
{ return SGVec4<T>(v1(0)+v2(0), v1(1)+v2(1), v1(2)+v2(2), v1(3)+v2(3)); }
|
||||
operator+(SGVec4<T> v1, const SGVec4<T>& v2)
|
||||
{ v1.simd4() += v2.simd4(); return v1; }
|
||||
|
||||
/// Binary -
|
||||
template<typename T>
|
||||
inline
|
||||
SGVec4<T>
|
||||
operator-(const SGVec4<T>& v1, const SGVec4<T>& v2)
|
||||
{ return SGVec4<T>(v1(0)-v2(0), v1(1)-v2(1), v1(2)-v2(2), v1(3)-v2(3)); }
|
||||
operator-(SGVec4<T> v1, const SGVec4<T>& v2)
|
||||
{ v1.simd4() -= v2.simd4(); return v1; }
|
||||
|
||||
/// Scalar multiplication
|
||||
template<typename S, typename T>
|
||||
inline
|
||||
SGVec4<T>
|
||||
operator*(S s, const SGVec4<T>& v)
|
||||
{ return SGVec4<T>(s*v(0), s*v(1), s*v(2), s*v(3)); }
|
||||
operator*(S s, SGVec4<T> v)
|
||||
{ v.simd4() *= s; return v; }
|
||||
|
||||
/// Scalar multiplication
|
||||
template<typename S, typename T>
|
||||
inline
|
||||
SGVec4<T>
|
||||
operator*(const SGVec4<T>& v, S s)
|
||||
{ return SGVec4<T>(s*v(0), s*v(1), s*v(2), s*v(3)); }
|
||||
operator*(SGVec4<T> v, S s)
|
||||
{ v.simd4() *= s; return v; }
|
||||
|
||||
/// multiplication as a multiplicator, that is assume that the first vector
|
||||
/// represents a 4x4 diagonal matrix with the diagonal elements in the vector.
|
||||
@@ -177,72 +185,42 @@ operator*(const SGVec4<T>& v, S s)
|
||||
template<typename T>
|
||||
inline
|
||||
SGVec4<T>
|
||||
mult(const SGVec4<T>& v1, const SGVec4<T>& v2)
|
||||
{ return SGVec4<T>(v1(0)*v2(0), v1(1)*v2(1), v1(2)*v2(2), v1(3)*v2(3)); }
|
||||
mult(SGVec4<T> v1, const SGVec4<T>& v2)
|
||||
{ v1.simd4() *= v2.simd4(); return v1; }
|
||||
|
||||
/// component wise min
|
||||
template<typename T>
|
||||
inline
|
||||
SGVec4<T>
|
||||
min(const SGVec4<T>& v1, const SGVec4<T>& v2)
|
||||
{
|
||||
return SGVec4<T>(SGMisc<T>::min(v1(0), v2(0)),
|
||||
SGMisc<T>::min(v1(1), v2(1)),
|
||||
SGMisc<T>::min(v1(2), v2(2)),
|
||||
SGMisc<T>::min(v1(3), v2(3)));
|
||||
}
|
||||
min(SGVec4<T> v1, const SGVec4<T>& v2)
|
||||
{ v1.simd4() = simd4::min(v1.simd4(), v2.simd4()); return v1; }
|
||||
template<typename S, typename T>
|
||||
inline
|
||||
SGVec4<T>
|
||||
min(const SGVec4<T>& v, S s)
|
||||
{
|
||||
return SGVec4<T>(SGMisc<T>::min(s, v(0)),
|
||||
SGMisc<T>::min(s, v(1)),
|
||||
SGMisc<T>::min(s, v(2)),
|
||||
SGMisc<T>::min(s, v(3)));
|
||||
}
|
||||
min(SGVec4<T> v, S s)
|
||||
{ v.simd4() = simd4::min(v.simd4(), simd4_t<T,4>(s)); return v; }
|
||||
template<typename S, typename T>
|
||||
inline
|
||||
SGVec4<T>
|
||||
min(S s, const SGVec4<T>& v)
|
||||
{
|
||||
return SGVec4<T>(SGMisc<T>::min(s, v(0)),
|
||||
SGMisc<T>::min(s, v(1)),
|
||||
SGMisc<T>::min(s, v(2)),
|
||||
SGMisc<T>::min(s, v(3)));
|
||||
}
|
||||
min(S s, SGVec4<T> v)
|
||||
{ v.simd4() = simd4::min(v.simd4(), simd4_t<T,4>(s)); return v; }
|
||||
|
||||
/// component wise max
|
||||
template<typename T>
|
||||
inline
|
||||
SGVec4<T>
|
||||
max(const SGVec4<T>& v1, const SGVec4<T>& v2)
|
||||
{
|
||||
return SGVec4<T>(SGMisc<T>::max(v1(0), v2(0)),
|
||||
SGMisc<T>::max(v1(1), v2(1)),
|
||||
SGMisc<T>::max(v1(2), v2(2)),
|
||||
SGMisc<T>::max(v1(3), v2(3)));
|
||||
}
|
||||
max(SGVec4<T> v1, const SGVec4<T>& v2)
|
||||
{ v1.simd4() = simd4::max(v1.simd4(), v2.simd4()); return v1; }
|
||||
template<typename S, typename T>
|
||||
inline
|
||||
SGVec4<T>
|
||||
max(const SGVec4<T>& v, S s)
|
||||
{
|
||||
return SGVec4<T>(SGMisc<T>::max(s, v(0)),
|
||||
SGMisc<T>::max(s, v(1)),
|
||||
SGMisc<T>::max(s, v(2)),
|
||||
SGMisc<T>::max(s, v(3)));
|
||||
}
|
||||
max(SGVec4<T> v, S s)
|
||||
{ v.simd4() = simd4::max(v.simd4(), simd4_t<T,4>(s)); return v; }
|
||||
template<typename S, typename T>
|
||||
inline
|
||||
SGVec4<T>
|
||||
max(S s, const SGVec4<T>& v)
|
||||
{
|
||||
return SGVec4<T>(SGMisc<T>::max(s, v(0)),
|
||||
SGMisc<T>::max(s, v(1)),
|
||||
SGMisc<T>::max(s, v(2)),
|
||||
SGMisc<T>::max(s, v(3)));
|
||||
}
|
||||
max(S s, SGVec4<T> v)
|
||||
{ v.simd4() = simd4::max(v.simd4(), simd4_t<T,4>(s)); return v; }
|
||||
|
||||
/// Add two vectors taking care of (integer) overflows. The values are limited
|
||||
/// to the respective minimum and maximum values.
|
||||
@@ -262,36 +240,39 @@ template<typename T>
|
||||
inline
|
||||
T
|
||||
dot(const SGVec4<T>& v1, const SGVec4<T>& v2)
|
||||
{ return v1(0)*v2(0) + v1(1)*v2(1) + v1(2)*v2(2) + v1(3)*v2(3); }
|
||||
{ return simd4::dot(v1.simd4(), v2.simd4()); }
|
||||
|
||||
/// The euclidean norm of the vector, that is what most people call length
|
||||
template<typename T>
|
||||
inline
|
||||
T
|
||||
norm(const SGVec4<T>& v)
|
||||
{ return sqrt(dot(v, v)); }
|
||||
{ return simd4::magnitude(v.simd4()); }
|
||||
|
||||
/// The euclidean norm of the vector, that is what most people call length
|
||||
template<typename T>
|
||||
inline
|
||||
T
|
||||
length(const SGVec4<T>& v)
|
||||
{ return sqrt(dot(v, v)); }
|
||||
{ return simd4::magnitude(v.simd4()); }
|
||||
|
||||
/// The 1-norm of the vector, this one is the fastest length function we
|
||||
/// can implement on modern cpu's
|
||||
template<typename T>
|
||||
inline
|
||||
T
|
||||
norm1(const SGVec4<T>& v)
|
||||
{ return fabs(v(0)) + fabs(v(1)) + fabs(v(2)) + fabs(v(3)); }
|
||||
norm1(SGVec4<T> v)
|
||||
{ v.simd4() = simd4::abs(v.simd4()); return (v(0)+v(1)+v(2)+v(3)); }
|
||||
|
||||
/// The inf-norm of the vector
|
||||
template<typename T>
|
||||
inline
|
||||
T
|
||||
normI(const SGVec4<T>& v)
|
||||
{ return SGMisc<T>::max(fabs(v(0)), fabs(v(1)), fabs(v(2)), fabs(v(2))); }
|
||||
normI(SGVec4<T> v)
|
||||
{
|
||||
v.simd4() = simd4::abs(v.simd4());
|
||||
return SGMisc<T>::max(v(0), v(1), v(2), v(3));
|
||||
}
|
||||
|
||||
/// The euclidean norm of the vector, that is what most people call length
|
||||
template<typename T>
|
||||
@@ -389,14 +370,14 @@ template<typename T>
|
||||
inline
|
||||
T
|
||||
dist(const SGVec4<T>& v1, const SGVec4<T>& v2)
|
||||
{ return norm(v1 - v2); }
|
||||
{ return simd4::magnitude(v1.simd4() - v2.simd4()); }
|
||||
|
||||
/// The squared euclidean distance of the two vectors
|
||||
template<typename T>
|
||||
inline
|
||||
T
|
||||
distSqr(const SGVec4<T>& v1, const SGVec4<T>& v2)
|
||||
{ SGVec4<T> tmp = v1 - v2; return dot(tmp, tmp); }
|
||||
distSqr(SGVec4<T> v1, const SGVec4<T>& v2)
|
||||
{ return simd4::magnitude2(v1.simd4() - v2.simd4()); }
|
||||
|
||||
// calculate the projection of u along the direction of d.
|
||||
template<typename T>
|
||||
@@ -404,12 +385,22 @@ inline
|
||||
SGVec4<T>
|
||||
projection(const SGVec4<T>& u, const SGVec4<T>& d)
|
||||
{
|
||||
T denom = dot(d, d);
|
||||
T denom = simd4::magnitude2(d.simd4());
|
||||
T ud = dot(u, d);
|
||||
if (SGLimits<T>::min() < denom) return u;
|
||||
else return d * (dot(u, d) / denom);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline
|
||||
SGVec4<T>
|
||||
interpolate(T tau, const SGVec4<T>& v1, const SGVec4<T>& v2)
|
||||
{
|
||||
SGVec4<T> r;
|
||||
r.simd4() = simd4::interpolate(tau, v1.simd4(), v2.simd4());
|
||||
return r;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
template<typename T>
|
||||
inline
|
||||
@@ -431,11 +422,11 @@ operator<<(std::basic_ostream<char_type, traits_type>& s, const SGVec4<T>& v)
|
||||
inline
|
||||
SGVec4f
|
||||
toVec4f(const SGVec4d& v)
|
||||
{ return SGVec4f((float)v(0), (float)v(1), (float)v(2), (float)v(3)); }
|
||||
{ SGVec4f f(v); return f; }
|
||||
|
||||
inline
|
||||
SGVec4d
|
||||
toVec4d(const SGVec4f& v)
|
||||
{ return SGVec4d(v(0), v(1), v(2), v(3)); }
|
||||
{ SGVec4d d(v); return d; }
|
||||
|
||||
#endif
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
#include <string>
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/misc/sgstream.hxx>
|
||||
#include <simgear/io/iostreams/sgstream.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/props/props.hxx>
|
||||
|
||||
|
||||
1305
simgear/math/simd.hxx
Normal file
1305
simgear/math/simd.hxx
Normal file
File diff suppressed because it is too large
Load Diff
1197
simgear/math/simd4x4.hxx
Normal file
1197
simgear/math/simd4x4.hxx
Normal file
File diff suppressed because it is too large
Load Diff
706
simgear/math/simd4x4_neon.hxx
Normal file
706
simgear/math/simd4x4_neon.hxx
Normal file
@@ -0,0 +1,706 @@
|
||||
// Copyright (C) 2016 Erik Hofman - erik@ehofman.com
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Library 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, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#ifndef __SIMD4X4_NEON_H__
|
||||
#define __SIMD4X4_NEON_H__ 1
|
||||
|
||||
#include "simd_neon.hxx"
|
||||
|
||||
#ifdef __ARM_NEON__
|
||||
template<>
|
||||
class simd4x4_t<float,4>
|
||||
{
|
||||
private:
|
||||
typedef float __mtx4f_t[4][4];
|
||||
|
||||
union alignas(16) {
|
||||
float32x4_t simd4x4[4];
|
||||
__mtx4f_t mtx;
|
||||
float array[4*4];
|
||||
}g;
|
||||
|
||||
public:
|
||||
simd4x4_t(void) {}
|
||||
simd4x4_t(float m00, float m01, float m02, float m03,
|
||||
float m10, float m11, float m12, float m13,
|
||||
float m20, float m21, float m22, float m23,
|
||||
float m30, float m31, float m32, float m33)
|
||||
{
|
||||
array[0] = m00; array[1] = m10;
|
||||
array[2] = m20; array[3] = m30;
|
||||
array[4] = m01; array[5] = m11;
|
||||
array[6] = m21; array[7] = m31;
|
||||
array[8] = m02; array[9] = m12;
|
||||
array[10] = m22; array[11] = m32;
|
||||
array[12] = m03; array[13] = m13;
|
||||
array[14] = m23; array[15] = m33;
|
||||
}
|
||||
simd4x4_t(const float m[4*4]) {
|
||||
for (int i=0; i<4; ++i) {
|
||||
simd4x4[i] = simd4_t<float,4>((const float*)&m[4*i]).v4();
|
||||
}
|
||||
}
|
||||
|
||||
explicit simd4x4_t(const __mtx4f_t m) {
|
||||
for (int i=0; i<4; ++i) {
|
||||
simd4x4[i] = simd4_t<float,4>(m[i]).v4();
|
||||
}
|
||||
}
|
||||
simd4x4_t(const simd4x4_t<float,4>& m) {
|
||||
for (int i=0; i<4; ++i) {
|
||||
simd4x4[i] = m.m4x4()[i];
|
||||
}
|
||||
}
|
||||
~simd4x4_t(void) {}
|
||||
|
||||
inline float32x4_t (&m4x4(void))[4] {
|
||||
return simd4x4;
|
||||
}
|
||||
|
||||
inline const float32x4_t (&m4x4(void) const)[4] {
|
||||
return simd4x4;
|
||||
}
|
||||
|
||||
inline const float (&ptr(void) const)[4][4] {
|
||||
return mtx;
|
||||
}
|
||||
|
||||
inline float (&ptr(void))[4][4] {
|
||||
return mtx;
|
||||
}
|
||||
|
||||
inline operator const float*(void) const {
|
||||
return array;
|
||||
}
|
||||
|
||||
inline operator float*(void) {
|
||||
return array;
|
||||
}
|
||||
|
||||
template<int M>
|
||||
inline void set(int i, const simd4_t<float,M>& v) {
|
||||
simd4x4[i] = v.v4();
|
||||
}
|
||||
|
||||
inline simd4x4_t<float,4>& operator=(const __mtx4f_t m) {
|
||||
for (int i=0; i<4; ++i) {
|
||||
simd4x4[i] = simd4_t<float,4>(m[i]).v4();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
inline simd4x4_t<float,4>& operator=(const simd4x4_t<float,4>& m) {
|
||||
for (int i=0; i<4; ++i) {
|
||||
simd4x4[i] = m.m4x4()[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline simd4x4_t<float,4>& operator+=(const simd4x4_t<float,4>& m) {
|
||||
for (int i=0; i<4; ++i) {
|
||||
simd4x4[i] = vaddq_f32(simd4x4[i], m.m4x4()[i]);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline simd4x4_t<float,4>& operator-=(const simd4x4_t<float,4>& m) {
|
||||
for (int i=0; i<4; ++i) {
|
||||
simd4x4[i] = vsubq_f32(simd4x4[i], m.m4x4()[i]);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline simd4x4_t<float,4>& operator*=(float f) {
|
||||
simd4_t<float,4> f4(f);
|
||||
for (int i=0; i<4; ++i) {
|
||||
simd4x4[i] = vmulq_f32(simd4x4[i], f4.v4());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
simd4x4_t<float,4>& operator*=(const simd4x4_t<float,4>& m2) {
|
||||
simd4x4_t<float,4> m1 = *this;
|
||||
float32x4_t row, col;
|
||||
for (int i=0; i<4; ++i) {
|
||||
col = vdupq_n_f32(m2.ptr()[i][0]);
|
||||
row = vmulq_f32(m1.m4x4()[0], col);
|
||||
for (int j=1; j<4; ++j) {
|
||||
col = vdupq_n_f32(m2.ptr()[i][j]);
|
||||
row = vaddq_f32(row, vmulq_f32(m1.m4x4()[j], col));
|
||||
}
|
||||
simd4x4[i] = row;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
template<int M>
|
||||
inline simd4_t<float,M> operator*(const simd4x4_t<float,4>& m, const simd4_t<float,M>& vi)
|
||||
{
|
||||
float32x4_t mv = vmulq_f32(m.m4x4()[0], vdupq_n_f32(vi.ptr()[0]));
|
||||
for (int i=1; i<M; ++i) {
|
||||
float32x4_t row = vmulq_f32(m.m4x4()[i], vdupq_n_f32(vi.ptr()[i]));
|
||||
mv = vaddq_f32(mv, row);
|
||||
}
|
||||
return mv;
|
||||
}
|
||||
|
||||
namespace simd4x4
|
||||
{
|
||||
|
||||
template<>
|
||||
inline simd4x4_t<float,4> rotation_matrix<float>(float angle, const simd4_t<float,3>& axis)
|
||||
{
|
||||
float s = std::sin(angle), c = std::cos(angle), t = 1.0-c;
|
||||
simd4_t<float,3> axt, at = axis*t, as = axis*s;
|
||||
simd4x4_t<float,4> m;
|
||||
|
||||
simd4x4::unit(m);
|
||||
axt = axis.ptr()[0]*at;
|
||||
m.m4x4()[0] = axt.v4();
|
||||
|
||||
axt = axis.ptr()[1]*at;
|
||||
m.ptr()[0][0] += c;
|
||||
m.ptr()[0][1] += as.ptr()[2];
|
||||
m.ptr()[0][2] -= as.ptr()[1];
|
||||
|
||||
m.m4x4()[1] = axt.v4();
|
||||
|
||||
axt = axis.ptr()[2]*at;
|
||||
m.ptr()[1][0] -= as.ptr()[2];
|
||||
m.ptr()[1][1] += c;
|
||||
m.ptr()[1][2] += as.ptr()[0];
|
||||
|
||||
m.m4x4()[2] = axt.v4();
|
||||
|
||||
m.ptr()[2][0] += as.ptr()[1];
|
||||
m.ptr()[2][1] -= as.ptr()[0];
|
||||
m.ptr()[2][2] += c;
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline simd4x4_t<float,4> transpose<float>(simd4x4_t<float,4> m) {
|
||||
// http://clb.demon.fi/MathGeoLib/nightly/docs/float4x4_neon.h_code.html
|
||||
float32x4x4_t x = vld4q_f32(m);
|
||||
vst1q_f32(&m[0], x.val[0]);
|
||||
vst1q_f32(&m[4], x.val[1]);
|
||||
vst1q_f32(&m[8], x.val[2]);
|
||||
vst1q_f32(&m[12], x.val[3]);
|
||||
return m;
|
||||
}
|
||||
|
||||
inline void translate(simd4x4_t<float,4>& m, const simd4_t<float,3>& dist) {
|
||||
m.m4x4()[3] = vsubq_f32(m.m4x4()[3], dist.v4());
|
||||
}
|
||||
|
||||
template<typename S>
|
||||
inline void pre_translate(simd4x4_t<float,4>& m, const simd4_t<S,3>& dist)
|
||||
{
|
||||
simd4x4_t<float,4> mt = simd4x4::transpose(m);
|
||||
float32x4_t row3 = mt.m4x4()[3];
|
||||
for (int i=0; i<3; ++i) {
|
||||
float32x4_t t = vdupq_n_f32(float(dist[i]));
|
||||
mt.m4x4()[i] = vaddq_f32(mt.m4x4()[i], vmulq_f32(t, row3));
|
||||
}
|
||||
m = simd4x4::transpose(mt);
|
||||
}
|
||||
|
||||
template<typename S>
|
||||
inline void post_translate(simd4x4_t<float,4>& m, const simd4_t<S,3>& dist)
|
||||
{
|
||||
float32x4_t col3 = m.m4x4()[3];
|
||||
for (int i=0; i<3; ++i) {
|
||||
float32x4_t t = vdupq_n_f32(float(dist[i]));
|
||||
col3 = vaddq_f32(col3, vmulq_f32(t, m.m4x4()[i]));
|
||||
}
|
||||
m.m4x4()[3] = col3;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline simd4_t<float,3> transform<float>(const simd4x4_t<float,4>& m, const simd4_t<float,3>& pt) {
|
||||
float32x4_t tpt = m.m4x4()[3];
|
||||
for (int i=0; i<3; ++i) {
|
||||
float32x4_t ptd = vdupq_n_f32(pt[i]);
|
||||
tpt = vaddq_f32(tpt, vmulq_f32(ptd, m.m4x4()[i]));
|
||||
}
|
||||
vsetq_lane_f32(0.0f, tpt, 3);
|
||||
return tpt;
|
||||
}
|
||||
|
||||
} /* namespace simd4x */
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
|
||||
template<>
|
||||
class simd4x4_t<double,4>
|
||||
{
|
||||
private:
|
||||
typedef double __mtx4d_t[4][4];
|
||||
|
||||
union alignas(32) {
|
||||
__m256d simd4x4[4];
|
||||
__mtx4d_t mtx;
|
||||
double array[4*4];
|
||||
};
|
||||
|
||||
public:
|
||||
simd4x4_t(void) {}
|
||||
simd4x4_t(double m00, double m01, double m02, double m03,
|
||||
double m10, double m11, double m12, double m13,
|
||||
double m20, double m21, double m22, double m23,
|
||||
double m30, double m31, double m32, double m33)
|
||||
{
|
||||
simd4x4[0] = _mm256_set_pd(m30,m20,m10,m00);
|
||||
simd4x4[1] = _mm256_set_pd(m31,m21,m11,m01);
|
||||
simd4x4[2] = _mm256_set_pd(m32,m22,m12,m02);
|
||||
simd4x4[3] = _mm256_set_pd(m33,m23,m13,m03);
|
||||
}
|
||||
explicit simd4x4_t(const double m[4*4]) {
|
||||
for (int i=0; i<4; ++i) {
|
||||
simd4x4[i] = simd4_t<double,4>((const double*)&m[4*i]).v4();
|
||||
}
|
||||
}
|
||||
|
||||
explicit simd4x4_t(const __mtx4d_t m) {
|
||||
for (int i=0; i<4; ++i) {
|
||||
simd4x4[i] = simd4_t<double,4>(m[i]).v4();
|
||||
}
|
||||
}
|
||||
simd4x4_t(const simd4x4_t<double,4>& m) {
|
||||
for (int i=0; i<4; ++i) {
|
||||
simd4x4[i] = m.m4x4()[i];
|
||||
}
|
||||
}
|
||||
~simd4x4_t(void) {}
|
||||
|
||||
inline __m256d (&m4x4(void))[4] {
|
||||
return simd4x4;
|
||||
}
|
||||
|
||||
inline const __m256d (&m4x4(void) const)[4] {
|
||||
return simd4x4;
|
||||
}
|
||||
|
||||
inline const double (&ptr(void) const)[4][4] {
|
||||
return mtx;
|
||||
}
|
||||
|
||||
inline double (&ptr(void))[4][4] {
|
||||
return mtx;
|
||||
}
|
||||
|
||||
inline operator const double*(void) const {
|
||||
return array;
|
||||
}
|
||||
|
||||
inline operator double*(void) {
|
||||
return array;
|
||||
}
|
||||
|
||||
template<int M>
|
||||
inline void set(int i, const simd4_t<double,M>& v) {
|
||||
simd4x4[i] = v.v4();
|
||||
}
|
||||
|
||||
inline simd4x4_t<double,4>& operator=(const __mtx4d_t m) {
|
||||
for (int i=0; i<4; ++i) {
|
||||
simd4x4[i] = simd4_t<double,4>(m[i]).v4();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
inline simd4x4_t<double,4>& operator=(const simd4x4_t<double,4>& m) {
|
||||
for (int i=0; i<4; ++i) {
|
||||
simd4x4[i] = m.m4x4()[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline simd4x4_t<double,4>& operator+=(const simd4x4_t<double,4>& m) {
|
||||
for (int i=0; i<4; ++i) {
|
||||
simd4x4[i] = _mm256_add_pd(simd4x4[i], m.m4x4()[i]);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline simd4x4_t<double,4>& operator-=(const simd4x4_t<double,4>& m) {
|
||||
for (int i=0; i<4; ++i) {
|
||||
simd4x4[i] = _mm256_sub_pd(simd4x4[i], m.m4x4()[i]);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline simd4x4_t<double,4>& operator*=(double f) {
|
||||
simd4_t<double,4> f4(f);
|
||||
for (int i=0; i<4; ++i) {
|
||||
simd4x4[i] = _mm256_mul_pd(simd4x4[i], f4.v4());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
simd4x4_t<double,4>& operator*=(const simd4x4_t<double,4>& m2) {
|
||||
simd4x4_t<double,4> m1 = *this;
|
||||
__m256d row, col;
|
||||
for (int i=0; i<4; ++i ) {
|
||||
col = _mm256_set1_pd(m2.ptr()[i][0]);
|
||||
row = _mm256_mul_pd(m1.m4x4()[0], col);
|
||||
for (int j=1; j<4; ++j) {
|
||||
col = _mm256_set1_pd(m2.ptr()[i][j]);
|
||||
row = _mm256_add_pd(row, _mm256_mul_pd(m1.m4x4()[j], col));
|
||||
}
|
||||
simd4x4[i] = row;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<int M>
|
||||
inline simd4_t<double,M> operator*(const simd4x4_t<double,4>& m, const simd4_t<double,M>& vi)
|
||||
{
|
||||
__m256d mv = _mm256_mul_pd(m.m4x4()[0], _mm256_set1_pd(vi.ptr()[0]));
|
||||
for (int i=1; i<M; ++i) {
|
||||
__m256d row = _mm256_mul_pd(m.m4x4()[i], _mm256_set1_pd(vi.ptr()[i]));
|
||||
mv = _mm256_add_pd(mv, row);
|
||||
}
|
||||
return mv;
|
||||
}
|
||||
|
||||
namespace simd4x4
|
||||
{
|
||||
|
||||
template<>
|
||||
inline simd4x4_t<double,4> rotation_matrix<double>(double angle, const simd4_t<double,3>& axis)
|
||||
{
|
||||
double s = std::sin(angle), c = std::cos(angle), t = 1.0-c;
|
||||
simd4_t<double,3> axt, at = axis*t, as = axis*s;
|
||||
simd4x4_t<double,4> m;
|
||||
|
||||
simd4x4::unit(m);
|
||||
axt = axis.ptr()[0]*at;
|
||||
m.m4x4()[0] = axt.v4();
|
||||
|
||||
axt = axis.ptr()[1]*at;
|
||||
m.ptr()[0][0] += c;
|
||||
m.ptr()[0][1] += as.ptr()[2];
|
||||
m.ptr()[0][2] -= as.ptr()[1];
|
||||
|
||||
m.m4x4()[1] = axt.v4();
|
||||
|
||||
axt = axis.ptr()[2]*at;
|
||||
m.ptr()[1][0] -= as.ptr()[2];
|
||||
m.ptr()[1][1] += c;
|
||||
m.ptr()[1][2] += as.ptr()[0];
|
||||
|
||||
m.m4x4()[2] = axt.v4();
|
||||
|
||||
m.ptr()[2][0] += as.ptr()[1];
|
||||
m.ptr()[2][1] -= as.ptr()[0];
|
||||
m.ptr()[2][2] += c;
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline simd4x4_t<double,4> transpose<double>(simd4x4_t<double,4> m) {
|
||||
// http://stackoverflow.com/questions/36167517/m256d-transpose4-equivalent
|
||||
__m256d tmp0 = _mm256_shuffle_pd(m.m4x4()[0], m.m4x4()[1], 0x0);
|
||||
__m256d tmp2 = _mm256_shuffle_pd(m.m4x4()[0], m.m4x4()[1], 0xF);
|
||||
__m256d tmp1 = _mm256_shuffle_pd(m.m4x4()[2], m.m4x4()[3], 0x0);
|
||||
__m256d tmp3 = _mm256_shuffle_pd(m.m4x4()[2], m.m4x4()[3], 0xF);
|
||||
|
||||
m.m4x4()[0] = _mm256_permute2f128_pd(tmp0, tmp1, 0x20);
|
||||
m.m4x4()[1] = _mm256_permute2f128_pd(tmp2, tmp3, 0x20);
|
||||
m.m4x4()[2] = _mm256_permute2f128_pd(tmp0, tmp1, 0x31);
|
||||
m.m4x4()[3] = _mm256_permute2f128_pd(tmp2, tmp3, 0x31);
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
inline void translate(simd4x4_t<double,4>& m, const simd4_t<double,3>& dist) {
|
||||
m.m4x4()[3] = _mm256_sub_pd(m.m4x4()[3], dist.v4());
|
||||
}
|
||||
|
||||
template<typename S>
|
||||
inline void pre_translate(simd4x4_t<double,4>& m, const simd4_t<S,3>& dist)
|
||||
{
|
||||
simd4_t<double,4> row3(m.ptr()[0][3],m.ptr()[1][3],m.ptr()[2][3],m.ptr()[3][3]);
|
||||
for (int i=0; i<3; ++i) {
|
||||
for (int j=0; j<4; ++j) {
|
||||
m.ptr()[j][i] += row3[j]*double(dist[i]);
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
// this is slower
|
||||
simd4x4_t<double,4> mt = simd4x4::transpose(m);
|
||||
__mm256d row3 = mt.m4x4()[3];
|
||||
for (int i=0; i<3; ++i) {
|
||||
__mm256d _mm256_set1_pd(float(dist[i]));
|
||||
mt.m4x4()[i] = _mm256_add_pd(mt.m4x4()[i], _mm256_mul_pd(t, row3));
|
||||
}
|
||||
m = simd4x4::transpose(mt);
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename S>
|
||||
inline void post_translate(simd4x4_t<double,4>& m, const simd4_t<S,3>& dist) {
|
||||
__m256d col3 = m.m4x4()[3];
|
||||
for (int i=0; i<3; ++i) {
|
||||
__m256d t = _mm256_set1_pd(double(dist[i]));
|
||||
col3 = _mm256_add_pd(col3, _mm256_mul_pd(t, m.m4x4()[i]));
|
||||
}
|
||||
m.m4x4()[3] = col3;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline simd4_t<double,3> transform<double>(const simd4x4_t<double,4>& m, const simd4_t<double,3>& pt) {
|
||||
simd4_t<double,3> res;
|
||||
__m256d tpt = m.m4x4()[3];
|
||||
for (int i=0; i<3; ++i) {
|
||||
__m256d ptd = _mm256_set1_pd(pt[i]);
|
||||
tpt = _mm256_add_pd(tpt, _mm256_mul_pd(ptd, m.m4x4()[i]));
|
||||
}
|
||||
res = tpt;
|
||||
res[3] = 0.0;
|
||||
return res;
|
||||
}
|
||||
|
||||
} /* namespace simd4x4 */
|
||||
# endif
|
||||
|
||||
|
||||
template<>
|
||||
class simd4x4_t<int,4>
|
||||
{
|
||||
private:
|
||||
typedef int __mtx4i_t[4][4];
|
||||
|
||||
union alignas(16) {
|
||||
int32x4_t simd4x4[4];
|
||||
__mtx4i_t mtx;
|
||||
int array[4*4];
|
||||
}g;
|
||||
|
||||
public:
|
||||
simd4x4_t(void) {}
|
||||
simd4x4_t(int m00, int m01, int m02, int m03,
|
||||
int m10, int m11, int m12, int m13,
|
||||
int m20, int m21, int m22, int m23,
|
||||
int m30, int m31, int m32, int m33)
|
||||
{
|
||||
array[0] = m00; array[1] = m10;
|
||||
array[2] = m20; array[3] = m30;
|
||||
array[4] = m01; array[5] = m11;
|
||||
array[6] = m21; array[7] = m31;
|
||||
array[8] = m02; array[9] = m12;
|
||||
array[10] = m22; array[11] = m32;
|
||||
array[12] = m03; array[13] = m13;
|
||||
array[14] = m23; array[15] = m33;
|
||||
}
|
||||
simd4x4_t(const int m[4*4]) {
|
||||
for (int i=0; i<4; ++i) {
|
||||
simd4x4[i] = simd4_t<int,4>((const int*)&m[4*i]).v4();
|
||||
}
|
||||
}
|
||||
explicit simd4x4_t(const __mtx4i_t m) {
|
||||
for (int i=0; i<4; ++i) {
|
||||
simd4x4[i] = simd4_t<int,4>(m[i]).v4();
|
||||
}
|
||||
}
|
||||
simd4x4_t(const simd4x4_t<int,4>& m) {
|
||||
for (int i=0; i<4; ++i) {
|
||||
simd4x4[i] = m.m4x4()[i];
|
||||
}
|
||||
}
|
||||
~simd4x4_t(void) {}
|
||||
|
||||
inline int32x4_t (&m4x4(void))[4] {
|
||||
return simd4x4;
|
||||
}
|
||||
|
||||
inline const int32x4_t (&m4x4(void) const)[4] {
|
||||
return simd4x4;
|
||||
}
|
||||
|
||||
inline const int (&ptr(void) const)[4][4] {
|
||||
return mtx;
|
||||
}
|
||||
|
||||
inline int (&ptr(void))[4][4] {
|
||||
return mtx;
|
||||
}
|
||||
|
||||
inline operator const int*(void) const {
|
||||
return array;
|
||||
}
|
||||
|
||||
inline operator int*(void) {
|
||||
return array;
|
||||
}
|
||||
|
||||
template<int M>
|
||||
inline void set(int i, const simd4_t<int,M>& v) {
|
||||
simd4x4[i] = v.v4();
|
||||
}
|
||||
|
||||
inline simd4x4_t<int,4>& operator=(const __mtx4i_t m) {
|
||||
for (int i=0; i<4; ++i) {
|
||||
simd4x4[i] = simd4_t<int,4>(m[i]).v4();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
inline simd4x4_t<int,4>& operator=(const simd4x4_t<int,4>& m) {
|
||||
for (int i=0; i<4; ++i) {
|
||||
simd4x4[i] = m.m4x4()[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline simd4x4_t<int,4>& operator+=(const simd4x4_t<int,4>& m) {
|
||||
for (int i=0; i<4; ++i) {
|
||||
simd4x4[i] += m.m4x4()[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline simd4x4_t<int,4>& operator-=(const simd4x4_t<int,4>& m) {
|
||||
for (int i=0; i<4; ++i) {
|
||||
simd4x4[i] -= m.m4x4()[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline simd4x4_t<int,4>& operator*=(int f) {
|
||||
simd4_t<int,4> f4(f);
|
||||
for (int i=0; i<4; ++i) {
|
||||
simd4x4[i] *= f4.v4();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
simd4x4_t<int,4>& operator*=(const simd4x4_t<int,4>& m2) {
|
||||
simd4x4_t<int,4> m1 = *this;
|
||||
simd4_t<int,4> row, col;
|
||||
|
||||
for (int i=0; i<4; ++i) {
|
||||
simd4_t<int,4> col(m2.ptr()[i][0]);
|
||||
row.v4() = m1.m4x4()[0] * col.v4();
|
||||
for (int j=1; j<4; ++j) {
|
||||
simd4_t<int,4> col(m2.ptr()[i][j]);
|
||||
row.v4() += m1.m4x4()[j] * col.v4();
|
||||
}
|
||||
simd4x4[i] = row.v4();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
template<int M>
|
||||
inline simd4_t<int,M> operator*(const simd4x4_t<int,4>& m, const simd4_t<int,M>& vi)
|
||||
{
|
||||
simd4_t<int,M> mv(m.m4x4()[0]);
|
||||
mv *= vi.ptr()[0];
|
||||
for (int i=1; i<M; ++i) {
|
||||
simd4_t<int,4> row(m.m4x4()[i]);
|
||||
row *= vi.ptr()[i];
|
||||
mv.v4() += row.v4();
|
||||
}
|
||||
for (int i=M; i<4; ++i) mv[i] = 0;
|
||||
return mv;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline simd4x4_t<int,4> operator*(const simd4x4_t<int,4>& m1, const simd4x4_t<int,4>& m2)
|
||||
{
|
||||
simd4_t<int,4> row, col;
|
||||
simd4x4_t<int,4> m;
|
||||
|
||||
for (int i=0; i<4; ++i) {
|
||||
simd4_t<int,4> col(m2.ptr()[i][0]);
|
||||
row.v4() = m1.m4x4()[0] * col.v4();
|
||||
for (int j=1; j<4; ++j) {
|
||||
simd4_t<int,4> col(m2.ptr()[i][j]);
|
||||
row.v4() += m1.m4x4()[j] * col.v4();
|
||||
}
|
||||
m.m4x4()[i] = row.v4();
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
namespace simd4x4
|
||||
{
|
||||
|
||||
template<>
|
||||
inline simd4x4_t<int,4> transpose<int>(simd4x4_t<int,4> m) {
|
||||
int32x4x4_t x = vld4q_s32(m);
|
||||
vst1q_s32(&m[0], x.val[0]);
|
||||
vst1q_s32(&m[4], x.val[1]);
|
||||
vst1q_s32(&m[8], x.val[2]);
|
||||
vst1q_s32(&m[12], x.val[3]);
|
||||
return m;
|
||||
}
|
||||
|
||||
inline void translate(simd4x4_t<int,4>& m, const simd4_t<int,3>& dist) {
|
||||
m.m4x4()[3] = vsubq_s32(m.m4x4()[3], dist.v4());
|
||||
}
|
||||
|
||||
template<typename S>
|
||||
inline void pre_translate(simd4x4_t<int,4>& m, const simd4_t<S,3>& dist)
|
||||
{
|
||||
simd4x4_t<int,4> mt = simd4x4::transpose(m);
|
||||
simd4_t<int,4> row3(mt.ptr()[3]);
|
||||
for (int i=0; i<3; ++i) {
|
||||
simd4_t<int,4> trow3 = int(dist[i]);
|
||||
trow3 *= row3.v4();
|
||||
mt.m4x4()[i] = vaddq_s32(mt.m4x4()[i], trow3.v4());
|
||||
}
|
||||
m = simd4x4::transpose(mt);
|
||||
}
|
||||
|
||||
template<typename S>
|
||||
inline void post_translate(simd4x4_t<int,4>& m, const simd4_t<S,3>& dist)
|
||||
{
|
||||
int32x4_t col3 = m.m4x4()[3];
|
||||
for (int i=0; i<3; ++i) {
|
||||
simd4_t<int,4> trow3 = int(dist[i]);
|
||||
trow3 *= m.m4x4()[i];
|
||||
col3 = vaddq_s32(col3, trow3.v4());
|
||||
}
|
||||
m.m4x4()[3] = col3;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline simd4_t<int,3> transform<int>(const simd4x4_t<int,4>& m, const simd4_t<int,3>& pt) {
|
||||
simd4_t<int,3> tpt = m.m4x4()[3];
|
||||
for (int i=0; i<3; ++i) {
|
||||
simd4_t<int,3> ptd = m.m4x4()[i];
|
||||
ptd *= pt[i];
|
||||
tpt.v4() = vaddq_s32(tpt.v4(), ptd.v4());
|
||||
}
|
||||
tpt[3] = 0.0;
|
||||
return tpt;
|
||||
}
|
||||
|
||||
|
||||
} /* namespace simd4x */
|
||||
#endif
|
||||
|
||||
#endif /* __SIMD4X4_NEON_H__ */
|
||||
|
||||
545
simgear/math/simd_neon.hxx
Normal file
545
simgear/math/simd_neon.hxx
Normal file
@@ -0,0 +1,545 @@
|
||||
// Copyright (C) 2016 Erik Hofman - erik@ehofman.com
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Library 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, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#ifndef __SIMD_NEON_H__
|
||||
#define __SIMD_NEON_H__ 1
|
||||
|
||||
#ifdef __ARM_NEON__
|
||||
|
||||
static const uint32_t m2a32[] alignas(16) = { 0xffffffff,0xffffffff,0,0 };
|
||||
static const uint32_t m3a32[] alignas(16) = { 0xffffffff,0xffffffff,0xffffffff,0 };
|
||||
|
||||
template<int N>
|
||||
class simd4_t<float,N>
|
||||
{
|
||||
private:
|
||||
typedef float __vec4f_t[N];
|
||||
|
||||
union alignas(16) {
|
||||
float32x4_t simd4;
|
||||
float32x2x2_t simd2x2;
|
||||
__vec4f_t vec;
|
||||
};
|
||||
|
||||
public:
|
||||
simd4_t(void) {}
|
||||
simd4_t(float f) {}
|
||||
simd4_t(float x, float y) : simd4_t(x,y,0,0) {}
|
||||
simd4_t(float x, float y, float z) : simd4_t(x,y,z,0) {}
|
||||
simd4_t(float x, float y, float z, float w) {
|
||||
alignas(16) float data[4] = { x, y, z, w };
|
||||
simd4 = vld1q_f32(data);
|
||||
}
|
||||
simd4_t(const __vec4f_t v) {}
|
||||
template<int M>
|
||||
simd4_t(const simd4_t<float,M>& v) {
|
||||
simd4 = v.v4();
|
||||
}
|
||||
simd4_t(const float32x4_t& v) {
|
||||
simd4 = v;
|
||||
}
|
||||
|
||||
inline const float32x2x2_t (&v2x2(void) const) {
|
||||
return simd2x2;
|
||||
}
|
||||
inline float32x2x2_t (&v2x2(void)) {
|
||||
return simd2x2;
|
||||
}
|
||||
|
||||
inline const float32x4_t (&v4(void) const) {
|
||||
return simd4;
|
||||
}
|
||||
inline float32x4_t (&v4(void)) {
|
||||
return simd4;
|
||||
}
|
||||
|
||||
inline const float (&ptr(void) const)[N] {
|
||||
return vec;
|
||||
}
|
||||
inline float (&ptr(void))[N] {
|
||||
return vec;
|
||||
}
|
||||
|
||||
inline operator const float*(void) const {
|
||||
return vec;
|
||||
}
|
||||
inline operator float*(void) {
|
||||
return vec;
|
||||
}
|
||||
|
||||
inline simd4_t<float,N>& operator+=(float f) {
|
||||
return operator+=(simd4_t<float,N>(f));
|
||||
}
|
||||
template<int M>
|
||||
inline simd4_t<float,N>& operator+=(const simd4_t<float,M>& v) {
|
||||
simd4 = vaddq_f32(simd4, v.v4());
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline simd4_t<float,N>& operator-=(float f) {
|
||||
return operator-=(simd4_t<float,N>(f));
|
||||
}
|
||||
template<int M>
|
||||
inline simd4_t<float,N>& operator-=(const simd4_t<float,M>& v) {
|
||||
simd4 = vsubq_f32(simd4, v.v4());
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline simd4_t<float,N>& operator*=(float f) {
|
||||
return operator*=(simd4_t<float,N>(f));
|
||||
}
|
||||
template<int M>
|
||||
inline simd4_t<float,N>& operator*=(const simd4_t<float,M>& v) {
|
||||
simd4 = vmulq_f32(simd4, v.v4());
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline simd4_t<float,N>& operator/=(float f) {
|
||||
return operator/=(simd4_t<float,4>(f));
|
||||
}
|
||||
template<int M>
|
||||
inline simd4_t<float,N>& operator/=(const simd4_t<float,M>& v) {
|
||||
// http://stackoverflow.com/questions/6759897/how-to-divide-in-neon-intrinsics-by-a-float-number
|
||||
float32x4_t recip = vrecpeq_f32(v.v4());
|
||||
recip = vmulq_f32(vrecpsq_f32(v.v4(), recip), recip);
|
||||
recip = vmulq_f32(vrecpsq_f32(v.v4(), recip), recip);
|
||||
recip = vmulq_f32(vrecpsq_f32(v.v4(), recip), recip);
|
||||
simd4 = vmulq_f32(simd4, recip);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
# define vandq_f32(a,b) vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(a), vreinterpretq_u32_f32(b)))
|
||||
|
||||
static const float32x4_t fmask2 = vld1q_f32((const float*)m2a32);
|
||||
static const float32x4_t fmask3 = vld1q_f32((const float*)m3a32);
|
||||
|
||||
template<>
|
||||
inline simd4_t<float,4>::simd4_t(const __vec4f_t v) {
|
||||
simd4 = vld1q_f32(v);
|
||||
}
|
||||
template<>
|
||||
inline simd4_t<float,3>::simd4_t(const __vec4f_t v) {
|
||||
simd4 = vandq_f32(vld1q_f32(v), fmask3);
|
||||
}
|
||||
template<>
|
||||
inline simd4_t<float,2>::simd4_t(const __vec4f_t v) {
|
||||
simd4 = vandq_f32(vld1q_f32(v), fmask2);
|
||||
}
|
||||
template<>
|
||||
inline simd4_t<float,4>::simd4_t(float f) {
|
||||
simd4 = vdupq_n_f32(f);
|
||||
}
|
||||
template<>
|
||||
inline simd4_t<float,3>::simd4_t(float f) {
|
||||
simd4 = vandq_f32(vdupq_n_f32(f), fmask3);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline simd4_t<float,2>::simd4_t(float f) {
|
||||
simd4 = vandq_f32(vdupq_n_f32(f), fmask2);
|
||||
}
|
||||
|
||||
|
||||
namespace simd4
|
||||
{
|
||||
// http://stackoverflow.com/questions/6931217/sum-all-elements-in-a-quadword-vector-in-arm-assembly-with-neon
|
||||
inline static float hsum_float32x4_neon(float32x4_t v) {
|
||||
float32x2_t r = vadd_f32(vget_high_f32(v), vget_low_f32(v));
|
||||
return vget_lane_f32(vpadd_f32(r, r), 0);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline float magnitude2(simd4_t<float,4> v) {
|
||||
return hsum_float32x4_neon(v.v4()*v.v4());
|
||||
}
|
||||
|
||||
template<>
|
||||
inline float dot(simd4_t<float,4> v1, const simd4_t<float,4>& v2) {
|
||||
return hsum_float32x4_neon(v1.v4()*v2.v4());
|
||||
}
|
||||
|
||||
template<>
|
||||
inline simd4_t<float,3> cross(const simd4_t<float,3>& v1, const simd4_t<float,3>& v2)
|
||||
{
|
||||
// from 0123 to 0213
|
||||
float32x2x2_t v1lh_2013 = vzip_f32(v1.v2x2().val[0], v1.v2x2().val[1]);
|
||||
float32x2x2_t v2lh_2013 = vzip_f32(v2.v2x2().val[0], v2.v2x2().val[1]);
|
||||
|
||||
// from 0213 to 2013
|
||||
v1lh_2013.val[0] = vrev64_f32(v1lh_2013.val[0]);
|
||||
v2lh_2013.val[0] = vrev64_f32(v2lh_2013.val[0]);
|
||||
|
||||
float32x4_t v1_2013 = vcombine_f32(v1lh_2013.val[0], v1lh_2013.val[1]);
|
||||
float32x4_t v2_2013 = vcombine_f32(v2lh_2013.val[0], v2lh_2013.val[1]);
|
||||
|
||||
// from 2013 to 2103
|
||||
float32x2x2_t v1lh_1203 = vzip_f32(v1lh_2013.val[0], v1lh_2013.val[1]);
|
||||
float32x2x2_t v2lh_1203 = vzip_f32(v2lh_2013.val[0], v2lh_2013.val[1]);
|
||||
|
||||
// from 2103 to 1203
|
||||
v1lh_1203.val[0] = vrev64_f32(v1lh_1203.val[0]);
|
||||
v2lh_1203.val[0] = vrev64_f32(v2lh_1203.val[0]);
|
||||
|
||||
float32x4_t v1_1203 = vcombine_f32(v1lh_1203.val[0], v1lh_1203.val[1]);
|
||||
float32x4_t v2_1203 = vcombine_f32(v2lh_1203.val[0], v2lh_1203.val[1]);
|
||||
|
||||
// calculate the cross product
|
||||
return vsubq_f32(vmulq_f32(v1_1203,v2_2013),vmulq_f32(v1_2013,v2_1203));
|
||||
}
|
||||
|
||||
|
||||
template<int N>
|
||||
inline simd4_t<float,N> min(simd4_t<float,N> v1, const simd4_t<float,N>& v2) {
|
||||
v1 = vminq_f32(v1.v4(), v2.v4());
|
||||
return v1;
|
||||
}
|
||||
|
||||
template<int N>
|
||||
inline simd4_t<float,N> max(simd4_t<float,N> v1, const simd4_t<float,N>& v2) {
|
||||
v1 = vmaxq_f32(v1.v4(), v2.v4());
|
||||
return v1;
|
||||
}
|
||||
|
||||
template<int N>
|
||||
inline simd4_t<float,N>abs(simd4_t<float,N> v) {
|
||||
return vabsq_f32(v.v4());
|
||||
}
|
||||
|
||||
} /* namsepace simd4 */
|
||||
|
||||
|
||||
// TODO: 64-bit support for doubles
|
||||
#if 0
|
||||
template<int N>
|
||||
class simd4_t<double,N>
|
||||
{
|
||||
private:
|
||||
typedef double __vec4d_t[N];
|
||||
|
||||
union alignas(32) {
|
||||
__m256d simd4;
|
||||
__vec4d_t vec;
|
||||
};
|
||||
|
||||
public:
|
||||
simd4_t(void) {}
|
||||
simd4_t(double d) {}
|
||||
simd4_t(double x, double y) : simd4_t(x,y,0,0) {}
|
||||
simd4_t(double x, double y, double z) : simd4_t(x,y,z,0) {}
|
||||
simd4_t(double x, double y, double z, double w) {
|
||||
simd4 = _mm256_set_pd(w,z,y,x);
|
||||
}
|
||||
simd4_t(const __vec4d_t v) { {}
|
||||
template<int M>
|
||||
simd4_t(const simd4_t<double,M>& v) {
|
||||
simd4 = v.v4();
|
||||
}
|
||||
simd4_t(const __m256d& v) {
|
||||
simd4 = v;
|
||||
}
|
||||
|
||||
inline const __m256d (&v4(void) const) {
|
||||
return simd4;
|
||||
}
|
||||
inline __m256d (&v4(void)) {
|
||||
return simd4;
|
||||
}
|
||||
|
||||
inline const double (&ptr(void) const)[N] {
|
||||
return vec;
|
||||
}
|
||||
inline double (&ptr(void))[N] {
|
||||
return vec;
|
||||
}
|
||||
|
||||
inline operator const double*(void) const {
|
||||
return vec;
|
||||
}
|
||||
inline operator double*(void) {
|
||||
return vec;
|
||||
}
|
||||
|
||||
inline simd4_t<double,N>& operator+=(double d) {
|
||||
return operator+=(simd4_t<double,N>(d));
|
||||
}
|
||||
template<int M>
|
||||
inline simd4_t<double,N>& operator+=(const simd4_t<double,M>& v) {
|
||||
simd4 = _mm256_add_pd(simd4, v.v4());
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline simd4_t<double,N>& operator-=(double d) {
|
||||
return operator-=(simd4_t<double,N>(d));
|
||||
}
|
||||
template<int M>
|
||||
inline simd4_t<double,N>& operator-=(const simd4_t<double,M>& v) {
|
||||
simd4 = _mm256_sub_pd(simd4, v.v4());
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline simd4_t<double,N>& operator*=(double d) {
|
||||
return operator*=(simd4_t<double,N>(d));
|
||||
}
|
||||
template<int M>
|
||||
inline simd4_t<double,N>& operator*=(const simd4_t<double,M>& v) {
|
||||
simd4 = _mm256_mul_pd(simd4, v.v4());
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline simd4_t<double,N>& operator/=(double d) {
|
||||
return operator/=(simd4_t<double,4>(d));
|
||||
}
|
||||
template<int M>
|
||||
inline simd4_t<double,N>& operator/=(const simd4_t<double,M>& v) {
|
||||
simd4 = _mm256_div_pd(simd4, v.v4());
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
static const __m256d dmask2 = _mm256_load_pd((const double*)m2a64);
|
||||
static const __m256d dmask3 = _mm256_load_pd((const double*)m3a64);
|
||||
|
||||
template<>
|
||||
inline simd4_t<double,4>::simd4_t(const __vec4d_t v) {
|
||||
simd4 = _mm256_loadu_pd(v);
|
||||
}
|
||||
template<>
|
||||
inline simd4_t<double,3>::simd4_t(const __vec4d_t v) {
|
||||
simd4 = _mm256_and_pd(_mm256_loadu_pd(v), dmask3);
|
||||
}
|
||||
template<>
|
||||
inline simd4_t<double,2>::simd4_t(const __vec4d_t v) {
|
||||
simd4 = _mm256_and_pd(_mm256_loadu_pd(v), dmask2);
|
||||
}
|
||||
template<>
|
||||
inline simd4_t<double,4>::simd4_t(double d) {
|
||||
simd4 = _mm256_set1_pd(d);
|
||||
}
|
||||
template<>
|
||||
inline simd4_t<double,3>::simd4_t(double d) {
|
||||
simd4 = _mm256_and_pd(_mm256_set1_pd(d), dmask3);
|
||||
}
|
||||
template<>
|
||||
inline simd4_t<double,2>::simd4_t(double d) {
|
||||
simd4 = _mm256_and_pd(_mm256_set1_pd(d), dmask2);
|
||||
}
|
||||
|
||||
namespace simd4
|
||||
{
|
||||
// http://berenger.eu/blog/sseavxsimd-horizontal-sum-sum-simd-vector-intrinsic/
|
||||
inline static float hsum_pd_avx(__m256d v) {
|
||||
const float64x4_t valupper = _mm256_extractf128_pd(v, 1);
|
||||
const float64x4_t vallower = _mm256_castpd256_pd128(v);
|
||||
_mm256_zeroupper();
|
||||
const float64x4_t valval = _mm_add_pd(valupper, vallower);
|
||||
const float64x4_t sums = _mm_add_pd(_mm_permute_pd(valval,1), valval);
|
||||
return _mm_cvtsd_f64(sums);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline double magnitude2(simd4_t<double,4> v) {
|
||||
return hsum_pd_avx(_mm256_mul_pd(v.v4(),v.v4()));
|
||||
}
|
||||
|
||||
template<>
|
||||
inline double dot(simd4_t<double,4> v1, const simd4_t<double,4>& v2) {
|
||||
return hsum_pd_avx(_mm256_mul_pd(v1.v4(),v2.v4()));
|
||||
}
|
||||
|
||||
template<>
|
||||
inline simd4_t<double,3> cross(const simd4_t<double,3>& v1, const simd4_t<double,3>& v2)
|
||||
{
|
||||
// https://gist.github.com/L2Program/219e07581e69110e7942
|
||||
__m256d v41 = v1.v4(), v42 = v2.v4();
|
||||
return _mm256_sub_pd(
|
||||
_mm256_mul_pd(
|
||||
_mm256_permute4x64_pd(v41,_MM_SHUFFLE(3, 0, 2, 1)),
|
||||
_mm256_permute4x64_pd(v42,_MM_SHUFFLE(3, 1, 0, 2))),
|
||||
_mm256_mul_pd(
|
||||
_mm256_permute4x64_pd(v41,_MM_SHUFFLE(3, 1, 0, 2)),
|
||||
_mm256_permute4x64_pd(v42,_MM_SHUFFLE(3, 0, 2, 1))));
|
||||
}
|
||||
|
||||
template<int N>
|
||||
inline simd4_t<double,N> min(simd4_t<double,N> v1, const simd4_t<double,N>& v2) {
|
||||
v1 = _mm256_min_pd(v1.v4(), v2.v4());
|
||||
return v1;
|
||||
}
|
||||
|
||||
template<int N>
|
||||
inline simd4_t<double,N> max(simd4_t<double,N> v1, const simd4_t<double,N>& v2) {
|
||||
v1 = _mm256_max_pd(v1.v4(), v2.v4());
|
||||
return v1;
|
||||
}
|
||||
|
||||
template<int N>
|
||||
inline simd4_t<double,N>abs(simd4_t<double,N> v) {
|
||||
static const __m256d sign_mask = _mm256_set1_pd(-0.); // -0. = 1 << 63
|
||||
v = _mm256_andnot_pd(sign_mask, v.v4());
|
||||
return v;
|
||||
}
|
||||
|
||||
} /* namespace simd4 */
|
||||
#endif
|
||||
|
||||
|
||||
template<int N>
|
||||
class simd4_t<int,N>
|
||||
{
|
||||
private:
|
||||
typedef int __vec4i_t[N];
|
||||
|
||||
union alignas(16) {
|
||||
int32x4_t simd4;
|
||||
__vec4i_t vec;
|
||||
};
|
||||
|
||||
public:
|
||||
simd4_t(void) {}
|
||||
simd4_t(int i) {}
|
||||
simd4_t(int x, int y) : simd4_t(x,y,0,0) {}
|
||||
simd4_t(int x, int y, int z) : simd4_t(x,y,z,0) {}
|
||||
simd4_t(int x, int y, int z, int w) {
|
||||
alignas(16) int32_t data[4] = { x, y, z, w };
|
||||
simd4 = vld1q_s32(data);
|
||||
}
|
||||
simd4_t(const __vec4i_t v) {}
|
||||
template<int M>
|
||||
simd4_t(const simd4_t<int,M>& v) {
|
||||
simd4 = v.v4();
|
||||
}
|
||||
simd4_t(const int32x4_t& v) {
|
||||
simd4 = v;
|
||||
}
|
||||
|
||||
inline int32x4_t (&v4(void)) {
|
||||
return simd4;
|
||||
}
|
||||
|
||||
inline const int32x4_t (&v4(void) const) {
|
||||
return simd4;
|
||||
}
|
||||
|
||||
inline const int (&ptr(void) const)[N] {
|
||||
return vec;
|
||||
}
|
||||
|
||||
inline int (&ptr(void))[N] {
|
||||
return vec;
|
||||
}
|
||||
|
||||
inline operator const int*(void) const {
|
||||
return vec;
|
||||
}
|
||||
|
||||
inline operator int*(void) {
|
||||
return vec;
|
||||
}
|
||||
|
||||
inline simd4_t<int,N>& operator+=(int i) {
|
||||
return operator+=(simd4_t<int,N>(i));
|
||||
}
|
||||
template<int M>
|
||||
inline simd4_t<int,N>& operator+=(const simd4_t<int,M>& v) {
|
||||
simd4 = vaddq_s32(simd4, v.v4());
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline simd4_t<int,N>& operator-=(int i) {
|
||||
return operator-=(simd4_t<int,N>(i));
|
||||
}
|
||||
template<int M>
|
||||
inline simd4_t<int,N>& operator-=(const simd4_t<int,M>& v) {
|
||||
simd4 = vsubq_s32(simd4, v.v4());
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline simd4_t<int,N>& operator*=(int i) {
|
||||
return operator*=(simd4_t<int,N>(i));
|
||||
}
|
||||
template<int M>
|
||||
inline simd4_t<int,N>& operator*=(const simd4_t<int,M>& v) {
|
||||
return operator*=(v.v4());
|
||||
}
|
||||
inline simd4_t<int,N>& operator*=(const int32x4_t& v) {
|
||||
simd4 = vmulq_s32(simd4, v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline simd4_t<int,N>& operator/=(int s) {
|
||||
return operator/=(simd4_t<int,4>(s));
|
||||
}
|
||||
template<int M>
|
||||
inline simd4_t<int,N>& operator/=(const simd4_t<int,M>& v) {
|
||||
for (int i=0; i<N; ++i) {
|
||||
vec[i] /= v[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
static const int32x4_t imask2 = vld1q_s32((int32_t*)m2a32);
|
||||
static const int32x4_t imask3 = vld1q_s32((int32_t*)m3a32);
|
||||
|
||||
template<>
|
||||
inline simd4_t<int,4>::simd4_t(const __vec4i_t v) {
|
||||
simd4 = vld1q_s32(v);
|
||||
}
|
||||
template<>
|
||||
inline simd4_t<int,3>::simd4_t(const __vec4i_t v) {
|
||||
simd4 = vandq_s32(vld1q_s32(v), imask3);
|
||||
}
|
||||
template<>
|
||||
inline simd4_t<int,2>::simd4_t(const __vec4i_t v) {
|
||||
simd4 = vandq_s32(vld1q_s32(v), imask2);
|
||||
}
|
||||
template<>
|
||||
inline simd4_t<int,4>::simd4_t(int i) {
|
||||
simd4 = vdupq_n_s32(i);
|
||||
}
|
||||
template<>
|
||||
inline simd4_t<int,3>::simd4_t(int i) {
|
||||
simd4 = vandq_s32(vdupq_n_s32(i), imask3);
|
||||
}
|
||||
template<>
|
||||
inline simd4_t<int,2>::simd4_t(int i) {
|
||||
simd4 = vandq_s32(vdupq_n_s32(i), imask2);
|
||||
}
|
||||
|
||||
|
||||
namespace simd4
|
||||
{
|
||||
|
||||
template<int N>
|
||||
inline simd4_t<int,N> min(simd4_t<int,N> v1, const simd4_t<int,N>& v2) {
|
||||
v1 = vminq_s32(v1.v4(), v2.v4());
|
||||
return v1;
|
||||
}
|
||||
|
||||
template<int N>
|
||||
inline simd4_t<int,N> max(simd4_t<int,N> v1, const simd4_t<int,N>& v2) {
|
||||
v1 = vmaxq_s32(v1.v4(), v2.v4());
|
||||
return v1;
|
||||
}
|
||||
|
||||
} /* namespace simd4 */
|
||||
|
||||
# endif
|
||||
|
||||
#endif /* __SIMD_NEON_H__ */
|
||||
|
||||
127
simgear/math/test_sgvec4.cxx
Normal file
127
simgear/math/test_sgvec4.cxx
Normal file
@@ -0,0 +1,127 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <cmath>
|
||||
#include <cfloat>
|
||||
|
||||
#include "SGMathFwd.hxx"
|
||||
#include "SGVec2.hxx"
|
||||
#include "SGVec3.hxx"
|
||||
#include "SGVec4.hxx"
|
||||
|
||||
// set to 0 for a timing test
|
||||
#define TESTV_ACCURACY 1
|
||||
|
||||
#define N 4
|
||||
#define T double
|
||||
|
||||
#if TESTV_ACCURACY
|
||||
# define TESTF(a, b) \
|
||||
if ( std::abs(a - b) > T(1e-7) ) \
|
||||
printf("line: %i, diff: %5.4e\n", __LINE__, double(std::abs(a - b)));
|
||||
# define TESTV(a, p,q,r,s) \
|
||||
if ( std::abs(a[0] - (p)) > T(1e-7) \
|
||||
|| std::abs(a[1] - (q)) > T(1e-7) \
|
||||
|| (N > 2 && std::abs(a[2] - (r)) > T(1e-7)) \
|
||||
|| (N > 3 && std::abs(a[3] - (s)) > T(1e-7)) \
|
||||
) \
|
||||
printf("line: %i, diff: %5.4e, %5.4e, %5.4e, %5.4e\n", __LINE__, double(std::abs(a[0]-(p))), double(std::abs(a[1]-(q))), double(std::abs(a[2]-(r))), double(std::abs(a[3]-(s))));
|
||||
# define MAX 1
|
||||
|
||||
#else
|
||||
# define TESTF(a, b)
|
||||
# define TESTV(a, p,q,r,s)
|
||||
# define MAX 1000000
|
||||
#endif
|
||||
|
||||
#define _VEC(NSTR) SGVec ## NSTR
|
||||
#define VEC(N) _VEC(N)
|
||||
|
||||
int main()
|
||||
{
|
||||
T init[4] = { T(1.31), T(3.43), T(5.69), T(1.0) };
|
||||
T p[4] = { T(1.03), T(3.55), T(2.707), T(-4.01) };
|
||||
float res = 0;
|
||||
long int i;
|
||||
VEC(N)<T> q;
|
||||
|
||||
for (int i=0; i<N; i++) {
|
||||
q[i] = init[i];
|
||||
}
|
||||
|
||||
simd4_t<T,N> qq, rr(T(-2.31), T(3.43), T(-4.69), T(-1.0));
|
||||
TESTV(rr, T(-2.31), T(3.43), T(-4.69), T(-1.0));
|
||||
|
||||
qq = simd4::min(rr, simd4_t<T,N>(T(0)));
|
||||
TESTV(qq, T(-2.31), 0, T(-4.69), T(-1.00));
|
||||
|
||||
qq = simd4::max(rr, simd4_t<T,N>(0.0));
|
||||
TESTV(qq, 0, T(3.43), 0, 0);
|
||||
|
||||
qq = simd4::abs(rr);
|
||||
TESTV(qq, T(2.31), T(3.43), T(4.69), T(1.00));
|
||||
|
||||
for (i=0; i<MAX; i++)
|
||||
{
|
||||
VEC(N)<T> v(p);
|
||||
VEC(N)<T> x(v), y(v);
|
||||
T f, g;
|
||||
|
||||
TESTV(x, p[0], p[1], p[2], p[3]);
|
||||
TESTV(y, p[0], p[1], p[2], p[3]);
|
||||
|
||||
x += q;
|
||||
TESTV(x, p[0]+q[0], p[1]+q[1], p[2]+q[2], p[3]+q[3]);
|
||||
|
||||
y -= q;
|
||||
TESTV(y, p[0]-q[0], p[1]-q[1], p[2]-q[2], p[3]-q[3]);
|
||||
|
||||
v = x; x *= T(1.7);
|
||||
TESTV(x, v[0]*T(1.7), v[1]*T(1.7), v[2]*T(1.7), v[3]*T(1.7));
|
||||
|
||||
v = y; y /= T(-1.3);
|
||||
TESTV(y, v[0]/T(-1.3), v[1]/T(-1.3), v[2]/T(-1.3), v[3]/T(-1.3));
|
||||
|
||||
v = +x;
|
||||
TESTV(v, x[0], x[1], x[2], x[3]);
|
||||
|
||||
v = -x;
|
||||
TESTV(v, -x[0], -x[1], -x[2], -x[3]);
|
||||
|
||||
v = y+x;
|
||||
TESTV(v, y[0]+x[0], y[1]+x[1], y[2]+x[2], y[3]+x[3]);
|
||||
|
||||
y = v*T(1.7);
|
||||
TESTV(y, v[0]*T(1.7), v[1]*T(1.7), v[2]*T(1.7), v[3]*T(1.7));
|
||||
|
||||
y = T(1.7)*v;
|
||||
TESTV(y, v[0]*T(1.7), v[1]*T(1.7), v[2]*T(1.7), v[3]*T(1.7));
|
||||
|
||||
v = y-x;
|
||||
TESTV(v, y[0]-x[0], y[1]-x[1], y[2]-x[2], y[3]-x[3]);
|
||||
|
||||
f = norm(v); g = 0;
|
||||
for (int i=0; i<N; i++) g += v[i]*v[i];
|
||||
g = std::sqrt(g);
|
||||
TESTF(f, g);
|
||||
|
||||
x = v; v -= y;
|
||||
VEC(N)<T> t = x - y;
|
||||
f = norm(v); g = 0;
|
||||
for (int i=0; i<N; i++) g += t[i]*t[i];
|
||||
g = std::sqrt(g);
|
||||
TESTF(f, g);
|
||||
|
||||
f += dot(x, v); // g = 0;
|
||||
for (int i=0; i<N; i++) g += x[i]*v[i];
|
||||
TESTF(f, g);
|
||||
|
||||
f += dot(v, v); // g = 0;
|
||||
for (int i=0; i<N; i++) g += v[i]*v[i];
|
||||
TESTF(f, g);
|
||||
|
||||
res += f;
|
||||
}
|
||||
|
||||
printf("res: %f\n", res);
|
||||
return 0;
|
||||
}
|
||||
@@ -12,14 +12,11 @@ set(HEADERS
|
||||
sg_dir.hxx
|
||||
sg_hash.hxx
|
||||
sg_path.hxx
|
||||
sgstream.hxx
|
||||
stdint.hxx
|
||||
stopwatch.hxx
|
||||
strutils.hxx
|
||||
tabbed_values.hxx
|
||||
texcoord.hxx
|
||||
zfstream.hxx
|
||||
gzcontainerfile.hxx
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
@@ -31,12 +28,9 @@ set(SOURCES
|
||||
sg_dir.cxx
|
||||
sg_path.cxx
|
||||
sg_hash.cxx
|
||||
sgstream.cxx
|
||||
strutils.cxx
|
||||
tabbed_values.cxx
|
||||
texcoord.cxx
|
||||
zfstream.cxx
|
||||
gzcontainerfile.cxx
|
||||
)
|
||||
|
||||
if (WINDOWS)
|
||||
@@ -59,14 +53,18 @@ add_executable(test_tabbed_values tabbed_values_test.cxx)
|
||||
add_test(tabbed_values ${EXECUTABLE_OUTPUT_PATH}/test_tabbed_values)
|
||||
target_link_libraries(test_tabbed_values ${TEST_LIBS})
|
||||
|
||||
add_executable(test_streams sgstream_test.cxx )
|
||||
add_test(streams ${EXECUTABLE_OUTPUT_PATH}/test_streams)
|
||||
target_link_libraries(test_streams ${TEST_LIBS})
|
||||
add_executable(test_strutils strutils_test.cxx)
|
||||
target_link_libraries(test_strutils ${TEST_LIBS})
|
||||
add_test(strutils ${EXECUTABLE_OUTPUT_PATH}/test_strutils)
|
||||
|
||||
add_executable(test_path path_test.cxx )
|
||||
add_test(path ${EXECUTABLE_OUTPUT_PATH}/test_path)
|
||||
target_link_libraries(test_path ${TEST_LIBS})
|
||||
|
||||
add_executable(test_sg_dir sg_dir_test.cxx)
|
||||
target_link_libraries(test_sg_dir ${TEST_LIBS})
|
||||
add_test(sg_dir ${EXECUTABLE_OUTPUT_PATH}/test_sg_dir)
|
||||
|
||||
endif(ENABLE_TESTS)
|
||||
|
||||
add_boost_test(SimpleMarkdown
|
||||
@@ -79,11 +77,6 @@ add_boost_test(SVGpreserveAspectRatio
|
||||
LIBRARIES ${TEST_LIBS}
|
||||
)
|
||||
|
||||
add_boost_test(strutils
|
||||
SOURCES strutils_test.cxx
|
||||
LIBRARIES ${TEST_LIBS}
|
||||
)
|
||||
|
||||
add_boost_test(utf8tolatin1
|
||||
SOURCES utf8tolatin1_test.cxx
|
||||
LIBRARIES ${TEST_LIBS}
|
||||
|
||||
@@ -4,30 +4,18 @@
|
||||
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <simgear/misc/test_macros.hxx>
|
||||
|
||||
#define COMPARE(a, b) \
|
||||
if( std::fabs((a) - (b)) > 1e-4 ) \
|
||||
{ \
|
||||
std::cerr << "line " << __LINE__ << ": failed: "\
|
||||
<< #a << " != " << #b << " d = " << ((a) - (b)) << std::endl; \
|
||||
return 1; \
|
||||
}
|
||||
#define COMPARE(a, b) SG_CHECK_EQUAL_EP2((a), (b), 1e-4)
|
||||
|
||||
#define VERIFY(a) \
|
||||
if( !(a) ) \
|
||||
{ \
|
||||
std::cerr << "line " << __LINE__ << ": failed: "\
|
||||
<< #a << std::endl; \
|
||||
return 1; \
|
||||
}
|
||||
|
||||
using namespace simgear;
|
||||
|
||||
int main (int ac, char ** av)
|
||||
{
|
||||
CSSBorder b = CSSBorder::parse("5");
|
||||
VERIFY(b.isValid());
|
||||
VERIFY(!b.isNone());
|
||||
SG_VERIFY(b.isValid());
|
||||
SG_VERIFY(!b.isNone());
|
||||
CSSBorder::Offsets o = b.getAbsOffsets(SGRect<int>());
|
||||
COMPARE(o.t, 5);
|
||||
COMPARE(o.r, 5);
|
||||
@@ -81,8 +69,8 @@ int main (int ac, char ** av)
|
||||
COMPARE(o.r, 0);
|
||||
COMPARE(o.b, 0);
|
||||
COMPARE(o.l, 0);
|
||||
VERIFY(b.getKeyword().empty());
|
||||
VERIFY(b.isNone());
|
||||
SG_VERIFY(b.getKeyword().empty());
|
||||
SG_VERIFY(b.isNone());
|
||||
|
||||
b = CSSBorder::parse("none");
|
||||
o = b.getRelOffsets(SGRect<int>(0,0,200,200));
|
||||
@@ -90,11 +78,11 @@ int main (int ac, char ** av)
|
||||
COMPARE(o.r, 0);
|
||||
COMPARE(o.b, 0);
|
||||
COMPARE(o.l, 0);
|
||||
VERIFY(b.getKeyword().empty());
|
||||
VERIFY(b.isNone());
|
||||
SG_VERIFY(b.getKeyword().empty());
|
||||
SG_VERIFY(b.isNone());
|
||||
|
||||
CSSBorder b2;
|
||||
VERIFY(!b2.isValid());
|
||||
SG_VERIFY(!b2.isValid());
|
||||
o = b.getAbsOffsets(SGRect<int>(0,0,200,200));
|
||||
COMPARE(o.t, 0);
|
||||
COMPARE(o.r, 0);
|
||||
|
||||
@@ -12,19 +12,26 @@ using std::endl;
|
||||
#include <simgear/misc/test_macros.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/misc/sg_dir.hxx>
|
||||
#include <simgear/misc/sgstream.hxx>
|
||||
#include <simgear/io/iostreams/sgstream.hxx>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
#if defined(SG_WINDOWS)
|
||||
#include <io.h> // for _wchmod
|
||||
#else
|
||||
#include <sys/stat.h> // for chmod
|
||||
#endif
|
||||
|
||||
void test_dir()
|
||||
{
|
||||
simgear::Dir temp = simgear::Dir::tempDir("foo");
|
||||
cout << "created:" << temp.path() << endl;
|
||||
|
||||
VERIFY(temp.exists());
|
||||
VERIFY(temp.path().isDir());
|
||||
VERIFY(!temp.path().isFile());
|
||||
SG_VERIFY(temp.exists());
|
||||
SG_VERIFY(temp.path().isDir());
|
||||
SG_VERIFY(!temp.path().isFile());
|
||||
|
||||
SGPath fileInDir = temp.file("foobaz");
|
||||
VERIFY(!fileInDir.exists());
|
||||
SG_VERIFY(!fileInDir.exists());
|
||||
|
||||
if (!temp.remove(true)) {
|
||||
cout << "remove failed!" << endl;
|
||||
@@ -40,11 +47,57 @@ void test_dir()
|
||||
<< "\n - Pictures: " << SGPath::standardLocation(SGPath::PICTURES)
|
||||
<< std::endl;
|
||||
|
||||
VERIFY( !SGPath::standardLocation(SGPath::HOME ).isNull() );
|
||||
VERIFY( !SGPath::standardLocation(SGPath::DESKTOP ).isNull() );
|
||||
VERIFY( !SGPath::standardLocation(SGPath::DOWNLOADS).isNull() );
|
||||
VERIFY( !SGPath::standardLocation(SGPath::DOCUMENTS).isNull() );
|
||||
VERIFY( !SGPath::standardLocation(SGPath::PICTURES ).isNull() );
|
||||
SG_VERIFY( !SGPath::standardLocation(SGPath::HOME ).isNull() );
|
||||
SG_VERIFY( !SGPath::standardLocation(SGPath::DESKTOP ).isNull() );
|
||||
SG_VERIFY( !SGPath::standardLocation(SGPath::DOWNLOADS).isNull() );
|
||||
SG_VERIFY( !SGPath::standardLocation(SGPath::DOCUMENTS).isNull() );
|
||||
SG_VERIFY( !SGPath::standardLocation(SGPath::PICTURES ).isNull() );
|
||||
}
|
||||
|
||||
// exercise the remove + rename that occurs when upgrading an
|
||||
// aircraft package
|
||||
void test_update_dir()
|
||||
{
|
||||
SGPath p(simgear::Dir::current().path());
|
||||
p.append("test_update_dir");
|
||||
simgear::Dir pd(p);
|
||||
pd.removeChildren();
|
||||
|
||||
{
|
||||
SGPath existingFile = p / "Cessna172P" / "somefile.txt";
|
||||
existingFile.create_dir(0755);
|
||||
|
||||
sg_ofstream of(existingFile, std::ios::out);
|
||||
of << "Foobar";
|
||||
of.close();
|
||||
|
||||
SG_VERIFY(existingFile.exists());
|
||||
}
|
||||
|
||||
{
|
||||
SGPath replacementFile = p / "NewDir" / "someotherfile.txt";
|
||||
replacementFile.create_dir(0755);
|
||||
|
||||
sg_ofstream of(replacementFile, std::ios::out);
|
||||
of << "Foobar";
|
||||
of.close();
|
||||
|
||||
SG_VERIFY(replacementFile.exists());
|
||||
}
|
||||
|
||||
{
|
||||
simgear::Dir od(p / "Cessna172P");
|
||||
SG_VERIFY(od.remove(true));
|
||||
}
|
||||
|
||||
SGPath replacementDir = p / "NewDir";
|
||||
SG_VERIFY(replacementDir.rename(p / "Cessna172P"));
|
||||
|
||||
SG_VERIFY((p / "Cessna172P" / "someotherfile.txt").exists());
|
||||
SG_VERIFY(!(p / "Cessna172P" / "somefile.txt").exists());
|
||||
SG_VERIFY(!(p / "NewDir").exists());
|
||||
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "passed update test");
|
||||
}
|
||||
|
||||
SGPath::Permissions validateNone(const SGPath&)
|
||||
@@ -77,181 +130,271 @@ void test_path_dir()
|
||||
temp.remove(true);
|
||||
SGPath p = temp.path();
|
||||
|
||||
VERIFY(p.isAbsolute());
|
||||
COMPARE(p.create_dir(0755), 0);
|
||||
SG_VERIFY(p.isAbsolute());
|
||||
SG_CHECK_EQUAL(p.create_dir(0755), 0);
|
||||
|
||||
SGPath sub = p / "subA" / "subB";
|
||||
VERIFY(!sub.exists());
|
||||
SG_VERIFY(!sub.exists());
|
||||
|
||||
SGPath subFile = sub / "fileABC.txt";
|
||||
COMPARE(subFile.create_dir(0755), 0);
|
||||
VERIFY(!subFile.exists());
|
||||
SG_CHECK_EQUAL(subFile.create_dir(0755), 0);
|
||||
SG_VERIFY(!subFile.exists());
|
||||
|
||||
sub.set_cached(false);
|
||||
VERIFY(sub.exists());
|
||||
VERIFY(sub.isDir());
|
||||
SG_VERIFY(sub.exists());
|
||||
SG_VERIFY(sub.isDir());
|
||||
|
||||
SGPath sub2 = p / "subA" / "fileA";
|
||||
{
|
||||
sg_ofstream os(sub2);
|
||||
VERIFY(os.is_open());
|
||||
SG_VERIFY(os.is_open());
|
||||
for (int i = 0; i < 50; ++i) {
|
||||
os << "ABCD" << endl;
|
||||
}
|
||||
}
|
||||
VERIFY(sub2.isFile());
|
||||
COMPARE(sub2.sizeInBytes(), 250);
|
||||
SG_VERIFY(sub2.isFile());
|
||||
SG_CHECK_EQUAL(sub2.sizeInBytes(), 250);
|
||||
|
||||
SGPath sub3 = p / "subß" / "file𝕽";
|
||||
sub3.create_dir(0755);
|
||||
|
||||
{
|
||||
sg_ofstream os(sub3);
|
||||
VERIFY(os.is_open());
|
||||
SG_VERIFY(os.is_open());
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
os << "EFGH" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
sub3.set_cached(false);
|
||||
VERIFY(sub3.exists());
|
||||
COMPARE(sub3.sizeInBytes(), 100);
|
||||
COMPARE(sub3.file(), "file𝕽");
|
||||
SG_VERIFY(sub3.exists());
|
||||
SG_CHECK_EQUAL(sub3.sizeInBytes(), 100);
|
||||
SG_CHECK_EQUAL(sub3.file(), "file𝕽");
|
||||
|
||||
simgear::Dir subD(p / "subA");
|
||||
simgear::PathList dirChildren = subD.children(simgear::Dir::TYPE_DIR | simgear::Dir::NO_DOT_OR_DOTDOT);
|
||||
COMPARE(dirChildren.size(), 1);
|
||||
COMPARE(dirChildren[0], subD.path() / "subB");
|
||||
SG_CHECK_EQUAL(dirChildren.size(), 1);
|
||||
SG_CHECK_EQUAL(dirChildren[0], subD.path() / "subB");
|
||||
|
||||
simgear::PathList fileChildren = subD.children(simgear::Dir::TYPE_FILE | simgear::Dir::NO_DOT_OR_DOTDOT);
|
||||
COMPARE(fileChildren.size(), 1);
|
||||
COMPARE(fileChildren[0], subD.path() / "fileA");
|
||||
SG_CHECK_EQUAL(fileChildren.size(), 1);
|
||||
SG_CHECK_EQUAL(fileChildren[0], subD.path() / "fileA");
|
||||
|
||||
simgear::Dir subS(sub3.dirPath());
|
||||
fileChildren = subS.children(simgear::Dir::TYPE_FILE | simgear::Dir::NO_DOT_OR_DOTDOT);
|
||||
COMPARE(fileChildren.size(), 1);
|
||||
COMPARE(fileChildren[0], subS.path() / "file𝕽");
|
||||
SG_CHECK_EQUAL(fileChildren.size(), 1);
|
||||
SG_CHECK_EQUAL(fileChildren[0], subS.path() / "file𝕽");
|
||||
|
||||
}
|
||||
|
||||
void test_permissions()
|
||||
{
|
||||
// start with an empty dir
|
||||
SGPath p = simgear::Dir::current().path() / "path_test_permissions";
|
||||
simgear::Dir pd(p);
|
||||
if (pd.exists()) {
|
||||
pd.removeChildren();
|
||||
} else {
|
||||
pd.create(0700);
|
||||
}
|
||||
|
||||
// windows doesn't seem to actualy create a read-only directory, so this
|
||||
// test fails in strange ways there.
|
||||
#if !defined(SG_WINDOWS)
|
||||
SGPath fileInRO = p / "read-only" / "fileA";
|
||||
SG_CHECK_EQUAL(fileInRO.create_dir(0500), 0);
|
||||
|
||||
SG_VERIFY(!fileInRO.exists());
|
||||
SG_VERIFY(!fileInRO.canWrite());
|
||||
|
||||
fileInRO.setPermissionChecker(&validateRead);
|
||||
SG_CHECK_EQUAL(fileInRO.canRead(), false);
|
||||
SG_CHECK_EQUAL(fileInRO.canWrite(), false);
|
||||
|
||||
fileInRO.setPermissionChecker(&validateWrite);
|
||||
SG_CHECK_EQUAL(fileInRO.canRead(), false);
|
||||
SG_CHECK_EQUAL(fileInRO.canWrite(), false);
|
||||
#endif
|
||||
|
||||
/////////
|
||||
SGPath fileInRW = p / "read-write" / "fileA";
|
||||
|
||||
fileInRW.setPermissionChecker(&validateRead);
|
||||
SG_CHECK_EQUAL(fileInRW.canRead(), false);
|
||||
SG_CHECK_EQUAL(fileInRW.canWrite(), false);
|
||||
SG_CHECK_EQUAL(fileInRW.create_dir(0700), -3); // not permitted
|
||||
|
||||
fileInRW.setPermissionChecker(nullptr);
|
||||
SG_CHECK_EQUAL(fileInRW.create_dir(0700), 0);
|
||||
|
||||
SG_VERIFY(!fileInRW.exists());
|
||||
SG_VERIFY(fileInRW.canWrite());
|
||||
|
||||
fileInRW.setPermissionChecker(&validateRead);
|
||||
SG_CHECK_EQUAL(fileInRW.canRead(), false);
|
||||
SG_CHECK_EQUAL(fileInRW.canWrite(), false);
|
||||
|
||||
fileInRW.setPermissionChecker(&validateWrite);
|
||||
SG_CHECK_EQUAL(fileInRW.canRead(), false);
|
||||
SG_CHECK_EQUAL(fileInRW.canWrite(), true);
|
||||
|
||||
|
||||
// create the file
|
||||
{
|
||||
sg_ofstream os(fileInRW);
|
||||
SG_VERIFY(os.is_open());
|
||||
os << "nonense" << endl;
|
||||
}
|
||||
|
||||
// should now be readable + writeable
|
||||
fileInRW.set_cached(false);
|
||||
fileInRW.setPermissionChecker(nullptr);
|
||||
SG_VERIFY(fileInRW.exists());
|
||||
SG_VERIFY(fileInRW.canWrite());
|
||||
SG_VERIFY(fileInRW.canRead());
|
||||
|
||||
fileInRW.setPermissionChecker(&validateRead);
|
||||
SG_CHECK_EQUAL(fileInRW.canRead(), true);
|
||||
SG_CHECK_EQUAL(fileInRW.canWrite(), false);
|
||||
|
||||
fileInRW.setPermissionChecker(&validateWrite);
|
||||
SG_CHECK_EQUAL(fileInRW.canRead(), false);
|
||||
SG_CHECK_EQUAL(fileInRW.canWrite(), true);
|
||||
|
||||
// mark the file as read-only
|
||||
#if defined(SG_WINDOWS)
|
||||
std::wstring wp = fileInRW.wstr();
|
||||
_wchmod(wp.c_str(), _S_IREAD);
|
||||
#else
|
||||
::chmod(fileInRW.c_str(), 0400);
|
||||
#endif
|
||||
|
||||
fileInRW.set_cached(false);
|
||||
fileInRW.setPermissionChecker(nullptr);
|
||||
|
||||
SG_VERIFY(fileInRW.exists());
|
||||
SG_CHECK_EQUAL(fileInRW.canWrite(), false);
|
||||
SG_VERIFY(fileInRW.canRead());
|
||||
|
||||
fileInRW.setPermissionChecker(&validateRead);
|
||||
SG_CHECK_EQUAL(fileInRW.canRead(), true);
|
||||
SG_CHECK_EQUAL(fileInRW.canWrite(), false);
|
||||
|
||||
fileInRW.setPermissionChecker(&validateWrite);
|
||||
SG_CHECK_EQUAL(fileInRW.canRead(), false);
|
||||
SG_CHECK_EQUAL(fileInRW.canWrite(), false);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
SGPath pa;
|
||||
VERIFY(pa.isNull());
|
||||
COMPARE(pa.exists(), false);
|
||||
SG_VERIFY(pa.isNull());
|
||||
SG_CHECK_EQUAL(pa.exists(), false);
|
||||
|
||||
// test basic parsing
|
||||
SGPath pb("/Foo/bar/something.png");
|
||||
COMPARE(pb.utf8Str(), std::string("/Foo/bar/something.png"));
|
||||
COMPARE(pb.local8BitStr(), std::string("/Foo/bar/something.png"));
|
||||
COMPARE(pb.dir(), std::string("/Foo/bar"));
|
||||
COMPARE(pb.file(), std::string("something.png"));
|
||||
COMPARE(pb.base(), std::string("/Foo/bar/something"));
|
||||
COMPARE(pb.file_base(), std::string("something"));
|
||||
COMPARE(pb.extension(), std::string("png"));
|
||||
VERIFY(pb.isAbsolute());
|
||||
VERIFY(!pb.isRelative());
|
||||
SG_CHECK_EQUAL(pb.utf8Str(), std::string("/Foo/bar/something.png"));
|
||||
SG_CHECK_EQUAL(pb.local8BitStr(), std::string("/Foo/bar/something.png"));
|
||||
SG_CHECK_EQUAL(pb.dir(), std::string("/Foo/bar"));
|
||||
SG_CHECK_EQUAL(pb.file(), std::string("something.png"));
|
||||
SG_CHECK_EQUAL(pb.base(), std::string("/Foo/bar/something"));
|
||||
SG_CHECK_EQUAL(pb.file_base(), std::string("something"));
|
||||
SG_CHECK_EQUAL(pb.extension(), std::string("png"));
|
||||
SG_VERIFY(pb.isAbsolute());
|
||||
SG_VERIFY(!pb.isRelative());
|
||||
|
||||
// relative paths
|
||||
SGPath ra("where/to/begin.txt");
|
||||
COMPARE(ra.utf8Str(), std::string("where/to/begin.txt"));
|
||||
COMPARE(ra.local8BitStr(), std::string("where/to/begin.txt"));
|
||||
COMPARE(ra.dir(), std::string("where/to"));
|
||||
COMPARE(ra.file(), std::string("begin.txt"));
|
||||
COMPARE(ra.file_base(), std::string("begin"));
|
||||
VERIFY(!ra.isAbsolute());
|
||||
VERIFY(ra.isRelative());
|
||||
SG_CHECK_EQUAL(ra.utf8Str(), std::string("where/to/begin.txt"));
|
||||
SG_CHECK_EQUAL(ra.local8BitStr(), std::string("where/to/begin.txt"));
|
||||
SG_CHECK_EQUAL(ra.dir(), std::string("where/to"));
|
||||
SG_CHECK_EQUAL(ra.file(), std::string("begin.txt"));
|
||||
SG_CHECK_EQUAL(ra.file_base(), std::string("begin"));
|
||||
SG_VERIFY(!ra.isAbsolute());
|
||||
SG_VERIFY(ra.isRelative());
|
||||
|
||||
// dots in paths / missing extensions
|
||||
SGPath pk("/Foo/bar.dot/thing");
|
||||
COMPARE(pk.dir(), std::string("/Foo/bar.dot"));
|
||||
COMPARE(pk.file(), std::string("thing"));
|
||||
COMPARE(pk.base(), std::string("/Foo/bar.dot/thing"));
|
||||
COMPARE(pk.file_base(), std::string("thing"));
|
||||
COMPARE(pk.extension(), std::string());
|
||||
SG_CHECK_EQUAL(pk.dir(), std::string("/Foo/bar.dot"));
|
||||
SG_CHECK_EQUAL(pk.file(), std::string("thing"));
|
||||
SG_CHECK_EQUAL(pk.base(), std::string("/Foo/bar.dot/thing"));
|
||||
SG_CHECK_EQUAL(pk.file_base(), std::string("thing"));
|
||||
SG_CHECK_EQUAL(pk.extension(), std::string());
|
||||
|
||||
// multiple file extensions
|
||||
SGPath pj("/Foo/zot.dot/thing.tar.gz");
|
||||
COMPARE(pj.dir(), std::string("/Foo/zot.dot"));
|
||||
COMPARE(pj.file(), std::string("thing.tar.gz"));
|
||||
COMPARE(pj.base(), std::string("/Foo/zot.dot/thing.tar"));
|
||||
COMPARE(pj.file_base(), std::string("thing"));
|
||||
COMPARE(pj.extension(), std::string("gz"));
|
||||
COMPARE(pj.complete_lower_extension(), std::string("tar.gz"));
|
||||
SG_CHECK_EQUAL(pj.dir(), std::string("/Foo/zot.dot"));
|
||||
SG_CHECK_EQUAL(pj.file(), std::string("thing.tar.gz"));
|
||||
SG_CHECK_EQUAL(pj.base(), std::string("/Foo/zot.dot/thing.tar"));
|
||||
SG_CHECK_EQUAL(pj.file_base(), std::string("thing"));
|
||||
SG_CHECK_EQUAL(pj.extension(), std::string("gz"));
|
||||
SG_CHECK_EQUAL(pj.complete_lower_extension(), std::string("tar.gz"));
|
||||
|
||||
// path fixing
|
||||
SGPath rd("where\\to\\begin.txt");
|
||||
COMPARE(rd.utf8Str(), std::string("where/to/begin.txt"));
|
||||
SG_CHECK_EQUAL(rd.utf8Str(), std::string("where/to/begin.txt"));
|
||||
|
||||
// test modification
|
||||
// append
|
||||
SGPath d1("/usr/local");
|
||||
SGPath pc = d1;
|
||||
COMPARE(pc.utf8Str(), std::string("/usr/local"));
|
||||
SG_CHECK_EQUAL(pc.utf8Str(), std::string("/usr/local"));
|
||||
pc.append("include");
|
||||
|
||||
COMPARE(pc.utf8Str(), std::string("/usr/local/include"));
|
||||
COMPARE(pc.file(), std::string("include"));
|
||||
SG_CHECK_EQUAL(pc.utf8Str(), std::string("/usr/local/include"));
|
||||
SG_CHECK_EQUAL(pc.file(), std::string("include"));
|
||||
|
||||
// concat
|
||||
SGPath pd = pb;
|
||||
pd.concat("-1");
|
||||
COMPARE(pd.utf8Str(), std::string("/Foo/bar/something.png-1"));
|
||||
SG_CHECK_EQUAL(pd.utf8Str(), std::string("/Foo/bar/something.png-1"));
|
||||
|
||||
// create with relative path
|
||||
SGPath rb(d1, "include/foo");
|
||||
COMPARE(rb.utf8Str(), std::string("/usr/local/include/foo"));
|
||||
VERIFY(rb.isAbsolute());
|
||||
SG_CHECK_EQUAL(rb.utf8Str(), std::string("/usr/local/include/foo"));
|
||||
SG_VERIFY(rb.isAbsolute());
|
||||
|
||||
// lower-casing of file extensions
|
||||
SGPath extA("FOO.ZIP");
|
||||
COMPARE(extA.base(), "FOO");
|
||||
COMPARE(extA.extension(), "ZIP");
|
||||
COMPARE(extA.lower_extension(), "zip");
|
||||
COMPARE(extA.complete_lower_extension(), "zip");
|
||||
SG_CHECK_EQUAL(extA.base(), "FOO");
|
||||
SG_CHECK_EQUAL(extA.extension(), "ZIP");
|
||||
SG_CHECK_EQUAL(extA.lower_extension(), "zip");
|
||||
SG_CHECK_EQUAL(extA.complete_lower_extension(), "zip");
|
||||
|
||||
SGPath extB("BAH/FOO.HTML.GZ");
|
||||
COMPARE(extB.extension(), "GZ");
|
||||
COMPARE(extB.base(), "BAH/FOO.HTML");
|
||||
COMPARE(extB.lower_extension(), "gz");
|
||||
COMPARE(extB.complete_lower_extension(), "html.gz");
|
||||
SG_CHECK_EQUAL(extB.extension(), "GZ");
|
||||
SG_CHECK_EQUAL(extB.base(), "BAH/FOO.HTML");
|
||||
SG_CHECK_EQUAL(extB.lower_extension(), "gz");
|
||||
SG_CHECK_EQUAL(extB.complete_lower_extension(), "html.gz");
|
||||
#ifdef _WIN32
|
||||
SGPath winAbs("C:\\Windows\\System32");
|
||||
COMPARE(winAbs.local8BitStr(), std::string("C:/Windows/System32"));
|
||||
SG_CHECK_EQUAL(winAbs.local8BitStr(), std::string("C:/Windows/System32"));
|
||||
|
||||
#endif
|
||||
|
||||
// paths with only the file components
|
||||
SGPath pf("something.txt.gz");
|
||||
COMPARE(pf.base(), "something.txt");
|
||||
COMPARE(pf.file(), "something.txt.gz");
|
||||
COMPARE(pf.dir(), "");
|
||||
COMPARE(pf.lower_extension(), "gz");
|
||||
COMPARE(pf.complete_lower_extension(), "txt.gz");
|
||||
SG_CHECK_EQUAL(pf.base(), "something.txt");
|
||||
SG_CHECK_EQUAL(pf.file(), "something.txt.gz");
|
||||
SG_CHECK_EQUAL(pf.dir(), "");
|
||||
SG_CHECK_EQUAL(pf.lower_extension(), "gz");
|
||||
SG_CHECK_EQUAL(pf.complete_lower_extension(), "txt.gz");
|
||||
|
||||
COMPARE(pf.canRead(), true);
|
||||
COMPARE(pf.canWrite(), true);
|
||||
SG_CHECK_EQUAL(pf.canRead(), false);
|
||||
SG_CHECK_EQUAL(pf.canWrite(), false);
|
||||
|
||||
SGPath pp(&validateNone);
|
||||
COMPARE(pp.canRead(), false);
|
||||
COMPARE(pp.canWrite(), false);
|
||||
|
||||
pp.append("./test-dir/file.txt");
|
||||
COMPARE(pp.create_dir(0700), -3);
|
||||
|
||||
pp.setPermissionChecker(&validateRead);
|
||||
COMPARE(pp.canRead(), true);
|
||||
COMPARE(pp.canWrite(), false);
|
||||
COMPARE(pp.create_dir(0700), -3);
|
||||
|
||||
pp.setPermissionChecker(&validateWrite);
|
||||
COMPARE(pp.canRead(), false);
|
||||
COMPARE(pp.canWrite(), true);
|
||||
SG_CHECK_EQUAL(pp.canRead(), false);
|
||||
SG_CHECK_EQUAL(pp.canWrite(), false);
|
||||
|
||||
test_dir();
|
||||
|
||||
test_path_dir();
|
||||
|
||||
test_permissions();
|
||||
|
||||
test_update_dir();
|
||||
|
||||
cout << "all tests passed OK" << endl;
|
||||
return 0; // passed
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@
|
||||
# include <dirent.h>
|
||||
# include <sys/stat.h>
|
||||
# include <unistd.h>
|
||||
# include <errno.h>
|
||||
#endif
|
||||
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
@@ -46,6 +45,7 @@
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <cerrno>
|
||||
#include <iostream>
|
||||
#include <algorithm> // for std::sort
|
||||
|
||||
@@ -94,7 +94,7 @@ Dir Dir::current()
|
||||
#endif
|
||||
if (!buf) {
|
||||
if (errno == 2) throw sg_exception("The current directory is invalid");
|
||||
else throw sg_exception(strerror(errno));
|
||||
else throw sg_exception(simgear::strutils::error_string(errno));
|
||||
}
|
||||
|
||||
SGPath p(buf);
|
||||
@@ -118,7 +118,8 @@ Dir Dir::tempDir(const std::string& templ)
|
||||
std::string s = p.local8BitStr();
|
||||
::snprintf(buf, 1024, "%s", s.c_str());
|
||||
if (!mkdtemp(buf)) {
|
||||
SG_LOG(SG_IO, SG_WARN, "mkdtemp failed:" << strerror(errno));
|
||||
SG_LOG(SG_IO, SG_WARN,
|
||||
"mkdtemp failed: " << simgear::strutils::error_string(errno));
|
||||
return Dir();
|
||||
}
|
||||
|
||||
@@ -135,6 +136,7 @@ Dir Dir::tempDir(const std::string& templ)
|
||||
Dir t(p);
|
||||
if (!t.create(0700)) {
|
||||
SG_LOG(SG_IO, SG_WARN, "failed to create temporary directory at " << p);
|
||||
return Dir();
|
||||
}
|
||||
|
||||
return t;
|
||||
@@ -277,6 +279,11 @@ PathList Dir::children(int types, const std::string& nameFilter) const
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Dir::isNull() const
|
||||
{
|
||||
return _path.isNull();
|
||||
}
|
||||
|
||||
bool Dir::isEmpty() const
|
||||
{
|
||||
#if defined(SG_WINDOWS)
|
||||
@@ -333,7 +340,9 @@ bool Dir::create(mode_t mode)
|
||||
int err = mkdir(ps.c_str(), mode);
|
||||
#endif
|
||||
if (err) {
|
||||
SG_LOG(SG_IO, SG_WARN, "directory creation failed: (" << _path << ") " << strerror(errno) );
|
||||
SG_LOG(SG_IO, SG_WARN,
|
||||
"directory creation failed for '" << _path.utf8Str() << "': " <<
|
||||
simgear::strutils::error_string(errno));
|
||||
}
|
||||
|
||||
return (err == 0);
|
||||
@@ -341,6 +350,10 @@ bool Dir::create(mode_t mode)
|
||||
|
||||
bool Dir::removeChildren() const
|
||||
{
|
||||
if (!exists()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ok;
|
||||
PathList cs = children(NO_DOT_OR_DOTDOT | INCLUDE_HIDDEN | TYPE_FILE | TYPE_DIR);
|
||||
BOOST_FOREACH(SGPath path, cs) {
|
||||
@@ -382,7 +395,9 @@ bool Dir::remove(bool recursive)
|
||||
int err = rmdir(ps.c_str());
|
||||
#endif
|
||||
if (err) {
|
||||
SG_LOG(SG_IO, SG_WARN, "rmdir failed:" << _path << ":" << strerror(errno));
|
||||
SG_LOG(SG_IO, SG_WARN,
|
||||
"rmdir failed for '" << _path.utf8Str() << "': " <<
|
||||
simgear::strutils::error_string(errno));
|
||||
}
|
||||
return (err == 0);
|
||||
}
|
||||
|
||||
@@ -55,7 +55,8 @@ namespace simgear
|
||||
static Dir current();
|
||||
|
||||
/**
|
||||
* create a temporary directory, using the supplied name
|
||||
* Create a temporary directory, using the supplied name.
|
||||
* The return value 'd' is such that d.isNull() in case this failed.
|
||||
*/
|
||||
static Dir tempDir(const std::string& templ);
|
||||
|
||||
@@ -71,7 +72,14 @@ namespace simgear
|
||||
};
|
||||
|
||||
PathList children(int types = 0, const std::string& nameGlob = "") const;
|
||||
|
||||
|
||||
/**
|
||||
* Check if the underlying SGPath is null.
|
||||
*
|
||||
* Note: this is the case for a default-constructed Dir instance.
|
||||
*/
|
||||
bool isNull() const;
|
||||
|
||||
/**
|
||||
* test if the directory contains no children (except '.' and '..')
|
||||
*/
|
||||
|
||||
42
simgear/misc/sg_dir_test.cxx
Normal file
42
simgear/misc/sg_dir_test.cxx
Normal file
@@ -0,0 +1,42 @@
|
||||
#include <cstdlib>
|
||||
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/misc/test_macros.hxx>
|
||||
#include "sg_dir.hxx"
|
||||
|
||||
|
||||
void test_isNull()
|
||||
{
|
||||
SG_VERIFY(simgear::Dir().isNull());
|
||||
}
|
||||
|
||||
void test_setRemoveOnDestroy()
|
||||
{
|
||||
SGPath path;
|
||||
{
|
||||
simgear::Dir d = simgear::Dir::tempDir("FlightGear");
|
||||
SG_VERIFY(!d.isNull() && d.exists() && d.isEmpty());
|
||||
d.setRemoveOnDestroy();
|
||||
|
||||
path = d.path(); // keep a copy of the path
|
||||
SG_VERIFY(path.exists() && path.isDir());
|
||||
}
|
||||
|
||||
SG_VERIFY(!path.exists());
|
||||
}
|
||||
|
||||
void test_tempDir()
|
||||
{
|
||||
simgear::Dir d = simgear::Dir::tempDir("FlightGear");
|
||||
SG_VERIFY(!d.isNull() && d.exists() && d.isEmpty());
|
||||
d.remove();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
test_isNull();
|
||||
test_setRemoveOnDestroy();
|
||||
test_tempDir();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -27,7 +27,7 @@
|
||||
#include <simgear_config.h>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
#include <simgear/misc/sgstream.hxx>
|
||||
#include <simgear/io/iostreams/sgstream.hxx>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
@@ -53,9 +53,9 @@ static const char sgDirPathSep = '/';
|
||||
static const char sgDirPathSepBad = '\\';
|
||||
|
||||
#ifdef _WIN32
|
||||
static const char sgSearchPathSep = ';';
|
||||
const char SGPath::pathListSep = ';';
|
||||
#else
|
||||
static const char sgSearchPathSep = ':';
|
||||
const char SGPath::pathListSep = ':';
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -334,7 +334,7 @@ SGPath SGPath::operator/( const std::string& p ) const
|
||||
#if defined(ENABLE_OLD_PATH_API)
|
||||
//add a new path component to the existing path string
|
||||
void SGPath::add( const string& p ) {
|
||||
append( sgSearchPathSep+p );
|
||||
append( SGPath::pathListSep+p );
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -460,10 +460,11 @@ void SGPath::validate() const
|
||||
|
||||
if (path.empty()) {
|
||||
_exists = false;
|
||||
_canWrite = _canRead = false;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
#if defined(SG_WINDOWS)
|
||||
struct _stat buf ;
|
||||
bool remove_trailing = false;
|
||||
std::wstring statPath(wstr());
|
||||
@@ -473,12 +474,24 @@ void SGPath::validate() const
|
||||
|
||||
if (_wstat(statPath.c_str(), &buf ) < 0) {
|
||||
_exists = false;
|
||||
_canRead = false;
|
||||
|
||||
// check parent directory for write-ability
|
||||
std::wstring parentPath = simgear::strutils::convertUtf8ToWString(dir());
|
||||
struct _stat parentBuf;
|
||||
if (_wstat(parentPath.c_str(), &parentBuf) >= 0) {
|
||||
_canWrite = parentBuf.st_mode & _S_IWRITE;
|
||||
} else {
|
||||
_canWrite = false;
|
||||
}
|
||||
} else {
|
||||
_exists = true;
|
||||
_isFile = ((S_IFREG & buf.st_mode ) !=0);
|
||||
_isDir = ((S_IFDIR & buf.st_mode ) !=0);
|
||||
_modTime = buf.st_mtime;
|
||||
_size = buf.st_size;
|
||||
_canRead = _S_IREAD & buf.st_mode;
|
||||
_canWrite = _S_IWRITE & buf.st_mode;
|
||||
}
|
||||
|
||||
#else
|
||||
@@ -486,15 +499,35 @@ void SGPath::validate() const
|
||||
|
||||
if (stat(path.c_str(), &buf ) < 0) {
|
||||
_exists = false;
|
||||
_canRead = false;
|
||||
|
||||
// check parent directory for write-ability
|
||||
std::string parentPath = dir();
|
||||
struct stat parentBuf;
|
||||
if (stat(parentPath.c_str(), &parentBuf) >= 0) {
|
||||
_canWrite = parentBuf.st_mode & S_IWUSR;
|
||||
} else {
|
||||
_canWrite = false;
|
||||
}
|
||||
} else {
|
||||
_exists = true;
|
||||
_isFile = ((S_ISREG(buf.st_mode )) != 0);
|
||||
_isDir = ((S_ISDIR(buf.st_mode )) != 0);
|
||||
_modTime = buf.st_mtime;
|
||||
_size = buf.st_size;
|
||||
_canRead = S_IRUSR & buf.st_mode;
|
||||
_canWrite = S_IWUSR & buf.st_mode;
|
||||
}
|
||||
|
||||
#endif
|
||||
// ensure permissions are no less restrictive than what the
|
||||
// permissions checker offers
|
||||
if ( _permission_checker ) {
|
||||
Permissions p = _permission_checker(*this);
|
||||
_canRead &= p.read;
|
||||
_canWrite &= p.write;
|
||||
}
|
||||
|
||||
_cached = true;
|
||||
}
|
||||
|
||||
@@ -504,18 +537,7 @@ void SGPath::checkAccess() const
|
||||
if( _rwCached && _cacheEnabled )
|
||||
return;
|
||||
|
||||
if( _permission_checker )
|
||||
{
|
||||
Permissions p = _permission_checker(*this);
|
||||
_canRead = p.read;
|
||||
_canWrite = p.write;
|
||||
}
|
||||
else
|
||||
{
|
||||
_canRead = true;
|
||||
_canWrite = true;
|
||||
}
|
||||
|
||||
validate();
|
||||
_rwCached = true;
|
||||
}
|
||||
|
||||
@@ -555,7 +577,7 @@ bool SGPath::isFile() const
|
||||
|
||||
int SGPath::create_dir(mode_t mode)
|
||||
{
|
||||
if( !canWrite() )
|
||||
if ( !permissionsAllowsWrite() )
|
||||
{
|
||||
SG_LOG( SG_IO,
|
||||
SG_WARN, "Error creating directory for '" << *this << "'"
|
||||
@@ -644,7 +666,7 @@ string_list sgPathSplit( const string &search_path ) {
|
||||
bool done = false;
|
||||
|
||||
while ( !done ) {
|
||||
int index = tmp.find(sgSearchPathSep);
|
||||
int index = tmp.find(SGPath::pathListSep);
|
||||
if (index >= 0) {
|
||||
result.push_back( tmp.substr(0, index) );
|
||||
tmp = tmp.substr( index + 1 );
|
||||
@@ -703,7 +725,7 @@ std::string SGPath::str_native() const
|
||||
//------------------------------------------------------------------------------
|
||||
bool SGPath::remove()
|
||||
{
|
||||
if( !canWrite() )
|
||||
if( !permissionsAllowsWrite() )
|
||||
{
|
||||
SG_LOG( SG_IO, SG_WARN, "file remove failed: (" << *this << ")"
|
||||
" reason: access denied" );
|
||||
@@ -712,7 +734,15 @@ bool SGPath::remove()
|
||||
|
||||
#if defined(SG_WINDOWS)
|
||||
std::wstring ps = wstr();
|
||||
int err = _wunlink(ps.c_str());
|
||||
|
||||
// windows forbids removing a read-only file, let's try to deal
|
||||
// with that case
|
||||
int err = _wchmod(ps.c_str(), _S_IWRITE | _S_IREAD);
|
||||
if (err != 0) {
|
||||
SG_LOG(SG_IO, SG_WARN, "failed to make file writeable prior to remove:" << *this);
|
||||
} else {
|
||||
err = _wunlink(ps.c_str());
|
||||
}
|
||||
#else
|
||||
std::string ps = local8BitStr();
|
||||
int err = ::unlink(ps.c_str());
|
||||
@@ -1018,3 +1048,10 @@ std::wstring SGPath::wstr() const
|
||||
{
|
||||
return simgear::strutils::convertUtf8ToWString(path);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool SGPath::permissionsAllowsWrite() const
|
||||
{
|
||||
return _permission_checker ? _permission_checker(*this).write : true;
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,9 @@ class SGPath {
|
||||
|
||||
public:
|
||||
|
||||
// OS-dependent separator used in paths lists
|
||||
static const char pathListSep;
|
||||
|
||||
struct Permissions
|
||||
{
|
||||
bool read : 1;
|
||||
@@ -332,6 +335,8 @@ private:
|
||||
void validate() const;
|
||||
void checkAccess() const;
|
||||
|
||||
bool permissionsAllowsWrite() const;
|
||||
|
||||
std::string path;
|
||||
PermissionChecker _permission_checker;
|
||||
|
||||
|
||||
@@ -27,6 +27,10 @@
|
||||
#include <string.h> // strerror_r() and strerror_s()
|
||||
#include <errno.h>
|
||||
|
||||
#if defined(HAVE_CPP11_CODECVT)
|
||||
#include <codecvt> // new in C++11
|
||||
#endif
|
||||
|
||||
#include "strutils.hxx"
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
@@ -171,6 +175,33 @@ namespace simgear {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
string_list split_on_any_of(const std::string& str, const char* seperators)
|
||||
{
|
||||
if (seperators == nullptr || (strlen(seperators) == 0)) {
|
||||
throw sg_exception("illegal/missing seperator string");
|
||||
}
|
||||
|
||||
string_list result;
|
||||
size_t pos = 0;
|
||||
size_t startPos = str.find_first_not_of(seperators, 0);
|
||||
for(;;)
|
||||
{
|
||||
pos = str.find_first_of(seperators, startPos);
|
||||
if (pos == string::npos) {
|
||||
result.push_back(str.substr(startPos));
|
||||
break;
|
||||
}
|
||||
result.push_back(str.substr(startPos, pos - startPos));
|
||||
startPos = str.find_first_not_of(seperators, pos);
|
||||
if (startPos == string::npos) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The lstrip(), rstrip() and strip() functions are implemented
|
||||
* in do_strip() which uses an additional parameter to indicate what
|
||||
@@ -234,6 +265,35 @@ namespace simgear {
|
||||
return do_strip( s, BOTHSTRIP );
|
||||
}
|
||||
|
||||
void
|
||||
stripTrailingNewlines_inplace(string& s)
|
||||
{
|
||||
// The following (harder to read) implementation is much slower on
|
||||
// my system (g++ 6.2.1 on Debian): 11.4 vs. 3.9 seconds on
|
||||
// 50,000,000 iterations performed on a short CRLF-terminated
|
||||
// string---and it is even a bit slower (12.9 seconds) with
|
||||
// std::next(it) instead of (it+1).
|
||||
//
|
||||
// for (string::reverse_iterator it = s.rbegin();
|
||||
// it != s.rend() && (*it == '\r' || *it == '\n'); /* empty */) {
|
||||
// it = string::reverse_iterator(s.erase( (it+1).base() ));
|
||||
// }
|
||||
|
||||
// Simple and fast
|
||||
while (!s.empty() && (s.back() == '\r' || s.back() == '\n')) {
|
||||
s.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
string
|
||||
stripTrailingNewlines(const string& s)
|
||||
{
|
||||
string res = s;
|
||||
stripTrailingNewlines_inplace(res);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
string
|
||||
rpad( const string & s, string::size_type length, char c )
|
||||
{
|
||||
@@ -312,12 +372,16 @@ namespace simgear {
|
||||
return result;
|
||||
}
|
||||
|
||||
int compare_versions(const string& v1, const string& v2)
|
||||
int compare_versions(const string& v1, const string& v2, int maxComponents)
|
||||
{
|
||||
vector<string> v1parts(split(v1, "."));
|
||||
vector<string> v2parts(split(v2, "."));
|
||||
|
||||
int lastPart = std::min(v1parts.size(), v2parts.size());
|
||||
if (maxComponents > 0) {
|
||||
lastPart = std::min(lastPart, maxComponents);
|
||||
}
|
||||
|
||||
for (int part=0; part < lastPart; ++part) {
|
||||
int part1 = to_int(v1parts[part]);
|
||||
int part2 = to_int(v2parts[part]);
|
||||
@@ -400,7 +464,11 @@ std::wstring convertUtf8ToWString(const std::string& a)
|
||||
{
|
||||
#ifdef SG_WINDOWS
|
||||
return convertMultiByteToWString(CP_UTF8, a);
|
||||
#elif defined(HAVE_CPP11_CODECVT)
|
||||
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> ucs2conv;
|
||||
return ucs2conv.from_bytes(a);
|
||||
#else
|
||||
return std::wstring();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -408,8 +476,11 @@ std::string convertWStringToUtf8(const std::wstring& w)
|
||||
{
|
||||
#ifdef SG_WINDOWS
|
||||
return convertWStringToMultiByte(CP_UTF8, w);
|
||||
#elif defined(HAVE_CPP11_CODECVT)
|
||||
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> ucs2conv;
|
||||
return ucs2conv.to_bytes(w);
|
||||
#else
|
||||
|
||||
return std::string();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -70,6 +70,24 @@ namespace simgear {
|
||||
std::string rstrip( const std::string& s );
|
||||
std::string strip( const std::string& s );
|
||||
|
||||
/**
|
||||
* Return a new string with any trailing \r and \n characters removed.
|
||||
* Typically useful to clean a CR-terminated line obtained from
|
||||
* std::getline() which, upon reading CRLF (\r\n), discards the Line
|
||||
* Feed character (\n) but leaves the Carriage Return (\r) in the
|
||||
* string.
|
||||
* @param s Input string
|
||||
* @return The cleaned string
|
||||
*/
|
||||
std::string stripTrailingNewlines(const std::string& s);
|
||||
|
||||
/**
|
||||
* Strip any trailing \r and \n characters from a string.
|
||||
* Should have slightly less overhead than stripTrailingNewlines().
|
||||
* @param s Input string (modified in-place)
|
||||
*/
|
||||
void stripTrailingNewlines_inplace(std::string& s);
|
||||
|
||||
/**
|
||||
* Right-padding of a string to a given length
|
||||
* @param s String to pad
|
||||
@@ -104,6 +122,17 @@ namespace simgear {
|
||||
const char* sep = 0,
|
||||
int maxsplit = 0 );
|
||||
|
||||
/**
|
||||
* split a string on any of several characters. Commonly used to deal
|
||||
* with strings containing whitespace, newlines. To parse CSS style
|
||||
* string, use with '\n\t ,' as the seperator list.
|
||||
*
|
||||
* Note consecutive seperators will not produce empty entries in the
|
||||
* the result, i.e splitting 'a,b,,c,d' with a ',' will produce a result
|
||||
* with four entries, not five.
|
||||
*/
|
||||
string_list split_on_any_of(const std::string&, const char* seperators);
|
||||
|
||||
/**
|
||||
* create a single string by joining the elements of a list with
|
||||
* another string.
|
||||
@@ -145,8 +174,10 @@ namespace simgear {
|
||||
* any number of terms are supported.
|
||||
* @return 0 if versions match, -ve number if v1 is lower, +ve if v1
|
||||
* is greater
|
||||
* @param maxComponents is the maximum number of components to look at.
|
||||
* This can be used to ignore (say) the patch level by setting it to 2
|
||||
*/
|
||||
int compare_versions(const std::string& v1, const std::string& v2);
|
||||
int compare_versions(const std::string& v1, const std::string& v2, int maxComponents = 0);
|
||||
|
||||
/**
|
||||
* Convert a string to upper case.
|
||||
|
||||
@@ -1,89 +1,181 @@
|
||||
/// Unit tests for function inside strutils package
|
||||
#define BOOST_TEST_MODULE misc
|
||||
#include <BoostTestTargetConfig.h>
|
||||
// Unit tests for functions inside the strutils package
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h> // _set_errno() on Windows
|
||||
#include <string>
|
||||
#include <fstream> // std::ifstream
|
||||
|
||||
#include <simgear/misc/test_macros.hxx>
|
||||
#include <simgear/compiler.h>
|
||||
#include "strutils.hxx"
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace strutils = simgear::strutils;
|
||||
|
||||
BOOST_AUTO_TEST_CASE( strutils_functions )
|
||||
void test_strip()
|
||||
{
|
||||
std::string a("abcd");
|
||||
BOOST_CHECK_EQUAL(strutils::strip(a), a);
|
||||
BOOST_CHECK_EQUAL(strutils::strip(" a "), "a");
|
||||
BOOST_CHECK_EQUAL(strutils::lstrip(" a "), "a ");
|
||||
BOOST_CHECK_EQUAL(strutils::rstrip("\ta "), "\ta");
|
||||
string a("abcd");
|
||||
SG_CHECK_EQUAL(strutils::strip(a), a);
|
||||
SG_CHECK_EQUAL(strutils::strip(" a "), "a");
|
||||
SG_CHECK_EQUAL(strutils::lstrip(" a "), "a ");
|
||||
SG_CHECK_EQUAL(strutils::rstrip("\ta "), "\ta");
|
||||
|
||||
// check internal spacing is preserved
|
||||
BOOST_CHECK_EQUAL(strutils::strip("\t \na \t b\r \n "), "a \t b");
|
||||
|
||||
|
||||
BOOST_CHECK(strutils::starts_with("banana", "ban"));
|
||||
BOOST_CHECK(!strutils::starts_with("abanana", "ban"));
|
||||
BOOST_CHECK(strutils::starts_with("banana", "banana")); // pass - string starts with itself
|
||||
BOOST_CHECK(!strutils::starts_with("ban", "banana")); // fail - original string is prefix of
|
||||
|
||||
BOOST_CHECK(strutils::ends_with("banana", "ana"));
|
||||
BOOST_CHECK(strutils::ends_with("foo.text", ".text"));
|
||||
BOOST_CHECK(!strutils::ends_with("foo.text", ".html"));
|
||||
|
||||
BOOST_CHECK_EQUAL(strutils::simplify("\ta\t b \nc\n\r \r\n"), "a b c");
|
||||
BOOST_CHECK_EQUAL(strutils::simplify("The quick - brown dog!"), "The quick - brown dog!");
|
||||
BOOST_CHECK_EQUAL(strutils::simplify("\r\n \r\n \t \r"), "");
|
||||
|
||||
BOOST_CHECK_EQUAL(strutils::to_int("999"), 999);
|
||||
BOOST_CHECK_EQUAL(strutils::to_int("0000000"), 0);
|
||||
BOOST_CHECK_EQUAL(strutils::to_int("-10000"), -10000);
|
||||
|
||||
string_list la = strutils::split("zero one two three four five");
|
||||
BOOST_CHECK_EQUAL(la[2], "two");
|
||||
BOOST_CHECK_EQUAL(la[5], "five");
|
||||
BOOST_CHECK_EQUAL(la.size(), 6);
|
||||
|
||||
string_list lb = strutils::split("alpha:beta:gamma:delta", ":", 2);
|
||||
BOOST_CHECK_EQUAL(lb.size(), 3);
|
||||
BOOST_CHECK_EQUAL(lb[0], "alpha");
|
||||
BOOST_CHECK_EQUAL(lb[1], "beta");
|
||||
BOOST_CHECK_EQUAL(lb[2], "gamma:delta");
|
||||
|
||||
std::string j = strutils::join(la, "&");
|
||||
BOOST_CHECK_EQUAL(j, "zero&one&two&three&four&five");
|
||||
|
||||
BOOST_CHECK_EQUAL(strutils::unescape("\\ \\n\\t\\x41\\117a"), " \n\tAOa");
|
||||
// Check internal spacing is preserved
|
||||
SG_CHECK_EQUAL(strutils::strip("\t \na \t b\r \n "), "a \t b");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( compare_versions )
|
||||
void test_stripTrailingNewlines()
|
||||
{
|
||||
BOOST_CHECK_LT(strutils::compare_versions("1.0.12", "1.1"), 0);
|
||||
BOOST_CHECK_GT(strutils::compare_versions("1.1", "1.0.12"), 0);
|
||||
BOOST_CHECK_EQUAL(strutils::compare_versions("10.6.7", "10.6.7"), 0);
|
||||
BOOST_CHECK_LT(strutils::compare_versions("2.0", "2.0.99"), 0);
|
||||
BOOST_CHECK_EQUAL(strutils::compare_versions("99", "99"), 0);
|
||||
BOOST_CHECK_GT(strutils::compare_versions("99", "98"), 0);
|
||||
|
||||
// since we compare numerically, leasing zeros shouldn't matter
|
||||
BOOST_CHECK_EQUAL(strutils::compare_versions("0.06.7", "0.6.07"), 0);
|
||||
SG_CHECK_EQUAL(strutils::stripTrailingNewlines("\rfoobar\n\r\n\n\r\r"),
|
||||
"\rfoobar");
|
||||
SG_CHECK_EQUAL(strutils::stripTrailingNewlines("\rfoobar\r"), "\rfoobar");
|
||||
SG_CHECK_EQUAL(strutils::stripTrailingNewlines("\rfoobar"), "\rfoobar");
|
||||
SG_CHECK_EQUAL(strutils::stripTrailingNewlines(""), "");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( md5_hex )
|
||||
void test_stripTrailingNewlines_inplace()
|
||||
{
|
||||
string s = "\r\n\r\rfoo\n\r\rbar\n\r\n\r\r\n\r\r";
|
||||
strutils::stripTrailingNewlines_inplace(s);
|
||||
SG_CHECK_EQUAL(s, "\r\n\r\rfoo\n\r\rbar");
|
||||
|
||||
s = "\rfoobar\r";
|
||||
strutils::stripTrailingNewlines_inplace(s);
|
||||
SG_CHECK_EQUAL(s, "\rfoobar");
|
||||
|
||||
s = "\rfoobar";
|
||||
strutils::stripTrailingNewlines_inplace(s);
|
||||
SG_CHECK_EQUAL(s, "\rfoobar");
|
||||
|
||||
s = "";
|
||||
strutils::stripTrailingNewlines_inplace(s);
|
||||
SG_CHECK_EQUAL(s, "");
|
||||
}
|
||||
|
||||
void test_starts_with()
|
||||
{
|
||||
SG_VERIFY(strutils::starts_with("banana", "ban"));
|
||||
SG_VERIFY(!strutils::starts_with("abanana", "ban"));
|
||||
// Pass - string starts with itself
|
||||
SG_VERIFY(strutils::starts_with("banana", "banana"));
|
||||
// Fail - original string is prefix of
|
||||
SG_VERIFY(!strutils::starts_with("ban", "banana"));
|
||||
}
|
||||
|
||||
void test_ends_with()
|
||||
{
|
||||
SG_VERIFY(strutils::ends_with("banana", "ana"));
|
||||
SG_VERIFY(strutils::ends_with("foo.text", ".text"));
|
||||
SG_VERIFY(!strutils::ends_with("foo.text", ".html"));
|
||||
}
|
||||
|
||||
void test_simplify()
|
||||
{
|
||||
SG_CHECK_EQUAL(strutils::simplify("\ta\t b \nc\n\r \r\n"), "a b c");
|
||||
SG_CHECK_EQUAL(strutils::simplify("The quick - brown dog!"),
|
||||
"The quick - brown dog!");
|
||||
SG_CHECK_EQUAL(strutils::simplify("\r\n \r\n \t \r"), "");
|
||||
}
|
||||
|
||||
void test_to_int()
|
||||
{
|
||||
SG_CHECK_EQUAL(strutils::to_int("999"), 999);
|
||||
SG_CHECK_EQUAL(strutils::to_int("0000000"), 0);
|
||||
SG_CHECK_EQUAL(strutils::to_int("-10000"), -10000);
|
||||
}
|
||||
|
||||
void test_split()
|
||||
{
|
||||
string_list l = strutils::split("zero one two three four five");
|
||||
SG_CHECK_EQUAL(l[2], "two");
|
||||
SG_CHECK_EQUAL(l[5], "five");
|
||||
SG_CHECK_EQUAL(l.size(), 6);
|
||||
|
||||
string j = strutils::join(l, "&");
|
||||
SG_CHECK_EQUAL(j, "zero&one&two&three&four&five");
|
||||
|
||||
l = strutils::split("alpha:beta:gamma:delta", ":", 2);
|
||||
SG_CHECK_EQUAL(l.size(), 3);
|
||||
SG_CHECK_EQUAL(l[0], "alpha");
|
||||
SG_CHECK_EQUAL(l[1], "beta");
|
||||
SG_CHECK_EQUAL(l[2], "gamma:delta");
|
||||
|
||||
l = strutils::split("", ",");
|
||||
SG_CHECK_EQUAL(l.size(), 1);
|
||||
SG_CHECK_EQUAL(l[0], "");
|
||||
|
||||
l = strutils::split(",", ",");
|
||||
SG_CHECK_EQUAL(l.size(), 2);
|
||||
SG_CHECK_EQUAL(l[0], "");
|
||||
SG_CHECK_EQUAL(l[1], "");
|
||||
|
||||
l = strutils::split(",,", ",");
|
||||
SG_CHECK_EQUAL(l.size(), 3);
|
||||
SG_CHECK_EQUAL(l[0], "");
|
||||
SG_CHECK_EQUAL(l[1], "");
|
||||
SG_CHECK_EQUAL(l[2], "");
|
||||
|
||||
l = strutils::split(" ", ",");
|
||||
SG_CHECK_EQUAL(l.size(), 1);
|
||||
SG_CHECK_EQUAL(l[0], " ");
|
||||
|
||||
|
||||
const char* testCases[] = {
|
||||
"alpha,bravo, charlie\tdelta\n\recho, \t\r\nfoxtrot golf,\n\t\n \n",
|
||||
" alpha bravo \t charlie,,,delta echo\nfoxtrot\rgolf"
|
||||
};
|
||||
|
||||
for (const char* data: testCases) {
|
||||
l = strutils::split_on_any_of(data, "\n\t\r ,");
|
||||
SG_CHECK_EQUAL(l.size(), 7);
|
||||
SG_CHECK_EQUAL(l[0], "alpha");
|
||||
SG_CHECK_EQUAL(l[1], "bravo");
|
||||
SG_CHECK_EQUAL(l[2], "charlie");
|
||||
SG_CHECK_EQUAL(l[3], "delta");
|
||||
SG_CHECK_EQUAL(l[4], "echo");
|
||||
SG_CHECK_EQUAL(l[5], "foxtrot");
|
||||
SG_CHECK_EQUAL(l[6], "golf");
|
||||
}
|
||||
}
|
||||
|
||||
void test_unescape()
|
||||
{
|
||||
SG_CHECK_EQUAL(strutils::unescape("\\ \\n\\t\\x41\\117a"), " \n\tAOa");
|
||||
}
|
||||
|
||||
void test_compare_versions()
|
||||
{
|
||||
SG_CHECK_LT(strutils::compare_versions("1.0.12", "1.1"), 0);
|
||||
SG_CHECK_GT(strutils::compare_versions("1.1", "1.0.12"), 0);
|
||||
SG_CHECK_EQUAL(strutils::compare_versions("10.6.7", "10.6.7"), 0);
|
||||
SG_CHECK_LT(strutils::compare_versions("2.0", "2.0.99"), 0);
|
||||
SG_CHECK_EQUAL(strutils::compare_versions("99", "99"), 0);
|
||||
SG_CHECK_GT(strutils::compare_versions("99", "98"), 0);
|
||||
|
||||
// Since we compare numerically, leading zeros shouldn't matter
|
||||
SG_CHECK_EQUAL(strutils::compare_versions("0.06.7", "0.6.07"), 0);
|
||||
|
||||
|
||||
SG_CHECK_EQUAL(strutils::compare_versions("10.6.7", "10.6.8", 2), 0);
|
||||
SG_CHECK_GT(strutils::compare_versions("10.7.7", "10.6.8", 2), 0);
|
||||
SG_CHECK_EQUAL(strutils::compare_versions("10.8.7", "10.6.8", 1), 0);
|
||||
}
|
||||
|
||||
void test_md5_hex()
|
||||
{
|
||||
// hex encoding
|
||||
unsigned char raw_data[] = {0x0f, 0x1a, 0xbc, 0xd2, 0xe3, 0x45, 0x67, 0x89};
|
||||
const std::string& hex_data =
|
||||
const string& hex_data =
|
||||
strutils::encodeHex(raw_data, sizeof(raw_data)/sizeof(raw_data[0]));
|
||||
BOOST_REQUIRE_EQUAL(hex_data, "0f1abcd2e3456789");
|
||||
BOOST_REQUIRE_EQUAL(strutils::encodeHex("abcde"), "6162636465");
|
||||
SG_CHECK_EQUAL(hex_data, "0f1abcd2e3456789");
|
||||
SG_CHECK_EQUAL(strutils::encodeHex("abcde"), "6162636465");
|
||||
|
||||
// md5
|
||||
BOOST_CHECK_EQUAL(strutils::md5("test"), "098f6bcd4621d373cade4e832627b4f6");
|
||||
SG_CHECK_EQUAL(strutils::md5("test"), "098f6bcd4621d373cade4e832627b4f6");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( error_string )
|
||||
void test_error_string()
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
_set_errno(0);
|
||||
@@ -99,7 +191,25 @@ BOOST_AUTO_TEST_CASE( error_string )
|
||||
int saved_errno = errno;
|
||||
#endif
|
||||
|
||||
BOOST_CHECK(!f.is_open());
|
||||
BOOST_CHECK_NE(saved_errno, 0);
|
||||
BOOST_CHECK_GT(strutils::error_string(saved_errno).size(), 0);
|
||||
SG_VERIFY(!f.is_open());
|
||||
SG_CHECK_NE(saved_errno, 0);
|
||||
SG_CHECK_GT(strutils::error_string(saved_errno).size(), 0);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
test_strip();
|
||||
test_stripTrailingNewlines();
|
||||
test_stripTrailingNewlines_inplace();
|
||||
test_starts_with();
|
||||
test_ends_with();
|
||||
test_simplify();
|
||||
test_to_int();
|
||||
test_split();
|
||||
test_unescape();
|
||||
test_compare_versions();
|
||||
test_md5_hex();
|
||||
test_error_string();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1,38 +1,180 @@
|
||||
// -*- coding: utf-8 -*-
|
||||
|
||||
#ifndef SG_MISC_TEST_MACROS_HXX
|
||||
#define SG_MISC_TEST_MACROS_HXX
|
||||
|
||||
#include <cmath> // for fabs()
|
||||
#include <iostream>
|
||||
#include <cmath> // for std::fabs()
|
||||
|
||||
#define COMPARE(a, b) \
|
||||
if ((a) != (b)) { \
|
||||
std::cerr << "failed:" << #a << " != " << #b << std::endl; \
|
||||
std::cerr << "\tgot:'" << a << "'" << std::endl; \
|
||||
std::cerr << "\tat:" << __FILE__ << ":" << __LINE__ << std::endl; \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
#define VERIFY(a) \
|
||||
// Assertion-like test
|
||||
#define SG_VERIFY(a) \
|
||||
if (!(a)) { \
|
||||
std::cerr << "failed:" << #a << std::endl; \
|
||||
std::cerr << "\tat:" << __FILE__ << ":" << __LINE__ << std::endl; \
|
||||
std::cerr << "failed: " << #a << std::endl; \
|
||||
std::cerr << "\tat " << __FILE__ << ":" << __LINE__ << std::endl; \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
#define COMPARE_EP(a, b) \
|
||||
if (fabs(a - b) > SG_EPSILON) { \
|
||||
std::cerr << "failed with epsilon:" << #a << " != " << #b << std::endl; \
|
||||
std::cerr << "\tgot:'" << a << "'" << std::endl; \
|
||||
std::cerr << "\tat:" << __FILE__ << ":" << __LINE__ << std::endl; \
|
||||
// Internal macro (implementation detail). 'a' and 'b' don't always support
|
||||
// operator<<. This is why we use (a0) and (b0) after '<<', otherwise in such
|
||||
// cases the test couldn't be compiled, even if 'stream' is false.
|
||||
#define SG_CHECK_EQUAL0(a, b, stream, a0, b0) \
|
||||
if ( !((a) == (b)) ) { \
|
||||
std::cerr << "failed: " << #a << " == " << #b << std::endl; \
|
||||
if (stream) { \
|
||||
std::cerr << "\tgot '" << (a0) << "' and '" << (b0) << "'" \
|
||||
<< std::endl; \
|
||||
}; \
|
||||
std::cerr << "\tat " << __FILE__ << ":" << __LINE__ << std::endl; \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
#define COMPARE_EP2(a, b, ep) \
|
||||
if (fabs(a - b) > ep) { \
|
||||
std::cerr << "failed with epsilon:" << #a << " != " << #b << std::endl; \
|
||||
std::cerr << "\tgot:'" << a << "'" << std::endl; \
|
||||
std::cerr << "\tat:" << __FILE__ << ":" << __LINE__ << std::endl; \
|
||||
// Test macros for user consumption. The _NOSTREAM variant is for cases where
|
||||
// 'a' or 'b' doesn't support operator<< (or where using it would have
|
||||
// undesirable side effects).
|
||||
#define SG_CHECK_EQUAL(a, b) SG_CHECK_EQUAL0((a), (b), true, (a), (b))
|
||||
#define SG_CHECK_EQUAL_NOSTREAM(a, b) SG_CHECK_EQUAL0((a), (b), false, "", "")
|
||||
|
||||
// “Approximate equality” test (EP stands for “epsilon”)
|
||||
#define SG_CHECK_EQUAL_EP0(a, b, stream, a0, b0) \
|
||||
if (std::fabs((a) - (b)) > SG_EPSILON) { \
|
||||
std::cerr << "failed: " << #a << " ~= " << #b << std::endl; \
|
||||
if (stream) { \
|
||||
std::cerr << "\tgot '" << (a0) << "' and '" << (b0) << "'" \
|
||||
<< std::endl; \
|
||||
}; \
|
||||
std::cerr << "\tat " << __FILE__ << ":" << __LINE__ << std::endl; \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
#define SG_CHECK_EQUAL_EP(a, b) SG_CHECK_EQUAL_EP0((a), (b), true, (a), (b))
|
||||
#define SG_CHECK_EQUAL_EP_NOSTREAM(a, b) \
|
||||
SG_CHECK_EQUAL_EP0((a), (b), false, "", "")
|
||||
|
||||
// Same as SG_CHECK_EQUAL_EP*, except the “epsilon” can be chosen by the caller
|
||||
#define SG_CHECK_EQUAL_EP2_0(a, b, ep, stream, a0, b0) \
|
||||
if (std::fabs((a) - (b)) > (ep)) { \
|
||||
std::cerr << "failed: " << #a << " ~= " << #b << std::endl; \
|
||||
if (stream) { \
|
||||
std::cerr << "\tgot '" << (a0) << "' and '" << (b0) << "'" \
|
||||
<< std::endl; \
|
||||
}; \
|
||||
std::cerr << "\tat " << __FILE__ << ":" << __LINE__ << std::endl; \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
#define SG_CHECK_EQUAL_EP2(a, b, ep) \
|
||||
SG_CHECK_EQUAL_EP2_0((a), (b), ep, true, (a), (b))
|
||||
#define SG_CHECK_EQUAL_EP2_NOSTREAM(a, b) \
|
||||
SG_CHECK_EQUAL_EP2_0((a), (b), ep, false, "", "")
|
||||
|
||||
// “Non-equal” test
|
||||
#define SG_CHECK_NE0(a, b, stream, a0, b0) \
|
||||
if ( !((a) != (b)) ) { \
|
||||
std::cerr << "failed: " << #a << " != " << #b << std::endl; \
|
||||
if (stream) { \
|
||||
std::cerr << "\tgot '" << (a0) << "' and '" << (b0) << "'" \
|
||||
<< std::endl; \
|
||||
}; \
|
||||
std::cerr << "\tat " << __FILE__ << ":" << __LINE__ << std::endl; \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
#define SG_CHECK_NE(a, b) SG_CHECK_NE0((a), (b), true, (a), (b))
|
||||
#define SG_CHECK_NE_NOSTREAM(a, b) SG_CHECK_NE0((a), (b), false, "", "")
|
||||
|
||||
// “Lower than” test
|
||||
#define SG_CHECK_LT0(a, b, stream, a0, b0) \
|
||||
if ( !((a) < (b)) ) { \
|
||||
std::cerr << "failed: " << #a << " < " << #b << std::endl; \
|
||||
if (stream) { \
|
||||
std::cerr << "\tgot '" << (a0) << "' and '" << (b0) << "'" \
|
||||
<< std::endl; \
|
||||
}; \
|
||||
std::cerr << "\tat " << __FILE__ << ":" << __LINE__ << std::endl; \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
#define SG_CHECK_LT(a, b) SG_CHECK_LT0((a), (b), true, (a), (b))
|
||||
#define SG_CHECK_LT_NOSTREAM(a, b) SG_CHECK_LT0((a), (b), false, "", "")
|
||||
|
||||
// “Lower than or equal” test
|
||||
#define SG_CHECK_LE0(a, b, stream, a0, b0) \
|
||||
if ( !((a) <= (b)) ) { \
|
||||
std::cerr << "failed: " << #a << " <= " << #b << std::endl; \
|
||||
if (stream) { \
|
||||
std::cerr << "\tgot '" << (a0) << "' and '" << (b0) << "'" \
|
||||
<< std::endl; \
|
||||
}; \
|
||||
std::cerr << "\tat " << __FILE__ << ":" << __LINE__ << std::endl; \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
#define SG_CHECK_LE(a, b) SG_CHECK_LE0((a), (b), true, (a), (b))
|
||||
#define SG_CHECK_LE_NOSTREAM(a, b) SG_CHECK_LE0((a), (b), false, "", "")
|
||||
|
||||
// “Greater than” test
|
||||
#define SG_CHECK_GT0(a, b, stream, a0, b0) \
|
||||
if ( !((a) > (b)) ) { \
|
||||
std::cerr << "failed: " << #a << " > " << #b << std::endl; \
|
||||
if (stream) { \
|
||||
std::cerr << "\tgot '" << (a0) << "' and '" << (b0) << "'" \
|
||||
<< std::endl; \
|
||||
}; \
|
||||
std::cerr << "\tat " << __FILE__ << ":" << __LINE__ << std::endl; \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
#define SG_CHECK_GT(a, b) SG_CHECK_GT0((a), (b), true, (a), (b))
|
||||
#define SG_CHECK_GT_NOSTREAM(a, b) SG_CHECK_GT0((a), (b), false, "", "")
|
||||
|
||||
// “Greater than or equal” test
|
||||
#define SG_CHECK_GE0(a, b, stream, a0, b0) \
|
||||
if ( !((a) >= (b)) ) { \
|
||||
std::cerr << "failed: " << #a << " >= " << #b << std::endl; \
|
||||
if (stream) { \
|
||||
std::cerr << "\tgot '" << (a0) << "' and '" << (b0) << "'" \
|
||||
<< std::endl; \
|
||||
}; \
|
||||
std::cerr << "\tat " << __FILE__ << ":" << __LINE__ << std::endl; \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
#define SG_CHECK_GE(a, b) SG_CHECK_GE0((a), (b), true, (a), (b))
|
||||
#define SG_CHECK_GE_NOSTREAM(a, b) SG_CHECK_GE0((a), (b), false, "", "")
|
||||
|
||||
// “Is NULL” test
|
||||
#define SG_CHECK_IS_NULL0(a, stream, a0) \
|
||||
if ( !((a) == nullptr) ) { \
|
||||
std::cerr << "failed: " << #a << " == nullptr" << std::endl; \
|
||||
if (stream) { \
|
||||
std::cerr << "\tgot '" << (a0) << "'" << std::endl; \
|
||||
}; \
|
||||
std::cerr << "\tat " << __FILE__ << ":" << __LINE__ << std::endl; \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
#define SG_CHECK_IS_NULL(a) SG_CHECK_IS_NULL0((a), true, (a))
|
||||
#define SG_CHECK_IS_NULL_NOSTREAM(a) SG_CHECK_IS_NULL0((a), false, "")
|
||||
|
||||
// “Is not NULL” test
|
||||
#define SG_CHECK_IS_NOT_NULL0(a, stream, a0) \
|
||||
if ( !((a) != nullptr) ) { \
|
||||
std::cerr << "failed: " << #a << " != nullptr" << std::endl; \
|
||||
if (stream) { \
|
||||
std::cerr << "\tgot '" << (a0) << "'" << std::endl; \
|
||||
}; \
|
||||
std::cerr << "\tat " << __FILE__ << ":" << __LINE__ << std::endl; \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
#define SG_CHECK_IS_NOT_NULL(a) SG_CHECK_IS_NOT_NULL0((a), true, (a))
|
||||
#define SG_CHECK_IS_NOT_NULL_NOSTREAM(a) SG_CHECK_IS_NOT_NULL0((a), false, "")
|
||||
|
||||
// “Always failing” test
|
||||
#define SG_TEST_FAIL(msg) \
|
||||
std::cerr << "failure: " << msg; \
|
||||
exit(1);
|
||||
|
||||
|
||||
#endif // of SG_MISC_TEST_MACROS_HXX
|
||||
|
||||
@@ -1200,7 +1200,7 @@ namespace nasal
|
||||
return ctx;
|
||||
};
|
||||
|
||||
typedef std::auto_ptr<Ghost> GhostPtr;
|
||||
typedef std::unique_ptr<Ghost> GhostPtr;
|
||||
MemberMap _members;
|
||||
fallback_getter_t _fallback_getter;
|
||||
fallback_setter_t _fallback_setter;
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
#ifndef _NAREF_H
|
||||
#define _NAREF_H
|
||||
|
||||
#if (defined(__x86_64) && defined(__linux__)) || defined(__sparcv9) || \
|
||||
defined(__powerpc64__)
|
||||
#if (defined(__x86_64) && defined(__linux__))
|
||||
/* NASAL_NAN64 mode requires 64 bit pointers that only use the
|
||||
* lower 48 bits; Win64 and Irix should work with this too, but
|
||||
* lower 48 bits; x86 Win64 and Irix should work with this too, but
|
||||
* have not been tested */
|
||||
# define NASAL_NAN64
|
||||
#elif defined(__BYTE_ORDER__)
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include <simgear/io/HTTPRequest.hxx>
|
||||
#include <simgear/io/HTTPClient.hxx>
|
||||
#include <simgear/misc/sg_dir.hxx>
|
||||
#include <simgear/misc/sgstream.hxx>
|
||||
#include <simgear/io/iostreams/sgstream.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include <simgear/package/Package.hxx>
|
||||
#include <simgear/package/Root.hxx>
|
||||
|
||||
@@ -126,62 +126,144 @@ int parseTest()
|
||||
pkg::Root* root = new pkg::Root(rootPath, "8.1.12");
|
||||
pkg::CatalogRef cat = pkg::Catalog::createFromPath(root, SGPath(SRC_DIR "/catalogTest1"));
|
||||
|
||||
VERIFY(cat.valid());
|
||||
SG_VERIFY(cat.valid());
|
||||
|
||||
COMPARE(cat->id(), "org.flightgear.test.catalog1");
|
||||
COMPARE(cat->url(), "http://localhost:2000/catalogTest1/catalog.xml");
|
||||
COMPARE(cat->description(), "First test catalog");
|
||||
SG_CHECK_EQUAL(cat->id(), "org.flightgear.test.catalog1");
|
||||
SG_CHECK_EQUAL(cat->url(), "http://localhost:2000/catalogTest1/catalog.xml");
|
||||
SG_CHECK_EQUAL(cat->description(), "First test catalog");
|
||||
|
||||
// check the packages too
|
||||
COMPARE(cat->packages().size(), 4);
|
||||
SG_CHECK_EQUAL(cat->packages().size(), 4);
|
||||
|
||||
pkg::PackageRef p1 = cat->packages().front();
|
||||
COMPARE(p1->catalog(), cat.ptr());
|
||||
SG_CHECK_EQUAL(p1->catalog(), cat.ptr());
|
||||
|
||||
COMPARE(p1->id(), "alpha");
|
||||
COMPARE(p1->qualifiedId(), "org.flightgear.test.catalog1.alpha");
|
||||
COMPARE(p1->name(), "Alpha package");
|
||||
COMPARE(p1->revision(), 8);
|
||||
COMPARE(p1->fileSizeBytes(), 593);
|
||||
SG_CHECK_EQUAL(p1->id(), "alpha");
|
||||
SG_CHECK_EQUAL(p1->qualifiedId(), "org.flightgear.test.catalog1.alpha");
|
||||
SG_CHECK_EQUAL(p1->name(), "Alpha package");
|
||||
SG_CHECK_EQUAL(p1->revision(), 8);
|
||||
SG_CHECK_EQUAL(p1->fileSizeBytes(), 593);
|
||||
|
||||
|
||||
pkg::PackageRef p2 = cat->getPackageById("c172p");
|
||||
VERIFY(p2.valid());
|
||||
COMPARE(p2->qualifiedId(), "org.flightgear.test.catalog1.c172p");
|
||||
COMPARE(p2->description(), "A plane made by Cessna");
|
||||
SG_VERIFY(p2.valid());
|
||||
SG_CHECK_EQUAL(p2->qualifiedId(), "org.flightgear.test.catalog1.c172p");
|
||||
SG_CHECK_EQUAL(p2->description(), "A plane made by Cessna on Jupiter");
|
||||
|
||||
pkg::Package::PreviewVec thumbs = p2->previewsForVariant(0);
|
||||
SG_CHECK_EQUAL(thumbs.size(), 3);
|
||||
|
||||
auto index = std::find_if(thumbs.begin(), thumbs.end(), [](const pkg::Package::Preview& t)
|
||||
{ return (t.type == pkg::Package::Preview::Type::EXTERIOR); });
|
||||
SG_VERIFY(index != thumbs.end());
|
||||
SG_CHECK_EQUAL(index->path, "thumb-exterior.png");
|
||||
SG_CHECK_EQUAL(index->url, "http://foo.bar.com/thumb-exterior.png");
|
||||
SG_VERIFY(index->type == pkg::Package::Preview::Type::EXTERIOR);
|
||||
|
||||
index = std::find_if(thumbs.begin(), thumbs.end(), [](const pkg::Package::Preview& t)
|
||||
{ return (t.type == pkg::Package::Preview::Type::PANEL); });
|
||||
SG_VERIFY(index != thumbs.end());
|
||||
SG_CHECK_EQUAL(index->path, "thumb-panel.png");
|
||||
SG_CHECK_EQUAL(index->url, "http://foo.bar.com/thumb-panel.png");
|
||||
SG_VERIFY(index->type == pkg::Package::Preview::Type::PANEL);
|
||||
|
||||
// old-style thumbnails
|
||||
string_list oldThumbUrls = p2->thumbnailUrls();
|
||||
SG_CHECK_EQUAL(oldThumbUrls.size(), 1);
|
||||
SG_CHECK_EQUAL(oldThumbUrls.at(0), "http://foo.bar.com/thumb-exterior.png");
|
||||
|
||||
string_list oldThumbPaths = p2->thumbnails();
|
||||
SG_CHECK_EQUAL(oldThumbPaths.size(), 1);
|
||||
SG_CHECK_EQUAL(oldThumbPaths.at(0), "exterior.png");
|
||||
|
||||
// test variants
|
||||
try {
|
||||
p2->indexOfVariant("fofofo");
|
||||
SG_TEST_FAIL("lookup of non-existant variant did not throw");
|
||||
} catch (sg_exception& e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
unsigned int skisVariantFull = p2->indexOfVariant("org.flightgear.test.catalog1.c172p-skis");
|
||||
SG_VERIFY(skisVariantFull > 0);
|
||||
|
||||
unsigned int skisVariant = p2->indexOfVariant("c172p-skis");
|
||||
SG_VERIFY(skisVariant > 0);
|
||||
|
||||
SG_CHECK_EQUAL(skisVariant, skisVariantFull);
|
||||
|
||||
SG_CHECK_EQUAL(p2->getLocalisedProp("description", skisVariant),
|
||||
"A plane with skis");
|
||||
SG_CHECK_EQUAL(p2->getLocalisedProp("author", skisVariant),
|
||||
"Standard author");
|
||||
|
||||
unsigned int floatsVariant = p2->indexOfVariant("c172p-floats");
|
||||
SG_VERIFY(floatsVariant > 0);
|
||||
|
||||
SG_CHECK_EQUAL(p2->getLocalisedProp("description", floatsVariant),
|
||||
"A plane with floats");
|
||||
SG_CHECK_EQUAL(p2->getLocalisedProp("author", floatsVariant),
|
||||
"Floats variant author");
|
||||
|
||||
pkg::Package::PreviewVec thumbs2 = p2->previewsForVariant(skisVariant);
|
||||
SG_CHECK_EQUAL(thumbs2.size(), 2);
|
||||
|
||||
index = std::find_if(thumbs2.begin(), thumbs2.end(), [](const pkg::Package::Preview& t)
|
||||
{ return (t.type == pkg::Package::Preview::Type::EXTERIOR); });
|
||||
SG_VERIFY(index != thumbs2.end());
|
||||
SG_CHECK_EQUAL(index->path, "thumb-exterior-skis.png");
|
||||
SG_CHECK_EQUAL(index->url, "http://foo.bar.com/thumb-exterior-skis.png");
|
||||
SG_VERIFY(index->type == pkg::Package::Preview::Type::EXTERIOR);
|
||||
|
||||
|
||||
// test filtering / searching too
|
||||
string_set tags(p2->tags());
|
||||
COMPARE(tags.size(), 4);
|
||||
VERIFY(tags.find("ifr") != tags.end());
|
||||
VERIFY(tags.find("cessna") != tags.end());
|
||||
VERIFY(tags.find("jet") == tags.end());
|
||||
SG_CHECK_EQUAL(tags.size(), 4);
|
||||
SG_VERIFY(tags.find("ifr") != tags.end());
|
||||
SG_VERIFY(tags.find("cessna") != tags.end());
|
||||
SG_VERIFY(tags.find("jet") == tags.end());
|
||||
|
||||
|
||||
SGPropertyNode_ptr queryA(new SGPropertyNode);
|
||||
queryA->setStringValue("tag", "ifr");
|
||||
VERIFY(p2->matches(queryA.ptr()));
|
||||
SG_VERIFY(p2->matches(queryA.ptr()));
|
||||
|
||||
SGPropertyNode_ptr queryB(new SGPropertyNode);
|
||||
queryB->setStringValue("name", "ces");
|
||||
VERIFY(p2->matches(queryB.ptr()));
|
||||
SG_VERIFY(p2->matches(queryB.ptr()));
|
||||
|
||||
SGPropertyNode_ptr queryC(new SGPropertyNode);
|
||||
queryC->setStringValue("name", "foo");
|
||||
VERIFY(!p2->matches(queryC.ptr()));
|
||||
SG_VERIFY(!p2->matches(queryC.ptr()));
|
||||
|
||||
SGPropertyNode_ptr queryD(new SGPropertyNode);
|
||||
queryD->setIntValue("rating-FDM", 3);
|
||||
VERIFY(p2->matches(queryD.ptr()));
|
||||
SG_VERIFY(p2->matches(queryD.ptr()));
|
||||
|
||||
SGPropertyNode_ptr queryE(new SGPropertyNode);
|
||||
queryE->setIntValue("rating-model", 5);
|
||||
queryE->setStringValue("description", "cessna");
|
||||
VERIFY(p2->matches(queryE.ptr()));
|
||||
SG_VERIFY(p2->matches(queryE.ptr()));
|
||||
|
||||
{
|
||||
SGPropertyNode_ptr queryText(new SGPropertyNode);
|
||||
queryText->setStringValue("any-of/text", "jupiter");
|
||||
SG_VERIFY(p2->matches(queryText.ptr()));
|
||||
}
|
||||
|
||||
{
|
||||
SGPropertyNode_ptr queryText(new SGPropertyNode);
|
||||
queryText->setStringValue("any-of/tag", "twin-engine");
|
||||
queryText->setStringValue("any-of/tag", "ga");
|
||||
SG_VERIFY(p2->matches(queryText.ptr()));
|
||||
}
|
||||
|
||||
// match variant descriptions
|
||||
{
|
||||
SGPropertyNode_ptr queryText(new SGPropertyNode);
|
||||
queryText->setStringValue("any-of/description", "float");
|
||||
SG_VERIFY(p2->matches(queryText.ptr()));
|
||||
}
|
||||
delete root;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -207,16 +289,16 @@ void testAddCatalog(HTTP::Client* cl)
|
||||
SGPath p(rootPath);
|
||||
p.append("org.flightgear.test.catalog1");
|
||||
p.append("catalog.xml");
|
||||
VERIFY(p.exists());
|
||||
COMPARE(root->allPackages().size(), 4);
|
||||
COMPARE(root->catalogs().size(), 1);
|
||||
SG_VERIFY(p.exists());
|
||||
SG_CHECK_EQUAL(root->allPackages().size(), 4);
|
||||
SG_CHECK_EQUAL(root->catalogs().size(), 1);
|
||||
|
||||
pkg::PackageRef p1 = root->getPackageById("alpha");
|
||||
COMPARE(p1->id(), "alpha");
|
||||
SG_CHECK_EQUAL(p1->id(), "alpha");
|
||||
|
||||
pkg::PackageRef p2 = root->getPackageById("org.flightgear.test.catalog1.c172p");
|
||||
COMPARE(p2->id(), "c172p");
|
||||
COMPARE(p2->qualifiedId(), "org.flightgear.test.catalog1.c172p");
|
||||
SG_CHECK_EQUAL(p2->id(), "c172p");
|
||||
SG_CHECK_EQUAL(p2->qualifiedId(), "org.flightgear.test.catalog1.c172p");
|
||||
|
||||
}
|
||||
|
||||
@@ -237,14 +319,14 @@ void testInstallPackage(HTTP::Client* cl)
|
||||
pkg::PackageRef p1 = root->getPackageById("org.flightgear.test.catalog1.c172p");
|
||||
pkg::InstallRef ins = p1->install();
|
||||
|
||||
VERIFY(ins->isQueued());
|
||||
SG_VERIFY(ins->isQueued());
|
||||
|
||||
waitForUpdateComplete(cl, root);
|
||||
VERIFY(p1->isInstalled());
|
||||
VERIFY(p1->existingInstall() == ins);
|
||||
SG_VERIFY(p1->isInstalled());
|
||||
SG_VERIFY(p1->existingInstall() == ins);
|
||||
|
||||
pkg::PackageRef commonDeps = root->getPackageById("common-sounds");
|
||||
VERIFY(commonDeps->existingInstall());
|
||||
SG_VERIFY(commonDeps->existingInstall());
|
||||
|
||||
// verify on disk state
|
||||
SGPath p(rootPath);
|
||||
@@ -252,17 +334,17 @@ void testInstallPackage(HTTP::Client* cl)
|
||||
p.append("Aircraft");
|
||||
p.append("c172p");
|
||||
|
||||
COMPARE(p, ins->path());
|
||||
SG_CHECK_EQUAL(p, ins->path());
|
||||
|
||||
p.append("c172p-floats-set.xml");
|
||||
VERIFY(p.exists());
|
||||
SG_VERIFY(p.exists());
|
||||
|
||||
SGPath p2(rootPath);
|
||||
p2.append("org.flightgear.test.catalog1");
|
||||
p2.append("Aircraft");
|
||||
p2.append("sounds");
|
||||
p2.append("sharedfile.txt");
|
||||
VERIFY(p2.exists());
|
||||
SG_VERIFY(p2.exists());
|
||||
}
|
||||
|
||||
void testUninstall(HTTP::Client* cl)
|
||||
@@ -283,11 +365,11 @@ void testUninstall(HTTP::Client* cl)
|
||||
|
||||
waitForUpdateComplete(cl, root);
|
||||
|
||||
VERIFY(p1->isInstalled());
|
||||
SG_VERIFY(p1->isInstalled());
|
||||
|
||||
ins->uninstall();
|
||||
|
||||
VERIFY(!ins->path().exists());
|
||||
SG_VERIFY(!ins->path().exists());
|
||||
}
|
||||
|
||||
void testRemoveCatalog(HTTP::Client* cl)
|
||||
@@ -311,7 +393,7 @@ void testRemoveCatalog(HTTP::Client* cl)
|
||||
|
||||
waitForUpdateComplete(cl, root);
|
||||
|
||||
VERIFY(p1->isInstalled());
|
||||
SG_VERIFY(p1->isInstalled());
|
||||
}
|
||||
|
||||
root->removeCatalogById("org.flightgear.test.catalog1");
|
||||
@@ -319,13 +401,13 @@ void testRemoveCatalog(HTTP::Client* cl)
|
||||
|
||||
SGPath p2(rootPath);
|
||||
p2.append("org.flightgear.test.catalog1");
|
||||
VERIFY(!p2.exists());
|
||||
SG_VERIFY(!p2.exists());
|
||||
|
||||
VERIFY(root->allPackages().empty());
|
||||
VERIFY(root->catalogs().empty());
|
||||
SG_VERIFY(root->allPackages().empty());
|
||||
SG_VERIFY(root->catalogs().empty());
|
||||
|
||||
pkg::CatalogRef c = root->getCatalogById("org.flightgear.test.catalog1");
|
||||
COMPARE(c, pkg::CatalogRef());
|
||||
SG_CHECK_EQUAL(c, pkg::CatalogRef());
|
||||
}
|
||||
|
||||
template <class T>
|
||||
@@ -359,37 +441,39 @@ void testRefreshCatalog(HTTP::Client* cl)
|
||||
|
||||
waitForUpdateComplete(cl, root);
|
||||
|
||||
VERIFY(p1->isInstalled());
|
||||
VERIFY(p2->isInstalled());
|
||||
SG_VERIFY(p1->isInstalled());
|
||||
SG_VERIFY(p2->isInstalled());
|
||||
}
|
||||
|
||||
VERIFY(root->packagesNeedingUpdate().empty());
|
||||
SG_VERIFY(root->packagesNeedingUpdate().empty());
|
||||
|
||||
global_catalogVersion = 2;
|
||||
|
||||
VERIFY(!cl->hasActiveRequests());
|
||||
SG_VERIFY(!cl->hasActiveRequests());
|
||||
root->refresh();
|
||||
|
||||
// should be a no-op due to catalog age testing
|
||||
VERIFY(!cl->hasActiveRequests());
|
||||
SG_VERIFY(!cl->hasActiveRequests());
|
||||
|
||||
// force it this time
|
||||
root->refresh(true);
|
||||
VERIFY(cl->hasActiveRequests());
|
||||
SG_VERIFY(cl->hasActiveRequests());
|
||||
waitForUpdateComplete(cl, root);
|
||||
|
||||
pkg::CatalogRef c = root->getCatalogById("org.flightgear.test.catalog1");
|
||||
COMPARE(c->ageInSeconds(), 0);
|
||||
SG_CHECK_EQUAL(c->ageInSeconds(), 0);
|
||||
|
||||
VERIFY(root->getPackageById("dc3") != pkg::PackageRef());
|
||||
COMPARE(root->packagesNeedingUpdate().size(), 2);
|
||||
SG_VERIFY(root->getPackageById("dc3") != pkg::PackageRef());
|
||||
SG_CHECK_EQUAL(root->packagesNeedingUpdate().size(), 2);
|
||||
|
||||
pkg::PackageList needingUpdate = root->packagesNeedingUpdate();
|
||||
VERIFY(contains(needingUpdate, root->getPackageById("common-sounds")));
|
||||
VERIFY(contains(needingUpdate, root->getPackageById("org.flightgear.test.catalog1.alpha")));
|
||||
SG_VERIFY(contains(needingUpdate, root->getPackageById("common-sounds")));
|
||||
SG_VERIFY(
|
||||
contains(needingUpdate,
|
||||
root->getPackageById("org.flightgear.test.catalog1.alpha")));
|
||||
|
||||
pkg::InstallRef ins = root->getPackageById("alpha")->existingInstall();
|
||||
VERIFY(ins->hasUpdate());
|
||||
SG_VERIFY(ins->hasUpdate());
|
||||
|
||||
for (pkg::PackageList::const_iterator it = needingUpdate.begin();
|
||||
it != needingUpdate.end(); ++it)
|
||||
@@ -399,8 +483,8 @@ void testRefreshCatalog(HTTP::Client* cl)
|
||||
|
||||
waitForUpdateComplete(cl, root);
|
||||
|
||||
VERIFY(root->packagesNeedingUpdate().empty());
|
||||
COMPARE(root->getPackageById("common-sounds")->revision(), 11);
|
||||
SG_VERIFY(root->packagesNeedingUpdate().empty());
|
||||
SG_CHECK_EQUAL(root->getPackageById("common-sounds")->revision(), 11);
|
||||
}
|
||||
|
||||
void testInstallTarPackage(HTTP::Client* cl)
|
||||
@@ -418,14 +502,14 @@ void testInstallTarPackage(HTTP::Client* cl)
|
||||
waitForUpdateComplete(cl, root);
|
||||
|
||||
pkg::PackageRef p1 = root->getPackageById("org.flightgear.test.catalog1.b737-NG");
|
||||
COMPARE(p1->id(), "b737-NG");
|
||||
SG_CHECK_EQUAL(p1->id(), "b737-NG");
|
||||
pkg::InstallRef ins = p1->install();
|
||||
|
||||
VERIFY(ins->isQueued());
|
||||
SG_VERIFY(ins->isQueued());
|
||||
|
||||
waitForUpdateComplete(cl, root);
|
||||
VERIFY(p1->isInstalled());
|
||||
VERIFY(p1->existingInstall() == ins);
|
||||
SG_VERIFY(p1->isInstalled());
|
||||
SG_VERIFY(p1->existingInstall() == ins);
|
||||
|
||||
// verify on disk state
|
||||
SGPath p(rootPath);
|
||||
@@ -433,10 +517,10 @@ void testInstallTarPackage(HTTP::Client* cl)
|
||||
p.append("Aircraft");
|
||||
p.append("b737NG");
|
||||
|
||||
COMPARE(p, ins->path());
|
||||
SG_CHECK_EQUAL(p, ins->path());
|
||||
|
||||
p.append("b737-900-set.xml");
|
||||
VERIFY(p.exists());
|
||||
SG_VERIFY(p.exists());
|
||||
}
|
||||
|
||||
|
||||
@@ -454,7 +538,7 @@ int main(int argc, char* argv[])
|
||||
parseTest();
|
||||
|
||||
testInstallPackage(&cl);
|
||||
|
||||
|
||||
testUninstall(&cl);
|
||||
|
||||
testRemoveCatalog(&cl);
|
||||
|
||||
@@ -29,9 +29,11 @@ namespace pkg
|
||||
|
||||
class Install;
|
||||
class Catalog;
|
||||
class Package;
|
||||
|
||||
typedef SGSharedPtr<Catalog> CatalogRef;
|
||||
typedef SGSharedPtr<Install> InstallRef;
|
||||
typedef SGSharedPtr<Package> PackageRef;
|
||||
|
||||
/**
|
||||
* package delegate is the mechanism to discover progress / completion /
|
||||
@@ -68,6 +70,8 @@ public:
|
||||
virtual void installProgress(InstallRef aInstall, unsigned int aBytes, unsigned int aTotal) = 0;
|
||||
virtual void finishInstall(InstallRef aInstall, StatusCode aReason) = 0;
|
||||
|
||||
virtual void finishUninstall(const PackageRef& aPackage) {};
|
||||
|
||||
/**
|
||||
* Notification when catalogs/packages are added or removed
|
||||
*/
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
#include <simgear/io/HTTPClient.hxx>
|
||||
#include <simgear/misc/sg_dir.hxx>
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
#include <simgear/misc/sgstream.hxx>
|
||||
#include <simgear/io/iostreams/sgstream.hxx>
|
||||
|
||||
extern "C" {
|
||||
void fill_memory_filefunc (zlib_filefunc_def*);
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
|
||||
#include <simgear/package/Catalog.hxx>
|
||||
@@ -29,7 +29,7 @@
|
||||
#include <simgear/package/Root.hxx>
|
||||
|
||||
namespace simgear {
|
||||
|
||||
|
||||
namespace pkg {
|
||||
|
||||
Package::Package(const SGPropertyNode* aProps, CatalogRef aCatalog) :
|
||||
@@ -48,63 +48,113 @@ void Package::initWithProps(const SGPropertyNode* aProps)
|
||||
}
|
||||
|
||||
m_id = m_props->getStringValue("id");
|
||||
|
||||
m_variants.push_back(m_id);
|
||||
for (auto var : m_props->getChildren("variant")) {
|
||||
m_variants.push_back(var->getStringValue("id"));
|
||||
}
|
||||
}
|
||||
|
||||
void Package::updateFromProps(const SGPropertyNode* aProps)
|
||||
{
|
||||
m_tags.clear();
|
||||
m_variants.clear();
|
||||
initWithProps(aProps);
|
||||
}
|
||||
|
||||
bool Package::matches(const SGPropertyNode* aFilter) const
|
||||
{
|
||||
int nChildren = aFilter->nChildren();
|
||||
for (int i = 0; i < nChildren; i++) {
|
||||
const SGPropertyNode* c = aFilter->getChild(i);
|
||||
const std::string& filter_name = c->getNameString();
|
||||
const std::string& filter_name = aFilter->getNameString();
|
||||
|
||||
if (strutils::starts_with(filter_name, "rating-")) {
|
||||
int minRating = c->getIntValue();
|
||||
std::string rname = c->getName() + 7;
|
||||
int ourRating = m_props->getChild("rating")->getIntValue(rname, 0);
|
||||
if (ourRating < minRating) {
|
||||
if (filter_name == "any-of") {
|
||||
const int anyChildren = aFilter->nChildren();
|
||||
for (int j = 0; j < anyChildren; j++) {
|
||||
const SGPropertyNode* anyChild = aFilter->getChild(j);
|
||||
if (matches(anyChild)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false; // none of our children matched
|
||||
} else if (filter_name.empty() || (filter_name == "all-of")) {
|
||||
const int allChildren = aFilter->nChildren();
|
||||
for (int j = 0; j < allChildren; j++) {
|
||||
const SGPropertyNode* allChild = aFilter->getChild(j);
|
||||
if (!matches(allChild)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (filter_name == "tag") {
|
||||
std::string tag(c->getStringValue());
|
||||
boost::to_lower(tag);
|
||||
if (m_tags.find(tag) == m_tags.end()) {
|
||||
return false;
|
||||
|
||||
return true; // all of our children matched
|
||||
}
|
||||
|
||||
if (strutils::starts_with(filter_name, "rating-")) {
|
||||
int minRating = aFilter->getIntValue();
|
||||
std::string rname = aFilter->getName() + 7;
|
||||
int ourRating = m_props->getChild("rating")->getIntValue(rname, 0);
|
||||
return (ourRating >= minRating);
|
||||
}
|
||||
|
||||
if (filter_name == "tag") {
|
||||
std::string tag(aFilter->getStringValue());
|
||||
boost::to_lower(tag);
|
||||
return (m_tags.find(tag) != m_tags.end());
|
||||
}
|
||||
|
||||
if (filter_name == "installed") {
|
||||
return (isInstalled() == aFilter->getBoolValue());
|
||||
}
|
||||
|
||||
bool handled = false;
|
||||
// substring search of name, description, across variants too
|
||||
if ((filter_name == "text") || (filter_name == "name")) {
|
||||
handled = true;
|
||||
std::string n(aFilter->getStringValue());
|
||||
boost::to_lower(n);
|
||||
|
||||
size_t pos = boost::to_lower_copy(name()).find(n);
|
||||
if (pos != std::string::npos) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (auto var : m_props->getChildren("variant")) {
|
||||
if (var->hasChild("name")) {
|
||||
std::string variantName(var->getStringValue("name"));
|
||||
boost::to_lower(variantName);
|
||||
size_t pos = variantName.find(n);
|
||||
if (pos != std::string::npos) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((filter_name == "text") || (filter_name == "description")) {
|
||||
handled = true;
|
||||
std::string n(aFilter->getStringValue());
|
||||
boost::to_lower(n);
|
||||
size_t pos = boost::to_lower_copy(description()).find(n);
|
||||
if (pos != std::string::npos) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (auto var : m_props->getChildren("variant")) {
|
||||
if (var->hasChild("description")) {
|
||||
std::string variantDesc(var->getStringValue("description"));
|
||||
boost::to_lower(variantDesc);
|
||||
size_t pos = variantDesc.find(n);
|
||||
if (pos != std::string::npos) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// substring search of name, description
|
||||
else if (filter_name == "name") {
|
||||
std::string n(c->getStringValue());
|
||||
boost::to_lower(n);
|
||||
size_t pos = boost::to_lower_copy(name()).find(n);
|
||||
if (pos == std::string::npos) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (filter_name == "description") {
|
||||
std::string n(c->getStringValue());
|
||||
boost::to_lower(n);
|
||||
size_t pos = boost::to_lower_copy(description()).find(n);
|
||||
if (pos == std::string::npos) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (filter_name == "installed") {
|
||||
if (isInstalled() != c->getBoolValue()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "unknown filter term:" << filter_name);
|
||||
} // of filter props iteration
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "unknown filter term:" << filter_name);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Package::isInstalled() const
|
||||
@@ -127,7 +177,7 @@ InstallRef Package::install()
|
||||
if (ins) {
|
||||
return ins;
|
||||
}
|
||||
|
||||
|
||||
// start a new install
|
||||
ins = new Install(this, pathOnDisk());
|
||||
m_catalog->root()->scheduleToUpdate(ins);
|
||||
@@ -169,7 +219,10 @@ std::string Package::qualifiedId() const
|
||||
|
||||
std::string Package::qualifiedVariantId(const unsigned int variantIndex) const
|
||||
{
|
||||
return m_catalog->id() + "." + variants()[variantIndex];
|
||||
if (variantIndex >= m_variants.size()) {
|
||||
throw sg_range_exception("invalid variant index " + std::to_string(variantIndex));
|
||||
}
|
||||
return m_catalog->id() + "." + m_variants[variantIndex];
|
||||
}
|
||||
|
||||
std::string Package::md5() const
|
||||
@@ -190,10 +243,10 @@ unsigned int Package::revision() const
|
||||
if (!m_props) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
return m_props->getIntValue("revision");
|
||||
}
|
||||
|
||||
|
||||
std::string Package::name() const
|
||||
{
|
||||
return m_props->getStringValue("name");
|
||||
@@ -203,17 +256,17 @@ size_t Package::fileSizeBytes() const
|
||||
{
|
||||
return m_props->getIntValue("file-size-bytes");
|
||||
}
|
||||
|
||||
|
||||
std::string Package::description() const
|
||||
{
|
||||
return getLocalisedProp("description");
|
||||
return getLocalisedProp("description", 0);
|
||||
}
|
||||
|
||||
string_set Package::tags() const
|
||||
{
|
||||
return m_tags;
|
||||
}
|
||||
|
||||
|
||||
SGPropertyNode* Package::properties() const
|
||||
{
|
||||
return m_props.ptr();
|
||||
@@ -225,7 +278,7 @@ string_list Package::thumbnailUrls() const
|
||||
if (!m_props) {
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
BOOST_FOREACH(SGPropertyNode* dl, m_props->getChildren("thumbnail")) {
|
||||
r.push_back(dl->getStringValue());
|
||||
}
|
||||
@@ -238,29 +291,29 @@ string_list Package::thumbnails() const
|
||||
if (!m_props) {
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
BOOST_FOREACH(SGPropertyNode* dl, m_props->getChildren("thumbnail-path")) {
|
||||
r.push_back(dl->getStringValue());
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
string_list Package::downloadUrls() const
|
||||
{
|
||||
string_list r;
|
||||
if (!m_props) {
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
BOOST_FOREACH(SGPropertyNode* dl, m_props->getChildren("url")) {
|
||||
r.push_back(dl->getStringValue());
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
std::string Package::getLocalisedProp(const std::string& aName) const
|
||||
std::string Package::getLocalisedProp(const std::string& aName, const unsigned int vIndex) const
|
||||
{
|
||||
return getLocalisedString(m_props, aName.c_str());
|
||||
return getLocalisedString(propsForVariant(vIndex, aName.c_str()), aName.c_str());
|
||||
}
|
||||
|
||||
std::string Package::getLocalisedString(const SGPropertyNode* aRoot, const char* aName) const
|
||||
@@ -272,54 +325,47 @@ std::string Package::getLocalisedString(const SGPropertyNode* aRoot, const char*
|
||||
return localeRoot->getStringValue(aName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return aRoot->getStringValue(aName);
|
||||
}
|
||||
|
||||
PackageList Package::dependencies() const
|
||||
{
|
||||
PackageList result;
|
||||
|
||||
|
||||
BOOST_FOREACH(SGPropertyNode* dep, m_props->getChildren("depends")) {
|
||||
std::string depName = dep->getStringValue("id");
|
||||
unsigned int rev = dep->getIntValue("revision", 0);
|
||||
|
||||
|
||||
// prefer local hangar package if possible, in case someone does something
|
||||
// silly with naming. Of course flightgear's aircraft search doesn't know
|
||||
// about hangars, so names still need to be unique.
|
||||
PackageRef depPkg = m_catalog->getPackageById(depName);
|
||||
if (!depPkg) {
|
||||
if (!depPkg) {
|
||||
Root* rt = m_catalog->root();
|
||||
depPkg = rt->getPackageById(depName);
|
||||
if (!depPkg) {
|
||||
throw sg_exception("Couldn't satisfy dependency of " + id() + " : " + depName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (depPkg->revision() < rev) {
|
||||
throw sg_range_exception("Couldn't find suitable revision of " + depName);
|
||||
}
|
||||
|
||||
|
||||
// forbid recursive dependency graphs, we don't need that level
|
||||
// of complexity for aircraft resources
|
||||
assert(depPkg->dependencies() == PackageList());
|
||||
|
||||
|
||||
result.push_back(depPkg);
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
string_list Package::variants() const
|
||||
{
|
||||
string_list result;
|
||||
result.push_back(id());
|
||||
|
||||
BOOST_FOREACH(SGPropertyNode* var, m_props->getChildren("variant")) {
|
||||
result.push_back(var->getStringValue("id"));
|
||||
}
|
||||
|
||||
return result;
|
||||
return m_variants;
|
||||
}
|
||||
|
||||
std::string Package::nameForVariant(const std::string& vid) const
|
||||
@@ -338,19 +384,85 @@ std::string Package::nameForVariant(const std::string& vid) const
|
||||
throw sg_exception("Unknow variant +" + vid + " in package " + id());
|
||||
}
|
||||
|
||||
unsigned int Package::indexOfVariant(const std::string& vid) const
|
||||
{
|
||||
// accept fully-qualified IDs here
|
||||
std::string actualId = vid;
|
||||
size_t lastDot = vid.rfind('.');
|
||||
if (lastDot != std::string::npos) {
|
||||
std::string catalogId = vid.substr(0, lastDot);
|
||||
if (catalogId != catalog()->id()) {
|
||||
throw sg_exception("Bad fully-qualified ID:" + vid + ", package mismatch" );
|
||||
}
|
||||
actualId = vid.substr(lastDot + 1);
|
||||
}
|
||||
|
||||
string_list::const_iterator it = std::find(m_variants.begin(), m_variants.end(), actualId);
|
||||
if (it == m_variants.end()) {
|
||||
throw sg_exception("Unknow variant " + vid + " in package " + id());
|
||||
}
|
||||
|
||||
return std::distance(m_variants.begin(), it);
|
||||
}
|
||||
|
||||
std::string Package::nameForVariant(const unsigned int vIndex) const
|
||||
{
|
||||
if (vIndex == 0)
|
||||
return name();
|
||||
return propsForVariant(vIndex, "name")->getStringValue("name");
|
||||
}
|
||||
|
||||
SGPropertyNode_ptr Package::propsForVariant(const unsigned int vIndex, const char* propName) const
|
||||
{
|
||||
if (vIndex == 0) {
|
||||
return m_props;
|
||||
}
|
||||
|
||||
// offset by minus one to allow for index 0 being the primary
|
||||
SGPropertyNode_ptr var = m_props->getChild("variant", vIndex - 1);
|
||||
if (var)
|
||||
return var->getStringValue("name");
|
||||
if (var) {
|
||||
if (!propName || var->hasChild(propName)) {
|
||||
return var;
|
||||
}
|
||||
|
||||
return m_props;
|
||||
}
|
||||
|
||||
throw sg_exception("Unknow variant in package " + id());
|
||||
}
|
||||
|
||||
Package::PreviewVec Package::previewsForVariant(unsigned int vIndex) const
|
||||
{
|
||||
SGPropertyNode_ptr var = propsForVariant(vIndex);
|
||||
return previewsFromProps(var);
|
||||
}
|
||||
|
||||
Package::Preview::Type previewTypeFromString(const std::string& s)
|
||||
{
|
||||
if (s == "exterior") return Package::Preview::Type::EXTERIOR;
|
||||
if (s == "interior") return Package::Preview::Type::INTERIOR;
|
||||
if (s == "panel") return Package::Preview::Type::PANEL;
|
||||
return Package::Preview::Type::UNKNOWN;
|
||||
}
|
||||
|
||||
Package::Preview::Preview(const std::string& aUrl, const std::string& aPath, Type aType) :
|
||||
url(aUrl),
|
||||
path(aPath),
|
||||
type(aType)
|
||||
{
|
||||
}
|
||||
|
||||
Package::PreviewVec Package::previewsFromProps(const SGPropertyNode_ptr& ptr) const
|
||||
{
|
||||
PreviewVec result;
|
||||
|
||||
for (auto thumbNode : ptr->getChildren("preview")) {
|
||||
Preview t(thumbNode->getStringValue("url"),
|
||||
thumbNode->getStringValue("path"),
|
||||
previewTypeFromString(thumbNode->getStringValue("type")));
|
||||
result.push_back(t);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // of namespace pkg
|
||||
|
||||
|
||||
@@ -80,7 +80,12 @@ public:
|
||||
* Fully-qualified ID, including our catalog'd ID
|
||||
*/
|
||||
std::string qualifiedVariantId(const unsigned int variantIndex) const;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
unsigned int indexOfVariant(const std::string& vid) const;
|
||||
|
||||
/**
|
||||
* human-readable name - note this is probably not localised,
|
||||
* although this is not ruled out for the future.
|
||||
@@ -109,7 +114,7 @@ public:
|
||||
*/
|
||||
std::string md5() const;
|
||||
|
||||
std::string getLocalisedProp(const std::string& aName) const;
|
||||
std::string getLocalisedProp(const std::string& aName, const unsigned int vIndex = 0) const;
|
||||
|
||||
unsigned int revision() const;
|
||||
|
||||
@@ -133,6 +138,35 @@ public:
|
||||
* thumbnail file paths within the package on disk
|
||||
*/
|
||||
string_list thumbnails() const;
|
||||
|
||||
/**
|
||||
* information about a preview image
|
||||
*/
|
||||
struct Preview {
|
||||
enum class Type
|
||||
{
|
||||
UNKNOWN,
|
||||
PANEL,
|
||||
INTERIOR,
|
||||
EXTERIOR
|
||||
|
||||
// NIGHT / GROUND as modifiers? does this add any
|
||||
// actual value for GUIs?
|
||||
};
|
||||
|
||||
Preview(const std::string& url, const std::string& path, Type ty = Type::UNKNOWN);
|
||||
|
||||
std::string url;
|
||||
std::string path;
|
||||
Type type = Type::UNKNOWN;
|
||||
};
|
||||
|
||||
typedef std::vector<Preview> PreviewVec;
|
||||
|
||||
/**
|
||||
* retrieve all the thumbnails for a variant
|
||||
*/
|
||||
PreviewVec previewsForVariant(unsigned int vIndex) const;
|
||||
|
||||
/**
|
||||
* Packages we depend upon.
|
||||
@@ -159,12 +193,17 @@ private:
|
||||
void updateFromProps(const SGPropertyNode* aProps);
|
||||
|
||||
std::string getLocalisedString(const SGPropertyNode* aRoot, const char* aName) const;
|
||||
|
||||
|
||||
PreviewVec previewsFromProps(const SGPropertyNode_ptr& ptr) const;
|
||||
|
||||
SGPropertyNode_ptr propsForVariant(const unsigned int vIndex, const char* propName = nullptr) const;
|
||||
|
||||
SGPropertyNode_ptr m_props;
|
||||
std::string m_id;
|
||||
string_set m_tags;
|
||||
CatalogRef m_catalog;
|
||||
|
||||
string_list m_variants;
|
||||
|
||||
mutable function_list<InstallCallback> _install_cb;
|
||||
};
|
||||
|
||||
|
||||
@@ -153,6 +153,12 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void fireFinishUninstall(PackageRef pkg)
|
||||
{
|
||||
std::for_each(delegates.begin(), delegates.end(),
|
||||
[pkg](Delegate* d) {d->finishUninstall(pkg);});
|
||||
}
|
||||
|
||||
DelegateVec delegates;
|
||||
|
||||
SGPath path;
|
||||
@@ -652,6 +658,7 @@ void Root::unregisterInstall(InstallRef ins)
|
||||
}
|
||||
|
||||
d->m_installs.erase(ins->package());
|
||||
d->fireFinishUninstall(ins->package());
|
||||
}
|
||||
|
||||
} // of namespace pkg
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#define SG_PACKAGE_ROOT_HXX
|
||||
|
||||
#include <vector>
|
||||
#include <memory> // for auto_ptr
|
||||
#include <memory> // for unique_ptr
|
||||
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/package/Delegate.hxx>
|
||||
@@ -161,7 +161,7 @@ private:
|
||||
|
||||
class ThumbnailDownloader;
|
||||
class RootPrivate;
|
||||
std::auto_ptr<RootPrivate> d;
|
||||
std::unique_ptr<RootPrivate> d;
|
||||
};
|
||||
|
||||
typedef SGSharedPtr<Root> RootRef;
|
||||
|
||||
@@ -48,6 +48,23 @@
|
||||
<revision>10</revision>
|
||||
</depends>
|
||||
|
||||
<preview>
|
||||
<type>exterior</type>
|
||||
<path>thumb-exterior.png</path>
|
||||
<url>http://foo.bar.com/thumb-exterior.png</url>
|
||||
</preview>
|
||||
|
||||
<preview>
|
||||
<type>panel</type>
|
||||
<path>thumb-panel.png</path>
|
||||
<url>http://foo.bar.com/thumb-panel.png</url>
|
||||
</preview>
|
||||
|
||||
<preview>
|
||||
<path>thumb-something.png</path>
|
||||
<url>http://foo.bar.com/thumb-something.png</url>
|
||||
</preview>
|
||||
|
||||
<variant>
|
||||
<id>c172p-2d-panel</id>
|
||||
<name>C172 with 2d panel only</name>
|
||||
@@ -56,11 +73,35 @@
|
||||
<variant>
|
||||
<id>c172p-floats</id>
|
||||
<name>C172 with floats</name>
|
||||
|
||||
<preview>
|
||||
<type>exterior</type>
|
||||
<path>thumb-exterior-floats.png</path>
|
||||
<url>http://foo.bar.com/thumb-exterior-floats.png</url>
|
||||
</preview>
|
||||
|
||||
<preview>
|
||||
<type>panel</type>
|
||||
<path>thumb-panel.png</path>
|
||||
<url>http://foo.bar.com/thumb-panel.png</url>
|
||||
</preview>
|
||||
</variant>
|
||||
|
||||
<variant>
|
||||
<id>c172p-skis</id>
|
||||
<name>C172 with skis</name>
|
||||
|
||||
<preview>
|
||||
<type>exterior</type>
|
||||
<path>thumb-exterior-skis.png</path>
|
||||
<url>http://foo.bar.com/thumb-exterior-skis.png</url>
|
||||
</preview>
|
||||
|
||||
<preview>
|
||||
<type>panel</type>
|
||||
<path>thumb-panel.png</path>
|
||||
<url>http://foo.bar.com/thumb-panel.png</url>
|
||||
</preview>
|
||||
</variant>
|
||||
|
||||
<md5>ec0e2ffdf98d6a5c05c77445e5447ff5</md5>
|
||||
@@ -99,6 +140,18 @@
|
||||
|
||||
<md5>a94ca5704f305b90767f40617d194ed6</md5>
|
||||
<url>http://localhost:2000/catalogTest1/b737.tar.gz</url>
|
||||
|
||||
<preview>
|
||||
<type>exterior</type>
|
||||
<path>thumb-exterior.png</path>
|
||||
<url>http://foo.bar.com/thumb-exterior.png</url>
|
||||
</preview>
|
||||
|
||||
<preview>
|
||||
<type>panel</type>
|
||||
<path>thumb-panel.png</path>
|
||||
<url>http://foo.bar.com/thumb-panel.png</url>
|
||||
</preview>
|
||||
</package>
|
||||
|
||||
|
||||
|
||||
@@ -20,15 +20,17 @@
|
||||
<url>http://localhost:2000/catalogTest1/alpha.zip</url>
|
||||
|
||||
<dir>alpha</dir>
|
||||
|
||||
</package>
|
||||
|
||||
<package>
|
||||
<id>c172p</id>
|
||||
<name>Cessna 172-P</name>
|
||||
<dir>c172p</dir>
|
||||
<description>A plane made by Cessna</description>
|
||||
<description>A plane made by Cessna on Jupiter</description>
|
||||
<revision type="int">42</revision>
|
||||
<file-size-bytes type="int">860</file-size-bytes>
|
||||
<author>Standard author</author>
|
||||
|
||||
<tag>cessna</tag>
|
||||
<tag>ga</tag>
|
||||
@@ -48,6 +50,23 @@
|
||||
<revision>10</revision>
|
||||
</depends>
|
||||
|
||||
<preview>
|
||||
<type>exterior</type>
|
||||
<path>thumb-exterior.png</path>
|
||||
<url>http://foo.bar.com/thumb-exterior.png</url>
|
||||
</preview>
|
||||
|
||||
<preview>
|
||||
<type>panel</type>
|
||||
<path>thumb-panel.png</path>
|
||||
<url>http://foo.bar.com/thumb-panel.png</url>
|
||||
</preview>
|
||||
|
||||
<preview>
|
||||
<path>thumb-something.png</path>
|
||||
<url>http://foo.bar.com/thumb-something.png</url>
|
||||
</preview>
|
||||
|
||||
<variant>
|
||||
<id>c172p-2d-panel</id>
|
||||
<name>C172 with 2d panel only</name>
|
||||
@@ -56,16 +75,46 @@
|
||||
<variant>
|
||||
<id>c172p-floats</id>
|
||||
<name>C172 with floats</name>
|
||||
<description>A plane with floats</description>
|
||||
<author>Floats variant author</author>
|
||||
|
||||
<preview>
|
||||
<type>exterior</type>
|
||||
<path>thumb-exterior-floats.png</path>
|
||||
<url>http://foo.bar.com/thumb-exterior-floats.png</url>
|
||||
</preview>
|
||||
|
||||
<preview>
|
||||
<type>panel</type>
|
||||
<path>thumb-panel.png</path>
|
||||
<url>http://foo.bar.com/thumb-panel.png</url>
|
||||
</preview>
|
||||
</variant>
|
||||
|
||||
<variant>
|
||||
<id>c172p-skis</id>
|
||||
<name>C172 with skis</name>
|
||||
<description>A plane with skis</description>
|
||||
|
||||
<preview>
|
||||
<type>exterior</type>
|
||||
<path>thumb-exterior-skis.png</path>
|
||||
<url>http://foo.bar.com/thumb-exterior-skis.png</url>
|
||||
</preview>
|
||||
|
||||
<preview>
|
||||
<type>panel</type>
|
||||
<path>thumb-panel.png</path>
|
||||
<url>http://foo.bar.com/thumb-panel.png</url>
|
||||
</preview>
|
||||
</variant>
|
||||
|
||||
<md5>ec0e2ffdf98d6a5c05c77445e5447ff5</md5>
|
||||
<url>http://localhost:2000/catalogTest1/c172p.zip</url>
|
||||
|
||||
<!-- legacy thumbnails also supported -->
|
||||
<thumbnail>http://foo.bar.com/thumb-exterior.png</thumbnail>
|
||||
<thumbnail-path>exterior.png</thumbnail-path>
|
||||
</package>
|
||||
|
||||
<package>
|
||||
|
||||
@@ -26,7 +26,7 @@ public:
|
||||
(*itr)->addChangeListener(this);
|
||||
}
|
||||
private:
|
||||
void valueChanged(SGPropertyNode* node);
|
||||
void valueChanged(SGPropertyNode* node) override;
|
||||
virtual void valueChangedImplementation();
|
||||
|
||||
};
|
||||
@@ -53,10 +53,10 @@ public:
|
||||
}
|
||||
bool isDirty() { return _dirty; }
|
||||
bool isValid() { return _valid; }
|
||||
void unregister_property(SGPropertyNode* node);
|
||||
virtual void unregister_property(SGPropertyNode* node) override;
|
||||
static void fireChangeListeners();
|
||||
private:
|
||||
virtual void valueChangedImplementation();
|
||||
virtual void valueChangedImplementation() override;
|
||||
virtual void valuesChanged();
|
||||
bool _dirty;
|
||||
bool _valid;
|
||||
@@ -90,7 +90,7 @@ public:
|
||||
if (initial)
|
||||
valuesChanged();
|
||||
}
|
||||
virtual void valuesChanged()
|
||||
virtual void valuesChanged() override
|
||||
{
|
||||
ExtendedPropertyAdapter<T, std::vector<SGPropertyNode*> > adaptor(_watched);
|
||||
T val = adaptor();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user