Compare commits

..

3 Commits

Author SHA1 Message Date
Automatic Release Builder
c92a953511 new version: 2016.4.3 2016-12-05 13:28:25 +01:00
James Turner
863ae19d1d Package::indexOfvariant works on fully-qualified IDs.
Should fix issues restoring variants in the launcher.
2016-11-29 15:44:19 +00:00
Automatic Release Builder
cd7b6d69b0 new version: 2016.4.2 2016-11-22 09:38:58 +01:00
124 changed files with 3123 additions and 9305 deletions

View File

@@ -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)
@@ -125,9 +114,6 @@ option(ENABLE_SOUND "Set to OFF to disable building SimGear's sound support"
option(USE_AEONWAVE "Set to ON to use AeonWave instead of OpenAL" OFF)
option(ENABLE_PKGUTIL "Set to ON to build the sg_pkgutil application (default)" ON)
option(ENABLE_DNS "Set to ON to use udns library and DNS service resolver" ON)
option(ENABLE_SIMD "Enable SSE/SSE2 support for x86 compilers" ON)
include (DetectArch)
# until the fstream fix is applied and generally available in OSG,
# keep the compatability link option as the default
@@ -253,6 +239,7 @@ 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)
@@ -270,18 +257,13 @@ else()
endif(ENABLE_RTI)
check_function_exists(gettimeofday HAVE_GETTIMEOFDAY)
check_function_exists(ftime HAVE_FTIME)
check_function_exists(timegm HAVE_TIMEGM)
check_function_exists(rint HAVE_RINT)
check_function_exists(mkdtemp HAVE_MKDTEMP)
check_function_exists(bcopy HAVE_BCOPY)
check_function_exists(mmap HAVE_MMAP)
if (NOT MSVC)
check_function_exists(timegm HAVE_TIMEGM)
if (NOT HAVE_TIMEGM)
message(FATAL_ERROR "Non-Windows platforms must support timegm()")
endif()
endif()
if(HAVE_UNISTD_H)
set(CMAKE_REQUIRED_INCLUDES ${CMAKE_INCLUDE_PATH})
check_cxx_source_compiles(
@@ -346,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})
@@ -361,7 +336,7 @@ 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 -Wno-overloaded-virtual -Wno-redeclared-class-member")
set(WARNING_FLAGS_C "-Wall")
@@ -372,13 +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(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
@@ -397,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")

View File

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

View File

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

View File

@@ -13,8 +13,4 @@ set(SIMGEAR_SOUND @ENABLE_SOUND@)
# find_dependency(OpenAL)
#endif()
# SSE/SSE2 support
set(ENABLE_SIMD @ENABLE_SIMD@)
include("${CMAKE_CURRENT_LIST_DIR}/SimGearTargets.cmake")

View File

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

View File

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

View File

@@ -35,33 +35,21 @@ void SHVector2_dtor(SHVector2 *v) {
}
void SHVector3_ctor(SHVector3 *v) {
#ifdef __SSE__
v->vec = _mm_setzero_ps();
#else
v->x=0.0f; v->y=0.0f; v->z=0.0f;
#endif
}
void SHVector3_dtor(SHVector3 *v) {
}
void SHVector4_ctor(SHVector4 *v) {
#ifdef __SSE__
v->vec = _mm_setzero_ps();
#else
v->x=0.0f; v->y=0.0f; v->z=0.0f; v->w=0.0f;
#endif
}
void SHVector4_dtor(SHVector4 *v) {
}
void SHRectangle_ctor(SHRectangle *r) {
#ifdef __SSE__
r->vec = _mm_setzero_ps();
#else
r->x=0.0f; r->y=0.0f; r->w=0.0f; r->h=0.0f;
#endif
}
void SHRectangle_dtor(SHRectangle *r) {
@@ -147,24 +135,3 @@ int shLineLineXsection(SHVector2 *o1, SHVector2 *v1,
xsection->y = o1->y + t1*v1->y;
return 1;
}
#ifdef __SSE__
# ifdef __SSE3__
# include <pmmintrin.h>
inline float hsum_ps_sse(__m128 v) {
__m128 shuf = _mm_movehdup_ps(v);
__m128 sums = _mm_add_ps(v, shuf);
shuf = _mm_movehl_ps(shuf, sums);
sums = _mm_add_ss(sums, shuf);
return _mm_cvtss_f32(sums);
}
# else
inline float hsum_ps_sse(__m128 v) {
__m128 shuf = _mm_shuffle_ps(v, v, _MM_SHUFFLE(2, 3, 0, 1));
__m128 sums = _mm_add_ps(v, shuf);
shuf = _mm_movehl_ps(shuf, sums);
sums = _mm_add_ss(sums, shuf);
return _mm_cvtss_f32(sums);
}
# endif
#endif

View File

@@ -21,11 +21,6 @@
#ifndef __SHVECTORS_H
#define __SHVECTORS_H
#ifdef __SSE__
# include <xmmintrin.h>
float hsum_ps_sse(__m128 v);
#endif
#include "shDefs.h"
/* Vector structures
@@ -38,17 +33,9 @@ typedef struct
void SHVector2_ctor(SHVector2 *v);
void SHVector2_dtor(SHVector2 *v);
typedef struct
{
#ifdef __SSE__
union ALIGN16 {
__m128 vec;
struct { SHfloat x,y,z,w; };
} ALIGN16C;
#else
SHfloat x,y,z;
#endif
} SHVector3;
void SHVector3_ctor(SHVector3 *v);
@@ -56,14 +43,7 @@ void SHVector3_dtor(SHVector3 *v);
typedef struct
{
#ifdef __SSE__
union ALIGN16 {
__m128 vec;
struct { SHfloat x,y,z,w; };
} ALIGN16C;
#else
SHfloat x,y,z,w;
#endif
} SHVector4;
void SHVector4_ctor(SHVector4 *v);
@@ -71,14 +51,7 @@ void SHVector4_dtor(SHVector4 *v);
typedef struct
{
#ifdef __SSE__
union ALIGN16 {
__m128 vec;
struct { SHfloat x,y,w,h; };
} ALIGN16C;
#else
SHfloat x,y,w,h;
#endif
} SHRectangle;
void SHRectangle_ctor(SHRectangle *r);
@@ -88,14 +61,7 @@ void shRectangleSet(SHRectangle *r, SHfloat x,
typedef struct
{
#ifdef __SSE__
union ALIGN16 {
__m128 mtx[4];
SHfloat m[4][4];
} ALIGN16C;
#else
SHfloat m[3][3];
#endif
} SHMatrix3x3;
void SHMatrix3x3_ctor(SHMatrix3x3 *m);
@@ -117,22 +83,12 @@ void SHMatrix3x3_dtor(SHMatrix3x3 *m);
*--------------------------------------------------------- */
#define SET2(v,xs,ys) { v.x=xs; v.y=ys; }
#ifdef __SSE__
# define SET3(v,xs,ys,zs,ws) { v.vec=_mm_set_ps(0,zs,ys,xs); }
# define SET4(v,xs,ys,zs,ws) { v.vec=_mm_set_ps(ws,zs,ys,xs); }
#else
# define SET3(v,xs,ys,zs) { v.x=xs; v.y=ys; v.z=zs; }
# define SET4(v,xs,ys,zs,ws) { v.x=xs; v.y=ys; v.z=zs; v.w=ws; }
#endif
#define 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 __SSE__
# define SET3V(v1,v2) { v1.vec=v2.vec; }
# define SET4V(v1,v2) { v1.vec=v2.vec; }
#else
# define SET3V(v1,v2) { v1.x=v2.x; v1.y=v2.y; v1.z=v2.z; }
# define SET4V(v1,v2) { v1.x=v2.x; v1.y=v2.y; v1.z=v2.z; v1.w=v2.w; }
#endif
#define 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 )
@@ -147,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 __SSE__
# define ADD3(v,xx,yy,zz,ww) { v.vec=_mm_add_ps(v.vec,_mm_set_ps(0,zz,yy,xx)); }
# define ADD4(v,xx,yy,zz,ww) { v.vec=_mm_add_ps(v.vec,_mm_set_ps(ww,zz,yy,xx)); }
#else
# define ADD3(v,xx,yy,zz) { v.x+=xx; v.y+=yy; v.z+=zz; }
# define ADD4(v,xx,yy,zz,ww) { v.x+=xx; v.y+=yy; v.z+=zz; v.w+=ww; }
#endif
#define 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 __SSE__
# define ADD3V(v1,v2) { v1.vec=_mm_add_ps(v1.vec,v2.vec); }
# define ADD4V(v1,v2) { v1.vec=_mm_add_ps(v1.vec,v2.vec); }
#else
# define ADD3V(v1,v2) { v1.x+=v2.x; v1.y+=v2.y; v1.z+=v2.z; }
# define ADD4V(v1,v2) { v1.x+=v2.x; v1.y+=v2.y; v1.z+=v2.z; v1.w+=v2.w; }
#endif
#define 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 __SSE__
# define SUB3(v,xx,yy,zz,ww) { v.vec=_mm_sub_ps(v.vec,_mm_set_ps(0,zz,yy,xx)); }
# define SUB4(v,xx,yy,zz,ww) { v.vec=_mm_sub_ps(v.vec,_mm_set_ps(ww,zz,yy,xx)); }
#else
# define SUB3(v,xx,yy,zz) { v.x-=xx; v.y-=yy; v.z-=zz; }
# define SUB4(v,xx,yy,zz,ww) { v.x-=xx; v.y-=yy; v.z-=zz; v.w-=v2.w; }
#endif
#define 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 __SSE__
# define SUB3V(v1,v2) { v1.vec=_mm_sub_ps(v1.vec,v2.vec); }
# define SUB4V(v1,v2) { v1.vec=_mm_sub_ps(v1.vec,v2.vec); }
#else
# define SUB3V(v1,v2) { v1.x-=v2.x; v1.y-=v2.y; v1.z-=v2.z; }
# define SUB4V(v1,v2) { v1.x-=v2.x; v1.y-=v2.y; v1.z-=v2.z; v1.w-=v2.w; }
#endif
#define 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 __SSE__
# define MUL3(v,f) { v.vec=_mm_mul_ps(v.vec,_mm_set1_ps(f)); }
# define MUL4(v,f) { v.vec=_mm_mul_ps(v.vec,_mm_set1_ps(f)); }
#else
# define MUL3(v,f) { v.x*=f; v.y*=f; v.z*=z; }
# define MUL4(v,f) { v.x*=f; v.y*=f; v.z*=z; v.w*=w; }
#endif
#define 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 __SSE__
# define DIV3(v,f) { v.vec=_mm_div_ps(v.vec,_mm_set1_ps(f)); }
# define DIV4(v,f) { v.vec=_mm_div_ps(v.vec,_mm_set1_ps(f)); }
#else
# define DIV3(v,f) { v.x/=f; v.y/=f; v.z/=z; }
# define DIV4(v,f) { v.x/=f; v.y/=f; v.z/=z; v.w/=w; }
#endif
#define 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 __SSE__
# define ABS_MASK _mm_set1_ps(-0.f)
# define ABS3(v) { v.vec=_mm_andnot_ps(ABS_MASK, v.vec); }
# define ABS4(v) { v.vec=_mm_andnot_ps(ABS_MASK, v.vec); }
#else
# define ABS3(v) { v.x=SH_ABS(v.x); v.y=SH_ABS(v.y); v.z=SH_ABS(v.z); }
# define ABS4(v) { v.x=SH_ABS(v.x); v.y=SH_ABS(v.y); v.z=SH_ABS(v.z); v.w=SH_ABS(v.w); }
#endif
#define 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 __SSE__
# define DOT4(v1,v2) hsum_ps_sse(_mm_mul_ps(v1.vec,v2.vec))
# define DOT4(v1,v2) hsum_ps_sse(_mm_mul_ps(v1.vec,v2.vec))
#else
# define DOT3(v1,v2) (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z)
# define DOT4(v1,v2) (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z + v1.w*v2.w)
#endif
#define 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)
@@ -237,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 __SSE__
# define OFFSET4V(v, o, s) { v.vec=_mm_add_ps(v.vec,_mm_mul_ps(o.vec,_mm_set1_ps(s))); }
# define OFFSET4V(v, o, s) { v.vec=_mm_add_ps(v.vec,_mm_mul_ps(o.vec,_mm_set1_ps(s))); }
#else
# define OFFSET3V(v, o, s) { v.x += o.x*s; v.y += o.y*s; v.z += o.z*s; }
# define OFFSET4V(v, o, s) { v.x += o.x*s; v.y += o.y*s; v.z += o.z*s; v.w += o.w*s; }
#endif
#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 __SSE__
# define SETMAT(mat, m00, m01, m02, m10, m11, m12, m20, m21, m22) { \
mat.mtx[0] = _mm_set_ps(0,m02,m01,m00); \
mat.mtx[1] = _mm_set_ps(0,m12,m11,m10); \
mat.mtx[2] = _mm_set_ps(0,m22,m21,m20); \
mat.mtx[3] = _mm_setzero_ps(); }
#else
# define SETMAT(mat, m00, m01, m02, m10, m11, m12, m20, m21, m22) { \
mat.m[0][0] = m00; mat.m[0][1] = m01; mat.m[0][2] = m02; \
#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 __SSE__
# define SETMATMAT(m1, m2) { \
m1.mtx[0] = m2.mtx[0]; \
m1.mtx[1] = m2.mtx[1]; \
m1.mtx[2] = m2.mtx[2]; }
#else
# define SETMATMAT(m1, m2) { \
#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 __SSE__
# define MULMATS(mat, s) { \
mat.mtx[0] = _mm_mul_ps(mat.mtx[0],_mm_set1_ps(s)); \
mat.mtx[1] = _mm_mul_ps(mat.mtx[1],_mm_set1_ps(s)); \
mat.mtx[2] = _mm_mul_ps(mat.mtx[2],_mm_set1_ps(s)); }
#else
# define MULMATS(mat, s) { \
#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 __SSE__
# define DIVMATS(mat, s) { \
mat.mtx[0] = _mm_mul_ps(mat.mtx[0],_mm_set1_ps(1/s)); \
mat.mtx[1] = _mm_mul_ps(mat.mtx[1],_mm_set1_ps(1/s)); \
mat.mtx[2] = _mm_mul_ps(mat.mtx[2],_mm_set1_ps(1/s)); }
#else
# define DIVMATS(mat, s) { \
#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 __SSE__
# define MULMATMAT(m2, m1, mout) { \
int i,j; \
for (i=0;i<4;i++) { \
__m128 a = m1.mtx[0]; \
__m128 b = _mm_set1_ps(m2.m[i][0]); \
mout.mtx[i] = a*b; \
for (j=1;j<4;j++) { \
a = m1.mtx[j]; \
b = _mm_set1_ps(m2.m[i][j]); \
mout.mtx[i] += a*b; } } }
#else
# define MULMATMAT(m1, m2, mout) { \
#define MULMATMAT(m1, m2, mout) { \
int i,j; \
for(i=0;i<3;i++) \
for(j=0;j<3;j++) \
@@ -322,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)

View File

@@ -640,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
{
@@ -722,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;

View File

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

View File

@@ -373,7 +373,7 @@ namespace canvas
SG_LOG
(
SG_GENERAL,
SG_DEBUG,
SG_INFO,
"canvas::Group: Moved element " << index << " to position " << index_new
);
}

View File

@@ -487,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)
{
@@ -512,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)
{

View File

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

View File

@@ -18,14 +18,12 @@
#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
{
@@ -59,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
{
@@ -682,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();
@@ -712,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);
}
@@ -798,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;
}
//----------------------------------------------------------------------------
@@ -891,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

View File

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

View File

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

View File

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

View File

@@ -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)
@@ -181,9 +181,9 @@ 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
{
@@ -195,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()
@@ -223,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)
/*
@@ -333,7 +332,7 @@ public:
~LogStreamPrivate()
{
for (simgear::LogCallback* cb : m_callbacks) {
BOOST_FOREACH(simgear::LogCallback* cb, m_callbacks) {
delete cb;
}
}
@@ -341,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,
@@ -353,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()
{
@@ -369,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) {
@@ -389,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);
}
@@ -423,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)
@@ -453,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);
}
@@ -461,23 +431,9 @@ 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;
}
};
/////////////////////////////////////////////////////////////////////////////
@@ -505,12 +461,6 @@ logstream::setLogLevels( sgDebugClass c, sgDebugPriority p )
global_privateLogstream->setLogLevels(c, p);
}
void logstream::setDeveloperMode(bool devMode)
{
global_privateLogstream->m_developerMode = devMode;
}
void
logstream::addCallback(simgear::LogCallback* cb)
{
@@ -607,11 +557,6 @@ logstream::logToFile( const SGPath& aPath, sgDebugClass c, sgDebugPriority p )
global_privateLogstream->addCallback(new FileLogCallback(aPath, c, p));
}
void logstream::setStartupLoggingEnabled(bool enabled)
{
global_privateLogstream->setStartupLoggingEnabled(enabled);
}
namespace simgear
{

View File

@@ -93,13 +93,6 @@ 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
*/
@@ -141,12 +134,6 @@ 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();
@@ -168,10 +155,10 @@ 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_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)
#endif

View File

@@ -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[])

View File

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

View File

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

View File

@@ -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!");

View File

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

View File

@@ -101,7 +101,7 @@ private:
SGSharedPtr<RTI13Ambassador> _ambassador;
// cached storage for updates
std::unique_ptr<RTI::AttributeHandleValuePairSet> _attributeValuePairSet;
std::auto_ptr<RTI::AttributeHandleValuePairSet> _attributeValuePairSet;
};
}

View File

@@ -1,4 +1,4 @@
add_subdirectory(iostreams)
include (SimGearComponent)

View File

@@ -22,7 +22,7 @@
//
#include "DNSClient.hxx"
#include <udns.h>
#include "udns.h"
#include <time.h>
#include <simgear/debug/logstream.hxx>
@@ -33,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),
@@ -126,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;
}
@@ -163,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;
}
@@ -211,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;
}
@@ -232,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

View File

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

View File

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

View File

