Compare commits
3 Commits
version/20
...
version/20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c92a953511 | ||
|
|
863ae19d1d | ||
|
|
cd7b6d69b0 |
100
CMakeLists.txt
100
CMakeLists.txt
@@ -9,17 +9,6 @@ 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)
|
||||
@@ -120,16 +109,11 @@ endif()
|
||||
|
||||
option(SIMGEAR_HEADLESS "Set to ON to build SimGear without GUI/graphics support" OFF)
|
||||
option(ENABLE_RTI "Set to ON to build SimGear with RTI support" OFF)
|
||||
option(ENABLE_GDAL "Set to ON to build SimGear with GDAL support" OFF)
|
||||
option(ENABLE_TESTS "Set to OFF to disable building SimGear's test applications" ON)
|
||||
option(ENABLE_SOUND "Set to OFF to disable building SimGear's sound support" ON)
|
||||
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)
|
||||
option(ENABLE_OPENMP "Enable OpenMP compiler support" OFF)
|
||||
|
||||
include (DetectArch)
|
||||
|
||||
# until the fstream fix is applied and generally available in OSG,
|
||||
# keep the compatability link option as the default
|
||||
@@ -249,12 +233,13 @@ else()
|
||||
# declaring symbols as declspec(import)
|
||||
add_definitions(-DHAVE_EXPAT_CONFIG_H -DXML_STATIC)
|
||||
set(EXPAT_INCLUDE_DIRS
|
||||
${PROJECT_SOURCE_DIR}/3rdparty/expat
|
||||
${PROJECT_BINARY_DIR}/3rdparty/expat)
|
||||
${PROJECT_SOURCE_DIR}/3rdparty/expat
|
||||
${PROJECT_BINARY_DIR}/3rdparty/expat)
|
||||
endif(SYSTEM_EXPAT)
|
||||
|
||||
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)
|
||||
|
||||
@@ -264,41 +249,21 @@ if(HAVE_INTTYPES_H)
|
||||
endif()
|
||||
|
||||
if(ENABLE_RTI)
|
||||
find_package(PkgConfig)
|
||||
if(PKG_CONFIG_FOUND)
|
||||
SET(ENV{PKG_CONFIG_PATH} "${CMAKE_INSTALL_PREFIX}/lib/pkgconfig:$ENV{PKG_CONFIG_PATH}")
|
||||
pkg_check_modules(RTI hla-rti13)
|
||||
endif(PKG_CONFIG_FOUND)
|
||||
if(RTI_FOUND)
|
||||
SET(RTI_INCLUDE_DIR "${RTI_INCLUDE_DIRS}")
|
||||
message(STATUS "RTI: ENABLED")
|
||||
else()
|
||||
message(STATUS "RTI: DISABLED")
|
||||
endif(RTI_FOUND)
|
||||
# See if we have any rti library variant installed
|
||||
message(STATUS "RTI: ENABLED")
|
||||
find_package(RTI)
|
||||
else()
|
||||
message(STATUS "RTI: DISABLED")
|
||||
endif(ENABLE_RTI)
|
||||
|
||||
if(ENABLE_GDAL)
|
||||
find_package(GDAL 2.0.0 REQUIRED)
|
||||
if (GDAL_FOUND)
|
||||
include_directories(${GDAL_INCLUDE_DIR})
|
||||
endif(GDAL_FOUND)
|
||||
endif(ENABLE_GDAL)
|
||||
|
||||
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(
|
||||
@@ -352,8 +317,8 @@ if (NOT ${HAVE_STD_ISNAN})
|
||||
endif()
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
set(WARNING_FLAGS_CXX "-Wall -fPIC")
|
||||
set(WARNING_FLAGS_C "-Wall -fPIC")
|
||||
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")
|
||||
@@ -363,13 +328,6 @@ if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
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})
|
||||
@@ -378,10 +336,10 @@ if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
GCC_ATOMIC_BUILTINS_FOUND)
|
||||
endif(CMAKE_COMPILER_IS_GNUCXX)
|
||||
|
||||
if (CLANG)
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
|
||||
# Boost redeclares class members
|
||||
set(WARNING_FLAGS_CXX "-Wall -fPIC -Wno-overloaded-virtual -Wno-redeclared-class-member")
|
||||
set(WARNING_FLAGS_C "-Wall -fPIC")
|
||||
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++")
|
||||
# fix Boost compilation :(
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
|
||||
@@ -389,26 +347,6 @@ if (CLANG)
|
||||
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 (ENABLE_OPENMP)
|
||||
find_package(OpenMP)
|
||||
if(OPENMP_FOUND)
|
||||
message(STATUS "OpenMP: ENABLED")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
|
||||
else()
|
||||
message(STATUS "OpenMP: NOT FOUND")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "OpenMP: DISABLED")
|
||||
endif()
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
@@ -427,15 +365,6 @@ 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)
|
||||
message(STATUS "For better linking performance, use OSG with patched fstream header")
|
||||
@@ -494,8 +423,7 @@ set(TEST_LIBS_INTERNAL_CORE
|
||||
${RT_LIBRARY}
|
||||
${DL_LIBRARY}
|
||||
${COCOA_LIBRARY}
|
||||
${CURL_LIBRARIES}
|
||||
${GDAL_LIBRARY})
|
||||
${CURL_LIBRARIES})
|
||||
set(TEST_LIBS SimGearCore ${TEST_LIBS_INTERNAL_CORE})
|
||||
|
||||
if(NOT SIMGEAR_HEADLESS)
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
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()
|
||||
76
SimGear.spec.in
Normal file
76
SimGear.spec.in
Normal file
@@ -0,0 +1,76 @@
|
||||
%define ver @VERSION@
|
||||
%define rel 1
|
||||
%define prefix /usr
|
||||
|
||||
Summary: Simulator Construction Gear.
|
||||
Name: @PACKAGE@
|
||||
Version: %ver
|
||||
Release: %rel
|
||||
Copyright: LGPL
|
||||
Group: Libraries/Graphics
|
||||
Source: %{name}-%{version}.tar.gz
|
||||
#URL:
|
||||
BuildRoot: /tmp/%{name}-%{version}-%{rel}-root
|
||||
Packager: Fill In As You Wish
|
||||
Docdir: %{prefix}/doc
|
||||
|
||||
%description
|
||||
This package contains a tools and libraries useful for constructing
|
||||
simulation and visualization applications such as FlightGear or TerraGear.
|
||||
|
||||
Authors:
|
||||
N/A
|
||||
|
||||
%prep
|
||||
%setup -n %{name}-%{version}
|
||||
|
||||
|
||||
%build
|
||||
# Needed for snapshot releases.
|
||||
if [ ! -f configure ]; then
|
||||
CFLAGS="$RPM_OPT_FLAGS" ./autogen.sh --prefix=%prefix
|
||||
else
|
||||
CFLAGS="$RPM_OPT_FLAGS" ./configure --prefix=%prefix
|
||||
fi
|
||||
|
||||
if [ "$SMP" != "" ]; then
|
||||
JSMP = '"MAKE=make -k -j $SMP"'
|
||||
fi
|
||||
|
||||
make ${JSMP};
|
||||
|
||||
|
||||
%install
|
||||
[ -d ${RPM_BUILD_ROOT} ] && rm -rf ${RPM_BUILD_ROOT}
|
||||
|
||||
make prefix=${RPM_BUILD_ROOT}%{prefix} install
|
||||
|
||||
#
|
||||
# Generating file lists and store them in file-lists
|
||||
# Starting with the directory listings
|
||||
#
|
||||
find ${RPM_BUILD_ROOT}%{prefix}/{bin,include,lib} -type d | sed "s#^${RPM_BUILD_ROOT}#\%attr (-\,root\,root) \%dir #" > file-lists
|
||||
%{?ETCDR:find ${RPM_BUILD_ROOT}%{!?SYSCF:%{prefix}}/etc -type d | sed "s#^${RPM_BUILD_ROOT}#\%attr (-\,root\,root) \%dir #" >> file-lists}
|
||||
|
||||
#
|
||||
# Then, the file listings
|
||||
#
|
||||
echo "%defattr (-, root, root)" >> file-lists
|
||||
%{?ETCDR:find ${RPM_BUILD_ROOT}%{!?SYSCF:%{prefix}}/etc/%{name}.conf -type f | sed -e "s#^${RPM_BUILD_ROOT}#%config #g" >> file-lists}
|
||||
find ${RPM_BUILD_ROOT}%{prefix} -type f | sed -e "s#^${RPM_BUILD_ROOT}##g" >> file-lists
|
||||
|
||||
|
||||
%clean
|
||||
(cd ..; rm -rf %{name}-%{version} ${RPM_BUILD_ROOT})
|
||||
|
||||
|
||||
%files -f file-lists
|
||||
%defattr (-, root, root)
|
||||
%doc AUTHORS
|
||||
%doc COPYING
|
||||
%doc ChangeLog
|
||||
%doc INSTALL
|
||||
%doc NEWS
|
||||
%doc README
|
||||
%doc %{name}.spec.in
|
||||
|
||||
@@ -13,13 +13,4 @@ set(SIMGEAR_SOUND @ENABLE_SOUND@)
|
||||
# find_dependency(OpenAL)
|
||||
#endif()
|
||||
|
||||
# SSE/SSE2 support
|
||||
|
||||
set(ENABLE_SIMD @ENABLE_SIMD@)
|
||||
|
||||
# Alternative terrain engine based on pagedLOD
|
||||
|
||||
set(ENABLE_GDAL @ENABLE_GDAL@)
|
||||
set(ENABLE_OPENMP @ENABLE_OPENMP@)
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/SimGearTargets.cmake")
|
||||
|
||||
@@ -5,7 +5,6 @@ foreach( mylibfolder
|
||||
bucket
|
||||
bvh
|
||||
debug
|
||||
embedded_resources
|
||||
ephemeris
|
||||
io
|
||||
magvar
|
||||
@@ -173,11 +172,6 @@ if(NOT SIMGEAR_HEADLESS)
|
||||
${OPENGL_LIBRARY}
|
||||
${JPEG_LIBRARY})
|
||||
|
||||
if(ENABLE_GDAL)
|
||||
target_link_libraries(SimGearScene
|
||||
${GDAL_LIBRARIES})
|
||||
endif()
|
||||
|
||||
# only actually needed by canvas/KeyboardEvent.cxx
|
||||
target_include_directories(SimGearScene PRIVATE ${PROJECT_SOURCE_DIR}/3rdparty/utf8/source)
|
||||
endif()
|
||||
|
||||
@@ -35,124 +35,123 @@ using std::endl;
|
||||
|
||||
void testBucketSpans()
|
||||
{
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
void testBasic()
|
||||
{
|
||||
SGBucket b1(5.1, 55.05);
|
||||
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());
|
||||
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());
|
||||
|
||||
SGBucket b2(-10.1, -43.8);
|
||||
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());
|
||||
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());
|
||||
|
||||
SGBucket b3(123.48, 9.01);
|
||||
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());
|
||||
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());
|
||||
|
||||
SGBucket defBuck;
|
||||
SG_VERIFY(!defBuck.isValid());
|
||||
VERIFY(!defBuck.isValid());
|
||||
|
||||
b3.make_bad();
|
||||
SG_VERIFY(!b3.isValid());
|
||||
VERIFY(!b3.isValid());
|
||||
|
||||
SGBucket atAntiMeridian(180.0, 12.3);
|
||||
SG_VERIFY(atAntiMeridian.isValid());
|
||||
SG_CHECK_EQUAL(atAntiMeridian.get_chunk_lon(), -180);
|
||||
SG_CHECK_EQUAL(atAntiMeridian.get_x(), 0);
|
||||
|
||||
VERIFY(atAntiMeridian.isValid());
|
||||
COMPARE(atAntiMeridian.get_chunk_lon(), -180);
|
||||
COMPARE(atAntiMeridian.get_x(), 0);
|
||||
|
||||
SGBucket atAntiMeridian2(-180.0, -78.1);
|
||||
SG_VERIFY(atAntiMeridian2.isValid());
|
||||
SG_CHECK_EQUAL(atAntiMeridian2.get_chunk_lon(), -180);
|
||||
SG_CHECK_EQUAL(atAntiMeridian2.get_x(), 0);
|
||||
|
||||
VERIFY(atAntiMeridian2.isValid());
|
||||
COMPARE(atAntiMeridian2.get_chunk_lon(), -180);
|
||||
COMPARE(atAntiMeridian2.get_x(), 0);
|
||||
|
||||
// check comparisom operator overload
|
||||
SGBucket b4(5.11, 55.1);
|
||||
SG_VERIFY(b1 == b4); // should be equal
|
||||
SG_VERIFY(b1 == b1);
|
||||
SG_VERIFY(b1 != defBuck);
|
||||
SG_VERIFY(b1 != b2);
|
||||
VERIFY(b1 == b4); // should be equal
|
||||
VERIFY(b1 == b1);
|
||||
VERIFY(b1 != defBuck);
|
||||
VERIFY(b1 != b2);
|
||||
|
||||
// check wrapping/clipping of inputs
|
||||
SGBucket wrapMeridian(-200.0, 45.0);
|
||||
SG_CHECK_EQUAL(wrapMeridian.get_chunk_lon(), 160);
|
||||
|
||||
COMPARE(wrapMeridian.get_chunk_lon(), 160);
|
||||
|
||||
SGBucket clipPole(48.9, 91);
|
||||
SG_CHECK_EQUAL(clipPole.get_chunk_lat(), 89);
|
||||
COMPARE(clipPole.get_chunk_lat(), 89);
|
||||
}
|
||||
|
||||
void testPolar()
|
||||
{
|
||||
SGBucket b1(0.0, 89.92);
|
||||
SGBucket b2(10.0, 89.96);
|
||||
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(b1.get_chunk_lat(), 89);
|
||||
COMPARE(b1.get_chunk_lon(), 0);
|
||||
COMPARE(b1.get_x(), 0);
|
||||
COMPARE(b1.get_y(), 7);
|
||||
|
||||
SG_CHECK_EQUAL_EP(b1.get_highest_lat(), 90.0);
|
||||
SG_CHECK_EQUAL_EP(b1.get_width_m(), 10.0);
|
||||
COMPARE_EP(b1.get_highest_lat(), 90.0);
|
||||
COMPARE_EP(b1.get_width_m(), 10.0);
|
||||
|
||||
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(b2.get_chunk_lat(), 89);
|
||||
COMPARE(b2.get_chunk_lon(), 0);
|
||||
COMPARE(b2.get_x(), 0);
|
||||
COMPARE(b2.get_y(), 7);
|
||||
|
||||
SG_CHECK_EQUAL(b1.gen_index(), b2.gen_index());
|
||||
COMPARE(b1.gen_index(), b2.gen_index());
|
||||
|
||||
SGGeod actualNorthPole1 = b1.get_corner(2);
|
||||
SGGeod actualNorthPole2 = b1.get_corner(3);
|
||||
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);
|
||||
COMPARE_EP(actualNorthPole1.getLatitudeDeg(), 90.0);
|
||||
COMPARE_EP(actualNorthPole1.getLongitudeDeg(), 12.0);
|
||||
COMPARE_EP(actualNorthPole2.getLatitudeDeg(), 90.0);
|
||||
COMPARE_EP(actualNorthPole2.getLongitudeDeg(), 0.0);
|
||||
|
||||
SGBucket b3(-2, 89.88);
|
||||
SGBucket b4(-7, 89.88);
|
||||
SG_CHECK_EQUAL(b3.gen_index(), b4.gen_index());
|
||||
COMPARE(b3.gen_index(), b4.gen_index());
|
||||
|
||||
// south pole
|
||||
SGBucket b5(-170, -89.88);
|
||||
SGBucket b6(-179, -89.88);
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
SGGeod actualSouthPole1 = b5.get_corner(0);
|
||||
SGGeod actualSouthPole2 = b5.get_corner(1);
|
||||
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);
|
||||
COMPARE_EP(actualSouthPole1.getLatitudeDeg(), -90.0);
|
||||
COMPARE_EP(actualSouthPole1.getLongitudeDeg(), -180);
|
||||
COMPARE_EP(actualSouthPole2.getLatitudeDeg(), -90.0);
|
||||
COMPARE_EP(actualSouthPole2.getLongitudeDeg(), -168);
|
||||
|
||||
SGBucket b7(200, 89.88);
|
||||
SG_CHECK_EQUAL(b7.get_chunk_lon(), -168);
|
||||
COMPARE(b7.get_chunk_lon(), -168);
|
||||
|
||||
}
|
||||
|
||||
@@ -161,15 +160,15 @@ void testNearPolar()
|
||||
{
|
||||
SGBucket b1(1, 88.5);
|
||||
SGBucket b2(-1, 88.8);
|
||||
SG_CHECK_EQUAL(b1.get_chunk_lon(), 0);
|
||||
SG_CHECK_EQUAL(b1.get_chunk_lat(), 88);
|
||||
SG_VERIFY(b1.gen_index() != b2.gen_index());
|
||||
COMPARE(b1.get_chunk_lon(), 0);
|
||||
COMPARE(b1.get_chunk_lat(), 88);
|
||||
VERIFY(b1.gen_index() != b2.gen_index());
|
||||
|
||||
SGBucket b3(176.1, 88.5);
|
||||
SG_CHECK_EQUAL(b3.get_chunk_lon(), 176);
|
||||
COMPARE(b3.get_chunk_lon(), 176);
|
||||
|
||||
SGBucket b4(-178, 88.5);
|
||||
SG_CHECK_EQUAL(b4.get_chunk_lon(), -180);
|
||||
COMPARE(b4.get_chunk_lon(), -180);
|
||||
}
|
||||
|
||||
void testOffset()
|
||||
@@ -177,74 +176,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);
|
||||
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);
|
||||
COMPARE(b1.get_chunk_lat(), 21);
|
||||
COMPARE(b1.get_chunk_lon(), -60);
|
||||
COMPARE(b1.get_x(), 1);
|
||||
COMPARE(b1.get_y(), 7);
|
||||
|
||||
// offset vertically
|
||||
SGBucket b2(b1.sibling(0, 1));
|
||||
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.get_chunk_lat(), 22);
|
||||
COMPARE(b2.get_chunk_lon(), -60);
|
||||
COMPARE(b2.get_x(), 0);
|
||||
COMPARE(b2.get_y(), 0);
|
||||
|
||||
SG_CHECK_EQUAL(b2.gen_index(), sgBucketOffset(-59.8, 21.9, 0, 1));
|
||||
COMPARE(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));
|
||||
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.get_chunk_lat(), 22);
|
||||
COMPARE(b3.get_chunk_lon(), -61);
|
||||
COMPARE(b3.get_x(), 1);
|
||||
COMPARE(b3.get_y(), 0);
|
||||
|
||||
SG_CHECK_EQUAL(b3.gen_index(), sgBucketOffset(-59.8, 21.9, -3, 1));
|
||||
COMPARE(b3.gen_index(), sgBucketOffset(-59.8, 21.9, -3, 1));
|
||||
}
|
||||
|
||||
void testPolarOffset()
|
||||
{
|
||||
SGBucket b1(-11.7, -89.6);
|
||||
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);
|
||||
COMPARE(b1.get_chunk_lat(), -90);
|
||||
COMPARE(b1.get_chunk_lon(), -12);
|
||||
COMPARE(b1.get_x(), 0);
|
||||
COMPARE(b1.get_y(), 3);
|
||||
|
||||
// offset horizontally
|
||||
SGBucket b2(b1.sibling(-2, 0));
|
||||
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.get_chunk_lat(), -90);
|
||||
COMPARE(b2.get_chunk_lon(), -36);
|
||||
COMPARE(b2.get_x(), 0);
|
||||
COMPARE(b2.get_y(), 3);
|
||||
|
||||
SG_CHECK_EQUAL(b2.gen_index(), sgBucketOffset(-11.7, -89.6, -2, 0));
|
||||
COMPARE(b2.gen_index(), sgBucketOffset(-11.7, -89.6, -2, 0));
|
||||
|
||||
// offset and wrap
|
||||
SGBucket b3(-170, 89.1);
|
||||
SGBucket b4(b3.sibling(-1, 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.get_chunk_lat(), 89);
|
||||
COMPARE(b4.get_chunk_lon(), 168);
|
||||
COMPARE(b4.get_x(), 0);
|
||||
COMPARE(b4.get_y(), 0);
|
||||
|
||||
SG_CHECK_EQUAL(b4.gen_index(), sgBucketOffset(-170, 89.1, -1, 0));
|
||||
COMPARE(b4.gen_index(), sgBucketOffset(-170, 89.1, -1, 0));
|
||||
|
||||
|
||||
SGBucket b5(177, 87.3);
|
||||
SGBucket b6(b5.sibling(1, 1));
|
||||
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.get_chunk_lat(), 87);
|
||||
COMPARE(b6.get_chunk_lon(), -180);
|
||||
COMPARE(b6.get_x(), 0);
|
||||
COMPARE(b6.get_y(), 3);
|
||||
|
||||
SG_CHECK_EQUAL(b6.gen_index(), sgBucketOffset(177, 87.3, 1, 1));
|
||||
COMPARE(b6.gen_index(), sgBucketOffset(177, 87.3, 1, 1));
|
||||
|
||||
// offset vertically towards the pole
|
||||
SGBucket b7(b1.sibling(0, -5));
|
||||
SG_VERIFY(!b7.isValid());
|
||||
VERIFY(!b7.isValid());
|
||||
|
||||
SG_VERIFY(!SGBucket(0, 90).sibling(0, 1).isValid());
|
||||
VERIFY(!SGBucket(0, 90).sibling(0, 1).isValid());
|
||||
}
|
||||
|
||||
// test behaviour of bucket-offset near the anti-meridian (180-meridian)
|
||||
@@ -252,17 +251,17 @@ void testOffsetWrap()
|
||||
{
|
||||
// near the equator
|
||||
SGBucket b1(-179.8, 16.8);
|
||||
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);
|
||||
COMPARE(b1.get_chunk_lat(), 16);
|
||||
COMPARE(b1.get_chunk_lon(), -180);
|
||||
COMPARE(b1.get_x(), 1);
|
||||
COMPARE(b1.get_y(), 6);
|
||||
|
||||
SGBucket b2(b1.sibling(-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));
|
||||
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));
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "BVHMaterial.hxx"
|
||||
|
||||
namespace simgear {
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "BVHPageNode.hxx"
|
||||
|
||||
#include "BVHPager.hxx"
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "BVHPageRequest.hxx"
|
||||
|
||||
namespace simgear {
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "BVHPager.hxx"
|
||||
|
||||
#include <list>
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "BVHStaticNode.hxx"
|
||||
|
||||
namespace simgear {
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include <iostream>
|
||||
#include <simgear/structure/SGSharedPtr.hxx>
|
||||
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "Canvas.hxx"
|
||||
#include "CanvasEventManager.hxx"
|
||||
#include "CanvasEventVisitor.hxx"
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "CanvasEvent.hxx"
|
||||
|
||||
namespace simgear
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "CanvasEventManager.hxx"
|
||||
#include <simgear/canvas/events/MouseEvent.hxx>
|
||||
#include <simgear/canvas/elements/CanvasElement.hxx>
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "CanvasEvent.hxx"
|
||||
#include "CanvasEventVisitor.hxx"
|
||||
#include <simgear/canvas/elements/CanvasElement.hxx>
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "CanvasMgr.hxx"
|
||||
#include "Canvas.hxx"
|
||||
#include "CanvasEventManager.hxx"
|
||||
|
||||
@@ -19,8 +19,6 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "Canvas.hxx"
|
||||
#include "CanvasObjectPlacement.hxx"
|
||||
#include <simgear/canvas/events/MouseEvent.hxx>
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "CanvasPlacement.hxx"
|
||||
#include <simgear/props/props.hxx>
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "CanvasMgr.hxx"
|
||||
#include "CanvasSystemAdapter.hxx"
|
||||
#include "CanvasWindow.hxx"
|
||||
|
||||
@@ -44,11 +44,6 @@
|
||||
#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,33 +35,21 @@ void SHVector2_dtor(SHVector2 *v) {
|
||||
}
|
||||
|
||||
void SHVector3_ctor(SHVector3 *v) {
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
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 SHIVA_USE_SIMD
|
||||
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 SHIVA_USE_SIMD
|
||||
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) {
|
||||
@@ -147,24 +135,3 @@ int shLineLineXsection(SHVector2 *o1, SHVector2 *v1,
|
||||
xsection->y = o1->y + t1*v1->y;
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# 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,21 +21,6 @@
|
||||
#ifndef __SHVECTORS_H
|
||||
#define __SHVECTORS_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <simgear/simgear_config.h>
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_SIMD
|
||||
# ifdef __SSE__
|
||||
// # define SHIVA_USE_SIMD
|
||||
# endif
|
||||
# endif
|
||||
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# include <xmmintrin.h>
|
||||
float hsum_ps_sse(__m128 v);
|
||||
#endif
|
||||
|
||||
#include "shDefs.h"
|
||||
|
||||
/* Vector structures
|
||||
@@ -48,17 +33,9 @@ typedef struct
|
||||
void SHVector2_ctor(SHVector2 *v);
|
||||
void SHVector2_dtor(SHVector2 *v);
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
union ALIGN16 {
|
||||
__m128 vec;
|
||||
struct { SHfloat x,y,z,w; };
|
||||
} ALIGN16C;
|
||||
#else
|
||||
SHfloat x,y,z;
|
||||
#endif
|
||||
} SHVector3;
|
||||
|
||||
void SHVector3_ctor(SHVector3 *v);
|
||||
@@ -66,14 +43,7 @@ void SHVector3_dtor(SHVector3 *v);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
union ALIGN16 {
|
||||
__m128 vec;
|
||||
struct { SHfloat x,y,z,w; };
|
||||
} ALIGN16C;
|
||||
#else
|
||||
SHfloat x,y,z,w;
|
||||
#endif
|
||||
} SHVector4;
|
||||
|
||||
void SHVector4_ctor(SHVector4 *v);
|
||||
@@ -81,14 +51,7 @@ void SHVector4_dtor(SHVector4 *v);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
union ALIGN16 {
|
||||
__m128 vec;
|
||||
struct { SHfloat x,y,w,h; };
|
||||
} ALIGN16C;
|
||||
#else
|
||||
SHfloat x,y,w,h;
|
||||
#endif
|
||||
} SHRectangle;
|
||||
|
||||
void SHRectangle_ctor(SHRectangle *r);
|
||||
@@ -98,14 +61,7 @@ void shRectangleSet(SHRectangle *r, SHfloat x,
|
||||
|
||||
typedef struct
|
||||
{
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
union ALIGN16 {
|
||||
__m128 mtx[4];
|
||||
SHfloat m[4][4];
|
||||
} ALIGN16C;
|
||||
#else
|
||||
SHfloat m[3][3];
|
||||
#endif
|
||||
} SHMatrix3x3;
|
||||
|
||||
void SHMatrix3x3_ctor(SHMatrix3x3 *m);
|
||||
@@ -127,22 +83,12 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
|
||||
*--------------------------------------------------------- */
|
||||
|
||||
#define SET2(v,xs,ys) { v.x=xs; v.y=ys; }
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# 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 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; }
|
||||
|
||||
#define SET2V(v1,v2) { v1.x=v2.x; v1.y=v2.y; }
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# 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 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; }
|
||||
|
||||
#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 )
|
||||
@@ -157,89 +103,48 @@ 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; }
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# 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 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; }
|
||||
|
||||
#define ADD2V(v1,v2) { v1.x+=v2.x; v1.y+=v2.y; }
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# 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 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; }
|
||||
|
||||
#define SUB2(v,xx,yy) { v.x-=xx; v.y-=yy; }
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# 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 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; }
|
||||
|
||||
#define SUB2V(v1,v2) { v1.x-=v2.x; v1.y-=v2.y; }
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# 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 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; }
|
||||
|
||||
#define MUL2(v,f) { v.x*=f; v.y*=f; }
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# 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 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; }
|
||||
|
||||
#define DIV2(v,f) { v.x/=f; v.y/=f; }
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# 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 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; }
|
||||
|
||||
#define ABS2(v) { v.x=SH_ABS(v.x); v.y=SH_ABS(v.y); }
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# 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 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); }
|
||||
|
||||
#define NORMSQ2(v) DOT2(v,v)
|
||||
#define NORMSQ3(v) DOT3(v,v)
|
||||
#define NORMSQ4(v) DOT4(v,v)
|
||||
#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 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); DIV2(v,n); }
|
||||
#define NORMALIZE3(v) { SHfloat n=NORM3(v); DIV3(v,n); }
|
||||
#define NORMALIZE4(v) { SHfloat n=NORM4(v); DIV4(v,n); }
|
||||
#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 DOT2(v1,v2) (v1.x*v2.x + v1.y*v2.y)
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# 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 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)
|
||||
|
||||
#define CROSS2(v1,v2) (v1.x*v2.y - v2.x*v1.y)
|
||||
|
||||
@@ -247,84 +152,37 @@ 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; }
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# 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
|
||||
#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; }
|
||||
|
||||
/*-----------------------------------------------------
|
||||
* Macros for matrix operations
|
||||
*-----------------------------------------------------*/
|
||||
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# 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; \
|
||||
#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
|
||||
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# 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) { \
|
||||
#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
|
||||
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# 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) { \
|
||||
#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
|
||||
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# 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) { \
|
||||
#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
|
||||
|
||||
#ifdef SHIVA_USE_SIMD
|
||||
# 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) { \
|
||||
#define MULMATMAT(m1, m2, mout) { \
|
||||
int i,j; \
|
||||
for(i=0;i<3;i++) \
|
||||
for(j=0;j<3;j++) \
|
||||
@@ -332,7 +190,6 @@ 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)
|
||||
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "CanvasElement.hxx"
|
||||
#include <simgear/canvas/Canvas.hxx>
|
||||
#include <simgear/canvas/CanvasEventVisitor.hxx>
|
||||
@@ -642,27 +640,6 @@ 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
|
||||
{
|
||||
@@ -724,9 +701,6 @@ 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,22 +172,6 @@ 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)
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "CanvasGroup.hxx"
|
||||
#include "CanvasImage.hxx"
|
||||
#include "CanvasMap.hxx"
|
||||
@@ -375,7 +373,7 @@ namespace canvas
|
||||
SG_LOG
|
||||
(
|
||||
SG_GENERAL,
|
||||
SG_DEBUG,
|
||||
SG_INFO,
|
||||
"canvas::Group: Moved element " << index << " to position " << index_new
|
||||
);
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "CanvasImage.hxx"
|
||||
|
||||
#include <simgear/canvas/Canvas.hxx>
|
||||
@@ -489,17 +487,11 @@ namespace canvas
|
||||
if( !fill.empty() // If no color is given default to white
|
||||
&& !parseColor(fill, color) )
|
||||
return;
|
||||
setFill(color);
|
||||
|
||||
_colors->front() = color;
|
||||
_colors->dirty();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setFill(const osg::Vec4& color)
|
||||
{
|
||||
_colors->front() = color;
|
||||
_colors->dirty();
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setOutset(const std::string& outset)
|
||||
{
|
||||
@@ -514,13 +506,6 @@ 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,7 +61,6 @@ 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
|
||||
@@ -96,10 +95,6 @@ namespace canvas
|
||||
|
||||
bool handleEvent(const EventPtr& event);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void setSourceRect(const SGRect<float>& sourceRect);
|
||||
protected:
|
||||
|
||||
enum ImageAttributes
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "CanvasMap.hxx"
|
||||
#include "map/geo_node_pair.hxx"
|
||||
#include "map/projection.hxx"
|
||||
|
||||
@@ -16,18 +16,14 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#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
|
||||
{
|
||||
@@ -61,153 +57,6 @@ 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
|
||||
{
|
||||
@@ -684,9 +533,7 @@ namespace canvas
|
||||
const Style& parent_style,
|
||||
ElementWeakPtr parent ):
|
||||
Element(canvas, node, parent_style, parent),
|
||||
_path( new PathDrawable(this) ),
|
||||
_hasSVG(false),
|
||||
_hasRect(false)
|
||||
_path( new PathDrawable(this) )
|
||||
{
|
||||
staticInit();
|
||||
|
||||
@@ -714,23 +561,6 @@ 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);
|
||||
}
|
||||
|
||||
@@ -800,75 +630,16 @@ 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( name == "cmd" )
|
||||
if( child->getNameString() == "cmd" )
|
||||
_attributes_dirty |= CMDS;
|
||||
else if( name == "coord" )
|
||||
else if( child->getNameString() == "coord" )
|
||||
_attributes_dirty |= COORDS;
|
||||
else if ( name == "svg")
|
||||
_hasSVG = true;
|
||||
_attributes_dirty |= SVG;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -893,67 +664,5 @@ 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,7 +21,6 @@
|
||||
|
||||
#include "CanvasElement.hxx"
|
||||
#include <boost/preprocessor/iteration/iterate.hpp>
|
||||
#include <simgear/math/SGRect.hxx>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
@@ -68,30 +67,18 @@ 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,
|
||||
SVG = COORDS << 1,
|
||||
RECT = SVG << 1
|
||||
COORDS = CMDS << 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);
|
||||
};
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "CanvasText.hxx"
|
||||
#include <simgear/canvas/Canvas.hxx>
|
||||
#include <simgear/canvas/CanvasSystemAdapter.hxx>
|
||||
@@ -497,31 +496,18 @@ namespace canvas
|
||||
{
|
||||
case LEFT_TO_RIGHT:
|
||||
{
|
||||
#if OSG_VERSION_LESS_THAN(3,5,2)
|
||||
osg::Vec2 delta(activefont->getKerning(previous_charcode,
|
||||
charcode,
|
||||
_kerningType));
|
||||
#else
|
||||
osg::Vec2 delta(activefont->getKerning(_fontSize,
|
||||
previous_charcode,
|
||||
charcode,
|
||||
_kerningType));
|
||||
#endif
|
||||
osg::Vec2 delta( activefont->getKerning( previous_charcode,
|
||||
charcode,
|
||||
_kerningType ) );
|
||||
cursor.x() += delta.x() * wr;
|
||||
cursor.y() += delta.y() * hr;
|
||||
break;
|
||||
}
|
||||
case RIGHT_TO_LEFT:
|
||||
{
|
||||
#if OSG_VERSION_LESS_THAN(3,5,2)
|
||||
osg::Vec2 delta(activefont->getKerning(charcode,
|
||||
previous_charcode,
|
||||
_kerningType));
|
||||
#else
|
||||
osg::Vec2 delta(activefont->getKerning(_fontSize, charcode,
|
||||
previous_charcode,
|
||||
_kerningType));
|
||||
#endif
|
||||
osg::Vec2 delta( activefont->getKerning( charcode,
|
||||
previous_charcode,
|
||||
_kerningType ) );
|
||||
cursor.x() -= delta.x() * wr;
|
||||
cursor.y() -= delta.y() * hr;
|
||||
break;
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "CustomEvent.hxx"
|
||||
|
||||
namespace simgear
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "DeviceEvent.hxx"
|
||||
#include <osgGA/GUIEventAdapter>
|
||||
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "KeyboardEvent.hxx"
|
||||
#include "utf8.h"
|
||||
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "MouseEvent.hxx"
|
||||
#include <osgGA/GUIEventAdapter>
|
||||
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "KeyboardEvent.hxx"
|
||||
|
||||
#include <osgViewer/Viewer>
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "BoxLayout.hxx"
|
||||
#include "SpacerItem.hxx"
|
||||
#include <simgear/canvas/Canvas.hxx>
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "Layout.hxx"
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "LayoutItem.hxx"
|
||||
#include <simgear/canvas/Canvas.hxx>
|
||||
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "NasalWidget.hxx"
|
||||
|
||||
#include <simgear/canvas/Canvas.hxx>
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "SpacerItem.hxx"
|
||||
|
||||
namespace simgear
|
||||
|
||||
@@ -109,13 +109,10 @@
|
||||
# define SG_UNIX
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define SG_DEPRECATED(func) func __attribute__ ((deprecated))
|
||||
#elif defined(_MSC_VER)
|
||||
#define SG_DEPRECATED(func) __declspec(deprecated) func
|
||||
#if defined( __GNUC__ )
|
||||
# define DEPRECATED __attribute__ ((deprecated))
|
||||
#else
|
||||
#pragma message("WARNING: You need to implement SG_DEPRECATED for this compiler")
|
||||
#define SG_DEPRECATED(func) func
|
||||
# define DEPRECATED
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
|
||||
@@ -18,8 +18,7 @@
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <simgear/debug/BufferedLogCallback.hxx>
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#define SG_DEBUG_BUFFEREDLOGCALLBACK_HXX
|
||||
|
||||
#include <vector>
|
||||
#include <memory> // for std::unique_ptr
|
||||
#include <memory> // for std::auto_ptr
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
@@ -70,10 +70,10 @@ public:
|
||||
unsigned int threadsafeCopy(vector_cstring& aOutput);
|
||||
private:
|
||||
class BufferedLogCallbackPrivate;
|
||||
std::unique_ptr<BufferedLogCallbackPrivate> d;
|
||||
std::auto_ptr<BufferedLogCallbackPrivate> d;
|
||||
};
|
||||
|
||||
|
||||
} // of namespace simgear
|
||||
|
||||
#endif // of SG_DEBUG_BUFFEREDLOGCALLBACK_HXX
|
||||
#endif // of SG_DEBUG_BUFFEREDLOGCALLBACK_HXX
|
||||
@@ -42,10 +42,6 @@ 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
|
||||
@@ -53,11 +49,8 @@ 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,7 +36,7 @@
|
||||
#include <simgear/threads/SGQueue.hxx>
|
||||
#include <simgear/threads/SGGuard.hxx>
|
||||
|
||||
#include <simgear/io/iostreams/sgstream.hxx>
|
||||
#include <simgear/misc/sgstream.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
|
||||
#if defined (SG_WINDOWS)
|
||||
@@ -46,7 +46,39 @@
|
||||
#include <io.h>
|
||||
#endif
|
||||
|
||||
|
||||
const char* debugClassToString(sgDebugClass c)
|
||||
{
|
||||
switch (c) {
|
||||
case SG_NONE: return "none";
|
||||
case SG_TERRAIN: return "terrain";
|
||||
case SG_ASTRO: return "astro";
|
||||
case SG_FLIGHT: return "flight";
|
||||
case SG_INPUT: return "input";
|
||||
case SG_GL: return "opengl";
|
||||
case SG_VIEW: return "view";
|
||||
case SG_COCKPIT: return "cockpit";
|
||||
case SG_GENERAL: return "general";
|
||||
case SG_MATH: return "math";
|
||||
case SG_EVENT: return "event";
|
||||
case SG_AIRCRAFT: return "aircraft";
|
||||
case SG_AUTOPILOT: return "autopilot";
|
||||
case SG_IO: return "io";
|
||||
case SG_CLIPPER: return "clipper";
|
||||
case SG_NETWORK: return "network";
|
||||
case SG_ATC: return "atc";
|
||||
case SG_NASAL: return "nasal";
|
||||
case SG_INSTR: return "instruments";
|
||||
case SG_SYSTEMS: return "systems";
|
||||
case SG_AI: return "ai";
|
||||
case SG_ENVIRONMENT:return "environment";
|
||||
case SG_SOUND: return "sound";
|
||||
case SG_NAVAID: return "navaid";
|
||||
case SG_GUI: return "gui";
|
||||
case SG_TERRASYNC: return "terrasync";
|
||||
case SG_PARTICLES: return "particles";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -70,40 +102,6 @@ void LogCallback::setLogLevels( sgDebugClass c, sgDebugPriority p )
|
||||
m_class = c;
|
||||
}
|
||||
|
||||
const char* LogCallback::debugClassToString(sgDebugClass c)
|
||||
{
|
||||
switch (c) {
|
||||
case SG_NONE: return "none";
|
||||
case SG_TERRAIN: return "terrain";
|
||||
case SG_ASTRO: return "astro";
|
||||
case SG_FLIGHT: return "flight";
|
||||
case SG_INPUT: return "input";
|
||||
case SG_GL: return "opengl";
|
||||
case SG_VIEW: return "view";
|
||||
case SG_COCKPIT: return "cockpit";
|
||||
case SG_GENERAL: return "general";
|
||||
case SG_MATH: return "math";
|
||||
case SG_EVENT: return "event";
|
||||
case SG_AIRCRAFT: return "aircraft";
|
||||
case SG_AUTOPILOT: return "autopilot";
|
||||
case SG_IO: return "io";
|
||||
case SG_CLIPPER: return "clipper";
|
||||
case SG_NETWORK: return "network";
|
||||
case SG_ATC: return "atc";
|
||||
case SG_NASAL: return "nasal";
|
||||
case SG_INSTR: return "instruments";
|
||||
case SG_SYSTEMS: return "systems";
|
||||
case SG_AI: return "ai";
|
||||
case SG_ENVIRONMENT:return "environment";
|
||||
case SG_SOUND: return "sound";
|
||||
case SG_NAVAID: return "navaid";
|
||||
case SG_GUI: return "gui";
|
||||
case SG_TERRASYNC: return "terrasync";
|
||||
case SG_PARTICLES: return "particles";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
} // of namespace simgear
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
@@ -179,13 +177,13 @@ public:
|
||||
|
||||
#endif
|
||||
|
||||
class logstream::LogStreamPrivate : public SGThread
|
||||
class LogStreamPrivate : public SGThread
|
||||
{
|
||||
private:
|
||||
/**
|
||||
* 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
|
||||
* 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.
|
||||
*/
|
||||
class LogEntry
|
||||
{
|
||||
@@ -197,24 +195,19 @@ private:
|
||||
{
|
||||
}
|
||||
|
||||
const sgDebugClass debugClass;
|
||||
const sgDebugPriority debugPriority;
|
||||
sgDebugClass debugClass;
|
||||
sgDebugPriority debugPriority;
|
||||
const char* file;
|
||||
const int line;
|
||||
const std::string message;
|
||||
int line;
|
||||
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)
|
||||
, m_wasRunning(m_parent->stop())
|
||||
PauseThread(LogStreamPrivate* parent) : m_parent(parent)
|
||||
{
|
||||
m_wasRunning = m_parent->stop();
|
||||
}
|
||||
|
||||
~PauseThread()
|
||||
@@ -225,13 +218,17 @@ private:
|
||||
}
|
||||
private:
|
||||
LogStreamPrivate* m_parent;
|
||||
const bool m_wasRunning;
|
||||
bool m_wasRunning;
|
||||
};
|
||||
|
||||
public:
|
||||
LogStreamPrivate() :
|
||||
m_logClass(SG_ALL),
|
||||
m_logPriority(SG_ALERT)
|
||||
m_logPriority(SG_ALERT),
|
||||
#if defined (SG_WINDOWS)
|
||||
m_stdout_isRedirectedAlready(false),
|
||||
m_stderr_isRedirectedAlready(false),
|
||||
#endif
|
||||
m_isRunning(false)
|
||||
{
|
||||
#if defined (SG_WINDOWS)
|
||||
/*
|
||||
@@ -335,7 +332,7 @@ public:
|
||||
|
||||
~LogStreamPrivate()
|
||||
{
|
||||
for (simgear::LogCallback* cb : m_callbacks) {
|
||||
BOOST_FOREACH(simgear::LogCallback* cb, m_callbacks) {
|
||||
delete cb;
|
||||
}
|
||||
}
|
||||
@@ -343,10 +340,6 @@ 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,
|
||||
@@ -355,13 +348,12 @@ public:
|
||||
|
||||
sgDebugClass m_logClass;
|
||||
sgDebugPriority m_logPriority;
|
||||
bool m_isRunning = false;
|
||||
bool m_isRunning;
|
||||
#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;
|
||||
bool m_stderr_isRedirectedAlready;
|
||||
bool m_stdout_isRedirectedAlready;
|
||||
#endif
|
||||
bool m_developerMode = false;
|
||||
|
||||
void startLog()
|
||||
{
|
||||
@@ -371,16 +363,6 @@ public:
|
||||
start();
|
||||
}
|
||||
|
||||
void setStartupLoggingEnabled(bool on)
|
||||
{
|
||||
if (m_startupLogging == on) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_startupLogging = on;
|
||||
m_startupEntries.clear();
|
||||
}
|
||||
|
||||
virtual void run()
|
||||
{
|
||||
while (1) {
|
||||
@@ -391,14 +373,8 @@ 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
|
||||
for (simgear::LogCallback* cb : m_callbacks) {
|
||||
BOOST_FOREACH(simgear::LogCallback* cb, m_callbacks) {
|
||||
(*cb)(entry.debugClass, entry.debugPriority,
|
||||
entry.file, entry.line, entry.message);
|
||||
}
|
||||
@@ -425,13 +401,6 @@ 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)
|
||||
@@ -455,7 +424,6 @@ 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);
|
||||
}
|
||||
@@ -463,126 +431,53 @@ 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;
|
||||
}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static std::unique_ptr<logstream> global_logstream;
|
||||
static logstream* global_logstream = NULL;
|
||||
static LogStreamPrivate* global_privateLogstream = NULL;
|
||||
static SGMutex global_logStreamLock;
|
||||
|
||||
logstream::logstream()
|
||||
{
|
||||
d.reset(new LogStreamPrivate);
|
||||
d->startLog();
|
||||
global_privateLogstream = new LogStreamPrivate;
|
||||
global_privateLogstream->startLog();
|
||||
}
|
||||
|
||||
logstream::~logstream()
|
||||
{
|
||||
popup_msgs.clear();
|
||||
d->stop();
|
||||
global_privateLogstream->stop();
|
||||
delete global_privateLogstream;
|
||||
}
|
||||
|
||||
void
|
||||
logstream::setLogLevels( sgDebugClass c, sgDebugPriority p )
|
||||
{
|
||||
d->setLogLevels(c, p);
|
||||
global_privateLogstream->setLogLevels(c, p);
|
||||
}
|
||||
|
||||
void logstream::setDeveloperMode(bool devMode)
|
||||
{
|
||||
d->m_developerMode = devMode;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
logstream::addCallback(simgear::LogCallback* cb)
|
||||
{
|
||||
d->addCallback(cb);
|
||||
global_privateLogstream->addCallback(cb);
|
||||
}
|
||||
|
||||
void
|
||||
logstream::removeCallback(simgear::LogCallback* cb)
|
||||
{
|
||||
d->removeCallback(cb);
|
||||
global_privateLogstream->removeCallback(cb);
|
||||
}
|
||||
|
||||
void
|
||||
logstream::log( sgDebugClass c, sgDebugPriority p,
|
||||
const char* fileName, int line, const std::string& msg)
|
||||
{
|
||||
d->log(c, p, fileName, line, msg);
|
||||
}
|
||||
|
||||
|
||||
void logstream::hexdump(sgDebugClass c, sgDebugPriority p, const char* fileName, int line, const void *mem, unsigned int len, unsigned int columns)
|
||||
{
|
||||
unsigned int i, j;
|
||||
char temp[3000], temp1[3000];
|
||||
*temp = 0;
|
||||
|
||||
for (i = 0; i < len + ((len % columns) ? (columns - len % columns) : 0); i++)
|
||||
{
|
||||
if (strlen(temp) > 500) return;
|
||||
/* print offset */
|
||||
if (i % columns == 0)
|
||||
{
|
||||
sprintf(temp1, "0x%06x: ", i);
|
||||
strcat(temp, temp1);
|
||||
}
|
||||
|
||||
/* print hex data */
|
||||
if (i < len)
|
||||
{
|
||||
sprintf(temp1, "%02x ", 0xFF & ((char*)mem)[i]);
|
||||
strcat(temp, temp1);
|
||||
}
|
||||
else /* end of block, just aligning for ASCII dump */
|
||||
{
|
||||
strcat(temp, " ");
|
||||
}
|
||||
|
||||
/* print ASCII dump */
|
||||
if (i % columns == (columns - 1))
|
||||
{
|
||||
for (j = i - (columns - 1); j <= i; j++)
|
||||
{
|
||||
if (j >= len) /* end of block, not really printing */
|
||||
{
|
||||
strcat(temp, " ");
|
||||
}
|
||||
else if (((((char*)mem)[j]) & (char)0x7f) > 32) /* printable char */
|
||||
{
|
||||
char t2[2];
|
||||
t2[0] = 0xFF & ((char*)mem)[j];
|
||||
t2[1] = 0;
|
||||
strcat(temp, t2);
|
||||
}
|
||||
else /* other char */
|
||||
{
|
||||
strcat(temp, ".");
|
||||
}
|
||||
}
|
||||
log(c, p, fileName, line, temp );
|
||||
*temp = 0;
|
||||
}
|
||||
}
|
||||
global_privateLogstream->log(c, p, fileName, line, msg);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -612,31 +507,31 @@ logstream::has_popup()
|
||||
bool
|
||||
logstream::would_log( sgDebugClass c, sgDebugPriority p ) const
|
||||
{
|
||||
return d->would_log(c,p);
|
||||
return global_privateLogstream->would_log(c,p);
|
||||
}
|
||||
|
||||
sgDebugClass
|
||||
logstream::get_log_classes() const
|
||||
{
|
||||
return d->m_logClass;
|
||||
return global_privateLogstream->m_logClass;
|
||||
}
|
||||
|
||||
sgDebugPriority
|
||||
logstream::get_log_priority() const
|
||||
{
|
||||
return d->m_logPriority;
|
||||
return global_privateLogstream->m_logPriority;
|
||||
}
|
||||
|
||||
void
|
||||
logstream::set_log_priority( sgDebugPriority p)
|
||||
{
|
||||
d->setLogLevels(d->m_logClass, p);
|
||||
global_privateLogstream->setLogLevels(global_privateLogstream->m_logClass, p);
|
||||
}
|
||||
|
||||
void
|
||||
logstream::set_log_classes( sgDebugClass c)
|
||||
{
|
||||
d->setLogLevels(c, d->m_logPriority);
|
||||
global_privateLogstream->setLogLevels(c, global_privateLogstream->m_logPriority);
|
||||
}
|
||||
|
||||
|
||||
@@ -652,54 +547,49 @@ sglog()
|
||||
SGGuard<SGMutex> g(global_logStreamLock);
|
||||
|
||||
if( !global_logstream )
|
||||
global_logstream.reset(new logstream);
|
||||
return *(global_logstream.get());
|
||||
global_logstream = new logstream();
|
||||
return *global_logstream;
|
||||
}
|
||||
|
||||
void
|
||||
logstream::logToFile( const SGPath& aPath, sgDebugClass c, sgDebugPriority p )
|
||||
{
|
||||
d->addCallback(new FileLogCallback(aPath, c, p));
|
||||
global_privateLogstream->addCallback(new FileLogCallback(aPath, c, p));
|
||||
}
|
||||
|
||||
void logstream::setStartupLoggingEnabled(bool enabled)
|
||||
namespace simgear
|
||||
{
|
||||
d->setStartupLoggingEnabled(enabled);
|
||||
}
|
||||
|
||||
void logstream::requestConsole()
|
||||
{
|
||||
void requestConsole()
|
||||
{
|
||||
#if defined (SG_WINDOWS)
|
||||
const bool stderrAlreadyRedirected = d->m_stderr_isRedirectedAlready;
|
||||
const bool stdoutAlreadyRedirected = d->m_stdout_isRedirectedAlready;
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 (!stderrAlreadyRedirected && !stdoutAlreadyRedirected) {
|
||||
if (!global_privateLogstream->m_stderr_isRedirectedAlready && !global_privateLogstream->m_stdout_isRedirectedAlready) {
|
||||
FreeConsole();
|
||||
if (AllocConsole()) {
|
||||
if (!stdoutAlreadyRedirected)
|
||||
if (!global_privateLogstream->m_stdout_isRedirectedAlready)
|
||||
freopen("conout$", "w", stdout);
|
||||
|
||||
if (!stderrAlreadyRedirected)
|
||||
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.
|
||||
//Clear the error state for each of the C++ standard stream objects.
|
||||
std::wcout.clear();
|
||||
std::cout.clear();
|
||||
std::wcerr.clear();
|
||||
@@ -712,19 +602,11 @@ void logstream::requestConsole()
|
||||
}
|
||||
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
void requestConsole()
|
||||
{
|
||||
sglog().requestConsole();
|
||||
}
|
||||
|
||||
|
||||
void shutdownLogging()
|
||||
{
|
||||
SGGuard<SGMutex> g(global_logStreamLock);
|
||||
global_logstream.reset();
|
||||
delete global_logstream;
|
||||
global_logstream = 0;
|
||||
}
|
||||
|
||||
} // of namespace simgear
|
||||
|
||||
@@ -30,8 +30,7 @@
|
||||
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
|
||||
// forward decls
|
||||
class SGPath;
|
||||
|
||||
@@ -50,8 +49,6 @@ protected:
|
||||
LogCallback(sgDebugClass c, sgDebugPriority p);
|
||||
|
||||
bool shouldLog(sgDebugClass c, sgDebugPriority p) const;
|
||||
|
||||
static const char* debugClassToString(sgDebugClass c);
|
||||
private:
|
||||
sgDebugClass m_class;
|
||||
sgDebugPriority m_priority;
|
||||
@@ -77,14 +74,6 @@ public:
|
||||
~logstream();
|
||||
|
||||
static void initGlobalLogstream();
|
||||
|
||||
/**
|
||||
* Helper force a console on platforms where it might optional, when
|
||||
* we need to show a console. This basically means Windows at the
|
||||
* moment - on other plaforms it's a no-op
|
||||
*/
|
||||
void requestConsole();
|
||||
|
||||
/**
|
||||
* Set the global log class and priority level.
|
||||
* @param c debug class
|
||||
@@ -104,25 +93,12 @@ 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
|
||||
*/
|
||||
void log( sgDebugClass c, sgDebugPriority p,
|
||||
const char* fileName, int line, const std::string& msg);
|
||||
|
||||
/**
|
||||
* output formatted hex dump of memory block
|
||||
*/
|
||||
void hexdump(sgDebugClass c, sgDebugPriority p, const char* fileName, int line, const void *mem, unsigned int len, unsigned int columns = 16);
|
||||
|
||||
|
||||
/**
|
||||
* support for the SG_POPUP logging class
|
||||
* set the content of the popup message
|
||||
@@ -158,21 +134,11 @@ 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();
|
||||
|
||||
std::vector<std::string> popup_msgs;
|
||||
|
||||
class LogStreamPrivate;
|
||||
|
||||
std::unique_ptr<LogStreamPrivate> d;
|
||||
};
|
||||
|
||||
logstream& sglog();
|
||||
@@ -189,14 +155,12 @@ logstream& sglog();
|
||||
do { if(sglog().would_log(C,P)) { \
|
||||
std::ostringstream os; os << M; \
|
||||
sglog().log(C, P, __FILE__, __LINE__, os.str()); \
|
||||
if ((P) == SG_POPUP) sglog().popup(os.str()); \
|
||||
if (P == SG_POPUP) sglog().popup(os.str()); \
|
||||
} } while(0)
|
||||
#ifdef FG_NDEBUG
|
||||
# define SG_LOG(C,P,M) do { if((P) == SG_POPUP) SG_LOGX(C,P,M) } while(0)
|
||||
# define SG_HEXDUMP(C,P,MEM,LEN)
|
||||
# define SG_LOG(C,P,M) do { if(P == SG_POPUP) SG_LOGX(C,P,M) } while(0)
|
||||
#else
|
||||
# define SG_LOG(C,P,M) SG_LOGX(C,P,M)
|
||||
# define SG_LOG_HEXDUMP(C,P,MEM,LEN) if(sglog().would_log(C,P)) sglog().hexdump(C, P, __FILE__, __LINE__, MEM, LEN)
|
||||
#endif
|
||||
|
||||
#define SG_ORIGIN __FILE__ ":" SG_STRINGIZE(__LINE__)
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
include (SimGearComponent)
|
||||
|
||||
set(HEADERS EmbeddedResource.hxx EmbeddedResourceManager.hxx)
|
||||
set(SOURCES EmbeddedResource.cxx EmbeddedResourceManager.cxx)
|
||||
|
||||
simgear_component(embedded_resources embedded_resources
|
||||
"${SOURCES}" "${HEADERS}")
|
||||
|
||||
if(ENABLE_TESTS)
|
||||
add_executable(test_embedded_resources embedded_resources_test.cxx)
|
||||
target_link_libraries(test_embedded_resources ${TEST_LIBS})
|
||||
add_test(embedded_resources
|
||||
${EXECUTABLE_OUTPUT_PATH}/test_embedded_resources)
|
||||
endif(ENABLE_TESTS)
|
||||
@@ -1,265 +0,0 @@
|
||||
// -*- coding: utf-8 -*-
|
||||
//
|
||||
// EmbeddedResource.cxx --- Class for pointing to/accessing an embedded resource
|
||||
// Copyright (C) 2017 Florent Rougon
|
||||
//
|
||||
// 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 Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA.
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <string>
|
||||
#include <iosfwd>
|
||||
#include <ios> // std::streamsize
|
||||
#include <ostream>
|
||||
#include <memory> // std::unique_ptr
|
||||
#include <utility> // std::move()
|
||||
#include <algorithm> // std::min()
|
||||
#include <limits> // std::numeric_limits
|
||||
#include <cstddef> // std::size_t, std::ptrdiff_t
|
||||
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include <simgear/io/iostreams/CharArrayStream.hxx>
|
||||
#include <simgear/io/iostreams/zlibstream.hxx>
|
||||
#include "EmbeddedResource.hxx"
|
||||
|
||||
using std::string;
|
||||
using std::unique_ptr;
|
||||
|
||||
// Inspired by <http://stackoverflow.com/a/21174979/4756009>
|
||||
template<typename Derived, typename Base>
|
||||
static unique_ptr<Derived> static_unique_ptr_cast(unique_ptr<Base> p)
|
||||
{
|
||||
auto d = static_cast<Derived *>(p.release());
|
||||
return unique_ptr<Derived>(d);
|
||||
}
|
||||
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
// ***************************************************************************
|
||||
// * AbstractEmbeddedResource class *
|
||||
// ***************************************************************************
|
||||
|
||||
AbstractEmbeddedResource::AbstractEmbeddedResource(const char *data,
|
||||
std::size_t size)
|
||||
: _data(data),
|
||||
_size(size)
|
||||
{ }
|
||||
|
||||
const char *AbstractEmbeddedResource::rawPtr() const
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
|
||||
std::size_t AbstractEmbeddedResource::rawSize() const
|
||||
{
|
||||
return _size;
|
||||
}
|
||||
|
||||
string AbstractEmbeddedResource::str() const
|
||||
{
|
||||
if (_size > std::numeric_limits<string::size_type>::max()) {
|
||||
throw sg_range_exception(
|
||||
"Resource too large to fit in an std::string (size: " +
|
||||
std::to_string(_size) + " bytes)");
|
||||
}
|
||||
|
||||
return string(_data, _size);
|
||||
}
|
||||
|
||||
// ***************************************************************************
|
||||
// * RawEmbeddedResource class *
|
||||
// ***************************************************************************
|
||||
|
||||
RawEmbeddedResource::RawEmbeddedResource(const char *data, std::size_t size)
|
||||
: AbstractEmbeddedResource(data, size)
|
||||
{ }
|
||||
|
||||
AbstractEmbeddedResource::CompressionType
|
||||
RawEmbeddedResource::compressionType() const
|
||||
{
|
||||
return AbstractEmbeddedResource::CompressionType::NONE;
|
||||
}
|
||||
|
||||
string RawEmbeddedResource::compressionDescr() const
|
||||
{
|
||||
return string("none");
|
||||
}
|
||||
|
||||
unique_ptr<std::streambuf> RawEmbeddedResource::streambuf() const
|
||||
{
|
||||
// This is a read-only variant of CharArrayStreambuf
|
||||
return unique_ptr<std::streambuf>(
|
||||
new ROCharArrayStreambuf(rawPtr(), rawSize()));
|
||||
}
|
||||
|
||||
unique_ptr<std::istream> RawEmbeddedResource::istream() const
|
||||
{
|
||||
return unique_ptr<std::istream>(new CharArrayIStream(rawPtr(), rawSize()));
|
||||
}
|
||||
|
||||
// ***************************************************************************
|
||||
// * ZlibEmbeddedResource class *
|
||||
// ***************************************************************************
|
||||
|
||||
ZlibEmbeddedResource::ZlibEmbeddedResource(const char *data,
|
||||
std::size_t compressedSize,
|
||||
std::size_t uncompressedSize)
|
||||
: AbstractEmbeddedResource(data, compressedSize),
|
||||
_uncompressedSize(uncompressedSize),
|
||||
_inBuf(nullptr),
|
||||
_inBufSize(262144), // adjusted below in the constructor body
|
||||
_outBuf(nullptr),
|
||||
_outBufSize(262144),
|
||||
_putbackSize(0) // default for best performance
|
||||
{
|
||||
static_assert(262144 <= std::numeric_limits<std::size_t>::max(),
|
||||
"The std::size_t type is unexpectedly small.");
|
||||
// No need to use an input buffer (where compressed data chunks are put for
|
||||
// zlib to read and decompress) larger than the whole compressed resource!
|
||||
_inBufSize = std::min(rawSize(), _inBufSize);
|
||||
}
|
||||
|
||||
AbstractEmbeddedResource::CompressionType
|
||||
ZlibEmbeddedResource::compressionType() const
|
||||
{ return AbstractEmbeddedResource::CompressionType::ZLIB; }
|
||||
|
||||
string ZlibEmbeddedResource::compressionDescr() const
|
||||
{ return string("zlib"); }
|
||||
|
||||
std::size_t ZlibEmbeddedResource::uncompressedSize() const
|
||||
{ return _uncompressedSize; }
|
||||
|
||||
char* ZlibEmbeddedResource::getInputBufferStart()
|
||||
{ return _inBuf; }
|
||||
|
||||
void ZlibEmbeddedResource::setInputBufferStart(char* inBuf)
|
||||
{ _inBuf = inBuf; }
|
||||
|
||||
std::size_t ZlibEmbeddedResource::getInputBufferSize()
|
||||
{ return _inBufSize; }
|
||||
|
||||
void ZlibEmbeddedResource::setInputBufferSize(std::size_t size)
|
||||
{ _inBufSize = size; }
|
||||
|
||||
char* ZlibEmbeddedResource::getOutputBufferStart()
|
||||
{ return _outBuf; }
|
||||
|
||||
void ZlibEmbeddedResource::setOutputBufferStart(char* outBuf)
|
||||
{ _outBuf = outBuf; }
|
||||
|
||||
std::size_t ZlibEmbeddedResource::getOutputBufferSize()
|
||||
{ return _outBufSize; }
|
||||
|
||||
void ZlibEmbeddedResource::setOutputBufferSize(std::size_t size)
|
||||
{ _outBufSize = size; }
|
||||
|
||||
std::size_t ZlibEmbeddedResource::getPutbackSize()
|
||||
{ return _putbackSize; }
|
||||
|
||||
void ZlibEmbeddedResource::setPutbackSize(std::size_t size)
|
||||
{ _putbackSize = size; }
|
||||
|
||||
unique_ptr<std::streambuf> ZlibEmbeddedResource::streambuf() const
|
||||
{
|
||||
unique_ptr<CharArrayIStream> rawReaderIStream(
|
||||
new CharArrayIStream(rawPtr(), rawSize()));
|
||||
|
||||
return unique_ptr<std::streambuf>(
|
||||
new ZlibDecompressorIStreambuf(
|
||||
std::move(rawReaderIStream),
|
||||
SGPath(), // rawReaderIStream isn't bound to a file
|
||||
ZLibCompressionFormat::ZLIB,
|
||||
_inBuf, _inBufSize, _outBuf, _outBufSize, _putbackSize));
|
||||
}
|
||||
|
||||
unique_ptr<std::istream> ZlibEmbeddedResource::istream() const
|
||||
{
|
||||
unique_ptr<CharArrayIStream> rawReaderIStream(
|
||||
new CharArrayIStream(rawPtr(), rawSize()));
|
||||
|
||||
return unique_ptr<std::istream>(
|
||||
new ZlibDecompressorIStream(
|
||||
std::move(rawReaderIStream),
|
||||
SGPath(), // rawReaderIStream isn't bound to a file
|
||||
ZLibCompressionFormat::ZLIB,
|
||||
_inBuf, _inBufSize, _outBuf, _outBufSize, _putbackSize));
|
||||
}
|
||||
|
||||
std::string ZlibEmbeddedResource::str() const
|
||||
{
|
||||
static constexpr std::size_t bufSize = 65536;
|
||||
static_assert(bufSize <= std::numeric_limits<std::streamsize>::max(),
|
||||
"Type std::streamsize is unexpectedly small");
|
||||
static_assert(bufSize <= std::numeric_limits<string::size_type>::max(),
|
||||
"Type std::string::size_type is unexpectedly small");
|
||||
unique_ptr<char[]> buf(new char[bufSize]);
|
||||
|
||||
auto decompressor =
|
||||
static_unique_ptr_cast<ZlibDecompressorIStream>(istream());
|
||||
std::streamsize nbCharsRead;
|
||||
string result;
|
||||
|
||||
if (_uncompressedSize > std::numeric_limits<string::size_type>::max()) {
|
||||
throw sg_range_exception(
|
||||
"Resource too large to fit in an std::string (uncompressed size: "
|
||||
+ std::to_string(_uncompressedSize) + " bytes)");
|
||||
} else {
|
||||
result.reserve(static_cast<string::size_type>(_uncompressedSize));
|
||||
}
|
||||
|
||||
do {
|
||||
decompressor->read(buf.get(), bufSize);
|
||||
nbCharsRead = decompressor->gcount();
|
||||
|
||||
if (nbCharsRead > 0) {
|
||||
result.append(buf.get(), nbCharsRead);
|
||||
}
|
||||
} while (*decompressor);
|
||||
|
||||
// decompressor->fail() would *not* indicate an error, due to the semantics
|
||||
// of std::istream::read().
|
||||
if (decompressor->bad()) {
|
||||
throw sg_io_exception("Error while extracting a compressed resource");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// ***************************************************************************
|
||||
// * Stream insertion operators *
|
||||
// ***************************************************************************
|
||||
std::ostream& operator<<(std::ostream& os,
|
||||
const RawEmbeddedResource& resource)
|
||||
{ // This won't escape double quotes, backslashes, etc. in resource.str().
|
||||
return os << "RawEmbeddedResource:\n"
|
||||
" compressionType = \"" << resource.compressionDescr() << "\"\n"
|
||||
" rawPtr = " << (void*) resource.rawPtr() << "\n"
|
||||
" rawSize = " << resource.rawSize();
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os,
|
||||
const ZlibEmbeddedResource& resource)
|
||||
{ // This won't escape double quotes, backslashes, etc. in resource.str().
|
||||
return os << "ZlibEmbeddedResource:\n"
|
||||
" compressionType = \"" << resource.compressionDescr() << "\"\n"
|
||||
" rawPtr = " << (void*) resource.rawPtr() << "\n"
|
||||
" rawSize = " << resource.rawSize() << "\n"
|
||||
" uncompressedSize = " << resource.uncompressedSize();
|
||||
}
|
||||
|
||||
} // of namespace simgear
|
||||
@@ -1,163 +0,0 @@
|
||||
// -*- coding: utf-8 -*-
|
||||
//
|
||||
// EmbeddedResource.hxx --- Class for pointing to/accessing an embedded resource
|
||||
// Copyright (C) 2017 Florent Rougon
|
||||
//
|
||||
// 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 Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA.
|
||||
|
||||
#ifndef FG_EMBEDDEDRESOURCE_HXX
|
||||
#define FG_EMBEDDEDRESOURCE_HXX
|
||||
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
#include <ostream>
|
||||
#include <memory> // std::unique_ptr
|
||||
#include <cstddef> // std::size_t, std::ptrdiff_t
|
||||
|
||||
#include <simgear/io/iostreams/zlibstream.hxx>
|
||||
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
// Abstract base class for embedded resources
|
||||
class AbstractEmbeddedResource
|
||||
{
|
||||
public:
|
||||
enum class CompressionType {
|
||||
NONE = 0,
|
||||
ZLIB
|
||||
};
|
||||
|
||||
// Constructor.
|
||||
//
|
||||
// 'data' and 'size' indicate the resource contents. There is no requirement
|
||||
// of null-termination, including for text data (given how
|
||||
// EmbeddedResourceManager::getString() works, including a null terminator
|
||||
// for text contents is actually counter-productive). The data may be of
|
||||
// arbitrary type and size: binary, text, whatever. The constructed object
|
||||
// (for derived classes since this one is abstract) does *not* hold a copy
|
||||
// of the data, it just keeps a pointer to it and provides methods to access
|
||||
// it. The data must therefore remain available as long as the object is in
|
||||
// use---this class was designed for use with data stored in static
|
||||
// variables.
|
||||
explicit AbstractEmbeddedResource(const char *data, std::size_t size);
|
||||
AbstractEmbeddedResource(const AbstractEmbeddedResource&) = default;
|
||||
AbstractEmbeddedResource(AbstractEmbeddedResource&&) = default;
|
||||
AbstractEmbeddedResource& operator=(const AbstractEmbeddedResource&) = default;
|
||||
AbstractEmbeddedResource& operator=(AbstractEmbeddedResource&&) = default;
|
||||
virtual ~AbstractEmbeddedResource() = default;
|
||||
|
||||
// Return the pointer to beginning-of-resource contents---the same that was
|
||||
// passed to the constructor.
|
||||
const char *rawPtr() const;
|
||||
// Return the resource size, as passed to the constructor. For a compressed
|
||||
// resource, this is the compressed size; such resources provide an
|
||||
// additional uncompressedSize() method.
|
||||
std::size_t rawSize() const;
|
||||
|
||||
// Return an std::string object containing a copy of the resource contents.
|
||||
// For a compressed resource, this is the data obtained after decompression.
|
||||
virtual std::string str() const;
|
||||
// Return an std::streambuf instance providing read-only access to the
|
||||
// resource contents (in uncompressed form for compressed resources). This
|
||||
// allows memory-friendly access to large resources by enabling incremental
|
||||
// processing with transparent decompression for compressed resources.
|
||||
virtual std::unique_ptr<std::streambuf> streambuf() const = 0;
|
||||
// Return an std::istream instance providing read-only access to the
|
||||
// resource contents (in uncompressed form for compressed resources).
|
||||
//
|
||||
// The same remark as for streambuf() applies. std::istream is simply a
|
||||
// higher-level interface than std::streambuf, otherwise both allow the same
|
||||
// kind of processing.
|
||||
virtual std::unique_ptr<std::istream> istream() const = 0;
|
||||
|
||||
// Return the resource compression type.
|
||||
virtual CompressionType compressionType() const = 0;
|
||||
// Return a string description of the resource compression type. Examples:
|
||||
// "none", "zlib".
|
||||
virtual std::string compressionDescr() const = 0;
|
||||
|
||||
private:
|
||||
// Pointer to the start of resource contents
|
||||
const char *_data;
|
||||
// Size of resource contents, in bytes
|
||||
std::size_t _size;
|
||||
};
|
||||
|
||||
// Class to describe an uncompressed resource. See AbstractEmbeddedResource.
|
||||
class RawEmbeddedResource : public AbstractEmbeddedResource
|
||||
{
|
||||
public:
|
||||
explicit RawEmbeddedResource(const char *data, std::size_t size);
|
||||
|
||||
AbstractEmbeddedResource::CompressionType compressionType() const override;
|
||||
std::string compressionDescr() const override;
|
||||
|
||||
// The str() method is inherited from AbstractEmbeddedResource
|
||||
std::unique_ptr<std::streambuf> streambuf() const override;
|
||||
std::unique_ptr<std::istream> istream() const override;
|
||||
};
|
||||
|
||||
// Class to describe a zlib-compressed resource.
|
||||
//
|
||||
// Instances of this class point to resource contents stored in the stream
|
||||
// format documented in RFC 1950.
|
||||
class ZlibEmbeddedResource : public AbstractEmbeddedResource
|
||||
{
|
||||
public:
|
||||
explicit ZlibEmbeddedResource(const char *data, std::size_t compressedSize,
|
||||
std::size_t uncompressedSize);
|
||||
|
||||
AbstractEmbeddedResource::CompressionType compressionType() const override;
|
||||
std::string compressionDescr() const override;
|
||||
// Return the resource uncompressed size, in bytes.
|
||||
std::size_t uncompressedSize() const;
|
||||
|
||||
std::string str() const override;
|
||||
std::unique_ptr<std::streambuf> streambuf() const override;
|
||||
std::unique_ptr<std::istream> istream() const override;
|
||||
|
||||
// Getters and setters for parameters used in streambuf() and istream().
|
||||
// Calling any of the setters affects the subsequent streambuf() and
|
||||
// istream() calls.
|
||||
char* getInputBufferStart();
|
||||
void setInputBufferStart(char* inBuf);
|
||||
std::size_t getInputBufferSize();
|
||||
void setInputBufferSize(std::size_t size);
|
||||
char* getOutputBufferStart();
|
||||
void setOutputBufferStart(char* outBuf);
|
||||
std::size_t getOutputBufferSize();
|
||||
void setOutputBufferSize(std::size_t size);
|
||||
std::size_t getPutbackSize();
|
||||
void setPutbackSize(std::size_t size);
|
||||
|
||||
private:
|
||||
std::size_t _uncompressedSize;
|
||||
char* _inBuf;
|
||||
std::size_t _inBufSize;
|
||||
char* _outBuf;
|
||||
std::size_t _outBufSize;
|
||||
std::size_t _putbackSize;
|
||||
};
|
||||
|
||||
// These functions are essentially intended for troubleshooting purposes.
|
||||
std::ostream& operator<<(std::ostream&, const RawEmbeddedResource&);
|
||||
std::ostream& operator<<(std::ostream&, const ZlibEmbeddedResource&);
|
||||
|
||||
} // of namespace simgear
|
||||
|
||||
#endif // of FG_EMBEDDEDRESOURCE_HXX
|
||||
@@ -1,235 +0,0 @@
|
||||
// -*- coding: utf-8 -*-
|
||||
//
|
||||
// EmbeddedResourceManager.cxx --- Manager class for resources embedded in an
|
||||
// executable
|
||||
// Copyright (C) 2017 Florent Rougon
|
||||
//
|
||||
// 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 Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA.
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <memory>
|
||||
#include <utility> // std::move()
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cstdlib>
|
||||
#include <cassert>
|
||||
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include "EmbeddedResource.hxx"
|
||||
#include "EmbeddedResourceManager.hxx"
|
||||
#include "EmbeddedResourceManager_private.hxx"
|
||||
|
||||
using std::string;
|
||||
using std::shared_ptr;
|
||||
using std::unique_ptr;
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
static unique_ptr<EmbeddedResourceManager> staticInstance;
|
||||
|
||||
// ***************************************************************************
|
||||
// * EmbeddedResourceManager::Impl *
|
||||
// ***************************************************************************
|
||||
EmbeddedResourceManager::Impl::Impl()
|
||||
: dirty(true)
|
||||
{ }
|
||||
|
||||
void
|
||||
EmbeddedResourceManager::Impl::rehash()
|
||||
{
|
||||
// Update the list of resource pools to search when looking up a resource.
|
||||
// This allows to optimize resource lookup: no need to parse, split and hash
|
||||
// the same locale string every time to find the corresponding resource
|
||||
// pools.
|
||||
poolSearchList = listOfResourcePoolsToSearch(selectedLocale);
|
||||
dirty = false;
|
||||
}
|
||||
|
||||
string
|
||||
EmbeddedResourceManager::Impl::getLocale() const
|
||||
{
|
||||
return selectedLocale;
|
||||
}
|
||||
|
||||
string
|
||||
EmbeddedResourceManager::Impl::selectLocale(const std::string& locale)
|
||||
{
|
||||
string previousLocale = std::move(selectedLocale);
|
||||
selectedLocale = locale;
|
||||
dirty = true;
|
||||
|
||||
return previousLocale;
|
||||
}
|
||||
|
||||
// Static method
|
||||
std::vector<string>
|
||||
EmbeddedResourceManager::Impl::localesSearchList(const string& locale)
|
||||
{
|
||||
std::vector<string> result;
|
||||
|
||||
if (locale.empty()) {
|
||||
result.push_back(string()); // only the default locale
|
||||
} else {
|
||||
std::size_t sepIdx = locale.find_first_of('_');
|
||||
|
||||
if (sepIdx == string::npos) {
|
||||
// Try the given “locale” first (e.g., fr), then the default locale
|
||||
result = std::vector<string>({locale, string()});
|
||||
} else {
|
||||
string langCode = locale.substr(0, sepIdx);
|
||||
// Try the given “locale” first (e.g., fr_FR), then the language code
|
||||
// (e.g., fr) and finally the default locale
|
||||
result = std::vector<string>({locale, langCode, string()});
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
auto
|
||||
EmbeddedResourceManager::Impl::listOfResourcePoolsToSearch(
|
||||
const string& locale) const
|
||||
-> std::vector< shared_ptr<ResourcePool> >
|
||||
{
|
||||
std::vector<string> searchedLocales = localesSearchList(locale);
|
||||
std::vector< shared_ptr<ResourcePool> > result;
|
||||
|
||||
for (const string& loc: searchedLocales) {
|
||||
auto poolPtrIt = localeToResourcePoolMap.find(loc);
|
||||
// Don't store pointers to empty resource pools in 'result'. This
|
||||
// optimizes resource fetching a little bit, but requires that all
|
||||
// resources are added before this method is called.
|
||||
if (poolPtrIt != localeToResourcePoolMap.end()) {
|
||||
// Copy a shared_ptr<ResourcePool>
|
||||
result.push_back(poolPtrIt->second);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Static method
|
||||
shared_ptr<const AbstractEmbeddedResource>
|
||||
EmbeddedResourceManager::Impl::lookupResourceInPools(
|
||||
const string& virtualPath,
|
||||
const std::vector< shared_ptr<ResourcePool> >& aPoolSearchList)
|
||||
{
|
||||
// Search the provided resource pools in proper order. For instance, the one
|
||||
// for 'fr_FR', then the one for 'fr' and finally the one for the default
|
||||
// locale. Return the first resource found in one of these pools.
|
||||
for (const shared_ptr<ResourcePool>& poolPtr: aPoolSearchList) {
|
||||
auto resourcePtrIt = poolPtr->find(virtualPath);
|
||||
|
||||
if (resourcePtrIt != poolPtr->end()) {
|
||||
// Copy a shared_ptr<const AbstractEmbeddedResource>
|
||||
return resourcePtrIt->second;
|
||||
}
|
||||
}
|
||||
|
||||
return shared_ptr<const AbstractEmbeddedResource>(); // null shared_ptr object
|
||||
}
|
||||
|
||||
void
|
||||
EmbeddedResourceManager::Impl::addResource(
|
||||
const string& virtualPath,
|
||||
unique_ptr<const AbstractEmbeddedResource> resourcePtr,
|
||||
const string& locale)
|
||||
{
|
||||
// Find the resource pool corresponding to the specified locale
|
||||
shared_ptr<ResourcePool>& resPoolPtr = localeToResourcePoolMap[locale];
|
||||
if (!resPoolPtr) {
|
||||
resPoolPtr.reset(new ResourcePool());
|
||||
}
|
||||
|
||||
auto emplaceRetval = resPoolPtr->emplace(virtualPath, std::move(resourcePtr));
|
||||
|
||||
if (!emplaceRetval.second) {
|
||||
const string localeDescr =
|
||||
(locale.empty()) ? "the default locale" : "locale '" + locale + "'";
|
||||
throw sg_error(
|
||||
"Virtual path already in use for " + localeDescr +
|
||||
" in the EmbeddedResourceManager: '" + virtualPath + "'");
|
||||
}
|
||||
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
// ***************************************************************************
|
||||
// * EmbeddedResourceManager *
|
||||
// ***************************************************************************
|
||||
EmbeddedResourceManager::EmbeddedResourceManager()
|
||||
: p(unique_ptr<Impl>(new Impl))
|
||||
{ }
|
||||
|
||||
const unique_ptr<EmbeddedResourceManager>&
|
||||
EmbeddedResourceManager::createInstance()
|
||||
{
|
||||
staticInstance.reset(new EmbeddedResourceManager);
|
||||
return staticInstance;
|
||||
}
|
||||
|
||||
const unique_ptr<EmbeddedResourceManager>&
|
||||
EmbeddedResourceManager::instance()
|
||||
{
|
||||
return staticInstance;
|
||||
}
|
||||
|
||||
string
|
||||
EmbeddedResourceManager::getLocale() const
|
||||
{
|
||||
return p->getLocale();
|
||||
}
|
||||
|
||||
string
|
||||
EmbeddedResourceManager::selectLocale(const std::string& locale)
|
||||
{
|
||||
return p->selectLocale(locale);
|
||||
}
|
||||
|
||||
void
|
||||
EmbeddedResourceManager::addResource(
|
||||
const string& virtualPath,
|
||||
unique_ptr<const AbstractEmbeddedResource> resourcePtr,
|
||||
const string& locale)
|
||||
{
|
||||
p->addResource(virtualPath, std::move(resourcePtr), locale);
|
||||
}
|
||||
|
||||
shared_ptr<const AbstractEmbeddedResource>
|
||||
EmbeddedResourceManager::getResourceOrNullPtr(const string& virtualPath) const
|
||||
{
|
||||
if (p->dirty) {
|
||||
p->rehash(); // update p->poolSearchList
|
||||
}
|
||||
|
||||
// Use the selected locale
|
||||
return p->lookupResourceInPools(virtualPath, p->poolSearchList);
|
||||
}
|
||||
|
||||
shared_ptr<const AbstractEmbeddedResource>
|
||||
EmbeddedResourceManager::getResourceOrNullPtr(const string& virtualPath,
|
||||
const string& locale) const
|
||||
{
|
||||
// In this overload, we don't use the cached list of pools
|
||||
// (p->poolSearchList), therefore there is no need to check the 'dirty' flag
|
||||
// or to rehash().
|
||||
return p->lookupResourceInPools(virtualPath,
|
||||
p->listOfResourcePoolsToSearch(locale));
|
||||
}
|
||||
|
||||
} // of namespace simgear
|
||||
@@ -1,204 +0,0 @@
|
||||
// -*- coding: utf-8 -*-
|
||||
//
|
||||
// EmbeddedResourceManager.hxx --- Manager class for resources embedded in an
|
||||
// executable
|
||||
// Copyright (C) 2017 Florent Rougon
|
||||
//
|
||||
// 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 Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA.
|
||||
|
||||
#ifndef FG_EMBEDDEDRESOURCEMANAGER_HXX
|
||||
#define FG_EMBEDDEDRESOURCEMANAGER_HXX
|
||||
|
||||
#include <string>
|
||||
#include <memory> // std::unique_ptr, std::shared_ptr
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <utility> // std::forward()
|
||||
#include <cstddef> // std::size_t
|
||||
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include "EmbeddedResource.hxx"
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
class EmbeddedResourceManager
|
||||
{
|
||||
public:
|
||||
EmbeddedResourceManager(const EmbeddedResourceManager&) = delete;
|
||||
EmbeddedResourceManager& operator=(const EmbeddedResourceManager&) = delete;
|
||||
EmbeddedResourceManager(EmbeddedResourceManager&&) = delete;
|
||||
EmbeddedResourceManager& operator=(EmbeddedResourceManager&&) = delete;
|
||||
// The instance is created by createInstance() -> private constructor
|
||||
// but it should be deleted by its owning std::unique_ptr -> public destructor
|
||||
~EmbeddedResourceManager() = default;
|
||||
|
||||
// Static creator
|
||||
static const std::unique_ptr<EmbeddedResourceManager>& createInstance();
|
||||
|
||||
// Singleton accessor
|
||||
static const std::unique_ptr<EmbeddedResourceManager>& instance();
|
||||
|
||||
// Return the currently-selected “locale”[*] for resource fetching.
|
||||
//
|
||||
// [*] For instance: std::string("") for the default locale
|
||||
// std::string("fr") for French
|
||||
// std::string("fr_FR") for French from France
|
||||
std::string getLocale() const;
|
||||
// Select the locale for which resources will be returned in the future, for
|
||||
// the getResourceOrNullPtr(), getResource(), getString(), getStreambuf()
|
||||
// and getIStream() overloads that don't have a 'locale' parameter.
|
||||
// May be called several times. Return the previously-selected locale.
|
||||
//
|
||||
// If you just want to fetch one or two resources in a particular “locale”
|
||||
// (language), it is simpler to use an overload of one of the
|
||||
// getResourceOrNullPtr(), getResource(), ..., getIStream() methods that has
|
||||
// a 'locale' parameter.
|
||||
std::string selectLocale(const std::string& locale);
|
||||
|
||||
// Add a resource for the specified locale to the embedded resource manager.
|
||||
// This method acts as a sink for its second argument (the std::unique_ptr
|
||||
// typically has to be std::move()d). If 'locale' is empty, the resource is
|
||||
// added for the default locale.
|
||||
void addResource(const std::string& virtualPath,
|
||||
std::unique_ptr<const AbstractEmbeddedResource> resourcePtr,
|
||||
const std::string& locale = std::string());
|
||||
|
||||
// Get access to a resource.
|
||||
//
|
||||
// Fetch the resource for the selected locale (cf. selectLocale()), with
|
||||
// fallback behavior[1]. If no resource is found for the given
|
||||
// 'virtualPath', return a null
|
||||
// std::shared_ptr<const AbstractEmbeddedResource>.
|
||||
//
|
||||
// [1] This means that for instance, if the selected locale is 'es_ES', the
|
||||
// resource is first looked up for the 'es_ES' “locale”; then, if not
|
||||
// found, for 'es'; and finally, if still not found, for the default
|
||||
// locale ''.
|
||||
std::shared_ptr<const AbstractEmbeddedResource> getResourceOrNullPtr(
|
||||
const std::string& virtualPath) const;
|
||||
// Same as the previous overload, except the resource is fetched for the
|
||||
// specified locale (with fallback behavior) instead of for the selected
|
||||
// locale. Use an empty 'locale' parameter to fetch the resource for the
|
||||
// default locale.
|
||||
std::shared_ptr<const AbstractEmbeddedResource> getResourceOrNullPtr(
|
||||
const std::string& virtualPath,
|
||||
const std::string& locale) const;
|
||||
|
||||
// Same overloads as for getResourceOrNullPtr(), except that if the resource
|
||||
// isn't found, then an sg_exception is raised. These methods never return
|
||||
// a null or empty std::shared_ptr<const AbstractEmbeddedResource>.
|
||||
template <typename ...Args>
|
||||
std::shared_ptr<const AbstractEmbeddedResource> getResource(
|
||||
const std::string& virtualPath, Args&& ...args) const
|
||||
{
|
||||
const auto resPtr = getResourceOrNullPtr(virtualPath,
|
||||
std::forward<Args>(args)...);
|
||||
|
||||
if (!resPtr) {
|
||||
throw sg_exception("No embedded resource found at virtual path '" +
|
||||
virtualPath + "'");
|
||||
}
|
||||
|
||||
return resPtr;
|
||||
}
|
||||
|
||||
// Get a resource contents in the form of an std::string. Raise an
|
||||
// sg_exception if no resource is found for the specified 'virtualPath'.
|
||||
//
|
||||
// The returned std::string is a copy of the resource contents (possibly
|
||||
// transparently decompressed, cf. simgear::ZlibEmbeddedResource).
|
||||
template <typename ...Args>
|
||||
std::string getString(const std::string& virtualPath, Args&& ...args) const
|
||||
{
|
||||
return getResource(virtualPath, std::forward<Args>(args)...)->str();
|
||||
}
|
||||
|
||||
// Get access to a resource via an std::streambuf instance. Raise an
|
||||
// sg_exception if no resource is found for the specified 'virtualPath'.
|
||||
//
|
||||
// This allows one to incrementally process the resource contents without
|
||||
// ever making a copy of it (including incremental, transparent
|
||||
// decompression if the resource happens to be compressed---cf.
|
||||
// simgear::ZlibEmbeddedResource).
|
||||
template <typename ...Args>
|
||||
std::unique_ptr<std::streambuf> getStreambuf(const std::string& virtualPath,
|
||||
Args&& ...args) const
|
||||
{
|
||||
return getResource(virtualPath, std::forward<Args>(args)...)->streambuf();
|
||||
}
|
||||
|
||||
// Get access to a resource via an std::istream instance. Raise an
|
||||
// sg_exception if no resource is found for the specified 'virtualPath'.
|
||||
//
|
||||
// The same remarks made for getStreambuf() apply here too.
|
||||
template <typename ...Args>
|
||||
std::unique_ptr<std::istream> getIStream(const std::string& virtualPath,
|
||||
Args&& ...args) const
|
||||
{
|
||||
return getResource(virtualPath, std::forward<Args>(args)...)->istream();
|
||||
}
|
||||
|
||||
private:
|
||||
// Constructor called from createInstance() only
|
||||
explicit EmbeddedResourceManager();
|
||||
|
||||
class Impl;
|
||||
const std::unique_ptr<Impl> p; // Pimpl idiom
|
||||
};
|
||||
|
||||
// Explicit template instantiations
|
||||
template
|
||||
std::shared_ptr<const AbstractEmbeddedResource>
|
||||
EmbeddedResourceManager::getResource(const std::string& virtualPath) const;
|
||||
|
||||
template
|
||||
std::string
|
||||
EmbeddedResourceManager::getString(const std::string& virtualPath) const;
|
||||
|
||||
template
|
||||
std::unique_ptr<std::streambuf>
|
||||
EmbeddedResourceManager::getStreambuf(const std::string& virtualPath) const;
|
||||
|
||||
template
|
||||
std::unique_ptr<std::istream>
|
||||
EmbeddedResourceManager::getIStream(const std::string& virtualPath) const;
|
||||
|
||||
// MSVC doesn't recognize these as template instantiations of what we defined
|
||||
// above (this seems to be with “Visual Studio 14 2015”), therefore only
|
||||
// include them with other compilers.
|
||||
#ifndef _MSC_VER
|
||||
template
|
||||
std::shared_ptr<const AbstractEmbeddedResource>
|
||||
EmbeddedResourceManager::getResource(const std::string& virtualPath,
|
||||
const std::string& locale) const;
|
||||
template
|
||||
std::string
|
||||
EmbeddedResourceManager::getString(const std::string& virtualPath,
|
||||
const std::string& locale) const;
|
||||
template
|
||||
std::unique_ptr<std::streambuf>
|
||||
EmbeddedResourceManager::getStreambuf(const std::string& virtualPath,
|
||||
const std::string& locale) const;
|
||||
template
|
||||
std::unique_ptr<std::istream>
|
||||
EmbeddedResourceManager::getIStream(const std::string& virtualPath,
|
||||
const std::string& locale) const;
|
||||
#endif // #ifndef _MSC_VER
|
||||
|
||||
} // of namespace simgear
|
||||
|
||||
#endif // of FG_EMBEDDEDRESOURCEMANAGER_HXX
|
||||
@@ -1,102 +0,0 @@
|
||||
// -*- coding: utf-8 -*-
|
||||
//
|
||||
// EmbeddedResourceManager_private.hxx --- Private implementation class for
|
||||
// SimGear's EmbeddedResourceManager
|
||||
// Copyright (C) 2017 Florent Rougon
|
||||
//
|
||||
// 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 Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA.
|
||||
|
||||
#ifndef FG_EMBEDDEDRESOURCEMANAGERPRIVATE_HXX
|
||||
#define FG_EMBEDDEDRESOURCEMANAGERPRIVATE_HXX
|
||||
|
||||
#include <string>
|
||||
#include <memory> // std::unique_ptr, std::shared_ptr
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "EmbeddedResource.hxx"
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
class EmbeddedResourceManager::Impl
|
||||
{
|
||||
public:
|
||||
explicit Impl();
|
||||
|
||||
// Each “locale” for which addResource() has been used has an associated
|
||||
// resource pool, that is a sort of directory of all resources declared in
|
||||
// this locale. The resource pool for a given locale (e.g., 'fr' or 'de_DE')
|
||||
// maps resource virtual paths to the corresponding resource descriptors
|
||||
// (via std::shared_ptr<const AbstractEmbeddedResource> instances).
|
||||
//
|
||||
// Note: for optimal lookup performance, a tree would probably be better,
|
||||
// since the expected use for each key here is to store a virtual
|
||||
// path. But such an optimization is likely unneeded in most cases.
|
||||
typedef std::unordered_map< std::string,
|
||||
std::shared_ptr<const AbstractEmbeddedResource> >
|
||||
ResourcePool;
|
||||
|
||||
// Return the list of “locales” to scan to implement fallback behavior when
|
||||
// fetching a resource for the specified locale. This list will be searched
|
||||
// from left to right. Examples:
|
||||
//
|
||||
// "" -> [""]
|
||||
// "fr" -> ["fr", ""]
|
||||
// "fr_FR" -> ["fr_FR", "fr", ""]
|
||||
static std::vector<std::string> localesSearchList(const std::string& locale);
|
||||
// Same as localesSearchList(), except it returns the resource pools instead
|
||||
// of the “locale” strings, and only those pools that are not empty.
|
||||
std::vector< std::shared_ptr<ResourcePool> > listOfResourcePoolsToSearch(
|
||||
const std::string& locale) const;
|
||||
// Look up, in each of the pools referred to by 'poolSearchList', the
|
||||
// resource associated to 'virtualPath'. Return the first match.
|
||||
static std::shared_ptr<const AbstractEmbeddedResource> lookupResourceInPools(
|
||||
const std::string& virtualPath,
|
||||
const std::vector< std::shared_ptr<ResourcePool> >& poolSearchList);
|
||||
|
||||
// Recompute p->poolSearchList. This method is automatically called whenever
|
||||
// needed (lazily), so it doesn't need to be part of the public interface.
|
||||
void rehash();
|
||||
|
||||
// Implement the corresponding EmbeddedResourceManager public methods
|
||||
std::string getLocale() const;
|
||||
std::string selectLocale(const std::string& locale);
|
||||
|
||||
// Ditto
|
||||
void addResource(const std::string& virtualPath,
|
||||
std::unique_ptr<const AbstractEmbeddedResource> resourcePtr,
|
||||
const std::string& locale);
|
||||
|
||||
std::string selectedLocale;
|
||||
// Each call to rehash() updates this member to contain precisely the
|
||||
// (ordered) list of pools to search for a resource in the selected
|
||||
// “locale”. This allows relatively cheap resource lookups, assuming the
|
||||
// desired “locale” doesn't change all the time.
|
||||
std::vector< std::shared_ptr<ResourcePool> > poolSearchList;
|
||||
// Indicate whether 'poolSearchList' must be updated (i.e., resources have
|
||||
// been added or the selected locale was changed without rehash() being
|
||||
// called afterwards).
|
||||
bool dirty;
|
||||
|
||||
// Maps each “locale name” to the corresponding resource pool.
|
||||
std::unordered_map< std::string,
|
||||
std::shared_ptr<ResourcePool> > localeToResourcePoolMap;
|
||||
};
|
||||
|
||||
} // of namespace simgear
|
||||
|
||||
#endif // of FG_EMBEDDEDRESOURCEMANAGERPRIVATE_HXX
|
||||
@@ -1,412 +0,0 @@
|
||||
// -*- coding: utf-8 -*-
|
||||
//
|
||||
// embedded_resources_test.cxx --- Automated tests for the embedded resources
|
||||
// system in SimGear
|
||||
//
|
||||
// Copyright (C) 2017 Florent Rougon
|
||||
//
|
||||
// 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 Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA.
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <ios> // std::streamsize
|
||||
#include <iostream> // std::cout (used for progress info)
|
||||
#include <limits> // std::numeric_limits
|
||||
#include <type_traits> // std::make_unsigned()
|
||||
#include <sstream>
|
||||
#include <cstdlib> // EXIT_SUCCESS
|
||||
#include <cstddef> // std::size_t
|
||||
|
||||
#include <simgear/misc/test_macros.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include <simgear/io/iostreams/CharArrayStream.hxx>
|
||||
#include <simgear/io/iostreams/zlibstream.hxx>
|
||||
#include "EmbeddedResource.hxx"
|
||||
#include "EmbeddedResourceManager.hxx"
|
||||
|
||||
using std::cout;
|
||||
using std::cerr;
|
||||
using std::endl;
|
||||
using std::string;
|
||||
using std::unique_ptr;
|
||||
using std::shared_ptr;
|
||||
using simgear::AbstractEmbeddedResource;
|
||||
using simgear::RawEmbeddedResource;
|
||||
using simgear::ZlibEmbeddedResource;
|
||||
using simgear::EmbeddedResourceManager;
|
||||
|
||||
typedef typename std::make_unsigned<std::streamsize>::type uStreamSize;
|
||||
|
||||
// Safely convert a non-negative std::streamsize into an std::size_t. If
|
||||
// impossible, bail out.
|
||||
std::size_t streamsizeToSize_t(std::streamsize n)
|
||||
{
|
||||
SG_CHECK_GE(n, 0);
|
||||
SG_CHECK_LE(static_cast<uStreamSize>(n),
|
||||
std::numeric_limits<std::size_t>::max());
|
||||
|
||||
return static_cast<std::size_t>(n);
|
||||
}
|
||||
|
||||
// This array is null-terminated, but we'll declare the resource size as
|
||||
// sizeof(res1Array) - 1 so that the null char is *not* part of it. This
|
||||
// way allows one to treat text and binary resources exactly the same way,
|
||||
// with the conversion to std::string via a simple
|
||||
// std::string(res1Array, resourceSize) not producing a bizarre std::string
|
||||
// instance whose last character would be '\0' (followed in memory by the same
|
||||
// '\0' used as C-style string terminator this time!).
|
||||
static const char res1Array[] = "This is a simple embedded resource test.";
|
||||
static const char res1frArray[] = "Ceci est un petit test de ressource "
|
||||
"embarquée.";
|
||||
static const char res1fr_FRArray[] = "Ceci est un petit test de ressource "
|
||||
"embarquée (variante fr_FR).";
|
||||
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.";
|
||||
// Should be enough to store the compressed lipsum (320 bytes are required
|
||||
// with zlib 1.2.8, keeping some room to account for possible future format
|
||||
// changes in the zlib output...). In any case, there is no risk of buffer
|
||||
// overflow, because simgear::CharArrayOStream prevents this by design.
|
||||
static char res2Array[350];
|
||||
static const char res2frArray[] = "Un lorem ipsum un peu plus court...";
|
||||
|
||||
|
||||
// Read data from a string and write it in compressed form to the specified
|
||||
// buffer.
|
||||
std::size_t writeCompressedDataToBuffer(const string& inputString,
|
||||
char *outBuf,
|
||||
std::size_t outBufSize)
|
||||
{
|
||||
simgear::CharArrayOStream res2Writer(outBuf, outBufSize);
|
||||
std::istringstream iss(inputString);
|
||||
simgear::ZlibCompressorIStream compressor(
|
||||
iss,
|
||||
SGPath(), /* no associated file */
|
||||
9 /* highest compression level */);
|
||||
static constexpr std::size_t bufSize = 1024;
|
||||
unique_ptr<char[]> buf(new char[bufSize]);
|
||||
std::size_t res2Size = 0;
|
||||
|
||||
do {
|
||||
compressor.read(buf.get(), bufSize);
|
||||
std::streamsize nBytes = compressor.gcount();
|
||||
if (nBytes > 0) { // at least one char could be read
|
||||
res2Writer.write(buf.get(), nBytes);
|
||||
res2Size += nBytes;
|
||||
}
|
||||
} while (compressor && res2Writer);
|
||||
|
||||
SG_VERIFY(compressor.eof()); // all the compressed data has been read
|
||||
// This would fail (among other causes) if the output buffer were too small
|
||||
// to hold all of the compressed data.
|
||||
SG_VERIFY(res2Writer);
|
||||
|
||||
return res2Size;
|
||||
}
|
||||
|
||||
void initResources()
|
||||
{
|
||||
cout << "Creating the EmbeddedResourceManager instance and adding a few "
|
||||
"resources to it" << endl;
|
||||
const auto& resMgr = EmbeddedResourceManager::createInstance();
|
||||
|
||||
// The resource will *not* consider the null terminator to be in.
|
||||
unique_ptr<const RawEmbeddedResource> res1(
|
||||
new RawEmbeddedResource(res1Array, sizeof(res1Array) - 1));
|
||||
resMgr->addResource("/path/to/resource1", std::move(res1));
|
||||
|
||||
unique_ptr<const RawEmbeddedResource> res1fr(
|
||||
new RawEmbeddedResource(res1frArray, sizeof(res1frArray) - 1));
|
||||
resMgr->addResource("/path/to/resource1", std::move(res1fr), "fr");
|
||||
|
||||
unique_ptr<const RawEmbeddedResource> res1fr_FR(
|
||||
new RawEmbeddedResource(res1fr_FRArray, sizeof(res1fr_FRArray) - 1));
|
||||
resMgr->addResource("/path/to/resource1", std::move(res1fr_FR), "fr_FR");
|
||||
|
||||
// Write the contents of 'lipsum' in compressed form to the 'res2Array'
|
||||
// static buffer.
|
||||
std::size_t res2Size = writeCompressedDataToBuffer(lipsum, res2Array,
|
||||
sizeof(res2Array));
|
||||
// Now we have a compressed resource to work with, plus the corresponding
|
||||
// uncompressed output -> perfect for tests!
|
||||
unique_ptr<const ZlibEmbeddedResource> res2(
|
||||
new ZlibEmbeddedResource(res2Array, res2Size, lipsum.size()));
|
||||
resMgr->addResource("/path/to/resource2", std::move(res2));
|
||||
|
||||
unique_ptr<const RawEmbeddedResource> res2fr(
|
||||
new RawEmbeddedResource(res2frArray, sizeof(res2frArray) - 1));
|
||||
resMgr->addResource("/path/to/resource2", std::move(res2fr), "fr");
|
||||
|
||||
// Explicitly select the default locale (typically, English). This is for
|
||||
// clarity, but isn't required.
|
||||
resMgr->selectLocale("");
|
||||
}
|
||||
|
||||
// Auxiliary function for test_RawEmbeddedResource()
|
||||
void auxTest_RawEmbeddedResource_streambuf()
|
||||
{
|
||||
cout << "Testing EmbeddedResourceManager::getStreambuf()" << endl;
|
||||
const auto& resMgr = EmbeddedResourceManager::instance();
|
||||
|
||||
unique_ptr<std::streambuf> sbuf(resMgr->getStreambuf("/path/to/resource1"));
|
||||
// Just to show an efficient algorithm. For real applications, use larger
|
||||
// buffer sizes!
|
||||
static constexpr std::size_t bufSize = 4;
|
||||
unique_ptr<char[]> buf(new char[bufSize]); // intermediate buffer
|
||||
std::streamsize nbCharsRead;
|
||||
string result;
|
||||
|
||||
do {
|
||||
nbCharsRead = sbuf->sgetn(buf.get(), bufSize);
|
||||
// The conversion to std::size_t is safe because sbuf->sgetn() returned a
|
||||
// non-negative value which, in this case, can't exceed bufSize.
|
||||
result.append(buf.get(), streamsizeToSize_t((nbCharsRead)));
|
||||
} while (nbCharsRead == bufSize);
|
||||
|
||||
SG_CHECK_EQUAL(result, "This is a simple embedded resource test.");
|
||||
}
|
||||
|
||||
// Auxiliary function for test_RawEmbeddedResource()
|
||||
void auxTest_RawEmbeddedResource_istream()
|
||||
{
|
||||
cout << "Testing EmbeddedResourceManager::getIStream()" << endl;
|
||||
const auto& resMgr = EmbeddedResourceManager::instance();
|
||||
|
||||
unique_ptr<std::istream> iStream(resMgr->getIStream("/path/to/resource1"));
|
||||
// This is convenient, but be aware that still in 2017, some buggy C++
|
||||
// compilers don't allow the exception to be caught: cf.
|
||||
// <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66145>.
|
||||
iStream->exceptions(std::ios_base::badbit);
|
||||
// Just to show an efficient algorithm. For real applications, use larger
|
||||
// buffer sizes!
|
||||
static constexpr std::size_t bufSize = 4;
|
||||
unique_ptr<char[]> buf(new char[bufSize]); // intermediate buffer
|
||||
string result;
|
||||
|
||||
do {
|
||||
iStream->read(buf.get(), bufSize);
|
||||
result.append(buf.get(), iStream->gcount());
|
||||
} while (*iStream); // iStream *points* to an std::istream
|
||||
|
||||
// 1) If set, badbit would have caused an exception to be raised (see above).
|
||||
// 2) failbit doesn't necessarily indicate an error here: it is set as soon
|
||||
// as the read() call can't provide the requested number of characters.
|
||||
SG_VERIFY(iStream->eof() && !iStream->bad());
|
||||
SG_CHECK_EQUAL(result, "This is a simple embedded resource test.");
|
||||
}
|
||||
|
||||
void test_RawEmbeddedResource()
|
||||
{
|
||||
cout << "Testing resource fetching methods of EmbeddedResourceManager with "
|
||||
"a RawEmbeddedResource" << endl;
|
||||
const auto& resMgr = EmbeddedResourceManager::instance();
|
||||
|
||||
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1"),
|
||||
string("This is a simple embedded resource test."));
|
||||
|
||||
// Get a shared_ptr to a const AbstractEmbeddedResource
|
||||
const auto res1abs = resMgr->getResource("/path/to/resource1");
|
||||
// Okay because we know this resource is not a compressed one
|
||||
const auto res1 =
|
||||
std::dynamic_pointer_cast<const RawEmbeddedResource>(res1abs);
|
||||
SG_VERIFY(res1);
|
||||
|
||||
// Print a representation of the resource metadata
|
||||
std::cout << "\n/path/to/resource1 -> " << *res1 << "\n\n";
|
||||
|
||||
// The following methods would work the same with res1abs
|
||||
SG_CHECK_EQUAL_NOSTREAM(res1->compressionType(),
|
||||
AbstractEmbeddedResource::CompressionType::NONE);
|
||||
SG_CHECK_EQUAL(res1->compressionDescr(), "none");
|
||||
|
||||
SG_CHECK_EQUAL(res1->rawPtr(), res1Array);
|
||||
SG_CHECK_EQUAL(res1->rawSize(), sizeof(res1Array) - 1); // see above
|
||||
SG_CHECK_EQUAL(res1->str(),
|
||||
string("This is a simple embedded resource test."));
|
||||
|
||||
auxTest_RawEmbeddedResource_streambuf();
|
||||
auxTest_RawEmbeddedResource_istream();
|
||||
|
||||
// Just reload and recheck the resource, because we can :)
|
||||
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1"),
|
||||
string("This is a simple embedded resource test."));
|
||||
}
|
||||
|
||||
void test_ZlibEmbeddedResource()
|
||||
{
|
||||
cout << "Testing resource fetching methods of EmbeddedResourceManager with "
|
||||
"a ZlibEmbeddedResource" << endl;
|
||||
const auto& resMgr = EmbeddedResourceManager::instance();
|
||||
|
||||
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource2"),
|
||||
lipsum);
|
||||
|
||||
// Get a shared_ptr to a const AbstractEmbeddedResource
|
||||
const auto res2abs = resMgr->getResource("/path/to/resource2");
|
||||
// Okay because we know this resource is a Zlib-compressed one
|
||||
const auto res2 =
|
||||
std::dynamic_pointer_cast<const ZlibEmbeddedResource>(res2abs);
|
||||
SG_VERIFY(res2);
|
||||
|
||||
SG_CHECK_EQUAL(res2->uncompressedSize(), lipsum.size());
|
||||
|
||||
// Print a representation of the resource metadata
|
||||
std::cout << "\n/path/to/resource2 -> " << *res2 << "\n\n";
|
||||
cout << "Resource 2 compression ratio: " <<
|
||||
static_cast<float>(res2->uncompressedSize()) /
|
||||
static_cast<float>(res2->rawSize()) << "\n";
|
||||
|
||||
// Just reload and recheck the resource
|
||||
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource2"), lipsum);
|
||||
}
|
||||
|
||||
void test_getMissingResources()
|
||||
{
|
||||
cout << "Testing the behavior of EmbeddedResourceManager when trying to "
|
||||
"fetch inexistent resources" << endl;
|
||||
const auto& resMgr = EmbeddedResourceManager::instance();
|
||||
SG_VERIFY(!resMgr->getResourceOrNullPtr("/inexistant/resource"));
|
||||
|
||||
bool gotException = false;
|
||||
try {
|
||||
resMgr->getResource("/inexistant/resource");
|
||||
} catch (const sg_exception&) {
|
||||
gotException = true;
|
||||
}
|
||||
SG_VERIFY(gotException);
|
||||
|
||||
gotException = false;
|
||||
try {
|
||||
resMgr->getString("/other/inexistant/resource");
|
||||
} catch (const sg_exception&) {
|
||||
gotException = true;
|
||||
}
|
||||
SG_VERIFY(gotException);
|
||||
}
|
||||
|
||||
void test_addAlreadyExistingResource()
|
||||
{
|
||||
cout << "Testing the behavior of EmbeddedResourceManager when trying to "
|
||||
"add an already existing resource" << endl;
|
||||
const auto& resMgr = EmbeddedResourceManager::instance();
|
||||
|
||||
for (const string& locale: {"", "fr", "fr_FR"}) {
|
||||
// For these tests, we don't care about the resource contents -> no need
|
||||
// to substract 1 from the result of sizeof() as we did above.
|
||||
unique_ptr<const RawEmbeddedResource> someRes(
|
||||
new RawEmbeddedResource(res1fr_FRArray, sizeof(res1fr_FRArray)));
|
||||
|
||||
bool gotException = false;
|
||||
try {
|
||||
resMgr->addResource("/path/to/resource1", std::move(someRes), locale);
|
||||
} catch (const sg_error&) {
|
||||
gotException = true;
|
||||
}
|
||||
SG_VERIFY(gotException);
|
||||
}
|
||||
}
|
||||
|
||||
void test_localeDependencyOfResourceFetching()
|
||||
{
|
||||
cout << "Testing the locale-dependency of resource fetching from "
|
||||
"EmbeddedResourceManager" << endl;
|
||||
const auto& resMgr = EmbeddedResourceManager::instance();
|
||||
resMgr->selectLocale(""); // select the default locale
|
||||
|
||||
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1"),
|
||||
"This is a simple embedded resource test.");
|
||||
|
||||
// Switch to the 'fr_FR' locale (French from France)
|
||||
resMgr->selectLocale("fr_FR");
|
||||
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1"),
|
||||
"Ceci est un petit test de ressource embarquée (variante "
|
||||
"fr_FR).");
|
||||
|
||||
// This one is for the 'fr' “locale”, obtained as fallback since there is no
|
||||
// resource mapped to /path/to/resource2 for the 'fr_FR' “locale”.
|
||||
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource2"),
|
||||
"Un lorem ipsum un peu plus court...");
|
||||
|
||||
// Explicitly ask for the resource in the default locale
|
||||
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1", ""),
|
||||
"This is a simple embedded resource test.");
|
||||
|
||||
// Switch to the 'fr' locale (French)
|
||||
resMgr->selectLocale("fr");
|
||||
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1"),
|
||||
"Ceci est un petit test de ressource embarquée.");
|
||||
|
||||
// Explicitly ask for the resource in the 'fr_FR' locale
|
||||
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1", "fr_FR"),
|
||||
"Ceci est un petit test de ressource embarquée "
|
||||
"(variante fr_FR).");
|
||||
|
||||
// Switch to the default locale
|
||||
resMgr->selectLocale("");
|
||||
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1"),
|
||||
"This is a simple embedded resource test.");
|
||||
|
||||
// Explicitly ask for the resource in the 'fr' locale
|
||||
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1", "fr"),
|
||||
"Ceci est un petit test de ressource embarquée.");
|
||||
|
||||
// Explicitly ask for the resource in the 'fr_FR' locale
|
||||
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1", "fr_FR"),
|
||||
"Ceci est un petit test de ressource embarquée "
|
||||
"(variante fr_FR).");
|
||||
|
||||
// Explicitly ask for the resource in the default locale
|
||||
SG_CHECK_EQUAL(resMgr->getString("/path/to/resource1", ""),
|
||||
"This is a simple embedded resource test.");
|
||||
}
|
||||
|
||||
void test_getLocaleAndSelectLocale()
|
||||
{
|
||||
cout << "Testing the getLocale() and selectLocale() methods of "
|
||||
"EmbeddedResourceManager" << endl;
|
||||
const auto& resMgr = EmbeddedResourceManager::instance();
|
||||
|
||||
for (const string& locale: {"", "fr", "fr_FR", "de_DE"}) {
|
||||
// The important effects of setLocale() are tested in
|
||||
// test_localeDependencyOfResourceFetching()
|
||||
resMgr->selectLocale(locale);
|
||||
SG_CHECK_EQUAL(resMgr->getLocale(), locale);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// Initialize the EmbeddedResourceManager instance, add a few resources
|
||||
// to it and call its selectLocale() method.
|
||||
initResources();
|
||||
|
||||
test_RawEmbeddedResource();
|
||||
test_ZlibEmbeddedResource();
|
||||
test_getMissingResources();
|
||||
test_addAlreadyExistingResource();
|
||||
test_localeDependencyOfResourceFetching();
|
||||
test_getLocaleAndSelectLocale();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -10,14 +10,13 @@ if(ENABLE_TESTS)
|
||||
add_executable(test_metar test_metar.cxx)
|
||||
|
||||
if (SIMGEAR_SHARED)
|
||||
target_link_libraries(test_metar SimGearScene ${GDAL_LIBRARY})
|
||||
target_link_libraries(test_metar SimGearScene)
|
||||
else()
|
||||
target_link_libraries(test_metar
|
||||
SimGearScene SimGearCore
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${ZLIB_LIBRARY}
|
||||
${RT_LIBRARY}
|
||||
${GDAL_LIBRARY})
|
||||
${RT_LIBRARY})
|
||||
endif()
|
||||
|
||||
add_test(metar ${EXECUTABLE_OUTPUT_PATH}/test_metar)
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#endif
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/misc/test_macros.hxx>
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
@@ -24,56 +23,75 @@ 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");
|
||||
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?
|
||||
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.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);
|
||||
FUZZY_COMPARE(m1.getTemperature_C(), 10, TEST_EPSILON);
|
||||
FUZZY_COMPARE(m1.getDewpoint_C(), 5, TEST_EPSILON);
|
||||
FUZZY_COMPARE(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");
|
||||
SG_CHECK_EQUAL(m1.getWindDir(), 270);
|
||||
SG_CHECK_EQUAL_EP2(m1.getWindSpeed_kt(), 12, TEST_EPSILON);
|
||||
COMPARE(m1.getWindDir(), 270);
|
||||
FUZZY_COMPARE(m1.getWindSpeed_kt(), 12, TEST_EPSILON);
|
||||
|
||||
SG_CHECK_EQUAL(m1.getWeather().size(), 0);
|
||||
SG_CHECK_EQUAL(m1.getClouds().size(), 2);
|
||||
COMPARE(m1.getWeather().size(), 0);
|
||||
COMPARE(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);
|
||||
FUZZY_COMPARE(m1.getTemperature_C(), 10, TEST_EPSILON);
|
||||
FUZZY_COMPARE(m1.getDewpoint_C(), 5, TEST_EPSILON);
|
||||
FUZZY_COMPARE(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");
|
||||
SG_CHECK_EQUAL(m1.getWindDir(), 270);
|
||||
SG_CHECK_EQUAL_EP2(m1.getWindSpeed_kt(), 12, TEST_EPSILON);
|
||||
COMPARE(m1.getWindDir(), 270);
|
||||
FUZZY_COMPARE(m1.getWindSpeed_kt(), 12, TEST_EPSILON);
|
||||
|
||||
SG_CHECK_EQUAL(m1.getWeather().size(), 0);
|
||||
SG_CHECK_EQUAL(m1.getClouds().size(), 2);
|
||||
COMPARE(m1.getWeather().size(), 0);
|
||||
COMPARE(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);
|
||||
FUZZY_COMPARE(m1.getTemperature_C(), 10, TEST_EPSILON);
|
||||
FUZZY_COMPARE(m1.getDewpoint_C(), 5, TEST_EPSILON);
|
||||
FUZZY_COMPARE(m1.getPressure_hPa(), 1025, TEST_EPSILON);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
* $Id$
|
||||
**************************************************************************/
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
#include <math.h>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
* $Id$
|
||||
**************************************************************************/
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
* $Id$
|
||||
**************************************************************************/
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include <cmath>
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/io/iostreams/sgstream.hxx>
|
||||
#include <simgear/misc/sgstream.hxx>
|
||||
|
||||
#include "stardata.hxx"
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ public:
|
||||
// bool unconditionalAttributeOwnershipDivestiture(const RTIHandle& objectHandle, const RTIHandleSet& attributeHandles)
|
||||
// {
|
||||
// try {
|
||||
// std::unique_ptr<RTI::AttributeHandleSet> attributeHandleSet(RTI::AttributeHandleSetFactory::create(attributeHandles.size()));
|
||||
// std::auto_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::unique_ptr<RTI::AttributeHandleSet> attributeHandleSet(RTI::AttributeHandleSetFactory::create(attributeHandles.size()));
|
||||
// std::auto_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::unique_ptr<RTI::AttributeHandleSet> attributeHandleSet(RTI::AttributeHandleSetFactory::create(attributeHandles.size()));
|
||||
// std::auto_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::unique_ptr<RTI::AttributeHandleSet> attributeHandleSet(RTI::AttributeHandleSetFactory::create(attributeHandles.size()));
|
||||
// std::auto_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::unique_ptr<RTI::AttributeHandleSet> attributeHandleSet(RTI::AttributeHandleSetFactory::create(attributeHandles.size()));
|
||||
// std::auto_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::unique_ptr<RTI::AttributeHandleSet> attributeHandleSet(RTI::AttributeHandleSetFactory::create(attributeHandles.size()));
|
||||
// std::auto_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::unique_ptr<RTI::AttributeHandleSet> attributeHandleSet(RTI::AttributeHandleSetFactory::create(attributeHandles.size()));
|
||||
// std::auto_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::unique_ptr<RTI::AttributeHandleSet> attributeHandleSet(RTI::AttributeHandleSetFactory::create(numAttributes));
|
||||
std::auto_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::unique_ptr<RTI::AttributeHandleSet> attributeHandleSet(RTI::AttributeHandleSetFactory::create(numAttributes));
|
||||
std::auto_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::unique_ptr<RTI::AttributeHandleSet> attributeHandleSet(RTI::AttributeHandleSetFactory::create(numAttributes));
|
||||
std::auto_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::unique_ptr<RTI::AttributeHandleValuePairSet> _attributeValuePairSet;
|
||||
std::auto_ptr<RTI::AttributeHandleValuePairSet> _attributeValuePairSet;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
add_subdirectory(iostreams)
|
||||
|
||||
|
||||
include (SimGearComponent)
|
||||
|
||||
|
||||
@@ -21,9 +21,8 @@
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "DNSClient.hxx"
|
||||
#include <udns.h>
|
||||
#include "udns.h"
|
||||
#include <time.h>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
@@ -34,33 +33,18 @@ 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( 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 )
|
||||
if( dns_open(NULL) < 0 )
|
||||
SG_LOG(SG_IO, SG_ALERT, "Can't open udns context" );
|
||||
}
|
||||
|
||||
~ClientPrivate() {
|
||||
dns_close(ctx);
|
||||
dns_free(ctx);
|
||||
if( --instanceCounter == 0 )
|
||||
dns_close(NULL);
|
||||
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),
|
||||
@@ -127,10 +111,10 @@ static void dnscbSRV(struct dns_ctx *ctx, struct dns_rr_srv *result, void *data)
|
||||
r->setComplete();
|
||||
}
|
||||
|
||||
void SRVRequest::submit( Client * client )
|
||||
void SRVRequest::submit()
|
||||
{
|
||||
// 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 )) {
|
||||
if (!dns_submit_srv(NULL, 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;
|
||||
}
|
||||
@@ -164,10 +148,10 @@ static void dnscbTXT(struct dns_ctx *ctx, struct dns_rr_txt *result, void *data)
|
||||
r->setComplete();
|
||||
}
|
||||
|
||||
void TXTRequest::submit( Client * client )
|
||||
void TXTRequest::submit()
|
||||
{
|
||||
// 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 )) {
|
||||
if (!dns_submit_txt(NULL, getDn().c_str(), DNS_C_IN, 0, dnscbTXT, this )) {
|
||||
SG_LOG(SG_IO, SG_ALERT, "Can't submit dns request for " << getDn());
|
||||
return;
|
||||
}
|
||||
@@ -191,11 +175,11 @@ static void dnscbNAPTR(struct dns_ctx *ctx, struct dns_rr_naptr *result, void *d
|
||||
r->ttl = result->dnsnaptr_ttl;
|
||||
for (int i = 0; i < result->dnsnaptr_nrr; i++) {
|
||||
if( !r->qservice.empty() && r->qservice != result->dnsnaptr_naptr[i].service )
|
||||
continue;
|
||||
return;
|
||||
|
||||
//TODO: case ignore and result flags may have more than one flag
|
||||
if( !r->qflags.empty() && r->qflags != result->dnsnaptr_naptr[i].flags )
|
||||
continue;
|
||||
return;
|
||||
|
||||
NAPTRRequest::NAPTR_ptr naptr(new NAPTRRequest::NAPTR);
|
||||
r->entries.push_back(naptr);
|
||||
@@ -212,9 +196,9 @@ static void dnscbNAPTR(struct dns_ctx *ctx, struct dns_rr_naptr *result, void *d
|
||||
r->setComplete();
|
||||
}
|
||||
|
||||
void NAPTRRequest::submit( Client * client )
|
||||
void NAPTRRequest::submit()
|
||||
{
|
||||
if (!dns_submit_naptr(client->d->ctx, getDn().c_str(), 0, dnscbNAPTR, this )) {
|
||||
if (!dns_submit_naptr(NULL, getDn().c_str(), 0, dnscbNAPTR, this )) {
|
||||
SG_LOG(SG_IO, SG_ALERT, "Can't submit dns request for " << getDn());
|
||||
return;
|
||||
}
|
||||
@@ -233,16 +217,15 @@ Client::Client() :
|
||||
|
||||
void Client::makeRequest(const Request_ptr& r)
|
||||
{
|
||||
r->submit(this);
|
||||
r->submit();
|
||||
}
|
||||
|
||||
void Client::update(int waitTimeout)
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
if( dns_timeouts( d->ctx, -1, now ) < 0 )
|
||||
if( dns_timeouts( NULL, waitTimeout, now ) < 0 )
|
||||
return;
|
||||
|
||||
dns_ioevent(d->ctx, now);
|
||||
dns_ioevent(NULL, now);
|
||||
}
|
||||
|
||||
} // of namespace DNS
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#ifndef SG_DNS_CLIENT_HXX
|
||||
#define SG_DNS_CLIENT_HXX
|
||||
|
||||
#include <memory> // for std::unique_ptr
|
||||
#include <memory> // for std::auto_ptr
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <ctime> // for time_t
|
||||
@@ -39,7 +39,6 @@ namespace simgear
|
||||
namespace DNS
|
||||
{
|
||||
|
||||
class Client;
|
||||
class Request : public SGReferenced
|
||||
{
|
||||
public:
|
||||
@@ -51,7 +50,7 @@ public:
|
||||
bool isTimeout() const;
|
||||
void setComplete( bool b = true ) { _complete = b; }
|
||||
|
||||
virtual void submit( Client * client) = 0;
|
||||
virtual void submit() = 0;
|
||||
|
||||
std::string cname;
|
||||
std::string qname;
|
||||
@@ -69,7 +68,7 @@ class NAPTRRequest : public Request
|
||||
{
|
||||
public:
|
||||
NAPTRRequest( const std::string & dn );
|
||||
virtual void submit( Client * client );
|
||||
virtual void submit();
|
||||
|
||||
struct NAPTR : SGReferenced {
|
||||
int order;
|
||||
@@ -92,7 +91,7 @@ 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 );
|
||||
virtual void submit();
|
||||
|
||||
struct SRV : SGReferenced {
|
||||
int priority;
|
||||
@@ -112,7 +111,7 @@ class TXTRequest : public Request
|
||||
{
|
||||
public:
|
||||
TXTRequest( const std::string & dn );
|
||||
virtual void submit( Client * client );
|
||||
virtual void submit();
|
||||
|
||||
typedef std::vector<string> TXT_list;
|
||||
typedef std::map<std::string,std::string> TXT_Attribute_map;
|
||||
@@ -132,8 +131,9 @@ public:
|
||||
|
||||
// void cancelRequest(const Request_ptr& r, std::string reason = std::string());
|
||||
|
||||
private:
|
||||
class ClientPrivate;
|
||||
std::unique_ptr<ClientPrivate> d;
|
||||
std::auto_ptr<ClientPrivate> d;
|
||||
};
|
||||
|
||||
} // of namespace DNS
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "HTTPClient.hxx"
|
||||
#include "HTTPFileRequest.hxx"
|
||||
@@ -169,32 +168,8 @@ void Client::update(int waitTimeout)
|
||||
return;
|
||||
}
|
||||
|
||||
int remainingActive, messagesInQueue;
|
||||
#if defined(SG_MAC)
|
||||
// Mac 10.8 libCurl lacks this, let's keep compat for now
|
||||
fd_set curlReadFDs, curlWriteFDs, curlErrorFDs;
|
||||
int maxFD;
|
||||
curl_multi_fdset(d->curlMulti,
|
||||
&curlReadFDs,
|
||||
&curlWriteFDs,
|
||||
&curlErrorFDs,
|
||||
&maxFD);
|
||||
|
||||
struct timeval timeout;
|
||||
long t;
|
||||
|
||||
curl_multi_timeout(d->curlMulti, &t);
|
||||
if ((t < 0) || (t > waitTimeout)) {
|
||||
t = waitTimeout;
|
||||
}
|
||||
|
||||
timeout.tv_sec = t / 1000;
|
||||
timeout.tv_usec = (t % 1000) * 1000;
|
||||
::select(maxFD, &curlReadFDs, &curlWriteFDs, &curlErrorFDs, &timeout);
|
||||
#else
|
||||
int numFds;
|
||||
int remainingActive, messagesInQueue, numFds;
|
||||
curl_multi_wait(d->curlMulti, NULL, 0, waitTimeout, &numFds);
|
||||
#endif
|
||||
curl_multi_perform(d->curlMulti, &remainingActive);
|
||||
|
||||
CURLMsg* msg;
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#ifndef SG_HTTP_CLIENT_HXX
|
||||
#define SG_HTTP_CLIENT_HXX
|
||||
|
||||
#include <memory> // for std::unique_ptr
|
||||
#include <memory> // for std::auto_ptr
|
||||
#include <stdint.h> // for uint_64t
|
||||
|
||||
#include <simgear/io/HTTPFileRequest.hxx>
|
||||
@@ -125,7 +125,7 @@ private:
|
||||
friend class Request;
|
||||
|
||||
class ClientPrivate;
|
||||
std::unique_ptr<ClientPrivate> d;
|
||||
std::auto_ptr<ClientPrivate> d;
|
||||
};
|
||||
|
||||
} // of namespace HTTP
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "HTTPFileRequest.hxx"
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
#include "HTTPRequest.hxx"
|
||||
|
||||
#include <simgear/io/iostreams/sgstream.hxx>
|
||||
#include <simgear/misc/sgstream.hxx>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "HTTPMemoryRequest.hxx"
|
||||
|
||||
namespace simgear
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "HTTPRepository.hxx"
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
@@ -37,7 +37,7 @@
|
||||
#include <simgear/misc/sg_dir.hxx>
|
||||
#include <simgear/io/HTTPClient.hxx>
|
||||
#include <simgear/io/sg_file.hxx>
|
||||
#include <simgear/io/iostreams/sgstream.hxx>
|
||||
#include <simgear/misc/sgstream.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include <simgear/timing/timestamp.hxx>
|
||||
|
||||
@@ -57,8 +57,6 @@ namespace simgear
|
||||
{
|
||||
}
|
||||
|
||||
virtual void cancel();
|
||||
|
||||
size_t contentSize() const
|
||||
{
|
||||
return _contentSize;
|
||||
@@ -104,6 +102,7 @@ public:
|
||||
hashCacheDirty(false),
|
||||
p(parent),
|
||||
isUpdating(false),
|
||||
updateEverything(false),
|
||||
status(HTTPRepository::REPO_NO_ERROR),
|
||||
totalDownloaded(0)
|
||||
{ ; }
|
||||
@@ -115,10 +114,14 @@ 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,
|
||||
@@ -193,10 +196,12 @@ class HTTPDirectory
|
||||
typedef std::vector<ChildInfo> ChildInfoList;
|
||||
ChildInfoList children;
|
||||
|
||||
|
||||
public:
|
||||
HTTPDirectory(HTTPRepoPrivate* repo, const std::string& path) :
|
||||
_repository(repo),
|
||||
_relativePath(path)
|
||||
_relativePath(path),
|
||||
_state(DoNotUpdate)
|
||||
{
|
||||
assert(repo);
|
||||
|
||||
@@ -233,6 +238,8 @@ public:
|
||||
fpath.append(".dirindex");
|
||||
_repository->updatedFileContents(fpath, hash);
|
||||
|
||||
_state = Updated;
|
||||
|
||||
children.clear();
|
||||
parseDirIndex(children);
|
||||
std::sort(children.begin(), children.end());
|
||||
@@ -240,6 +247,7 @@ public:
|
||||
|
||||
void failedToUpdate(HTTPRepository::ResultCode status)
|
||||
{
|
||||
_state = UpdateFailed;
|
||||
if (_relativePath.empty()) {
|
||||
// root dir failed
|
||||
_repository->failedToGetRootIndex(status);
|
||||
@@ -253,7 +261,7 @@ public:
|
||||
if (_repository->installedCopyPath.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
string_list indexNames = indexChildren();
|
||||
const_string_list_iterator nameIt = indexNames.begin();
|
||||
for (; nameIt != indexNames.end(); ++nameIt) {
|
||||
@@ -276,7 +284,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);
|
||||
@@ -298,7 +306,11 @@ public:
|
||||
|
||||
void updateChildrenBasedOnHash()
|
||||
{
|
||||
//SG_LOG(SG_TERRASYNC, SG_DEBUG, "updated children for:" << relativePath());
|
||||
// if we got here for a dir which is still updating or excluded
|
||||
// from updates, just bail out right now.
|
||||
if (_state != Updated) {
|
||||
return;
|
||||
}
|
||||
|
||||
copyInstalledChildren();
|
||||
|
||||
@@ -333,6 +345,9 @@ 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();
|
||||
}
|
||||
}
|
||||
@@ -350,6 +365,95 @@ 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;
|
||||
@@ -390,6 +494,11 @@ 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);
|
||||
}
|
||||
}
|
||||
@@ -494,11 +603,6 @@ 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;
|
||||
@@ -508,14 +612,6 @@ 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) {
|
||||
@@ -560,7 +656,16 @@ 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) :
|
||||
@@ -598,14 +703,38 @@ SGPath HTTPRepository::fsBase() const
|
||||
|
||||
void HTTPRepository::update()
|
||||
{
|
||||
if (_d->isUpdating) {
|
||||
_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");
|
||||
return;
|
||||
}
|
||||
|
||||
_d->status = REPO_NO_ERROR;
|
||||
_d->isUpdating = true;
|
||||
_d->failures.clear();
|
||||
_d->updateDir(_d->rootDir, std::string(), 0);
|
||||
_d->updatePaths.push_back(relPath);
|
||||
|
||||
HTTPDirectory* dir = _d->getOrCreateDirectory(relPath);
|
||||
dir->markSubtreeAsEnabled();
|
||||
dir->markAncestorChainAsEnabled();
|
||||
|
||||
_d->updateWaiting();
|
||||
}
|
||||
|
||||
bool HTTPRepository::isDoingSync() const
|
||||
@@ -665,12 +794,6 @@ HTTPRepository::failure() const
|
||||
return _d->status;
|
||||
}
|
||||
|
||||
void HTTPRepoGetRequest::cancel()
|
||||
{
|
||||
_directory->repository()->http->cancelRequest(this, "Reposiotry cancelled");
|
||||
_directory = 0;
|
||||
}
|
||||
|
||||
class FileGetRequest : public HTTPRepoGetRequest
|
||||
{
|
||||
public:
|
||||
@@ -702,6 +825,7 @@ 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());
|
||||
@@ -738,7 +862,7 @@ HTTPRepository::failure() const
|
||||
std::string fileName; // if empty, we're getting the directory itself
|
||||
SGPath pathInRepo;
|
||||
simgear::sha1nfo hashContext;
|
||||
std::unique_ptr<SGBinaryFile> file;
|
||||
std::auto_ptr<SGBinaryFile> file;
|
||||
};
|
||||
|
||||
class DirGetRequest : public HTTPRepoGetRequest
|
||||
@@ -798,8 +922,8 @@ HTTPRepository::failure() const
|
||||
of.write(body.data(), body.size());
|
||||
of.close();
|
||||
_directory->dirIndexUpdated(hash);
|
||||
|
||||
//SG_LOG(SG_TERRASYNC, SG_INFO, "updated dir index " << _directory->absolutePath());
|
||||
} else {
|
||||
_directory->markAsUpToDate();
|
||||
}
|
||||
|
||||
_directory->repository()->totalDownloaded += contentSize();
|
||||
@@ -810,7 +934,7 @@ HTTPRepository::failure() const
|
||||
SGTimeStamp st;
|
||||
st.stamp();
|
||||
_directory->updateChildrenBasedOnHash();
|
||||
SG_LOG(SG_TERRASYNC, SG_DEBUG, "after update of:" << _directory->absolutePath() << " child update took:" << st.elapsedMSec());
|
||||
SG_LOG(SG_TERRASYNC, SG_INFO, "after update of:" << _directory->absolutePath() << " child update took:" << st.elapsedMSec());
|
||||
} catch (sg_exception& ) {
|
||||
_directory->failedToUpdate(HTTPRepository::REPO_ERROR_IO);
|
||||
}
|
||||
@@ -875,6 +999,7 @@ 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);
|
||||
@@ -1041,6 +1166,25 @@ 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;
|
||||
}
|
||||
|
||||
@@ -1136,4 +1280,21 @@ 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()) {
|
||||
isUpdating = false;
|
||||
}
|
||||
}
|
||||
|
||||
} // of namespace simgear
|
||||
|
||||
@@ -57,6 +57,13 @@ 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;
|
||||
@@ -73,7 +80,7 @@ public:
|
||||
private:
|
||||
bool isBare() const;
|
||||
|
||||
std::unique_ptr<HTTPRepoPrivate> _d;
|
||||
std::auto_ptr<HTTPRepoPrivate> _d;
|
||||
};
|
||||
|
||||
} // of namespace simgear
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "HTTPRequest.hxx"
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
@@ -133,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() < 2) {
|
||||
if (parts.size() != 3) {
|
||||
throw sg_io_exception("bad HTTP response:" + r);
|
||||
}
|
||||
|
||||
_responseVersion = decodeHTTPVersion(parts[0]);
|
||||
_responseStatus = strutils::to_int(parts[1]);
|
||||
_responseReason = parts.size() > 2 ? parts[2] : "";
|
||||
_responseReason = parts[2];
|
||||
|
||||
setReadyState(STATUS_RECEIVED);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
include (SimGearComponent)
|
||||
|
||||
set(HEADERS
|
||||
sgstream.hxx
|
||||
gzfstream.hxx
|
||||
gzcontainerfile.hxx
|
||||
CharArrayStream.hxx
|
||||
zlibstream.hxx
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
sgstream.cxx
|
||||
gzfstream.cxx
|
||||
gzcontainerfile.cxx
|
||||
CharArrayStream.cxx
|
||||
zlibstream.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)
|
||||
|
||||
add_executable(test_CharArrayStream CharArrayStream_test.cxx)
|
||||
target_link_libraries(test_CharArrayStream ${TEST_LIBS})
|
||||
add_test(CharArrayStream ${EXECUTABLE_OUTPUT_PATH}/test_CharArrayStream)
|
||||
|
||||
add_executable(test_zlibstream zlibstream_test.cxx)
|
||||
target_link_libraries(test_zlibstream ${TEST_LIBS})
|
||||
add_test(zlibstream ${EXECUTABLE_OUTPUT_PATH}/test_zlibstream)
|
||||
|
||||
endif(ENABLE_TESTS)
|
||||
@@ -1,291 +0,0 @@
|
||||
// -*- coding: utf-8 -*-
|
||||
//
|
||||
// CharArrayStream.cxx --- IOStreams classes for reading from, and writing to
|
||||
// char arrays
|
||||
//
|
||||
// Copyright (C) 2017 Florent Rougon
|
||||
//
|
||||
// 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 Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA.
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <ios> // std::streamsize
|
||||
#include <istream> // std::istream and std::iostream
|
||||
#include <ostream> // std::ostream
|
||||
#include <type_traits> // std::make_unsigned()
|
||||
#include <cstddef> // std::size_t, std::ptrdiff_t
|
||||
#include <cassert>
|
||||
|
||||
#include "CharArrayStream.hxx"
|
||||
|
||||
using traits = std::char_traits<char>;
|
||||
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
// ***************************************************************************
|
||||
// * CharArrayStreambuf class *
|
||||
// ***************************************************************************
|
||||
|
||||
CharArrayStreambuf::CharArrayStreambuf(char* buf, std::size_t bufSize)
|
||||
: _buf(buf),
|
||||
_bufSize(bufSize)
|
||||
{
|
||||
setg(_buf, _buf, _buf + _bufSize);
|
||||
setp(_buf, _buf + _bufSize);
|
||||
}
|
||||
|
||||
char* CharArrayStreambuf::data() const
|
||||
{
|
||||
return _buf;
|
||||
}
|
||||
|
||||
std::size_t CharArrayStreambuf::size() const
|
||||
{
|
||||
return _bufSize;
|
||||
}
|
||||
|
||||
int CharArrayStreambuf::underflow()
|
||||
{
|
||||
return (gptr() == egptr()) ? traits::eof() : traits::to_int_type(*gptr());
|
||||
}
|
||||
|
||||
int CharArrayStreambuf::overflow(int c)
|
||||
{
|
||||
// cf. §27.7.1, footnote 309 of the C++11 standard
|
||||
if (traits::eq_int_type(c, traits::eof())) {
|
||||
return traits::not_eof(c);
|
||||
} else {
|
||||
// This class never writes beyond the end of the array (_buf + _bufSize)
|
||||
return traits::eof();
|
||||
}
|
||||
}
|
||||
|
||||
std::streamsize CharArrayStreambuf::xsgetn(char* dest, std::streamsize n)
|
||||
{
|
||||
assert(n >= 0);
|
||||
std::ptrdiff_t avail = egptr() - gptr();
|
||||
// Compute min(avail, n). The cast is safe, because in its branch, one has
|
||||
// 0 <= n < avail, which is of type std::ptrdiff_t.
|
||||
std::ptrdiff_t nbChars = ( (n >= avail) ?
|
||||
avail : static_cast<std::ptrdiff_t>(n) );
|
||||
std::copy(gptr(), gptr() + nbChars, dest);
|
||||
// eback() == _buf and egptr() == _buf + _bufSize
|
||||
// I don't use gbump(), because it takes an int...
|
||||
setg(eback(), gptr() + nbChars, egptr());
|
||||
|
||||
// Cast safe because 0 <= nbChars <= n, which is of type std::streamsize
|
||||
return static_cast<std::streamsize>(nbChars); // number of chars copied
|
||||
}
|
||||
|
||||
std::streamsize CharArrayStreambuf::xsputn(const char* s, std::streamsize n)
|
||||
{
|
||||
assert(n >= 0);
|
||||
std::ptrdiff_t availSpace = epptr() - pptr();
|
||||
// Compute min(availSpace, n). The cast is safe, because in its branch, one
|
||||
// has 0 <= n < availSpace, which is of type std::ptrdiff_t.
|
||||
std::ptrdiff_t nbChars = ( (n >= availSpace) ?
|
||||
availSpace : static_cast<std::ptrdiff_t>(n) );
|
||||
std::copy(s, s + nbChars, pptr());
|
||||
// epptr() == _buf + _bufSize
|
||||
// I don't use pbump(), because it takes an int...
|
||||
setp(pptr() + nbChars, epptr());
|
||||
|
||||
// Cast safe because 0 <= nbChars <= n, which is of type std::streamsize
|
||||
return static_cast<std::streamsize>(nbChars); // number of chars copied
|
||||
}
|
||||
|
||||
std::streamsize CharArrayStreambuf::showmanyc()
|
||||
{
|
||||
// It is certain that underflow() will return EOF if gptr() == egptr().
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::streampos CharArrayStreambuf::seekoff(std::streamoff off,
|
||||
std::ios_base::seekdir way,
|
||||
std::ios_base::openmode which)
|
||||
{
|
||||
bool positionInputSeq = false;
|
||||
bool positionOutputSeq = false;
|
||||
char* ptr = nullptr;
|
||||
|
||||
// cf. §27.8.2.4 of the C++11 standard
|
||||
if ((which & std::ios_base::in) == std::ios_base::in) {
|
||||
positionInputSeq = true;
|
||||
ptr = gptr();
|
||||
}
|
||||
|
||||
if ((which & std::ios_base::out) == std::ios_base::out) {
|
||||
positionOutputSeq = true;
|
||||
ptr = pptr();
|
||||
}
|
||||
|
||||
if ((!positionInputSeq && !positionOutputSeq) ||
|
||||
(positionInputSeq && positionOutputSeq &&
|
||||
way != std::ios_base::beg && way != std::ios_base::end)) {
|
||||
return std::streampos(std::streamoff(-1));
|
||||
}
|
||||
|
||||
// If we reached this point and (positionInputSeq && positionOutputSeq),
|
||||
// then (way == std::ios_base::beg || way == std::ios_base::end) and
|
||||
// therefore 'ptr' won't be used.
|
||||
std::streamoff refOffset;
|
||||
static_assert(sizeof(std::streamoff) >= sizeof(std::ptrdiff_t),
|
||||
"Unexpected: sizeof(std::streamoff) < sizeof(std::ptrdiff_t)");
|
||||
static_assert(sizeof(std::streamoff) >= sizeof(std::size_t),
|
||||
"Unexpected: sizeof(std::streamoff) < sizeof(std::size_t)");
|
||||
|
||||
if (way == std::ios_base::beg) {
|
||||
refOffset = 0;
|
||||
} else if (way == std::ios_base::cur) {
|
||||
refOffset = static_cast<std::streamoff>(ptr - _buf);
|
||||
} else {
|
||||
assert(way == std::ios_base::end);
|
||||
refOffset = static_cast<std::streamoff>(_bufSize);
|
||||
}
|
||||
|
||||
// Offset, relatively to _buf, where we are supposed to seek
|
||||
std::streamoff totalOffset = refOffset + off;
|
||||
typedef typename std::make_unsigned<std::streamoff>::type uStreamOff;
|
||||
|
||||
if (totalOffset < 0 || static_cast<uStreamOff>(totalOffset) > _bufSize) {
|
||||
return std::streampos(std::streamoff(-1));
|
||||
} else {
|
||||
// Safe because 0 <= totalOffset <= _bufSize, which is an std::size_t
|
||||
char* newPtr = _buf + static_cast<std::size_t>(totalOffset);
|
||||
|
||||
if (positionInputSeq) {
|
||||
// eback() == _buf and egptr() == _buf + _bufSize
|
||||
setg(eback(), newPtr, egptr());
|
||||
}
|
||||
|
||||
if (positionOutputSeq) {
|
||||
// epptr() == _buf + _bufSize
|
||||
setp(newPtr, epptr());
|
||||
}
|
||||
|
||||
// C++11's §27.8.2.4 item 12 (for stringbuf) would return refOffset. This
|
||||
// makes no sense IMHO, in particular when 'way' is std::ios_base::beg or
|
||||
// std::ios_base::end. Return the new offset (from the beginning of
|
||||
// '_buf') instead. Note that this doesn't violate anything, because
|
||||
// §27.6.3.4.2 grants full freedom as to the semantics of seekoff() to
|
||||
// classes derived from basic_streambuf.
|
||||
//
|
||||
// My interpretation is consistent with items 13 and 14 of §27.8.2.4
|
||||
// concerning seekpos(), whereas item 12 is not (if item 12 were followed
|
||||
// to the letter, seekoff() would always return 0 on success when
|
||||
// way == std::ios_base::beg, and therefore items 13 and 14 would be
|
||||
// incompatible).
|
||||
return std::streampos(totalOffset);
|
||||
}
|
||||
}
|
||||
|
||||
std::streampos CharArrayStreambuf::seekpos(std::streampos pos,
|
||||
std::ios_base::openmode which)
|
||||
{
|
||||
return seekoff(std::streamoff(pos), std::ios_base::beg, which);
|
||||
}
|
||||
|
||||
// ***************************************************************************
|
||||
// * ROCharArrayStreambuf class *
|
||||
// ***************************************************************************
|
||||
ROCharArrayStreambuf::ROCharArrayStreambuf(const char* buf, std::size_t bufSize)
|
||||
: CharArrayStreambuf(const_cast<char*>(buf), bufSize)
|
||||
{ }
|
||||
|
||||
const char* ROCharArrayStreambuf::data() const
|
||||
{
|
||||
return const_cast<const char*>(CharArrayStreambuf::data());
|
||||
}
|
||||
|
||||
int ROCharArrayStreambuf::overflow(int c)
|
||||
{
|
||||
return traits::eof(); // indicate failure
|
||||
}
|
||||
|
||||
std::streamsize ROCharArrayStreambuf::xsputn(const char* s, std::streamsize n)
|
||||
{
|
||||
return 0; // number of chars written
|
||||
}
|
||||
|
||||
// ***************************************************************************
|
||||
// * CharArrayIStream class *
|
||||
// ***************************************************************************
|
||||
|
||||
CharArrayIStream::CharArrayIStream(const char* buf, std::size_t bufSize)
|
||||
: std::istream(nullptr),
|
||||
_streamBuf(buf, bufSize)
|
||||
{
|
||||
// Associate _streamBuf to 'this' and clear the error state flags
|
||||
rdbuf(&_streamBuf);
|
||||
}
|
||||
|
||||
const char* CharArrayIStream::data() const
|
||||
{
|
||||
return _streamBuf.data();
|
||||
}
|
||||
|
||||
std::size_t CharArrayIStream::size() const
|
||||
{
|
||||
return _streamBuf.size();
|
||||
}
|
||||
|
||||
// ***************************************************************************
|
||||
// * CharArrayOStream class *
|
||||
// ***************************************************************************
|
||||
|
||||
CharArrayOStream::CharArrayOStream(char* buf, std::size_t bufSize)
|
||||
: std::ostream(nullptr),
|
||||
_streamBuf(buf, bufSize)
|
||||
{
|
||||
// Associate _streamBuf to 'this' and clear the error state flags
|
||||
rdbuf(&_streamBuf);
|
||||
}
|
||||
|
||||
char* CharArrayOStream::data() const
|
||||
{
|
||||
return _streamBuf.data();
|
||||
}
|
||||
|
||||
std::size_t CharArrayOStream::size() const
|
||||
{
|
||||
return _streamBuf.size();
|
||||
}
|
||||
|
||||
// ***************************************************************************
|
||||
// * CharArrayIOStream class *
|
||||
// ***************************************************************************
|
||||
|
||||
CharArrayIOStream::CharArrayIOStream(char* buf, std::size_t bufSize)
|
||||
: std::iostream(nullptr),
|
||||
_streamBuf(buf, bufSize)
|
||||
{
|
||||
// Associate _streamBuf to 'this' and clear the error state flags
|
||||
rdbuf(&_streamBuf);
|
||||
}
|
||||
|
||||
char* CharArrayIOStream::data() const
|
||||
{
|
||||
return _streamBuf.data();
|
||||
}
|
||||
|
||||
std::size_t CharArrayIOStream::size() const
|
||||
{
|
||||
return _streamBuf.size();
|
||||
}
|
||||
|
||||
} // of namespace simgear
|
||||
@@ -1,169 +0,0 @@
|
||||
// -*- coding: utf-8 -*-
|
||||
//
|
||||
// CharArrayStream.hxx --- IOStreams classes for reading from, and writing to
|
||||
// char arrays
|
||||
//
|
||||
// Copyright (C) 2017 Florent Rougon
|
||||
//
|
||||
// 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 Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA.
|
||||
|
||||
#ifndef SG_CHAR_ARRAY_STREAM_HXX
|
||||
#define SG_CHAR_ARRAY_STREAM_HXX
|
||||
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
#include <streambuf>
|
||||
#include <ios> // std::streamsize, std::streampos...
|
||||
#include <cstddef> // std::size_t
|
||||
|
||||
// Contrary to std::stringstream and its (i/o)stringstream friends, the
|
||||
// classes in this file allow one to work on an array of char (that could be
|
||||
// for instance static data) without having to make a whole copy of it.
|
||||
//
|
||||
// There are five classes defined here in the 'simgear' namespace:
|
||||
// - CharArrayStreambuf subclass of std::streambuf stream buffer
|
||||
// - ROCharArrayStreambuf subclass of CharArrayStreambuf stream buffer
|
||||
// - CharArrayIStream subclass of std::istream input stream
|
||||
// - CharArrayOStream subclass of std::ostream output stream
|
||||
// - CharArrayIOStream subclass of std::iostream input/output stream
|
||||
//
|
||||
// The main class is CharArrayStreambuf. ROCharArrayStreambuf is a read-only
|
||||
// subclass of CharArrayStreambuf. The other three are very simple convenience
|
||||
// classes, using either CharArrayStreambuf or ROCharArrayStreambuf as their
|
||||
// stream buffer class. One can easily work with CharArrayStreambuf or
|
||||
// ROCharArrayStreambuf only, either directly or after attaching an instance
|
||||
// to an std::istream, std::ostream or std::iostream instance (using for
|
||||
// example constructors like std::istream(std::streambuf* sb) or the rdbuf()
|
||||
// method of stream classes).
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
// Input/output stream buffer class that reads from, and writes to a fixed
|
||||
// buffer in memory specified in the constructor. This buffer must remain
|
||||
// alive as long as the stream buffer object is used (the CharArrayStreambuf
|
||||
// class works directly on that buffer without making any copy of it).
|
||||
//
|
||||
// Because reads and writes are directly performed on the buffer specified in
|
||||
// the constructor, this stream buffer class has no caching behavior. You may
|
||||
// use pubsync() if you like, but that is completely useless by design (it
|
||||
// uses the default implementation in std::streambuf, which does nothing).
|
||||
//
|
||||
// CharArrayStreambuf may share similarities in features with
|
||||
// std::strstreambuf (deprecated since C++98). However, at least one big
|
||||
// difference is that CharArrayStreambuf does no dynamic memory allocation
|
||||
// whatsoever. It works on a fixed-size-fixed-location buffer passed in the
|
||||
// constructor, and nothing more. It does prevent overflowing the buffer,
|
||||
// since it knows perfectly well where the buffer starts and ends.
|
||||
class CharArrayStreambuf: public std::streambuf
|
||||
{
|
||||
public:
|
||||
explicit CharArrayStreambuf(char* buf, std::size_t bufSize);
|
||||
|
||||
// Accessors for the buffer start pointer and size (same method names as for
|
||||
// std::string)
|
||||
char* data() const;
|
||||
std::size_t size() const;
|
||||
|
||||
protected:
|
||||
virtual int underflow() override;
|
||||
virtual int overflow(int c = std::char_traits<char>::eof()) override;
|
||||
// Optional override when subclassing std::streambuf. This is the most
|
||||
// efficient way of reading several characters.
|
||||
virtual std::streamsize xsgetn(char* dest, std::streamsize n) override;
|
||||
// Ditto for writing
|
||||
virtual std::streamsize xsputn(const char* s, std::streamsize n) override;
|
||||
virtual std::streamsize showmanyc() override;
|
||||
virtual std::streampos seekoff(
|
||||
std::streamoff off,
|
||||
std::ios_base::seekdir way,
|
||||
std::ios_base::openmode which = std::ios_base::in | std::ios_base::out)
|
||||
override;
|
||||
virtual std::streampos seekpos(
|
||||
std::streampos pos,
|
||||
std::ios_base::openmode which = std::ios_base::in | std::ios_base::out)
|
||||
override;
|
||||
|
||||
private:
|
||||
// These two define the buffer managed by the CharArrayStreambuf instance.
|
||||
char* const _buf;
|
||||
const std::size_t _bufSize;
|
||||
};
|
||||
|
||||
// Read-only version of CharArrayStreambuf
|
||||
class ROCharArrayStreambuf: public CharArrayStreambuf
|
||||
{
|
||||
public:
|
||||
explicit ROCharArrayStreambuf(const char* buf, std::size_t bufSize);
|
||||
|
||||
// Accessor for the buffer start pointer (same method name as for
|
||||
// std::string)
|
||||
const char* data() const;
|
||||
|
||||
private:
|
||||
// Override methods pertaining to write access
|
||||
virtual int overflow(int c = std::char_traits<char>::eof()) override;
|
||||
virtual std::streamsize xsputn(const char* s, std::streamsize n) override;
|
||||
};
|
||||
|
||||
// Convenience class: std::istream subclass based on ROCharArrayStreambuf
|
||||
class CharArrayIStream: public std::istream
|
||||
{
|
||||
public:
|
||||
// Same parameters as for ROCharArrayStreambuf
|
||||
explicit CharArrayIStream(const char* buf, std::size_t bufSize);
|
||||
|
||||
// Accessors for the underlying buffer start pointer and size
|
||||
const char* data() const;
|
||||
std::size_t size() const;
|
||||
|
||||
private:
|
||||
ROCharArrayStreambuf _streamBuf;
|
||||
};
|
||||
|
||||
// Convenience class: std::ostream subclass based on CharArrayStreambuf
|
||||
class CharArrayOStream: public std::ostream
|
||||
{
|
||||
public:
|
||||
// Same parameters as for CharArrayStreambuf
|
||||
explicit CharArrayOStream(char* buf, std::size_t bufSize);
|
||||
|
||||
// Accessors for the underlying buffer start pointer and size
|
||||
char* data() const;
|
||||
std::size_t size() const;
|
||||
|
||||
private:
|
||||
CharArrayStreambuf _streamBuf;
|
||||
};
|
||||
|
||||
// Convenience class: std::iostream subclass based on CharArrayStreambuf
|
||||
class CharArrayIOStream: public std::iostream
|
||||
{
|
||||
public:
|
||||
// Same parameters as for CharArrayStreambuf
|
||||
explicit CharArrayIOStream(char* buf, std::size_t bufSize);
|
||||
|
||||
// Accessors for the underlying buffer start pointer and size
|
||||
char* data() const;
|
||||
std::size_t size() const;
|
||||
|
||||
private:
|
||||
CharArrayStreambuf _streamBuf;
|
||||
};
|
||||
|
||||
} // of namespace simgear
|
||||
|
||||
#endif // of SG_CHAR_ARRAY_STREAM_HXX
|
||||
@@ -1,439 +0,0 @@
|
||||
// -*- coding: utf-8 -*-
|
||||
//
|
||||
// CharArrayStream_test.cxx --- Automated tests for CharArrayStream.cxx
|
||||
//
|
||||
// Copyright (C) 2017 Florent Rougon
|
||||
//
|
||||
// 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 Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA.
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <ios> // std::basic_ios, std::streamsize...
|
||||
#include <iostream> // std::ios_base, std::cerr, etc.
|
||||
#include <sstream>
|
||||
#include <limits> // std::numeric_limits
|
||||
#include <type_traits> // std::make_unsigned()
|
||||
#include <memory> // std::unique_ptr
|
||||
#include <cassert>
|
||||
#include <cstdlib> // EXIT_SUCCESS
|
||||
#include <cstddef> // std::size_t
|
||||
#include <cstring> // std::strlen()
|
||||
#include <algorithm> // std::fill_n()
|
||||
#include <vector>
|
||||
|
||||
#include <simgear/misc/test_macros.hxx>
|
||||
#include "CharArrayStream.hxx"
|
||||
|
||||
using std::string;
|
||||
using std::cout;
|
||||
using std::cerr;
|
||||
using traits = std::char_traits<char>;
|
||||
|
||||
typedef typename std::make_unsigned<std::streamsize>::type uStreamSize;
|
||||
|
||||
|
||||
// Safely convert a non-negative std::streamsize into an std::size_t. If
|
||||
// impossible, bail out.
|
||||
static std::size_t streamsizeToSize_t(std::streamsize n)
|
||||
{
|
||||
SG_CHECK_GE(n, 0);
|
||||
SG_CHECK_LE(static_cast<uStreamSize>(n),
|
||||
std::numeric_limits<std::size_t>::max());
|
||||
|
||||
return static_cast<std::size_t>(n);
|
||||
}
|
||||
|
||||
void test_CharArrayStreambuf_basicOperations()
|
||||
{
|
||||
cerr << "Testing basic operations on CharArrayStreambuf\n";
|
||||
|
||||
const string text = "0123456789abcdefghijklmnopqrstuvwxyz\nABCDEF\nGHIJK "
|
||||
"LMNOPQ";
|
||||
std::istringstream text_ss(text);
|
||||
string canary = "YoY";
|
||||
// Reserve space for our little canary
|
||||
const std::size_t bufSize = text.size() + canary.size();
|
||||
std::unique_ptr<char[]> buf(new char[bufSize]);
|
||||
int ch;
|
||||
std::streamsize n;
|
||||
std::streampos pos;
|
||||
|
||||
// Only allow arraySBuf to read from, and write to the first text.size()
|
||||
// chars of 'buf'
|
||||
simgear::CharArrayStreambuf arraySBuf(&buf[0], text.size());
|
||||
|
||||
// 1) Write a copy of the 'text' string at buf.get(), testing various write
|
||||
// and seek methods.
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Write "01" with two sputc() calls
|
||||
ch = arraySBuf.sputc(text[0]);
|
||||
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == '0' && buf[0] == '0');
|
||||
ch = arraySBuf.sputc(text[1]);
|
||||
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == '1' && buf[1] == '1');
|
||||
// Write the 34 following chars of 'text' with one sputn() call
|
||||
n = arraySBuf.sputn(&text[2], 34);
|
||||
SG_CHECK_EQUAL(n, 34);
|
||||
SG_CHECK_EQUAL(string(&buf[2], 34), "23456789abcdefghijklmnopqrstuvwxyz");
|
||||
|
||||
// Indirect test of seekpos(): position the write stream pointer a bit further
|
||||
pos = arraySBuf.pubseekpos(43, std::ios_base::out);
|
||||
SG_CHECK_EQUAL(pos, std::streampos(43));
|
||||
// Write 7 more chars with sputn()
|
||||
n = arraySBuf.sputn(&text[43], 7);
|
||||
SG_CHECK_EQUAL(n, 7);
|
||||
SG_CHECK_EQUAL(string(&buf[43], 7), "\nGHIJK ");
|
||||
// Indirect test of seekoff(): seek backwards relatively to the current write
|
||||
// pointer position
|
||||
pos = arraySBuf.pubseekoff(-std::streamoff(std::strlen("\nABCDEF\nGHIJK ")),
|
||||
std::ios_base::cur, std::ios_base::out);
|
||||
// 10 + 26, i.e., after the lowercase alphabet
|
||||
SG_CHECK_EQUAL(pos, std::streampos(36));
|
||||
// Write "\nABCD" to buf in one sputn() call
|
||||
n = arraySBuf.sputn(&text[36], 5);
|
||||
// Now write "EF" in two sputc() calls
|
||||
ch = arraySBuf.sputc(text[41]);
|
||||
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == 'E' && buf[41] == 'E');
|
||||
ch = arraySBuf.sputc(text[42]);
|
||||
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == 'F' && buf[42] == 'F');
|
||||
|
||||
// Place a canary to check that arraySBuf doesn't write beyond the end of buf
|
||||
std::copy(canary.begin(), canary.end(), &buf[text.size()]);
|
||||
|
||||
// Check seeking from arraySBuf's end (which is at offset text.size(), *not*
|
||||
// bufSize: cf. the construction of arraySBuf!).
|
||||
pos = arraySBuf.pubseekoff(-std::streamoff(std::strlen("LMNOPQ")),
|
||||
std::ios_base::end, std::ios_base::out);
|
||||
SG_CHECK_EQUAL(pos, std::streampos(text.size() - std::strlen("LMNOPQ")));
|
||||
// Write "LMNOPQ" to buf in one sputn() call. The other characters won't be
|
||||
// written, because they would go past the end of the buffer managed by
|
||||
// 'arraySBuf' (i.e., the first text.size() chars of 'buf').
|
||||
static const char someChars[] = "LMNOPQ+buffer overrun that will be blocked";
|
||||
n = arraySBuf.sputn(someChars, sizeof(someChars));
|
||||
|
||||
// Check the number of chars actually written
|
||||
SG_CHECK_EQUAL(n, std::strlen("LMNOPQ"));
|
||||
// Check that our canary starting at buf[text.size()] is still there and
|
||||
// intact
|
||||
SG_CHECK_EQUAL(string(&buf[text.size()], canary.size()), canary);
|
||||
// Check that we now have an exact copy of 'text' in the managed buffer
|
||||
SG_CHECK_EQUAL(string(&buf[0], text.size()), text);
|
||||
|
||||
// 2) Read back the copy of 'text' in 'buf', using various read and seek
|
||||
// methods.
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
ch = arraySBuf.sgetc();
|
||||
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == '0');
|
||||
ch = arraySBuf.sbumpc();
|
||||
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == '0');
|
||||
ch = arraySBuf.sbumpc();
|
||||
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == '1');
|
||||
ch = arraySBuf.snextc();
|
||||
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == '3');
|
||||
ch = arraySBuf.sbumpc();
|
||||
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == '3');
|
||||
ch = arraySBuf.sbumpc();
|
||||
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == '4');
|
||||
ch = arraySBuf.sputbackc('4');
|
||||
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == '4');
|
||||
ch = arraySBuf.sputbackc('u'); // doesn't match what we read from the stream
|
||||
SG_VERIFY(ch == EOF);
|
||||
ch = arraySBuf.sputbackc('3'); // this one does
|
||||
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == '3');
|
||||
|
||||
static constexpr std::streamsize buf2Size = 10;
|
||||
char buf2[buf2Size];
|
||||
// Most efficient way (with the underlying xsgetn()) to read several chars
|
||||
// at once.
|
||||
n = arraySBuf.sgetn(buf2, buf2Size);
|
||||
SG_CHECK_EQUAL(n, buf2Size);
|
||||
SG_CHECK_EQUAL(string(buf2, static_cast<std::size_t>(buf2Size)),
|
||||
"3456789abc");
|
||||
|
||||
ch = arraySBuf.sungetc(); // same as sputbackc(), except no value to check
|
||||
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == 'c');
|
||||
ch = arraySBuf.sungetc();
|
||||
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == 'b');
|
||||
ch = arraySBuf.sbumpc();
|
||||
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == 'b');
|
||||
ch = arraySBuf.sputbackc('b'); // this one does
|
||||
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == 'b');
|
||||
|
||||
n = arraySBuf.sgetn(buf2, buf2Size);
|
||||
SG_CHECK_EQUAL(n, buf2Size);
|
||||
SG_CHECK_EQUAL(string(buf2, static_cast<std::size_t>(buf2Size)),
|
||||
"bcdefghijk");
|
||||
|
||||
ch = arraySBuf.sungetc();
|
||||
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == 'k');
|
||||
|
||||
static char buf3[64];
|
||||
n = arraySBuf.sgetn(buf3, sizeof(buf3));
|
||||
SG_CHECK_EQUAL(n, 36);
|
||||
SG_CHECK_EQUAL(string(buf3, 36), "klmnopqrstuvwxyz\nABCDEF\nGHIJK LMNOPQ");
|
||||
|
||||
SG_CHECK_EQUAL(arraySBuf.sbumpc(), EOF);
|
||||
|
||||
// Check we can independently set the read and write pointers for arraySBuf
|
||||
pos = arraySBuf.pubseekpos(10, std::ios_base::in);
|
||||
SG_CHECK_EQUAL(pos, std::streampos(10));
|
||||
pos = arraySBuf.pubseekpos(13, std::ios_base::out);
|
||||
SG_CHECK_EQUAL(pos, std::streampos(13));
|
||||
|
||||
// Write "DEF" where there is currently "def" in 'buf'.
|
||||
for (int i = 0; i < 3; i++) {
|
||||
char c = 'D' + i;
|
||||
ch = arraySBuf.sputc(c);
|
||||
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == c && buf[i+13] == c);
|
||||
}
|
||||
|
||||
n = arraySBuf.sgetn(buf3, 6);
|
||||
SG_CHECK_EQUAL(n, 6);
|
||||
SG_CHECK_EQUAL(string(buf3, 6), "abcDEF");
|
||||
|
||||
// Set both stream pointers at once (read and write)
|
||||
pos = arraySBuf.pubseekpos(10, std::ios_base::in | std::ios_base::out);
|
||||
SG_VERIFY(pos == std::streampos(10));
|
||||
|
||||
// Write "ABC" to buf in one sputn() call
|
||||
n = arraySBuf.sputn("ABC", 3);
|
||||
SG_CHECK_EQUAL(n, 3);
|
||||
SG_CHECK_EQUAL(string(&buf[10], 3), "ABC");
|
||||
|
||||
// Indirect test of seekoff(): seek backwards relatively to the current read
|
||||
// pointer position
|
||||
pos = arraySBuf.pubseekoff(-3, std::ios_base::cur, std::ios_base::in);
|
||||
SG_CHECK_EQUAL(pos, std::streampos(7));
|
||||
|
||||
n = arraySBuf.sgetn(buf3, 12);
|
||||
SG_CHECK_EQUAL(n, 12);
|
||||
SG_CHECK_EQUAL(string(buf3, 12), "789ABCDEFghi");
|
||||
}
|
||||
|
||||
void test_CharArrayStreambuf_readOrWriteLargestPossibleAmount()
|
||||
{
|
||||
cerr << "Testing reading and writing from/to CharArrayStreambuf with the "
|
||||
"largest possible value passed as the 'n' argument for sgetn()/sputn() "
|
||||
"(number of chars to read or write)\n";
|
||||
|
||||
const string text = "0123456789abcdefghijklmnopqrstuvwxyz\nABCDEF\nGHIJK "
|
||||
"LMNOPQ";
|
||||
string canary = "ZaZ";
|
||||
// Reserve space for our little canary
|
||||
const std::size_t bufSize = text.size() + canary.size();
|
||||
std::unique_ptr<char[]> buf(new char[bufSize]);
|
||||
std::streamsize n_s;
|
||||
std::size_t n;
|
||||
std::streampos pos;
|
||||
|
||||
// Place a canary to check that arraySBuf doesn't write beyond the end of buf
|
||||
std::copy(canary.begin(), canary.end(), &buf[text.size()]);
|
||||
|
||||
// Only allow arraySBuf to read from, and write to the first text.size()
|
||||
// chars of 'buf'
|
||||
simgear::CharArrayStreambuf arraySBuf(&buf[0], text.size());
|
||||
|
||||
n_s = arraySBuf.sputn(text.c_str(),
|
||||
std::numeric_limits<std::streamsize>::max());
|
||||
// The conversion to std::size_t is safe because arraySBuf.sputn() returns a
|
||||
// non-negative value which, in this case, can't exceed the size of the
|
||||
// buffer managed by 'arraySBuf', i.e. text.size().
|
||||
n = streamsizeToSize_t(n_s);
|
||||
SG_CHECK_EQUAL(n, arraySBuf.size());
|
||||
SG_CHECK_EQUAL(n, text.size());
|
||||
SG_CHECK_EQUAL(string(&buf[0], n), text);
|
||||
|
||||
// Check that our canary starting at &buf[text.size()] is still there and
|
||||
// intact
|
||||
SG_CHECK_EQUAL(string(&buf[text.size()], canary.size()), canary);
|
||||
|
||||
// The “get” stream pointer is still at the beginning of the buffer managed
|
||||
// by 'arraySBuf'. Let's ask for the maximum amount of chars from it to be
|
||||
// written to a new buffer, 'buf2'.
|
||||
std::unique_ptr<char[]> buf2(new char[text.size()]);
|
||||
n_s = arraySBuf.sgetn(&buf2[0],
|
||||
std::numeric_limits<std::streamsize>::max());
|
||||
// The conversion to std::size_t is safe because arraySBuf.sgetn() returns a
|
||||
// non-negative value which, in this case, can't exceed the size of the
|
||||
// buffer managed by 'arraySBuf', i.e. text.size().
|
||||
n = streamsizeToSize_t(n_s);
|
||||
SG_CHECK_EQUAL(n, arraySBuf.size());
|
||||
SG_CHECK_EQUAL(string(&buf2[0], n), text);
|
||||
|
||||
SG_CHECK_EQUAL(arraySBuf.sbumpc(), EOF);
|
||||
}
|
||||
|
||||
void test_CharArrayIStream_simple()
|
||||
{
|
||||
// This also tests ROCharArrayStreambuf, since it is used as
|
||||
// CharArrayIStream's stream buffer class.
|
||||
cerr << "Testing read operations from CharArrayIStream\n";
|
||||
|
||||
const string text = "0123456789abcdefghijklmnopqrstuvwxyz\nABCDEF\nGHIJK "
|
||||
"LMNOPQ";
|
||||
std::unique_ptr<char[]> buf(new char[text.size()]);
|
||||
std::size_t n;
|
||||
|
||||
simgear::CharArrayIStream caStream(&text[0], text.size());
|
||||
caStream.exceptions(std::ios_base::badbit); // throw if badbit is set
|
||||
|
||||
SG_CHECK_EQUAL(caStream.data(), &text[0]);
|
||||
SG_CHECK_EQUAL(caStream.size(), text.size());
|
||||
|
||||
SG_VERIFY(caStream.get(buf[0])); // get pointer = 1
|
||||
SG_CHECK_EQUAL(buf[0], text[0]);
|
||||
|
||||
caStream.putback(buf[0]); // get pointer = 0
|
||||
SG_CHECK_EQUAL(caStream.get(), traits::to_int_type(text[0])); // get ptr = 1
|
||||
|
||||
// std::iostream::operator bool() will return false due to EOF being reached
|
||||
SG_VERIFY(!caStream.read(&buf[1],
|
||||
std::numeric_limits<std::streamsize>::max()));
|
||||
// If badbit had been set, it would have caused an exception to be raised
|
||||
SG_VERIFY(caStream.eof() && caStream.fail() && !caStream.bad());
|
||||
|
||||
// The conversion to std::size_t is safe because caStream.gcount() returns a
|
||||
// non-negative value which, in this case, can't exceed the size of the
|
||||
// buffer managed by caStream's associated stream buffer, i.e. text.size().
|
||||
n = streamsizeToSize_t(caStream.gcount());
|
||||
SG_CHECK_EQUAL(n, caStream.size() - 1);
|
||||
SG_CHECK_EQUAL(string(caStream.data(), caStream.size()), text);
|
||||
|
||||
SG_CHECK_EQUAL(caStream.get(), EOF);
|
||||
SG_VERIFY(caStream.eof() && caStream.fail() && !caStream.bad());
|
||||
|
||||
// Test stream extraction: operator>>()
|
||||
caStream.clear(); // clear the error state flags
|
||||
SG_VERIFY(caStream.seekg(0)); // rewind
|
||||
std::vector<string> expectedWords = {
|
||||
"0123456789abcdefghijklmnopqrstuvwxyz",
|
||||
"ABCDEF",
|
||||
"GHIJK",
|
||||
"LMNOPQ"
|
||||
};
|
||||
string str;
|
||||
|
||||
for (int i = 0; caStream >> str; i++) {
|
||||
SG_CHECK_EQUAL(str, expectedWords[i]);
|
||||
}
|
||||
|
||||
SG_VERIFY(caStream.eof() && caStream.fail() && !caStream.bad());
|
||||
}
|
||||
|
||||
void test_CharArrayOStream_simple()
|
||||
{
|
||||
cerr << "Testing write operations to CharArrayOStream\n";
|
||||
|
||||
const string text = "0123456789abcdefghijklmnopqrstuvwxyz\nABCDEF\nGHIJK "
|
||||
"LMNOPQ";
|
||||
// One could also use an std::vector<char>, but then beware of reallocations!
|
||||
std::unique_ptr<char[]> buf(new char[text.size()]);
|
||||
std::fill_n(buf.get(), text.size(), '\0'); // to ensure reproducible results
|
||||
|
||||
simgear::CharArrayOStream caStream(&buf[0], text.size());
|
||||
|
||||
SG_CHECK_EQUAL(caStream.data(), &buf[0]);
|
||||
SG_CHECK_EQUAL(caStream.size(), text.size());
|
||||
|
||||
SG_VERIFY(caStream.put(text[0])); // buf[0] = text[0], put pointer = 1
|
||||
SG_CHECK_EQUAL(buf[0], text[0]);
|
||||
|
||||
SG_VERIFY(caStream.seekp(8)); // put pointer = 8
|
||||
// buf[8:23] = text[8:23] (meaning: buf[8] = text[8], ..., buf[22] = text[22])
|
||||
SG_VERIFY(caStream.write(&text[8], 15)); // put pointer = 23
|
||||
buf[1] = 'X'; // write garbage to buf[1]
|
||||
buf[2] = 'Y'; // and to buf[2]
|
||||
SG_VERIFY(caStream.seekp(-22, std::ios_base::cur)); // put pointer = 23-22 = 1
|
||||
SG_VERIFY(caStream.write(&text[1], 7)); // buf[1:8] = text[1:8]
|
||||
|
||||
// The std::ios_base::beg argument is superfluous here---just for testing.
|
||||
SG_VERIFY(caStream.seekp(23, std::ios_base::beg)); // put pointer = 23
|
||||
// Test stream insertion: operator<<()
|
||||
SG_VERIFY(caStream << text.substr(23, 10));
|
||||
SG_VERIFY(caStream.write(&text[33], text.size() - 33)); // all that remains
|
||||
SG_VERIFY(!caStream.put('Z')); // doesn't fit in caStream's buffer
|
||||
SG_VERIFY(caStream.bad()); // put() set the stream's badbit flag
|
||||
|
||||
SG_CHECK_EQUAL(string(caStream.data(), caStream.size()), text);
|
||||
}
|
||||
|
||||
void test_CharArrayIOStream_readWriteSeekPutbackEtc()
|
||||
{
|
||||
cerr << "Testing read, write, seek, putback... from/to CharArrayIOStream\n";
|
||||
|
||||
const string text = "0123456789abcdefghijklmnopqrstuvwxyz\nABCDEF\nGHIJK "
|
||||
"LMNOPQ";
|
||||
std::unique_ptr<char[]> buf(new char[text.size()]);
|
||||
std::size_t n;
|
||||
char ch;
|
||||
|
||||
simgear::CharArrayIOStream caStream(&buf[0], text.size());
|
||||
caStream.exceptions(std::ios_base::badbit); // throw if badbit is set
|
||||
|
||||
SG_CHECK_EQUAL(caStream.data(), &buf[0]);
|
||||
SG_CHECK_EQUAL(caStream.size(), text.size());
|
||||
|
||||
SG_VERIFY(caStream.put(text[0])); // buf[0] = text[0], put pointer = 1
|
||||
SG_CHECK_EQUAL(buf[0], text[0]);
|
||||
SG_VERIFY(caStream.get(ch)); // read it back from buf, get pointer = 1
|
||||
SG_CHECK_EQUAL(ch, text[0]);
|
||||
|
||||
caStream.putback(buf[0]); // get pointer = 0
|
||||
SG_CHECK_EQUAL(caStream.get(), traits::to_int_type(text[0])); // get ptr = 1
|
||||
|
||||
SG_VERIFY(caStream.seekp(5));
|
||||
// buf[5:10] = text[5:10] (meaning: buf[5] = text[5], ..., buf[9] = text[9])
|
||||
SG_VERIFY(caStream.write(&text[5], 5)); // put pointer = 10
|
||||
buf[1] = 'X'; // write garbage to buf[1]
|
||||
buf[2] = 'Y'; // and to buf[2]
|
||||
SG_VERIFY(caStream.seekp(-9, std::ios_base::cur)); // put pointer = 10 - 9 = 1
|
||||
SG_VERIFY(caStream.write(&text[1], 4)); // buf[1:5] = text[1:5]
|
||||
|
||||
SG_VERIFY(caStream.seekp(10)); // put pointer = 10
|
||||
// buf[10:] = text[10:]
|
||||
SG_VERIFY(caStream.write(&text[10], text.size() - 10));
|
||||
|
||||
std::unique_ptr<char[]> buf2(new char[caStream.size() - 10]);
|
||||
SG_VERIFY(caStream.seekg(10)); // get pointer = 10
|
||||
// std::iostream::operator bool() will return false due to EOF being reached
|
||||
SG_VERIFY(!caStream.read(&buf2[0],
|
||||
std::numeric_limits<std::streamsize>::max()));
|
||||
// If badbit had been set, it would have caused an exception to be raised
|
||||
SG_VERIFY(caStream.eof() && caStream.fail() && !caStream.bad());
|
||||
|
||||
// The conversion to std::size_t is safe because caStream.gcount() returns a
|
||||
// non-negative value which, in this case, can't exceed the size of the
|
||||
// buffer managed by caStream's associated stream buffer, i.e. text.size().
|
||||
n = streamsizeToSize_t(caStream.gcount());
|
||||
SG_CHECK_EQUAL(n, caStream.size() - 10);
|
||||
SG_CHECK_EQUAL(caStream.get(), EOF);
|
||||
|
||||
SG_CHECK_EQUAL(string(&buf2[0], caStream.size() - 10),
|
||||
string(&text[10], text.size() - 10));
|
||||
SG_CHECK_EQUAL(string(caStream.data(), caStream.size()), text);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
test_CharArrayStreambuf_basicOperations();
|
||||
test_CharArrayStreambuf_readOrWriteLargestPossibleAmount();
|
||||
test_CharArrayIStream_simple();
|
||||
test_CharArrayOStream_simple();
|
||||
test_CharArrayIOStream_readWriteSeekPutbackEtc();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -1,845 +0,0 @@
|
||||
// -*- 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 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 Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA.
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <string>
|
||||
#include <ios> // std::streamsize
|
||||
#include <istream>
|
||||
#include <memory> // std::unique_ptr
|
||||
#include <utility> // std::move()
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
#include <limits> // std::numeric_limits
|
||||
#include <type_traits> // std::make_unsigned(), std::underlying_type
|
||||
#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>;
|
||||
|
||||
// Cast an enum value to its underlying type
|
||||
template <typename T>
|
||||
static constexpr typename std::underlying_type<T>::type enumValue(T e) {
|
||||
return static_cast<typename std::underlying_type<T>::type>(e);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Requirement: 'val' must be non-negative.
|
||||
//
|
||||
// Return:
|
||||
// - 'val' cast as BoundType if it's lower than the largest value BoundType
|
||||
// can represent;
|
||||
// - this largest value otherwise.
|
||||
template<class BoundType, class T>
|
||||
static BoundType clipCast(T val)
|
||||
{
|
||||
typedef typename std::make_unsigned<T>::type uT;
|
||||
typedef typename std::make_unsigned<BoundType>::type uBoundType;
|
||||
assert(val >= 0); // otherwise, the comparison and cast to uT would be unsafe
|
||||
|
||||
// Casts to avoid the signed-compare warning; they don't affect the values,
|
||||
// since both are non-negative.
|
||||
if (static_cast<uT>(val) <
|
||||
static_cast<uBoundType>( std::numeric_limits<BoundType>::max() )) {
|
||||
return static_cast<BoundType>(val);
|
||||
} else {
|
||||
return std::numeric_limits<BoundType>::max();
|
||||
}
|
||||
}
|
||||
|
||||
// Requirement: 'size' must be non-negative.
|
||||
//
|
||||
// Return:
|
||||
// - 'size' if it is lower than or equal to std::numeric_limits<uInt>::max();
|
||||
// - std::numeric_limits<uInt>::max() cast as a T otherwise (this is always
|
||||
// possible in a lossless way, since in this case, one has
|
||||
// 0 <= std::numeric_limits<uInt>::max() < size, and 'size' is of type T).
|
||||
//
|
||||
// Note: uInt is the type of z_stream.avail_in and z_stream.avail_out, hence
|
||||
// the function name.
|
||||
template<class T>
|
||||
static T zlibChunk(T size)
|
||||
{
|
||||
typedef typename std::make_unsigned<T>::type uT;
|
||||
assert(size >= 0); // otherwise, the comparison and cast to uT would be unsafe
|
||||
|
||||
if (static_cast<uT>(size) <= std::numeric_limits<uInt>::max()) {
|
||||
return size;
|
||||
} else {
|
||||
// In this case, we are sure that T can represent
|
||||
// std::numeric_limits<uInt>::max(), thus the cast is safe.
|
||||
return static_cast<T>(std::numeric_limits<uInt>::max());
|
||||
}
|
||||
}
|
||||
|
||||
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),
|
||||
_iStream_p(nullptr),
|
||||
_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(
|
||||
std::unique_ptr<std::istream> iStream_p,
|
||||
const SGPath& path,
|
||||
char* inBuf,
|
||||
std::size_t inBufSize,
|
||||
char *outBuf,
|
||||
std::size_t outBufSize,
|
||||
std::size_t putbackSize)
|
||||
: ZlibAbstractIStreambuf(*iStream_p, path, inBuf, inBufSize,
|
||||
outBuf, outBufSize, putbackSize)
|
||||
{
|
||||
// Take ownership of the object. This is a way to ensure that the _iStream
|
||||
// reference stays valid as long as our instance is alive, and that the
|
||||
// corresponding std::istream object is automatically destroyed as soon as
|
||||
// our instance is itself destroyed.
|
||||
_iStream_p = std::move(iStream_p);
|
||||
}
|
||||
|
||||
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 OperationType::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 OperationType::COMPRESSION:
|
||||
throw std::logic_error(
|
||||
"Called ZlibAbstractIStreambuf::handleZ_BUF_ERROR() with "
|
||||
"operationType() == ZlibAbstractIStreambuf::OperationType::COMPRESSION");
|
||||
default:
|
||||
throw std::logic_error(
|
||||
"Unexpected operationType() in "
|
||||
"ZlibAbstractIStreambuf::handleZ_BUF_ERROR(): " +
|
||||
std::to_string(enumValue(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) {
|
||||
_zstream.avail_out = clipCast<uInt>(remainingSpaceInOutBuf);
|
||||
|
||||
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 = clipCast<uInt>(alreadyAvailable);
|
||||
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 = clipCast<std::streamsize>(
|
||||
_inBuf + _inBufSize - _inBufEndPtr);
|
||||
_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); <-- already done in clipCast<uInt>()
|
||||
_zstream.avail_in = clipCast<uInt>(availableChars);
|
||||
|
||||
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)
|
||||
{
|
||||
// Despite the somewhat misleading footnote 296 of §27.5.3 of the C++11
|
||||
// standard, one can't assume std::size_t to be at least as large as
|
||||
// std::streamsize (64 bits std::streamsize in a 32 bits Windows program).
|
||||
std::streamsize remaining = n;
|
||||
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) {
|
||||
// Number of available chars in _outBuf
|
||||
std::ptrdiff_t avail = egptr() - gptr();
|
||||
if (avail == 0) { // our internal buffer is empty
|
||||
break;
|
||||
}
|
||||
|
||||
// We need an int for gbump(), at least in C++11.
|
||||
int chunkSize_i = clipCast<int>(avail);
|
||||
if (chunkSize_i > remaining) {
|
||||
chunkSize_i = static_cast<int>(remaining);
|
||||
}
|
||||
assert(chunkSize_i >= 0);
|
||||
|
||||
std::copy(gptr(), gptr() + chunkSize_i, writePtr);
|
||||
gbump(chunkSize_i);
|
||||
writePtr += chunkSize_i;
|
||||
// This cast is okay because 0 <= chunkSize_i <= remaining, which is an
|
||||
// std::streamsize
|
||||
remaining -= static_cast<std::streamsize>(chunkSize_i);
|
||||
}
|
||||
|
||||
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) {
|
||||
std::streamsize chunkSize_s = zlibChunk(remaining);
|
||||
// chunkSize_s > 0 and does fit in a zlib uInt: that's the whole point of
|
||||
// zlibChunk.
|
||||
_zstream.avail_out = static_cast<uInt>(chunkSize_s);
|
||||
|
||||
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_s - _zstream.avail_out is the number of chars written by zlib.
|
||||
// 0 <= _zstream.avail_out <= chunkSize_s, which is an std::streamsize.
|
||||
remaining -= chunkSize_s - static_cast<std::streamsize>(_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, copy chars to the putback area.
|
||||
std::size_t nbPutbackChars = xsgetn_preparePutbackArea(
|
||||
origGptr, dest, reinterpret_cast<char*>(_zstream.next_out));
|
||||
setg(_outBuf + _putbackSize - nbPutbackChars, // start of putback area
|
||||
_outBuf + _putbackSize, // the buffer for pending,
|
||||
_outBuf + _putbackSize); // available data is empty
|
||||
|
||||
assert(remaining >= 0);
|
||||
assert(n - remaining >= 0);
|
||||
// Total number of chars copied.
|
||||
return n - remaining;
|
||||
}
|
||||
|
||||
// Utility method for xsgetn(): copy some chars to the putback area
|
||||
std::size_t ZlibAbstractIStreambuf::xsgetn_preparePutbackArea(
|
||||
char* origGptr, char* dest, char* writePtr)
|
||||
{
|
||||
// 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.
|
||||
|
||||
// It seems std::ptrdiff_t is the signed counterpart of std::size_t,
|
||||
// therefore this should always hold (even with equality).
|
||||
static_assert(sizeof(std::size_t) >= sizeof(std::ptrdiff_t),
|
||||
"Unexpected: sizeof(std::size_t) < sizeof(std::ptrdiff_t)");
|
||||
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;
|
||||
|
||||
// Are there chars in _outBuf that need to be copied to the putback area?
|
||||
if (nbPutbackChars > inDestBuffer) {
|
||||
std::size_t chunkSize = nbPutbackChars - inDestBuffer; // yes, this number
|
||||
std::copy(origGptr - chunkSize, origGptr,
|
||||
_outBuf + _putbackSize - nbPutbackChars);
|
||||
nbPutbackCharsToGo -= chunkSize;
|
||||
}
|
||||
|
||||
// Finally, copy those that are not in _outBuf
|
||||
std::copy(writePtr - nbPutbackCharsToGo, writePtr,
|
||||
_outBuf + _putbackSize - nbPutbackCharsToGo);
|
||||
|
||||
return nbPutbackChars;
|
||||
}
|
||||
|
||||
// ***************************************************************************
|
||||
// * 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(
|
||||
std::unique_ptr<std::istream> iStream_p,
|
||||
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)
|
||||
: ZlibCompressorIStreambuf(*iStream_p, path, compressionLevel, format,
|
||||
memStrategy, inBuf, inBufSize, outBuf, outBufSize,
|
||||
putbackSize)
|
||||
{
|
||||
_iStream_p = std::move(iStream_p); // take ownership of the object
|
||||
}
|
||||
|
||||
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 OperationType::COMPRESSION;
|
||||
}
|
||||
|
||||
void ZlibCompressorIStreambuf::zStreamInit(int compressionLevel,
|
||||
ZLibCompressionFormat format,
|
||||
ZLibMemoryStrategy memStrategy)
|
||||
{
|
||||
int windowBits, memLevel;
|
||||
|
||||
// Intentionally not listing ZLibCompressionFormat::AUTODETECT here (it is
|
||||
// only for decompression!)
|
||||
switch (format) {
|
||||
case ZLibCompressionFormat::ZLIB:
|
||||
windowBits = 15;
|
||||
break;
|
||||
case ZLibCompressionFormat::GZIP:
|
||||
windowBits = 31;
|
||||
break;
|
||||
default:
|
||||
throw std::logic_error("Unexpected compression format: " +
|
||||
std::to_string(enumValue(format)));
|
||||
}
|
||||
|
||||
switch (memStrategy) {
|
||||
case ZLibMemoryStrategy::FAVOR_MEMORY_OVER_SPEED:
|
||||
memLevel = 8;
|
||||
break;
|
||||
case ZLibMemoryStrategy::FAVOR_SPEED_OVER_MEMORY:
|
||||
memLevel = 9;
|
||||
break;
|
||||
default:
|
||||
throw std::logic_error("Unexpected memory strategy: " +
|
||||
std::to_string(enumValue(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(
|
||||
std::unique_ptr<std::istream> iStream_p,
|
||||
const SGPath& path,
|
||||
ZLibCompressionFormat format,
|
||||
char* inBuf,
|
||||
std::size_t inBufSize,
|
||||
char *outBuf,
|
||||
std::size_t outBufSize,
|
||||
std::size_t putbackSize)
|
||||
: ZlibDecompressorIStreambuf(*iStream_p, path, format, inBuf, inBufSize,
|
||||
outBuf, outBufSize, putbackSize)
|
||||
{
|
||||
_iStream_p = std::move(iStream_p); // take ownership of the object
|
||||
}
|
||||
|
||||
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 OperationType::DECOMPRESSION;
|
||||
}
|
||||
|
||||
void ZlibDecompressorIStreambuf::zStreamInit(ZLibCompressionFormat format)
|
||||
{
|
||||
int windowBits;
|
||||
|
||||
switch (format) {
|
||||
case ZLibCompressionFormat::ZLIB:
|
||||
windowBits = 15;
|
||||
break;
|
||||
case ZLibCompressionFormat::GZIP:
|
||||
windowBits = 31;
|
||||
break;
|
||||
case ZLibCompressionFormat::AUTODETECT:
|
||||
windowBits = 47; // 47 = 32 + 15
|
||||
break;
|
||||
default:
|
||||
throw std::logic_error("Unexpected compression format: " +
|
||||
std::to_string(enumValue(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)
|
||||
: std::istream(nullptr),
|
||||
_streamBuf(iStream, path, compressionLevel, format, memStrategy, inBuf,
|
||||
inBufSize, outBuf, outBufSize, putbackSize)
|
||||
{
|
||||
// Associate _streamBuf to 'this' and clear the error state flags
|
||||
rdbuf(&_streamBuf);
|
||||
}
|
||||
|
||||
ZlibCompressorIStream::ZlibCompressorIStream(
|
||||
std::unique_ptr<std::istream> iStream_p,
|
||||
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)
|
||||
: std::istream(nullptr),
|
||||
_streamBuf(std::move(iStream_p), path, compressionLevel, format,
|
||||
memStrategy, inBuf, inBufSize, outBuf, outBufSize, putbackSize)
|
||||
{
|
||||
// Associate _streamBuf to 'this' and clear the error state flags
|
||||
rdbuf(&_streamBuf);
|
||||
}
|
||||
|
||||
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)
|
||||
: std::istream(nullptr),
|
||||
_streamBuf(iStream, path, format, inBuf, inBufSize, outBuf, outBufSize,
|
||||
putbackSize)
|
||||
{
|
||||
// Associate _streamBuf to 'this' and clear the error state flags
|
||||
rdbuf(&_streamBuf);
|
||||
}
|
||||
|
||||
ZlibDecompressorIStream::ZlibDecompressorIStream(
|
||||
std::unique_ptr<std::istream> iStream_p,
|
||||
const SGPath& path,
|
||||
ZLibCompressionFormat format,
|
||||
char* inBuf,
|
||||
std::size_t inBufSize,
|
||||
char *outBuf,
|
||||
std::size_t outBufSize,
|
||||
std::size_t putbackSize)
|
||||
: std::istream(nullptr),
|
||||
_streamBuf(std::move(iStream_p), path, format, inBuf, inBufSize,
|
||||
outBuf, outBufSize, putbackSize)
|
||||
{
|
||||
// Associate _streamBuf to 'this' and clear the error state flags
|
||||
rdbuf(&_streamBuf);
|
||||
}
|
||||
|
||||
ZlibDecompressorIStream::~ZlibDecompressorIStream()
|
||||
{ }
|
||||
|
||||
} // of namespace simgear
|
||||
@@ -1,477 +0,0 @@
|
||||
// -*- 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 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 Library General Public
|
||||
// License along with this library; 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 <ios> // std::streamsize
|
||||
#include <istream>
|
||||
#include <streambuf>
|
||||
#include <memory> // std::unique_ptr
|
||||
#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 class ZLibCompressionFormat {
|
||||
ZLIB = 0,
|
||||
GZIP,
|
||||
AUTODETECT
|
||||
};
|
||||
|
||||
enum class ZLibMemoryStrategy {
|
||||
FAVOR_MEMORY_OVER_SPEED = 0,
|
||||
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);
|
||||
|
||||
// Alternate constructor with sink semantics for the “source” std::istream.
|
||||
// When used, the class takes ownership of the std::istream instance pointed
|
||||
// to by the first constructor argument, and keeps it alive as long as the
|
||||
// object this constructor is for is itself alive.
|
||||
explicit ZlibAbstractIStreambuf(std::unique_ptr<std::istream> iStream_p,
|
||||
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;
|
||||
virtual ~ZlibAbstractIStreambuf();
|
||||
|
||||
protected:
|
||||
enum class OperationType {
|
||||
COMPRESSION = 0,
|
||||
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;
|
||||
// Pointer to the same, used when calling the constructor that takes an
|
||||
// std::unique_ptr<std::istream> as its first argument; empty
|
||||
// std::unique_ptr object otherwise.
|
||||
std::unique_ptr<std::istream> _iStream_p;
|
||||
|
||||
// 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.
|
||||
virtual 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).
|
||||
virtual std::streamsize xsgetn(char* dest, std::streamsize n) override;
|
||||
// Utility method for xsgetn()
|
||||
std::size_t xsgetn_preparePutbackArea(char* origGptr, char* dest,
|
||||
char* writePtr);
|
||||
// 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 ZLibCompressionFormat::ZLIB or
|
||||
// ZLibCompressionFormat::GZIP
|
||||
// memStrategy either ZLibMemoryStrategy::FAVOR_MEMORY_OVER_SPEED or
|
||||
// ZLibMemoryStrategy::FAVOR_SPEED_OVER_MEMORY
|
||||
explicit ZlibCompressorIStreambuf(
|
||||
std::istream& iStream,
|
||||
const SGPath& path = SGPath(),
|
||||
int compressionLevel = Z_DEFAULT_COMPRESSION,
|
||||
ZLibCompressionFormat format = ZLibCompressionFormat::ZLIB,
|
||||
ZLibMemoryStrategy memStrategy = ZLibMemoryStrategy::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);
|
||||
|
||||
// Alternate constructor with sink semantics for the “source” std::istream.
|
||||
explicit ZlibCompressorIStreambuf(
|
||||
std::unique_ptr<std::istream> _iStream_p,
|
||||
const SGPath& path = SGPath(),
|
||||
int compressionLevel = Z_DEFAULT_COMPRESSION,
|
||||
ZLibCompressionFormat format = ZLibCompressionFormat::ZLIB,
|
||||
ZLibMemoryStrategy memStrategy = ZLibMemoryStrategy::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;
|
||||
virtual ~ZlibCompressorIStreambuf();
|
||||
|
||||
protected:
|
||||
virtual 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.
|
||||
virtual 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 ZLibCompressionFormat::ZLIB,
|
||||
// ZLibCompressionFormat::GZIP or
|
||||
// ZLibCompressionFormat::AUTODETECT
|
||||
explicit ZlibDecompressorIStreambuf(
|
||||
std::istream& iStream,
|
||||
const SGPath& path = SGPath(),
|
||||
ZLibCompressionFormat format = ZLibCompressionFormat::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
|
||||
|
||||
// Alternate constructor with sink semantics for the “source” std::istream.
|
||||
explicit ZlibDecompressorIStreambuf(
|
||||
std::unique_ptr<std::istream> _iStream_p,
|
||||
const SGPath& path = SGPath(),
|
||||
ZLibCompressionFormat format = ZLibCompressionFormat::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;
|
||||
virtual ~ZlibDecompressorIStreambuf();
|
||||
|
||||
protected:
|
||||
virtual OperationType operationType() const override;
|
||||
void zStreamInit(ZLibCompressionFormat format);
|
||||
virtual 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 = ZLibCompressionFormat::ZLIB,
|
||||
ZLibMemoryStrategy memStrategy = ZLibMemoryStrategy::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
|
||||
|
||||
// Alternate constructor with sink semantics for the “source” std::istream.
|
||||
explicit ZlibCompressorIStream(
|
||||
std::unique_ptr<std::istream> _iStream_p,
|
||||
const SGPath& path = SGPath(),
|
||||
int compressionLevel = Z_DEFAULT_COMPRESSION,
|
||||
ZLibCompressionFormat format = ZLibCompressionFormat::ZLIB,
|
||||
ZLibMemoryStrategy memStrategy = ZLibMemoryStrategy::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;
|
||||
virtual ~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 = ZLibCompressionFormat::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
|
||||
|
||||
// Alternate constructor with sink semantics for the “source” std::istream.
|
||||
explicit ZlibDecompressorIStream(
|
||||
std::unique_ptr<std::istream> _iStream_p,
|
||||
const SGPath& path = SGPath(),
|
||||
ZLibCompressionFormat format = ZLibCompressionFormat::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;
|
||||
virtual ~ZlibDecompressorIStream();
|
||||
|
||||
private:
|
||||
ZlibDecompressorIStreambuf _streamBuf;
|
||||
};
|
||||
|
||||
} // of namespace simgear
|
||||
|
||||
#endif // of SG_ZLIBSTREAM_HXX
|
||||
@@ -1,732 +0,0 @@
|
||||
// -*- coding: utf-8 -*-
|
||||
//
|
||||
// zlibstream_test.cxx --- Automated tests for zlibstream.cxx / zlibstream.hxx
|
||||
//
|
||||
// Copyright (C) 2017 Florent Rougon
|
||||
//
|
||||
// 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 Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
// MA 02110-1301 USA.
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <ios> // std::basic_ios, std::streamsize...
|
||||
#include <iostream> // std::ios_base, std::cerr, etc.
|
||||
#include <sstream>
|
||||
#include <array>
|
||||
#include <random>
|
||||
#include <memory> // std::unique_ptr
|
||||
#include <utility> // std::move()
|
||||
#include <limits> // std::numeric_limits
|
||||
#include <type_traits> // std::make_unsigned()
|
||||
#include <functional> // std::bind()
|
||||
#include <cassert>
|
||||
#include <cstdlib> // EXIT_SUCCESS
|
||||
#include <cstddef> // std::size_t
|
||||
#include <cstring> // strcmp()
|
||||
|
||||
#include <zlib.h> // Z_BEST_COMPRESSION
|
||||
|
||||
#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>
|
||||
|
||||
using std::string;
|
||||
using std::cout;
|
||||
using std::cerr;
|
||||
using traits = std::char_traits<char>;
|
||||
|
||||
typedef typename std::make_unsigned<std::streamsize>::type uStreamSize;
|
||||
|
||||
// Safely convert a non-negative std::streamsize into an std::size_t. If
|
||||
// impossible, bail out.
|
||||
static std::size_t streamsizeToSize_t(std::streamsize n)
|
||||
{
|
||||
SG_CHECK_GE(n, 0);
|
||||
SG_CHECK_LE(static_cast<uStreamSize>(n),
|
||||
std::numeric_limits<std::size_t>::max());
|
||||
|
||||
return static_cast<std::size_t>(n);
|
||||
}
|
||||
|
||||
// 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 reading test_IStreamConstructorWithSinkSemantics() below to see
|
||||
// how to use the classes efficiently.
|
||||
|
||||
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::ZLibCompressionFormat::ZLIB,
|
||||
simgear::ZLibMemoryStrategy::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::ZLibCompressionFormat::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_CHECK_EQUAL(n, bufSize);
|
||||
SG_CHECK_EQUAL(string(buf, static_cast<std::size_t>(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_CHECK_EQUAL(n, bufSize);
|
||||
SG_CHECK_EQUAL(string(buf, static_cast<std::size_t>(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_CHECK_EQUAL(n, 36);
|
||||
SG_CHECK_EQUAL(string(buf2, 36), "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();
|
||||
}
|
||||
|
||||
// Test simgear::ZlibDecompressorIStreambuf::[x]sgetn(), asking the largest
|
||||
// possible amount of chars every time it is called (i.e., the largest value
|
||||
// that can be represented by std::streamsize).
|
||||
void test_ZlibDecompressorIStreambuf_readLargestPossibleAmount()
|
||||
{
|
||||
// Nothing special with these values
|
||||
constexpr std::size_t maxDataSize = 8192;
|
||||
std::istringstream input_ss(randomString(4096, maxDataSize));
|
||||
|
||||
simgear::ZlibCompressorIStream compIStream(
|
||||
input_ss, // input stream
|
||||
SGPath(), // this stream is not associated to a file
|
||||
9, // compression level
|
||||
simgear::ZLibCompressionFormat::ZLIB,
|
||||
simgear::ZLibMemoryStrategy::FAVOR_SPEED_OVER_MEMORY,
|
||||
nullptr, // dynamically allocate the input buffer
|
||||
230, // input buffer size
|
||||
nullptr, // dynamically allocate the output buffer
|
||||
120, // output buffer size
|
||||
1 // putback size
|
||||
);
|
||||
|
||||
// Decompressor stream buffer (std::streambuf subclass) that gets input data
|
||||
// from our compressor 'compIStream' (std::istream subclass)
|
||||
simgear::ZlibDecompressorIStreambuf decompSBuf(
|
||||
compIStream, SGPath(), simgear::ZLibCompressionFormat::ZLIB,
|
||||
nullptr, 150, nullptr, 175, 2);
|
||||
|
||||
std::unique_ptr<char[]> buf(new char[maxDataSize]);
|
||||
std::ostringstream roundTripResult_ss;
|
||||
std::streamsize totalCharsToRead = input_ss.str().size();
|
||||
|
||||
while (totalCharsToRead > 0) {
|
||||
// Ask sgetn() the largest possible amount of chars. Of course, we know we
|
||||
// can't get more than maxDataSize, but this does exercise the code in
|
||||
// interesting ways due to the various types involved (zlib's uInt,
|
||||
// std::size_t and std::streamsize, which have various sizes depending on
|
||||
// the platform).
|
||||
std::streamsize nbCharsRead = decompSBuf.sgetn(
|
||||
&buf[0], std::numeric_limits<std::streamsize>::max());
|
||||
if (nbCharsRead == 0) {
|
||||
break; // no more data
|
||||
}
|
||||
|
||||
// The conversion to std::size_t is safe because decompSBuf.sgetn()
|
||||
// returned a non-negative value which, in this case, can't exceed
|
||||
// maxDataSize.
|
||||
roundTripResult_ss << string(&buf[0], streamsizeToSize_t((nbCharsRead)));
|
||||
}
|
||||
|
||||
SG_CHECK_EQUAL(decompSBuf.sgetc(), EOF);
|
||||
SG_CHECK_EQUAL(roundTripResult_ss.str(), input_ss.str());
|
||||
}
|
||||
|
||||
void test_formattedInputFromDecompressor()
|
||||
{
|
||||
cerr << "Testing ZlibDecompressorIStream >> std::string\n";
|
||||
|
||||
static char inBuf[6];
|
||||
static char outBuf[15];
|
||||
string compressed = compress(
|
||||
lipsum, simgear::ZLibCompressionFormat::ZLIB, Z_BEST_COMPRESSION,
|
||||
simgear::ZLibMemoryStrategy::FAVOR_MEMORY_OVER_SPEED, /* putback size */ 0);
|
||||
std::istringstream compressed_ss(compressed);
|
||||
|
||||
simgear::ZlibDecompressorIStream decompressor(
|
||||
compressed_ss, SGPath(), simgear::ZLibCompressionFormat::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::ZLibCompressionFormat::ZLIB,
|
||||
simgear::ZLibMemoryStrategy::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::ZLibCompressionFormat::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;
|
||||
} catch (const std::exception& e) {
|
||||
// gcc fails to catch std::ios_base::failure due to an inconsistent C++11
|
||||
// ABI between headers and libraries. See bug#66145 for more details.
|
||||
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66145
|
||||
if (!strcmp(e.what(), "basic_ios::clear"))
|
||||
gotException = true;
|
||||
else
|
||||
throw e;
|
||||
}
|
||||
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));
|
||||
// The conversion to std::size_t is safe because decompressor.read()
|
||||
// returns a non-negative value which, in this case, can't exceed
|
||||
// sizeof(buf2).
|
||||
rest += string(buf2, streamsizeToSize_t(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::ZLibCompressionFormat::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::ZLibCompressionFormat::ZLIB,
|
||||
simgear::ZLibCompressionFormat::GZIP}) {
|
||||
for (int compressionLevel: {1, 4, 7, 9}) {
|
||||
for (auto memStrategy: {
|
||||
simgear::ZLibMemoryStrategy::FAVOR_MEMORY_OVER_SPEED,
|
||||
simgear::ZLibMemoryStrategy::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::ZLibCompressionFormat::ZLIB;
|
||||
const int compressionLevel = Z_DEFAULT_COMPRESSION;
|
||||
const auto memStrategy =
|
||||
simgear::ZLibMemoryStrategy::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::ZLibCompressionFormat::ZLIB :
|
||||
simgear::ZLibCompressionFormat::GZIP;
|
||||
|
||||
roundTripWithIStreams(
|
||||
compFormat, Z_BEST_COMPRESSION,
|
||||
simgear::ZLibMemoryStrategy::FAVOR_MEMORY_OVER_SPEED,
|
||||
compInBufSize, compOutBufSize, decompInBufSize, decompOutBufSize,
|
||||
compPutbackSize, decompPutbackSize,
|
||||
/* automatic format detection for decompression */ true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Utility function showing how to return a (unique_ptr to a)
|
||||
// ZlibCompressorIStream instance, that keeps a reference to its data source
|
||||
// as long as the ZlibCompressorIStream instance is alive. Thus, calling code
|
||||
// doesn't have to worry about the lifetime of said data source (here, an
|
||||
// std::istringstream instance).
|
||||
std::unique_ptr<simgear::ZlibCompressorIStream>
|
||||
IStreamConstructorWithSinkSemantics_compressorFactory(const string& str)
|
||||
{
|
||||
std::unique_ptr<std::istringstream> iss(new std::istringstream(str));
|
||||
|
||||
// The returned compressor object retains a “reference” (of unique_ptr type)
|
||||
// to the std::istringstream object pointed to by 'iss' as long as it is
|
||||
// alive. When the returned compressor object (wrapped in a unique_ptr) is
|
||||
// destroyed, this std::istringstream object will be automatically
|
||||
// destroyed too.
|
||||
//
|
||||
// Note: it's an implementation detail, but this test also indirectly
|
||||
// exercises the ZlibCompressorIStreambuf constructor taking an
|
||||
// argument of type std::unique_ptr<std::istream>.
|
||||
return std::unique_ptr<simgear::ZlibCompressorIStream>(
|
||||
new simgear::ZlibCompressorIStream(std::move(iss)));
|
||||
}
|
||||
|
||||
void test_IStreamConstructorWithSinkSemantics()
|
||||
{
|
||||
cerr << "Testing the unique_ptr-based ZlibCompressorIStream constructor\n";
|
||||
|
||||
string someString = randomString(4096, 8192); // arbitrary values
|
||||
// This shows how to get a new compressor or decompressor object from a
|
||||
// factory function. Of course, we could create the object directly on the
|
||||
// stack without using a separate function!
|
||||
std::unique_ptr<simgear::ZlibCompressorIStream> compressor =
|
||||
IStreamConstructorWithSinkSemantics_compressorFactory(someString);
|
||||
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. We won't need the
|
||||
// compressor afterwards, so let's just std::move() its unique_ptr.
|
||||
simgear::ZlibDecompressorIStream decompressor(std::move(compressor));
|
||||
decompressor.exceptions(std::ios_base::badbit);
|
||||
|
||||
std::ostringstream roundTripResult;
|
||||
// Of course, you may want to adjust bufSize depending on the application.
|
||||
static constexpr std::size_t bufSize = 1024;
|
||||
std::unique_ptr<char[]> buf(new char[bufSize]);
|
||||
|
||||
// Relatively efficient way of reading from the decompressor (modulo
|
||||
// possible adjustments to 'bufSize', of course). The decompressed data is
|
||||
// first written to 'buf', then copied to 'roundTripResult'. There is no
|
||||
// other useless copy via, for instance, an intermediate std::string object,
|
||||
// as would be the case if we used std::string(buf.get(), bufSize).
|
||||
//
|
||||
// Of course, ideally 'roundTripResult' would directly pull from
|
||||
// 'decompressor' without going through 'buf', but I don't think this is
|
||||
// possible with std::stringstream and friends. Such an optimized data flow
|
||||
// is however straightforward to implement if you replace 'roundTripResult'
|
||||
// with a custom data sink that calls decompressor.read().
|
||||
do {
|
||||
decompressor.read(buf.get(), bufSize);
|
||||
if (decompressor.gcount() > 0) { // at least one char could be read
|
||||
roundTripResult.write(buf.get(), decompressor.gcount());
|
||||
}
|
||||
} while (decompressor && roundTripResult);
|
||||
|
||||
// 1) If set, badbit would have caused an exception to be raised (see above).
|
||||
// 2) failbit doesn't necessarily indicate an error here: it is set as soon
|
||||
// as the read() call can't provide the requested number of characters.
|
||||
SG_VERIFY(decompressor.eof() && !decompressor.bad());
|
||||
// Because of std::ostringstream::write(), 'roundTripResult' might have its
|
||||
// failbit or badbit set, either of which would indicate a real problem.
|
||||
SG_VERIFY(roundTripResult);
|
||||
SG_CHECK_EQUAL(roundTripResult.str(), someString);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
test_pipeCompOrDecompIStreambufIntoOStream();
|
||||
test_StreambufBasicOperations();
|
||||
test_ZlibDecompressorIStreambuf_readLargestPossibleAmount();
|
||||
test_RoundTripMultiWithIStreams();
|
||||
test_formattedInputFromDecompressor();
|
||||
test_ZlibDecompressorIStream_readPutbackEtc();
|
||||
test_IStreamConstructorWithSinkSemantics();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -94,17 +94,6 @@ 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,
|
||||
@@ -545,16 +534,15 @@ bool SGBinObject::read_bin( const SGPath& file ) {
|
||||
fans_vas.clear();
|
||||
fan_materials.clear();
|
||||
|
||||
gzFile fp = gzFileFromSGPath(file, "rb");
|
||||
if ( fp == NULL ) {
|
||||
SGPath withGZ = file;
|
||||
withGZ.concat(".gz");
|
||||
fp = gzFileFromSGPath(withGZ, "rb");
|
||||
if (fp == nullptr) {
|
||||
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 ) {
|
||||
SG_LOG( SG_EVENT, SG_ALERT,
|
||||
"ERROR: opening " << file << " or " << withGZ << " for reading!");
|
||||
"ERROR: opening " << file << " or " << filegz << " for reading!");
|
||||
|
||||
throw sg_io_exception("Error opening for reading (and .gz)", sg_location(file));
|
||||
throw sg_io_exception("Error opening for reading (and .gz)", sg_location(f));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -969,8 +957,9 @@ bool SGBinObject::write_bin_file(const SGPath& file)
|
||||
SGPath file2(file);
|
||||
file2.create_dir( 0755 );
|
||||
|
||||
gzFile fp = gzFileFromSGPath(file, "wb9");
|
||||
if ( fp == nullptr ) {
|
||||
gzFile fp;
|
||||
std::string localPath = file.local8BitStr();
|
||||
if ( (fp = gzopen( localPath.c_str(), "wb9" )) == NULL ) {
|
||||
cout << "ERROR: opening " << file << " for writing!" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include <string>
|
||||
@@ -69,27 +69,16 @@ SGFile::~SGFile() {
|
||||
bool SGFile::open( const SGProtocolDir d ) {
|
||||
set_dir( d );
|
||||
|
||||
#if defined(SG_WINDOWS)
|
||||
std::wstring n = file_name.wstr();
|
||||
#else
|
||||
std::string n = file_name.utf8Str();
|
||||
#endif
|
||||
|
||||
|
||||
std::string n = file_name.local8BitStr();
|
||||
if ( get_dir() == SG_IO_OUT ) {
|
||||
#if defined(SG_WINDOWS)
|
||||
#ifdef _WIN32
|
||||
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." );
|
||||
@@ -156,7 +145,7 @@ int SGFile::readline( char *buf, int length ) {
|
||||
result = i;
|
||||
}
|
||||
lseek( fp, pos + result, SEEK_SET );
|
||||
|
||||
|
||||
// just in case ...
|
||||
buf[ result ] = '\0';
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
$Id: netBuffer.cxx 1568 2002-09-02 06:05:49Z sjbaker $
|
||||
*/
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "sg_netBuffer.hxx"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
// to write or something...]
|
||||
// Maybe assert valid handle, too?
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include "sg_netChannel.hxx"
|
||||
|
||||
#include <memory>
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <errno.h>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
|
||||
@@ -18,7 +16,6 @@
|
||||
#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;
|
||||
@@ -26,82 +23,23 @@ using std::endl;
|
||||
|
||||
using namespace simgear;
|
||||
|
||||
|
||||
class Watchdog
|
||||
{
|
||||
public:
|
||||
Watchdog() : _interval(0), _timer(0), _running(false) {}
|
||||
~Watchdog() {
|
||||
stop();
|
||||
#define COMPARE(a, b) \
|
||||
if ((a) != (b)) { \
|
||||
cerr << "failed:" << #a << " != " << #b << endl; \
|
||||
cerr << "\tgot:'" << a << "'" << endl; \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
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 );
|
||||
|
||||
const char * EXISTING_RECORD = argc > 1 ? argv[1] : "terrasync.flightgear.org";
|
||||
const char * QSERVICE = argc > 2 ? argv[2] : "https+ws20";
|
||||
|
||||
Watchdog watchdog;
|
||||
watchdog.start(100);
|
||||
|
||||
simgear::Socket::initSockets();
|
||||
|
||||
|
||||
DNS::Client cl;
|
||||
#define EXISTING_RECORD "terrasync.flightgear.org"
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
cout << "test existing NAPTR: " << EXISTING_RECORD << endl;
|
||||
// test existing NAPTR
|
||||
// fgtest.t3r.de. 600 IN NAPTR 999 99 "U" "test" "!^.*$!http://dnstest.flightgear.org/!" .
|
||||
{
|
||||
DNS::NAPTRRequest * naptrRequest = new DNS::NAPTRRequest(EXISTING_RECORD);
|
||||
DNS::Request_ptr r(naptrRequest);
|
||||
@@ -112,24 +50,23 @@ int main(int argc, char* argv[])
|
||||
}
|
||||
|
||||
if( r->isTimeout() ) {
|
||||
cerr << "timeout testing existing record " << EXISTING_RECORD << endl;
|
||||
cerr << "timeout testing existing record " EXISTING_RECORD << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if(naptrRequest->entries.empty()) {
|
||||
cerr << "no results for " << EXISTING_RECORD << endl;
|
||||
cerr << "no results for " EXISTING_RECORD << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
cout << "test for ascending preference/order" << endl;
|
||||
// test for ascending preference/order
|
||||
int order = -1, preference = -1;
|
||||
for( DNS::NAPTRRequest::NAPTR_list::const_iterator it = naptrRequest->entries.begin(); it != naptrRequest->entries.end(); ++it ) {
|
||||
cout << "NAPTR " << (*it)->order << " " << (*it)->preference << " '" << (*it)->service << "' '" << (*it)->regexp << "' '" << (*it)->replacement << "'" << endl;
|
||||
// currently only support "U" which implies empty replacement
|
||||
SG_CHECK_EQUAL((*it)->flags, "U" );
|
||||
SG_CHECK_EQUAL(naptrRequest->entries[0]->replacement, "" );
|
||||
COMPARE((*it)->flags, "U" );
|
||||
COMPARE(naptrRequest->entries[0]->replacement, "" );
|
||||
|
||||
// currently only support ws20, disable temporarily
|
||||
//SG_CHECK_EQUAL((*it)->service, "ws20" );
|
||||
// currently only support ws20
|
||||
COMPARE((*it)->service, "ws20" );
|
||||
|
||||
if( (*it)->order < order ) {
|
||||
cerr << "NAPTR entries not ascending for field 'order'" << endl;
|
||||
@@ -157,31 +94,8 @@ int main(int argc, char* argv[])
|
||||
|
||||
}
|
||||
}
|
||||
cout << "test existing NAPTR with explicit qservice: " << QSERVICE << endl;
|
||||
{
|
||||
DNS::NAPTRRequest * naptrRequest = new DNS::NAPTRRequest(EXISTING_RECORD);
|
||||
naptrRequest->qservice = QSERVICE;
|
||||
DNS::Request_ptr r(naptrRequest);
|
||||
cl.makeRequest(r);
|
||||
while( !r->isComplete() && !r->isTimeout()) {
|
||||
SGTimeStamp::sleepForMSec(200);
|
||||
cl.update(0);
|
||||
}
|
||||
|
||||
if( r->isTimeout() ) {
|
||||
cerr << "timeout testing existing record " << EXISTING_RECORD << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if(naptrRequest->entries.empty()) {
|
||||
cerr << "no results for " << EXISTING_RECORD << endl;
|
||||
//return EXIT_FAILURE; // not yet a failure - probably add this for 2017.4 and create DNS entries
|
||||
}
|
||||
for( DNS::NAPTRRequest::NAPTR_list::const_iterator it = naptrRequest->entries.begin(); it != naptrRequest->entries.end(); ++it ) {
|
||||
cout << "NAPTR " << (*it)->order << " " << (*it)->preference << " '" << (*it)->service << "' '" << (*it)->regexp << "' '" << (*it)->replacement << "'" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
cout << "test non-existing NAPTR" << endl;
|
||||
// test non-existing NAPTR
|
||||
{
|
||||
DNS::NAPTRRequest * naptrRequest = new DNS::NAPTRRequest("jurkxkqdiufqzpfvzqok.prozhqrlcaavbxifkkhf");
|
||||
DNS::Request_ptr r(naptrRequest);
|
||||
@@ -195,7 +109,7 @@ int main(int argc, char* argv[])
|
||||
cerr << "timeout testing non-existing record." << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
SG_CHECK_EQUAL(naptrRequest->entries.size(), 0 );
|
||||
COMPARE(naptrRequest->entries.size(), 0 );
|
||||
}
|
||||
|
||||
cout << "all tests passed ok" << endl;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <cerrno>
|
||||
#include <errno.h>
|
||||
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#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>
|
||||
|
||||
@@ -42,6 +41,18 @@ 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
|
||||
{
|
||||
@@ -115,9 +126,8 @@ public:
|
||||
d << contentStr;
|
||||
push(d.str().c_str());
|
||||
} else if (path == "/test_headers") {
|
||||
SG_CHECK_EQUAL(requestHeaders["X-Foo"], string("Bar"));
|
||||
SG_CHECK_EQUAL(requestHeaders["X-AnotherHeader"],
|
||||
string("A longer value"));
|
||||
COMPARE(requestHeaders["X-Foo"], string("Bar"));
|
||||
COMPARE(requestHeaders["X-AnotherHeader"], string("A longer value"));
|
||||
|
||||
string contentStr(BODY1);
|
||||
stringstream d;
|
||||
@@ -294,7 +304,7 @@ public:
|
||||
} else if (path == "/test_put") {
|
||||
std::cerr << "sending PUT response" << std::endl;
|
||||
|
||||
SG_CHECK_EQUAL(buffer, BODY3);
|
||||
COMPARE(buffer, BODY3);
|
||||
stringstream d;
|
||||
d << "HTTP/1.1 " << 204 << " " << reasonForCode(204) << "\r\n";
|
||||
d << "\r\n"; // final CRLF to terminate the headers
|
||||
@@ -304,7 +314,7 @@ public:
|
||||
|
||||
std::string entityStr = "http://localhost:2000/something.txt";
|
||||
|
||||
SG_CHECK_EQUAL(buffer, BODY3);
|
||||
COMPARE(buffer, BODY3);
|
||||
stringstream d;
|
||||
d << "HTTP/1.1 " << 201 << " " << reasonForCode(201) << "\r\n";
|
||||
d << "Location:" << entityStr << "\r\n";
|
||||
@@ -375,18 +385,18 @@ int main(int argc, char* argv[])
|
||||
|
||||
// test URL parsing
|
||||
TestRequest* tr1 = new TestRequest("http://localhost.woo.zar:2000/test1?foo=bar");
|
||||
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");
|
||||
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");
|
||||
|
||||
TestRequest* tr2 = new TestRequest("http://192.168.1.1/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");
|
||||
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");
|
||||
|
||||
// basic get request
|
||||
{
|
||||
@@ -395,11 +405,11 @@ int main(int argc, char* argv[])
|
||||
cl.makeRequest(tr);
|
||||
|
||||
waitForComplete(&cl, tr);
|
||||
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));
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseReason(), string("OK"));
|
||||
COMPARE(tr->responseLength(), strlen(BODY1));
|
||||
COMPARE(tr->responseBytesReceived(), strlen(BODY1));
|
||||
COMPARE(tr->bodyData, string(BODY1));
|
||||
}
|
||||
|
||||
{
|
||||
@@ -408,11 +418,11 @@ int main(int argc, char* argv[])
|
||||
cl.makeRequest(tr);
|
||||
|
||||
waitForComplete(&cl, tr);
|
||||
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));
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseReason(), string("OK"));
|
||||
COMPARE(tr->responseLength(), strlen(BODY3));
|
||||
COMPARE(tr->responseBytesReceived(), strlen(BODY3));
|
||||
COMPARE(tr->bodyData, string(BODY3));
|
||||
}
|
||||
|
||||
{
|
||||
@@ -420,7 +430,7 @@ int main(int argc, char* argv[])
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -431,11 +441,11 @@ int main(int argc, char* argv[])
|
||||
cl.makeRequest(tr);
|
||||
|
||||
waitForComplete(&cl, tr);
|
||||
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));
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseReason(), string("OK"));
|
||||
COMPARE(tr->responseLength(), strlen(BODY1));
|
||||
COMPARE(tr->responseBytesReceived(), strlen(BODY1));
|
||||
COMPARE(tr->bodyData, string(BODY1));
|
||||
}
|
||||
|
||||
// larger get request
|
||||
@@ -448,9 +458,9 @@ int main(int argc, char* argv[])
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->responseBytesReceived(), body2Size);
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(body2, body2Size));
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseBytesReceived(), body2Size);
|
||||
COMPARE(tr->bodyData, string(body2, body2Size));
|
||||
}
|
||||
|
||||
cerr << "testing chunked transfer encoding" << endl;
|
||||
@@ -460,12 +470,12 @@ int main(int argc, char* argv[])
|
||||
cl.makeRequest(tr);
|
||||
|
||||
waitForComplete(&cl, tr);
|
||||
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");
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseReason(), string("OK"));
|
||||
COMPARE(tr->responseBytesReceived(), 30);
|
||||
COMPARE(tr->bodyData, "ABCDEFGHABCDEFABCDSTUVABCDSTUV");
|
||||
// check trailers made it too
|
||||
SG_CHECK_EQUAL(tr->headers["x-foobar"], string("wibble"));
|
||||
COMPARE(tr->headers["x-foobar"], string("wibble"));
|
||||
}
|
||||
|
||||
// test 404
|
||||
@@ -474,9 +484,9 @@ int main(int argc, char* argv[])
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 404);
|
||||
SG_CHECK_EQUAL(tr->responseReason(), string("not found"));
|
||||
SG_CHECK_EQUAL(tr->responseLength(), 0);
|
||||
COMPARE(tr->responseCode(), 404);
|
||||
COMPARE(tr->responseReason(), string("not found"));
|
||||
COMPARE(tr->responseLength(), 0);
|
||||
}
|
||||
|
||||
cout << "done 404 test" << endl;
|
||||
@@ -486,7 +496,7 @@ int main(int argc, char* argv[])
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
}
|
||||
|
||||
cout << "done1" << endl;
|
||||
@@ -496,9 +506,9 @@ int main(int argc, char* argv[])
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->responseLength(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(BODY1));
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseLength(), strlen(BODY1));
|
||||
COMPARE(tr->bodyData, string(BODY1));
|
||||
}
|
||||
|
||||
cout << "done2" << endl;
|
||||
@@ -508,9 +518,9 @@ int main(int argc, char* argv[])
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->responseLength(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(BODY1));
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseLength(), strlen(BODY1));
|
||||
COMPARE(tr->bodyData, string(BODY1));
|
||||
}
|
||||
cout << "done3" << endl;
|
||||
// test connectToHost failure
|
||||
@@ -524,7 +534,7 @@ int main(int argc, char* argv[])
|
||||
|
||||
|
||||
const int HOST_NOT_FOUND_CODE = CURLE_COULDNT_RESOLVE_HOST;
|
||||
SG_CHECK_EQUAL(tr->responseCode(), HOST_NOT_FOUND_CODE);
|
||||
COMPARE(tr->responseCode(), HOST_NOT_FOUND_CODE);
|
||||
}
|
||||
|
||||
cout << "testing abrupt close" << endl;
|
||||
@@ -536,7 +546,7 @@ int main(int argc, char* argv[])
|
||||
waitForFailed(&cl, tr);
|
||||
|
||||
const int SERVER_NO_DATA_CODE = CURLE_GOT_NOTHING;
|
||||
SG_CHECK_EQUAL(tr->responseCode(), SERVER_NO_DATA_CODE);
|
||||
COMPARE(tr->responseCode(), SERVER_NO_DATA_CODE);
|
||||
}
|
||||
|
||||
cout << "testing proxy close" << endl;
|
||||
@@ -547,9 +557,9 @@ cout << "testing proxy close" << endl;
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->responseLength(), body2Size);
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(body2, body2Size));
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseLength(), body2Size);
|
||||
COMPARE(tr->bodyData, string(body2, body2Size));
|
||||
}
|
||||
|
||||
{
|
||||
@@ -558,9 +568,9 @@ cout << "testing proxy close" << endl;
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->responseBytesReceived(), body2Size);
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(body2, body2Size));
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseBytesReceived(), body2Size);
|
||||
COMPARE(tr->bodyData, string(body2, body2Size));
|
||||
}
|
||||
|
||||
// pipelining
|
||||
@@ -585,17 +595,17 @@ cout << "testing proxy close" << endl;
|
||||
cl.makeRequest(tr3);
|
||||
|
||||
waitForComplete(&cl, tr3);
|
||||
SG_VERIFY(tr->complete);
|
||||
SG_VERIFY(tr2->complete);
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(BODY1));
|
||||
VERIFY(tr->complete);
|
||||
VERIFY(tr2->complete);
|
||||
COMPARE(tr->bodyData, string(BODY1));
|
||||
|
||||
SG_CHECK_EQUAL(tr2->responseLength(), strlen(BODY3));
|
||||
SG_CHECK_EQUAL(tr2->responseBytesReceived(), strlen(BODY3));
|
||||
SG_CHECK_EQUAL(tr2->bodyData, string(BODY3));
|
||||
COMPARE(tr2->responseLength(), strlen(BODY3));
|
||||
COMPARE(tr2->responseBytesReceived(), strlen(BODY3));
|
||||
COMPARE(tr2->bodyData, string(BODY3));
|
||||
|
||||
SG_CHECK_EQUAL(tr3->bodyData, string(BODY1));
|
||||
COMPARE(tr3->bodyData, string(BODY1));
|
||||
|
||||
SG_CHECK_EQUAL(testServer.connectCount(), 1);
|
||||
COMPARE(testServer.connectCount(), 1);
|
||||
}
|
||||
|
||||
// multiple requests with an HTTP 1.0 server
|
||||
@@ -616,17 +626,17 @@ cout << "testing proxy close" << endl;
|
||||
cl.makeRequest(tr3);
|
||||
|
||||
waitForComplete(&cl, tr3);
|
||||
SG_VERIFY(tr->complete);
|
||||
SG_VERIFY(tr2->complete);
|
||||
VERIFY(tr->complete);
|
||||
VERIFY(tr2->complete);
|
||||
|
||||
SG_CHECK_EQUAL(tr->responseLength(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr->responseBytesReceived(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(BODY1));
|
||||
COMPARE(tr->responseLength(), strlen(BODY1));
|
||||
COMPARE(tr->responseBytesReceived(), strlen(BODY1));
|
||||
COMPARE(tr->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));
|
||||
COMPARE(tr2->responseLength(), strlen(BODY3));
|
||||
COMPARE(tr2->responseBytesReceived(), strlen(BODY3));
|
||||
COMPARE(tr2->bodyData, string(BODY3));
|
||||
COMPARE(tr3->bodyData, string(BODY1));
|
||||
}
|
||||
|
||||
// POST
|
||||
@@ -638,7 +648,7 @@ cout << "testing proxy close" << endl;
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 204);
|
||||
COMPARE(tr->responseCode(), 204);
|
||||
}
|
||||
|
||||
// PUT
|
||||
@@ -650,7 +660,7 @@ cout << "testing proxy close" << endl;
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 204);
|
||||
COMPARE(tr->responseCode(), 204);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -661,7 +671,7 @@ cout << "testing proxy close" << endl;
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 201);
|
||||
COMPARE(tr->responseCode(), 201);
|
||||
}
|
||||
|
||||
// test_zero_length_content
|
||||
@@ -671,9 +681,9 @@ cout << "testing proxy close" << endl;
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->bodyData, string());
|
||||
SG_CHECK_EQUAL(tr->responseBytesReceived(), 0);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->bodyData, string());
|
||||
COMPARE(tr->responseBytesReceived(), 0);
|
||||
}
|
||||
|
||||
// test cancel
|
||||
@@ -701,12 +711,12 @@ cout << "testing proxy close" << endl;
|
||||
|
||||
waitForComplete(&cl, tr3);
|
||||
|
||||
SG_CHECK_EQUAL(tr->responseCode(), -1);
|
||||
SG_CHECK_EQUAL(tr2->responseReason(), "my reason 2");
|
||||
COMPARE(tr->responseCode(), -1);
|
||||
COMPARE(tr2->responseReason(), "my reason 2");
|
||||
|
||||
SG_CHECK_EQUAL(tr3->responseLength(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr3->responseBytesReceived(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr3->bodyData, string(BODY1));
|
||||
COMPARE(tr3->responseLength(), strlen(BODY1));
|
||||
COMPARE(tr3->responseBytesReceived(), strlen(BODY1));
|
||||
COMPARE(tr3->bodyData, string(BODY1));
|
||||
}
|
||||
|
||||
// test cancel
|
||||
@@ -732,16 +742,16 @@ cout << "testing proxy close" << endl;
|
||||
|
||||
waitForComplete(&cl, tr3);
|
||||
|
||||
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(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseLength(), strlen(BODY1));
|
||||
COMPARE(tr->responseBytesReceived(), strlen(BODY1));
|
||||
COMPARE(tr->bodyData, string(BODY1));
|
||||
|
||||
SG_CHECK_EQUAL(tr2->responseCode(), -1);
|
||||
COMPARE(tr2->responseCode(), -1);
|
||||
|
||||
SG_CHECK_EQUAL(tr3->responseLength(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr3->responseBytesReceived(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr3->bodyData, string(BODY1));
|
||||
COMPARE(tr3->responseLength(), strlen(BODY1));
|
||||
COMPARE(tr3->responseBytesReceived(), strlen(BODY1));
|
||||
COMPARE(tr3->bodyData, string(BODY1));
|
||||
}
|
||||
|
||||
{
|
||||
@@ -766,12 +776,12 @@ cout << "testing proxy close" << endl;
|
||||
cl.makeRequest(tr2);
|
||||
|
||||
waitForComplete(&cl, tr2);
|
||||
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));
|
||||
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));
|
||||
}
|
||||
|
||||
cout << "all tests passed ok" << endl;
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#endif
|
||||
|
||||
#include <simgear/misc/sg_dir.hxx>
|
||||
#include <simgear/misc/test_macros.hxx>
|
||||
|
||||
#include "sg_binobj.hxx"
|
||||
|
||||
@@ -23,7 +22,19 @@ 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) {
|
||||
@@ -50,15 +61,15 @@ void test_empty()
|
||||
SGBinObject empty;
|
||||
SGPath path(simgear::Dir::current().file("empty.btg.gz"));
|
||||
bool ok = empty.write_bin_file(path);
|
||||
SG_VERIFY(ok);
|
||||
VERIFY( ok );
|
||||
SGBinObject rd;
|
||||
ok = rd.read_bin(path) ;
|
||||
SG_VERIFY(ok);
|
||||
|
||||
SG_CHECK_EQUAL(rd.get_wgs84_nodes().size(), 0);
|
||||
SG_VERIFY(rd.get_pt_materials().empty());
|
||||
}
|
||||
ok = rd.read_bin(path) ;
|
||||
VERIFY( ok);
|
||||
|
||||
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) {
|
||||
@@ -71,7 +82,7 @@ void comparePoints(const SGBinObject& rd, const std::vector<SGVec3d>& b)
|
||||
cout << pos << endl;
|
||||
}
|
||||
|
||||
SG_VERIFY(equivalent(pos, b[i], 0.1));
|
||||
VERIFY(equivalent(pos, b[i], 0.1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +90,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];
|
||||
SG_VERIFY(equivalent(pos, b[i], 0.001f));
|
||||
VERIFY(equivalent(pos, b[i], 0.001f));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,13 +137,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]);
|
||||
SG_VERIFY(vA == vB);
|
||||
|
||||
SG_CHECK_EQUAL(a.get_tri_materials()[i], b.get_tri_materials()[i]);
|
||||
VERIFY(vA == vB);
|
||||
|
||||
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]);
|
||||
SG_VERIFY(tA == tB);
|
||||
VERIFY(tA == tB);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,17 +189,16 @@ void test_basic()
|
||||
basic.set_texcoords(texCoords);
|
||||
|
||||
bool ok = basic.write_bin_file(path);
|
||||
SG_VERIFY( ok );
|
||||
VERIFY( ok );
|
||||
|
||||
SGBinObject rd;
|
||||
ok = rd.read_bin(path) ;
|
||||
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());
|
||||
|
||||
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());
|
||||
|
||||
comparePoints(rd, points);
|
||||
compareTexCoords(rd, texCoords);
|
||||
}
|
||||
@@ -216,16 +226,15 @@ void test_many_tcs()
|
||||
generate_tris(basic, 20000);
|
||||
|
||||
bool ok = basic.write_bin_file(path);
|
||||
SG_VERIFY( ok );
|
||||
VERIFY( ok );
|
||||
|
||||
SGBinObject rd;
|
||||
ok = rd.read_bin(path) ;
|
||||
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());
|
||||
|
||||
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());
|
||||
|
||||
comparePoints(rd, points);
|
||||
compareTexCoords(rd, texCoords);
|
||||
compareTris(basic, rd);
|
||||
@@ -254,16 +263,15 @@ void test_big()
|
||||
generate_tris(basic, 200000);
|
||||
|
||||
bool ok = basic.write_bin_file(path);
|
||||
SG_VERIFY( ok );
|
||||
VERIFY( ok );
|
||||
|
||||
SGBinObject rd;
|
||||
ok = rd.read_bin(path) ;
|
||||
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());
|
||||
|
||||
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());
|
||||
|
||||
comparePoints(rd, points);
|
||||
compareTexCoords(rd, texCoords);
|
||||
compareTris(basic, rd);
|
||||
@@ -292,15 +300,15 @@ void test_some_objects()
|
||||
generate_tris(basic, 30000); // a number smaller than 2^15!
|
||||
|
||||
bool ok = basic.write_bin_file(path);
|
||||
SG_VERIFY( ok );
|
||||
VERIFY( ok );
|
||||
|
||||
SGBinObject rd;
|
||||
ok = rd.read_bin(path) ;
|
||||
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());
|
||||
|
||||
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());
|
||||
|
||||
comparePoints(rd, points);
|
||||
compareTexCoords(rd, texCoords);
|
||||
compareTris(basic, rd);
|
||||
@@ -329,16 +337,15 @@ void test_many_objects()
|
||||
generate_tris(basic, 200000);
|
||||
|
||||
bool ok = basic.write_bin_file(path);
|
||||
SG_VERIFY( ok );
|
||||
VERIFY( ok );
|
||||
|
||||
SGBinObject rd;
|
||||
ok = rd.read_bin(path) ;
|
||||
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());
|
||||
|
||||
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());
|
||||
|
||||
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/io/iostreams/sgstream.hxx>
|
||||
#include <simgear/misc/sgstream.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include <simgear/structure/callback.hxx>
|
||||
|
||||
@@ -71,7 +71,7 @@ public:
|
||||
int requestCount;
|
||||
bool getWillFail;
|
||||
bool returnCorruptData;
|
||||
std::unique_ptr<SGCallback> accessCallback;
|
||||
std::auto_ptr<SGCallback> accessCallback;
|
||||
|
||||
void clearRequestCounts();
|
||||
|
||||
@@ -400,7 +400,7 @@ void waitForUpdateComplete(HTTP::Client* cl, HTTPRepository* repo)
|
||||
|
||||
void testBasicClone(HTTP::Client* cl)
|
||||
{
|
||||
std::unique_ptr<HTTPRepository> repo;
|
||||
std::auto_ptr<HTTPRepository> repo;
|
||||
SGPath p(simgear::Dir::current().path());
|
||||
p.append("http_repo_basic");
|
||||
simgear::Dir pd(p);
|
||||
@@ -408,6 +408,7 @@ 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());
|
||||
@@ -435,7 +436,7 @@ void testBasicClone(HTTP::Client* cl)
|
||||
|
||||
void testModifyLocalFiles(HTTP::Client* cl)
|
||||
{
|
||||
std::unique_ptr<HTTPRepository> repo;
|
||||
std::auto_ptr<HTTPRepository> repo;
|
||||
SGPath p(simgear::Dir::current().path());
|
||||
p.append("http_repo_modify_local_2");
|
||||
simgear::Dir pd(p);
|
||||
@@ -445,6 +446,7 @@ 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());
|
||||
@@ -476,7 +478,7 @@ void testNoChangesUpdate()
|
||||
|
||||
void testMergeExistingFileWithoutDownload(HTTP::Client* cl)
|
||||
{
|
||||
std::unique_ptr<HTTPRepository> repo;
|
||||
std::auto_ptr<HTTPRepository> repo;
|
||||
SGPath p(simgear::Dir::current().path());
|
||||
p.append("http_repo_merge_existing");
|
||||
simgear::Dir pd(p);
|
||||
@@ -486,6 +488,7 @@ 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
|
||||
@@ -518,7 +521,7 @@ void testMergeExistingFileWithoutDownload(HTTP::Client* cl)
|
||||
|
||||
void testLossOfLocalFiles(HTTP::Client* cl)
|
||||
{
|
||||
std::unique_ptr<HTTPRepository> repo;
|
||||
std::auto_ptr<HTTPRepository> repo;
|
||||
SGPath p(simgear::Dir::current().path());
|
||||
p.append("http_repo_lose_local");
|
||||
simgear::Dir pd(p);
|
||||
@@ -528,6 +531,7 @@ 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");
|
||||
@@ -552,7 +556,7 @@ void testLossOfLocalFiles(HTTP::Client* cl)
|
||||
|
||||
void testAbandonMissingFiles(HTTP::Client* cl)
|
||||
{
|
||||
std::unique_ptr<HTTPRepository> repo;
|
||||
std::auto_ptr<HTTPRepository> repo;
|
||||
SGPath p(simgear::Dir::current().path());
|
||||
p.append("http_repo_missing_files");
|
||||
simgear::Dir pd(p);
|
||||
@@ -565,6 +569,7 @@ 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) {
|
||||
@@ -576,7 +581,7 @@ void testAbandonMissingFiles(HTTP::Client* cl)
|
||||
|
||||
void testAbandonCorruptFiles(HTTP::Client* cl)
|
||||
{
|
||||
std::unique_ptr<HTTPRepository> repo;
|
||||
std::auto_ptr<HTTPRepository> repo;
|
||||
SGPath p(simgear::Dir::current().path());
|
||||
p.append("http_repo_corrupt_files");
|
||||
simgear::Dir pd(p);
|
||||
@@ -589,10 +594,11 @@ 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");
|
||||
}
|
||||
|
||||
@@ -605,6 +611,149 @@ 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;
|
||||
@@ -614,9 +763,53 @@ 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::unique_ptr<HTTPRepository> repo;
|
||||
std::auto_ptr<HTTPRepository> repo;
|
||||
SGPath p(simgear::Dir::current().path());
|
||||
p.append("http_repo_server_modify_during_sync");
|
||||
simgear::Dir pd(p);
|
||||
@@ -629,6 +822,7 @@ 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));
|
||||
|
||||
@@ -638,7 +832,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 modify during sync test");
|
||||
throw sg_exception("Bad result from corrupt files test");
|
||||
}
|
||||
|
||||
std::cout << "Passed test modify server during sync" << std::endl;
|
||||
@@ -647,7 +841,7 @@ void testServerModifyDuringSync(HTTP::Client* cl)
|
||||
|
||||
void testDestroyDuringSync(HTTP::Client* cl)
|
||||
{
|
||||
std::unique_ptr<HTTPRepository> repo;
|
||||
std::auto_ptr<HTTPRepository> repo;
|
||||
SGPath p(simgear::Dir::current().path());
|
||||
p.append("http_repo_destory_during_sync");
|
||||
simgear::Dir pd(p);
|
||||
@@ -660,6 +854,7 @@ void testDestroyDuringSync(HTTP::Client* cl)
|
||||
|
||||
repo.reset(new HTTPRepository(p, cl));
|
||||
repo->setBaseUrl("http://localhost:2000/repo");
|
||||
repo->setEntireRepositoryMode();
|
||||
|
||||
repo->update();
|
||||
|
||||
@@ -674,63 +869,10 @@ 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_INFO );
|
||||
sglog().setLogLevels( SG_ALL, SG_DEBUG );
|
||||
|
||||
HTTP::Client cl;
|
||||
cl.setMaxConnections(1);
|
||||
@@ -765,14 +907,13 @@ 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;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// Test harness.
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <simgear_config.h>
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include <iostream>
|
||||
@@ -31,7 +30,7 @@ void testTarGz()
|
||||
uint8_t* buf = (uint8_t*) alloca(8192);
|
||||
size_t bufSize = f.read((char*) buf, 8192);
|
||||
|
||||
SG_VERIFY(TarExtractor::isTarData(buf, bufSize));
|
||||
VERIFY(TarExtractor::isTarData(buf, bufSize));
|
||||
|
||||
}
|
||||
|
||||
@@ -46,7 +45,7 @@ void testPlainTar()
|
||||
uint8_t* buf = (uint8_t*) alloca(8192);
|
||||
size_t bufSize = f.read((char*) buf, 8192);
|
||||
|
||||
SG_VERIFY(TarExtractor::isTarData(buf, bufSize));
|
||||
VERIFY(TarExtractor::isTarData(buf, bufSize));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#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>
|
||||
|
||||
@@ -42,6 +41,18 @@ 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
|
||||
{
|
||||
@@ -115,9 +126,8 @@ public:
|
||||
d << contentStr;
|
||||
push(d.str().c_str());
|
||||
} else if (path == "/test_headers") {
|
||||
SG_CHECK_EQUAL(requestHeaders["X-Foo"], string("Bar"));
|
||||
SG_CHECK_EQUAL(requestHeaders["X-AnotherHeader"],
|
||||
string("A longer value"));
|
||||
COMPARE(requestHeaders["X-Foo"], string("Bar"));
|
||||
COMPARE(requestHeaders["X-AnotherHeader"], string("A longer value"));
|
||||
|
||||
string contentStr(BODY1);
|
||||
stringstream d;
|
||||
@@ -294,7 +304,7 @@ public:
|
||||
} else if (path == "/test_put") {
|
||||
std::cerr << "sending PUT response" << std::endl;
|
||||
|
||||
SG_CHECK_EQUAL(buffer, BODY3);
|
||||
COMPARE(buffer, BODY3);
|
||||
stringstream d;
|
||||
d << "HTTP/1.1 " << 204 << " " << reasonForCode(204) << "\r\n";
|
||||
d << "\r\n"; // final CRLF to terminate the headers
|
||||
@@ -304,7 +314,7 @@ public:
|
||||
|
||||
std::string entityStr = "http://localhost:2000/something.txt";
|
||||
|
||||
SG_CHECK_EQUAL(buffer, BODY3);
|
||||
COMPARE(buffer, BODY3);
|
||||
stringstream d;
|
||||
d << "HTTP/1.1 " << 201 << " " << reasonForCode(201) << "\r\n";
|
||||
d << "Location:" << entityStr << "\r\n";
|
||||
@@ -375,18 +385,18 @@ int main(int argc, char* argv[])
|
||||
|
||||
// test URL parsing
|
||||
TestRequest* tr1 = new TestRequest("http://localhost.woo.zar:2000/test1?foo=bar");
|
||||
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");
|
||||
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");
|
||||
|
||||
TestRequest* tr2 = new TestRequest("http://192.168.1.1/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");
|
||||
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");
|
||||
|
||||
// basic get request
|
||||
{
|
||||
@@ -395,11 +405,11 @@ int main(int argc, char* argv[])
|
||||
cl.makeRequest(tr);
|
||||
|
||||
waitForComplete(&cl, tr);
|
||||
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));
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseReason(), string("OK"));
|
||||
COMPARE(tr->responseLength(), strlen(BODY1));
|
||||
COMPARE(tr->responseBytesReceived(), strlen(BODY1));
|
||||
COMPARE(tr->bodyData, string(BODY1));
|
||||
}
|
||||
|
||||
{
|
||||
@@ -408,11 +418,11 @@ int main(int argc, char* argv[])
|
||||
cl.makeRequest(tr);
|
||||
|
||||
waitForComplete(&cl, tr);
|
||||
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));
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseReason(), string("OK"));
|
||||
COMPARE(tr->responseLength(), strlen(BODY3));
|
||||
COMPARE(tr->responseBytesReceived(), strlen(BODY3));
|
||||
COMPARE(tr->bodyData, string(BODY3));
|
||||
}
|
||||
|
||||
{
|
||||
@@ -420,7 +430,7 @@ int main(int argc, char* argv[])
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -431,11 +441,11 @@ int main(int argc, char* argv[])
|
||||
cl.makeRequest(tr);
|
||||
|
||||
waitForComplete(&cl, tr);
|
||||
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));
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseReason(), string("OK"));
|
||||
COMPARE(tr->responseLength(), strlen(BODY1));
|
||||
COMPARE(tr->responseBytesReceived(), strlen(BODY1));
|
||||
COMPARE(tr->bodyData, string(BODY1));
|
||||
}
|
||||
|
||||
// larger get request
|
||||
@@ -448,9 +458,9 @@ int main(int argc, char* argv[])
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->responseBytesReceived(), body2Size);
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(body2, body2Size));
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseBytesReceived(), body2Size);
|
||||
COMPARE(tr->bodyData, string(body2, body2Size));
|
||||
}
|
||||
|
||||
cerr << "testing chunked transfer encoding" << endl;
|
||||
@@ -460,12 +470,12 @@ int main(int argc, char* argv[])
|
||||
cl.makeRequest(tr);
|
||||
|
||||
waitForComplete(&cl, tr);
|
||||
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");
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseReason(), string("OK"));
|
||||
COMPARE(tr->responseBytesReceived(), 30);
|
||||
COMPARE(tr->bodyData, "ABCDEFGHABCDEFABCDSTUVABCDSTUV");
|
||||
// check trailers made it too
|
||||
SG_CHECK_EQUAL(tr->headers["x-foobar"], string("wibble"));
|
||||
COMPARE(tr->headers["x-foobar"], string("wibble"));
|
||||
}
|
||||
|
||||
// test 404
|
||||
@@ -474,9 +484,9 @@ int main(int argc, char* argv[])
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 404);
|
||||
SG_CHECK_EQUAL(tr->responseReason(), string("not found"));
|
||||
SG_CHECK_EQUAL(tr->responseLength(), 0);
|
||||
COMPARE(tr->responseCode(), 404);
|
||||
COMPARE(tr->responseReason(), string("not found"));
|
||||
COMPARE(tr->responseLength(), 0);
|
||||
}
|
||||
|
||||
cout << "done 404 test" << endl;
|
||||
@@ -486,7 +496,7 @@ int main(int argc, char* argv[])
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
}
|
||||
|
||||
cout << "done1" << endl;
|
||||
@@ -496,9 +506,9 @@ int main(int argc, char* argv[])
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->responseLength(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(BODY1));
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseLength(), strlen(BODY1));
|
||||
COMPARE(tr->bodyData, string(BODY1));
|
||||
}
|
||||
|
||||
cout << "done2" << endl;
|
||||
@@ -508,9 +518,9 @@ int main(int argc, char* argv[])
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->responseLength(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(BODY1));
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseLength(), strlen(BODY1));
|
||||
COMPARE(tr->bodyData, string(BODY1));
|
||||
}
|
||||
cout << "done3" << endl;
|
||||
// test connectToHost failure
|
||||
@@ -522,7 +532,7 @@ int main(int argc, char* argv[])
|
||||
waitForFailed(&cl, tr);
|
||||
|
||||
const int HOST_NOT_FOUND_CODE = CURLE_COULDNT_RESOLVE_HOST;
|
||||
SG_CHECK_EQUAL(tr->responseCode(), HOST_NOT_FOUND_CODE);
|
||||
COMPARE(tr->responseCode(), HOST_NOT_FOUND_CODE);
|
||||
}
|
||||
|
||||
cout << "testing abrupt close" << endl;
|
||||
@@ -534,7 +544,7 @@ int main(int argc, char* argv[])
|
||||
waitForFailed(&cl, tr);
|
||||
|
||||
const int SERVER_NO_DATA_CODE = CURLE_GOT_NOTHING;
|
||||
SG_CHECK_EQUAL(tr->responseCode(), SERVER_NO_DATA_CODE);
|
||||
COMPARE(tr->responseCode(), SERVER_NO_DATA_CODE);
|
||||
}
|
||||
|
||||
cout << "testing proxy close" << endl;
|
||||
@@ -545,9 +555,9 @@ cout << "testing proxy close" << endl;
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->responseLength(), body2Size);
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(body2, body2Size));
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseLength(), body2Size);
|
||||
COMPARE(tr->bodyData, string(body2, body2Size));
|
||||
}
|
||||
|
||||
{
|
||||
@@ -556,9 +566,9 @@ cout << "testing proxy close" << endl;
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->responseBytesReceived(), body2Size);
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(body2, body2Size));
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseBytesReceived(), body2Size);
|
||||
COMPARE(tr->bodyData, string(body2, body2Size));
|
||||
}
|
||||
|
||||
// pipelining
|
||||
@@ -583,17 +593,17 @@ cout << "testing proxy close" << endl;
|
||||
cl.makeRequest(tr3);
|
||||
|
||||
waitForComplete(&cl, tr3);
|
||||
SG_VERIFY(tr->complete);
|
||||
SG_VERIFY(tr2->complete);
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(BODY1));
|
||||
VERIFY(tr->complete);
|
||||
VERIFY(tr2->complete);
|
||||
COMPARE(tr->bodyData, string(BODY1));
|
||||
|
||||
SG_CHECK_EQUAL(tr2->responseLength(), strlen(BODY3));
|
||||
SG_CHECK_EQUAL(tr2->responseBytesReceived(), strlen(BODY3));
|
||||
SG_CHECK_EQUAL(tr2->bodyData, string(BODY3));
|
||||
COMPARE(tr2->responseLength(), strlen(BODY3));
|
||||
COMPARE(tr2->responseBytesReceived(), strlen(BODY3));
|
||||
COMPARE(tr2->bodyData, string(BODY3));
|
||||
|
||||
SG_CHECK_EQUAL(tr3->bodyData, string(BODY1));
|
||||
COMPARE(tr3->bodyData, string(BODY1));
|
||||
|
||||
SG_CHECK_EQUAL(testServer.connectCount(), 1);
|
||||
COMPARE(testServer.connectCount(), 1);
|
||||
}
|
||||
|
||||
// multiple requests with an HTTP 1.0 server
|
||||
@@ -614,17 +624,17 @@ cout << "testing proxy close" << endl;
|
||||
cl.makeRequest(tr3);
|
||||
|
||||
waitForComplete(&cl, tr3);
|
||||
SG_VERIFY(tr->complete);
|
||||
SG_VERIFY(tr2->complete);
|
||||
VERIFY(tr->complete);
|
||||
VERIFY(tr2->complete);
|
||||
|
||||
SG_CHECK_EQUAL(tr->responseLength(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr->responseBytesReceived(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr->bodyData, string(BODY1));
|
||||
COMPARE(tr->responseLength(), strlen(BODY1));
|
||||
COMPARE(tr->responseBytesReceived(), strlen(BODY1));
|
||||
COMPARE(tr->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));
|
||||
COMPARE(tr2->responseLength(), strlen(BODY3));
|
||||
COMPARE(tr2->responseBytesReceived(), strlen(BODY3));
|
||||
COMPARE(tr2->bodyData, string(BODY3));
|
||||
COMPARE(tr3->bodyData, string(BODY1));
|
||||
}
|
||||
|
||||
// POST
|
||||
@@ -636,7 +646,7 @@ cout << "testing proxy close" << endl;
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 204);
|
||||
COMPARE(tr->responseCode(), 204);
|
||||
}
|
||||
|
||||
// PUT
|
||||
@@ -648,7 +658,7 @@ cout << "testing proxy close" << endl;
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 204);
|
||||
COMPARE(tr->responseCode(), 204);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -659,7 +669,7 @@ cout << "testing proxy close" << endl;
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 201);
|
||||
COMPARE(tr->responseCode(), 201);
|
||||
}
|
||||
|
||||
// test_zero_length_content
|
||||
@@ -669,9 +679,9 @@ cout << "testing proxy close" << endl;
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
SG_CHECK_EQUAL(tr->responseCode(), 200);
|
||||
SG_CHECK_EQUAL(tr->bodyData, string());
|
||||
SG_CHECK_EQUAL(tr->responseBytesReceived(), 0);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->bodyData, string());
|
||||
COMPARE(tr->responseBytesReceived(), 0);
|
||||
}
|
||||
|
||||
// test cancel
|
||||
@@ -699,12 +709,12 @@ cout << "testing proxy close" << endl;
|
||||
|
||||
waitForComplete(&cl, tr3);
|
||||
|
||||
SG_CHECK_EQUAL(tr->responseCode(), -1);
|
||||
SG_CHECK_EQUAL(tr2->responseReason(), "my reason 2");
|
||||
COMPARE(tr->responseCode(), -1);
|
||||
COMPARE(tr2->responseReason(), "my reason 2");
|
||||
|
||||
SG_CHECK_EQUAL(tr3->responseLength(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr3->responseBytesReceived(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr3->bodyData, string(BODY1));
|
||||
COMPARE(tr3->responseLength(), strlen(BODY1));
|
||||
COMPARE(tr3->responseBytesReceived(), strlen(BODY1));
|
||||
COMPARE(tr3->bodyData, string(BODY1));
|
||||
}
|
||||
|
||||
// test cancel
|
||||
@@ -730,16 +740,16 @@ cout << "testing proxy close" << endl;
|
||||
|
||||
waitForComplete(&cl, tr3);
|
||||
|
||||
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(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseLength(), strlen(BODY1));
|
||||
COMPARE(tr->responseBytesReceived(), strlen(BODY1));
|
||||
COMPARE(tr->bodyData, string(BODY1));
|
||||
|
||||
SG_CHECK_EQUAL(tr2->responseCode(), -1);
|
||||
COMPARE(tr2->responseCode(), -1);
|
||||
|
||||
SG_CHECK_EQUAL(tr3->responseLength(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr3->responseBytesReceived(), strlen(BODY1));
|
||||
SG_CHECK_EQUAL(tr3->bodyData, string(BODY1));
|
||||
COMPARE(tr3->responseLength(), strlen(BODY1));
|
||||
COMPARE(tr3->responseBytesReceived(), strlen(BODY1));
|
||||
COMPARE(tr3->bodyData, string(BODY1));
|
||||
}
|
||||
|
||||
{
|
||||
@@ -764,12 +774,12 @@ cout << "testing proxy close" << endl;
|
||||
cl.makeRequest(tr2);
|
||||
|
||||
waitForComplete(&cl, tr2);
|
||||
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));
|
||||
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));
|
||||
}
|
||||
|
||||
cout << "all tests passed ok" << endl;
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#include <simgear_config.h>
|
||||
|
||||
#include "untar.hxx"
|
||||
|
||||
#include <cstdlib>
|
||||
@@ -103,7 +101,7 @@ public:
|
||||
};
|
||||
|
||||
size_t bytesRemaining;
|
||||
std::unique_ptr<SGFile> currentFile;
|
||||
std::auto_ptr<SGFile> currentFile;
|
||||
size_t currentFileSize;
|
||||
z_stream zlibStream;
|
||||
uint8_t* zlibOutput;
|
||||
|
||||
@@ -44,7 +44,7 @@ public:
|
||||
bool hasError() const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<TarExtractorPrivate> d;
|
||||
std::auto_ptr<TarExtractorPrivate> d;
|
||||
};
|
||||
|
||||
} // of namespace simgear
|
||||
|
||||
@@ -33,7 +33,6 @@ set(HEADERS
|
||||
sg_types.hxx
|
||||
sg_random.h
|
||||
simd.hxx
|
||||
simd4x4.hxx
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
@@ -47,10 +46,6 @@ 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)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user