@@ -23,7 +23,7 @@
#include "HTTPRequest.hxx"
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/misc/sgstream.hxx>
namespace simgear
{

View File

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

View File

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

View File

@@ -132,15 +132,15 @@ Request::HTTPVersion decodeHTTPVersion(const std::string& v)
//------------------------------------------------------------------------------
void Request::responseStart(const std::string& r)
{
const int maxSplit = 2; // HTTP/1.1 nnn reason-string?
const int maxSplit = 2; // HTTP/1.1 nnn reason-string
string_list parts = strutils::split(r, NULL, maxSplit);
if (parts.size() < 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);
}

View File

@@ -1,23 +0,0 @@
include (SimGearComponent)
set(HEADERS
sgstream.hxx
gzfstream.hxx
gzcontainerfile.hxx
)
set(SOURCES
sgstream.cxx
gzfstream.cxx
gzcontainerfile.cxx
)
simgear_component(IOStreams io/iostreams "${SOURCES}" "${HEADERS}")
if(ENABLE_TESTS)
add_executable(test_streams sgstream_test.cxx )
target_link_libraries(test_streams ${TEST_LIBS})
add_test(streams ${EXECUTABLE_OUTPUT_PATH}/test_streams)
endif(ENABLE_TESTS)

View File

@@ -1,717 +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 program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <string>
#include <ios> // std::streamsize
#include <istream>
#include <algorithm>
#include <stdexcept>
#include <unordered_map>
#include <limits> // std::numeric_limits
#include <cstddef> // std::size_t, std::ptrdiff_t
#include <cassert>
#include <zlib.h>
#include <simgear/io/iostreams/zlibstream.hxx>
#include <simgear/misc/strutils.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/structure/exception.hxx>
#include <simgear/debug/logstream.hxx>
using std::string;
using traits = std::char_traits<char>;
// Private utility function
static string zlibErrorMessage(const z_stream& zstream, int errorCode)
{
string res;
std::unordered_map<int, string> errorCodeToMessageMap = {
{Z_OK, "zlib: no error (code Z_OK)"},
{Z_STREAM_END, "zlib stream end"},
{Z_NEED_DICT, "zlib: Z_NEED_DICT"},
{Z_STREAM_ERROR, "zlib stream error"},
{Z_DATA_ERROR, "zlib data error"},
{Z_MEM_ERROR, "zlib memory error"},
{Z_BUF_ERROR, "zlib buffer error"},
{Z_VERSION_ERROR, "zlib version error"}
};
if (errorCode == Z_ERRNO) {
res = simgear::strutils::error_string(errno);
} else if (zstream.msg != nullptr) {
// Caution: this only works if the zstream structure hasn't been
// deallocated!
res = "zlib: " + string(zstream.msg);
} else {
try {
res = errorCodeToMessageMap.at(errorCode);
} catch (const std::out_of_range&) {
res = string("unknown zlib error (code " + std::to_string(errorCode) +
")");
}
}
return res;
}
// Return the largest value that can be represented with zlib's uInt type,
// which is the type of z_stream.avail_in and z_stream.avail_out, hence the
// function name.
static std::size_t zlibMaxChunkSize()
{
uLong flags = ::zlibCompileFlags();
std::size_t res;
switch (flags & 0x3) {
case 0x3:
SG_LOG(SG_IO, SG_WARN,
"Unknown size for zlib's uInt type (code 3). Will assume 64 bits, "
"but the actual value is probably higher.");
// No 'break' here, this is intentional.
case 0x2:
res = 0xFFFFFFFFFFFFFFFF; // 2^64 - 1
break;
case 0x1:
res = 0xFFFFFFFF; // 2^32 - 1
break;
case 0x0:
res = 0xFFFF; // 2^16 - 1
break;
default:
throw std::logic_error("It should be impossible to get here.");
}
return res;
}
static const std::size_t zlibMaxChunk = ::zlibMaxChunkSize();
namespace simgear
{
// ***************************************************************************
// * ZlibAbstractIStreambuf class *
// ***************************************************************************
// Common initialization. Subclasses must complete the z_stream struct
// initialization with a call to deflateInit2() or inflateInit2(), typically.
ZlibAbstractIStreambuf::ZlibAbstractIStreambuf(std::istream& iStream,
const SGPath& path,
char* inBuf,
std::size_t inBufSize,
char *outBuf,
std::size_t outBufSize,
std::size_t putbackSize)
: _iStream(iStream),
_path(path),
_inBuf(inBuf),
_inBufSize(inBufSize),
_outBuf(outBuf),
_outBufSize(outBufSize),
_putbackSize(putbackSize)
{
assert(_inBufSize > 0);
assert(_putbackSize >= 0); // guaranteed unless the type is changed...
assert(_putbackSize < _outBufSize);
if (_inBuf == nullptr) {
_inBuf = new char[_inBufSize];
_inBufMustBeFreed = true;
}
if (_outBuf == nullptr) {
_outBuf = new char[_outBufSize];
_outBufMustBeFreed = true;
}
_inBufEndPtr = _inBuf;
// The input buffer is empty.
_zstream.next_in = reinterpret_cast<unsigned char *>(_inBuf);
_zstream.avail_in = 0;
// ZLib's documentation says its init functions such as inflateInit2() might
// consume stream input, therefore let's fill the input buffer now. This
// way, constructors of derived classes just have to call the appropriate
// ZLib init function: the data will already be in place.
getInputData();
// Force underflow() of the stream buffer on the first read. We could use
// some other value, but I avoid nullptr in order to be sure we can always
// reliably compare the three pointers with < and >, as well as compute the
// difference between any two of them.
setg(_outBuf, _outBuf, _outBuf);
}
ZlibAbstractIStreambuf::~ZlibAbstractIStreambuf()
{
if (_inBufMustBeFreed) {
delete[] _inBuf;
}
if (_outBufMustBeFreed) {
delete[] _outBuf;
}
}
// Fill or refill the output buffer, and update the three pointers
// corresponding to eback(), gptr() and egptr().
int ZlibAbstractIStreambuf::underflow()
{
if (_allFinished) {
return traits::eof();
}
// According to the C++11 standard: “The public members of basic_streambuf
// call this virtual function only if gptr() is null or gptr() >= egptr()”.
// Still, it seems some people do the following or similar (maybe in case
// underflow() is called “incorrectly”?). See for instance N. Josuttis, The
// C++ Standard Library (1st edition), p. 584. One sure thing is that it
// can't hurt (except performance, marginally), so let's do it.
if (gptr() < egptr()) {
return traits::to_int_type(*gptr());
}
assert(gptr() == egptr());
assert(egptr() - eback() >= 0);
std::size_t nbPutbackChars = std::min(
static_cast<std::size_t>(egptr() - eback()), // OK because egptr() >= eback()
_putbackSize);
std::copy(egptr() - nbPutbackChars, egptr(),
_outBuf + _putbackSize - nbPutbackChars);
setg(_outBuf + _putbackSize - nbPutbackChars, // start of putback area
_outBuf + _putbackSize, // start of obtained data
fillOutputBuffer()); // one-past-end of obtained data
return (gptr() == egptr()) ? traits::eof() : traits::to_int_type(*gptr());
}
// Simple utility method for fillOutputBuffer(), used to improve readability.
// Return the remaining space available in the output buffer, where zlib can
// write.
std::size_t
ZlibAbstractIStreambuf::fOB_remainingSpace(unsigned char* nextOutPtr) const
{
std::ptrdiff_t remainingSpaceInOutBuf = _outBuf + _outBufSize -
reinterpret_cast<char*>(nextOutPtr);
assert(remainingSpaceInOutBuf >= 0);
return static_cast<std::size_t>(remainingSpaceInOutBuf);
}
// Simple method for dealing with the Z_BUF_ERROR code that may be returned
// by zlib's deflate() and inflate() methods.
[[ noreturn ]] void ZlibAbstractIStreambuf::handleZ_BUF_ERROR() const
{
switch (operationType()) {
case OPERATION_TYPE_DECOMPRESSION:
{
string message = (_path.isNull()) ?
"Got Z_BUF_ERROR from zlib while decompressing a stream. The stream "
"was probably incomplete."
:
"Got Z_BUF_ERROR from zlib during decompression. The compressed stream "
"was probably incomplete.";
// When _path.isNull(), sg_location(_path) is equivalent to sg_location()
throw sg_io_exception(message, sg_location(_path));
}
case OPERATION_TYPE_COMPRESSION:
throw std::logic_error(
"Called ZlibAbstractIStreambuf::handleZ_BUF_ERROR() with "
"operationType() == OPERATION_TYPE_DECOMPRESSION");
default:
throw std::logic_error(
"Unexpected operationType() in "
"ZlibAbstractIStreambuf::handleZ_BUF_ERROR(): " +
std::to_string(operationType()));
}
}
// Fill or refill the output buffer. Return a pointer to the first unused char
// in _outBuf (i.e., right after the data written by this method, if any;
// otherwise: _outBuf + _putbackSize).
char* ZlibAbstractIStreambuf::fillOutputBuffer()
{
bool allInputRead = false;
std::size_t remainingSpaceInOutBuf;
int retCode;
// We have to do these unpleasant casts, because zlib uses pointers to
// unsigned char for its input and output buffers, while the IOStreams
// library in the C++ standard uses plain char pointers...
_zstream.next_out = reinterpret_cast<unsigned char*>(_outBuf + _putbackSize);
remainingSpaceInOutBuf = fOB_remainingSpace(_zstream.next_out);
while (remainingSpaceInOutBuf > 0) {
// This does fit in a zlib uInt: that's the whole point of zlibMaxChunk.
_zstream.avail_out = static_cast<uInt>(
std::min(remainingSpaceInOutBuf, zlibMaxChunk));
if (_zstream.avail_in == 0 && !allInputRead) {
// Get data from _iStream, store it in _inBuf
allInputRead = getInputData();
}
// Make zlib process some data (compress or decompress it). This updates
// _zstream.{avail,next}_{in,out} (4 fields of the z_stream struct).
retCode = zlibProcessData();
if (retCode == Z_BUF_ERROR) {
handleZ_BUF_ERROR(); // doesn't return
} else if (retCode == Z_STREAM_END) {
assert(_zstream.avail_in == 0); // all of _inBuf must have been used
_allFinished = true;
break;
} else if (retCode < 0) { // negative codes are errors
throw sg_io_exception(::zlibErrorMessage(_zstream, retCode),
sg_location(_path));
}
remainingSpaceInOutBuf = fOB_remainingSpace(_zstream.next_out);
}
return reinterpret_cast<char*>(_zstream.next_out);
}
// This method provides input data to zlib:
// - if data is already available in _inBuf, it updates _zstream.avail_in
// accordingly (using the largest possible amount given _zstream.avail_in's
// type, i.e., uInt);
// - otherwise, it reads() as much data as possible into _inBuf from
// _iStream and updates _zstream.avail_in to tell zlib about this new
// available data (again: largest possible amount given the uInt type).
//
// This method must be called only when _zstream.avail_in == 0, which means
// zlib has read everything we fed it (and does *not* mean, by the way, that
// the input buffer starting at _inBuf is empty; this is because the buffer
// size might exceed what can be represented by an uInt).
//
// On return:
// - either EOF has been reached for _iStream, and the return value is true;
// - or _zstream.avail_in > 0, and the return value is false.
//
// Note: don't read more in the previous paragraph concerning the state on
// return. In the first case, there is *no guarantee* about
// _zstream.avail_in's value; in the second case, there is *no guarantee*
// about whether EOF has been reached for _iStream.
bool ZlibAbstractIStreambuf::getInputData()
{
bool allInputRead = false;
assert(_zstream.avail_in == 0);
std::ptrdiff_t alreadyAvailable =
_inBufEndPtr - reinterpret_cast<char*>(_zstream.next_in);
assert(alreadyAvailable >= 0);
// Data already available?
if (alreadyAvailable > 0) {
_zstream.avail_in = static_cast<uInt>(
std::min(static_cast<std::size_t>(alreadyAvailable),
zlibMaxChunk));
return allInputRead;
}
if (_inBufEndPtr == _inBuf + _inBufSize) { // buffer full, rewind
_inBufEndPtr = _inBuf;
_zstream.next_in = reinterpret_cast<unsigned char*>(_inBuf);
}
// Fill the input buffer (as much as possible)
while (_inBufEndPtr < _inBuf + _inBufSize && !_iStream.eof()) {
std::streamsize nbCharsToRead = std::min(
_inBuf + _inBufSize - _inBufEndPtr,
std::numeric_limits<std::streamsize>::max()); // max we can pass to read()
_iStream.read(_inBufEndPtr, nbCharsToRead);
if (_iStream.bad()) {
string errMsg = simgear::strutils::error_string(errno);
string msgStart = (_path.isNull()) ?
"Error while reading from a stream" : "Read error";
throw sg_io_exception(msgStart + ": " + errMsg, sg_location(_path));
}
// Could be zero if at EOF
std::streamsize nbCharsRead = _iStream.gcount();
// std::streamsize is a signed integral type!
assert(0 <= nbCharsRead);
_inBufEndPtr += nbCharsRead;
}
std::ptrdiff_t availableChars =
_inBufEndPtr - reinterpret_cast<char*>(_zstream.next_in);
assert(availableChars >= 0);
_zstream.avail_in = static_cast<uInt>(
std::min(static_cast<std::size_t>(availableChars),
zlibMaxChunk));
if (_iStream.eof()) {
allInputRead = true;
} else {
// Trying to rewind a fully read std::istringstream with seekg() can lead
// to a weird state, where the stream doesn't return any character but
// doesn't report EOF either. Make sure we are not in this situation.
assert(_zstream.avail_in > 0);
}
return allInputRead;
}
// Implementing this method is optional, but should provide better
// performance. It makes zlib write the data directly in the buffer starting
// at 'dest'. Without it, the data would go through an intermediate buffer
// (_outBuf) before being copied to its (hopefully) final destination.
std::streamsize ZlibAbstractIStreambuf::xsgetn(char* dest, std::streamsize n)
{
std::size_t remaining = static_cast<std::size_t>(n);
std::size_t chunkSize;
std::ptrdiff_t avail;
const char* origGptr = gptr();
char* writePtr = dest; // we'll need dest later -> work with a copy
// First, let's take data present in our internal buffer (_outBuf)
while (remaining > 0) {
avail = egptr() - gptr(); // number of available chars in _outBuf
if (avail == 0) { // our internal buffer is empty
break;
}
chunkSize = std::min(remaining, static_cast<std::size_t>(avail));
std::copy(gptr(), gptr() + chunkSize, writePtr);
gbump(chunkSize);
writePtr += chunkSize;
remaining -= chunkSize;
}
if (remaining == 0) {
// Everything we needed was already in _outBuf. The putback area is set up
// as it should between eback() and the current gptr(), so we are fine to
// return.
return n;
}
// Now, let's make it so that the remaining data we need is directly written
// to the destination area, without going through _outBuf.
_zstream.next_out = reinterpret_cast<unsigned char*>(writePtr);
bool allInputRead = false;
int retCode;
while (remaining > 0) {
chunkSize = std::min(remaining, zlibMaxChunk);
// It does fit in a zlib uInt: that's the whole point of zlibMaxChunk.
_zstream.avail_out = static_cast<uInt>(chunkSize);
if (_zstream.avail_in == 0 && !allInputRead) {
allInputRead = getInputData();
}
// Make zlib process some data (compress or decompress). This updates
// _zstream.{avail,next}_{in,out} (4 fields of the z_stream struct).
retCode = zlibProcessData();
// chunkSize - _zstream.avail_out is the nb of chars written by zlib
remaining -= chunkSize - _zstream.avail_out;
if (retCode == Z_BUF_ERROR) {
handleZ_BUF_ERROR(); // doesn't return
} else if (retCode == Z_STREAM_END) {
assert(_zstream.avail_in == 0); // all of _inBuf must have been used
_allFinished = true;
break;
} else if (retCode < 0) { // negative codes are errors
throw sg_io_exception(::zlibErrorMessage(_zstream, retCode),
sg_location(_path));
}
}
// Finally, prepare the putback area.
//
// We could use reinterpret_cast<char*>(_zstream.next_out) everywhere below,
// except it is hardly readable. This points after the latest data we wrote.
writePtr = reinterpret_cast<char*>(_zstream.next_out);
// There are two buffers containing characters we potentially have to copy
// to the putback area: the one starting at _outBuf and the one starting at
// dest. In the following diagram, ***** represents those from _outBuf,
// which are right-aligned at origGptr[1], and the series of # represents
// those in the [dest, writePtr) address range:
//
// |_outBuf |eback() *****|origGptr |_outBuf + _outBufSize
//
// |dest #################|writePtr
//
// Together, these two memory blocks logically form a contiguous stream of
// chars we gave to the “client”: *****#################. All we have to do
// now is copy the appropriate amount of chars from this logical stream to
// the putback area, according to _putbackSize. These chars are the last
// ones in said stream (i.e., right portion of *****#################), but
// we have to copy them in order of increasing address to avoid possible
// overlapping problems in _outBuf. This is because some of the chars to
// copy may be located before _outBuf + _putbackSize (i.e., already be in
// the putback area).
//
// [1] This means that the last char represented by a star is at address
// origGptr-1.
assert(writePtr - dest >= 0);
std::size_t inDestBuffer = static_cast<std::size_t>(writePtr - dest);
assert(origGptr - eback() >= 0);
std::size_t nbPutbackChars = std::min(
static_cast<std::size_t>(origGptr - eback()) + inDestBuffer,
_putbackSize);
std::size_t nbPutbackCharsToGo = nbPutbackChars;
// chunkSize has an unsigned type; precomputing it before the following test
// wouldn't work.
// Are there chars in _outBuf that need to be copied to the putback area?
if (nbPutbackChars > inDestBuffer) {
chunkSize = nbPutbackChars - inDestBuffer; // yes, this number
std::copy(origGptr - chunkSize, origGptr,
_outBuf + _putbackSize - nbPutbackChars);
nbPutbackCharsToGo -= chunkSize;
}
std::copy(writePtr - nbPutbackCharsToGo, writePtr,
_outBuf + _putbackSize - nbPutbackCharsToGo);
setg(_outBuf + _putbackSize - nbPutbackChars, // start of putback area
_outBuf + _putbackSize, // the buffer for pending,
_outBuf + _putbackSize); // available data is empty
std::streamsize rem = static_cast<std::streamsize>(remaining);
// This is guaranteed because n is of type std::streamsize and the whole
// algorithm ensures that 0 <= remaining <= n.
assert(rem >= 0);
assert(static_cast<std::size_t>(rem) == remaining);
assert(n - rem >= 0);
// Total number of chars copied.
return n - rem;
}
// ***************************************************************************
// * ZlibCompressorIStreambuf class *
// ***************************************************************************
ZlibCompressorIStreambuf::ZlibCompressorIStreambuf(
std::istream& iStream,
const SGPath& path,
int compressionLevel,
ZLibCompressionFormat format,
ZLibMemoryStrategy memStrategy,
char* inBuf,
std::size_t inBufSize,
char *outBuf,
std::size_t outBufSize,
std::size_t putbackSize)
: ZlibAbstractIStreambuf(iStream, path, inBuf, inBufSize, outBuf, outBufSize,
putbackSize)
{
zStreamInit(compressionLevel, format, memStrategy);
}
ZlibCompressorIStreambuf::~ZlibCompressorIStreambuf()
{
int retCode = deflateEnd(&_zstream); // deallocate the z_stream struct
if (retCode != Z_OK) {
// In C++11, we can't throw exceptions from a destructor.
SG_LOG(SG_IO, SG_ALERT, "ZlibCompressorIStreambuf: " <<
::zlibErrorMessage(_zstream, retCode));
}
}
ZlibAbstractIStreambuf::OperationType
ZlibCompressorIStreambuf::operationType() const
{
return OPERATION_TYPE_COMPRESSION;
}
void ZlibCompressorIStreambuf::zStreamInit(int compressionLevel,
ZLibCompressionFormat format,
ZLibMemoryStrategy memStrategy)
{
int windowBits, memLevel;
// Intentionally not listing ZLIB_COMPRESSION_FORMAT_AUTODETECT here (it is
// only for decompression!)
switch (format) {
case ZLIB_COMPRESSION_FORMAT_ZLIB:
windowBits = 15;
break;
case ZLIB_COMPRESSION_FORMAT_GZIP:
windowBits = 31;
break;
default:
throw std::logic_error("Unexpected compression format: " +
std::to_string(format));
}
switch (memStrategy) {
case ZLIB_FAVOR_MEMORY_OVER_SPEED:
memLevel = 8;
break;
case ZLIB_FAVOR_SPEED_OVER_MEMORY:
memLevel = 9;
break;
default:
throw std::logic_error("Unexpected memory strategy: " +
std::to_string(memStrategy));
}
_zstream.zalloc = Z_NULL; // No custom memory allocation routines
_zstream.zfree = Z_NULL; // Ditto. Therefore, the 'opaque' field won't
_zstream.opaque = Z_NULL; // be used, actually.
int retCode = deflateInit2(&_zstream, compressionLevel, Z_DEFLATED,
windowBits, memLevel, Z_DEFAULT_STRATEGY);
if (retCode != Z_OK) {
throw sg_io_exception(::zlibErrorMessage(_zstream, retCode),
sg_location(_path));
}
}
int ZlibCompressorIStreambuf::zlibProcessData()
{
// Compress as much data as possible given _zstream.avail_in and
// _zstream.avail_out. The input data starts at _zstream.next_in, the output
// at _zstream.next_out, and these four fields are all updated by this call
// to reflect exactly the amount of data zlib has consumed and produced.
return deflate(&_zstream, _zstream.avail_in ? Z_NO_FLUSH : Z_FINISH);
}
// ***************************************************************************
// * ZlibDecompressorIStreambuf class *
// ***************************************************************************
ZlibDecompressorIStreambuf::ZlibDecompressorIStreambuf(
std::istream& iStream,
const SGPath& path,
ZLibCompressionFormat format,
char* inBuf,
std::size_t inBufSize,
char *outBuf,
std::size_t outBufSize,
std::size_t putbackSize)
: ZlibAbstractIStreambuf(iStream, path, inBuf, inBufSize, outBuf, outBufSize,
putbackSize)
{
zStreamInit(format);
}
ZlibDecompressorIStreambuf::~ZlibDecompressorIStreambuf()
{
int retCode = inflateEnd(&_zstream); // deallocate the z_stream struct
if (retCode != Z_OK) {
// In C++11, we can't throw exceptions from a destructor.
SG_LOG(SG_IO, SG_ALERT, "ZlibDecompressorIStreambuf: " <<
::zlibErrorMessage(_zstream, retCode));
}
}
ZlibAbstractIStreambuf::OperationType
ZlibDecompressorIStreambuf::operationType() const
{
return OPERATION_TYPE_DECOMPRESSION;
}
void ZlibDecompressorIStreambuf::zStreamInit(ZLibCompressionFormat format)
{
int windowBits;
switch (format) {
case ZLIB_COMPRESSION_FORMAT_ZLIB:
windowBits = 15;
break;
case ZLIB_COMPRESSION_FORMAT_GZIP:
windowBits = 31;
break;
case ZLIB_COMPRESSION_FORMAT_AUTODETECT:
windowBits = 47; // 47 = 32 + 15
break;
default:
throw std::logic_error("Unexpected compression format: " +
std::to_string(format));
}
_zstream.zalloc = Z_NULL; // No custom memory allocation routines
_zstream.zfree = Z_NULL; // Ditto. Therefore, the 'opaque' field won't
_zstream.opaque = Z_NULL; // be used, actually.
int retCode = inflateInit2(&_zstream, windowBits);
if (retCode != Z_OK) {
throw sg_io_exception(::zlibErrorMessage(_zstream, retCode),
sg_location(_path));
}
}
int ZlibDecompressorIStreambuf::zlibProcessData()
{
// Decompress as much data as possible given _zstream.avail_in and
// _zstream.avail_out. The input data starts at _zstream.next_in, the output
// at _zstream.next_out, and these four fields are all updated by this call
// to reflect exactly the amount of data zlib has consumed and produced.
return inflate(&_zstream, _zstream.avail_in ? Z_NO_FLUSH : Z_FINISH);
}
// ***************************************************************************
// * ZlibCompressorIStream class *
// ***************************************************************************
ZlibCompressorIStream::ZlibCompressorIStream(std::istream& iStream,
const SGPath& path,
int compressionLevel,
ZLibCompressionFormat format,
ZLibMemoryStrategy memStrategy,
char* inBuf,
std::size_t inBufSize,
char *outBuf,
std::size_t outBufSize,
std::size_t putbackSize)
: _streamBuf(iStream, path, compressionLevel, format, memStrategy, inBuf,
inBufSize, outBuf, outBufSize, putbackSize)
{
rdbuf(&_streamBuf); // Associate _streamBuf to 'this'
}
ZlibCompressorIStream::~ZlibCompressorIStream()
{ }
// ***************************************************************************
// * ZlibDecompressorIStream class *
// ***************************************************************************
ZlibDecompressorIStream::ZlibDecompressorIStream(std::istream& iStream,
const SGPath& path,
ZLibCompressionFormat format,
char* inBuf,
std::size_t inBufSize,
char *outBuf,
std::size_t outBufSize,
std::size_t putbackSize)
: _streamBuf(iStream, path, format, inBuf, inBufSize, outBuf, outBufSize,
putbackSize)
{
rdbuf(&_streamBuf); // Associate _streamBuf to 'this'
}
ZlibDecompressorIStream::~ZlibDecompressorIStream()
{ }
} // of namespace simgear

View File

@@ -1,401 +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 program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#ifndef SG_ZLIBSTREAM_HXX
#define SG_ZLIBSTREAM_HXX
#include <iosfwd>
#include <ios> // std::streamsize
#include <zlib.h> // struct z_stream
#include <simgear/misc/sg_path.hxx>
// This file contains:
//
// - two stream buffer classes (ZlibCompressorIStreambuf and
// ZlibDecompressorIStreambuf), both based on the same abstract class:
// ZlibAbstractIStreambuf;
//
// - two std::istream subclasses (ZlibCompressorIStream and
// ZlibDecompressorIStream), each creating and using the corresponding
// stream buffer class from the previous item.
//
// All these allow one to work with RFC 1950 and RFC 1952 compression
// formats, respectively known as the zlib and gzip formats.
//
// These classes are *input* streaming classes, which means they can
// efficiently handle arbitrary amounts of data without using any disk
// space nor increasing amounts of memory, and allow “client code” to pull
// exactly as much data as it wants at any given time, resuming later
// when it is ready to handle the next chunk.
//
// So, for example, assuming you've created an instance of
// ZlibCompressorIStream (bound to some input stream of your choice, let's
// call it iStream), you could read 512 bytes of data from it, and you
// would get the first 512 bytes of *compressed* data corresponding to what
// iStream provided. Then you could resume at any time and ask for the next
// 512 bytes of compressed data (or any other amount), etc.
//
// Therefore, these classes are well suited, among others, to compress or
// decompress data streams while at the same time packing the result into
// discrete chunks or packets with size constraints (you can think of the
// process as making sausages :).
//
// The input being in each case an std::istream (for compressing as well as
// for decompressing), it can be tied to an arbitrary source: a file with
// sg_ifstream or std::ifstream, a memory buffer with std::istringstream or
// std::stringstream, a TCP socket with a custom std::streambuf subclass[1]
// to interface with the sockets API, etc.
//
// [1] Possibly wrapped in an std::istream.
//
// The stream buffer classes upon which ZlibCompressorIStream and
// ZlibDecompressorIStream are built have an xsgetn() implementation that
// avoids useless copies of data by asking zlib to write directly to the
// destination buffer. This xsgetn() method is used when calling read() on
// the std::istream subclasses, or sgetn() if you are using the stream
// buffer classes directly (i.e., ZlibCompressorIStreambuf and
// ZlibDecompressorIStreambuf). Other std::istream methods may instead rely
// only on the internal buffer and the underflow() method, and therefore be
// less efficient for large amounts of data. You may want to take a look at
// zlibstream_test.cxx to see various ways of using these classes.
//
// In case you use std::istream& operator>>(std::istream&, std::string&) or
// its overload friends, beware that it splits fields at spaces, and by
// default ignores spaces at the beginning of a field (cf. std::skipws,
// std::noskipws and friends). As far as I understand it, most of these
// operators are mainly intended in the IOStreams library to be used to
// read an int here, a double there, a space-delimited string afterwards,
// etc. (the exception could be the overload writing to a stream buffer,
// however it doesn't seem to be very efficient on my system with GNU
// libstdc++ [it is not using xsgetn()], so beware also of this one if you
// are handling large amounts of data). For moderately complex or large
// input handling, I'd suggest to use std::istream methods such as read(),
// gcount() and getline() (std::getline() can be useful too). Or directly
// use the stream buffer classes, in particular with sgetn().
namespace simgear
{
enum ZLibCompressionFormat {
ZLIB_COMPRESSION_FORMAT_ZLIB = 0,
ZLIB_COMPRESSION_FORMAT_GZIP,
ZLIB_COMPRESSION_FORMAT_AUTODETECT
};
enum ZLibMemoryStrategy {
ZLIB_FAVOR_MEMORY_OVER_SPEED = 0,
ZLIB_FAVOR_SPEED_OVER_MEMORY
};
// Abstract base class for both the compressor and decompressor stream buffers.
class ZlibAbstractIStreambuf: public std::streambuf
{
public:
/**
* @brief Constructor for ZlibAbstractIStreambuf.
* @param iStream Input stream to read from.
* @param path Optional path to the file corresponding to iStream,
* if any. Only used for error messages.
* @param inBuf Pointer to the input buffer (data read from iStream is
* written there before being compressed or decompressed).
* If nullptr, the buffer is allocated on the heap in the
* constructor and deallocated in the destructor.
* @param inBufSize Size of the input buffer, in chars.
* @param outBuf Pointer to the output buffer. Data is read by zlib
* from the input buffer, compressed or decompressed, and
* the result is directly written to the output buffer.
* If nullptr, the buffer is allocated on the heap in the
* constructor and deallocated in the destructor.
* @param outBufSize Size of the output buffer, in chars.
* @param putbackSize Size of the putback area inside the output buffer, in
* chars.
*
* It is required that putbackSize < outBufSize. It is guaranteed that,
* if at least putbackSize chars have been read without any putback (or
* unget) operation intermixed, then at least putbackSize chars can be
* put back in sequence. If you don't need this feature, use zero for the
* putbackSize value (the default) for best performance.
*/
explicit ZlibAbstractIStreambuf(std::istream& iStream,
const SGPath& path = SGPath(),
char* inBuf = nullptr,
std::size_t inBufSize = 262144,
char* outBuf = nullptr,
std::size_t outBufSize = 262144,
std::size_t putbackSize = 0);
ZlibAbstractIStreambuf(const ZlibAbstractIStreambuf&) = delete;
ZlibAbstractIStreambuf& operator=(const ZlibAbstractIStreambuf&) = delete;
~ZlibAbstractIStreambuf();
protected:
enum OperationType {
OPERATION_TYPE_COMPRESSION = 0,
OPERATION_TYPE_DECOMPRESSION
};
virtual OperationType operationType() const = 0;
// Either compress or decompress a chunk of data (depending on the
// particular subclass implementation). The semantics are the same as for
// zlib's inflate() and deflate() functions applied to member _zstream,
// concerning 1) the return value and 2) where, and how much to read and
// write (which thus depends on _zstream.avail_in, _zstream.next_in,
// _zstream.avail_out and _zstream.next_out).
virtual int zlibProcessData() = 0;
// The input stream, from which data is read before being processed by zlib
std::istream& _iStream;
// Corresponding path, if any (default-constructed SGPath instance otherwise)
const SGPath _path;
// Structure used to communicate with zlib
z_stream _zstream;
private:
// Callback whose role is to refill the output buffer when it's empty and
// the “client” tries to read more.
int underflow() override;
// Optional override when subclassing std::streambuf. This is the most
// efficient way of reading several characters (as soon as we've emptied the
// output buffer, data is written by zlib directly to the destination
// buffer).
std::streamsize xsgetn(char* dest, std::streamsize n) override;
// Make sure there is data to read in the input buffer, or signal EOF.
bool getInputData();
// Utility method for fillOutputBuffer()
std::size_t fOB_remainingSpace(unsigned char* nextOutPtr) const;
// Fill the output buffer (using zlib functions) as much as possible.
char* fillOutputBuffer();
// Utility method
[[ noreturn ]] void handleZ_BUF_ERROR() const;
bool _allFinished = false;
// The buffers
// ~~~~~~~~~~~
//
// The input buffer receives data obtained from _iStream, before it is
// processed by zlib. In underflow(), zlib reads from this buffer it and
// writes the resulting data(*) to the output buffer. Then we point the
// standard std::streambuf pointers (gptr() and friends) directly towards
// the data inside that output buffer. xsgetn() is even more optimized: it
// first empties the output buffer, then makes zlib write the remaining data
// directly to the destination area.
//
// (*) Compressed or decompressed, depending on the particular
// implementation of zlibProcessData() in each subclass.
char* _inBuf;
const std::size_t _inBufSize;
// _inBufEndPtr points right after the last data retrieved from _iStream and
// stored into _inBuf. When zlib has read all such data, _zstream.next_in is
// equal to _inBufEndPtr (after proper casting). Except in this particular
// situation, only _zstream.next_in <= _inBufEndPtr is guaranteed.
char* _inBufEndPtr;
// Layout of the _outBuf buffer:
//
// |_outBuf <putback area> |_outBuf + _putbackSize |_outBuf + _outBufSize
//
// The first _putbackSize chars in _outBuf are reserved for the putback area
// (right-aligned at _outBuf + _putbackSize). The actual output buffer thus
// starts at _outBuf + _putbackSize. At any given time for callers of this
// class, the number of characters that can be put back is gptr() - eback().
// It may be lower than _putbackSize if we haven't read that many characters
// yet. It may also be larger if gptr() > _outBuf + _putbackSize, i.e.,
// when the buffer for pending data is non-empty.
//
// At any given time, callers should see:
//
// _outBuf <= eback() <= _outBuf + _putbackSize <= gptr() <= egptr()
// <= _outBuf + _outBufSize
//
// (hoping this won't get out of sync with the code!)
char *_outBuf;
const std::size_t _outBufSize;
// Space reserved for characters to be put back into the stream. Must be
// strictly smaller than _outBufSize (this is checked in the constructor).
// It is guaranteed that this number of chars can be put back, except of
// course if we haven't read that many characters from the input stream yet.
// If characters are buffered in _outBuf[2], then it may be that more
// characters than _putbackSize can be put back (it is essentially a matter
// for std::streambuf of decreasing the “next pointer for the input
// sequence”, i.e., the one returned by gptr()).
//
// [2] In the [_outBuf + _putbackSize, _outBuf + _outBufSize) area.
const std::size_t _putbackSize;
// Since the constructor optionally allocates memory for the input and
// output buffers, these members allow the destructor to know which buffers
// have to be deallocated, if any.
bool _inBufMustBeFreed = false;
bool _outBufMustBeFreed = false;
};
// Stream buffer class for compressing data. Input data is obtained from an
// std::istream instance; the corresponding compressed data can be read using
// the standard std::streambuf read interface (mainly: sbumpc(), sgetc(),
// snextc(), sgetn(), sputbackc(), sungetc()). Input, uncompressed data is
// “pulled” as needed for the amount of compressed data requested by the
// “client” using the methods I just listed.
class ZlibCompressorIStreambuf: public ZlibAbstractIStreambuf
{
public:
// Same parameters as for ZlibAbstractIStreambuf, except:
//
// compressionLevel: in the [0,9] range. 0 means no compression at all.
// Levels 1 to 9 yield compressed data, with 1 giving
// the highest compression speed but worst compression
// ratio, and 9 the highest compression ratio but lowest
// compression speed.
// format either ZLIB_COMPRESSION_FORMAT_ZLIB or
// ZLIB_COMPRESSION_FORMAT_GZIP
// memStrategy either ZLIB_FAVOR_MEMORY_OVER_SPEED or
// ZLIB_FAVOR_SPEED_OVER_MEMORY
explicit ZlibCompressorIStreambuf(
std::istream& iStream,
const SGPath& path = SGPath(),
int compressionLevel = Z_DEFAULT_COMPRESSION,
ZLibCompressionFormat format = ZLIB_COMPRESSION_FORMAT_ZLIB,
ZLibMemoryStrategy memStrategy = ZLIB_FAVOR_SPEED_OVER_MEMORY,
char* inBuf = nullptr,
std::size_t inBufSize = 262144,
char* outBuf = nullptr,
std::size_t outBufSize = 262144,
std::size_t putbackSize = 0);
ZlibCompressorIStreambuf(const ZlibCompressorIStreambuf&) = delete;
ZlibCompressorIStreambuf& operator=(const ZlibCompressorIStreambuf&) = delete;
~ZlibCompressorIStreambuf();
protected:
OperationType operationType() const override;
// Initialize the z_stream struct used by zlib
void zStreamInit(int compressionLevel, ZLibCompressionFormat format,
ZLibMemoryStrategy memStrategy);
// Call zlib's deflate() function to compress data.
int zlibProcessData() override;
};
// Stream buffer class for decompressing data. Input data is obtained from an
// std::istream instance; the corresponding decompressed data can be read
// using the standard std::streambuf read interface (mainly: sbumpc(),
// sgetc(), snextc(), sgetn(), sputbackc(), sungetc()). Input, compressed data
// is “pulled” as needed for the amount of uncompressed data requested by the
// “client” using the methods I just listed.
class ZlibDecompressorIStreambuf: public ZlibAbstractIStreambuf
{
public:
// Same parameters as for ZlibAbstractIStreambuf, except:
//
// format ZLIB_COMPRESSION_FORMAT_ZLIB,
// ZLIB_COMPRESSION_FORMAT_GZIP or
// ZLIB_COMPRESSION_FORMAT_AUTODETECT
explicit ZlibDecompressorIStreambuf(
std::istream& iStream,
const SGPath& path = SGPath(),
ZLibCompressionFormat format = ZLIB_COMPRESSION_FORMAT_ZLIB,
char* inBuf = nullptr,
std::size_t inBufSize = 262144,
char* outBuf = nullptr,
std::size_t outBufSize = 262144,
std::size_t putbackSize = 0); // default optimized for speed
ZlibDecompressorIStreambuf(const ZlibDecompressorIStreambuf&) = delete;
ZlibDecompressorIStreambuf& operator=(const ZlibDecompressorIStreambuf&)
= delete;
~ZlibDecompressorIStreambuf();
protected:
OperationType operationType() const override;
void zStreamInit(ZLibCompressionFormat format);
int zlibProcessData() override;
};
// std::istream subclass for compressing data. Input data is obtained from an
// std::istream instance; the corresponding compressed data can be read using
// the standard std::istream interface (read(), readsome(), gcount(), get(),
// getline(), operator>>(), peek(), putback(), ignore(), unget()... plus
// operator overloads such as istream& operator>>(istream&, string&) as
// defined in <string>, and std::getline()). Input, uncompressed data is
// “pulled” as needed for the amount of compressed data requested by the
// “client”.
//
// To get data efficiently from an instance of this class, use its read()
// method (typically in conjunction with gcount(), inside a loop).
class ZlibCompressorIStream: public std::istream
{
public:
// Same parameters as for ZlibCompressorIStreambuf
explicit ZlibCompressorIStream(
std::istream& iStream,
const SGPath& path = SGPath(),
int compressionLevel = Z_DEFAULT_COMPRESSION,
ZLibCompressionFormat format = ZLIB_COMPRESSION_FORMAT_ZLIB,
ZLibMemoryStrategy memStrategy = ZLIB_FAVOR_SPEED_OVER_MEMORY,
char* inBuf = nullptr,
std::size_t inBufSize = 262144,
char* outBuf = nullptr,
std::size_t outBufSize = 262144,
std::size_t putbackSize = 0); // default optimized for speed
ZlibCompressorIStream(const ZlibCompressorIStream&) = delete;
ZlibCompressorIStream& operator=(const ZlibCompressorIStream&) = delete;
~ZlibCompressorIStream();
private:
ZlibCompressorIStreambuf _streamBuf;
};
// std::istream subclass for decompressing data. Input data is obtained from
// an std::istream instance; the corresponding decompressed data can be read
// using the standard std::istream interface (read(), readsome(), gcount(),
// get(), getline(), operator>>(), peek(), putback(), ignore(), unget()...
// plus operator overloads such as istream& operator>>(istream&, string&) as
// defined in <string>, and std::getline()). Input, compressed data is
// “pulled” as needed for the amount of uncompressed data requested by the
// “client”.
//
// To get data efficiently from an instance of this class, use its read()
// method (typically in conjunction with gcount(), inside a loop).
class ZlibDecompressorIStream: public std::istream
{
public:
// Same parameters as for ZlibDecompressorIStreambuf
explicit ZlibDecompressorIStream(
std::istream& iStream,
const SGPath& path = SGPath(),
ZLibCompressionFormat format = ZLIB_COMPRESSION_FORMAT_ZLIB,
char* inBuf = nullptr,
std::size_t inBufSize = 262144,
char* outBuf = nullptr,
std::size_t outBufSize = 262144,
std::size_t putbackSize = 0); // default optimized for speed
ZlibDecompressorIStream(const ZlibDecompressorIStream&) = delete;
ZlibDecompressorIStream& operator=(const ZlibDecompressorIStream&) = delete;
~ZlibDecompressorIStream();
private:
ZlibDecompressorIStreambuf _streamBuf;
};
} // of namespace simgear
#endif // of SG_ZLIBSTREAM_HXX

View File

@@ -1,562 +0,0 @@
// -*- coding: utf-8 -*-
//
// zlibstream_test.cxx --- Automated tests for zlibstream.cxx / zlibstream.hxx
//
// Copyright (C) 2017 Florent Rougon
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <ios> // std::basic_ios, std::streamsize...
#include <iostream> // std::ios_base, std::cerr, etc.
#include <sstream>
#include <array>
#include <random>
#include <limits> // std::numeric_limits
#include <functional> // std::bind()
#include <cassert>
#include <cstdlib> // EXIT_SUCCESS
#include <cstddef> // std::size_t
#include <cstring> // strcmp()
using std::string;
using std::cout;
using std::cerr;
using traits = std::char_traits<char>;
#include <simgear/misc/test_macros.hxx>
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/io/iostreams/zlibstream.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/misc/sg_dir.hxx>
// In many tests below, I use very small buffer sizes. Of course, this is bad
// for performance. The reason I do it this way is simply because it better
// exercises the code we want to *test* here (we are more likely to find bugs
// if the buffer(s) has/have to be refilled one or more times). Similarly, if
// you don't need the putback feature in non-test code, best performance is
// achieved with putback size = 0.
//
// I suggest you read roundTripWithIStreams() below to see how to use the
// classes most efficiently (especially the comments!).
static std::default_random_engine randomNumbersGenerator;
// Sample string for tests
static const string lipsum = "\
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque congue ornare\n\
congue. Mauris mollis est et porttitor condimentum. Vivamus laoreet blandit\n\
odio eget consectetur. Etiam quis magna eu enim luctus pretium. In et\n\
tristique nunc, non efficitur metus. Nullam efficitur tristique velit.\n\
Praesent et luctus nunc. Mauris eros eros, rutrum at molestie quis, egestas et\n\
lorem. Ut nulla turpis, eleifend sed mauris ac, faucibus molestie nulla.\n\
Quisque viverra vel turpis nec efficitur. Proin non rutrum velit. Nam sodales\n\
metus felis, eu pharetra velit posuere ut.\n\
\n\
Suspendisse pellentesque tincidunt ligula et pretium. Etiam id justo mauris.\n\
Aenean porta, sapien in suscipit tristique, metus diam malesuada dui, in porta\n\
felis mi non felis. Etiam vel aliquam leo, non vehicula magna. Proin justo\n\
massa, ultrices at porta eu, tempor in ligula. Duis enim ipsum, dictum quis\n\
ultricies et, tempus eu libero. Morbi vulputate libero ut dolor rutrum, a\n\
imperdiet nibh egestas. Phasellus finibus massa vel tempus hendrerit. Nulla\n\
lobortis est non ligula viverra, quis egestas ante hendrerit. Pellentesque\n\
finibus mollis blandit. In ac sollicitudin mauris, eget dignissim mauris.";
// Utility function: generate a random string whose length is in the
// [minLen, maxLen] range.
string randomString(string::size_type minLen, string::size_type maxLen)
{
std::uniform_int_distribution<string::size_type> sLenDist(minLen, maxLen);
std::uniform_int_distribution<int> byteDist(0, 255);
auto randomByte = std::bind(byteDist, randomNumbersGenerator);
string::size_type len = sLenDist(randomNumbersGenerator);
string str;
while (str.size() < len) {
str += traits::to_char_type(randomByte());
}
return str;
}
// Utility function: perform a compression-decompression cycle on the input
// string using the stream buffer classes:
//
// 1) Compress the input string, write the result to a temporary file using
// std::ostream& operator<<(streambuf* sb), where 'sb' points to a
// ZlibCompressorIStreambuf instance.
//
// 2) Close the temporary file to make sure all the data is flushed.
//
// 3) Create a ZlibDecompressorIStreambuf instance reading from the
// temporary file, then decompress to an std::ostringstream using
// std::ostream& operator<<(streambuf* sb), where 'sb' points to our
// ZlibDecompressorIStreambuf instance.
//
// 4) Compare the result with the input string.
//
// Of course, it is possible to do this without any temporary file, even
// without any std::stringstream or such to hold the intermediate, compressed
// data. See the other tests for actual compressor + decompressor pipelines.
void pipeCompOrDecompIStreambufIntoOStream(const string& input)
{
// Test “std::ostream << &ZlibCompressorIStreambuf”
std::istringstream input_ss(input);
simgear::ZlibCompressorIStreambuf compSBuf(input_ss, SGPath(), 9);
simgear::Dir tmpDir = simgear::Dir::tempDir("FlightGear");
tmpDir.setRemoveOnDestroy();
SGPath path = tmpDir.path() / "testFile.dat";
sg_ofstream oFile(path,
std::ios::binary | std::ios::trunc | std::ios::out);
oFile << &compSBuf;
// Make sure the compressed stream is flushed, otherwise when we read the
// file back, decompression is likely to fail with Z_BUF_ERROR.
oFile.close();
SG_CHECK_EQUAL(compSBuf.sgetc(), EOF);
// Read back and decompress the data we've written to 'path', testing
// “std::ostream << &ZlibDecompressorIStreambuf”
sg_ifstream iFile(path, std::ios::binary | std::ios::in);
simgear::ZlibDecompressorIStreambuf decompSBuf(iFile, path);
std::ostringstream roundTripResult_ss;
// This is also possible, though maybe not as good for error detection:
//
// decompSBuf >> roundTripResult_ss.rdbuf();
roundTripResult_ss << &decompSBuf;
SG_CHECK_EQUAL(decompSBuf.sgetc(), EOF);
SG_CHECK_EQUAL(roundTripResult_ss.str(), input);
}
// Perform a compression-decompression cycle on bunch of strings, using the
// stream buffer classes directly (not the std::istream subclasses).
void test_pipeCompOrDecompIStreambufIntoOStream()
{
cerr << "Compression-decompression cycles using the stream buffer classes "
"directly\n";
pipeCompOrDecompIStreambufIntoOStream(""); // empty string
pipeCompOrDecompIStreambufIntoOStream("a");
pipeCompOrDecompIStreambufIntoOStream("lorem ipsum");
for (int i=0; i < 10; i++) {
pipeCompOrDecompIStreambufIntoOStream(randomString(0, 20));
}
for (int i=0; i < 10; i++) {
pipeCompOrDecompIStreambufIntoOStream(randomString(21, 1000));
}
assert(std::numeric_limits<string::size_type>::max() >= 65535);
for (int i=0; i < 10; i++) {
pipeCompOrDecompIStreambufIntoOStream(randomString(1000, 65535));
}
}
void test_StreambufBasicOperations()
{
cerr << "Testing basic operations on ZlibDecompressorIStreambuf\n";
const string text = "0123456789abcdefghijklmnopqrstuvwxyz\nABCDEF\nGHIJK "
"LMNOPQ";
std::istringstream text_ss(text);
static constexpr std::size_t compInBufSize = 6;
static constexpr std::size_t compOutBufSize = 4;
static constexpr std::size_t compPutbackSize = 0;
simgear::ZlibCompressorIStreambuf compSBuf(
text_ss, SGPath(), 8, simgear::ZLIB_COMPRESSION_FORMAT_ZLIB,
simgear::ZLIB_FAVOR_SPEED_OVER_MEMORY, nullptr, compInBufSize, nullptr,
compOutBufSize, compPutbackSize);
std::stringstream compressedOutput_ss;
compressedOutput_ss << &compSBuf;
static constexpr std::size_t decompInBufSize = 5;
static constexpr std::size_t decompOutBufSize = 4;
static constexpr std::size_t decompPutbackSize = 2;
simgear::ZlibDecompressorIStreambuf decompSBuf(
compressedOutput_ss, SGPath(), simgear::ZLIB_COMPRESSION_FORMAT_ZLIB,
nullptr, decompInBufSize, nullptr, decompOutBufSize, decompPutbackSize);
int ch = decompSBuf.sgetc();
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == '0');
ch = decompSBuf.sbumpc();
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == '0');
ch = decompSBuf.sbumpc();
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == '1');
ch = decompSBuf.snextc();
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == '3');
ch = decompSBuf.sbumpc();
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == '3');
ch = decompSBuf.sbumpc();
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == '4');
ch = decompSBuf.sputbackc('4');
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == '4');
ch = decompSBuf.sputbackc('u'); // doesn't match what we read from the stream
SG_VERIFY(ch == EOF);
ch = decompSBuf.sputbackc('3'); // this one does
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == '3');
static constexpr std::streamsize bufSize = 10;
char buf[bufSize];
// Most efficient way (with the underlying xsgetn()) to read several chars
// at once.
std::streamsize n = decompSBuf.sgetn(buf, bufSize);
SG_VERIFY(n == bufSize && string(buf, bufSize) == "3456789abc");
ch = decompSBuf.sungetc(); // same as sputbackc(), except no value to check
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == 'c');
ch = decompSBuf.sungetc();
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == 'b');
ch = decompSBuf.sbumpc();
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == 'b');
ch = decompSBuf.sputbackc('b'); // this one does
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == 'b');
n = decompSBuf.sgetn(buf, bufSize);
SG_VERIFY(n == bufSize && string(buf, bufSize) == "bcdefghijk");
ch = decompSBuf.sungetc();
SG_VERIFY(ch != EOF && traits::to_char_type(ch) == 'k');
static char buf2[64];
n = decompSBuf.sgetn(buf2, sizeof(buf2));
SG_VERIFY(n == 36 && string(buf2, n) == "klmnopqrstuvwxyz\nABCDEF\nGHIJK "
"LMNOPQ");
ch = decompSBuf.sbumpc();
SG_CHECK_EQUAL(ch, EOF);
}
// Utility function: take a string as input to compress and return the
// compressed output as a string.
string compress(const string& dataToCompress,
simgear::ZLibCompressionFormat compressionFormat,
int compressionLevel,
simgear::ZLibMemoryStrategy memStrategy,
std::size_t putbackSize,
SGPath path = SGPath())
{
// Static storage is only okay for very small sizes like these, and because
// we won't call the function from several threads at the same time. Plain
// char arrays would be fine here, but I'll use std::array to show it can be
// used here too.
static std::array<char, 7> inBuf;
static std::array<char, 7> outBuf;
std::istringstream iss(dataToCompress);
simgear::ZlibCompressorIStream compressor(
iss, path, compressionLevel, compressionFormat, memStrategy,
&inBuf.front(), inBuf.size(), &outBuf.front(), outBuf.size(),
putbackSize);
compressor.exceptions(std::ios_base::badbit); // throw if badbit is set
std::ostringstream compressedData_ss;
compressor >> compressedData_ss.rdbuf();
return compressedData_ss.str();
}
void test_formattedInputFromDecompressor()
{
cerr << "Testing ZlibDecompressorIStream >> std::string\n";
static char inBuf[6];
static char outBuf[15];
string compressed = compress(
lipsum, simgear::ZLIB_COMPRESSION_FORMAT_ZLIB, Z_BEST_COMPRESSION,
simgear::ZLIB_FAVOR_MEMORY_OVER_SPEED, /* putback size */ 0);
std::istringstream compressed_ss(compressed);
simgear::ZlibDecompressorIStream decompressor(
compressed_ss, SGPath(), simgear::ZLIB_COMPRESSION_FORMAT_ZLIB,
inBuf, sizeof(inBuf), outBuf, sizeof(outBuf), /* putback size */ 1);
decompressor.exceptions(std::ios_base::badbit); // throw if badbit is set
int count = 0;
string word;
while (decompressor >> word) {
count++;
}
SG_CHECK_EQUAL(count, 175); // Number of words in 'lipsum'
// If set, badbit would have caused an exception to be raised
SG_VERIFY(!decompressor.bad());
if (!decompressor.eof()) {
assert(decompressor.fail());
cerr << "Decompressor: stream extraction (operator>>) failed before "
"reaching EOF.\n";
SG_TEST_FAIL("Did not expect operator>> to fail with an std::string "
"argument.");
}
}
void test_ZlibDecompressorIStream_readPutbackEtc()
{
cerr << "Testing many operations on ZlibDecompressorIStream (read(), "
"putback(), etc.\n";
static char compInBuf[4];
static char compOutBuf[6];
static char decompInBuf[8];
static char decompOutBuf[5];
const string text = "0123456789abcdefghijklmnopqrstuvwxyz\nABCDEF\nGHIJK "
"LMNOPQ";
std::istringstream text_ss(text);
simgear::ZlibCompressorIStream compressor(
text_ss, SGPath(), Z_BEST_COMPRESSION,
simgear::ZLIB_COMPRESSION_FORMAT_ZLIB,
simgear::ZLIB_FAVOR_MEMORY_OVER_SPEED,
compInBuf, sizeof(compInBuf), compOutBuf, sizeof(compOutBuf),
/* putback size */ 0);
compressor.exceptions(std::ios_base::badbit); // throw if badbit is set
// Use the compressor (subclass of std::istream) as input to the decompressor
simgear::ZlibDecompressorIStream decompressor(
compressor, SGPath(), simgear::ZLIB_COMPRESSION_FORMAT_ZLIB,
decompInBuf, sizeof(decompInBuf), decompOutBuf, sizeof(decompOutBuf),
/* putback size */ 3);
decompressor.exceptions(std::ios_base::badbit);
{
static std::array<char, 17> buf;
decompressor.read(&buf.front(), 10);
SG_VERIFY(decompressor.good() && decompressor.gcount() == 10);
SG_CHECK_EQUAL(string(&buf.front(), 10), "0123456789");
SG_VERIFY(decompressor.putback('9'));
SG_VERIFY(decompressor.putback('8'));
SG_VERIFY(decompressor.putback('7'));
SG_VERIFY(decompressor.get(buf[10]));
SG_VERIFY(decompressor.read(&buf[11], 6));
SG_CHECK_EQUAL(string(&buf.front(), 17), "0123456789789abcd");
}
{
bool gotException = false;
try {
// 'Z' is not the last character read from the stream
decompressor.putback('Z');
} catch (std::ios_base::failure) {
gotException = true;
}
SG_VERIFY(gotException && decompressor.bad());
}
{
int c;
decompressor.clear();
// Check we can resume normally now that we've cleared the error state flags
c = decompressor.get();
SG_VERIFY(c != traits::eof() && traits::to_char_type(c) == 'e');
// unget() and get() in sequence
SG_VERIFY(decompressor.unget());
c = decompressor.get();
SG_VERIFY(c != traits::eof() && traits::to_char_type(c)== 'e');
// peek() looks at, but doesn't consume
c = decompressor.peek();
SG_VERIFY(c != traits::eof() && traits::to_char_type(c) == 'f');
c = decompressor.get();
SG_VERIFY(c != traits::eof() && traits::to_char_type(c)== 'f');
}
{
static std::array<char, 21> buf; // 20 chars + terminating NUL char
// std::istream::getline() will be stopped by \n after reading 20 chars
SG_VERIFY(decompressor.getline(&buf.front(), 25));
SG_VERIFY(decompressor.gcount() == 21 &&
!strcmp(&buf.front(), "ghijklmnopqrstuvwxyz"));
string str;
// std::getline() will be stopped by \n after 7 chars have been extracted,
// namely 6 chars plus the \n which is extracted and discarded.
SG_VERIFY(std::getline(decompressor, str));
SG_CHECK_EQUAL(str, "ABCDEF");
SG_VERIFY(decompressor.ignore(2));
SG_VERIFY(decompressor >> str);
SG_CHECK_EQUAL(str, "IJK");
static char buf2[5];
// Read up to sizeof(buf) chars, without waiting if not that many are
// immediately avaiable.
int nbCharsRead = decompressor.readsome(buf2, sizeof(buf2));
string rest(buf2, nbCharsRead);
do {
decompressor.read(buf2, sizeof(buf2));
rest += string(buf2, decompressor.gcount());
} while (decompressor);
SG_CHECK_EQUAL(rest, " LMNOPQ");
}
// If set, badbit would have caused an exception to be raised, anyway
SG_VERIFY(decompressor.eof() && !decompressor.bad());
}
// Utility function: parametrized round-trip test with a compressor +
// decompressor pipeline.
//
//
// Note: this is nice conceptually, allows to keep memory use constant even in
// case an arbitrary amount of data is passed through, and exercises the
// stream buffer classes well, however this technique is more than twice
// as slow on my computer than using an intermediate buffer like this:
//
// std::stringstream compressedData_ss;
// compressor >> compressedData_ss.rdbuf();
//
// simgear::ZlibDecompressorIStream decompressor(compressedData_ss,
// SGPath(), ...
void roundTripWithIStreams(
simgear::ZLibCompressionFormat compressionFormat,
int compressionLevel,
simgear::ZLibMemoryStrategy memStrategy,
std::size_t compInBufSize,
std::size_t compOutBufSize,
std::size_t decompInBufSize,
std::size_t decompOutBufSize,
std::size_t compPutbackSize,
std::size_t decompPutbackSize,
bool useAutoFormatForDecompression = false)
{
const simgear::ZLibCompressionFormat decompFormat =
(useAutoFormatForDecompression) ?
simgear::ZLIB_COMPRESSION_FORMAT_AUTODETECT : compressionFormat;
std::istringstream lipsum_ss(lipsum);
// This tests the optional dynamic buffer allocation in ZlibAbstractIStreambuf
simgear::ZlibCompressorIStream compressor(
lipsum_ss, SGPath(), compressionLevel, compressionFormat, memStrategy,
nullptr, compInBufSize, nullptr, compOutBufSize, compPutbackSize);
compressor.exceptions(std::ios_base::badbit); // throw if badbit is set
// Use the compressor as input to the decompressor (pipeline). The
// decompressor uses read() with chunks that are as large as possible given
// the available space in its input buffer. These read() calls are served by
// ZlibCompressorIStreambuf::xsgetn(), which is efficient.
simgear::ZlibDecompressorIStream decompressor(
compressor, SGPath(), decompFormat,
nullptr, decompInBufSize, nullptr, decompOutBufSize, decompPutbackSize);
decompressor.exceptions(std::ios_base::badbit);
std::ostringstream roundTripResult;
// This, on the other hand, appears not to use xsgetn() (tested with the GNU
// libstdc++ from g++ 6.3.0, 20170124). This causes useless copying of the
// data to the decompressor output buffer, instead of writing it directly to
// roundTripResult's internal buffer. This is simple and nice in appearance,
// but if you are after performance, better use decompressor.read()
// (typically in conjunction with gcount(), inside a loop).
decompressor >> roundTripResult.rdbuf();
SG_CHECK_EQUAL(roundTripResult.str(), lipsum);
}
// Round-trip conversion with simgear::ZlibCompressorIStream and
// simgear::ZlibDecompressorIStream, using various parameter combinations.
void test_RoundTripMultiWithIStreams()
{
cerr <<
"Compression-decompression cycles using the std::istream subclasses (many\n"
"combinations of buffer sizes and compression parameters tested)\n";
{ // More variations on these later
const std::size_t compPutbackSize = 1;
const std::size_t decompPutbackSize = 1;
for (auto format: {simgear::ZLIB_COMPRESSION_FORMAT_ZLIB,
simgear::ZLIB_COMPRESSION_FORMAT_GZIP}) {
for (int compressionLevel: {1, 4, 7, 9}) {
for (auto memStrategy: {simgear::ZLIB_FAVOR_MEMORY_OVER_SPEED,
simgear::ZLIB_FAVOR_SPEED_OVER_MEMORY}) {
for (std::size_t compInBufSize: {3, 4}) {
for (std::size_t compOutBufSize: {3, 5}) {
for (std::size_t decompInBufSize: {3, 4}) {
for (std::size_t decompOutBufSize: {3, 4,}) {
roundTripWithIStreams(
format, compressionLevel, memStrategy, compInBufSize,
compOutBufSize, decompInBufSize, decompOutBufSize,
compPutbackSize, decompPutbackSize);
}
}
}
}
}
}
}
}
{
const auto format = simgear::ZLIB_COMPRESSION_FORMAT_ZLIB;
const int compressionLevel = Z_DEFAULT_COMPRESSION;
const auto memStrategy = simgear::ZLIB_FAVOR_SPEED_OVER_MEMORY;
for (std::size_t compInBufSize: {3, 4, 31, 256, 19475}) {
for (std::size_t compOutBufSize: {3, 5, 9, 74, 4568}) {
for (std::size_t decompInBufSize: {3, 4, 256, 24568}) {
for (std::size_t decompOutBufSize: {3, 5, 42, 4568}) {
for (std::size_t compPutbackSize: {0, 1, 2}) {
for (std::size_t decompPutbackSize: {0, 1, 2}) {
roundTripWithIStreams(
format, compressionLevel, memStrategy, compInBufSize,
compOutBufSize, decompInBufSize, decompOutBufSize,
compPutbackSize, decompPutbackSize);
}
}
}
}
}
}
}
{
const std::size_t compInBufSize = 5;
const std::size_t compOutBufSize = 107;
const std::size_t decompInBufSize = 65536;
const std::size_t decompOutBufSize = 84;
int i = 0;
for (std::size_t compPutbackSize: {25, 40, 105}) {
for (std::size_t decompPutbackSize: {30, 60, 81}) {
const simgear::ZLibCompressionFormat compFormat = (i++ % 2) ?
simgear::ZLIB_COMPRESSION_FORMAT_ZLIB :
simgear::ZLIB_COMPRESSION_FORMAT_GZIP;
roundTripWithIStreams(
compFormat, Z_BEST_COMPRESSION, simgear::ZLIB_FAVOR_MEMORY_OVER_SPEED,
compInBufSize, compOutBufSize, decompInBufSize, decompOutBufSize,
compPutbackSize, decompPutbackSize,
/* automatic format detection for decompression */ true);
}
}
}
}
int main(int argc, char** argv)
{
test_pipeCompOrDecompIStreambufIntoOStream();
test_StreambufBasicOperations();
test_RoundTripMultiWithIStreams();
test_formattedInputFromDecompressor();
test_ZlibDecompressorIStream_readPutbackEtc();
return EXIT_SUCCESS;
}

View File

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

View File

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

View File

@@ -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,80 +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 );
Watchdog watchdog;
watchdog.start(100);
simgear::Socket::initSockets();
DNS::Client cl;
cout << "test update without prior pending request" << endl;
{
cout << "polling.";
for( int i = 0; i < 20; i++ ) {
SGTimeStamp::sleepForMSec(200);
cl.update(0);
cout << ".";
cout.flush();
}
cout << "done" << endl;
}
#define EXISTING_RECORD "terrasync.flightgear.org"
cout << "test existing NAPTR: " EXISTING_RECORD << endl;
// 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);
@@ -118,15 +58,15 @@ int main(int argc, char* argv[])
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 ) {
// 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
SG_CHECK_EQUAL((*it)->service, "ws20" );
COMPARE((*it)->service, "ws20" );
if( (*it)->order < order ) {
cerr << "NAPTR entries not ascending for field 'order'" << endl;
@@ -155,7 +95,7 @@ int main(int argc, char* argv[])
}
}
cout << "test non-existing NAPTR" << endl;
// test non-existing NAPTR
{
DNS::NAPTRRequest * naptrRequest = new DNS::NAPTRRequest("jurkxkqdiufqzpfvzqok.prozhqrlcaavbxifkkhf");
DNS::Request_ptr r(naptrRequest);
@@ -169,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;

View File

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

View File

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

View File

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

View File

@@ -30,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));
}
@@ -45,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));
}

View File

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

View File

@@ -101,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;

View File

@@ -44,7 +44,7 @@ public:
bool hasError() const;
private:
std::unique_ptr<TarExtractorPrivate> d;
std::auto_ptr<TarExtractorPrivate> d;
};
} // of namespace simgear

View File

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

View File

@@ -28,9 +28,6 @@
#include "SGRect.hxx"
#include "sg_random.h"
int lineno = 0;
template<typename T>
bool
Vec3Test(void)
@@ -41,45 +38,45 @@ Vec3Test(void)
v1 = SGVec3<T>(1, 2, 3);
v2 = SGVec3<T>(3, 2, 1);
if (equivalent(v1, v2))
{ lineno = __LINE__; return false; }
return false;
// Check the unary minus operator
v3 = SGVec3<T>(-1, -2, -3);
if (!equivalent(-v1, v3))
{ lineno = __LINE__; return false; }
return false;
// Check the unary plus operator
v3 = SGVec3<T>(1, 2, 3);
if (!equivalent(+v1, v3))
{ lineno = __LINE__; return false; }
return false;
// Check the addition operator
v3 = SGVec3<T>(4, 4, 4);
if (!equivalent(v1 + v2, v3))
{ lineno = __LINE__; return false; }
return false;
// Check the subtraction operator
v3 = SGVec3<T>(-2, 0, 2);
if (!equivalent(v1 - v2, v3))
{ lineno = __LINE__; return false; }
return false;
// Check the scaler multiplication operator
v3 = SGVec3<T>(2, 4, 6);
if (!equivalent(2*v1, v3))
{ lineno = __LINE__; return false; }
return false;
// Check the dot product
if (fabs(dot(v1, v2) - 10) > 10*SGLimits<T>::epsilon())
{ lineno = __LINE__; return false; }
return false;
// Check the cross product
v3 = SGVec3<T>(-4, 8, -4);
if (!equivalent(cross(v1, v2), v3))
{ lineno = __LINE__; return false; }
return false;
// Check the euclidean length
if (fabs(14 - length(v1)*length(v1)) > 14*SGLimits<T>::epsilon())
{ lineno = __LINE__; return false; }
return false;
return true;
}
@@ -92,11 +89,11 @@ isSameRotation(const SGQuat<T>& q1, const SGQuat<T>& q2)
const SGVec3<T> e2(0, 1, 0);
const SGVec3<T> e3(0, 0, 1);
if (!equivalent(q1.transform(e1), q2.transform(e1)))
{ lineno = __LINE__; return false; }
return false;
if (!equivalent(q1.transform(e2), q2.transform(e2)))
{ lineno = __LINE__; return false; }
return false;
if (!equivalent(q1.transform(e3), q2.transform(e3)))
{ lineno = __LINE__; return false; }
return false;
return true;
}
@@ -114,37 +111,37 @@ QuatTest(void)
v1 = SGVec3<T>(1, 2, 3);
v2 = SGVec3<T>(1, -2, -3);
if (!equivalent(q1.transform(v1), v2))
{ lineno = __LINE__; return false; }
return false;
// Check a rotation around the x axis
q1 = SGQuat<T>::fromAngleAxis(0.5*SGMisc<T>::pi(), e1);
v2 = SGVec3<T>(1, 3, -2);
if (!equivalent(q1.transform(v1), v2))
{ lineno = __LINE__; return false; }
return false;
// Check a rotation around the y axis
q1 = SGQuat<T>::fromAngleAxis(SGMisc<T>::pi(), e2);
v2 = SGVec3<T>(-1, 2, -3);
if (!equivalent(q1.transform(v1), v2))
{ lineno = __LINE__; return false; }
return false;
// Check a rotation around the y axis
q1 = SGQuat<T>::fromAngleAxis(0.5*SGMisc<T>::pi(), e2);
v2 = SGVec3<T>(-3, 2, 1);
if (!equivalent(q1.transform(v1), v2))
{ lineno = __LINE__; return false; }
return false;
// Check a rotation around the z axis
q1 = SGQuat<T>::fromAngleAxis(SGMisc<T>::pi(), e3);
v2 = SGVec3<T>(-1, -2, 3);
if (!equivalent(q1.transform(v1), v2))
{ lineno = __LINE__; return false; }
return false;
// Check a rotation around the z axis
q1 = SGQuat<T>::fromAngleAxis(0.5*SGMisc<T>::pi(), e3);
v2 = SGVec3<T>(2, -1, 3);
if (!equivalent(q1.transform(v1), v2))
{ lineno = __LINE__; return false; }
return false;
// Now check some successive transforms
// We can reuse the prevously tested stuff
@@ -153,7 +150,7 @@ QuatTest(void)
q3 = q1*q2;
v2 = q2.transform(q1.transform(v1));
if (!equivalent(q3.transform(v1), v2))
{ lineno = __LINE__; return false; }
return false;
/// Test from Euler angles
float x = 0.2*SGMisc<T>::pi();
@@ -165,7 +162,7 @@ QuatTest(void)
v2 = q3.transform(q2.transform(q1.transform(v1)));
q4 = SGQuat<T>::fromEulerRad(z, y, x);
if (!equivalent(q4.transform(v1), v2))
{ lineno = __LINE__; return false; }
return false;
/// Test angle axis forward and back transform
q1 = SGQuat<T>::fromAngleAxis(0.2*SGMisc<T>::pi(), e1);
@@ -175,15 +172,15 @@ QuatTest(void)
q1.getAngleAxis(angleAxis);
q4 = SGQuat<T>::fromAngleAxis(angleAxis);
if (!isSameRotation(q1, q4))
{ lineno = __LINE__; return false; }
return false;
q2.getAngleAxis(angleAxis);
q4 = SGQuat<T>::fromAngleAxis(angleAxis);
if (!isSameRotation(q2, q4))
{ lineno = __LINE__; return false; }
return false;
q3.getAngleAxis(angleAxis);
q4 = SGQuat<T>::fromAngleAxis(angleAxis);
if (!isSameRotation(q3, q4))
{ lineno = __LINE__; return false; }
return false;
/// Test angle axis forward and back transform
q1 = SGQuat<T>::fromAngleAxis(0.2*SGMisc<T>::pi(), e1);
@@ -192,15 +189,15 @@ QuatTest(void)
SGVec3<T> positiveAngleAxis = q1.getPositiveRealImag();
q4 = SGQuat<T>::fromPositiveRealImag(positiveAngleAxis);
if (!isSameRotation(q1, q4))
{ lineno = __LINE__; return false; }
return false;
positiveAngleAxis = q2.getPositiveRealImag();
q4 = SGQuat<T>::fromPositiveRealImag(positiveAngleAxis);
if (!isSameRotation(q2, q4))
{ lineno = __LINE__; return false; }
return false;
positiveAngleAxis = q3.getPositiveRealImag();
q4 = SGQuat<T>::fromPositiveRealImag(positiveAngleAxis);
if (!isSameRotation(q3, q4))
{ lineno = __LINE__; return false; }
return false;
return true;
}
@@ -222,13 +219,13 @@ QuatDerivativeTest(void)
// Check if we can restore the angular velocity
SGVec3<T> av2 = SGQuat<T>::forwardDifferenceVelocity(o0, o1, dt);
if (!equivalent(av, av2))
{ lineno = __LINE__; return false; }
return false;
// Test with the equivalent orientation
o1 = -o1;
av2 = SGQuat<T>::forwardDifferenceVelocity(o0, o1, dt);
if (!equivalent(av, av2))
{ lineno = __LINE__; return false; }
return false;
}
return true;
}
@@ -253,23 +250,23 @@ MatrixTest(void)
invert(m2, m0);
m3 = transNeg(m0);
if (!equivalent(m1, m2))
{ lineno = __LINE__; return false; }
return false;
if (!equivalent(m2, m3))
{ lineno = __LINE__; return false; }
return false;
// Check matrix multiplication and inversion
if (!equivalent(m0*m1, SGMatrix<T>::unit()))
{ lineno = __LINE__; return false; }
return false;
if (!equivalent(m1*m0, SGMatrix<T>::unit()))
{ lineno = __LINE__; return false; }
return false;
if (!equivalent(m0*m2, SGMatrix<T>::unit()))
{ lineno = __LINE__; return false; }
return false;
if (!equivalent(m2*m0, SGMatrix<T>::unit()))
{ lineno = __LINE__; return false; }
return false;
if (!equivalent(m0*m3, SGMatrix<T>::unit()))
{ lineno = __LINE__; return false; }
return false;
if (!equivalent(m3*m0, SGMatrix<T>::unit()))
{ lineno = __LINE__; return false; }
return false;
return true;
}
@@ -279,26 +276,26 @@ void doRectTest()
{
SGRect<T> rect(10, 15, 20, 25);
SG_CHECK_EQUAL(rect.x(), 10)
SG_CHECK_EQUAL(rect.y(), 15)
SG_CHECK_EQUAL(rect.width(), 20)
SG_CHECK_EQUAL(rect.height(), 25)
COMPARE(rect.x(), 10)
COMPARE(rect.y(), 15)
COMPARE(rect.width(), 20)
COMPARE(rect.height(), 25)
SG_CHECK_EQUAL(rect.pos(), SGVec2<T>(10, 15))
SG_CHECK_EQUAL(rect.size(), SGVec2<T>(20, 25))
COMPARE(rect.pos(), SGVec2<T>(10, 15))
COMPARE(rect.size(), SGVec2<T>(20, 25))
SG_CHECK_EQUAL(rect.l(), 10)
SG_CHECK_EQUAL(rect.t(), 15)
SG_CHECK_EQUAL(rect.r(), 30)
SG_CHECK_EQUAL(rect.b(), 40)
COMPARE(rect.l(), 10)
COMPARE(rect.t(), 15)
COMPARE(rect.r(), 30)
COMPARE(rect.b(), 40)
SG_VERIFY(rect == rect)
SG_VERIFY(rect == SGRect<T>(10, 15, 20, 25))
SG_VERIFY(rect != SGRect<T>(11, 15, 20, 25))
VERIFY(rect == rect)
VERIFY(rect == SGRect<T>(10, 15, 20, 25))
VERIFY(rect != SGRect<T>(11, 15, 20, 25))
SG_VERIFY(rect.contains(10, 15))
SG_VERIFY(!rect.contains(9, 15))
SG_VERIFY(rect.contains(9, 15, 1))
VERIFY(rect.contains(10, 15))
VERIFY(!rect.contains(9, 15))
VERIFY(rect.contains(9, 15, 1))
}
bool
@@ -323,13 +320,13 @@ GeodesyTest(void)
if (epsDeg < fabs(geod0.getLongitudeDeg() - geod1.getLongitudeDeg()) ||
epsDeg < fabs(geod0.getLatitudeDeg() - geod1.getLatitudeDeg()) ||
epsM < fabs(geod0.getElevationM() - geod1.getElevationM()))
{ lineno = __LINE__; return false; }
return false;
// Test the conversion routines to radial coordinates
geoc0 = SGGeoc::fromCart(cart0);
cart1 = SGVec3<double>::fromGeoc(geoc0);
if (!equivalent(cart0, cart1))
{ lineno = __LINE__; return false; }
return false;
// test course / advance routines
// uses examples from Williams aviation formulary
@@ -339,12 +336,12 @@ GeodesyTest(void)
double distNm = SGGeodesy::distanceRad(lax, jfk) * SG_RAD_TO_NM;
std::cout << "distance is " << distNm << std::endl;
if (0.5 < fabs(distNm - 2144)) // 2144 nm
{ lineno = __LINE__; return false; }
return false;
double crsDeg = SGGeodesy::courseRad(lax, jfk) * SG_RADIANS_TO_DEGREES;
std::cout << "course is " << crsDeg << std::endl;
if (0.5 < fabs(crsDeg - 66)) // 66 degrees
{ lineno = __LINE__; return false; }
return false;
SGGeoc adv;
SGGeodesy::advanceRadM(lax, crsDeg * SG_DEGREES_TO_RADIANS, 100 * SG_NM_TO_METER, adv);
@@ -352,7 +349,7 @@ GeodesyTest(void)
if (0.01 < fabs(adv.getLongitudeRad() - (-2.034206)) ||
0.01 < fabs(adv.getLatitudeRad() - 0.604180))
{ lineno = __LINE__; return false; }
return false;
return true;
}
@@ -364,25 +361,25 @@ main(void)
// Do vector tests
if (!Vec3Test<float>())
{ fprintf(stderr, "Error at line: %i called from line: %i\n", lineno, __LINE__); return EXIT_FAILURE; }
return EXIT_FAILURE;
if (!Vec3Test<double>())
{ fprintf(stderr, "Error at line: %i called from line: %i\n", lineno, __LINE__); return EXIT_FAILURE; }
return EXIT_FAILURE;
// Do quaternion tests
if (!QuatTest<float>())
{ fprintf(stderr, "Error at line: %i called from line: %i\n", lineno, __LINE__); return EXIT_FAILURE; }
return EXIT_FAILURE;
if (!QuatTest<double>())
{ fprintf(stderr, "Error at line: %i called from line: %i\n", lineno, __LINE__); return EXIT_FAILURE; }
return EXIT_FAILURE;
if (!QuatDerivativeTest<float>())
{ fprintf(stderr, "Error at line: %i called from line: %i\n", lineno, __LINE__); return EXIT_FAILURE; }
return EXIT_FAILURE;
if (!QuatDerivativeTest<double>())
{ fprintf(stderr, "Error at line: %i called from line: %i\n", lineno, __LINE__); return EXIT_FAILURE; }
return EXIT_FAILURE;
// Do matrix tests
if (!MatrixTest<float>())
{ fprintf(stderr, "Error at line: %i called from line: %i\n", lineno, __LINE__); return EXIT_FAILURE; }
return EXIT_FAILURE;
if (!MatrixTest<double>())
{ fprintf(stderr, "Error at line: %i called from line: %i\n", lineno, __LINE__); return EXIT_FAILURE; }
return EXIT_FAILURE;
// Do rect tests
doRectTest<int>();
@@ -390,7 +387,7 @@ main(void)
// Check geodetic/geocentric/cartesian conversions
if (!GeodesyTest())
{ fprintf(stderr, "Error at line: %i called from line: %i\n", lineno, __LINE__); return EXIT_FAILURE; }
return EXIT_FAILURE;
std::cout << "Successfully passed all tests!" << std::endl;
return EXIT_SUCCESS;

View File

@@ -18,8 +18,6 @@
#ifndef SGMatrix_H
#define SGMatrix_H
#include <simgear/math/simd4x4.hxx>
/// Expression templates for poor programmers ... :)
template<typename T>
struct TransNegRef;
@@ -40,13 +38,13 @@ public:
/// uninitialized values in the debug build very fast ...
#ifndef NDEBUG
for (unsigned i = 0; i < nEnts; ++i)
_data[i] = SGLimits<T>::quiet_NaN();
_data.flat[i] = SGLimits<T>::quiet_NaN();
#endif
}
/// Constructor. Initialize by the content of a plain array,
/// make sure it has at least 16 elements
explicit SGMatrix(const T* data)
{ _data = simd4x4_t<T,4>(data); }
{ for (unsigned i = 0; i < nEnts; ++i) _data.flat[i] = data[i]; }
/// Constructor, build up a SGMatrix from given elements
SGMatrix(T m00, T m01, T m02, T m03,
@@ -54,8 +52,14 @@ public:
T m20, T m21, T m22, T m23,
T m30, T m31, T m32, T m33)
{
_data = simd4x4_t<T,4>(m00,m01,m02,m03,m10,m11,m12,m13,
m20,m21,m22,m23,m30,m31,m32,m33);
_data.flat[0] = m00; _data.flat[1] = m10;
_data.flat[2] = m20; _data.flat[3] = m30;
_data.flat[4] = m01; _data.flat[5] = m11;
_data.flat[6] = m21; _data.flat[7] = m31;
_data.flat[8] = m02; _data.flat[9] = m12;
_data.flat[10] = m22; _data.flat[11] = m32;
_data.flat[12] = m03; _data.flat[13] = m13;
_data.flat[14] = m23; _data.flat[15] = m33;
}
/// Constructor, build up a SGMatrix from a translation
@@ -76,8 +80,14 @@ public:
template<typename S>
void set(const SGVec3<S>& trans)
{
simd4x4::unit(_data);
simd4x4::translate(_data, trans.simd3());
_data.flat[0] = 1; _data.flat[4] = 0;
_data.flat[8] = 0; _data.flat[12] = T(trans(0));
_data.flat[1] = 0; _data.flat[5] = 1;
_data.flat[9] = 0; _data.flat[13] = T(trans(1));
_data.flat[2] = 0; _data.flat[6] = 0;
_data.flat[10] = 1; _data.flat[14] = T(trans(2));
_data.flat[3] = 0; _data.flat[7] = 0;
_data.flat[11] = 0; _data.flat[15] = 1;
}
/// Set from a scale/rotation and tranlation
@@ -88,77 +98,82 @@ public:
T xx = x*x; T yy = y*y; T zz = z*z;
T wx = w*x; T wy = w*y; T wz = w*z;
T xy = x*y; T xz = x*z; T yz = y*z;
_data[0] = 1-2*(yy+zz); _data[1] = 2*(xy-wz);
_data[2] = 2*(xz+wy); _data[3] = 0;
_data[4] = 2*(xy+wz); _data[5] = 1-2*(xx+zz);
_data[6] = 2*(yz-wx); _data[7] = 0;
_data[8] = 2*(xz-wy); _data[9] = 2*(yz+wx);
_data[10] = 1-2*(xx+yy); _data[11] = 0;
_data[12] = 0; _data[13] = 0;
_data[14] = 0; _data[15] = 1;
_data.flat[0] = 1-2*(yy+zz); _data.flat[1] = 2*(xy-wz);
_data.flat[2] = 2*(xz+wy); _data.flat[3] = 0;
_data.flat[4] = 2*(xy+wz); _data.flat[5] = 1-2*(xx+zz);
_data.flat[6] = 2*(yz-wx); _data.flat[7] = 0;
_data.flat[8] = 2*(xz-wy); _data.flat[9] = 2*(yz+wx);
_data.flat[10] = 1-2*(xx+yy); _data.flat[11] = 0;
_data.flat[12] = 0; _data.flat[13] = 0;
_data.flat[14] = 0; _data.flat[15] = 1;
}
/// set from a transposed negated matrix
void set(const TransNegRef<T>& tm)
{
const SGMatrix& m = tm.m;
_data = simd4x4::transpose(m.simd4x4());
_data[3] = m(3,0);
_data[7] = m(3,1);
_data[11] = m(3,2);
_data.flat[0] = m(0,0);
_data.flat[1] = m(0,1);
_data.flat[2] = m(0,2);
_data.flat[3] = m(3,0);
_data.flat[4] = m(1,0);
_data.flat[5] = m(1,1);
_data.flat[6] = m(1,2);
_data.flat[7] = m(3,1);
_data.flat[8] = m(2,0);
_data.flat[9] = m(2,1);
_data.flat[10] = m(2,2);
_data.flat[11] = m(3,2);
// Well, this one is ugly here, as that xform method on the current
// object needs the above data to be already set ...
SGVec3<T> t = xformVec(SGVec3<T>(m(0,3), m(1,3), m(2,3)));
_data.set(3, -t.simd3());
_data[15] = m(3,3);
_data.flat[12] = -t(0);
_data.flat[13] = -t(1);
_data.flat[14] = -t(2);
_data.flat[15] = m(3,3);
}
/// Access by index, the index is unchecked
const T& operator()(unsigned i, unsigned j) const
{ return _data[i + 4*j]; }
{ return _data.flat[i + 4*j]; }
/// Access by index, the index is unchecked
T& operator()(unsigned i, unsigned j)
{ return _data[i + 4*j]; }
{ return _data.flat[i + 4*j]; }
/// Access raw data by index, the index is unchecked
const T& operator[](unsigned i) const
{ return _data[i]; }
{ return _data.flat[i]; }
/// Access by index, the index is unchecked
T& operator[](unsigned i)
{ return _data[i]; }
{ return _data.flat[i]; }
/// Get the data pointer
const T* data(void) const
{ return _data; }
{ return _data.flat; }
/// Get the data pointer
T* data(void)
{ return _data; }
{ return _data.flat; }
/// Readonly interface function to ssg's sgMat4/sgdMat4
const T (&sg(void) const)[4][4]
{ return _data.ptr(); }
{ return _data.carray; }
/// Interface function to ssg's sgMat4/sgdMat4
T (&sg(void))[4][4]
{ return _data.ptr(); }
/// Readonly raw storage interface
const simd4x4_t<T,4> (&simd4x4(void) const)
{ return _data; }
/// Readonly raw storage interface
simd4x4_t<T,4> (&simd4x4(void))
{ return _data; }
{ return _data.carray; }
/// Inplace addition
SGMatrix& operator+=(const SGMatrix& m)
{ _data += m.simd4x4(); return *this; }
{ for (unsigned i = 0; i < nEnts; ++i) _data.flat[i] += m._data.flat[i]; return *this; }
/// Inplace subtraction
SGMatrix& operator-=(const SGMatrix& m)
{ _data -= m.simd4x4(); return *this; }
{ for (unsigned i = 0; i < nEnts; ++i) _data.flat[i] -= m._data.flat[i]; return *this; }
/// Inplace scalar multiplication
template<typename S>
SGMatrix& operator*=(S s)
{ _data *= s; return *this; }
{ for (unsigned i = 0; i < nEnts; ++i) _data.flat[i] *= s; return *this; }
/// Inplace scalar multiplication by 1/s
template<typename S>
SGMatrix& operator/=(S s)
@@ -169,13 +184,27 @@ public:
template<typename S>
SGMatrix& preMultTranslate(const SGVec3<S>& t)
{
simd4x4::pre_translate(_data,t.simd3());
for (unsigned i = 0; i < 3; ++i) {
T tmp = T(t(i));
if (tmp == 0)
continue;
(*this)(i,0) += tmp*(*this)(3,0);
(*this)(i,1) += tmp*(*this)(3,1);
(*this)(i,2) += tmp*(*this)(3,2);
(*this)(i,3) += tmp*(*this)(3,3);
}
return *this;
}
template<typename S>
SGMatrix& postMultTranslate(const SGVec3<S>& t)
{
simd4x4::post_translate(_data,t.simd3());
SGVec4<T> col3((*this)(0,3), (*this)(1,3), (*this)(2,3), (*this)(3,3));
for (unsigned i = 0; i < SGMatrix<T>::nCols-1; ++i) {
SGVec4<T> tmp((*this)(0,i), (*this)(1,i), (*this)(2,i), (*this)(3,i));
col3 += T(t(i))*tmp;
}
(*this)(0,3) = col3(0); (*this)(1,3) = col3(1);
(*this)(2,3) = col3(2); (*this)(3,3) = col3(3);
return *this;
}
@@ -201,27 +230,51 @@ public:
SGVec3<T> xformPt(const SGVec3<T>& pt) const
{
SGVec3<T> tpt;
tpt.simd3() = simd4x4::transform(_data,pt.simd3());
tpt(0) = (*this)(0,3);
tpt(1) = (*this)(1,3);
tpt(2) = (*this)(2,3);
for (unsigned i = 0; i < SGMatrix<T>::nCols-1; ++i) {
T tmp = pt(i);
tpt(0) += tmp*(*this)(0,i);
tpt(1) += tmp*(*this)(1,i);
tpt(2) += tmp*(*this)(2,i);
}
return tpt;
}
SGVec3<T> xformVec(const SGVec3<T>& v) const
{
SGVec3<T> tv;
tv.simd3() = _data * v.simd3();
T tmp = v(0);
tv(0) = tmp*(*this)(0,0);
tv(1) = tmp*(*this)(1,0);
tv(2) = tmp*(*this)(2,0);
for (unsigned i = 1; i < SGMatrix<T>::nCols-1; ++i) {
T tmp = v(i);
tv(0) += tmp*(*this)(0,i);
tv(1) += tmp*(*this)(1,i);
tv(2) += tmp*(*this)(2,i);
}
return tv;
}
/// Return an all zero matrix
static SGMatrix zeros(void)
{ SGMatrix r; simd4x4::zeros(r.simd4x4()); return r; }
{ return SGMatrix(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); }
/// Return a unit matrix
static SGMatrix unit(void)
{ SGMatrix r; simd4x4::unit(r.simd4x4()); return r; }
{ return SGMatrix(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); }
private:
/// Required to make that alias safe.
union Data {
T flat[16];
T carray[4][4];
};
simd4x4_t<T,4> _data;
/// The actual data, the matrix is stored in column major order,
/// that matches the storage format of OpenGL
Data _data;
};
/// Class to distinguish between a matrix and the matrix with a transposed
@@ -243,45 +296,61 @@ operator+(const SGMatrix<T>& m)
template<typename T>
inline
SGMatrix<T>
operator-(SGMatrix<T> m)
operator-(const SGMatrix<T>& m)
{
m.simd4x4() = -m.simd4x4();
return m;
SGMatrix<T> ret;
for (unsigned i = 0; i < SGMatrix<T>::nEnts; ++i)
ret[i] = -m[i];
return ret;
}
/// Binary +
template<typename T>
inline
SGMatrix<T>
operator+(SGMatrix<T> m1, const SGMatrix<T>& m2)
operator+(const SGMatrix<T>& m1, const SGMatrix<T>& m2)
{
m1.simd4x4() += m2.simd4x4();
return m1;
SGMatrix<T> ret;
for (unsigned i = 0; i < SGMatrix<T>::nEnts; ++i)
ret[i] = m1[i] + m2[i];
return ret;
}
/// Binary -
template<typename T>
inline
SGMatrix<T>
operator-(SGMatrix<T> m1, const SGMatrix<T>& m2)
operator-(const SGMatrix<T>& m1, const SGMatrix<T>& m2)
{
m1.simd4x4() -= m2.simd4x4();
return m1;
SGMatrix<T> ret;
for (unsigned i = 0; i < SGMatrix<T>::nEnts; ++i)
ret[i] = m1[i] - m2[i];
return ret;
}
/// Scalar multiplication
template<typename S, typename T>
inline
SGMatrix<T>
operator*(S s, SGMatrix<T> m)
{ m.simd4x4() *= s; return m; }
operator*(S s, const SGMatrix<T>& m)
{
SGMatrix<T> ret;
for (unsigned i = 0; i < SGMatrix<T>::nEnts; ++i)
ret[i] = s*m[i];
return ret;
}
/// Scalar multiplication
template<typename S, typename T>
inline
SGMatrix<T>
operator*(SGMatrix<T> m, S s)
{ m.simd4x4() *= s; return m; }
operator*(const SGMatrix<T>& m, S s)
{
SGMatrix<T> ret;
for (unsigned i = 0; i < SGMatrix<T>::nEnts; ++i)
ret[i] = s*m[i];
return ret;
}
/// Vector multiplication
template<typename T>
@@ -290,7 +359,18 @@ SGVec4<T>
operator*(const SGMatrix<T>& m, const SGVec4<T>& v)
{
SGVec4<T> mv;
mv.simd4() = m.simd4x4() * v.simd4();
T tmp = v(0);
mv(0) = tmp*m(0,0);
mv(1) = tmp*m(1,0);
mv(2) = tmp*m(2,0);
mv(3) = tmp*m(3,0);
for (unsigned i = 1; i < SGMatrix<T>::nCols; ++i) {
T tmp = v(i);
mv(0) += tmp*m(0,i);
mv(1) += tmp*m(1,i);
mv(2) += tmp*m(2,i);
mv(3) += tmp*m(3,i);
}
return mv;
}
@@ -325,7 +405,20 @@ SGMatrix<T>
operator*(const SGMatrix<T>& m1, const SGMatrix<T>& m2)
{
SGMatrix<T> m;
m.simd4x4() = m1.simd4x4() * m2.simd4x4();
for (unsigned j = 0; j < SGMatrix<T>::nCols; ++j) {
T tmp = m2(0,j);
m(0,j) = tmp*m1(0,0);
m(1,j) = tmp*m1(1,0);
m(2,j) = tmp*m1(2,0);
m(3,j) = tmp*m1(3,0);
for (unsigned i = 1; i < SGMatrix<T>::nCols; ++i) {
T tmp = m2(i,j);
m(0,j) += tmp*m1(0,i);
m(1,j) += tmp*m1(1,i);
m(2,j) += tmp*m1(2,i);
m(3,j) += tmp*m1(3,i);
}
}
return m;
}

View File

@@ -23,7 +23,6 @@
#include <simgear/math/SGLimits.hxx>
#include <simgear/math/SGMisc.hxx>
#include <simgear/math/SGMathFwd.hxx>
#include <simgear/math/simd.hxx>
/// 2D Vector Class
template<typename T>
@@ -45,11 +44,11 @@ public:
}
/// Constructor. Initialize by the given values
SGVec2(T x, T y)
{ _data = simd4_t<T,2>(x, y); }
{ data()[0] = x; data()[1] = y; }
/// Constructor. Initialize by the content of a plain array,
/// make sure it has at least 2 elements
explicit SGVec2(const T* d)
{ _data = d ? simd4_t<T,2>(d) : simd4_t<T,2>(T(0)); }
{ data()[0] = d[0]; data()[1] = d[1]; }
template<typename S>
explicit SGVec2(const SGVec2<S>& d)
{ data()[0] = d[0]; data()[1] = d[1]; }
@@ -83,30 +82,25 @@ public:
/// Access raw data
const T (&data(void) const)[2]
{ return _data.ptr(); }
{ return _data; }
/// Access raw data
T (&data(void))[2]
{ return _data.ptr(); }
const simd4_t<T,2> (&simd2(void) const)
{ return _data; }
/// Readonly raw storage interface
simd4_t<T,2> (&simd2(void))
{ return _data; }
/// Inplace addition
SGVec2& operator+=(const SGVec2& v)
{ _data += v.simd2(); return *this; }
{ data()[0] += v(0); data()[1] += v(1); return *this; }
/// Inplace subtraction
SGVec2& operator-=(const SGVec2& v)
{ _data -= v.simd2(); return *this; }
{ data()[0] -= v(0); data()[1] -= v(1); return *this; }
/// Inplace scalar multiplication
template<typename S>
SGVec2& operator*=(S s)
{ _data *= s; return *this; }
{ data()[0] *= s; data()[1] *= s; return *this; }
/// Inplace scalar multiplication by 1/s
template<typename S>
SGVec2& operator/=(S s)
{ _data*=(1/T(s)); return *this; }
{ return operator*=(1/T(s)); }
/// Return an all zero vector
static SGVec2 zeros(void)
@@ -118,7 +112,7 @@ public:
{ return SGVec2(0, 1); }
private:
simd4_t<T,2> _data;
T _data[2];
};
/// Unary +, do nothing ...
@@ -132,36 +126,36 @@ operator+(const SGVec2<T>& v)
template<typename T>
inline
SGVec2<T>
operator-(SGVec2<T> v)
{ v *= -1; return v; }
operator-(const SGVec2<T>& v)
{ return SGVec2<T>(-v(0), -v(1)); }
/// Binary +
template<typename T>
inline
SGVec2<T>
operator+(SGVec2<T> v1, const SGVec2<T>& v2)
{ v1.simd2() += v2.simd2(); return v1; }
operator+(const SGVec2<T>& v1, const SGVec2<T>& v2)
{ return SGVec2<T>(v1(0)+v2(0), v1(1)+v2(1)); }
/// Binary -
template<typename T>
inline
SGVec2<T>
operator-(SGVec2<T> v1, const SGVec2<T>& v2)
{ v1.simd2() -= v2.simd2(); return v1; }
operator-(const SGVec2<T>& v1, const SGVec2<T>& v2)
{ return SGVec2<T>(v1(0)-v2(0), v1(1)-v2(1)); }
/// Scalar multiplication
template<typename S, typename T>
inline
SGVec2<T>
operator*(S s, SGVec2<T> v)
{ v.simd2() *= s; return v; }
operator*(S s, const SGVec2<T>& v)
{ return SGVec2<T>(s*v(0), s*v(1)); }
/// Scalar multiplication
template<typename S, typename T>
inline
SGVec2<T>
operator*(SGVec2<T> v, S s)
{ v.simd2() *= s; return v; }
operator*(const SGVec2<T>& v, S s)
{ return SGVec2<T>(s*v(0), s*v(1)); }
/// multiplication as a multiplicator, that is assume that the first vector
/// represents a 2x2 diagonal matrix with the diagonal elements in the vector.
@@ -169,42 +163,42 @@ operator*(SGVec2<T> v, S s)
template<typename T>
inline
SGVec2<T>
mult(SGVec2<T> v1, const SGVec2<T>& v2)
{ v1.simd2() *= v2.simd2(); return v1; }
mult(const SGVec2<T>& v1, const SGVec2<T>& v2)
{ return SGVec2<T>(v1(0)*v2(0), v1(1)*v2(1)); }
/// component wise min
template<typename T>
inline
SGVec2<T>
min(SGVec2<T> v1, const SGVec2<T>& v2)
{ v1.simd2() = simd4::min(v1.simd2(), v2.simd2()); return v1; }
min(const SGVec2<T>& v1, const SGVec2<T>& v2)
{return SGVec2<T>(SGMisc<T>::min(v1(0), v2(0)), SGMisc<T>::min(v1(1), v2(1)));}
template<typename S, typename T>
inline
SGVec2<T>
min(SGVec2<T> v, S s)
{ v.simd2() = simd4::min(v.simd2(), simd4_t<T,2>(s)); return v; }
min(const SGVec2<T>& v, S s)
{ return SGVec2<T>(SGMisc<T>::min(s, v(0)), SGMisc<T>::min(s, v(1))); }
template<typename S, typename T>
inline
SGVec2<T>
min(S s, SGVec2<T> v)
{ v.sim2() = simd4::min(v.simd2(), simd4_t<T,2>(s)); return v; }
min(S s, const SGVec2<T>& v)
{ return SGVec2<T>(SGMisc<T>::min(s, v(0)), SGMisc<T>::min(s, v(1))); }
/// component wise max
template<typename T>
inline
SGVec2<T>
max(const SGVec2<T>& v1, const SGVec2<T>& v2)
{ v1 = simd4::max(v1.simd2(), v2.simd2()); return v1; }
{return SGVec2<T>(SGMisc<T>::max(v1(0), v2(0)), SGMisc<T>::max(v1(1), v2(1)));}
template<typename S, typename T>
inline
SGVec2<T>
max(const SGVec2<T>& v, S s)
{ v = simd4::max(v.simd2(), simd4_t<T,2>(s)); return v; }
{ return SGVec2<T>(SGMisc<T>::max(s, v(0)), SGMisc<T>::max(s, v(1))); }
template<typename S, typename T>
inline
SGVec2<T>
max(S s, const SGVec2<T>& v)
{ v = simd4::max(v.simd2(), simd4_t<T,2>(s)); return v; }
{ return SGVec2<T>(SGMisc<T>::max(s, v(0)), SGMisc<T>::max(s, v(1))); }
/// Add two vectors taking care of (integer) overflows. The values are limited
/// to the respective minimum and maximum values.
@@ -222,39 +216,36 @@ template<typename T>
inline
T
dot(const SGVec2<T>& v1, const SGVec2<T>& v2)
{ return simd4::dot(v1.simd2(), v2.simd2()); }
{ return v1(0)*v2(0) + v1(1)*v2(1); }
/// The euclidean norm of the vector, that is what most people call length
template<typename T>
inline
T
norm(const SGVec2<T>& v)
{ return simd4::magnitude(v.simd2()); }
{ return sqrt(dot(v, v)); }
/// The euclidean norm of the vector, that is what most people call length
template<typename T>
inline
T
length(const SGVec2<T>& v)
{ return simd4::magnitude(v.simd2()); }
{ return sqrt(dot(v, v)); }
/// The 1-norm of the vector, this one is the fastest length function we
/// can implement on modern cpu's
template<typename T>
inline
T
norm1(SGVec2<T> v)
{ v.simd2() = simd4::abs(v.simd2()); return (v(0)+v(1)); }
norm1(const SGVec2<T>& v)
{ return fabs(v(0)) + fabs(v(1)); }
/// The inf-norm of the vector
template<typename T>
inline
T
normI(SGVec2<T> v)
{
v.simd2() = simd4::abs(v.simd2());
return SGMisc<T>::max(v(0), v(1));
}
normI(const SGVec2<T>& v)
{ return SGMisc<T>::max(fabs(v(0)), fabs(v(1))); }
/// The euclidean norm of the vector, that is what most people call length
template<typename T>
@@ -344,14 +335,14 @@ template<typename T>
inline
T
dist(const SGVec2<T>& v1, const SGVec2<T>& v2)
{ return simd4::magnitude(v1.simd2() - v2.simd2()); }
{ return norm(v1 - v2); }
/// The squared euclidean distance of the two vectors
template<typename T>
inline
T
distSqr(SGVec2<T> v1, const SGVec2<T>& v2)
{ return simd4::magnitude2(v1.simd2() - v2.simd2()); }
distSqr(const SGVec2<T>& v1, const SGVec2<T>& v2)
{ SGVec2<T> tmp = v1 - v2; return dot(tmp, tmp); }
// calculate the projection of u along the direction of d.
template<typename T>
@@ -359,22 +350,12 @@ inline
SGVec2<T>
projection(const SGVec2<T>& u, const SGVec2<T>& d)
{
T denom = simd4::magnitude2(d.simd2());
T denom = dot(d, d);
T ud = dot(u, d);
if (SGLimits<T>::min() < denom) return u;
else return d * (dot(u, d) / denom);
}
template<typename T>
inline
SGVec2<T>
interpolate(T tau, const SGVec2<T>& v1, const SGVec2<T>& v2)
{
SGVec2<T> r;
r.simd2() = simd4::interpolate(tau, v1.simd2(), v2.simd2());
return r;
}
#ifndef NDEBUG
template<typename T>
inline
@@ -395,11 +376,11 @@ operator<<(std::basic_ostream<char_type, traits_type>& s, const SGVec2<T>& v)
inline
SGVec2f
toVec2f(const SGVec2d& v)
{ SGVec2f f(v); return f; }
{ return SGVec2f((float)v(0), (float)v(1)); }
inline
SGVec2d
toVec2d(const SGVec2f& v)
{ SGVec2d d(v); return d; }
{ return SGVec2d(v(0), v(1)); }
#endif

View File

@@ -22,7 +22,6 @@
#include <simgear/math/SGVec2.hxx>
#include <simgear/math/SGGeodesy.hxx>
#include <simgear/math/simd.hxx>
/// 3D Vector Class
template<typename T>
@@ -55,16 +54,16 @@ public:
/// Constructor. Initialize by the given values
SGVec3(T x, T y, T z)
{ _data = simd4_t<T,3>(x, y, z); }
{ data()[0] = x; data()[1] = y; data()[2] = z; }
/// Constructor. Initialize by the content of a plain array,
/// make sure it has at least 3 elements
explicit SGVec3(const T* d)
{ _data = d ? simd4_t<T,3>(d) : simd4_t<T,3>(T(0)); }
{ data()[0] = d[0]; data()[1] = d[1]; data()[2] = d[2]; }
template<typename S>
explicit SGVec3(const SGVec3<S>& d)
{ data()[0] = d[0]; data()[1] = d[1]; data()[2] = d[2]; }
explicit SGVec3(const SGVec2<T>& v2, const T& v3 = 0)
{ _data = v2.simd2(); data()[2] = v3; }
{ data()[0] = v2[0]; data()[1] = v2[1]; data()[2] = v3; }
/// Access by index, the index is unchecked
const T& operator()(unsigned i) const
@@ -101,31 +100,25 @@ public:
/// Readonly raw storage interface
const T (&data(void) const)[3]
{ return _data.ptr(); }
/// Readonly raw storage interface
T (&data(void))[3]
{ return _data.ptr(); }
/// Readonly raw storage interface
const simd4_t<T,3> (&simd3(void) const)
{ return _data; }
/// Readonly raw storage interface
simd4_t<T,3> (&simd3(void))
T (&data(void))[3]
{ return _data; }
/// Inplace addition
SGVec3& operator+=(const SGVec3& v)
{ _data += v.simd3(); return *this; }
{ data()[0] += v(0); data()[1] += v(1); data()[2] += v(2); return *this; }
/// Inplace subtraction
SGVec3& operator-=(const SGVec3& v)
{ _data -= v.simd3(); return *this; }
{ data()[0] -= v(0); data()[1] -= v(1); data()[2] -= v(2); return *this; }
/// Inplace scalar multiplication
template<typename S>
SGVec3& operator*=(S s)
{ _data *= s; return *this; }
{ data()[0] *= s; data()[1] *= s; data()[2] *= s; return *this; }
/// Inplace scalar multiplication by 1/s
template<typename S>
SGVec3& operator/=(S s)
{ _data*=(1/T(s)); return *this; }
{ return operator*=(1/T(s)); }
/// Return an all zero vector
static SGVec3 zeros(void)
@@ -146,8 +139,7 @@ public:
static SGVec3 fromGeoc(const SGGeoc& geoc);
private:
simd4_t<T,3> _data;
T _data[3];
};
template<>
@@ -201,36 +193,36 @@ operator+(const SGVec3<T>& v)
template<typename T>
inline
SGVec3<T>
operator-(SGVec3<T> v)
{ v *= -1; return v; }
operator-(const SGVec3<T>& v)
{ return SGVec3<T>(-v(0), -v(1), -v(2)); }
/// Binary +
template<typename T>
inline
SGVec3<T>
operator+(SGVec3<T> v1, const SGVec3<T>& v2)
{ v1.simd3() += v2.simd3(); return v1; }
operator+(const SGVec3<T>& v1, const SGVec3<T>& v2)
{ return SGVec3<T>(v1(0)+v2(0), v1(1)+v2(1), v1(2)+v2(2)); }
/// Binary -
template<typename T>
inline
SGVec3<T>
operator-(SGVec3<T> v1, const SGVec3<T>& v2)
{ v1.simd3() -= v2.simd3(); return v1; }
operator-(const SGVec3<T>& v1, const SGVec3<T>& v2)
{ return SGVec3<T>(v1(0)-v2(0), v1(1)-v2(1), v1(2)-v2(2)); }
/// Scalar multiplication
template<typename S, typename T>
inline
SGVec3<T>
operator*(S s, SGVec3<T> v)
{ v.simd3() *= s; return v; }
operator*(S s, const SGVec3<T>& v)
{ return SGVec3<T>(s*v(0), s*v(1), s*v(2)); }
/// Scalar multiplication
template<typename S, typename T>
inline
SGVec3<T>
operator*(SGVec3<T> v, S s)
{ v.simd3() *= s; return v; }
operator*(const SGVec3<T>& v, S s)
{ return SGVec3<T>(s*v(0), s*v(1), s*v(2)); }
/// multiplication as a multiplicator, that is assume that the first vector
/// represents a 3x3 diagonal matrix with the diagonal elements in the vector.
@@ -238,42 +230,66 @@ operator*(SGVec3<T> v, S s)
template<typename T>
inline
SGVec3<T>
mult(SGVec3<T> v1, const SGVec3<T>& v2)
{ v1.simd3() *= v2.simd3(); return v1; }
mult(const SGVec3<T>& v1, const SGVec3<T>& v2)
{ return SGVec3<T>(v1(0)*v2(0), v1(1)*v2(1), v1(2)*v2(2)); }
/// component wise min
template<typename T>
inline
SGVec3<T>
min(SGVec3<T> v1, const SGVec3<T>& v2)
{ v1.simd3() = simd4::min(v1.simd3(), v2.simd3()); return v1; }
min(const SGVec3<T>& v1, const SGVec3<T>& v2)
{
return SGVec3<T>(SGMisc<T>::min(v1(0), v2(0)),
SGMisc<T>::min(v1(1), v2(1)),
SGMisc<T>::min(v1(2), v2(2)));
}
template<typename S, typename T>
inline
SGVec3<T>
min(SGVec3<T> v, S s)
{ v.simd3() = simd4::min(v.simd3(), simd4_t<T,3>(s)); return v; }
min(const SGVec3<T>& v, S s)
{
return SGVec3<T>(SGMisc<T>::min(s, v(0)),
SGMisc<T>::min(s, v(1)),
SGMisc<T>::min(s, v(2)));
}
template<typename S, typename T>
inline
SGVec3<T>
min(S s, SGVec3<T> v)
{ v.simd3() = simd4::min(v.simd3(), simd4_t<T,3>(s)); return v; }
min(S s, const SGVec3<T>& v)
{
return SGVec3<T>(SGMisc<T>::min(s, v(0)),
SGMisc<T>::min(s, v(1)),
SGMisc<T>::min(s, v(2)));
}
/// component wise max
template<typename T>
inline
SGVec3<T>
max(SGVec3<T> v1, const SGVec3<T>& v2)
{ v1.simd3() = simd4::max(v1.simd3(), v2.simd3()); return v1; }
max(const SGVec3<T>& v1, const SGVec3<T>& v2)
{
return SGVec3<T>(SGMisc<T>::max(v1(0), v2(0)),
SGMisc<T>::max(v1(1), v2(1)),
SGMisc<T>::max(v1(2), v2(2)));
}
template<typename S, typename T>
inline
SGVec3<T>
max(SGVec3<T> v, S s)
{ v.simd3() = simd4::max(v.simd3(), simd4_t<T,3>(s)); return v; }
max(const SGVec3<T>& v, S s)
{
return SGVec3<T>(SGMisc<T>::max(s, v(0)),
SGMisc<T>::max(s, v(1)),
SGMisc<T>::max(s, v(2)));
}
template<typename S, typename T>
inline
SGVec3<T>
max(S s, SGVec3<T> v)
{ v.simd3() = simd4::max(v.simd3(), simd4_t<T,3>(s)); return v; }
max(S s, const SGVec3<T>& v)
{
return SGVec3<T>(SGMisc<T>::max(s, v(0)),
SGMisc<T>::max(s, v(1)),
SGMisc<T>::max(s, v(2)));
}
/// Add two vectors taking care of (integer) overflows. The values are limited
/// to the respective minimum and maximum values.
@@ -292,46 +308,47 @@ template<typename T>
inline
T
dot(const SGVec3<T>& v1, const SGVec3<T>& v2)
{ return simd4::dot(v1.simd3(), v2.simd3()); }
{ return v1(0)*v2(0) + v1(1)*v2(1) + v1(2)*v2(2); }
/// The euclidean norm of the vector, that is what most people call length
template<typename T>
inline
T
norm(const SGVec3<T>& v)
{ return simd4::magnitude(v.simd3()); }
{ return sqrt(dot(v, v)); }
/// The euclidean norm of the vector, that is what most people call length
template<typename T>
inline
T
length(const SGVec3<T>& v)
{ return simd4::magnitude(v.simd3()); }
{ return sqrt(dot(v, v)); }
/// The 1-norm of the vector, this one is the fastest length function we
/// can implement on modern cpu's
template<typename T>
inline
T
norm1(SGVec3<T> v)
{ v.simd3() = simd4::abs(v.simd3()); return (v(0)+v(1)+v(2)); }
norm1(const SGVec3<T>& v)
{ return fabs(v(0)) + fabs(v(1)) + fabs(v(2)); }
/// The inf-norm of the vector
template<typename T>
inline
T
normI(SGVec3<T> v)
{
v.simd3() = simd4::abs(v.simd3());
return SGMisc<T>::max(v(0), v(1), v(2));
}
normI(const SGVec3<T>& v)
{ return SGMisc<T>::max(fabs(v(0)), fabs(v(1)), fabs(v(2))); }
/// Vector cross product
template<typename T>
inline
SGVec3<T>
cross(SGVec3<T> v1, const SGVec3<T>& v2)
{ v1.simd3() = simd4::cross(v1.simd3(), v2.simd3()); return v1; }
cross(const SGVec3<T>& v1, const SGVec3<T>& v2)
{
return SGVec3<T>(v1(1)*v2(2) - v1(2)*v2(1),
v1(2)*v2(0) - v1(0)*v2(2),
v1(0)*v2(1) - v1(1)*v2(0));
}
/// return any normalized vector perpendicular to v
template<typename T>
@@ -451,14 +468,14 @@ template<typename T>
inline
T
dist(const SGVec3<T>& v1, const SGVec3<T>& v2)
{ return simd4::magnitude(v1.simd3() - v2.simd3()); }
{ return norm(v1 - v2); }
/// The squared euclidean distance of the two vectors
template<typename T>
inline
T
distSqr(SGVec3<T> v1, const SGVec3<T>& v2)
{ return simd4::magnitude2(v1.simd3() - v2.simd3()); }
distSqr(const SGVec3<T>& v1, const SGVec3<T>& v2)
{ SGVec3<T> tmp = v1 - v2; return dot(tmp, tmp); }
// calculate the projection of u along the direction of d.
template<typename T>
@@ -466,22 +483,12 @@ inline
SGVec3<T>
projection(const SGVec3<T>& u, const SGVec3<T>& d)
{
T denom = simd4::magnitude2(d.simd3());
T denom = dot(d, d);
T ud = dot(u, d);
if (SGLimits<T>::min() < denom) return u;
else return d * (dot(u, d) / denom);
}
template<typename T>
inline
SGVec3<T>
interpolate(T tau, const SGVec3<T>& v1, const SGVec3<T>& v2)
{
SGVec3<T> r;
r.simd3() = simd4::interpolate(tau, v1.simd3(), v2.simd3());
return r;
}
#ifndef NDEBUG
template<typename T>
inline
@@ -503,11 +510,11 @@ operator<<(std::basic_ostream<char_type, traits_type>& s, const SGVec3<T>& v)
inline
SGVec3f
toVec3f(const SGVec3d& v)
{ SGVec3f f(v); return f; }
{ return SGVec3f((float)v(0), (float)v(1), (float)v(2)); }
inline
SGVec3d
toVec3d(const SGVec3f& v)
{ SGVec3d d(v); return d; }
{ return SGVec3d(v(0), v(1), v(2)); }
#endif

View File

@@ -20,8 +20,6 @@
#include <iosfwd>
#include <simgear/math/simd.hxx>
/// 4D Vector Class
template<typename T>
class SGVec4 {
@@ -42,16 +40,16 @@ public:
}
/// Constructor. Initialize by the given values
SGVec4(T x, T y, T z, T w)
{ _data = simd4_t<T,4>(x, y, z, w); }
{ data()[0] = x; data()[1] = y; data()[2] = z; data()[3] = w; }
/// Constructor. Initialize by the content of a plain array,
/// make sure it has at least 3 elements
explicit SGVec4(const T* d)
{ _data = d ? simd4_t<T,4>(d) : simd4_t<T,4>(T(0)); }
{ data()[0] = d[0]; data()[1] = d[1]; data()[2] = d[2]; data()[3] = d[3]; }
template<typename S>
explicit SGVec4(const SGVec4<S>& d)
{ data()[0] = d[0]; data()[1] = d[1]; data()[2] = d[2]; data()[3] = d[3]; }
explicit SGVec4(const SGVec3<T>& v3, const T& v4 = 0)
{ _data = v3.simd3(); data()[3] = v4; }
{ data()[0] = v3[0]; data()[1] = v3[1]; data()[2] = v3[2]; data()[3] = v4; }
/// Access by index, the index is unchecked
const T& operator()(unsigned i) const
@@ -94,31 +92,25 @@ public:
/// Readonly raw storage interface
const T (&data(void) const)[4]
{ return _data.ptr(); }
/// Readonly raw storage interface
T (&data(void))[4]
{ return _data.ptr(); }
/// Readonly raw storage interface
const simd4_t<T,4> (&simd4(void) const)
{ return _data; }
/// Readonly raw storage interface
simd4_t<T,4> (&simd4(void))
T (&data(void))[4]
{ return _data; }
/// Inplace addition
SGVec4& operator+=(const SGVec4& v)
{ _data += v.simd4(); return *this; }
{ data()[0]+=v(0);data()[1]+=v(1);data()[2]+=v(2);data()[3]+=v(3);return *this; }
/// Inplace subtraction
SGVec4& operator-=(const SGVec4& v)
{ _data -= v.simd4(); return *this; }
{ data()[0]-=v(0);data()[1]-=v(1);data()[2]-=v(2);data()[3]-=v(3);return *this; }
/// Inplace scalar multiplication
template<typename S>
SGVec4& operator*=(S s)
{ _data *= s; return *this; }
{ data()[0] *= s; data()[1] *= s; data()[2] *= s; data()[3] *= s; return *this; }
/// Inplace scalar multiplication by 1/s
template<typename S>
SGVec4& operator/=(S s)
{ _data*=(1/T(s)); return *this; }
{ return operator*=(1/T(s)); }
/// Return an all zero vector
static SGVec4 zeros(void)
@@ -134,7 +126,7 @@ public:
{ return SGVec4(0, 0, 0, 1); }
private:
simd4_t<T,4> _data;
T _data[4];
};
/// Unary +, do nothing ...
@@ -148,36 +140,36 @@ operator+(const SGVec4<T>& v)
template<typename T>
inline
SGVec4<T>
operator-(SGVec4<T> v)
{ v *= -1; return v; }
operator-(const SGVec4<T>& v)
{ return SGVec4<T>(-v(0), -v(1), -v(2), -v(3)); }
/// Binary +
template<typename T>
inline
SGVec4<T>
operator+(SGVec4<T> v1, const SGVec4<T>& v2)
{ v1.simd4() += v2.simd4(); return v1; }
operator+(const SGVec4<T>& v1, const SGVec4<T>& v2)
{ return SGVec4<T>(v1(0)+v2(0), v1(1)+v2(1), v1(2)+v2(2), v1(3)+v2(3)); }
/// Binary -
template<typename T>
inline
SGVec4<T>
operator-(SGVec4<T> v1, const SGVec4<T>& v2)
{ v1.simd4() -= v2.simd4(); return v1; }
operator-(const SGVec4<T>& v1, const SGVec4<T>& v2)
{ return SGVec4<T>(v1(0)-v2(0), v1(1)-v2(1), v1(2)-v2(2), v1(3)-v2(3)); }
/// Scalar multiplication
template<typename S, typename T>
inline
SGVec4<T>
operator*(S s, SGVec4<T> v)
{ v.simd4() *= s; return v; }
operator*(S s, const SGVec4<T>& v)
{ return SGVec4<T>(s*v(0), s*v(1), s*v(2), s*v(3)); }
/// Scalar multiplication
template<typename S, typename T>
inline
SGVec4<T>
operator*(SGVec4<T> v, S s)
{ v.simd4() *= s; return v; }
operator*(const SGVec4<T>& v, S s)
{ return SGVec4<T>(s*v(0), s*v(1), s*v(2), s*v(3)); }
/// multiplication as a multiplicator, that is assume that the first vector
/// represents a 4x4 diagonal matrix with the diagonal elements in the vector.
@@ -185,42 +177,72 @@ operator*(SGVec4<T> v, S s)
template<typename T>
inline
SGVec4<T>
mult(SGVec4<T> v1, const SGVec4<T>& v2)
{ v1.simd4() *= v2.simd4(); return v1; }
mult(const SGVec4<T>& v1, const SGVec4<T>& v2)
{ return SGVec4<T>(v1(0)*v2(0), v1(1)*v2(1), v1(2)*v2(2), v1(3)*v2(3)); }
/// component wise min
template<typename T>
inline
SGVec4<T>
min(SGVec4<T> v1, const SGVec4<T>& v2)
{ v1.simd4() = simd4::min(v1.simd4(), v2.simd4()); return v1; }
min(const SGVec4<T>& v1, const SGVec4<T>& v2)
{
return SGVec4<T>(SGMisc<T>::min(v1(0), v2(0)),
SGMisc<T>::min(v1(1), v2(1)),
SGMisc<T>::min(v1(2), v2(2)),
SGMisc<T>::min(v1(3), v2(3)));
}
template<typename S, typename T>
inline
SGVec4<T>
min(SGVec4<T> v, S s)
{ v.simd4() = simd4::min(v.simd4(), simd4_t<T,4>(s)); return v; }
min(const SGVec4<T>& v, S s)
{
return SGVec4<T>(SGMisc<T>::min(s, v(0)),
SGMisc<T>::min(s, v(1)),
SGMisc<T>::min(s, v(2)),
SGMisc<T>::min(s, v(3)));
}
template<typename S, typename T>
inline
SGVec4<T>
min(S s, SGVec4<T> v)
{ v.simd4() = simd4::min(v.simd4(), simd4_t<T,4>(s)); return v; }
min(S s, const SGVec4<T>& v)
{
return SGVec4<T>(SGMisc<T>::min(s, v(0)),
SGMisc<T>::min(s, v(1)),
SGMisc<T>::min(s, v(2)),
SGMisc<T>::min(s, v(3)));
}
/// component wise max
template<typename T>
inline
SGVec4<T>
max(SGVec4<T> v1, const SGVec4<T>& v2)
{ v1.simd4() = simd4::max(v1.simd4(), v2.simd4()); return v1; }
max(const SGVec4<T>& v1, const SGVec4<T>& v2)
{
return SGVec4<T>(SGMisc<T>::max(v1(0), v2(0)),
SGMisc<T>::max(v1(1), v2(1)),
SGMisc<T>::max(v1(2), v2(2)),
SGMisc<T>::max(v1(3), v2(3)));
}
template<typename S, typename T>
inline
SGVec4<T>
max(SGVec4<T> v, S s)
{ v.simd4() = simd4::max(v.simd4(), simd4_t<T,4>(s)); return v; }
max(const SGVec4<T>& v, S s)
{
return SGVec4<T>(SGMisc<T>::max(s, v(0)),
SGMisc<T>::max(s, v(1)),
SGMisc<T>::max(s, v(2)),
SGMisc<T>::max(s, v(3)));
}
template<typename S, typename T>
inline
SGVec4<T>
max(S s, SGVec4<T> v)
{ v.simd4() = simd4::max(v.simd4(), simd4_t<T,4>(s)); return v; }
max(S s, const SGVec4<T>& v)
{
return SGVec4<T>(SGMisc<T>::max(s, v(0)),
SGMisc<T>::max(s, v(1)),
SGMisc<T>::max(s, v(2)),
SGMisc<T>::max(s, v(3)));
}
/// Add two vectors taking care of (integer) overflows. The values are limited
/// to the respective minimum and maximum values.
@@ -240,39 +262,36 @@ template<typename T>
inline
T
dot(const SGVec4<T>& v1, const SGVec4<T>& v2)
{ return simd4::dot(v1.simd4(), v2.simd4()); }
{ return v1(0)*v2(0) + v1(1)*v2(1) + v1(2)*v2(2) + v1(3)*v2(3); }
/// The euclidean norm of the vector, that is what most people call length
template<typename T>
inline
T
norm(const SGVec4<T>& v)
{ return simd4::magnitude(v.simd4()); }
{ return sqrt(dot(v, v)); }
/// The euclidean norm of the vector, that is what most people call length
template<typename T>
inline
T
length(const SGVec4<T>& v)
{ return simd4::magnitude(v.simd4()); }
{ return sqrt(dot(v, v)); }
/// The 1-norm of the vector, this one is the fastest length function we
/// can implement on modern cpu's
template<typename T>
inline
T
norm1(SGVec4<T> v)
{ v.simd4() = simd4::abs(v.simd4()); return (v(0)+v(1)+v(2)+v(3)); }
norm1(const SGVec4<T>& v)
{ return fabs(v(0)) + fabs(v(1)) + fabs(v(2)) + fabs(v(3)); }
/// The inf-norm of the vector
template<typename T>
inline
T
normI(SGVec4<T> v)
{
v.simd4() = simd4::abs(v.simd4());
return SGMisc<T>::max(v(0), v(1), v(2), v(3));
}
normI(const SGVec4<T>& v)
{ return SGMisc<T>::max(fabs(v(0)), fabs(v(1)), fabs(v(2)), fabs(v(2))); }
/// The euclidean norm of the vector, that is what most people call length
template<typename T>
@@ -370,14 +389,14 @@ template<typename T>
inline
T
dist(const SGVec4<T>& v1, const SGVec4<T>& v2)
{ return simd4::magnitude(v1.simd4() - v2.simd4()); }
{ return norm(v1 - v2); }
/// The squared euclidean distance of the two vectors
template<typename T>
inline
T
distSqr(SGVec4<T> v1, const SGVec4<T>& v2)
{ return simd4::magnitude2(v1.simd4() - v2.simd4()); }
distSqr(const SGVec4<T>& v1, const SGVec4<T>& v2)
{ SGVec4<T> tmp = v1 - v2; return dot(tmp, tmp); }
// calculate the projection of u along the direction of d.
template<typename T>
@@ -385,22 +404,12 @@ inline
SGVec4<T>
projection(const SGVec4<T>& u, const SGVec4<T>& d)
{
T denom = simd4::magnitude2(d.simd4());
T denom = dot(d, d);
T ud = dot(u, d);
if (SGLimits<T>::min() < denom) return u;
else return d * (dot(u, d) / denom);
}
template<typename T>
inline
SGVec4<T>
interpolate(T tau, const SGVec4<T>& v1, const SGVec4<T>& v2)
{
SGVec4<T> r;
r.simd4() = simd4::interpolate(tau, v1.simd4(), v2.simd4());
return r;
}
#ifndef NDEBUG
template<typename T>
inline
@@ -422,11 +431,11 @@ operator<<(std::basic_ostream<char_type, traits_type>& s, const SGVec4<T>& v)
inline
SGVec4f
toVec4f(const SGVec4d& v)
{ SGVec4f f(v); return f; }
{ return SGVec4f((float)v(0), (float)v(1), (float)v(2), (float)v(3)); }
inline
SGVec4d
toVec4d(const SGVec4f& v)
{ SGVec4d d(v); return d; }
{ return SGVec4d(v(0), v(1), v(2), v(3)); }
#endif

View File

@@ -31,7 +31,7 @@
#include <string>
#include <simgear/debug/logstream.hxx>
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/misc/sgstream.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/props/props.hxx>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,706 +0,0 @@
// Copyright (C) 2016 Erik Hofman - erik@ehofman.com
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef __SIMD4X4_NEON_H__
#define __SIMD4X4_NEON_H__ 1
#include "simd_neon.hxx"
#ifdef __ARM_NEON__
template<>
class simd4x4_t<float,4>
{
private:
typedef float __mtx4f_t[4][4];
union alignas(16) {
float32x4_t simd4x4[4];
__mtx4f_t mtx;
float array[4*4];
}g;
public:
simd4x4_t(void) {}
simd4x4_t(float m00, float m01, float m02, float m03,
float m10, float m11, float m12, float m13,
float m20, float m21, float m22, float m23,
float m30, float m31, float m32, float m33)
{
array[0] = m00; array[1] = m10;
array[2] = m20; array[3] = m30;
array[4] = m01; array[5] = m11;
array[6] = m21; array[7] = m31;
array[8] = m02; array[9] = m12;
array[10] = m22; array[11] = m32;
array[12] = m03; array[13] = m13;
array[14] = m23; array[15] = m33;
}
simd4x4_t(const float m[4*4]) {
for (int i=0; i<4; ++i) {
simd4x4[i] = simd4_t<float,4>((const float*)&m[4*i]).v4();
}
}
explicit simd4x4_t(const __mtx4f_t m) {
for (int i=0; i<4; ++i) {
simd4x4[i] = simd4_t<float,4>(m[i]).v4();
}
}
simd4x4_t(const simd4x4_t<float,4>& m) {
for (int i=0; i<4; ++i) {
simd4x4[i] = m.m4x4()[i];
}
}
~simd4x4_t(void) {}
inline float32x4_t (&m4x4(void))[4] {
return simd4x4;
}
inline const float32x4_t (&m4x4(void) const)[4] {
return simd4x4;
}
inline const float (&ptr(void) const)[4][4] {
return mtx;
}
inline float (&ptr(void))[4][4] {
return mtx;
}
inline operator const float*(void) const {
return array;
}
inline operator float*(void) {
return array;
}
template<int M>
inline void set(int i, const simd4_t<float,M>& v) {
simd4x4[i] = v.v4();
}
inline simd4x4_t<float,4>& operator=(const __mtx4f_t m) {
for (int i=0; i<4; ++i) {
simd4x4[i] = simd4_t<float,4>(m[i]).v4();
}
return *this;
}
inline simd4x4_t<float,4>& operator=(const simd4x4_t<float,4>& m) {
for (int i=0; i<4; ++i) {
simd4x4[i] = m.m4x4()[i];
}
return *this;
}
inline simd4x4_t<float,4>& operator+=(const simd4x4_t<float,4>& m) {
for (int i=0; i<4; ++i) {
simd4x4[i] = vaddq_f32(simd4x4[i], m.m4x4()[i]);
}
return *this;
}
inline simd4x4_t<float,4>& operator-=(const simd4x4_t<float,4>& m) {
for (int i=0; i<4; ++i) {
simd4x4[i] = vsubq_f32(simd4x4[i], m.m4x4()[i]);
}
return *this;
}
inline simd4x4_t<float,4>& operator*=(float f) {
simd4_t<float,4> f4(f);
for (int i=0; i<4; ++i) {
simd4x4[i] = vmulq_f32(simd4x4[i], f4.v4());
}
return *this;
}
simd4x4_t<float,4>& operator*=(const simd4x4_t<float,4>& m2) {
simd4x4_t<float,4> m1 = *this;
float32x4_t row, col;
for (int i=0; i<4; ++i) {
col = vdupq_n_f32(m2.ptr()[i][0]);
row = vmulq_f32(m1.m4x4()[0], col);
for (int j=1; j<4; ++j) {
col = vdupq_n_f32(m2.ptr()[i][j]);
row = vaddq_f32(row, vmulq_f32(m1.m4x4()[j], col));
}
simd4x4[i] = row;
}
return *this;
}
};
template<int M>
inline simd4_t<float,M> operator*(const simd4x4_t<float,4>& m, const simd4_t<float,M>& vi)
{
float32x4_t mv = vmulq_f32(m.m4x4()[0], vdupq_n_f32(vi.ptr()[0]));
for (int i=1; i<M; ++i) {
float32x4_t row = vmulq_f32(m.m4x4()[i], vdupq_n_f32(vi.ptr()[i]));
mv = vaddq_f32(mv, row);
}
return mv;
}
namespace simd4x4
{
template<>
inline simd4x4_t<float,4> rotation_matrix<float>(float angle, const simd4_t<float,3>& axis)
{
float s = std::sin(angle), c = std::cos(angle), t = 1.0-c;
simd4_t<float,3> axt, at = axis*t, as = axis*s;
simd4x4_t<float,4> m;
simd4x4::unit(m);
axt = axis.ptr()[0]*at;
m.m4x4()[0] = axt.v4();
axt = axis.ptr()[1]*at;
m.ptr()[0][0] += c;
m.ptr()[0][1] += as.ptr()[2];
m.ptr()[0][2] -= as.ptr()[1];
m.m4x4()[1] = axt.v4();
axt = axis.ptr()[2]*at;
m.ptr()[1][0] -= as.ptr()[2];
m.ptr()[1][1] += c;
m.ptr()[1][2] += as.ptr()[0];
m.m4x4()[2] = axt.v4();
m.ptr()[2][0] += as.ptr()[1];
m.ptr()[2][1] -= as.ptr()[0];
m.ptr()[2][2] += c;
return m;
}
template<>
inline simd4x4_t<float,4> transpose<float>(simd4x4_t<float,4> m) {
// http://clb.demon.fi/MathGeoLib/nightly/docs/float4x4_neon.h_code.html
float32x4x4_t x = vld4q_f32(m);
vst1q_f32(&m[0], x.val[0]);
vst1q_f32(&m[4], x.val[1]);
vst1q_f32(&m[8], x.val[2]);
vst1q_f32(&m[12], x.val[3]);
return m;
}
inline void translate(simd4x4_t<float,4>& m, const simd4_t<float,3>& dist) {
m.m4x4()[3] = vsubq_f32(m.m4x4()[3], dist.v4());
}
template<typename S>
inline void pre_translate(simd4x4_t<float,4>& m, const simd4_t<S,3>& dist)
{
simd4x4_t<float,4> mt = simd4x4::transpose(m);
float32x4_t row3 = mt.m4x4()[3];
for (int i=0; i<3; ++i) {
float32x4_t t = vdupq_n_f32(float(dist[i]));
mt.m4x4()[i] = vaddq_f32(mt.m4x4()[i], vmulq_f32(t, row3));
}
m = simd4x4::transpose(mt);
}
template<typename S>
inline void post_translate(simd4x4_t<float,4>& m, const simd4_t<S,3>& dist)
{
float32x4_t col3 = m.m4x4()[3];
for (int i=0; i<3; ++i) {
float32x4_t t = vdupq_n_f32(float(dist[i]));
col3 = vaddq_f32(col3, vmulq_f32(t, m.m4x4()[i]));
}
m.m4x4()[3] = col3;
}
template<>
inline simd4_t<float,3> transform<float>(const simd4x4_t<float,4>& m, const simd4_t<float,3>& pt) {
float32x4_t tpt = m.m4x4()[3];
for (int i=0; i<3; ++i) {
float32x4_t ptd = vdupq_n_f32(pt[i]);
tpt = vaddq_f32(tpt, vmulq_f32(ptd, m.m4x4()[i]));
}
vsetq_lane_f32(0.0f, tpt, 3);
return tpt;
}
} /* namespace simd4x */
#if 0
template<>
class simd4x4_t<double,4>
{
private:
typedef double __mtx4d_t[4][4];
union alignas(32) {
__m256d simd4x4[4];
__mtx4d_t mtx;
double array[4*4];
};
public:
simd4x4_t(void) {}
simd4x4_t(double m00, double m01, double m02, double m03,
double m10, double m11, double m12, double m13,
double m20, double m21, double m22, double m23,
double m30, double m31, double m32, double m33)
{
simd4x4[0] = _mm256_set_pd(m30,m20,m10,m00);
simd4x4[1] = _mm256_set_pd(m31,m21,m11,m01);
simd4x4[2] = _mm256_set_pd(m32,m22,m12,m02);
simd4x4[3] = _mm256_set_pd(m33,m23,m13,m03);
}
explicit simd4x4_t(const double m[4*4]) {
for (int i=0; i<4; ++i) {
simd4x4[i] = simd4_t<double,4>((const double*)&m[4*i]).v4();
}
}
explicit simd4x4_t(const __mtx4d_t m) {
for (int i=0; i<4; ++i) {
simd4x4[i] = simd4_t<double,4>(m[i]).v4();
}
}
simd4x4_t(const simd4x4_t<double,4>& m) {
for (int i=0; i<4; ++i) {
simd4x4[i] = m.m4x4()[i];
}
}
~simd4x4_t(void) {}
inline __m256d (&m4x4(void))[4] {
return simd4x4;
}
inline const __m256d (&m4x4(void) const)[4] {
return simd4x4;
}
inline const double (&ptr(void) const)[4][4] {
return mtx;
}
inline double (&ptr(void))[4][4] {
return mtx;
}
inline operator const double*(void) const {
return array;
}
inline operator double*(void) {
return array;
}
template<int M>
inline void set(int i, const simd4_t<double,M>& v) {
simd4x4[i] = v.v4();
}
inline simd4x4_t<double,4>& operator=(const __mtx4d_t m) {
for (int i=0; i<4; ++i) {
simd4x4[i] = simd4_t<double,4>(m[i]).v4();
}
return *this;
}
inline simd4x4_t<double,4>& operator=(const simd4x4_t<double,4>& m) {
for (int i=0; i<4; ++i) {
simd4x4[i] = m.m4x4()[i];
}
return *this;
}
inline simd4x4_t<double,4>& operator+=(const simd4x4_t<double,4>& m) {
for (int i=0; i<4; ++i) {
simd4x4[i] = _mm256_add_pd(simd4x4[i], m.m4x4()[i]);
}
return *this;
}
inline simd4x4_t<double,4>& operator-=(const simd4x4_t<double,4>& m) {
for (int i=0; i<4; ++i) {
simd4x4[i] = _mm256_sub_pd(simd4x4[i], m.m4x4()[i]);
}
return *this;
}
inline simd4x4_t<double,4>& operator*=(double f) {
simd4_t<double,4> f4(f);
for (int i=0; i<4; ++i) {
simd4x4[i] = _mm256_mul_pd(simd4x4[i], f4.v4());
}
return *this;
}
simd4x4_t<double,4>& operator*=(const simd4x4_t<double,4>& m2) {
simd4x4_t<double,4> m1 = *this;
__m256d row, col;
for (int i=0; i<4; ++i ) {
col = _mm256_set1_pd(m2.ptr()[i][0]);
row = _mm256_mul_pd(m1.m4x4()[0], col);
for (int j=1; j<4; ++j) {
col = _mm256_set1_pd(m2.ptr()[i][j]);
row = _mm256_add_pd(row, _mm256_mul_pd(m1.m4x4()[j], col));
}
simd4x4[i] = row;
}
return *this;
}
};
template<int M>
inline simd4_t<double,M> operator*(const simd4x4_t<double,4>& m, const simd4_t<double,M>& vi)
{
__m256d mv = _mm256_mul_pd(m.m4x4()[0], _mm256_set1_pd(vi.ptr()[0]));
for (int i=1; i<M; ++i) {
__m256d row = _mm256_mul_pd(m.m4x4()[i], _mm256_set1_pd(vi.ptr()[i]));
mv = _mm256_add_pd(mv, row);
}
return mv;
}
namespace simd4x4
{
template<>
inline simd4x4_t<double,4> rotation_matrix<double>(double angle, const simd4_t<double,3>& axis)
{
double s = std::sin(angle), c = std::cos(angle), t = 1.0-c;
simd4_t<double,3> axt, at = axis*t, as = axis*s;
simd4x4_t<double,4> m;
simd4x4::unit(m);
axt = axis.ptr()[0]*at;
m.m4x4()[0] = axt.v4();
axt = axis.ptr()[1]*at;
m.ptr()[0][0] += c;
m.ptr()[0][1] += as.ptr()[2];
m.ptr()[0][2] -= as.ptr()[1];
m.m4x4()[1] = axt.v4();
axt = axis.ptr()[2]*at;
m.ptr()[1][0] -= as.ptr()[2];
m.ptr()[1][1] += c;
m.ptr()[1][2] += as.ptr()[0];
m.m4x4()[2] = axt.v4();
m.ptr()[2][0] += as.ptr()[1];
m.ptr()[2][1] -= as.ptr()[0];
m.ptr()[2][2] += c;
return m;
}
template<>
inline simd4x4_t<double,4> transpose<double>(simd4x4_t<double,4> m) {
// http://stackoverflow.com/questions/36167517/m256d-transpose4-equivalent
__m256d tmp0 = _mm256_shuffle_pd(m.m4x4()[0], m.m4x4()[1], 0x0);
__m256d tmp2 = _mm256_shuffle_pd(m.m4x4()[0], m.m4x4()[1], 0xF);
__m256d tmp1 = _mm256_shuffle_pd(m.m4x4()[2], m.m4x4()[3], 0x0);
__m256d tmp3 = _mm256_shuffle_pd(m.m4x4()[2], m.m4x4()[3], 0xF);
m.m4x4()[0] = _mm256_permute2f128_pd(tmp0, tmp1, 0x20);
m.m4x4()[1] = _mm256_permute2f128_pd(tmp2, tmp3, 0x20);
m.m4x4()[2] = _mm256_permute2f128_pd(tmp0, tmp1, 0x31);
m.m4x4()[3] = _mm256_permute2f128_pd(tmp2, tmp3, 0x31);
return m;
}
inline void translate(simd4x4_t<double,4>& m, const simd4_t<double,3>& dist) {
m.m4x4()[3] = _mm256_sub_pd(m.m4x4()[3], dist.v4());
}
template<typename S>
inline void pre_translate(simd4x4_t<double,4>& m, const simd4_t<S,3>& dist)
{
simd4_t<double,4> row3(m.ptr()[0][3],m.ptr()[1][3],m.ptr()[2][3],m.ptr()[3][3]);
for (int i=0; i<3; ++i) {
for (int j=0; j<4; ++j) {
m.ptr()[j][i] += row3[j]*double(dist[i]);
}
}
#if 0
// this is slower
simd4x4_t<double,4> mt = simd4x4::transpose(m);
__mm256d row3 = mt.m4x4()[3];
for (int i=0; i<3; ++i) {
__mm256d _mm256_set1_pd(float(dist[i]));
mt.m4x4()[i] = _mm256_add_pd(mt.m4x4()[i], _mm256_mul_pd(t, row3));
}
m = simd4x4::transpose(mt);
#endif
}
template<typename S>
inline void post_translate(simd4x4_t<double,4>& m, const simd4_t<S,3>& dist) {
__m256d col3 = m.m4x4()[3];
for (int i=0; i<3; ++i) {
__m256d t = _mm256_set1_pd(double(dist[i]));
col3 = _mm256_add_pd(col3, _mm256_mul_pd(t, m.m4x4()[i]));
}
m.m4x4()[3] = col3;
}
template<>
inline simd4_t<double,3> transform<double>(const simd4x4_t<double,4>& m, const simd4_t<double,3>& pt) {
simd4_t<double,3> res;
__m256d tpt = m.m4x4()[3];
for (int i=0; i<3; ++i) {
__m256d ptd = _mm256_set1_pd(pt[i]);
tpt = _mm256_add_pd(tpt, _mm256_mul_pd(ptd, m.m4x4()[i]));
}
res = tpt;
res[3] = 0.0;
return res;
}
} /* namespace simd4x4 */
# endif
template<>
class simd4x4_t<int,4>
{
private:
typedef int __mtx4i_t[4][4];
union alignas(16) {
int32x4_t simd4x4[4];
__mtx4i_t mtx;
int array[4*4];
}g;
public:
simd4x4_t(void) {}
simd4x4_t(int m00, int m01, int m02, int m03,
int m10, int m11, int m12, int m13,
int m20, int m21, int m22, int m23,
int m30, int m31, int m32, int m33)
{
array[0] = m00; array[1] = m10;
array[2] = m20; array[3] = m30;
array[4] = m01; array[5] = m11;
array[6] = m21; array[7] = m31;
array[8] = m02; array[9] = m12;
array[10] = m22; array[11] = m32;
array[12] = m03; array[13] = m13;
array[14] = m23; array[15] = m33;
}
simd4x4_t(const int m[4*4]) {
for (int i=0; i<4; ++i) {
simd4x4[i] = simd4_t<int,4>((const int*)&m[4*i]).v4();
}
}
explicit simd4x4_t(const __mtx4i_t m) {
for (int i=0; i<4; ++i) {
simd4x4[i] = simd4_t<int,4>(m[i]).v4();
}
}
simd4x4_t(const simd4x4_t<int,4>& m) {
for (int i=0; i<4; ++i) {
simd4x4[i] = m.m4x4()[i];
}
}
~simd4x4_t(void) {}
inline int32x4_t (&m4x4(void))[4] {
return simd4x4;
}
inline const int32x4_t (&m4x4(void) const)[4] {
return simd4x4;
}
inline const int (&ptr(void) const)[4][4] {
return mtx;
}
inline int (&ptr(void))[4][4] {
return mtx;
}
inline operator const int*(void) const {
return array;
}
inline operator int*(void) {
return array;
}
template<int M>
inline void set(int i, const simd4_t<int,M>& v) {
simd4x4[i] = v.v4();
}
inline simd4x4_t<int,4>& operator=(const __mtx4i_t m) {
for (int i=0; i<4; ++i) {
simd4x4[i] = simd4_t<int,4>(m[i]).v4();
}
return *this;
}
inline simd4x4_t<int,4>& operator=(const simd4x4_t<int,4>& m) {
for (int i=0; i<4; ++i) {
simd4x4[i] = m.m4x4()[i];
}
return *this;
}
inline simd4x4_t<int,4>& operator+=(const simd4x4_t<int,4>& m) {
for (int i=0; i<4; ++i) {
simd4x4[i] += m.m4x4()[i];
}
return *this;
}
inline simd4x4_t<int,4>& operator-=(const simd4x4_t<int,4>& m) {
for (int i=0; i<4; ++i) {
simd4x4[i] -= m.m4x4()[i];
}
return *this;
}
inline simd4x4_t<int,4>& operator*=(int f) {
simd4_t<int,4> f4(f);
for (int i=0; i<4; ++i) {
simd4x4[i] *= f4.v4();
}
return *this;
}
simd4x4_t<int,4>& operator*=(const simd4x4_t<int,4>& m2) {
simd4x4_t<int,4> m1 = *this;
simd4_t<int,4> row, col;
for (int i=0; i<4; ++i) {
simd4_t<int,4> col(m2.ptr()[i][0]);
row.v4() = m1.m4x4()[0] * col.v4();
for (int j=1; j<4; ++j) {
simd4_t<int,4> col(m2.ptr()[i][j]);
row.v4() += m1.m4x4()[j] * col.v4();
}
simd4x4[i] = row.v4();
}
return *this;
}
};
template<int M>
inline simd4_t<int,M> operator*(const simd4x4_t<int,4>& m, const simd4_t<int,M>& vi)
{
simd4_t<int,M> mv(m.m4x4()[0]);
mv *= vi.ptr()[0];
for (int i=1; i<M; ++i) {
simd4_t<int,4> row(m.m4x4()[i]);
row *= vi.ptr()[i];
mv.v4() += row.v4();
}
for (int i=M; i<4; ++i) mv[i] = 0;
return mv;
}
template<>
inline simd4x4_t<int,4> operator*(const simd4x4_t<int,4>& m1, const simd4x4_t<int,4>& m2)
{
simd4_t<int,4> row, col;
simd4x4_t<int,4> m;
for (int i=0; i<4; ++i) {
simd4_t<int,4> col(m2.ptr()[i][0]);
row.v4() = m1.m4x4()[0] * col.v4();
for (int j=1; j<4; ++j) {
simd4_t<int,4> col(m2.ptr()[i][j]);
row.v4() += m1.m4x4()[j] * col.v4();
}
m.m4x4()[i] = row.v4();
}
return m;
}
namespace simd4x4
{
template<>
inline simd4x4_t<int,4> transpose<int>(simd4x4_t<int,4> m) {
int32x4x4_t x = vld4q_s32(m);
vst1q_s32(&m[0], x.val[0]);
vst1q_s32(&m[4], x.val[1]);
vst1q_s32(&m[8], x.val[2]);
vst1q_s32(&m[12], x.val[3]);
return m;
}
inline void translate(simd4x4_t<int,4>& m, const simd4_t<int,3>& dist) {
m.m4x4()[3] = vsubq_s32(m.m4x4()[3], dist.v4());
}
template<typename S>
inline void pre_translate(simd4x4_t<int,4>& m, const simd4_t<S,3>& dist)
{
simd4x4_t<int,4> mt = simd4x4::transpose(m);
simd4_t<int,4> row3(mt.ptr()[3]);
for (int i=0; i<3; ++i) {
simd4_t<int,4> trow3 = int(dist[i]);
trow3 *= row3.v4();
mt.m4x4()[i] = vaddq_s32(mt.m4x4()[i], trow3.v4());
}
m = simd4x4::transpose(mt);
}
template<typename S>
inline void post_translate(simd4x4_t<int,4>& m, const simd4_t<S,3>& dist)
{
int32x4_t col3 = m.m4x4()[3];
for (int i=0; i<3; ++i) {
simd4_t<int,4> trow3 = int(dist[i]);
trow3 *= m.m4x4()[i];
col3 = vaddq_s32(col3, trow3.v4());
}
m.m4x4()[3] = col3;
}
template<>
inline simd4_t<int,3> transform<int>(const simd4x4_t<int,4>& m, const simd4_t<int,3>& pt) {
simd4_t<int,3> tpt = m.m4x4()[3];
for (int i=0; i<3; ++i) {
simd4_t<int,3> ptd = m.m4x4()[i];
ptd *= pt[i];
tpt.v4() = vaddq_s32(tpt.v4(), ptd.v4());
}
tpt[3] = 0.0;
return tpt;
}
} /* namespace simd4x */
#endif
#endif /* __SIMD4X4_NEON_H__ */

View File

@@ -1,545 +0,0 @@
// Copyright (C) 2016 Erik Hofman - erik@ehofman.com
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef __SIMD_NEON_H__
#define __SIMD_NEON_H__ 1
#ifdef __ARM_NEON__
static const uint32_t m2a32[] alignas(16) = { 0xffffffff,0xffffffff,0,0 };
static const uint32_t m3a32[] alignas(16) = { 0xffffffff,0xffffffff,0xffffffff,0 };
template<int N>
class simd4_t<float,N>
{
private:
typedef float __vec4f_t[N];
union alignas(16) {
float32x4_t simd4;
float32x2x2_t simd2x2;
__vec4f_t vec;
};
public:
simd4_t(void) {}
simd4_t(float f) {}
simd4_t(float x, float y) : simd4_t(x,y,0,0) {}
simd4_t(float x, float y, float z) : simd4_t(x,y,z,0) {}
simd4_t(float x, float y, float z, float w) {
alignas(16) float data[4] = { x, y, z, w };
simd4 = vld1q_f32(data);
}
simd4_t(const __vec4f_t v) {}
template<int M>
simd4_t(const simd4_t<float,M>& v) {
simd4 = v.v4();
}
simd4_t(const float32x4_t& v) {
simd4 = v;
}
inline const float32x2x2_t (&v2x2(void) const) {
return simd2x2;
}
inline float32x2x2_t (&v2x2(void)) {
return simd2x2;
}
inline const float32x4_t (&v4(void) const) {
return simd4;
}
inline float32x4_t (&v4(void)) {
return simd4;
}
inline const float (&ptr(void) const)[N] {
return vec;
}
inline float (&ptr(void))[N] {
return vec;
}
inline operator const float*(void) const {
return vec;
}
inline operator float*(void) {
return vec;
}
inline simd4_t<float,N>& operator+=(float f) {
return operator+=(simd4_t<float,N>(f));
}
template<int M>
inline simd4_t<float,N>& operator+=(const simd4_t<float,M>& v) {
simd4 = vaddq_f32(simd4, v.v4());
return *this;
}
inline simd4_t<float,N>& operator-=(float f) {
return operator-=(simd4_t<float,N>(f));
}
template<int M>
inline simd4_t<float,N>& operator-=(const simd4_t<float,M>& v) {
simd4 = vsubq_f32(simd4, v.v4());
return *this;
}
inline simd4_t<float,N>& operator*=(float f) {
return operator*=(simd4_t<float,N>(f));
}
template<int M>
inline simd4_t<float,N>& operator*=(const simd4_t<float,M>& v) {
simd4 = vmulq_f32(simd4, v.v4());
return *this;
}
inline simd4_t<float,N>& operator/=(float f) {
return operator/=(simd4_t<float,4>(f));
}
template<int M>
inline simd4_t<float,N>& operator/=(const simd4_t<float,M>& v) {
// http://stackoverflow.com/questions/6759897/how-to-divide-in-neon-intrinsics-by-a-float-number
float32x4_t recip = vrecpeq_f32(v.v4());
recip = vmulq_f32(vrecpsq_f32(v.v4(), recip), recip);
recip = vmulq_f32(vrecpsq_f32(v.v4(), recip), recip);
recip = vmulq_f32(vrecpsq_f32(v.v4(), recip), recip);
simd4 = vmulq_f32(simd4, recip);
return *this;
}
};
# define vandq_f32(a,b) vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(a), vreinterpretq_u32_f32(b)))
static const float32x4_t fmask2 = vld1q_f32((const float*)m2a32);
static const float32x4_t fmask3 = vld1q_f32((const float*)m3a32);
template<>
inline simd4_t<float,4>::simd4_t(const __vec4f_t v) {
simd4 = vld1q_f32(v);
}
template<>
inline simd4_t<float,3>::simd4_t(const __vec4f_t v) {
simd4 = vandq_f32(vld1q_f32(v), fmask3);
}
template<>
inline simd4_t<float,2>::simd4_t(const __vec4f_t v) {
simd4 = vandq_f32(vld1q_f32(v), fmask2);
}
template<>
inline simd4_t<float,4>::simd4_t(float f) {
simd4 = vdupq_n_f32(f);
}
template<>
inline simd4_t<float,3>::simd4_t(float f) {
simd4 = vandq_f32(vdupq_n_f32(f), fmask3);
}
template<>
inline simd4_t<float,2>::simd4_t(float f) {
simd4 = vandq_f32(vdupq_n_f32(f), fmask2);
}
namespace simd4
{
// http://stackoverflow.com/questions/6931217/sum-all-elements-in-a-quadword-vector-in-arm-assembly-with-neon
inline static float hsum_float32x4_neon(float32x4_t v) {
float32x2_t r = vadd_f32(vget_high_f32(v), vget_low_f32(v));
return vget_lane_f32(vpadd_f32(r, r), 0);
}
template<>
inline float magnitude2(simd4_t<float,4> v) {
return hsum_float32x4_neon(v.v4()*v.v4());
}
template<>
inline float dot(simd4_t<float,4> v1, const simd4_t<float,4>& v2) {
return hsum_float32x4_neon(v1.v4()*v2.v4());
}
template<>
inline simd4_t<float,3> cross(const simd4_t<float,3>& v1, const simd4_t<float,3>& v2)
{
// from 0123 to 0213
float32x2x2_t v1lh_2013 = vzip_f32(v1.v2x2().val[0], v1.v2x2().val[1]);
float32x2x2_t v2lh_2013 = vzip_f32(v2.v2x2().val[0], v2.v2x2().val[1]);
// from 0213 to 2013
v1lh_2013.val[0] = vrev64_f32(v1lh_2013.val[0]);
v2lh_2013.val[0] = vrev64_f32(v2lh_2013.val[0]);
float32x4_t v1_2013 = vcombine_f32(v1lh_2013.val[0], v1lh_2013.val[1]);
float32x4_t v2_2013 = vcombine_f32(v2lh_2013.val[0], v2lh_2013.val[1]);
// from 2013 to 2103
float32x2x2_t v1lh_1203 = vzip_f32(v1lh_2013.val[0], v1lh_2013.val[1]);
float32x2x2_t v2lh_1203 = vzip_f32(v2lh_2013.val[0], v2lh_2013.val[1]);
// from 2103 to 1203
v1lh_1203.val[0] = vrev64_f32(v1lh_1203.val[0]);
v2lh_1203.val[0] = vrev64_f32(v2lh_1203.val[0]);
float32x4_t v1_1203 = vcombine_f32(v1lh_1203.val[0], v1lh_1203.val[1]);
float32x4_t v2_1203 = vcombine_f32(v2lh_1203.val[0], v2lh_1203.val[1]);
// calculate the cross product
return vsubq_f32(vmulq_f32(v1_1203,v2_2013),vmulq_f32(v1_2013,v2_1203));
}
template<int N>
inline simd4_t<float,N> min(simd4_t<float,N> v1, const simd4_t<float,N>& v2) {
v1 = vminq_f32(v1.v4(), v2.v4());
return v1;
}
template<int N>
inline simd4_t<float,N> max(simd4_t<float,N> v1, const simd4_t<float,N>& v2) {
v1 = vmaxq_f32(v1.v4(), v2.v4());
return v1;
}
template<int N>
inline simd4_t<float,N>abs(simd4_t<float,N> v) {
return vabsq_f32(v.v4());
}
} /* namsepace simd4 */
// TODO: 64-bit support for doubles
#if 0
template<int N>
class simd4_t<double,N>
{
private:
typedef double __vec4d_t[N];
union alignas(32) {
__m256d simd4;
__vec4d_t vec;
};
public:
simd4_t(void) {}
simd4_t(double d) {}
simd4_t(double x, double y) : simd4_t(x,y,0,0) {}
simd4_t(double x, double y, double z) : simd4_t(x,y,z,0) {}
simd4_t(double x, double y, double z, double w) {
simd4 = _mm256_set_pd(w,z,y,x);
}
simd4_t(const __vec4d_t v) { {}
template<int M>
simd4_t(const simd4_t<double,M>& v) {
simd4 = v.v4();
}
simd4_t(const __m256d& v) {
simd4 = v;
}
inline const __m256d (&v4(void) const) {
return simd4;
}
inline __m256d (&v4(void)) {
return simd4;
}
inline const double (&ptr(void) const)[N] {
return vec;
}
inline double (&ptr(void))[N] {
return vec;
}
inline operator const double*(void) const {
return vec;
}
inline operator double*(void) {
return vec;
}
inline simd4_t<double,N>& operator+=(double d) {
return operator+=(simd4_t<double,N>(d));
}
template<int M>
inline simd4_t<double,N>& operator+=(const simd4_t<double,M>& v) {
simd4 = _mm256_add_pd(simd4, v.v4());
return *this;
}
inline simd4_t<double,N>& operator-=(double d) {
return operator-=(simd4_t<double,N>(d));
}
template<int M>
inline simd4_t<double,N>& operator-=(const simd4_t<double,M>& v) {
simd4 = _mm256_sub_pd(simd4, v.v4());
return *this;
}
inline simd4_t<double,N>& operator*=(double d) {
return operator*=(simd4_t<double,N>(d));
}
template<int M>
inline simd4_t<double,N>& operator*=(const simd4_t<double,M>& v) {
simd4 = _mm256_mul_pd(simd4, v.v4());
return *this;
}
inline simd4_t<double,N>& operator/=(double d) {
return operator/=(simd4_t<double,4>(d));
}
template<int M>
inline simd4_t<double,N>& operator/=(const simd4_t<double,M>& v) {
simd4 = _mm256_div_pd(simd4, v.v4());
return *this;
}
};
static const __m256d dmask2 = _mm256_load_pd((const double*)m2a64);
static const __m256d dmask3 = _mm256_load_pd((const double*)m3a64);
template<>
inline simd4_t<double,4>::simd4_t(const __vec4d_t v) {
simd4 = _mm256_loadu_pd(v);
}
template<>
inline simd4_t<double,3>::simd4_t(const __vec4d_t v) {
simd4 = _mm256_and_pd(_mm256_loadu_pd(v), dmask3);
}
template<>
inline simd4_t<double,2>::simd4_t(const __vec4d_t v) {
simd4 = _mm256_and_pd(_mm256_loadu_pd(v), dmask2);
}
template<>
inline simd4_t<double,4>::simd4_t(double d) {
simd4 = _mm256_set1_pd(d);
}
template<>
inline simd4_t<double,3>::simd4_t(double d) {
simd4 = _mm256_and_pd(_mm256_set1_pd(d), dmask3);
}
template<>
inline simd4_t<double,2>::simd4_t(double d) {
simd4 = _mm256_and_pd(_mm256_set1_pd(d), dmask2);
}
namespace simd4
{
// http://berenger.eu/blog/sseavxsimd-horizontal-sum-sum-simd-vector-intrinsic/
inline static float hsum_pd_avx(__m256d v) {
const float64x4_t valupper = _mm256_extractf128_pd(v, 1);
const float64x4_t vallower = _mm256_castpd256_pd128(v);
_mm256_zeroupper();
const float64x4_t valval = _mm_add_pd(valupper, vallower);
const float64x4_t sums = _mm_add_pd(_mm_permute_pd(valval,1), valval);
return _mm_cvtsd_f64(sums);
}
template<>
inline double magnitude2(simd4_t<double,4> v) {
return hsum_pd_avx(_mm256_mul_pd(v.v4(),v.v4()));
}
template<>
inline double dot(simd4_t<double,4> v1, const simd4_t<double,4>& v2) {
return hsum_pd_avx(_mm256_mul_pd(v1.v4(),v2.v4()));
}
template<>
inline simd4_t<double,3> cross(const simd4_t<double,3>& v1, const simd4_t<double,3>& v2)
{
// https://gist.github.com/L2Program/219e07581e69110e7942
__m256d v41 = v1.v4(), v42 = v2.v4();
return _mm256_sub_pd(
_mm256_mul_pd(
_mm256_permute4x64_pd(v41,_MM_SHUFFLE(3, 0, 2, 1)),
_mm256_permute4x64_pd(v42,_MM_SHUFFLE(3, 1, 0, 2))),
_mm256_mul_pd(
_mm256_permute4x64_pd(v41,_MM_SHUFFLE(3, 1, 0, 2)),
_mm256_permute4x64_pd(v42,_MM_SHUFFLE(3, 0, 2, 1))));
}
template<int N>
inline simd4_t<double,N> min(simd4_t<double,N> v1, const simd4_t<double,N>& v2) {
v1 = _mm256_min_pd(v1.v4(), v2.v4());
return v1;
}
template<int N>
inline simd4_t<double,N> max(simd4_t<double,N> v1, const simd4_t<double,N>& v2) {
v1 = _mm256_max_pd(v1.v4(), v2.v4());
return v1;
}
template<int N>
inline simd4_t<double,N>abs(simd4_t<double,N> v) {
static const __m256d sign_mask = _mm256_set1_pd(-0.); // -0. = 1 << 63
v = _mm256_andnot_pd(sign_mask, v.v4());
return v;
}
} /* namespace simd4 */
#endif
template<int N>
class simd4_t<int,N>
{
private:
typedef int __vec4i_t[N];
union alignas(16) {
int32x4_t simd4;
__vec4i_t vec;
};
public:
simd4_t(void) {}
simd4_t(int i) {}
simd4_t(int x, int y) : simd4_t(x,y,0,0) {}
simd4_t(int x, int y, int z) : simd4_t(x,y,z,0) {}
simd4_t(int x, int y, int z, int w) {
alignas(16) int32_t data[4] = { x, y, z, w };
simd4 = vld1q_s32(data);
}
simd4_t(const __vec4i_t v) {}
template<int M>
simd4_t(const simd4_t<int,M>& v) {
simd4 = v.v4();
}
simd4_t(const int32x4_t& v) {
simd4 = v;
}
inline int32x4_t (&v4(void)) {
return simd4;
}
inline const int32x4_t (&v4(void) const) {
return simd4;
}
inline const int (&ptr(void) const)[N] {
return vec;
}
inline int (&ptr(void))[N] {
return vec;
}
inline operator const int*(void) const {
return vec;
}
inline operator int*(void) {
return vec;
}
inline simd4_t<int,N>& operator+=(int i) {
return operator+=(simd4_t<int,N>(i));
}
template<int M>
inline simd4_t<int,N>& operator+=(const simd4_t<int,M>& v) {
simd4 = vaddq_s32(simd4, v.v4());
return *this;
}
inline simd4_t<int,N>& operator-=(int i) {
return operator-=(simd4_t<int,N>(i));
}
template<int M>
inline simd4_t<int,N>& operator-=(const simd4_t<int,M>& v) {
simd4 = vsubq_s32(simd4, v.v4());
return *this;
}
inline simd4_t<int,N>& operator*=(int i) {
return operator*=(simd4_t<int,N>(i));
}
template<int M>
inline simd4_t<int,N>& operator*=(const simd4_t<int,M>& v) {
return operator*=(v.v4());
}
inline simd4_t<int,N>& operator*=(const int32x4_t& v) {
simd4 = vmulq_s32(simd4, v);
return *this;
}
inline simd4_t<int,N>& operator/=(int s) {
return operator/=(simd4_t<int,4>(s));
}
template<int M>
inline simd4_t<int,N>& operator/=(const simd4_t<int,M>& v) {
for (int i=0; i<N; ++i) {
vec[i] /= v[i];
}
return *this;
}
};
static const int32x4_t imask2 = vld1q_s32((int32_t*)m2a32);
static const int32x4_t imask3 = vld1q_s32((int32_t*)m3a32);
template<>
inline simd4_t<int,4>::simd4_t(const __vec4i_t v) {
simd4 = vld1q_s32(v);
}
template<>
inline simd4_t<int,3>::simd4_t(const __vec4i_t v) {
simd4 = vandq_s32(vld1q_s32(v), imask3);
}
template<>
inline simd4_t<int,2>::simd4_t(const __vec4i_t v) {
simd4 = vandq_s32(vld1q_s32(v), imask2);
}
template<>
inline simd4_t<int,4>::simd4_t(int i) {
simd4 = vdupq_n_s32(i);
}
template<>
inline simd4_t<int,3>::simd4_t(int i) {
simd4 = vandq_s32(vdupq_n_s32(i), imask3);
}
template<>
inline simd4_t<int,2>::simd4_t(int i) {
simd4 = vandq_s32(vdupq_n_s32(i), imask2);
}
namespace simd4
{
template<int N>
inline simd4_t<int,N> min(simd4_t<int,N> v1, const simd4_t<int,N>& v2) {
v1 = vminq_s32(v1.v4(), v2.v4());
return v1;
}
template<int N>
inline simd4_t<int,N> max(simd4_t<int,N> v1, const simd4_t<int,N>& v2) {
v1 = vmaxq_s32(v1.v4(), v2.v4());
return v1;
}
} /* namespace simd4 */
# endif
#endif /* __SIMD_NEON_H__ */

View File

@@ -1,127 +0,0 @@
#include <stdio.h>
#include <cmath>
#include <cfloat>
#include "SGMathFwd.hxx"
#include "SGVec2.hxx"
#include "SGVec3.hxx"
#include "SGVec4.hxx"
// set to 0 for a timing test
#define TESTV_ACCURACY 1
#define N 4
#define T double
#if TESTV_ACCURACY
# define TESTF(a, b) \
if ( std::abs(a - b) > T(1e-7) ) \
printf("line: %i, diff: %5.4e\n", __LINE__, double(std::abs(a - b)));
# define TESTV(a, p,q,r,s) \
if ( std::abs(a[0] - (p)) > T(1e-7) \
|| std::abs(a[1] - (q)) > T(1e-7) \
|| (N > 2 && std::abs(a[2] - (r)) > T(1e-7)) \
|| (N > 3 && std::abs(a[3] - (s)) > T(1e-7)) \
) \
printf("line: %i, diff: %5.4e, %5.4e, %5.4e, %5.4e\n", __LINE__, double(std::abs(a[0]-(p))), double(std::abs(a[1]-(q))), double(std::abs(a[2]-(r))), double(std::abs(a[3]-(s))));
# define MAX 1
#else
# define TESTF(a, b)
# define TESTV(a, p,q,r,s)
# define MAX 1000000
#endif
#define _VEC(NSTR) SGVec ## NSTR
#define VEC(N) _VEC(N)
int main()
{
T init[4] = { T(1.31), T(3.43), T(5.69), T(1.0) };
T p[4] = { T(1.03), T(3.55), T(2.707), T(-4.01) };
float res = 0;
long int i;
VEC(N)<T> q;
for (int i=0; i<N; i++) {
q[i] = init[i];
}
simd4_t<T,N> qq, rr(T(-2.31), T(3.43), T(-4.69), T(-1.0));
TESTV(rr, T(-2.31), T(3.43), T(-4.69), T(-1.0));
qq = simd4::min(rr, simd4_t<T,N>(T(0)));
TESTV(qq, T(-2.31), 0, T(-4.69), T(-1.00));
qq = simd4::max(rr, simd4_t<T,N>(0.0));
TESTV(qq, 0, T(3.43), 0, 0);
qq = simd4::abs(rr);
TESTV(qq, T(2.31), T(3.43), T(4.69), T(1.00));
for (i=0; i<MAX; i++)
{
VEC(N)<T> v(p);
VEC(N)<T> x(v), y(v);
T f, g;
TESTV(x, p[0], p[1], p[2], p[3]);
TESTV(y, p[0], p[1], p[2], p[3]);
x += q;
TESTV(x, p[0]+q[0], p[1]+q[1], p[2]+q[2], p[3]+q[3]);
y -= q;
TESTV(y, p[0]-q[0], p[1]-q[1], p[2]-q[2], p[3]-q[3]);
v = x; x *= T(1.7);
TESTV(x, v[0]*T(1.7), v[1]*T(1.7), v[2]*T(1.7), v[3]*T(1.7));
v = y; y /= T(-1.3);
TESTV(y, v[0]/T(-1.3), v[1]/T(-1.3), v[2]/T(-1.3), v[3]/T(-1.3));
v = +x;
TESTV(v, x[0], x[1], x[2], x[3]);
v = -x;
TESTV(v, -x[0], -x[1], -x[2], -x[3]);
v = y+x;
TESTV(v, y[0]+x[0], y[1]+x[1], y[2]+x[2], y[3]+x[3]);
y = v*T(1.7);
TESTV(y, v[0]*T(1.7), v[1]*T(1.7), v[2]*T(1.7), v[3]*T(1.7));
y = T(1.7)*v;
TESTV(y, v[0]*T(1.7), v[1]*T(1.7), v[2]*T(1.7), v[3]*T(1.7));
v = y-x;
TESTV(v, y[0]-x[0], y[1]-x[1], y[2]-x[2], y[3]-x[3]);
f = norm(v); g = 0;
for (int i=0; i<N; i++) g += v[i]*v[i];
g = std::sqrt(g);
TESTF(f, g);
x = v; v -= y;
VEC(N)<T> t = x - y;
f = norm(v); g = 0;
for (int i=0; i<N; i++) g += t[i]*t[i];
g = std::sqrt(g);
TESTF(f, g);
f += dot(x, v); // g = 0;
for (int i=0; i<N; i++) g += x[i]*v[i];
TESTF(f, g);
f += dot(v, v); // g = 0;
for (int i=0; i<N; i++) g += v[i]*v[i];
TESTF(f, g);
res += f;
}
printf("res: %f\n", res);
return 0;
}

View File

@@ -12,11 +12,14 @@ set(HEADERS
sg_dir.hxx
sg_hash.hxx
sg_path.hxx
sgstream.hxx
stdint.hxx
stopwatch.hxx
strutils.hxx
tabbed_values.hxx
texcoord.hxx
zfstream.hxx
gzcontainerfile.hxx
)
set(SOURCES
@@ -28,9 +31,12 @@ set(SOURCES
sg_dir.cxx
sg_path.cxx
sg_hash.cxx
sgstream.cxx
strutils.cxx
tabbed_values.cxx
texcoord.cxx
zfstream.cxx
gzcontainerfile.cxx
)
if (WINDOWS)
@@ -53,18 +59,14 @@ add_executable(test_tabbed_values tabbed_values_test.cxx)
add_test(tabbed_values ${EXECUTABLE_OUTPUT_PATH}/test_tabbed_values)
target_link_libraries(test_tabbed_values ${TEST_LIBS})
add_executable(test_strutils strutils_test.cxx)
target_link_libraries(test_strutils ${TEST_LIBS})
add_test(strutils ${EXECUTABLE_OUTPUT_PATH}/test_strutils)
add_executable(test_streams sgstream_test.cxx )
add_test(streams ${EXECUTABLE_OUTPUT_PATH}/test_streams)
target_link_libraries(test_streams ${TEST_LIBS})
add_executable(test_path path_test.cxx )
add_test(path ${EXECUTABLE_OUTPUT_PATH}/test_path)
target_link_libraries(test_path ${TEST_LIBS})
add_executable(test_sg_dir sg_dir_test.cxx)
target_link_libraries(test_sg_dir ${TEST_LIBS})
add_test(sg_dir ${EXECUTABLE_OUTPUT_PATH}/test_sg_dir)
endif(ENABLE_TESTS)
add_boost_test(SimpleMarkdown
@@ -77,6 +79,11 @@ add_boost_test(SVGpreserveAspectRatio
LIBRARIES ${TEST_LIBS}
)
add_boost_test(strutils
SOURCES strutils_test.cxx
LIBRARIES ${TEST_LIBS}
)
add_boost_test(utf8tolatin1
SOURCES utf8tolatin1_test.cxx
LIBRARIES ${TEST_LIBS}

View File

@@ -4,18 +4,30 @@
#include <cmath>
#include <iostream>
#include <simgear/misc/test_macros.hxx>
#define COMPARE(a, b) SG_CHECK_EQUAL_EP2((a), (b), 1e-4)
#define COMPARE(a, b) \
if( std::fabs((a) - (b)) > 1e-4 ) \
{ \
std::cerr << "line " << __LINE__ << ": failed: "\
<< #a << " != " << #b << " d = " << ((a) - (b)) << std::endl; \
return 1; \
}
#define VERIFY(a) \
if( !(a) ) \
{ \
std::cerr << "line " << __LINE__ << ": failed: "\
<< #a << std::endl; \
return 1; \
}
using namespace simgear;
int main (int ac, char ** av)
{
CSSBorder b = CSSBorder::parse("5");
SG_VERIFY(b.isValid());
SG_VERIFY(!b.isNone());
VERIFY(b.isValid());
VERIFY(!b.isNone());
CSSBorder::Offsets o = b.getAbsOffsets(SGRect<int>());
COMPARE(o.t, 5);
COMPARE(o.r, 5);
@@ -69,8 +81,8 @@ int main (int ac, char ** av)
COMPARE(o.r, 0);
COMPARE(o.b, 0);
COMPARE(o.l, 0);
SG_VERIFY(b.getKeyword().empty());
SG_VERIFY(b.isNone());
VERIFY(b.getKeyword().empty());
VERIFY(b.isNone());
b = CSSBorder::parse("none");
o = b.getRelOffsets(SGRect<int>(0,0,200,200));
@@ -78,11 +90,11 @@ int main (int ac, char ** av)
COMPARE(o.r, 0);
COMPARE(o.b, 0);
COMPARE(o.l, 0);
SG_VERIFY(b.getKeyword().empty());
SG_VERIFY(b.isNone());
VERIFY(b.getKeyword().empty());
VERIFY(b.isNone());
CSSBorder b2;
SG_VERIFY(!b2.isValid());
VERIFY(!b2.isValid());
o = b.getAbsOffsets(SGRect<int>(0,0,200,200));
COMPARE(o.t, 0);
COMPARE(o.r, 0);

View File

@@ -22,7 +22,7 @@
#define GZ_CONTAINER_FILE_HXX
#include <string>
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/misc/sgstream.hxx>
class SGPropertyNode;

View File

@@ -12,26 +12,19 @@ using std::endl;
#include <simgear/misc/test_macros.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/misc/sg_dir.hxx>
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/debug/logstream.hxx>
#if defined(SG_WINDOWS)
#include <io.h> // for _wchmod
#else
#include <sys/stat.h> // for chmod
#endif
#include <simgear/misc/sgstream.hxx>
void test_dir()
{
simgear::Dir temp = simgear::Dir::tempDir("foo");
cout << "created:" << temp.path() << endl;
SG_VERIFY(temp.exists());
SG_VERIFY(temp.path().isDir());
SG_VERIFY(!temp.path().isFile());
VERIFY(temp.exists());
VERIFY(temp.path().isDir());
VERIFY(!temp.path().isFile());
SGPath fileInDir = temp.file("foobaz");
SG_VERIFY(!fileInDir.exists());
VERIFY(!fileInDir.exists());
if (!temp.remove(true)) {
cout << "remove failed!" << endl;
@@ -47,57 +40,11 @@ void test_dir()
<< "\n - Pictures: " << SGPath::standardLocation(SGPath::PICTURES)
<< std::endl;
SG_VERIFY( !SGPath::standardLocation(SGPath::HOME ).isNull() );
SG_VERIFY( !SGPath::standardLocation(SGPath::DESKTOP ).isNull() );
SG_VERIFY( !SGPath::standardLocation(SGPath::DOWNLOADS).isNull() );
SG_VERIFY( !SGPath::standardLocation(SGPath::DOCUMENTS).isNull() );
SG_VERIFY( !SGPath::standardLocation(SGPath::PICTURES ).isNull() );
}
// exercise the remove + rename that occurs when upgrading an
// aircraft package
void test_update_dir()
{
SGPath p(simgear::Dir::current().path());
p.append("test_update_dir");
simgear::Dir pd(p);
pd.removeChildren();
{
SGPath existingFile = p / "Cessna172P" / "somefile.txt";
existingFile.create_dir(0755);
sg_ofstream of(existingFile, std::ios::out);
of << "Foobar";
of.close();
SG_VERIFY(existingFile.exists());
}
{
SGPath replacementFile = p / "NewDir" / "someotherfile.txt";
replacementFile.create_dir(0755);
sg_ofstream of(replacementFile, std::ios::out);
of << "Foobar";
of.close();
SG_VERIFY(replacementFile.exists());
}
{
simgear::Dir od(p / "Cessna172P");
SG_VERIFY(od.remove(true));
}
SGPath replacementDir = p / "NewDir";
SG_VERIFY(replacementDir.rename(p / "Cessna172P"));
SG_VERIFY((p / "Cessna172P" / "someotherfile.txt").exists());
SG_VERIFY(!(p / "Cessna172P" / "somefile.txt").exists());
SG_VERIFY(!(p / "NewDir").exists());
SG_LOG(SG_GENERAL, SG_INFO, "passed update test");
VERIFY( !SGPath::standardLocation(SGPath::HOME ).isNull() );
VERIFY( !SGPath::standardLocation(SGPath::DESKTOP ).isNull() );
VERIFY( !SGPath::standardLocation(SGPath::DOWNLOADS).isNull() );
VERIFY( !SGPath::standardLocation(SGPath::DOCUMENTS).isNull() );
VERIFY( !SGPath::standardLocation(SGPath::PICTURES ).isNull() );
}
SGPath::Permissions validateNone(const SGPath&)
@@ -130,271 +77,181 @@ void test_path_dir()
temp.remove(true);
SGPath p = temp.path();
SG_VERIFY(p.isAbsolute());
SG_CHECK_EQUAL(p.create_dir(0755), 0);
VERIFY(p.isAbsolute());
COMPARE(p.create_dir(0755), 0);
SGPath sub = p / "subA" / "subB";
SG_VERIFY(!sub.exists());
VERIFY(!sub.exists());
SGPath subFile = sub / "fileABC.txt";
SG_CHECK_EQUAL(subFile.create_dir(0755), 0);
SG_VERIFY(!subFile.exists());
COMPARE(subFile.create_dir(0755), 0);
VERIFY(!subFile.exists());
sub.set_cached(false);
SG_VERIFY(sub.exists());
SG_VERIFY(sub.isDir());
VERIFY(sub.exists());
VERIFY(sub.isDir());
SGPath sub2 = p / "subA" / "fileA";
{
sg_ofstream os(sub2);
SG_VERIFY(os.is_open());
VERIFY(os.is_open());
for (int i = 0; i < 50; ++i) {
os << "ABCD" << endl;
}
}
SG_VERIFY(sub2.isFile());
SG_CHECK_EQUAL(sub2.sizeInBytes(), 250);
VERIFY(sub2.isFile());
COMPARE(sub2.sizeInBytes(), 250);
SGPath sub3 = p / "subß" / "file𝕽";
sub3.create_dir(0755);
{
sg_ofstream os(sub3);
SG_VERIFY(os.is_open());
VERIFY(os.is_open());
for (int i = 0; i < 20; ++i) {
os << "EFGH" << endl;
}
}
sub3.set_cached(false);
SG_VERIFY(sub3.exists());
SG_CHECK_EQUAL(sub3.sizeInBytes(), 100);
SG_CHECK_EQUAL(sub3.file(), "file𝕽");
VERIFY(sub3.exists());
COMPARE(sub3.sizeInBytes(), 100);
COMPARE(sub3.file(), "file𝕽");
simgear::Dir subD(p / "subA");
simgear::PathList dirChildren = subD.children(simgear::Dir::TYPE_DIR | simgear::Dir::NO_DOT_OR_DOTDOT);
SG_CHECK_EQUAL(dirChildren.size(), 1);
SG_CHECK_EQUAL(dirChildren[0], subD.path() / "subB");
COMPARE(dirChildren.size(), 1);
COMPARE(dirChildren[0], subD.path() / "subB");
simgear::PathList fileChildren = subD.children(simgear::Dir::TYPE_FILE | simgear::Dir::NO_DOT_OR_DOTDOT);
SG_CHECK_EQUAL(fileChildren.size(), 1);
SG_CHECK_EQUAL(fileChildren[0], subD.path() / "fileA");
COMPARE(fileChildren.size(), 1);
COMPARE(fileChildren[0], subD.path() / "fileA");
simgear::Dir subS(sub3.dirPath());
fileChildren = subS.children(simgear::Dir::TYPE_FILE | simgear::Dir::NO_DOT_OR_DOTDOT);
SG_CHECK_EQUAL(fileChildren.size(), 1);
SG_CHECK_EQUAL(fileChildren[0], subS.path() / "file𝕽");
COMPARE(fileChildren.size(), 1);
COMPARE(fileChildren[0], subS.path() / "file𝕽");
}
void test_permissions()
{
// start with an empty dir
SGPath p = simgear::Dir::current().path() / "path_test_permissions";
simgear::Dir pd(p);
if (pd.exists()) {
pd.removeChildren();
} else {
pd.create(0700);
}
// windows doesn't seem to actualy create a read-only directory, so this
// test fails in strange ways there.
#if !defined(SG_WINDOWS)
SGPath fileInRO = p / "read-only" / "fileA";
SG_CHECK_EQUAL(fileInRO.create_dir(0500), 0);
SG_VERIFY(!fileInRO.exists());
SG_VERIFY(!fileInRO.canWrite());
fileInRO.setPermissionChecker(&validateRead);
SG_CHECK_EQUAL(fileInRO.canRead(), false);
SG_CHECK_EQUAL(fileInRO.canWrite(), false);
fileInRO.setPermissionChecker(&validateWrite);
SG_CHECK_EQUAL(fileInRO.canRead(), false);
SG_CHECK_EQUAL(fileInRO.canWrite(), false);
#endif
/////////
SGPath fileInRW = p / "read-write" / "fileA";
fileInRW.setPermissionChecker(&validateRead);
SG_CHECK_EQUAL(fileInRW.canRead(), false);
SG_CHECK_EQUAL(fileInRW.canWrite(), false);
SG_CHECK_EQUAL(fileInRW.create_dir(0700), -3); // not permitted
fileInRW.setPermissionChecker(nullptr);
SG_CHECK_EQUAL(fileInRW.create_dir(0700), 0);
SG_VERIFY(!fileInRW.exists());
SG_VERIFY(fileInRW.canWrite());
fileInRW.setPermissionChecker(&validateRead);
SG_CHECK_EQUAL(fileInRW.canRead(), false);
SG_CHECK_EQUAL(fileInRW.canWrite(), false);
fileInRW.setPermissionChecker(&validateWrite);
SG_CHECK_EQUAL(fileInRW.canRead(), false);
SG_CHECK_EQUAL(fileInRW.canWrite(), true);
// create the file
{
sg_ofstream os(fileInRW);
SG_VERIFY(os.is_open());
os << "nonense" << endl;
}
// should now be readable + writeable
fileInRW.set_cached(false);
fileInRW.setPermissionChecker(nullptr);
SG_VERIFY(fileInRW.exists());
SG_VERIFY(fileInRW.canWrite());
SG_VERIFY(fileInRW.canRead());
fileInRW.setPermissionChecker(&validateRead);
SG_CHECK_EQUAL(fileInRW.canRead(), true);
SG_CHECK_EQUAL(fileInRW.canWrite(), false);
fileInRW.setPermissionChecker(&validateWrite);
SG_CHECK_EQUAL(fileInRW.canRead(), false);
SG_CHECK_EQUAL(fileInRW.canWrite(), true);
// mark the file as read-only
#if defined(SG_WINDOWS)
std::wstring wp = fileInRW.wstr();
_wchmod(wp.c_str(), _S_IREAD);
#else
::chmod(fileInRW.c_str(), 0400);
#endif
fileInRW.set_cached(false);
fileInRW.setPermissionChecker(nullptr);
SG_VERIFY(fileInRW.exists());
SG_CHECK_EQUAL(fileInRW.canWrite(), false);
SG_VERIFY(fileInRW.canRead());
fileInRW.setPermissionChecker(&validateRead);
SG_CHECK_EQUAL(fileInRW.canRead(), true);
SG_CHECK_EQUAL(fileInRW.canWrite(), false);
fileInRW.setPermissionChecker(&validateWrite);
SG_CHECK_EQUAL(fileInRW.canRead(), false);
SG_CHECK_EQUAL(fileInRW.canWrite(), false);
}
int main(int argc, char* argv[])
{
SGPath pa;
SG_VERIFY(pa.isNull());
SG_CHECK_EQUAL(pa.exists(), false);
VERIFY(pa.isNull());
COMPARE(pa.exists(), false);
// test basic parsing
SGPath pb("/Foo/bar/something.png");
SG_CHECK_EQUAL(pb.utf8Str(), std::string("/Foo/bar/something.png"));
SG_CHECK_EQUAL(pb.local8BitStr(), std::string("/Foo/bar/something.png"));
SG_CHECK_EQUAL(pb.dir(), std::string("/Foo/bar"));
SG_CHECK_EQUAL(pb.file(), std::string("something.png"));
SG_CHECK_EQUAL(pb.base(), std::string("/Foo/bar/something"));
SG_CHECK_EQUAL(pb.file_base(), std::string("something"));
SG_CHECK_EQUAL(pb.extension(), std::string("png"));
SG_VERIFY(pb.isAbsolute());
SG_VERIFY(!pb.isRelative());
COMPARE(pb.utf8Str(), std::string("/Foo/bar/something.png"));
COMPARE(pb.local8BitStr(), std::string("/Foo/bar/something.png"));
COMPARE(pb.dir(), std::string("/Foo/bar"));
COMPARE(pb.file(), std::string("something.png"));
COMPARE(pb.base(), std::string("/Foo/bar/something"));
COMPARE(pb.file_base(), std::string("something"));
COMPARE(pb.extension(), std::string("png"));
VERIFY(pb.isAbsolute());
VERIFY(!pb.isRelative());
// relative paths
SGPath ra("where/to/begin.txt");
SG_CHECK_EQUAL(ra.utf8Str(), std::string("where/to/begin.txt"));
SG_CHECK_EQUAL(ra.local8BitStr(), std::string("where/to/begin.txt"));
SG_CHECK_EQUAL(ra.dir(), std::string("where/to"));
SG_CHECK_EQUAL(ra.file(), std::string("begin.txt"));
SG_CHECK_EQUAL(ra.file_base(), std::string("begin"));
SG_VERIFY(!ra.isAbsolute());
SG_VERIFY(ra.isRelative());
COMPARE(ra.utf8Str(), std::string("where/to/begin.txt"));
COMPARE(ra.local8BitStr(), std::string("where/to/begin.txt"));
COMPARE(ra.dir(), std::string("where/to"));
COMPARE(ra.file(), std::string("begin.txt"));
COMPARE(ra.file_base(), std::string("begin"));
VERIFY(!ra.isAbsolute());
VERIFY(ra.isRelative());
// dots in paths / missing extensions
SGPath pk("/Foo/bar.dot/thing");
SG_CHECK_EQUAL(pk.dir(), std::string("/Foo/bar.dot"));
SG_CHECK_EQUAL(pk.file(), std::string("thing"));
SG_CHECK_EQUAL(pk.base(), std::string("/Foo/bar.dot/thing"));
SG_CHECK_EQUAL(pk.file_base(), std::string("thing"));
SG_CHECK_EQUAL(pk.extension(), std::string());
COMPARE(pk.dir(), std::string("/Foo/bar.dot"));
COMPARE(pk.file(), std::string("thing"));
COMPARE(pk.base(), std::string("/Foo/bar.dot/thing"));
COMPARE(pk.file_base(), std::string("thing"));
COMPARE(pk.extension(), std::string());
// multiple file extensions
SGPath pj("/Foo/zot.dot/thing.tar.gz");
SG_CHECK_EQUAL(pj.dir(), std::string("/Foo/zot.dot"));
SG_CHECK_EQUAL(pj.file(), std::string("thing.tar.gz"));
SG_CHECK_EQUAL(pj.base(), std::string("/Foo/zot.dot/thing.tar"));
SG_CHECK_EQUAL(pj.file_base(), std::string("thing"));
SG_CHECK_EQUAL(pj.extension(), std::string("gz"));
SG_CHECK_EQUAL(pj.complete_lower_extension(), std::string("tar.gz"));
COMPARE(pj.dir(), std::string("/Foo/zot.dot"));
COMPARE(pj.file(), std::string("thing.tar.gz"));
COMPARE(pj.base(), std::string("/Foo/zot.dot/thing.tar"));
COMPARE(pj.file_base(), std::string("thing"));
COMPARE(pj.extension(), std::string("gz"));
COMPARE(pj.complete_lower_extension(), std::string("tar.gz"));
// path fixing
SGPath rd("where\\to\\begin.txt");
SG_CHECK_EQUAL(rd.utf8Str(), std::string("where/to/begin.txt"));
COMPARE(rd.utf8Str(), std::string("where/to/begin.txt"));
// test modification
// append
SGPath d1("/usr/local");
SGPath pc = d1;
SG_CHECK_EQUAL(pc.utf8Str(), std::string("/usr/local"));
COMPARE(pc.utf8Str(), std::string("/usr/local"));
pc.append("include");
SG_CHECK_EQUAL(pc.utf8Str(), std::string("/usr/local/include"));
SG_CHECK_EQUAL(pc.file(), std::string("include"));
COMPARE(pc.utf8Str(), std::string("/usr/local/include"));
COMPARE(pc.file(), std::string("include"));
// concat
SGPath pd = pb;
pd.concat("-1");
SG_CHECK_EQUAL(pd.utf8Str(), std::string("/Foo/bar/something.png-1"));
COMPARE(pd.utf8Str(), std::string("/Foo/bar/something.png-1"));
// create with relative path
SGPath rb(d1, "include/foo");
SG_CHECK_EQUAL(rb.utf8Str(), std::string("/usr/local/include/foo"));
SG_VERIFY(rb.isAbsolute());
COMPARE(rb.utf8Str(), std::string("/usr/local/include/foo"));
VERIFY(rb.isAbsolute());
// lower-casing of file extensions
SGPath extA("FOO.ZIP");
SG_CHECK_EQUAL(extA.base(), "FOO");
SG_CHECK_EQUAL(extA.extension(), "ZIP");
SG_CHECK_EQUAL(extA.lower_extension(), "zip");
SG_CHECK_EQUAL(extA.complete_lower_extension(), "zip");
COMPARE(extA.base(), "FOO");
COMPARE(extA.extension(), "ZIP");
COMPARE(extA.lower_extension(), "zip");
COMPARE(extA.complete_lower_extension(), "zip");
SGPath extB("BAH/FOO.HTML.GZ");
SG_CHECK_EQUAL(extB.extension(), "GZ");
SG_CHECK_EQUAL(extB.base(), "BAH/FOO.HTML");
SG_CHECK_EQUAL(extB.lower_extension(), "gz");
SG_CHECK_EQUAL(extB.complete_lower_extension(), "html.gz");
COMPARE(extB.extension(), "GZ");
COMPARE(extB.base(), "BAH/FOO.HTML");
COMPARE(extB.lower_extension(), "gz");
COMPARE(extB.complete_lower_extension(), "html.gz");
#ifdef _WIN32
SGPath winAbs("C:\\Windows\\System32");
SG_CHECK_EQUAL(winAbs.local8BitStr(), std::string("C:/Windows/System32"));
COMPARE(winAbs.local8BitStr(), std::string("C:/Windows/System32"));
#endif
// paths with only the file components
SGPath pf("something.txt.gz");
SG_CHECK_EQUAL(pf.base(), "something.txt");
SG_CHECK_EQUAL(pf.file(), "something.txt.gz");
SG_CHECK_EQUAL(pf.dir(), "");
SG_CHECK_EQUAL(pf.lower_extension(), "gz");
SG_CHECK_EQUAL(pf.complete_lower_extension(), "txt.gz");
COMPARE(pf.base(), "something.txt");
COMPARE(pf.file(), "something.txt.gz");
COMPARE(pf.dir(), "");
COMPARE(pf.lower_extension(), "gz");
COMPARE(pf.complete_lower_extension(), "txt.gz");
SG_CHECK_EQUAL(pf.canRead(), false);
SG_CHECK_EQUAL(pf.canWrite(), false);
COMPARE(pf.canRead(), true);
COMPARE(pf.canWrite(), true);
SGPath pp(&validateNone);
SG_CHECK_EQUAL(pp.canRead(), false);
SG_CHECK_EQUAL(pp.canWrite(), false);
COMPARE(pp.canRead(), false);
COMPARE(pp.canWrite(), false);
pp.append("./test-dir/file.txt");
COMPARE(pp.create_dir(0700), -3);
pp.setPermissionChecker(&validateRead);
COMPARE(pp.canRead(), true);
COMPARE(pp.canWrite(), false);
COMPARE(pp.create_dir(0700), -3);
pp.setPermissionChecker(&validateWrite);
COMPARE(pp.canRead(), false);
COMPARE(pp.canWrite(), true);
test_dir();
test_path_dir();
test_permissions();
test_update_dir();
cout << "all tests passed OK" << endl;
return 0; // passed
}

View File

@@ -37,6 +37,7 @@
# include <dirent.h>
# include <sys/stat.h>
# include <unistd.h>
# include <errno.h>
#endif
#include <simgear/misc/strutils.hxx>
@@ -45,7 +46,6 @@
#include <cstring>
#include <cstdlib>
#include <cerrno>
#include <iostream>
#include <algorithm> // for std::sort
@@ -94,7 +94,7 @@ Dir Dir::current()
#endif
if (!buf) {
if (errno == 2) throw sg_exception("The current directory is invalid");
else throw sg_exception(simgear::strutils::error_string(errno));
else throw sg_exception(strerror(errno));
}
SGPath p(buf);
@@ -118,8 +118,7 @@ Dir Dir::tempDir(const std::string& templ)
std::string s = p.local8BitStr();
::snprintf(buf, 1024, "%s", s.c_str());
if (!mkdtemp(buf)) {
SG_LOG(SG_IO, SG_WARN,
"mkdtemp failed: " << simgear::strutils::error_string(errno));
SG_LOG(SG_IO, SG_WARN, "mkdtemp failed:" << strerror(errno));
return Dir();
}
@@ -136,7 +135,6 @@ Dir Dir::tempDir(const std::string& templ)
Dir t(p);
if (!t.create(0700)) {
SG_LOG(SG_IO, SG_WARN, "failed to create temporary directory at " << p);
return Dir();
}
return t;
@@ -279,11 +277,6 @@ PathList Dir::children(int types, const std::string& nameFilter) const
return result;
}
bool Dir::isNull() const
{
return _path.isNull();
}
bool Dir::isEmpty() const
{
#if defined(SG_WINDOWS)
@@ -340,9 +333,7 @@ bool Dir::create(mode_t mode)
int err = mkdir(ps.c_str(), mode);
#endif
if (err) {
SG_LOG(SG_IO, SG_WARN,
"directory creation failed for '" << _path.utf8Str() << "': " <<
simgear::strutils::error_string(errno));
SG_LOG(SG_IO, SG_WARN, "directory creation failed: (" << _path << ") " << strerror(errno) );
}
return (err == 0);
@@ -350,10 +341,6 @@ bool Dir::create(mode_t mode)
bool Dir::removeChildren() const
{
if (!exists()) {
return true;
}
bool ok;
PathList cs = children(NO_DOT_OR_DOTDOT | INCLUDE_HIDDEN | TYPE_FILE | TYPE_DIR);
BOOST_FOREACH(SGPath path, cs) {
@@ -395,9 +382,7 @@ bool Dir::remove(bool recursive)
int err = rmdir(ps.c_str());
#endif
if (err) {
SG_LOG(SG_IO, SG_WARN,
"rmdir failed for '" << _path.utf8Str() << "': " <<
simgear::strutils::error_string(errno));
SG_LOG(SG_IO, SG_WARN, "rmdir failed:" << _path << ":" << strerror(errno));
}
return (err == 0);
}

View File

@@ -55,8 +55,7 @@ namespace simgear
static Dir current();
/**
* Create a temporary directory, using the supplied name.
* The return value 'd' is such that d.isNull() in case this failed.
* create a temporary directory, using the supplied name
*/
static Dir tempDir(const std::string& templ);
@@ -72,14 +71,7 @@ namespace simgear
};
PathList children(int types = 0, const std::string& nameGlob = "") const;
/**
* Check if the underlying SGPath is null.
*
* Note: this is the case for a default-constructed Dir instance.
*/
bool isNull() const;
/**
* test if the directory contains no children (except '.' and '..')
*/

View File

@@ -1,42 +0,0 @@
#include <cstdlib>
#include <simgear/misc/sg_path.hxx>
#include <simgear/misc/test_macros.hxx>
#include "sg_dir.hxx"
void test_isNull()
{
SG_VERIFY(simgear::Dir().isNull());
}
void test_setRemoveOnDestroy()
{
SGPath path;
{
simgear::Dir d = simgear::Dir::tempDir("FlightGear");
SG_VERIFY(!d.isNull() && d.exists() && d.isEmpty());
d.setRemoveOnDestroy();
path = d.path(); // keep a copy of the path
SG_VERIFY(path.exists() && path.isDir());
}
SG_VERIFY(!path.exists());
}
void test_tempDir()
{
simgear::Dir d = simgear::Dir::tempDir("FlightGear");
SG_VERIFY(!d.isNull() && d.exists() && d.isEmpty());
d.remove();
}
int main(int argc, char **argv)
{
test_isNull();
test_setRemoveOnDestroy();
test_tempDir();
return EXIT_SUCCESS;
}

View File

@@ -27,7 +27,7 @@
#include <simgear_config.h>
#include <simgear/debug/logstream.hxx>
#include <simgear/misc/strutils.hxx>
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/misc/sgstream.hxx>
#include <stdio.h>
#include <sys/stat.h>
@@ -460,11 +460,10 @@ void SGPath::validate() const
if (path.empty()) {
_exists = false;
_canWrite = _canRead = false;
return;
}
#if defined(SG_WINDOWS)
#ifdef _WIN32
struct _stat buf ;
bool remove_trailing = false;
std::wstring statPath(wstr());
@@ -474,24 +473,12 @@ void SGPath::validate() const
if (_wstat(statPath.c_str(), &buf ) < 0) {
_exists = false;
_canRead = false;
// check parent directory for write-ability
std::wstring parentPath = simgear::strutils::convertUtf8ToWString(dir());
struct _stat parentBuf;
if (_wstat(parentPath.c_str(), &parentBuf) >= 0) {
_canWrite = parentBuf.st_mode & _S_IWRITE;
} else {
_canWrite = false;
}
} else {
_exists = true;
_isFile = ((S_IFREG & buf.st_mode ) !=0);
_isDir = ((S_IFDIR & buf.st_mode ) !=0);
_modTime = buf.st_mtime;
_size = buf.st_size;
_canRead = _S_IREAD & buf.st_mode;
_canWrite = _S_IWRITE & buf.st_mode;
}
#else
@@ -499,35 +486,15 @@ void SGPath::validate() const
if (stat(path.c_str(), &buf ) < 0) {
_exists = false;
_canRead = false;
// check parent directory for write-ability
std::string parentPath = dir();
struct stat parentBuf;
if (stat(parentPath.c_str(), &parentBuf) >= 0) {
_canWrite = parentBuf.st_mode & S_IWUSR;
} else {
_canWrite = false;
}
} else {
_exists = true;
_isFile = ((S_ISREG(buf.st_mode )) != 0);
_isDir = ((S_ISDIR(buf.st_mode )) != 0);
_modTime = buf.st_mtime;
_size = buf.st_size;
_canRead = S_IRUSR & buf.st_mode;
_canWrite = S_IWUSR & buf.st_mode;
}
#endif
// ensure permissions are no less restrictive than what the
// permissions checker offers
if ( _permission_checker ) {
Permissions p = _permission_checker(*this);
_canRead &= p.read;
_canWrite &= p.write;
}
_cached = true;
}
@@ -537,7 +504,18 @@ void SGPath::checkAccess() const
if( _rwCached && _cacheEnabled )
return;
validate();
if( _permission_checker )
{
Permissions p = _permission_checker(*this);
_canRead = p.read;
_canWrite = p.write;
}
else
{
_canRead = true;
_canWrite = true;
}
_rwCached = true;
}
@@ -577,7 +555,7 @@ bool SGPath::isFile() const
int SGPath::create_dir(mode_t mode)
{
if ( !permissionsAllowsWrite() )
if( !canWrite() )
{
SG_LOG( SG_IO,
SG_WARN, "Error creating directory for '" << *this << "'"
@@ -725,7 +703,7 @@ std::string SGPath::str_native() const
//------------------------------------------------------------------------------
bool SGPath::remove()
{
if( !permissionsAllowsWrite() )
if( !canWrite() )
{
SG_LOG( SG_IO, SG_WARN, "file remove failed: (" << *this << ")"
" reason: access denied" );
@@ -734,15 +712,7 @@ bool SGPath::remove()
#if defined(SG_WINDOWS)
std::wstring ps = wstr();
// windows forbids removing a read-only file, let's try to deal
// with that case
int err = _wchmod(ps.c_str(), _S_IWRITE | _S_IREAD);
if (err != 0) {
SG_LOG(SG_IO, SG_WARN, "failed to make file writeable prior to remove:" << *this);
} else {
err = _wunlink(ps.c_str());
}
int err = _wunlink(ps.c_str());
#else
std::string ps = local8BitStr();
int err = ::unlink(ps.c_str());
@@ -1048,10 +1018,3 @@ std::wstring SGPath::wstr() const
{
return simgear::strutils::convertUtf8ToWString(path);
}
//------------------------------------------------------------------------------
bool SGPath::permissionsAllowsWrite() const
{
return _permission_checker ? _permission_checker(*this).write : true;
}

View File

@@ -335,8 +335,6 @@ private:
void validate() const;
void checkAccess() const;
bool permissionsAllowsWrite() const;
std::string path;
PermissionChecker _permission_checker;

View File

@@ -40,7 +40,7 @@
#include <string>
#include <zlib.h>
#include <simgear/io/iostreams/gzfstream.hxx>
#include <simgear/misc/zfstream.hxx>
class SGPath;

View File

@@ -1,53 +1,52 @@
#include <iostream>
#include <cstdlib> // for EXIT_SUCCESS
#include <fstream>
using std::string;
#include <cstdlib> // for EXIT_FAILURE
using std::ofstream;
using std::cout;
using std::endl;
#include <simgear/misc/test_macros.hxx>
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/misc/sgstream.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/misc/sg_dir.hxx>
int main()
{
const string fileName = "testfile";
simgear::Dir tmpDir = simgear::Dir::tempDir("FlightGear");
tmpDir.setRemoveOnDestroy();
SGPath p(tmpDir.path() / fileName);
const char* fileName = "testfile";
{
sg_ofstream f(p, std::ios::binary | std::ios::trunc | std::ios::out);
ofstream f;
f.open(fileName, std::ios::binary | std::ios::trunc | std::ios::out);
f.write("first line ends with line-feed\n"
"second line ends with just a cr\r"
"third line ends with both\r\n"
"fourth line as well\r\n"
"fifth line is another CR/LF line\r\n"
"end of test\r\n", 158);
}
f.close();
}
SGPath p(fileName);
sg_gzifstream sg(p);
std::string stuff;
sg >> skipeol;
sg >> stuff;
SG_CHECK_EQUAL(stuff, "second");
if (stuff != "second") return EXIT_FAILURE;
cout << "Detection of LF works." << endl;
sg >> skipeol;
sg >> stuff;
SG_CHECK_EQUAL(stuff, "third");
if (stuff != "third") return EXIT_FAILURE;
cout << "Detection of CR works." << endl;
sg >> skipeol;
sg >> stuff;
SG_CHECK_EQUAL(stuff, "fourth");
if (stuff != "fourth") return EXIT_FAILURE;
cout << "Detection of CR/LF works." << endl;
sg >> skipeol;
sg >> skipeol;
sg >> stuff;
SG_CHECK_EQUAL(stuff, "end");
if (stuff != "end") return EXIT_FAILURE;
cout << "Detection of 2 following CR/LF lines works." << endl;
return EXIT_SUCCESS;

View File

@@ -175,33 +175,6 @@ namespace simgear {
return result;
}
string_list split_on_any_of(const std::string& str, const char* seperators)
{
if (seperators == nullptr || (strlen(seperators) == 0)) {
throw sg_exception("illegal/missing seperator string");
}
string_list result;
size_t pos = 0;
size_t startPos = str.find_first_not_of(seperators, 0);
for(;;)
{
pos = str.find_first_of(seperators, startPos);
if (pos == string::npos) {
result.push_back(str.substr(startPos));
break;
}
result.push_back(str.substr(startPos, pos - startPos));
startPos = str.find_first_not_of(seperators, pos);
if (startPos == string::npos) {
break;
}
}
return result;
}
/**
* The lstrip(), rstrip() and strip() functions are implemented
* in do_strip() which uses an additional parameter to indicate what
@@ -265,35 +238,6 @@ namespace simgear {
return do_strip( s, BOTHSTRIP );
}
void
stripTrailingNewlines_inplace(string& s)
{
// The following (harder to read) implementation is much slower on
// my system (g++ 6.2.1 on Debian): 11.4 vs. 3.9 seconds on
// 50,000,000 iterations performed on a short CRLF-terminated
// string---and it is even a bit slower (12.9 seconds) with
// std::next(it) instead of (it+1).
//
// for (string::reverse_iterator it = s.rbegin();
// it != s.rend() && (*it == '\r' || *it == '\n'); /* empty */) {
// it = string::reverse_iterator(s.erase( (it+1).base() ));
// }
// Simple and fast
while (!s.empty() && (s.back() == '\r' || s.back() == '\n')) {
s.pop_back();
}
}
string
stripTrailingNewlines(const string& s)
{
string res = s;
stripTrailingNewlines_inplace(res);
return res;
}
string
rpad( const string & s, string::size_type length, char c )
{
@@ -372,16 +316,12 @@ namespace simgear {
return result;
}
int compare_versions(const string& v1, const string& v2, int maxComponents)
int compare_versions(const string& v1, const string& v2)
{
vector<string> v1parts(split(v1, "."));
vector<string> v2parts(split(v2, "."));
int lastPart = std::min(v1parts.size(), v2parts.size());
if (maxComponents > 0) {
lastPart = std::min(lastPart, maxComponents);
}
for (int part=0; part < lastPart; ++part) {
int part1 = to_int(v1parts[part]);
int part2 = to_int(v2parts[part]);

View File

@@ -70,24 +70,6 @@ namespace simgear {
std::string rstrip( const std::string& s );
std::string strip( const std::string& s );
/**
* Return a new string with any trailing \r and \n characters removed.
* Typically useful to clean a CR-terminated line obtained from
* std::getline() which, upon reading CRLF (\r\n), discards the Line
* Feed character (\n) but leaves the Carriage Return (\r) in the
* string.
* @param s Input string
* @return The cleaned string
*/
std::string stripTrailingNewlines(const std::string& s);
/**
* Strip any trailing \r and \n characters from a string.
* Should have slightly less overhead than stripTrailingNewlines().
* @param s Input string (modified in-place)
*/
void stripTrailingNewlines_inplace(std::string& s);
/**
* Right-padding of a string to a given length
* @param s String to pad
@@ -122,17 +104,6 @@ namespace simgear {
const char* sep = 0,
int maxsplit = 0 );
/**
* split a string on any of several characters. Commonly used to deal
* with strings containing whitespace, newlines. To parse CSS style
* string, use with '\n\t ,' as the seperator list.
*
* Note consecutive seperators will not produce empty entries in the
* the result, i.e splitting 'a,b,,c,d' with a ',' will produce a result
* with four entries, not five.
*/
string_list split_on_any_of(const std::string&, const char* seperators);
/**
* create a single string by joining the elements of a list with
* another string.
@@ -174,10 +145,8 @@ namespace simgear {
* any number of terms are supported.
* @return 0 if versions match, -ve number if v1 is lower, +ve if v1
* is greater
* @param maxComponents is the maximum number of components to look at.
* This can be used to ignore (say) the patch level by setting it to 2
*/
int compare_versions(const std::string& v1, const std::string& v2, int maxComponents = 0);
int compare_versions(const std::string& v1, const std::string& v2);
/**
* Convert a string to upper case.

View File

@@ -1,181 +1,108 @@
// Unit tests for functions inside the strutils package
/// Unit tests for function inside strutils package
#define BOOST_TEST_MODULE misc
#include <BoostTestTargetConfig.h>
#include <errno.h>
#include <stdlib.h> // _set_errno() on Windows
#include <string>
#include <fstream> // std::ifstream
#include <simgear/misc/test_macros.hxx>
#include <simgear/compiler.h>
#include <simgear/misc/strutils.hxx>
using std::string;
#include "strutils.hxx"
namespace strutils = simgear::strutils;
void test_strip()
BOOST_AUTO_TEST_CASE( strutils_functions )
{
string a("abcd");
SG_CHECK_EQUAL(strutils::strip(a), a);
SG_CHECK_EQUAL(strutils::strip(" a "), "a");
SG_CHECK_EQUAL(strutils::lstrip(" a "), "a ");
SG_CHECK_EQUAL(strutils::rstrip("\ta "), "\ta");
std::string a("abcd");
BOOST_CHECK_EQUAL(strutils::strip(a), a);
BOOST_CHECK_EQUAL(strutils::strip(" a "), "a");
BOOST_CHECK_EQUAL(strutils::lstrip(" a "), "a ");
BOOST_CHECK_EQUAL(strutils::rstrip("\ta "), "\ta");
// Check internal spacing is preserved
SG_CHECK_EQUAL(strutils::strip("\t \na \t b\r \n "), "a \t b");
}
// check internal spacing is preserved
BOOST_CHECK_EQUAL(strutils::strip("\t \na \t b\r \n "), "a \t b");
void test_stripTrailingNewlines()
{
SG_CHECK_EQUAL(strutils::stripTrailingNewlines("\rfoobar\n\r\n\n\r\r"),
"\rfoobar");
SG_CHECK_EQUAL(strutils::stripTrailingNewlines("\rfoobar\r"), "\rfoobar");
SG_CHECK_EQUAL(strutils::stripTrailingNewlines("\rfoobar"), "\rfoobar");
SG_CHECK_EQUAL(strutils::stripTrailingNewlines(""), "");
}
void test_stripTrailingNewlines_inplace()
{
string s = "\r\n\r\rfoo\n\r\rbar\n\r\n\r\r\n\r\r";
strutils::stripTrailingNewlines_inplace(s);
SG_CHECK_EQUAL(s, "\r\n\r\rfoo\n\r\rbar");
BOOST_CHECK(strutils::starts_with("banana", "ban"));
BOOST_CHECK(!strutils::starts_with("abanana", "ban"));
BOOST_CHECK(strutils::starts_with("banana", "banana")); // pass - string starts with itself
BOOST_CHECK(!strutils::starts_with("ban", "banana")); // fail - original string is prefix of
s = "\rfoobar\r";
strutils::stripTrailingNewlines_inplace(s);
SG_CHECK_EQUAL(s, "\rfoobar");
BOOST_CHECK(strutils::ends_with("banana", "ana"));
BOOST_CHECK(strutils::ends_with("foo.text", ".text"));
BOOST_CHECK(!strutils::ends_with("foo.text", ".html"));
s = "\rfoobar";
strutils::stripTrailingNewlines_inplace(s);
SG_CHECK_EQUAL(s, "\rfoobar");
BOOST_CHECK_EQUAL(strutils::simplify("\ta\t b \nc\n\r \r\n"), "a b c");
BOOST_CHECK_EQUAL(strutils::simplify("The quick - brown dog!"), "The quick - brown dog!");
BOOST_CHECK_EQUAL(strutils::simplify("\r\n \r\n \t \r"), "");
s = "";
strutils::stripTrailingNewlines_inplace(s);
SG_CHECK_EQUAL(s, "");
}
BOOST_CHECK_EQUAL(strutils::to_int("999"), 999);
BOOST_CHECK_EQUAL(strutils::to_int("0000000"), 0);
BOOST_CHECK_EQUAL(strutils::to_int("-10000"), -10000);
void test_starts_with()
{
SG_VERIFY(strutils::starts_with("banana", "ban"));
SG_VERIFY(!strutils::starts_with("abanana", "ban"));
// Pass - string starts with itself
SG_VERIFY(strutils::starts_with("banana", "banana"));
// Fail - original string is prefix of
SG_VERIFY(!strutils::starts_with("ban", "banana"));
}
void test_ends_with()
{
SG_VERIFY(strutils::ends_with("banana", "ana"));
SG_VERIFY(strutils::ends_with("foo.text", ".text"));
SG_VERIFY(!strutils::ends_with("foo.text", ".html"));
}
void test_simplify()
{
SG_CHECK_EQUAL(strutils::simplify("\ta\t b \nc\n\r \r\n"), "a b c");
SG_CHECK_EQUAL(strutils::simplify("The quick - brown dog!"),
"The quick - brown dog!");
SG_CHECK_EQUAL(strutils::simplify("\r\n \r\n \t \r"), "");
}
void test_to_int()
{
SG_CHECK_EQUAL(strutils::to_int("999"), 999);
SG_CHECK_EQUAL(strutils::to_int("0000000"), 0);
SG_CHECK_EQUAL(strutils::to_int("-10000"), -10000);
}
void test_split()
{
string_list l = strutils::split("zero one two three four five");
SG_CHECK_EQUAL(l[2], "two");
SG_CHECK_EQUAL(l[5], "five");
SG_CHECK_EQUAL(l.size(), 6);
BOOST_CHECK_EQUAL(l[2], "two");
BOOST_CHECK_EQUAL(l[5], "five");
BOOST_CHECK_EQUAL(l.size(), 6);
string j = strutils::join(l, "&");
SG_CHECK_EQUAL(j, "zero&one&two&three&four&five");
std::string j = strutils::join(l, "&");
BOOST_CHECK_EQUAL(j, "zero&one&two&three&four&five");
l = strutils::split("alpha:beta:gamma:delta", ":", 2);
SG_CHECK_EQUAL(l.size(), 3);
SG_CHECK_EQUAL(l[0], "alpha");
SG_CHECK_EQUAL(l[1], "beta");
SG_CHECK_EQUAL(l[2], "gamma:delta");
BOOST_CHECK_EQUAL(l.size(), 3);
BOOST_CHECK_EQUAL(l[0], "alpha");
BOOST_CHECK_EQUAL(l[1], "beta");
BOOST_CHECK_EQUAL(l[2], "gamma:delta");
l = strutils::split("", ",");
SG_CHECK_EQUAL(l.size(), 1);
SG_CHECK_EQUAL(l[0], "");
BOOST_CHECK_EQUAL(l.size(), 1);
BOOST_CHECK_EQUAL(l[0], "");
l = strutils::split(",", ",");
SG_CHECK_EQUAL(l.size(), 2);
SG_CHECK_EQUAL(l[0], "");
SG_CHECK_EQUAL(l[1], "");
BOOST_CHECK_EQUAL(l.size(), 2);
BOOST_CHECK_EQUAL(l[0], "");
BOOST_CHECK_EQUAL(l[1], "");
l = strutils::split(",,", ",");
SG_CHECK_EQUAL(l.size(), 3);
SG_CHECK_EQUAL(l[0], "");
SG_CHECK_EQUAL(l[1], "");
SG_CHECK_EQUAL(l[2], "");
BOOST_CHECK_EQUAL(l.size(), 3);
BOOST_CHECK_EQUAL(l[0], "");
BOOST_CHECK_EQUAL(l[1], "");
BOOST_CHECK_EQUAL(l[2], "");
l = strutils::split(" ", ",");
SG_CHECK_EQUAL(l.size(), 1);
SG_CHECK_EQUAL(l[0], " ");
BOOST_CHECK_EQUAL(l.size(), 1);
BOOST_CHECK_EQUAL(l[0], " ");
const char* testCases[] = {
"alpha,bravo, charlie\tdelta\n\recho, \t\r\nfoxtrot golf,\n\t\n \n",
" alpha bravo \t charlie,,,delta echo\nfoxtrot\rgolf"
};
for (const char* data: testCases) {
l = strutils::split_on_any_of(data, "\n\t\r ,");
SG_CHECK_EQUAL(l.size(), 7);
SG_CHECK_EQUAL(l[0], "alpha");
SG_CHECK_EQUAL(l[1], "bravo");
SG_CHECK_EQUAL(l[2], "charlie");
SG_CHECK_EQUAL(l[3], "delta");
SG_CHECK_EQUAL(l[4], "echo");
SG_CHECK_EQUAL(l[5], "foxtrot");
SG_CHECK_EQUAL(l[6], "golf");
}
BOOST_CHECK_EQUAL(strutils::unescape("\\ \\n\\t\\x41\\117a"), " \n\tAOa");
}
void test_unescape()
BOOST_AUTO_TEST_CASE( compare_versions )
{
SG_CHECK_EQUAL(strutils::unescape("\\ \\n\\t\\x41\\117a"), " \n\tAOa");
BOOST_CHECK_LT(strutils::compare_versions("1.0.12", "1.1"), 0);
BOOST_CHECK_GT(strutils::compare_versions("1.1", "1.0.12"), 0);
BOOST_CHECK_EQUAL(strutils::compare_versions("10.6.7", "10.6.7"), 0);
BOOST_CHECK_LT(strutils::compare_versions("2.0", "2.0.99"), 0);
BOOST_CHECK_EQUAL(strutils::compare_versions("99", "99"), 0);
BOOST_CHECK_GT(strutils::compare_versions("99", "98"), 0);
// since we compare numerically, leasing zeros shouldn't matter
BOOST_CHECK_EQUAL(strutils::compare_versions("0.06.7", "0.6.07"), 0);
}
void test_compare_versions()
{
SG_CHECK_LT(strutils::compare_versions("1.0.12", "1.1"), 0);
SG_CHECK_GT(strutils::compare_versions("1.1", "1.0.12"), 0);
SG_CHECK_EQUAL(strutils::compare_versions("10.6.7", "10.6.7"), 0);
SG_CHECK_LT(strutils::compare_versions("2.0", "2.0.99"), 0);
SG_CHECK_EQUAL(strutils::compare_versions("99", "99"), 0);
SG_CHECK_GT(strutils::compare_versions("99", "98"), 0);
// Since we compare numerically, leading zeros shouldn't matter
SG_CHECK_EQUAL(strutils::compare_versions("0.06.7", "0.6.07"), 0);
SG_CHECK_EQUAL(strutils::compare_versions("10.6.7", "10.6.8", 2), 0);
SG_CHECK_GT(strutils::compare_versions("10.7.7", "10.6.8", 2), 0);
SG_CHECK_EQUAL(strutils::compare_versions("10.8.7", "10.6.8", 1), 0);
}
void test_md5_hex()
BOOST_AUTO_TEST_CASE( md5_hex )
{
// hex encoding
unsigned char raw_data[] = {0x0f, 0x1a, 0xbc, 0xd2, 0xe3, 0x45, 0x67, 0x89};
const string& hex_data =
const std::string& hex_data =
strutils::encodeHex(raw_data, sizeof(raw_data)/sizeof(raw_data[0]));
SG_CHECK_EQUAL(hex_data, "0f1abcd2e3456789");
SG_CHECK_EQUAL(strutils::encodeHex("abcde"), "6162636465");
BOOST_REQUIRE_EQUAL(hex_data, "0f1abcd2e3456789");
BOOST_REQUIRE_EQUAL(strutils::encodeHex("abcde"), "6162636465");
// md5
SG_CHECK_EQUAL(strutils::md5("test"), "098f6bcd4621d373cade4e832627b4f6");
BOOST_CHECK_EQUAL(strutils::md5("test"), "098f6bcd4621d373cade4e832627b4f6");
}
void test_error_string()
BOOST_AUTO_TEST_CASE( error_string )
{
#if defined(_WIN32)
_set_errno(0);
@@ -191,25 +118,7 @@ void test_error_string()
int saved_errno = errno;
#endif
SG_VERIFY(!f.is_open());
SG_CHECK_NE(saved_errno, 0);
SG_CHECK_GT(strutils::error_string(saved_errno).size(), 0);
}
int main(int argc, char* argv[])
{
test_strip();
test_stripTrailingNewlines();
test_stripTrailingNewlines_inplace();
test_starts_with();
test_ends_with();
test_simplify();
test_to_int();
test_split();
test_unescape();
test_compare_versions();
test_md5_hex();
test_error_string();
return EXIT_SUCCESS;
BOOST_CHECK(!f.is_open());
BOOST_CHECK_NE(saved_errno, 0);
BOOST_CHECK_GT(strutils::error_string(saved_errno).size(), 0);
}

View File

@@ -1,179 +1,42 @@
// -*- coding: utf-8 -*-
#ifndef SG_MISC_TEST_MACROS_HXX
#define SG_MISC_TEST_MACROS_HXX
#include <iostream>
#include <cmath> // for std::fabs()
#include <cmath> // for fabs()
#define COMPARE(a, b) \
if ((a) != (b)) { \
std::cerr << "failed:" << #a << " != " << #b << std::endl; \
std::cerr << "\tgot:'" << a << "'" << std::endl; \
std::cerr << "\tat:" << __FILE__ << ":" << __LINE__ << std::endl; \
exit(1); \
}
// Assertion-like test
#define SG_VERIFY(a) \
#define VERIFY(a) \
if (!(a)) { \
std::cerr << "failed: " << #a << std::endl; \
std::cerr << "\tat " << __FILE__ << ":" << __LINE__ << std::endl; \
std::cerr << "failed:" << #a << std::endl; \
std::cerr << "\tat:" << __FILE__ << ":" << __LINE__ << std::endl; \
exit(1); \
}
// Internal macro (implementation detail). 'a' and 'b' don't always support
// operator<<. This is why we use (a0) and (b0) after '<<', otherwise in such
// cases the test couldn't be compiled, even if 'stream' is false.
#define SG_CHECK_EQUAL0(a, b, stream, a0, b0) \
if ( !((a) == (b)) ) { \
std::cerr << "failed: " << #a << " == " << #b << std::endl; \
if (stream) { \
std::cerr << "\tgot '" << (a0) << "' and '" << (b0) << "'" \
<< std::endl; \
}; \
std::cerr << "\tat " << __FILE__ << ":" << __LINE__ << std::endl; \
#define COMPARE_EP(a, b) \
if (fabs(a - b) > SG_EPSILON) { \
std::cerr << "failed with epsilon:" << #a << " != " << #b << std::endl; \
std::cerr << "\tgot:'" << a << "'" << std::endl; \
std::cerr << "\tat:" << __FILE__ << ":" << __LINE__ << std::endl; \
exit(1); \
}
// Test macros for user consumption. The _NOSTREAM variant is for cases where
// 'a' or 'b' doesn't support operator<< (or where using it would have
// undesirable side effects).
#define SG_CHECK_EQUAL(a, b) SG_CHECK_EQUAL0((a), (b), true, (a), (b))
#define SG_CHECK_EQUAL_NOSTREAM(a, b) SG_CHECK_EQUAL0((a), (b), false, "", "")
// “Approximate equality” test (EP stands for “epsilon”)
#define SG_CHECK_EQUAL_EP0(a, b, stream, a0, b0) \
if (std::fabs((a) - (b)) > SG_EPSILON) { \
std::cerr << "failed: " << #a << " ~= " << #b << std::endl; \
if (stream) { \
std::cerr << "\tgot '" << (a0) << "' and '" << (b0) << "'" \
<< std::endl; \
}; \
std::cerr << "\tat " << __FILE__ << ":" << __LINE__ << std::endl; \
#define COMPARE_EP2(a, b, ep) \
if (fabs(a - b) > ep) { \
std::cerr << "failed with epsilon:" << #a << " != " << #b << std::endl; \
std::cerr << "\tgot:'" << a << "'" << std::endl; \
std::cerr << "\tat:" << __FILE__ << ":" << __LINE__ << std::endl; \
exit(1); \
}
#define SG_CHECK_EQUAL_EP(a, b) SG_CHECK_EQUAL_EP0((a), (b), true, (a), (b))
#define SG_CHECK_EQUAL_EP_NOSTREAM(a, b) \
SG_CHECK_EQUAL_EP0((a), (b), false, "", "")
// Same as SG_CHECK_EQUAL_EP*, except the “epsilon” can be chosen by the caller
#define SG_CHECK_EQUAL_EP2_0(a, b, ep, stream, a0, b0) \
if (std::fabs((a) - (b)) > (ep)) { \
std::cerr << "failed: " << #a << " ~= " << #b << std::endl; \
if (stream) { \
std::cerr << "\tgot '" << (a0) << "' and '" << (b0) << "'" \
<< std::endl; \
}; \
std::cerr << "\tat " << __FILE__ << ":" << __LINE__ << std::endl; \
exit(1); \
}
#define SG_CHECK_EQUAL_EP2(a, b, ep) \
SG_CHECK_EQUAL_EP2_0((a), (b), ep, true, (a), (b))
#define SG_CHECK_EQUAL_EP2_NOSTREAM(a, b) \
SG_CHECK_EQUAL_EP2_0((a), (b), ep, false, "", "")
// “Non-equal” test
#define SG_CHECK_NE0(a, b, stream, a0, b0) \
if ( !((a) != (b)) ) { \
std::cerr << "failed: " << #a << " != " << #b << std::endl; \
if (stream) { \
std::cerr << "\tgot '" << (a0) << "' and '" << (b0) << "'" \
<< std::endl; \
}; \
std::cerr << "\tat " << __FILE__ << ":" << __LINE__ << std::endl; \
exit(1); \
}
#define SG_CHECK_NE(a, b) SG_CHECK_NE0((a), (b), true, (a), (b))
#define SG_CHECK_NE_NOSTREAM(a, b) SG_CHECK_NE0((a), (b), false, "", "")
// “Lower than” test
#define SG_CHECK_LT0(a, b, stream, a0, b0) \
if ( !((a) < (b)) ) { \
std::cerr << "failed: " << #a << " < " << #b << std::endl; \
if (stream) { \
std::cerr << "\tgot '" << (a0) << "' and '" << (b0) << "'" \
<< std::endl; \
}; \
std::cerr << "\tat " << __FILE__ << ":" << __LINE__ << std::endl; \
exit(1); \
}
#define SG_CHECK_LT(a, b) SG_CHECK_LT0((a), (b), true, (a), (b))
#define SG_CHECK_LT_NOSTREAM(a, b) SG_CHECK_LT0((a), (b), false, "", "")
// “Lower than or equal” test
#define SG_CHECK_LE0(a, b, stream, a0, b0) \
if ( !((a) <= (b)) ) { \
std::cerr << "failed: " << #a << " <= " << #b << std::endl; \
if (stream) { \
std::cerr << "\tgot '" << (a0) << "' and '" << (b0) << "'" \
<< std::endl; \
}; \
std::cerr << "\tat " << __FILE__ << ":" << __LINE__ << std::endl; \
exit(1); \
}
#define SG_CHECK_LE(a, b) SG_CHECK_LE0((a), (b), true, (a), (b))
#define SG_CHECK_LE_NOSTREAM(a, b) SG_CHECK_LE0((a), (b), false, "", "")
// “Greater than” test
#define SG_CHECK_GT0(a, b, stream, a0, b0) \
if ( !((a) > (b)) ) { \
std::cerr << "failed: " << #a << " > " << #b << std::endl; \
if (stream) { \
std::cerr << "\tgot '" << (a0) << "' and '" << (b0) << "'" \
<< std::endl; \
}; \
std::cerr << "\tat " << __FILE__ << ":" << __LINE__ << std::endl; \
exit(1); \
}
#define SG_CHECK_GT(a, b) SG_CHECK_GT0((a), (b), true, (a), (b))
#define SG_CHECK_GT_NOSTREAM(a, b) SG_CHECK_GT0((a), (b), false, "", "")
// “Greater than or equal” test
#define SG_CHECK_GE0(a, b, stream, a0, b0) \
if ( !((a) >= (b)) ) { \
std::cerr << "failed: " << #a << " >= " << #b << std::endl; \
if (stream) { \
std::cerr << "\tgot '" << (a0) << "' and '" << (b0) << "'" \
<< std::endl; \
}; \
std::cerr << "\tat " << __FILE__ << ":" << __LINE__ << std::endl; \
exit(1); \
}
#define SG_CHECK_GE(a, b) SG_CHECK_GE0((a), (b), true, (a), (b))
#define SG_CHECK_GE_NOSTREAM(a, b) SG_CHECK_GE0((a), (b), false, "", "")
// “Is NULL” test
#define SG_CHECK_IS_NULL0(a, stream, a0) \
if ( !((a) == nullptr) ) { \
std::cerr << "failed: " << #a << " == nullptr" << std::endl; \
if (stream) { \
std::cerr << "\tgot '" << (a0) << "'" << std::endl; \
}; \
std::cerr << "\tat " << __FILE__ << ":" << __LINE__ << std::endl; \
exit(1); \
}
#define SG_CHECK_IS_NULL(a) SG_CHECK_IS_NULL0((a), true, (a))
#define SG_CHECK_IS_NULL_NOSTREAM(a) SG_CHECK_IS_NULL0((a), false, "")
// “Is not NULL” test
#define SG_CHECK_IS_NOT_NULL0(a, stream, a0) \
if ( !((a) != nullptr) ) { \
std::cerr << "failed: " << #a << " != nullptr" << std::endl; \
if (stream) { \
std::cerr << "\tgot '" << (a0) << "'" << std::endl; \
}; \
std::cerr << "\tat " << __FILE__ << ":" << __LINE__ << std::endl; \
exit(1); \
}
#define SG_CHECK_IS_NOT_NULL(a) SG_CHECK_IS_NOT_NULL0((a), true, (a))
#define SG_CHECK_IS_NOT_NULL_NOSTREAM(a) SG_CHECK_IS_NOT_NULL0((a), false, "")
// “Always failing” test
#define SG_TEST_FAIL(msg) \
std::cerr << "failure: " << msg; \
std::cerr << "failure:" << msg; \
exit(1);

View File

@@ -34,7 +34,7 @@
#include <simgear/structure/exception.hxx>
#include <zlib.h>
#include "gzfstream.hxx"
#include "zfstream.hxx"
//
// Construct a gzfilebuf object.

View File

@@ -1,5 +1,5 @@
/**
* \file gzfstream.hxx
* \file zfstream.hxx
* A C++ I/O streams interface to the zlib gz* functions.
*/
@@ -24,8 +24,8 @@
//
// $Id$
#ifndef _gzfstream_hxx
#define _gzfstream_hxx
#ifndef _zfstream_hxx
#define _zfstream_hxx
#include <simgear/compiler.h>
@@ -164,4 +164,4 @@ struct gzofstream_base
gzfilebuf gzbuf;
};
#endif // _gzfstream_hxx
#endif // _zfstream_hxx

View File

@@ -1200,7 +1200,7 @@ namespace nasal
return ctx;
};
typedef std::unique_ptr<Ghost> GhostPtr;
typedef std::auto_ptr<Ghost> GhostPtr;
MemberMap _members;
fallback_getter_t _fallback_getter;
fallback_setter_t _fallback_setter;

View File

@@ -27,7 +27,7 @@
#include <simgear/io/HTTPRequest.hxx>
#include <simgear/io/HTTPClient.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/package/Package.hxx>
#include <simgear/package/Root.hxx>

View File

@@ -126,55 +126,46 @@ int parseTest()
pkg::Root* root = new pkg::Root(rootPath, "8.1.12");
pkg::CatalogRef cat = pkg::Catalog::createFromPath(root, SGPath(SRC_DIR "/catalogTest1"));
SG_VERIFY(cat.valid());
VERIFY(cat.valid());
SG_CHECK_EQUAL(cat->id(), "org.flightgear.test.catalog1");
SG_CHECK_EQUAL(cat->url(), "http://localhost:2000/catalogTest1/catalog.xml");
SG_CHECK_EQUAL(cat->description(), "First test catalog");
COMPARE(cat->id(), "org.flightgear.test.catalog1");
COMPARE(cat->url(), "http://localhost:2000/catalogTest1/catalog.xml");
COMPARE(cat->description(), "First test catalog");
// check the packages too
SG_CHECK_EQUAL(cat->packages().size(), 4);
COMPARE(cat->packages().size(), 4);
pkg::PackageRef p1 = cat->packages().front();
SG_CHECK_EQUAL(p1->catalog(), cat.ptr());
COMPARE(p1->catalog(), cat.ptr());
SG_CHECK_EQUAL(p1->id(), "alpha");
SG_CHECK_EQUAL(p1->qualifiedId(), "org.flightgear.test.catalog1.alpha");
SG_CHECK_EQUAL(p1->name(), "Alpha package");
SG_CHECK_EQUAL(p1->revision(), 8);
SG_CHECK_EQUAL(p1->fileSizeBytes(), 593);
COMPARE(p1->id(), "alpha");
COMPARE(p1->qualifiedId(), "org.flightgear.test.catalog1.alpha");
COMPARE(p1->name(), "Alpha package");
COMPARE(p1->revision(), 8);
COMPARE(p1->fileSizeBytes(), 593);
pkg::PackageRef p2 = cat->getPackageById("c172p");
SG_VERIFY(p2.valid());
SG_CHECK_EQUAL(p2->qualifiedId(), "org.flightgear.test.catalog1.c172p");
SG_CHECK_EQUAL(p2->description(), "A plane made by Cessna on Jupiter");
VERIFY(p2.valid());
COMPARE(p2->qualifiedId(), "org.flightgear.test.catalog1.c172p");
COMPARE(p2->description(), "A plane made by Cessna");
pkg::Package::PreviewVec thumbs = p2->previewsForVariant(0);
SG_CHECK_EQUAL(thumbs.size(), 3);
pkg::Package::ThumbnailVec thumbs = p2->thumbnailsForVariant(0);
COMPARE(thumbs.size(), 3);
auto index = std::find_if(thumbs.begin(), thumbs.end(), [](const pkg::Package::Preview& t)
{ return (t.type == pkg::Package::Preview::Type::EXTERIOR); });
SG_VERIFY(index != thumbs.end());
SG_CHECK_EQUAL(index->path, "thumb-exterior.png");
SG_CHECK_EQUAL(index->url, "http://foo.bar.com/thumb-exterior.png");
SG_VERIFY(index->type == pkg::Package::Preview::Type::EXTERIOR);
auto index = std::find_if(thumbs.begin(), thumbs.end(), [](const pkg::Package::Thumbnail& t)
{ return (t.type == pkg::Package::Thumbnail::Type::EXTERIOR); });
VERIFY(index != thumbs.end());
COMPARE(index->path, "thumb-exterior.png");
COMPARE(index->url, "http://foo.bar.com/thumb-exterior.png");
VERIFY(index->type == pkg::Package::Thumbnail::Type::EXTERIOR);
index = std::find_if(thumbs.begin(), thumbs.end(), [](const pkg::Package::Preview& t)
{ return (t.type == pkg::Package::Preview::Type::PANEL); });
SG_VERIFY(index != thumbs.end());
SG_CHECK_EQUAL(index->path, "thumb-panel.png");
SG_CHECK_EQUAL(index->url, "http://foo.bar.com/thumb-panel.png");
SG_VERIFY(index->type == pkg::Package::Preview::Type::PANEL);
// old-style thumbnails
string_list oldThumbUrls = p2->thumbnailUrls();
SG_CHECK_EQUAL(oldThumbUrls.size(), 1);
SG_CHECK_EQUAL(oldThumbUrls.at(0), "http://foo.bar.com/thumb-exterior.png");
string_list oldThumbPaths = p2->thumbnails();
SG_CHECK_EQUAL(oldThumbPaths.size(), 1);
SG_CHECK_EQUAL(oldThumbPaths.at(0), "exterior.png");
index = std::find_if(thumbs.begin(), thumbs.end(), [](const pkg::Package::Thumbnail& t)
{ return (t.type == pkg::Package::Thumbnail::Type::PANEL); });
VERIFY(index != thumbs.end());
COMPARE(index->path, "thumb-panel.png");
COMPARE(index->url, "http://foo.bar.com/thumb-panel.png");
VERIFY(index->type == pkg::Package::Thumbnail::Type::PANEL);
// test variants
try {
@@ -185,85 +176,54 @@ int parseTest()
}
unsigned int skisVariantFull = p2->indexOfVariant("org.flightgear.test.catalog1.c172p-skis");
SG_VERIFY(skisVariantFull > 0);
VERIFY(skisVariantFull > 0);
unsigned int skisVariant = p2->indexOfVariant("c172p-skis");
SG_VERIFY(skisVariant > 0);
VERIFY(skisVariant > 0);
SG_CHECK_EQUAL(skisVariant, skisVariantFull);
COMPARE(skisVariant, skisVariantFull);
SG_CHECK_EQUAL(p2->getLocalisedProp("description", skisVariant),
"A plane with skis");
SG_CHECK_EQUAL(p2->getLocalisedProp("author", skisVariant),
"Standard author");
pkg::Package::ThumbnailVec thumbs2 = p2->thumbnailsForVariant(skisVariant);
COMPARE(thumbs2.size(), 2);
unsigned int floatsVariant = p2->indexOfVariant("c172p-floats");
SG_VERIFY(floatsVariant > 0);
SG_CHECK_EQUAL(p2->getLocalisedProp("description", floatsVariant),
"A plane with floats");
SG_CHECK_EQUAL(p2->getLocalisedProp("author", floatsVariant),
"Floats variant author");
pkg::Package::PreviewVec thumbs2 = p2->previewsForVariant(skisVariant);
SG_CHECK_EQUAL(thumbs2.size(), 2);
index = std::find_if(thumbs2.begin(), thumbs2.end(), [](const pkg::Package::Preview& t)
{ return (t.type == pkg::Package::Preview::Type::EXTERIOR); });
SG_VERIFY(index != thumbs2.end());
SG_CHECK_EQUAL(index->path, "thumb-exterior-skis.png");
SG_CHECK_EQUAL(index->url, "http://foo.bar.com/thumb-exterior-skis.png");
SG_VERIFY(index->type == pkg::Package::Preview::Type::EXTERIOR);
index = std::find_if(thumbs2.begin(), thumbs2.end(), [](const pkg::Package::Thumbnail& t)
{ return (t.type == pkg::Package::Thumbnail::Type::EXTERIOR); });
VERIFY(index != thumbs2.end());
COMPARE(index->path, "thumb-exterior-skis.png");
COMPARE(index->url, "http://foo.bar.com/thumb-exterior-skis.png");
VERIFY(index->type == pkg::Package::Thumbnail::Type::EXTERIOR);
// test filtering / searching too
string_set tags(p2->tags());
SG_CHECK_EQUAL(tags.size(), 4);
SG_VERIFY(tags.find("ifr") != tags.end());
SG_VERIFY(tags.find("cessna") != tags.end());
SG_VERIFY(tags.find("jet") == tags.end());
COMPARE(tags.size(), 4);
VERIFY(tags.find("ifr") != tags.end());
VERIFY(tags.find("cessna") != tags.end());
VERIFY(tags.find("jet") == tags.end());
SGPropertyNode_ptr queryA(new SGPropertyNode);
queryA->setStringValue("tag", "ifr");
SG_VERIFY(p2->matches(queryA.ptr()));
VERIFY(p2->matches(queryA.ptr()));
SGPropertyNode_ptr queryB(new SGPropertyNode);
queryB->setStringValue("name", "ces");
SG_VERIFY(p2->matches(queryB.ptr()));
VERIFY(p2->matches(queryB.ptr()));
SGPropertyNode_ptr queryC(new SGPropertyNode);
queryC->setStringValue("name", "foo");
SG_VERIFY(!p2->matches(queryC.ptr()));
VERIFY(!p2->matches(queryC.ptr()));
SGPropertyNode_ptr queryD(new SGPropertyNode);
queryD->setIntValue("rating-FDM", 3);
SG_VERIFY(p2->matches(queryD.ptr()));
VERIFY(p2->matches(queryD.ptr()));
SGPropertyNode_ptr queryE(new SGPropertyNode);
queryE->setIntValue("rating-model", 5);
queryE->setStringValue("description", "cessna");
SG_VERIFY(p2->matches(queryE.ptr()));
VERIFY(p2->matches(queryE.ptr()));
{
SGPropertyNode_ptr queryText(new SGPropertyNode);
queryText->setStringValue("any-of/text", "jupiter");
SG_VERIFY(p2->matches(queryText.ptr()));
}
{
SGPropertyNode_ptr queryText(new SGPropertyNode);
queryText->setStringValue("any-of/tag", "twin-engine");
queryText->setStringValue("any-of/tag", "ga");
SG_VERIFY(p2->matches(queryText.ptr()));
}
// match variant descriptions
{
SGPropertyNode_ptr queryText(new SGPropertyNode);
queryText->setStringValue("any-of/description", "float");
SG_VERIFY(p2->matches(queryText.ptr()));
}
delete root;
return EXIT_SUCCESS;
}
@@ -289,16 +249,16 @@ void testAddCatalog(HTTP::Client* cl)
SGPath p(rootPath);
p.append("org.flightgear.test.catalog1");
p.append("catalog.xml");
SG_VERIFY(p.exists());
SG_CHECK_EQUAL(root->allPackages().size(), 4);
SG_CHECK_EQUAL(root->catalogs().size(), 1);
VERIFY(p.exists());
COMPARE(root->allPackages().size(), 4);
COMPARE(root->catalogs().size(), 1);
pkg::PackageRef p1 = root->getPackageById("alpha");
SG_CHECK_EQUAL(p1->id(), "alpha");
COMPARE(p1->id(), "alpha");
pkg::PackageRef p2 = root->getPackageById("org.flightgear.test.catalog1.c172p");
SG_CHECK_EQUAL(p2->id(), "c172p");
SG_CHECK_EQUAL(p2->qualifiedId(), "org.flightgear.test.catalog1.c172p");
COMPARE(p2->id(), "c172p");
COMPARE(p2->qualifiedId(), "org.flightgear.test.catalog1.c172p");
}
@@ -319,14 +279,14 @@ void testInstallPackage(HTTP::Client* cl)
pkg::PackageRef p1 = root->getPackageById("org.flightgear.test.catalog1.c172p");
pkg::InstallRef ins = p1->install();
SG_VERIFY(ins->isQueued());
VERIFY(ins->isQueued());
waitForUpdateComplete(cl, root);
SG_VERIFY(p1->isInstalled());
SG_VERIFY(p1->existingInstall() == ins);
VERIFY(p1->isInstalled());
VERIFY(p1->existingInstall() == ins);
pkg::PackageRef commonDeps = root->getPackageById("common-sounds");
SG_VERIFY(commonDeps->existingInstall());
VERIFY(commonDeps->existingInstall());
// verify on disk state
SGPath p(rootPath);
@@ -334,17 +294,17 @@ void testInstallPackage(HTTP::Client* cl)
p.append("Aircraft");
p.append("c172p");
SG_CHECK_EQUAL(p, ins->path());
COMPARE(p, ins->path());
p.append("c172p-floats-set.xml");
SG_VERIFY(p.exists());
VERIFY(p.exists());
SGPath p2(rootPath);
p2.append("org.flightgear.test.catalog1");
p2.append("Aircraft");
p2.append("sounds");
p2.append("sharedfile.txt");
SG_VERIFY(p2.exists());
VERIFY(p2.exists());
}
void testUninstall(HTTP::Client* cl)
@@ -365,11 +325,11 @@ void testUninstall(HTTP::Client* cl)
waitForUpdateComplete(cl, root);
SG_VERIFY(p1->isInstalled());
VERIFY(p1->isInstalled());
ins->uninstall();
SG_VERIFY(!ins->path().exists());
VERIFY(!ins->path().exists());
}
void testRemoveCatalog(HTTP::Client* cl)
@@ -393,7 +353,7 @@ void testRemoveCatalog(HTTP::Client* cl)
waitForUpdateComplete(cl, root);
SG_VERIFY(p1->isInstalled());
VERIFY(p1->isInstalled());
}
root->removeCatalogById("org.flightgear.test.catalog1");
@@ -401,13 +361,13 @@ void testRemoveCatalog(HTTP::Client* cl)
SGPath p2(rootPath);
p2.append("org.flightgear.test.catalog1");
SG_VERIFY(!p2.exists());
VERIFY(!p2.exists());
SG_VERIFY(root->allPackages().empty());
SG_VERIFY(root->catalogs().empty());
VERIFY(root->allPackages().empty());
VERIFY(root->catalogs().empty());
pkg::CatalogRef c = root->getCatalogById("org.flightgear.test.catalog1");
SG_CHECK_EQUAL(c, pkg::CatalogRef());
COMPARE(c, pkg::CatalogRef());
}
template <class T>
@@ -441,39 +401,37 @@ void testRefreshCatalog(HTTP::Client* cl)
waitForUpdateComplete(cl, root);
SG_VERIFY(p1->isInstalled());
SG_VERIFY(p2->isInstalled());
VERIFY(p1->isInstalled());
VERIFY(p2->isInstalled());
}
SG_VERIFY(root->packagesNeedingUpdate().empty());
VERIFY(root->packagesNeedingUpdate().empty());
global_catalogVersion = 2;
SG_VERIFY(!cl->hasActiveRequests());
VERIFY(!cl->hasActiveRequests());
root->refresh();
// should be a no-op due to catalog age testing
SG_VERIFY(!cl->hasActiveRequests());
VERIFY(!cl->hasActiveRequests());
// force it this time
root->refresh(true);
SG_VERIFY(cl->hasActiveRequests());
VERIFY(cl->hasActiveRequests());
waitForUpdateComplete(cl, root);
pkg::CatalogRef c = root->getCatalogById("org.flightgear.test.catalog1");
SG_CHECK_EQUAL(c->ageInSeconds(), 0);
COMPARE(c->ageInSeconds(), 0);
SG_VERIFY(root->getPackageById("dc3") != pkg::PackageRef());
SG_CHECK_EQUAL(root->packagesNeedingUpdate().size(), 2);
VERIFY(root->getPackageById("dc3") != pkg::PackageRef());
COMPARE(root->packagesNeedingUpdate().size(), 2);
pkg::PackageList needingUpdate = root->packagesNeedingUpdate();
SG_VERIFY(contains(needingUpdate, root->getPackageById("common-sounds")));
SG_VERIFY(
contains(needingUpdate,
root->getPackageById("org.flightgear.test.catalog1.alpha")));
VERIFY(contains(needingUpdate, root->getPackageById("common-sounds")));
VERIFY(contains(needingUpdate, root->getPackageById("org.flightgear.test.catalog1.alpha")));
pkg::InstallRef ins = root->getPackageById("alpha")->existingInstall();
SG_VERIFY(ins->hasUpdate());
VERIFY(ins->hasUpdate());
for (pkg::PackageList::const_iterator it = needingUpdate.begin();
it != needingUpdate.end(); ++it)
@@ -483,8 +441,8 @@ void testRefreshCatalog(HTTP::Client* cl)
waitForUpdateComplete(cl, root);
SG_VERIFY(root->packagesNeedingUpdate().empty());
SG_CHECK_EQUAL(root->getPackageById("common-sounds")->revision(), 11);
VERIFY(root->packagesNeedingUpdate().empty());
COMPARE(root->getPackageById("common-sounds")->revision(), 11);
}
void testInstallTarPackage(HTTP::Client* cl)
@@ -502,14 +460,14 @@ void testInstallTarPackage(HTTP::Client* cl)
waitForUpdateComplete(cl, root);
pkg::PackageRef p1 = root->getPackageById("org.flightgear.test.catalog1.b737-NG");
SG_CHECK_EQUAL(p1->id(), "b737-NG");
COMPARE(p1->id(), "b737-NG");
pkg::InstallRef ins = p1->install();
SG_VERIFY(ins->isQueued());
VERIFY(ins->isQueued());
waitForUpdateComplete(cl, root);
SG_VERIFY(p1->isInstalled());
SG_VERIFY(p1->existingInstall() == ins);
VERIFY(p1->isInstalled());
VERIFY(p1->existingInstall() == ins);
// verify on disk state
SGPath p(rootPath);
@@ -517,10 +475,10 @@ void testInstallTarPackage(HTTP::Client* cl)
p.append("Aircraft");
p.append("b737NG");
SG_CHECK_EQUAL(p, ins->path());
COMPARE(p, ins->path());
p.append("b737-900-set.xml");
SG_VERIFY(p.exists());
VERIFY(p.exists());
}

View File

@@ -33,7 +33,7 @@
#include <simgear/io/HTTPClient.hxx>
#include <simgear/misc/sg_dir.hxx>
#include <simgear/misc/strutils.hxx>
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/misc/sgstream.hxx>
extern "C" {
void fill_memory_filefunc (zlib_filefunc_def*);
@@ -152,11 +152,6 @@ protected:
return;
}
// disable caching on the owner's path, otherwise the upcoming
// delete & rename confuse everything
m_owner->m_path.set_cached(false);
m_extractPath.set_cached(false);
if (m_owner->path().exists()) {
Dir destDir(m_owner->path());
destDir.remove(true /* recursive */);

View File

@@ -48,113 +48,63 @@ void Package::initWithProps(const SGPropertyNode* aProps)
}
m_id = m_props->getStringValue("id");
m_variants.push_back(m_id);
for (auto var : m_props->getChildren("variant")) {
m_variants.push_back(var->getStringValue("id"));
}
}
void Package::updateFromProps(const SGPropertyNode* aProps)
{
m_tags.clear();
m_variants.clear();
initWithProps(aProps);
}
bool Package::matches(const SGPropertyNode* aFilter) const
{
const std::string& filter_name = aFilter->getNameString();
int nChildren = aFilter->nChildren();
for (int i = 0; i < nChildren; i++) {
const SGPropertyNode* c = aFilter->getChild(i);
const std::string& filter_name = c->getNameString();
if (filter_name == "any-of") {
const int anyChildren = aFilter->nChildren();
for (int j = 0; j < anyChildren; j++) {
const SGPropertyNode* anyChild = aFilter->getChild(j);
if (matches(anyChild)) {
return true;
}
}
return false; // none of our children matched
} else if (filter_name.empty() || (filter_name == "all-of")) {
const int allChildren = aFilter->nChildren();
for (int j = 0; j < allChildren; j++) {
const SGPropertyNode* allChild = aFilter->getChild(j);
if (!matches(allChild)) {
if (strutils::starts_with(filter_name, "rating-")) {
int minRating = c->getIntValue();
std::string rname = c->getName() + 7;
int ourRating = m_props->getChild("rating")->getIntValue(rname, 0);
if (ourRating < minRating) {
return false;
}
}
return true; // all of our children matched
}
if (strutils::starts_with(filter_name, "rating-")) {
int minRating = aFilter->getIntValue();
std::string rname = aFilter->getName() + 7;
int ourRating = m_props->getChild("rating")->getIntValue(rname, 0);
return (ourRating >= minRating);
}
if (filter_name == "tag") {
std::string tag(aFilter->getStringValue());
boost::to_lower(tag);
return (m_tags.find(tag) != m_tags.end());
}
if (filter_name == "installed") {
return (isInstalled() == aFilter->getBoolValue());
}
bool handled = false;
// substring search of name, description, across variants too
if ((filter_name == "text") || (filter_name == "name")) {
handled = true;
std::string n(aFilter->getStringValue());
boost::to_lower(n);
size_t pos = boost::to_lower_copy(name()).find(n);
if (pos != std::string::npos) {
return true;
}
for (auto var : m_props->getChildren("variant")) {
if (var->hasChild("name")) {
std::string variantName(var->getStringValue("name"));
boost::to_lower(variantName);
size_t pos = variantName.find(n);
if (pos != std::string::npos) {
return true;
}
}
}
}
if ((filter_name == "text") || (filter_name == "description")) {
handled = true;
std::string n(aFilter->getStringValue());
boost::to_lower(n);
size_t pos = boost::to_lower_copy(description()).find(n);
if (pos != std::string::npos) {
return true;
}
for (auto var : m_props->getChildren("variant")) {
if (var->hasChild("description")) {
std::string variantDesc(var->getStringValue("description"));
boost::to_lower(variantDesc);
size_t pos = variantDesc.find(n);
if (pos != std::string::npos) {
return true;
}
else if (filter_name == "tag") {
std::string tag(c->getStringValue());
boost::to_lower(tag);
if (m_tags.find(tag) == m_tags.end()) {
return false;
}
}
}
// substring search of name, description
else if (filter_name == "name") {
std::string n(c->getStringValue());
boost::to_lower(n);
size_t pos = boost::to_lower_copy(name()).find(n);
if (pos == std::string::npos) {
return false;
}
}
else if (filter_name == "description") {
std::string n(c->getStringValue());
boost::to_lower(n);
size_t pos = boost::to_lower_copy(description()).find(n);
if (pos == std::string::npos) {
return false;
}
}
else if (filter_name == "installed") {
if (isInstalled() != c->getBoolValue()) {
return false;
}
}
else
SG_LOG(SG_GENERAL, SG_WARN, "unknown filter term:" << filter_name);
} // of filter props iteration
if (!handled) {
SG_LOG(SG_GENERAL, SG_WARN, "unknown filter term:" << filter_name);
}
return false;
return true;
}
bool Package::isInstalled() const
@@ -219,10 +169,7 @@ std::string Package::qualifiedId() const
std::string Package::qualifiedVariantId(const unsigned int variantIndex) const
{
if (variantIndex >= m_variants.size()) {
throw sg_range_exception("invalid variant index " + std::to_string(variantIndex));
}
return m_catalog->id() + "." + m_variants[variantIndex];
return m_catalog->id() + "." + variants()[variantIndex];
}
std::string Package::md5() const
@@ -259,7 +206,7 @@ size_t Package::fileSizeBytes() const
std::string Package::description() const
{
return getLocalisedProp("description", 0);
return getLocalisedProp("description");
}
string_set Package::tags() const
@@ -311,9 +258,9 @@ string_list Package::downloadUrls() const
return r;
}
std::string Package::getLocalisedProp(const std::string& aName, const unsigned int vIndex) const
std::string Package::getLocalisedProp(const std::string& aName) const
{
return getLocalisedString(propsForVariant(vIndex, aName.c_str()), aName.c_str());
return getLocalisedString(m_props, aName.c_str());
}
std::string Package::getLocalisedString(const SGPropertyNode* aRoot, const char* aName) const
@@ -365,7 +312,14 @@ PackageList Package::dependencies() const
string_list Package::variants() const
{
return m_variants;
string_list result;
result.push_back(id());
BOOST_FOREACH(SGPropertyNode* var, m_props->getChildren("variant")) {
result.push_back(var->getStringValue("id"));
}
return result;
}
std::string Package::nameForVariant(const std::string& vid) const
@@ -397,67 +351,73 @@ unsigned int Package::indexOfVariant(const std::string& vid) const
actualId = vid.substr(lastDot + 1);
}
string_list::const_iterator it = std::find(m_variants.begin(), m_variants.end(), actualId);
if (it == m_variants.end()) {
throw sg_exception("Unknow variant " + vid + " in package " + id());
if (actualId == id()) {
return 0;
}
return std::distance(m_variants.begin(), it);
unsigned int result = 1;
for (SGPropertyNode* var : m_props->getChildren("variant")) {
if (var->getStringValue("id") == actualId) {
return result;
}
result++;
}
throw sg_exception("Unknow variant " + vid + " in package " + id());
}
std::string Package::nameForVariant(const unsigned int vIndex) const
{
return propsForVariant(vIndex, "name")->getStringValue("name");
}
SGPropertyNode_ptr Package::propsForVariant(const unsigned int vIndex, const char* propName) const
{
if (vIndex == 0) {
return m_props;
}
if (vIndex == 0)
return name();
// offset by minus one to allow for index 0 being the primary
SGPropertyNode_ptr var = m_props->getChild("variant", vIndex - 1);
if (var) {
if (!propName || var->hasChild(propName)) {
return var;
}
return m_props;
}
if (var)
return var->getStringValue("name");
throw sg_exception("Unknow variant in package " + id());
}
Package::PreviewVec Package::previewsForVariant(unsigned int vIndex) const
Package::ThumbnailVec Package::thumbnailsForVariant(unsigned int vIndex) const
{
SGPropertyNode_ptr var = propsForVariant(vIndex);
return previewsFromProps(var);
if (vIndex == 0) {
return thumbnailsFromProps(m_props);
}
SGPropertyNode_ptr var = m_props->getChild("variant", vIndex - 1);
if (!var) {
throw sg_exception("Unknow variant in package " + id());
}
return thumbnailsFromProps(var);
}
Package::Preview::Type previewTypeFromString(const std::string& s)
Package::Thumbnail::Type thumbnailTypeFromString(const std::string& s)
{
if (s == "exterior") return Package::Preview::Type::EXTERIOR;
if (s == "interior") return Package::Preview::Type::INTERIOR;
if (s == "panel") return Package::Preview::Type::PANEL;
return Package::Preview::Type::UNKNOWN;
if (s == "exterior") return Package::Thumbnail::Type::EXTERIOR;
if (s == "interior") return Package::Thumbnail::Type::INTERIOR;
if (s == "panel") return Package::Thumbnail::Type::PANEL;
return Package::Thumbnail::Type::UNKNOWN;
}
Package::Preview::Preview(const std::string& aUrl, const std::string& aPath, Type aType) :
Package::Thumbnail::Thumbnail(const std::string& aUrl, const std::string& aPath, Type aType) :
url(aUrl),
path(aPath),
type(aType)
{
}
Package::PreviewVec Package::previewsFromProps(const SGPropertyNode_ptr& ptr) const
Package::ThumbnailVec Package::thumbnailsFromProps(const SGPropertyNode_ptr& ptr) const
{
PreviewVec result;
ThumbnailVec result;
for (auto thumbNode : ptr->getChildren("preview")) {
Preview t(thumbNode->getStringValue("url"),
for (auto thumbNode : ptr->getChildren("thumbnail")) {
Thumbnail t(thumbNode->getStringValue("url"),
thumbNode->getStringValue("path"),
previewTypeFromString(thumbNode->getStringValue("type")));
thumbnailTypeFromString(thumbNode->getStringValue("type")));
result.push_back(t);
}

View File

@@ -114,7 +114,7 @@ public:
*/
std::string md5() const;
std::string getLocalisedProp(const std::string& aName, const unsigned int vIndex = 0) const;
std::string getLocalisedProp(const std::string& aName) const;
unsigned int revision() const;
@@ -140,9 +140,9 @@ public:
string_list thumbnails() const;
/**
* information about a preview image
* information about a thumbnail
*/
struct Preview {
struct Thumbnail {
enum class Type
{
UNKNOWN,
@@ -154,19 +154,19 @@ public:
// actual value for GUIs?
};
Preview(const std::string& url, const std::string& path, Type ty = Type::UNKNOWN);
Thumbnail(const std::string& url, const std::string& path, Type ty = Type::UNKNOWN);
std::string url;
std::string path;
Type type = Type::UNKNOWN;
};
typedef std::vector<Preview> PreviewVec;
typedef std::vector<Thumbnail> ThumbnailVec;
/**
* retrieve all the thumbnails for a variant
*/
PreviewVec previewsForVariant(unsigned int vIndex) const;
ThumbnailVec thumbnailsForVariant(unsigned int vIndex) const;
/**
* Packages we depend upon.
@@ -194,16 +194,13 @@ private:
std::string getLocalisedString(const SGPropertyNode* aRoot, const char* aName) const;
PreviewVec previewsFromProps(const SGPropertyNode_ptr& ptr) const;
SGPropertyNode_ptr propsForVariant(const unsigned int vIndex, const char* propName = nullptr) const;
ThumbnailVec thumbnailsFromProps(const SGPropertyNode_ptr& ptr) const;
SGPropertyNode_ptr m_props;
std::string m_id;
string_set m_tags;
CatalogRef m_catalog;
string_list m_variants;
mutable function_list<InstallCallback> _install_cb;
};

View File

@@ -477,7 +477,7 @@ void Root::installProgress(InstallRef aInstall, unsigned int aBytes, unsigned in
void Root::startNext(InstallRef aCurrent)
{
if (d->updateDeque.empty() || (d->updateDeque.front() != aCurrent)) {
if (d->updateDeque.front() != aCurrent) {
SG_LOG(SG_GENERAL, SG_ALERT, "current install of package not head of the deque");
} else {
d->updateDeque.pop_front();

View File

@@ -19,7 +19,7 @@
#define SG_PACKAGE_ROOT_HXX
#include <vector>
#include <memory> // for unique_ptr
#include <memory> // for auto_ptr
#include <simgear/misc/sg_path.hxx>
#include <simgear/package/Delegate.hxx>
@@ -161,7 +161,7 @@ private:
class ThumbnailDownloader;
class RootPrivate;
std::unique_ptr<RootPrivate> d;
std::auto_ptr<RootPrivate> d;
};
typedef SGSharedPtr<Root> RootRef;

View File

@@ -48,22 +48,22 @@
<revision>10</revision>
</depends>
<preview>
<thumbnail>
<type>exterior</type>
<path>thumb-exterior.png</path>
<url>http://foo.bar.com/thumb-exterior.png</url>
</preview>
</thumbnail>
<preview>
<thumbnail>
<type>panel</type>
<path>thumb-panel.png</path>
<url>http://foo.bar.com/thumb-panel.png</url>
</preview>
</thumbnail>
<preview>
<thumbnail>
<path>thumb-something.png</path>
<url>http://foo.bar.com/thumb-something.png</url>
</preview>
</thumbnail>
<variant>
<id>c172p-2d-panel</id>
@@ -74,34 +74,34 @@
<id>c172p-floats</id>
<name>C172 with floats</name>
<preview>
<thumbnail>
<type>exterior</type>
<path>thumb-exterior-floats.png</path>
<url>http://foo.bar.com/thumb-exterior-floats.png</url>
</preview>
</thumbnail>
<preview>
<thumbnail>
<type>panel</type>
<path>thumb-panel.png</path>
<url>http://foo.bar.com/thumb-panel.png</url>
</preview>
</thumbnail>
</variant>
<variant>
<id>c172p-skis</id>
<name>C172 with skis</name>
<preview>
<thumbnail>
<type>exterior</type>
<path>thumb-exterior-skis.png</path>
<url>http://foo.bar.com/thumb-exterior-skis.png</url>
</preview>
</thumbnail>
<preview>
<thumbnail>
<type>panel</type>
<path>thumb-panel.png</path>
<url>http://foo.bar.com/thumb-panel.png</url>
</preview>
</thumbnail>
</variant>
<md5>ec0e2ffdf98d6a5c05c77445e5447ff5</md5>
@@ -141,17 +141,17 @@
<md5>a94ca5704f305b90767f40617d194ed6</md5>
<url>http://localhost:2000/catalogTest1/b737.tar.gz</url>
<preview>
<thumbnail>
<type>exterior</type>
<path>thumb-exterior.png</path>
<url>http://foo.bar.com/thumb-exterior.png</url>
</preview>
</thumbnail>
<preview>
<thumbnail>
<type>panel</type>
<path>thumb-panel.png</path>
<url>http://foo.bar.com/thumb-panel.png</url>
</preview>
</thumbnail>
</package>

View File

@@ -20,17 +20,15 @@
<url>http://localhost:2000/catalogTest1/alpha.zip</url>
<dir>alpha</dir>
</package>
<package>
<id>c172p</id>
<name>Cessna 172-P</name>
<dir>c172p</dir>
<description>A plane made by Cessna on Jupiter</description>
<description>A plane made by Cessna</description>
<revision type="int">42</revision>
<file-size-bytes type="int">860</file-size-bytes>
<author>Standard author</author>
<tag>cessna</tag>
<tag>ga</tag>
@@ -50,22 +48,22 @@
<revision>10</revision>
</depends>
<preview>
<thumbnail>
<type>exterior</type>
<path>thumb-exterior.png</path>
<url>http://foo.bar.com/thumb-exterior.png</url>
</preview>
</thumbnail>
<preview>
<thumbnail>
<type>panel</type>
<path>thumb-panel.png</path>
<url>http://foo.bar.com/thumb-panel.png</url>
</preview>
</thumbnail>
<preview>
<thumbnail>
<path>thumb-something.png</path>
<url>http://foo.bar.com/thumb-something.png</url>
</preview>
</thumbnail>
<variant>
<id>c172p-2d-panel</id>
@@ -75,46 +73,40 @@
<variant>
<id>c172p-floats</id>
<name>C172 with floats</name>
<description>A plane with floats</description>
<author>Floats variant author</author>
<preview>
<thumbnail>
<type>exterior</type>
<path>thumb-exterior-floats.png</path>
<url>http://foo.bar.com/thumb-exterior-floats.png</url>
</preview>
</thumbnail>
<preview>
<thumbnail>
<type>panel</type>
<path>thumb-panel.png</path>
<url>http://foo.bar.com/thumb-panel.png</url>
</preview>
</thumbnail>
</variant>
<variant>
<id>c172p-skis</id>
<name>C172 with skis</name>
<description>A plane with skis</description>
<preview>
<thumbnail>
<type>exterior</type>
<path>thumb-exterior-skis.png</path>
<url>http://foo.bar.com/thumb-exterior-skis.png</url>
</preview>
</thumbnail>
<preview>
<thumbnail>
<type>panel</type>
<path>thumb-panel.png</path>
<url>http://foo.bar.com/thumb-panel.png</url>
</preview>
</thumbnail>
</variant>
<md5>ec0e2ffdf98d6a5c05c77445e5447ff5</md5>
<url>http://localhost:2000/catalogTest1/c172p.zip</url>
<!-- legacy thumbnails also supported -->
<thumbnail>http://foo.bar.com/thumb-exterior.png</thumbnail>
<thumbnail-path>exterior.png</thumbnail-path>
</package>
<package>

View File

@@ -26,7 +26,7 @@ public:
(*itr)->addChangeListener(this);
}
private:
void valueChanged(SGPropertyNode* node) override;
void valueChanged(SGPropertyNode* node);
virtual void valueChangedImplementation();
};
@@ -53,10 +53,10 @@ public:
}
bool isDirty() { return _dirty; }
bool isValid() { return _valid; }
virtual void unregister_property(SGPropertyNode* node) override;
void unregister_property(SGPropertyNode* node);
static void fireChangeListeners();
private:
virtual void valueChangedImplementation() override;
virtual void valueChangedImplementation();
virtual void valuesChanged();
bool _dirty;
bool _valid;
@@ -90,7 +90,7 @@ public:
if (initial)
valuesChanged();
}
virtual void valuesChanged() override
virtual void valuesChanged()
{
ExtendedPropertyAdapter<T, std::vector<SGPropertyNode*> > adaptor(_watched);
T val = adaptor();

View File

@@ -11,10 +11,14 @@
#include <cmath>
#include <iostream>
#include <simgear/misc/test_macros.hxx>
#define VERIFY_CLOSE(a, b) SG_CHECK_EQUAL_EP2((a), (b), 1e-5)
#define VERIFY_CLOSE(a, b) \
if( std::fabs(a - b) > 1e-5 ) \
{ \
std::cerr << "failed: line " << __LINE__ << ": "\
<< a << " != " << b\
<< std::endl; \
return 1; \
}
int main(int argc, char* argv[])
{

View File

@@ -34,7 +34,6 @@ using std::cerr;
# include <boost/range.hpp>
# include <simgear/compiler.h>
# include <simgear/debug/logstream.hxx>
# include <simgear/sg_inlines.h>
# include "PropertyInterpolationMgr.hxx"
# include "vectorPropTemplates.hxx"
@@ -1100,7 +1099,7 @@ SGPropertyNode::addChildren( const std::string& name,
}
/**
* Get a non-const child by position.
* Get a non-const child by index.
*/
SGPropertyNode *
SGPropertyNode::getChild (int position)
@@ -1113,7 +1112,7 @@ SGPropertyNode::getChild (int position)
/**
* Get a const child by position.
* Get a const child by index.
*/
const SGPropertyNode *
SGPropertyNode::getChild (int position) const
@@ -1124,16 +1123,6 @@ SGPropertyNode::getChild (int position) const
return 0;
}
unsigned int SGPropertyNode::getPosition() const
{
if (_parent == nullptr) {
return 0;
}
auto it = std::find(_parent->_children.begin(), _parent->_children.end(), this);
assert(it != _parent->_children.end());
return std::distance(_parent->_children.begin(), it);
}
/**
* Get a non-const child by name and index, creating if necessary.
@@ -2493,11 +2482,6 @@ SGPropertyNode::eraseChild(simgear::PropertyList::iterator child)
// Implementation of SGPropertyChangeListener.
////////////////////////////////////////////////////////////////////////
SGPropertyChangeListener::SGPropertyChangeListener(bool recursive)
{
SG_UNUSED(recursive); // for the moment, all listeners are recursive
}
SGPropertyChangeListener::~SGPropertyChangeListener ()
{
for (int i = static_cast<int>(_properties.size() - 1); i >= 0; i--)

View File

@@ -735,7 +735,6 @@ public:
virtual void childRemoved(SGPropertyNode * parent, SGPropertyNode * child);
protected:
SGPropertyChangeListener(bool recursive = false);
friend class SGPropertyNode;
virtual void register_property (SGPropertyNode * node);
virtual void unregister_property (SGPropertyNode * node);
@@ -848,10 +847,6 @@ public:
*/
const SGPropertyNode * getParent () const { return _parent; }
/**
* Get node's position in parent (*NOT* index)
*/
unsigned int getPosition() const;
//
// Children.

View File

@@ -21,7 +21,6 @@
#include <simgear/misc/sg_path.hxx>
#include <simgear/xml/easyxml.hxx>
#include <simgear/misc/ResourceManager.hxx>
#include <simgear/io/iostreams/sgstream.hxx>
#include "props.hxx"
#include "props_io.hxx"
@@ -697,7 +696,7 @@ writeProperties (const SGPath &path, const SGPropertyNode * start_node,
SGPath dpath(path);
dpath.create_dir(0755);
sg_ofstream output(path);
ofstream output(path.local8BitStr().c_str());
if (output.good()) {
writeProperties(output, start_node, write_all, archive_flag);
} else {

View File

@@ -9,16 +9,12 @@
#include <simgear/compiler.h>
#include <memory> // std::unique_ptr
#include <iostream>
#include <map>
#include "props.hxx"
#include "props_io.hxx"
#include <simgear/misc/test_macros.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/misc/test_macros.hxx>
using std::cout;
using std::cerr;
@@ -304,34 +300,18 @@ test_property_nodes ()
grandchild = child->getChild("bar", 3, true);
grandchild->setDoubleValue(400);
SG_CHECK_EQUAL(grandchild->getPosition(), 3);
child = root.getChild("hack", 0, true);
SG_CHECK_EQUAL(child->getPosition(), 1);
grandchild = child->getChild("bar", 0, true);
grandchild->setDoubleValue(100);
grandchild = child->getChild("bar", 3, true);
grandchild->setDoubleValue(200);
SG_CHECK_EQUAL(grandchild->getPosition(), 1);
grandchild = child->getChild("bar", 1, true);
grandchild->setDoubleValue(300);
grandchild = child->getChild("bar", 2, true);
grandchild->setDoubleValue(400);
SG_CHECK_EQUAL(grandchild->getPosition(), 3);
dump_node(&root);
SG_CHECK_EQUAL(child->getPosition(), 1);
SG_CHECK_EQUAL(grandchild->getPosition(), 3);
cout << "Trying path (expect /foo[0]/bar[0])" << endl;
grandchild = root.getNode("/hack/../foo/./bar[0]");
cout << "Path is " << grandchild->getPath() << endl;
@@ -349,8 +329,6 @@ test_property_nodes ()
cerr << "** FAILED to create /a/b/c" << endl;
dump_node(&root);
cout << endl;
}
void test_addChild()
@@ -409,540 +387,6 @@ void test_addChild()
}
bool ensureNListeners(SGPropertyNode* node, int n)
{
if (node->nListeners() != n) {
return false;
}
for (int c=0; c < node->nChildren(); ++c) {
if (!ensureNListeners(node->getChild(c), n)) {
return false;
}
}
return true;
}
void defineSamplePropertyTree(SGPropertyNode_ptr root)
{
root->setIntValue("position/body/a", 42);
root->setIntValue("position/body/c", 1);
root->setIntValue("position/body/d", 600);
root->setDoubleValue("velocity/body/x", 1.23);
root->setDoubleValue("velocity/body/y", 4.56);
root->setDoubleValue("velocity/body/z", 7.89);
root->setStringValue("settings/render/foo", "flightgear");
root->setStringValue("version", "10.3.19");
for (int i=0; i<4; ++i) {
root->setDoubleValue("controls/engine[" + std::to_string(i) + "]/throttle", 0.1 * i);
root->setBoolValue("controls/engine[" + std::to_string(i) + "]/starter", i % 2);
root->setDoubleValue("engine[" + std::to_string(i) + "]/rpm", 1000 * i);
root->setDoubleValue("engine[" + std::to_string(i) + "]/temp", 100 * i);
}
root->setDoubleValue("controls/doors/door[2]/position-norm", 0.5);
}
class TestListener : public SGPropertyChangeListener
{
public:
TestListener(SGPropertyNode* root, bool recursive = false) :
_root(root) {}
virtual void valueChanged(SGPropertyNode* node) override
{
std::cout << "saw value changed for:" << node->getPath() << std::endl;
valueChangeCount[node]++;
}
int checkValueChangeCount(const std::string& path) const
{
auto it = valueChangeCount.find(_root->getNode(path));
if (it == valueChangeCount.end()) {
return 0;
}
return it->second;
}
struct ParentChange
{
SGPropertyNode_ptr parent, child;
};
virtual void childAdded(SGPropertyNode * parent, SGPropertyNode * child) override
{
adds.push_back({parent, child});
}
virtual void childRemoved(SGPropertyNode * parent, SGPropertyNode * child) override
{
removes.push_back({parent, child});
}
std::vector<SGPropertyNode*> addedTo(SGPropertyNode* pr)
{
std::vector<SGPropertyNode*> result;
for (auto c : adds) {
if (c.parent == pr) {
result.push_back(c.child);
}
}
return result;
}
std::vector<SGPropertyNode*> removedFrom(SGPropertyNode* pr)
{
std::vector<SGPropertyNode*> result;
for (auto c : removes) {
if (c.parent == pr) {
result.push_back(c.child);
}
}
return result;
}
bool checkAdded(const std::string& pr, SGPropertyNode* n)
{
auto adds = addedTo(_root->getNode(pr));
return std::find(adds.begin(), adds.end(), n) != adds.end();
}
bool checkRemoved(const std::string& pr, SGPropertyNode* n)
{
auto removes = removedFrom(_root->getNode(pr));
return std::find(removes.begin(), removes.end(), n) != removes.end();
}
bool checkRemoved(SGPropertyNode* pr, SGPropertyNode* n)
{
auto removes = removedFrom(pr);
return std::find(removes.begin(), removes.end(), n) != removes.end();
}
void resetChangeCounts()
{
valueChangeCount.clear();
adds.clear();
removes.clear();
}
private:
SGPropertyNode* _root;
std::map<SGPropertyNode*, unsigned int> valueChangeCount;
std::vector<ParentChange> adds;
std::vector<ParentChange> removes;
};
void testListener()
{
SGPropertyNode_ptr tree(new SGPropertyNode);
defineSamplePropertyTree(tree);
// basic listen
{
TestListener l(tree.get());
tree->getNode("position/body/c")->addChangeListener(&l);
tree->getNode("velocity/body/z")->addChangeListener(&l);
tree->getNode("controls/engine[2]/starter")->addChangeListener(&l);
tree->setBoolValue("controls/engine[2]/starter", true);
SG_CHECK_EQUAL(l.checkValueChangeCount("controls/engine[2]/starter"), 1);
SG_CHECK_EQUAL(l.checkValueChangeCount("position/body/c"), 0);
tree->setIntValue("position/body/c", 123);
SG_CHECK_EQUAL(l.checkValueChangeCount("controls/engine[2]/starter"), 1);
SG_CHECK_EQUAL(l.checkValueChangeCount("position/body/c"), 1);
// verify that changing non-listened props doesn't affect anything
tree->setIntValue("position/body/a", 19);
tree->setBoolValue("controls/engine[1]/starter", true);
SG_CHECK_EQUAL(l.checkValueChangeCount("controls/engine[2]/starter"), 1);
SG_CHECK_EQUAL(l.checkValueChangeCount("position/body/c"), 1);
}
// initial value set
{
TestListener l(tree.get());
tree->getNode("velocity/body/y")->addChangeListener(&l, true);
tree->getNode("controls/engine[2]/starter")->addChangeListener(&l, true);
SG_CHECK_EQUAL(l.checkValueChangeCount("controls/engine[2]/starter"), 1);
SG_CHECK_EQUAL(l.checkValueChangeCount("velocity/body/y"), 1);
}
// delete listener while listening, should be fine
{
std::unique_ptr<TestListener> l( new TestListener(tree.get()));
tree->getNode("position/body/c")->addChangeListener(l.get());
tree->getNode("velocity/body/z")->addChangeListener(l.get());
tree->getNode("controls/engine[2]/starter")->addChangeListener(l.get());
SG_CHECK_EQUAL(tree->getNode("position/body/c")->nListeners(), 1);
SG_CHECK_EQUAL(
tree->getNode("controls/engine[2]/starter")->nListeners(), 1);
l.reset();
SG_CHECK_EQUAL(tree->getNode("position/body/c")->nListeners(), 0);
SG_CHECK_EQUAL(
tree->getNode("controls/engine[2]/starter")->nListeners(), 0);
tree->getNode("position/body/c")->setIntValue(49.0);
tree->getNode("controls/engine[2]/starter")->setBoolValue(true);
}
// add/remove of child listen
{
TestListener l(tree.get());
tree->getNode("controls")->addChangeListener(&l);
tree->getNode("velocity/body")->addChangeListener(&l);
tree->setDoubleValue("controls/pitch", 0.5);
SG_VERIFY(l.checkAdded("controls", tree->getNode("controls/pitch")));
tree->setIntValue("controls/yaw", 12);
SG_VERIFY(l.checkAdded("controls", tree->getNode("controls/yaw")));
// branch as well as leaf nodes should work the same
tree->setBoolValue("controls/gears/gear[1]/locked", true);
SG_VERIFY(l.checkAdded("controls", tree->getNode("controls/gears")));
SGPropertyNode_ptr rm = tree->getNode("velocity/body/z");
tree->getNode("velocity/body")->removeChild("z");
SG_VERIFY(l.checkRemoved("velocity/body", rm.get()));
SG_CHECK_EQUAL(
l.checkRemoved("velocity/body", tree->getNode("velocity/body/x")),
false);
}
tree = new SGPropertyNode;
defineSamplePropertyTree(tree);
// ensure basic listenter is not recursive
// disabled for now, since all listeners are recurisve
#if 0
{
TestListener l(tree.get(), false /* not recursive */);
tree->getNode("position/body")->addChangeListener(&l);
tree->getNode("controls/")->addChangeListener(&l);
SG_CHECK_EQUAL(l.checkValueChangeCount("position/body/a"), 0);
tree->setIntValue("position/body/a", 111);
tree->setIntValue("position/body/z", 43);
SG_CHECK_EQUAL(l.checkValueChangeCount("position/body/a"), 0);
SG_CHECK_EQUAL(l.checkValueChangeCount("position/body/z"), 0);
tree->getNode("position/body/new", true);
SG_VERIFY(l.checkAdded("position/body",
tree->getNode("position/body/new")));
}
#endif
tree = new SGPropertyNode;
defineSamplePropertyTree(tree);
// recursive listen
{
TestListener l(tree.get(), true /* recursive */);
tree->getNode("position/body")->addChangeListener(&l);
tree->getNode("controls/")->addChangeListener(&l);
SG_CHECK_EQUAL(l.checkValueChangeCount("position/body/a"), 0);
tree->setIntValue("position/body/a", 111);
tree->setIntValue("position/body/z", 43);
SG_CHECK_EQUAL(l.checkValueChangeCount("position/body/a"), 1);
SG_CHECK_EQUAL(l.checkValueChangeCount("position/body/z"), 1);
SG_VERIFY(l.checkAdded("position/body",
tree->getNode("position/body/z")));
tree->setBoolValue("controls/engine[3]/starter", true);
SG_CHECK_EQUAL(l.checkValueChangeCount("controls/engine[3]/starter"), 1);
tree->setBoolValue("controls/engines[1]/fuel-cutoff", true);
SG_VERIFY(
l.checkAdded("controls/engines[1]",
tree->getNode("controls/engines[1]/fuel-cutoff")));
SG_CHECK_EQUAL(
l.checkValueChangeCount("controls/engines[1]/fuel-cutoff"), 1);
tree->setDoubleValue("controls/doors/door[2]/position-norm", 0.5);
SG_CHECK_EQUAL(
l.checkValueChangeCount("controls/doors/door[2]/position-norm"), 1);
SGPropertyNode_ptr door2Node = tree->getNode("controls/doors/door[2]");
SGPropertyNode_ptr door2PosNode = tree->getNode("controls/doors/door[2]/position-norm");
tree->getNode("controls/doors")->removeChild(door2Node);
SG_VERIFY(l.checkRemoved("controls/doors", door2Node));
// default is not recurse for children
SG_CHECK_EQUAL(l.checkRemoved(door2Node, door2PosNode), false);
// adds *are* seen recursively
tree->setStringValue("controls/lights/light[3]/foo/state", "dim");
SG_VERIFY(
l.checkAdded("controls/lights/light[3]/foo",
tree->getNode("controls/lights/light[3]/foo/state")));
// remove a listener
tree->getNode("controls")->removeChangeListener(&l);
// removing listener does not trigger remove notifications
SG_VERIFY(!l.checkRemoved("controls",
tree->getNode("controls/engine[3]")));
SG_VERIFY(!l.checkRemoved("controls/engine[3]",
tree->getNode("controls/engine[3]/starter")));
tree->setBoolValue("controls/engines[1]/fuel-cutoff", false);
tree->setBoolValue("controls/engines[9]/fuel-cutoff", true);
// values should be unchanged
SG_CHECK_EQUAL(
l.checkValueChangeCount("controls/engines[1]/fuel-cutoff"), 1);
SG_CHECK_EQUAL(
l.checkValueChangeCount("controls/engines[9]/fuel-cutoff"), 0);
// ensure additional calls to fireChildrenRecursive don't cause multiple adds
// disabled for now since we don't add recursive listeners to sub-trees
// SG_VERIFY(ensureNListeners(tree->getNode("position/body"), 1));
SG_VERIFY(ensureNListeners(tree->getNode("controls"), 0));
tree->getNode("position/body")->fireCreatedRecursive();
// disabled for now since we don't add recursive listeners to sub-trees
//SG_VERIFY(ensureNListeners(tree->getNode("position/body"), 1));
}
}
void testAliasedListeners()
{
SGPropertyNode_ptr tree(new SGPropertyNode);
defineSamplePropertyTree(tree);
TestListener l(tree.get());
tree->getNode("position/world/x", true)->alias(tree->getNode("position/body/a"));
tree->getNode("position/earth", true)->alias(tree->getNode("position/body"));
tree->getNode("position/world/x")->addChangeListener(&l);
tree->getNode("position/earth")->addChangeListener(&l);
tree->setIntValue("position/body/a", 99);
SG_CHECK_EQUAL(tree->getIntValue("position/world/x"), 99);
SG_CHECK_EQUAL(l.checkValueChangeCount("position/world/x"), 1);
tree->setIntValue("position/world/x", 101);
SG_CHECK_EQUAL(tree->getIntValue("position/body/a"), 101);
SG_CHECK_EQUAL(l.checkValueChangeCount("position/world/x"), 2);
}
class TiedPropertyDonor
{
public:
int someMember = 100;
const char* getWrappedA() const { return wrappedMember.c_str(); }
void setWrappedA(const char* s) { wrappedMember = std::string(s); }
std::string wrappedMember;
double wrappedB = 1.23;
double getWrappedB() const { return wrappedB; }
};
void tiedPropertiesTest()
{
TiedPropertyDonor donor;
SGPropertyNode_ptr tree(new SGPropertyNode);
defineSamplePropertyTree(tree);
tree->setDoubleValue("position/body/mass", 145.0);
tree->tie("position/body/a", SGRawValuePointer<int>(&donor.someMember));
tree->tie("settings/render/foo", SGRawValueMethods<TiedPropertyDonor, const char*>(donor, &TiedPropertyDonor::getWrappedA, &TiedPropertyDonor::setWrappedA));
tree->tie("position/body/mass", SGRawValueMethods<TiedPropertyDonor, double>(donor, &TiedPropertyDonor::getWrappedB, nullptr));
// tie sets current values of the property onto the setter
SG_CHECK_EQUAL(tree->getStringValue("settings/render/foo"),
std::string("flightgear"));
SG_CHECK_EQUAL(tree->getIntValue("position/body/a"), 42);
// but can't write to this one!
SG_CHECK_EQUAL(tree->getDoubleValue("position/body/mass"), 1.23);
donor.setWrappedA("hello world");
donor.someMember = 13;
SG_CHECK_EQUAL(tree->getIntValue("position/body/a"), 13);
SG_CHECK_EQUAL(tree->getStringValue("settings/render/foo"),
std::string("hello world"));
donor.someMember = 45;
donor.wrappedMember = "apples";
donor.wrappedB = 5000.0;
SG_CHECK_EQUAL(tree->getIntValue("position/body/a"), 45);
SG_CHECK_EQUAL(tree->getStringValue("settings/render/foo"),
std::string("apples"));
SG_CHECK_EQUAL(tree->getDoubleValue("position/body/mass"), 5000.0);
// set value externally
tree->setIntValue("position/body/a", 99);
SG_CHECK_EQUAL(donor.someMember, 99);
tree->setStringValue("settings/render/foo", "lemons");
SG_CHECK_EQUAL(donor.wrappedMember, "lemons");
// set read-only
bool success = tree->setDoubleValue("position/body/mass", 10000.0);
SG_VERIFY(!success);
SG_CHECK_EQUAL(donor.wrappedB, 5000.0); // must not have changed
// un-tieing
tree->untie("position/body/a");
tree->untie("position/body/mass");
SG_CHECK_EQUAL(tree->getIntValue("position/body/a"), 99);
SG_CHECK_EQUAL(tree->getDoubleValue("position/body/mass"), 5000.0);
}
void tiedPropertiesListeners()
{
TiedPropertyDonor donor;
SGPropertyNode_ptr tree(new SGPropertyNode);
defineSamplePropertyTree(tree);
tree->tie("position/body/a", SGRawValuePointer<int>(&donor.someMember));
tree->tie("settings/render/foo", SGRawValueMethods<TiedPropertyDonor, const char*>(donor, &TiedPropertyDonor::getWrappedA, &TiedPropertyDonor::setWrappedA));
tree->tie("position/body/mass", SGRawValueMethods<TiedPropertyDonor, double>(donor, &TiedPropertyDonor::getWrappedB, nullptr));
// tie sets current values of the property onto the setter
SG_CHECK_EQUAL(tree->getStringValue("settings/render/foo"),
std::string("flightgear"));
SG_CHECK_EQUAL(tree->getIntValue("position/body/a"), 42);
TestListener l(tree.get());
tree->getNode("position/body/a")->addChangeListener(&l);
tree->getNode("position/body/mass")->addChangeListener(&l);
tree->getNode("settings/render/foo")->addChangeListener(&l);
// firstly test changes via setXXX API and verify they work
tree->setIntValue("position/body/a", 99);
SG_CHECK_EQUAL(donor.someMember, 99);
SG_CHECK_EQUAL(l.checkValueChangeCount("position/body/a"), 1);
tree->setDoubleValue("position/body/mass", -123.0);
SG_CHECK_EQUAL(donor.wrappedB, 1.23); // read-only!
SG_CHECK_EQUAL(l.checkValueChangeCount("position/body/mass"), 0);
tree->setStringValue("settings/render/foo", "thingA");
tree->setStringValue("settings/render/foo", "thingB");
SG_CHECK_EQUAL(donor.wrappedMember, std::string("thingB"));
SG_CHECK_EQUAL(l.checkValueChangeCount("settings/render/foo"), 2);
// now change values from inside the donor and verify it doesn't fire
// the listener
donor.wrappedMember = "pineapples";
SG_CHECK_EQUAL(tree->getStringValue("settings/render/foo"),
std::string("pineapples"));
SG_CHECK_EQUAL(l.checkValueChangeCount("settings/render/foo"), 2);
donor.someMember = 256;
SG_CHECK_EQUAL(tree->getIntValue("position/body/a"), 256);
SG_CHECK_EQUAL(l.checkValueChangeCount("position/body/a"), 1);
// now fire value changed
l.resetChangeCounts();
donor.wrappedB = 4.000000001;
tree->getNode("position/body/a")->fireValueChanged();
SG_CHECK_EQUAL(l.checkValueChangeCount("position/body/a"), 1);
SG_CHECK_EQUAL(tree->getIntValue("position/body/a"), 256);
l.resetChangeCounts();
tree->getNode("position/body/a")->fireValueChanged();
SG_CHECK_EQUAL(l.checkValueChangeCount("position/body/a"), 1);
tree->setDoubleValue("position/body/mass", 4.000000001);
// Not changed
SG_CHECK_EQUAL(l.checkValueChangeCount("position/body/mass"), 0);
donor.someMember = 970;
SG_CHECK_EQUAL(l.checkValueChangeCount("position/body/a"), 1); // not changed
tree->getNode("position/body/a")->fireValueChanged();
SG_CHECK_EQUAL(l.checkValueChangeCount("position/body/a"), 2); // changed
donor.wrappedMember = "pears";
// Not changed
SG_CHECK_EQUAL(l.checkValueChangeCount("settings/render/foo"), 0);
tree->getNode("settings/render/foo")->fireValueChanged();
// Changed
SG_CHECK_EQUAL(l.checkValueChangeCount("settings/render/foo"), 1);
}
void testDeleterListener()
{
SGPropertyNode_ptr tree = new SGPropertyNode;
defineSamplePropertyTree(tree);
// recursive listen
{
TestListener* l = new TestListener(tree.get(), true /* recursive */);
tree->getNode("position/body")->addChangeListener(l);
tree->getNode("controls/")->addChangeListener(l);
SG_CHECK_EQUAL(l->checkValueChangeCount("position/body/a"), 0);
// create additional children
tree->setFloatValue("position/body/sub/theta", 0.1234);
SG_VERIFY(l->checkAdded("position/body/sub",
tree->getNode("position/body/sub/theta")));
SG_CHECK_EQUAL(l->checkValueChangeCount("position/body/sub/theta"), 1);
delete l;
tree->setFloatValue("position/body/sub/theta", 99.123);
tree->setIntValue("position/body/a", 33);
// verify no listeners at all
SG_VERIFY(ensureNListeners(tree, 0));
}
}
int main (int ac, char ** av)
{
test_value();
@@ -950,11 +394,11 @@ int main (int ac, char ** av)
for (int i = 1; i < ac; i++) {
try {
// cout << "Reading " << av[i] << endl;
cout << "Reading " << av[i] << endl;
SGPropertyNode root;
readProperties(SGPath::fromLocal8Bit(av[i]), &root);
writeProperties(cout, &root, true);
// cout << endl;
cout << endl;
} catch (std::string &message) {
cout << "Aborted with " << message << endl;
}
@@ -962,13 +406,5 @@ int main (int ac, char ** av)
test_addChild();
testListener();
tiedPropertiesTest();
tiedPropertiesListeners();
testDeleterListener();
// disable test for the moment
// testAliasedListeners();
return 0;
}

View File

@@ -49,7 +49,7 @@
#include <simgear/debug/logstream.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/misc/sgstream.hxx>
#include <simgear/threads/SGGuard.hxx>
#include <simgear/scene/util/SGReaderWriterOptions.hxx>

View File

@@ -1,4 +1,4 @@
// matlib.cxx -- class to handle material properties
// materialmgr.cxx -- class to handle material properties
//
// Written by Curtis Olson, started May 1998.
//
@@ -36,7 +36,7 @@
#include <simgear/debug/logstream.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/misc/sgstream.hxx>
#include <simgear/props/props.hxx>
#include <simgear/props/props_io.hxx>
#include <simgear/props/condition.hxx>

View File

@@ -69,7 +69,7 @@ class SGMaterialLib : public SGReferenced
private:
class MatLibPrivate;
std::unique_ptr<MatLibPrivate> d;
std::auto_ptr<MatLibPrivate> d;
// associative array of materials
typedef std::vector< SGSharedPtr<SGMaterial> > material_list;

View File

@@ -39,7 +39,7 @@
#include <simgear/debug/logstream.hxx>
#include <simgear/math/sg_random.h>
#include <simgear/misc/sg_path.hxx>
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/misc/sgstream.hxx>
#include <simgear/scene/model/modellib.hxx>
#include "matmodel.hxx"

View File

@@ -1,27 +1,43 @@
#include <simgear/compiler.h>
#include <simgear/misc/test_macros.hxx>
#include "parseBlendFunc.hxx"
#include <simgear/props/props.hxx>
#include <osg/BlendFunc>
#include <iostream>
#define COMPARE(a, b) \
if( (a) != (b) ) \
{ \
std::cerr << "line " << __LINE__ << ": failed: "\
<< #a << " != " << #b << std::endl; \
return 1; \
}
#define VERIFY(a) \
if( !(a) ) \
{ \
std::cerr << "line " << __LINE__ << ": failed: "\
<< #a << std::endl; \
return 1; \
}
int main (int ac, char ** av)
{
osg::ref_ptr<osg::StateSet> ss = new osg::StateSet;
// default blendfunc
SG_VERIFY( simgear::parseBlendFunc(ss) );
VERIFY( simgear::parseBlendFunc(ss) );
osg::BlendFunc* bf = dynamic_cast<osg::BlendFunc*>(
ss->getAttribute(osg::StateAttribute::BLENDFUNC)
);
SG_VERIFY( bf );
SG_CHECK_EQUAL(bf->getSource(), osg::BlendFunc::SRC_ALPHA);
SG_CHECK_EQUAL(bf->getDestination(), osg::BlendFunc::ONE_MINUS_SRC_ALPHA);
SG_CHECK_EQUAL(bf->getSource(), bf->getSourceAlpha());
SG_CHECK_EQUAL(bf->getDestination(), bf->getDestinationAlpha());
VERIFY( bf );
COMPARE(bf->getSource(), osg::BlendFunc::SRC_ALPHA);
COMPARE(bf->getDestination(), osg::BlendFunc::ONE_MINUS_SRC_ALPHA);
COMPARE(bf->getSource(), bf->getSourceAlpha());
COMPARE(bf->getDestination(), bf->getDestinationAlpha());
// now set some values
SGPropertyNode_ptr src = new SGPropertyNode,
@@ -30,17 +46,17 @@ int main (int ac, char ** av)
src->setStringValue("src-alpha");
dest->setStringValue("constant-color");
SG_VERIFY( simgear::parseBlendFunc(ss, src, dest) );
VERIFY( simgear::parseBlendFunc(ss, src, dest) );
bf = dynamic_cast<osg::BlendFunc*>(
ss->getAttribute(osg::StateAttribute::BLENDFUNC)
);
SG_VERIFY( bf );
SG_CHECK_EQUAL(bf->getSource(), osg::BlendFunc::SRC_ALPHA);
SG_CHECK_EQUAL(bf->getDestination(), osg::BlendFunc::CONSTANT_COLOR);
SG_CHECK_EQUAL(bf->getSource(), bf->getSourceAlpha());
SG_CHECK_EQUAL(bf->getDestination(), bf->getDestinationAlpha());
VERIFY( bf );
COMPARE(bf->getSource(), osg::BlendFunc::SRC_ALPHA);
COMPARE(bf->getDestination(), osg::BlendFunc::CONSTANT_COLOR);
COMPARE(bf->getSource(), bf->getSourceAlpha());
COMPARE(bf->getDestination(), bf->getDestinationAlpha());
std::cout << "all tests passed successfully!" << std::endl;
return 0;

View File

@@ -450,8 +450,7 @@ SGMaterialAnimation::createAnimationGroup(osg::Group& parent)
stateSet->setTextureAttribute(0, texture2D,
osg::StateAttribute::OVERRIDE);
stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON);
osg::ref_ptr<osg::Image> textureImage = texture2D->getImage();
if (textureImage && textureImage->isImageTranslucent()) {
if (texture2D->getImage()->isImageTranslucent()) {
stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
}

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